@marimo-team/islands 0.25.3-dev5 → 0.25.3-dev51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{ConnectedDataExplorerComponent-CNMliVzw.js → ConnectedDataExplorerComponent-Da5OoEUL.js} +14 -14
- package/dist/{ErrorBoundary-BiZWOrv4.js → ErrorBoundary-CbxCC2kk.js} +8 -8
- package/dist/{abnfDiagram-VCTEODGH-D-Ez91gV.js → abnfDiagram-VCTEODGH-Bz3mrprX.js} +2 -2
- package/dist/any-language-editor-k2B8YI3Y.js +75 -0
- package/dist/{architectureDiagram-5GKGNRK7-CTpcNtO8.js → architectureDiagram-5GKGNRK7-Cs5ujb9K.js} +1 -1
- package/dist/{arrays-CN1djC4k.js → arrays-R6JsImiT.js} +8 -0
- package/dist/{button-CvjkwdSk.js → button-BxB3RDjN.js} +1 -1
- package/dist/{capabilities-CQ5IjhzL.js → capabilities-BgR0osfN.js} +2 -2
- package/dist/{chat-ui-0lKdOoQ3.js → chat-ui-C1Gjgokv.js} +1103 -1103
- package/dist/{check-EDwSwP89.js → check-P35dc2dH.js} +1 -1
- package/dist/{chunk-IMKFNOWR-DGxi-zeo.js → chunk-IMKFNOWR-CcTSaxZt.js} +1 -1
- package/dist/{chunk-SHT3W25Y-CV9J3rAB.js → chunk-SHT3W25Y-DcJcjOaO.js} +1 -1
- package/dist/{chunk-SVP7TREG-BL-9Fqgg.js → chunk-SVP7TREG-BVtDTjaF.js} +1 -1
- package/dist/{chunk-TICWLB2K-Cu_XMjDM.js → chunk-TICWLB2K-BkDFkh8q.js} +1 -1
- package/dist/{chunk-YOKDWASO-DPZk8vhS.js → chunk-YOKDWASO-DKplTrBY.js} +20 -20
- package/dist/{classDiagram-ZZMXUADV-BXXV13Hq.js → classDiagram-ZZMXUADV-CfAt7HT5.js} +2 -2
- package/dist/{classDiagram-v2-VYDZK3BY-B7FXJ1Cp.js → classDiagram-v2-VYDZK3BY-CGEjNYa2.js} +2 -2
- package/dist/{code-visibility-CW5_8ZOI.js → code-visibility-DoZ9lxzx.js} +6 -6
- package/dist/{common-mzWw4FdI.js → common-D1PacyXO.js} +3112 -3015
- package/dist/{copy-icon-Ft7t9bqz.js → copy-icon-CPoxUG7G.js} +3 -3
- package/dist/{cynefinDiagram-5FMLGOSQ-CH96rwba.js → cynefinDiagram-5FMLGOSQ-BW6NmfJi.js} +1 -1
- package/dist/{dagre-GXQ25YYZ-BD3PmpFY.js → dagre-GXQ25YYZ-CWg3CTuF.js} +1 -1
- package/dist/{data-grid-overlay-editor-CJmehSdc.js → data-grid-overlay-editor-B50UpkXd.js} +5 -5
- package/dist/{data-grid-types-BH_r7g7l.js → data-grid-types-0PJ-9N5n.js} +1 -1
- package/dist/{diagram-S7CK7UJ4-BsSJJldB.js → diagram-S7CK7UJ4-BPxtoS-z.js} +1 -1
- package/dist/{diagram-UQ7AKVKN-muQtNZc4.js → diagram-UQ7AKVKN-DA-vJdFL.js} +1 -1
- package/dist/{diagram-VX7I27RA-CdhuZAqR.js → diagram-VX7I27RA-D6bkR84S.js} +1 -1
- package/dist/{diagram-Z3DM3KII-BD8mXjOj.js → diagram-Z3DM3KII-m60g4cAT.js} +1 -1
- package/dist/{dialog-CEBFZNyc.js → dialog-C1PcpCqX.js} +3 -3
- package/dist/{dist-DpBpA_n1.js → dist-Lm-D-Vr6.js} +3 -3
- package/dist/{dropdown-menu-CP_qCYHC.js → dropdown-menu-MG5hiCIa.js} +76 -76
- package/dist/{ebnfDiagram-PWID7BFC-BIN1sweE.js → ebnfDiagram-PWID7BFC-CK99sho2.js} +2 -2
- package/dist/{erDiagram-RLTQ6QDP-Ctp5cfoN.js → erDiagram-RLTQ6QDP-Bryq0tdE.js} +1 -1
- package/dist/{error-banner-D-WCIiMt.js → error-banner-Sl2FlSJ4.js} +2 -2
- package/dist/{error-entries-DSvFWT_M.js → error-entries-CMNtA8NK.js} +959 -959
- package/dist/{feedback-button-BcT1FBud.js → feedback-button-DqPqwryB.js} +29 -29
- package/dist/{flowDiagram-HODETNUW-3EfLrlFb.js → flowDiagram-HODETNUW-D-3aRpfS.js} +2 -2
- package/dist/{formats-CeOmU1MU.js → formats-6pLWWrY2.js} +3 -3
- package/dist/{ganttDiagram-EL5Y4UJY-BQT16JSc.js → ganttDiagram-EL5Y4UJY-Dh_4cBJZ.js} +2 -2
- package/dist/{glide-data-editor-XckatD0j.js → glide-data-editor-BxMhUd3K.js} +370 -368
- package/dist/{glide-utils-DJyqh0EM.js → glide-utils-Bt8_pBa2.js} +16 -16
- package/dist/{globals-jRrLCkrP.js → globals-DTJa_doJ.js} +3 -3
- package/dist/{highlighted-body-KPVGNVTW-zwjq21tf.js → highlighted-body-KPVGNVTW-3Ziwfz-V.js} +2 -2
- package/dist/{html-to-image-B8s5SgGm.js → html-to-image-By72ALyI.js} +29 -29
- package/dist/{infoDiagram-27XIBGKW-DipQoHxl.js → infoDiagram-27XIBGKW-s1C1kgfU.js} +1 -1
- package/dist/{ishikawaDiagram-5VMMS53U-DgFMjSGz.js → ishikawaDiagram-5VMMS53U-CCsRgolY.js} +1 -1
- package/dist/{kanban-definition-UXKFOSKX-w1ACVjgl.js → kanban-definition-UXKFOSKX-BrmxDlcc.js} +1 -1
- package/dist/{label-BDKF3Afc.js → label-BRnUC0NX.js} +2 -2
- package/dist/{linear-Cnaq3Tfb.js → linear-DWERh7eI.js} +121 -73
- package/dist/{loader-BQOfjTIJ.js → loader-Bm8cy3e8.js} +12 -12
- package/dist/main.js +2358 -2221
- package/dist/{mermaid-CHo49Yv-.js → mermaid-DKI0869I.js} +38 -38
- package/dist/mermaid-HWGCJPDP-Du3-HPQA.js +2 -0
- package/dist/{mindmap-definition-YA3MSWOX-CrT0KSfX.js → mindmap-definition-YA3MSWOX-641xJysF.js} +1 -1
- package/dist/{number-overlay-editor-pZx8dUDV.js → number-overlay-editor-Z_H6x5b-.js} +3 -3
- package/dist/{objects-CzUgoMIF.js → objects-DCmMvZtn.js} +1 -3
- package/dist/{once-BtE-wKRL.js → once-eG8Vmvjd.js} +1 -1
- package/dist/{pegDiagram-XKGWAZYB-BqvywNJA.js → pegDiagram-XKGWAZYB-keVUAUeS.js} +2 -2
- package/dist/{pieDiagram-E7YTZNPT-BfxJ_XCc.js → pieDiagram-E7YTZNPT-DHktXPO_.js} +1 -1
- package/dist/precisionRound-DhAzeRcm.js +49 -0
- package/dist/{process-output-CKRXyszb.js → process-output-D-OMW5sV.js} +4 -4
- package/dist/{quadrantDiagram-AXDQQJYC-Ccw0xMjN.js → quadrantDiagram-AXDQQJYC-Blrm7V7_.js} +1 -1
- package/dist/{railroadDiagram-O6MQD6OU-8vFLVTPu.js → railroadDiagram-O6MQD6OU-BVEF4OV_.js} +2 -2
- package/dist/{react-vega-BED0DjiB.js → react-vega-C4T9Rb_w.js} +12284 -12270
- package/dist/{requirementDiagram-BXWQKSXE-a_MxGvxe.js → requirementDiagram-BXWQKSXE-CWo-UAKV.js} +1 -1
- package/dist/{reveal-component-79ez8-D5.js → reveal-component-tAB5K3QE.js} +10 -10
- package/dist/{select-BButFi-d.js → select-B0LRDH0g.js} +4 -4
- package/dist/{sequenceDiagram-WJ2MYXX4-CXFMnxGh.js → sequenceDiagram-WJ2MYXX4-CHQvLnq4.js} +1 -1
- package/dist/{spec-BwarBpUJ.js → spec-C-LRKGtT.js} +4 -4
- package/dist/{stateDiagram-D77RDMKH-DQeA_CwI.js → stateDiagram-D77RDMKH-BOJZgbUJ.js} +2 -2
- package/dist/{stateDiagram-v2-MP3YSRHH-BxxQucm1.js → stateDiagram-v2-MP3YSRHH-C53XXdYH.js} +2 -2
- package/dist/style.css +1 -1
- package/dist/{swimlanes-42K2YHIH-Do6L-cYg.js → swimlanes-42K2YHIH-D2o1gOtw.js} +1 -1
- package/dist/{swimlanesDiagram-VR7AAH4N-DUzPvSVU.js → swimlanesDiagram-VR7AAH4N-BHFzN1Wk.js} +2 -2
- package/dist/{swiper-component-DKntyn8p.js → swiper-component-DfhRvAFi.js} +2 -2
- package/dist/{time-BieKwdYS.js → time-BaMbJs4z.js} +18 -18
- package/dist/{timeline-definition-24CTP7MA-D3c46WM5.js → timeline-definition-24CTP7MA-rv7QUa46.js} +1 -1
- package/dist/{toDate-vIXljnsr.js → toDate-CjPuEPou.js} +21 -20
- package/dist/{toast-B9UMEi8u.js → toast-CFWpihJU.js} +4 -4
- package/dist/{tooltip-BO1REbqs.js → tooltip-CKO1pHJo.js} +2 -2
- package/dist/{use-restore-focus-BEM7sFWl.js → use-restore-focus-DBCITLeF.js} +1 -1
- package/dist/{useAsyncData-Deh5kszT.js → useAsyncData-ChgNtdmE.js} +15 -14
- package/dist/{useDateFormatter-D8XruNGG.js → useDateFormatter-c_yJtkAe.js} +2 -2
- package/dist/{useDeepCompareMemoize-DXwzRINI.js → useDeepCompareMemoize-Bet7kSsI.js} +1 -1
- package/dist/{useIframeCapabilities-DBETvvPl.js → useIframeCapabilities-BkdeVdXC.js} +1 -1
- package/dist/{useLifecycle-j7D9uD1o.js → useLifecycle-VVmz_24o.js} +3 -3
- package/dist/{useTheme-CRnjkku0.js → useTheme-DympCsQg.js} +5 -5
- package/dist/{vega-component-DTG6zxaY.js → vega-component-DnfQeoyt.js} +147 -147
- package/dist/{vega-embed-container-BNHE18G9.js → vega-embed-container--LYZqguO.js} +2 -2
- package/dist/{vega-loader.browser-DNST6uVf.js → vega-loader.browser-B9Pz0EGP.js} +298 -507
- package/dist/{vennDiagram-4TSXK5OY-BSZD8JcZ.js → vennDiagram-4TSXK5OY-D36Zk7t-.js} +1 -1
- package/dist/{wardleyDiagram-VM6X3IG4-48cUWkfA.js → wardleyDiagram-VM6X3IG4-BLBolvyQ.js} +1 -1
- package/dist/{xychartDiagram-S5SC5T6Z-CswYgYeu.js → xychartDiagram-S5SC5T6Z-CfH57l0v.js} +2 -2
- package/package.json +4 -4
- package/src/__tests__/fixtures/{pair-preview.ts → pair-config.ts} +3 -2
- package/src/__tests__/host-imports.test.ts +1 -0
- package/src/__tests__/mount.test.ts +8 -8
- package/src/components/app-config/ai-config.tsx +5 -1
- package/src/components/chat/__tests__/message-queue.test.tsx +17 -0
- package/src/components/chat/chat-panel.tsx +3 -7
- package/src/components/chat/chat-utils.ts +4 -8
- package/src/components/data-table/__tests__/context-menu-interaction.test.tsx +130 -0
- package/src/components/data-table/__tests__/date-popover.test.tsx +72 -0
- package/src/components/data-table/__tests__/export-actions.test.tsx +417 -6
- package/src/components/data-table/column-visibility-dropdown.tsx +8 -13
- package/src/components/data-table/columns.tsx +2 -1
- package/src/components/data-table/context-menu.tsx +36 -20
- package/src/components/data-table/date-popover.tsx +6 -4
- package/src/components/data-table/export-actions.tsx +263 -161
- package/src/components/data-table/row-viewer-panel/row-viewer.tsx +2 -2
- package/src/components/data-table/schemas.ts +14 -36
- package/src/components/editor/Output.tsx +5 -1
- package/src/components/editor/__tests__/dynamic-favicon.test.tsx +111 -18
- package/src/components/editor/actions/__tests__/pair-with-agent-commands.test.ts +80 -252
- package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +186 -34
- package/src/components/editor/actions/pair-with-agent-commands.ts +42 -51
- package/src/components/editor/actions/pair-with-agent-modal.tsx +96 -36
- package/src/components/editor/ai/add-cell-with-ai.tsx +3 -3
- package/src/components/editor/cell/CreateCellButton.tsx +8 -10
- package/src/components/editor/cell/code/__tests__/language-toggle.test.tsx +54 -0
- package/src/components/editor/cell/code/cell-editor.tsx +1 -1
- package/src/components/editor/cell/code/language-toggle.tsx +8 -7
- package/src/components/editor/chrome/wrapper/__tests__/attachment-status.test.tsx +277 -78
- package/src/components/editor/chrome/wrapper/footer-items/attachment-status.tsx +196 -41
- package/src/components/editor/connections/components.tsx +1 -1
- package/src/components/editor/controls/__tests__/command-palette.test.tsx +53 -0
- package/src/components/editor/controls/command-palette.tsx +22 -10
- package/src/components/editor/dynamic-favicon.tsx +21 -40
- package/src/components/editor/errors/__tests__/send-error-report-button.test.tsx +139 -36
- package/src/components/editor/errors/send-error-report-button.tsx +10 -7
- package/src/components/editor/file-tree/file-viewer.tsx +4 -2
- package/src/components/editor/output/MarimoTracebackOutput.tsx +28 -22
- package/src/components/editor/output/console/ConsoleOutput.tsx +8 -18
- package/src/components/pages/home-page.tsx +4 -4
- package/src/components/slides/slide-cell-view.tsx +1 -1
- package/src/components/tracing/tracing.tsx +2 -2
- package/src/core/attachments/__tests__/toast.test.tsx +58 -0
- package/src/core/attachments/state.ts +5 -8
- package/src/core/attachments/toast.ts +15 -0
- package/src/core/cells/__tests__/error-report.test.ts +53 -3
- package/src/core/cells/__tests__/notebook-outline.test.ts +113 -0
- package/src/core/cells/__tests__/session.test.ts +18 -3
- package/src/core/cells/cells.ts +2 -1
- package/src/core/cells/error-report.ts +14 -6
- package/src/core/cells/pending-delete-service.ts +2 -6
- package/src/core/cells/session.ts +19 -1
- package/src/core/config/pair.ts +5 -3
- package/src/core/datasets/__tests__/data-source-discovery.test.ts +4 -4
- package/src/core/datasets/data-source-discovery.ts +4 -6
- package/src/core/edit-app.tsx +2 -2
- package/src/core/host/AGENTS.md +11 -2
- package/src/core/host/__tests__/attachments.test.ts +56 -0
- package/src/core/host/__tests__/document.test.ts +126 -0
- package/src/core/host/__tests__/events.test.ts +49 -0
- package/src/core/host/__tests__/fixtures.ts +41 -0
- package/src/core/host/__tests__/notebook-client.test.ts +496 -0
- package/src/core/host/connection.ts +81 -0
- package/src/core/host/events.ts +68 -0
- package/src/core/host/notebook-client.ts +316 -0
- package/src/core/host/resources/attachments.ts +19 -0
- package/src/core/host/resources/document.ts +101 -0
- package/src/core/hotkeys/actions.ts +2 -4
- package/src/core/islands/__tests__/bootstrap.test.ts +0 -26
- package/src/core/islands/bootstrap.ts +0 -1
- package/src/core/network/connection.ts +4 -2
- package/src/core/run-app.tsx +2 -2
- package/src/core/runtime/__tests__/runtime.test.ts +5 -12
- package/src/core/runtime/runtime.ts +5 -5
- package/src/core/static/files.ts +2 -0
- package/src/core/websocket/__tests__/{useMarimoKernelConnection.test.ts → connection-state.test.ts} +15 -17
- package/src/core/websocket/__tests__/{useWebSocket.test.ts → createConnectionTransport.test.ts} +1 -1
- package/src/core/websocket/__tests__/useNotebookClient.hook.test.tsx +149 -0
- package/src/core/websocket/__tests__/{useMarimoKernelConnection.hook.test.tsx → useNotebookSession.hook.test.tsx} +168 -253
- package/src/core/websocket/connection-state.ts +70 -0
- package/src/core/websocket/state.ts +7 -0
- package/src/core/websocket/transports/__tests__/sse.test.ts +18 -4
- package/src/core/websocket/transports/create.ts +31 -0
- package/src/core/websocket/transports/sse.ts +14 -2
- package/src/core/websocket/transports/transport.ts +12 -23
- package/src/core/websocket/types.ts +9 -39
- package/src/core/websocket/useNotebookClient.ts +78 -0
- package/src/core/websocket/{useMarimoKernelConnection.tsx → useNotebookSession.tsx} +53 -233
- package/src/hooks/__tests__/useAsyncData.test.tsx +148 -0
- package/src/hooks/useAsyncData.ts +3 -3
- package/src/hooks/useDataSourceDiscovery.ts +6 -4
- package/src/hooks/useElapsedTime.ts +3 -3
- package/src/hooks/useInputHistory.ts +4 -7
- package/src/hooks/useResizeObserver.ts +4 -7
- package/src/mount.tsx +3 -3
- package/src/plugins/core/registerReactComponent.tsx +1 -1
- package/src/plugins/impl/DropdownPlugin.tsx +2 -6
- package/src/plugins/impl/FormPlugin.tsx +1 -1
- package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +84 -0
- package/src/plugins/impl/anywidget/__tests__/registry.test.ts +2 -0
- package/src/plugins/impl/anywidget/__tests__/widget-binding.test.ts +241 -2
- package/src/plugins/impl/anywidget/runtime.ts +21 -9
- package/src/plugins/impl/anywidget/widget-binding.ts +160 -82
- package/src/plugins/impl/code/any-language-editor.tsx +2 -2
- package/src/plugins/impl/data-editor/glide-data-editor.tsx +8 -5
- package/src/plugins/impl/data-editor/glide-utils.ts +3 -1
- package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +5 -3
- package/src/plugins/impl/panel/PanelPlugin.tsx +15 -16
- package/src/plugins/impl/panel/__tests__/utils.test.ts +39 -1
- package/src/plugins/impl/panel/utils.ts +17 -0
- package/src/plugins/impl/vega/make-selectable.ts +3 -6
- package/src/stories/__tests__/editor-story.test.tsx +33 -0
- package/src/stories/editor.stories.tsx +24 -17
- package/src/utils/__tests__/arrays.test.ts +35 -0
- package/src/utils/__tests__/numbers.test.ts +48 -1
- package/src/utils/__tests__/objects.test.ts +21 -0
- package/src/utils/arrays.ts +10 -0
- package/src/utils/numbers.ts +28 -17
- package/src/utils/objects.ts +3 -7
- package/src/utils/once.ts +19 -19
- package/dist/any-language-editor-DZ_YTVus.js +0 -76
- package/dist/mermaid-HWGCJPDP-ByCsZnYF.js +0 -2
- package/dist/precisionRound-DMIVQrt2.js +0 -97
- package/src/core/host/__tests__/protocol.test.ts +0 -214
- package/src/core/host/protocol.ts +0 -1943
- package/src/core/websocket/useWebSocket.tsx +0 -88
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
TableIcon,
|
|
15
15
|
} from "lucide-react";
|
|
16
16
|
import React from "react";
|
|
17
|
+
import useEvent from "react-use-event-hook";
|
|
17
18
|
import { downloadSizeLimitAtom } from "./download-policy/atoms";
|
|
18
19
|
import type {
|
|
19
20
|
DownloadAsArgs,
|
|
@@ -22,6 +23,7 @@ import type {
|
|
|
22
23
|
ExportMetadata,
|
|
23
24
|
GetExportMetadata,
|
|
24
25
|
} from "./schemas";
|
|
26
|
+
import { useAsyncData } from "@/hooks/useAsyncData";
|
|
25
27
|
import { logNever } from "@/utils/assertNever";
|
|
26
28
|
import { cn } from "@/utils/cn";
|
|
27
29
|
import { copyToClipboard } from "@/utils/copy";
|
|
@@ -274,19 +276,130 @@ const failureTitleForAction = (action: ExportAction): string =>
|
|
|
274
276
|
const failureDescription = (error: unknown): string =>
|
|
275
277
|
typeof error === "string" ? error : prettyError(error);
|
|
276
278
|
|
|
279
|
+
const OBSOLETE_GEOMETRY_MESSAGE =
|
|
280
|
+
"The selected geometry is no longer available. Choose another geometry to export.";
|
|
281
|
+
|
|
282
|
+
type GeographicBlocker =
|
|
283
|
+
| "format-unavailable"
|
|
284
|
+
| "geometry-required"
|
|
285
|
+
| "choose-geometry"
|
|
286
|
+
| "obsolete-geometry"
|
|
287
|
+
| "missing-crs";
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Find the first reason that a geographic export cannot start with the
|
|
291
|
+
* selected geometry. The dialog uses it to disable a format, and the retry
|
|
292
|
+
* after a package installation uses it to decide whether to export.
|
|
293
|
+
*/
|
|
294
|
+
function geographicBlocker(
|
|
295
|
+
metadata: ExportMetadata,
|
|
296
|
+
format: GeographicFormat,
|
|
297
|
+
geometryColumn: string | null,
|
|
298
|
+
): GeographicBlocker | null {
|
|
299
|
+
if (metadata.formats[format]?.available === false) {
|
|
300
|
+
return "format-unavailable";
|
|
301
|
+
}
|
|
302
|
+
const selected = metadata.geometry_columns.find(
|
|
303
|
+
(column) => column.name === geometryColumn,
|
|
304
|
+
);
|
|
305
|
+
if (geometryColumn !== null && !selected) {
|
|
306
|
+
return "obsolete-geometry";
|
|
307
|
+
}
|
|
308
|
+
if (metadata.geometry_columns.length === 0) {
|
|
309
|
+
return format === "geojson" ? "geometry-required" : null;
|
|
310
|
+
}
|
|
311
|
+
if (!selected) {
|
|
312
|
+
return "choose-geometry";
|
|
313
|
+
}
|
|
314
|
+
if (format === "geojson" && !selected.crs) {
|
|
315
|
+
return "missing-crs";
|
|
316
|
+
}
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const geometryDisplayName = (name: string | null): string | null =>
|
|
321
|
+
name === "" ? "(unnamed geometry)" : name;
|
|
322
|
+
|
|
323
|
+
function geographicBlockerReason(
|
|
324
|
+
blocker: GeographicBlocker | null,
|
|
325
|
+
formatLabel: string,
|
|
326
|
+
geometryColumn: string | null,
|
|
327
|
+
): string | null {
|
|
328
|
+
switch (blocker) {
|
|
329
|
+
case "choose-geometry":
|
|
330
|
+
return `Choose a primary geometry column to export ${formatLabel}.`;
|
|
331
|
+
case "obsolete-geometry":
|
|
332
|
+
return OBSOLETE_GEOMETRY_MESSAGE;
|
|
333
|
+
case "missing-crs":
|
|
334
|
+
return `Geometry column ${geometryDisplayName(geometryColumn)} needs a CRS for GeoJSON export. Declare its source CRS in Python before exporting.`;
|
|
335
|
+
case "format-unavailable":
|
|
336
|
+
case "geometry-required":
|
|
337
|
+
case null:
|
|
338
|
+
return null;
|
|
339
|
+
default:
|
|
340
|
+
logNever(blocker);
|
|
341
|
+
return null;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* One open dialog for one table source. Closing the dialog or replacing the
|
|
347
|
+
* source creates a new context, and work started under an older context must
|
|
348
|
+
* not change the dialog or start an export.
|
|
349
|
+
*/
|
|
350
|
+
interface ExportContext {
|
|
351
|
+
source: unknown;
|
|
352
|
+
}
|
|
353
|
+
|
|
277
354
|
export const ExportActions: React.FC<ExportActionProps> = (props) => {
|
|
278
355
|
const { getExportMetadata, metadataSource } = props;
|
|
279
356
|
const [exportDialogOpen, setExportDialogOpen] = React.useState(false);
|
|
280
357
|
const [failure, setFailure] = React.useState<ExportFailure | null>(null);
|
|
281
358
|
const [settings, setSettings] =
|
|
282
359
|
React.useState<ExportSettings>(DEFAULT_SETTINGS);
|
|
283
|
-
const [
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
360
|
+
const [exportContext, setExportContext] = React.useState<ExportContext>(
|
|
361
|
+
() => ({ source: metadataSource }),
|
|
362
|
+
);
|
|
363
|
+
if (exportContext.source !== metadataSource) {
|
|
364
|
+
setExportContext({ source: metadataSource });
|
|
365
|
+
}
|
|
366
|
+
const mounted = React.useRef(false);
|
|
367
|
+
const isCurrentContext = useEvent(
|
|
368
|
+
(context: ExportContext) => mounted.current && context === exportContext,
|
|
369
|
+
);
|
|
370
|
+
const metadataRequest = useAsyncData(async () => {
|
|
371
|
+
if (!exportDialogOpen || !getExportMetadata) {
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
374
|
+
return { context: exportContext, metadata: await getExportMetadata({}) };
|
|
375
|
+
}, [exportDialogOpen, getExportMetadata, exportContext]);
|
|
376
|
+
// The hook keeps earlier data while it reloads and after a failure. Only a
|
|
377
|
+
// completed response for the current context can enable an export.
|
|
378
|
+
const metadata =
|
|
379
|
+
metadataRequest.status === "success" &&
|
|
380
|
+
metadataRequest.data?.context === exportContext
|
|
381
|
+
? metadataRequest.data.metadata
|
|
382
|
+
: null;
|
|
383
|
+
const metadataError =
|
|
384
|
+
metadataRequest.status === "error"
|
|
385
|
+
? failureDescription(metadataRequest.error)
|
|
386
|
+
: null;
|
|
287
387
|
const [geometryColumn, setGeometryColumn] = React.useState<string | null>(
|
|
288
388
|
null,
|
|
289
389
|
);
|
|
390
|
+
const [reconciledMetadata, setReconciledMetadata] =
|
|
391
|
+
React.useState<ExportMetadata | null>(null);
|
|
392
|
+
if (metadata !== reconciledMetadata) {
|
|
393
|
+
setReconciledMetadata(metadata);
|
|
394
|
+
if (metadata) {
|
|
395
|
+
setGeometryColumn((current) =>
|
|
396
|
+
current !== null &&
|
|
397
|
+
metadata.geometry_columns.some((column) => column.name === current)
|
|
398
|
+
? current
|
|
399
|
+
: metadata.default_geometry_column,
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
290
403
|
const hasGeometry = !!metadata?.geometry_columns.length;
|
|
291
404
|
const [expandedFormat, setExpandedFormat] = React.useState<
|
|
292
405
|
ConfigurableFormat | GeographicFormat | null
|
|
@@ -294,43 +407,17 @@ export const ExportActions: React.FC<ExportActionProps> = (props) => {
|
|
|
294
407
|
const triggerRef = React.useRef<HTMLButtonElement>(null);
|
|
295
408
|
const dialogRef = React.useRef<HTMLDivElement>(null);
|
|
296
409
|
const latestActionId = React.useRef(0);
|
|
410
|
+
React.useLayoutEffect(() => {
|
|
411
|
+
mounted.current = true;
|
|
412
|
+
return () => {
|
|
413
|
+
mounted.current = false;
|
|
414
|
+
latestActionId.current += 1;
|
|
415
|
+
};
|
|
416
|
+
}, []);
|
|
297
417
|
React.useEffect(() => {
|
|
298
418
|
latestActionId.current += 1;
|
|
419
|
+
setFailure(null);
|
|
299
420
|
}, [metadataSource]);
|
|
300
|
-
React.useEffect(() => {
|
|
301
|
-
if (!exportDialogOpen || !getExportMetadata) {
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
|
-
let active = true;
|
|
305
|
-
setMetadata(null);
|
|
306
|
-
setMetadataLoading(true);
|
|
307
|
-
setMetadataError(null);
|
|
308
|
-
void getExportMetadata({}).then(
|
|
309
|
-
(result) => {
|
|
310
|
-
if (!active) {
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
setMetadata(result);
|
|
314
|
-
setGeometryColumn((current) =>
|
|
315
|
-
current !== null &&
|
|
316
|
-
result.geometry_columns.some((column) => column.name === current)
|
|
317
|
-
? current
|
|
318
|
-
: result.default_geometry_column,
|
|
319
|
-
);
|
|
320
|
-
setMetadataLoading(false);
|
|
321
|
-
},
|
|
322
|
-
(error: unknown) => {
|
|
323
|
-
if (!active) {
|
|
324
|
-
return;
|
|
325
|
-
}
|
|
326
|
-
setMetadataError(failureDescription(error));
|
|
327
|
-
setMetadataLoading(false);
|
|
328
|
-
},
|
|
329
|
-
);
|
|
330
|
-
return () => {
|
|
331
|
-
active = false;
|
|
332
|
-
};
|
|
333
|
-
}, [exportDialogOpen, getExportMetadata, metadataSource, metadataRefresh]);
|
|
334
421
|
const policy = useAtomValue(downloadSizeLimitAtom);
|
|
335
422
|
const overLimit = !!(
|
|
336
423
|
policy &&
|
|
@@ -368,9 +455,8 @@ export const ExportActions: React.FC<ExportActionProps> = (props) => {
|
|
|
368
455
|
setFailure(null);
|
|
369
456
|
setExpandedFormat(null);
|
|
370
457
|
setSettings(DEFAULT_SETTINGS);
|
|
371
|
-
setMetadata(null);
|
|
372
|
-
setMetadataError(null);
|
|
373
458
|
setGeometryColumn(null);
|
|
459
|
+
setExportContext({ source: metadataSource });
|
|
374
460
|
}
|
|
375
461
|
};
|
|
376
462
|
|
|
@@ -553,57 +639,67 @@ export const ExportActions: React.FC<ExportActionProps> = (props) => {
|
|
|
553
639
|
}
|
|
554
640
|
};
|
|
555
641
|
|
|
556
|
-
|
|
557
|
-
|
|
642
|
+
// Package installation can finish after the dialog closes or the source
|
|
643
|
+
// changes, so the retry carries the context in which installation began.
|
|
644
|
+
const retryAfterInstall = useEvent(
|
|
645
|
+
async (action: ExportAction, context: ExportContext) => {
|
|
646
|
+
if (!isCurrentContext(context)) {
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
if (!isGeographic(action.format) || !getExportMetadata) {
|
|
650
|
+
retryAction(action);
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
558
653
|
const actionId = latestActionId.current;
|
|
654
|
+
const isCurrent = () =>
|
|
655
|
+
isCurrentContext(context) && actionId === latestActionId.current;
|
|
559
656
|
let result: ExportMetadata;
|
|
560
657
|
try {
|
|
561
658
|
result = await getExportMetadata({});
|
|
562
|
-
} catch
|
|
563
|
-
if (
|
|
564
|
-
|
|
659
|
+
} catch {
|
|
660
|
+
if (isCurrent()) {
|
|
661
|
+
metadataRequest.refetch();
|
|
565
662
|
}
|
|
566
663
|
return;
|
|
567
664
|
}
|
|
568
|
-
if (
|
|
569
|
-
return;
|
|
570
|
-
}
|
|
571
|
-
setMetadata(result);
|
|
572
|
-
setMetadataError(null);
|
|
573
|
-
if (result.formats[action.format]?.available === false) {
|
|
574
|
-
setFailure(null);
|
|
665
|
+
if (!isCurrent()) {
|
|
575
666
|
return;
|
|
576
667
|
}
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
(
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
return;
|
|
668
|
+
// Validation reads its own response, so a display refresh cannot
|
|
669
|
+
// start an export that this check rejected.
|
|
670
|
+
metadataRequest.refetch();
|
|
671
|
+
const blocker = geographicBlocker(result, action.format, geometryColumn);
|
|
672
|
+
switch (blocker) {
|
|
673
|
+
case null:
|
|
674
|
+
retryAction(action);
|
|
675
|
+
return;
|
|
676
|
+
case "obsolete-geometry":
|
|
677
|
+
setGeometryColumn(result.default_geometry_column);
|
|
678
|
+
setActionFailure(actionId, {
|
|
679
|
+
kind: "error",
|
|
680
|
+
title: failureTitleForAction(action),
|
|
681
|
+
description: OBSOLETE_GEOMETRY_MESSAGE,
|
|
682
|
+
action,
|
|
683
|
+
});
|
|
684
|
+
return;
|
|
685
|
+
case "choose-geometry":
|
|
686
|
+
return;
|
|
687
|
+
case "format-unavailable":
|
|
688
|
+
case "geometry-required":
|
|
689
|
+
case "missing-crs":
|
|
690
|
+
setFailure(null);
|
|
691
|
+
return;
|
|
692
|
+
default:
|
|
693
|
+
logNever(blocker);
|
|
604
694
|
}
|
|
605
|
-
}
|
|
606
|
-
|
|
695
|
+
},
|
|
696
|
+
);
|
|
697
|
+
|
|
698
|
+
const createInstallRetry = (action: ExportAction) => {
|
|
699
|
+
const context = exportContext;
|
|
700
|
+
return () => {
|
|
701
|
+
void retryAfterInstall(action, context);
|
|
702
|
+
};
|
|
607
703
|
};
|
|
608
704
|
|
|
609
705
|
const toggleExpanded = (format: ExportFormat) => {
|
|
@@ -665,14 +761,15 @@ export const ExportActions: React.FC<ExportActionProps> = (props) => {
|
|
|
665
761
|
const expanded =
|
|
666
762
|
configurableFormat !== null &&
|
|
667
763
|
expandedFormat === configurableFormat;
|
|
764
|
+
const FormatHeader = configurableFormat ? "button" : "div";
|
|
668
765
|
const panelId = `export-options-${option.format}`;
|
|
766
|
+
const descriptionId = `${panelId}-description`;
|
|
767
|
+
const summaryId = `${panelId}-summary`;
|
|
669
768
|
const rowFailure =
|
|
670
769
|
failure?.action.format === option.format ? failure : null;
|
|
671
770
|
const summary =
|
|
672
771
|
isGeographic(option.format) && hasGeometry
|
|
673
|
-
? geometryColumn
|
|
674
|
-
? "(unnamed geometry)"
|
|
675
|
-
: (geometryColumn ?? "Choose geometry")
|
|
772
|
+
? (geometryDisplayName(geometryColumn) ?? "Choose geometry")
|
|
676
773
|
: settingsSummary(settings, option.format);
|
|
677
774
|
const selectedGeometryIndex =
|
|
678
775
|
metadata?.geometry_columns.findIndex(
|
|
@@ -681,20 +778,14 @@ export const ExportActions: React.FC<ExportActionProps> = (props) => {
|
|
|
681
778
|
const eligibility = isGeographic(option.format)
|
|
682
779
|
? metadata?.formats[option.format]
|
|
683
780
|
: undefined;
|
|
684
|
-
const
|
|
685
|
-
metadata
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
selectedGeometry !== undefined &&
|
|
689
|
-
!selectedGeometry.crs;
|
|
781
|
+
const blocker =
|
|
782
|
+
isGeographic(option.format) && metadata
|
|
783
|
+
? geographicBlocker(metadata, option.format, geometryColumn)
|
|
784
|
+
: null;
|
|
690
785
|
const geometryReason = metadataError
|
|
691
786
|
? `Could not load geometry options: ${metadataError}`
|
|
692
787
|
: (eligibility?.reason ??
|
|
693
|
-
(
|
|
694
|
-
? `Choose a primary geometry column to export ${optionLabel}.`
|
|
695
|
-
: missingCrs
|
|
696
|
-
? `Geometry column ${geometryColumn === "" ? "(unnamed geometry)" : geometryColumn} needs a CRS for GeoJSON export. Declare its source CRS in Python before exporting.`
|
|
697
|
-
: null));
|
|
788
|
+
geographicBlockerReason(blocker, optionLabel, geometryColumn));
|
|
698
789
|
const missingPackageFailure =
|
|
699
790
|
rowFailure?.kind === "missing-packages" ? rowFailure : null;
|
|
700
791
|
const missingPackages =
|
|
@@ -704,13 +795,7 @@ export const ExportActions: React.FC<ExportActionProps> = (props) => {
|
|
|
704
795
|
: []);
|
|
705
796
|
const geographicUnavailable =
|
|
706
797
|
isGeographic(option.format) &&
|
|
707
|
-
((!!getExportMetadata && !metadata) ||
|
|
708
|
-
metadataLoading ||
|
|
709
|
-
!!metadataError ||
|
|
710
|
-
eligibility?.available === false ||
|
|
711
|
-
missingCrs ||
|
|
712
|
-
(!!metadata?.geometry_columns.length &&
|
|
713
|
-
geometryColumn === null));
|
|
798
|
+
((!!getExportMetadata && !metadata) || blocker !== null);
|
|
714
799
|
const copyUnavailable =
|
|
715
800
|
isGeographic(option.format) &&
|
|
716
801
|
(geographicUnavailable || missingPackages.length > 0);
|
|
@@ -720,55 +805,78 @@ export const ExportActions: React.FC<ExportActionProps> = (props) => {
|
|
|
720
805
|
data-testid={`export-row-${option.format}`}
|
|
721
806
|
className={cn(expanded && "bg-muted/40")}
|
|
722
807
|
>
|
|
723
|
-
<div className="grid
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
808
|
+
<div className="grid grid-cols-[minmax(0,1fr)_auto] items-center hover:bg-accent/50">
|
|
809
|
+
<FormatHeader
|
|
810
|
+
type={configurableFormat ? "button" : undefined}
|
|
811
|
+
className={cn(
|
|
812
|
+
"grid min-h-[60px] min-w-0 grid-cols-[28px_minmax(0,1fr)] items-center gap-x-1 py-2 pl-2.5 pr-1 text-left",
|
|
813
|
+
configurableFormat &&
|
|
814
|
+
"cursor-pointer focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-inset focus-visible:ring-ring",
|
|
815
|
+
)}
|
|
816
|
+
aria-label={
|
|
817
|
+
configurableFormat ? `${optionLabel} options` : undefined
|
|
818
|
+
}
|
|
819
|
+
aria-describedby={
|
|
820
|
+
configurableFormat
|
|
821
|
+
? [descriptionId, summary && summaryId]
|
|
822
|
+
.filter(Boolean)
|
|
823
|
+
.join(" ")
|
|
824
|
+
: undefined
|
|
825
|
+
}
|
|
826
|
+
aria-expanded={configurableFormat ? expanded : undefined}
|
|
827
|
+
aria-controls={configurableFormat ? panelId : undefined}
|
|
828
|
+
onClick={
|
|
829
|
+
configurableFormat
|
|
830
|
+
? () => toggleExpanded(option.format)
|
|
831
|
+
: undefined
|
|
832
|
+
}
|
|
833
|
+
>
|
|
834
|
+
<span
|
|
835
|
+
aria-hidden={true}
|
|
836
|
+
className="inline-flex h-6 w-6 items-center justify-center"
|
|
733
837
|
>
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
838
|
+
{configurableFormat && (
|
|
839
|
+
<ChevronRightIcon
|
|
840
|
+
className={cn(
|
|
841
|
+
"h-4 w-4 text-muted-foreground transition-transform",
|
|
842
|
+
expanded && "rotate-90",
|
|
843
|
+
)}
|
|
844
|
+
/>
|
|
845
|
+
)}
|
|
846
|
+
</span>
|
|
847
|
+
<span className="flex min-w-0 items-center gap-3 pl-1">
|
|
848
|
+
<option.icon
|
|
849
|
+
aria-hidden={true}
|
|
850
|
+
data-testid={`export-format-icon-${option.format}`}
|
|
851
|
+
className="h-4 w-4 shrink-0 text-muted-foreground"
|
|
739
852
|
/>
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
<div className="flex min-w-0 items-center gap-3 pl-1">
|
|
745
|
-
<option.icon
|
|
746
|
-
aria-hidden={true}
|
|
747
|
-
data-testid={`export-format-icon-${option.format}`}
|
|
748
|
-
className="h-4 w-4 shrink-0 text-muted-foreground"
|
|
749
|
-
/>
|
|
750
|
-
<div className="grid min-w-0 gap-1">
|
|
751
|
-
<span className="flex min-w-0 items-baseline gap-x-2">
|
|
752
|
-
<span className="text-sm font-medium leading-tight">
|
|
753
|
-
{optionLabel}
|
|
754
|
-
</span>
|
|
755
|
-
{summary && (
|
|
756
|
-
<span
|
|
757
|
-
data-testid={`export-summary-${option.format}`}
|
|
758
|
-
className="min-w-0 truncate text-xs text-muted-foreground"
|
|
759
|
-
>
|
|
760
|
-
{summary}
|
|
853
|
+
<span className="grid min-w-0 gap-1">
|
|
854
|
+
<span className="flex min-w-0 items-baseline gap-x-2">
|
|
855
|
+
<span className="text-sm font-medium leading-tight">
|
|
856
|
+
{optionLabel}
|
|
761
857
|
</span>
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
858
|
+
{summary && (
|
|
859
|
+
<span
|
|
860
|
+
id={summaryId}
|
|
861
|
+
data-testid={`export-summary-${option.format}`}
|
|
862
|
+
className="min-w-0 truncate text-xs text-muted-foreground"
|
|
863
|
+
>
|
|
864
|
+
{summary}
|
|
865
|
+
</span>
|
|
866
|
+
)}
|
|
867
|
+
</span>
|
|
868
|
+
<span
|
|
869
|
+
id={descriptionId}
|
|
870
|
+
className="text-xs text-muted-foreground leading-tight"
|
|
871
|
+
>
|
|
872
|
+
{option.format === "parquet" && hasGeometry
|
|
873
|
+
? "Parquet with geometry and CRS metadata"
|
|
874
|
+
: option.description}
|
|
875
|
+
</span>
|
|
768
876
|
</span>
|
|
769
|
-
</
|
|
770
|
-
</
|
|
771
|
-
<div className="flex items-center gap-0.5">
|
|
877
|
+
</span>
|
|
878
|
+
</FormatHeader>
|
|
879
|
+
<div className="flex items-center gap-0.5 pr-2.5">
|
|
772
880
|
<Tooltip
|
|
773
881
|
content={
|
|
774
882
|
option.canDownload
|
|
@@ -888,9 +996,7 @@ export const ExportActions: React.FC<ExportActionProps> = (props) => {
|
|
|
888
996
|
type="button"
|
|
889
997
|
size="xs"
|
|
890
998
|
variant="outline"
|
|
891
|
-
onClick={
|
|
892
|
-
setMetadataRefresh((current) => current + 1)
|
|
893
|
-
}
|
|
999
|
+
onClick={metadataRequest.refetch}
|
|
894
1000
|
>
|
|
895
1001
|
Retry
|
|
896
1002
|
</Button>
|
|
@@ -906,17 +1012,13 @@ export const ExportActions: React.FC<ExportActionProps> = (props) => {
|
|
|
906
1012
|
missingPackageFailure?.description ??
|
|
907
1013
|
eligibility?.reason
|
|
908
1014
|
}
|
|
909
|
-
onInstall={(
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
format:
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
: "parquet",
|
|
917
|
-
},
|
|
918
|
-
);
|
|
919
|
-
}}
|
|
1015
|
+
onInstall={createInstallRetry(
|
|
1016
|
+
missingPackageFailure?.action ?? {
|
|
1017
|
+
destination: "download",
|
|
1018
|
+
format:
|
|
1019
|
+
option.format === "geojson" ? "geojson" : "parquet",
|
|
1020
|
+
},
|
|
1021
|
+
)}
|
|
920
1022
|
className="items-start"
|
|
921
1023
|
/>
|
|
922
1024
|
</div>
|
|
@@ -34,6 +34,7 @@ import { DelayMount } from "@/components/utils/delay-mount";
|
|
|
34
34
|
import { useAsyncData } from "@/hooks/useAsyncData";
|
|
35
35
|
import { Banner, ErrorBanner } from "@/plugins/impl/common/error-banner";
|
|
36
36
|
import type { GetRowResult } from "@/plugins/impl/DataTablePlugin";
|
|
37
|
+
import { Objects } from "@/utils/objects";
|
|
37
38
|
import { NAMELESS_COLUMN_PREFIX, renderCellValue } from "../columns";
|
|
38
39
|
import { prettifyRowCount } from "../pagination";
|
|
39
40
|
import {
|
|
@@ -88,8 +89,7 @@ export const RowViewerPanel: React.FC<RowViewerPanelProps> = ({
|
|
|
88
89
|
handleRowSelectionChange?.((prev) => {
|
|
89
90
|
if (isRowSelected) {
|
|
90
91
|
// Remove this row from selection
|
|
91
|
-
|
|
92
|
-
return rest;
|
|
92
|
+
return Objects.filter(prev, (_, key) => key !== String(rowIdx));
|
|
93
93
|
}
|
|
94
94
|
// Add this row to selection
|
|
95
95
|
return { ...prev, [rowIdx]: true };
|
|
@@ -3,42 +3,6 @@
|
|
|
3
3
|
import z from "zod";
|
|
4
4
|
import { rpc } from "@/plugins/core/rpc";
|
|
5
5
|
|
|
6
|
-
export type DownloadFormat = "csv" | "json" | "parquet" | "tsv" | "geojson";
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Per-request export options. Each field is optional. A missing field keeps
|
|
10
|
-
* the widget's default for that setting.
|
|
11
|
-
*/
|
|
12
|
-
export interface DownloadAsOptions {
|
|
13
|
-
separator?: string;
|
|
14
|
-
encoding?: string;
|
|
15
|
-
ensure_ascii?: boolean;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface DownloadAsRequest {
|
|
19
|
-
format: DownloadFormat;
|
|
20
|
-
options?: DownloadAsOptions;
|
|
21
|
-
geometry_column?: string | null;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export type DownloadAsArgs = (req: DownloadAsRequest) => Promise<{
|
|
25
|
-
url: string;
|
|
26
|
-
filename: string;
|
|
27
|
-
error?: string | null;
|
|
28
|
-
missing_packages?: string[] | null;
|
|
29
|
-
code?:
|
|
30
|
-
| "geometry_required"
|
|
31
|
-
| "invalid_geometry"
|
|
32
|
-
| "invalid_metadata"
|
|
33
|
-
| "unsupported_representation"
|
|
34
|
-
| "unsupported_version"
|
|
35
|
-
| "missing_packages"
|
|
36
|
-
| "missing_crs"
|
|
37
|
-
| "conversion_failed"
|
|
38
|
-
| null;
|
|
39
|
-
column?: string | null;
|
|
40
|
-
}>;
|
|
41
|
-
|
|
42
6
|
const DownloadRequestSchema = z.object({
|
|
43
7
|
geometry_column: z.string().nullish(),
|
|
44
8
|
options: z
|
|
@@ -88,6 +52,20 @@ export const DownloadGeoJSONSchema = rpc
|
|
|
88
52
|
}),
|
|
89
53
|
);
|
|
90
54
|
|
|
55
|
+
export type DownloadAsRequest =
|
|
56
|
+
| z.infer<typeof DownloadAsSchema.input>
|
|
57
|
+
| z.infer<typeof DownloadGeoJSONSchema.input>;
|
|
58
|
+
|
|
59
|
+
type DownloadResponse =
|
|
60
|
+
| z.infer<typeof DownloadAsSchema.output>
|
|
61
|
+
| z.infer<typeof DownloadGeoJSONSchema.output>;
|
|
62
|
+
|
|
63
|
+
export type DownloadFormat = DownloadAsRequest["format"];
|
|
64
|
+
export type DownloadAsOptions = NonNullable<DownloadAsRequest["options"]>;
|
|
65
|
+
export type DownloadAsArgs = (
|
|
66
|
+
req: DownloadAsRequest,
|
|
67
|
+
) => Promise<DownloadResponse>;
|
|
68
|
+
|
|
91
69
|
export type DownloadAsFunction = (
|
|
92
70
|
req: z.infer<typeof DownloadAsSchema.input>,
|
|
93
71
|
) => Promise<z.infer<typeof DownloadAsSchema.output>>;
|
|
@@ -66,7 +66,10 @@ export type OnRefactorWithAI = (opts: {
|
|
|
66
66
|
* Renders an output based on an OutputMessage.
|
|
67
67
|
*/
|
|
68
68
|
export const OutputRenderer: React.FC<{
|
|
69
|
-
message: Pick<
|
|
69
|
+
message: Pick<
|
|
70
|
+
OutputMessage,
|
|
71
|
+
"channel" | "data" | "mimetype" | "code" | "traceback_format"
|
|
72
|
+
>;
|
|
70
73
|
cellId?: CellId;
|
|
71
74
|
onRefactorWithAI?: OnRefactorWithAI;
|
|
72
75
|
wrapText?: boolean;
|
|
@@ -185,6 +188,7 @@ export const OutputRenderer: React.FC<{
|
|
|
185
188
|
<MarimoTracebackOutput
|
|
186
189
|
onRefactorWithAI={onRefactorWithAI}
|
|
187
190
|
traceback={data}
|
|
191
|
+
tracebackFormat={message.traceback_format}
|
|
188
192
|
code={message.code}
|
|
189
193
|
cellId={cellId}
|
|
190
194
|
/>
|