@opentui/solid 0.5.17 → 0.6.1

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/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // index.ts
2
- import { CliRenderer, createCliRenderer, engine as engine2 } from "@opentui/core";
2
+ import { CliRenderer, createCliRenderer, engine } from "@opentui/core";
3
3
  import { createTestRenderer } from "@opentui/core/testing";
4
4
 
5
5
  // src/elements/catalogue.ts
@@ -99,7 +99,7 @@ function getComponentCatalogue() {
99
99
  return componentCatalogue;
100
100
  }
101
101
  // src/elements/hooks.ts
102
- import { engine, Timeline } from "@opentui/core";
102
+ import { getTimelineEngine, Timeline } from "@opentui/core";
103
103
  import { createContext, createSignal, onCleanup, onMount, useContext } from "solid-js/dist/solid.js";
104
104
  var RendererContext = createContext();
105
105
  var useRenderer = () => {
@@ -188,6 +188,7 @@ var useSelectionHandler = (callback) => {
188
188
  });
189
189
  };
190
190
  var useTimeline = (options = {}) => {
191
+ const engine = getTimelineEngine(useRenderer());
191
192
  const timeline = new Timeline(options);
192
193
  onMount(() => {
193
194
  if (options.autoplay !== false) {
@@ -196,18 +197,22 @@ var useTimeline = (options = {}) => {
196
197
  engine.register(timeline);
197
198
  });
198
199
  onCleanup(() => {
199
- timeline.pause();
200
- engine.unregister(timeline);
200
+ try {
201
+ timeline.pause();
202
+ } finally {
203
+ engine.unregister(timeline);
204
+ }
201
205
  });
202
206
  return timeline;
203
207
  };
204
208
  // src/elements/extras.ts
209
+ import { Renderable as Renderable2 } from "@opentui/core";
205
210
  import { createEffect, createMemo as createMemo2, getOwner, onCleanup as onCleanup3, runWithOwner, splitProps, untrack as untrack2 } from "solid-js/dist/solid.js";
206
211
 
207
212
  // src/reconciler.ts
208
- import { BaseRenderable, createTextAttributes, ImageRenderable as ImageRenderable2, InputRenderable as InputRenderable2, InputRenderableEvents, isTextNodeRenderable, isEditBufferRenderable, parseColor, Renderable, RootTextNodeRenderable, ScrollBoxRenderable as ScrollBoxRenderable2, SelectRenderable as SelectRenderable2, SelectRenderableEvents, TabSelectRenderable as TabSelectRenderable2, TabSelectRenderableEvents, TextNodeRenderable as TextNodeRenderable2, TextRenderable as TextRenderable2 } from "@opentui/core";
213
+ import { BaseRenderable, createTextAttributes, ImageRenderable as ImageRenderable2, InputRenderable as InputRenderable2, InputRenderableEvents, isTextNodeRenderable, isEditBufferRenderable, Renderable, RootTextNodeRenderable, ScrollBoxRenderable as ScrollBoxRenderable2, SelectRenderable as SelectRenderable2, SelectRenderableEvents, TabSelectRenderable as TabSelectRenderable2, TabSelectRenderableEvents, TextNodeRenderable as TextNodeRenderable2, TextRenderable as TextRenderable2, TextTableRenderable } from "@opentui/core";
209
214
  import { decodeHTMLStrict } from "entities";
210
- import { onCleanup as onCleanup2, useContext as useContext2 } from "solid-js/dist/solid.js";
215
+ import { createContext as createContext2, onCleanup as onCleanup2, useContext as useContext2 } from "solid-js/dist/solid.js";
211
216
 
212
217
  // src/renderer/universal.js
213
218
  import { createRoot, createRenderEffect, createMemo, createComponent, untrack, mergeProps } from "solid-js/dist/solid.js";
@@ -503,6 +508,7 @@ class TextNode extends TextNodeRenderable2 {
503
508
  return node;
504
509
  }
505
510
  }
511
+ var RenderableContext = createContext2();
506
512
  var logId = (node) => {
507
513
  if (!node)
508
514
  return;
@@ -599,7 +605,7 @@ function _getParentNode(childNode) {
599
605
  return parent;
600
606
  }
601
607
  var {
602
- render: _render,
608
+ render: renderRoot,
603
609
  effect,
604
610
  memo: memo2,
605
611
  createComponent: createComponent2,
@@ -615,7 +621,7 @@ var {
615
621
  createElement(tagName) {
616
622
  log("Creating element:", tagName);
617
623
  const id = getNextId(tagName);
618
- const solidRenderer = useContext2(RendererContext);
624
+ const solidRenderer = useContext2(RenderableContext)?.() ?? useContext2(RendererContext);
619
625
  if (!solidRenderer) {
620
626
  throw new Error("No renderer found");
621
627
  }
@@ -662,9 +668,9 @@ var {
662
668
  return;
663
669
  }
664
670
  if (name === "style") {
665
- node.attributes |= createTextAttributes(value);
666
- node.fg = value.fg ? parseColor(value.fg) : node.fg;
667
- node.bg = value.bg ? parseColor(value.bg) : node.bg;
671
+ node.attributes = node.attributes & ~createTextAttributes(prev) | createTextAttributes(value);
672
+ node.fg = value?.fg;
673
+ node.bg = value?.bg;
668
674
  return;
669
675
  }
670
676
  return;
@@ -764,7 +770,11 @@ var {
764
770
  break;
765
771
  case "text":
766
772
  case "content": {
767
- const textValue = typeof value === "string" ? value : Array.isArray(value) ? value.join("") : `${value}`;
773
+ if (name === "content" && node instanceof TextTableRenderable) {
774
+ node.content = value;
775
+ break;
776
+ }
777
+ const textValue = typeof value === "string" ? value : Array.isArray(value) ? value.join("") : `${value ?? ""}`;
768
778
  node[name] = textValue;
769
779
  break;
770
780
  }
@@ -816,32 +826,64 @@ var {
816
826
  return nextSibling;
817
827
  }
818
828
  });
829
+ function _render(code, root) {
830
+ return renderRoot(() => RenderableContext.Provider({
831
+ value: root instanceof Renderable ? () => root.ctx : undefined,
832
+ get children() {
833
+ return code();
834
+ }
835
+ }), root);
836
+ }
819
837
 
820
838
  // src/elements/extras.ts
821
839
  function Portal(props) {
822
840
  const renderer = useRenderer();
823
- const marker = createSlotNode(), mount = () => props.mount || renderer.root, owner = getOwner();
841
+ const marker = createSlotNode(), owner = getOwner();
824
842
  let content;
843
+ let container;
825
844
  createEffect(() => {
826
- content || (content = runWithOwner(owner, () => createMemo2(() => props.children)));
827
- const el = mount();
828
- const container = createElement("box"), renderRoot = container;
829
- Object.defineProperty(container, "_$host", {
830
- get() {
831
- return marker.parent;
832
- },
833
- configurable: true
845
+ const mount = createMemo2(() => {
846
+ const el = props.mount || renderer.root;
847
+ const scene = el instanceof Renderable2 ? el.ctx.nativeScene : renderer.nativeScene;
848
+ const currentScene = container instanceof Renderable2 ? container.ctx.nativeScene : renderer.nativeScene;
849
+ if (container && scene !== currentScene && container.getChildrenCount() !== 0) {
850
+ throw new Error("Cannot retarget a Portal with existing nodes between native scenes");
851
+ }
852
+ return el;
834
853
  });
835
- insert(renderRoot, content);
836
- el.add(container);
837
- props.ref && props.ref(container);
838
- onCleanup3(() => {
839
- el.remove(container);
840
- process.nextTick(() => {
841
- if (!container.parent) {
842
- container.destroyRecursively();
854
+ const context = () => {
855
+ const el = untrack2(mount);
856
+ return el instanceof Renderable2 && el.ctx.nativeScene !== renderer.nativeScene ? el.ctx : renderer;
857
+ };
858
+ createEffect(() => {
859
+ const el = mount();
860
+ content ||= runWithOwner(owner, () => RenderableContext.Provider({
861
+ value: context,
862
+ get children() {
863
+ return props.children;
864
+ }
865
+ }));
866
+ let nextContainer;
867
+ RenderableContext.Provider({
868
+ value: context,
869
+ get children() {
870
+ nextContainer = createElement("box");
871
+ return;
843
872
  }
844
873
  });
874
+ Object.defineProperty(nextContainer, "_$host", {
875
+ get() {
876
+ return marker.parent;
877
+ },
878
+ configurable: true
879
+ });
880
+ insert(nextContainer, content);
881
+ el.add(nextContainer);
882
+ container = nextContainer;
883
+ onCleanup3(() => _removeNode(el, nextContainer));
884
+ props.ref && props.ref(nextContainer);
885
+ }, undefined, {
886
+ render: true
845
887
  });
846
888
  }, undefined, {
847
889
  render: true
@@ -869,14 +911,7 @@ function Dynamic(props) {
869
911
  return createDynamic(() => props.component, others);
870
912
  }
871
913
  // src/elements/slot.ts
872
- import { BaseRenderable as BaseRenderable2, isTextNodeRenderable as isTextNodeRenderable2, TextNodeRenderable as TextNodeRenderable3, TextRenderable as TextRenderable3, Yoga } from "@opentui/core";
873
- function getLayoutNodeConstructor(parent) {
874
- const parentLayoutNode = parent?.getLayoutNode?.();
875
- return parentLayoutNode?.constructor;
876
- }
877
- function createLayoutSlotYogaNode(parentNodeConstructor) {
878
- return parentNodeConstructor?.create?.() ?? Yoga.default.Node.create();
879
- }
914
+ import { BaseRenderable as BaseRenderable2, isTextNodeRenderable as isTextNodeRenderable2, Renderable as Renderable3, TextNodeRenderable as TextNodeRenderable3, TextRenderable as TextRenderable3 } from "@opentui/core";
880
915
 
881
916
  class SlotBaseRenderable extends BaseRenderable2 {
882
917
  constructor(id) {
@@ -934,61 +969,31 @@ class TextSlotRenderable extends TextNodeRenderable3 {
934
969
  this.slotParent = undefined;
935
970
  this.parent?.remove(this);
936
971
  slotParent?.didDestroySlotChild(this);
972
+ super.destroy();
973
+ }
974
+ destroyRecursively() {
975
+ this.destroy();
937
976
  }
938
977
  }
939
978
 
940
- class LayoutSlotRenderable extends SlotBaseRenderable {
941
- destroyed = false;
942
- yogaNodeFreed = false;
979
+ class LayoutSlotRenderable extends Renderable3 {
943
980
  constructor(id, parent, layoutParent) {
944
- super(id);
945
- this._visible = false;
981
+ super(layoutParent.ctx, {
982
+ id,
983
+ visible: false
984
+ });
946
985
  this.slotParent = parent;
947
- this.yogaNodeConstructor = getLayoutNodeConstructor(layoutParent);
948
- this.yogaNode = createLayoutSlotYogaNode(this.yogaNodeConstructor);
949
- this.yogaNode.setDisplay(Yoga.Display.None);
950
- }
951
- getLayoutNode() {
952
- return this.yogaNode;
953
986
  }
954
- updateFromLayout() {}
955
- updateLayout() {}
956
- onRemove() {}
957
987
  isCompatibleWith(layoutParent) {
958
- return this.yogaNodeConstructor === getLayoutNodeConstructor(layoutParent);
959
- }
960
- detachFromSlot() {
961
- this.slotParent = undefined;
962
- }
963
- freeYogaNode() {
964
- if (this.yogaNodeFreed) {
965
- return;
966
- }
967
- this.yogaNodeFreed = true;
968
- try {
969
- this.yogaNode.free();
970
- } catch {}
988
+ return layoutParent instanceof Renderable3 && layoutParent.ctx.nativeScene === this.ctx.nativeScene;
971
989
  }
972
990
  disposeWithoutSlotCascade() {
973
- if (this.destroyed) {
974
- return;
975
- }
976
- this.destroyed = true;
977
- this.detachFromSlot();
978
- this.freeYogaNode();
979
- }
980
- destroyRecursively() {
991
+ this.slotParent = undefined;
981
992
  this.destroy();
982
993
  }
983
- destroy() {
984
- if (this.destroyed) {
985
- return;
986
- }
987
- this.destroyed = true;
994
+ destroySelf() {
988
995
  const slotParent = this.slotParent;
989
996
  this.slotParent = undefined;
990
- this.parent?.remove(this);
991
- this.freeYogaNode();
992
997
  slotParent?.didDestroySlotChild(this);
993
998
  }
994
999
  }
@@ -1082,7 +1087,7 @@ class SlotRenderable extends SlotBaseRenderable {
1082
1087
  }
1083
1088
  disposeDetachedIncompatibleLayoutNodes(parent) {
1084
1089
  for (const [mappedParent, layoutNode] of this.layoutNodesByParent) {
1085
- if (layoutNode.parent || layoutNode.isCompatibleWith(parent)) {
1090
+ if (layoutNode.parent || !this.isTextSlotParent(parent) && layoutNode.isCompatibleWith(parent)) {
1086
1091
  continue;
1087
1092
  }
1088
1093
  this.layoutNodesByParent.delete(mappedParent);
@@ -1130,7 +1135,10 @@ class SlotRenderable extends SlotBaseRenderable {
1130
1135
  }
1131
1136
  this.disposeDetachedTextNodes();
1132
1137
  this.disposeDetachedIncompatibleLayoutNodes(parent);
1133
- const layoutNode = new LayoutSlotRenderable(`slot-layout-${this.id}-${++this.layoutNodeCount}`, this, parent);
1138
+ const id = `slot-layout-${this.id}-${++this.layoutNodeCount}`;
1139
+ if (!(parent instanceof Renderable3))
1140
+ throw new Error("Layout slots require a renderable parent");
1141
+ const layoutNode = new LayoutSlotRenderable(id, this, parent);
1134
1142
  this.layoutNodesByParent.set(parent, layoutNode);
1135
1143
  return layoutNode;
1136
1144
  }
@@ -1142,6 +1150,9 @@ class SlotRenderable extends SlotBaseRenderable {
1142
1150
  }
1143
1151
  didRemoveSlotChild(parent, child) {
1144
1152
  const hasOtherAttachedSlotChildren = this.hasOtherAttachedSlotChildren(child);
1153
+ if (this.parent === parent) {
1154
+ this.parent = this.getAttachedSlotParent(child);
1155
+ }
1145
1156
  if (hasOtherAttachedSlotChildren && child instanceof TextSlotRenderable && this.getTextNodeForParent(parent) === child) {
1146
1157
  this.textNodesByParent.delete(parent);
1147
1158
  child.disposeWithoutSlotCascade();
@@ -1150,12 +1161,9 @@ class SlotRenderable extends SlotBaseRenderable {
1150
1161
  this.layoutNodesByParent.delete(parent);
1151
1162
  child.disposeWithoutSlotCascade();
1152
1163
  }
1153
- if (this.parent === parent) {
1154
- this.parent = this.getAttachedSlotParent(child);
1155
- }
1156
1164
  }
1157
1165
  didDestroySlotChild(child) {
1158
- const nodesByParent = child instanceof LayoutSlotRenderable ? this.layoutNodesByParent : this.textNodesByParent;
1166
+ const nodesByParent = child instanceof TextSlotRenderable ? this.textNodesByParent : this.layoutNodesByParent;
1159
1167
  for (const [parent, node] of nodesByParent) {
1160
1168
  if (node !== child)
1161
1169
  continue;
@@ -1170,20 +1178,25 @@ class SlotRenderable extends SlotBaseRenderable {
1170
1178
  }
1171
1179
  this.destroyed = true;
1172
1180
  this.parent = null;
1173
- const layoutNodes = new Set(this.layoutNodesByParent.values());
1181
+ const nodes = new Set([...this.layoutNodesByParent.values(), ...this.textNodesByParent.values()]);
1174
1182
  this.layoutNodesByParent.clear();
1175
- for (const layoutNode of layoutNodes) {
1176
- layoutNode.destroy();
1177
- }
1178
- const textNodes = new Set(this.textNodesByParent.values());
1179
1183
  this.textNodesByParent.clear();
1180
- for (const textNode of textNodes) {
1181
- textNode.destroy();
1184
+ let failure;
1185
+ for (const node of nodes) {
1186
+ try {
1187
+ node.destroy();
1188
+ } catch (error) {
1189
+ failure ??= {
1190
+ error
1191
+ };
1192
+ }
1182
1193
  }
1194
+ if (failure)
1195
+ throw failure.error;
1183
1196
  }
1184
1197
  }
1185
1198
  // src/scrollback.ts
1186
- import { BoxRenderable as BoxRenderable2, RootRenderable } from "@opentui/core";
1199
+ import { BoxRenderable as BoxRenderable2 } from "@opentui/core";
1187
1200
  import { createSignal as createSignal2 } from "solid-js/dist/solid.js";
1188
1201
  var solidScrollbackRootCounter = 0;
1189
1202
  var MAX_AUTO_HEIGHT_PASSES = 4;
@@ -1196,17 +1209,12 @@ function normalizeSnapshotDimension(value, axis) {
1196
1209
  }
1197
1210
  return Math.max(1, Math.trunc(value));
1198
1211
  }
1199
- function createSnapshotRendererValue(renderContext, root, width, height, firstLineOffset) {
1212
+ function createSnapshotRendererValue(renderContext, width, height, firstLineOffset) {
1200
1213
  const [snapshotWidth] = createSignal2(width);
1201
1214
  const [snapshotHeight, setSnapshotHeight] = createSignal2(height);
1202
1215
  const renderer = Object.create(renderContext);
1203
1216
  let offset = firstLineOffset;
1204
1217
  Object.defineProperties(renderer, {
1205
- root: {
1206
- value: root,
1207
- enumerable: true,
1208
- configurable: true
1209
- },
1210
1218
  width: {
1211
1219
  get: snapshotWidth,
1212
1220
  enumerable: true,
@@ -1236,40 +1244,21 @@ function createSnapshotRendererValue(renderContext, root, width, height, firstLi
1236
1244
  }
1237
1245
  };
1238
1246
  }
1239
- function runLifecyclePasses(renderContext) {
1240
- for (const renderable of renderContext.getLifecyclePasses()) {
1241
- renderable.onLifecyclePass?.call(renderable);
1242
- }
1243
- }
1244
1247
  function clearLifecyclePasses(renderContext) {
1245
1248
  for (const renderable of [...renderContext.getLifecyclePasses()]) {
1246
1249
  renderContext.unregisterLifecyclePass(renderable);
1247
1250
  }
1248
1251
  }
1249
- function measureSnapshotHeight(renderContext, root) {
1250
- const measureRoot = new RootRenderable(renderContext);
1251
- try {
1252
- measureRoot.add(root);
1253
- runLifecyclePasses(renderContext);
1254
- measureRoot.calculateLayout();
1255
- return Math.max(1, Math.trunc(root.getLayoutNode().getComputedLayout().height));
1256
- } finally {
1257
- if (root.parent === measureRoot) {
1258
- measureRoot.remove(root);
1259
- }
1260
- measureRoot.destroyRecursively();
1261
- }
1262
- }
1263
1252
  function resolveSnapshotHeight(renderContext, root, snapshotRenderer) {
1264
1253
  for (let pass = 0;pass < MAX_AUTO_HEIGHT_PASSES; pass++) {
1265
- const measuredHeight = measureSnapshotHeight(renderContext, root);
1254
+ const measuredHeight = renderContext.nativeScene.measureSnapshot(root);
1266
1255
  if (measuredHeight === snapshotRenderer.getHeight()) {
1267
1256
  clearLifecyclePasses(renderContext);
1268
1257
  return measuredHeight;
1269
1258
  }
1270
1259
  snapshotRenderer.setHeight(measuredHeight);
1271
1260
  }
1272
- return measureSnapshotHeight(renderContext, root);
1261
+ return renderContext.nativeScene.measureSnapshot(root);
1273
1262
  }
1274
1263
  function createScrollbackWriter(node, options = {}) {
1275
1264
  return (ctx) => {
@@ -1278,7 +1267,8 @@ function createScrollbackWriter(node, options = {}) {
1278
1267
  const startOnNewLine = options.startOnNewLine ?? true;
1279
1268
  const firstLineWidth = !startOnNewLine && ctx.tailColumn > 0 && ctx.tailColumn < ctx.width ? Math.min(width, ctx.width - ctx.tailColumn) : width;
1280
1269
  const firstLineOffset = width - firstLineWidth;
1281
- const root = new BoxRenderable2(ctx.renderContext, {
1270
+ const snapshotRenderer = createSnapshotRendererValue(ctx.renderContext, width, height ?? Math.max(1, ctx.renderContext.height), firstLineOffset);
1271
+ const root = new BoxRenderable2(snapshotRenderer.renderer, {
1282
1272
  id: `solid-scrollback-root-${solidScrollbackRootCounter++}`,
1283
1273
  position: "absolute",
1284
1274
  left: 0,
@@ -1290,7 +1280,11 @@ function createScrollbackWriter(node, options = {}) {
1290
1280
  shouldFill: false,
1291
1281
  flexDirection: "column"
1292
1282
  });
1293
- const snapshotRenderer = createSnapshotRendererValue(ctx.renderContext, root, width, height ?? Math.max(1, ctx.renderContext.height), firstLineOffset);
1283
+ Object.defineProperty(snapshotRenderer.renderer, "root", {
1284
+ value: root,
1285
+ enumerable: true,
1286
+ configurable: true
1287
+ });
1294
1288
  let dispose;
1295
1289
  let disposed = false;
1296
1290
  const teardown = () => {
@@ -1512,6 +1506,9 @@ function Slot(props) {
1512
1506
 
1513
1507
  // index.ts
1514
1508
  var mountSolidRoot = (renderer, node) => {
1509
+ if (renderer.isDestroyed)
1510
+ return;
1511
+ engine.attach(renderer);
1515
1512
  let dispose;
1516
1513
  let disposeRequested = false;
1517
1514
  let disposed = false;
@@ -1564,7 +1561,6 @@ var render = async (node, rendererOrConfig = {}) => {
1564
1561
  rendererOrConfig.onDestroy?.();
1565
1562
  }
1566
1563
  });
1567
- engine2.attach(renderer);
1568
1564
  mountSolidRoot(renderer, node);
1569
1565
  };
1570
1566
  var testRender = async (node, renderConfig = {}) => {
@@ -1574,7 +1570,6 @@ var testRender = async (node, renderConfig = {}) => {
1574
1570
  renderConfig.onDestroy?.();
1575
1571
  }
1576
1572
  });
1577
- engine2.attach(testSetup.renderer);
1578
1573
  mountSolidRoot(testSetup.renderer, node);
1579
1574
  return testSetup;
1580
1575
  };
@@ -1586,6 +1581,7 @@ export {
1586
1581
  LineBreakRenderable,
1587
1582
  LinkRenderable,
1588
1583
  Portal,
1584
+ RenderableContext,
1589
1585
  RendererContext,
1590
1586
  Slot,
1591
1587
  SlotRenderable,
@@ -1613,6 +1609,7 @@ export {
1613
1609
  onBlur,
1614
1610
  onFocus,
1615
1611
  onResize,
1612
+ _removeNode as removeNode,
1616
1613
  render,
1617
1614
  setProp,
1618
1615
  spread,
@@ -1629,4 +1626,4 @@ export {
1629
1626
  writeSolidToScrollback
1630
1627
  };
1631
1628
 
1632
- //# debugId=4E43020CDEA5CEE364756E2164756E21
1629
+ //# debugId=8E8E054AAA0C759464756E2164756E21