@marimo-team/islands 0.25.1-dev2 → 0.25.1-dev20

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 (112) hide show
  1. package/dist/{ConnectedDataExplorerComponent-KVhjDMSe.js → ConnectedDataExplorerComponent-CwMXfDQR.js} +3 -3
  2. package/dist/{ErrorBoundary-DO0ie20N.js → ErrorBoundary-BU3l6Hi2.js} +1 -1
  3. package/dist/{abnfDiagram-VCTEODGH-CpN0FRda.js → abnfDiagram-VCTEODGH-AFhOQTt8.js} +3 -3
  4. package/dist/{architectureDiagram-5GKGNRK7-DDyj5SRB.js → architectureDiagram-5GKGNRK7-BngksAAQ.js} +5 -5
  5. package/dist/{blockDiagram-I7D4REHJ-BbXEXJRu.js → blockDiagram-I7D4REHJ-CvBmppdy.js} +9 -9
  6. package/dist/{c4Diagram-7LVT6UL2-DcLsbQmR.js → c4Diagram-7LVT6UL2-EceL4_jH.js} +7 -7
  7. package/dist/{cells-DYIkSxs6.js → cells-Cksce8br.js} +4123 -4150
  8. package/dist/{channel-B5XnTarr.js → channel-z7HZXHwv.js} +1 -1
  9. package/dist/{chat-ui-BXj3uYjy.js → chat-ui-ruucqBTA.js} +45 -45
  10. package/dist/{chunk-4HAMMTFA-D2qv8E0X.js → chunk-4HAMMTFA-BPTTOOC5.js} +5 -5
  11. package/dist/{chunk-75Z2AOVW-5_HsvXwr.js → chunk-75Z2AOVW-C4AOleKf.js} +1 -1
  12. package/dist/{chunk-DU6HZSFF-BUgAHvnM.js → chunk-DU6HZSFF-1TwWR6gh.js} +1 -1
  13. package/dist/{chunk-F27PBJKO-DHmYWZXr.js → chunk-F27PBJKO-DMI1PBe4.js} +1 -1
  14. package/dist/{chunk-GMAD6QVW-Czynz8aA.js → chunk-GMAD6QVW-vStrMz2P.js} +3 -3
  15. package/dist/{chunk-GVQU2GXP-yQEdulRS.js → chunk-GVQU2GXP-2HwWgs4u.js} +2 -2
  16. package/dist/{chunk-IMKFNOWR-BfwTebD0.js → chunk-IMKFNOWR-UOVZuZVn.js} +5 -5
  17. package/dist/{chunk-L3NEJ4N5-DwRWnDDK.js → chunk-L3NEJ4N5-RhC7i0I2.js} +5 -5
  18. package/dist/{chunk-OSK3NFVY-BBKU_Ign.js → chunk-OSK3NFVY-CZV09N-U.js} +6 -6
  19. package/dist/{chunk-P2QGCYS3-C0iGU29N.js → chunk-P2QGCYS3-0TAWai9C.js} +1 -1
  20. package/dist/{chunk-POPQ4Y6H-BCbS3Ozc.js → chunk-POPQ4Y6H-Cc3_56GX.js} +1 -1
  21. package/dist/{chunk-PWAF6VOD-CTFCL0jI.js → chunk-PWAF6VOD-lNxB9k8z.js} +1 -1
  22. package/dist/{chunk-SHT3W25Y-BspJEJ5H.js → chunk-SHT3W25Y-CrEPyWy1.js} +7 -7
  23. package/dist/{chunk-SVP7TREG-DX3rFNTe.js → chunk-SVP7TREG-uIyyySp6.js} +2 -2
  24. package/dist/{chunk-TICWLB2K-fVF7Uujg.js → chunk-TICWLB2K-DdXM3eg6.js} +5 -5
  25. package/dist/classDiagram-ZZMXUADV-BiATbBE4.js +30 -0
  26. package/dist/classDiagram-v2-VYDZK3BY-JnH97GYw.js +30 -0
  27. package/dist/{code-visibility-64_ACQvC.js → code-visibility-DDDujOI0.js} +17 -17
  28. package/dist/{common-LlPE7GR6.js → common-BkYqr2IQ.js} +620 -620
  29. package/dist/{cynefinDiagram-5FMLGOSQ-CFQrqzSX.js → cynefinDiagram-5FMLGOSQ-zknaAsGy.js} +3 -3
  30. package/dist/{dagre-GXQ25YYZ-xHNAUTyG.js → dagre-GXQ25YYZ-BhMOoMGJ.js} +10 -10
  31. package/dist/{data-grid-overlay-editor-BUvmnptP.js → data-grid-overlay-editor-3APq_6Z0.js} +1 -1
  32. package/dist/{diagram-S7CK7UJ4-Bt1rPbAx.js → diagram-S7CK7UJ4-V5-Z-EDY.js} +4 -4
  33. package/dist/{diagram-UQ7AKVKN-D1sq5FoZ.js → diagram-UQ7AKVKN-qw8AjDal.js} +3 -3
  34. package/dist/{diagram-VSXAHHWV-BKpmG23x.js → diagram-VSXAHHWV-CR7BJygZ.js} +2 -2
  35. package/dist/{diagram-VX7I27RA-BMwGFVD6.js → diagram-VX7I27RA-DfuqFb1E.js} +5 -5
  36. package/dist/{diagram-Z3DM3KII--KLDR9Pl.js → diagram-Z3DM3KII-DpWwlYZB.js} +3 -3
  37. package/dist/{dist-BaHjhkpZ.js → dist-CcyKTBTF.js} +1 -1
  38. package/dist/{ebnfDiagram-PWID7BFC-WnG4cvSQ.js → ebnfDiagram-PWID7BFC-C20OnNMZ.js} +3 -3
  39. package/dist/{erDiagram-RLTQ6QDP-DhzKiYHc.js → erDiagram-RLTQ6QDP-DBXLWO9v.js} +12 -12
  40. package/dist/{error-entries-C0NsUqXl.js → error-entries-CvCFdpY2.js} +18 -18
  41. package/dist/{feedback-button-B98mEc3M.js → feedback-button-DWSXRUAw.js} +19 -19
  42. package/dist/flowDiagram-HODETNUW-DL52nMkt.js +16 -0
  43. package/dist/{formats-BR5iGb4s.js → formats-Cl_67ghI.js} +2 -2
  44. package/dist/{ganttDiagram-EL5Y4UJY-DbzEBLbl.js → ganttDiagram-EL5Y4UJY-cRRaDt88.js} +2 -2
  45. package/dist/{gitGraphDiagram-WWUBYQGX-BMf0iKi1.js → gitGraphDiagram-WWUBYQGX-Pox5k9-Y.js} +2 -2
  46. package/dist/{glide-data-editor-CBIPbkXG.js → glide-data-editor-DGmuodBB.js} +3 -3
  47. package/dist/{globals-VpAtGRbg.js → globals-ciicQhoq.js} +1 -1
  48. package/dist/{html-to-image-CeYYvQNb.js → html-to-image-CcfkLMrx.js} +32 -31
  49. package/dist/{infoDiagram-27XIBGKW-BagtCX_5.js → infoDiagram-27XIBGKW-ntpxMfrq.js} +2 -2
  50. package/dist/{ishikawaDiagram-5VMMS53U-DByDehYy.js → ishikawaDiagram-5VMMS53U-BllIlHE5.js} +3 -3
  51. package/dist/{journeyDiagram-3NMN7TZE-D2LcKDUO.js → journeyDiagram-3NMN7TZE-EPJDPlt0.js} +2 -2
  52. package/dist/{kanban-definition-UXKFOSKX-BxwkbBI_.js → kanban-definition-UXKFOSKX-GYtNMjtS.js} +9 -9
  53. package/dist/{katex-C6D4cX_N.js → katex-BNCmKUol.js} +983 -1120
  54. package/dist/{loader-D2W4oM-3.js → loader-DEZSzvma.js} +253 -248
  55. package/dist/main.js +622 -620
  56. package/dist/{mermaid-DPNuhemn.js → mermaid-Dnzoxjt1.js} +49 -49
  57. package/dist/{mhchem-DWCF06w9.js → mhchem-BEc7A1or.js} +1 -1
  58. package/dist/{mindmap-definition-YA3MSWOX-Cgvojw3y.js → mindmap-definition-YA3MSWOX-Cth76xgH.js} +11 -11
  59. package/dist/{number-overlay-editor-ClZmv3Qw.js → number-overlay-editor-CcdYLWWR.js} +1 -1
  60. package/dist/{pegDiagram-XKGWAZYB-DahHhYyN.js → pegDiagram-XKGWAZYB-Cu4uOXii.js} +3 -3
  61. package/dist/{pieDiagram-E7YTZNPT-CCtX0oCK.js → pieDiagram-E7YTZNPT-Bh3eDa9s.js} +3 -3
  62. package/dist/{process-output-BCTjlyuH.js → process-output-C6apL85F.js} +2 -2
  63. package/dist/{quadrantDiagram-AXDQQJYC-DftINo6g.js → quadrantDiagram-AXDQQJYC--kqzFC3w.js} +1 -1
  64. package/dist/{railroadDiagram-O6MQD6OU-Du5NsPUb.js → railroadDiagram-O6MQD6OU-DG-M6Rnc.js} +3 -3
  65. package/dist/{react-vega-5AcMW8gG.js → react-vega-ewj7q_U5.js} +1 -1
  66. package/dist/{requirementDiagram-BXWQKSXE-0pmmxL6_.js → requirementDiagram-BXWQKSXE-DCwsThh0.js} +11 -11
  67. package/dist/{reveal-component-C1s_K6O8.js → reveal-component-DUPORxps.js} +744 -743
  68. package/dist/{sankeyDiagram-P5KCCOFB-CDKR5TJH.js → sankeyDiagram-P5KCCOFB-BdzhT0BM.js} +1 -1
  69. package/dist/{sequenceDiagram-WJ2MYXX4-DB-ChWwU.js → sequenceDiagram-WJ2MYXX4-CbXB1_PU.js} +4 -4
  70. package/dist/{stateDiagram-D77RDMKH-orN6AwMx.js → stateDiagram-D77RDMKH-A-Zf3Sb2.js} +13 -13
  71. package/dist/stateDiagram-v2-MP3YSRHH-D9YMnUGx.js +30 -0
  72. package/dist/style.css +1 -1
  73. package/dist/{swimlanes-42K2YHIH-BkSDcqDT.js → swimlanes-42K2YHIH-B_pMf0J4.js} +10 -10
  74. package/dist/swimlanesDiagram-VR7AAH4N-Ctgy-yGP.js +30 -0
  75. package/dist/{timeline-definition-24CTP7MA-BuqJUIRZ.js → timeline-definition-24CTP7MA-DTXFjTTM.js} +3 -3
  76. package/dist/{toDate-WXg1yLcI.js → toDate-C67bxMWn.js} +139 -111
  77. package/dist/{vega-component-D0QXopU8.js → vega-component-tFOzgarw.js} +12 -12
  78. package/dist/{vega-embed-container-CbnTccZi.js → vega-embed-container-sSR5hAsy.js} +2 -2
  79. package/dist/{vega-loader.browser-D95WfUcj.js → vega-loader.browser-DNST6uVf.js} +2 -2
  80. package/dist/{vennDiagram-4TSXK5OY-CTUlJ2PK.js → vennDiagram-4TSXK5OY-BPHjZo0R.js} +3 -3
  81. package/dist/{wardleyDiagram-VM6X3IG4-3evLUb0M.js → wardleyDiagram-VM6X3IG4-6fTLYBuD.js} +3 -3
  82. package/dist/{xychartDiagram-S5SC5T6Z-DL2E3K8-.js → xychartDiagram-S5SC5T6Z-BYjwjrzE.js} +5 -5
  83. package/package.json +1 -2
  84. package/src/components/data-table/charts/__tests__/chart-panel.test.tsx +286 -0
  85. package/src/components/data-table/charts/charts.tsx +26 -13
  86. package/src/components/editor/chrome/components/contribute-snippet-button.tsx +6 -1
  87. package/src/components/editor/notebook-cell.tsx +2 -2
  88. package/src/components/editor/output/MarimoTracebackOutput.tsx +1 -1
  89. package/src/components/editor/output/VideoOutput.tsx +1 -1
  90. package/src/components/editor/output/ansi-reduce.ts +8 -4
  91. package/src/components/editor/output/console/text-rendering.tsx +3 -1
  92. package/src/components/home/components.tsx +1 -0
  93. package/src/components/pages/run-page.tsx +1 -0
  94. package/src/components/static-html/share-modal.tsx +2 -1
  95. package/src/components/static-html/static-banner.tsx +2 -2
  96. package/src/components/ui/links.tsx +6 -1
  97. package/src/components/ui/typography.tsx +16 -8
  98. package/src/core/kernel/__tests__/session.test.ts +110 -1
  99. package/src/core/kernel/session.ts +46 -15
  100. package/src/css/globals.css +18 -21
  101. package/src/css/katex.min.css +260 -241
  102. package/src/plugins/impl/vega/__tests__/utils.test.ts +16 -1
  103. package/src/plugins/impl/vega/__tests__/vega.test.ts +37 -0
  104. package/src/plugins/impl/vega/loader.ts +23 -1
  105. package/src/plugins/impl/vega/utils.ts +5 -4
  106. package/src/plugins/layout/RoutesPlugin.tsx +4 -6
  107. package/dist/classDiagram-ZZMXUADV-Fe-V14Sc.js +0 -30
  108. package/dist/classDiagram-v2-VYDZK3BY-xTDg7lOs.js +0 -30
  109. package/dist/flowDiagram-HODETNUW-6dMdB8pR.js +0 -16
  110. package/dist/katex-DFBiSucW.js +0 -21840
  111. package/dist/stateDiagram-v2-MP3YSRHH-o05_rJN8.js +0 -30
  112. package/dist/swimlanesDiagram-VR7AAH4N-D_nFQfRc.js +0 -30
@@ -5,7 +5,7 @@ import { cn } from "@/utils/cn";
5
5
  const H1 = React.forwardRef<
6
6
  HTMLHeadingElement,
7
7
  React.ComponentPropsWithoutRef<"h1">
8
- >(({ className, ...props }, ref) => {
8
+ >(({ className, children, ...props }, ref) => {
9
9
  return (
10
10
  <h1
11
11
  className={cn(
@@ -14,7 +14,9 @@ const H1 = React.forwardRef<
14
14
  )}
15
15
  ref={ref}
16
16
  {...props}
17
- />
17
+ >
18
+ {children}
19
+ </h1>
18
20
  );
19
21
  });
20
22
  H1.displayName = "H1";
@@ -22,7 +24,7 @@ H1.displayName = "H1";
22
24
  const H2 = React.forwardRef<
23
25
  HTMLHeadingElement,
24
26
  React.ComponentPropsWithoutRef<"h2">
25
- >(({ className, ...props }, ref) => {
27
+ >(({ className, children, ...props }, ref) => {
26
28
  return (
27
29
  <h2
28
30
  className={cn(
@@ -31,7 +33,9 @@ const H2 = React.forwardRef<
31
33
  )}
32
34
  ref={ref}
33
35
  {...props}
34
- />
36
+ >
37
+ {children}
38
+ </h2>
35
39
  );
36
40
  });
37
41
  H2.displayName = "H2";
@@ -39,7 +43,7 @@ H2.displayName = "H2";
39
43
  const H3 = React.forwardRef<
40
44
  HTMLHeadingElement,
41
45
  React.ComponentPropsWithoutRef<"h3">
42
- >(({ className, ...props }, ref) => {
46
+ >(({ className, children, ...props }, ref) => {
43
47
  return (
44
48
  <h3
45
49
  className={cn(
@@ -48,7 +52,9 @@ const H3 = React.forwardRef<
48
52
  )}
49
53
  ref={ref}
50
54
  {...props}
51
- />
55
+ >
56
+ {children}
57
+ </h3>
52
58
  );
53
59
  });
54
60
  H3.displayName = "H3";
@@ -56,7 +62,7 @@ H3.displayName = "H3";
56
62
  const H4 = React.forwardRef<
57
63
  HTMLHeadingElement,
58
64
  React.ComponentPropsWithoutRef<"h4">
59
- >(({ className, ...props }, ref) => {
65
+ >(({ className, children, ...props }, ref) => {
60
66
  return (
61
67
  <h4
62
68
  className={cn(
@@ -65,7 +71,9 @@ const H4 = React.forwardRef<
65
71
  )}
66
72
  ref={ref}
67
73
  {...props}
68
- />
74
+ >
75
+ {children}
76
+ </h4>
69
77
  );
70
78
  });
71
79
  H4.displayName = "H4";
@@ -1,10 +1,119 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
- import { describe, expect, it } from "vitest";
2
+ import { afterEach, describe, expect, it, vi } from "vitest";
3
+ import type { AppMode } from "@/core/mode";
3
4
  import { generateSessionId, isSessionId } from "../session";
4
5
 
6
+ const URL_SESSION_ID = "s_abc123";
7
+
8
+ /**
9
+ * Load a fresh copy of the session module against the given URL and mode.
10
+ * The module caches its session id, so each case needs its own instance.
11
+ */
12
+ async function loadSession(opts: { mode: AppMode | undefined; url: string }) {
13
+ vi.resetModules();
14
+ window.history.replaceState({}, "", opts.url);
15
+ const { store } = await import("@/core/state/jotai");
16
+ const { initialModeAtom } = await import("@/core/mode");
17
+ store.set(initialModeAtom, opts.mode);
18
+ return await import("../session");
19
+ }
20
+
21
+ function currentSearchParams(): URLSearchParams {
22
+ return new URL(window.location.href).searchParams;
23
+ }
24
+
5
25
  describe("Session", () => {
26
+ afterEach(() => {
27
+ window.history.replaceState({}, "", "/");
28
+ });
29
+
6
30
  it("should create a session", () => {
7
31
  const id = generateSessionId();
8
32
  expect(isSessionId(id)).toBe(true);
9
33
  });
34
+
35
+ it("resumes the session named in the URL in edit mode", async () => {
36
+ const { getSessionId } = await loadSession({
37
+ mode: "edit",
38
+ url: `/?session_id=${URL_SESSION_ID}`,
39
+ });
40
+ expect(getSessionId()).toBe(URL_SESSION_ID);
41
+ expect(currentSearchParams().has("session_id")).toBe(false);
42
+ });
43
+
44
+ it("keeps the URL session id in kiosk mode so a refresh resumes it", async () => {
45
+ const { getSessionId } = await loadSession({
46
+ mode: "edit",
47
+ url: `/?kiosk=true&session_id=${URL_SESSION_ID}`,
48
+ });
49
+ expect(getSessionId()).toBe(URL_SESSION_ID);
50
+ expect(currentSearchParams().get("session_id")).toBe(URL_SESSION_ID);
51
+ expect(currentSearchParams().get("kiosk")).toBe("true");
52
+ });
53
+
54
+ it("keeps the kiosk flag in edit mode when the URL names no session", async () => {
55
+ const { getSessionId } = await loadSession({
56
+ mode: "edit",
57
+ url: "/?kiosk=true",
58
+ });
59
+ expect(isSessionId(getSessionId())).toBe(true);
60
+ expect(currentSearchParams().get("kiosk")).toBe("true");
61
+ });
62
+
63
+ it("starts a new session when the URL session id is malformed", async () => {
64
+ const { getSessionId } = await loadSession({
65
+ mode: "edit",
66
+ url: "/?session_id=not-a-session",
67
+ });
68
+ const id = getSessionId();
69
+ expect(isSessionId(id)).toBe(true);
70
+ expect(id).not.toBe("not-a-session");
71
+ });
72
+
73
+ it("ignores the URL session id in read mode", async () => {
74
+ const { getSessionId } = await loadSession({
75
+ mode: "read",
76
+ url: `/?session_id=${URL_SESSION_ID}`,
77
+ });
78
+ const id = getSessionId();
79
+ expect(isSessionId(id)).toBe(true);
80
+ expect(id).not.toBe(URL_SESSION_ID);
81
+ // The param is still scrubbed so a refresh cannot reintroduce it.
82
+ expect(currentSearchParams().has("session_id")).toBe(false);
83
+ });
84
+
85
+ it("drops both the session id and the kiosk flag in read mode", async () => {
86
+ const { getSessionId } = await loadSession({
87
+ mode: "read",
88
+ url: `/?kiosk=true&session_id=${URL_SESSION_ID}`,
89
+ });
90
+ expect(getSessionId()).not.toBe(URL_SESSION_ID);
91
+ expect(currentSearchParams().has("session_id")).toBe(false);
92
+ // The kiosk flag is forwarded to the websocket URL, and the server
93
+ // refuses kiosk connections to an app, so it must not survive here.
94
+ expect(currentSearchParams().has("kiosk")).toBe(false);
95
+ });
96
+
97
+ it("drops a lone kiosk flag in read mode", async () => {
98
+ const { getSessionId } = await loadSession({
99
+ mode: "read",
100
+ url: "/?kiosk=true",
101
+ });
102
+ expect(isSessionId(getSessionId())).toBe(true);
103
+ expect(currentSearchParams().has("kiosk")).toBe(false);
104
+ });
105
+
106
+ it("ignores the URL session id when the mode is not yet known", async () => {
107
+ const { getSessionId } = await loadSession({
108
+ mode: undefined,
109
+ url: `/?session_id=${URL_SESSION_ID}`,
110
+ });
111
+ expect(getSessionId()).not.toBe(URL_SESSION_ID);
112
+ expect(currentSearchParams().has("session_id")).toBe(false);
113
+ });
114
+
115
+ it("returns the same id on every call", async () => {
116
+ const { getSessionId } = await loadSession({ mode: "read", url: "/" });
117
+ expect(getSessionId()).toBe(getSessionId());
118
+ });
10
119
  });
@@ -4,6 +4,8 @@ import { Logger } from "@/utils/Logger";
4
4
  import type { TypedString } from "@/utils/typed";
5
5
  import { updateQueryParams } from "@/utils/urls";
6
6
  import { KnownQueryParams } from "../constants";
7
+ import { initialModeAtom } from "../mode";
8
+ import { store } from "../state/jotai";
7
9
 
8
10
  export type SessionId = TypedString<"SessionId">;
9
11
 
@@ -20,31 +22,60 @@ export function isSessionId(value: string | null): value is SessionId {
20
22
  return /^s_[\da-z]{6}$/.test(value);
21
23
  }
22
24
 
23
- const sessionId = (() => {
25
+ let sessionId: SessionId | null = null;
26
+
27
+ function resolveSessionId(): SessionId {
28
+ // Only the editor lets the URL steer the connection: kiosk mode and
29
+ // external editors reattach to a running session through `session_id`.
30
+ // An app served with `marimo run` never does. There, the session id is the
31
+ // only thing that routes a browser to its kernel, so a link that picked it
32
+ // would let the link's author attach to the reader's session afterwards,
33
+ // and a stray `kiosk` flag would make the server refuse the connection.
34
+ const urlMaySteerConnection = store.get(initialModeAtom) === "edit";
35
+
24
36
  const url = new URL(window.location.href);
25
37
  const id = url.searchParams.get(
26
38
  KnownQueryParams.sessionId,
27
39
  ) as SessionId | null;
28
- if (isSessionId(id)) {
29
- // Remove the session_id from the URL
30
- updateQueryParams((params) => {
31
- // Keep the session_id if we are in kiosk mode
32
- // this is so we can resume the same session if the user refreshes the page
33
- if (params.has(KnownQueryParams.kiosk)) {
34
- return;
35
- }
40
+ const urlSessionId = isSessionId(id) ? id : null;
41
+
42
+ updateQueryParams((params) => {
43
+ if (!urlMaySteerConnection) {
36
44
  params.delete(KnownQueryParams.sessionId);
45
+ params.delete(KnownQueryParams.kiosk);
46
+ return;
47
+ }
48
+ // Keep the session_id in kiosk mode so that a refresh resumes the same
49
+ // session.
50
+ if (params.has(KnownQueryParams.kiosk)) {
51
+ return;
52
+ }
53
+ params.delete(KnownQueryParams.sessionId);
54
+ });
55
+
56
+ if (urlSessionId === null) {
57
+ Logger.debug("Starting a new session", { sessionId: id });
58
+ return generateSessionId();
59
+ }
60
+ if (!urlMaySteerConnection) {
61
+ Logger.debug("Ignoring session_id from URL outside the editor", {
62
+ sessionId: urlSessionId,
37
63
  });
38
- Logger.debug("Connecting to existing session", { sessionId: id });
39
- return id;
64
+ return generateSessionId();
40
65
  }
41
- Logger.debug("Starting a new session", { sessionId: id });
42
- return generateSessionId();
43
- })();
66
+ Logger.debug("Connecting to existing session", { sessionId: urlSessionId });
67
+ return urlSessionId;
68
+ }
44
69
 
45
70
  /**
46
- * Resume an existing session or start a new one
71
+ * Resume an existing session or start a new one.
72
+ *
73
+ * Resolved on first use rather than at import time, because the URL is only
74
+ * trusted once the app mode is known.
47
75
  */
48
76
  export function getSessionId(): SessionId {
77
+ if (sessionId === null) {
78
+ sessionId = resolveSessionId();
79
+ }
49
80
  return sessionId;
50
81
  }
@@ -55,29 +55,21 @@
55
55
  --background: light-dark(hsl(0deg 0% 100%), hsl(150deg 7.7% 10.2%));
56
56
  --foreground: light-dark(hsl(222.2deg 47.4% 11.2%), hsl(155deg 7% 93%));
57
57
  --muted: light-dark(hsl(210deg 40% 96.1%), hsl(180deg 14% 1%));
58
- --muted-foreground: light-dark(
59
- hsl(215.4deg 16.3% 46.9%),
60
- hsl(155deg 5% 68.3%)
61
- );
58
+ --muted-foreground: light-dark(hsl(215.4deg 16.3% 46.9%),
59
+ hsl(155deg 5% 68.3%));
62
60
  --popover: light-dark(hsl(0deg 0% 100%), hsl(151deg 5.5% 15.2%));
63
- --popover-foreground: light-dark(
64
- hsl(222.2deg 47.4% 11.2%),
65
- hsl(155deg 5% 68.3%)
66
- );
61
+ --popover-foreground: light-dark(hsl(222.2deg 47.4% 11.2%),
62
+ hsl(155deg 5% 68.3%));
67
63
  --card: light-dark(hsl(0deg 0% 100%), hsl(151deg 5.5% 15.2%));
68
- --card-foreground: light-dark(
69
- hsl(222.2deg 47.4% 11.2%),
70
- hsl(155deg 5% 76.3%)
71
- );
64
+ --card-foreground: light-dark(hsl(222.2deg 47.4% 11.2%),
65
+ hsl(155deg 5% 76.3%));
72
66
  --border: light-dark(hsl(214.3deg 31.8% 91.4%), hsl(153deg 3.7% 24.2%));
73
67
  --input: light-dark(hsl(0deg 0% 64%), hsl(154deg 3.3% 28.7%));
74
68
  --primary: light-dark(hsl(208deg 93.5% 47.4%), hsl(192deg 59.8% 39%));
75
69
  --primary-foreground: light-dark(hsl(210deg 40% 98%), hsl(190deg 80% 84%));
76
70
  --secondary: light-dark(hsl(210deg 40% 96.1%), hsl(155deg 7% 93%));
77
- --secondary-foreground: light-dark(
78
- hsl(222.2deg 47.4% 11.2%),
79
- hsl(151deg 5.5% 15.2%)
80
- );
71
+ --secondary-foreground: light-dark(hsl(222.2deg 47.4% 11.2%),
72
+ hsl(151deg 5.5% 15.2%));
81
73
  --accent: light-dark(hsl(209deg 100% 96.5%), hsl(192deg 56.6% 26.5%));
82
74
  --accent-foreground: light-dark(hsl(211deg 90% 42%), hsl(190deg 80% 84%));
83
75
  --ring: hsl(215deg 20.2% 65.1%);
@@ -105,10 +97,8 @@
105
97
 
106
98
  /* Base shadows. */
107
99
  --base-shadow: light-dark(hsl(0deg 0% 85% / 40%), hsla(0deg 0% 36% / 60%));
108
- --base-shadow-darker: light-dark(
109
- hsl(0deg 0% 50% / 40%),
110
- hsl(0deg 0% 50% / 60%)
111
- );
100
+ --base-shadow-darker: light-dark(hsl(0deg 0% 50% / 40%),
101
+ hsl(0deg 0% 50% / 60%));
112
102
  --base-shadow-opacity: 5%;
113
103
 
114
104
  /* Codemirror editor */
@@ -162,7 +152,14 @@
162
152
  animation-duration: 0.01ms !important;
163
153
  animation-delay: 0s !important;
164
154
  animation-iteration-count: 1 !important;
165
- transition-duration: 0.01ms !important;
155
+
156
+ /*
157
+ Set to 0s, because `transition-property` defaults to `all`, so any non-zero
158
+ duration gives every element a transition. Libraries that change an
159
+ attribute and measure synchronously (e.g. Plotly hover labels) would read
160
+ stale geometry.
161
+ */
162
+ transition-duration: 0s !important;
166
163
  transition-delay: 0s !important;
167
164
  }
168
165