mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-07 02:51:50 +00:00
feat: fix types and add type checking plus lazybundle on launch andddd dev flag
This commit is contained in:
parent
5e5e65f6d5
commit
32302c37dd
34 changed files with 1807 additions and 977 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react'
|
||||
import React, { type ReactNode } from 'react'
|
||||
import { c as _c } from 'react/compiler-runtime'
|
||||
|
||||
import Link from './components/Link.js'
|
||||
|
|
@ -6,7 +6,7 @@ import Text from './components/Text.js'
|
|||
import type { Color } from './styles.js'
|
||||
import { type NamedColor, Parser, type Color as TermioColor, type TextStyle } from './termio.js'
|
||||
type Props = {
|
||||
children: string
|
||||
children?: ReactNode
|
||||
/** When true, force all text to be rendered with dim styling */
|
||||
dimColor?: boolean
|
||||
}
|
||||
|
|
@ -22,6 +22,11 @@ type SpanProps = {
|
|||
hyperlink?: string
|
||||
}
|
||||
|
||||
type Span = {
|
||||
text: string
|
||||
props: SpanProps
|
||||
}
|
||||
|
||||
/**
|
||||
* Component that parses ANSI escape codes and renders them using Text components.
|
||||
*
|
||||
|
|
@ -30,7 +35,7 @@ type SpanProps = {
|
|||
*
|
||||
* Memoized to prevent re-renders when parent changes but children string is the same.
|
||||
*/
|
||||
export const Ansi = React.memo(function Ansi(t0) {
|
||||
export const Ansi = React.memo(function Ansi(t0: Props) {
|
||||
const $ = _c(12)
|
||||
|
||||
const { children, dimColor } = t0
|
||||
|
|
@ -78,7 +83,7 @@ export const Ansi = React.memo(function Ansi(t0) {
|
|||
let t3
|
||||
|
||||
if ($[7] !== dimColor) {
|
||||
t3 = (span, i) => {
|
||||
t3 = (span: Span, i: number) => {
|
||||
const hyperlink = span.props.hyperlink
|
||||
|
||||
if (dimColor) {
|
||||
|
|
@ -165,10 +170,6 @@ export const Ansi = React.memo(function Ansi(t0) {
|
|||
|
||||
return t3
|
||||
})
|
||||
type Span = {
|
||||
text: string
|
||||
props: SpanProps
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse an ANSI string into spans using the termio parser.
|
||||
|
|
@ -359,7 +360,7 @@ type BaseTextStyleProps = {
|
|||
}
|
||||
|
||||
// Wrapper component that handles bold/dim mutual exclusivity for Text
|
||||
function StyledText(t0) {
|
||||
function StyledText(t0: BaseTextStyleProps & { bold?: boolean; dim?: boolean; children?: ReactNode }) {
|
||||
const $ = _c(14)
|
||||
let bold
|
||||
let children
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ type Props = PropsWithChildren<{
|
|||
* from scrolling content) and so signal-exit cleanup can exit the alt
|
||||
* screen if the component's own unmount doesn't run.
|
||||
*/
|
||||
export function AlternateScreen(t0) {
|
||||
export function AlternateScreen(t0: Props) {
|
||||
const $ = _c(7)
|
||||
|
||||
const { children, mouseTracking: t1 } = t0
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import '../global.d.ts'
|
||||
|
||||
import React, { type Ref } from 'react'
|
||||
import React, { type ReactNode, type Ref } from 'react'
|
||||
import { c as _c } from 'react/compiler-runtime'
|
||||
import type { Except } from 'type-fest'
|
||||
|
||||
|
|
@ -11,6 +11,7 @@ import type { KeyboardEvent } from '../events/keyboard-event.js'
|
|||
import type { Styles } from '../styles.js'
|
||||
import * as warn from '../warn.js'
|
||||
export type Props = Except<Styles, 'textWrap'> & {
|
||||
children?: ReactNode
|
||||
ref?: Ref<DOMElement>
|
||||
/**
|
||||
* Tab order index. Nodes with `tabIndex >= 0` participate in
|
||||
|
|
@ -50,7 +51,7 @@ export type Props = Except<Styles, 'textWrap'> & {
|
|||
/**
|
||||
* `<Box>` is an essential Ink component to build your layout. It's like `<div style="display: flex">` in the browser.
|
||||
*/
|
||||
function Box(t0) {
|
||||
function Box(t0: Props) {
|
||||
const $ = _c(42)
|
||||
let autoFocus
|
||||
let children
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { createContext, useEffect, useState } from 'react'
|
||||
import React, { createContext, type ReactNode, useEffect, useState } from 'react'
|
||||
import { c as _c } from 'react/compiler-runtime'
|
||||
|
||||
import { BLURRED_FRAME_INTERVAL_MS, FRAME_INTERVAL_MS } from '../constants.js'
|
||||
|
|
@ -87,7 +87,7 @@ export const ClockContext = createContext<Clock | null>(null)
|
|||
// Own component so App.tsx doesn't re-render when the clock is created.
|
||||
// The clock value is stable (created once via useState), so the provider
|
||||
// never causes consumer re-renders on its own.
|
||||
export function ClockProvider(t0) {
|
||||
export function ClockProvider(t0: { readonly children: ReactNode }) {
|
||||
const $ = _c(7)
|
||||
|
||||
const { children } = t0
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ export type Props = {
|
|||
readonly fallback?: ReactNode
|
||||
}
|
||||
|
||||
export default function Link(t0) {
|
||||
export default function Link(t0: Props) {
|
||||
const $ = _c(5)
|
||||
|
||||
const { children, url, fallback } = t0
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ export type Props = {
|
|||
/**
|
||||
* Adds one or more newline (\n) characters. Must be used within <Text> components.
|
||||
*/
|
||||
export default function Newline(t0) {
|
||||
export default function Newline(t0: Props) {
|
||||
const $ = _c(4)
|
||||
|
||||
const { count: t1 } = t0
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ type Props = Omit<BoxProps, 'noSelect'> & {
|
|||
* tracking). No-op in the main-screen scrollback render where the
|
||||
* terminal's native selection is used instead.
|
||||
*/
|
||||
export function NoSelect(t0) {
|
||||
export function NoSelect(t0: Props) {
|
||||
const $ = _c(8)
|
||||
let boxProps
|
||||
let children
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ type Props = {
|
|||
* (width × lines.length) and hands the joined string straight to output.write(),
|
||||
* which already splits on '\n' and parses ANSI into the screen buffer.
|
||||
*/
|
||||
export function RawAnsi(t0) {
|
||||
export function RawAnsi(t0: Props) {
|
||||
const $ = _c(6)
|
||||
|
||||
const { lines, width } = t0
|
||||
|
|
|
|||
|
|
@ -252,7 +252,7 @@ function ScrollBox({ children, ref, stickyScroll, ...style }: PropsWithChildren<
|
|||
// commit, which is too late for the first frame.
|
||||
return (
|
||||
<ink-box
|
||||
ref={el => {
|
||||
ref={(el: DOMElement | null) => {
|
||||
domRef.current = el
|
||||
|
||||
if (el) {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { createContext, useSyncExternalStore } from 'react'
|
||||
import React, { createContext, type ReactNode, useSyncExternalStore } from 'react'
|
||||
import { c as _c } from 'react/compiler-runtime'
|
||||
|
||||
import {
|
||||
|
|
@ -23,7 +23,7 @@ TerminalFocusContext.displayName = 'TerminalFocusContext'
|
|||
// Separate component so App.tsx doesn't re-render on focus changes.
|
||||
// Children are a stable prop reference, so they don't re-render either —
|
||||
// only components that consume the context will re-render.
|
||||
export function TerminalFocusProvider(t0) {
|
||||
export function TerminalFocusProvider(t0: { readonly children: ReactNode }) {
|
||||
const $ = _c(6)
|
||||
|
||||
const { children } = t0
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ const memoizedStylesForWrap: Record<NonNullable<Styles['textWrap']>, Styles> = {
|
|||
/**
|
||||
* This component can display text, and change its style to make it colorful, bold, underline, italic or strikethrough.
|
||||
*/
|
||||
export default function Text(t0) {
|
||||
export default function Text(t0: Props) {
|
||||
const $ = _c(29)
|
||||
|
||||
const {
|
||||
|
|
|
|||
2
ui-tui/packages/hermes-ink/src/ink/devtools.ts
Normal file
2
ui-tui/packages/hermes-ink/src/ink/devtools.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
/** Optional react-devtools hook; package may be absent. */
|
||||
export {}
|
||||
10
ui-tui/packages/hermes-ink/src/ink/events/paste-event.ts
Normal file
10
ui-tui/packages/hermes-ink/src/ink/events/paste-event.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { TerminalEvent } from './terminal-event.js'
|
||||
|
||||
export class PasteEvent extends TerminalEvent {
|
||||
readonly text: string
|
||||
|
||||
constructor(text: string) {
|
||||
super('paste', { bubbles: true, cancelable: true })
|
||||
this.text = text
|
||||
}
|
||||
}
|
||||
12
ui-tui/packages/hermes-ink/src/ink/events/resize-event.ts
Normal file
12
ui-tui/packages/hermes-ink/src/ink/events/resize-event.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { TerminalEvent } from './terminal-event.js'
|
||||
|
||||
export class ResizeEvent extends TerminalEvent {
|
||||
readonly columns: number
|
||||
readonly rows: number
|
||||
|
||||
constructor(columns: number, rows: number) {
|
||||
super('resize', { bubbles: true, cancelable: true })
|
||||
this.columns = columns
|
||||
this.rows = rows
|
||||
}
|
||||
}
|
||||
|
|
@ -339,8 +339,6 @@ export default class Ink {
|
|||
}
|
||||
}
|
||||
|
||||
// @ts-expect-error @types/react-reconciler@0.32.3 declares 11 args with transitionCallbacks,
|
||||
// but react-reconciler 0.33.0 source only accepts 10 args (no transitionCallbacks)
|
||||
this.container = reconciler.createContainer(
|
||||
this.rootNode,
|
||||
ConcurrentRoot,
|
||||
|
|
@ -357,7 +355,7 @@ export default class Ink {
|
|||
noop // onDefaultTransitionIndicator
|
||||
)
|
||||
|
||||
if ('production' === 'development') {
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
reconciler.injectIntoDevTools({
|
||||
bundleType: 0,
|
||||
// Reporting React DOM's version, not Ink's
|
||||
|
|
@ -955,7 +953,6 @@ export default class Ink {
|
|||
}
|
||||
pause(): void {
|
||||
// Flush pending React updates and render before pausing.
|
||||
// @ts-expect-error flushSyncFromReconciler exists in react-reconciler 0.31 but not in @types/react-reconciler
|
||||
reconciler.flushSyncFromReconciler()
|
||||
this.onRender()
|
||||
this.isPaused = true
|
||||
|
|
@ -1783,9 +1780,7 @@ export default class Ink {
|
|||
</App>
|
||||
)
|
||||
|
||||
// @ts-expect-error updateContainerSync exists in react-reconciler but not in @types/react-reconciler
|
||||
reconciler.updateContainerSync(tree, this.container, null, noop)
|
||||
// @ts-expect-error flushSyncWork exists in react-reconciler but not in @types/react-reconciler
|
||||
reconciler.flushSyncWork()
|
||||
}
|
||||
unmount(error?: Error | number | null): void {
|
||||
|
|
@ -1857,9 +1852,7 @@ export default class Ink {
|
|||
this.drainTimer = null
|
||||
}
|
||||
|
||||
// @ts-expect-error updateContainerSync exists in react-reconciler but not in @types/react-reconciler
|
||||
reconciler.updateContainerSync(null, this.container, null, noop)
|
||||
// @ts-expect-error flushSyncWork exists in react-reconciler but not in @types/react-reconciler
|
||||
reconciler.flushSyncWork()
|
||||
instances.delete(this.options.stdout)
|
||||
|
||||
|
|
@ -1966,8 +1959,8 @@ export default class Ink {
|
|||
|
||||
const intercept = (
|
||||
chunk: Uint8Array | string,
|
||||
encodingOrCb?: BufferEncoding | ((err?: Error) => void),
|
||||
cb?: (err?: Error) => void
|
||||
encodingOrCb?: BufferEncoding | ((err?: Error | null) => void),
|
||||
cb?: (err?: Error | null) => void
|
||||
): boolean => {
|
||||
const callback = typeof encodingOrCb === 'function' ? encodingOrCb : cb
|
||||
|
||||
|
|
|
|||
|
|
@ -176,27 +176,12 @@ export function resetProfileCounters(): void {
|
|||
}
|
||||
// --- END ---
|
||||
|
||||
const reconciler = createReconciler<
|
||||
ElementNames,
|
||||
Props,
|
||||
DOMElement,
|
||||
DOMElement,
|
||||
TextNode,
|
||||
DOMElement,
|
||||
unknown,
|
||||
unknown,
|
||||
DOMElement,
|
||||
HostContext,
|
||||
null, // UpdatePayload - not used in React 19
|
||||
NodeJS.Timeout,
|
||||
-1,
|
||||
null
|
||||
>({
|
||||
const reconciler = createReconciler({
|
||||
getRootHostContext: () => ({ isInsideText: false }),
|
||||
prepareForCommit: () => null,
|
||||
preparePortalMount: () => null,
|
||||
clearContainer: () => false,
|
||||
resetAfterCommit(rootNode) {
|
||||
resetAfterCommit(rootNode: DOMElement) {
|
||||
_lastCommitMs = _commitStart > 0 ? performance.now() - _commitStart : 0
|
||||
_commitStart = 0
|
||||
|
||||
|
|
@ -261,19 +246,19 @@ const reconciler = createReconciler<
|
|||
return createTextNode(text)
|
||||
},
|
||||
resetTextContent() {},
|
||||
hideTextInstance(node) {
|
||||
hideTextInstance(node: TextNode) {
|
||||
setTextNodeValue(node, '')
|
||||
},
|
||||
unhideTextInstance(node, text) {
|
||||
unhideTextInstance(node: TextNode, text: string) {
|
||||
setTextNodeValue(node, text)
|
||||
},
|
||||
getPublicInstance: (instance): DOMElement => instance as DOMElement,
|
||||
hideInstance(node) {
|
||||
getPublicInstance: (instance: DOMElement): DOMElement => instance,
|
||||
hideInstance(node: DOMElement) {
|
||||
node.isHidden = true
|
||||
node.yogaNode?.setDisplay(LayoutDisplay.None)
|
||||
markDirty(node)
|
||||
},
|
||||
unhideInstance(node) {
|
||||
unhideInstance(node: DOMElement) {
|
||||
node.isHidden = false
|
||||
node.yogaNode?.setDisplay(LayoutDisplay.Flex)
|
||||
markDirty(node)
|
||||
|
|
@ -344,7 +329,7 @@ const reconciler = createReconciler<
|
|||
commitTextUpdate(node: TextNode, _oldText: string, newText: string): void {
|
||||
setTextNodeValue(node, newText)
|
||||
},
|
||||
removeChild(node, removeNode) {
|
||||
removeChild(node: DOMElement, removeNode: DOMElement | TextNode) {
|
||||
removeChildNode(node, removeNode)
|
||||
cleanupYogaNode(removeNode)
|
||||
|
||||
|
|
|
|||
|
|
@ -63,14 +63,11 @@ export function renderToScreen(el: ReactElement, width: number): { screen: Scree
|
|||
stylePool = new StylePool()
|
||||
charPool = new CharPool()
|
||||
hyperlinkPool = new HyperlinkPool()
|
||||
// @ts-expect-error react-reconciler 0.33 takes 10 args; @types says 11
|
||||
container = reconciler.createContainer(root, LegacyRoot, null, false, null, 'search-render', noop, noop, noop, noop)
|
||||
}
|
||||
|
||||
const t0 = performance.now()
|
||||
// @ts-expect-error updateContainerSync exists but not in @types
|
||||
reconciler.updateContainerSync(el, container, null, noop)
|
||||
// @ts-expect-error flushSyncWork exists but not in @types
|
||||
reconciler.flushSyncWork()
|
||||
const t1 = performance.now()
|
||||
|
||||
|
|
@ -105,9 +102,7 @@ export function renderToScreen(el: ReactElement, width: number): { screen: Scree
|
|||
const t3 = performance.now()
|
||||
|
||||
// Unmount so next call gets a fresh tree. Leaves root/container/pools.
|
||||
// @ts-expect-error updateContainerSync exists but not in @types
|
||||
reconciler.updateContainerSync(null, container, null, noop)
|
||||
// @ts-expect-error flushSyncWork exists but not in @types
|
||||
reconciler.flushSyncWork()
|
||||
|
||||
timing.reconcile += t1 - t0
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue