@marimo-team/frontend 0.24.1-dev50 → 0.24.1-dev52
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/{JsonOutput-BI2qFHVr.js → JsonOutput--lW6GQQ9.js} +10 -10
- package/dist/assets/{agent-panel-PB0I1dIC.js → agent-panel-i6F-fC5e.js} +1 -1
- package/dist/assets/{cell-editor-5wcJHF0s.js → cell-editor-X3xHqmCs.js} +1 -1
- package/dist/assets/{column-preview-D6cIOnWQ.js → column-preview-DKbMmEB5.js} +1 -1
- package/dist/assets/{command-palette-DkGgAhyM.js → command-palette-L1LZCmet.js} +1 -1
- package/dist/assets/{edit-page-Bpqbjwkb.js → edit-page-DOpi9pL6.js} +4 -4
- package/dist/assets/file-explorer-panel-z-rke195.js +1 -0
- package/dist/assets/{index-CRLe8Sox.js → index-CNnOirLv.js} +2 -2
- package/dist/assets/{layout-Ddsz1eHh.js → layout-DmbNDyY3.js} +3 -3
- package/dist/assets/{panels-D53vWR00.js → panels-BMikLSqU.js} +1 -1
- package/dist/assets/{reveal-component-C0L5MU2W.js → reveal-component-6t8SeOsr.js} +1 -1
- package/dist/assets/{run-page-DXZapRtY.js → run-page-BIyr61hZ.js} +1 -1
- package/dist/assets/{scratchpad-panel-ja6doGoa.js → scratchpad-panel-B2KUccqM.js} +1 -1
- package/dist/assets/{session-panel-ftyqfsYy.js → session-panel-1RH7Zh0c.js} +1 -1
- package/dist/assets/{skeleton-CxFczTPo.js → skeleton-DZV2_Wnl.js} +1 -1
- package/dist/assets/{useNotebookActions-DpphIKt5.js → useNotebookActions-BcAzvJBD.js} +1 -1
- package/dist/index.html +3 -3
- package/package.json +4 -2
- package/src/components/editor/file-tree/__tests__/file-render-mode.test.ts +3 -0
- package/src/components/editor/file-tree/__tests__/file-viewer.test.tsx +21 -0
- package/src/components/editor/file-tree/__tests__/renderers.test.tsx +129 -0
- package/src/components/editor/file-tree/file-viewer.tsx +1 -0
- package/src/components/editor/file-tree/renderers.tsx +27 -1
- package/src/components/storage/__tests__/storage-file-viewer.test.tsx +84 -0
- package/src/components/storage/storage-file-viewer.tsx +1 -0
- package/src/plugins/__tests__/plugin-schema.test.ts +343 -0
- package/src/plugins/__tests__/plugin-schema.ts +398 -0
- package/src/plugins/plugins.ts +1 -1
- package/dist/assets/file-explorer-panel-DaDDgJV2.js +0 -1
- package/src/components/editor/file-tree/__tests__/renderers.test.ts +0 -31
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{a as e}from"./rolldown-runtime-B0Z9INg1.js";import{c as t,f as n,l as r,r as i,u as a}from"./useEvent-xfs0Sn4r.js";import{t as o}from"./react-CBDbhulC.js";import{Ao as s,Bi as c,Ct as l,E as u,Fa as d,It as f,Kr as p,Qi as m,Qr as h,T as g,Wn as _,Xi as v,Xr as y,Zi as b,Zr as x,bo as S,cn as C,do as w,ei as T,g as E,go as ee,ir as D,li as O,ln as te,m as k,ra as A,sa as j,t as M,ta as N,ti as P,y as ne,zi as F}from"./cells-B1S_6FG3.js";import{Cn as I,kt as L,vt as R}from"./schemas-aTUejmFi.js";import{t as z}from"./compiler-runtime-CYIjoINj.js";import{C as re,n as B,r as V}from"./ai-model-dropdown-DjQy2bo-.js";import{g as H,n as U}from"./useEventListener-D27Dii_l.js";import{t as ie}from"./objects-snlMn4yk.js";import{C as ae,r as oe}from"./utils-R_pHe-RB.js";import{n as W,t as G}from"./constants-BDbGiS7O.js";import{A as se,M as ce,N as K,O as le,T as ue,j as de,k as fe,v as pe}from"./config-CUBl5qBg.js";import{t as me}from"./switch-CBR-ykv3.js";import{c as he}from"./readonly-python-code-De0vvuWh.js";import{t as q}from"./cn-ldLVeaqm.js";import{t as ge}from"./jsx-runtime-B74pBk57.js";import{o as _e}from"./alert-dialog-DBoy-FMx.js";import{i as ve,n as ye,r as be,t as xe}from"./tabs-c6mpccpH.js";import{a as Se,c as Ce,i as we,n as Te,r as Ee,s as De,t as Oe}from"./select-BUEX2Vfl.js";import{Pt as ke,X as Ae}from"./JsonOutput
|
|
1
|
+
import{a as e}from"./rolldown-runtime-B0Z9INg1.js";import{c as t,f as n,l as r,r as i,u as a}from"./useEvent-xfs0Sn4r.js";import{t as o}from"./react-CBDbhulC.js";import{Ao as s,Bi as c,Ct as l,E as u,Fa as d,It as f,Kr as p,Qi as m,Qr as h,T as g,Wn as _,Xi as v,Xr as y,Zi as b,Zr as x,bo as S,cn as C,do as w,ei as T,g as E,go as ee,ir as D,li as O,ln as te,m as k,ra as A,sa as j,t as M,ta as N,ti as P,y as ne,zi as F}from"./cells-B1S_6FG3.js";import{Cn as I,kt as L,vt as R}from"./schemas-aTUejmFi.js";import{t as z}from"./compiler-runtime-CYIjoINj.js";import{C as re,n as B,r as V}from"./ai-model-dropdown-DjQy2bo-.js";import{g as H,n as U}from"./useEventListener-D27Dii_l.js";import{t as ie}from"./objects-snlMn4yk.js";import{C as ae,r as oe}from"./utils-R_pHe-RB.js";import{n as W,t as G}from"./constants-BDbGiS7O.js";import{A as se,M as ce,N as K,O as le,T as ue,j as de,k as fe,v as pe}from"./config-CUBl5qBg.js";import{t as me}from"./switch-CBR-ykv3.js";import{c as he}from"./readonly-python-code-De0vvuWh.js";import{t as q}from"./cn-ldLVeaqm.js";import{t as ge}from"./jsx-runtime-B74pBk57.js";import{o as _e}from"./alert-dialog-DBoy-FMx.js";import{i as ve,n as ye,r as be,t as xe}from"./tabs-c6mpccpH.js";import{a as Se,c as Ce,i as we,n as Te,r as Ee,s as De,t as Oe}from"./select-BUEX2Vfl.js";import{Pt as ke,X as Ae}from"./JsonOutput--lW6GQQ9.js";import{a as je,c as J,o as Me,p as Ne,t as Pe}from"./download-CIdXYNHv.js";import{t as Fe}from"./tooltip-D7dq6bnc.js";import{n as Ie,r as Le,t as Re}from"./button-BMBznEru.js";import{i as ze,r as Be,t as Ve}from"./strings-CpYReX2Q.js";import{r as He}from"./requests-C6_IaFh7.js";import{t as Y}from"./createLucideIcon-B7HeEoG2.js";import{F as Ue,I as We,L as Ge,P as Ke,a as qe,i as Je,u as Ye}from"./layout-DmbNDyY3.js";import{t as Xe}from"./check-BoA6XBpj.js";import{r as Ze}from"./useCellActionButton-DXyUqFdJ.js";import{t as Qe}from"./code-CF0HKE9K.js";import{t as $e}from"./copy-BMXKNElG.js";import{t as et}from"./eye-off-CFuPxa9F.js";import{t as tt}from"./external-link-Bp3BxyRA.js";import{t as nt}from"./file-DF8n85RA.js";import{t as rt}from"./globe-BQf75qV_.js";import{n as it,r as at,t as ot}from"./youtube-DCJ-ihUU.js";import{n as st}from"./components-DwH_Yb1N.js";import{i as ct,n as lt,r as ut}from"./markdown-export-0rLbaq8b.js";import{t as dt}from"./image-ab13yGv4.js";import{D as ft,E as pt,O as mt,T as ht,b as gt,j as _t}from"./write-secret-modal-BG_B6y1P.js";import{t as vt}from"./link-Cap-MZiw.js";import{t as yt}from"./spinner-Ck-Hgfvj.js";import{r as bt}from"./input-DeyycFtq.js";import{t as xt}from"./settings-C-cYas75.js";import{t as St}from"./square-B58Ib8Ir.js";import{t as Ct}from"./triangle-alert-MihykDYG.js";import{t as wt}from"./use-toast-DpWHUGuF.js";import{n as Tt,t as Et}from"./paths-zcL04qUW.js";import{o as Dt}from"./session-CEdta5_Q.js";import{n as Ot}from"./useAsyncData-Buhk-UgR.js";import{n as kt}from"./copy-Cg05mM3C.js";import{t as At}from"./copy-icon-LPQzn7_f.js";import{r as jt}from"./useRunCells-DHnbXALT.js";import{r as Mt,t as Nt}from"./share-C2MPy_VX.js";import{n as Pt,t as Ft}from"./alert-dmKwNsrp.js";import{a as It}from"./cell-link-BbAJokoX.js";import{t as Lt}from"./icons-CjLa9BiH.js";import{i as Rt,n as zt}from"./error-entries-DEjvzkum.js";import{n as Bt,t as Vt}from"./pair-with-agent-modal-CBIL9_6g.js";import{n as Ht}from"./add-connection-dialog-CcnJ2LkX.js";import{t as Ut}from"./github-YRgb6LMu.js";import{n as Wt}from"./marimo-icons-C3KxMV9A.js";import{t as Gt}from"./links-DPh7yoP-.js";import{i as Kt,n as qt,t as Jt}from"./skeleton-DZV2_Wnl.js";import{t as Yt}from"./types-CoHi_MQq.js";var Xt=Y(`circle-chevron-down`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`m16 10-4 4-4-4`,key:`894hmk`}]]),Zt=Y(`circle-chevron-right`,[[`circle`,{cx:`12`,cy:`12`,r:`10`,key:`1mglay`}],[`path`,{d:`m10 8 4 4-4 4`,key:`1wy4r4`}]]),Qt=Y(`clipboard-copy`,[[`rect`,{width:`8`,height:`4`,x:`8`,y:`2`,rx:`1`,ry:`1`,key:`tgr4d6`}],[`path`,{d:`M8 4H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-2`,key:`4jdomd`}],[`path`,{d:`M16 4h2a2 2 0 0 1 2 2v4`,key:`3hqy98`}],[`path`,{d:`M21 14H11`,key:`1bme5i`}],[`path`,{d:`m15 10-4 4 4 4`,key:`5dvupr`}]]),$t=Y(`cloud-download`,[[`path`,{d:`M12 13v8l-4-4`,key:`1f5nwf`}],[`path`,{d:`m12 21 4-4`,key:`1lfcce`}],[`path`,{d:`M4.393 15.269A7 7 0 1 1 15.71 8h1.79a4.5 4.5 0 0 1 2.436 8.284`,key:`ui1hmy`}]]),en=Y(`command`,[[`path`,{d:`M15 6v12a3 3 0 1 0 3-3H6a3 3 0 1 0 3 3V6a3 3 0 1 0-3 3h12a3 3 0 1 0-3-3`,key:`11bfej`}]]),tn=Y(`diamond-plus`,[[`path`,{d:`M12 8v8`,key:`napkw2`}],[`path`,{d:`M2.7 10.3a2.41 2.41 0 0 0 0 3.41l7.59 7.59a2.41 2.41 0 0 0 3.41 0l7.59-7.59a2.41 2.41 0 0 0 0-3.41L13.7 2.71a2.41 2.41 0 0 0-3.41 0z`,key:`1ey20j`}],[`path`,{d:`M8 12h8`,key:`1wcyev`}]]),nn=Y(`fast-forward`,[[`path`,{d:`M12 6a2 2 0 0 1 3.414-1.414l6 6a2 2 0 0 1 0 2.828l-6 6A2 2 0 0 1 12 18z`,key:`b19h5q`}],[`path`,{d:`M2 6a2 2 0 0 1 3.414-1.414l6 6a2 2 0 0 1 0 2.828l-6 6A2 2 0 0 1 2 18z`,key:`h7h5ge`}]]),rn=Y(`files`,[[`path`,{d:`M15 2h-4a2 2 0 0 0-2 2v11a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V8`,key:`14sh0y`}],[`path`,{d:`M16.706 2.706A2.4 2.4 0 0 0 15 2v5a1 1 0 0 0 1 1h5a2.4 2.4 0 0 0-.706-1.706z`,key:`1970lx`}],[`path`,{d:`M5 7a2 2 0 0 0-2 2v11a2 2 0 0 0 2 2h8a2 2 0 0 0 1.732-1`,key:`l4dndm`}]]),an=Y(`list`,[[`path`,{d:`M3 5h.01`,key:`18ugdj`}],[`path`,{d:`M3 12h.01`,key:`nlz23k`}],[`path`,{d:`M3 19h.01`,key:`noohij`}],[`path`,{d:`M8 5h13`,key:`1pao27`}],[`path`,{d:`M8 12h13`,key:`1za7za`}],[`path`,{d:`M8 19h13`,key:`m83p4d`}]]),on=Y(`message-circle-question-mark`,[[`path`,{d:`M2.992 16.342a2 2 0 0 1 .094 1.167l-1.065 3.29a1 1 0 0 0 1.236 1.168l3.413-.998a2 2 0 0 1 1.099.092 10 10 0 1 0-4.777-4.719`,key:`1sd12s`}],[`path`,{d:`M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3`,key:`1u773s`}],[`path`,{d:`M12 17h.01`,key:`p32p05`}]]),sn=Y(`notebook`,[[`path`,{d:`M2 6h4`,key:`aawbzj`}],[`path`,{d:`M2 10h4`,key:`l0bgd4`}],[`path`,{d:`M2 14h4`,key:`1gsvsf`}],[`path`,{d:`M2 18h4`,key:`1bu2t1`}],[`rect`,{width:`16`,height:`20`,x:`4`,y:`2`,rx:`2`,key:`1nb95v`}],[`path`,{d:`M16 2v20`,key:`rotuqe`}]]),cn=Y(`panel-left`,[[`rect`,{width:`18`,height:`18`,x:`3`,y:`3`,rx:`2`,key:`afitv7`}],[`path`,{d:`M9 3v18`,key:`fh3hqa`}]]),ln=Y(`presentation`,[[`path`,{d:`M2 3h20`,key:`91anmk`}],[`path`,{d:`M21 3v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V3`,key:`2k9sn8`}],[`path`,{d:`m7 21 5-5 5 5`,key:`bip4we`}]]),un=Y(`share-2`,[[`circle`,{cx:`18`,cy:`5`,r:`3`,key:`gq8acd`}],[`circle`,{cx:`6`,cy:`12`,r:`3`,key:`w7nqdw`}],[`circle`,{cx:`18`,cy:`19`,r:`3`,key:`1xt0gg`}],[`line`,{x1:`8.59`,x2:`15.42`,y1:`13.51`,y2:`17.49`,key:`47mynk`}],[`line`,{x1:`15.41`,x2:`8.59`,y1:`6.51`,y2:`10.49`,key:`1n3mei`}]]),dn=Y(`square-power`,[[`path`,{d:`M12 7v4`,key:`xawao1`}],[`path`,{d:`M7.998 9.003a5 5 0 1 0 8-.005`,key:`1pek45`}],[`rect`,{x:`3`,y:`3`,width:`18`,height:`18`,rx:`2`,key:`h1oib`}]]),fn=Y(`undo-2`,[[`path`,{d:`M9 14 4 9l5-5`,key:`102s5s`}],[`path`,{d:`M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5a5.5 5.5 0 0 1-5.5 5.5H11`,key:`f3b9sd`}]]),X=ge(),Z=z(),Q=e(o(),1);function pn(e,t){let n=(0,Z.c)(11),r;n[0]===t?r=n[1]:(r=new w(t),n[0]=t,n[1]=r);let i=r,a;n[2]!==e||n[3]!==i?(a=()=>i.get(e),n[2]=e,n[3]=i,n[4]=a):a=n[4];let[o,s]=(0,Q.useState)(a),c;n[5]!==e||n[6]!==i?(c=t=>{s(t),i.set(e,t)},n[5]=e,n[6]=i,n[7]=c):c=n[7];let l=c,u;return n[8]!==l||n[9]!==o?(u=[o,l],n[8]=l,n[9]=o,n[10]=u):u=n[10],u}function mn(e,t){return{...e,Binaries:{...e.Binaries,Browser:t}}}function hn(e,t,n,r){return{marimo:e,Locale:n||void 0,Binaries:{Browser:t},"Environment Collection Error":r}}function gn(e,t){let n=Math.max(2,...Array.from(t.matchAll(/`+/g),e=>e[0].length)),r="`".repeat(n+1);return`${r}${e}\n${t}\n${r}`}function _n(e,t){let n=e.includes(`?`)?`&`:`?`,r=[],i=e,a=0;for(let[e,o]of Object.entries(t)){if(o.length===0)continue;let t=`${e}=${encodeURIComponent(o)}`,s=`${i}${a===0?n:`&`}${t}`;if(s.length>6e3){r.push(e);continue}i=s,a+=1}return{url:i,omitted:r}}function vn(e,t){return[`<details>`,`<summary>${Ve.htmlEscape(e)??``}</summary>`,``,t,``,`</details>`].join(`
|
|
2
2
|
`)}function yn(e){return vn(`Environment`,gn(`json`,JSON.stringify(e,null,2)))}function bn(e){return vn(`Errors`,gn(`text`,e.map(zt).join(`
|
|
3
3
|
|
|
4
4
|
---
|
package/dist/index.html
CHANGED
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
<marimo-server-token data-token="{{ server_token }}" hidden></marimo-server-token>
|
|
67
67
|
<!-- /TODO -->
|
|
68
68
|
<title>{{ title }}</title>
|
|
69
|
-
<script type="module" crossorigin src="./assets/index-
|
|
69
|
+
<script type="module" crossorigin src="./assets/index-CNnOirLv.js"></script>
|
|
70
70
|
<link rel="modulepreload" crossorigin href="./assets/rolldown-runtime-B0Z9INg1.js">
|
|
71
71
|
<link rel="modulepreload" crossorigin href="./assets/react-CBDbhulC.js">
|
|
72
72
|
<link rel="modulepreload" crossorigin href="./assets/compiler-runtime-CYIjoINj.js">
|
|
@@ -205,7 +205,7 @@
|
|
|
205
205
|
<link rel="modulepreload" crossorigin href="./assets/formats-DSzGCKeO.js">
|
|
206
206
|
<link rel="modulepreload" crossorigin href="./assets/table-DmqOOjfe.js">
|
|
207
207
|
<link rel="modulepreload" crossorigin href="./assets/react-resizable-panels.browser.esm-BSRAHC4P.js">
|
|
208
|
-
<link rel="modulepreload" crossorigin href="./assets/JsonOutput
|
|
208
|
+
<link rel="modulepreload" crossorigin href="./assets/JsonOutput--lW6GQQ9.js">
|
|
209
209
|
<link rel="modulepreload" crossorigin href="./assets/chart-no-axes-column-ChZdjJhj.js">
|
|
210
210
|
<link rel="modulepreload" crossorigin href="./assets/square-function-DkZweMAs.js">
|
|
211
211
|
<link rel="modulepreload" crossorigin href="./assets/spec-CUFquO6R.js">
|
|
@@ -220,7 +220,7 @@
|
|
|
220
220
|
<link rel="modulepreload" crossorigin href="./assets/globe-BQf75qV_.js">
|
|
221
221
|
<link rel="modulepreload" crossorigin href="./assets/markdown-export-0rLbaq8b.js">
|
|
222
222
|
<link rel="modulepreload" crossorigin href="./assets/types-BflGgckY.js">
|
|
223
|
-
<link rel="modulepreload" crossorigin href="./assets/column-preview-
|
|
223
|
+
<link rel="modulepreload" crossorigin href="./assets/column-preview-DKbMmEB5.js">
|
|
224
224
|
<link rel="modulepreload" crossorigin href="./assets/square-B58Ib8Ir.js">
|
|
225
225
|
<link rel="modulepreload" crossorigin href="./assets/triangle-alert-MihykDYG.js">
|
|
226
226
|
<link rel="modulepreload" crossorigin href="./assets/prop-types-Djf4O40e.js">
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marimo-team/frontend",
|
|
3
|
-
"version": "0.24.1-
|
|
3
|
+
"version": "0.24.1-dev52",
|
|
4
4
|
"main": "dist/main.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -179,6 +179,7 @@
|
|
|
179
179
|
"lint:oxlint": "oxlint --fix",
|
|
180
180
|
"lint:stylelint": "stylelint src/**/*.css --fix",
|
|
181
181
|
"preview": "vite preview",
|
|
182
|
+
"plugins:generate-schema": "cross-env UPDATE_PLUGIN_SCHEMA=1 vitest run src/plugins/__tests__/plugin-schema.test.ts",
|
|
182
183
|
"dev:quarto": "VITE_MARIMO_ISLANDS=true vite",
|
|
183
184
|
"dev:islands": "cross-env VITE_MARIMO_ISLANDS=true vite --config islands/vite.config.mts",
|
|
184
185
|
"build:islands": "cross-env VITE_MARIMO_ISLANDS=true vite --config islands/vite.config.mts build",
|
|
@@ -228,6 +229,7 @@
|
|
|
228
229
|
"vega-typings": "^2.1.0",
|
|
229
230
|
"vite": "^8.2.1",
|
|
230
231
|
"vite-plugin-wasm": "^3.6.0",
|
|
231
|
-
"vitest": "^4.1.10"
|
|
232
|
+
"vitest": "^4.1.10",
|
|
233
|
+
"yaml": "^2.8.3"
|
|
232
234
|
}
|
|
233
235
|
}
|
|
@@ -8,7 +8,10 @@ describe("getFileRenderMode", () => {
|
|
|
8
8
|
["text/plain", false, "text"],
|
|
9
9
|
["text/x-rust", false, "text"],
|
|
10
10
|
["application/json", false, "text"],
|
|
11
|
+
["application/toml", false, "text"],
|
|
12
|
+
["application/x-toml", false, "text"],
|
|
11
13
|
["application/xml", false, "text"],
|
|
14
|
+
["text/x-toml", false, "text"],
|
|
12
15
|
["text/csv", false, "csv"],
|
|
13
16
|
["image/png", true, "media"],
|
|
14
17
|
["video/mp4", true, "media"],
|
|
@@ -15,13 +15,19 @@ vi.mock("@/plugins/impl/code/LazyAnyLanguageCodeMirror", () => ({
|
|
|
15
15
|
LazyAnyLanguageCodeMirror: ({
|
|
16
16
|
value,
|
|
17
17
|
onChange,
|
|
18
|
+
language,
|
|
19
|
+
readOnly,
|
|
18
20
|
}: {
|
|
19
21
|
value?: string;
|
|
20
22
|
onChange?: (value: string) => void;
|
|
23
|
+
language?: string;
|
|
24
|
+
readOnly?: boolean;
|
|
21
25
|
}) => (
|
|
22
26
|
<textarea
|
|
23
27
|
data-testid="code-editor"
|
|
28
|
+
data-language={language}
|
|
24
29
|
value={value ?? ""}
|
|
30
|
+
readOnly={readOnly}
|
|
25
31
|
onChange={(evt) => onChange?.(evt.target.value)}
|
|
26
32
|
/>
|
|
27
33
|
),
|
|
@@ -106,6 +112,21 @@ describe("FileViewer bounded previews", () => {
|
|
|
106
112
|
expect(screen.queryByText(/too large to preview/i)).not.toBeInTheDocument();
|
|
107
113
|
});
|
|
108
114
|
|
|
115
|
+
it("renders TOML files as editable TOML", async () => {
|
|
116
|
+
renderViewer({
|
|
117
|
+
file: { ...file, name: "pyproject.toml", size: 20 },
|
|
118
|
+
contents: '[project]\nname = "demo"',
|
|
119
|
+
mimeType: "application/toml",
|
|
120
|
+
isBase64: false,
|
|
121
|
+
isTooLarge: false,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const editor =
|
|
125
|
+
await screen.findByTestId<HTMLTextAreaElement>("code-editor");
|
|
126
|
+
expect(editor).toHaveAttribute("data-language", "toml");
|
|
127
|
+
expect(editor).not.toHaveAttribute("readonly");
|
|
128
|
+
});
|
|
129
|
+
|
|
109
130
|
it("preserves edits to an initially empty text file across remount", async () => {
|
|
110
131
|
const emptyFile: FileInfo = {
|
|
111
132
|
id: "/workspace/empty.txt",
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
import { fireEvent, render, screen } from "@testing-library/react";
|
|
3
|
+
import { describe, expect, it, vi } from "vitest";
|
|
4
|
+
import { buildMediaSource, FileContentRenderer } from "../renderers";
|
|
5
|
+
|
|
6
|
+
vi.mock("@/plugins/impl/code/LazyAnyLanguageCodeMirror", () => ({
|
|
7
|
+
LazyAnyLanguageCodeMirror: ({
|
|
8
|
+
value,
|
|
9
|
+
language,
|
|
10
|
+
readOnly,
|
|
11
|
+
onChange,
|
|
12
|
+
}: {
|
|
13
|
+
value?: string;
|
|
14
|
+
language?: string;
|
|
15
|
+
readOnly?: boolean;
|
|
16
|
+
onChange?: (value: string) => void;
|
|
17
|
+
}) => (
|
|
18
|
+
<textarea
|
|
19
|
+
data-testid="code-editor"
|
|
20
|
+
data-language={language}
|
|
21
|
+
value={value}
|
|
22
|
+
readOnly={readOnly}
|
|
23
|
+
onChange={(event) => onChange?.(event.target.value)}
|
|
24
|
+
/>
|
|
25
|
+
),
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
vi.mock("@/theme/useTheme", () => ({
|
|
29
|
+
useTheme: () => ({ theme: "light" }),
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
describe("buildMediaSource", () => {
|
|
33
|
+
it("returns base64 source for binary media", () => {
|
|
34
|
+
expect(
|
|
35
|
+
buildMediaSource({
|
|
36
|
+
contents: "aGVsbG8=",
|
|
37
|
+
mimeType: "image/png",
|
|
38
|
+
isBase64: true,
|
|
39
|
+
}),
|
|
40
|
+
).toEqual({
|
|
41
|
+
base64: "aGVsbG8=",
|
|
42
|
+
mime: "image/png",
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("returns UTF-8 data URL for text-based media", () => {
|
|
47
|
+
const svg = '<svg xmlns="http://www.w3.org/2000/svg"></svg>';
|
|
48
|
+
expect(
|
|
49
|
+
buildMediaSource({
|
|
50
|
+
contents: svg,
|
|
51
|
+
mimeType: "image/svg+xml",
|
|
52
|
+
isBase64: false,
|
|
53
|
+
}),
|
|
54
|
+
).toEqual({
|
|
55
|
+
url: `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`,
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe("FileContentRenderer", () => {
|
|
61
|
+
it("renders editable TOML with syntax highlighting", () => {
|
|
62
|
+
const onChange = vi.fn();
|
|
63
|
+
render(
|
|
64
|
+
<FileContentRenderer
|
|
65
|
+
mimeType="application/toml"
|
|
66
|
+
contents={'name = "demo"'}
|
|
67
|
+
readOnly={false}
|
|
68
|
+
onChange={onChange}
|
|
69
|
+
/>,
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const editor = screen.getByTestId<HTMLTextAreaElement>("code-editor");
|
|
73
|
+
expect(editor).toHaveAttribute("data-language", "toml");
|
|
74
|
+
expect(editor).not.toHaveAttribute("readonly");
|
|
75
|
+
|
|
76
|
+
fireEvent.change(editor, { target: { value: 'name = "updated"' } });
|
|
77
|
+
expect(onChange).toHaveBeenCalledWith('name = "updated"');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it.each(["application/toml", "application/x-toml", "text/x-toml"])(
|
|
81
|
+
"renders %s as read-only TOML by default",
|
|
82
|
+
(mimeType) => {
|
|
83
|
+
render(
|
|
84
|
+
<FileContentRenderer mimeType={mimeType} contents={'name = "demo"'} />,
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
const editor = screen.getByTestId<HTMLTextAreaElement>("code-editor");
|
|
88
|
+
expect(editor).toHaveAttribute("data-language", "toml");
|
|
89
|
+
expect(editor).toHaveAttribute("readonly");
|
|
90
|
+
},
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
it.each([
|
|
94
|
+
"application/octet-stream",
|
|
95
|
+
"text/plain",
|
|
96
|
+
"text/plain; charset=utf-8",
|
|
97
|
+
])("uses the filename when a provider returns %s", (mimeType) => {
|
|
98
|
+
render(
|
|
99
|
+
<FileContentRenderer
|
|
100
|
+
mimeType={mimeType}
|
|
101
|
+
filename="pyproject.TOML"
|
|
102
|
+
contents={'name = "demo"'}
|
|
103
|
+
/>,
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
expect(screen.getByTestId("code-editor")).toHaveAttribute(
|
|
107
|
+
"data-language",
|
|
108
|
+
"toml",
|
|
109
|
+
);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it.each(["file.constructor", "file.__proto__"])(
|
|
113
|
+
"ignores inherited extension-map property in %s",
|
|
114
|
+
(filename) => {
|
|
115
|
+
render(
|
|
116
|
+
<FileContentRenderer
|
|
117
|
+
mimeType="text/plain"
|
|
118
|
+
filename={filename}
|
|
119
|
+
contents="text"
|
|
120
|
+
/>,
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
expect(screen.getByTestId("code-editor")).toHaveAttribute(
|
|
124
|
+
"data-language",
|
|
125
|
+
"markdown",
|
|
126
|
+
);
|
|
127
|
+
},
|
|
128
|
+
);
|
|
129
|
+
});
|
|
@@ -16,6 +16,7 @@ import { useTheme } from "@/theme/useTheme";
|
|
|
16
16
|
import { Arrays } from "@/utils/arrays";
|
|
17
17
|
import { type Base64String, base64ToDataURL } from "@/utils/json/base64";
|
|
18
18
|
import { Objects } from "@/utils/objects";
|
|
19
|
+
import { Paths } from "@/utils/paths";
|
|
19
20
|
|
|
20
21
|
const PAGE_SIZE = 25;
|
|
21
22
|
|
|
@@ -38,13 +39,36 @@ export const MIME_TO_LANGUAGE: Record<string, string> = {
|
|
|
38
39
|
"text/css": "css",
|
|
39
40
|
"text/x-python": "python",
|
|
40
41
|
"application/json": "json",
|
|
42
|
+
"application/toml": "toml",
|
|
43
|
+
"application/x-toml": "toml",
|
|
41
44
|
"application/xml": "xml",
|
|
45
|
+
"text/x-toml": "toml",
|
|
42
46
|
"text/x-yaml": "yaml",
|
|
43
47
|
"text/csv": "markdown",
|
|
44
48
|
"text/plain": "markdown",
|
|
45
49
|
default: "markdown",
|
|
46
50
|
};
|
|
47
51
|
|
|
52
|
+
const EXTENSION_TO_LANGUAGE: Record<string, string> = {
|
|
53
|
+
toml: "toml",
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const GENERIC_MIME_TYPES = new Set(["application/octet-stream", "text/plain"]);
|
|
57
|
+
|
|
58
|
+
function resolveLanguage(mimeType: string, filename?: string): string {
|
|
59
|
+
const normalizedMimeType = mimeType.split(";", 1)[0].trim().toLowerCase();
|
|
60
|
+
if (GENERIC_MIME_TYPES.has(normalizedMimeType) && filename) {
|
|
61
|
+
const extension = Paths.extension(filename).toLowerCase();
|
|
62
|
+
const language = Object.hasOwn(EXTENSION_TO_LANGUAGE, extension)
|
|
63
|
+
? EXTENSION_TO_LANGUAGE[extension]
|
|
64
|
+
: undefined;
|
|
65
|
+
if (language) {
|
|
66
|
+
return language;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return MIME_TO_LANGUAGE[normalizedMimeType] || MIME_TO_LANGUAGE.default;
|
|
70
|
+
}
|
|
71
|
+
|
|
48
72
|
/**
|
|
49
73
|
* Media viewer props: provide either a direct `url` or `base64` + `mime`.
|
|
50
74
|
* When `url` is set it takes precedence.
|
|
@@ -167,6 +191,7 @@ export const MediaRenderer: React.FC<MediaSource & { mimeType: string }> = ({
|
|
|
167
191
|
*/
|
|
168
192
|
export const FileContentRenderer: React.FC<{
|
|
169
193
|
mimeType: string;
|
|
194
|
+
filename?: string;
|
|
170
195
|
/** Text content for text/CSV files. */
|
|
171
196
|
contents?: string;
|
|
172
197
|
/** Media source for image/audio/video/PDF files. */
|
|
@@ -177,6 +202,7 @@ export const FileContentRenderer: React.FC<{
|
|
|
177
202
|
extensions?: Extension[];
|
|
178
203
|
}> = ({
|
|
179
204
|
mimeType,
|
|
205
|
+
filename,
|
|
180
206
|
contents,
|
|
181
207
|
mediaSource,
|
|
182
208
|
readOnly = true,
|
|
@@ -202,7 +228,7 @@ export const FileContentRenderer: React.FC<{
|
|
|
202
228
|
}
|
|
203
229
|
|
|
204
230
|
if (contents != null) {
|
|
205
|
-
const language =
|
|
231
|
+
const language = resolveLanguage(mimeType, filename);
|
|
206
232
|
return (
|
|
207
233
|
<div className="flex-1 overflow-auto">
|
|
208
234
|
<Suspense>
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { render, screen } from "@testing-library/react";
|
|
4
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import { TooltipProvider } from "@/components/ui/tooltip";
|
|
6
|
+
import { DownloadStorage } from "@/core/storage/request-registry";
|
|
7
|
+
import { StorageFileViewer } from "../storage-file-viewer";
|
|
8
|
+
|
|
9
|
+
vi.mock("@/components/editor/cell/useAddCell", () => ({
|
|
10
|
+
useAddCodeToNewCell: () => vi.fn(),
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
vi.mock("@/plugins/impl/code/LazyAnyLanguageCodeMirror", () => ({
|
|
14
|
+
LazyAnyLanguageCodeMirror: ({
|
|
15
|
+
value,
|
|
16
|
+
language,
|
|
17
|
+
readOnly,
|
|
18
|
+
}: {
|
|
19
|
+
value?: string;
|
|
20
|
+
language?: string;
|
|
21
|
+
readOnly?: boolean;
|
|
22
|
+
}) => (
|
|
23
|
+
<textarea
|
|
24
|
+
data-testid="code-editor"
|
|
25
|
+
data-language={language}
|
|
26
|
+
value={value}
|
|
27
|
+
readOnly={readOnly}
|
|
28
|
+
/>
|
|
29
|
+
),
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
vi.mock("@/theme/useTheme", () => ({
|
|
33
|
+
useTheme: () => ({ theme: "light" }),
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
describe("StorageFileViewer", () => {
|
|
37
|
+
afterEach(() => {
|
|
38
|
+
vi.restoreAllMocks();
|
|
39
|
+
vi.unstubAllGlobals();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("renders TOML from generic storage metadata as read-only TOML", async () => {
|
|
43
|
+
const request = vi.spyOn(DownloadStorage, "request").mockResolvedValue({
|
|
44
|
+
request_id: "request-id",
|
|
45
|
+
url: "https://example.com/pyproject.toml?signature=secret",
|
|
46
|
+
});
|
|
47
|
+
vi.stubGlobal(
|
|
48
|
+
"fetch",
|
|
49
|
+
vi.fn().mockResolvedValue({
|
|
50
|
+
ok: true,
|
|
51
|
+
text: async () => '[project]\nname = "demo"',
|
|
52
|
+
}),
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
render(
|
|
56
|
+
<TooltipProvider>
|
|
57
|
+
<StorageFileViewer
|
|
58
|
+
entry={{
|
|
59
|
+
path: "config/pyproject.toml",
|
|
60
|
+
kind: "file",
|
|
61
|
+
size: 24,
|
|
62
|
+
lastModified: null,
|
|
63
|
+
mimeType: null,
|
|
64
|
+
}}
|
|
65
|
+
namespace="files"
|
|
66
|
+
protocol="s3"
|
|
67
|
+
backendType="fsspec"
|
|
68
|
+
onBack={vi.fn()}
|
|
69
|
+
/>
|
|
70
|
+
</TooltipProvider>,
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const editor =
|
|
74
|
+
await screen.findByTestId<HTMLTextAreaElement>("code-editor");
|
|
75
|
+
expect(editor).toHaveAttribute("data-language", "toml");
|
|
76
|
+
expect(editor).toHaveAttribute("readonly");
|
|
77
|
+
expect(editor).toHaveValue('[project]\nname = "demo"');
|
|
78
|
+
expect(request).toHaveBeenCalledWith({
|
|
79
|
+
namespace: "files",
|
|
80
|
+
path: "config/pyproject.toml",
|
|
81
|
+
preview: true,
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
});
|
|
@@ -237,6 +237,7 @@ export const StorageFileViewer: React.FC<Props> = ({
|
|
|
237
237
|
{renderMetadata({})}
|
|
238
238
|
<FileContentRenderer
|
|
239
239
|
mimeType={mime}
|
|
240
|
+
filename={name}
|
|
240
241
|
contents={preview.type === "text" ? preview.content : undefined}
|
|
241
242
|
mediaSource={
|
|
242
243
|
preview.type === "media" ? { url: preview.url } : undefined
|