@marimo-team/frontend 0.23.15-dev73 → 0.23.15-dev75

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.
@@ -0,0 +1,12 @@
1
+ import{s as _e}from"./chunk-LvLJmgfZ.js";import{d as R,i as St,l as At,p as _t,u as N}from"./useEvent-D91BmmQi.js";import{t as Dt}from"./react-Bj1aDYRI.js";import{An as Et,Cr as It,E as De,Fn as Pt,Hi as Lt,Jt as Tt,Kn as Ot,Li as Ht,Ln as Rt,Lt as Ee,Nn as $t,Nr as Bt,Or as Ft,Pn as qt,T as Ut,Tr as Yt,ct as Kt,ea as ke,g as Vt,m as Ie,si as Gt,st as Jt,t as Xt,ut as Pe,wr as Le,y as Qt}from"./cells-Cf7bHHq3.js";import{t as z}from"./compiler-runtime-B3qBwwSJ.js";import{n as Zt,r as ea,x as ta}from"./ai-model-dropdown-DXgLphFa.js";import{g as $,n as aa}from"./useEventListener-B_w3A283.js";import{t as Te}from"./objects-BlG0ZrO0.js";import{C as na,r as oa}from"./utils-Z80SV4_d.js";import{n as C,t as Oe}from"./constants-CDyU_g2F.js";import{T as ee,v as He,w as Re}from"./config-BbOTTCYY.js";import{c as sa}from"./readonly-python-code-CW_G5e4E.js";import{t as ra}from"./cn-DYvqRARy.js";import{t as la}from"./jsx-runtime-Blw4afVn.js";import{o as ia}from"./alert-dialog-BXegNC0I.js";import{a as da,c as ca,i as ha,n as ma,r as ua,s as pa,t as fa}from"./select-B5TFNjmU.js";import{It as xa}from"./JsonOutput-DnGSkLeP.js";import{c as $e,d as we,n as ba,o as ya,r as ge,t as ka}from"./download-BomYt3nl.js";import{t as wa}from"./tooltip-CFX4fYHU.js";import{r as ga,t as P}from"./button-eIihcbUw.js";import{i as ja,t as je}from"./strings-Djnbwmfl.js";import{r as L}from"./requests-9-v2bhoi.js";import{t as k}from"./createLucideIcon-w-Qo9n0R.js";import{F as va,I as Ca,L as Na,R as Be,a as Fe,d as za,i as qe,u as Ue}from"./layout-BOwE7yok.js";import{t as Ye}from"./check-rzLRd0j9.js";import{r as Ma}from"./useCellActionButton-CRfIEVB5.js";import{t as Wa}from"./code-B2aKtXoe.js";import{t as Sa}from"./copy-3iHFdt22.js";import{t as Ke}from"./eye-off-BOZA7gbn.js";import{t as Ve}from"./external-link-BOlXZUla.js";import{t as te}from"./file-CJH9JNGH.js";import{u as Aa}from"./form-CgWNb44_.js";import{n as _a,r as Da,t as Ea}from"./youtube-OwaksuQt.js";import{i as Ia,n as Ge}from"./add-connection-dialog-CEp3SJR2.js";import{t as Pa}from"./house-DS0r7dNZ.js";import{t as La}from"./image-5XI2VU-q.js";import{t as Ta}from"./link-CHdiBDr5.js";import{r as Oa}from"./input-CqrUjynb.js";import{t as Ha}from"./settings-D_PiwXwo.js";import{t as Ra}from"./sparkles-DUCpRFlS.js";import{y as $a}from"./textarea-CpFfEUet.js";import{t as Ba}from"./square-DFICx2Qv.js";import{t as Je}from"./triangle-alert-BL4YVsPy.js";import{t as W}from"./use-toast-BwJoaaDM.js";import{n as Xe,t as Fa}from"./paths-BKO-pHK6.js";import{o as qa}from"./session-CEFnN9Cq.js";import{n as ae}from"./copy-BtTOqPTS.js";import{t as Qe}from"./copy-icon-C2pG0ZNK.js";import{r as Ua}from"./useRunCells-CSSF_mJ0.js";import{a as Ze,c as et,i as Ya,n as tt,r as at}from"./dialog-D1DEsRZ2.js";import{n as ne}from"./ImperativeModal-BIavOiUv.js";import{r as Ka,t as nt}from"./share-CYpcX4N4.js";import{a as Va}from"./cell-link-DqBkyAFe.js";import{n as ot}from"./useAsyncData-Bg5iAViP.js";import{a as Ga}from"./renderShortcut-BZLr8D7H.js";import{t as Ja}from"./icons-CIPE8AIY.js";import{i as Xa,n as st}from"./error-entries-Birw-8VX.js";import{t as Qa}from"./pair-with-agent-modal-CKOYwOYw.js";import{t as Za}from"./github-_JH-0hIO.js";import{n as rt}from"./marimo-icons-C_nFooxr.js";import{t as en}from"./links-CToBjIIj.js";import{i as tn,n as lt,t as ve}from"./skeleton-B_4CZVEm.js";import{t as it}from"./types-Da7lA5YC.js";var an=k("circle-chevron-down",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m16 10-4 4-4-4",key:"894hmk"}]]),nn=k("circle-chevron-right",[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"m10 8 4 4-4 4",key:"1wy4r4"}]]),dt=k("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"}]]),ct=k("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"}]]),ht=k("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"}]]),on=k("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"}]]),sn=k("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"}]]),rn=k("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"}]]),mt=k("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"}]]),ln=k("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"}]]),dn=k("panel-left",[["rect",{width:"18",height:"18",x:"3",y:"3",rx:"2",key:"afitv7"}],["path",{d:"M9 3v18",key:"fh3hqa"}]]),ut=k("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"}]]),cn=k("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"}]]),hn=k("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"}]]),pt=k("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"}]]),mn=z(),T=_e(Dt(),1);function Ce(e,t){let n=(0,mn.c)(11),o;n[0]===t?o=n[1]:(o=new Ht(t),n[0]=t,n[1]=o);let s=o,l;n[2]!==e||n[3]!==s?(l=()=>s.get(e),n[2]=e,n[3]=s,n[4]=l):l=n[4];let[r,d]=(0,T.useState)(l),i;n[5]!==e||n[6]!==s?(i=x=>{d(x),s.set(e,x)},n[5]=e,n[6]=s,n[7]=i):i=n[7];let c=i,h;return n[8]!==c||n[9]!==r?(h=[r,c],n[8]=c,n[9]=r,n[10]=h):h=n[10],h}function un(e,t){return{...e,Binaries:{...e.Binaries,Browser:t}}}function pn(e,t,n,o){return{marimo:e,Locale:n||void 0,Binaries:{Browser:t},"Environment Collection Error":o}}function Ne(e,t){let n=Math.max(2,...Array.from(t.matchAll(/`+/g),s=>s[0].length)),o="`".repeat(n+1);return`${o}${e}
2
+ ${t}
3
+ ${o}`}function fn(e,t){let n=e.includes("?")?"&":"?",o=[],s=e,l=0;for(let[r,d]of Object.entries(t)){if(d.length===0)continue;let i=`${r}=${encodeURIComponent(d)}`,c=`${s}${l===0?n:"&"}${i}`;if(c.length>6e3){o.push(r);continue}s=c,l+=1}return{url:s,omitted:o}}function ze(e,t){return["<details>",`<summary>${je.htmlEscape(e)??""}</summary>`,"",t,"","</details>"].join(`
4
+ `)}function xn(e){return ze("Environment",Ne("json",JSON.stringify(e,null,2)))}function ft(e){return ze("Errors",Ne("text",e.map(st).join(`
5
+
6
+ ---
7
+
8
+ `)))}function bn(e){return ze("Code",Ne("python",e))}var xt=z(),a=_e(la(),1),yn=aa,bt=e=>{let t=(0,xt.c)(16),{content:n}=e,[o,s]=(0,T.useState)(!1),l=!o&&"max-h-24 overflow-hidden",r;t[0]===l?r=t[1]:(r=ra("text-xs bg-muted rounded p-2 overflow-x-auto whitespace-pre-wrap",l),t[0]=l,t[1]=r);let d;t[2]!==n||t[3]!==r?(d=(0,a.jsx)("pre",{className:r,children:n}),t[2]=n,t[3]=r,t[4]=d):d=t[4];let i;t[5]===o?i=t[6]:(i=!o&&(0,a.jsx)("div",{className:"pointer-events-none absolute inset-x-0 bottom-0 h-10 rounded-b bg-gradient-to-b from-transparent to-muted"}),t[5]=o,t[6]=i);let c;t[7]!==d||t[8]!==i?(c=(0,a.jsxs)("div",{className:"relative",children:[d,i]}),t[7]=d,t[8]=i,t[9]=c):c=t[9];let h;t[10]===Symbol.for("react.memo_cache_sentinel")?(h=()=>s(wn),t[10]=h):h=t[10];let x=o?"Show less":"Show more",m;t[11]===x?m=t[12]:(m=(0,a.jsx)(P,{type:"button",variant:"link",size:"xs",className:"self-start",onClick:h,children:x}),t[11]=x,t[12]=m);let b;return t[13]!==c||t[14]!==m?(b=(0,a.jsxs)("div",{className:"flex flex-col gap-1",children:[c,m]}),t[13]=c,t[14]=m,t[15]=b):b=t[15],b};const kn=e=>{let t=(0,xt.c)(6),{children:n}=e,{openModal:o,closeModal:s}=ne(),l;t[0]!==s||t[1]!==o?(l=()=>o((0,a.jsx)(yt,{onClose:s})),t[0]=s,t[1]=o,t[2]=l):l=t[2];let r;return t[3]!==n||t[4]!==l?(r=(0,a.jsx)(yn,{onClick:l,children:n}),t[3]=n,t[4]=l,t[5]=r):r=t[5],r},yt=()=>{let{getEnvironmentInfo:e,readCode:t}=L(),n=ot(async()=>e(),[e]),o=N(Qt),s=(0,T.useMemo)(()=>Xa(St),[o]),l=za(o.cellIds.inOrderIds.map(u=>o.cellData[u])),r=N(Lt),d=N(He),i=r!==null&&l&&d.state===Re.OPEN,c=i?void 0:r===null?"Save the notebook first.":l?"Connect the notebook to include its source.":"Notebook source is hidden in this view.",[h,x]=Ce("marimo:issue-report:include-errors",!1),[m,b]=Ce("marimo:issue-report:include-code",!1),f=n.data?un(n.data,navigator.userAgent):n.status==="error"?pn(sa(),navigator.userAgent,navigator.language,"Server environment information unavailable"):void 0,y=ot(async()=>{if(!m||!i)return;let{contents:u}=await t();return u},[m,i,t]),{url:w,omitted:g}=(0,T.useMemo)(()=>{if(!f)return{url:C.bugReportUrl,omitted:[]};let u={env:xn(f)};return h&&s.length>0&&(u["bug-description"]=ft(s)),m&&y.data&&(u["reproduction-code"]=bn(y.data)),fn(C.bugReportUrl,u)},[f,s,h,m,y.data]),v=g.map(u=>u==="bug-description"?"errors":u==="reproduction-code"?"code":u).join(", ");return(0,a.jsxs)(tt,{className:"w-[540px] max-w-[90vw]",children:[(0,a.jsxs)(Ze,{children:[(0,a.jsx)(et,{children:"Report an issue"}),(0,a.jsx)(at,{children:"Open a GitHub bug report prefilled with your environment, and optionally your current errors and notebook code. Nothing is uploaded automatically; review the details before posting."})]}),(0,a.jsxs)("div",{className:"flex flex-col gap-4 max-h-[60vh] overflow-y-auto",children:[(0,a.jsxs)("div",{className:"flex flex-col gap-3",children:[(0,a.jsx)(P,{type:"button",variant:"default",size:"xs",asChild:!0,className:"self-start",children:(0,a.jsxs)("a",{href:w,target:"_blank",rel:"noreferrer",children:[(0,a.jsx)(Ve,{className:"w-4 h-4 mr-2"}),"Open GitHub issue"]})}),(0,a.jsxs)("div",{className:"flex flex-col gap-2",children:[(0,a.jsxs)("div",{className:"flex items-center gap-2 text-sm",children:[(0,a.jsx)(Ee,{id:"issue-include-errors",checked:h,disabled:s.length===0,onCheckedChange:u=>x(u===!0),"aria-label":"Include errors"}),(0,a.jsx)("label",{htmlFor:"issue-include-errors",children:"Include errors"})]}),(0,a.jsxs)("div",{className:"flex flex-col gap-1",children:[(0,a.jsxs)("div",{className:"flex items-center gap-2 text-sm",children:[(0,a.jsx)(Ee,{id:"issue-include-code",checked:m,disabled:!i,onCheckedChange:u=>b(u===!0),"aria-label":"Include notebook code"}),(0,a.jsx)("label",{htmlFor:"issue-include-code",children:"Include notebook code"})]}),c&&(0,a.jsx)("span",{className:"text-xs text-muted-foreground ml-6",children:c}),m&&y.status==="error"&&(0,a.jsx)("span",{className:"text-xs text-(--red-11) ml-6",children:"Notebook source could not be loaded."})]})]}),v?(0,a.jsxs)("div",{className:"flex items-start gap-2 text-xs text-(--yellow-11)",children:[(0,a.jsx)(Je,{className:"w-3.5 h-3.5 shrink-0 mt-0.5"}),(0,a.jsxs)("span",{children:["Too large to prefill and left out of the link: ",v,". Paste them into the issue manually."]})]}):(0,a.jsx)("p",{className:"text-xs text-muted-foreground",children:"Your environment is always included."})]}),n.status==="pending"&&(0,a.jsxs)("div",{className:"flex flex-col gap-2",children:[(0,a.jsx)("span",{className:"text-sm text-muted-foreground",children:"Loading environment details\u2026"}),(0,a.jsx)(ve,{className:"h-4 w-full"}),(0,a.jsx)(ve,{className:"h-4 w-3/4"}),(0,a.jsx)(ve,{className:"h-4 w-1/2"})]}),n.status==="error"&&(0,a.jsxs)("div",{className:"flex items-center gap-2 text-sm",children:[(0,a.jsx)(Je,{className:"w-4 h-4 text-(--yellow-11) shrink-0"}),(0,a.jsx)("span",{children:"Server environment information unavailable"}),(0,a.jsx)(P,{type:"button",variant:"link",size:"xs",onClick:()=>n.refetch(),children:"Retry"})]}),f&&(0,a.jsxs)("div",{className:"flex flex-col gap-1",children:[(0,a.jsxs)("div",{className:"flex items-center justify-between",children:[(0,a.jsx)("span",{className:"text-sm font-medium",children:"Environment details"}),(0,a.jsx)(Qe,{className:"w-3.5 h-3.5",value:JSON.stringify(f,null,2),ariaLabel:"Copy environment JSON",toastTitle:"Environment details copied"})]}),(0,a.jsx)(bt,{content:JSON.stringify(f,null,2)})]}),f&&s.length>0&&(0,a.jsxs)("div",{className:"flex flex-col gap-1",children:[(0,a.jsxs)("div",{className:"flex items-center justify-between",children:[(0,a.jsx)("span",{className:"text-sm font-medium",children:"Errors"}),(0,a.jsx)(Qe,{className:"w-3.5 h-3.5",value:ft(s),ariaLabel:"Copy errors",toastTitle:"Errors copied"})]}),(0,a.jsx)(bt,{content:s.map(st).join(`
9
+
10
+ ---
11
+
12
+ `)})]}),f&&s.length===0&&(0,a.jsx)("span",{className:"text-sm text-muted-foreground",children:"No errors detected."}),(0,a.jsx)("div",{className:"border-t pt-3",children:(0,a.jsxs)("p",{className:"text-sm text-muted-foreground",children:["Other feedback? Take our"," ",(0,a.jsx)("a",{href:C.feedbackForm,target:"_blank",rel:"noreferrer",className:"underline",children:"two-minute survey"})," ","or chat with us on"," ",(0,a.jsx)("a",{href:C.discordLink,target:"_blank",rel:"noreferrer",className:"underline",children:"Discord"}),"."]})})]})]})};function wn(e){return!e}var kt=z(),oe="https://static.marimo.app";const gn=e=>{let t=(0,kt.c)(25),{onClose:n}=e,[o,s]=(0,T.useState)(""),{exportAsHTML:l}=L(),r=`${o}-${Math.random().toString(36).slice(2,6)}`,d=`${oe}/static/${r}`,i;t[0]!==l||t[1]!==n||t[2]!==r?(i=async u=>{u.preventDefault(),n();let H=await l({download:!1,includeCode:!0,files:va.INSTANCE.filenames()}),S=W({title:"Uploading static notebook...",description:"Please wait."});await fetch(`${oe}/api/static`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({html:H,path:r})}).catch(()=>{S.dismiss(),W({title:"Error uploading static page",description:(0,a.jsxs)("div",{children:["Please try again later. If the problem persists, please file a bug report on"," ",(0,a.jsx)("a",{href:C.issuesPage,target:"_blank",className:"underline",children:"GitHub"}),"."]})})}),S.dismiss(),W({title:"Static page uploaded!",description:(0,a.jsxs)("div",{children:["The URL has been copied to your clipboard.",(0,a.jsx)("br",{}),"You can share it with anyone."]})})},t[0]=l,t[1]=n,t[2]=r,t[3]=i):i=t[3];let c;t[4]===Symbol.for("react.memo_cache_sentinel")?(c=(0,a.jsx)(et,{children:"Share static notebook"}),t[4]=c):c=t[4];let h;t[5]===Symbol.for("react.memo_cache_sentinel")?(h=(0,a.jsxs)(Ze,{children:[c,(0,a.jsxs)(at,{children:["You can publish a static, non-interactive version of this notebook to the public web. We will create a link for you that lives on"," ",(0,a.jsx)("a",{href:oe,target:"_blank",children:oe}),"."]})]}),t[5]=h):h=t[5];let x;t[6]===Symbol.for("react.memo_cache_sentinel")?(x=u=>{s(u.target.value.toLowerCase().replaceAll(/\s/g,"-").replaceAll(/[^\da-z-]/g,""))},t[6]=x):x=t[6];let m;t[7]===o?m=t[8]:(m=(0,a.jsx)(Oa,{"data-testid":"slug-input",id:"slug",autoFocus:!0,value:o,placeholder:"Notebook slug",onChange:x,required:!0,autoComplete:"off"}),t[7]=o,t[8]=m);let b;t[9]===d?b=t[10]:(b=(0,a.jsxs)("div",{className:"font-semibold text-sm text-muted-foreground gap-2 flex flex-col",children:["Anyone will be able to access your notebook at this URL:",(0,a.jsxs)("div",{className:"flex items-center gap-2",children:[(0,a.jsx)(jn,{text:d}),(0,a.jsx)("span",{className:"text-primary",children:d})]})]}),t[9]=d,t[10]=b);let f;t[11]!==m||t[12]!==b?(f=(0,a.jsxs)("div",{className:"flex flex-col gap-6 py-4",children:[m,b]}),t[11]=m,t[12]=b,t[13]=f):f=t[13];let y;t[14]===n?y=t[15]:(y=(0,a.jsx)(P,{"data-testid":"cancel-share-static-notebook-button",variant:"secondary",onClick:n,children:"Cancel"}),t[14]=n,t[15]=y);let w;t[16]===d?w=t[17]:(w=(0,a.jsx)(P,{"data-testid":"share-static-notebook-button","aria-label":"Save",variant:"default",type:"submit",onClick:async()=>{await ae(d)},children:"Create"}),t[16]=d,t[17]=w);let g;t[18]!==y||t[19]!==w?(g=(0,a.jsxs)(Ya,{children:[y,w]}),t[18]=y,t[19]=w,t[20]=g):g=t[20];let v;return t[21]!==i||t[22]!==g||t[23]!==f?(v=(0,a.jsx)(tt,{className:"w-fit",children:(0,a.jsxs)("form",{onSubmit:i,children:[h,f,g]})}),t[21]=i,t[22]=g,t[23]=f,t[24]=v):v=t[24],v};var jn=e=>{let t=(0,kt.c)(8),[n,o]=T.useState(!1),s;t[0]===e.text?s=t[1]:(s=ga.stopPropagation(async c=>{c.preventDefault(),await ae(e.text),o(!0),setTimeout(()=>o(!1),2e3)}),t[0]=e.text,t[1]=s);let l=s,r;t[2]===Symbol.for("react.memo_cache_sentinel")?(r=(0,a.jsx)(Sa,{size:14,strokeWidth:1.5}),t[2]=r):r=t[2];let d;t[3]===l?d=t[4]:(d=(0,a.jsx)(P,{"data-testid":"copy-static-notebook-url-button",onClick:l,size:"xs",variant:"secondary",children:r}),t[3]=l,t[4]=d);let i;return t[5]!==n||t[6]!==d?(i=(0,a.jsx)(wa,{content:"Copied!",open:n,children:d}),t[5]=n,t[6]=d,t[7]=i):i=t[7],i},vn=z();function Cn(){let e=document.getElementsByClassName(Oe.outputArea);for(let t of e){let n=t.getBoundingClientRect();if(n.bottom>0&&n.top<window.innerHeight){let o=ke.findElement(t);if(!o){$.warn("Could not find HTMLCellId for visible output area",t);continue}return{cellId:ke.parse(o.id)}}}return $.warn("No visible output area found for scroll anchor"),null}function Nn(e){if(!e){$.warn("No scroll anchor provided to restore scroll position");return}let t=document.getElementById(ke.create(e.cellId));if(!t){$.warn("Could not find cell element to restore scroll position",e.cellId);return}if(!t.querySelector(`.${Oe.outputArea}`)){$.warn("Could not find output area to restore scroll position",e.cellId);return}t.scrollIntoView({block:"start",behavior:"auto"})}function wt(){let e=(0,vn.c)(2),t=R(Pe),n;return e[0]===t?n=e[1]:(n=()=>{let o=Cn();t(s=>({mode:Kt(s.mode),cellAnchor:(o==null?void 0:o.cellId)??null})),requestAnimationFrame(()=>{requestAnimationFrame(()=>{Nn(o)})})},e[0]=t,e[1]=n),n}const gt=_t(!1);var zn=z();const Mn=()=>{let e=(0,zn.c)(7),{selectedLayout:t}=Fe(),{setLayoutView:n}=qe();if(ee()&&!Ot("wasm_layouts"))return null;let o;e[0]===n?o=e[1]:(o=d=>n(d),e[0]=n,e[1]=o);let s;e[2]===Symbol.for("react.memo_cache_sentinel")?(s=(0,a.jsx)(pa,{className:"min-w-[110px] border-border bg-background","data-testid":"layout-select",children:(0,a.jsx)(ca,{placeholder:"Select a view"})}),e[2]=s):s=e[2];let l;e[3]===Symbol.for("react.memo_cache_sentinel")?(l=(0,a.jsx)(ma,{children:(0,a.jsxs)(ua,{children:[(0,a.jsx)(da,{children:"View as"}),it.map(Sn)]})}),e[3]=l):l=e[3];let r;return e[4]!==t||e[5]!==o?(r=(0,a.jsxs)(fa,{"data-testid":"layout-select",value:t,onValueChange:o,children:[s,l]}),e[4]=t,e[5]=o,e[6]=r):r=e[6],r};function Wn(e){return(0,a.jsx)(jt(e),{className:"h-4 w-4"})}function jt(e){switch(e){case"vertical":return rn;case"grid":return Da;case"slides":return ut;default:return ja(e),Ba}}function vt(e){return je.startCase(e)}function Sn(e){return(0,a.jsx)(ha,{value:e,children:(0,a.jsxs)("div",{className:"flex items-center gap-1.5 leading-5",children:[Wn(e),(0,a.jsx)("span",{children:vt(e)})]})},e)}async function An(e){let{filename:t,preset:n,downloadPDF:o}=e;await o({filename:t,webpdf:!1,preset:n,includeInputs:!0,rasterServer:"static"})}var _n=z();function Dn(e){let t=(0,_n.c)(5),{openPrompt:n,closeModal:o}=ne(),{sendCopy:s}=L(),l;return t[0]!==o||t[1]!==n||t[2]!==s||t[3]!==e?(l=()=>{if(!e)return null;let r=Fa.guessDeliminator(e);n({title:"Copy notebook",description:"Enter a new filename for the notebook copy.",defaultValue:`_${Xe.basename(e)}`,confirmText:"Copy notebook",spellCheck:!1,onConfirm:d=>{let i=r.join(Xe.dirname(e),d);s({source:e,destination:i}).then(()=>{o(),W({title:"Notebook copied",description:"A copy of the notebook has been created."}),en(i)})}})},t[0]=o,t[1]=n,t[2]=s,t[3]=e,t[4]=l):l=t[4],l}var En=z();function Ct(){let e=(0,En.c)(4),{openConfirm:t}=ne(),n=R(He),{sendRestart:o}=L(),s;return e[0]!==t||e[1]!==o||e[2]!==n?(s=()=>{t({title:"Restart Kernel",description:"This will restart the Python kernel. You'll lose all data that's in memory. You will also lose any unsaved changes, so make sure to save your work before restarting.",variant:"destructive",confirmAction:(0,a.jsx)(ia,{onClick:async()=>{n({state:Re.CLOSING}),await o(),Ka()},"aria-label":"Confirm Restart",children:"Restart"})})},e[0]=t,e[1]=o,e[2]=n,e[3]=s):s=e[3],s}var In=z(),Pn=new Tt,Ln=e=>Pn.isSupported(e)?"markdown":"code";const Nt=()=>{let e=(0,In.c)(3),{updateCellConfig:t}=De(),{saveCellConfig:n}=L(),o;return e[0]!==n||e[1]!==t?(o=async(s,l)=>{let r=Ie(),d={};for(let c of r.cellIds.inOrderIds){let h=r.cellData[c];h===void 0||h.config.hide_code===s||Ln(h.code)===l&&(d[c]={hide_code:s})}let i=Te.entries(d);if(i.length!==0){await n({configs:d});for(let[c,h]of i)t({cellId:c,config:h})}},e[0]=n,e[1]=t,e[2]=o):o=e[2],o};var Tn=z(),O=e=>{e==null||e.preventDefault(),e==null||e.stopPropagation()};function On(){var We,Se,Ae;let e=(0,Tn.c)(56),t=Va(),{openModal:n,closeModal:o}=ne(),{toggleApplication:s}=$t(),{selectedPanel:l}=qt(),[r]=At(Pe),d=N(Jt),i=Nt(),[c]=na(),h=N(Et),x=N(oa),{updateCellConfig:m,undoDeleteCell:b,clearAllCellOutputs:f,addSetupCellIfDoesntExist:y,collapseAllCells:w,expandAllCells:g}=De(),v=Ct(),u=Ua(),H=Dn(t),S=R(gt),re=R(Zt),{handleClick:le}=ea(),ie=R(ta),{exportAsIPYNB:de,exportAsMarkdown:ce,readCode:A,saveCellConfig:he,updateCellOutputs:_}=L(),D=tn(),me=N(Vt),ue=N(Xt),pe=N(Ut),{selectedLayout:B}=Fe(),{setLayoutView:fe}=qe(),F=wt(),q=((We=c.sharing)==null?void 0:We.html)??!0,U=((Se=c.sharing)==null?void 0:Se.wasm)??!0,Y=((Ae=c.sharing)==null?void 0:Ae.molab)??!0,Me=!ee(),xe=B==="slides",zt=ao,Mt=to,K;e[0]!==t||e[1]!==D||e[2]!==_?(K=async p=>{let{preset:j,title:M}=p;if(!t){se();return}await $e(M,async ye=>{await lt({takeScreenshots:()=>D({progress:ye}),updateCellOutputs:_}),await An({filename:t,preset:j,downloadPDF:ba})})},e[0]=t,e[1]=D,e[2]=_,e[3]=K):K=e[3];let E=K,V;e[4]===E?V=e[5]:(V=async()=>{if(Me){await E({preset:"document",title:"Downloading Document PDF..."});return}let p=new Event("export-beforeprint"),j=new Event("export-afterprint");window.dispatchEvent(p),setTimeout(eo,0),setTimeout(()=>window.dispatchEvent(j),0)},e[4]=E,e[5]=V);let G=V,J;e[6]!==de||e[7]!==t||e[8]!==D||e[9]!==_?(J=async()=>{if(!t){se();return}await $e("Downloading IPYNB...",async p=>{await lt({takeScreenshots:()=>D({progress:p}),updateCellOutputs:_});let j=await de({download:!1});ge(new Blob([j],{type:"application/x-ipynb+json"}),we.toIPYNB(document.title))})},e[6]=de,e[7]=t,e[8]=D,e[9]=_,e[10]=J):J=e[10];let be=J,X;e[11]===Symbol.for("react.memo_cache_sentinel")?(X=(0,a.jsx)(xa,{size:14,strokeWidth:1.5}),e[11]=X):X=e[11];let Q;e[12]===Symbol.for("react.memo_cache_sentinel")?(Q=(0,a.jsx)(Be,{size:14,strokeWidth:1.5}),e[12]=Q):Q=e[12];let I;e[13]===t?I=e[14]:(I=async()=>{if(!t){se();return}await Ue({filename:t,includeCode:!0})},e[13]=t,e[14]=I);let Z;return e[15]!==y||e[16]!==x||e[17]!==ue||e[18]!==h||e[19]!==f||e[20]!==o||e[21]!==w||e[22]!==H||e[23]!==E||e[24]!==g||e[25]!==ce||e[26]!==t||e[27]!==G||e[28]!==be||e[29]!==me||e[30]!==xe||e[31]!==d||e[32]!==n||e[33]!==le||e[34]!==A||e[35]!==v||e[36]!==u||e[37]!==he||e[38]!==B||e[39]!==l||e[40]!==i||e[41]!==S||e[42]!==ie||e[43]!==fe||e[44]!==re||e[45]!==q||e[46]!==Y||e[47]!==U||e[48]!==I||e[49]!==s||e[50]!==F||e[51]!==b||e[52]!==pe||e[53]!==m||e[54]!==r.mode?(Z=[{icon:X,label:"Download",handle:O,dropdown:[{icon:Q,label:"Download as HTML",handle:I},{icon:(0,a.jsx)(Be,{size:14,strokeWidth:1.5}),label:"Download as HTML (exclude code)",handle:async()=>{if(!t){se();return}await Ue({filename:t,includeCode:!1})}},{icon:(0,a.jsx)(Ja,{strokeWidth:1.5,style:{width:14,height:14}}),label:"Download as Markdown",handle:async()=>{let p=await ce({download:!1});ge(new Blob([p],{type:"text/plain"}),we.toMarkdown(document.title))}},{icon:(0,a.jsx)(ln,{size:14,strokeWidth:1.5}),label:"Download as ipynb",handle:be},{icon:(0,a.jsx)(Wa,{size:14,strokeWidth:1.5}),label:"Download Python code",handle:async()=>{let p=await A();ge(new Blob([p.contents],{type:"text/plain"}),we.toPY(document.title))}},{divider:!0,icon:(0,a.jsx)(La,{size:14,strokeWidth:1.5}),label:"Download as PNG",disabled:r.mode!=="present",tooltip:r.mode==="present"?void 0:(0,a.jsxs)("span",{children:["Only available in app view. ",(0,a.jsx)("br",{}),"Toggle with: ",Ga("global.hideCode",!1)]}),handle:Zn},xe?{divider:!0,icon:(0,a.jsx)(te,{size:14,strokeWidth:1.5}),label:"Download as PDF",handle:O,dropdown:[{icon:(0,a.jsx)(te,{size:14,strokeWidth:1.5}),label:"Document Layout",handle:G},{icon:(0,a.jsx)(te,{size:14,strokeWidth:1.5}),label:"Slides Layout",rightElement:Mt(!0),hidden:!Me,handle:async()=>{await E({preset:"slides",title:"Downloading Slides PDF..."})}}]}:{divider:!0,icon:(0,a.jsx)(te,{size:14,strokeWidth:1.5}),label:"Download as PDF",handle:G}]},{icon:(0,a.jsx)(Ra,{size:14,strokeWidth:1.5}),label:"Pair with an agent",hidden:ee(),handle:async()=>{n((0,a.jsx)(Qa,{onClose:o}))}},{icon:(0,a.jsx)(cn,{size:14,strokeWidth:1.5}),label:"Share",handle:O,hidden:!q&&!U&&!Y,dropdown:[{icon:(0,a.jsx)(Aa,{size:14,strokeWidth:1.5}),label:"Publish HTML to web",hidden:!q,handle:async()=>{n((0,a.jsx)(gn,{onClose:o}))}},{icon:(0,a.jsx)(Ta,{size:14,strokeWidth:1.5}),label:"Create WebAssembly link",hidden:!U,handle:async()=>{await ae(nt({code:(await A()).contents})),W({title:"Copied",description:"Link copied to clipboard."})}},{icon:(0,a.jsx)(rt,{size:14,strokeWidth:1.5}),label:"Create molab notebook",hidden:!Y,handle:async()=>{let p=nt({code:(await A()).contents,baseUrl:`${C.molab}/new`});window.open(p,"_blank")}}]},{icon:(0,a.jsx)(dn,{size:14,strokeWidth:1.5}),label:"Helper panel",redundant:!0,handle:O,dropdown:Pt.flatMap(p=>{let j=p.type==="ai"&&!x;if(Rt({panel:p,capabilities:h,aiEnabled:x})&&!j)return[];let{type:M,Icon:ye,additionalKeywords:Wt}=p;return{label:je.startCase(M),rightElement:zt(l===M),icon:(0,a.jsx)(ye,{size:14,strokeWidth:1.5}),handle:()=>{if(j){le("ai","ai-features");return}s(M)},additionalKeywords:Wt}})},{icon:(0,a.jsx)(ut,{size:14,strokeWidth:1.5}),label:"Present as",handle:O,dropdown:[{icon:r.mode==="present"?(0,a.jsx)($a,{size:14,strokeWidth:1.5}):(0,a.jsx)(Ca,{size:14,strokeWidth:1.5}),label:"Toggle app view",hotkey:"global.hideCode",handle:()=>{F()}},...it.map((p,j)=>{let M=jt(p);return{divider:j===0,label:vt(p),icon:(0,a.jsx)(M,{size:14,strokeWidth:1.5}),rightElement:(0,a.jsx)("div",{className:"w-8 flex justify-end",children:B===p&&(0,a.jsx)(Ye,{size:14})}),handle:()=>{fe(p),r.mode==="edit"&&F()}}})]},{icon:(0,a.jsx)(sn,{size:14,strokeWidth:1.5}),label:"Duplicate notebook",hidden:!t||ee(),handle:H},{icon:(0,a.jsx)(dt,{size:14,strokeWidth:1.5}),label:"Copy code to clipboard",hidden:!t,handle:async()=>{await ae((await A()).contents),W({title:"Copied",description:"Code copied to clipboard."})}},{icon:(0,a.jsx)(Ma,{size:14,strokeWidth:1.5}),label:"Enable all cells",hidden:!me||d,handle:async()=>{let p=Gt(Ie());await he({configs:Te.fromEntries(p.map(Qn))});for(let j of p)m({cellId:j,config:{disabled:!1}})}},{divider:!0,icon:(0,a.jsx)(ht,{size:14,strokeWidth:1.5}),label:"Add setup cell",handle:()=>{y({})}},{icon:(0,a.jsx)(Yt,{size:14,strokeWidth:1.5}),label:"Add database connection",handle:()=>{n((0,a.jsx)(Ge,{onClose:o}))}},{icon:(0,a.jsx)(Ia,{size:14,strokeWidth:1.5}),label:"Add remote storage",handle:()=>{n((0,a.jsx)(Ge,{defaultTab:"storage",onClose:o}))}},{icon:(0,a.jsx)(pt,{size:14,strokeWidth:1.5}),label:pe,hidden:!ue||d,handle:()=>{b()}},{icon:(0,a.jsx)(hn,{size:14,strokeWidth:1.5}),label:"Restart kernel",variant:"danger",handle:v,additionalKeywords:["reset","reload","restart"]},{icon:(0,a.jsx)(on,{size:14,strokeWidth:1.5}),label:"Re-run all cells",redundant:!0,hotkey:"global.runAll",handle:async()=>{u()}},{icon:(0,a.jsx)(Ft,{size:14,strokeWidth:1.5}),label:"Clear all outputs",redundant:!0,handle:()=>{f()}},{icon:(0,a.jsx)(Le,{size:14,strokeWidth:1.5}),label:"Show all code",hotkey:"global.showAllCode",handle:()=>i(!1,"code"),redundant:!0},{icon:(0,a.jsx)(Ke,{size:14,strokeWidth:1.5}),label:"Hide all code",hotkey:"global.hideAllCode",handle:()=>i(!0,"code"),redundant:!0},{icon:(0,a.jsx)(Le,{size:14,strokeWidth:1.5}),label:"Show all markdown code",hotkey:"global.showAllMarkdownCode",handle:()=>i(!1,"markdown"),redundant:!0},{icon:(0,a.jsx)(Ke,{size:14,strokeWidth:1.5}),label:"Hide all markdown code",hotkey:"global.hideAllMarkdownCode",handle:()=>i(!0,"markdown"),redundant:!0},{icon:(0,a.jsx)(nn,{size:14,strokeWidth:1.5}),label:"Collapse all sections",hotkey:"global.collapseAllSections",handle:w,redundant:!0},{icon:(0,a.jsx)(an,{size:14,strokeWidth:1.5}),label:"Expand all sections",hotkey:"global.expandAllSections",handle:g,redundant:!0},{divider:!0,icon:(0,a.jsx)(ct,{size:14,strokeWidth:1.5}),label:"Command palette",hotkey:"global.commandPalette",handle:()=>S(Xn)},{icon:(0,a.jsx)(Na,{size:14,strokeWidth:1.5}),label:"Keyboard shortcuts",hotkey:"global.showHelp",handle:()=>ie(Jn)},{icon:(0,a.jsx)(Ha,{size:14,strokeWidth:1.5}),label:"User settings",handle:()=>re(Gn),redundant:!0,additionalKeywords:["preferences","options","configuration"]},{icon:(0,a.jsx)(mt,{size:14,strokeWidth:1.5}),label:"Report an issue",additionalKeywords:["feedback","bug","issue","report","diagnostics"],handle:()=>n((0,a.jsx)(yt,{onClose:o}))},{icon:(0,a.jsx)(Ve,{size:14,strokeWidth:1.5}),label:"Resources",handle:O,dropdown:[{icon:(0,a.jsx)(Bt,{size:14,strokeWidth:1.5}),label:"Documentation",handle:Vn},{icon:(0,a.jsx)(Za,{className:"h-3.5 w-3.5"}),label:"GitHub",handle:Kn},{icon:(0,a.jsx)(_a,{size:14,strokeWidth:1.5}),label:"Discord Community",handle:Yn},{icon:(0,a.jsx)(Ea,{className:"h-3.5 w-3.5"}),label:"YouTube",handle:Un},{icon:(0,a.jsx)(It,{size:14,strokeWidth:1.5}),label:"Changelog",handle:qn}]},{divider:!0,icon:(0,a.jsx)(Pa,{size:14,strokeWidth:1.5}),label:"Open home",hidden:!location.search.includes("file"),handle:Fn},{icon:(0,a.jsx)(rt,{size:14,strokeWidth:1.5}),label:"New notebook",hidden:!location.search.includes("file"),handle:Bn}].filter($n).map(Hn),e[15]=y,e[16]=x,e[17]=ue,e[18]=h,e[19]=f,e[20]=o,e[21]=w,e[22]=H,e[23]=E,e[24]=g,e[25]=ce,e[26]=t,e[27]=G,e[28]=be,e[29]=me,e[30]=xe,e[31]=d,e[32]=n,e[33]=le,e[34]=A,e[35]=v,e[36]=u,e[37]=he,e[38]=B,e[39]=l,e[40]=i,e[41]=S,e[42]=ie,e[43]=fe,e[44]=re,e[45]=q,e[46]=Y,e[47]=U,e[48]=I,e[49]=s,e[50]=F,e[51]=b,e[52]=pe,e[53]=m,e[54]=r.mode,e[55]=Z):Z=e[55],Z}function Hn(e){return e.dropdown?{...e,dropdown:e.dropdown.filter(Rn)}:e}function Rn(e){return!e.hidden}function $n(e){return!e.hidden}function Bn(){let e=qa();window.open(e,"_blank")}function Fn(){let e=document.baseURI.split("?")[0];window.open(e,"_blank","noopener")}function qn(){window.open(C.releasesPage,"_blank")}function Un(){window.open(C.youtube,"_blank")}function Yn(){window.open(C.discordLink,"_blank")}function Kn(){window.open(C.githubPage,"_blank")}function Vn(){window.open(C.docsPage,"_blank")}function Gn(e){return!e}function Jn(e){return!e}function Xn(e){return!e}function Qn(e){return[e,{disabled:!1}]}async function Zn(){let e=document.getElementById("App");e&&await ya({element:e,filename:document.title,prepare:ka})}function eo(){return window.print()}function to(e){return e?(0,a.jsx)("span",{className:"ml-3 shrink-0 rounded-full border border-emerald-200 bg-emerald-50 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-emerald-700",children:"Recommended"}):null}function ao(e){return(0,a.jsx)("div",{className:"w-8 flex justify-end",children:e&&(0,a.jsx)(Ye,{size:14})})}function se(){W({title:"Error",description:"Notebooks must be named to be exported.",variant:"danger"})}export{gt as a,Ce as c,ht as d,ct as f,Mn as i,pt as l,Nt as n,wt as o,dt as p,Ct as r,kn as s,On as t,mt as u};
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-D8mKmEIz.js"></script>
69
+ <script type="module" crossorigin src="./assets/index-D73cl2ti.js"></script>
70
70
  <link rel="modulepreload" crossorigin href="./assets/preload-helper-TXkS1QI3.js">
71
71
  <link rel="modulepreload" crossorigin href="./assets/objects-BlG0ZrO0.js">
72
72
  <link rel="modulepreload" crossorigin href="./assets/chunk-LvLJmgfZ.js">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marimo-team/frontend",
3
- "version": "0.23.15-dev73",
3
+ "version": "0.23.15-dev75",
4
4
  "main": "dist/main.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -50,6 +50,7 @@ function wrapper({ children }: { children: React.ReactNode }) {
50
50
  describe("FeedbackModal issue reporting", () => {
51
51
  beforeEach(() => {
52
52
  vi.clearAllMocks();
53
+ localStorage.clear();
53
54
  store.set(requestClientAtom, MockRequestClient.create());
54
55
  store.set(viewStateAtom, { mode: "edit", cellAnchor: null });
55
56
  store.set(connectionAtom, { state: WebSocketState.OPEN });
@@ -87,7 +88,7 @@ describe("FeedbackModal issue reporting", () => {
87
88
  ).toBeInTheDocument();
88
89
  });
89
90
 
90
- it("copies partial details when the environment request fails", async () => {
91
+ it("copies partial environment JSON when the environment request fails", async () => {
91
92
  store.set(
92
93
  requestClientAtom,
93
94
  MockRequestClient.create({
@@ -97,7 +98,9 @@ describe("FeedbackModal issue reporting", () => {
97
98
  render(<FeedbackModal onClose={vi.fn()} />, { wrapper });
98
99
 
99
100
  await screen.findByText("Server environment information unavailable");
100
- fireEvent.click(screen.getByRole("button", { name: "Copy issue details" }));
101
+ fireEvent.click(
102
+ screen.getByRole("button", { name: "Copy environment JSON" }),
103
+ );
101
104
  await waitFor(() =>
102
105
  expect(copyModule.copyToClipboard).toHaveBeenCalledWith(
103
106
  expect.stringContaining("Environment Collection Error"),
@@ -125,10 +128,8 @@ describe("FeedbackModal issue reporting", () => {
125
128
  );
126
129
  });
127
130
 
128
- it("includes notebook source only after explicit opt-in", async () => {
129
- const readCode = vi.fn().mockResolvedValue({
130
- contents: "secret = 'review before posting'",
131
- });
131
+ it("prefills the notebook code after checking include code", async () => {
132
+ const readCode = vi.fn().mockResolvedValue({ contents: "import marimo" });
132
133
  store.set(
133
134
  requestClientAtom,
134
135
  MockRequestClient.create({
@@ -137,51 +138,43 @@ describe("FeedbackModal issue reporting", () => {
137
138
  }),
138
139
  );
139
140
  render(<FeedbackModal onClose={vi.fn()} />, { wrapper });
141
+ const link = screen.getByRole("link", { name: "Open GitHub issue" });
140
142
  await screen.findByText("Environment details");
141
143
 
142
144
  expect(readCode).not.toHaveBeenCalled();
145
+ expect(link.getAttribute("href") ?? "").not.toContain("reproduction-code=");
146
+
143
147
  fireEvent.click(
144
- screen.getByRole("checkbox", { name: "Include full notebook source" }),
148
+ screen.getByRole("checkbox", { name: "Include notebook code" }),
145
149
  );
146
150
  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
- );
151
+ await waitFor(() => {
152
+ const href = link.getAttribute("href") ?? "";
153
+ expect(href).toContain("reproduction-code=");
154
+ expect(href).toContain(encodeURIComponent("import marimo"));
155
+ });
154
156
  });
155
157
 
156
- it("discards notebook source after unchecking", async () => {
157
- const readCode = vi.fn().mockResolvedValue({ contents: "x = 1" });
158
+ it("disables include code and explains why when disconnected", async () => {
159
+ store.set(connectionAtom, { state: WebSocketState.CONNECTING });
158
160
  store.set(
159
161
  requestClientAtom,
160
162
  MockRequestClient.create({
161
163
  getEnvironmentInfo: vi.fn().mockResolvedValue(environment),
162
- readCode,
163
164
  }),
164
165
  );
165
166
  render(<FeedbackModal onClose={vi.fn()} />, { wrapper });
166
167
  await screen.findByText("Environment details");
167
168
 
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
- );
169
+ expect(
170
+ screen.getByRole("checkbox", { name: "Include notebook code" }),
171
+ ).toBeDisabled();
172
+ expect(
173
+ screen.getByText("Connect the notebook to include its source."),
174
+ ).toBeInTheDocument();
181
175
  });
182
176
 
183
- it("disables the notebook option and explains why when disconnected", async () => {
184
- store.set(connectionAtom, { state: WebSocketState.CONNECTING });
177
+ it("disables include errors when there are no errors", async () => {
185
178
  store.set(
186
179
  requestClientAtom,
187
180
  MockRequestClient.create({
@@ -192,11 +185,8 @@ describe("FeedbackModal issue reporting", () => {
192
185
  await screen.findByText("Environment details");
193
186
 
194
187
  expect(
195
- screen.getByRole("checkbox", { name: "Include full notebook source" }),
188
+ screen.getByRole("checkbox", { name: "Include errors" }),
196
189
  ).toBeDisabled();
197
- expect(
198
- screen.getByText("Connect the notebook to include its source."),
199
- ).toBeInTheDocument();
200
190
  });
201
191
 
202
192
  it("prefills the GitHub issue link with the environment once loaded", async () => {
@@ -5,7 +5,7 @@ import { Slot as SlotPrimitive } from "radix-ui";
5
5
  const Slot = SlotPrimitive.Slot;
6
6
 
7
7
  import { useAtomValue } from "jotai";
8
- import { CheckIcon, ExternalLinkIcon, TriangleAlertIcon } from "lucide-react";
8
+ import { ExternalLinkIcon, TriangleAlertIcon } from "lucide-react";
9
9
  import React, { type PropsWithChildren, useMemo, useState } from "react";
10
10
  import { CopyClipboardIcon } from "@/components/icons/copy-icon";
11
11
  import { useImperativeModal } from "@/components/modal/ImperativeModal";
@@ -18,16 +18,16 @@ import {
18
18
  DialogTitle,
19
19
  } from "@/components/ui/dialog";
20
20
  import { Skeleton } from "@/components/ui/skeleton";
21
- import { toast } from "@/components/ui/use-toast";
22
21
  import { notebookAtom } from "@/core/cells/cells";
23
22
  import { Constants } from "@/core/constants";
24
23
  import {
25
24
  buildBugReportUrl,
26
- buildIssueDetails,
27
25
  createPartialEnvironment,
28
26
  type EnvironmentDiagnostics,
29
27
  enrichEnvironment,
30
- type NotebookSource,
28
+ formatCodeSection,
29
+ formatEnvironmentSection,
30
+ formatErrorsSection,
31
31
  } from "@/core/diagnostics/issue-details";
32
32
  import {
33
33
  formatCellError,
@@ -41,8 +41,8 @@ import { filenameAtom } from "@/core/saving/file-state";
41
41
  import { store } from "@/core/state/jotai";
42
42
  import { WebSocketState } from "@/core/websocket/types";
43
43
  import { useAsyncData } from "@/hooks/useAsyncData";
44
+ import { useLocalStorage } from "@/hooks/useLocalStorage";
44
45
  import { cn } from "@/utils/cn";
45
- import { copyToClipboard } from "@/utils/copy";
46
46
 
47
47
  const CollapsiblePreview: React.FC<{ content: string }> = ({ content }) => {
48
48
  const [expanded, setExpanded] = useState(false);
@@ -108,8 +108,6 @@ export const FeedbackModal: React.FC<{
108
108
  codeAvailable &&
109
109
  connection.state === WebSocketState.OPEN;
110
110
 
111
- const [includeNotebook, setIncludeNotebook] = useState(false);
112
-
113
111
  const notebookSourceReason = notebookSourceAvailable
114
112
  ? undefined
115
113
  : filename === null
@@ -118,13 +116,14 @@ export const FeedbackModal: React.FC<{
118
116
  ? "Notebook source is hidden in this view."
119
117
  : "Connect the notebook to include its source.";
120
118
 
121
- const notebookSourceRequest = useAsyncData(async () => {
122
- if (!includeNotebook || !notebookSourceAvailable || filename === null) {
123
- return undefined;
124
- }
125
- const { contents } = await readCode();
126
- return { filename, contents } satisfies NotebookSource;
127
- }, [includeNotebook, notebookSourceAvailable, filename, readCode]);
119
+ const [includeErrors, setIncludeErrors] = useLocalStorage(
120
+ "marimo:issue-report:include-errors",
121
+ false,
122
+ );
123
+ const [includeCode, setIncludeCode] = useLocalStorage(
124
+ "marimo:issue-report:include-code",
125
+ false,
126
+ );
128
127
 
129
128
  const environment: EnvironmentDiagnostics | undefined =
130
129
  environmentRequest.data
@@ -138,83 +137,122 @@ export const FeedbackModal: React.FC<{
138
137
  )
139
138
  : undefined;
140
139
 
141
- const [issueDetailsCopied, setIssueDetailsCopied] = useState(false);
142
-
143
- const githubIssueUrl = useMemo(() => {
144
- if (!environment) {
145
- return Constants.bugReportUrl;
140
+ const codeRequest = useAsyncData(async () => {
141
+ if (!includeCode || !notebookSourceAvailable) {
142
+ return undefined;
146
143
  }
147
- return buildBugReportUrl(
148
- Constants.bugReportUrl,
149
- buildIssueDetails({ environment, errors }),
150
- );
151
- }, [environment, errors]);
144
+ const { contents } = await readCode();
145
+ return contents;
146
+ }, [includeCode, notebookSourceAvailable, readCode]);
152
147
 
153
- const copyIssueDetails = async () => {
148
+ const { url: githubIssueUrl, omitted } = useMemo(() => {
154
149
  if (!environment) {
155
- return;
150
+ return { url: Constants.bugReportUrl, omitted: [] as string[] };
151
+ }
152
+ const fields: Record<string, string> = {
153
+ env: formatEnvironmentSection(environment),
154
+ };
155
+ if (includeErrors && errors.length > 0) {
156
+ fields["bug-description"] = formatErrorsSection(errors);
156
157
  }
157
- const notebookForReport = includeNotebook
158
- ? notebookSourceRequest.data
159
- : undefined;
160
- await copyToClipboard(
161
- buildIssueDetails({ environment, errors, notebook: notebookForReport }),
162
- );
163
- setIssueDetailsCopied(true);
164
- setTimeout(() => setIssueDetailsCopied(false), 2000);
165
- toast({
166
- title:
167
- environmentRequest.status === "error"
168
- ? "Partial issue details copied"
169
- : "Issue details copied",
170
- });
171
- };
158
+ if (includeCode && codeRequest.data) {
159
+ fields["reproduction-code"] = formatCodeSection(codeRequest.data);
160
+ }
161
+ return buildBugReportUrl(Constants.bugReportUrl, fields);
162
+ }, [environment, errors, includeErrors, includeCode, codeRequest.data]);
163
+
164
+ const omittedLabels = omitted
165
+ .map((field) =>
166
+ field === "bug-description"
167
+ ? "errors"
168
+ : field === "reproduction-code"
169
+ ? "code"
170
+ : field,
171
+ )
172
+ .join(", ");
172
173
 
173
174
  return (
174
175
  <DialogContent className="w-[540px] max-w-[90vw]">
175
176
  <DialogHeader>
176
177
  <DialogTitle>Report an issue</DialogTitle>
177
178
  <DialogDescription>
178
- Copy your environment and any current errors to include in a GitHub
179
- bug report. Nothing is uploaded automatically; review the details
180
- before posting.
179
+ Open a GitHub bug report prefilled with your environment, and
180
+ optionally your current errors and notebook code. Nothing is uploaded
181
+ automatically; review the details before posting.
181
182
  </DialogDescription>
182
183
  </DialogHeader>
183
184
 
184
185
  <div className="flex flex-col gap-4 max-h-[60vh] overflow-y-auto">
185
- <div className="flex flex-wrap gap-2">
186
+ <div className="flex flex-col gap-3">
186
187
  <Button
187
188
  type="button"
188
189
  variant="default"
189
190
  size="xs"
190
- disabled={
191
- !environment ||
192
- (includeNotebook && notebookSourceRequest.isFetching)
193
- }
194
- onClick={copyIssueDetails}
191
+ asChild={true}
192
+ className="self-start"
195
193
  >
196
- {issueDetailsCopied && (
197
- <CheckIcon
198
- className="w-4 h-4 mr-2 text-(--grass-11)"
199
- color="white"
200
- />
201
- )}
202
- {issueDetailsCopied ? "Copied!" : "Copy issue details"}
203
- </Button>
204
- <Button type="button" variant="outline" size="xs" asChild={true}>
205
194
  <a href={githubIssueUrl} target="_blank" rel="noreferrer">
206
195
  <ExternalLinkIcon className="w-4 h-4 mr-2" />
207
196
  Open GitHub issue
208
197
  </a>
209
198
  </Button>
210
- </div>
211
199
 
212
- {includeNotebook && (
213
- <p className="text-xs text-muted-foreground">
214
- The GitHub link prefills your environment only. Use Copy issue
215
- details to include the full notebook source.
216
- </p>
217
- )}
200
+ <div className="flex flex-col gap-2">
201
+ <div className="flex items-center gap-2 text-sm">
202
+ <Checkbox
203
+ id="issue-include-errors"
204
+ checked={includeErrors}
205
+ disabled={errors.length === 0}
206
+ onCheckedChange={(checked) =>
207
+ setIncludeErrors(checked === true)
208
+ }
209
+ aria-label="Include errors"
210
+ />
211
+ <label htmlFor="issue-include-errors">Include errors</label>
212
+ </div>
213
+
214
+ <div className="flex flex-col gap-1">
215
+ <div className="flex items-center gap-2 text-sm">
216
+ <Checkbox
217
+ id="issue-include-code"
218
+ checked={includeCode}
219
+ disabled={!notebookSourceAvailable}
220
+ onCheckedChange={(checked) =>
221
+ setIncludeCode(checked === true)
222
+ }
223
+ aria-label="Include notebook code"
224
+ />
225
+ <label htmlFor="issue-include-code">
226
+ Include notebook code
227
+ </label>
228
+ </div>
229
+ {notebookSourceReason && (
230
+ <span className="text-xs text-muted-foreground ml-6">
231
+ {notebookSourceReason}
232
+ </span>
233
+ )}
234
+ {includeCode && codeRequest.status === "error" && (
235
+ <span className="text-xs text-(--red-11) ml-6">
236
+ Notebook source could not be loaded.
237
+ </span>
238
+ )}
239
+ </div>
240
+ </div>
241
+
242
+ {omittedLabels ? (
243
+ <div className="flex items-start gap-2 text-xs text-(--yellow-11)">
244
+ <TriangleAlertIcon className="w-3.5 h-3.5 shrink-0 mt-0.5" />
245
+ <span>
246
+ Too large to prefill and left out of the link: {omittedLabels}.
247
+ Paste them into the issue manually.
248
+ </span>
249
+ </div>
250
+ ) : (
251
+ <p className="text-xs text-muted-foreground">
252
+ Your environment is always included.
253
+ </p>
254
+ )}
255
+ </div>
218
256
 
219
257
  {environmentRequest.status === "pending" && (
220
258
  <div className="flex flex-col gap-2">
@@ -261,7 +299,15 @@ export const FeedbackModal: React.FC<{
261
299
 
262
300
  {environment && errors.length > 0 && (
263
301
  <div className="flex flex-col gap-1">
264
- <span className="text-sm font-medium">Current errors</span>
302
+ <div className="flex items-center justify-between">
303
+ <span className="text-sm font-medium">Errors</span>
304
+ <CopyClipboardIcon
305
+ className="w-3.5 h-3.5"
306
+ value={formatErrorsSection(errors)}
307
+ ariaLabel="Copy errors"
308
+ toastTitle="Errors copied"
309
+ />
310
+ </div>
265
311
  <CollapsiblePreview
266
312
  content={errors.map(formatCellError).join("\n\n---\n\n")}
267
313
  />
@@ -270,46 +316,10 @@ export const FeedbackModal: React.FC<{
270
316
 
271
317
  {environment && errors.length === 0 && (
272
318
  <span className="text-sm text-muted-foreground">
273
- No current errors detected.
319
+ No errors detected.
274
320
  </span>
275
321
  )}
276
322
 
277
- <div className="flex flex-col gap-1">
278
- <div className="flex items-start gap-2 text-sm">
279
- <Checkbox
280
- id="issue-include-notebook"
281
- className="mt-0.5"
282
- checked={includeNotebook}
283
- disabled={!notebookSourceAvailable}
284
- onCheckedChange={(checked) =>
285
- setIncludeNotebook(checked === true)
286
- }
287
- aria-label="Include full notebook source"
288
- />
289
- <label htmlFor="issue-include-notebook">
290
- Include full notebook source
291
- </label>
292
- </div>
293
- <p className="text-xs text-muted-foreground ml-6">
294
- Copies the entire saved Python source, including comments, literal
295
- data, embedded credentials, and package metadata. Outputs and
296
- external files are not included. Review it before posting.{" "}
297
- <span className="font-bold">
298
- For private notebooks, paste a minimal reproduction instead.
299
- </span>
300
- </p>
301
- {notebookSourceReason && (
302
- <span className="text-xs text-muted-foreground ml-6">
303
- {notebookSourceReason}
304
- </span>
305
- )}
306
- {includeNotebook && notebookSourceRequest.status === "error" && (
307
- <span className="text-xs text-(--red-11) ml-6">
308
- Notebook source could not be loaded.
309
- </span>
310
- )}
311
- </div>
312
-
313
323
  <div className="border-t pt-3">
314
324
  <p className="text-sm text-muted-foreground">
315
325
  Other feedback? Take our{" "}