tinky 1.0.0 → 1.1.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.
Files changed (89) hide show
  1. package/lib/components/App.js +6 -6
  2. package/lib/components/Box.d.ts +2 -2
  3. package/lib/components/Box.js +2 -2
  4. package/lib/components/Static.d.ts +1 -1
  5. package/lib/components/Text.d.ts +1 -1
  6. package/lib/components/Text.js +3 -3
  7. package/lib/components/Transform.js +1 -1
  8. package/lib/{dom.js → core/dom.js} +3 -3
  9. package/lib/{log-update.js → core/log-update.js} +1 -1
  10. package/lib/{output.d.ts → core/output.d.ts} +1 -1
  11. package/lib/{render-border.js → core/render-border.js} +1 -1
  12. package/lib/{render-node-to-output.js → core/render-node-to-output.js} +4 -4
  13. package/lib/{tinky.js → core/tinky.js} +12 -12
  14. package/lib/hooks/use-app.js +1 -1
  15. package/lib/hooks/use-focus-manager.d.ts +1 -1
  16. package/lib/hooks/use-focus-manager.js +1 -1
  17. package/lib/hooks/use-focus.js +1 -1
  18. package/lib/hooks/use-input.js +2 -2
  19. package/lib/hooks/use-is-screen-reader-enabled.js +1 -1
  20. package/lib/hooks/use-stderr.js +1 -1
  21. package/lib/hooks/use-stdin.js +1 -1
  22. package/lib/hooks/use-stdout.js +1 -1
  23. package/lib/index.d.ts +12 -12
  24. package/lib/index.js +6 -6
  25. package/lib/utils/check-ci.d.ts +10 -0
  26. package/lib/utils/check-ci.js +21 -0
  27. package/lib/utils/cli-cursor.d.ts +20 -0
  28. package/lib/utils/cli-cursor.js +35 -0
  29. package/lib/{measure-element.d.ts → utils/measure-element.d.ts} +1 -1
  30. package/lib/utils/patch-console.d.ts +34 -0
  31. package/lib/utils/patch-console.js +83 -0
  32. package/lib/{render-background.d.ts → utils/render-background.d.ts} +2 -2
  33. package/lib/utils/signal-exit.d.ts +15 -0
  34. package/lib/utils/signal-exit.js +62 -0
  35. package/lib/{squash-text-nodes.d.ts → utils/squash-text-nodes.d.ts} +1 -1
  36. package/lib/{wrap-text.d.ts → utils/wrap-text.d.ts} +1 -1
  37. package/package.json +2 -5
  38. package/lib/signal-exit.d.ts +0 -11
  39. package/lib/signal-exit.js +0 -24
  40. /package/lib/{components → contexts}/AccessibilityContext.d.ts +0 -0
  41. /package/lib/{components → contexts}/AccessibilityContext.js +0 -0
  42. /package/lib/{components → contexts}/AppContext.d.ts +0 -0
  43. /package/lib/{components → contexts}/AppContext.js +0 -0
  44. /package/lib/{components → contexts}/BackgroundContext.d.ts +0 -0
  45. /package/lib/{components → contexts}/BackgroundContext.js +0 -0
  46. /package/lib/{components → contexts}/FocusContext.d.ts +0 -0
  47. /package/lib/{components → contexts}/FocusContext.js +0 -0
  48. /package/lib/{components → contexts}/StderrContext.d.ts +0 -0
  49. /package/lib/{components → contexts}/StderrContext.js +0 -0
  50. /package/lib/{components → contexts}/StdinContext.d.ts +0 -0
  51. /package/lib/{components → contexts}/StdinContext.js +0 -0
  52. /package/lib/{components → contexts}/StdoutContext.d.ts +0 -0
  53. /package/lib/{components → contexts}/StdoutContext.js +0 -0
  54. /package/lib/{devtools-window-polyfill.d.ts → core/devtools-window-polyfill.d.ts} +0 -0
  55. /package/lib/{devtools-window-polyfill.js → core/devtools-window-polyfill.js} +0 -0
  56. /package/lib/{devtools.d.ts → core/devtools.d.ts} +0 -0
  57. /package/lib/{devtools.js → core/devtools.js} +0 -0
  58. /package/lib/{dom.d.ts → core/dom.d.ts} +0 -0
  59. /package/lib/{instances.d.ts → core/instances.d.ts} +0 -0
  60. /package/lib/{instances.js → core/instances.js} +0 -0
  61. /package/lib/{log-update.d.ts → core/log-update.d.ts} +0 -0
  62. /package/lib/{output.js → core/output.js} +0 -0
  63. /package/lib/{reconciler.d.ts → core/reconciler.d.ts} +0 -0
  64. /package/lib/{reconciler.js → core/reconciler.js} +0 -0
  65. /package/lib/{render-border.d.ts → core/render-border.d.ts} +0 -0
  66. /package/lib/{render-node-to-output.d.ts → core/render-node-to-output.d.ts} +0 -0
  67. /package/lib/{render.d.ts → core/render.d.ts} +0 -0
  68. /package/lib/{render.js → core/render.js} +0 -0
  69. /package/lib/{renderer.d.ts → core/renderer.d.ts} +0 -0
  70. /package/lib/{renderer.js → core/renderer.js} +0 -0
  71. /package/lib/{styles.d.ts → core/styles.d.ts} +0 -0
  72. /package/lib/{styles.js → core/styles.js} +0 -0
  73. /package/lib/{taffy-node.d.ts → core/taffy-node.d.ts} +0 -0
  74. /package/lib/{taffy-node.js → core/taffy-node.js} +0 -0
  75. /package/lib/{tinky.d.ts → core/tinky.d.ts} +0 -0
  76. /package/lib/{colorize.d.ts → utils/colorize.d.ts} +0 -0
  77. /package/lib/{colorize.js → utils/colorize.js} +0 -0
  78. /package/lib/{dimension.d.ts → utils/dimension.d.ts} +0 -0
  79. /package/lib/{dimension.js → utils/dimension.js} +0 -0
  80. /package/lib/{get-max-width.d.ts → utils/get-max-width.d.ts} +0 -0
  81. /package/lib/{get-max-width.js → utils/get-max-width.js} +0 -0
  82. /package/lib/{measure-element.js → utils/measure-element.js} +0 -0
  83. /package/lib/{measure-text.d.ts → utils/measure-text.d.ts} +0 -0
  84. /package/lib/{measure-text.js → utils/measure-text.js} +0 -0
  85. /package/lib/{parse-keypress.d.ts → utils/parse-keypress.d.ts} +0 -0
  86. /package/lib/{parse-keypress.js → utils/parse-keypress.js} +0 -0
  87. /package/lib/{render-background.js → utils/render-background.js} +0 -0
  88. /package/lib/{squash-text-nodes.js → utils/squash-text-nodes.js} +0 -0
  89. /package/lib/{wrap-text.js → utils/wrap-text.js} +0 -0
@@ -2,12 +2,12 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { EventEmitter } from "node:events";
3
3
  import process from "node:process";
4
4
  import { PureComponent } from "react";
5
- import cliCursor from "cli-cursor";
6
- import { AppContext } from "./AppContext.js";
7
- import { StdinContext } from "./StdinContext.js";
8
- import { StdoutContext } from "./StdoutContext.js";
9
- import { StderrContext } from "./StderrContext.js";
10
- import { FocusContext } from "./FocusContext.js";
5
+ import * as cliCursor from "../utils/cli-cursor.js";
6
+ import { AppContext } from "../contexts/AppContext.js";
7
+ import { StdinContext } from "../contexts/StdinContext.js";
8
+ import { StdoutContext } from "../contexts/StdoutContext.js";
9
+ import { StderrContext } from "../contexts/StderrContext.js";
10
+ import { FocusContext } from "../contexts/FocusContext.js";
11
11
  import { ErrorOverview } from "./ErrorOverview.js";
12
12
  const tab = "\t";
13
13
  const shiftTab = "\u001B[Z";
@@ -1,6 +1,6 @@
1
1
  import { type Except } from "type-fest";
2
- import { type Styles } from "../styles.js";
3
- import { type DOMElement } from "../dom.js";
2
+ import { type Styles } from "../core/styles.js";
3
+ import { type DOMElement } from "../core/dom.js";
4
4
  /**
5
5
  * Props for the Box component.
6
6
  */
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { forwardRef, useContext } from "react";
3
- import { AccessibilityContext } from "./AccessibilityContext.js";
4
- import { backgroundContext } from "./BackgroundContext.js";
3
+ import { AccessibilityContext } from "../contexts/AccessibilityContext.js";
4
+ import { backgroundContext } from "../contexts/BackgroundContext.js";
5
5
  /**
6
6
  * `<Box>` is an essential Tinky component to build your layout. It's like
7
7
  * `<div style="display: flex">` in the browser.
@@ -1,5 +1,5 @@
1
1
  import { type ReactNode } from "react";
2
- import { type Styles } from "../styles.js";
2
+ import { type Styles } from "../core/styles.js";
3
3
  /**
4
4
  * Props for the Static component.
5
5
  */
@@ -1,7 +1,7 @@
1
1
  import { type ReactNode } from "react";
2
2
  import { type ForegroundColorName } from "chalk";
3
3
  import { type LiteralUnion } from "type-fest";
4
- import { type Styles } from "../styles.js";
4
+ import { type Styles } from "../core/styles.js";
5
5
  /**
6
6
  * Props for the Text component.
7
7
  */
@@ -1,9 +1,9 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useContext } from "react";
3
3
  import chalk from "chalk";
4
- import { colorize } from "../colorize.js";
5
- import { AccessibilityContext } from "./AccessibilityContext.js";
6
- import { backgroundContext } from "./BackgroundContext.js";
4
+ import { colorize } from "../utils/colorize.js";
5
+ import { AccessibilityContext } from "../contexts/AccessibilityContext.js";
6
+ import { backgroundContext } from "../contexts/BackgroundContext.js";
7
7
  /**
8
8
  * This component can display text and change its style to make it bold,
9
9
  * underlined, italic, or strikethrough.
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useContext } from "react";
3
- import { AccessibilityContext } from "./AccessibilityContext.js";
3
+ import { AccessibilityContext } from "../contexts/AccessibilityContext.js";
4
4
  /**
5
5
  * Transform a string representation of React components before they're written
6
6
  * to output. For example, you might want to apply a gradient to text, add a
@@ -1,7 +1,7 @@
1
1
  import stringWidth from "string-width";
2
- import { measureText } from "./measure-text.js";
3
- import { wrapText } from "./wrap-text.js";
4
- import { squashTextNodes } from "./squash-text-nodes.js";
2
+ import { measureText } from "../utils/measure-text.js";
3
+ import { wrapText } from "../utils/wrap-text.js";
4
+ import { squashTextNodes } from "../utils/squash-text-nodes.js";
5
5
  import { TaffyNode } from "./taffy-node.js";
6
6
  /**
7
7
  * Creates a new DOM element.
@@ -1,5 +1,5 @@
1
1
  import ansiEscapes from "ansi-escapes";
2
- import cliCursor from "cli-cursor";
2
+ import * as cliCursor from "../utils/cli-cursor.js";
3
3
  /**
4
4
  * Creates a standard LogUpdate instance.
5
5
  * Re-renders the entire previous output on each update.
@@ -1,5 +1,5 @@
1
1
  import { type OutputTransformer } from "./render-node-to-output.js";
2
- import { type Dimension } from "./dimension.js";
2
+ import { type Dimension } from "../utils/dimension.js";
3
3
  /**
4
4
  * Represents a clipping rectangle.
5
5
  */
@@ -1,6 +1,6 @@
1
1
  import cliBoxes from "cli-boxes";
2
2
  import chalk from "chalk";
3
- import { colorize } from "./colorize.js";
3
+ import { colorize } from "../utils/colorize.js";
4
4
  /**
5
5
  * Renders the border for a DOM node.
6
6
  * Calculates border dimensions and draws border characters with specified
@@ -1,11 +1,11 @@
1
1
  import widestLine from "widest-line";
2
2
  import indentString from "indent-string";
3
3
  import { Display } from "taffy-layout";
4
- import { wrapText } from "./wrap-text.js";
5
- import { getMaxWidth } from "./get-max-width.js";
6
- import { squashTextNodes } from "./squash-text-nodes.js";
4
+ import { wrapText } from "../utils/wrap-text.js";
5
+ import { getMaxWidth } from "../utils/get-max-width.js";
6
+ import { squashTextNodes } from "../utils/squash-text-nodes.js";
7
7
  import { renderBorder } from "./render-border.js";
8
- import { renderBackground } from "./render-background.js";
8
+ import { renderBackground } from "../utils/render-background.js";
9
9
  /**
10
10
  * Applies padding to text based on the layout of the first text node.
11
11
  *
@@ -2,10 +2,10 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import process from "node:process";
3
3
  import { throttle } from "es-toolkit/compat";
4
4
  import ansiEscapes from "ansi-escapes";
5
- import isInCi from "is-in-ci";
5
+ import { isCI } from "../utils/check-ci.js";
6
6
  import autoBind from "auto-bind";
7
- import { onExit } from "./signal-exit.js";
8
- import patchConsole from "patch-console";
7
+ import { onExit } from "../utils/signal-exit.js";
8
+ import { patchConsole } from "../utils/patch-console.js";
9
9
  import { LegacyRoot } from "react-reconciler/constants.js";
10
10
  import wrapAnsi from "wrap-ansi";
11
11
  import { reconciler } from "./reconciler.js";
@@ -13,8 +13,8 @@ import { renderer } from "./renderer.js";
13
13
  import * as dom from "./dom.js";
14
14
  import { logUpdate } from "./log-update.js";
15
15
  import { instances } from "./instances.js";
16
- import { App } from "./components/App.js";
17
- import { AccessibilityContext } from "./components/AccessibilityContext.js";
16
+ import { App } from "../components/App.js";
17
+ import { AccessibilityContext } from "../contexts/AccessibilityContext.js";
18
18
  const noop = () => {
19
19
  // no-op
20
20
  };
@@ -98,7 +98,7 @@ export class Tinky {
98
98
  // Unmount when process exits
99
99
  this.unsubscribeExit = onExit(() => {
100
100
  this.unmount();
101
- }, { alwaysLast: false });
101
+ });
102
102
  if (process.env["DEV"] === "true") {
103
103
  reconciler.injectIntoDevTools({
104
104
  bundleType: 0,
@@ -110,7 +110,7 @@ export class Tinky {
110
110
  if (options.patchConsole) {
111
111
  this.patchConsole();
112
112
  }
113
- if (!isInCi) {
113
+ if (!isCI) {
114
114
  options.stdout.on("resize", this.resized);
115
115
  this.unsubscribeResize = () => {
116
116
  options.stdout.off("resize", this.resized);
@@ -197,7 +197,7 @@ export class Tinky {
197
197
  this.options.stdout.write(this.fullStaticOutput + output);
198
198
  return;
199
199
  }
200
- if (isInCi) {
200
+ if (isCI) {
201
201
  if (hasStaticOutput) {
202
202
  this.options.stdout.write(staticOutput);
203
203
  }
@@ -284,7 +284,7 @@ export class Tinky {
284
284
  this.options.stdout.write(data + this.fullStaticOutput + this.lastOutput);
285
285
  return;
286
286
  }
287
- if (isInCi) {
287
+ if (isCI) {
288
288
  this.options.stdout.write(data);
289
289
  return;
290
290
  }
@@ -306,7 +306,7 @@ export class Tinky {
306
306
  this.options.stdout.write(this.fullStaticOutput + this.lastOutput);
307
307
  return;
308
308
  }
309
- if (isInCi) {
309
+ if (isCI) {
310
310
  this.options.stderr.write(data);
311
311
  return;
312
312
  }
@@ -338,7 +338,7 @@ export class Tinky {
338
338
  }
339
339
  // CIs don't handle erasing ansi escapes well, so it's better to
340
340
  // only render last frame of non-static output
341
- if (isInCi) {
341
+ if (isCI) {
342
342
  this.options.stdout.write(this.lastOutput + "\n");
343
343
  }
344
344
  else if (!this.options.debug) {
@@ -382,7 +382,7 @@ export class Tinky {
382
382
  * Clears the output.
383
383
  */
384
384
  clear() {
385
- if (!isInCi && !this.options.debug) {
385
+ if (!isCI && !this.options.debug) {
386
386
  this.log.clear();
387
387
  }
388
388
  }
@@ -1,5 +1,5 @@
1
1
  import { useContext } from "react";
2
- import { AppContext } from "../components/AppContext.js";
2
+ import { AppContext } from "../contexts/AppContext.js";
3
3
  /**
4
4
  * `useApp` is a React hook that exposes a method to manually exit the app
5
5
  * (unmount).
@@ -1,4 +1,4 @@
1
- import { type FocusProps } from "../components/FocusContext.js";
1
+ import { type FocusProps } from "../contexts/FocusContext.js";
2
2
  /**
3
3
  * Focus management control methods.
4
4
  */
@@ -1,5 +1,5 @@
1
1
  import { useContext } from "react";
2
- import { FocusContext } from "../components/FocusContext.js";
2
+ import { FocusContext } from "../contexts/FocusContext.js";
3
3
  /**
4
4
  * This hook exposes methods to enable or disable focus management for all
5
5
  * components or manually switch focus to the next or previous components.
@@ -1,5 +1,5 @@
1
1
  import { useEffect, useContext, useMemo } from "react";
2
- import { FocusContext } from "../components/FocusContext.js";
2
+ import { FocusContext } from "../contexts/FocusContext.js";
3
3
  import { useStdin } from "./use-stdin.js";
4
4
  /**
5
5
  * A component that uses the `useFocus` hook becomes "focusable" to Tinky, so
@@ -1,6 +1,6 @@
1
1
  import { useEffect } from "react";
2
- import { parseKeypress, nonAlphanumericKeys } from "../parse-keypress.js";
3
- import { reconciler } from "../reconciler.js";
2
+ import { parseKeypress, nonAlphanumericKeys } from "../utils/parse-keypress.js";
3
+ import { reconciler } from "../core/reconciler.js";
4
4
  import { useStdin } from "./use-stdin.js";
5
5
  /**
6
6
  * This hook is used for handling user input. It's a more convenient
@@ -1,5 +1,5 @@
1
1
  import { useContext } from "react";
2
- import { AccessibilityContext } from "../components/AccessibilityContext.js";
2
+ import { AccessibilityContext } from "../contexts/AccessibilityContext.js";
3
3
  /**
4
4
  * Returns whether a screen reader is enabled. This is useful when you want to
5
5
  * render different output for screen readers.
@@ -1,5 +1,5 @@
1
1
  import { useContext } from "react";
2
- import { StderrContext } from "../components/StderrContext.js";
2
+ import { StderrContext } from "../contexts/StderrContext.js";
3
3
  /**
4
4
  * `useStderr` is a React hook that exposes the stderr stream.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  import { useContext } from "react";
2
- import { StdinContext } from "../components/StdinContext.js";
2
+ import { StdinContext } from "../contexts/StdinContext.js";
3
3
  /**
4
4
  * `useStdin` is a React hook that exposes the stdin stream.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  import { useContext } from "react";
2
- import { StdoutContext } from "../components/StdoutContext.js";
2
+ import { StdoutContext } from "../contexts/StdoutContext.js";
3
3
  /**
4
4
  * `useStdout` is a React hook that exposes the stdout stream where Tinky
5
5
  * renders your app.
package/lib/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- export { render, type RenderOptions, type Instance } from "./render.js";
1
+ export { render, type RenderOptions, type Instance } from "./core/render.js";
2
2
  export { Box, type BoxProps } from "./components/Box.js";
3
3
  export { Text, type TextProps } from "./components/Text.js";
4
- export { AppContext, type AppProps } from "./components/AppContext.js";
5
- export { StdinContext, type StdinProps } from "./components/StdinContext.js";
6
- export { StdoutContext, type StdoutProps } from "./components/StdoutContext.js";
7
- export { StderrContext, type StderrProps } from "./components/StderrContext.js";
4
+ export { AppContext, type AppProps } from "./contexts/AppContext.js";
5
+ export { StdinContext, type StdinProps } from "./contexts/StdinContext.js";
6
+ export { StdoutContext, type StdoutProps } from "./contexts/StdoutContext.js";
7
+ export { StderrContext, type StderrProps } from "./contexts/StderrContext.js";
8
8
  export { Static, type StaticProps } from "./components/Static.js";
9
9
  export { Transform, type TransformProps } from "./components/Transform.js";
10
10
  export { Newline, type NewlineProps } from "./components/Newline.js";
@@ -17,10 +17,10 @@ export { useStderr } from "./hooks/use-stderr.js";
17
17
  export { useFocus, type FocusOptions, type FocusState, } from "./hooks/use-focus.js";
18
18
  export { useFocusManager, type FocusManager, } from "./hooks/use-focus-manager.js";
19
19
  export { useIsScreenReaderEnabled } from "./hooks/use-is-screen-reader-enabled.js";
20
- export { measureElement } from "./measure-element.js";
21
- export { type Dimension } from "./dimension.js";
22
- export { type DOMElement, type DOMNode, type DOMNodeAttribute, type ElementNames, type NodeNames, type TextName, type TextNode, type TinkyNode, } from "./dom.js";
23
- export { type Styles } from "./styles.js";
24
- export { type OutputTransformer } from "./render-node-to-output.js";
25
- export { type RenderMetrics } from "./tinky.js";
26
- export { type TaffyNode } from "./taffy-node.js";
20
+ export { measureElement } from "./utils/measure-element.js";
21
+ export { type Dimension } from "./utils/dimension.js";
22
+ export { type DOMElement, type DOMNode, type DOMNodeAttribute, type ElementNames, type NodeNames, type TextName, type TextNode, type TinkyNode, } from "./core/dom.js";
23
+ export { type Styles } from "./core/styles.js";
24
+ export { type OutputTransformer } from "./core/render-node-to-output.js";
25
+ export { type RenderMetrics } from "./core/tinky.js";
26
+ export { type TaffyNode } from "./core/taffy-node.js";
package/lib/index.js CHANGED
@@ -1,10 +1,10 @@
1
- export { render } from "./render.js";
1
+ export { render } from "./core/render.js";
2
2
  export { Box } from "./components/Box.js";
3
3
  export { Text } from "./components/Text.js";
4
- export { AppContext } from "./components/AppContext.js";
5
- export { StdinContext } from "./components/StdinContext.js";
6
- export { StdoutContext } from "./components/StdoutContext.js";
7
- export { StderrContext } from "./components/StderrContext.js";
4
+ export { AppContext } from "./contexts/AppContext.js";
5
+ export { StdinContext } from "./contexts/StdinContext.js";
6
+ export { StdoutContext } from "./contexts/StdoutContext.js";
7
+ export { StderrContext } from "./contexts/StderrContext.js";
8
8
  export { Static } from "./components/Static.js";
9
9
  export { Transform } from "./components/Transform.js";
10
10
  export { Newline } from "./components/Newline.js";
@@ -17,4 +17,4 @@ export { useStderr } from "./hooks/use-stderr.js";
17
17
  export { useFocus, } from "./hooks/use-focus.js";
18
18
  export { useFocusManager, } from "./hooks/use-focus-manager.js";
19
19
  export { useIsScreenReaderEnabled } from "./hooks/use-is-screen-reader-enabled.js";
20
- export { measureElement } from "./measure-element.js";
20
+ export { measureElement } from "./utils/measure-element.js";
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Determines if the current environment is a Continuous Integration (CI) environment.
3
+ *
4
+ * It checks for the presence of the `CI` or `CONTINUOUS_INTEGRATION` environment variables.
5
+ * These variables are commonly set by CI providers like GitHub Actions, Travis CI, CircleCI, etc.
6
+ *
7
+ * The check follows the convention that if the variable is present, it is considered true,
8
+ * unless explicitly set to '0' or 'false' (case-sensitive for the value check, though env vars are OS dependent).
9
+ */
10
+ export declare const isCI: boolean;
@@ -0,0 +1,21 @@
1
+ import process from "node:process";
2
+ /**
3
+ * Checks if a specific environment variable is set and not explicitly disabled.
4
+ *
5
+ * @param key - The name of the environment variable to check.
6
+ * @returns `true` if the environment variable is set and its value is not '0' or 'false', otherwise `false`.
7
+ */
8
+ const check = (key) => {
9
+ const value = process.env[key];
10
+ return value !== undefined && value !== "0" && value !== "false";
11
+ };
12
+ /**
13
+ * Determines if the current environment is a Continuous Integration (CI) environment.
14
+ *
15
+ * It checks for the presence of the `CI` or `CONTINUOUS_INTEGRATION` environment variables.
16
+ * These variables are commonly set by CI providers like GitHub Actions, Travis CI, CircleCI, etc.
17
+ *
18
+ * The check follows the convention that if the variable is present, it is considered true,
19
+ * unless explicitly set to '0' or 'false' (case-sensitive for the value check, though env vars are OS dependent).
20
+ */
21
+ export const isCI = check("CI") || check("CONTINUOUS_INTEGRATION");
@@ -0,0 +1,20 @@
1
+ import { type Writable } from "node:stream";
2
+ /**
3
+ * Show the cursor.
4
+ *
5
+ * @param stream - The stream to show the cursor on. Defaults to `process.stderr`.
6
+ */
7
+ export declare function show(stream?: Writable): void;
8
+ /**
9
+ * Hide the cursor.
10
+ *
11
+ * @param stream - The stream to hide the cursor on. Defaults to `process.stderr`.
12
+ */
13
+ export declare function hide(stream?: Writable): void;
14
+ /**
15
+ * Toggle cursor visibility.
16
+ *
17
+ * @param force - If `true`, shows the cursor. If `false`, hides the cursor.
18
+ * @param stream - The stream to toggle the cursor on. Defaults to `process.stderr`.
19
+ */
20
+ export declare function toggle(force?: boolean, stream?: Writable): void;
@@ -0,0 +1,35 @@
1
+ import process from "node:process";
2
+ const kHideCursor = "\u001B[?25l";
3
+ const kShowCursor = "\u001B[?25h";
4
+ /**
5
+ * Show the cursor.
6
+ *
7
+ * @param stream - The stream to show the cursor on. Defaults to `process.stderr`.
8
+ */
9
+ export function show(stream = process.stderr) {
10
+ stream.write(kShowCursor);
11
+ }
12
+ /**
13
+ * Hide the cursor.
14
+ *
15
+ * @param stream - The stream to hide the cursor on. Defaults to `process.stderr`.
16
+ */
17
+ export function hide(stream = process.stderr) {
18
+ stream.write(kHideCursor);
19
+ }
20
+ /**
21
+ * Toggle cursor visibility.
22
+ *
23
+ * @param force - If `true`, shows the cursor. If `false`, hides the cursor.
24
+ * @param stream - The stream to toggle the cursor on. Defaults to `process.stderr`.
25
+ */
26
+ export function toggle(force, stream = process.stderr) {
27
+ if (force !== undefined) {
28
+ if (force) {
29
+ show(stream);
30
+ }
31
+ else {
32
+ hide(stream);
33
+ }
34
+ }
35
+ }
@@ -1,4 +1,4 @@
1
- import { type DOMElement } from "./dom.js";
1
+ import { type DOMElement } from "../core/dom.js";
2
2
  import { type Dimension } from "./dimension.js";
3
3
  /**
4
4
  * Measure the dimensions of a particular `<Box>` element.
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Union type representing the standard output streams.
3
+ */
4
+ type Stream = "stdout" | "stderr";
5
+ /**
6
+ * Callback function to handle intercepted console output.
7
+ *
8
+ * @param stream - The stream the output is intended for ('stdout' or 'stderr').
9
+ * @param data - The formatted string to be logged.
10
+ */
11
+ type Callback = (stream: Stream, data: string) => void;
12
+ /**
13
+ * Patches global console methods to intercept output and redirect it to a callback.
14
+ *
15
+ * This function wraps standard console methods (like `console.log`, `console.error`, etc.)
16
+ * so that they call the provided callback instead of writing directly to the stream.
17
+ * It uses `util.format` to format arguments exactly like the original console methods.
18
+ *
19
+ * @param callback - The function to call with the intercepted output.
20
+ * @returns A function that restores the original console methods when called.
21
+ *
22
+ * @example
23
+ * ```ts
24
+ * const restore = patchConsole((stream, data) => {
25
+ * console.log(`[${stream}] ${data}`);
26
+ * });
27
+ *
28
+ * console.log("Hello"); // Output: [stdout] Hello
29
+ *
30
+ * restore(); // Console is back to normal
31
+ * ```
32
+ */
33
+ export declare const patchConsole: (callback: Callback) => (() => void);
34
+ export {};
@@ -0,0 +1,83 @@
1
+ import util from "util";
2
+ /**
3
+ * List of console methods to patch, categorized by their target stream.
4
+ */
5
+ const METHODS = {
6
+ stdout: [
7
+ "log",
8
+ "info",
9
+ "dir",
10
+ "dirxml",
11
+ "table",
12
+ "count",
13
+ "countReset",
14
+ "group",
15
+ "groupCollapsed",
16
+ "groupEnd",
17
+ "time",
18
+ "timeLog",
19
+ "timeEnd",
20
+ "debug",
21
+ ],
22
+ stderr: ["error", "warn", "trace", "assert"],
23
+ };
24
+ /**
25
+ * Patches global console methods to intercept output and redirect it to a callback.
26
+ *
27
+ * This function wraps standard console methods (like `console.log`, `console.error`, etc.)
28
+ * so that they call the provided callback instead of writing directly to the stream.
29
+ * It uses `util.format` to format arguments exactly like the original console methods.
30
+ *
31
+ * @param callback - The function to call with the intercepted output.
32
+ * @returns A function that restores the original console methods when called.
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * const restore = patchConsole((stream, data) => {
37
+ * console.log(`[${stream}] ${data}`);
38
+ * });
39
+ *
40
+ * console.log("Hello"); // Output: [stdout] Hello
41
+ *
42
+ * restore(); // Console is back to normal
43
+ * ```
44
+ */
45
+ export const patchConsole = (callback) => {
46
+ // Use ConsoleMethod to validly type the stored methods
47
+ const originalMethods = {};
48
+ const patch = (stream, method) => {
49
+ // Cast console to a record of ConsoleMethods to allow index access without explicit any
50
+ const consoleMethods = console;
51
+ originalMethods[method] = consoleMethods[method];
52
+ consoleMethods[method] = (...args) => {
53
+ // Handle console.assert specifically as it only outputs if assertion fails
54
+ if (method === "assert") {
55
+ if (!args[0]) {
56
+ // Remove the assertion condition and format the rest
57
+ callback(stream, util.format(...args.slice(1)));
58
+ }
59
+ return;
60
+ }
61
+ callback(stream, util.format(...args));
62
+ };
63
+ };
64
+ for (const method of METHODS.stdout) {
65
+ if (console[method]) {
66
+ patch("stdout", method);
67
+ }
68
+ }
69
+ for (const method of METHODS.stderr) {
70
+ if (console[method]) {
71
+ patch("stderr", method);
72
+ }
73
+ }
74
+ return () => {
75
+ // Cast console to a record of ConsoleMethods to restore original methods
76
+ const consoleMethods = console;
77
+ for (const [method, original] of Object.entries(originalMethods)) {
78
+ if (original) {
79
+ consoleMethods[method] = original;
80
+ }
81
+ }
82
+ };
83
+ };
@@ -1,5 +1,5 @@
1
- import { type DOMNode } from "./dom.js";
2
- import { type Output } from "./output.js";
1
+ import { type DOMNode } from "../core/dom.js";
2
+ import { type Output } from "../core/output.js";
3
3
  /**
4
4
  * Renders the background color of a node to the output.
5
5
  *
@@ -0,0 +1,15 @@
1
+ type Callback = (code?: number | null, signal?: NodeJS.Signals | null) => void;
2
+ /**
3
+ * Registers a callback to be executed when the process exits or receives a termination signal.
4
+ *
5
+ * This function is an internal replacement for the `signal-exit` library.
6
+ * It ensures that the callback is executed when the process ends, whether naturally
7
+ * or via signals like SIGINT (Ctrl+C).
8
+ *
9
+ * @param callback - The function to call on exit.
10
+ * @param options - Configuration options.
11
+ * @param options.alwaysLast - (Ignored in this simplified implementation) Ensures callback runs after other exit handlers.
12
+ * @returns A function to unsubscribe the exit listener.
13
+ */
14
+ export declare function onExit(callback: Callback): () => void;
15
+ export {};
@@ -0,0 +1,62 @@
1
+ import process from "node:process";
2
+ // Track all registered listeners
3
+ const callbacks = new Set();
4
+ let isAttached = false;
5
+ /**
6
+ * Handles signal events by invoking all registered callbacks and then exiting.
7
+ *
8
+ * @param signal - The signal that triggered the exit.
9
+ */
10
+ const handleSignal = (signal) => {
11
+ for (const callback of callbacks) {
12
+ callback(null, signal);
13
+ }
14
+ // Restore default behavior: exit the process
15
+ // We need to exit explicitly because adding a listener prevents the default exit
16
+ process.exit(128 + 1);
17
+ };
18
+ /**
19
+ * Handles the 'exit' event by invoking all registered callbacks.
20
+ *
21
+ * @param code - The exit code.
22
+ */
23
+ const handleExit = (code) => {
24
+ for (const callback of callbacks) {
25
+ callback(code, null);
26
+ }
27
+ };
28
+ /**
29
+ * Attaches the global event listeners if they haven't been attached yet.
30
+ */
31
+ const attach = () => {
32
+ if (isAttached) {
33
+ return;
34
+ }
35
+ isAttached = true;
36
+ process.on("exit", handleExit);
37
+ process.on("SIGINT", handleSignal);
38
+ process.on("SIGTERM", handleSignal);
39
+ // SIGHUP is not supported on Windows, but safe to listen to on other platforms
40
+ if (process.platform !== "win32") {
41
+ process.on("SIGHUP", handleSignal);
42
+ }
43
+ };
44
+ /**
45
+ * Registers a callback to be executed when the process exits or receives a termination signal.
46
+ *
47
+ * This function is an internal replacement for the `signal-exit` library.
48
+ * It ensures that the callback is executed when the process ends, whether naturally
49
+ * or via signals like SIGINT (Ctrl+C).
50
+ *
51
+ * @param callback - The function to call on exit.
52
+ * @param options - Configuration options.
53
+ * @param options.alwaysLast - (Ignored in this simplified implementation) Ensures callback runs after other exit handlers.
54
+ * @returns A function to unsubscribe the exit listener.
55
+ */
56
+ export function onExit(callback) {
57
+ attach();
58
+ callbacks.add(callback);
59
+ return () => {
60
+ callbacks.delete(callback);
61
+ };
62
+ }
@@ -1,4 +1,4 @@
1
- import { type DOMElement } from "./dom.js";
1
+ import { type DOMElement } from "../core/dom.js";
2
2
  /**
3
3
  * Consolidates multiple text nodes into a single string.
4
4
  * Useful for combining adjacent text nodes to minimize write operations.
@@ -1,4 +1,4 @@
1
- import { type Styles } from "./styles.js";
1
+ import { type Styles } from "../core/styles.js";
2
2
  /**
3
3
  * Wraps or truncates text based on the specified maximum width and wrap type.
4
4
  * Results are cached to improve performance.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tinky",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "React for CLIs, re-imagined with the Taffy engine",
5
5
  "keywords": [
6
6
  "react",
@@ -45,15 +45,11 @@
45
45
  "auto-bind": "^5.0.1",
46
46
  "chalk": "^5.6.2",
47
47
  "cli-boxes": "^4.0.1",
48
- "cli-cursor": "^5.0.0",
49
48
  "cli-truncate": "^5.1.1",
50
49
  "code-excerpt": "^4.0.0",
51
50
  "es-toolkit": "^1.43.0",
52
51
  "indent-string": "^5.0.0",
53
- "is-in-ci": "^2.0.0",
54
- "patch-console": "^2.0.0",
55
52
  "react-reconciler": "^0.33.0",
56
- "signal-exit": "^4.1.0",
57
53
  "stack-utils": "^2.0.6",
58
54
  "taffy-layout": "^0.1.0",
59
55
  "tslint": "^5.20.1",
@@ -72,6 +68,7 @@
72
68
  "@commitlint/config-conventional": "^20.3.0",
73
69
  "@semantic-release/changelog": "^6.0.3",
74
70
  "@semantic-release/git": "^10.0.1",
71
+ "@semantic-release/github": "^12.0.0",
75
72
  "@semantic-release/npm": "^13.1.3",
76
73
  "@sinonjs/fake-timers": "^15.1.0",
77
74
  "@types/bun": "^1.3.6",
@@ -1,11 +0,0 @@
1
- /**
2
- * Registers a callback to be executed when the process exits or on signal.
3
- *
4
- * @param callback - The function to call on exit.
5
- * @param options - Configuration options.
6
- * @param options.alwaysLast - Ensures callback runs after other exit handlers.
7
- * @returns A function to unsubscribe the exit listener.
8
- */
9
- export declare function onExit(callback: (code?: number | null, signal?: NodeJS.Signals | null) => void, options?: {
10
- alwaysLast?: boolean;
11
- }): () => void;
@@ -1,24 +0,0 @@
1
- import process from "node:process";
2
- import { onExit as onSignalExit } from "signal-exit";
3
- /**
4
- * Registers a callback to be executed when the process exits or on signal.
5
- *
6
- * @param callback - The function to call on exit.
7
- * @param options - Configuration options.
8
- * @param options.alwaysLast - Ensures callback runs after other exit handlers.
9
- * @returns A function to unsubscribe the exit listener.
10
- */
11
- export function onExit(callback, options) {
12
- const unsubscribeSignalExit = onSignalExit(callback, options);
13
- // Polyfill for Bun: signal-exit does not seem to catch the 'exit' event
14
- // in this specific test environment, so we attach a manual listener
15
- // to ensure the final render frame is output.
16
- const onProcessExit = (code) => {
17
- callback(code, null);
18
- };
19
- process.on("exit", onProcessExit);
20
- return () => {
21
- unsubscribeSignalExit();
22
- process.off("exit", onProcessExit);
23
- };
24
- }
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes