@marimo-team/frontend 0.22.1-dev34 → 0.22.1-dev39
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/assets/{CellStatus-DKyVv7Zj.js → CellStatus-Cf0Jlrcs.js} +1 -1
- package/dist/assets/{ConnectedDataExplorerComponent-BUc9LHJJ.js → ConnectedDataExplorerComponent-DUxaLoL7.js} +1 -1
- package/dist/assets/{JsonOutput-D1i8P1dG.js → JsonOutput-IpD2GLtO.js} +2 -2
- package/dist/assets/{MarimoErrorOutput-RmM7djc-.js → MarimoErrorOutput-dTNCLY-Q.js} +1 -1
- package/dist/assets/{Plot-C7NE7pEx.js → Plot-BAM1jEAz.js} +72 -72
- package/dist/assets/{RenderHTML-Bmn77an6.js → RenderHTML-C5GEp4ca.js} +1 -1
- package/dist/assets/{add-cell-with-ai-RyZ9Xe2b.js → add-cell-with-ai-C0J3LyiV.js} +1 -1
- package/dist/assets/{add-connection-dialog-ogwy8tvS.js → add-connection-dialog-C42PDYI7.js} +1 -1
- package/dist/assets/{agent-panel-BHLkHj7k.js → agent-panel-CH-jHjEl.js} +1 -1
- package/dist/assets/{ai-model-dropdown-Dnf-CxbP.js → ai-model-dropdown-D14GiszF.js} +1 -1
- package/dist/assets/{app-config-button-D1Z_xsvC.js → app-config-button-v-557oRb.js} +1 -1
- package/dist/assets/{cell-editor-_GDTh-4a.js → cell-editor-Dd6UaL1A.js} +2 -2
- package/dist/assets/{cell-link-D4UrIH9w.js → cell-link-Cimoe3Fv.js} +1 -1
- package/dist/assets/{cells-ArUhhHls.js → cells-CcsG9Aum.js} +1 -1
- package/dist/assets/{chat-display-YmFjOXkV.js → chat-display-hfpeXiYe.js} +1 -1
- package/dist/assets/{chat-panel-B76rxYTh.js → chat-panel-DxT370nA.js} +1 -1
- package/dist/assets/{chat-ui-BitNq1z6.js → chat-ui-Dv4y0-td.js} +1 -1
- package/dist/assets/{column-preview-NDhbeu0E.js → column-preview-ZSErTRFA.js} +1 -1
- package/dist/assets/{command-palette-uJxkhle4.js → command-palette-CjF_cblG.js} +1 -1
- package/dist/assets/{common-CRBlPqv5.js → common-BZK7spst.js} +1 -1
- package/dist/assets/{components-DwGcJvMB.js → components-D2OlyENc.js} +1 -1
- package/dist/assets/{components-BfHGr__b.js → components-DpxyscxU.js} +1 -1
- package/dist/assets/{datasource-D9e37ifa.js → datasource-D9nfSxKS.js} +1 -1
- package/dist/assets/{dependency-graph-panel-BBmN-Vc7.js → dependency-graph-panel-5MbMtFss.js} +1 -1
- package/dist/assets/{documentation-panel-Dm2xtsTq.js → documentation-panel-DPdXS3YO.js} +1 -1
- package/dist/assets/{download-7EtMZf2Y.js → download-BFQaUFKI.js} +1 -1
- package/dist/assets/{edit-page-Cr_DnrkM.js → edit-page-CMd8_Psc.js} +4 -4
- package/dist/assets/{error-panel-C3-vTzaH.js → error-panel-DV8jpRsf.js} +1 -1
- package/dist/assets/{file-explorer-panel-g9KppC7Y.js → file-explorer-panel-Dr_ZNDk3.js} +1 -1
- package/dist/assets/{file-icons-JXAG6vK-.js → file-icons-DSJsG_mI.js} +1 -1
- package/dist/assets/{floating-outline-B6Qyid7Q.js → floating-outline-Cfa1ESSb.js} +1 -1
- package/dist/assets/{focus-DCN0oEe0.js → focus-C5u0JQUq.js} +1 -1
- package/dist/assets/{form-BVnQnVQ2.js → form-3ZUGKch9.js} +1 -1
- package/dist/assets/glide-data-editor-a3qLDl-r.js +132 -0
- package/dist/assets/{home-page-Bg02jazh.js → home-page-BrqppCUS.js} +1 -1
- package/dist/assets/{hooks-zQJ9iU_R.js → hooks-CHE17GG1.js} +1 -1
- package/dist/assets/{html-to-image-C5XSE7QT.js → html-to-image-BRbQwG7G.js} +1 -1
- package/dist/assets/{index-BaQAJwyb.css → index-BkdonYlq.css} +1 -1
- package/dist/assets/index-Bt8G6SSE.js +42 -0
- package/dist/assets/{kiosk-mode-CriCUOI1.js → kiosk-mode-Cu86-jaD.js} +1 -1
- package/dist/assets/{layout-Cudicm29.js → layout-B-lTkLKA.js} +1 -1
- package/dist/assets/{logs-panel-CoWO9c8s.js → logs-panel-CNVgwoHO.js} +1 -1
- package/dist/assets/{markdown-renderer-CyUc0f8D.js → markdown-renderer-B91NzmVT.js} +1 -1
- package/dist/assets/{name-cell-input-DeM6upjB.js → name-cell-input-hsV_b1Op.js} +1 -1
- package/dist/assets/{outline-panel-C4P3wA7Z.js → outline-panel-C3jvSnZF.js} +1 -1
- package/dist/assets/{packages-panel-DnrjjQDF.js → packages-panel-0_Z5vM7i.js} +1 -1
- package/dist/assets/{panels-BCRqI88j.js → panels-uc8QhzpO.js} +1 -1
- package/dist/assets/{process-output-e_aMblRk.js → process-output-CsvKn_Mr.js} +1 -1
- package/dist/assets/{readonly-python-code-CVgh88Tn.js → readonly-python-code-oFCRG7Wt.js} +1 -1
- package/dist/assets/{run-page-C-ySBXLo.js → run-page-DKCqiq_f.js} +1 -1
- package/dist/assets/{scratchpad-panel-C5Pogi1P.js → scratchpad-panel-ChHLgDv_.js} +1 -1
- package/dist/assets/{session-panel-DEsnZWks.js → session-panel-CPKKt31q.js} +1 -1
- package/dist/assets/{snippets-panel-DQgE3W8I.js → snippets-panel-CDqEDwpY.js} +1 -1
- package/dist/assets/{spec-CBbUxOvL.js → spec-CFx2bitO.js} +1 -1
- package/dist/assets/{state-pGNeffyB.js → state-BBVlYaRC.js} +1 -1
- package/dist/assets/{state-CV8Wy3e4.js → state-C93JW11U.js} +1 -1
- package/dist/assets/{textarea-DqzNK0s9.js → textarea-DJEKmYtw.js} +1 -1
- package/dist/assets/{tracing-BUFVOmZw.js → tracing-CLWi5jD3.js} +1 -1
- package/dist/assets/{tracing-panel-BsLloPWd.js → tracing-panel-ksnvVrMH.js} +2 -2
- package/dist/assets/{useAddCell-cC7JUC0q.js → useAddCell-mJ1PkF-E.js} +1 -1
- package/dist/assets/{useCellActionButton-BiYBXHfb.js → useCellActionButton-4HWj1rt3.js} +1 -1
- package/dist/assets/{useDeleteCell-D2p4Dz0U.js → useDeleteCell-B0OAS0ly.js} +1 -1
- package/dist/assets/{useDependencyPanelTab-c5-eXlcr.js → useDependencyPanelTab-BuZ0fgAR.js} +1 -1
- package/dist/assets/useLifecycle-N3bfh_O1.js +1 -0
- package/dist/assets/useNotebookActions-DuHUqtII.js +1 -0
- package/dist/assets/{useRunCells-DgBY-vh9.js → useRunCells-Du76UV1R.js} +1 -1
- package/dist/assets/{useSplitCell-SS0kKwVk.js → useSplitCell-CeL7eJq1.js} +1 -1
- package/dist/index.html +25 -25
- package/package.json +1 -1
- package/src/__mocks__/common.ts +4 -4
- package/src/components/chat/acp/agent-panel.tsx +2 -2
- package/src/components/data-table/__tests__/columns.test.tsx +7 -7
- package/src/components/data-table/cell-hover-template/types.ts +1 -1
- package/src/components/data-table/cell-hover-text/types.ts +1 -1
- package/src/components/data-table/cell-selection/__tests__/feature.test.ts +1 -1
- package/src/components/data-table/cell-selection/types.ts +1 -1
- package/src/components/data-table/cell-styling/types.ts +1 -1
- package/src/components/data-table/charts/chart-spec/altair-generator.ts +2 -2
- package/src/components/data-table/column-formatting/types.ts +2 -2
- package/src/components/data-table/column-summary/legacy-chart-spec.ts +1 -1
- package/src/components/data-table/column-wrapping/types.ts +1 -1
- package/src/components/data-table/copy-column/types.ts +1 -1
- package/src/components/data-table/data-table.tsx +12 -12
- package/src/components/data-table/focus-row/types.ts +1 -1
- package/src/components/data-table/loading-table.tsx +1 -1
- package/src/components/data-table/range-focus/__tests__/atoms.test.ts +2 -2
- package/src/components/data-table/range-focus/atoms.ts +2 -2
- package/src/components/dependency-graph/dependency-graph-tree.tsx +1 -1
- package/src/components/editor/__tests__/dynamic-favicon.test.tsx +1 -1
- package/src/components/editor/actions/pair-with-agent-modal.tsx +142 -0
- package/src/components/editor/actions/useNotebookActions.tsx +10 -0
- package/src/components/editor/ai/ai-completion-editor.tsx +1 -1
- package/src/components/editor/app-container.tsx +1 -1
- package/src/components/editor/chrome/panels/empty-state.tsx +1 -0
- package/src/components/editor/controls/keyboard-shortcuts.tsx +1 -1
- package/src/components/editor/navigation/__tests__/navigation.test.ts +1 -1
- package/src/components/editor/navigation/navigation.ts +1 -1
- package/src/components/editor/notebook-cell.tsx +1 -1
- package/src/components/editor/output/JsonOutput.tsx +4 -4
- package/src/components/editor/output/ansi-reduce.ts +2 -2
- package/src/components/editor/output/console/ConsoleOutput.tsx +1 -1
- package/src/components/editor/renderers/cells-renderer.tsx +1 -1
- package/src/components/editor/renderers/grid-layout/grid-layout.tsx +1 -1
- package/src/components/editor/renderers/plugins.ts +1 -1
- package/src/components/editor/renderers/slides-layout/types.ts +2 -2
- package/src/components/editor/renderers/vertical-layout/__tests__/useFocusFirstEditor.test.ts +2 -2
- package/src/components/editor/renderers/vertical-layout/__tests__/vertical-layout.test.ts +1 -1
- package/src/components/find-replace/find-replace.tsx +3 -1
- package/src/components/forms/form.tsx +1 -1
- package/src/components/forms/options.ts +1 -1
- package/src/components/static-html/static-banner.tsx +2 -2
- package/src/components/terminal/terminal.tsx +4 -4
- package/src/components/ui/button.tsx +1 -1
- package/src/components/ui/command.tsx +1 -1
- package/src/core/ai/context/providers/__tests__/datasource.test.ts +1 -1
- package/src/core/ai/context/providers/__tests__/error.test.ts +1 -1
- package/src/core/ai/context/providers/__tests__/variable.test.ts +1 -1
- package/src/core/ai/context/registry.ts +2 -2
- package/src/core/ai/tools/registry.ts +1 -1
- package/src/core/cells/__tests__/cells.test.ts +2 -2
- package/src/core/cells/__tests__/scrollCellIntoView.test.ts +1 -1
- package/src/core/cells/__tests__/session.test.ts +1 -1
- package/src/core/cells/__tests__/utils.test.ts +1 -1
- package/src/core/cells/cells.ts +1 -1
- package/src/core/cells/ids.ts +1 -1
- package/src/core/codemirror/ai/request.ts +1 -1
- package/src/core/codemirror/copilot/__tests__/language-server.test.ts +1 -1
- package/src/core/codemirror/copilot/__tests__/transport.test.ts +1 -1
- package/src/core/codemirror/copilot/language-server.ts +1 -1
- package/src/core/codemirror/copilot/types.ts +1 -1
- package/src/core/codemirror/facet.ts +1 -1
- package/src/core/codemirror/language/__tests__/sql.test.ts +4 -4
- package/src/core/codemirror/language/languages/sql/completion-builder.ts +1 -1
- package/src/core/codemirror/language/metadata.ts +1 -1
- package/src/core/codemirror/language/types.ts +1 -1
- package/src/core/codemirror/lsp/__tests__/notebook-lsp.test.ts +1 -1
- package/src/core/codemirror/lsp/notebook-lsp.ts +1 -1
- package/src/core/codemirror/misc/__tests__/dnd.test.ts +1 -1
- package/src/core/codemirror/rtc/loro/awareness.ts +1 -1
- package/src/core/config/feature-flag.tsx +1 -1
- package/src/core/dom/outline.ts +1 -1
- package/src/core/export/__tests__/hooks.test.ts +1 -1
- package/src/core/hotkeys/__tests__/hotkeys.test.ts +1 -1
- package/src/core/hotkeys/shortcuts.ts +1 -1
- package/src/core/islands/__tests__/bridge.test.ts +2 -2
- package/src/core/islands/bridge.ts +2 -2
- package/src/core/islands/components/output-wrapper.tsx +1 -1
- package/src/core/islands/parse.ts +1 -1
- package/src/core/lsp/__tests__/transport.test.ts +1 -1
- package/src/core/network/DeferredRequestRegistry.ts +1 -1
- package/src/core/network/__tests__/requests-network.test.ts +1 -1
- package/src/core/network/api.ts +2 -2
- package/src/core/network/requests-lazy.ts +1 -1
- package/src/core/network/requests-toasting.tsx +1 -1
- package/src/core/static/files.ts +1 -1
- package/src/core/vscode/vscode-bindings.ts +1 -1
- package/src/core/wasm/bridge.ts +3 -3
- package/src/core/wasm/worker/tracer.ts +1 -1
- package/src/core/websocket/useWebSocket.tsx +2 -2
- package/src/css/globals.css +37 -61
- package/src/custom.d.ts +1 -1
- package/src/hooks/__tests__/useDuplicateShortcuts.test.ts +2 -2
- package/src/hooks/debug.ts +3 -3
- package/src/hooks/useDebounce.ts +1 -1
- package/src/hooks/useEventListener.ts +1 -1
- package/src/hooks/useHotkey.ts +1 -1
- package/src/hooks/useLifecycle.ts +2 -2
- package/src/hooks/useNonce.ts +1 -1
- package/src/hooks/useResizeObserver.ts +2 -2
- package/src/main.tsx +1 -1
- package/src/plugins/core/RenderHTML.tsx +3 -3
- package/src/plugins/core/__test__/registerReactComponent.test.ts +1 -1
- package/src/plugins/core/registerReactComponent.tsx +4 -4
- package/src/plugins/core/rpc.ts +1 -1
- package/src/plugins/impl/DataTablePlugin.tsx +1 -1
- package/src/plugins/impl/FileBrowserPlugin.tsx +1 -1
- package/src/plugins/impl/FormPlugin.tsx +1 -1
- package/src/plugins/impl/__tests__/MatrixPlugin.test.tsx +1 -1
- package/src/plugins/impl/anywidget/AnyWidgetPlugin.tsx +1 -1
- package/src/plugins/impl/anywidget/model.ts +1 -1
- package/src/plugins/impl/anywidget/types.ts +2 -2
- package/src/plugins/impl/anywidget/widget-binding.ts +1 -1
- package/src/plugins/impl/chat/ChatPlugin.tsx +1 -1
- package/src/plugins/impl/chat/chat-ui.tsx +1 -1
- package/src/plugins/impl/data-editor/glide-data-editor.tsx +1 -1
- package/src/plugins/impl/data-explorer/ConnectedDataExplorerComponent.tsx +2 -2
- package/src/plugins/impl/data-explorer/components/query-form.tsx +1 -1
- package/src/plugins/impl/data-explorer/functions/function.ts +1 -1
- package/src/plugins/impl/data-explorer/queries/types.ts +1 -1
- package/src/plugins/impl/data-frames/DataFramePlugin.tsx +1 -1
- package/src/plugins/impl/data-frames/forms/renderers.tsx +1 -1
- package/src/plugins/impl/data-frames/utils/operators.ts +1 -1
- package/src/plugins/impl/matplotlib/MatplotlibPlugin.tsx +1 -1
- package/src/plugins/impl/mpl-interactive/MplInteractivePlugin.tsx +1 -1
- package/src/plugins/impl/panel/PanelPlugin.tsx +2 -2
- package/src/plugins/impl/plotly/Plot.tsx +3 -3
- package/src/plugins/impl/plotly/PlotlyPlugin.tsx +62 -44
- package/src/plugins/impl/plotly/__tests__/PlotlyPlugin.test.tsx +114 -0
- package/src/plugins/impl/plotly/__tests__/selection.test.ts +158 -196
- package/src/plugins/impl/plotly/selection.ts +274 -56
- package/src/plugins/impl/vega/batched.ts +1 -1
- package/src/plugins/impl/vega/make-selectable.ts +1 -1
- package/src/plugins/impl/vega/types.ts +1 -1
- package/src/plugins/layout/DownloadPlugin.tsx +1 -1
- package/src/plugins/layout/LazyPlugin.tsx +1 -1
- package/src/plugins/layout/RoutesPlugin.tsx +1 -1
- package/src/plugins/layout/mermaid/mermaid.tsx +1 -1
- package/src/plugins/plugins.ts +1 -1
- package/src/stories/data-explorer.stories.tsx +1 -1
- package/src/stories/dataframe.stories.tsx +1 -1
- package/src/stories/editor.stories.tsx +1 -1
- package/src/stories/select.stories.tsx +1 -1
- package/src/stories/switchable-multi-select.stories.tsx +1 -1
- package/src/utils/Logger.ts +1 -1
- package/src/utils/__tests__/arrays.test.ts +1 -1
- package/src/utils/__tests__/blob.test.ts +1 -1
- package/src/utils/__tests__/dates.test.ts +1 -1
- package/src/utils/__tests__/errors.test.ts +1 -1
- package/src/utils/__tests__/objects.test.ts +3 -3
- package/src/utils/__tests__/waitForWs.test.ts +1 -1
- package/src/utils/arrays.ts +1 -1
- package/src/utils/assertNever.ts +1 -1
- package/src/utils/batch-requests.ts +2 -2
- package/src/utils/createReducer.ts +2 -2
- package/src/utils/id-tree.tsx +2 -2
- package/src/utils/idle.ts +1 -1
- package/src/utils/invariant.ts +1 -2
- package/src/utils/maps.ts +1 -1
- package/src/utils/math.ts +0 -1
- package/src/utils/multi-map.ts +1 -1
- package/src/utils/objects.ts +1 -1
- package/src/utils/once.ts +2 -2
- package/src/utils/staticImplements.ts +1 -1
- package/src/utils/storage/jotai.ts +1 -1
- package/src/utils/tracer.ts +2 -2
- package/dist/assets/glide-data-editor-CDqunAkw.js +0 -132
- package/dist/assets/index-KI45dku7.js +0 -35
- package/dist/assets/useLifecycle-D202VvPd.js +0 -1
- package/dist/assets/useNotebookActions-Bb4xxjuJ.js +0 -1
|
@@ -2,20 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
import { pick } from "lodash-es";
|
|
4
4
|
import type * as Plotly from "plotly.js";
|
|
5
|
-
|
|
5
|
+
|
|
6
6
|
import { createParser, type PlotlyTemplateParser } from "./parse-from-template";
|
|
7
7
|
|
|
8
8
|
type AxisName = string;
|
|
9
9
|
type AxisDatum = unknown;
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
const SUNBURST_DATA_KEYS: (keyof Plotly.SunburstPlotDatum)[] = [
|
|
12
|
+
"color",
|
|
13
|
+
"curveNumber",
|
|
14
|
+
"entry",
|
|
15
|
+
"hovertext",
|
|
16
|
+
"id",
|
|
17
|
+
"label",
|
|
18
|
+
"parent",
|
|
19
|
+
"percentEntry",
|
|
20
|
+
"percentParent",
|
|
21
|
+
"percentRoot",
|
|
22
|
+
"pointNumber",
|
|
23
|
+
"root",
|
|
24
|
+
"value",
|
|
25
|
+
] as const;
|
|
17
26
|
|
|
18
|
-
const
|
|
27
|
+
const LINE_CLICK_TRACE_TYPES = new Set(["scatter", "scattergl"]);
|
|
19
28
|
|
|
20
29
|
const STANDARD_POINT_KEYS: string[] = [
|
|
21
30
|
"x",
|
|
@@ -29,61 +38,282 @@ const STANDARD_POINT_KEYS: string[] = [
|
|
|
29
38
|
"pointIndex",
|
|
30
39
|
] as const;
|
|
31
40
|
|
|
41
|
+
type PointWithFullData = Plotly.PlotDatum & {
|
|
42
|
+
pointNumbers?: number[];
|
|
43
|
+
fullData?: {
|
|
44
|
+
type?: string;
|
|
45
|
+
mode?: string;
|
|
46
|
+
x?: unknown[];
|
|
47
|
+
y?: unknown[];
|
|
48
|
+
hovertemplate?: string | string[];
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
interface TraceSource {
|
|
53
|
+
type?: string;
|
|
54
|
+
mode?: string;
|
|
55
|
+
x?: unknown[];
|
|
56
|
+
y?: unknown[];
|
|
57
|
+
hovertemplate?: string | string[];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export type ModeBarButton = NonNullable<
|
|
61
|
+
Plotly.Config["modeBarButtonsToAdd"]
|
|
62
|
+
>[number];
|
|
63
|
+
|
|
64
|
+
function coalesceField<T>(
|
|
65
|
+
primary: T | undefined,
|
|
66
|
+
fallback: T | undefined,
|
|
67
|
+
): T | undefined {
|
|
68
|
+
return primary ?? fallback;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function getTraceSource(point: Plotly.PlotDatum): TraceSource {
|
|
72
|
+
const withFullData = point as PointWithFullData;
|
|
73
|
+
const data = (point.data ?? {}) as TraceSource;
|
|
74
|
+
const fullData = (withFullData.fullData ?? {}) as TraceSource;
|
|
75
|
+
|
|
76
|
+
// Plotly click payloads sometimes include partial `data` plus richer `fullData`.
|
|
77
|
+
// Merge field-by-field so we don't lose type/mode/x/y metadata for pure lines.
|
|
78
|
+
return {
|
|
79
|
+
type: coalesceField(data.type, fullData.type),
|
|
80
|
+
mode: coalesceField(data.mode, fullData.mode),
|
|
81
|
+
x: coalesceField(data.x, fullData.x),
|
|
82
|
+
y: coalesceField(data.y, fullData.y),
|
|
83
|
+
hovertemplate: coalesceField(data.hovertemplate, fullData.hovertemplate),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function asFiniteNumber(value: unknown): number | undefined {
|
|
88
|
+
return typeof value === "number" && Number.isFinite(value)
|
|
89
|
+
? value
|
|
90
|
+
: undefined;
|
|
91
|
+
}
|
|
92
|
+
|
|
32
93
|
function getPointIndex(point: Plotly.PlotDatum): number | undefined {
|
|
33
|
-
|
|
34
|
-
|
|
94
|
+
const pointIndex = asFiniteNumber(point.pointIndex);
|
|
95
|
+
if (pointIndex !== undefined) {
|
|
96
|
+
return pointIndex;
|
|
35
97
|
}
|
|
36
98
|
|
|
37
|
-
|
|
38
|
-
|
|
99
|
+
const pointNumber = asFiniteNumber(point.pointNumber);
|
|
100
|
+
if (pointNumber !== undefined) {
|
|
101
|
+
return pointNumber;
|
|
39
102
|
}
|
|
40
103
|
|
|
41
|
-
|
|
104
|
+
const pointNumbers = (point as PointWithFullData).pointNumbers;
|
|
105
|
+
if (!Array.isArray(pointNumbers)) {
|
|
106
|
+
return undefined;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return pointNumbers.map(asFiniteNumber).find((n) => n !== undefined);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function isLinePoint(point: Plotly.PlotDatum): boolean {
|
|
113
|
+
const trace = getTraceSource(point);
|
|
114
|
+
if (!LINE_CLICK_TRACE_TYPES.has(String(trace.type))) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const mode = trace.mode;
|
|
119
|
+
if (typeof mode !== "string") {
|
|
120
|
+
// Some Plotly click payloads omit mode on point.data, especially with
|
|
121
|
+
// line traces; treat scatter/scattergl as line-like in this case.
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return mode.split("+").includes("lines");
|
|
42
126
|
}
|
|
43
127
|
|
|
44
|
-
function
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
128
|
+
function isPureLineMode(mode: unknown): boolean {
|
|
129
|
+
if (typeof mode !== "string") {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
const parts = mode.split("+");
|
|
133
|
+
return parts.includes("lines") && !parts.includes("markers");
|
|
49
134
|
}
|
|
50
135
|
|
|
51
|
-
export function
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
136
|
+
export function hasPureLineTrace(
|
|
137
|
+
data: readonly Plotly.Data[] | undefined,
|
|
138
|
+
): boolean {
|
|
139
|
+
if (!data) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return data.some((trace) => {
|
|
144
|
+
const traceType = (trace as { type?: unknown }).type;
|
|
145
|
+
const isScatterLike =
|
|
146
|
+
traceType === undefined || LINE_CLICK_TRACE_TYPES.has(String(traceType));
|
|
147
|
+
if (!isScatterLike) {
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
return isPureLineMode((trace as { mode?: unknown }).mode);
|
|
55
151
|
});
|
|
56
152
|
}
|
|
57
153
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
154
|
+
function createDragmodeButton(
|
|
155
|
+
name: string,
|
|
156
|
+
title: string,
|
|
157
|
+
svg: string,
|
|
158
|
+
dragmode: Plotly.Layout["dragmode"],
|
|
159
|
+
setDragmode: (dragmode: Plotly.Layout["dragmode"]) => void,
|
|
160
|
+
): ModeBarButton {
|
|
161
|
+
return {
|
|
162
|
+
name,
|
|
163
|
+
title,
|
|
164
|
+
icon: { svg },
|
|
165
|
+
click: () => setDragmode(dragmode),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function lineSelectionButtons(
|
|
170
|
+
setDragmode: (dragmode: Plotly.Layout["dragmode"]) => void,
|
|
171
|
+
): ModeBarButton[] {
|
|
172
|
+
return [
|
|
173
|
+
createDragmodeButton(
|
|
174
|
+
"line-box-select",
|
|
175
|
+
"Box select",
|
|
176
|
+
`<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
177
|
+
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
178
|
+
<rect x="4" y="4" width="16" height="16" stroke-dasharray="2 2" />
|
|
179
|
+
</svg>`,
|
|
180
|
+
"select",
|
|
181
|
+
setDragmode,
|
|
182
|
+
),
|
|
183
|
+
createDragmodeButton(
|
|
184
|
+
"line-lasso-select",
|
|
185
|
+
"Lasso select",
|
|
186
|
+
`<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
|
|
187
|
+
stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
188
|
+
<path d="M6 8c0-2.2 2.2-4 5-4s5 1.8 5 4-2.2 4-5 4-5 1.8-5 4 2.2 4 5 4" />
|
|
189
|
+
<circle cx="17.5" cy="16.5" r="1.5" />
|
|
190
|
+
</svg>`,
|
|
191
|
+
"lasso",
|
|
192
|
+
setDragmode,
|
|
193
|
+
),
|
|
194
|
+
];
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function mergeModeBarButtonsToAdd(
|
|
198
|
+
defaults: readonly ModeBarButton[],
|
|
199
|
+
userButtons: readonly ModeBarButton[] | undefined,
|
|
200
|
+
): ModeBarButton[] {
|
|
201
|
+
const merged: ModeBarButton[] = [];
|
|
202
|
+
const seenStrings = new Set<string>();
|
|
203
|
+
|
|
204
|
+
const pushButton = (button: ModeBarButton) => {
|
|
205
|
+
if (typeof button === "string") {
|
|
206
|
+
if (seenStrings.has(button)) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
seenStrings.add(button);
|
|
210
|
+
merged.push(button);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
merged.push(button);
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
defaults.forEach(pushButton);
|
|
217
|
+
userButtons?.forEach(pushButton);
|
|
218
|
+
return merged;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export function shouldHandleClickSelection(
|
|
222
|
+
points: readonly Plotly.PlotDatum[],
|
|
223
|
+
): boolean {
|
|
224
|
+
return points.some((point) => {
|
|
225
|
+
const type = getTraceSource(point).type;
|
|
226
|
+
return (
|
|
227
|
+
type === "bar" ||
|
|
228
|
+
type === "heatmap" ||
|
|
229
|
+
type === "histogram" ||
|
|
230
|
+
isLinePoint(point)
|
|
231
|
+
);
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function extractIndices(points: readonly Plotly.PlotDatum[]): number[] {
|
|
236
|
+
return points
|
|
237
|
+
.map(getPointIndex)
|
|
238
|
+
.filter((pointIndex): pointIndex is number => pointIndex !== undefined);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function getIndexedValue(series: unknown, index: number): unknown {
|
|
242
|
+
if (Array.isArray(series) || ArrayBuffer.isView(series)) {
|
|
243
|
+
return (series as ArrayLike<unknown>)[index];
|
|
244
|
+
}
|
|
245
|
+
if (typeof series === "object" && series !== null && "length" in series) {
|
|
246
|
+
const maybeLength = Number(
|
|
247
|
+
(series as { length?: unknown }).length ?? Number.NaN,
|
|
248
|
+
);
|
|
249
|
+
if (Number.isFinite(maybeLength) && index >= 0 && index < maybeLength) {
|
|
250
|
+
return (series as Record<number, unknown>)[index];
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return undefined;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function withInferredXY(
|
|
257
|
+
point: Plotly.PlotDatum,
|
|
258
|
+
fields: Record<AxisName, AxisDatum>,
|
|
259
|
+
): Record<AxisName, AxisDatum> {
|
|
260
|
+
// For some pure-line clicks Plotly provides index metadata but omits x/y.
|
|
261
|
+
// Recover x/y from trace arrays so Python gets a stable payload.
|
|
262
|
+
if (fields.x !== undefined && fields.y !== undefined) {
|
|
263
|
+
return fields;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const pointIndex = getPointIndex(point);
|
|
267
|
+
if (pointIndex === undefined) {
|
|
268
|
+
return fields;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const nextFields: Record<AxisName, AxisDatum> = { ...fields };
|
|
272
|
+
if (nextFields.pointIndex === undefined) {
|
|
273
|
+
nextFields.pointIndex = pointIndex;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const trace = getTraceSource(point);
|
|
277
|
+
if (nextFields.x === undefined) {
|
|
278
|
+
const inferredX = getIndexedValue(trace.x, pointIndex);
|
|
279
|
+
if (inferredX !== undefined) {
|
|
280
|
+
nextFields.x = inferredX;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
if (nextFields.y === undefined) {
|
|
284
|
+
const inferredY = getIndexedValue(trace.y, pointIndex);
|
|
285
|
+
if (inferredY !== undefined) {
|
|
286
|
+
nextFields.y = inferredY;
|
|
287
|
+
}
|
|
68
288
|
}
|
|
69
289
|
|
|
290
|
+
return nextFields;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export function extractPoints(
|
|
294
|
+
points: readonly Plotly.PlotDatum[],
|
|
295
|
+
): Record<AxisName, AxisDatum>[] {
|
|
70
296
|
let parser: PlotlyTemplateParser | undefined;
|
|
71
297
|
|
|
72
298
|
return points.map((point) => {
|
|
73
|
-
const standardPointFields =
|
|
299
|
+
const standardPointFields = withInferredXY(
|
|
300
|
+
point,
|
|
301
|
+
pick(point, STANDARD_POINT_KEYS),
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
const trace = getTraceSource(point);
|
|
74
305
|
|
|
75
306
|
// Get the first hovertemplate
|
|
76
|
-
const hovertemplate = Array.isArray(
|
|
77
|
-
?
|
|
78
|
-
:
|
|
307
|
+
const hovertemplate = Array.isArray(trace.hovertemplate)
|
|
308
|
+
? trace.hovertemplate[0]
|
|
309
|
+
: trace.hovertemplate;
|
|
79
310
|
|
|
80
311
|
// For chart types with standard point keys (e.g. heatmaps),
|
|
81
312
|
// or when there's no hovertemplate, pick keys directly from the point.
|
|
82
|
-
if (!hovertemplate ||
|
|
313
|
+
if (!hovertemplate || trace.type === "heatmap") {
|
|
83
314
|
return standardPointFields;
|
|
84
315
|
}
|
|
85
316
|
|
|
86
|
-
// Update or create a parser
|
|
87
317
|
parser = parser
|
|
88
318
|
? parser.update(hovertemplate)
|
|
89
319
|
: createParser(hovertemplate);
|
|
@@ -94,22 +324,10 @@ export function extractPoints(
|
|
|
94
324
|
});
|
|
95
325
|
}
|
|
96
326
|
|
|
97
|
-
export function
|
|
98
|
-
|
|
99
|
-
):
|
|
100
|
-
|
|
101
|
-
return undefined;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const points = evt.points.filter(isClickSelectablePoint);
|
|
105
|
-
if (points.length === 0) {
|
|
106
|
-
return undefined;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return {
|
|
110
|
-
selections: Arrays.EMPTY,
|
|
111
|
-
points: extractPoints(points),
|
|
112
|
-
indices: extractIndices(points),
|
|
113
|
-
range: undefined,
|
|
114
|
-
};
|
|
327
|
+
export function extractSunburstPoints(
|
|
328
|
+
points: readonly Plotly.PlotDatum[],
|
|
329
|
+
): Record<string, unknown>[] {
|
|
330
|
+
return points.map((point) => pick(point, SUNBURST_DATA_KEYS));
|
|
115
331
|
}
|
|
332
|
+
|
|
333
|
+
export const extractTreemapPoints = extractSunburstPoints;
|
|
@@ -160,7 +160,7 @@ function findCommonParams(
|
|
|
160
160
|
if (!signatureCounts.has(signature)) {
|
|
161
161
|
signatureCounts.set(signature, { count: 0, param });
|
|
162
162
|
}
|
|
163
|
-
//
|
|
163
|
+
// oxlint-disable-next-line typescript/no-non-null-assertion
|
|
164
164
|
signatureCounts.get(signature)!.count++;
|
|
165
165
|
}
|
|
166
166
|
}
|
|
@@ -25,7 +25,7 @@ export type {
|
|
|
25
25
|
} from "vega-lite/types_unstable/spec/unit.js";
|
|
26
26
|
|
|
27
27
|
export type VegaLiteUnitSpec = TopLevelUnitSpec<Field>;
|
|
28
|
-
//
|
|
28
|
+
// oxlint-disable-next-line typescript/no-explicit-any
|
|
29
29
|
export type GenericVegaSpec = GenericUnitSpec<any, any, any>;
|
|
30
30
|
export type EncodingType = keyof Encoding<Field>;
|
|
31
31
|
export type Encodings = Encoding<Field>;
|
|
@@ -14,7 +14,7 @@ interface Data {
|
|
|
14
14
|
showLoadingIndicator: boolean;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
//
|
|
17
|
+
// oxlint-disable-next-line typescript/consistent-type-definitions
|
|
18
18
|
type PluginFunctions = {
|
|
19
19
|
load: (req: {}) => Promise<{
|
|
20
20
|
html: string;
|
|
@@ -63,7 +63,7 @@ function randomAlpha() {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
const Mermaid: React.FC<Props> = ({ diagram }) => {
|
|
66
|
-
//
|
|
66
|
+
// oxlint-disable-next-line react/hook-use-state
|
|
67
67
|
const [id] = useState(() => randomAlpha());
|
|
68
68
|
|
|
69
69
|
const darkMode = useTheme().theme === "dark";
|
package/src/plugins/plugins.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
-
/*
|
|
2
|
+
/* oxlint-disable typescript/no-explicit-any */
|
|
3
3
|
|
|
4
4
|
import { NavigationMenuPlugin } from "@/plugins/layout/NavigationMenuPlugin";
|
|
5
5
|
import { initializeUIElement } from "../core/dom/ui-element";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
-
/*
|
|
2
|
+
/* oxlint-disable react-hooks/rules-of-hooks */
|
|
3
3
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
4
4
|
import { useState } from "react";
|
|
5
5
|
import { DataFrameComponent } from "@/plugins/impl/data-frames/DataFramePlugin";
|
|
@@ -52,7 +52,7 @@ const Editor = (opts: { extensions?: Extension[] }): React.ReactNode => {
|
|
|
52
52
|
});
|
|
53
53
|
|
|
54
54
|
return () => view.destroy();
|
|
55
|
-
//
|
|
55
|
+
// oxlint-disable-next-line react-hooks/exhaustive-deps
|
|
56
56
|
}, [ref.current]);
|
|
57
57
|
|
|
58
58
|
return <div className="cm" ref={ref} />;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
-
/*
|
|
2
|
+
/* oxlint-disable react-hooks/rules-of-hooks */
|
|
3
3
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
4
4
|
import { useState } from "react";
|
|
5
5
|
import { SwitchableMultiSelect } from "@/components/forms/switchable-multi-select";
|
package/src/utils/Logger.ts
CHANGED
|
@@ -124,7 +124,7 @@ describe("arrays", () => {
|
|
|
124
124
|
});
|
|
125
125
|
|
|
126
126
|
it("should handle undefined/null array", () => {
|
|
127
|
-
//
|
|
127
|
+
// oxlint-disable-next-line typescript/no-explicit-any
|
|
128
128
|
expect(arrayToggle(undefined as any, 1)).toEqual([1]);
|
|
129
129
|
});
|
|
130
130
|
|
|
@@ -30,7 +30,7 @@ describe("Blob serialization and deserialization", () => {
|
|
|
30
30
|
const serialized = await serializeBlob(testBlob);
|
|
31
31
|
const deserialized = deserializeBlob(serialized);
|
|
32
32
|
const reader = new FileReader();
|
|
33
|
-
//
|
|
33
|
+
// oxlint-disable-next-line unicorn/prefer-blob-reading-methods
|
|
34
34
|
reader.readAsText(deserialized);
|
|
35
35
|
await new Promise((resolve) => {
|
|
36
36
|
reader.onload = () => {
|
|
@@ -73,7 +73,7 @@ describe("dates", () => {
|
|
|
73
73
|
|
|
74
74
|
describe("with different locales", () => {
|
|
75
75
|
// Save original implementation
|
|
76
|
-
//
|
|
76
|
+
// oxlint-disable-next-line typescript/unbound-method
|
|
77
77
|
const originalToLocaleDateString = Date.prototype.toLocaleDateString;
|
|
78
78
|
|
|
79
79
|
afterAll(() => {
|
|
@@ -46,7 +46,7 @@ describe("prettyError", () => {
|
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
it("handles circular references", () => {
|
|
49
|
-
//
|
|
49
|
+
// oxlint-disable-next-line typescript/no-explicit-any
|
|
50
50
|
const circular: any = { foo: "bar" };
|
|
51
51
|
circular.self = circular;
|
|
52
52
|
expect(prettyError(circular)).toBe("[object Object]");
|
|
@@ -29,9 +29,9 @@ describe("Objects", () => {
|
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
it("should return falsy input unchanged", () => {
|
|
32
|
-
//
|
|
32
|
+
// oxlint-disable-next-line typescript/no-explicit-any
|
|
33
33
|
expect(Objects.mapValues(null as any, (v) => v)).toBe(null);
|
|
34
|
-
//
|
|
34
|
+
// oxlint-disable-next-line typescript/no-explicit-any
|
|
35
35
|
expect(Objects.mapValues(undefined as any, (v) => v)).toBe(undefined);
|
|
36
36
|
});
|
|
37
37
|
});
|
|
@@ -249,7 +249,7 @@ describe("Objects", () => {
|
|
|
249
249
|
|
|
250
250
|
it("should handle omitting non-existent keys", () => {
|
|
251
251
|
const obj = { a: 1, b: 2 };
|
|
252
|
-
//
|
|
252
|
+
// oxlint-disable-next-line typescript/no-explicit-any
|
|
253
253
|
const result = Objects.omit(obj, ["c" as any]);
|
|
254
254
|
expect(result).toEqual({ a: 1, b: 2 });
|
|
255
255
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
-
/*
|
|
2
|
+
/* oxlint-disable typescript/no-explicit-any */
|
|
3
3
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
4
4
|
import { waitForWs } from "../waitForWs";
|
|
5
5
|
|
package/src/utils/arrays.ts
CHANGED
|
@@ -53,7 +53,7 @@ export function arrayShallowEquals<T>(a: T[], b: T[]): boolean {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
export const Arrays = {
|
|
56
|
-
//
|
|
56
|
+
// oxlint-disable-next-line typescript/no-explicit-any
|
|
57
57
|
EMPTY: [] as any,
|
|
58
58
|
zip: <T, U>(a: T[], b: U[]): [T, U][] => {
|
|
59
59
|
invariant(a.length === b.length, "Arrays must be the same length");
|
package/src/utils/assertNever.ts
CHANGED
|
@@ -15,6 +15,6 @@ export function assertNever(x: never): never {
|
|
|
15
15
|
*/
|
|
16
16
|
export function logNever(x: never): void {
|
|
17
17
|
Logger.warn(`Unexpected object: ${JSON.stringify(x)}`);
|
|
18
|
-
//
|
|
18
|
+
// oxlint-ignore-next-line -- noVoidTypeReturn: function returns void but value is needed for exhaustiveness
|
|
19
19
|
return x;
|
|
20
20
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
-
/*
|
|
2
|
+
/* oxlint-disable typescript/no-explicit-any */
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Debounces multiple calls to a loader function, returning the same promise for
|
|
@@ -14,7 +14,7 @@ export function batch<T, REQ extends unknown[]>(
|
|
|
14
14
|
return (...args: REQ): Promise<T> => {
|
|
15
15
|
const key = toKey(...args);
|
|
16
16
|
if (requestCache.has(key)) {
|
|
17
|
-
//
|
|
17
|
+
// oxlint-disable-next-line typescript/no-non-null-assertion
|
|
18
18
|
return requestCache.get(key)!;
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
-
/*
|
|
2
|
+
/* oxlint-disable typescript/no-explicit-any */
|
|
3
3
|
|
|
4
4
|
import { atom, useSetAtom } from "jotai";
|
|
5
5
|
import type { Reducer } from "react";
|
|
@@ -139,7 +139,7 @@ export function createReducerAndAtoms<
|
|
|
139
139
|
);
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
//
|
|
142
|
+
// oxlint-disable-next-line typescript/no-non-null-assertion
|
|
143
143
|
return actionsMap.get(setState)!;
|
|
144
144
|
}
|
|
145
145
|
|
package/src/utils/id-tree.tsx
CHANGED
|
@@ -43,7 +43,7 @@ export class TreeNode<T> {
|
|
|
43
43
|
const stack = [...this.children];
|
|
44
44
|
|
|
45
45
|
while (stack.length > 0) {
|
|
46
|
-
//
|
|
46
|
+
// oxlint-disable-next-line typescript/no-non-null-assertion
|
|
47
47
|
const node = stack.pop()!;
|
|
48
48
|
count++;
|
|
49
49
|
|
|
@@ -62,7 +62,7 @@ export class TreeNode<T> {
|
|
|
62
62
|
const stack = [...this.children];
|
|
63
63
|
|
|
64
64
|
while (stack.length > 0) {
|
|
65
|
-
//
|
|
65
|
+
// oxlint-disable-next-line typescript/no-non-null-assertion
|
|
66
66
|
const node = stack.pop()!;
|
|
67
67
|
result.push(node.value);
|
|
68
68
|
|
package/src/utils/idle.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
export function onIdle(callback: () => void) {
|
|
9
9
|
if ("scheduler" in window) {
|
|
10
|
-
//
|
|
10
|
+
// oxlint-disable-next-line typescript/no-explicit-any
|
|
11
11
|
(window as any).scheduler.postTask(callback, {
|
|
12
12
|
priority: "background",
|
|
13
13
|
});
|