@marimo-team/islands 0.23.15-dev49 → 0.23.15-dev50

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 (41) hide show
  1. package/dist/{ConnectedDataExplorerComponent-7p7rt9iw.js → ConnectedDataExplorerComponent-Cmq_9LVS.js} +4 -4
  2. package/dist/{ErrorBoundary-B_CAG7_e.js → ErrorBoundary-CnMJ8jR9.js} +1 -1
  3. package/dist/{chat-ui-C0z13gJB.js → chat-ui-Dy0arY8E.js} +6 -6
  4. package/dist/{code-visibility-CoTgpfnd.js → code-visibility-B5xe1Xp2.js} +9 -9
  5. package/dist/{formats-CitsMtUm.js → formats-CdvkxTS6.js} +1 -1
  6. package/dist/{glide-data-editor-CwZz71BD.js → glide-data-editor-D5w23DvC.js} +3 -3
  7. package/dist/{html-to-image-CEo5pRYw.js → html-to-image-f-kPEnn0.js} +5 -5
  8. package/dist/{input-BGPrFH3g.js → input-CF5Sgjba.js} +1 -1
  9. package/dist/main.js +19 -18
  10. package/dist/{mermaid-UdmxG2PZ.js → mermaid-BaZJ2mds.js} +2 -2
  11. package/dist/{process-output-BOvvilRG.js → process-output-O9FeFAVM.js} +1 -1
  12. package/dist/{reveal-component-Bvigsc8L.js → reveal-component-BkZp_qsV.js} +5 -5
  13. package/dist/{spec-DSs9v0xx.js → spec-DxIove3q.js} +1 -1
  14. package/dist/style.css +1 -1
  15. package/dist/{toDate-BRJgtOGm.js → toDate-CEOdz9nC.js} +1 -1
  16. package/dist/{useAsyncData-BSOyAbac.js → useAsyncData-Cs0mpJTy.js} +1 -1
  17. package/dist/{useDeepCompareMemoize-BTLeWzuR.js → useDeepCompareMemoize-DoOZicGb.js} +1 -1
  18. package/dist/{useLifecycle-C6wHjkhW.js → useLifecycle-DR_F1d7F.js} +1 -1
  19. package/dist/{useTheme-Df_vGflw.js → useTheme-BA-8MM7N.js} +1 -0
  20. package/dist/{vega-component-BAvmvTjO.js → vega-component-BN0fz-QA.js} +5 -5
  21. package/package.json +1 -1
  22. package/src/__mocks__/requests.ts +3 -0
  23. package/src/components/editor/actions/useNotebookActions.tsx +8 -0
  24. package/src/components/editor/chrome/components/__tests__/feedback-button.test.tsx +221 -0
  25. package/src/components/editor/chrome/components/feedback-button.tsx +308 -77
  26. package/src/core/ai/context/providers/error.ts +6 -158
  27. package/src/core/constants.ts +2 -0
  28. package/src/core/diagnostics/__tests__/issue-details.test.ts +173 -0
  29. package/src/core/diagnostics/issue-details.ts +139 -0
  30. package/src/core/errors/__tests__/error-entries.test.ts +25 -0
  31. package/src/core/errors/error-entries.ts +176 -0
  32. package/src/core/islands/bridge.ts +1 -0
  33. package/src/core/network/__tests__/requests-lazy.test.ts +18 -0
  34. package/src/core/network/__tests__/requests-network.test.ts +7 -0
  35. package/src/core/network/requests-lazy.ts +13 -1
  36. package/src/core/network/requests-network.ts +3 -0
  37. package/src/core/network/requests-static.ts +1 -0
  38. package/src/core/network/requests-toasting.tsx +1 -0
  39. package/src/core/network/types.ts +3 -0
  40. package/src/core/wasm/bridge.ts +8 -0
  41. package/src/core/wasm/worker/types.ts +2 -0
@@ -5,7 +5,7 @@ import { t as __commonJSMin } from "./chunk-BNovOVIE.js";
5
5
  import { _ as Logger } from "./button-BacYv-bE.js";
6
6
  import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
7
7
  import { u as createLucideIcon } from "./dist--2Bqjvs0.js";
8
- import { T as atom, a as KnownQueryParams, g as store, v as waitFor, y as isIslands } from "./useTheme-Df_vGflw.js";
8
+ import { T as atom, a as KnownQueryParams, g as store, v as waitFor, y as isIslands } from "./useTheme-BA-8MM7N.js";
9
9
  import { t as invariant } from "./invariant-wRzNXIsJ.js";
10
10
  var CircleQuestionMark = createLucideIcon("circle-question-mark", [
11
11
  ["circle", {
@@ -1,7 +1,7 @@
1
1
  import { s as __toESM } from "./chunk-BNovOVIE.js";
2
2
  import { t as require_react } from "./react-DA-nE2FX.js";
3
3
  import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
4
- import { A as useEvent_default } from "./useTheme-Df_vGflw.js";
4
+ import { A as useEvent_default } from "./useTheme-BA-8MM7N.js";
5
5
  import { t as invariant } from "./invariant-wRzNXIsJ.js";
6
6
  var import_compiler_runtime = require_compiler_runtime(), import_react = /* @__PURE__ */ __toESM(require_react(), 1), Result = {
7
7
  error(e, s) {
@@ -1,6 +1,6 @@
1
1
  import { s as __toESM } from "./chunk-BNovOVIE.js";
2
2
  import { t as require_react } from "./react-DA-nE2FX.js";
3
- import { k as dequal } from "./useTheme-Df_vGflw.js";
3
+ import { k as dequal } from "./useTheme-BA-8MM7N.js";
4
4
  var import_react = /* @__PURE__ */ __toESM(require_react(), 1);
5
5
  function useDeepCompareMemoize(e) {
6
6
  let i = import_react.useRef(e);
@@ -4,7 +4,7 @@ import { t as require_react } from "./react-DA-nE2FX.js";
4
4
  import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
5
5
  import { u as createLucideIcon } from "./dist--2Bqjvs0.js";
6
6
  import { t as require_jsx_runtime } from "./jsx-runtime-DebpN0FN.js";
7
- import { C as useSetAtom, T as atom } from "./useTheme-Df_vGflw.js";
7
+ import { C as useSetAtom, T as atom } from "./useTheme-BA-8MM7N.js";
8
8
  var Calendar = createLucideIcon("calendar", [
9
9
  ["path", {
10
10
  d: "M8 2v4",
@@ -740,6 +740,7 @@ const Constants = {
740
740
  githubPage: "https://github.com/marimo-team/marimo",
741
741
  releasesPage: "https://github.com/marimo-team/marimo/releases",
742
742
  issuesPage: "https://github.com/marimo-team/marimo/issues",
743
+ bugReportUrl: "https://github.com/marimo-team/marimo/issues/new?template=bug_report.yaml",
743
744
  feedbackForm: "https://marimo.io/feedback",
744
745
  discordLink: "https://marimo.io/discord?ref=notebook",
745
746
  docsPage: "https://docs.marimo.io",
@@ -2,23 +2,23 @@ import { s as __toESM } from "./chunk-BNovOVIE.js";
2
2
  import { _ as Logger, c as Objects, g as cn, h as Events } from "./button-BacYv-bE.js";
3
3
  import { t as require_react } from "./react-DA-nE2FX.js";
4
4
  import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
5
- import { c as asRemoteURL, v as CircleQuestionMark } from "./toDate-BRJgtOGm.js";
5
+ import { c as asRemoteURL, v as CircleQuestionMark } from "./toDate-CEOdz9nC.js";
6
6
  import "./react-dom-BTJzcVJ9.js";
7
7
  import { t as require_jsx_runtime } from "./jsx-runtime-DebpN0FN.js";
8
8
  import "./zod-CijjQh4u.js";
9
9
  import { n as ErrorBanner } from "./error-banner-DFPfz_Qf.js";
10
10
  import { t as Tooltip } from "./tooltip-Czds6Qr8.js";
11
11
  import { t as debounce_default } from "./debounce-BOD3DbfP.js";
12
- import { A as useEvent_default, n as useTheme } from "./useTheme-Df_vGflw.js";
12
+ import { A as useEvent_default, n as useTheme } from "./useTheme-BA-8MM7N.js";
13
13
  import { s as uniq } from "./arrays-sEtDRoG4.js";
14
- import { a as isValid, i as AlertTitle, n as Alert, t as arrow } from "./formats-CitsMtUm.js";
14
+ import { a as isValid, i as AlertTitle, n as Alert, t as arrow } from "./formats-CdvkxTS6.js";
15
15
  import { n as formats } from "./vega-loader.browser-CZ-J8Py3.js";
16
16
  import { a as getContainerWidth, n as vegaLoadData, s as tooltipHandler } from "./loader-Boph2xIS.js";
17
17
  import { t as j } from "./react-vega-B0sAlDTL.js";
18
18
  import "./defaultLocale-u-3osm0P.js";
19
19
  import "./defaultLocale-BoHTsDG6.js";
20
- import { t as useAsyncData } from "./useAsyncData-BSOyAbac.js";
21
- import { t as useDeepCompareMemoize } from "./useDeepCompareMemoize-BTLeWzuR.js";
20
+ import { t as useAsyncData } from "./useAsyncData-Cs0mpJTy.js";
21
+ import { t as useDeepCompareMemoize } from "./useDeepCompareMemoize-DoOZicGb.js";
22
22
  import { t as Semaphore } from "./semaphore-CNDGTzkX.js";
23
23
  var import_compiler_runtime = require_compiler_runtime(), import_react = /* @__PURE__ */ __toESM(require_react(), 1);
24
24
  function fixRelativeUrl(e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marimo-team/islands",
3
- "version": "0.23.15-dev49",
3
+ "version": "0.23.15-dev50",
4
4
  "main": "dist/main.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -44,6 +44,9 @@ export const MockRequestClient = {
44
44
  validateSQL: vi.fn().mockResolvedValue({}),
45
45
  openFile: vi.fn().mockResolvedValue({}),
46
46
  getUsageStats: vi.fn().mockResolvedValue({}),
47
+ getEnvironmentInfo: vi
48
+ .fn()
49
+ .mockRejectedValue(new Error("Environment information unavailable")),
47
50
  sendPdb: vi.fn().mockResolvedValue({}),
48
51
  sendSetBreakpoints: vi.fn().mockResolvedValue({}),
49
52
  sendListFiles: vi.fn().mockResolvedValue({ files: [] }),
@@ -28,6 +28,7 @@ import {
28
28
  KeyboardIcon,
29
29
  LayoutTemplateIcon,
30
30
  LinkIcon,
31
+ MessageCircleQuestionIcon,
31
32
  MessagesSquareIcon,
32
33
  NotebookIcon,
33
34
  PanelLeftIcon,
@@ -44,6 +45,7 @@ import {
44
45
  settingDialogAtom,
45
46
  useOpenSettingsToTab,
46
47
  } from "@/components/app-config/state";
48
+ import { FeedbackModal } from "@/components/editor/chrome/components/feedback-button";
47
49
  import { MarkdownIcon } from "@/components/editor/cell/code/icons";
48
50
  import { GitHubIcon } from "@/components/icons/github";
49
51
  import { MarimoPlusIcon } from "@/components/icons/marimo-icons";
@@ -642,6 +644,12 @@ export function useNotebookActions() {
642
644
  redundant: true,
643
645
  additionalKeywords: ["preferences", "options", "configuration"],
644
646
  },
647
+ {
648
+ icon: <MessageCircleQuestionIcon size={14} strokeWidth={1.5} />,
649
+ label: "Report an issue",
650
+ additionalKeywords: ["feedback", "bug", "issue", "report", "diagnostics"],
651
+ handle: () => openModal(<FeedbackModal onClose={closeModal} />),
652
+ },
645
653
  {
646
654
  icon: <ExternalLinkIcon size={14} strokeWidth={1.5} />,
647
655
  label: "Resources",
@@ -0,0 +1,221 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import { fireEvent, render, screen, waitFor } from "@testing-library/react";
4
+ import { Provider } from "jotai";
5
+ import type React from "react";
6
+ import { beforeEach, describe, expect, it, vi } from "vitest";
7
+ import { MockRequestClient } from "@/__mocks__/requests";
8
+ import { Dialog } from "@/components/ui/dialog";
9
+ import { Constants } from "@/core/constants";
10
+ import { TooltipProvider } from "@/components/ui/tooltip";
11
+ import { viewStateAtom } from "@/core/mode";
12
+ import { connectionAtom } from "@/core/network/connection";
13
+ import { requestClientAtom } from "@/core/network/requests";
14
+ import type { EnvironmentInfo } from "@/core/network/types";
15
+ import { filenameAtom } from "@/core/saving/file-state";
16
+ import { store } from "@/core/state/jotai";
17
+ import { WebSocketState } from "@/core/websocket/types";
18
+ import * as copyModule from "@/utils/copy";
19
+ import { FeedbackModal } from "../feedback-button";
20
+
21
+ vi.mock("@/utils/copy", () => ({
22
+ copyToClipboard: vi.fn().mockResolvedValue(undefined),
23
+ }));
24
+
25
+ const environment: EnvironmentInfo = {
26
+ marimo: "1.2.3",
27
+ editable: false,
28
+ location: "~/.venv/site-packages/marimo",
29
+ OS: "Darwin",
30
+ "OS Version": "25.0",
31
+ Processor: "arm",
32
+ "Python Version": "3.12.9",
33
+ Locale: "en_US",
34
+ Binaries: { Browser: "chrome 140", Node: "v22", uv: "0.11" },
35
+ Dependencies: { click: "8.4.2" },
36
+ "Optional Dependencies": { pandas: "3.0.0" },
37
+ "Experimental Flags": {},
38
+ };
39
+
40
+ function wrapper({ children }: { children: React.ReactNode }) {
41
+ return (
42
+ <Provider store={store}>
43
+ <TooltipProvider>
44
+ <Dialog open={true}>{children}</Dialog>
45
+ </TooltipProvider>
46
+ </Provider>
47
+ );
48
+ }
49
+
50
+ describe("FeedbackModal issue reporting", () => {
51
+ beforeEach(() => {
52
+ vi.clearAllMocks();
53
+ store.set(requestClientAtom, MockRequestClient.create());
54
+ store.set(viewStateAtom, { mode: "edit", cellAnchor: null });
55
+ store.set(connectionAtom, { state: WebSocketState.OPEN });
56
+ store.set(filenameAtom, "/project/example.py");
57
+ });
58
+
59
+ it("loads and previews issue environment details", async () => {
60
+ store.set(
61
+ requestClientAtom,
62
+ MockRequestClient.create({
63
+ getEnvironmentInfo: vi.fn().mockResolvedValue(environment),
64
+ }),
65
+ );
66
+ render(<FeedbackModal onClose={vi.fn()} />, { wrapper });
67
+
68
+ expect(screen.getByText("Loading environment details…")).toBeVisible();
69
+ await screen.findByText("Environment details");
70
+ expect(screen.getByText(/"marimo": "1.2.3"/)).toBeInTheDocument();
71
+ });
72
+
73
+ it("toggles the environment preview between show more and show less", async () => {
74
+ store.set(
75
+ requestClientAtom,
76
+ MockRequestClient.create({
77
+ getEnvironmentInfo: vi.fn().mockResolvedValue(environment),
78
+ }),
79
+ );
80
+ render(<FeedbackModal onClose={vi.fn()} />, { wrapper });
81
+ await screen.findByText("Environment details");
82
+
83
+ const toggle = screen.getByRole("button", { name: "Show more" });
84
+ fireEvent.click(toggle);
85
+ expect(
86
+ screen.getByRole("button", { name: "Show less" }),
87
+ ).toBeInTheDocument();
88
+ });
89
+
90
+ it("copies partial details when the environment request fails", async () => {
91
+ store.set(
92
+ requestClientAtom,
93
+ MockRequestClient.create({
94
+ getEnvironmentInfo: vi.fn().mockRejectedValue(new Error("offline")),
95
+ }),
96
+ );
97
+ render(<FeedbackModal onClose={vi.fn()} />, { wrapper });
98
+
99
+ await screen.findByText("Server environment information unavailable");
100
+ fireEvent.click(screen.getByRole("button", { name: "Copy issue details" }));
101
+ await waitFor(() =>
102
+ expect(copyModule.copyToClipboard).toHaveBeenCalledWith(
103
+ expect.stringContaining("Environment Collection Error"),
104
+ ),
105
+ );
106
+ });
107
+
108
+ it("copies raw environment JSON", async () => {
109
+ store.set(
110
+ requestClientAtom,
111
+ MockRequestClient.create({
112
+ getEnvironmentInfo: vi.fn().mockResolvedValue(environment),
113
+ }),
114
+ );
115
+ render(<FeedbackModal onClose={vi.fn()} />, { wrapper });
116
+ await screen.findByText("Environment details");
117
+
118
+ fireEvent.click(
119
+ screen.getByRole("button", { name: "Copy environment JSON" }),
120
+ );
121
+ await waitFor(() =>
122
+ expect(copyModule.copyToClipboard).toHaveBeenCalledWith(
123
+ expect.stringContaining('"marimo": "1.2.3"'),
124
+ ),
125
+ );
126
+ });
127
+
128
+ it("includes notebook source only after explicit opt-in", async () => {
129
+ const readCode = vi.fn().mockResolvedValue({
130
+ contents: "secret = 'review before posting'",
131
+ });
132
+ store.set(
133
+ requestClientAtom,
134
+ MockRequestClient.create({
135
+ getEnvironmentInfo: vi.fn().mockResolvedValue(environment),
136
+ readCode,
137
+ }),
138
+ );
139
+ render(<FeedbackModal onClose={vi.fn()} />, { wrapper });
140
+ await screen.findByText("Environment details");
141
+
142
+ expect(readCode).not.toHaveBeenCalled();
143
+ fireEvent.click(
144
+ screen.getByRole("checkbox", { name: "Include full notebook source" }),
145
+ );
146
+ await waitFor(() => expect(readCode).toHaveBeenCalledOnce());
147
+
148
+ fireEvent.click(screen.getByRole("button", { name: "Copy issue details" }));
149
+ await waitFor(() =>
150
+ expect(copyModule.copyToClipboard).toHaveBeenCalledWith(
151
+ expect.stringContaining("Notebook source: example.py"),
152
+ ),
153
+ );
154
+ });
155
+
156
+ it("discards notebook source after unchecking", async () => {
157
+ const readCode = vi.fn().mockResolvedValue({ contents: "x = 1" });
158
+ store.set(
159
+ requestClientAtom,
160
+ MockRequestClient.create({
161
+ getEnvironmentInfo: vi.fn().mockResolvedValue(environment),
162
+ readCode,
163
+ }),
164
+ );
165
+ render(<FeedbackModal onClose={vi.fn()} />, { wrapper });
166
+ await screen.findByText("Environment details");
167
+
168
+ const checkbox = screen.getByRole("checkbox", {
169
+ name: "Include full notebook source",
170
+ });
171
+ fireEvent.click(checkbox);
172
+ await waitFor(() => expect(readCode).toHaveBeenCalledOnce());
173
+ fireEvent.click(checkbox);
174
+
175
+ fireEvent.click(screen.getByRole("button", { name: "Copy issue details" }));
176
+ await waitFor(() =>
177
+ expect(copyModule.copyToClipboard).toHaveBeenCalledWith(
178
+ expect.not.stringContaining("Notebook source"),
179
+ ),
180
+ );
181
+ });
182
+
183
+ it("disables the notebook option and explains why when disconnected", async () => {
184
+ store.set(connectionAtom, { state: WebSocketState.CONNECTING });
185
+ store.set(
186
+ requestClientAtom,
187
+ MockRequestClient.create({
188
+ getEnvironmentInfo: vi.fn().mockResolvedValue(environment),
189
+ }),
190
+ );
191
+ render(<FeedbackModal onClose={vi.fn()} />, { wrapper });
192
+ await screen.findByText("Environment details");
193
+
194
+ expect(
195
+ screen.getByRole("checkbox", { name: "Include full notebook source" }),
196
+ ).toBeDisabled();
197
+ expect(
198
+ screen.getByText("Connect the notebook to include its source."),
199
+ ).toBeInTheDocument();
200
+ });
201
+
202
+ it("prefills the GitHub issue link with the environment once loaded", async () => {
203
+ store.set(
204
+ requestClientAtom,
205
+ MockRequestClient.create({
206
+ getEnvironmentInfo: vi.fn().mockResolvedValue(environment),
207
+ }),
208
+ );
209
+ render(<FeedbackModal onClose={vi.fn()} />, { wrapper });
210
+
211
+ const link = screen.getByRole("link", { name: "Open GitHub issue" });
212
+ expect(link).toHaveAttribute("href", Constants.bugReportUrl);
213
+
214
+ await screen.findByText("Environment details");
215
+ await waitFor(() => {
216
+ const href = link.getAttribute("href") ?? "";
217
+ expect(href).toContain("&env=");
218
+ expect(href).toContain(encodeURIComponent('"marimo": "1.2.3"'));
219
+ });
220
+ });
221
+ });