Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

@opentui/react

Package Overview
Dependencies
Maintainers
3
Versions
271
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@opentui/react - npm Package Compare versions

Comparing version
0.3.4
to
0.4.0
+587
chunk-h361mw37.js
// @bun
import {
__require
} from "./chunk-2mx7fq49.js";
// src/components/index.ts
import {
ASCIIFontRenderable,
BoxRenderable,
CodeRenderable,
DiffRenderable,
InputRenderable,
LineNumberRenderable,
MarkdownRenderable,
ScrollBoxRenderable,
SelectRenderable,
TabSelectRenderable,
TextareaRenderable,
TextRenderable
} from "@opentui/core";
// src/components/text.ts
import { TextAttributes, TextNodeRenderable } from "@opentui/core";
var textNodeKeys = ["span", "b", "strong", "i", "em", "u", "br", "a"];
class SpanRenderable extends TextNodeRenderable {
ctx;
constructor(ctx, options) {
super(options);
this.ctx = ctx;
}
}
class TextModifierRenderable extends SpanRenderable {
constructor(options, modifier) {
super(null, options);
if (modifier === "b" || modifier === "strong") {
this.attributes = (this.attributes || 0) | TextAttributes.BOLD;
} else if (modifier === "i" || modifier === "em") {
this.attributes = (this.attributes || 0) | TextAttributes.ITALIC;
} else if (modifier === "u") {
this.attributes = (this.attributes || 0) | TextAttributes.UNDERLINE;
}
}
}
class BoldSpanRenderable extends TextModifierRenderable {
constructor(_ctx, options) {
super(options, "b");
}
}
class ItalicSpanRenderable extends TextModifierRenderable {
constructor(_ctx, options) {
super(options, "i");
}
}
class UnderlineSpanRenderable extends TextModifierRenderable {
constructor(_ctx, options) {
super(options, "u");
}
}
class LineBreakRenderable extends SpanRenderable {
constructor(_ctx, options) {
super(null, options);
this.add();
}
add() {
return super.add(`
`);
}
}
class LinkRenderable extends SpanRenderable {
constructor(_ctx, options) {
const linkOptions = {
...options,
link: { url: options.href }
};
super(null, linkOptions);
}
}
// src/components/index.ts
var baseComponents = {
box: BoxRenderable,
text: TextRenderable,
code: CodeRenderable,
diff: DiffRenderable,
markdown: MarkdownRenderable,
input: InputRenderable,
select: SelectRenderable,
textarea: TextareaRenderable,
scrollbox: ScrollBoxRenderable,
"ascii-font": ASCIIFontRenderable,
"tab-select": TabSelectRenderable,
"line-number": LineNumberRenderable,
span: SpanRenderable,
br: LineBreakRenderable,
b: BoldSpanRenderable,
strong: BoldSpanRenderable,
i: ItalicSpanRenderable,
em: ItalicSpanRenderable,
u: UnderlineSpanRenderable,
a: LinkRenderable
};
var componentCatalogue = { ...baseComponents };
function extend(objects) {
Object.assign(componentCatalogue, objects);
}
function getComponentCatalogue() {
return componentCatalogue;
}
// src/components/app.tsx
import { createContext, useContext } from "react";
var AppContext = createContext({
keyHandler: null,
renderer: null
});
var useAppContext = () => {
return useContext(AppContext);
};
// src/reconciler/renderer.ts
import { CliRenderEvents, engine } from "@opentui/core";
import React2 from "react";
// src/components/error-boundary.tsx
import React from "react";
// jsx-dev-runtime.js
import { Fragment, jsxDEV } from "react/jsx-dev-runtime";
// src/components/error-boundary.tsx
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
render() {
if (this.state.hasError && this.state.error) {
return /* @__PURE__ */ jsxDEV("box", {
style: { flexDirection: "column", padding: 2 },
children: /* @__PURE__ */ jsxDEV("text", {
fg: "red",
children: this.state.error.stack || this.state.error.message
}, undefined, false, undefined, this)
}, undefined, false, undefined, this);
}
return this.props.children;
}
}
// src/reconciler/reconciler.ts
import ReactReconciler from "react-reconciler";
import { ConcurrentRoot } from "react-reconciler/constants";
// src/reconciler/host-config.ts
import { TextNodeRenderable as TextNodeRenderable2 } from "@opentui/core";
// package.json
var package_default = {
name: "@opentui/react",
version: "0.4.0",
description: "React renderer for building terminal user interfaces using OpenTUI core",
license: "MIT",
repository: {
type: "git",
url: "https://github.com/anomalyco/opentui",
directory: "packages/react"
},
module: "src/index.ts",
type: "module",
private: true,
main: "src/index.ts",
exports: {
".": {
import: "./src/index.ts",
types: "./src/index.ts"
},
"./test-utils": {
import: "./src/test-utils.ts",
types: "./src/test-utils.d.ts"
},
"./runtime-plugin-support": {
import: "./scripts/runtime-plugin-support.ts",
types: "./scripts/runtime-plugin-support.ts"
},
"./runtime-plugin-support/configure": {
import: "./scripts/runtime-plugin-support-configure.ts",
types: "./scripts/runtime-plugin-support-configure.ts"
},
"./jsx-runtime": {
import: "./jsx-runtime.js",
types: "./jsx-runtime.d.ts"
},
"./jsx-dev-runtime": {
import: "./jsx-dev-runtime.js",
types: "./jsx-dev-runtime.d.ts"
}
},
scripts: {
build: "bun run scripts/build.ts",
"build:examples": "bun examples/build.ts",
"build:dev": "bun run scripts/build.ts --dev",
publish: "bun run scripts/publish.ts",
test: "bun test"
},
devDependencies: {
"@opentui/keymap": "workspace:*",
"@types/bun": "latest",
"@types/node": "^24.0.0",
"@types/react": "^19.2.0",
"@types/react-reconciler": "^0.33.0",
"@types/ws": "^8.18.1",
react: ">=19.2.0",
"react-devtools-core": "^7.0.1",
typescript: "^5",
ws: "^8.18.0"
},
peerDependencies: {
react: ">=19.2.0",
"react-devtools-core": "^7.0.1",
ws: "^8.18.0"
},
peerDependenciesMeta: {
"react-devtools-core": {
optional: true
},
ws: {
optional: true
}
},
dependencies: {
"@opentui/core": "workspace:*",
"react-reconciler": "^0.33.0"
}
};
// src/reconciler/host-config.ts
import { createContext as createContext2 } from "react";
import { DefaultEventPriority, NoEventPriority } from "react-reconciler/constants";
// src/utils/id.ts
var idCounter = new Map;
function getNextId(type) {
if (!idCounter.has(type)) {
idCounter.set(type, 0);
}
const value = idCounter.get(type) + 1;
idCounter.set(type, value);
return `${type}-${value}`;
}
// src/utils/index.ts
import {
InputRenderable as InputRenderable2,
InputRenderableEvents,
isRenderable,
SelectRenderable as SelectRenderable2,
SelectRenderableEvents,
TabSelectRenderable as TabSelectRenderable2,
TabSelectRenderableEvents,
TextareaRenderable as TextareaRenderable2
} from "@opentui/core";
function initEventListeners(instance, eventName, listener, previousListener) {
if (previousListener) {
instance.off(eventName, previousListener);
}
if (listener) {
instance.on(eventName, listener);
}
}
function setStyle(instance, styles, oldStyles) {
if (oldStyles != null && typeof oldStyles === "object") {
for (const styleName in oldStyles) {
if (oldStyles.hasOwnProperty(styleName)) {
if (styles == null || !styles.hasOwnProperty(styleName)) {
instance[styleName] = null;
}
}
}
}
if (styles != null && typeof styles === "object") {
for (const styleName in styles) {
if (styles.hasOwnProperty(styleName)) {
const value = styles[styleName];
const oldValue = oldStyles?.[styleName];
if (value !== oldValue) {
instance[styleName] = value;
}
}
}
}
}
function setProperty(instance, type, propKey, propValue, oldPropValue) {
switch (propKey) {
case "onChange":
if (instance instanceof InputRenderable2) {
initEventListeners(instance, InputRenderableEvents.CHANGE, propValue, oldPropValue);
} else if (instance instanceof SelectRenderable2) {
initEventListeners(instance, SelectRenderableEvents.SELECTION_CHANGED, propValue, oldPropValue);
} else if (instance instanceof TabSelectRenderable2) {
initEventListeners(instance, TabSelectRenderableEvents.SELECTION_CHANGED, propValue, oldPropValue);
}
break;
case "onInput":
if (instance instanceof InputRenderable2) {
initEventListeners(instance, InputRenderableEvents.INPUT, propValue, oldPropValue);
}
break;
case "onSubmit":
if (instance instanceof InputRenderable2) {
initEventListeners(instance, InputRenderableEvents.ENTER, propValue, oldPropValue);
} else if (instance instanceof TextareaRenderable2) {
instance.onSubmit = propValue;
}
break;
case "onSelect":
if (instance instanceof SelectRenderable2) {
initEventListeners(instance, SelectRenderableEvents.ITEM_SELECTED, propValue, oldPropValue);
} else if (instance instanceof TabSelectRenderable2) {
initEventListeners(instance, TabSelectRenderableEvents.ITEM_SELECTED, propValue, oldPropValue);
}
break;
case "focused":
if (isRenderable(instance)) {
if (!!propValue) {
instance.focus();
} else {
instance.blur();
}
}
break;
case "style":
setStyle(instance, propValue, oldPropValue);
break;
case "children":
break;
default:
instance[propKey] = propValue;
}
}
function setInitialProperties(instance, type, props) {
for (const propKey in props) {
if (!props.hasOwnProperty(propKey)) {
continue;
}
const propValue = props[propKey];
if (propValue == null) {
continue;
}
setProperty(instance, type, propKey, propValue);
}
}
function updateProperties(instance, type, oldProps, newProps) {
for (const propKey in oldProps) {
const oldProp = oldProps[propKey];
if (oldProps.hasOwnProperty(propKey) && oldProp != null && !newProps.hasOwnProperty(propKey)) {
setProperty(instance, type, propKey, null, oldProp);
}
}
for (const propKey in newProps) {
const newProp = newProps[propKey];
const oldProp = oldProps[propKey];
if (newProps.hasOwnProperty(propKey) && newProp !== oldProp && (newProp != null || oldProp != null)) {
setProperty(instance, type, propKey, newProp, oldProp);
}
}
}
// src/reconciler/host-config.ts
var currentUpdatePriority = NoEventPriority;
var hostConfig = {
supportsMutation: true,
supportsPersistence: false,
supportsHydration: false,
supportsMicrotasks: true,
scheduleMicrotask: queueMicrotask,
createInstance(type, props, rootContainerInstance, hostContext) {
if (textNodeKeys.includes(type) && !hostContext.isInsideText) {
throw new Error(`Component of type "${type}" must be created inside of a text node`);
}
const id = getNextId(type);
const components = getComponentCatalogue();
if (!components[type]) {
throw new Error(`Unknown component type: ${type}`);
}
return new components[type](rootContainerInstance.ctx, {
id,
...props
});
},
appendChild(parent, child) {
parent.add(child);
},
removeChild(parent, child) {
parent.remove(child.id);
},
insertBefore(parent, child, beforeChild) {
parent.insertBefore(child, beforeChild);
},
insertInContainerBefore(parent, child, beforeChild) {
parent.insertBefore(child, beforeChild);
},
removeChildFromContainer(parent, child) {
parent.remove(child.id);
},
prepareForCommit(containerInfo) {
return null;
},
resetAfterCommit(containerInfo) {
containerInfo.requestRender();
},
getRootHostContext(rootContainerInstance) {
return { isInsideText: false };
},
getChildHostContext(parentHostContext, type, rootContainerInstance) {
const isInsideText = ["text", ...textNodeKeys].includes(type);
return { ...parentHostContext, isInsideText };
},
shouldSetTextContent(type, props) {
return false;
},
createTextInstance(text, rootContainerInstance, hostContext) {
if (!hostContext.isInsideText) {
throw new Error("Text must be created inside of a text node");
}
return TextNodeRenderable2.fromString(text);
},
scheduleTimeout: setTimeout,
cancelTimeout: clearTimeout,
noTimeout: -1,
shouldAttemptEagerTransition() {
return true;
},
finalizeInitialChildren(instance, type, props, rootContainerInstance, hostContext) {
setInitialProperties(instance, type, props);
return false;
},
commitMount(instance, type, props, internalInstanceHandle) {},
commitUpdate(instance, type, oldProps, newProps, internalInstanceHandle) {
updateProperties(instance, type, oldProps, newProps);
},
commitTextUpdate(textInstance, oldText, newText) {
textInstance.children = [newText];
},
appendChildToContainer(container, child) {
container.add(child);
},
appendInitialChild(parent, child) {
parent.add(child);
},
hideInstance(instance) {
instance.visible = false;
},
unhideInstance(instance, props) {
instance.visible = true;
},
hideTextInstance(textInstance) {
textInstance.visible = false;
},
unhideTextInstance(textInstance, text) {
textInstance.visible = true;
},
clearContainer(container) {
const children = container.getChildren();
children.forEach((child) => container.remove(child.id));
},
setCurrentUpdatePriority(newPriority) {
currentUpdatePriority = newPriority;
},
getCurrentUpdatePriority: () => currentUpdatePriority,
resolveUpdatePriority() {
if (currentUpdatePriority !== NoEventPriority) {
return currentUpdatePriority;
}
return DefaultEventPriority;
},
maySuspendCommit() {
return false;
},
maySuspendCommitOnUpdate() {
return false;
},
maySuspendCommitInSyncRender() {
return false;
},
NotPendingTransition: null,
HostTransitionContext: createContext2(null),
resetFormInstance() {},
requestPostPaintCallback() {},
trackSchedulerEvent() {},
resolveEventType() {
return null;
},
resolveEventTimeStamp() {
return -1.1;
},
preloadInstance() {
return true;
},
startSuspendingCommit() {},
suspendInstance() {},
waitForCommitToBeReady() {
return null;
},
detachDeletedInstance(instance) {
if (!instance.parent) {
instance.destroyRecursively();
}
},
getPublicInstance(instance) {
return instance;
},
preparePortalMount(containerInfo) {},
isPrimaryRenderer: true,
getInstanceFromNode() {
return null;
},
beforeActiveInstanceBlur() {},
afterActiveInstanceBlur() {},
prepareScopeUpdate() {},
getInstanceFromScope() {
return null;
},
rendererPackageName: "@opentui/react",
rendererVersion: package_default.version
};
// src/reconciler/reconciler.ts
var reconciler = ReactReconciler(hostConfig);
if (process.env["DEV"] === "true") {
try {
await import("./chunk-bdqvmfwv.js");
} catch (error) {
if (error.code === "ERR_MODULE_NOT_FOUND") {
console.warn(`
The environment variable DEV is set to true, so opentui tried to import \`react-devtools-core\`,
but this failed as it was not installed. Debugging with React DevTools requires it.
To install use this command:
$ bun add react-devtools-core@7 -d
`.trim() + `
`);
} else {
throw error;
}
}
}
reconciler.injectIntoDevTools();
function _render(element, root) {
const container = reconciler.createContainer(root, ConcurrentRoot, null, false, null, "", console.error, console.error, console.error, () => {});
reconciler.updateContainer(element, container, null, () => {});
return container;
}
// src/reconciler/renderer.ts
var _r = reconciler;
var flushSync = _r.flushSyncFromReconciler ?? _r.flushSync;
var { createPortal } = reconciler;
function createRoot(renderer) {
let container = null;
const cleanup = () => {
if (container) {
reconciler.updateContainer(null, container, null, () => {});
reconciler.flushSyncWork();
container = null;
}
};
renderer.once(CliRenderEvents.DESTROY, cleanup);
return {
render: (node) => {
engine.attach(renderer);
container = _render(React2.createElement(AppContext.Provider, { value: { keyHandler: renderer.keyInput, renderer } }, React2.createElement(ErrorBoundary, null, node)), renderer.root);
},
unmount: cleanup
};
}
export { baseComponents, componentCatalogue, extend, getComponentCatalogue, AppContext, useAppContext, Fragment, jsxDEV, flushSync, createPortal, createRoot };
import { plugin as registerBunPlugin } from "bun";
import * as coreRuntime from "@opentui/core";
import { createRuntimePlugin, } from "@opentui/core/runtime-plugin";
import * as reactRuntime from "react";
import * as reactJsxRuntime from "react/jsx-runtime";
import * as reactJsxDevRuntime from "react/jsx-dev-runtime";
import * as opentuiReactRuntime from "../index.js";
const runtimePluginSupportInstalledKey = "__opentuiReactRuntimePluginSupportInstalled__";
const defaultRuntimeModules = {
"@opentui/react": opentuiReactRuntime,
"@opentui/react/jsx-runtime": reactJsxRuntime,
"@opentui/react/jsx-dev-runtime": reactJsxDevRuntime,
react: reactRuntime,
"react/jsx-runtime": reactJsxRuntime,
"react/jsx-dev-runtime": reactJsxDevRuntime,
};
function normalizeRewriteKey(rewrite) {
return `${rewrite?.nodeModulesRuntimeSpecifiers ?? true}:${rewrite?.nodeModulesBareSpecifiers ?? false}`;
}
function createRuntimeModules(options) {
return {
...defaultRuntimeModules,
...(options?.additional ?? {}),
};
}
function assertCompatibleInstall(install, modules, options) {
for (const specifier of Object.keys(modules)) {
if (!install.specifiers.has(specifier)) {
throw new Error(`OpenTUI React runtime plugin support is already installed without ${specifier}. Call ensureRuntimePluginSupport({ additional }) from @opentui/react/runtime-plugin-support/configure before importing @opentui/react/runtime-plugin-support.`);
}
}
if (options?.core && options.core !== install.core) {
throw new Error("OpenTUI React runtime plugin support is already installed with a different core runtime module.");
}
if (options?.rewrite && normalizeRewriteKey(options.rewrite) !== install.rewriteKey) {
throw new Error("OpenTUI React runtime plugin support is already installed with different rewrite options.");
}
}
export function ensureRuntimePluginSupport(options = {}) {
const state = globalThis;
const modules = createRuntimeModules(options);
const core = options.core ?? coreRuntime;
const rewriteKey = normalizeRewriteKey(options.rewrite);
const install = state[runtimePluginSupportInstalledKey];
if (install) {
assertCompatibleInstall(install, modules, options);
return false;
}
registerBunPlugin(createRuntimePlugin({
core,
additional: modules,
rewrite: options.rewrite,
}));
state[runtimePluginSupportInstalledKey] = {
specifiers: new Set(Object.keys(modules)),
core,
rewriteKey,
};
return true;
}
//# sourceMappingURL=runtime-plugin-support-configure.js.map
{"version":3,"file":"runtime-plugin-support-configure.js","sourceRoot":"","sources":["runtime-plugin-support-configure.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,iBAAiB,EAAE,MAAM,KAAK,CAAA;AACjD,OAAO,KAAK,WAAW,MAAM,eAAe,CAAA;AAC5C,OAAO,EACL,mBAAmB,GAGpB,MAAM,8BAA8B,CAAA;AACrC,OAAO,KAAK,YAAY,MAAM,OAAO,CAAA;AACrC,OAAO,KAAK,eAAe,MAAM,mBAAmB,CAAA;AACpD,OAAO,KAAK,kBAAkB,MAAM,uBAAuB,CAAA;AAC3D,OAAO,KAAK,mBAAmB,MAAM,aAAa,CAAA;AAElD,MAAM,gCAAgC,GAAG,+CAA+C,CAAA;AAkBxF,MAAM,qBAAqB,GAAuC;IAChE,gBAAgB,EAAE,mBAA8C;IAChE,4BAA4B,EAAE,eAA0C;IACxE,gCAAgC,EAAE,kBAA6C;IAC/E,KAAK,EAAE,YAAuC;IAC9C,mBAAmB,EAAE,eAA0C;IAC/D,uBAAuB,EAAE,kBAA6C;CACvE,CAAA;AAED,SAAS,mBAAmB,CAAC,OAAgD;IAC3E,OAAO,GAAG,OAAO,EAAE,4BAA4B,IAAI,IAAI,IAAI,OAAO,EAAE,yBAAyB,IAAI,KAAK,EAAE,CAAA;AAC1G,CAAC;AAED,SAAS,oBAAoB,CAAC,OAA0C;IACtE,OAAO;QACL,GAAG,qBAAqB;QACxB,GAAG,CAAC,OAAO,EAAE,UAAU,IAAI,EAAE,CAAC;KAC/B,CAAA;AACH,CAAC;AAED,SAAS,uBAAuB,CAC9B,OAAoC,EACpC,OAA2C,EAC3C,OAA0C;IAE1C,KAAK,MAAM,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7C,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YACvC,MAAM,IAAI,KAAK,CACb,qEAAqE,SAAS,gKAAgK,CAC/O,CAAA;QACH,CAAC;IACH,CAAC;IAED,IAAI,OAAO,EAAE,IAAI,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI,EAAE,CAAC;QACnD,MAAM,IAAI,KAAK,CAAC,iGAAiG,CAAC,CAAA;IACpH,CAAC;IAED,IAAI,OAAO,EAAE,OAAO,IAAI,mBAAmB,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,UAAU,EAAE,CAAC;QACpF,MAAM,IAAI,KAAK,CAAC,2FAA2F,CAAC,CAAA;IAC9G,CAAC;AACH,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,UAA4C,EAAE;IACvF,MAAM,KAAK,GAAG,UAAuC,CAAA;IACrD,MAAM,OAAO,GAAG,oBAAoB,CAAC,OAAO,CAAC,CAAA;IAC7C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAK,WAAuC,CAAA;IACrE,MAAM,UAAU,GAAG,mBAAmB,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;IAEvD,MAAM,OAAO,GAAG,KAAK,CAAC,gCAAgC,CAAC,CAAA;IACvD,IAAI,OAAO,EAAE,CAAC;QACZ,uBAAuB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;QAClD,OAAO,KAAK,CAAA;IACd,CAAC;IAED,iBAAiB,CACf,mBAAmB,CAAC;QAClB,IAAI;QACJ,UAAU,EAAE,OAAO;QACnB,OAAO,EAAE,OAAO,CAAC,OAAO;KACzB,CAAC,CACH,CAAA;IAED,KAAK,CAAC,gCAAgC,CAAC,GAAG;QACxC,UAAU,EAAE,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACzC,IAAI;QACJ,UAAU;KACX,CAAA;IACD,OAAO,IAAI,CAAA;AACb,CAAC"}
const errorMessage = "@opentui/react/runtime-plugin-support/configure is Bun-only and is not available in Node.js. Use Bun to import this entrypoint."
export function ensureRuntimePluginSupport() {
throw new Error(errorMessage)
}
throw new Error(errorMessage)
import { ensureRuntimePluginSupport } from "./runtime-plugin-support-configure.js";
export { ensureRuntimePluginSupport };
ensureRuntimePluginSupport();
//# sourceMappingURL=runtime-plugin-support.js.map
{"version":3,"file":"runtime-plugin-support.js","sourceRoot":"","sources":["runtime-plugin-support.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,MAAM,uCAAuC,CAAA;AAElF,OAAO,EAAE,0BAA0B,EAAE,CAAA;AAGrC,0BAA0B,EAAE,CAAA"}
const errorMessage = "@opentui/react/runtime-plugin-support is Bun-only and is not available in Node.js. Use Bun to import this entrypoint."
export function ensureRuntimePluginSupport() {
throw new Error(errorMessage)
}
throw new Error(errorMessage)
+1
-1

@@ -14,3 +14,3 @@ // @bun

useAppContext
} from "./chunk-52d5gdqd.js";
} from "./chunk-h361mw37.js";
import"./chunk-2mx7fq49.js";

@@ -17,0 +17,0 @@ // src/hooks/use-blur.ts

@@ -7,3 +7,3 @@ {

"type": "module",
"version": "0.3.4",
"version": "0.4.0",
"description": "React renderer for building terminal user interfaces using OpenTUI core",

@@ -34,7 +34,11 @@ "license": "MIT",

"types": "./scripts/runtime-plugin-support.d.ts",
"import": "./scripts/runtime-plugin-support.ts"
"bun": "./scripts/runtime-plugin-support.js",
"node": "./scripts/runtime-plugin-support.node.js",
"default": "./scripts/runtime-plugin-support.node.js"
},
"./runtime-plugin-support/configure": {
"types": "./scripts/runtime-plugin-support-configure.d.ts",
"import": "./scripts/runtime-plugin-support-configure.ts"
"bun": "./scripts/runtime-plugin-support-configure.js",
"node": "./scripts/runtime-plugin-support-configure.node.js",
"default": "./scripts/runtime-plugin-support-configure.node.js"
},

@@ -53,3 +57,3 @@ "./jsx-runtime": {

"dependencies": {
"@opentui/core": "0.3.4",
"@opentui/core": "0.4.0",
"react-reconciler": "^0.33.0"

@@ -56,0 +60,0 @@ },

// @bun
import {
createRoot
} from "./chunk-52d5gdqd.js";
} from "./chunk-h361mw37.js";
import"./chunk-2mx7fq49.js";

@@ -6,0 +6,0 @@

// @bun
import {
__require
} from "./chunk-2mx7fq49.js";
// src/components/index.ts
import {
ASCIIFontRenderable,
BoxRenderable,
CodeRenderable,
DiffRenderable,
InputRenderable,
LineNumberRenderable,
MarkdownRenderable,
ScrollBoxRenderable,
SelectRenderable,
TabSelectRenderable,
TextareaRenderable,
TextRenderable
} from "@opentui/core";
// src/components/text.ts
import { TextAttributes, TextNodeRenderable } from "@opentui/core";
var textNodeKeys = ["span", "b", "strong", "i", "em", "u", "br", "a"];
class SpanRenderable extends TextNodeRenderable {
ctx;
constructor(ctx, options) {
super(options);
this.ctx = ctx;
}
}
class TextModifierRenderable extends SpanRenderable {
constructor(options, modifier) {
super(null, options);
if (modifier === "b" || modifier === "strong") {
this.attributes = (this.attributes || 0) | TextAttributes.BOLD;
} else if (modifier === "i" || modifier === "em") {
this.attributes = (this.attributes || 0) | TextAttributes.ITALIC;
} else if (modifier === "u") {
this.attributes = (this.attributes || 0) | TextAttributes.UNDERLINE;
}
}
}
class BoldSpanRenderable extends TextModifierRenderable {
constructor(_ctx, options) {
super(options, "b");
}
}
class ItalicSpanRenderable extends TextModifierRenderable {
constructor(_ctx, options) {
super(options, "i");
}
}
class UnderlineSpanRenderable extends TextModifierRenderable {
constructor(_ctx, options) {
super(options, "u");
}
}
class LineBreakRenderable extends SpanRenderable {
constructor(_ctx, options) {
super(null, options);
this.add();
}
add() {
return super.add(`
`);
}
}
class LinkRenderable extends SpanRenderable {
constructor(_ctx, options) {
const linkOptions = {
...options,
link: { url: options.href }
};
super(null, linkOptions);
}
}
// src/components/index.ts
var baseComponents = {
box: BoxRenderable,
text: TextRenderable,
code: CodeRenderable,
diff: DiffRenderable,
markdown: MarkdownRenderable,
input: InputRenderable,
select: SelectRenderable,
textarea: TextareaRenderable,
scrollbox: ScrollBoxRenderable,
"ascii-font": ASCIIFontRenderable,
"tab-select": TabSelectRenderable,
"line-number": LineNumberRenderable,
span: SpanRenderable,
br: LineBreakRenderable,
b: BoldSpanRenderable,
strong: BoldSpanRenderable,
i: ItalicSpanRenderable,
em: ItalicSpanRenderable,
u: UnderlineSpanRenderable,
a: LinkRenderable
};
var componentCatalogue = { ...baseComponents };
function extend(objects) {
Object.assign(componentCatalogue, objects);
}
function getComponentCatalogue() {
return componentCatalogue;
}
// src/components/app.tsx
import { createContext, useContext } from "react";
var AppContext = createContext({
keyHandler: null,
renderer: null
});
var useAppContext = () => {
return useContext(AppContext);
};
// src/reconciler/renderer.ts
import { CliRenderEvents, engine } from "@opentui/core";
import React2 from "react";
// src/components/error-boundary.tsx
import React from "react";
// jsx-dev-runtime.js
import { Fragment, jsxDEV } from "react/jsx-dev-runtime";
// src/components/error-boundary.tsx
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
render() {
if (this.state.hasError && this.state.error) {
return /* @__PURE__ */ jsxDEV("box", {
style: { flexDirection: "column", padding: 2 },
children: /* @__PURE__ */ jsxDEV("text", {
fg: "red",
children: this.state.error.stack || this.state.error.message
}, undefined, false, undefined, this)
}, undefined, false, undefined, this);
}
return this.props.children;
}
}
// src/reconciler/reconciler.ts
import ReactReconciler from "react-reconciler";
import { ConcurrentRoot } from "react-reconciler/constants";
// src/reconciler/host-config.ts
import { TextNodeRenderable as TextNodeRenderable2 } from "@opentui/core";
// package.json
var package_default = {
name: "@opentui/react",
version: "0.3.4",
description: "React renderer for building terminal user interfaces using OpenTUI core",
license: "MIT",
repository: {
type: "git",
url: "https://github.com/anomalyco/opentui",
directory: "packages/react"
},
module: "src/index.ts",
type: "module",
private: true,
main: "src/index.ts",
exports: {
".": {
import: "./src/index.ts",
types: "./src/index.ts"
},
"./test-utils": {
import: "./src/test-utils.ts",
types: "./src/test-utils.d.ts"
},
"./runtime-plugin-support": {
import: "./scripts/runtime-plugin-support.ts",
types: "./scripts/runtime-plugin-support.ts"
},
"./runtime-plugin-support/configure": {
import: "./scripts/runtime-plugin-support-configure.ts",
types: "./scripts/runtime-plugin-support-configure.ts"
},
"./jsx-runtime": {
import: "./jsx-runtime.js",
types: "./jsx-runtime.d.ts"
},
"./jsx-dev-runtime": {
import: "./jsx-dev-runtime.js",
types: "./jsx-dev-runtime.d.ts"
}
},
scripts: {
build: "bun run scripts/build.ts",
"build:examples": "bun examples/build.ts",
"build:dev": "bun run scripts/build.ts --dev",
publish: "bun run scripts/publish.ts",
test: "bun test"
},
devDependencies: {
"@opentui/keymap": "workspace:*",
"@types/bun": "latest",
"@types/node": "^24.0.0",
"@types/react": "^19.2.0",
"@types/react-reconciler": "^0.33.0",
"@types/ws": "^8.18.1",
react: ">=19.2.0",
"react-devtools-core": "^7.0.1",
typescript: "^5",
ws: "^8.18.0"
},
peerDependencies: {
react: ">=19.2.0",
"react-devtools-core": "^7.0.1",
ws: "^8.18.0"
},
peerDependenciesMeta: {
"react-devtools-core": {
optional: true
},
ws: {
optional: true
}
},
dependencies: {
"@opentui/core": "workspace:*",
"react-reconciler": "^0.33.0"
}
};
// src/reconciler/host-config.ts
import { createContext as createContext2 } from "react";
import { DefaultEventPriority, NoEventPriority } from "react-reconciler/constants";
// src/utils/id.ts
var idCounter = new Map;
function getNextId(type) {
if (!idCounter.has(type)) {
idCounter.set(type, 0);
}
const value = idCounter.get(type) + 1;
idCounter.set(type, value);
return `${type}-${value}`;
}
// src/utils/index.ts
import {
InputRenderable as InputRenderable2,
InputRenderableEvents,
isRenderable,
SelectRenderable as SelectRenderable2,
SelectRenderableEvents,
TabSelectRenderable as TabSelectRenderable2,
TabSelectRenderableEvents,
TextareaRenderable as TextareaRenderable2
} from "@opentui/core";
function initEventListeners(instance, eventName, listener, previousListener) {
if (previousListener) {
instance.off(eventName, previousListener);
}
if (listener) {
instance.on(eventName, listener);
}
}
function setStyle(instance, styles, oldStyles) {
if (oldStyles != null && typeof oldStyles === "object") {
for (const styleName in oldStyles) {
if (oldStyles.hasOwnProperty(styleName)) {
if (styles == null || !styles.hasOwnProperty(styleName)) {
instance[styleName] = null;
}
}
}
}
if (styles != null && typeof styles === "object") {
for (const styleName in styles) {
if (styles.hasOwnProperty(styleName)) {
const value = styles[styleName];
const oldValue = oldStyles?.[styleName];
if (value !== oldValue) {
instance[styleName] = value;
}
}
}
}
}
function setProperty(instance, type, propKey, propValue, oldPropValue) {
switch (propKey) {
case "onChange":
if (instance instanceof InputRenderable2) {
initEventListeners(instance, InputRenderableEvents.CHANGE, propValue, oldPropValue);
} else if (instance instanceof SelectRenderable2) {
initEventListeners(instance, SelectRenderableEvents.SELECTION_CHANGED, propValue, oldPropValue);
} else if (instance instanceof TabSelectRenderable2) {
initEventListeners(instance, TabSelectRenderableEvents.SELECTION_CHANGED, propValue, oldPropValue);
}
break;
case "onInput":
if (instance instanceof InputRenderable2) {
initEventListeners(instance, InputRenderableEvents.INPUT, propValue, oldPropValue);
}
break;
case "onSubmit":
if (instance instanceof InputRenderable2) {
initEventListeners(instance, InputRenderableEvents.ENTER, propValue, oldPropValue);
} else if (instance instanceof TextareaRenderable2) {
instance.onSubmit = propValue;
}
break;
case "onSelect":
if (instance instanceof SelectRenderable2) {
initEventListeners(instance, SelectRenderableEvents.ITEM_SELECTED, propValue, oldPropValue);
} else if (instance instanceof TabSelectRenderable2) {
initEventListeners(instance, TabSelectRenderableEvents.ITEM_SELECTED, propValue, oldPropValue);
}
break;
case "focused":
if (isRenderable(instance)) {
if (!!propValue) {
instance.focus();
} else {
instance.blur();
}
}
break;
case "style":
setStyle(instance, propValue, oldPropValue);
break;
case "children":
break;
default:
instance[propKey] = propValue;
}
}
function setInitialProperties(instance, type, props) {
for (const propKey in props) {
if (!props.hasOwnProperty(propKey)) {
continue;
}
const propValue = props[propKey];
if (propValue == null) {
continue;
}
setProperty(instance, type, propKey, propValue);
}
}
function updateProperties(instance, type, oldProps, newProps) {
for (const propKey in oldProps) {
const oldProp = oldProps[propKey];
if (oldProps.hasOwnProperty(propKey) && oldProp != null && !newProps.hasOwnProperty(propKey)) {
setProperty(instance, type, propKey, null, oldProp);
}
}
for (const propKey in newProps) {
const newProp = newProps[propKey];
const oldProp = oldProps[propKey];
if (newProps.hasOwnProperty(propKey) && newProp !== oldProp && (newProp != null || oldProp != null)) {
setProperty(instance, type, propKey, newProp, oldProp);
}
}
}
// src/reconciler/host-config.ts
var currentUpdatePriority = NoEventPriority;
var hostConfig = {
supportsMutation: true,
supportsPersistence: false,
supportsHydration: false,
supportsMicrotasks: true,
scheduleMicrotask: queueMicrotask,
createInstance(type, props, rootContainerInstance, hostContext) {
if (textNodeKeys.includes(type) && !hostContext.isInsideText) {
throw new Error(`Component of type "${type}" must be created inside of a text node`);
}
const id = getNextId(type);
const components = getComponentCatalogue();
if (!components[type]) {
throw new Error(`Unknown component type: ${type}`);
}
return new components[type](rootContainerInstance.ctx, {
id,
...props
});
},
appendChild(parent, child) {
parent.add(child);
},
removeChild(parent, child) {
parent.remove(child.id);
},
insertBefore(parent, child, beforeChild) {
parent.insertBefore(child, beforeChild);
},
insertInContainerBefore(parent, child, beforeChild) {
parent.insertBefore(child, beforeChild);
},
removeChildFromContainer(parent, child) {
parent.remove(child.id);
},
prepareForCommit(containerInfo) {
return null;
},
resetAfterCommit(containerInfo) {
containerInfo.requestRender();
},
getRootHostContext(rootContainerInstance) {
return { isInsideText: false };
},
getChildHostContext(parentHostContext, type, rootContainerInstance) {
const isInsideText = ["text", ...textNodeKeys].includes(type);
return { ...parentHostContext, isInsideText };
},
shouldSetTextContent(type, props) {
return false;
},
createTextInstance(text, rootContainerInstance, hostContext) {
if (!hostContext.isInsideText) {
throw new Error("Text must be created inside of a text node");
}
return TextNodeRenderable2.fromString(text);
},
scheduleTimeout: setTimeout,
cancelTimeout: clearTimeout,
noTimeout: -1,
shouldAttemptEagerTransition() {
return true;
},
finalizeInitialChildren(instance, type, props, rootContainerInstance, hostContext) {
setInitialProperties(instance, type, props);
return false;
},
commitMount(instance, type, props, internalInstanceHandle) {},
commitUpdate(instance, type, oldProps, newProps, internalInstanceHandle) {
updateProperties(instance, type, oldProps, newProps);
},
commitTextUpdate(textInstance, oldText, newText) {
textInstance.children = [newText];
},
appendChildToContainer(container, child) {
container.add(child);
},
appendInitialChild(parent, child) {
parent.add(child);
},
hideInstance(instance) {
instance.visible = false;
},
unhideInstance(instance, props) {
instance.visible = true;
},
hideTextInstance(textInstance) {
textInstance.visible = false;
},
unhideTextInstance(textInstance, text) {
textInstance.visible = true;
},
clearContainer(container) {
const children = container.getChildren();
children.forEach((child) => container.remove(child.id));
},
setCurrentUpdatePriority(newPriority) {
currentUpdatePriority = newPriority;
},
getCurrentUpdatePriority: () => currentUpdatePriority,
resolveUpdatePriority() {
if (currentUpdatePriority !== NoEventPriority) {
return currentUpdatePriority;
}
return DefaultEventPriority;
},
maySuspendCommit() {
return false;
},
maySuspendCommitOnUpdate() {
return false;
},
maySuspendCommitInSyncRender() {
return false;
},
NotPendingTransition: null,
HostTransitionContext: createContext2(null),
resetFormInstance() {},
requestPostPaintCallback() {},
trackSchedulerEvent() {},
resolveEventType() {
return null;
},
resolveEventTimeStamp() {
return -1.1;
},
preloadInstance() {
return true;
},
startSuspendingCommit() {},
suspendInstance() {},
waitForCommitToBeReady() {
return null;
},
detachDeletedInstance(instance) {
if (!instance.parent) {
instance.destroyRecursively();
}
},
getPublicInstance(instance) {
return instance;
},
preparePortalMount(containerInfo) {},
isPrimaryRenderer: true,
getInstanceFromNode() {
return null;
},
beforeActiveInstanceBlur() {},
afterActiveInstanceBlur() {},
prepareScopeUpdate() {},
getInstanceFromScope() {
return null;
},
rendererPackageName: "@opentui/react",
rendererVersion: package_default.version
};
// src/reconciler/reconciler.ts
var reconciler = ReactReconciler(hostConfig);
if (process.env["DEV"] === "true") {
try {
await import("./chunk-bdqvmfwv.js");
} catch (error) {
if (error.code === "ERR_MODULE_NOT_FOUND") {
console.warn(`
The environment variable DEV is set to true, so opentui tried to import \`react-devtools-core\`,
but this failed as it was not installed. Debugging with React DevTools requires it.
To install use this command:
$ bun add react-devtools-core@7 -d
`.trim() + `
`);
} else {
throw error;
}
}
}
reconciler.injectIntoDevTools();
function _render(element, root) {
const container = reconciler.createContainer(root, ConcurrentRoot, null, false, null, "", console.error, console.error, console.error, () => {});
reconciler.updateContainer(element, container, null, () => {});
return container;
}
// src/reconciler/renderer.ts
var _r = reconciler;
var flushSync = _r.flushSyncFromReconciler ?? _r.flushSync;
var { createPortal } = reconciler;
function createRoot(renderer) {
let container = null;
const cleanup = () => {
if (container) {
reconciler.updateContainer(null, container, null, () => {});
reconciler.flushSyncWork();
container = null;
}
};
renderer.once(CliRenderEvents.DESTROY, cleanup);
return {
render: (node) => {
engine.attach(renderer);
container = _render(React2.createElement(AppContext.Provider, { value: { keyHandler: renderer.keyInput, renderer } }, React2.createElement(ErrorBoundary, null, node)), renderer.root);
},
unmount: cleanup
};
}
export { baseComponents, componentCatalogue, extend, getComponentCatalogue, AppContext, useAppContext, Fragment, jsxDEV, flushSync, createPortal, createRoot };
import { plugin as registerBunPlugin } from "bun"
import * as coreRuntime from "@opentui/core"
import {
createRuntimePlugin,
type RuntimeModuleEntry,
type RuntimePluginRewriteOptions,
} from "@opentui/core/runtime-plugin"
import * as reactRuntime from "react"
import * as reactJsxRuntime from "react/jsx-runtime"
import * as reactJsxDevRuntime from "react/jsx-dev-runtime"
import * as opentuiReactRuntime from "../index.js"
const runtimePluginSupportInstalledKey = "__opentuiReactRuntimePluginSupportInstalled__"
export interface ReactRuntimePluginSupportOptions {
additional?: Record<string, RuntimeModuleEntry>
core?: RuntimeModuleEntry
rewrite?: RuntimePluginRewriteOptions
}
interface RuntimePluginSupportInstall {
specifiers: ReadonlySet<string>
core: RuntimeModuleEntry
rewriteKey: string
}
type RuntimePluginSupportState = typeof globalThis & {
[runtimePluginSupportInstalledKey]?: RuntimePluginSupportInstall
}
const defaultRuntimeModules: Record<string, RuntimeModuleEntry> = {
"@opentui/react": opentuiReactRuntime as Record<string, unknown>,
"@opentui/react/jsx-runtime": reactJsxRuntime as Record<string, unknown>,
"@opentui/react/jsx-dev-runtime": reactJsxDevRuntime as Record<string, unknown>,
react: reactRuntime as Record<string, unknown>,
"react/jsx-runtime": reactJsxRuntime as Record<string, unknown>,
"react/jsx-dev-runtime": reactJsxDevRuntime as Record<string, unknown>,
}
function normalizeRewriteKey(rewrite: RuntimePluginRewriteOptions | undefined): string {
return `${rewrite?.nodeModulesRuntimeSpecifiers ?? true}:${rewrite?.nodeModulesBareSpecifiers ?? false}`
}
function createRuntimeModules(options?: ReactRuntimePluginSupportOptions): Record<string, RuntimeModuleEntry> {
return {
...defaultRuntimeModules,
...(options?.additional ?? {}),
}
}
function assertCompatibleInstall(
install: RuntimePluginSupportInstall,
modules: Record<string, RuntimeModuleEntry>,
options?: ReactRuntimePluginSupportOptions,
): void {
for (const specifier of Object.keys(modules)) {
if (!install.specifiers.has(specifier)) {
throw new Error(
`OpenTUI React runtime plugin support is already installed without ${specifier}. Call ensureRuntimePluginSupport({ additional }) from @opentui/react/runtime-plugin-support/configure before importing @opentui/react/runtime-plugin-support.`,
)
}
}
if (options?.core && options.core !== install.core) {
throw new Error("OpenTUI React runtime plugin support is already installed with a different core runtime module.")
}
if (options?.rewrite && normalizeRewriteKey(options.rewrite) !== install.rewriteKey) {
throw new Error("OpenTUI React runtime plugin support is already installed with different rewrite options.")
}
}
export function ensureRuntimePluginSupport(options: ReactRuntimePluginSupportOptions = {}): boolean {
const state = globalThis as RuntimePluginSupportState
const modules = createRuntimeModules(options)
const core = options.core ?? (coreRuntime as Record<string, unknown>)
const rewriteKey = normalizeRewriteKey(options.rewrite)
const install = state[runtimePluginSupportInstalledKey]
if (install) {
assertCompatibleInstall(install, modules, options)
return false
}
registerBunPlugin(
createRuntimePlugin({
core,
additional: modules,
rewrite: options.rewrite,
}),
)
state[runtimePluginSupportInstalledKey] = {
specifiers: new Set(Object.keys(modules)),
core,
rewriteKey,
}
return true
}
import { ensureRuntimePluginSupport } from "./runtime-plugin-support-configure.js"
export { ensureRuntimePluginSupport }
export type { ReactRuntimePluginSupportOptions } from "./runtime-plugin-support-configure.js"
ensureRuntimePluginSupport()