@marimo-team/islands 0.23.2-dev24 → 0.23.2-dev27
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/{chat-ui-Cv0kWRcQ.js → chat-ui-CQtPb6Dj.js} +1 -1
- package/dist/main.js +3 -3
- package/dist/{process-output-BM9cXHzx.js → process-output-CzeGyEyz.js} +2 -2
- package/package.json +1 -1
- package/src/core/cells/__tests__/apply-transaction.test.ts +42 -0
- package/src/core/cells/__tests__/logs.test.ts +101 -0
- package/src/core/cells/logs.ts +9 -1
|
@@ -6,7 +6,7 @@ import { _ as Logger, c as Objects, g as cn, l as useEventListener, t as Button
|
|
|
6
6
|
import { t as require_react } from "./react-DA-nE2FX.js";
|
|
7
7
|
import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
|
|
8
8
|
import { r as toast } from "./copy-Bp6CK_Fg.js";
|
|
9
|
-
import { A as MarimoIncomingMessageEvent, An as Trash2, Bt as variablesAtom, C as contextToXml, Cn as Anchor2, D as AccordionContent, E as Accordion, Hn as CircleX, I as ChatBubbleIcon, In as Info, Jt as getTableType, Kt as allTablesAtom, Ln as File$1, Mt as moveToEndOfEditor, O as AccordionItem, Pn as LoaderCircle, Q as cellErrorsAtom, S as Sections, St as displayCellName, T as AIContextRegistry, U as deserializeBlob, Vt as PluralWord, W as base64ToDataURL, Xt as getRequestClient, Z as renderHTML, a as toPng, an as ZodLocalStorage, d as Spinner, en as singleFacet, f as Popover, g as isOutputEmpty, h as PopoverTrigger, k as AccordionTrigger, kn as Wrench, kt as createVariableInfoElement, m as PopoverContent, n as blobToString, nt as notebookAtom, o as MarkdownRenderer, qt as dataSourceConnectionsAtom, t as processOutput, un as CellOutputId, w as AIContextProvider, x as Boosts, xn as atomWithStorage, y as DatasourceContextProvider, zt as jotaiJsonStorage } from "./process-output-
|
|
9
|
+
import { A as MarimoIncomingMessageEvent, An as Trash2, Bt as variablesAtom, C as contextToXml, Cn as Anchor2, D as AccordionContent, E as Accordion, Hn as CircleX, I as ChatBubbleIcon, In as Info, Jt as getTableType, Kt as allTablesAtom, Ln as File$1, Mt as moveToEndOfEditor, O as AccordionItem, Pn as LoaderCircle, Q as cellErrorsAtom, S as Sections, St as displayCellName, T as AIContextRegistry, U as deserializeBlob, Vt as PluralWord, W as base64ToDataURL, Xt as getRequestClient, Z as renderHTML, a as toPng, an as ZodLocalStorage, d as Spinner, en as singleFacet, f as Popover, g as isOutputEmpty, h as PopoverTrigger, k as AccordionTrigger, kn as Wrench, kt as createVariableInfoElement, m as PopoverContent, n as blobToString, nt as notebookAtom, o as MarkdownRenderer, qt as dataSourceConnectionsAtom, t as processOutput, un as CellOutputId, w as AIContextProvider, x as Boosts, xn as atomWithStorage, y as DatasourceContextProvider, zt as jotaiJsonStorage } from "./process-output-CzeGyEyz.js";
|
|
10
10
|
import "./chunk-5FQGJX7Z-VIref9gx.js";
|
|
11
11
|
import { u as createLucideIcon } from "./dist-CTtLBPLZ.js";
|
|
12
12
|
import { C as logNever, I as X, n as Strings, t as Label } from "./label-BebYlsDV.js";
|
package/dist/main.js
CHANGED
|
@@ -22,7 +22,7 @@ import { _ as Logger, c as Objects, g as cn, h as Events, i as NOT_SET, l as use
|
|
|
22
22
|
import { t as require_react } from "./react-DA-nE2FX.js";
|
|
23
23
|
import { t as require_compiler_runtime } from "./compiler-runtime-CEbnTgxf.js";
|
|
24
24
|
import { n as Copy, r as toast, t as copyToClipboard } from "./copy-Bp6CK_Fg.js";
|
|
25
|
-
import { $ as createActions, $t as isUninstantiated, A as MarimoIncomingMessageEvent, An as Trash2, At as PathBuilder, B as DotFilledIcon, Bn as Database, Ct as getValidName, D as AccordionContent, Dn as Root2$4, Dt as customPythonLanguageSupport, E as Accordion, En as Item$1, Et as Checkbox, F as BorderAllIcon, Fn as Layers, Ft as generateUUID, G as base64ToDataView, Gn as esm_default$1, Gt as require_client, H as PinRightIcon, Hn as CircleX, Ht as PluralWords, In as Info, It as useChromeActions, J as extractBase64FromDataURL, K as base64ToUint8Array, Kn as import_lib, L as CheckIcon, Ln as File, Lt as repl, M as MarimoValueReadyEvent, Mn as PaintRoller, N as MarimoValueUpdateEvent, Nn as NotebookPen, Nt as goToCellLine, O as AccordionItem, On as Trigger2, Ot as MarkdownLanguageAdapter, P as createInputEvent, Pn as LoaderCircle, Pt as DeferredRequestRegistry, Qt as useRequestClient, R as ChevronDownIcon, Rn as FileText, Rt as adaptForLocalStorage, Sn as selectAtom, St as displayCellName, Tn as Content2$1, Tt as normalizeName, U as deserializeBlob, Un as CircleAlert, Ut as DATA_TYPE_ICON, V as PinLeftIcon, Vn as Columns2, Vt as PluralWord, Wn as Braces, Wt as getDataTypeColor, X as safeExtractSetUIElementMessageBuffers, Xt as getRequestClient, Y as isDataURLString, Yt as convertStatsName, Z as renderHTML, Zt as requestClientAtom, _ as useExpandedConsoleOutput, _n as jsonParseWithSpecialChar, _t as isErrorMime, a as toPng, at as reducer, b as getDatasourceContext, bn as atomWithReducer, bt as DATA_CELL_ID, c as useCellFocusActions, cn as parseDataset, ct as useCellNames, d as Spinner, dn as HTMLCellId, dt as getInitialAppMode, et as getCellEditorView, f as Popover$1, fn as SCRATCH_CELL_ID, ft as initialModeAtom, g as isOutputEmpty, gn as RANDOM_ID_ATTR, gt as outputIsStale, h as PopoverTrigger, hn as OBJECT_ID_ATTR, ht as outputIsLoading, i as PythonIcon, in as NotebookScopedLocalStorage, it as numColumnsAtom, j as MarimoValueInputEvent, jn as Table2, jt as Paths, k as AccordionTrigger, kn as Wrench, l as useLastFocusedCellId, ln as parseInitialValue, lt as createCell, m as PopoverContent, mn as findCellId, mt as viewStateAtom, n as blobToString, nn as extractAllTracebackInfo, nt as notebookAtom, o as MarkdownRenderer, on as filenameAtom, ot as useCellActions, p as PopoverClose$1, pn as UIElementId, pt as kioskModeAtom, q as dataViewToBase64, r as filesToBase64, rn as getTracebackInfo, rt as notebookOutline, s as LazyAnyLanguageCodeMirror, sn as parseAttrValue, st as useCellIds, t as processOutput, tn as elementContainsMarimoCellFile, tt as getCellNames, u as maybeAddAltairImport, un as CellOutputId, ut as AnsiUp, v as useExpandedOutput, vn as jsonToMarkdown, vt as headingToIdentifier, wn as Close$1, wt as isInternalCellName, xn as atomWithStorage, xt as getCellDomProps, yn as jsonToTSV, yt as sanitizeHtml, z as ChevronRightIcon, zn as Eye, zt as jotaiJsonStorage, __tla as __tla_0 } from "./process-output-
|
|
25
|
+
import { $ as createActions, $t as isUninstantiated, A as MarimoIncomingMessageEvent, An as Trash2, At as PathBuilder, B as DotFilledIcon, Bn as Database, Ct as getValidName, D as AccordionContent, Dn as Root2$4, Dt as customPythonLanguageSupport, E as Accordion, En as Item$1, Et as Checkbox, F as BorderAllIcon, Fn as Layers, Ft as generateUUID, G as base64ToDataView, Gn as esm_default$1, Gt as require_client, H as PinRightIcon, Hn as CircleX, Ht as PluralWords, In as Info, It as useChromeActions, J as extractBase64FromDataURL, K as base64ToUint8Array, Kn as import_lib, L as CheckIcon, Ln as File, Lt as repl, M as MarimoValueReadyEvent, Mn as PaintRoller, N as MarimoValueUpdateEvent, Nn as NotebookPen, Nt as goToCellLine, O as AccordionItem, On as Trigger2, Ot as MarkdownLanguageAdapter, P as createInputEvent, Pn as LoaderCircle, Pt as DeferredRequestRegistry, Qt as useRequestClient, R as ChevronDownIcon, Rn as FileText, Rt as adaptForLocalStorage, Sn as selectAtom, St as displayCellName, Tn as Content2$1, Tt as normalizeName, U as deserializeBlob, Un as CircleAlert, Ut as DATA_TYPE_ICON, V as PinLeftIcon, Vn as Columns2, Vt as PluralWord, Wn as Braces, Wt as getDataTypeColor, X as safeExtractSetUIElementMessageBuffers, Xt as getRequestClient, Y as isDataURLString, Yt as convertStatsName, Z as renderHTML, Zt as requestClientAtom, _ as useExpandedConsoleOutput, _n as jsonParseWithSpecialChar, _t as isErrorMime, a as toPng, at as reducer, b as getDatasourceContext, bn as atomWithReducer, bt as DATA_CELL_ID, c as useCellFocusActions, cn as parseDataset, ct as useCellNames, d as Spinner, dn as HTMLCellId, dt as getInitialAppMode, et as getCellEditorView, f as Popover$1, fn as SCRATCH_CELL_ID, ft as initialModeAtom, g as isOutputEmpty, gn as RANDOM_ID_ATTR, gt as outputIsStale, h as PopoverTrigger, hn as OBJECT_ID_ATTR, ht as outputIsLoading, i as PythonIcon, in as NotebookScopedLocalStorage, it as numColumnsAtom, j as MarimoValueInputEvent, jn as Table2, jt as Paths, k as AccordionTrigger, kn as Wrench, l as useLastFocusedCellId, ln as parseInitialValue, lt as createCell, m as PopoverContent, mn as findCellId, mt as viewStateAtom, n as blobToString, nn as extractAllTracebackInfo, nt as notebookAtom, o as MarkdownRenderer, on as filenameAtom, ot as useCellActions, p as PopoverClose$1, pn as UIElementId, pt as kioskModeAtom, q as dataViewToBase64, r as filesToBase64, rn as getTracebackInfo, rt as notebookOutline, s as LazyAnyLanguageCodeMirror, sn as parseAttrValue, st as useCellIds, t as processOutput, tn as elementContainsMarimoCellFile, tt as getCellNames, u as maybeAddAltairImport, un as CellOutputId, ut as AnsiUp, v as useExpandedOutput, vn as jsonToMarkdown, vt as headingToIdentifier, wn as Close$1, wt as isInternalCellName, xn as atomWithStorage, xt as getCellDomProps, yn as jsonToTSV, yt as sanitizeHtml, z as ChevronRightIcon, zn as Eye, zt as jotaiJsonStorage, __tla as __tla_0 } from "./process-output-CzeGyEyz.js";
|
|
26
26
|
import { __tla as __tla_1 } from "./chunk-5FQGJX7Z-VIref9gx.js";
|
|
27
27
|
import { o as useSize, s as Root$4, u as createLucideIcon } from "./dist-CTtLBPLZ.js";
|
|
28
28
|
import { A as $896ba0a80a8f4d36$export$85fd5fdf27bacc79, C as DEFAULT_COLOR_SCHEME, D as SCALE_TYPE_DESCRIPTIONS, E as EMPTY_VALUE$1, F as ListFilter, I as ChartPie, L as ChartColumn, M as $5a387cc49350e6db$export$722debc0e56fea39, N as Table$1, O as TIME_UNIT_DESCRIPTIONS, P as SquareFunction, S as DEFAULT_AGGREGATION, T as DEFAULT_TIME_UNIT, _ as AGGREGATION_TYPE_DESCRIPTIONS, a as AGGREGATION_FNS$1, b as COLOR_SCHEMES, c as COLOR_BY_FIELDS, d as NONE_VALUE, f as SELECTABLE_DATA_TYPES, g as TIME_UNITS, h as STRING_AGGREGATION_FNS, i as convertDataTypeToSelectable, j as $fb18d541ea1ad717$export$ad991b66133851cf, k as escapeFieldName, l as COMBINED_TIME_UNITS, m as SORT_TYPES, n as createSpecWithoutData, o as BIN_AGGREGATION, p as SINGLE_TIME_UNITS, r as isFieldSet, s as CHART_TYPES, t as augmentSpecWithData, u as ChartType, v as AGGREGATION_TYPE_ICON, w as DEFAULT_MAX_BINS_FACET, x as COUNT_FIELD, y as CHART_TYPE_ICON } from "./spec-3EPbPQZH.js";
|
|
@@ -37252,7 +37252,7 @@ ${w}`,
|
|
|
37252
37252
|
};
|
|
37253
37253
|
}
|
|
37254
37254
|
};
|
|
37255
|
-
var LazyChatbot = import_react.lazy(() => import("./chat-ui-
|
|
37255
|
+
var LazyChatbot = import_react.lazy(() => import("./chat-ui-CQtPb6Dj.js").then((e) => ({
|
|
37256
37256
|
default: e.Chatbot
|
|
37257
37257
|
}))), messageSchema = array(object({
|
|
37258
37258
|
id: string(),
|
|
@@ -68920,7 +68920,7 @@ ${c}
|
|
|
68920
68920
|
return Logger.warn("Failed to get version from mount config"), null;
|
|
68921
68921
|
}
|
|
68922
68922
|
}
|
|
68923
|
-
const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.23.2-
|
|
68923
|
+
const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.23.2-dev27"), showCodeInRunModeAtom = atom(true);
|
|
68924
68924
|
atom(null);
|
|
68925
68925
|
var VIRTUAL_FILE_REGEX = /\/@file\/([^\s"&'/]+)\.([\dA-Za-z]+)/g, VirtualFileTracker = class e {
|
|
68926
68926
|
constructor() {
|
|
@@ -25589,8 +25589,8 @@ ${n.sqlString}
|
|
|
25589
25589
|
message: JSON.stringify(t3)
|
|
25590
25590
|
});
|
|
25591
25591
|
});
|
|
25592
|
-
let t2 = e.output.data.filter((e2) => "type" in e2 && e2.type === "exception" || e2.type === "internal");
|
|
25593
|
-
if (t2.length > 0 && !didAlreadyToastError) {
|
|
25592
|
+
let t2 = e.output.data.filter((e2) => "type" in e2 && e2.type === "exception" || e2.type === "internal"), n2 = store.get(initialModeAtom) === "read";
|
|
25593
|
+
if (t2.length > 0 && !didAlreadyToastError && n2) {
|
|
25594
25594
|
didAlreadyToastError = true;
|
|
25595
25595
|
let e2 = t2.find((e3) => e3.traceback);
|
|
25596
25596
|
toast(e2 ? {
|
package/package.json
CHANGED
|
@@ -305,4 +305,46 @@ describe("applyTransactionChanges edge cases", () => {
|
|
|
305
305
|
"
|
|
306
306
|
`);
|
|
307
307
|
});
|
|
308
|
+
|
|
309
|
+
it("set-code updates the mounted editor view's document", () => {
|
|
310
|
+
// Existing tests only check cellData.code — this covers the editor
|
|
311
|
+
// view side, so regressions in the reducer's imperative sync (or in
|
|
312
|
+
// the CellEditor useEffect that backs it up) don't go unnoticed.
|
|
313
|
+
setup('x = "BEFORE"');
|
|
314
|
+
const [a] = state.cellIds.inOrderIds;
|
|
315
|
+
const editorView = state.cellHandles[a].current?.editorViewOrNull;
|
|
316
|
+
expect(editorView?.state.doc.toString()).toBe('x = "BEFORE"');
|
|
317
|
+
|
|
318
|
+
apply([{ type: "set-code", cellId: a, code: 'x = "AFTER"' }]);
|
|
319
|
+
|
|
320
|
+
expect(state.cellData[a].code).toBe('x = "AFTER"');
|
|
321
|
+
expect(editorView?.state.doc.toString()).toBe('x = "AFTER"');
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
it("create-cell then set-code on same cell updates editor", () => {
|
|
325
|
+
// Mirrors the code_mode flow that exposed marimo-pair#27: create_cell
|
|
326
|
+
// in one batch, edit_cell in a second batch, each arriving as a
|
|
327
|
+
// separate transaction.
|
|
328
|
+
setup();
|
|
329
|
+
apply([
|
|
330
|
+
{
|
|
331
|
+
type: "create-cell",
|
|
332
|
+
cellId: cellId("repro"),
|
|
333
|
+
code: 'x = "BEFORE"',
|
|
334
|
+
name: "repro_bug",
|
|
335
|
+
config: {},
|
|
336
|
+
},
|
|
337
|
+
]);
|
|
338
|
+
const editorView =
|
|
339
|
+
state.cellHandles[cellId("repro")].current?.editorViewOrNull;
|
|
340
|
+
expect(editorView?.state.doc.toString()).toBe('x = "BEFORE"');
|
|
341
|
+
|
|
342
|
+
apply([
|
|
343
|
+
{ type: "set-code", cellId: cellId("repro"), code: 'x = "AFTER"' },
|
|
344
|
+
{ type: "reorder-cells", cellIds: [cellId("repro")] },
|
|
345
|
+
]);
|
|
346
|
+
|
|
347
|
+
expect(state.cellData[cellId("repro")].code).toBe('x = "AFTER"');
|
|
348
|
+
expect(editorView?.state.doc.toString()).toBe('x = "AFTER"');
|
|
349
|
+
});
|
|
308
350
|
});
|
|
@@ -5,6 +5,11 @@ import { cellId } from "@/__tests__/branded";
|
|
|
5
5
|
import type { CellMessage } from "../../kernel/messages";
|
|
6
6
|
import { formatLogTimestamp, getCellLogsForMessage } from "../logs";
|
|
7
7
|
|
|
8
|
+
// Stable mock reference so every (re)import of use-toast sees the same spy,
|
|
9
|
+
// even after vi.resetModules() clears the module cache between tests.
|
|
10
|
+
const { toastMock } = vi.hoisted(() => ({ toastMock: vi.fn() }));
|
|
11
|
+
vi.mock("@/components/ui/use-toast", () => ({ toast: toastMock }));
|
|
12
|
+
|
|
8
13
|
describe("getCellLogsForMessage", () => {
|
|
9
14
|
beforeEach(() => {
|
|
10
15
|
// Mock console.log to avoid cluttering test output
|
|
@@ -324,6 +329,102 @@ describe("getCellLogsForMessage", () => {
|
|
|
324
329
|
});
|
|
325
330
|
});
|
|
326
331
|
|
|
332
|
+
describe("getCellLogsForMessage - internal error toast", () => {
|
|
333
|
+
// Re-imported per test after vi.resetModules() so the module-level
|
|
334
|
+
// `didAlreadyToastError` flag starts fresh and all jotai atom references
|
|
335
|
+
// (initialModeAtom, etc.) match the versions used by the reset logs.ts.
|
|
336
|
+
let getLogs: typeof import("../logs").getCellLogsForMessage;
|
|
337
|
+
let store: typeof import("@/core/state/jotai").store;
|
|
338
|
+
let initialModeAtom: typeof import("@/core/mode").initialModeAtom;
|
|
339
|
+
|
|
340
|
+
beforeEach(async () => {
|
|
341
|
+
vi.spyOn(console, "log").mockImplementation(() => {
|
|
342
|
+
// no-op
|
|
343
|
+
});
|
|
344
|
+
vi.resetModules();
|
|
345
|
+
({ getCellLogsForMessage: getLogs } = await import("../logs"));
|
|
346
|
+
({ store } = await import("@/core/state/jotai"));
|
|
347
|
+
({ initialModeAtom } = await import("@/core/mode"));
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
afterEach(() => {
|
|
351
|
+
vi.restoreAllMocks();
|
|
352
|
+
vi.clearAllMocks();
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
const makeErrorCellMessage = (id: CellMessage["cell_id"]): CellMessage => ({
|
|
356
|
+
cell_id: id,
|
|
357
|
+
console: [],
|
|
358
|
+
output: {
|
|
359
|
+
mimetype: "application/vnd.marimo+error",
|
|
360
|
+
data: [
|
|
361
|
+
{
|
|
362
|
+
type: "exception",
|
|
363
|
+
exception_type: "ValueError",
|
|
364
|
+
msg: "something exploded",
|
|
365
|
+
traceback: ["File foo.py, line 1", "ValueError: something exploded"],
|
|
366
|
+
},
|
|
367
|
+
],
|
|
368
|
+
channel: "marimo-error",
|
|
369
|
+
timestamp: 0,
|
|
370
|
+
} as unknown as CellMessage["output"],
|
|
371
|
+
status: "idle",
|
|
372
|
+
stale_inputs: null,
|
|
373
|
+
timestamp: 0,
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
test("shows toast for internal errors in app (read) mode", () => {
|
|
377
|
+
store.set(initialModeAtom, "read");
|
|
378
|
+
|
|
379
|
+
getLogs(makeErrorCellMessage(cellId("cell-err-1")));
|
|
380
|
+
|
|
381
|
+
expect(toastMock).toHaveBeenCalledTimes(1);
|
|
382
|
+
expect(toastMock).toHaveBeenCalledWith(
|
|
383
|
+
expect.objectContaining({
|
|
384
|
+
title: "An internal error occurred",
|
|
385
|
+
variant: "danger",
|
|
386
|
+
}),
|
|
387
|
+
);
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
test("does not show toast for internal errors in edit mode", () => {
|
|
391
|
+
store.set(initialModeAtom, "edit");
|
|
392
|
+
|
|
393
|
+
getLogs(makeErrorCellMessage(cellId("cell-err-2")));
|
|
394
|
+
|
|
395
|
+
expect(toastMock).not.toHaveBeenCalled();
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
test("edit-mode errors do not consume the once-per-session toast slot", () => {
|
|
399
|
+
// Errors received while in edit mode should be silently skipped...
|
|
400
|
+
store.set(initialModeAtom, "edit");
|
|
401
|
+
getLogs(makeErrorCellMessage(cellId("cell-err-3")));
|
|
402
|
+
expect(toastMock).not.toHaveBeenCalled();
|
|
403
|
+
|
|
404
|
+
// ...and a subsequent error in app mode should still toast.
|
|
405
|
+
store.set(initialModeAtom, "read");
|
|
406
|
+
getLogs(makeErrorCellMessage(cellId("cell-err-4")));
|
|
407
|
+
expect(toastMock).toHaveBeenCalledTimes(1);
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
test("toast only fires once across multiple app-mode errors", () => {
|
|
411
|
+
store.set(initialModeAtom, "read");
|
|
412
|
+
|
|
413
|
+
getLogs(makeErrorCellMessage(cellId("cell-err-5")));
|
|
414
|
+
getLogs(makeErrorCellMessage(cellId("cell-err-6")));
|
|
415
|
+
|
|
416
|
+
expect(toastMock).toHaveBeenCalledTimes(1);
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
test("suppresses toast when initial mode has not been set", () => {
|
|
420
|
+
// Leave initialModeAtom at its default (undefined); getInitialAppMode
|
|
421
|
+
// will throw and the logic should swallow it without toasting.
|
|
422
|
+
getLogs(makeErrorCellMessage(cellId("cell-err-7")));
|
|
423
|
+
|
|
424
|
+
expect(toastMock).not.toHaveBeenCalled();
|
|
425
|
+
});
|
|
426
|
+
});
|
|
427
|
+
|
|
327
428
|
describe("formatLogTimestamp", () => {
|
|
328
429
|
test("formats unix timestamp correctly", () => {
|
|
329
430
|
// January 1, 2024, 12:00:00 PM UTC
|
package/src/core/cells/logs.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { Strings } from "@/utils/strings";
|
|
|
7
7
|
import type { CellMessage, OutputMessage } from "../kernel/messages";
|
|
8
8
|
import { isErrorMime } from "../mime";
|
|
9
9
|
import type { CellId } from "./ids";
|
|
10
|
+
import { initialModeAtom } from "../mode";
|
|
10
11
|
import { store } from "../state/jotai";
|
|
11
12
|
import { tracebackModalAtom } from "../errors/traceback-atom";
|
|
12
13
|
import React from "react";
|
|
@@ -83,7 +84,14 @@ export function getCellLogsForMessage(cell: CellMessage): CellLog[] {
|
|
|
83
84
|
error.type === "internal",
|
|
84
85
|
);
|
|
85
86
|
|
|
86
|
-
|
|
87
|
+
// Only show the toast in app mode: edit mode already surfaces errors in
|
|
88
|
+
// the cell UI, so toasting there would be noisy and duplicative. Read the
|
|
89
|
+
// atom directly so an unset initial mode (e.g. in tests/islands) simply
|
|
90
|
+
// returns undefined instead of throwing and masking real errors.
|
|
91
|
+
const isAppMode = store.get(initialModeAtom) === "read";
|
|
92
|
+
|
|
93
|
+
// Only show toast once, and only in app mode
|
|
94
|
+
if (exceptionErrors.length > 0 && !didAlreadyToastError && isAppMode) {
|
|
87
95
|
didAlreadyToastError = true;
|
|
88
96
|
|
|
89
97
|
// Find first error with a traceback
|