@marimo-team/frontend 0.23.2-dev33 → 0.23.2-dev37

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 (72) hide show
  1. package/dist/assets/{CellStatus-BbjcipkC.js → CellStatus-CjPI69hZ.js} +1 -1
  2. package/dist/assets/JsonOutput-CcRaYhT-.js +49 -0
  3. package/dist/assets/{MarimoErrorOutput-5375ui1_.js → MarimoErrorOutput-BYuSTceB.js} +1 -1
  4. package/dist/assets/{RenderHTML-CuG7mL48.js → RenderHTML-DQttWYDe.js} +1 -1
  5. package/dist/assets/{add-cell-with-ai-CwfTE2uS.js → add-cell-with-ai-CPCm86Mb.js} +1 -1
  6. package/dist/assets/{add-connection-dialog-DNp43sa6.js → add-connection-dialog-dNJgn8Ox.js} +1 -1
  7. package/dist/assets/{agent-panel-Cti8l2Lw.js → agent-panel-CZV8iRUC.js} +1 -1
  8. package/dist/assets/{ai-model-dropdown-CIDp_OAZ.js → ai-model-dropdown-CHNX-Yvn.js} +1 -1
  9. package/dist/assets/{app-config-button-C5kBtMkE.js → app-config-button-B7ptjTn8.js} +1 -1
  10. package/dist/assets/{cell-editor-BF3cUKtr.js → cell-editor-OuSBQcHa.js} +1 -1
  11. package/dist/assets/{cell-link-X1vUDC-l.js → cell-link-CeI1GPxX.js} +1 -1
  12. package/dist/assets/{cells-CqMx0oBX.js → cells-CkQXmtbU.js} +22 -22
  13. package/dist/assets/{chat-display-Bc4wVzhv.js → chat-display-qsryOR04.js} +1 -1
  14. package/dist/assets/{chat-panel-BvkBh85e.js → chat-panel-B0ITjHrb.js} +1 -1
  15. package/dist/assets/{chat-ui-1CuehUu7.js → chat-ui-FgswEfHK.js} +1 -1
  16. package/dist/assets/{column-preview-DWBUAIll.js → column-preview-hHmxmCnB.js} +1 -1
  17. package/dist/assets/{command-palette-9-CbS3k1.js → command-palette-BlNedegf.js} +1 -1
  18. package/dist/assets/{common-DTojyQQA.js → common-B1gBVTty.js} +1 -1
  19. package/dist/assets/{components-CO3DLEcG.js → components-4Llkti3I.js} +1 -1
  20. package/dist/assets/{components-uWLk0Ao8.js → components-GayobceR.js} +1 -1
  21. package/dist/assets/{datasource-BCHVdrCo.js → datasource-DohSpOc8.js} +1 -1
  22. package/dist/assets/{dependency-graph-panel-BWn7h9wb.js → dependency-graph-panel-_bVWly6X.js} +1 -1
  23. package/dist/assets/{documentation-panel-CuOt-Z6x.js → documentation-panel-DBQM75dO.js} +1 -1
  24. package/dist/assets/{download-D_pubC1I.js → download-aayD25Wi.js} +1 -1
  25. package/dist/assets/{edit-page-Cg5ghFqX.js → edit-page-Cw8d_f2S.js} +3 -3
  26. package/dist/assets/{error-panel-CkkpsopE.js → error-panel-D-9BQN8U.js} +1 -1
  27. package/dist/assets/{file-explorer-panel-ByZF9zx-.js → file-explorer-panel-CxfXG24m.js} +1 -1
  28. package/dist/assets/{file-icons-DN_baPhK.js → file-icons-UkZeY45t.js} +1 -1
  29. package/dist/assets/{floating-outline-DJk8g3xf.js → floating-outline--H6knKWf.js} +1 -1
  30. package/dist/assets/{focus-DiGH69Kn.js → focus-DxuOmtKI.js} +1 -1
  31. package/dist/assets/{form-KmDaZVTO.js → form-3fPAWHbK.js} +1 -1
  32. package/dist/assets/{home-page-C7mQiKLD.js → home-page-_beA3R51.js} +1 -1
  33. package/dist/assets/{hooks-BM9EfiOp.js → hooks-B4ys0c0n.js} +1 -1
  34. package/dist/assets/{html-to-image-Mg1yV_4p.js → html-to-image-RqGk5tNG.js} +1 -1
  35. package/dist/assets/{index-CPsIlB0T.js → index-lpZP7WFc.js} +4 -4
  36. package/dist/assets/{kiosk-mode-BdbymF22.js → kiosk-mode-CtVLUXra.js} +1 -1
  37. package/dist/assets/{layout-DNGjXY3z.js → layout-Dz6l5gDf.js} +1 -1
  38. package/dist/assets/{logs-panel-CkaYjpxa.js → logs-panel-Gg9rCqvY.js} +1 -1
  39. package/dist/assets/{markdown-renderer-CkoTHWJE.js → markdown-renderer-CPtNggG4.js} +1 -1
  40. package/dist/assets/{name-cell-input-TTOOCLnQ.js → name-cell-input-D3CxUPEc.js} +1 -1
  41. package/dist/assets/{outline-panel-CYuwRjGv.js → outline-panel-COUl_K0O.js} +1 -1
  42. package/dist/assets/{packages-panel-D4QOK5Cb.js → packages-panel-C_MusfCT.js} +1 -1
  43. package/dist/assets/{panels-ZZ8FKmBF.js → panels-DwDu5SGI.js} +1 -1
  44. package/dist/assets/{process-output-CE7gMOBm.js → process-output-B4IfPdhN.js} +1 -1
  45. package/dist/assets/{readonly-python-code-B9SF0mix.js → readonly-python-code-CcyUzrM-.js} +1 -1
  46. package/dist/assets/{run-page-DeaWGZc4.js → run-page-C2kQLw9h.js} +1 -1
  47. package/dist/assets/{scratchpad-panel-Btac0sjQ.js → scratchpad-panel-BN9Sv2Hh.js} +1 -1
  48. package/dist/assets/{session-panel-xhkVht2h.js → session-panel-CMI4faW3.js} +1 -1
  49. package/dist/assets/{snippets-panel-Bpmn4gv5.js → snippets-panel-lJwSEA5M.js} +1 -1
  50. package/dist/assets/{state-C4QFfXtk.js → state-BoK4XWzN.js} +1 -1
  51. package/dist/assets/{state-DhU3c-w1.js → state-DbB3Bx1n.js} +1 -1
  52. package/dist/assets/{textarea-DseU4cOm.js → textarea-jLEyGvlo.js} +1 -1
  53. package/dist/assets/{tracing-Dk06HpUt.js → tracing-CFor4yuK.js} +1 -1
  54. package/dist/assets/{tracing-panel-gm0fFlZw.js → tracing-panel-DM9aAZvv.js} +2 -2
  55. package/dist/assets/{useAddCell-B1k_8hAF.js → useAddCell-pxYjm9Vz.js} +1 -1
  56. package/dist/assets/{useCellActionButton-BsYRhLlf.js → useCellActionButton-GNljpGS_.js} +1 -1
  57. package/dist/assets/{useDeleteCell-CJxlmHz2.js → useDeleteCell-1EiCOF19.js} +1 -1
  58. package/dist/assets/{useDependencyPanelTab-BSOvUeMb.js → useDependencyPanelTab-DLosV83A.js} +1 -1
  59. package/dist/assets/{useNotebookActions-nKUVBj_L.js → useNotebookActions-D_eNWvNH.js} +1 -1
  60. package/dist/assets/{useRunCells-CVXE46G7.js → useRunCells-BGxIfw3R.js} +1 -1
  61. package/dist/assets/{useSplitCell-DICuWMVK.js → useSplitCell-oguWPZqA.js} +1 -1
  62. package/dist/index.html +22 -22
  63. package/package.json +1 -1
  64. package/src/components/data-table/__tests__/sentinel-cell.test.tsx +83 -0
  65. package/src/components/data-table/__tests__/utils.test.ts +128 -0
  66. package/src/components/data-table/column-header.tsx +11 -2
  67. package/src/components/data-table/columns.tsx +16 -2
  68. package/src/components/data-table/sentinel-cell.tsx +90 -0
  69. package/src/components/data-table/types.ts +23 -0
  70. package/src/components/data-table/utils.ts +74 -1
  71. package/src/components/ui/toast.tsx +16 -7
  72. package/dist/assets/JsonOutput-CEbiCmLZ.js +0 -49
@@ -0,0 +1,90 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+
3
+ import type { CellValueSentinel, CellValueSentinelType } from "./types";
4
+
5
+ const WHITESPACE_CHARS: Record<string, { marker: string; name: string }> = {
6
+ " ": { marker: "\u2423", name: "space" }, // open box (space symbol)
7
+ "\t": { marker: "\\t", name: "tab" },
8
+ "\n": { marker: "\\n", name: "newline" },
9
+ "\r": { marker: "\\r", name: "newline" },
10
+ };
11
+
12
+ function renderWhitespaceMarkers(str: string): string {
13
+ return [...str].map((ch) => WHITESPACE_CHARS[ch]?.marker ?? ch).join("");
14
+ }
15
+
16
+ function describeWhitespace(str: string): string {
17
+ const counts: Record<string, number> = {};
18
+ for (const ch of str) {
19
+ const name = WHITESPACE_CHARS[ch]?.name ?? "character";
20
+ counts[name] = (counts[name] ?? 0) + 1;
21
+ }
22
+ return Object.entries(counts)
23
+ .map(([name, count]) => `${count} ${name}${count > 1 ? "s" : ""}`)
24
+ .join(", ");
25
+ }
26
+
27
+ interface SentinelConfig {
28
+ label: (value: CellValueSentinel["value"]) => string;
29
+ tooltip: (value: CellValueSentinel["value"]) => string;
30
+ ariaLabel: (value: CellValueSentinel["value"]) => string;
31
+ }
32
+
33
+ const SENTINEL_CONFIG: Record<CellValueSentinelType, SentinelConfig> = {
34
+ null: {
35
+ label: () => "None",
36
+ tooltip: () => "None",
37
+ ariaLabel: () => "None",
38
+ },
39
+ "empty-string": {
40
+ label: () => "<empty>",
41
+ tooltip: () => "<empty>",
42
+ ariaLabel: () => "empty string",
43
+ },
44
+ whitespace: {
45
+ label: (value) => renderWhitespaceMarkers(String(value)),
46
+ tooltip: (value) => describeWhitespace(String(value)),
47
+ ariaLabel: (value) => describeWhitespace(String(value)),
48
+ },
49
+ nan: {
50
+ label: () => "NaN",
51
+ tooltip: () => "NaN",
52
+ ariaLabel: () => "NaN",
53
+ },
54
+ "positive-infinity": {
55
+ label: () => "inf",
56
+ tooltip: () => "Infinity",
57
+ ariaLabel: () => "infinity",
58
+ },
59
+ "negative-infinity": {
60
+ label: () => "-inf",
61
+ tooltip: () => "-Infinity",
62
+ ariaLabel: () => "negative infinity",
63
+ },
64
+ nat: {
65
+ label: () => "NaT",
66
+ tooltip: () => "NaT (Not a Time)",
67
+ ariaLabel: () => "Not a Time",
68
+ },
69
+ };
70
+
71
+ export function SentinelCell({
72
+ sentinel,
73
+ }: {
74
+ sentinel: CellValueSentinel;
75
+ }): React.ReactElement {
76
+ const config = SENTINEL_CONFIG[sentinel.type];
77
+ const label = config.label(sentinel.value);
78
+ const tooltip = config.tooltip(sentinel.value);
79
+ const ariaLabel = config.ariaLabel(sentinel.value);
80
+
81
+ return (
82
+ <span
83
+ className="italic text-muted-foreground bg-muted rounded px-1"
84
+ aria-label={ariaLabel}
85
+ title={tooltip}
86
+ >
87
+ <span className="opacity-70">{label}</span>
88
+ </span>
89
+ );
90
+ }
@@ -95,6 +95,29 @@ export type DataTableSelection =
95
95
  | "multi-cell"
96
96
  | null;
97
97
 
98
+ export type CellValueSentinel =
99
+ | { type: "null"; value: null | undefined }
100
+ | { type: "empty-string"; value: string }
101
+ | { type: "whitespace"; value: string }
102
+ | { type: "nan"; value: number | string }
103
+ | { type: "positive-infinity"; value: number | string }
104
+ | { type: "negative-infinity"; value: number | string }
105
+ | { type: "nat"; value: string };
106
+
107
+ export type CellValueSentinelType = CellValueSentinel["type"];
108
+
109
+ export function isNumericType(
110
+ dataType: DataType | undefined,
111
+ ): dataType is "number" | "integer" {
112
+ return dataType === "number" || dataType === "integer";
113
+ }
114
+
115
+ export function isTemporalType(
116
+ dataType: DataType | undefined,
117
+ ): dataType is "date" | "datetime" | "time" {
118
+ return dataType === "date" || dataType === "datetime" || dataType === "time";
119
+ }
120
+
98
121
  export function extractTimezone(dtype: string | undefined): string | undefined {
99
122
  if (!dtype) {
100
123
  return undefined;
@@ -5,7 +5,15 @@ import type { TableData } from "@/plugins/impl/DataTablePlugin";
5
5
  import { vegaLoadData } from "@/plugins/impl/vega/loader";
6
6
  import { jsonParseWithSpecialChar } from "@/utils/json/json-parser";
7
7
  import { getMimeValues } from "./mime-cell";
8
- import { INDEX_COLUMN_NAME } from "./types";
8
+ import type { DataType } from "@/core/kernel/messages";
9
+ import {
10
+ type CellValueSentinel,
11
+ INDEX_COLUMN_NAME,
12
+ isNumericType,
13
+ isTemporalType,
14
+ } from "./types";
15
+
16
+ const WHITESPACE_ONLY_RE = /^[\s]+$/;
9
17
 
10
18
  /**
11
19
  * Convenience function to load table data.
@@ -85,6 +93,71 @@ export function getPageIndexForRow(
85
93
  return null;
86
94
  }
87
95
 
96
+ // String representations of numeric special values.
97
+ // Only matched when the caller indicates the column is numeric.
98
+ type StringValueSentinelType = Extract<
99
+ CellValueSentinel,
100
+ { value: number | string }
101
+ >["type"];
102
+
103
+ const NUMERIC_STRING_SPECIALS: Record<string, StringValueSentinelType> = {
104
+ NaN: "nan",
105
+ Infinity: "positive-infinity",
106
+ "-Infinity": "negative-infinity",
107
+ inf: "positive-infinity",
108
+ "-inf": "negative-infinity",
109
+ };
110
+
111
+ /**
112
+ * Detect if a cell value is a sentinel (null, empty string, whitespace,
113
+ * NaN, infinity, NaT). Column-type-dependent sentinels (string "NaN",
114
+ * "NaT", etc.) are matched based on `dataType`.
115
+ */
116
+ export function detectSentinel(
117
+ value: unknown,
118
+ dataType: DataType | undefined,
119
+ ): CellValueSentinel | null {
120
+ if (value == null) {
121
+ return { type: "null", value };
122
+ }
123
+
124
+ if (typeof value === "string") {
125
+ if (value === "") {
126
+ return { type: "empty-string", value };
127
+ }
128
+ if (WHITESPACE_ONLY_RE.test(value)) {
129
+ return { type: "whitespace", value };
130
+ }
131
+ // String "NaN"/"Infinity" in a numeric column = actual special float value
132
+ if (isNumericType(dataType)) {
133
+ const type = NUMERIC_STRING_SPECIALS[value];
134
+ if (type) {
135
+ return { type, value };
136
+ }
137
+ }
138
+ // String "NaT" in a temporal column = pandas Not-a-Time sentinel
139
+ if (isTemporalType(dataType) && value === "NaT") {
140
+ return { type: "nat", value };
141
+ }
142
+ return null;
143
+ }
144
+
145
+ if (typeof value === "number") {
146
+ if (Number.isNaN(value)) {
147
+ return { type: "nan", value };
148
+ }
149
+ if (value === Number.POSITIVE_INFINITY) {
150
+ return { type: "positive-infinity", value };
151
+ }
152
+ if (value === Number.NEGATIVE_INFINITY) {
153
+ return { type: "negative-infinity", value };
154
+ }
155
+ return null;
156
+ }
157
+
158
+ return null;
159
+ }
160
+
88
161
  /**
89
162
  * Stringify an unknown value. Converts objects to JSON strings.
90
163
  * @param opts.value - The value to stringify.
@@ -24,32 +24,41 @@ const ToastViewport = React.forwardRef<
24
24
  ));
25
25
  ToastViewport.displayName = ToastPrimitives.Viewport.displayName;
26
26
 
27
+ const VARIANT_CLASSES = {
28
+ default: "bg-background border",
29
+ danger:
30
+ "group destructive text-error border-destructive bg-(--red-1) shadow-md-solid shadow-error",
31
+ } as const satisfies Record<string, string>;
32
+
33
+ type ToastVariant = keyof typeof VARIANT_CLASSES;
34
+
27
35
  const toastVariants = cva(
28
36
  "data-[swipe=move]:transition-none group relative pointer-events-auto flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=move]:translate-x-(--radix-toast-swipe-move-x) data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-(--radix-toast-swipe-end-x) data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=open]:slide-in-from-top-full sm:data-[state=open]:slide-in-from-bottom-full data-[state=closed]:slide-out-to-right-full",
29
37
  {
30
38
  variants: {
31
- variant: {
32
- default: "bg-background border",
33
- danger:
34
- "group destructive text-error border-destructive bg-(--red-1) shadow-md-solid shadow-error",
35
- },
39
+ variant: VARIANT_CLASSES,
36
40
  },
37
41
  defaultVariants: {
38
- variant: "default",
42
+ variant: "default" satisfies ToastVariant,
39
43
  },
40
44
  },
41
45
  );
42
46
 
47
+ function isToastVariant(value: unknown): value is ToastVariant {
48
+ return typeof value === "string" && value in VARIANT_CLASSES;
49
+ }
50
+
43
51
  const Toast = React.forwardRef<
44
52
  React.ElementRef<typeof ToastPrimitives.Root>,
45
53
  React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
46
54
  VariantProps<typeof toastVariants>
47
55
  >(({ className, variant, ...props }, ref) => {
56
+ const resolvedVariant = isToastVariant(variant) ? variant : "default";
48
57
  return (
49
58
  <ToastPrimitives.Root
50
59
  ref={ref}
51
60
  className={cn(
52
- toastVariants({ variant: variant || "default" }),
61
+ toastVariants({ variant: resolvedVariant }),
53
62
  "print:hidden",
54
63
  className,
55
64
  )}