@opentui/solid 0.1.88 → 0.1.90

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 CHANGED
@@ -128,6 +128,8 @@ Returns the current component catalogue that powers JSX tag lookup.
128
128
 
129
129
  - `useRenderer()`
130
130
  - `onResize(callback)`
131
+ - `onFocus(callback)`
132
+ - `onBlur(callback)`
131
133
  - `useTerminalDimensions()`
132
134
  - `useKeyboard(handler, options?)`
133
135
  - `usePaste(handler)`
package/dist/index.d.ts CHANGED
@@ -23,6 +23,8 @@ export function spread(node: any, accessor: any, skipChildren: any): void;
23
23
  export function setProp(node: any, name: any, value: any, prev: any): any;
24
24
  export function render(node: any, rendererOrConfig?: {}): Promise<void>;
25
25
  export function onResize(callback: any): void;
26
+ export function onFocus(callback: any): void;
27
+ export function onBlur(callback: any): void;
26
28
  declare var mergeProps3: typeof mergeProps;
27
29
  declare function memo2(fn: any): import("solid-js").Accessor<any>;
28
30
  export var insertNode: any;
package/index.js CHANGED
@@ -80,6 +80,24 @@ var usePaste = (callback) => {
80
80
  });
81
81
  };
82
82
  var useKeyHandler = useKeyboard;
83
+ var onFocus = (callback) => {
84
+ const renderer = useRenderer();
85
+ onMount(() => {
86
+ renderer.on("focus", callback);
87
+ });
88
+ onCleanup(() => {
89
+ renderer.off("focus", callback);
90
+ });
91
+ };
92
+ var onBlur = (callback) => {
93
+ const renderer = useRenderer();
94
+ onMount(() => {
95
+ renderer.on("blur", callback);
96
+ });
97
+ onCleanup(() => {
98
+ renderer.off("blur", callback);
99
+ });
100
+ };
83
101
  var useSelectionHandler = (callback) => {
84
102
  const renderer = useRenderer();
85
103
  onMount(() => {
@@ -984,11 +1002,18 @@ function Slot(props) {
984
1002
  });
985
1003
  const entryIds = createMemo3(() => entries().map((entry) => entry.id));
986
1004
  const entriesById = createMemo3(() => new Map(entries().map((entry) => [entry.id, entry])));
987
- const fallbackChildren = children(() => local.children);
988
1005
  const slotName = () => String(local.name);
1006
+ const FallbackView = () => {
1007
+ const resolvedFallbackChildren = children(() => local.children);
1008
+ return memo2(resolvedFallbackChildren);
1009
+ };
1010
+ const renderFallback = () => {
1011
+ return createComponent2(FallbackView, {});
1012
+ };
1013
+ const resolveFallback = (fallbackValue) => fallbackValue?.() ?? null;
989
1014
  const renderPluginFailurePlaceholder = (failure, fallbackValue) => {
990
1015
  if (!pluginFailurePlaceholder()) {
991
- return fallbackValue;
1016
+ return resolveFallback(fallbackValue);
992
1017
  }
993
1018
  try {
994
1019
  return pluginFailurePlaceholder()(failure);
@@ -1000,11 +1025,10 @@ function Slot(props) {
1000
1025
  source: "solid",
1001
1026
  error
1002
1027
  });
1003
- return fallbackValue;
1028
+ return resolveFallback(fallbackValue);
1004
1029
  }
1005
1030
  };
1006
1031
  const renderEntry = (entry, fallbackOnError) => {
1007
- const fallbackValue = fallbackOnError ?? null;
1008
1032
  let initialRender;
1009
1033
  try {
1010
1034
  initialRender = entry.renderer(registry().context, slotProps);
@@ -1016,12 +1040,12 @@ function Slot(props) {
1016
1040
  source: "solid",
1017
1041
  error
1018
1042
  });
1019
- return renderPluginFailurePlaceholder(failure, fallbackValue);
1043
+ return renderPluginFailurePlaceholder(failure, fallbackOnError);
1020
1044
  }
1021
1045
  const resolvedInitialRender = children(() => initialRender);
1022
1046
  const hasInitialOutput = resolvedInitialRender.toArray().some((node) => node !== null && node !== undefined && node !== false);
1023
1047
  if (!hasInitialOutput) {
1024
- return fallbackValue;
1048
+ return resolveFallback(fallbackOnError);
1025
1049
  }
1026
1050
  return createComponent2(ErrorBoundary, {
1027
1051
  fallback: (error) => {
@@ -1032,7 +1056,7 @@ function Slot(props) {
1032
1056
  source: "solid",
1033
1057
  error
1034
1058
  });
1035
- return renderPluginFailurePlaceholder(failure, fallbackValue);
1059
+ return renderPluginFailurePlaceholder(failure, fallbackOnError);
1036
1060
  },
1037
1061
  get children() {
1038
1062
  return resolvedInitialRender();
@@ -1049,7 +1073,7 @@ function Slot(props) {
1049
1073
  return renderEntry(resolvedEntry);
1050
1074
  });
1051
1075
  };
1052
- const appendView = [memo2(fallbackChildren), createComponent2(For, {
1076
+ const appendView = [renderFallback, createComponent2(For, {
1053
1077
  get each() {
1054
1078
  return entryIds();
1055
1079
  },
@@ -1060,22 +1084,21 @@ function Slot(props) {
1060
1084
  return memo2(() => {
1061
1085
  const resolvedEntries = entries();
1062
1086
  const mode = local.mode ?? "append";
1063
- const fallback = fallbackChildren();
1064
1087
  if (resolvedEntries.length === 0) {
1065
- return fallback;
1088
+ return renderFallback();
1066
1089
  }
1067
1090
  if (mode === "single_winner") {
1068
1091
  const winner = resolvedEntries[0];
1069
1092
  if (!winner) {
1070
- return fallback;
1093
+ return renderFallback();
1071
1094
  }
1072
- return renderEntry(winner, fallback);
1095
+ return renderEntry(winner, renderFallback);
1073
1096
  }
1074
1097
  if (mode === "replace") {
1075
1098
  const renderedEntries = resolvedEntries.map((entry) => renderEntry(entry));
1076
1099
  const hasPluginOutput = renderedEntries.some((entry) => entry !== null && entry !== undefined && entry !== false);
1077
1100
  if (!hasPluginOutput) {
1078
- return fallback;
1101
+ return renderFallback();
1079
1102
  }
1080
1103
  return renderedEntries;
1081
1104
  }
@@ -1166,6 +1189,8 @@ export {
1166
1189
  setProp,
1167
1190
  render,
1168
1191
  onResize,
1192
+ onFocus,
1193
+ onBlur,
1169
1194
  mergeProps3 as mergeProps,
1170
1195
  memo2 as memo,
1171
1196
  insertNode,
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "main": "index.js",
5
5
  "types": "index.d.ts",
6
6
  "type": "module",
7
- "version": "0.1.88",
7
+ "version": "0.1.90",
8
8
  "description": "SolidJS renderer for OpenTUI",
9
9
  "license": "MIT",
10
10
  "repository": {
@@ -35,7 +35,7 @@
35
35
  "dependencies": {
36
36
  "@babel/core": "7.28.0",
37
37
  "@babel/preset-typescript": "7.27.1",
38
- "@opentui/core": "0.1.88",
38
+ "@opentui/core": "0.1.90",
39
39
  "babel-plugin-module-resolver": "5.0.2",
40
40
  "babel-preset-solid": "1.9.10",
41
41
  "entities": "7.0.1",
@@ -1,4 +1,3 @@
1
- import solidTransformPlugin from "./solid-plugin.js"
2
- import { plugin, type BunPlugin } from "bun"
1
+ import { ensureSolidTransformPlugin } from "./solid-plugin.js"
3
2
 
4
- plugin(solidTransformPlugin)
3
+ ensureSolidTransformPlugin()
@@ -9,9 +9,9 @@ import {
9
9
  import * as solidJsRuntime from "solid-js"
10
10
  import * as solidJsStoreRuntime from "solid-js/store"
11
11
  import * as solidRuntime from "../index"
12
- import { createSolidTransformPlugin } from "./solid-plugin"
12
+ import { ensureSolidTransformPlugin } from "./solid-plugin"
13
13
 
14
- const runtimePluginSupportInstalledKey = "__opentuiSolidRuntimePluginSupportInstalled__"
14
+ const runtimePluginSupportInstalledKey = Symbol.for("opentui.solid.runtime-plugin-support")
15
15
 
16
16
  type RuntimePluginSupportState = typeof globalThis & {
17
17
  [runtimePluginSupportInstalledKey]?: boolean
@@ -38,14 +38,12 @@ export function ensureRuntimePluginSupport(): boolean {
38
38
  return false
39
39
  }
40
40
 
41
- registerBunPlugin(
42
- createSolidTransformPlugin({
43
- moduleName: runtimeModuleIdForSpecifier("@opentui/solid"),
44
- resolvePath(specifier) {
45
- return resolveRuntimeSpecifier(specifier)
46
- },
47
- }),
48
- )
41
+ ensureSolidTransformPlugin({
42
+ moduleName: runtimeModuleIdForSpecifier("@opentui/solid"),
43
+ resolvePath(specifier) {
44
+ return resolveRuntimeSpecifier(specifier)
45
+ },
46
+ })
49
47
 
50
48
  registerBunPlugin(
51
49
  createRuntimePlugin({
@@ -4,6 +4,8 @@ export interface CreateSolidTransformPluginOptions {
4
4
  moduleName?: string;
5
5
  resolvePath?: ResolveImportPath;
6
6
  }
7
+ export declare function ensureSolidTransformPlugin(input?: CreateSolidTransformPluginOptions): boolean;
8
+ export declare function resetSolidTransformPluginState(): void;
7
9
  export declare function createSolidTransformPlugin(input?: CreateSolidTransformPluginOptions): BunPlugin;
8
10
  declare const solidTransformPlugin: BunPlugin;
9
11
  export default solidTransformPlugin;
@@ -1,47 +1,112 @@
1
1
  import { transformAsync } from "@babel/core"
2
- import { readFile } from "node:fs/promises"
3
2
  // @ts-expect-error - Types not important.
4
3
  import ts from "@babel/preset-typescript"
5
4
  // @ts-expect-error - Types not important.
6
5
  import moduleResolver from "babel-plugin-module-resolver"
7
6
  // @ts-expect-error - Types not important.
8
7
  import solid from "babel-preset-solid"
9
- import { type BunPlugin } from "bun"
8
+ import { plugin as registerBunPlugin, type BunPlugin } from "bun"
10
9
 
11
10
  export type ResolveImportPath = (specifier: string) => string | null
12
11
 
12
+ const solidTransformStateKey = Symbol.for("opentui.solid.transform")
13
+
14
+ type SolidTransformRuntime = {
15
+ moduleName?: string
16
+ resolvePath?: ResolveImportPath
17
+ }
18
+
19
+ type SolidTransformState = {
20
+ installed: boolean
21
+ runtime?: SolidTransformRuntime
22
+ }
23
+
24
+ type GlobalSolidTransformState = typeof globalThis & {
25
+ [solidTransformStateKey]?: SolidTransformState
26
+ }
27
+
13
28
  export interface CreateSolidTransformPluginOptions {
14
29
  moduleName?: string
15
30
  resolvePath?: ResolveImportPath
16
31
  }
17
32
 
18
- export function createSolidTransformPlugin(input: CreateSolidTransformPluginOptions = {}): BunPlugin {
19
- const moduleName = input.moduleName ?? "@opentui/solid"
20
- const resolvePath = input.resolvePath
33
+ const getSolidTransformState = (): SolidTransformState => {
34
+ const state = globalThis as GlobalSolidTransformState
35
+ state[solidTransformStateKey] ??= { installed: false }
36
+ return state[solidTransformStateKey]
37
+ }
38
+
39
+ const getSolidTransformRuntime = (): SolidTransformRuntime => {
40
+ return getSolidTransformState().runtime ?? {}
41
+ }
42
+
43
+ const sourcePath = (path: string): string => {
44
+ const searchIndex = path.indexOf("?")
45
+ const hashIndex = path.indexOf("#")
46
+ const end = [searchIndex, hashIndex].filter((index) => index >= 0).sort((a, b) => a - b)[0]
47
+ return end === undefined ? path : path.slice(0, end)
48
+ }
49
+
50
+ const hasSolidTransformRuntime = (input: CreateSolidTransformPluginOptions): boolean => {
51
+ return input.moduleName !== undefined || input.resolvePath !== undefined
52
+ }
53
+
54
+ export function ensureSolidTransformPlugin(input: CreateSolidTransformPluginOptions = {}): boolean {
55
+ const state = getSolidTransformState()
56
+
57
+ if (hasSolidTransformRuntime(input)) {
58
+ state.runtime = {
59
+ moduleName: input.moduleName,
60
+ resolvePath: input.resolvePath,
61
+ }
62
+ }
63
+
64
+ if (state.installed) {
65
+ return false
66
+ }
67
+
68
+ registerBunPlugin(createSolidTransformPlugin())
69
+ state.installed = true
70
+ return true
71
+ }
21
72
 
73
+ export function resetSolidTransformPluginState(): void {
74
+ const state = getSolidTransformState()
75
+ state.installed = false
76
+ delete state.runtime
77
+ }
78
+
79
+ export function createSolidTransformPlugin(input: CreateSolidTransformPluginOptions = {}): BunPlugin {
22
80
  return {
23
81
  name: "bun-plugin-solid",
24
82
  setup: (build) => {
25
- build.onLoad({ filter: /[\/\\]node_modules[\/\\]solid-js[\/\\]dist[\/\\]server\.js$/ }, async (args) => {
26
- const path = args.path.replace("server.js", "solid.js")
27
- const file = Bun.file(path)
28
- const code = await file.text()
29
- return { contents: code, loader: "js" }
30
- })
83
+ build.onLoad(
84
+ { filter: /[\/\\]node_modules[\/\\]solid-js[\/\\]dist[\/\\]server\.js(?:[?#].*)?$/ },
85
+ async (args) => {
86
+ const path = sourcePath(args.path).replace("server.js", "solid.js")
87
+ const file = Bun.file(path)
88
+ const code = await file.text()
89
+ return { contents: code, loader: "js" }
90
+ },
91
+ )
31
92
 
32
93
  build.onLoad(
33
- { filter: /[\/\\]node_modules[\/\\]solid-js[\/\\]store[\/\\]dist[\/\\]server\.js$/ },
94
+ { filter: /[\/\\]node_modules[\/\\]solid-js[\/\\]store[\/\\]dist[\/\\]server\.js(?:[?#].*)?$/ },
34
95
  async (args) => {
35
- const path = args.path.replace("server.js", "store.js")
96
+ const path = sourcePath(args.path).replace("server.js", "store.js")
36
97
  const file = Bun.file(path)
37
98
  const code = await file.text()
38
99
  return { contents: code, loader: "js" }
39
100
  },
40
101
  )
41
102
 
42
- build.onLoad({ filter: /\.(js|ts)x$/ }, async (args) => {
43
- const file = Bun.file(args.path)
103
+ build.onLoad({ filter: /\.(js|ts)x(?:[?#].*)?$/ }, async (args) => {
104
+ const path = sourcePath(args.path)
105
+ const file = Bun.file(path)
44
106
  const code = await file.text()
107
+ const runtime = getSolidTransformRuntime()
108
+ const moduleName = input.moduleName ?? runtime.moduleName ?? "@opentui/solid"
109
+ const resolvePath = input.resolvePath ?? runtime.resolvePath
45
110
  const plugins = resolvePath
46
111
  ? [
47
112
  [
@@ -56,7 +121,7 @@ export function createSolidTransformPlugin(input: CreateSolidTransformPluginOpti
56
121
  : []
57
122
 
58
123
  const transforms = await transformAsync(code, {
59
- filename: args.path,
124
+ filename: path,
60
125
  plugins,
61
126
  presets: [
62
127
  [
@@ -32,5 +32,7 @@ export declare const usePaste: (callback: (event: PasteEvent) => void) => void;
32
32
  * @deprecated renamed to useKeyboard
33
33
  */
34
34
  export declare const useKeyHandler: (callback: (key: KeyEvent) => void, options?: UseKeyboardOptions) => void;
35
+ export declare const onFocus: (callback: () => void) => void;
36
+ export declare const onBlur: (callback: () => void) => void;
35
37
  export declare const useSelectionHandler: (callback: (selection: Selection) => void) => void;
36
38
  export declare const useTimeline: (options?: TimelineOptions) => Timeline;