streamlit-nightly 1.53.1.dev20260120__py3-none-any.whl → 1.53.1.dev20260121__py3-none-any.whl
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.
- streamlit/connections/snowflake_connection.py +5 -2
- streamlit/elements/widgets/radio.py +91 -31
- streamlit/proto/Radio_pb2.py +4 -2
- streamlit/proto/Radio_pb2.pyi +20 -3
- streamlit/static/index.html +1 -1
- streamlit/static/manifest.json +308 -308
- streamlit/static/static/js/{ErrorOutline.esm.CqVEQ-uJ.js → ErrorOutline.esm.C6VdLmcj.js} +1 -1
- streamlit/static/static/js/{FileDownload.esm.G-HJG6nt.js → FileDownload.esm.g3HiTWnX.js} +1 -1
- streamlit/static/static/js/{FileHelper.Bd4bPp0Y.js → FileHelper.DkZahZIe.js} +1 -1
- streamlit/static/static/js/{FormClearHelper.B74VElyu.js → FormClearHelper.CnDHLc6s.js} +1 -1
- streamlit/static/static/js/{InputInstructions.ClBLOwfm.js → InputInstructions.CJwgA0Sj.js} +1 -1
- streamlit/static/static/js/{Particles.CyYskark.js → Particles.BA0kQsJM.js} +1 -1
- streamlit/static/static/js/{ProgressBar.vWX1Igzt.js → ProgressBar.N6Nzla-b.js} +1 -1
- streamlit/static/static/js/{StreamlitSyntaxHighlighter.BNhyeEtl.js → StreamlitSyntaxHighlighter.DF8uiu4N.js} +1 -1
- streamlit/static/static/js/{TableChart.esm.Mk-Hvy1t.js → TableChart.esm.Ne8F0PNs.js} +1 -1
- streamlit/static/static/js/{Toolbar.NhlhlTzK.js → Toolbar.h597G9NG.js} +1 -1
- streamlit/static/static/js/{WidgetLabelHelpIconInline.Ch9VDnrM.js → WidgetLabelHelpIconInline.BMPpOfi3.js} +1 -1
- streamlit/static/static/js/{base-input.DSh9H2uv.js → base-input.CTx0w60a.js} +1 -1
- streamlit/static/static/js/{checkbox.DCFAQrRB.js → checkbox.otYZO7w2.js} +1 -1
- streamlit/static/static/js/{createDownloadLinkElement.D5zpcd24.js → createDownloadLinkElement.Az9SilWU.js} +1 -1
- streamlit/static/static/js/{data-grid-overlay-editor.GAqbIurd.js → data-grid-overlay-editor.Blj_5PJn.js} +1 -1
- streamlit/static/static/js/{downloader.C1MBZFo2.js → downloader.ur6VTRo6.js} +1 -1
- streamlit/static/static/js/{embed.CZG6oOsc.js → embed.Bo5Mp3yp.js} +1 -1
- streamlit/static/static/js/{es6.toHnBzIb.js → es6.Q-xD3Lx8.js} +2 -2
- streamlit/static/static/js/{formatNumber.BsU-5o3V.js → formatNumber._OZtRfH-.js} +1 -1
- streamlit/static/static/js/{iconPosition.OKOYrH6A.js → iconPosition.owgMmPXc.js} +1 -1
- streamlit/static/static/js/{iframeResizer.contentWindow.D6yxU4DL.js → iframeResizer.contentWindow.CVCEnOeT.js} +1 -1
- streamlit/static/static/js/{index.DwgEkDLk.js → index.1wQkO9-P.js} +1 -1
- streamlit/static/static/js/{index.DhGqPx_f.js → index.2pNQcghF.js} +1 -1
- streamlit/static/static/js/{index.BHWCIpbw.js → index.9GyNEnVn.js} +1 -1
- streamlit/static/static/js/{index._t3w-7R0.js → index.BBFXXyOE.js} +1 -1
- streamlit/static/static/js/{index.DbCS2N3T.js → index.BOH_U_Ua.js} +1 -1
- streamlit/static/static/js/{index.YRiVxrFw.js → index.BTy77A4v.js} +1 -1
- streamlit/static/static/js/{index.DR8Ty3j4.js → index.BZMDU-qx.js} +1 -1
- streamlit/static/static/js/{index.EOvPT8N-.js → index.BdKmWvyd.js} +1 -1
- streamlit/static/static/js/{index.SBwNLdli.js → index.BkFxYQoo.js} +1 -1
- streamlit/static/static/js/{index.0iCXW13-.js → index.BlZAOJTf.js} +1 -1
- streamlit/static/static/js/{index.DGrQJk9x.js → index.BtKtuLWl.js} +1 -1
- streamlit/static/static/js/{index.C_w1nWis.js → index.Buip23l3.js} +1 -1
- streamlit/static/static/js/{index.CXLtvxrQ.js → index.Bw5t0A1a.js} +1 -1
- streamlit/static/static/js/{index.B8mzpc1l.js → index.ByHx8_go.js} +1 -1
- streamlit/static/static/js/{index.Bs4E_0D7.js → index.Byo1IU24.js} +1 -1
- streamlit/static/static/js/{index.Bh0_7PvD.js → index.ByzGGHA-.js} +1 -1
- streamlit/static/static/js/{index.BVbT0yWJ.js → index.C-xuUZos.js} +1 -1
- streamlit/static/static/js/{index.Dl2MB5Tx.js → index.C0X75KQC.js} +1 -1
- streamlit/static/static/js/{index.CV4niwLB.js → index.C5MX205D.js} +1 -1
- streamlit/static/static/js/{index.Blo8DAcU.js → index.CECvn-YK.js} +1 -1
- streamlit/static/static/js/{index.P0_E3iNi.js → index.CHfpPdI8.js} +1 -1
- streamlit/static/static/js/{index.BU8aUWlo.js → index.CT8A-jH3.js} +1 -1
- streamlit/static/static/js/{index.CJCEiqv2.js → index.CT93Qeic.js} +1 -1
- streamlit/static/static/js/{index.E4x2UBkh.js → index.CZKvO8_W.js} +1 -1
- streamlit/static/static/js/{index.AkleAg_w.js → index.Ccjs4i9w.js} +1 -1
- streamlit/static/static/js/{index.BkYhb--A.js → index.CeCHrefy.js} +1 -1
- streamlit/static/static/js/{index.qjO5OK90.js → index.Cpn4aUEp.js} +6 -6
- streamlit/static/static/js/{index.5CwvMIZq.js → index.D-VZVj73.js} +1 -1
- streamlit/static/static/js/{index.D94k70Hg.js → index.DA1Zwrh6.js} +1 -1
- streamlit/static/static/js/{index.COcj-WpN.js → index.DAddHkEE.js} +1 -1
- streamlit/static/static/js/{index.BXqtIpgi.js → index.DAjvUscA.js} +1 -1
- streamlit/static/static/js/{index.D7Hgmilz.js → index.DPa8c-h5.js} +1 -1
- streamlit/static/static/js/{index.XgBfXgN1.js → index.DUoWDMOZ.js} +1 -1
- streamlit/static/static/js/{index.CqywI2eW.js → index.DYqiNZKP.js} +1 -1
- streamlit/static/static/js/{index.BW0F_Pbs.js → index.DcZ6Y6tq.js} +1 -1
- streamlit/static/static/js/{index.dGk9EWLh.js → index.DfDGWP9w.js} +1 -1
- streamlit/static/static/js/{index.D0h2EJ_T.js → index.DiEHlHgD.js} +1 -1
- streamlit/static/static/js/{index.Ch4X_YdF.js → index.DkmkM-3F.js} +1 -1
- streamlit/static/static/js/{index.Dq0ZbGYZ.js → index.Vo6SwZW9.js} +1 -1
- streamlit/static/static/js/{index.Bhr3IHkD.js → index.X86xhkRz.js} +1 -1
- streamlit/static/static/js/index.Z5FSW1PS.js +3 -0
- streamlit/static/static/js/{index.DJ8OTQbk.js → index.d4eN08zp.js} +1 -1
- streamlit/static/static/js/{index.1sBZt_1I.js → index.hri4IXS4.js} +5 -5
- streamlit/static/static/js/{index.CGt5hnLi.js → index.jADyDjmJ.js} +1 -1
- streamlit/static/static/js/{index.ia5Ub9p7.js → index.jZuoBudL.js} +1 -1
- streamlit/static/static/js/{index.C6LOxzFC.js → index.oieFJFkX.js} +1 -1
- streamlit/static/static/js/{index.-pgbbiGJ.js → index.rpudgXQV.js} +1 -1
- streamlit/static/static/js/{index.MZ7ugsN-.js → index.zR1le1Pe.js} +1 -1
- streamlit/static/static/js/{input.Bavj6HHJ.js → input.FrKOgyiF.js} +1 -1
- streamlit/static/static/js/{main.dnUTEH_j.js → main.DRehwjQT.js} +1 -1
- streamlit/static/static/js/{memory.Czf1Sxzc.js → memory.B2wgPAbQ.js} +1 -1
- streamlit/static/static/js/{number-overlay-editor.CRQIke3a.js → number-overlay-editor.CnTj7Q_W.js} +1 -1
- streamlit/static/static/js/{pandasStylerUtils.agmr-LQ2.js → pandasStylerUtils.CWancxgS.js} +1 -1
- streamlit/static/static/js/{sandbox.BlCiIomw.js → sandbox.DuleaI7G.js} +1 -1
- streamlit/static/static/js/{styled-components.Bz3KSbhj.js → styled-components.g3zaW9ch.js} +1 -1
- streamlit/static/static/js/{throttle.B1o314FW.js → throttle.Dta7xRnz.js} +1 -1
- streamlit/static/static/js/{timepicker.3x4ndo9E.js → timepicker.Bg1hWKQe.js} +1 -1
- streamlit/static/static/js/{toConsumableArray.BeHbBK8g.js → toConsumableArray.C3xlVjBM.js} +1 -1
- streamlit/static/static/js/uniqueId.2p-nMqYS.js +1 -0
- streamlit/static/static/js/{useBasicWidgetState.CFP4_PTk.js → useBasicWidgetState.CERZ06R-.js} +1 -1
- streamlit/static/static/js/{useIntlLocale.BJubkaPQ.js → useIntlLocale.DTs1RN9G.js} +1 -1
- streamlit/static/static/js/{useTextInputAutoExpand.DBGwhM4R.js → useTextInputAutoExpand.B6f9P3jg.js} +1 -1
- streamlit/static/static/js/{useUpdateUiValue.CyufNQfR.js → useUpdateUiValue.ByVBXV8J.js} +1 -1
- streamlit/static/static/js/{useWaveformController.Dj5h0D8Y.js → useWaveformController.D6wABMbB.js} +1 -1
- streamlit/static/static/js/{withCalculatedWidth.D0IRb-7w.js → withCalculatedWidth.Bd4vFDHU.js} +1 -1
- streamlit/static/static/js/{withFullScreenWrapper.CHBnw0Co.js → withFullScreenWrapper.CDnDcnIh.js} +1 -1
- streamlit/testing/v1/element_tree.py +2 -2
- streamlit/watcher/event_based_path_watcher.py +37 -7
- streamlit/watcher/path_watcher.py +60 -1
- streamlit/watcher/util.py +26 -10
- streamlit/web/bootstrap.py +11 -2
- {streamlit_nightly-1.53.1.dev20260120.dist-info → streamlit_nightly-1.53.1.dev20260121.dist-info}/METADATA +1 -1
- {streamlit_nightly-1.53.1.dev20260120.dist-info → streamlit_nightly-1.53.1.dev20260121.dist-info}/RECORD +104 -104
- {streamlit_nightly-1.53.1.dev20260120.dist-info → streamlit_nightly-1.53.1.dev20260121.dist-info}/WHEEL +1 -1
- streamlit/static/static/js/index.DRGDE5oo.js +0 -3
- streamlit/static/static/js/uniqueId.Zn1vZBLX.js +0 -1
- {streamlit_nightly-1.53.1.dev20260120.data → streamlit_nightly-1.53.1.dev20260121.data}/scripts/streamlit.cmd +0 -0
- {streamlit_nightly-1.53.1.dev20260120.dist-info → streamlit_nightly-1.53.1.dev20260121.dist-info}/entry_points.txt +0 -0
- {streamlit_nightly-1.53.1.dev20260120.dist-info → streamlit_nightly-1.53.1.dev20260121.dist-info}/top_level.txt +0 -0
streamlit/static/static/js/{useWaveformController.Dj5h0D8Y.js → useWaveformController.D6wABMbB.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{l as Z,r as i,aP as M,aa as $,aQ as O}from"./index.qjO5OK90.js";async function q(l,e=16e3){if(!l||l.size===0)throw new Error("Invalid or empty blob provided");if(!window.AudioContext)throw new Error("AudioContext not supported in this browser");const o=new AudioContext;try{const n=await l.arrayBuffer(),r=await o.decodeAudioData(n),d=e??r.sampleRate,s=await Q(r,d);return J(s,d)}finally{o.close()}}async function Q(l,e){const{duration:o,numberOfChannels:n,sampleRate:r}=l,d=Math.ceil(o*e);if(!window.OfflineAudioContext)throw new Error("OfflineAudioContext not supported");const s=new OfflineAudioContext(1,d,e),p=s.createBufferSource();if(p.buffer=l,n>1){const h=s.createChannelSplitter(n),m=s.createChannelMerger(1);p.connect(h);for(let c=0;c<n;c++){const u=s.createGain();u.gain.value=1/n,h.connect(u,c),u.connect(m,0,0)}m.connect(s.destination)}else p.connect(s.destination);p.start(0);try{return await s.startRendering()}catch(h){throw new Error(`Failed to resample audio from ${r}Hz to ${e}Hz: ${h instanceof Error?h.message:String(h)}`)}}function J(l,e){const n=l.length,r=n*2+44,d=new ArrayBuffer(r),s=new DataView(d),p=l.getChannelData(0),h=(c,u)=>{for(let y=0;y<u.length;y++)s.setUint8(c+y,u.charCodeAt(y))};h(0,"RIFF"),s.setUint32(4,r-8,!0),h(8,"WAVE"),h(12,"fmt "),s.setUint32(16,16,!0),s.setUint16(20,1,!0),s.setUint16(22,1,!0),s.setUint32(24,e,!0),s.setUint32(28,e*2,!0),s.setUint16(32,2,!0),s.setUint16(34,16,!0),h(36,"data"),s.setUint32(40,n*2,!0);let m=44;for(let c=0;c<n;c++){const u=Math.max(-1,Math.min(1,p[c]));s.setInt16(m,u*32767,!0),m+=2}return new Blob([d],{type:"audio/wav"})}class W{constructor(){this.wavesurfer=null,this.currentBlobUrl=null,this.events={},this.isPlaying=!1}initialize(e){this.wavesurfer=e,this.setupEventListeners()}setupEventListeners(){this.wavesurfer&&(this.teardownEventListeners(),this.handleTimeUpdate=e=>{this.events.onTimeUpdate?.(e*1e3)},this.handlePause=()=>{this.isPlaying=!1,this.events.onPause?.()},this.handlePlay=()=>{this.isPlaying=!0,this.events.onPlay?.()},this.handleFinish=()=>{this.isPlaying=!1,this.events.onFinish?.()},this.handleReady=()=>{this.events.onReady?.()},this.handleError=e=>{const o=e instanceof Error?e:new Error(String(e));this.events.onError?.(o)},this.wavesurfer.on("timeupdate",this.handleTimeUpdate),this.wavesurfer.on("pause",this.handlePause),this.wavesurfer.on("play",this.handlePlay),this.wavesurfer.on("finish",this.handleFinish),this.wavesurfer.on("ready",this.handleReady),this.wavesurfer.on("error",this.handleError))}setEventHandlers(e){this.events=e}async load(e){if(!this.wavesurfer)throw new Error("WaveSurfer not initialized");this.cleanupPreviousUrl();let o,n=null;try{if(e instanceof Blob)n=URL.createObjectURL(e),o=n;else if(e instanceof ArrayBuffer){const r=new Blob([e]);n=URL.createObjectURL(r),o=n}else o=e;this.currentBlobUrl=n,await this.wavesurfer.load(o)}catch(r){throw this.cleanupPreviousUrl(),r}}async play(){if(!this.wavesurfer)throw new Error("WaveSurfer not initialized");await this.wavesurfer.play()}pause(){this.wavesurfer&&this.wavesurfer.pause()}getDuration(){return this.wavesurfer?this.wavesurfer.getDuration()*1e3:0}getCurrentTime(){return this.wavesurfer?this.wavesurfer.getCurrentTime()*1e3:0}getIsPlaying(){return this.isPlaying}seekToStart(){this.wavesurfer&&this.wavesurfer.seekTo(0)}cleanupPreviousUrl(){this.currentBlobUrl&&(URL.revokeObjectURL(this.currentBlobUrl),this.currentBlobUrl=null)}destroy(){this.pause(),this.cleanupPreviousUrl(),this.wavesurfer&&(this.teardownEventListeners(),this.wavesurfer.empty(),this.wavesurfer=null),this.events={},this.isPlaying=!1,this.handleTimeUpdate=void 0,this.handlePause=void 0,this.handlePlay=void 0,this.handleFinish=void 0,this.handleReady=void 0,this.handleError=void 0}teardownEventListeners(){this.wavesurfer&&(this.handleTimeUpdate&&(this.wavesurfer.un("timeupdate",this.handleTimeUpdate),this.handleTimeUpdate=void 0),this.handlePause&&(this.wavesurfer.un("pause",this.handlePause),this.handlePause=void 0),this.handlePlay&&(this.wavesurfer.un("play",this.handlePlay),this.handlePlay=void 0),this.handleFinish&&(this.wavesurfer.un("finish",this.handleFinish),this.handleFinish=void 0),this.handleReady&&(this.wavesurfer.un("ready",this.handleReady),this.handleReady=void 0),this.handleError&&(this.wavesurfer.un("error",this.handleError),this.handleError=void 0))}}function I(l){return l.name==="NotAllowedError"||l.name==="PermissionDeniedError"||l.message?.toLowerCase().includes("permission denied")}class K{constructor(e={}){this.wavesurfer=null,this.recordPlugin=null,this.isRecording=!1,this.recordEndResolve=null,this.recordEndReject=null,this.events={},this.options=e}initialize(e,o){this.wavesurfer=e;try{const n={renderRecordedAudio:!1,mimeType:"audio/webm"};this.recordPlugin=e.registerPlugin(o.create(n)),this.setupEventListeners()}catch(n){const r=n instanceof Error?n:new Error(String(n));throw I(r)?(this.events.onPermissionDenied?.(),new Error("Microphone permission denied")):(this.events.onError?.(r),r)}}setupEventListeners(){this.recordPlugin&&(this.recordPlugin.on("record-start",()=>{this.isRecording=!0,this.events.onRecordStart?.()}),this.recordPlugin.on("record-end",e=>{this.isRecording=!1,this.events.onRecordEnd?.(e),this.recordEndResolve&&e&&e.size>0?(this.recordEndResolve(e),this.recordEndResolve=null,this.recordEndReject=null):this.recordEndReject?(this.recordEndReject(new Error("Invalid or empty recording")),this.recordEndResolve=null,this.recordEndReject=null):(this.recordEndResolve=null,this.recordEndReject=null)}),this.recordPlugin.on("record-progress",e=>{this.events.onRecordProgress?.(e)}))}setEventHandlers(e){this.events=e}async startRecording(){if(!this.recordPlugin)throw new Error("Record plugin not initialized");if(this.isRecording)return;const e=typeof this.options.sampleRate=="number"?this.options.sampleRate:void 0,o={};e!==void 0&&(o.sampleRate={ideal:e}),await this.startRecordingWithConstraints(o,e!==void 0)}async startRecordingWithConstraints(e,o){if(!this.recordPlugin)throw new Error("Record plugin not initialized");try{const n=Object.keys(e).length?e:void 0;await this.recordPlugin.startRecording(n)}catch(n){const r=n instanceof Error?n:new Error(String(n));if(I(r))throw this.events.onPermissionDenied?.(),new Error("Microphone permission denied");if(o&&(r.name==="OverconstrainedError"||r.name==="NotReadableError")){this.options.sampleRate=void 0,await this.startRecordingWithConstraints({},!1);return}throw this.events.onError?.(r),r}}async stopRecording(){if(!this.recordPlugin||!this.isRecording)throw new Error("Not currently recording");try{return await new Promise((e,o)=>{this.recordEndResolve=e,this.recordEndReject=o,this.recordPlugin?.stopRecording()})}catch(e){const o=e instanceof Error?e:new Error(String(e));throw this.events.onError?.(o),o}}cancelRecording(){this.recordPlugin&&this.isRecording&&(this.recordPlugin.stopRecording(),this.isRecording=!1,this.recordEndResolve=null,this.recordEndReject=null)}destroy(){this.cancelRecording(),this.recordPlugin&&(this.recordPlugin.destroy(),this.recordPlugin=null),this.wavesurfer=null,this.events={}}}const X=4,Y=4,ee=8,re=0,te=16e3;function se({containerRef:l,sampleRate:e,events:o,waveformPadding:n=0}){const r=Z(),[d,s]=i.useState("idle"),[p,h]=i.useState(null),[m,c]=i.useState(!1),u=i.useRef(null),y=i.useRef(null),a=i.useRef(null),f=i.useRef(o),S=i.useRef(!1),D=i.useRef(!1),E=i.useRef(new Set),P=i.useRef(!1),b=e===void 0?te:e,A=i.useCallback(()=>{E.current.clear(),c(!1),y.current&&(y.current.destroy(),y.current=null),a.current&&(a.current.destroy(),a.current=null),u.current&&(u.current.destroy(),u.current=null),S.current=!1,P.current=!1,s("idle"),h(null)},[]),x=i.useCallback(()=>{const t=Array.from(E.current);E.current.clear(),t.forEach(w=>{w.resolve()})},[]),_=i.useCallback(t=>{c(!1);const w=Array.from(E.current);E.current.clear(),w.forEach(v=>{v.reject(t)})},[]),T=i.useCallback(t=>{const w={onPlay:()=>{c(!0),f.current.onPlaybackPlay?.()},onPause:()=>{c(!1),f.current.onPlaybackPause?.()},onFinish:()=>{c(!1),f.current.onPlaybackFinish?.()},onReady:()=>{x()},onError:v=>{c(!1),_(v),f.current.onError?.(v)}};t.setEventHandlers(w)},[x,_]);i.useEffect(()=>{f.current=o,a.current&&T(a.current)},[o,T]);const U=i.useCallback(async()=>{if(!(S.current||D.current||!l.current)){D.current=!0;try{const[t,w]=await Promise.all([M(()=>import("./wavesurfer.esm.D1Sty35j.js"),[],import.meta.url),M(()=>import("./record.DytFsBUt.js"),[],import.meta.url)]),v=t.default,B=w.default,k=v.create({container:l.current,waveColor:r.colors.primary,progressColor:r.colors.bodyText,height:n>0?$(r.sizes.largestElementHeight)-2*n:"auto",barWidth:X,barGap:Y,barRadius:ee,cursorWidth:re,interact:!0});u.current=k,P.current=!1;const C=new K({sampleRate:b});C.initialize(k,B),C.setEventHandlers({onRecordProgress:R=>{f.current.onProgressMs?.(R)},onPermissionDenied:()=>{f.current.onPermissionDenied(),s("idle")},onError:R=>{f.current.onError(R),s("idle")}}),y.current=C;const g=new W;g.initialize(k),a.current=g,T(g),S.current=!0}catch(t){const w=t instanceof Error?t:new Error(String(t));f.current.onError?.(w)}finally{D.current=!1}}},[l,r,b,T,n]);i.useEffect(()=>(U(),()=>{A()}),[A,U]),i.useEffect(()=>{const t=u.current;if(t){if(d==="recording"){t.setOptions({waveColor:r.colors.primary,progressColor:r.colors.primary});return}if(P.current){t.setOptions({interact:!0,waveColor:O(r.colors.fadedText40,r.colors.secondaryBg),progressColor:r.colors.bodyText});return}t.setOptions({waveColor:r.colors.primary,progressColor:r.colors.bodyText})}},[d,r.colors.bodyText,r.colors.fadedText40,r.colors.primary,r.colors.secondaryBg]);const F=i.useCallback(async()=>{if(d!=="recording"){if(S.current||await U(),!y.current)throw new Error("Record backend not initialized");u.current&&u.current.setOptions({waveColor:r.colors.primary,progressColor:r.colors.primary}),P.current=!1,await y.current.startRecording(),s("recording"),h(null),c(!1),f.current.onRecordStart?.()}},[d,U,r.colors.primary]),L=i.useCallback(()=>{a.current&&u.current&&(a.current.destroy(),a.current=new W,a.current.initialize(u.current),E.current.clear(),c(!1),T(a.current)),P.current=!1},[T]),z=i.useCallback(()=>{a.current?.seekToStart(),c(!1),P.current=!0,u.current&&u.current.setOptions({interact:!0,waveColor:O(r.colors.fadedText40,r.colors.secondaryBg),progressColor:r.colors.bodyText})},[r.colors.bodyText,r.colors.fadedText40,r.colors.secondaryBg]),H=i.useCallback(async()=>{if(d!=="recording")throw new Error("Not currently recording");if(!y.current||!a.current)throw new Error("Backends not initialized");try{const t=await y.current.stopRecording();h(t),await new Promise((k,C)=>{if(!a.current){C(new Error("Player not initialized"));return}const g={resolve:()=>{E.current.delete(g),k()},reject:R=>{E.current.delete(g),C(R)}};E.current.add(g),a.current.load(t).catch(R=>{E.current.delete(g),C(R instanceof Error?R:new Error(String(R)))})}),s("idle"),c(!1),z();const v={durationMs:a.current?.getDuration()??0,sampleRate:typeof b=="number"?b:null,mimeType:t.type||"audio/webm",size:t.size},B={blob:t,meta:v};return f.current.onRecordReady?.(t),B}catch(t){const w=t instanceof Error?t:new Error(String(t));return c(!1),s("idle"),f.current.onError(w),{blob:new Blob,meta:{durationMs:0,sampleRate:null,mimeType:"audio/webm",size:0}}}},[d,z,b]),j=i.useCallback(async t=>{const w=t??p;if(!w){const v=new Error("No recorded audio to approve");f.current.onError(v);return}try{const v=await q(w,b);await f.current.onApprove?.(v),h(null),s("idle")}catch(v){const B=v instanceof Error?v:new Error(String(v));f.current.onError(B)}},[p,b]),N=i.useCallback(()=>{d==="recording"&&y.current?.cancelRecording(),L(),h(null),s("idle"),c(!1),P.current=!1,f.current.onCancel?.()},[d,L]),V=i.useMemo(()=>({isPlaying:()=>a.current?.getIsPlaying()??!1,play:async()=>{if(!a.current)throw new Error("Player not initialized");await a.current.play()},pause:()=>{a.current?.pause()},load:async t=>{if(S.current||await U(),!a.current)throw new Error("Player not initialized");await a.current.load(t),z()},getCurrentTimeMs:()=>a.current?.getCurrentTime()??0,getDurationMs:()=>a.current?.getDuration()??0}),[z,U]),G=i.useCallback(t=>{f.current=t},[]);return i.useEffect(()=>()=>{A()},[A]),{state:d,isPlaybackPlaying:m,mountRef:l,start:F,stop:H,approve:j,cancel:N,destroy:A,playback:V,setEventHandlers:G}}export{se as u};
|
|
1
|
+
import{l as Z,r as i,aP as M,aa as $,aQ as O}from"./index.Cpn4aUEp.js";async function q(l,e=16e3){if(!l||l.size===0)throw new Error("Invalid or empty blob provided");if(!window.AudioContext)throw new Error("AudioContext not supported in this browser");const o=new AudioContext;try{const n=await l.arrayBuffer(),r=await o.decodeAudioData(n),d=e??r.sampleRate,s=await Q(r,d);return J(s,d)}finally{o.close()}}async function Q(l,e){const{duration:o,numberOfChannels:n,sampleRate:r}=l,d=Math.ceil(o*e);if(!window.OfflineAudioContext)throw new Error("OfflineAudioContext not supported");const s=new OfflineAudioContext(1,d,e),p=s.createBufferSource();if(p.buffer=l,n>1){const h=s.createChannelSplitter(n),m=s.createChannelMerger(1);p.connect(h);for(let c=0;c<n;c++){const u=s.createGain();u.gain.value=1/n,h.connect(u,c),u.connect(m,0,0)}m.connect(s.destination)}else p.connect(s.destination);p.start(0);try{return await s.startRendering()}catch(h){throw new Error(`Failed to resample audio from ${r}Hz to ${e}Hz: ${h instanceof Error?h.message:String(h)}`)}}function J(l,e){const n=l.length,r=n*2+44,d=new ArrayBuffer(r),s=new DataView(d),p=l.getChannelData(0),h=(c,u)=>{for(let y=0;y<u.length;y++)s.setUint8(c+y,u.charCodeAt(y))};h(0,"RIFF"),s.setUint32(4,r-8,!0),h(8,"WAVE"),h(12,"fmt "),s.setUint32(16,16,!0),s.setUint16(20,1,!0),s.setUint16(22,1,!0),s.setUint32(24,e,!0),s.setUint32(28,e*2,!0),s.setUint16(32,2,!0),s.setUint16(34,16,!0),h(36,"data"),s.setUint32(40,n*2,!0);let m=44;for(let c=0;c<n;c++){const u=Math.max(-1,Math.min(1,p[c]));s.setInt16(m,u*32767,!0),m+=2}return new Blob([d],{type:"audio/wav"})}class W{constructor(){this.wavesurfer=null,this.currentBlobUrl=null,this.events={},this.isPlaying=!1}initialize(e){this.wavesurfer=e,this.setupEventListeners()}setupEventListeners(){this.wavesurfer&&(this.teardownEventListeners(),this.handleTimeUpdate=e=>{this.events.onTimeUpdate?.(e*1e3)},this.handlePause=()=>{this.isPlaying=!1,this.events.onPause?.()},this.handlePlay=()=>{this.isPlaying=!0,this.events.onPlay?.()},this.handleFinish=()=>{this.isPlaying=!1,this.events.onFinish?.()},this.handleReady=()=>{this.events.onReady?.()},this.handleError=e=>{const o=e instanceof Error?e:new Error(String(e));this.events.onError?.(o)},this.wavesurfer.on("timeupdate",this.handleTimeUpdate),this.wavesurfer.on("pause",this.handlePause),this.wavesurfer.on("play",this.handlePlay),this.wavesurfer.on("finish",this.handleFinish),this.wavesurfer.on("ready",this.handleReady),this.wavesurfer.on("error",this.handleError))}setEventHandlers(e){this.events=e}async load(e){if(!this.wavesurfer)throw new Error("WaveSurfer not initialized");this.cleanupPreviousUrl();let o,n=null;try{if(e instanceof Blob)n=URL.createObjectURL(e),o=n;else if(e instanceof ArrayBuffer){const r=new Blob([e]);n=URL.createObjectURL(r),o=n}else o=e;this.currentBlobUrl=n,await this.wavesurfer.load(o)}catch(r){throw this.cleanupPreviousUrl(),r}}async play(){if(!this.wavesurfer)throw new Error("WaveSurfer not initialized");await this.wavesurfer.play()}pause(){this.wavesurfer&&this.wavesurfer.pause()}getDuration(){return this.wavesurfer?this.wavesurfer.getDuration()*1e3:0}getCurrentTime(){return this.wavesurfer?this.wavesurfer.getCurrentTime()*1e3:0}getIsPlaying(){return this.isPlaying}seekToStart(){this.wavesurfer&&this.wavesurfer.seekTo(0)}cleanupPreviousUrl(){this.currentBlobUrl&&(URL.revokeObjectURL(this.currentBlobUrl),this.currentBlobUrl=null)}destroy(){this.pause(),this.cleanupPreviousUrl(),this.wavesurfer&&(this.teardownEventListeners(),this.wavesurfer.empty(),this.wavesurfer=null),this.events={},this.isPlaying=!1,this.handleTimeUpdate=void 0,this.handlePause=void 0,this.handlePlay=void 0,this.handleFinish=void 0,this.handleReady=void 0,this.handleError=void 0}teardownEventListeners(){this.wavesurfer&&(this.handleTimeUpdate&&(this.wavesurfer.un("timeupdate",this.handleTimeUpdate),this.handleTimeUpdate=void 0),this.handlePause&&(this.wavesurfer.un("pause",this.handlePause),this.handlePause=void 0),this.handlePlay&&(this.wavesurfer.un("play",this.handlePlay),this.handlePlay=void 0),this.handleFinish&&(this.wavesurfer.un("finish",this.handleFinish),this.handleFinish=void 0),this.handleReady&&(this.wavesurfer.un("ready",this.handleReady),this.handleReady=void 0),this.handleError&&(this.wavesurfer.un("error",this.handleError),this.handleError=void 0))}}function I(l){return l.name==="NotAllowedError"||l.name==="PermissionDeniedError"||l.message?.toLowerCase().includes("permission denied")}class K{constructor(e={}){this.wavesurfer=null,this.recordPlugin=null,this.isRecording=!1,this.recordEndResolve=null,this.recordEndReject=null,this.events={},this.options=e}initialize(e,o){this.wavesurfer=e;try{const n={renderRecordedAudio:!1,mimeType:"audio/webm"};this.recordPlugin=e.registerPlugin(o.create(n)),this.setupEventListeners()}catch(n){const r=n instanceof Error?n:new Error(String(n));throw I(r)?(this.events.onPermissionDenied?.(),new Error("Microphone permission denied")):(this.events.onError?.(r),r)}}setupEventListeners(){this.recordPlugin&&(this.recordPlugin.on("record-start",()=>{this.isRecording=!0,this.events.onRecordStart?.()}),this.recordPlugin.on("record-end",e=>{this.isRecording=!1,this.events.onRecordEnd?.(e),this.recordEndResolve&&e&&e.size>0?(this.recordEndResolve(e),this.recordEndResolve=null,this.recordEndReject=null):this.recordEndReject?(this.recordEndReject(new Error("Invalid or empty recording")),this.recordEndResolve=null,this.recordEndReject=null):(this.recordEndResolve=null,this.recordEndReject=null)}),this.recordPlugin.on("record-progress",e=>{this.events.onRecordProgress?.(e)}))}setEventHandlers(e){this.events=e}async startRecording(){if(!this.recordPlugin)throw new Error("Record plugin not initialized");if(this.isRecording)return;const e=typeof this.options.sampleRate=="number"?this.options.sampleRate:void 0,o={};e!==void 0&&(o.sampleRate={ideal:e}),await this.startRecordingWithConstraints(o,e!==void 0)}async startRecordingWithConstraints(e,o){if(!this.recordPlugin)throw new Error("Record plugin not initialized");try{const n=Object.keys(e).length?e:void 0;await this.recordPlugin.startRecording(n)}catch(n){const r=n instanceof Error?n:new Error(String(n));if(I(r))throw this.events.onPermissionDenied?.(),new Error("Microphone permission denied");if(o&&(r.name==="OverconstrainedError"||r.name==="NotReadableError")){this.options.sampleRate=void 0,await this.startRecordingWithConstraints({},!1);return}throw this.events.onError?.(r),r}}async stopRecording(){if(!this.recordPlugin||!this.isRecording)throw new Error("Not currently recording");try{return await new Promise((e,o)=>{this.recordEndResolve=e,this.recordEndReject=o,this.recordPlugin?.stopRecording()})}catch(e){const o=e instanceof Error?e:new Error(String(e));throw this.events.onError?.(o),o}}cancelRecording(){this.recordPlugin&&this.isRecording&&(this.recordPlugin.stopRecording(),this.isRecording=!1,this.recordEndResolve=null,this.recordEndReject=null)}destroy(){this.cancelRecording(),this.recordPlugin&&(this.recordPlugin.destroy(),this.recordPlugin=null),this.wavesurfer=null,this.events={}}}const X=4,Y=4,ee=8,re=0,te=16e3;function se({containerRef:l,sampleRate:e,events:o,waveformPadding:n=0}){const r=Z(),[d,s]=i.useState("idle"),[p,h]=i.useState(null),[m,c]=i.useState(!1),u=i.useRef(null),y=i.useRef(null),a=i.useRef(null),f=i.useRef(o),S=i.useRef(!1),D=i.useRef(!1),E=i.useRef(new Set),P=i.useRef(!1),b=e===void 0?te:e,A=i.useCallback(()=>{E.current.clear(),c(!1),y.current&&(y.current.destroy(),y.current=null),a.current&&(a.current.destroy(),a.current=null),u.current&&(u.current.destroy(),u.current=null),S.current=!1,P.current=!1,s("idle"),h(null)},[]),x=i.useCallback(()=>{const t=Array.from(E.current);E.current.clear(),t.forEach(w=>{w.resolve()})},[]),_=i.useCallback(t=>{c(!1);const w=Array.from(E.current);E.current.clear(),w.forEach(v=>{v.reject(t)})},[]),T=i.useCallback(t=>{const w={onPlay:()=>{c(!0),f.current.onPlaybackPlay?.()},onPause:()=>{c(!1),f.current.onPlaybackPause?.()},onFinish:()=>{c(!1),f.current.onPlaybackFinish?.()},onReady:()=>{x()},onError:v=>{c(!1),_(v),f.current.onError?.(v)}};t.setEventHandlers(w)},[x,_]);i.useEffect(()=>{f.current=o,a.current&&T(a.current)},[o,T]);const U=i.useCallback(async()=>{if(!(S.current||D.current||!l.current)){D.current=!0;try{const[t,w]=await Promise.all([M(()=>import("./wavesurfer.esm.D1Sty35j.js"),[],import.meta.url),M(()=>import("./record.DytFsBUt.js"),[],import.meta.url)]),v=t.default,B=w.default,k=v.create({container:l.current,waveColor:r.colors.primary,progressColor:r.colors.bodyText,height:n>0?$(r.sizes.largestElementHeight)-2*n:"auto",barWidth:X,barGap:Y,barRadius:ee,cursorWidth:re,interact:!0});u.current=k,P.current=!1;const C=new K({sampleRate:b});C.initialize(k,B),C.setEventHandlers({onRecordProgress:R=>{f.current.onProgressMs?.(R)},onPermissionDenied:()=>{f.current.onPermissionDenied(),s("idle")},onError:R=>{f.current.onError(R),s("idle")}}),y.current=C;const g=new W;g.initialize(k),a.current=g,T(g),S.current=!0}catch(t){const w=t instanceof Error?t:new Error(String(t));f.current.onError?.(w)}finally{D.current=!1}}},[l,r,b,T,n]);i.useEffect(()=>(U(),()=>{A()}),[A,U]),i.useEffect(()=>{const t=u.current;if(t){if(d==="recording"){t.setOptions({waveColor:r.colors.primary,progressColor:r.colors.primary});return}if(P.current){t.setOptions({interact:!0,waveColor:O(r.colors.fadedText40,r.colors.secondaryBg),progressColor:r.colors.bodyText});return}t.setOptions({waveColor:r.colors.primary,progressColor:r.colors.bodyText})}},[d,r.colors.bodyText,r.colors.fadedText40,r.colors.primary,r.colors.secondaryBg]);const F=i.useCallback(async()=>{if(d!=="recording"){if(S.current||await U(),!y.current)throw new Error("Record backend not initialized");u.current&&u.current.setOptions({waveColor:r.colors.primary,progressColor:r.colors.primary}),P.current=!1,await y.current.startRecording(),s("recording"),h(null),c(!1),f.current.onRecordStart?.()}},[d,U,r.colors.primary]),L=i.useCallback(()=>{a.current&&u.current&&(a.current.destroy(),a.current=new W,a.current.initialize(u.current),E.current.clear(),c(!1),T(a.current)),P.current=!1},[T]),z=i.useCallback(()=>{a.current?.seekToStart(),c(!1),P.current=!0,u.current&&u.current.setOptions({interact:!0,waveColor:O(r.colors.fadedText40,r.colors.secondaryBg),progressColor:r.colors.bodyText})},[r.colors.bodyText,r.colors.fadedText40,r.colors.secondaryBg]),H=i.useCallback(async()=>{if(d!=="recording")throw new Error("Not currently recording");if(!y.current||!a.current)throw new Error("Backends not initialized");try{const t=await y.current.stopRecording();h(t),await new Promise((k,C)=>{if(!a.current){C(new Error("Player not initialized"));return}const g={resolve:()=>{E.current.delete(g),k()},reject:R=>{E.current.delete(g),C(R)}};E.current.add(g),a.current.load(t).catch(R=>{E.current.delete(g),C(R instanceof Error?R:new Error(String(R)))})}),s("idle"),c(!1),z();const v={durationMs:a.current?.getDuration()??0,sampleRate:typeof b=="number"?b:null,mimeType:t.type||"audio/webm",size:t.size},B={blob:t,meta:v};return f.current.onRecordReady?.(t),B}catch(t){const w=t instanceof Error?t:new Error(String(t));return c(!1),s("idle"),f.current.onError(w),{blob:new Blob,meta:{durationMs:0,sampleRate:null,mimeType:"audio/webm",size:0}}}},[d,z,b]),j=i.useCallback(async t=>{const w=t??p;if(!w){const v=new Error("No recorded audio to approve");f.current.onError(v);return}try{const v=await q(w,b);await f.current.onApprove?.(v),h(null),s("idle")}catch(v){const B=v instanceof Error?v:new Error(String(v));f.current.onError(B)}},[p,b]),N=i.useCallback(()=>{d==="recording"&&y.current?.cancelRecording(),L(),h(null),s("idle"),c(!1),P.current=!1,f.current.onCancel?.()},[d,L]),V=i.useMemo(()=>({isPlaying:()=>a.current?.getIsPlaying()??!1,play:async()=>{if(!a.current)throw new Error("Player not initialized");await a.current.play()},pause:()=>{a.current?.pause()},load:async t=>{if(S.current||await U(),!a.current)throw new Error("Player not initialized");await a.current.load(t),z()},getCurrentTimeMs:()=>a.current?.getCurrentTime()??0,getDurationMs:()=>a.current?.getDuration()??0}),[z,U]),G=i.useCallback(t=>{f.current=t},[]);return i.useEffect(()=>()=>{A()},[A]),{state:d,isPlaybackPlaying:m,mountRef:l,start:F,stop:H,approve:j,cancel:N,destroy:A,playback:V,setEventHandlers:G}}export{se as u};
|
streamlit/static/static/js/{withCalculatedWidth.D0IRb-7w.js → withCalculatedWidth.Bd4vFDHU.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{h as l,k as h,j as s,d as n}from"./index.
|
|
1
|
+
import{h as l,k as h,j as s,d as n}from"./index.Cpn4aUEp.js";const o=t=>{const a=e=>{const{width:i,elementRef:c}=h();return s(n,{ref:c,children:s(t,{...e,width:i})})};return a.displayName=`withCalculatedWidth(${t.displayName||t.name})`,l(a,t)};export{o as w};
|
streamlit/static/static/js/{withFullScreenWrapper.CHBnw0Co.js → withFullScreenWrapper.CDnDcnIh.js}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as n,z as f,b5 as p,b6 as h,l as x,k as y,j as i,h as g}from"./index.
|
|
1
|
+
import{r as n,z as f,b5 as p,b6 as h,l as x,k as y,j as i,h as g}from"./index.Cpn4aUEp.js";const m=n.createContext(null);m.displayName="ElementFullscreenContext";const w=f("div",{target:"e5bcvgj0"})(({theme:e,isExpanded:t})=>({width:"100%",height:"100%",...t?{position:"fixed",top:0,left:0,bottom:0,right:0,background:e.colors.bgColor,zIndex:e.zIndices.fullscreenWrapper,padding:e.spacing.md,paddingTop:e.sizes.fullScreenHeaderHeight,overflow:"auto",display:"flex",alignItems:"center",justifyContent:"center"}:{}})),C=()=>{const{setFullScreen:e}=n.useContext(p),[t,s]=n.useState(!1),{fullHeight:a,fullWidth:c}=h(),l=n.useCallback(r=>{s(r),e(r)},[e]),u=n.useCallback(()=>{document.body.style.overflow="hidden",l(!0)},[l]),o=n.useCallback(()=>{document.body.style.overflow="unset",l(!1)},[l]),d=n.useCallback(r=>{r.keyCode===27&&t&&o()},[o,t]);return n.useEffect(()=>(document.addEventListener("keydown",d,!1),()=>{document.removeEventListener("keydown",d,!1)}),[d]),n.useMemo(()=>({expanded:t,zoomIn:u,zoomOut:o,fullHeight:a,fullWidth:c}),[t,u,o,a,c])},b=({children:e})=>{const t=x(),{expanded:s,fullHeight:a,fullWidth:c,zoomIn:l,zoomOut:u}=C(),{width:o,elementRef:d}=y(),r=n.useMemo(()=>({width:s?c:o,height:s?a:void 0,expanded:s,expand:l,collapse:u}),[s,a,c,o,l,u]);return i(m.Provider,{value:r,children:i(w,{ref:d,isExpanded:s,"data-testid":"stFullScreenFrame",theme:t,children:e})})};function S(e){const t=s=>i(b,{children:i(e,{...s})});return t.displayName=`withFullScreenWrapper(${e.displayName||e.name})`,g(t,e)}export{m as E,S as w};
|
|
@@ -993,8 +993,8 @@ class Radio(Widget, Generic[T]):
|
|
|
993
993
|
"""
|
|
994
994
|
ws = WidgetState()
|
|
995
995
|
ws.id = self.id
|
|
996
|
-
if self.index is not None:
|
|
997
|
-
ws.
|
|
996
|
+
if self.index is not None and len(self.options) > 0:
|
|
997
|
+
ws.string_value = self.options[self.index]
|
|
998
998
|
return ws
|
|
999
999
|
|
|
1000
1000
|
|
|
@@ -69,7 +69,20 @@ def _get_abs_folder_path(path: str) -> str:
|
|
|
69
69
|
|
|
70
70
|
|
|
71
71
|
class EventBasedPathWatcher:
|
|
72
|
-
"""Watches a single path on disk using watchdog.
|
|
72
|
+
"""Watches a single path on disk using watchdog.
|
|
73
|
+
|
|
74
|
+
Behavior differs based on whether watching a file or directory:
|
|
75
|
+
|
|
76
|
+
**File watching:** Detects content changes via MD5 hash comparison. The
|
|
77
|
+
callback receives the file path and is invoked when content changes. With
|
|
78
|
+
allow_nonexistent=True, also detects file creation.
|
|
79
|
+
|
|
80
|
+
**Directory watching:** Detects any file activity within the directory
|
|
81
|
+
(creation, deletion, modification). The callback receives the actual
|
|
82
|
+
changed file path (not the directory). Note that glob_pattern only affects
|
|
83
|
+
the initial state hash, not which events trigger callbacks - all file
|
|
84
|
+
events in the directory invoke the callback.
|
|
85
|
+
"""
|
|
73
86
|
|
|
74
87
|
@staticmethod
|
|
75
88
|
def close_all() -> None:
|
|
@@ -91,17 +104,22 @@ class EventBasedPathWatcher:
|
|
|
91
104
|
Parameters
|
|
92
105
|
----------
|
|
93
106
|
path : str
|
|
94
|
-
The path to watch.
|
|
107
|
+
The path to watch (file or directory).
|
|
95
108
|
on_changed : Callable[[str], None]
|
|
96
|
-
Callback
|
|
109
|
+
Callback invoked when changes are detected. For files, receives
|
|
110
|
+
the file path. For directories, receives the path of the actual
|
|
111
|
+
changed file within the directory.
|
|
97
112
|
glob_pattern : str or None
|
|
98
|
-
A glob pattern
|
|
99
|
-
|
|
100
|
-
directory
|
|
113
|
+
A glob pattern for initial state detection when watching a
|
|
114
|
+
directory (e.g., "*.py"). Note: This does NOT filter which file
|
|
115
|
+
events trigger the callback - all file events in the directory
|
|
116
|
+
will invoke the callback regardless of this pattern.
|
|
101
117
|
allow_nonexistent : bool
|
|
102
118
|
If True, the watcher will not raise an exception if the path does
|
|
103
119
|
not exist. This can be used to watch for the creation of a file or
|
|
104
|
-
directory at a given path.
|
|
120
|
+
directory at a given path. Note: The parent directory of the path
|
|
121
|
+
must exist for watching to work. If the parent doesn't exist, the
|
|
122
|
+
watcher is silently skipped.
|
|
105
123
|
"""
|
|
106
124
|
self._path = os.path.realpath(path)
|
|
107
125
|
self._on_changed = on_changed
|
|
@@ -189,6 +207,18 @@ class _MultiPathWatcher:
|
|
|
189
207
|
folder_handler, folder_path, recursive=True
|
|
190
208
|
)
|
|
191
209
|
self._folder_handlers[folder_path] = folder_handler
|
|
210
|
+
except FileNotFoundError:
|
|
211
|
+
# This happens when watching a non-existent file whose parent
|
|
212
|
+
# directory also doesn't exist (e.g., .streamlit/config.toml
|
|
213
|
+
# when .streamlit/ hasn't been created yet). This is expected
|
|
214
|
+
# and not an error - we just can't watch until the directory
|
|
215
|
+
# is created.
|
|
216
|
+
_LOGGER.debug(
|
|
217
|
+
"Cannot watch path %s: directory %s does not exist",
|
|
218
|
+
path,
|
|
219
|
+
folder_path,
|
|
220
|
+
)
|
|
221
|
+
return
|
|
192
222
|
except Exception as ex:
|
|
193
223
|
_LOGGER.warning(
|
|
194
224
|
"Failed to schedule watch observer for path %s",
|
|
@@ -137,8 +137,37 @@ def watch_file(
|
|
|
137
137
|
path: str,
|
|
138
138
|
on_file_changed: Callable[[str], None],
|
|
139
139
|
watcher_type: str | None = None,
|
|
140
|
+
*, # keyword-only arguments:
|
|
141
|
+
allow_nonexistent: bool = False,
|
|
140
142
|
) -> bool:
|
|
141
|
-
|
|
143
|
+
"""Watch a file for changes.
|
|
144
|
+
|
|
145
|
+
The callback is invoked when the file's content changes (detected via MD5).
|
|
146
|
+
If allow_nonexistent is True, the watcher will also detect when the file
|
|
147
|
+
is created.
|
|
148
|
+
|
|
149
|
+
Parameters
|
|
150
|
+
----------
|
|
151
|
+
path
|
|
152
|
+
Path to the file to watch.
|
|
153
|
+
on_file_changed
|
|
154
|
+
Callback invoked with the file path when changes are detected.
|
|
155
|
+
watcher_type
|
|
156
|
+
Optional watcher type ('watchdog', 'poll', 'auto', or 'none').
|
|
157
|
+
allow_nonexistent
|
|
158
|
+
If True, watch for file creation even if the file doesn't exist yet.
|
|
159
|
+
Note: The file's parent directory must exist for watching to work.
|
|
160
|
+
If the parent directory doesn't exist, the watcher silently skips
|
|
161
|
+
watching (the file can't be created without its parent directory).
|
|
162
|
+
|
|
163
|
+
Returns
|
|
164
|
+
-------
|
|
165
|
+
bool
|
|
166
|
+
True if the watcher was successfully created.
|
|
167
|
+
"""
|
|
168
|
+
return _watch_path(
|
|
169
|
+
path, on_file_changed, watcher_type, allow_nonexistent=allow_nonexistent
|
|
170
|
+
)
|
|
142
171
|
|
|
143
172
|
|
|
144
173
|
def watch_dir(
|
|
@@ -149,6 +178,36 @@ def watch_dir(
|
|
|
149
178
|
glob_pattern: str | None = None,
|
|
150
179
|
allow_nonexistent: bool = False,
|
|
151
180
|
) -> bool:
|
|
181
|
+
"""Watch a directory for file changes.
|
|
182
|
+
|
|
183
|
+
The callback is invoked for any file activity within the directory,
|
|
184
|
+
including file creation, deletion, and content modifications. The callback
|
|
185
|
+
receives the path of the actual changed file (not the directory path).
|
|
186
|
+
|
|
187
|
+
Note: The glob_pattern parameter only affects the initial state detection
|
|
188
|
+
(which files are counted when determining if the directory changed). It does
|
|
189
|
+
NOT filter which file events trigger the callback - all file events in the
|
|
190
|
+
directory will invoke the callback regardless of glob_pattern.
|
|
191
|
+
|
|
192
|
+
Parameters
|
|
193
|
+
----------
|
|
194
|
+
path
|
|
195
|
+
Path to the directory to watch.
|
|
196
|
+
on_dir_changed
|
|
197
|
+
Callback invoked with the changed file path when changes are detected.
|
|
198
|
+
watcher_type
|
|
199
|
+
Optional watcher type ('watchdog', 'poll', 'auto', or 'none').
|
|
200
|
+
glob_pattern
|
|
201
|
+
Glob pattern for initial state detection (e.g., "*.py"). Does not
|
|
202
|
+
filter runtime events.
|
|
203
|
+
allow_nonexistent
|
|
204
|
+
If True, watch for directory creation even if it doesn't exist yet.
|
|
205
|
+
|
|
206
|
+
Returns
|
|
207
|
+
-------
|
|
208
|
+
bool
|
|
209
|
+
True if the watcher was successfully created.
|
|
210
|
+
"""
|
|
152
211
|
# Add a trailing slash to the path to ensure
|
|
153
212
|
# that its interpreted as a directory.
|
|
154
213
|
path = os.path.join(path, "")
|
streamlit/watcher/util.py
CHANGED
|
@@ -63,11 +63,19 @@ def calc_md5_with_blocking_retries(
|
|
|
63
63
|
# There's a race condition where sometimes file_path no longer exists when
|
|
64
64
|
# we try to read it (since the file is in the process of being written).
|
|
65
65
|
# So here we retry a few times using this loop. See issue #186.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
try:
|
|
67
|
+
content = _do_with_retries(
|
|
68
|
+
lambda: _get_file_content(path),
|
|
69
|
+
(FileNotFoundError, PermissionError),
|
|
70
|
+
path,
|
|
71
|
+
)
|
|
72
|
+
except StreamlitMaxRetriesError:
|
|
73
|
+
# If allow_nonexistent is True and the file was deleted between our
|
|
74
|
+
# exists check and the read, treat it as nonexistent instead of raising.
|
|
75
|
+
if allow_nonexistent:
|
|
76
|
+
content = path.encode("UTF-8")
|
|
77
|
+
else:
|
|
78
|
+
raise
|
|
71
79
|
|
|
72
80
|
return calc_md5(content)
|
|
73
81
|
|
|
@@ -91,11 +99,19 @@ def path_modification_time(path: str, allow_nonexistent: bool = False) -> float:
|
|
|
91
99
|
|
|
92
100
|
# Use retries to avoid race condition where file may be in the process of being
|
|
93
101
|
# modified.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
102
|
+
try:
|
|
103
|
+
return _do_with_retries(
|
|
104
|
+
lambda: os.stat(path).st_mtime,
|
|
105
|
+
(FileNotFoundError, PermissionError),
|
|
106
|
+
path,
|
|
107
|
+
)
|
|
108
|
+
except StreamlitMaxRetriesError:
|
|
109
|
+
# If allow_nonexistent is True and the file was deleted between our
|
|
110
|
+
# exists check and the stat call, return 0.0 instead of raising.
|
|
111
|
+
# This handles the race condition where a file is deleted while being watched.
|
|
112
|
+
if allow_nonexistent:
|
|
113
|
+
return 0.0
|
|
114
|
+
raise
|
|
99
115
|
|
|
100
116
|
|
|
101
117
|
def _get_file_content(file_path: str) -> bytes:
|
streamlit/web/bootstrap.py
CHANGED
|
@@ -325,8 +325,17 @@ def _install_config_watchers(flag_options: dict[str, Any]) -> None:
|
|
|
325
325
|
load_config_options(flag_options)
|
|
326
326
|
|
|
327
327
|
for filename in config.get_config_files("config.toml"):
|
|
328
|
-
if
|
|
329
|
-
|
|
328
|
+
# Watch each config file path directly, even if it doesn't exist yet.
|
|
329
|
+
# This allows detecting both file creation and subsequent modifications.
|
|
330
|
+
# We use the poll watcher because:
|
|
331
|
+
# 1. It handles non-existent paths gracefully, including when parent
|
|
332
|
+
# directories (like ~/.streamlit/) don't exist yet. The event-based
|
|
333
|
+
# watcher requires the parent directory to exist to schedule a watch.
|
|
334
|
+
# 2. Config files change rarely, so the polling overhead is negligible.
|
|
335
|
+
# 3. The 200ms poll interval latency is imperceptible for config reloads.
|
|
336
|
+
watch_file(
|
|
337
|
+
filename, on_config_changed, watcher_type="poll", allow_nonexistent=True
|
|
338
|
+
)
|
|
330
339
|
|
|
331
340
|
|
|
332
341
|
def run_asgi_app(
|