@marimo-team/islands 0.25.3-dev141 → 0.25.3-dev16

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 (125) hide show
  1. package/dist/{ConnectedDataExplorerComponent-CNSxdKyJ.js → ConnectedDataExplorerComponent-CNMliVzw.js} +1 -1
  2. package/dist/{ErrorBoundary-Cg59O3xL.js → ErrorBoundary-BEmYzeHb.js} +1 -1
  3. package/dist/{abnfDiagram-VCTEODGH-DQDjNXt3.js → abnfDiagram-VCTEODGH-D-Ez91gV.js} +2 -2
  4. package/dist/{any-language-editor-B9REGlD9.js → any-language-editor-DZ_YTVus.js} +1 -1
  5. package/dist/{architectureDiagram-5GKGNRK7-Nmls7Wxn.js → architectureDiagram-5GKGNRK7-CTpcNtO8.js} +1 -1
  6. package/dist/{chat-ui-BDpymeoe.js → chat-ui-DaHynk8g.js} +436 -437
  7. package/dist/{chunk-IMKFNOWR-CoIFEo-1.js → chunk-IMKFNOWR-DGxi-zeo.js} +1 -1
  8. package/dist/{chunk-SHT3W25Y-lHiGx3ZC.js → chunk-SHT3W25Y-CV9J3rAB.js} +1 -1
  9. package/dist/{chunk-SVP7TREG-BvsHIrY_.js → chunk-SVP7TREG-BL-9Fqgg.js} +1 -1
  10. package/dist/{chunk-TICWLB2K-BC8YIm_n.js → chunk-TICWLB2K-Cu_XMjDM.js} +1 -1
  11. package/dist/{classDiagram-ZZMXUADV-Bt98lkkv.js → classDiagram-ZZMXUADV-BXXV13Hq.js} +2 -2
  12. package/dist/{classDiagram-v2-VYDZK3BY-D9bfbcC3.js → classDiagram-v2-VYDZK3BY-B7FXJ1Cp.js} +2 -2
  13. package/dist/{code-visibility-Da3iitn_.js → code-visibility-D75HwL4W.js} +14 -14
  14. package/dist/{common-C6FHcu5p.js → common-hR234FGW.js} +6704 -6409
  15. package/dist/{cynefinDiagram-5FMLGOSQ-CgWra7wt.js → cynefinDiagram-5FMLGOSQ-CH96rwba.js} +1 -1
  16. package/dist/{dagre-GXQ25YYZ-CFZ8-35c.js → dagre-GXQ25YYZ-BD3PmpFY.js} +1 -1
  17. package/dist/{data-grid-overlay-editor-mJt3Pg4S.js → data-grid-overlay-editor-D-NMDtk0.js} +1 -1
  18. package/dist/{diagram-S7CK7UJ4-lCNQrW7d.js → diagram-S7CK7UJ4-BsSJJldB.js} +1 -1
  19. package/dist/{diagram-UQ7AKVKN-CQPQ6pP_.js → diagram-UQ7AKVKN-muQtNZc4.js} +1 -1
  20. package/dist/{diagram-VX7I27RA-Cf3b0llz.js → diagram-VX7I27RA-CdhuZAqR.js} +1 -1
  21. package/dist/{diagram-Z3DM3KII-Bt4QLrpn.js → diagram-Z3DM3KII-BD8mXjOj.js} +1 -1
  22. package/dist/{ebnfDiagram-PWID7BFC-D3NAyPFY.js → ebnfDiagram-PWID7BFC-BIN1sweE.js} +2 -2
  23. package/dist/{erDiagram-RLTQ6QDP-Tlu09_Nm.js → erDiagram-RLTQ6QDP-Ctp5cfoN.js} +1 -1
  24. package/dist/{error-banner-Da600cwz.js → error-banner-D-WCIiMt.js} +10 -5
  25. package/dist/{cells-C8iCYvLU.js → error-entries-Bv6bZPF3.js} +2262 -2206
  26. package/dist/{feedback-button-DaPwHIdM.js → feedback-button-B3sliPJj.js} +115 -116
  27. package/dist/{flowDiagram-HODETNUW-CAYbpJmV.js → flowDiagram-HODETNUW-3EfLrlFb.js} +2 -2
  28. package/dist/{formats-BGh2thRc.js → formats-27CW_UP4.js} +1 -1
  29. package/dist/{glide-data-editor-Ctz2mH6k.js → glide-data-editor-Dufy2ePW.js} +3 -3
  30. package/dist/{globals-EpFoRj_E.js → globals-B02fTTAH.js} +1 -1
  31. package/dist/{html-to-image-CZY1pIZH.js → html-to-image-Bf8QMGi4.js} +23 -23
  32. package/dist/{infoDiagram-27XIBGKW-XrDxhr0x.js → infoDiagram-27XIBGKW-DipQoHxl.js} +1 -1
  33. package/dist/{ishikawaDiagram-5VMMS53U-CD1muzov.js → ishikawaDiagram-5VMMS53U-DgFMjSGz.js} +1 -1
  34. package/dist/{kanban-definition-UXKFOSKX-Dg9W142_.js → kanban-definition-UXKFOSKX-w1ACVjgl.js} +1 -1
  35. package/dist/main.js +164 -163
  36. package/dist/{mermaid-CiAXjLpA.js → mermaid-CHo49Yv-.js} +33 -33
  37. package/dist/{mindmap-definition-YA3MSWOX-DgCidgYJ.js → mindmap-definition-YA3MSWOX-CrT0KSfX.js} +1 -1
  38. package/dist/{number-overlay-editor-CtxrummL.js → number-overlay-editor-EF8mCVoy.js} +1 -1
  39. package/dist/{pegDiagram-XKGWAZYB-coOYYKB6.js → pegDiagram-XKGWAZYB-BqvywNJA.js} +2 -2
  40. package/dist/{pieDiagram-E7YTZNPT-BnXTc3bj.js → pieDiagram-E7YTZNPT-BfxJ_XCc.js} +1 -1
  41. package/dist/{process-output-e4jj9Ijt.js → process-output-BZg56CCb.js} +3 -3
  42. package/dist/{railroadDiagram-O6MQD6OU-B28-u83n.js → railroadDiagram-O6MQD6OU-8vFLVTPu.js} +2 -2
  43. package/dist/{requirementDiagram-BXWQKSXE-CdYCTMu0.js → requirementDiagram-BXWQKSXE-a_MxGvxe.js} +1 -1
  44. package/dist/{reveal-component-DrIXvR_7.js → reveal-component-Boolki8h.js} +4 -4
  45. package/dist/{sequenceDiagram-WJ2MYXX4-CM3aRAe_.js → sequenceDiagram-WJ2MYXX4-CXFMnxGh.js} +1 -1
  46. package/dist/{stateDiagram-D77RDMKH-DOZUMVj8.js → stateDiagram-D77RDMKH-DQeA_CwI.js} +2 -2
  47. package/dist/{stateDiagram-v2-MP3YSRHH-CISYxma6.js → stateDiagram-v2-MP3YSRHH-BxxQucm1.js} +2 -2
  48. package/dist/style.css +1 -1
  49. package/dist/{swimlanes-42K2YHIH-BkSppxp8.js → swimlanes-42K2YHIH-Do6L-cYg.js} +1 -1
  50. package/dist/{swimlanesDiagram-VR7AAH4N-c8G5pGxC.js → swimlanesDiagram-VR7AAH4N-DUzPvSVU.js} +2 -2
  51. package/dist/{timeline-definition-24CTP7MA-CaMQayqK.js → timeline-definition-24CTP7MA-D3c46WM5.js} +1 -1
  52. package/dist/{toDate-BNJf01u1.js → toDate-UtiwmnH2.js} +41 -39
  53. package/dist/{vega-component-DG29TAB7.js → vega-component-BeF9dfyO.js} +3 -3
  54. package/dist/{vennDiagram-4TSXK5OY-CF8pfaK3.js → vennDiagram-4TSXK5OY-BSZD8JcZ.js} +1 -1
  55. package/dist/{wardleyDiagram-VM6X3IG4-dGPu39d-.js → wardleyDiagram-VM6X3IG4-48cUWkfA.js} +1 -1
  56. package/dist/{xychartDiagram-S5SC5T6Z-CP6-9q56.js → xychartDiagram-S5SC5T6Z-CswYgYeu.js} +1 -1
  57. package/package.json +5 -2
  58. package/src/__tests__/fixtures/{pair-preview.ts → pair-config.ts} +3 -2
  59. package/src/__tests__/host-imports.test.ts +100 -0
  60. package/src/__tests__/mount.test.ts +28 -8
  61. package/src/components/data-table/__tests__/context-menu.test.ts +176 -0
  62. package/src/components/data-table/__tests__/date-filter-inputs.test.tsx +24 -1
  63. package/src/components/data-table/__tests__/export-actions.test.tsx +329 -6
  64. package/src/components/data-table/context-menu.tsx +125 -26
  65. package/src/components/data-table/date-filter-inputs.tsx +15 -42
  66. package/src/components/data-table/export-actions.tsx +192 -116
  67. package/src/components/data-table/schemas.ts +14 -36
  68. package/src/components/data-table/utils.ts +18 -0
  69. package/src/components/editor/Output.tsx +9 -2
  70. package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +80 -252
  71. package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +186 -34
  72. package/src/components/editor/actions/pair-with-agent-commands.ts +42 -51
  73. package/src/components/editor/actions/pair-with-agent-modal.tsx +96 -36
  74. package/src/components/editor/chrome/panels/error-panel.tsx +1 -0
  75. package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +372 -0
  76. package/src/components/editor/chrome/wrapper/footer-item.tsx +32 -24
  77. package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +237 -0
  78. package/src/components/editor/chrome/wrapper/footer.tsx +2 -0
  79. package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +548 -0
  80. package/src/components/editor/errors/send-error-report-button.tsx +69 -0
  81. package/src/components/editor/output/MarimoErrorOutput.tsx +21 -5
  82. package/src/components/editor/output/MarimoTracebackOutput.tsx +35 -20
  83. package/src/components/editor/output/__tests__/ansi-reduce.test.ts +18 -0
  84. package/src/components/editor/output/ansi-reduce.ts +6 -0
  85. package/src/core/attachments/__tests__/toast.test.tsx +58 -0
  86. package/src/core/attachments/state.ts +21 -0
  87. package/src/core/attachments/toast.ts +15 -0
  88. package/src/core/cells/__tests__/cells.test.ts +52 -0
  89. package/src/core/cells/__tests__/error-report.test.ts +139 -0
  90. package/src/core/cells/__tests__/session.test.ts +50 -1
  91. package/src/core/cells/error-report.ts +63 -0
  92. package/src/core/cells/session.ts +28 -1
  93. package/src/core/config/pair.ts +5 -3
  94. package/src/core/edit-app.tsx +2 -2
  95. package/src/core/host/AGENTS.md +6 -0
  96. package/src/core/host/__tests__/events.test.ts +46 -0
  97. package/src/core/host/__tests__/notebook-client.test.ts +191 -0
  98. package/src/core/host/__tests__/protocol.test.ts +214 -0
  99. package/src/core/host/connection.ts +81 -0
  100. package/src/core/host/events.ts +60 -0
  101. package/src/core/host/notebook-client.ts +226 -0
  102. package/src/core/host/protocol.ts +1943 -0
  103. package/src/core/islands/__tests__/bootstrap.test.ts +26 -0
  104. package/src/core/islands/bootstrap.ts +1 -0
  105. package/src/core/run-app.tsx +2 -2
  106. package/src/core/runtime/__tests__/runtime.test.ts +55 -11
  107. package/src/core/runtime/runtime.ts +14 -1
  108. package/src/core/runtime/types.ts +2 -0
  109. package/src/core/websocket/__tests__/{useMarimoKernelConnection.test.ts → connection-state.test.ts} +15 -17
  110. package/src/core/websocket/__tests__/{useWebSocket.test.ts → createConnectionTransport.test.ts} +1 -1
  111. package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +140 -0
  112. package/src/core/websocket/__tests__/{useMarimoKernelConnection.hook.test.tsx → useNotebookSession.hook.test.tsx} +352 -65
  113. package/src/core/websocket/connection-state.ts +70 -0
  114. package/src/core/websocket/transports/__tests__/sse.test.ts +1 -4
  115. package/src/core/websocket/transports/create.ts +31 -0
  116. package/src/core/websocket/transports/transport.ts +12 -23
  117. package/src/core/websocket/types.ts +9 -39
  118. package/src/core/websocket/useNotebookClient.ts +80 -0
  119. package/src/core/websocket/{useMarimoKernelConnection.tsx → useNotebookSession.tsx} +67 -225
  120. package/src/mount.tsx +7 -3
  121. package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +84 -0
  122. package/src/utils/__tests__/dates.test.ts +116 -0
  123. package/src/utils/dates.ts +94 -0
  124. package/dist/error-entries-CzoYkIQZ.js +0 -56
  125. package/src/core/websocket/useWebSocket.tsx +0 -88
@@ -136,6 +136,90 @@ describe("LoadingDataTableComponent", () => {
136
136
  expect(downloadGeoJSON).toHaveBeenCalledTimes(1);
137
137
  });
138
138
 
139
+ it("refreshes export metadata when a dataframe transform changes the data", async () => {
140
+ const host = document.createElement("div");
141
+ const metadataFor = (name: string) => ({
142
+ geometry_columns: [{ name, encoding: "objects", crs: "EPSG:4326" }],
143
+ primary_geometry_column: name,
144
+ default_geometry_column: name,
145
+ formats: {
146
+ parquet: { available: true, reason: null, missing_packages: [] },
147
+ },
148
+ });
149
+ const getExportMetadata = vi
150
+ .fn()
151
+ .mockResolvedValueOnce(metadataFor("before"))
152
+ .mockResolvedValueOnce(metadataFor("after"));
153
+ const view = (data: string) => (
154
+ <Provider store={store}>
155
+ <TooltipProvider>
156
+ <LoadingDataTableComponent
157
+ label={null}
158
+ totalRows={1}
159
+ pagination={false}
160
+ pageSize={10}
161
+ selection={null}
162
+ showDownload={true}
163
+ showFilters={false}
164
+ showColumnSummaries={false}
165
+ showDataTypes={false}
166
+ showPageSizeSelector={false}
167
+ showColumnExplorer={false}
168
+ showRowExplorer={false}
169
+ showChartBuilder={false}
170
+ rowHeaders={[]}
171
+ fieldTypes={undefined}
172
+ totalColumns={1}
173
+ maxColumns="all"
174
+ hasStableRowId={false}
175
+ lazy={false}
176
+ host={host}
177
+ showSearch={false}
178
+ value={[]}
179
+ setValue={vi.fn()}
180
+ data={data}
181
+ search={vi.fn().mockResolvedValue({
182
+ data,
183
+ total_rows: 1,
184
+ cell_styles: null,
185
+ cell_hover_texts: null,
186
+ })}
187
+ download_as={vi.fn()}
188
+ download_geojson={vi.fn()}
189
+ get_export_metadata={getExportMetadata}
190
+ get_column_summaries={vi.fn()}
191
+ get_data_url={vi.fn() as GetDataUrl}
192
+ get_row_ids={vi.fn() as GetRowIds}
193
+ get_size_bytes={vi.fn().mockResolvedValue({ size_bytes: null })}
194
+ />
195
+ </TooltipProvider>
196
+ </Provider>
197
+ );
198
+ const { rerender } = render(
199
+ view(JSON.stringify([{ before: "POINT (1 2)" }])),
200
+ );
201
+ await waitFor(() =>
202
+ expect(
203
+ screen.getByRole("columnheader", { name: "before" }),
204
+ ).toBeInTheDocument(),
205
+ );
206
+ fireEvent.click(screen.getByTestId("export-button"));
207
+ await waitFor(() =>
208
+ expect(screen.getByTestId("export-summary-parquet")).toHaveTextContent(
209
+ "before",
210
+ ),
211
+ );
212
+
213
+ rerender(view(JSON.stringify([{ after: "POINT (3 4)" }])));
214
+
215
+ await waitFor(() =>
216
+ expect(screen.getByTestId("export-summary-parquet")).toHaveTextContent(
217
+ "after",
218
+ ),
219
+ );
220
+ expect(getExportMetadata).toHaveBeenCalledTimes(2);
221
+ });
222
+
139
223
  it("does not log duplicate keys when a pivot becomes empty", async () => {
140
224
  const host = document.createElement("div");
141
225
  const consoleError = vi
@@ -4,6 +4,9 @@ import {
4
4
  exactDateTime,
5
5
  getDateFormat,
6
6
  getShortTimeZone,
7
+ parseLocalISODate,
8
+ parseLocalISODateTime,
9
+ parseTimeToDate,
7
10
  prettyDate,
8
11
  timeAgo,
9
12
  } from "../dates";
@@ -191,6 +194,119 @@ describe("dates", () => {
191
194
  });
192
195
  });
193
196
 
197
+ describe("parseTimeToDate", () => {
198
+ it("parses 24-hour times", () => {
199
+ expect(parseTimeToDate("14:30")).toEqual(new Date(1970, 0, 1, 14, 30));
200
+ expect(parseTimeToDate("14:30:05")).toEqual(
201
+ new Date(1970, 0, 1, 14, 30, 5),
202
+ );
203
+ });
204
+
205
+ it("parses times with an am/pm suffix", () => {
206
+ expect(parseTimeToDate("2:30 PM")).toEqual(new Date(1970, 0, 1, 14, 30));
207
+ expect(parseTimeToDate("12:00 am")).toEqual(new Date(1970, 0, 1, 0, 0));
208
+ expect(parseTimeToDate("12:00 pm")).toEqual(new Date(1970, 0, 1, 12, 0));
209
+ });
210
+
211
+ it("rejects an out-of-range hour instead of letting Date normalize it", () => {
212
+ // new Date(1970, 0, 1, 25, 0) would silently become 01:00 the next day.
213
+ expect(parseTimeToDate("25:00")).toBeUndefined();
214
+ });
215
+
216
+ it("rejects an out-of-range minute instead of letting Date normalize it", () => {
217
+ // new Date(1970, 0, 1, 12, 99) would silently become 13:39.
218
+ expect(parseTimeToDate("12:99")).toBeUndefined();
219
+ });
220
+
221
+ it("rejects an out-of-range second", () => {
222
+ expect(parseTimeToDate("12:30:99")).toBeUndefined();
223
+ });
224
+
225
+ it("requires an hour from 1 to 12 with an am/pm suffix", () => {
226
+ expect(parseTimeToDate("0:30 am")).toBeUndefined();
227
+ expect(parseTimeToDate("13:30 pm")).toBeUndefined();
228
+ });
229
+
230
+ it("parses milliseconds", () => {
231
+ expect(parseTimeToDate("14:30:05.123")).toEqual(
232
+ new Date(1970, 0, 1, 14, 30, 5, 123),
233
+ );
234
+ });
235
+
236
+ it("pads a short millisecond fraction and truncates a long one", () => {
237
+ expect(parseTimeToDate("14:30:05.5")).toEqual(
238
+ new Date(1970, 0, 1, 14, 30, 5, 500),
239
+ );
240
+ expect(parseTimeToDate("14:30:05.123456")).toEqual(
241
+ new Date(1970, 0, 1, 14, 30, 5, 123),
242
+ );
243
+ });
244
+ });
245
+
246
+ describe("parseLocalISODate", () => {
247
+ it("parses a date-only string into local midnight", () => {
248
+ expect(parseLocalISODate("2024-01-15")).toEqual(new Date(2024, 0, 15));
249
+ });
250
+
251
+ it("returns undefined for a non-date-only string", () => {
252
+ expect(parseLocalISODate("2024-01-15T00:00:00")).toBeUndefined();
253
+ expect(parseLocalISODate("")).toBeUndefined();
254
+ expect(parseLocalISODate("not-a-date")).toBeUndefined();
255
+ });
256
+
257
+ it("returns undefined for a calendar date that doesn't exist", () => {
258
+ expect(parseLocalISODate("2024-02-30")).toBeUndefined();
259
+ expect(parseLocalISODate("2024-13-01")).toBeUndefined();
260
+ });
261
+ });
262
+
263
+ describe("parseLocalISODateTime", () => {
264
+ it("parses a 'T'-separated datetime into local wall-clock time", () => {
265
+ expect(parseLocalISODateTime("2024-01-15T08:30:00")).toEqual(
266
+ new Date(2024, 0, 15, 8, 30, 0),
267
+ );
268
+ });
269
+
270
+ it("parses a space-separated datetime", () => {
271
+ expect(parseLocalISODateTime("2024-01-15 08:30:00")).toEqual(
272
+ new Date(2024, 0, 15, 8, 30, 0),
273
+ );
274
+ });
275
+
276
+ it("defaults seconds to 0 when omitted", () => {
277
+ expect(parseLocalISODateTime("2024-01-15T08:30")).toEqual(
278
+ new Date(2024, 0, 15, 8, 30, 0),
279
+ );
280
+ });
281
+
282
+ it("ignores a trailing Z instead of shifting to UTC", () => {
283
+ expect(parseLocalISODateTime("2024-01-15T08:30:00Z")).toEqual(
284
+ new Date(2024, 0, 15, 8, 30, 0),
285
+ );
286
+ });
287
+
288
+ it("ignores a trailing UTC offset instead of shifting the time", () => {
289
+ expect(parseLocalISODateTime("2024-01-15T08:30:00+05:00")).toEqual(
290
+ new Date(2024, 0, 15, 8, 30, 0),
291
+ );
292
+ expect(parseLocalISODateTime("2024-01-15T08:30:00-0500")).toEqual(
293
+ new Date(2024, 0, 15, 8, 30, 0),
294
+ );
295
+ });
296
+
297
+ it("ignores sub-second precision", () => {
298
+ expect(parseLocalISODateTime("2024-01-15T08:30:00.123")).toEqual(
299
+ new Date(2024, 0, 15, 8, 30, 0),
300
+ );
301
+ });
302
+
303
+ it("returns undefined for a date-only or unparsable string", () => {
304
+ expect(parseLocalISODateTime("2024-01-15")).toBeUndefined();
305
+ expect(parseLocalISODateTime("not-a-datetime")).toBeUndefined();
306
+ expect(parseLocalISODateTime("")).toBeUndefined();
307
+ });
308
+ });
309
+
194
310
  describe("getDateFormat", () => {
195
311
  it("returns the correct format for date", () => {
196
312
  expect(getDateFormat("2023-05-15")).toBe("yyyy-MM-dd");
@@ -165,6 +165,44 @@ export function dateToLocalISOTime(d: Date): string {
165
165
  return `${pad2(d.getHours())}:${pad2(d.getMinutes())}:${pad2(d.getSeconds())}`;
166
166
  }
167
167
 
168
+ /**
169
+ * Parse a time string into a Date anchored at the Unix epoch day. Supports
170
+ * the backend's wire format (`HH:MM:SS`, as produced by `dateToLocalISOTime`)
171
+ * as well as looser pasted text: optional seconds, optional milliseconds,
172
+ * and an optional `am`/`pm` suffix (e.g. `"14:30"`, `"2:30 PM"`,
173
+ * `"14:30:00.123"`).
174
+ */
175
+ export function parseTimeToDate(value: string): Date | undefined {
176
+ const match = /^(\d{1,2}):(\d{2})(?::(\d{2}))?(?:\.(\d+))?\s*(am|pm)?$/i.exec(
177
+ value.trim(),
178
+ );
179
+ if (!match) {
180
+ return undefined;
181
+ }
182
+ const [, hStr, mStr, sStr, msStr, ampm] = match;
183
+ let hour = Number.parseInt(hStr, 10);
184
+ const minute = Number.parseInt(mStr, 10);
185
+ const second = sStr ? Number.parseInt(sStr, 10) : 0;
186
+ const ms = msStr ? Number.parseInt(msStr.padEnd(3, "0").slice(0, 3), 10) : 0;
187
+ if (minute > 59 || second > 59) {
188
+ return undefined;
189
+ }
190
+ if (ampm) {
191
+ if (hour < 1 || hour > 12) {
192
+ return undefined;
193
+ }
194
+ const isPm = ampm.toLowerCase() === "pm";
195
+ if (hour === 12) {
196
+ hour = isPm ? 12 : 0;
197
+ } else if (isPm) {
198
+ hour += 12;
199
+ }
200
+ } else if (hour > 23) {
201
+ return undefined;
202
+ }
203
+ return new Date(1970, 0, 1, hour, minute, second, ms);
204
+ }
205
+
168
206
  /**
169
207
  * Format a Date as `YYYY-MM-DDTHH:MM:SS` (no timezone suffix) using local
170
208
  * fields. See `dateToLocalISODate` for the rationale on local vs UTC.
@@ -173,6 +211,62 @@ export function dateToLocalISODateTime(d: Date): string {
173
211
  return `${dateToLocalISODate(d)}T${dateToLocalISOTime(d)}`;
174
212
  }
175
213
 
214
+ /**
215
+ * Parse a `YYYY-MM-DD` string into a Date anchored at local midnight.
216
+ *
217
+ * Unlike `new Date("YYYY-MM-DD")`, which the spec parses as UTC midnight,
218
+ * this treats the string as a calendar date in the viewer's own timezone.
219
+ * Returns undefined if the string doesn't match, or encodes a calendar
220
+ * date that doesn't exist (e.g. month 13, Feb 30), since `Date` would
221
+ * otherwise silently roll those over into a different date.
222
+ */
223
+ export function parseLocalISODate(value: string): Date | undefined {
224
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value.trim());
225
+ if (!match) {
226
+ return undefined;
227
+ }
228
+ const [, yStr, mStr, dStr] = match;
229
+ const year = Number.parseInt(yStr, 10);
230
+ const month = Number.parseInt(mStr, 10);
231
+ const day = Number.parseInt(dStr, 10);
232
+ const date = new Date(year, month - 1, day);
233
+ if (
234
+ date.getFullYear() !== year ||
235
+ date.getMonth() !== month - 1 ||
236
+ date.getDate() !== day
237
+ ) {
238
+ return undefined;
239
+ }
240
+ return date;
241
+ }
242
+
243
+ /**
244
+ * Parse an ISO-ish `YYYY-MM-DD[T ]HH:MM[:SS[.sss]][Z|+HH:MM]` datetime string
245
+ * as wall-clock local time, ignoring any trailing `Z` or UTC offset.
246
+ *
247
+ * This keeps pasted datetimes consistent with the date/time pickers' local-
248
+ * time basis: pasting `2024-01-15T08:30:00Z` displays `08:30` instead of
249
+ * being shifted by the viewer's timezone.
250
+ */
251
+ export function parseLocalISODateTime(value: string): Date | undefined {
252
+ const match =
253
+ /^(\d{4})-(\d{2})-(\d{2})[T ](\d{2}):(\d{2})(?::(\d{2})(?:\.\d+)?)?(?:Z|[+-]\d{2}:?\d{2})?$/.exec(
254
+ value.trim(),
255
+ );
256
+ if (!match) {
257
+ return undefined;
258
+ }
259
+ const [, yStr, moStr, dStr, hStr, miStr, sStr] = match;
260
+ return new Date(
261
+ Number.parseInt(yStr, 10),
262
+ Number.parseInt(moStr, 10) - 1,
263
+ Number.parseInt(dStr, 10),
264
+ Number.parseInt(hStr, 10),
265
+ Number.parseInt(miStr, 10),
266
+ sStr ? Number.parseInt(sStr, 10) : 0,
267
+ );
268
+ }
269
+
176
270
  export const supportedDateFormats = ["yyyy", "yyyy-MM", "yyyy-MM-dd"] as const;
177
271
  export type DateFormat = (typeof supportedDateFormats)[number];
178
272
 
@@ -1,56 +0,0 @@
1
- import { At as e, N as t, b as n, d as r, jt as i, kt as a } from "./cells-C8iCYvLU.js";
2
- import { C as o } from "./select-BButFi-d.js";
3
- //#region src/core/errors/error-entries.ts
4
- function s(t) {
5
- if (e(t.mimetype)) {
6
- if (!Array.isArray(t.data)) return null;
7
- let e = t.data.filter((e) => !e.type.includes("ancestor"));
8
- return e.length === 0 ? null : { errorData: e };
9
- }
10
- return i(t.mimetype) ? typeof t.data != "string" || t.data.length === 0 ? null : {
11
- errorData: [],
12
- tracebackHtml: t.data
13
- } : null;
14
- }
15
- function c(e) {
16
- return e.some((e) => "traceback" in e && e.traceback);
17
- }
18
- function l(e) {
19
- let t = e?.find((e) => i(e.mimetype));
20
- if (!(!t || typeof t.data != "string" || t.data.length === 0)) return t.data;
21
- }
22
- function u(e, t) {
23
- if (e.tracebackHtml) return e.tracebackHtml;
24
- if (!c(e.errorData)) return l(t);
25
- }
26
- function d(e) {
27
- let { cellIds: n, cellRuntime: i, cellData: o } = e.get(r), c = [];
28
- for (let [e, r] of n.inOrderIds.entries()) {
29
- let n = i[r], l = n.output;
30
- if (!l || !a(l.mimetype)) continue;
31
- let d = s(l);
32
- if (!d) continue;
33
- let f = t(o[r].name, e), p = o[r].lastCodeRun ?? o[r].code;
34
- c.push({
35
- cellId: r,
36
- cellName: f,
37
- cellCode: p,
38
- errorData: d.errorData,
39
- tracebackHtml: u(d, n.consoleOutputs)
40
- });
41
- }
42
- return c;
43
- }
44
- function f(e) {
45
- return e.type === "setup-refs" ? "The setup cell cannot have references" : e.type === "cycle" ? "This cell is in a cycle" : e.type === "multiple-defs" ? `The variable '${e.name}' was defined by another cell` : e.type === "import-star" || e.type === "ancestor-stopped" || e.type === "ancestor-prevented" || e.type === "exception" || e.type === "strict-exception" ? e.msg : e.type === "interruption" ? "This cell was interrupted and needs to be re-run" : e.type === "syntax" || e.type === "unknown" || e.type === "sql-error" ? e.msg : e.type === "internal" ? e.msg || "An internal error occurred" : (o(e), "Unknown error");
46
- }
47
- function p(e) {
48
- let t = f(e);
49
- return "traceback" in e && e.traceback && (t += `\n\nTraceback:\n${n(e.traceback)}`), t;
50
- }
51
- function m(e) {
52
- let t = [e.cellName];
53
- return e.tracebackHtml && t.push(n(e.tracebackHtml)), e.errorData.length > 0 && t.push(e.errorData.map(p).join("\n\n")), t.join("\n\n");
54
- }
55
- //#endregion
56
- export { d as i, m as n, p as r, f as t };
@@ -1,88 +0,0 @@
1
- /* Copyright 2026 Marimo. All rights reserved. */
2
-
3
- import { useEffect, useState } from "react";
4
- import { Logger } from "@/utils/Logger";
5
- import { createPyodideConnection } from "../wasm/bridge";
6
- import { isWasm } from "../wasm/utils";
7
- import { BasicTransport } from "./transports/basic";
8
- import { SseTransport } from "./transports/sse";
9
- import { WsTransport } from "./transports/ws";
10
- import type { IConnectionTransport } from "./transports/transport";
11
-
12
- interface UseConnectionTransportOptions {
13
- url: () => string;
14
- static: boolean;
15
- transportType: "websocket" | "sse";
16
- /** Request headers for the SSE transport (auth); unused by WebSockets. */
17
- headers: () => Record<string, string>;
18
- waitToConnect: () => Promise<void>;
19
- onOpen: (event: WebSocketEventMap["open"]) => void;
20
- onMessage: (event: WebSocketEventMap["message"]) => void;
21
- onClose: (event: WebSocketEventMap["close"]) => void;
22
- onError: (event: WebSocketEventMap["error"]) => void;
23
- }
24
-
25
- export function createConnectionTransport(
26
- options: Pick<
27
- UseConnectionTransportOptions,
28
- "url" | "static" | "transportType" | "headers"
29
- >,
30
- ): IConnectionTransport {
31
- if (options.static) {
32
- return BasicTransport.empty();
33
- }
34
- if (isWasm()) {
35
- return createPyodideConnection();
36
- }
37
- // urlProvider is passed lazily; it may change after a runtime redirect.
38
- if (options.transportType === "sse") {
39
- return new SseTransport(options.url, options.headers);
40
- }
41
- return new WsTransport(options.url);
42
- }
43
-
44
- /**
45
- * A hook for creating a connection transport with React.
46
- */
47
- export function useConnectionTransport(options: UseConnectionTransportOptions) {
48
- const { onOpen, onMessage, onClose, onError, waitToConnect } = options;
49
-
50
- // oxlint-disable-next-line react/hook-use-state
51
- const [transport] = useState<IConnectionTransport>(() => {
52
- const socket = createConnectionTransport(options);
53
-
54
- socket.addEventListener("open", onOpen);
55
- socket.addEventListener("close", onClose);
56
- socket.addEventListener("error", onError);
57
- socket.addEventListener("message", onMessage);
58
-
59
- return socket;
60
- });
61
-
62
- useEffect(() => {
63
- // If it's closed, reconnect
64
- // This starts closed, so we need to connect for the first time
65
- if (transport.readyState === WebSocket.CLOSED) {
66
- void waitToConnect()
67
- .then(() => transport.reconnect())
68
- .catch((error) => {
69
- Logger.error("Healthy connection never made", error);
70
- transport.close();
71
- });
72
- }
73
-
74
- return () => {
75
- Logger.warn(
76
- "useConnectionTransport is unmounting. This likely means there is a bug.",
77
- );
78
- transport.close();
79
- transport.removeEventListener("open", onOpen);
80
- transport.removeEventListener("close", onClose);
81
- transport.removeEventListener("error", onError);
82
- transport.removeEventListener("message", onMessage);
83
- };
84
- // oxlint-disable-next-line react-hooks/exhaustive-deps
85
- }, [transport]);
86
-
87
- return transport;
88
- }