@marimo-team/frontend 0.24.1-dev32 → 0.24.1-dev36

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 (79) hide show
  1. package/dist/assets/{CellStatus-DdKYN88d.js → CellStatus-XBYtudpk.js} +1 -1
  2. package/dist/assets/{JsonOutput-htT793Wj.js → JsonOutput-CbpX5Kgt.js} +12 -12
  3. package/dist/assets/MarimoErrorOutput-DX8DBZiw.js +11 -0
  4. package/dist/assets/{RenderHTML-Cu5JtYZJ.js → RenderHTML-DsxkHR2d.js} +1 -1
  5. package/dist/assets/{RunButton-BOnl_Dk8.js → RunButton-DBtIzlFD.js} +1 -1
  6. package/dist/assets/{add-cell-with-ai-Chy4ITIT.js → add-cell-with-ai-BrrI8i5Q.js} +1 -1
  7. package/dist/assets/{add-connection-dialog-DGYhyXo6.js → add-connection-dialog-DYkpW2NI.js} +1 -1
  8. package/dist/assets/{agent-panel--Nt4UmNj.js → agent-panel-CA8lil4S.js} +5 -5
  9. package/dist/assets/{ai-model-dropdown-BUCPZ0ND.js → ai-model-dropdown-Divs-dGW.js} +1 -1
  10. package/dist/assets/{app-config-button-BOxbrv3L.js → app-config-button-BipXNxWD.js} +1 -1
  11. package/dist/assets/{blob-BVQSODqn.js → blob-DCmp9MuM.js} +1 -1
  12. package/dist/assets/{cell-editor-DgOfL9Xv.js → cell-editor-BfrmXiOh.js} +5 -5
  13. package/dist/assets/{cell-link-Bx8FWjns.js → cell-link-CvOBew-Z.js} +1 -1
  14. package/dist/assets/{cells-Biwqg0z6.js → cells-BIt-E5vq.js} +57 -57
  15. package/dist/assets/{chat-display-B2qUbgnh.js → chat-display-B27UEebd.js} +1 -1
  16. package/dist/assets/{chat-panel-gxL2S-HQ.js → chat-panel-C4jJ8gOg.js} +1 -1
  17. package/dist/assets/{chat-ui-BhnwgDpa.js → chat-ui-DJ41RveI.js} +1 -1
  18. package/dist/assets/{column-preview-BaRqZ9P8.js → column-preview-BVzFL_8v.js} +1 -1
  19. package/dist/assets/{command-palette-Cbie8JV9.js → command-palette-SErocd73.js} +1 -1
  20. package/dist/assets/{common-sBRwGAKy.js → common-BGAfLk4T.js} +1 -1
  21. package/dist/assets/{components-DsJAWbBR.js → components-DMNnJpT_.js} +1 -1
  22. package/dist/assets/{data-source-discovery-DCNYJylO.js → data-source-discovery-DdefgaOP.js} +1 -1
  23. package/dist/assets/{datasource-DxCwZdAq.js → datasource-0Urr2OFR.js} +2 -2
  24. package/dist/assets/{dependency-graph-panel-CLOQ_RvL.js → dependency-graph-panel-CQSSUBtY.js} +1 -1
  25. package/dist/assets/{documentation-panel-D9w8PAT9.js → documentation-panel-Mo3B93Dq.js} +1 -1
  26. package/dist/assets/{download-CfUkZlw7.js → download-DYiwRe8_.js} +1 -1
  27. package/dist/assets/{edit-page-DKAg2YW9.js → edit-page-xv_028Km.js} +4 -4
  28. package/dist/assets/{error-entries-DpvSrZSw.js → error-entries-BTA-sOIB.js} +1 -1
  29. package/dist/assets/{error-panel-uRl1_l47.js → error-panel-BMpvsfkK.js} +1 -1
  30. package/dist/assets/{file-explorer-panel-DwA9Lprz.js → file-explorer-panel-BATjfG7j.js} +1 -1
  31. package/dist/assets/{file-icons-DpREEjwW.js → file-icons-7V36_u0o.js} +1 -1
  32. package/dist/assets/{file-name-input-C7AQKuoU.js → file-name-input-BCi-JD7l.js} +1 -1
  33. package/dist/assets/{floating-outline-4I6DvqaR.js → floating-outline-CmgEk2gZ.js} +1 -1
  34. package/dist/assets/{focus-CeTtpVjV.js → focus-agtnrA0w.js} +1 -1
  35. package/dist/assets/{form-B10bgcbQ.js → form-DbRAwKK1.js} +1 -1
  36. package/dist/assets/{form-Cj9BLARR.js → form-sf0A_nV4.js} +1 -1
  37. package/dist/assets/{home-page-BNJihtii.js → home-page-DbqvhmFo.js} +1 -1
  38. package/dist/assets/{html-to-image-ChfBVn8-.js → html-to-image-BvGsu9Nz.js} +1 -1
  39. package/dist/assets/index-BBAof751.css +2 -0
  40. package/dist/assets/{index-DemYxG9z.js → index-CSJwPAAg.js} +5 -5
  41. package/dist/assets/{layout-YPBtS2D6.js → layout-BBB9DQha.js} +3 -3
  42. package/dist/assets/{logs-panel-ClE58_4G.js → logs-panel-CtZ-76hx.js} +1 -1
  43. package/dist/assets/{markdown-renderer-BOS4jZLt.js → markdown-renderer-CP1Up_3K.js} +1 -1
  44. package/dist/assets/{name-cell-input-UMsLssMV.js → name-cell-input-7EVAIGJb.js} +1 -1
  45. package/dist/assets/{outline-panel-DweKocZK.js → outline-panel-BUPKUip_.js} +1 -1
  46. package/dist/assets/{packages-panel-Cbw5J5QD.js → packages-panel-DMfz7YUA.js} +1 -1
  47. package/dist/assets/{panel-accordion-state-CgvX-ElJ.js → panel-accordion-state-Rl5fu2O3.js} +1 -1
  48. package/dist/assets/{panels-DRv39UwA.js → panels-v1_Bo-9b.js} +1 -1
  49. package/dist/assets/{process-output-BMxr8Z3g.js → process-output-CwYvnqai.js} +1 -1
  50. package/dist/assets/{radio-group-YNmbPL7y.js → radio-group-CFoizYY0.js} +1 -1
  51. package/dist/assets/{readonly-code-display-N9tCXdCz.js → readonly-code-display-evFzJqo3.js} +1 -1
  52. package/dist/assets/{readonly-python-code-BHrAhtGV.js → readonly-python-code-DeHfRkyx.js} +1 -1
  53. package/dist/assets/{reveal-component-Cl6cd65E.js → reveal-component-Xpt5ATiU.js} +1 -1
  54. package/dist/assets/{run-page-Phwq7OJt.js → run-page-CyrHzfTi.js} +1 -1
  55. package/dist/assets/{scratchpad-panel-CvihhlAP.js → scratchpad-panel-CwLyjPgK.js} +1 -1
  56. package/dist/assets/{session-panel-BmQ0PDHO.js → session-panel-wwlutzcx.js} +1 -1
  57. package/dist/assets/{skeleton-BsZq3Vmq.js → skeleton-CqtoHThq.js} +1 -1
  58. package/dist/assets/{snippets-panel-cYP0GMUf.js → snippets-panel-CmNQL12H.js} +1 -1
  59. package/dist/assets/{state-DJon00Ew.js → state-D2PXKlv7.js} +1 -1
  60. package/dist/assets/{state-Dc5AjWPq.js → state-D9aD2R5_.js} +1 -1
  61. package/dist/assets/{tracing-dGSNt8Nm.js → tracing-D-Pv6KLc.js} +1 -1
  62. package/dist/assets/{tracing-panel-CQCgx3e4.js → tracing-panel-CE9SGDWn.js} +2 -2
  63. package/dist/assets/{tree-actions-pW_UgP3m.js → tree-actions-B7cSMuSB.js} +1 -1
  64. package/dist/assets/{useCellActionButton-De17Z9R3.js → useCellActionButton-CDwMe7L6.js} +1 -1
  65. package/dist/assets/{useDeleteCell-BgFMPTmg.js → useDeleteCell-C6_GiKMv.js} +1 -1
  66. package/dist/assets/{useDependencyPanelTab-w8VLtsK3.js → useDependencyPanelTab-DL7ebTCt.js} +1 -1
  67. package/dist/assets/{useNotebookActions-BI53NQV3.js → useNotebookActions-DNf7n1OO.js} +1 -1
  68. package/dist/assets/{useRunCells-CAfRBo7h.js → useRunCells-SSVpCuNe.js} +1 -1
  69. package/dist/assets/{useSplitCell-N8Ohs-Pp.js → useSplitCell-ChS6iNjW.js} +1 -1
  70. package/dist/index.html +24 -24
  71. package/package.json +1 -1
  72. package/src/components/data-table/__tests__/columns.test.tsx +166 -2
  73. package/src/components/data-table/columns.tsx +87 -10
  74. package/src/utils/__tests__/url-parser.test.ts +26 -0
  75. package/src/utils/json/__tests__/json-parser.test.ts +33 -0
  76. package/src/utils/json/json-parser.ts +5 -3
  77. package/src/utils/url-parser.ts +4 -3
  78. package/dist/assets/MarimoErrorOutput-Db7coyFA.js +0 -11
  79. package/dist/assets/index-CxZ6uZaz.css +0 -2
@@ -1,9 +1,9 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
3
  import type { Column } from "@tanstack/react-table";
4
- import { fireEvent, render } from "@testing-library/react";
4
+ import { fireEvent, render, waitFor } from "@testing-library/react";
5
5
  import { I18nProvider } from "react-aria";
6
- import { describe, expect, it, test, vi } from "vitest";
6
+ import { beforeAll, describe, expect, it, test, vi } from "vitest";
7
7
  import { TooltipProvider } from "@/components/ui/tooltip";
8
8
  import { Logger } from "@/utils/Logger";
9
9
  import { parseContent } from "@/utils/url-parser";
@@ -809,6 +809,48 @@ describe("renderCellValue with string + edge whitespace", () => {
809
809
  </I18nProvider>,
810
810
  );
811
811
 
812
+ it("closes a long-string popover when its shadow-DOM trigger is clicked again", async () => {
813
+ const value = "This string is long enough to render in a cell popover.";
814
+ const result = renderCellValue({
815
+ column: createMockStringColumn(),
816
+ renderValue: () => value,
817
+ getValue: () => value,
818
+ selectCell: undefined,
819
+ cellStyles: "",
820
+ });
821
+ const host = document.createElement("div");
822
+ document.body.append(host);
823
+ try {
824
+ const shadowRoot = host.attachShadow({ mode: "open" });
825
+ const { unmount } = render(
826
+ <I18nProvider locale="en-US">
827
+ <TooltipProvider>{result}</TooltipProvider>
828
+ </I18nProvider>,
829
+ { container: shadowRoot as unknown as HTMLElement },
830
+ );
831
+ try {
832
+ const trigger =
833
+ shadowRoot.querySelector<HTMLButtonElement>("button[data-state]");
834
+ const getCopyButton = () =>
835
+ document.querySelector('[aria-label="Copy to clipboard"]') ??
836
+ shadowRoot.querySelector('[aria-label="Copy to clipboard"]');
837
+
838
+ expect(trigger).not.toBeNull();
839
+ fireEvent.pointerDown(trigger!);
840
+ fireEvent.click(trigger!);
841
+ await waitFor(() => expect(getCopyButton()).not.toBeNull());
842
+
843
+ fireEvent.pointerDown(trigger!);
844
+ fireEvent.click(trigger!);
845
+ expect(getCopyButton()).toBeNull();
846
+ } finally {
847
+ unmount();
848
+ }
849
+ } finally {
850
+ host.remove();
851
+ }
852
+ });
853
+
812
854
  it("renders edge whitespace markers and still detects the URL in the middle", () => {
813
855
  const mockColumn = createMockStringColumn();
814
856
  const value = " https://example.com ";
@@ -891,6 +933,128 @@ describe("renderCellValue with string + edge whitespace", () => {
891
933
  });
892
934
  });
893
935
 
936
+ describe("renderCellValue with JSON document strings", () => {
937
+ beforeAll(() => {
938
+ // JsonViewer reads the color scheme through matchMedia, which jsdom
939
+ // does not implement.
940
+ Object.defineProperty(window, "matchMedia", {
941
+ writable: true,
942
+ value: vi.fn().mockImplementation((query: string) => ({
943
+ matches: false,
944
+ media: query,
945
+ onchange: null,
946
+ addListener: vi.fn(),
947
+ removeListener: vi.fn(),
948
+ addEventListener: vi.fn(),
949
+ removeEventListener: vi.fn(),
950
+ dispatchEvent: vi.fn(),
951
+ })),
952
+ });
953
+ });
954
+
955
+ const createMockStringColumn = () =>
956
+ ({
957
+ id: "json_data",
958
+ columnDef: {
959
+ meta: {
960
+ dataType: "string" as const,
961
+ dtype: "str",
962
+ },
963
+ },
964
+ getColumnFormatting: () => undefined,
965
+ getColumnWrapping: () => undefined,
966
+ applyColumnFormatting: (value: unknown) => value,
967
+ }) as unknown as Column<unknown>;
968
+
969
+ const renderCell = (value: string) => {
970
+ const result = renderCellValue({
971
+ column: createMockStringColumn(),
972
+ renderValue: () => value,
973
+ getValue: () => value,
974
+ selectCell: undefined,
975
+ cellStyles: "",
976
+ });
977
+ return render(
978
+ <I18nProvider locale="en-US">
979
+ <TooltipProvider>{result}</TooltipProvider>
980
+ </I18nProvider>,
981
+ );
982
+ };
983
+
984
+ it("opens the JSON viewer for a str cell that holds a JSON object", () => {
985
+ const value =
986
+ '{"vendor":"ACME","attachments":[{"name":"inv.pdf","url":"https://example.com/p/AAA"}]}';
987
+ const { container } = renderCell(value);
988
+
989
+ const trigger = container.querySelector("button");
990
+ expect(trigger).toBeTruthy();
991
+ fireEvent.click(trigger!);
992
+
993
+ // Popover content mounts in a portal, so query the document.
994
+ expect(document.querySelector(".marimo-json-output")).toBeTruthy();
995
+ expect(document.body.textContent).toContain('"vendor":"ACME"');
996
+ });
997
+
998
+ it("opens the JSON viewer for a str cell that holds a JSON array", () => {
999
+ const value = '[{"name":"inv.pdf","url":"https://example.com/p/AAA"}]';
1000
+ const { container } = renderCell(value);
1001
+
1002
+ const trigger = container.querySelector("button");
1003
+ expect(trigger).toBeTruthy();
1004
+ fireEvent.click(trigger!);
1005
+
1006
+ expect(document.querySelector(".marimo-json-output")).toBeTruthy();
1007
+ expect(document.body.textContent).toContain('"name":"inv.pdf"');
1008
+ });
1009
+
1010
+ it("opens the JSON viewer when the JSON document has edge whitespace", () => {
1011
+ const { container } = renderCell(' {"a": 1} ');
1012
+
1013
+ const trigger = container.querySelector("button");
1014
+ expect(trigger).toBeTruthy();
1015
+ fireEvent.click(trigger!);
1016
+
1017
+ expect(document.querySelector(".marimo-json-output")).toBeTruthy();
1018
+ });
1019
+
1020
+ it("keeps a Python repr as inline text", () => {
1021
+ const value = "{'vendor': 'ACME'}";
1022
+ const { container } = renderCell(value);
1023
+
1024
+ expect(container.querySelector("button")).toBeNull();
1025
+ expect(container.textContent).toBe(value);
1026
+ });
1027
+
1028
+ it("keeps a truncated JSON document as inline text", () => {
1029
+ const value = '{"vendor":"ACME","attachments":';
1030
+ const { container } = renderCell(value);
1031
+
1032
+ expect(container.querySelector("button")).toBeNull();
1033
+ expect(container.textContent).toBe(value);
1034
+ });
1035
+
1036
+ it("keeps JSON scalar strings as inline text", () => {
1037
+ const value = '"hello"';
1038
+ const { container } = renderCell(value);
1039
+
1040
+ expect(container.querySelector("button")).toBeNull();
1041
+ expect(container.textContent).toBe(value);
1042
+ });
1043
+
1044
+ it("keeps the text popout for long strings that are not JSON", () => {
1045
+ const value =
1046
+ "This long plain sentence exceeds the fifty character popout threshold.";
1047
+ const { container } = renderCell(value);
1048
+
1049
+ const trigger = container.querySelector("button");
1050
+ expect(trigger).toBeTruthy();
1051
+ fireEvent.click(trigger!);
1052
+
1053
+ expect(document.querySelector(".marimo-json-output")).toBeNull();
1054
+ expect(document.body.textContent).toContain(value);
1055
+ });
1056
+ });
1057
+
894
1058
  describe("renderCellValue with boolean values", () => {
895
1059
  const createMockColumn = () =>
896
1060
  ({
@@ -7,12 +7,14 @@ const PopoverClose = PopoverPrimitive.Close;
7
7
 
8
8
  import type { Column, ColumnDef } from "@tanstack/react-table";
9
9
  import { formatDate, isValid } from "date-fns";
10
+ import { useRef } from "react";
10
11
  import { useLocale, useNumberFormatter } from "react-aria";
11
12
  import { WithLocale } from "@/core/i18n/with-locale";
12
13
  import type { DataType } from "@/core/kernel/messages";
13
14
  import type { CalculateTopKRows } from "@/plugins/impl/DataTablePlugin";
14
15
  import { cn } from "@/utils/cn";
15
16
  import { type DateFormat, exactDateTime, getDateFormat } from "@/utils/dates";
17
+ import { Events } from "@/utils/events";
16
18
  import { Logger } from "@/utils/Logger";
17
19
  import { Maps } from "@/utils/maps";
18
20
  import { maxFractionalDigits } from "@/utils/numbers";
@@ -382,9 +384,10 @@ const PopoutColumn = ({
382
384
  rawStringValue,
383
385
  edges,
384
386
  contentClassName,
385
- buttonText,
387
+ scrollable = false,
386
388
  wrapped,
387
389
  children,
390
+ buttonText,
388
391
  }: {
389
392
  cellStyles?: string;
390
393
  selectCell?: () => void;
@@ -393,10 +396,12 @@ const PopoutColumn = ({
393
396
  // still use `rawStringValue`. Middle is sliced from `rawStringValue`.
394
397
  edges?: { leading: string; trailing: string };
395
398
  contentClassName?: string;
396
- buttonText?: string;
399
+ scrollable?: boolean;
397
400
  wrapped?: boolean;
401
+ buttonText?: string;
398
402
  children: React.ReactNode;
399
403
  }) => {
404
+ const triggerRef = useRef<HTMLButtonElement>(null);
400
405
  const hasEdgeWhitespace =
401
406
  edges !== undefined &&
402
407
  (edges.leading.length > 0 || edges.trailing.length > 0);
@@ -412,6 +417,7 @@ const PopoutColumn = ({
412
417
  <EmotionCacheProvider container={null}>
413
418
  <Popover>
414
419
  <PopoverTrigger
420
+ ref={triggerRef}
415
421
  className={cn(cellStyles, "max-w-fit outline-hidden")}
416
422
  onClick={selectCell}
417
423
  onMouseDown={(e) => {
@@ -432,29 +438,77 @@ const PopoutColumn = ({
432
438
  </span>
433
439
  </PopoverTrigger>
434
440
  <PopoverContent
435
- className={contentClassName}
441
+ className={cn(
442
+ contentClassName,
443
+ scrollable && "flex flex-col overflow-hidden",
444
+ )}
436
445
  align="start"
437
446
  alignOffset={10}
447
+ onInteractOutside={(event) => {
448
+ // Radix sees the shadow host as the target. Resolve the original
449
+ // target so pointer-down does not dismiss before click toggles.
450
+ const target = Events.composedTarget(event.detail.originalEvent);
451
+ if (triggerRef.current?.contains(target)) {
452
+ event.preventDefault();
453
+ }
454
+ }}
438
455
  >
439
- <div className="absolute top-2 right-2">
456
+ <div
457
+ className={cn(
458
+ "flex -mt-3 -mr-2",
459
+ scrollable ? "shrink-0 justify-end" : "float-right",
460
+ )}
461
+ >
440
462
  <CopyClipboardIcon
441
463
  value={rawStringValue}
442
- className="w-2.5 h-2.5"
464
+ className="size-3 hover:text-link"
465
+ buttonClassName="flex size-5 items-center justify-center"
443
466
  tooltip={false}
444
467
  />
445
- <PopoverClose>
446
- <Button variant="link" size="xs">
468
+ <PopoverClose asChild={true}>
469
+ <Button
470
+ variant="link"
471
+ size="xs"
472
+ className={cn("size-5 p-0", !buttonText && "ml-1.5 mr-1")}
473
+ aria-label="Close"
474
+ >
447
475
  {buttonText ?? "Close"}
448
476
  </Button>
449
477
  </PopoverClose>
450
478
  </div>
451
- {children}
479
+ {scrollable ? (
480
+ <div className="min-h-0 overflow-auto">{children}</div>
481
+ ) : (
482
+ children
483
+ )}
452
484
  </PopoverContent>
453
485
  </Popover>
454
486
  </EmotionCacheProvider>
455
487
  );
456
488
  };
457
489
 
490
+ /**
491
+ * Parse a string that holds a JSON object or array document.
492
+ * Returns `undefined` for everything else, including JSON scalars,
493
+ * so ordinary text keeps its plain rendering.
494
+ */
495
+ function parseJsonDocument(text: string): unknown {
496
+ const trimmedText = text.trim();
497
+
498
+ const first = trimmedText[0];
499
+ const last = trimmedText[trimmedText.length - 1];
500
+ const isBracketPair =
501
+ (first === "{" && last === "}") || (first === "[" && last === "]");
502
+ if (!isBracketPair) {
503
+ return undefined;
504
+ }
505
+ try {
506
+ return JSON.parse(trimmedText) as unknown;
507
+ } catch {
508
+ return undefined;
509
+ }
510
+ }
511
+
458
512
  function isPrimitiveOrNullish(value: unknown): boolean {
459
513
  if (value == null) {
460
514
  return true;
@@ -639,6 +693,28 @@ export function renderCellValue<TData, TValue>({
639
693
  splitLeadingTrailingWhitespace(stringValue);
640
694
  const hasEdgeWhitespace = leading.length > 0 || trailing.length > 0;
641
695
 
696
+ // A str cell that holds a JSON document opens the same JSON viewer as
697
+ // native list/struct cells.
698
+ const jsonDocument = parseJsonDocument(middle);
699
+ if (jsonDocument !== undefined) {
700
+ return (
701
+ <PopoutColumn
702
+ cellStyles={cellStyles}
703
+ selectCell={selectCell}
704
+ rawStringValue={stringValue}
705
+ edges={{ leading, trailing }}
706
+ wrapped={isWrapped}
707
+ >
708
+ <JsonOutput
709
+ data={jsonDocument}
710
+ format="tree"
711
+ valueTypes="json"
712
+ className="max-h-64"
713
+ />
714
+ </PopoutColumn>
715
+ );
716
+ }
717
+
642
718
  // Parse only the inner content for URL detection so URLDetector doesn't
643
719
  // split on the whitespace padding.
644
720
  const parts = parseContent(hasEdgeWhitespace ? middle : stringValue);
@@ -662,9 +738,10 @@ export function renderCellValue<TData, TValue>({
662
738
  selectCell={selectCell}
663
739
  rawStringValue={stringValue}
664
740
  edges={{ leading, trailing }}
665
- contentClassName="max-h-64 overflow-auto whitespace-pre-wrap wrap-break-word text-sm w-96"
666
- buttonText="X"
741
+ contentClassName="max-h-64 whitespace-pre-wrap wrap-break-word text-sm w-96"
742
+ scrollable={true}
667
743
  wrapped={isWrapped}
744
+ buttonText="X"
668
745
  >
669
746
  <MarkdownUrlDetector
670
747
  content={stringValue}
@@ -98,4 +98,30 @@ describe("parseContent", () => {
98
98
  { type: "url", url: "https://github.com" },
99
99
  ]);
100
100
  });
101
+
102
+ it("stops at JSON delimiters instead of swallowing them (#10567)", () => {
103
+ const parts = parseContent(
104
+ '[{"name":"inv.pdf","url":"https://example.com/p/AAA"}]',
105
+ );
106
+ expect(parts).toEqual([
107
+ { type: "text", value: '[{"name":"inv.pdf","url":"' },
108
+ { type: "url", url: "https://example.com/p/AAA" },
109
+ { type: "text", value: '"}]' },
110
+ ]);
111
+ });
112
+
113
+ it("does not include a trailing double-quote in the href", () => {
114
+ const parts = parseContent('"https://example.com/x"');
115
+ expect(parts).toContainEqual({
116
+ type: "url",
117
+ url: "https://example.com/x",
118
+ });
119
+ });
120
+
121
+ it("keeps apostrophes, which are valid in URLs", () => {
122
+ const parts = parseContent("https://example.com/O'Reilly");
123
+ expect(parts).toEqual([
124
+ { type: "url", url: "https://example.com/O'Reilly" },
125
+ ]);
126
+ });
101
127
  });
@@ -0,0 +1,33 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+ import { describe, expect, it } from "vitest";
3
+ import { jsonToMarkdown } from "../json-parser";
4
+
5
+ describe("jsonToMarkdown URL linkification", () => {
6
+ it("links a plain URL", () => {
7
+ const md = jsonToMarkdown([{ link: "https://marimo.io/path" }]);
8
+ expect(md).toContain("[https://marimo.io/path](https://marimo.io/path)");
9
+ });
10
+
11
+ it("stops the link at the JSON delimiter, not the trailing quote (#10567)", () => {
12
+ const md = jsonToMarkdown([
13
+ { attachments: '[{"url":"https://example.com/p/AAA"}]' },
14
+ ]);
15
+ // The link target ends exactly at the URL; the closing `"}]` is left as
16
+ // text rather than pulled into the href.
17
+ expect(md).toContain("(https://example.com/p/AAA)");
18
+ expect(md).not.toContain('https://example.com/p/AAA"');
19
+ });
20
+
21
+ it("does not swallow a trailing double-quote into the link", () => {
22
+ const md = jsonToMarkdown([{ v: '"https://example.com/x"' }]);
23
+ expect(md).toContain("(https://example.com/x)");
24
+ expect(md).not.toContain('https://example.com/x"');
25
+ });
26
+
27
+ it("leaves existing markdown links untouched", () => {
28
+ const md = jsonToMarkdown([{ v: "[docs](https://marimo.io/docs)" }]);
29
+ expect(md).toContain("[docs](https://marimo.io/docs)");
30
+ // Must not double-wrap into [[docs](url)](url).
31
+ expect(md).not.toContain("[[docs]");
32
+ });
33
+ });
@@ -1,7 +1,10 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
+ import { URL_REGEX } from "../url-parser";
3
4
  import type { JsonString } from "./base64";
4
5
 
6
+ const GLOBAL_URL_REGEX = new RegExp(URL_REGEX.source, "g");
7
+
5
8
  declare global {
6
9
  interface BigInt {
7
10
  toJSON(): unknown;
@@ -126,9 +129,8 @@ function formatValueForMarkdown(value: unknown): string {
126
129
  return placeholder;
127
130
  });
128
131
 
129
- // Convert plain URLs to markdown links
130
- const urlRegex = /(https?:\/\/\S+)/g;
131
- stringValue = stringValue.replaceAll(urlRegex, "[$1]($1)");
132
+ // Convert plain URLs to markdown links using the shared URL matcher.
133
+ stringValue = stringValue.replaceAll(GLOBAL_URL_REGEX, "[$1]($1)");
132
134
 
133
135
  // Restore markdown links from placeholders
134
136
  for (const [placeholder, original] of placeholders) {
@@ -1,6 +1,7 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
- const urlRegex = /(https?:\/\/\S+)/;
3
+ /** Matches HTTP(S) URLs without consuming surrounding delimiters. */
4
+ export const URL_REGEX = /(https?:\/\/[^\s"<>`{}|\\^]+)/;
4
5
  const imageRegex = /\.(png|jpe?g|gif|webp|svg|ico)(\?.*)?$/i;
5
6
  const dataImageRegex = /^data:image\//i;
6
7
  const knownImageDomains = ["avatars.githubusercontent.com"];
@@ -19,9 +20,9 @@ export function parseContent(text: string): ContentPart[] {
19
20
  return [{ type: "image", url: text }];
20
21
  }
21
22
 
22
- const parts = text.split(urlRegex).filter((part) => part !== "");
23
+ const parts = text.split(URL_REGEX).filter((part) => part !== "");
23
24
  return parts.map((part) => {
24
- const isUrl = urlRegex.test(part);
25
+ const isUrl = URL_REGEX.test(part);
25
26
  if (isUrl) {
26
27
  const isImage =
27
28
  imageRegex.test(part) ||
@@ -1,11 +0,0 @@
1
- import{o as le}from"./chunk-C4rtOYze.js";import{d as ae,f as ie,i as Qe,l as oe,n as Re,p as Ve,u as ce}from"./useEvent-GCVIknuR.js";import{t as He}from"./react-C60yClUi.js";import{$n as de,Bn as me,E as Ke,Gi as Ge,Ir as Je,Ki as pe,Kn as Y,aa as Z,it as Xe,jr as Ye,y as Ze}from"./cells-Biwqg0z6.js";import{t as A}from"./compiler-runtime-DnimoHo0.js";import{g as et,p as tt}from"./useEventListener-DpNnLPuE.js";import{t as st}from"./invariant-BUdrueMv.js";import{a as rt,i as nt,x as lt}from"./utils-C8Yt16Y7.js";import{t as ue}from"./cn-DCUzRj2J.js";import{t as at}from"./jsx-runtime-3wZU7vFS.js";import{a as it,i as ot,o as ct,r as dt,t as mt}from"./config-DjP9S1hr.js";import{a as pt,p as ut,r as xt,t as ht}from"./dropdown-menu-DWGvf_Bw.js";import{t as Q}from"./tooltip-C9LWGQ7Y.js";import{r as xe,t as E}from"./button-W__YVea9.js";import{n as ft}from"./strings-DmKKoXrM.js";import{t as he}from"./createLucideIcon-B8oVfJNp.js";import{t as gt}from"./check-CyyJPf_5.js";import{r as fe}from"./x-4y-nyVDU.js";import{t as jt}from"./chevron-right-C9ZBG6Lx.js";import{t as yt}from"./hat-glasses-xLTnAC_n.js";import{t as bt}from"./package-ROei-DG7.js";import{t as vt}from"./sparkles-DwqcWrJl.js";import{l as ge,r as Nt}from"./datasource-DxCwZdAq.js";import{a as It}from"./dist-Bm9PT5P9.js";import{t as wt}from"./uuid-e9lSomwA.js";import{t as kt}from"./use-toast-DjpWVza6.js";import{t as $t}from"./RenderHTML-Cu5JtYZJ.js";import{t as je}from"./kbd-PEowR7op.js";import{t as q}from"./links-BeU4kHTI.js";import{r as _t,t as At}from"./alert-CHWQT4zh.js";import{o as Tt,t as Ft}from"./useInstallPackage-D58nBaNf.js";import{n as St,o as Ct}from"./state-DJon00Ew.js";import{n as $}from"./cell-link-Bx8FWjns.js";var L=he("square-arrow-out-up-right",[["path",{d:"M21 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h6",key:"y09zxi"}],["path",{d:"m21 3-9 9",key:"mpx6sq"}],["path",{d:"M15 3h6v6",key:"1q9fwt"}]]),ye=he("terminal",[["path",{d:"M12 19h8",key:"baeox8"}],["path",{d:"m4 17 6-6-6-6",key:"1yngyt"}]]),be=String.raw`_cell_([^\W_][\w-]*?)(_\w*)`,ve=String.raw`(?<!_)${be}`;`${be}`;var Mt=new RegExp(ve),ee=new RegExp(ve,"g");function Ne(t){let e=[];ee.lastIndex=0;let n=0,r=ee.exec(t);for(;r!==null;)r.index>n&&e.push(t.slice(n,r.index)),e.push({cellId:r[1],name:r[2]}),n=r.index+r[0].length,r=ee.exec(t);return n<t.length&&e.push(t.slice(n)),e}function Et(t){return Mt.test(t)}function Pt(t){let e=null,n=t.cursor();do n.name==="ExpressionStatement"&&(e=n.node);while(n.next());return e}function zt(t){let e=t.split(`
2
- `),n=" ",r=Pt(It.parse(t));if(!r)return["def _():",...te(e,n),`${n}return`,"","","_()"].join(`
3
- `);let a=t.slice(0,r.from).trim(),l=t.slice(r.from).trim(),o=a.split(`
4
- `),i=l.split(`
5
- `);return["def _():",...te(o,n),`${n}return ${i[0]}`,...te(i.slice(1),n),"","","_()"].join(`
6
- `)}function te(t,e){if(t.length===1&&t[0]==="")return[];let n=[];for(let r of t)r===""?n.push(""):n.push(e+r);return n}function qt(t,e){var n;if(t.type==="multiple-defs")return[{title:"Fix: Wrap in a function",description:"Make this cell's variables local by wrapping the cell in a function.",fixType:"manual",onFix:async r=>{st(r.editor,"Editor is null");let a=zt(r.editor.state.doc.toString());r.editor.dispatch({changes:{from:0,to:r.editor.state.doc.length,insert:a}})}}];if(t.type==="exception"&&t.exception_type==="NameError"){let r=(n=t.msg.match(/name '(.+)' is not defined/))==null?void 0:n[1];if(!r||!(r in Ie))return[];let a=Lt(r);return[{title:`Fix: Add '${a}'`,description:"Add a new cell for the missing import",fixType:"manual",onFix:async l=>{l.addCodeBelow(a)}}]}return t.type==="sql-error"&&e.aiEnabled?[{title:"Fix with AI",description:"Fix the SQL statement",fixType:"ai",onFix:async r=>{var o;let a=Nt(r.cellId,Qe),l=`Fix the SQL statement: ${t.msg}.`;a&&(l+=`
7
- Database schema: ${a}`),(o=r.aiFix)==null||o.setAiCompletionCell({cellId:r.cellId,initialPrompt:l,triggerImmediately:r.aiFix.triggerFix})}}]:[]}function Lt(t){let e=Ie[t];return e===t?`import ${e}`:`import ${e} as ${t}`}var Ie={mo:"marimo",alt:"altair",bokeh:"bokeh",dask:"dask",np:"numpy",pd:"pandas",pl:"polars",plotly:"plotly",plt:"matplotlib.pyplot",px:"plotly.express",scipy:"scipy",sk:"sklearn",sns:"seaborn",stats:"scipy.stats",tf:"tensorflow",torch:"torch",xr:"xarray",dt:"datetime",json:"json",math:"math",os:"os",re:"re",sys:"sys"},we=1;const R=Z("marimo:acp:sessions:v1",{sessions:[],activeTabId:null},Y),Wt=Ve(t=>{let e=t(R);return e.activeTabId&&e.sessions.find(n=>n.tabId===e.activeTabId)||null},(t,e,n)=>{e(R,r=>({...r,activeTabId:n}))});function Dt(){return`tab_${wt()}`}function ke(t,e=20){return t.length<=e?t:`${t.slice(0,e-3)}...`}function Ut(t,e){let n=$e(e.agentId),r=Date.now(),a=e.firstMessage?ke(e.firstMessage.trim()):`New ${e.agentId} session`,l=Dt();if(n==="single"){let o=t.sessions.filter(c=>c.agentId===e.agentId),i=t.sessions.filter(c=>c.agentId!==e.agentId);if(o.length>0){let c=o[0],d={agentId:e.agentId,title:a,createdAt:r,lastUsedAt:r,tabId:c.tabId,externalAgentSessionId:null,selectedModel:e.model??c.selectedModel??null};return{...t,sessions:[...i.slice(0,we-1),d],activeTabId:d.tabId}}}return{...t,sessions:[...t.sessions.slice(0,we-1),{agentId:e.agentId,tabId:l,title:a,createdAt:r,lastUsedAt:r,externalAgentSessionId:null,selectedModel:e.model??null}],activeTabId:l}}function Bt(t,e){let n=t.sessions.filter(r=>r.tabId!==e);return{sessions:n,activeTabId:t.activeTabId===e?n.length>0?n[n.length-1].tabId:null:t.activeTabId}}function Ot(t,e){let n=t.activeTabId;return n?{...t,sessions:t.sessions.map(r=>r.tabId===n?{...r,title:ke(e)}:r)}:t}function Qt(t,e){return{...t,sessions:t.sessions.map(n=>n.tabId===e?{...n,lastUsedAt:Date.now()}:n)}}function Rt(t,e){let n=t.activeTabId;return n?{...t,sessions:t.sessions.map(r=>r.tabId===n?{...r,externalAgentSessionId:e,lastUsedAt:Date.now()}:r)}:t}function Vt(){return["claude","gemini","codex","opencode","cursor"]}function Ht(t){return ft(t)}function Kt(t){let e=V[t].port,n=window.location.hostname;return`${window.location.protocol==="https:"?"wss:":"ws:"}//${n}:${e}/message`}var V={claude:{port:3017,command:"npx @zed-industries/claude-code-acp",sessionSupport:"single"},gemini:{port:3019,command:"npx @google/gemini-cli --experimental-acp",sessionSupport:"single"},codex:{port:3021,command:"npx @zed-industries/codex-acp",sessionSupport:"single"},opencode:{port:3023,command:"npx opencode-ai acp",sessionSupport:"single"},cursor:{port:3025,command:"agent acp",sessionSupport:"single"}};function $e(t){return V[t].sessionSupport}function Gt(t){let e=V[t].port,n=V[t].command;return`npx stdio-to-ws "${tt()?`cmd /c ${n}`:n}" --port ${e}`}var Jt=A();const _e=Z("marimo:chrome:ai-panel-tab","chat",Y);function Ae(){let t=(0,Jt.c)(3),[e,n]=oe(_e),r;return t[0]!==e||t[1]!==n?(r={aiPanelTab:e,setAiPanelTab:n},t[0]=e,t[1]=n,t[2]=r):r=t[2],r}var Xt=A(),Yt="code_mode";function Zt(t,e){return t||(de("external_agents")?e:"chat")}function Te(){let t=(0,Xt.c)(6),{openApplication:e}=me(),{setAiPanelTab:n}=Ae(),{saveModeChange:r}=mt(),a=ae(Ct),l=ie(),o;return t[0]!==e||t[1]!==r||t[2]!==n||t[3]!==a||t[4]!==l?(o=async i=>{let c=Zt(i.panel,l.get(_e)),d=l.get(rt),p=de("external_agents")&&l.get(R).sessions.length>0;if(!(c==="agents"?p:d)){kt({title:c==="agents"?"No agent session":"AI not configured",description:c==="agents"?"Start an agent session or switch to the chat panel.":"Configure an AI provider and chat model to fix with AI.",variant:"danger"});return}i.panel&&n(i.panel),c==="chat"&&await r(Yt).catch(es),a({prompt:i.prompt,submit:i.submit??!1}),e("ai")},t[0]=e,t[1]=r,t[2]=n,t[3]=a,t[4]=l,t[5]=o):o=t[5],Re(o)}function es(){return et.error("Failed to save mode change")}var ts=A(),ss=Z("marimo:ai-autofix-mode","autofix",Y);function rs(){let t=(0,ts.c)(3),[e,n]=oe(ss),r;return t[0]!==e||t[1]!==n?(r={fixMode:e,setFixMode:n},t[0]=e,t[1]=n,t[2]=r):r=t[2],r}var se=A(),s=le(at(),1);function Fe(t,e){if(t!=null)return`@error://${t}
8
-
9
- Please fix this error.`;let n=e==null?void 0:e.trim();return n?`My code gives the following error. Please fix it:
10
-
11
- ${n}`:"Please fix this error."}const w=t=>{let e=(0,se.c)(25),{errors:n,cellId:r,className:a}=t,l=ie(),{createNewCell:o}=Ke(),i=ce(nt),c;if(e[0]!==i||e[1]!==n){let v;e[3]===i?v=e[4]:(v=k=>qt(k,{aiEnabled:i}),e[3]=i,e[4]=v),c=n.flatMap(v),e[0]=i,e[1]=n,e[2]=c}else c=e[2];let d=c,p=ae(St),u=Te();if(d.length===0)return null;let m=d[0],x;e[5]!==r||e[6]!==o||e[7]!==m||e[8]!==p||e[9]!==l?(x=v=>{var S;let k=(S=l.get(Ze).cellHandles[r].current)==null?void 0:S.editorView;m.onFix({addCodeBelow:P=>{o({cellId:r,autoFocus:!1,before:!1,code:P})},editor:k,cellId:r,aiFix:{setAiCompletionCell:p,triggerFix:v}}),k==null||k.focus()},e[5]=r,e[6]=o,e[7]=m,e[8]=p,e[9]=l,e[10]=x):x=e[10];let g=x,h;e[11]!==r||e[12]!==u?(h=()=>{u({prompt:Fe(r)})},e[11]=r,e[12]=u,e[13]=h):h=e[13];let y=h,j;e[14]===a?j=e[15]:(j=ue("my-2",a),e[14]=a,e[15]=j);let b;e[16]!==m.description||e[17]!==m.fixType||e[18]!==m.title||e[19]!==g||e[20]!==y?(b=m.fixType==="ai"?(0,s.jsx)(Ce,{tooltip:m.description,openPrompt:()=>g(!1),applyAutofix:()=>g(!0),openChat:y}):(0,s.jsx)(Q,{content:m.description,align:"start",children:(0,s.jsxs)(E,{size:"xs",variant:"outline",className:"font-normal",onClick:()=>g(!1),children:[(0,s.jsx)(ge,{className:"h-3 w-3 mr-2"}),m.title]})}),e[16]=m.description,e[17]=m.fixType,e[18]=m.title,e[19]=g,e[20]=y,e[21]=b):b=e[21];let N;return e[22]!==j||e[23]!==b?(N=(0,s.jsx)("div",{className:j,children:b}),e[22]=j,e[23]=b,e[24]=N):N=e[24],N};var Se={autofix:{Icon:ge,title:"Inline AI Fix",description:"Apply AI fixes inline in the cell"},chat:{Icon:yt,title:"Fix with AI assistant",description:"Open the AI sidebar to fix"},prompt:{Icon:vt,title:"Suggest a prompt",description:"Edit the prompt before applying"}},ns=["autofix","prompt","chat"];const Ce=t=>{let e=(0,se.c)(18),{tooltip:n,openPrompt:r,applyAutofix:a,openChat:l}=t,{fixMode:o,setFixMode:i}=rs(),c=r;o==="chat"?c=l:o==="autofix"&&(c=a);let{Icon:d,title:p}=Se[o],u;e[0]===d?u=e[1]:(u=(0,s.jsx)(d,{className:"h-3 w-3 mr-2 mb-0.5"}),e[0]=d,e[1]=u);let m;e[2]!==c||e[3]!==u||e[4]!==p?(m=(0,s.jsxs)(E,{size:"xs",variant:"outline",className:"font-normal rounded-r-none border-r-0",onClick:c,children:[u,p]}),e[2]=c,e[3]=u,e[4]=p,e[5]=m):m=e[5];let x;e[6]!==m||e[7]!==n?(x=(0,s.jsx)(Q,{content:n,align:"start",children:m}),e[6]=m,e[7]=n,e[8]=x):x=e[8];let g;e[9]===Symbol.for("react.memo_cache_sentinel")?(g=(0,s.jsx)(ut,{asChild:!0,children:(0,s.jsx)(E,{size:"xs",variant:"outline",className:"rounded-l-none px-2","aria-label":"Fix options",children:(0,s.jsx)(fe,{className:"h-3 w-3"})})}),e[9]=g):g=e[9];let h;e[10]!==o||e[11]!==i?(h=ns.map(b=>(0,s.jsx)(pt,{className:"flex items-center gap-2",onClick:()=>i(b),children:(0,s.jsx)(ls,{mode:b,selected:b===o})},b)),e[10]=o,e[11]=i,e[12]=h):h=e[12];let y;e[13]===h?y=e[14]:(y=(0,s.jsxs)(ht,{children:[g,(0,s.jsx)(xt,{align:"end",className:"w-56",children:h})]}),e[13]=h,e[14]=y);let j;return e[15]!==x||e[16]!==y?(j=(0,s.jsxs)("div",{className:"flex",children:[x,y]}),e[15]=x,e[16]=y,e[17]=j):j=e[17],j};var ls=t=>{let e=(0,se.c)(15),{mode:n,selected:r}=t,{Icon:a,title:l,description:o}=Se[n],i;e[0]===a?i=e[1]:(i=(0,s.jsx)(a,{className:"h-4 w-4 shrink-0"}),e[0]=a,e[1]=i);let c;e[2]===l?c=e[3]:(c=(0,s.jsx)("span",{className:"font-medium",children:l}),e[2]=l,e[3]=c);let d;e[4]===o?d=e[5]:(d=(0,s.jsx)("span",{className:"text-xs text-muted-foreground",children:o}),e[4]=o,e[5]=d);let p;e[6]!==c||e[7]!==d?(p=(0,s.jsxs)("div",{className:"flex flex-col",children:[c,d]}),e[6]=c,e[7]=d,e[8]=p):p=e[8];let u;e[9]===r?u=e[10]:(u=r&&(0,s.jsx)(gt,{className:"h-4 w-4 ml-auto shrink-0"}),e[9]=r,e[10]=u);let m;return e[11]!==i||e[12]!==p||e[13]!==u?(m=(0,s.jsxs)("div",{className:"flex items-center gap-2 w-full",children:[i,p,u]}),e[11]=i,e[12]=p,e[13]=u,e[14]=m):m=e[14],m},Me=A();const as=t=>{let e=(0,Me.c)(12),{local:n}=t,r;e[0]===n.name?r=e[1]:(r=(0,s.jsx)("span",{className:"font-code",children:n.name}),e[0]=n.name,e[1]=r);let a;e[2]===n.cellId?a=e[3]:(a=(0,s.jsx)($,{cellId:n.cellId}),e[2]=n.cellId,e[3]=a);let l;e[4]!==r||e[5]!==a?(l=(0,s.jsxs)("div",{className:"max-w-xs",children:["Local variable ",r," in cell"," ",a,"."]}),e[4]=r,e[5]=a,e[6]=l):l=e[6];let o=l,i;e[7]===n.name?i=e[8]:(i=(0,s.jsx)("span",{className:"font-code cursor-help",children:n.name}),e[7]=n.name,e[8]=i);let c;return e[9]!==i||e[10]!==o?(c=(0,s.jsx)(Q,{content:o,children:i}),e[9]=i,e[10]=o,e[11]=c):c=e[11],c},Ee=t=>{let e=(0,Me.c)(4),{segments:n}=t,r;e[0]===n?r=e[1]:(r=n.map(is),e[0]=n,e[1]=r);let a;return e[2]===r?a=e[3]:(a=(0,s.jsx)(s.Fragment,{children:r}),e[2]=r,e[3]=a),a};function is(t,e){return typeof t=="string"?(0,s.jsx)("span",{children:t},e):(0,s.jsx)(as,{local:t},e)}var Pe=/(https?:\/\/\S+)/,os=/\.(png|jpe?g|gif|webp|svg|ico)(\?.*)?$/i,ze=/^data:image\//i,cs=["avatars.githubusercontent.com"];function qe(t){return ze.test(t)?[{type:"image",url:t}]:t.split(Pe).filter(e=>e!=="").map(e=>Pe.test(e)?os.test(e)||ze.test(e)||cs.some(n=>e.includes(n))?{type:"image",url:e}:{type:"url",url:e}:{type:"text",value:e})}var Le=A(),W=le(He(),1),ds=new Xe;const re=t=>{let e=RegExp("\x1B\\[[0-9;]*m","g");return t.replaceAll(e,"")};var We=/(pip\s+install|uv\s+add|uv\s+pip\s+install)\s+/gi;function ms(t){We.lastIndex=0;let e=We.exec(t);if(!e)return null;let n=e.index+e[0].length,r=t.slice(n).split(/\s+/),a="",l=0;for(let i of r){let c=i.trim();if(!c)continue;if(c.startsWith("-")){l+=c.length+1;continue}let d=c.match(/^[\w,.[\]-]+/);if(d){a=d[0];break}break}if(!a)return null;let o=n+l+a.length;return{package:a,endIndex:o}}function T(t,e=""){if(!t)return null;if(!/https?:\/\//.test(t))return t;let n=qe(t);return n.length===1&&n[0].type==="text"?t:(0,s.jsx)(s.Fragment,{children:n.map((r,a)=>{let l=e?`${e}-${a}`:a;if(r.type==="url"){let o=re(r.url);return(0,s.jsx)("a",{href:o,target:"_blank",rel:"noopener noreferrer",onClick:xe.stopPropagation(),className:"text-link hover:underline",children:o},l)}if(r.type==="image"){let o=re(r.url);return(0,s.jsx)("a",{href:o,target:"_blank",rel:"noopener noreferrer",onClick:xe.stopPropagation(),className:"text-link hover:underline",children:o},l)}return(0,s.jsx)(W.Fragment,{children:r.value},l)})})}var ps=t=>{let e=(0,Le.c)(6),{packages:n,children:r}=t,{handleInstallPackages:a}=Ft(),l;e[0]!==a||e[1]!==n?(l=i=>{a(n),i.preventDefault()},e[0]=a,e[1]=n,e[2]=l):l=e[2];let o;return e[3]!==r||e[4]!==l?(o=(0,s.jsx)("button",{onClick:l,className:"text-link hover:underline",type:"button",children:r}),e[3]=r,e[4]=l,e[5]=o):o=e[5],o};const us=t=>{if(!(t instanceof pe.Text))return;let e=re(t.data);if(!/(pip\s+install|uv\s+add|uv\s+pip\s+install)/i.test(e))return;let n=[],r=/(pip\s+install|uv\s+add|uv\s+pip\s+install)\s+/gi,a;for(;(a=r.exec(e))!==null;){let i=a.index,c=ms(e.slice(i));c&&n.push({match:a,result:c})}if(n.length===0)return;let l=[],o=0;if(n.forEach((i,c)=>{let{match:d,result:p}=i,u=d.index,m=u+p.endIndex;if(o<u){let g=e.slice(o,u);l.push((0,s.jsx)(W.Fragment,{children:T(g,`before-${c}`)},`before-${c}`))}let x=e.slice(u,m);l.push((0,s.jsx)(ps,{packages:[p.package],children:x},`install-${c}`)),o=m}),o<e.length){let i=e.slice(o);l.push((0,s.jsx)(W.Fragment,{children:T(i,"after")},"after"))}return(0,s.jsx)(s.Fragment,{children:l})},xs=t=>{if(!(t instanceof pe.Text))return;let e=t.data;if(!/https?:\/\//.test(e))return;let n=T(e);if(typeof n!="string")return(0,s.jsx)(s.Fragment,{children:n})},hs=(...t)=>e=>{for(let n of t){let r=n(e);if(r!==void 0)return r}},fs=(t,e)=>(0,s.jsx)("span",{children:Ge(ds.ansi_to_html(t),{replace:n=>e(n)})}),gs=t=>{let e=(0,Le.c)(4),{text:n}=t,r;e[0]===n?r=e[1]:(r=fs(n,hs(us,xs)),e[0]=n,e[1]=r);let a=r,l;return e[2]===a?l=e[3]:(l=(0,s.jsx)(s.Fragment,{children:a}),e[2]=a,e[3]=l),l};var ne=A(),De=t=>{let e=(0,ne.c)(16),{traceback:n}=t,[r,a]=(0,W.useState)(!0),l;e[0]===r?l=e[1]:(l=()=>a(!r),e[0]=r,e[1]=l);let o=r?"Collapse traceback":"Expand traceback",i;e[2]===r?i=e[3]:(i=r?(0,s.jsx)(fe,{className:"h-3 w-3"}):(0,s.jsx)(jt,{className:"h-3 w-3"}),e[2]=r,e[3]=i);let c;e[4]===Symbol.for("react.memo_cache_sentinel")?(c=(0,s.jsx)("span",{className:"text-[0.6875rem] uppercase tracking-wider",children:"Traceback"}),e[4]=c):c=e[4];let d;e[5]!==r||e[6]!==l||e[7]!==o||e[8]!==i?(d=(0,s.jsxs)("button",{type:"button",onClick:l,"aria-expanded":r,"aria-label":o,className:"flex items-center gap-1 text-muted-foreground/70 hover:text-muted-foreground transition-colors",children:[i,c]}),e[5]=r,e[6]=l,e[7]=o,e[8]=i,e[9]=d):d=e[9];let p;e[10]!==r||e[11]!==n?(p=r&&(0,s.jsx)("div",{className:"font-code text-sm mt-1 p-3 bg-muted rounded border overflow-auto max-h-[50vh] cursor-text select-text",children:$t({html:n})}),e[10]=r,e[11]=n,e[12]=p):p=e[12];let u;return e[13]!==d||e[14]!==p?(u=(0,s.jsxs)("div",{className:"mt-2",children:[d,p]}),e[13]=d,e[14]=p,e[15]=u):u=e[15],u},F=t=>{let e=(0,ne.c)(10),n=t.title??"Tip",r;e[0]===n?r=e[1]:(r=(0,s.jsx)(ct,{className:"pt-2 pb-0 font-normal",children:n}),e[0]=n,e[1]=r);let a;e[2]===t.children?a=e[3]:(a=(0,s.jsx)(ot,{className:"mr-24 text-[0.84375rem]",wrapperClassName:"pt-0 pb-2",children:t.children}),e[2]=t.children,e[3]=a);let l;e[4]!==r||e[5]!==a?(l=(0,s.jsxs)(it,{value:"item-1",className:"text-muted-foreground",children:[r,a]}),e[4]=r,e[5]=a,e[6]=l):l=e[6];let o;return e[7]!==t.className||e[8]!==l?(o=(0,s.jsx)(dt,{type:"single",collapsible:!0,className:t.className,children:l}),e[7]=t.className,e[8]=l,e[9]=o):o=e[9],o};const js=t=>{let e=(0,ne.c)(38),{errors:n,cellId:r,className:a}=t,l=me(),o=ce(lt).display.cell_output==="above",i="This cell wasn't run because it has errors",c="destructive",d="text-error",p=null;if(n.some(ys))i="Interrupted";else if(n.some(bs))i="An internal error occurred";else if(n.some(vs))i="Ancestor prevented from running",c="default",d="text-secondary-foreground";else if(n.some(Ns))i="Ancestor stopped",c="default",d="text-secondary-foreground";else if(n.some(Is))i="SQL error";else if(n.some(ws))i=null,p="Cell not run";else{let N;e[0]===n?N=e[1]:(N=n.find(ks),e[0]=n,e[1]=N);let v=N;v&&"exception_type"in v&&(i=v.exception_type)}let u,m,x,g,h,y;if(e[2]!==c||e[3]!==r||e[4]!==l||e[5]!==a||e[6]!==n||e[7]!==o||e[8]!==p||e[9]!==d||e[10]!==i){let N=n.filter($s),v=n.filter(_s),k=n.filter(As),S=n.filter(Ts),P=n.filter(Fs),D=n.filter(Ss),U=n.filter(Cs),H=n.filter(Ms),K=n.filter(Es),G=n.filter(Ps),z=n.filter(zs),J=n.filter(qs),X=n.filter(Ls),B;e[17]===l?B=e[18]:(B=()=>{l.openApplication("scratchpad")},e[17]=l,e[18]=B);let Ue=B,Be=()=>{let I=[];if(z.length>0||J.length>0){let f=z.some(Ws),_=!f&&z.some(Ds);I.push((0,s.jsxs)("div",{children:[z.map(Us),J.map(Bs),f&&(0,s.jsxs)(E,{size:"xs",variant:"outline",className:"mt-2 font-normal",onClick:()=>Tt(l),children:[(0,s.jsx)(bt,{className:"h-3.5 w-3.5 mr-1.5 mb-0.5"}),(0,s.jsx)("span",{children:"Open package manager"})]}),_&&(0,s.jsxs)(E,{size:"xs",variant:"outline",className:"mt-2 font-normal",onClick:()=>l.openApplication("terminal"),children:[(0,s.jsx)(ye,{className:"h-3.5 w-3.5 mr-1.5"}),(0,s.jsx)("span",{children:"Open terminal"})]}),r&&(0,s.jsx)(w,{errors:[...z,...J],cellId:r})]},"syntax-unknown"))}if(N.length>0&&I.push((0,s.jsxs)("div",{children:[(0,s.jsx)("p",{className:"text-muted-foreground font-medium",children:"The setup cell cannot be run because it has references."}),(0,s.jsx)("ul",{className:"list-disc",children:N.flatMap(Os)}),r&&(0,s.jsx)(w,{errors:N,cellId:r}),(0,s.jsxs)(F,{title:"Why can't the setup cell have references?",className:"mb-2",children:[(0,s.jsx)("p",{className:"pb-2",children:"The setup cell contains logic that must be run before any other cell runs, including top-level imports used by top-level functions. For this reason, it can't refer to other cells' variables."}),(0,s.jsx)("p",{className:"py-2",children:"Try simplifying the setup cell to only contain only necessary variables."}),(0,s.jsxs)("p",{className:"py-2",children:[(0,s.jsxs)(q,{href:"https://links.marimo.app/errors-setup",children:["Learn more at our docs"," ",(0,s.jsx)(L,{size:"0.75rem",className:"inline"})]}),"."]})]})]},"setup-refs")),v.length>0&&I.push((0,s.jsxs)("div",{children:[(0,s.jsx)("p",{className:"text-muted-foreground font-medium",children:"This cell is in a cycle."}),(0,s.jsx)("ul",{className:"list-disc",children:v.flatMap(Qs)}),r&&(0,s.jsx)(w,{errors:v,cellId:r}),(0,s.jsxs)(F,{title:"What are cycles and how do I resolve them?",className:"mb-2",children:[(0,s.jsx)("p",{className:"pb-2",children:"An example of a cycle is if one cell declares a variable 'a' and reads 'b', and another cell declares 'b' and and reads 'a'. Cycles like this make it impossible for marimo to know how to run your cells, and generally suggest that your code has a bug."}),(0,s.jsx)("p",{className:"py-2",children:"Try merging these cells into a single cell to eliminate the cycle."}),(0,s.jsxs)("p",{className:"py-2",children:[(0,s.jsxs)(q,{href:"https://links.marimo.app/errors-cycles",children:["Learn more at our docs"," ",(0,s.jsx)(L,{size:"0.75rem",className:"inline"})]}),"."]})]})]},"cycle")),k.length>0){let f=k[0].name;I.push((0,s.jsxs)("div",{children:[(0,s.jsx)("p",{className:"text-muted-foreground font-medium",children:"This cell redefines variables from other cells."}),k.map(Vs),r&&(0,s.jsx)(w,{errors:k,cellId:r}),(0,s.jsxs)(F,{title:"Why can't I redefine variables?",children:[(0,s.jsx)("p",{className:"pb-2",children:"marimo requires that each variable is defined in just one cell. This constraint enables reactive and reproducible execution, arbitrary cell reordering, seamless UI elements, execution as a script, and more."}),(0,s.jsxs)("p",{className:"py-2",children:["Try merging this cell with the mentioned cells or wrapping it in a function. Alternatively, prefix variables with an underscore (e.g., ",(0,s.jsxs)(je,{className:"inline",children:["_",f]}),"). to make them private to this cell."]}),(0,s.jsxs)("p",{className:"py-2",children:[(0,s.jsxs)(q,{href:"https://links.marimo.app/errors-multiple-definitions",children:["Learn more at our docs"," ",(0,s.jsx)(L,{size:"0.75rem",className:"inline"})]}),"."]})]}),(0,s.jsx)(F,{title:"Need a scratchpad?",children:(0,s.jsxs)("div",{className:"flex flex-row gap-2 items-center",children:[(0,s.jsxs)(E,{size:"xs",variant:"link",className:"my-2 font-normal mx-0 px-0",onClick:Ue,children:[(0,s.jsx)(Ye,{className:"h-3"}),(0,s.jsx)("span",{children:"Try the scratchpad"})]}),(0,s.jsx)("span",{children:"to experiment without restrictions on variable names."})]})})]},"multiple-defs"))}return S.length>0&&I.push((0,s.jsxs)("div",{children:[S.map(Hs),r&&(0,s.jsx)(w,{errors:S,cellId:r}),(0,s.jsxs)(F,{title:"Why can't I use `import *`?",children:[(0,s.jsx)("p",{className:"pb-2",children:"Star imports are incompatible with marimo's git-friendly file format and reproducible reactive execution."}),(0,s.jsx)("p",{className:"py-2",children:"marimo's Python file format stores code in functions, so notebooks can be imported as regular Python modules without executing all their code. But Python disallows `import *` everywhere except at the top-level of a module."}),(0,s.jsx)("p",{className:"py-2",children:"Star imports would also silently add names to globals, which would be incompatible with reactive execution."}),(0,s.jsxs)("p",{className:"py-2",children:[(0,s.jsxs)(q,{href:"https://links.marimo.app/errors-import-star",children:["Learn more at our docs"," ",(0,s.jsx)(L,{size:"0.75rem",className:"inline"})]}),"."]})]})]},"import-star")),P.length>0&&I.push((0,s.jsxs)("div",{children:[P.map(Ks),r&&(0,s.jsx)(w,{errors:P,cellId:r})]},"interruption")),D.length>0&&I.push((0,s.jsxs)("ul",{children:[D.map((f,_)=>f.exception_type==="NameError"&&f.msg.startsWith("name 'mo'")?(0,s.jsx)("li",{className:"my-2",children:(0,s.jsxs)("div",{children:[(0,s.jsx)("p",{className:"text-muted-foreground",children:"name 'mo' is not defined."}),(0,s.jsxs)("p",{className:"text-muted-foreground mt-2",children:["The marimo module (imported as"," ",(0,s.jsx)(je,{className:"inline",children:"mo"}),") is required for Markdown, SQL, and UI elements."]})]})},`exception-${_}`):f.exception_type==="NameError"&&f.msg.startsWith("name '_")?(0,s.jsx)("li",{className:"my-2",children:(0,s.jsxs)("div",{children:[(0,s.jsx)("p",{className:"text-muted-foreground",children:(0,s.jsx)(Ee,{segments:Ne(f.msg)})}),(0,s.jsxs)("p",{className:"text-muted-foreground mt-2",children:["Variables prefixed with an underscore are local to a cell"," ","(",(0,s.jsxs)(q,{href:"https://links.marimo.app/local-variables",children:["docs"," ",(0,s.jsx)(L,{size:"0.75rem",className:"inline"})]}),")."]}),o&&(0,s.jsx)("div",{className:"text-muted-foreground mt-2",children:"See the console area for a traceback."})]})},`exception-${_}`):(0,s.jsx)("li",{className:"my-2",children:f.raising_cell==null?(0,s.jsxs)("div",{children:[(0,s.jsx)("p",{className:"text-muted-foreground",children:T(f.msg,`exception-${_}`)}),"traceback"in f&&f.traceback?(0,s.jsx)(De,{traceback:f.traceback}):o&&(0,s.jsx)("div",{className:"text-muted-foreground mt-2",children:"See the console area for a traceback."})]}):(0,s.jsxs)("div",{children:[T(f.msg,`exception-${_}`),(0,s.jsx)($,{cellId:f.raising_cell}),"traceback"in f&&f.traceback&&(0,s.jsx)(De,{traceback:f.traceback})]})},`exception-${_}`)),D.some(Gs)&&(0,s.jsx)(F,{children:"Fix the error in the mentioned cells, or handle the exceptions with try/except blocks."}),r&&(0,s.jsx)(w,{errors:D,cellId:r})]},"exception")),U.length>0&&I.push((0,s.jsxs)("ul",{children:[U.map(Js),r&&(0,s.jsx)(w,{errors:U,cellId:r}),(0,s.jsx)(F,{children:U.some(Xs)?"Ensure that the referenced cells define the required variables, or turn off strict execution.":"Something is wrong with your declarations. Fix any discrepancies, or turn off strict execution."})]},"strict-exception")),H.length>0&&I.push((0,s.jsxs)("div",{children:[H.map(Ys),r&&(0,s.jsx)(w,{errors:H,cellId:r})]},"internal")),K.length>0&&I.push((0,s.jsxs)("div",{children:[K.map(Zs),r&&(0,s.jsx)(w,{errors:K,cellId:r})]},"ancestor-prevented")),G.length>0&&I.push((0,s.jsxs)("div",{children:[G.map(er),r&&(0,s.jsx)(w,{errors:G,cellId:r})]},"ancestor-stopped")),X.length>0&&I.push((0,s.jsxs)("div",{children:[X.map(tr),r&&(0,s.jsx)(w,{errors:X,cellId:r,className:"mt-2.5"})]},"sql-errors")),I},C;e[19]===p?C=e[20]:(C=p&&(0,s.jsxs)("div",{className:"flex items-center gap-1 font-code text-[0.6875rem] uppercase tracking-wider text-muted-foreground/70",children:[p,(0,s.jsx)(Q,{content:"marimo didn't run this cell because it detected an error.",delayDuration:200,children:(0,s.jsx)(Je,{className:"size-3 cursor-help"})})]}),e[19]=p,e[20]=C);let M;e[21]!==d||e[22]!==i?(M=i&&(0,s.jsx)(_t,{className:`font-code font-medium ${d}`,children:i}),e[21]=d,e[22]=i,e[23]=M):M=e[23];let O;e[24]!==C||e[25]!==M?(O=(0,s.jsxs)("div",{className:"space-y-0.5",children:[C,M]}),e[24]=C,e[25]=M,e[26]=O):O=e[26];let Oe=O;u=At,g=c,e[27]===a?h=e[28]:(h=ue("border-none font-code text-sm text-[0.84375rem] p-0 text-muted-foreground normal has-[svg]:pl-0 space-y-2",a),e[27]=a,e[28]=h),y=Oe,m="flex flex-col gap-4",x=Be(),e[2]=c,e[3]=r,e[4]=l,e[5]=a,e[6]=n,e[7]=o,e[8]=p,e[9]=d,e[10]=i,e[11]=u,e[12]=m,e[13]=x,e[14]=g,e[15]=h,e[16]=y}else u=e[11],m=e[12],x=e[13],g=e[14],h=e[15],y=e[16];let j;e[29]!==m||e[30]!==x?(j=(0,s.jsx)("div",{className:m,children:x}),e[29]=m,e[30]=x,e[31]=j):j=e[31];let b;return e[32]!==u||e[33]!==g||e[34]!==h||e[35]!==y||e[36]!==j?(b=(0,s.jsxs)(u,{variant:g,className:h,children:[y,j]}),e[32]=u,e[33]=g,e[34]=h,e[35]=y,e[36]=j,e[37]=b):b=e[37],b};function ys(t){return t.type==="interruption"}function bs(t){return t.type==="internal"}function vs(t){return t.type==="ancestor-prevented"}function Ns(t){return t.type==="ancestor-stopped"}function Is(t){return t.type==="sql-error"}function ws(t){return t.type==="multiple-defs"||t.type==="cycle"||t.type==="setup-refs"||t.type==="import-star"}function ks(t){return t.type==="exception"}function $s(t){return t.type==="setup-refs"}function _s(t){return t.type==="cycle"}function As(t){return t.type==="multiple-defs"}function Ts(t){return t.type==="import-star"}function Fs(t){return t.type==="interruption"}function Ss(t){return t.type==="exception"}function Cs(t){return t.type==="strict-exception"}function Ms(t){return t.type==="internal"}function Es(t){return t.type==="ancestor-prevented"}function Ps(t){return t.type==="ancestor-stopped"}function zs(t){return t.type==="syntax"}function qs(t){return t.type==="unknown"}function Ls(t){return t.type==="sql-error"}function Ws(t){return t.msg.includes("!pip")}function Ds(t){return t.msg.includes("use os.subprocess")}function Us(t,e){return(0,s.jsx)("pre",{children:T(t.msg,`syntax-${e}`)},`syntax-${e}`)}function Bs(t,e){return(0,s.jsx)("pre",{children:T(t.msg,`unknown-${e}`)},`unknown-${e}`)}function Os(t,e){return t.edges_with_vars.map((n,r)=>(0,s.jsxs)("li",{className:"my-0.5 ml-8 text-muted-foreground/40",children:[(0,s.jsx)($,{cellId:n[0]}),(0,s.jsxs)("span",{className:"text-muted-foreground",children:[": "," ",n[1].length===1?n[1][0]:n[1].join(", ")]})]},`setup-refs-${e}-${r}`))}function Qs(t,e){return t.edges_with_vars.map((n,r)=>(0,s.jsxs)("li",{className:"my-0.5 ml-8 text-muted-foreground/40",children:[(0,s.jsx)($,{cellId:n[0]}),(0,s.jsxs)("span",{className:"text-muted-foreground",children:[" -> ",n[1].length===1?n[1][0]:n[1].join(", ")," -> "]}),(0,s.jsx)($,{cellId:n[2]})]},`cycle-${e}-${r}`))}function Rs(t,e){return(0,s.jsx)("li",{className:"my-0.5 ml-8 text-muted-foreground/40",children:(0,s.jsx)($,{cellId:t})},`cell-${e}`)}function Vs(t,e){return(0,s.jsxs)(W.Fragment,{children:[(0,s.jsx)("p",{className:"text-muted-foreground mt-2",children:`'${t.name}' was also defined by:`}),(0,s.jsx)("ul",{className:"list-disc",children:t.cells.map(Rs)})]},`multiple-defs-${e}`)}function Hs(t,e){return(0,s.jsx)("p",{className:"text-muted-foreground",children:t.msg},`import-star-${e}`)}function Ks(t,e){return(0,s.jsx)("p",{children:"This cell was interrupted and needs to be re-run."},`interruption-${e}`)}function Gs(t){return t.raising_cell!=null}function Js(t,e){return(0,s.jsx)("li",{className:"my-2",children:t.blamed_cell==null?(0,s.jsx)("p",{children:t.msg}):(0,s.jsxs)("div",{children:[t.msg,(0,s.jsx)($,{cellId:t.blamed_cell})]})},`strict-exception-${e}`)}function Xs(t){return t.blamed_cell!=null}function Ys(t,e){return(0,s.jsx)("p",{children:t.msg},`internal-${e}`)}function Zs(t,e){return(0,s.jsxs)("div",{children:[t.msg,t.blamed_cell==null?(0,s.jsxs)("span",{children:["(",(0,s.jsx)($,{cellId:t.raising_cell}),")"]}):(0,s.jsxs)("span",{children:["(",(0,s.jsx)($,{cellId:t.raising_cell}),"\xA0blames\xA0",(0,s.jsx)($,{cellId:t.blamed_cell}),")"]})]},`ancestor-prevented-${e}`)}function er(t,e){return(0,s.jsxs)("div",{children:[t.msg,(0,s.jsx)($,{cellId:t.raising_cell})]},`ancestor-stopped-${e}`)}function tr(t,e){return(0,s.jsx)("div",{className:"space-y-2 mt-2",children:(0,s.jsx)("p",{className:"text-muted-foreground whitespace-pre-wrap",children:t.msg})},`sql-error-${e}`)}export{ye as C,Ne as S,Wt as _,Ce as a,Ot as b,Ae as c,Gt as d,Ht as f,Bt as g,Vt as h,Ee as i,Ut as l,Kt as m,gs as n,Fe as o,$e as p,qe as r,Te as s,js as t,R as u,Rt as v,Et as x,Qt as y};