@marimo-team/islands 0.20.3-dev89 → 0.20.3-dev91

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/dist/main.js CHANGED
@@ -70351,7 +70351,7 @@ Image URL: ${r.imageUrl}`)), contextToXml({
70351
70351
  return Logger.warn("Failed to get version from mount config"), null;
70352
70352
  }
70353
70353
  }
70354
- const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.20.3-dev89"), showCodeInRunModeAtom = atom(true);
70354
+ const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.20.3-dev91"), showCodeInRunModeAtom = atom(true);
70355
70355
  atom(null);
70356
70356
  var import_compiler_runtime$88 = require_compiler_runtime();
70357
70357
  function useKeydownOnElement(e, r) {
@@ -99666,26 +99666,31 @@ ${c}
99666
99666
  }), r[0] = e, r[1] = c), c;
99667
99667
  };
99668
99668
  var ConsoleOutputInternal = (e) => {
99669
- let r = (0, import_compiler_runtime$4.c)(58), c = import_react.useRef(null), { wrapText: d, setWrapText: f } = useWrapText(), [_, v] = useExpandedConsoleOutput(e.cellId), { consoleOutputs: y, stale: S, cellName: w, cellId: E, onSubmitDebugger: O, onClear: M, onRefactorWithAI: I, className: z } = e, G = y.length > 0, q = useSelectAllContent(G), IY = useOverflowDetection(c, G), LY;
99670
- if (r[0] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel") ? (LY = () => {
99669
+ let r = (0, import_compiler_runtime$4.c)(62), c = import_react.useRef(null), { wrapText: d, setWrapText: f } = useWrapText(), [_, v] = useExpandedConsoleOutput(e.cellId), [y, S] = import_react.useState(""), w;
99670
+ r[0] === y ? w = r[1] : (w = {
99671
+ value: y,
99672
+ setValue: S
99673
+ }, r[0] = y, r[1] = w);
99674
+ let E = useInputHistory(w), { consoleOutputs: O, stale: M, cellName: I, cellId: z, onSubmitDebugger: G, onClear: q, onRefactorWithAI: IY, className: LY } = e, RY = O.length > 0, zY = useSelectAllContent(RY), BY = useOverflowDetection(c, RY), VY;
99675
+ if (r[2] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel") ? (VY = () => {
99671
99676
  let e2 = c.current;
99672
99677
  if (!e2) return;
99673
99678
  let r2 = e2.scrollHeight - e2.clientHeight;
99674
99679
  r2 - e2.scrollTop < 120 && (e2.scrollTop = r2);
99675
- }, r[0] = LY) : LY = r[0], (0, import_react.useLayoutEffect)(LY), !G && isInternalCellName(w)) return null;
99676
- let RY, zY, BY, VY, HY, UY, WY, GY, KY, qY;
99677
- if (r[1] !== E || r[2] !== z || r[3] !== y || r[4] !== G || r[5] !== _ || r[6] !== IY || r[7] !== M || r[8] !== I || r[9] !== O || r[10] !== q || r[11] !== v || r[12] !== f || r[13] !== S || r[14] !== d) {
99680
+ }, r[2] = VY) : VY = r[2], (0, import_react.useLayoutEffect)(VY), !RY && isInternalCellName(I)) return null;
99681
+ let HY, UY, WY, GY, KY, qY, JY, YY, XY, ZY;
99682
+ if (r[3] !== z || r[4] !== LY || r[5] !== O || r[6] !== RY || r[7] !== E || r[8] !== _ || r[9] !== BY || r[10] !== q || r[11] !== IY || r[12] !== G || r[13] !== zY || r[14] !== v || r[15] !== f || r[16] !== M || r[17] !== y || r[18] !== d) {
99678
99683
  let e2 = [
99679
- ...y
99680
- ].reverse(), w2 = e2.some(_temp$2), LY2 = e2.findIndex(_temp2$2), JY2;
99681
- r[25] === y ? JY2 = r[26] : (JY2 = () => y.filter(_temp3$1).map(_temp4$1).join("\n"), r[25] = y, r[26] = JY2);
99682
- let YY2 = JY2;
99683
- qY = "relative group", r[27] !== YY2 || r[28] !== G || r[29] !== _ || r[30] !== IY || r[31] !== v || r[32] !== f || r[33] !== d ? (zY = G && (0, import_jsx_runtime.jsxs)("div", {
99684
+ ...O
99685
+ ].reverse(), w2 = e2.some(_temp$2), I2 = e2.findIndex(_temp2$2), VY2;
99686
+ r[29] === O ? VY2 = r[30] : (VY2 = () => O.filter(_temp3$1).map(_temp4$1).join("\n"), r[29] = O, r[30] = VY2);
99687
+ let QY2 = VY2;
99688
+ HY = "relative group", r[31] !== QY2 || r[32] !== RY || r[33] !== _ || r[34] !== BY || r[35] !== v || r[36] !== f || r[37] !== d ? (UY = RY && (0, import_jsx_runtime.jsxs)("div", {
99684
99689
  className: "absolute top-1 right-4 z-10 opacity-0 group-hover:opacity-100 flex items-center gap-1 print:hidden",
99685
99690
  children: [
99686
99691
  (0, import_jsx_runtime.jsx)(CopyClipboardIcon, {
99687
99692
  tooltip: "Copy console output",
99688
- value: YY2,
99693
+ value: QY2,
99689
99694
  className: "h-4 w-4"
99690
99695
  }),
99691
99696
  (0, import_jsx_runtime.jsx)(Tooltip, {
@@ -99702,7 +99707,7 @@ ${c}
99702
99707
  })
99703
99708
  })
99704
99709
  }),
99705
- (IY || _) && (0, import_jsx_runtime.jsx)(Button, {
99710
+ (BY || _) && (0, import_jsx_runtime.jsx)(Button, {
99706
99711
  "aria-label": _ ? "Collapse output" : "Expand output",
99707
99712
  className: "p-0 mb-px",
99708
99713
  onClick: () => v(!_),
@@ -99721,21 +99726,24 @@ ${c}
99721
99726
  })
99722
99727
  })
99723
99728
  ]
99724
- }), r[27] = YY2, r[28] = G, r[29] = _, r[30] = IY, r[31] = v, r[32] = f, r[33] = d, r[34] = zY) : zY = r[34], RY = S ? "This console output is stale" : void 0, BY = "console-output-area", VY = c, HY = q, UY = 0;
99725
- let XY2 = S && "marimo-output-stale", ZY = G ? "p-5" : "p-3";
99726
- r[35] !== z || r[36] !== XY2 || r[37] !== ZY ? (WY = cn("console-output-area overflow-hidden rounded-b-lg flex flex-col-reverse w-full gap-1 focus:outline-hidden", XY2, ZY, z), r[35] = z, r[36] = XY2, r[37] = ZY, r[38] = WY) : WY = r[38], r[39] === _ ? GY = r[40] : (GY = _ ? {
99729
+ }), r[31] = QY2, r[32] = RY, r[33] = _, r[34] = BY, r[35] = v, r[36] = f, r[37] = d, r[38] = UY) : UY = r[38], WY = M ? "This console output is stale" : void 0, GY = "console-output-area", KY = c, qY = zY, JY = 0;
99730
+ let $Y2 = M && "marimo-output-stale", eX2 = RY ? "p-5" : "p-3";
99731
+ r[39] !== LY || r[40] !== $Y2 || r[41] !== eX2 ? (YY = cn("console-output-area overflow-hidden rounded-b-lg flex flex-col-reverse w-full gap-1 focus:outline-hidden", $Y2, eX2, LY), r[39] = LY, r[40] = $Y2, r[41] = eX2, r[42] = YY) : YY = r[42], r[43] === _ ? XY = r[44] : (XY = _ ? {
99727
99732
  maxHeight: "none"
99728
- } : void 0, r[39] = _, r[40] = GY), KY = e2.map((e3, r2) => {
99733
+ } : void 0, r[43] = _, r[44] = XY), ZY = e2.map((e3, r2) => {
99729
99734
  if (e3.channel === "pdb") return null;
99730
99735
  if (e3.channel === "stdin") {
99731
99736
  invariant(typeof e3.data == "string", "Expected data to be a string");
99732
- let c2 = y.length - r2 - 1, d2 = e3.mimetype === "text/password";
99733
- return e3.response == null && LY2 === r2 ? (0, import_jsx_runtime.jsx)(StdInput, {
99737
+ let c2 = O.length - r2 - 1, d2 = e3.mimetype === "text/password";
99738
+ return e3.response == null && I2 === r2 ? (0, import_jsx_runtime.jsx)(StdInput, {
99734
99739
  output: e3.data,
99735
99740
  isPdb: w2,
99736
99741
  isPassword: d2,
99737
- onSubmit: (e4) => O(e4, c2),
99738
- onClear: M
99742
+ onSubmit: (e4) => G(e4, c2),
99743
+ onClear: q,
99744
+ value: y,
99745
+ setValue: S,
99746
+ inputHistory: E
99739
99747
  }, r2) : (0, import_jsx_runtime.jsx)(StdInputWithResponse, {
99740
99748
  output: e3.data,
99741
99749
  response: e3.response,
@@ -99744,94 +99752,91 @@ ${c}
99744
99752
  }
99745
99753
  return (0, import_jsx_runtime.jsx)(import_react.Fragment, {
99746
99754
  children: (0, import_jsx_runtime.jsx)(OutputRenderer, {
99747
- cellId: E,
99748
- onRefactorWithAI: I,
99755
+ cellId: z,
99756
+ onRefactorWithAI: IY,
99749
99757
  message: e3,
99750
99758
  wrapText: d
99751
99759
  })
99752
99760
  }, r2);
99753
- }), r[1] = E, r[2] = z, r[3] = y, r[4] = G, r[5] = _, r[6] = IY, r[7] = M, r[8] = I, r[9] = O, r[10] = q, r[11] = v, r[12] = f, r[13] = S, r[14] = d, r[15] = RY, r[16] = zY, r[17] = BY, r[18] = VY, r[19] = HY, r[20] = UY, r[21] = WY, r[22] = GY, r[23] = KY, r[24] = qY;
99754
- } else RY = r[15], zY = r[16], BY = r[17], VY = r[18], HY = r[19], UY = r[20], WY = r[21], GY = r[22], KY = r[23], qY = r[24];
99755
- let JY;
99756
- r[41] !== E || r[42] !== w ? (JY = (0, import_jsx_runtime.jsx)(NameCellContentEditable, {
99757
- value: w,
99758
- cellId: E,
99761
+ }), r[3] = z, r[4] = LY, r[5] = O, r[6] = RY, r[7] = E, r[8] = _, r[9] = BY, r[10] = q, r[11] = IY, r[12] = G, r[13] = zY, r[14] = v, r[15] = f, r[16] = M, r[17] = y, r[18] = d, r[19] = HY, r[20] = UY, r[21] = WY, r[22] = GY, r[23] = KY, r[24] = qY, r[25] = JY, r[26] = YY, r[27] = XY, r[28] = ZY;
99762
+ } else HY = r[19], UY = r[20], WY = r[21], GY = r[22], KY = r[23], qY = r[24], JY = r[25], YY = r[26], XY = r[27], ZY = r[28];
99763
+ let QY;
99764
+ r[45] !== z || r[46] !== I ? (QY = (0, import_jsx_runtime.jsx)(NameCellContentEditable, {
99765
+ value: I,
99766
+ cellId: z,
99759
99767
  className: "bg-(--slate-4) border-(--slate-4) hover:bg-(--slate-5) dark:border-(--sky-5) dark:bg-(--sky-6) dark:text-(--sky-12) text-(--slate-12) rounded-l rounded-br-lg absolute right-0 bottom-0 text-xs px-1.5 py-0.5 font-mono max-w-[75%] whitespace-nowrap overflow-hidden"
99760
- }), r[41] = E, r[42] = w, r[43] = JY) : JY = r[43];
99761
- let YY;
99762
- r[44] !== RY || r[45] !== JY || r[46] !== BY || r[47] !== VY || r[48] !== HY || r[49] !== UY || r[50] !== WY || r[51] !== GY || r[52] !== KY ? (YY = (0, import_jsx_runtime.jsxs)("div", {
99763
- title: RY,
99764
- "data-testid": BY,
99765
- ref: VY,
99766
- ...HY,
99767
- tabIndex: UY,
99768
- className: WY,
99769
- style: GY,
99768
+ }), r[45] = z, r[46] = I, r[47] = QY) : QY = r[47];
99769
+ let $Y;
99770
+ r[48] !== QY || r[49] !== WY || r[50] !== GY || r[51] !== KY || r[52] !== qY || r[53] !== JY || r[54] !== YY || r[55] !== XY || r[56] !== ZY ? ($Y = (0, import_jsx_runtime.jsxs)("div", {
99771
+ title: WY,
99772
+ "data-testid": GY,
99773
+ ref: KY,
99774
+ ...qY,
99775
+ tabIndex: JY,
99776
+ className: YY,
99777
+ style: XY,
99770
99778
  children: [
99771
- KY,
99772
- JY
99779
+ ZY,
99780
+ QY
99773
99781
  ]
99774
- }), r[44] = RY, r[45] = JY, r[46] = BY, r[47] = VY, r[48] = HY, r[49] = UY, r[50] = WY, r[51] = GY, r[52] = KY, r[53] = YY) : YY = r[53];
99775
- let XY;
99776
- return r[54] !== zY || r[55] !== YY || r[56] !== qY ? (XY = (0, import_jsx_runtime.jsxs)("div", {
99777
- className: qY,
99782
+ }), r[48] = QY, r[49] = WY, r[50] = GY, r[51] = KY, r[52] = qY, r[53] = JY, r[54] = YY, r[55] = XY, r[56] = ZY, r[57] = $Y) : $Y = r[57];
99783
+ let eX;
99784
+ return r[58] !== HY || r[59] !== UY || r[60] !== $Y ? (eX = (0, import_jsx_runtime.jsxs)("div", {
99785
+ className: HY,
99778
99786
  children: [
99779
- zY,
99780
- YY
99787
+ UY,
99788
+ $Y
99781
99789
  ]
99782
- }), r[54] = zY, r[55] = YY, r[56] = qY, r[57] = XY) : XY = r[57], XY;
99790
+ }), r[58] = HY, r[59] = UY, r[60] = $Y, r[61] = eX) : eX = r[61], eX;
99783
99791
  }, StdInput = (e) => {
99784
- let r = (0, import_compiler_runtime$4.c)(24), [c, d] = import_react.useState(""), f;
99785
- r[0] === c ? f = r[1] : (f = {
99786
- value: c,
99787
- setValue: d
99788
- }, r[0] = c, r[1] = f);
99789
- let { navigateUp: _, navigateDown: v, addToHistory: y } = useInputHistory(f), S;
99790
- r[2] === e.output ? S = r[3] : (S = renderText(e.output), r[2] = e.output, r[3] = S);
99791
- let w = e.isPassword ? "password" : "text", E, O;
99792
- r[4] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel") ? (E = (e2) => d(e2.target.value), O = (0, import_jsx_runtime.jsx)(ChevronRight, {
99792
+ let r = (0, import_compiler_runtime$4.c)(25), { value: c, setValue: d, inputHistory: f, output: _, isPassword: v, isPdb: y, onSubmit: S, onClear: w } = e, { navigateUp: E, navigateDown: O, addToHistory: M } = f, I;
99793
+ r[0] === _ ? I = r[1] : (I = renderText(_), r[0] = _, r[1] = I);
99794
+ let z = v ? "password" : "text", G;
99795
+ r[2] === d ? G = r[3] : (G = (e2) => d(e2.target.value), r[2] = d, r[3] = G);
99796
+ let q;
99797
+ r[4] === /* @__PURE__ */ Symbol.for("react.memo_cache_sentinel") ? (q = (0, import_jsx_runtime.jsx)(ChevronRight, {
99793
99798
  className: "w-5 h-5"
99794
- }), r[4] = E, r[5] = O) : (E = r[4], O = r[5]);
99795
- let M;
99796
- r[6] !== y || r[7] !== v || r[8] !== _ || r[9] !== e || r[10] !== c ? (M = (r2) => {
99797
- if (r2.key === "ArrowUp") {
99798
- _(), r2.preventDefault();
99799
+ }), r[4] = q) : q = r[4];
99800
+ let IY;
99801
+ r[5] !== M || r[6] !== O || r[7] !== E || r[8] !== S || r[9] !== d || r[10] !== c ? (IY = (e2) => {
99802
+ if (e2.key === "ArrowUp") {
99803
+ E(), e2.preventDefault();
99799
99804
  return;
99800
99805
  }
99801
- if (r2.key === "ArrowDown") {
99802
- v(), r2.preventDefault();
99806
+ if (e2.key === "ArrowDown") {
99807
+ O(), e2.preventDefault();
99803
99808
  return;
99804
99809
  }
99805
- r2.key === "Enter" && !r2.shiftKey && (c && (y(c), e.onSubmit(c), d("")), r2.preventDefault(), r2.stopPropagation()), r2.key === "Enter" && r2.metaKey && (r2.preventDefault(), r2.stopPropagation());
99806
- }, r[6] = y, r[7] = v, r[8] = _, r[9] = e, r[10] = c, r[11] = M) : M = r[11];
99807
- let I;
99808
- r[12] !== w || r[13] !== M || r[14] !== c ? (I = (0, import_jsx_runtime.jsx)(Input, {
99810
+ e2.key === "Enter" && !e2.shiftKey && (c && (M(c), S(c), d("")), e2.preventDefault(), e2.stopPropagation()), e2.key === "Enter" && e2.metaKey && (e2.preventDefault(), e2.stopPropagation());
99811
+ }, r[5] = M, r[6] = O, r[7] = E, r[8] = S, r[9] = d, r[10] = c, r[11] = IY) : IY = r[11];
99812
+ let LY;
99813
+ r[12] !== z || r[13] !== G || r[14] !== IY || r[15] !== c ? (LY = (0, import_jsx_runtime.jsx)(Input, {
99809
99814
  "data-testid": "console-input",
99810
99815
  "data-stdin-blocking": true,
99811
- type: w,
99816
+ type: z,
99812
99817
  autoComplete: "off",
99813
99818
  autoFocus: true,
99814
99819
  value: c,
99815
- onChange: E,
99816
- icon: O,
99820
+ onChange: G,
99821
+ icon: q,
99817
99822
  className: "m-0 h-8 focus-visible:shadow-xs-solid",
99818
99823
  placeholder: "stdin",
99819
- onKeyDownCapture: M
99820
- }), r[12] = w, r[13] = M, r[14] = c, r[15] = I) : I = r[15];
99821
- let z;
99822
- r[16] !== e.isPdb || r[17] !== e.onClear || r[18] !== e.onSubmit ? (z = e.isPdb && (0, import_jsx_runtime.jsx)(DebuggerControls, {
99823
- onSubmit: e.onSubmit,
99824
- onClear: e.onClear
99825
- }), r[16] = e.isPdb, r[17] = e.onClear, r[18] = e.onSubmit, r[19] = z) : z = r[19];
99826
- let G;
99827
- return r[20] !== S || r[21] !== I || r[22] !== z ? (G = (0, import_jsx_runtime.jsxs)("div", {
99824
+ onKeyDownCapture: IY
99825
+ }), r[12] = z, r[13] = G, r[14] = IY, r[15] = c, r[16] = LY) : LY = r[16];
99826
+ let RY;
99827
+ r[17] !== y || r[18] !== w || r[19] !== S ? (RY = y && (0, import_jsx_runtime.jsx)(DebuggerControls, {
99828
+ onSubmit: S,
99829
+ onClear: w
99830
+ }), r[17] = y, r[18] = w, r[19] = S, r[20] = RY) : RY = r[20];
99831
+ let zY;
99832
+ return r[21] !== I || r[22] !== LY || r[23] !== RY ? (zY = (0, import_jsx_runtime.jsxs)("div", {
99828
99833
  className: "flex gap-2 items-center pt-2",
99829
99834
  children: [
99830
- S,
99831
99835
  I,
99832
- z
99836
+ LY,
99837
+ RY
99833
99838
  ]
99834
- }), r[20] = S, r[21] = I, r[22] = z, r[23] = G) : G = r[23], G;
99839
+ }), r[21] = I, r[22] = LY, r[23] = RY, r[24] = zY) : zY = r[24], zY;
99835
99840
  }, StdInputWithResponse = (e) => {
99836
99841
  let r = (0, import_compiler_runtime$4.c)(8), c;
99837
99842
  r[0] === e.output ? c = r[1] : (c = renderText(e.output), r[0] = e.output, r[1] = c);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marimo-team/islands",
3
- "version": "0.20.3-dev89",
3
+ "version": "0.20.3-dev91",
4
4
  "main": "dist/main.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -18,7 +18,10 @@ import { isInternalCellName } from "@/core/cells/names";
18
18
  import { useExpandedConsoleOutput } from "@/core/cells/outputs";
19
19
  import type { WithResponse } from "@/core/cells/types";
20
20
  import type { OutputMessage } from "@/core/kernel/messages";
21
- import { useInputHistory } from "@/hooks/useInputHistory";
21
+ import {
22
+ type UseInputHistoryReturn,
23
+ useInputHistory,
24
+ } from "@/hooks/useInputHistory";
22
25
  import { useOverflowDetection } from "@/hooks/useOverflowDetection";
23
26
  import { useSelectAllContent } from "@/hooks/useSelectAllContent";
24
27
  import { cn } from "@/utils/cn";
@@ -54,6 +57,11 @@ const ConsoleOutputInternal = (props: Props): React.ReactNode => {
54
57
  const ref = React.useRef<HTMLDivElement>(null);
55
58
  const { wrapText, setWrapText } = useWrapText();
56
59
  const [isExpanded, setIsExpanded] = useExpandedConsoleOutput(props.cellId);
60
+ const [stdinValue, setStdinValue] = React.useState("");
61
+ const inputHistory = useInputHistory({
62
+ value: stdinValue,
63
+ setValue: setStdinValue,
64
+ });
57
65
  const {
58
66
  consoleOutputs,
59
67
  stale,
@@ -210,6 +218,9 @@ const ConsoleOutputInternal = (props: Props): React.ReactNode => {
210
218
  isPassword={isPassword}
211
219
  onSubmit={(text) => onSubmitDebugger(text, originalIdx)}
212
220
  onClear={onClear}
221
+ value={stdinValue}
222
+ setValue={setStdinValue}
223
+ inputHistory={inputHistory}
213
224
  />
214
225
  );
215
226
  }
@@ -249,25 +260,32 @@ const StdInput = (props: {
249
260
  onSubmit: (text: string) => void;
250
261
  onClear?: () => void;
251
262
  output: string;
252
- response?: string;
253
263
  isPdb: boolean;
254
264
  isPassword?: boolean;
265
+ value: string;
266
+ setValue: (value: string) => void;
267
+ inputHistory: UseInputHistoryReturn;
255
268
  }) => {
256
- const [value, setValue] = React.useState("");
257
-
258
- const { navigateUp, navigateDown, addToHistory } = useInputHistory({
269
+ const {
259
270
  value,
260
271
  setValue,
261
- });
272
+ inputHistory,
273
+ output,
274
+ isPassword,
275
+ isPdb,
276
+ onSubmit,
277
+ onClear,
278
+ } = props;
279
+ const { navigateUp, navigateDown, addToHistory } = inputHistory;
262
280
 
263
281
  return (
264
282
  <div className="flex gap-2 items-center pt-2">
265
- {renderText(props.output)}
283
+ {renderText(output)}
266
284
  <Input
267
285
  data-testid="console-input"
268
286
  // This is used in <StdinBlockingAlert> to find the input
269
287
  data-stdin-blocking={true}
270
- type={props.isPassword ? "password" : "text"}
288
+ type={isPassword ? "password" : "text"}
271
289
  autoComplete="off"
272
290
  autoFocus={true}
273
291
  value={value}
@@ -292,7 +310,7 @@ const StdInput = (props: {
292
310
  if (e.key === "Enter" && !e.shiftKey) {
293
311
  if (value) {
294
312
  addToHistory(value);
295
- props.onSubmit(value);
313
+ onSubmit(value);
296
314
  setValue("");
297
315
  }
298
316
  e.preventDefault();
@@ -306,9 +324,7 @@ const StdInput = (props: {
306
324
  }
307
325
  }}
308
326
  />
309
- {props.isPdb && (
310
- <DebuggerControls onSubmit={props.onSubmit} onClear={props.onClear} />
311
- )}
327
+ {isPdb && <DebuggerControls onSubmit={onSubmit} onClear={onClear} />}
312
328
  </div>
313
329
  );
314
330
  };
@@ -1,9 +1,10 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
- import { render, screen } from "@testing-library/react";
4
- import { describe, expect, it } from "vitest";
3
+ import { fireEvent, render, screen } from "@testing-library/react";
4
+ import { describe, expect, it, vi } from "vitest";
5
5
  import { TooltipProvider } from "@/components/ui/tooltip";
6
6
  import type { CellId } from "@/core/cells/ids";
7
+ import type { WithResponse } from "@/core/cells/types";
7
8
  import type { OutputMessage } from "@/core/kernel/messages";
8
9
  import { ConsoleOutput } from "../ConsoleOutput";
9
10
 
@@ -20,6 +21,10 @@ global.ResizeObserver = class ResizeObserver {
20
21
  }
21
22
  };
22
23
 
24
+ const renderWithProvider = (ui: React.ReactElement) => {
25
+ return render(<TooltipProvider>{ui}</TooltipProvider>);
26
+ };
27
+
23
28
  describe("ConsoleOutput integration", () => {
24
29
  const createOutput = (data: string, channel = "stdout"): OutputMessage => ({
25
30
  channel: channel as "stdout" | "stderr",
@@ -31,7 +36,7 @@ describe("ConsoleOutput integration", () => {
31
36
  const defaultProps = {
32
37
  cellId: "cell-1" as CellId,
33
38
  cellName: "test_cell",
34
- consoleOutputs: [],
39
+ consoleOutputs: [] as WithResponse<OutputMessage>[],
35
40
  stale: false,
36
41
  debuggerActive: false,
37
42
  onSubmitDebugger: () => {
@@ -50,14 +55,151 @@ describe("ConsoleOutput integration", () => {
50
55
  ],
51
56
  };
52
57
 
53
- render(
54
- <TooltipProvider>
55
- <ConsoleOutput {...props} />
56
- </TooltipProvider>,
57
- );
58
+ renderWithProvider(<ConsoleOutput {...props} />);
58
59
 
59
60
  const link = screen.getByRole("link", { name: "https://marimo.io" });
60
61
  expect(link).toBeInTheDocument();
61
62
  expect(link).toHaveAttribute("href", "https://marimo.io");
62
63
  });
63
64
  });
65
+
66
+ describe("ConsoleOutput pdb history", () => {
67
+ const defaultProps = {
68
+ cellId: "cell-1" as CellId,
69
+ cellName: "test_cell",
70
+ consoleOutputs: [] as WithResponse<OutputMessage>[],
71
+ stale: false,
72
+ debuggerActive: false,
73
+ onSubmitDebugger: vi.fn(),
74
+ };
75
+
76
+ const stdinPrompt = (
77
+ data: string,
78
+ response?: string,
79
+ ): WithResponse<OutputMessage> => ({
80
+ channel: "stdin" as const,
81
+ mimetype: "text/plain",
82
+ data,
83
+ timestamp: 0,
84
+ response,
85
+ });
86
+
87
+ it("should persist command history across StdInput remounts", () => {
88
+ // Initial state: pdb prompt waiting for input
89
+ const outputs1: WithResponse<OutputMessage>[] = [stdinPrompt("(Pdb) ")];
90
+
91
+ const onSubmitDebugger = vi.fn();
92
+ const { rerender } = renderWithProvider(
93
+ <ConsoleOutput
94
+ {...defaultProps}
95
+ consoleOutputs={outputs1}
96
+ onSubmitDebugger={onSubmitDebugger}
97
+ />,
98
+ );
99
+
100
+ const input = screen.getByTestId("console-input");
101
+
102
+ // Type "next" and submit
103
+ fireEvent.change(input, { target: { value: "next" } });
104
+ fireEvent.keyDown(input, { key: "Enter" });
105
+
106
+ expect(onSubmitDebugger).toHaveBeenCalledWith("next", 0);
107
+
108
+ // Simulate server response: old stdin gets a response, new stdin prompt appears
109
+ const outputs2: WithResponse<OutputMessage>[] = [
110
+ stdinPrompt("(Pdb) ", "next"),
111
+ stdinPrompt("(Pdb) "),
112
+ ];
113
+
114
+ rerender(
115
+ <TooltipProvider>
116
+ <ConsoleOutput
117
+ {...defaultProps}
118
+ consoleOutputs={outputs2}
119
+ onSubmitDebugger={onSubmitDebugger}
120
+ />
121
+ </TooltipProvider>,
122
+ );
123
+
124
+ // New StdInput mounted — press ArrowUp to recall previous command
125
+ const newInput = screen.getByTestId("console-input");
126
+ fireEvent.keyDown(newInput, { key: "ArrowUp" });
127
+
128
+ expect(newInput).toHaveValue("next");
129
+ });
130
+
131
+ it("should navigate through multiple history entries across remounts", () => {
132
+ const onSubmitDebugger = vi.fn();
133
+
134
+ // First prompt
135
+ const outputs1: WithResponse<OutputMessage>[] = [stdinPrompt("(Pdb) ")];
136
+
137
+ const { rerender } = renderWithProvider(
138
+ <ConsoleOutput
139
+ {...defaultProps}
140
+ consoleOutputs={outputs1}
141
+ onSubmitDebugger={onSubmitDebugger}
142
+ />,
143
+ );
144
+
145
+ // Submit "step"
146
+ let input = screen.getByTestId("console-input");
147
+ fireEvent.change(input, { target: { value: "step" } });
148
+ fireEvent.keyDown(input, { key: "Enter" });
149
+
150
+ // Second prompt
151
+ const outputs2: WithResponse<OutputMessage>[] = [
152
+ stdinPrompt("(Pdb) ", "step"),
153
+ stdinPrompt("(Pdb) "),
154
+ ];
155
+
156
+ rerender(
157
+ <TooltipProvider>
158
+ <ConsoleOutput
159
+ {...defaultProps}
160
+ consoleOutputs={outputs2}
161
+ onSubmitDebugger={onSubmitDebugger}
162
+ />
163
+ </TooltipProvider>,
164
+ );
165
+
166
+ // Submit "print(x)"
167
+ input = screen.getByTestId("console-input");
168
+ fireEvent.change(input, { target: { value: "print(x)" } });
169
+ fireEvent.keyDown(input, { key: "Enter" });
170
+
171
+ // Third prompt
172
+ const outputs3: WithResponse<OutputMessage>[] = [
173
+ stdinPrompt("(Pdb) ", "step"),
174
+ stdinPrompt("(Pdb) ", "print(x)"),
175
+ stdinPrompt("(Pdb) "),
176
+ ];
177
+
178
+ rerender(
179
+ <TooltipProvider>
180
+ <ConsoleOutput
181
+ {...defaultProps}
182
+ consoleOutputs={outputs3}
183
+ onSubmitDebugger={onSubmitDebugger}
184
+ />
185
+ </TooltipProvider>,
186
+ );
187
+
188
+ // ArrowUp should show most recent command first
189
+ input = screen.getByTestId("console-input");
190
+ fireEvent.keyDown(input, { key: "ArrowUp" });
191
+ expect(input).toHaveValue("print(x)");
192
+
193
+ // ArrowUp again should show older command
194
+ fireEvent.keyDown(input, { key: "ArrowUp" });
195
+ expect(input).toHaveValue("step");
196
+
197
+ // ArrowDown should go back to "print(x)"
198
+ fireEvent.keyDown(input, { key: "ArrowDown" });
199
+ expect(input).toHaveValue("print(x)");
200
+
201
+ // ArrowDown again should return to empty input
202
+ fireEvent.keyDown(input, { key: "ArrowDown" });
203
+ expect(input).toHaveValue("");
204
+ });
205
+ });
@@ -8,7 +8,7 @@ interface UseInputHistoryOptions {
8
8
  setValue: (value: string) => void;
9
9
  }
10
10
 
11
- interface UseInputHistoryReturn {
11
+ export interface UseInputHistoryReturn {
12
12
  /** Command history array */
13
13
  history: string[];
14
14
  /** Navigate to previous command (ArrowUp) */