@opentui/react 0.2.1 → 0.2.3
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 +87 -0
- package/{chunk-pr7s7hvy.js → chunk-nczcqb5q.js} +20 -14
- package/index.js +70 -17
- package/package.json +11 -7
- package/scripts/runtime-plugin-support-configure.d.ts +7 -0
- package/scripts/runtime-plugin-support-configure.ts +99 -0
- package/scripts/runtime-plugin-support.d.ts +3 -1
- package/scripts/runtime-plugin-support.ts +3 -39
- package/src/hooks/index.d.ts +4 -0
- package/src/hooks/use-blur.d.ts +10 -0
- package/src/hooks/use-focus.d.ts +10 -0
- package/src/hooks/use-paste.d.ts +10 -0
- package/src/hooks/use-selection.d.ts +12 -0
- package/src/reconciler/host-config.d.ts +8 -1
- package/src/reconciler/renderer.d.ts +3 -0
- package/test-utils.js +1 -1
package/README.md
CHANGED
|
@@ -60,6 +60,10 @@ For optimal TypeScript support, configure your `tsconfig.json`:
|
|
|
60
60
|
- [Hooks](#hooks)
|
|
61
61
|
- [useRenderer()](#userenderer)
|
|
62
62
|
- [useKeyboard(handler, options?)](#usekeyboardhandler-options)
|
|
63
|
+
- [usePaste(handler)](#usepastehandler)
|
|
64
|
+
- [useFocus(handler)](#usefocushandler)
|
|
65
|
+
- [useBlur(handler)](#useblurhandler)
|
|
66
|
+
- [useSelectionHandler(handler)](#useselectionhandlerhandler)
|
|
63
67
|
- [useOnResize(callback)](#useonresizecallback)
|
|
64
68
|
- [useTerminalDimensions()](#useterminaldimensions)
|
|
65
69
|
- [useTimeline(options?)](#usetimelineoptions)
|
|
@@ -239,6 +243,89 @@ function App() {
|
|
|
239
243
|
}
|
|
240
244
|
```
|
|
241
245
|
|
|
246
|
+
#### `usePaste(handler)`
|
|
247
|
+
|
|
248
|
+
Handle terminal paste events (bracketed paste).
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
import { decodePasteBytes } from "@opentui/core"
|
|
252
|
+
import { usePaste } from "@opentui/react"
|
|
253
|
+
|
|
254
|
+
function App() {
|
|
255
|
+
usePaste((event) => {
|
|
256
|
+
const text = decodePasteBytes(event.bytes)
|
|
257
|
+
console.log("Pasted text:", text)
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
return <text>Paste something into the terminal</text>
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
**Parameters:**
|
|
265
|
+
|
|
266
|
+
- `handler`: Callback function that receives a `PasteEvent` object with `bytes: Uint8Array` (decode with `decodePasteBytes` from `@opentui/core`)
|
|
267
|
+
|
|
268
|
+
#### `useFocus(handler)`
|
|
269
|
+
|
|
270
|
+
Subscribe to terminal window focus events. Fires when the terminal window gains focus.
|
|
271
|
+
|
|
272
|
+
```tsx
|
|
273
|
+
import { useFocus } from "@opentui/react"
|
|
274
|
+
|
|
275
|
+
function App() {
|
|
276
|
+
useFocus(() => {
|
|
277
|
+
console.log("Terminal gained focus")
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
return <text>Focus-aware component</text>
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
**Parameters:**
|
|
285
|
+
|
|
286
|
+
- `handler`: Callback function invoked when the terminal gains focus
|
|
287
|
+
|
|
288
|
+
#### `useBlur(handler)`
|
|
289
|
+
|
|
290
|
+
Subscribe to terminal window blur events. Fires when the terminal window loses focus.
|
|
291
|
+
|
|
292
|
+
```tsx
|
|
293
|
+
import { useBlur } from "@opentui/react"
|
|
294
|
+
|
|
295
|
+
function App() {
|
|
296
|
+
useBlur(() => {
|
|
297
|
+
console.log("Terminal lost focus")
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
return <text>Blur-aware component</text>
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
**Parameters:**
|
|
305
|
+
|
|
306
|
+
- `handler`: Callback function invoked when the terminal loses focus
|
|
307
|
+
|
|
308
|
+
#### `useSelectionHandler(handler)`
|
|
309
|
+
|
|
310
|
+
Handle text selection events (e.g., when the user selects text via mouse drag).
|
|
311
|
+
|
|
312
|
+
```tsx
|
|
313
|
+
import { useSelectionHandler } from "@opentui/react"
|
|
314
|
+
|
|
315
|
+
function App() {
|
|
316
|
+
useSelectionHandler((selection) => {
|
|
317
|
+
const text = selection.getSelectedText()
|
|
318
|
+
console.log("Selected:", text)
|
|
319
|
+
})
|
|
320
|
+
|
|
321
|
+
return <text selectable>Select this text with your mouse</text>
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
**Parameters:**
|
|
326
|
+
|
|
327
|
+
- `handler`: Callback function that receives a `Selection` object with methods like `getSelectedText()`
|
|
328
|
+
|
|
242
329
|
#### `useOnResize(callback)`
|
|
243
330
|
|
|
244
331
|
Handle terminal resize events.
|
|
@@ -166,7 +166,7 @@ import { TextNodeRenderable as TextNodeRenderable2 } from "@opentui/core";
|
|
|
166
166
|
// package.json
|
|
167
167
|
var package_default = {
|
|
168
168
|
name: "@opentui/react",
|
|
169
|
-
version: "0.2.
|
|
169
|
+
version: "0.2.3",
|
|
170
170
|
description: "React renderer for building terminal user interfaces using OpenTUI core",
|
|
171
171
|
license: "MIT",
|
|
172
172
|
repository: {
|
|
@@ -191,6 +191,10 @@ var package_default = {
|
|
|
191
191
|
import: "./scripts/runtime-plugin-support.ts",
|
|
192
192
|
types: "./scripts/runtime-plugin-support.ts"
|
|
193
193
|
},
|
|
194
|
+
"./runtime-plugin-support/configure": {
|
|
195
|
+
import: "./scripts/runtime-plugin-support-configure.ts",
|
|
196
|
+
types: "./scripts/runtime-plugin-support-configure.ts"
|
|
197
|
+
},
|
|
194
198
|
"./jsx-runtime": {
|
|
195
199
|
import: "./jsx-runtime.js",
|
|
196
200
|
types: "./jsx-runtime.d.ts"
|
|
@@ -211,16 +215,16 @@ var package_default = {
|
|
|
211
215
|
"@opentui/keymap": "workspace:*",
|
|
212
216
|
"@types/bun": "latest",
|
|
213
217
|
"@types/node": "^24.0.0",
|
|
214
|
-
"@types/react": "^19.
|
|
215
|
-
"@types/react-reconciler": "^0.
|
|
218
|
+
"@types/react": "^19.2.0",
|
|
219
|
+
"@types/react-reconciler": "^0.33.0",
|
|
216
220
|
"@types/ws": "^8.18.1",
|
|
217
|
-
react: ">=19.
|
|
221
|
+
react: ">=19.2.0",
|
|
218
222
|
"react-devtools-core": "^7.0.1",
|
|
219
223
|
typescript: "^5",
|
|
220
224
|
ws: "^8.18.0"
|
|
221
225
|
},
|
|
222
226
|
peerDependencies: {
|
|
223
|
-
react: ">=19.
|
|
227
|
+
react: ">=19.2.0",
|
|
224
228
|
"react-devtools-core": "^7.0.1",
|
|
225
229
|
ws: "^8.18.0"
|
|
226
230
|
},
|
|
@@ -234,7 +238,7 @@ var package_default = {
|
|
|
234
238
|
},
|
|
235
239
|
dependencies: {
|
|
236
240
|
"@opentui/core": "workspace:*",
|
|
237
|
-
"react-reconciler": "^0.
|
|
241
|
+
"react-reconciler": "^0.33.0"
|
|
238
242
|
}
|
|
239
243
|
};
|
|
240
244
|
|
|
@@ -376,6 +380,8 @@ var hostConfig = {
|
|
|
376
380
|
supportsMutation: true,
|
|
377
381
|
supportsPersistence: false,
|
|
378
382
|
supportsHydration: false,
|
|
383
|
+
supportsMicrotasks: true,
|
|
384
|
+
scheduleMicrotask: queueMicrotask,
|
|
379
385
|
createInstance(type, props, rootContainerInstance, hostContext) {
|
|
380
386
|
if (textNodeKeys.includes(type) && !hostContext.isInsideText) {
|
|
381
387
|
throw new Error(`Component of type "${type}" must be created inside of a text node`);
|
|
@@ -431,7 +437,7 @@ var hostConfig = {
|
|
|
431
437
|
cancelTimeout: clearTimeout,
|
|
432
438
|
noTimeout: -1,
|
|
433
439
|
shouldAttemptEagerTransition() {
|
|
434
|
-
return
|
|
440
|
+
return true;
|
|
435
441
|
},
|
|
436
442
|
finalizeInitialChildren(instance, type, props, rootContainerInstance, hostContext) {
|
|
437
443
|
setInitialProperties(instance, type, props);
|
|
@@ -440,11 +446,9 @@ var hostConfig = {
|
|
|
440
446
|
commitMount(instance, type, props, internalInstanceHandle) {},
|
|
441
447
|
commitUpdate(instance, type, oldProps, newProps, internalInstanceHandle) {
|
|
442
448
|
updateProperties(instance, type, oldProps, newProps);
|
|
443
|
-
instance.requestRender();
|
|
444
449
|
},
|
|
445
450
|
commitTextUpdate(textInstance, oldText, newText) {
|
|
446
451
|
textInstance.children = [newText];
|
|
447
|
-
textInstance.requestRender();
|
|
448
452
|
},
|
|
449
453
|
appendChildToContainer(container, child) {
|
|
450
454
|
container.add(child);
|
|
@@ -454,19 +458,15 @@ var hostConfig = {
|
|
|
454
458
|
},
|
|
455
459
|
hideInstance(instance) {
|
|
456
460
|
instance.visible = false;
|
|
457
|
-
instance.requestRender();
|
|
458
461
|
},
|
|
459
462
|
unhideInstance(instance, props) {
|
|
460
463
|
instance.visible = true;
|
|
461
|
-
instance.requestRender();
|
|
462
464
|
},
|
|
463
465
|
hideTextInstance(textInstance) {
|
|
464
466
|
textInstance.visible = false;
|
|
465
|
-
textInstance.requestRender();
|
|
466
467
|
},
|
|
467
468
|
unhideTextInstance(textInstance, text) {
|
|
468
469
|
textInstance.visible = true;
|
|
469
|
-
textInstance.requestRender();
|
|
470
470
|
},
|
|
471
471
|
clearContainer(container) {
|
|
472
472
|
const children = container.getChildren();
|
|
@@ -485,6 +485,12 @@ var hostConfig = {
|
|
|
485
485
|
maySuspendCommit() {
|
|
486
486
|
return false;
|
|
487
487
|
},
|
|
488
|
+
maySuspendCommitOnUpdate() {
|
|
489
|
+
return false;
|
|
490
|
+
},
|
|
491
|
+
maySuspendCommitInSyncRender() {
|
|
492
|
+
return false;
|
|
493
|
+
},
|
|
488
494
|
NotPendingTransition: null,
|
|
489
495
|
HostTransitionContext: createContext2(null),
|
|
490
496
|
resetFormInstance() {},
|
|
@@ -550,7 +556,7 @@ $ bun add react-devtools-core@7 -d
|
|
|
550
556
|
}
|
|
551
557
|
reconciler.injectIntoDevTools();
|
|
552
558
|
function _render(element, root) {
|
|
553
|
-
const container = reconciler.createContainer(root, ConcurrentRoot, null, false, null, "", console.error, console.error, console.error,
|
|
559
|
+
const container = reconciler.createContainer(root, ConcurrentRoot, null, false, null, "", console.error, console.error, console.error, () => {});
|
|
554
560
|
reconciler.updateContainer(element, container, null, () => {});
|
|
555
561
|
return container;
|
|
556
562
|
}
|
package/index.js
CHANGED
|
@@ -11,9 +11,9 @@ import {
|
|
|
11
11
|
getComponentCatalogue,
|
|
12
12
|
jsxDEV,
|
|
13
13
|
useAppContext
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-nczcqb5q.js";
|
|
15
15
|
import"./chunk-2mx7fq49.js";
|
|
16
|
-
// src/hooks/use-
|
|
16
|
+
// src/hooks/use-blur.ts
|
|
17
17
|
import { useEffect } from "react";
|
|
18
18
|
|
|
19
19
|
// src/hooks/use-event.ts
|
|
@@ -29,11 +29,44 @@ function useEffectEvent(handler) {
|
|
|
29
29
|
}, []);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
// src/hooks/use-renderer.ts
|
|
33
|
+
var useRenderer = () => {
|
|
34
|
+
const { renderer } = useAppContext();
|
|
35
|
+
if (!renderer) {
|
|
36
|
+
throw new Error("Renderer not found.");
|
|
37
|
+
}
|
|
38
|
+
return renderer;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// src/hooks/use-blur.ts
|
|
42
|
+
var useBlur = (handler) => {
|
|
43
|
+
const renderer = useRenderer();
|
|
44
|
+
const stableHandler = useEffectEvent(handler);
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
renderer.on("blur", stableHandler);
|
|
47
|
+
return () => {
|
|
48
|
+
renderer.off("blur", stableHandler);
|
|
49
|
+
};
|
|
50
|
+
}, [renderer]);
|
|
51
|
+
};
|
|
52
|
+
// src/hooks/use-focus.ts
|
|
53
|
+
import { useEffect as useEffect2 } from "react";
|
|
54
|
+
var useFocus = (handler) => {
|
|
55
|
+
const renderer = useRenderer();
|
|
56
|
+
const stableHandler = useEffectEvent(handler);
|
|
57
|
+
useEffect2(() => {
|
|
58
|
+
renderer.on("focus", stableHandler);
|
|
59
|
+
return () => {
|
|
60
|
+
renderer.off("focus", stableHandler);
|
|
61
|
+
};
|
|
62
|
+
}, [renderer]);
|
|
63
|
+
};
|
|
32
64
|
// src/hooks/use-keyboard.ts
|
|
65
|
+
import { useEffect as useEffect3 } from "react";
|
|
33
66
|
var useKeyboard = (handler, options = { release: false }) => {
|
|
34
67
|
const { keyHandler } = useAppContext();
|
|
35
68
|
const stableHandler = useEffectEvent(handler);
|
|
36
|
-
|
|
69
|
+
useEffect3(() => {
|
|
37
70
|
keyHandler?.on("keypress", stableHandler);
|
|
38
71
|
if (options?.release) {
|
|
39
72
|
keyHandler?.on("keyrelease", stableHandler);
|
|
@@ -46,20 +79,24 @@ var useKeyboard = (handler, options = { release: false }) => {
|
|
|
46
79
|
};
|
|
47
80
|
}, [keyHandler, options.release]);
|
|
48
81
|
};
|
|
49
|
-
// src/hooks/use-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
82
|
+
// src/hooks/use-paste.ts
|
|
83
|
+
import { useEffect as useEffect4 } from "react";
|
|
84
|
+
var usePaste = (handler) => {
|
|
85
|
+
const { keyHandler } = useAppContext();
|
|
86
|
+
const stableHandler = useEffectEvent(handler);
|
|
87
|
+
useEffect4(() => {
|
|
88
|
+
keyHandler?.on("paste", stableHandler);
|
|
89
|
+
return () => {
|
|
90
|
+
keyHandler?.off("paste", stableHandler);
|
|
91
|
+
};
|
|
92
|
+
}, [keyHandler]);
|
|
56
93
|
};
|
|
57
94
|
// src/hooks/use-resize.ts
|
|
58
|
-
import { useEffect as
|
|
95
|
+
import { useEffect as useEffect5 } from "react";
|
|
59
96
|
var useOnResize = (callback) => {
|
|
60
97
|
const renderer = useRenderer();
|
|
61
98
|
const stableCallback = useEffectEvent(callback);
|
|
62
|
-
|
|
99
|
+
useEffect5(() => {
|
|
63
100
|
renderer.on("resize", stableCallback);
|
|
64
101
|
return () => {
|
|
65
102
|
renderer.off("resize", stableCallback);
|
|
@@ -67,6 +104,18 @@ var useOnResize = (callback) => {
|
|
|
67
104
|
}, [renderer]);
|
|
68
105
|
return renderer;
|
|
69
106
|
};
|
|
107
|
+
// src/hooks/use-selection.ts
|
|
108
|
+
import { useEffect as useEffect6 } from "react";
|
|
109
|
+
var useSelectionHandler = (handler) => {
|
|
110
|
+
const renderer = useRenderer();
|
|
111
|
+
const stableHandler = useEffectEvent(handler);
|
|
112
|
+
useEffect6(() => {
|
|
113
|
+
renderer.on("selection", stableHandler);
|
|
114
|
+
return () => {
|
|
115
|
+
renderer.off("selection", stableHandler);
|
|
116
|
+
};
|
|
117
|
+
}, [renderer]);
|
|
118
|
+
};
|
|
70
119
|
// src/hooks/use-terminal-dimensions.ts
|
|
71
120
|
import { useState } from "react";
|
|
72
121
|
var useTerminalDimensions = () => {
|
|
@@ -83,10 +132,10 @@ var useTerminalDimensions = () => {
|
|
|
83
132
|
};
|
|
84
133
|
// src/hooks/use-timeline.ts
|
|
85
134
|
import { engine, Timeline } from "@opentui/core";
|
|
86
|
-
import { useEffect as
|
|
135
|
+
import { useEffect as useEffect7 } from "react";
|
|
87
136
|
var useTimeline = (options = {}) => {
|
|
88
137
|
const timeline = new Timeline(options);
|
|
89
|
-
|
|
138
|
+
useEffect7(() => {
|
|
90
139
|
if (!options.autoplay) {
|
|
91
140
|
timeline.play();
|
|
92
141
|
}
|
|
@@ -102,7 +151,7 @@ var useTimeline = (options = {}) => {
|
|
|
102
151
|
import {
|
|
103
152
|
createSlotRegistry
|
|
104
153
|
} from "@opentui/core";
|
|
105
|
-
import React, { Fragment as Fragment2, useEffect as
|
|
154
|
+
import React, { Fragment as Fragment2, useEffect as useEffect8, useMemo, useRef as useRef2, useState as useState2 } from "react";
|
|
106
155
|
function createReactSlotRegistry(renderer, context, options = {}) {
|
|
107
156
|
return createSlotRegistry(renderer, "react:slot-registry", context, options);
|
|
108
157
|
}
|
|
@@ -181,12 +230,12 @@ function Slot(props) {
|
|
|
181
230
|
const slotName = String(props.name);
|
|
182
231
|
const renderFailuresByPluginRef = useRef2(new Map);
|
|
183
232
|
const pendingRenderReportsRef = useRef2(new Map);
|
|
184
|
-
|
|
233
|
+
useEffect8(() => {
|
|
185
234
|
return registry.subscribe(() => {
|
|
186
235
|
setVersion((current) => current + 1);
|
|
187
236
|
});
|
|
188
237
|
}, [registry]);
|
|
189
|
-
|
|
238
|
+
useEffect8(() => {
|
|
190
239
|
if (pendingRenderReportsRef.current.size === 0) {
|
|
191
240
|
return;
|
|
192
241
|
}
|
|
@@ -318,9 +367,13 @@ import { createElement as createElement2 } from "react";
|
|
|
318
367
|
export {
|
|
319
368
|
useTimeline,
|
|
320
369
|
useTerminalDimensions,
|
|
370
|
+
useSelectionHandler,
|
|
321
371
|
useRenderer,
|
|
372
|
+
usePaste,
|
|
322
373
|
useOnResize,
|
|
323
374
|
useKeyboard,
|
|
375
|
+
useFocus,
|
|
376
|
+
useBlur,
|
|
324
377
|
useAppContext,
|
|
325
378
|
getComponentCatalogue,
|
|
326
379
|
flushSync,
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"types": "src/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
7
|
-
"version": "0.2.
|
|
7
|
+
"version": "0.2.3",
|
|
8
8
|
"description": "React renderer for building terminal user interfaces using OpenTUI core",
|
|
9
9
|
"license": "MIT",
|
|
10
10
|
"repository": {
|
|
@@ -32,6 +32,10 @@
|
|
|
32
32
|
"types": "./scripts/runtime-plugin-support.d.ts",
|
|
33
33
|
"import": "./scripts/runtime-plugin-support.ts"
|
|
34
34
|
},
|
|
35
|
+
"./runtime-plugin-support/configure": {
|
|
36
|
+
"types": "./scripts/runtime-plugin-support-configure.d.ts",
|
|
37
|
+
"import": "./scripts/runtime-plugin-support-configure.ts"
|
|
38
|
+
},
|
|
35
39
|
"./jsx-runtime": {
|
|
36
40
|
"types": "./jsx-runtime.d.ts",
|
|
37
41
|
"import": "./jsx-runtime.js",
|
|
@@ -44,23 +48,23 @@
|
|
|
44
48
|
}
|
|
45
49
|
},
|
|
46
50
|
"dependencies": {
|
|
47
|
-
"@opentui/core": "0.2.
|
|
48
|
-
"react-reconciler": "^0.
|
|
51
|
+
"@opentui/core": "0.2.3",
|
|
52
|
+
"react-reconciler": "^0.33.0"
|
|
49
53
|
},
|
|
50
54
|
"devDependencies": {
|
|
51
55
|
"@opentui/keymap": "workspace:*",
|
|
52
56
|
"@types/bun": "latest",
|
|
53
57
|
"@types/node": "^24.0.0",
|
|
54
|
-
"@types/react": "^19.
|
|
55
|
-
"@types/react-reconciler": "^0.
|
|
58
|
+
"@types/react": "^19.2.0",
|
|
59
|
+
"@types/react-reconciler": "^0.33.0",
|
|
56
60
|
"@types/ws": "^8.18.1",
|
|
57
|
-
"react": ">=19.
|
|
61
|
+
"react": ">=19.2.0",
|
|
58
62
|
"react-devtools-core": "^7.0.1",
|
|
59
63
|
"typescript": "^5",
|
|
60
64
|
"ws": "^8.18.0"
|
|
61
65
|
},
|
|
62
66
|
"peerDependencies": {
|
|
63
|
-
"react": ">=19.
|
|
67
|
+
"react": ">=19.2.0",
|
|
64
68
|
"react-devtools-core": "^7.0.1",
|
|
65
69
|
"ws": "^8.18.0"
|
|
66
70
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type RuntimeModuleEntry, type RuntimePluginRewriteOptions } from "@opentui/core/runtime-plugin";
|
|
2
|
+
export interface ReactRuntimePluginSupportOptions {
|
|
3
|
+
additional?: Record<string, RuntimeModuleEntry>;
|
|
4
|
+
core?: RuntimeModuleEntry;
|
|
5
|
+
rewrite?: RuntimePluginRewriteOptions;
|
|
6
|
+
}
|
|
7
|
+
export declare function ensureRuntimePluginSupport(options?: ReactRuntimePluginSupportOptions): boolean;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { plugin as registerBunPlugin } from "bun"
|
|
2
|
+
import * as coreRuntime from "@opentui/core"
|
|
3
|
+
import {
|
|
4
|
+
createRuntimePlugin,
|
|
5
|
+
type RuntimeModuleEntry,
|
|
6
|
+
type RuntimePluginRewriteOptions,
|
|
7
|
+
} from "@opentui/core/runtime-plugin"
|
|
8
|
+
import * as reactRuntime from "react"
|
|
9
|
+
import * as reactJsxRuntime from "react/jsx-runtime"
|
|
10
|
+
import * as reactJsxDevRuntime from "react/jsx-dev-runtime"
|
|
11
|
+
import * as opentuiReactRuntime from "../index.js"
|
|
12
|
+
|
|
13
|
+
const runtimePluginSupportInstalledKey = "__opentuiReactRuntimePluginSupportInstalled__"
|
|
14
|
+
|
|
15
|
+
export interface ReactRuntimePluginSupportOptions {
|
|
16
|
+
additional?: Record<string, RuntimeModuleEntry>
|
|
17
|
+
core?: RuntimeModuleEntry
|
|
18
|
+
rewrite?: RuntimePluginRewriteOptions
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface RuntimePluginSupportInstall {
|
|
22
|
+
specifiers: ReadonlySet<string>
|
|
23
|
+
core: RuntimeModuleEntry
|
|
24
|
+
rewriteKey: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
type RuntimePluginSupportState = typeof globalThis & {
|
|
28
|
+
[runtimePluginSupportInstalledKey]?: RuntimePluginSupportInstall
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const defaultRuntimeModules: Record<string, RuntimeModuleEntry> = {
|
|
32
|
+
"@opentui/react": opentuiReactRuntime as Record<string, unknown>,
|
|
33
|
+
"@opentui/react/jsx-runtime": reactJsxRuntime as Record<string, unknown>,
|
|
34
|
+
"@opentui/react/jsx-dev-runtime": reactJsxDevRuntime as Record<string, unknown>,
|
|
35
|
+
react: reactRuntime as Record<string, unknown>,
|
|
36
|
+
"react/jsx-runtime": reactJsxRuntime as Record<string, unknown>,
|
|
37
|
+
"react/jsx-dev-runtime": reactJsxDevRuntime as Record<string, unknown>,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function normalizeRewriteKey(rewrite: RuntimePluginRewriteOptions | undefined): string {
|
|
41
|
+
return `${rewrite?.nodeModulesRuntimeSpecifiers ?? true}:${rewrite?.nodeModulesBareSpecifiers ?? false}`
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function createRuntimeModules(options?: ReactRuntimePluginSupportOptions): Record<string, RuntimeModuleEntry> {
|
|
45
|
+
return {
|
|
46
|
+
...defaultRuntimeModules,
|
|
47
|
+
...(options?.additional ?? {}),
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function assertCompatibleInstall(
|
|
52
|
+
install: RuntimePluginSupportInstall,
|
|
53
|
+
modules: Record<string, RuntimeModuleEntry>,
|
|
54
|
+
options?: ReactRuntimePluginSupportOptions,
|
|
55
|
+
): void {
|
|
56
|
+
for (const specifier of Object.keys(modules)) {
|
|
57
|
+
if (!install.specifiers.has(specifier)) {
|
|
58
|
+
throw new Error(
|
|
59
|
+
`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.`,
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (options?.core && options.core !== install.core) {
|
|
65
|
+
throw new Error("OpenTUI React runtime plugin support is already installed with a different core runtime module.")
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (options?.rewrite && normalizeRewriteKey(options.rewrite) !== install.rewriteKey) {
|
|
69
|
+
throw new Error("OpenTUI React runtime plugin support is already installed with different rewrite options.")
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function ensureRuntimePluginSupport(options: ReactRuntimePluginSupportOptions = {}): boolean {
|
|
74
|
+
const state = globalThis as RuntimePluginSupportState
|
|
75
|
+
const modules = createRuntimeModules(options)
|
|
76
|
+
const core = options.core ?? (coreRuntime as Record<string, unknown>)
|
|
77
|
+
const rewriteKey = normalizeRewriteKey(options.rewrite)
|
|
78
|
+
|
|
79
|
+
const install = state[runtimePluginSupportInstalledKey]
|
|
80
|
+
if (install) {
|
|
81
|
+
assertCompatibleInstall(install, modules, options)
|
|
82
|
+
return false
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
registerBunPlugin(
|
|
86
|
+
createRuntimePlugin({
|
|
87
|
+
core,
|
|
88
|
+
additional: modules,
|
|
89
|
+
rewrite: options.rewrite,
|
|
90
|
+
}),
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
state[runtimePluginSupportInstalledKey] = {
|
|
94
|
+
specifiers: new Set(Object.keys(modules)),
|
|
95
|
+
core,
|
|
96
|
+
rewriteKey,
|
|
97
|
+
}
|
|
98
|
+
return true
|
|
99
|
+
}
|
|
@@ -1 +1,3 @@
|
|
|
1
|
-
|
|
1
|
+
import { ensureRuntimePluginSupport } from "./runtime-plugin-support-configure.js";
|
|
2
|
+
export { ensureRuntimePluginSupport };
|
|
3
|
+
export type { ReactRuntimePluginSupportOptions } from "./runtime-plugin-support-configure.js";
|
|
@@ -1,42 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import * as coreRuntime from "@opentui/core"
|
|
3
|
-
import { createRuntimePlugin, type RuntimeModuleEntry } from "@opentui/core/runtime-plugin"
|
|
4
|
-
import * as reactRuntime from "react"
|
|
5
|
-
import * as reactJsxRuntime from "react/jsx-runtime"
|
|
6
|
-
import * as reactJsxDevRuntime from "react/jsx-dev-runtime"
|
|
7
|
-
import * as opentuiReactRuntime from "../index.js"
|
|
1
|
+
import { ensureRuntimePluginSupport } from "./runtime-plugin-support-configure.js"
|
|
8
2
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
type RuntimePluginSupportState = typeof globalThis & {
|
|
12
|
-
[runtimePluginSupportInstalledKey]?: boolean
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const additionalRuntimeModules: Record<string, RuntimeModuleEntry> = {
|
|
16
|
-
"@opentui/react": opentuiReactRuntime as Record<string, unknown>,
|
|
17
|
-
"@opentui/react/jsx-runtime": reactJsxRuntime as Record<string, unknown>,
|
|
18
|
-
"@opentui/react/jsx-dev-runtime": reactJsxDevRuntime as Record<string, unknown>,
|
|
19
|
-
react: reactRuntime as Record<string, unknown>,
|
|
20
|
-
"react/jsx-runtime": reactJsxRuntime as Record<string, unknown>,
|
|
21
|
-
"react/jsx-dev-runtime": reactJsxDevRuntime as Record<string, unknown>,
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function ensureRuntimePluginSupport(): boolean {
|
|
25
|
-
const state = globalThis as RuntimePluginSupportState
|
|
26
|
-
|
|
27
|
-
if (state[runtimePluginSupportInstalledKey]) {
|
|
28
|
-
return false
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
registerBunPlugin(
|
|
32
|
-
createRuntimePlugin({
|
|
33
|
-
core: coreRuntime as Record<string, unknown>,
|
|
34
|
-
additional: additionalRuntimeModules,
|
|
35
|
-
}),
|
|
36
|
-
)
|
|
37
|
-
|
|
38
|
-
state[runtimePluginSupportInstalledKey] = true
|
|
39
|
-
return true
|
|
40
|
-
}
|
|
3
|
+
export { ensureRuntimePluginSupport }
|
|
4
|
+
export type { ReactRuntimePluginSupportOptions } from "./runtime-plugin-support-configure.js"
|
|
41
5
|
|
|
42
6
|
ensureRuntimePluginSupport()
|
package/src/hooks/index.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
export * from "./use-blur.js";
|
|
2
|
+
export * from "./use-focus.js";
|
|
1
3
|
export * from "./use-keyboard.js";
|
|
4
|
+
export * from "./use-paste.js";
|
|
2
5
|
export * from "./use-renderer.js";
|
|
3
6
|
export * from "./use-resize.js";
|
|
7
|
+
export * from "./use-selection.js";
|
|
4
8
|
export * from "./use-terminal-dimensions.js";
|
|
5
9
|
export * from "./use-timeline.js";
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { PasteEvent } from "@opentui/core";
|
|
2
|
+
/**
|
|
3
|
+
* Subscribe to terminal paste events (bracketed paste).
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* usePaste((event) => {
|
|
7
|
+
* console.log("Pasted:", event.text)
|
|
8
|
+
* })
|
|
9
|
+
*/
|
|
10
|
+
export declare const usePaste: (handler: (event: PasteEvent) => void) => void;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Selection } from "@opentui/core";
|
|
2
|
+
/**
|
|
3
|
+
* Subscribe to text selection events.
|
|
4
|
+
* Fires when the user selects text in the terminal (e.g., via mouse drag).
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* useSelectionHandler((selection) => {
|
|
8
|
+
* const text = selection.getSelectedText()
|
|
9
|
+
* console.log("Selected:", text)
|
|
10
|
+
* })
|
|
11
|
+
*/
|
|
12
|
+
export declare const useSelectionHandler: (handler: (selection: Selection) => void) => void;
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import type { HostConfig } from "react-reconciler";
|
|
2
2
|
import type { Container, HostContext, Instance, Props, PublicInstance, TextInstance, Type } from "../types/host.js";
|
|
3
|
+
type ReconcilerExtensions = {
|
|
4
|
+
maySuspendCommitOnUpdate(type: Type, oldProps: Props, newProps: Props): boolean;
|
|
5
|
+
maySuspendCommitInSyncRender(type: Type, props: Props): boolean;
|
|
6
|
+
rendererPackageName: string;
|
|
7
|
+
rendererVersion: string;
|
|
8
|
+
};
|
|
3
9
|
export declare const hostConfig: HostConfig<Type, Props, Container, Instance, TextInstance, unknown, // SuspenseInstance
|
|
4
10
|
unknown, // HydratableInstance
|
|
5
11
|
unknown, // FormInstance
|
|
6
12
|
PublicInstance, HostContext, unknown, // ChildSet
|
|
7
13
|
unknown, // TimeoutHandle
|
|
8
14
|
unknown, // NoTimeout
|
|
9
|
-
unknown
|
|
15
|
+
unknown> & ReconcilerExtensions;
|
|
16
|
+
export {};
|
|
@@ -3,6 +3,9 @@ import { type ReactNode } from "react";
|
|
|
3
3
|
declare const flushSync: {
|
|
4
4
|
(): void;
|
|
5
5
|
<R>(fn: () => R): R;
|
|
6
|
+
} & {
|
|
7
|
+
(): void;
|
|
8
|
+
<R>(fn: () => R): R;
|
|
6
9
|
};
|
|
7
10
|
declare const createPortal: (children: ReactNode, containerInfo: any, implementation: any, key?: string | null) => import("react-reconciler").ReactPortal;
|
|
8
11
|
export type Root = {
|