@marimo-team/islands 0.23.14-dev39 → 0.23.14-dev45

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 (30) hide show
  1. package/dist/{chat-ui-CO8WuXGb.js → chat-ui-DXPRhRO2.js} +3132 -3132
  2. package/dist/{code-visibility-CqDnWUEQ.js → code-visibility-D-7iipEs.js} +717 -708
  3. package/dist/{html-to-image-DLSOS-bE.js → html-to-image-DOqQBSQc.js} +2236 -2208
  4. package/dist/main.js +998 -987
  5. package/dist/{process-output-B9ysqk1y.js → process-output-kGk2Jc2x.js} +1 -1
  6. package/dist/{reveal-component-BtwLhHQU.js → reveal-component-BrJ-d3iB.js} +2 -2
  7. package/package.json +1 -1
  8. package/src/__mocks__/requests.ts +1 -0
  9. package/src/components/app-config/user-config-form.tsx +26 -0
  10. package/src/components/debugger/debugger-code.tsx +7 -2
  11. package/src/components/editor/code/readonly-python-code.tsx +12 -4
  12. package/src/components/editor/notebook-cell.tsx +7 -0
  13. package/src/components/editor/output/MarimoTracebackOutput.tsx +18 -1
  14. package/src/core/cells/__tests__/cell.test.ts +39 -0
  15. package/src/core/cells/cell.ts +5 -3
  16. package/src/core/codemirror/cells/debugger-decorations.ts +188 -0
  17. package/src/core/codemirror/cells/debugger-state.ts +92 -0
  18. package/src/core/codemirror/cells/extensions.ts +22 -0
  19. package/src/core/config/feature-flag.tsx +2 -0
  20. package/src/core/islands/bootstrap.ts +1 -0
  21. package/src/core/islands/bridge.ts +1 -0
  22. package/src/core/network/requests-lazy.ts +1 -0
  23. package/src/core/network/requests-network.ts +8 -0
  24. package/src/core/network/requests-static.ts +1 -0
  25. package/src/core/network/requests-toasting.tsx +1 -0
  26. package/src/core/network/types.ts +2 -0
  27. package/src/core/wasm/bridge.ts +4 -0
  28. package/src/core/websocket/useMarimoKernelConnection.tsx +9 -0
  29. package/src/plugins/core/RenderHTML.tsx +35 -1
  30. package/src/plugins/core/__test__/RenderHTML.test.ts +44 -0
@@ -49,6 +49,7 @@ export function createErrorToastingRequests(
49
49
  sendListFiles: "Failed to list files",
50
50
  sendSearchFiles: "Failed to search files",
51
51
  sendPdb: "Failed to start debug session",
52
+ sendSetBreakpoints: "", // No toast
52
53
  sendCreateFileOrFolder: "Failed to create file or folder",
53
54
  sendDeleteFileOrFolder: "Failed to delete file or folder",
54
55
  sendCopyFileOrFolder: "Failed to duplicate file or folder",
@@ -66,6 +66,7 @@ export type ListDataSourceConnectionRequest =
66
66
  schemas["ListDataSourceConnectionRequest"];
67
67
  export type ValidateSQLRequest = schemas["ValidateSQLRequest"];
68
68
  export type DebugCellRequest = schemas["DebugCellRequest"];
69
+ export type SetBreakpointsRequest = schemas["SetBreakpointsRequest"];
69
70
  export type ReadCodeResponse = schemas["ReadCodeResponse"];
70
71
  export type RecentFilesResponse = schemas["RecentFilesResponse"];
71
72
  export type RenameNotebookRequest = schemas["RenameNotebookRequest"];
@@ -172,6 +173,7 @@ export interface EditRequests {
172
173
  getUsageStats: () => Promise<UsageResponse>;
173
174
  // Debugger
174
175
  sendPdb: (request: DebugCellRequest) => Promise<null>;
176
+ sendSetBreakpoints: (request: SetBreakpointsRequest) => Promise<null>;
175
177
  // File explorer requests
176
178
  sendListFiles: (request: FileListRequest) => Promise<FileListResponse>;
177
179
  sendSearchFiles: (request: FileSearchRequest) => Promise<FileSearchResponse>;
@@ -257,6 +257,10 @@ export class PyodideBridge implements RunRequests, EditRequests {
257
257
  throwNotImplemented();
258
258
  };
259
259
 
260
+ sendSetBreakpoints: EditRequests["sendSetBreakpoints"] = async () => {
261
+ throwNotImplemented();
262
+ };
263
+
260
264
  sendRun: EditRequests["sendRun"] = async (request) => {
261
265
  await this.rpc.proxy.request.loadPackages(request.codes.join("\n"));
262
266
 
@@ -32,6 +32,7 @@ import { cacheInfoAtom } from "../cache/requests";
32
32
  import { SCRATCH_CELL_ID } from "../cells/ids";
33
33
  import { useRunsActions } from "../cells/runs";
34
34
  import { focusAndScrollCellOutputIntoView } from "../cells/scrollCellIntoView";
35
+ import { debuggerCurrentLineAtom } from "../codemirror/cells/debugger-state";
35
36
  import type { CellData } from "../cells/types";
36
37
  import { capabilitiesAtom } from "../config/capabilities";
37
38
  import { useSetAppConfig } from "../config/config";
@@ -196,6 +197,7 @@ export function useMarimoKernelConnection(opts: {
196
197
  const runtimeManager = useRuntimeManager();
197
198
  const setCacheInfo = useSetAtom(cacheInfoAtom);
198
199
  const setKernelStartupError = useSetAtom(kernelStartupErrorAtom);
200
+ const setDebuggerCurrentLine = useSetAtom(debuggerCurrentLineAtom);
199
201
  const {
200
202
  setNamespaces: setStorageNamespaces,
201
203
  filterFromVariables: filterStorageFromVariables,
@@ -392,6 +394,13 @@ export function useMarimoKernelConnection(opts: {
392
394
  case "focus-cell":
393
395
  focusAndScrollCellOutputIntoView(msg.data.cell_id);
394
396
  return;
397
+ case "active-line":
398
+ setDebuggerCurrentLine(
399
+ msg.data.line == null
400
+ ? null
401
+ : { cellId: msg.data.cell_id, line: msg.data.line },
402
+ );
403
+ return;
395
404
  case "notebook-document-transaction":
396
405
  handleDocumentTransaction(msg.data.transaction);
397
406
  return;
@@ -199,6 +199,37 @@ const keyImagesBySrc: TransformFn = (
199
199
  return cloneElement(reactNode, { key: `${src}-${index}` });
200
200
  };
201
201
 
202
+ // Decorator: applies a checked-state-based key to task-list checkbox
203
+ // <input> elements (e.g. GFM `- [ ]` / `- [x]` rendered by
204
+ // pymdownx.tasklist) so they remount when their checked-ness changes.
205
+ //
206
+ // pymdownx.tasklist omits the `checked` HTML attribute entirely for
207
+ // unchecked items (rather than emitting checked="false"). When markdown
208
+ // edits shift which item lands at a given tree position (e.g. deleting
209
+ // completed items above unchecked ones), reusing the same DOM <input>
210
+ // across a checked -> unchecked item is a controlled -> uncontrolled prop
211
+ // transition for React: React stops managing the DOM `checked` property in
212
+ // that transition instead of resetting it, leaving the previous item's
213
+ // checked state visually stuck until a full remount. Baking checked-ness
214
+ // into the key forces a fresh node instead of reusing the stale one.
215
+ const keyTaskListCheckboxesByChecked: TransformFn = (
216
+ reactNode: ReactNode,
217
+ domNode: DOMNode,
218
+ index: number,
219
+ ): JSX.Element | undefined => {
220
+ if (!(domNode instanceof Element) || domNode.name !== "input") {
221
+ return undefined;
222
+ }
223
+ if (domNode.attribs?.type !== "checkbox") {
224
+ return undefined;
225
+ }
226
+ if (!isValidElement(reactNode)) {
227
+ return undefined;
228
+ }
229
+ const checked = "checked" in domNode.attribs;
230
+ return cloneElement(reactNode, { key: `checkbox-${index}-${checked}` });
231
+ };
232
+
202
233
  // Wrap elements with data-marimo-doc attribute in a DocHoverTarget
203
234
  const wrapDocHoverTargets: TransformFn = (
204
235
  reactNode: ReactNode,
@@ -326,7 +357,10 @@ function parseHtml({
326
357
  // and may further wrap/clone it. Used for cross-cutting concerns that
327
358
  // should apply regardless of which (if any) match-and-replace transform
328
359
  // ran above.
329
- const decoratorFunctions: TransformFn[] = [keyImagesBySrc];
360
+ const decoratorFunctions: TransformFn[] = [
361
+ keyImagesBySrc,
362
+ keyTaskListCheckboxesByChecked,
363
+ ];
330
364
 
331
365
  return parse(html, {
332
366
  replace: (domNode: DOMNode, index: number) => {
@@ -114,6 +114,50 @@ describe("parseHtml", () => {
114
114
  expect(result.key).toBe("https://cdn.example.com/b.png-0");
115
115
  });
116
116
 
117
+ test("checked task-list checkbox is keyed by index and checked state", () => {
118
+ const html = '<li><input type="checkbox" checked disabled> item</li>';
119
+ const result = parseHtml({ html }) as React.ReactElement<{
120
+ children: React.ReactElement[];
121
+ }>;
122
+ const checkbox = result.props.children[0];
123
+ expect(checkbox.key).toBe("checkbox-0-true");
124
+ });
125
+
126
+ test("unchecked task-list checkbox is keyed by index and checked state", () => {
127
+ const html = '<li><input type="checkbox" disabled> item</li>';
128
+ const result = parseHtml({ html }) as React.ReactElement<{
129
+ children: React.ReactElement[];
130
+ }>;
131
+ const checkbox = result.props.children[0];
132
+ expect(checkbox.key).toBe("checkbox-0-false");
133
+ });
134
+
135
+ test("task list: item reused at same position gets a new key when checked state flips", () => {
136
+ // Simulates editing markdown so an unchecked item now sits where a
137
+ // checked item used to be (e.g. an earlier checked item was deleted).
138
+ const before = parseHtml({
139
+ html: '<ul><li><input type="checkbox" checked disabled> a</li></ul>',
140
+ }) as React.ReactElement<{ children: React.ReactElement }>;
141
+ const after = parseHtml({
142
+ html: '<ul><li><input type="checkbox" disabled> b</li></ul>',
143
+ }) as React.ReactElement<{ children: React.ReactElement }>;
144
+ const beforeLi = before.props.children as React.ReactElement<{
145
+ children: React.ReactElement[];
146
+ }>;
147
+ const afterLi = after.props.children as React.ReactElement<{
148
+ children: React.ReactElement[];
149
+ }>;
150
+ const beforeKey = beforeLi.props.children[0].key;
151
+ const afterKey = afterLi.props.children[0].key;
152
+ expect(beforeKey).not.toBe(afterKey);
153
+ });
154
+
155
+ test("non-checkbox input is left alone", () => {
156
+ const html = '<input type="text" value="hi">';
157
+ const result = parseHtml({ html }) as React.ReactElement;
158
+ expect(result.key).toBeNull();
159
+ });
160
+
117
161
  test("codehilite with copy button", () => {
118
162
  const html =
119
163
  '<div class="codehilite"><pre><code>console.log("Hello");</code></pre></div>';