feat: improve mobile touch/pen interactions across custom tools

- Add onTouchStart/onTouchEnd handlers to all interactive elements
- Add touchAction: 'manipulation' CSS to prevent 300ms click delay
- Increase minimum touch target sizes to 44px for accessibility
- Fix ImageGen: Generate button, Copy/Download/Delete, input field
- Fix VideoGen: Upload, URL input, prompt, duration, Generate button
- Fix Transcription: Start/Stop/Pause buttons, textarea, Save/Cancel
- Fix Multmux: Create Session, Refresh, session list, input fields

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2025-12-10 10:27:44 -08:00
parent 96887c8ba1
commit b6af3ecba8
4 changed files with 173 additions and 17 deletions

View File

@ -804,6 +804,8 @@ export class ImageGenShape extends BaseBoxShapeUtil<IImageGen> {
} }
}} }}
onPointerDown={(e) => e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()}
onTouchStart={(e) => e.stopPropagation()}
onTouchEnd={(e) => e.stopPropagation()}
style={{ style={{
flex: 1, flex: 1,
padding: '6px 10px', padding: '6px 10px',
@ -819,6 +821,8 @@ export class ImageGenShape extends BaseBoxShapeUtil<IImageGen> {
justifyContent: 'center', justifyContent: 'center',
gap: '4px', gap: '4px',
transition: 'background-color 0.15s', transition: 'background-color 0.15s',
touchAction: 'manipulation',
minHeight: '44px',
}} }}
onMouseEnter={(e) => (e.currentTarget.style.backgroundColor = '#f0f0f0')} onMouseEnter={(e) => (e.currentTarget.style.backgroundColor = '#f0f0f0')}
onMouseLeave={(e) => (e.currentTarget.style.backgroundColor = '#fff')} onMouseLeave={(e) => (e.currentTarget.style.backgroundColor = '#fff')}
@ -850,6 +854,8 @@ export class ImageGenShape extends BaseBoxShapeUtil<IImageGen> {
console.log('✅ ImageGen: Image download initiated') console.log('✅ ImageGen: Image download initiated')
}} }}
onPointerDown={(e) => e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()}
onTouchStart={(e) => e.stopPropagation()}
onTouchEnd={(e) => e.stopPropagation()}
style={{ style={{
flex: 1, flex: 1,
padding: '6px 10px', padding: '6px 10px',
@ -865,6 +871,8 @@ export class ImageGenShape extends BaseBoxShapeUtil<IImageGen> {
justifyContent: 'center', justifyContent: 'center',
gap: '4px', gap: '4px',
transition: 'opacity 0.15s', transition: 'opacity 0.15s',
touchAction: 'manipulation',
minHeight: '44px',
}} }}
onMouseEnter={(e) => (e.currentTarget.style.opacity = '0.9')} onMouseEnter={(e) => (e.currentTarget.style.opacity = '0.9')}
onMouseLeave={(e) => (e.currentTarget.style.opacity = '1')} onMouseLeave={(e) => (e.currentTarget.style.opacity = '1')}
@ -883,6 +891,8 @@ export class ImageGenShape extends BaseBoxShapeUtil<IImageGen> {
}) })
}} }}
onPointerDown={(e) => e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()}
onTouchStart={(e) => e.stopPropagation()}
onTouchEnd={(e) => e.stopPropagation()}
style={{ style={{
padding: '6px 10px', padding: '6px 10px',
backgroundColor: '#fff', backgroundColor: '#fff',
@ -896,6 +906,9 @@ export class ImageGenShape extends BaseBoxShapeUtil<IImageGen> {
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
transition: 'background-color 0.15s, color 0.15s', transition: 'background-color 0.15s, color 0.15s',
touchAction: 'manipulation',
minWidth: '44px',
minHeight: '44px',
}} }}
onMouseEnter={(e) => { onMouseEnter={(e) => {
e.currentTarget.style.backgroundColor = '#fee' e.currentTarget.style.backgroundColor = '#fee'
@ -952,6 +965,8 @@ export class ImageGenShape extends BaseBoxShapeUtil<IImageGen> {
borderRadius: "6px", borderRadius: "6px",
fontSize: 13, fontSize: 13,
padding: "0 10px", padding: "0 10px",
touchAction: "manipulation",
minHeight: "44px",
}} }}
type="text" type="text"
placeholder="Enter image prompt..." placeholder="Enter image prompt..."
@ -975,6 +990,9 @@ export class ImageGenShape extends BaseBoxShapeUtil<IImageGen> {
onPointerDown={(e) => { onPointerDown={(e) => {
e.stopPropagation() e.stopPropagation()
}} }}
onTouchStart={(e) => {
e.stopPropagation()
}}
onClick={(e) => { onClick={(e) => {
e.stopPropagation() e.stopPropagation()
}} }}
@ -993,6 +1011,9 @@ export class ImageGenShape extends BaseBoxShapeUtil<IImageGen> {
fontWeight: "500", fontWeight: "500",
fontSize: "13px", fontSize: "13px",
opacity: shape.props.prompt.trim() && !shape.props.isLoading ? 1 : 0.6, opacity: shape.props.prompt.trim() && !shape.props.isLoading ? 1 : 0.6,
touchAction: "manipulation",
minWidth: "44px",
minHeight: "44px",
}} }}
onPointerDown={(e) => { onPointerDown={(e) => {
e.stopPropagation() e.stopPropagation()
@ -1001,6 +1022,16 @@ export class ImageGenShape extends BaseBoxShapeUtil<IImageGen> {
handleGenerate() handleGenerate()
} }
}} }}
onTouchStart={(e) => {
e.stopPropagation()
}}
onTouchEnd={(e) => {
e.stopPropagation()
e.preventDefault()
if (shape.props.prompt.trim() && !shape.props.isLoading) {
handleGenerate()
}
}}
onClick={(e) => { onClick={(e) => {
e.preventDefault() e.preventDefault()
e.stopPropagation() e.stopPropagation()
@ -1045,6 +1076,8 @@ export class ImageGenShape extends BaseBoxShapeUtil<IImageGen> {
}) })
}} }}
onPointerDown={(e) => e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()}
onTouchStart={(e) => e.stopPropagation()}
onTouchEnd={(e) => e.stopPropagation()}
style={{ style={{
padding: "2px 6px", padding: "2px 6px",
backgroundColor: "#fcc", backgroundColor: "#fcc",
@ -1053,6 +1086,9 @@ export class ImageGenShape extends BaseBoxShapeUtil<IImageGen> {
cursor: "pointer", cursor: "pointer",
fontSize: "10px", fontSize: "10px",
flexShrink: 0, flexShrink: 0,
touchAction: "manipulation",
minWidth: "32px",
minHeight: "32px",
}} }}
> >

View File

@ -569,8 +569,11 @@ export class MultmuxShape extends BaseBoxShapeUtil<IMultmuxShape> {
color: '#cdd6f4', color: '#cdd6f4',
fontFamily: 'monospace', fontFamily: 'monospace',
fontSize: '14px', fontSize: '14px',
touchAction: 'manipulation',
minHeight: '44px',
}} }}
onPointerDown={(e) => e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()}
onTouchStart={(e) => e.stopPropagation()}
onMouseDown={(e) => e.stopPropagation()} onMouseDown={(e) => e.stopPropagation()}
/> />
<button <button
@ -586,8 +589,16 @@ export class MultmuxShape extends BaseBoxShapeUtil<IMultmuxShape> {
fontFamily: 'monospace', fontFamily: 'monospace',
fontSize: '16px', fontSize: '16px',
transition: 'background-color 0.2s', transition: 'background-color 0.2s',
touchAction: 'manipulation',
minHeight: '44px',
}} }}
onPointerDown={(e) => e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()}
onTouchStart={(e) => e.stopPropagation()}
onTouchEnd={(e) => {
e.stopPropagation()
e.preventDefault()
handleCreateSession()
}}
onMouseDown={(e) => e.stopPropagation()} onMouseDown={(e) => e.stopPropagation()}
> >
+ Create New Session + Create New Session
@ -614,8 +625,16 @@ export class MultmuxShape extends BaseBoxShapeUtil<IMultmuxShape> {
color: '#cdd6f4', color: '#cdd6f4',
cursor: 'pointer', cursor: 'pointer',
fontFamily: 'monospace', fontFamily: 'monospace',
touchAction: 'manipulation',
minHeight: '44px',
}} }}
onPointerDown={(e) => e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()}
onTouchStart={(e) => e.stopPropagation()}
onTouchEnd={(e) => {
e.stopPropagation()
e.preventDefault()
if (!loadingSessions) fetchSessions()
}}
onMouseDown={(e) => e.stopPropagation()} onMouseDown={(e) => e.stopPropagation()}
> >
{loadingSessions ? 'Loading...' : 'Refresh Sessions'} {loadingSessions ? 'Loading...' : 'Refresh Sessions'}
@ -648,8 +667,16 @@ export class MultmuxShape extends BaseBoxShapeUtil<IMultmuxShape> {
cursor: 'pointer', cursor: 'pointer',
textAlign: 'left', textAlign: 'left',
fontFamily: 'monospace', fontFamily: 'monospace',
touchAction: 'manipulation',
minHeight: '44px',
}} }}
onPointerDown={(e) => e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()}
onTouchStart={(e) => e.stopPropagation()}
onTouchEnd={(e) => {
e.stopPropagation()
e.preventDefault()
handleJoinSession(session.id)
}}
onMouseDown={(e) => e.stopPropagation()} onMouseDown={(e) => e.stopPropagation()}
> >
<div style={{ fontWeight: 'bold' }}>{session.name}</div> <div style={{ fontWeight: 'bold' }}>{session.name}</div>
@ -674,8 +701,16 @@ export class MultmuxShape extends BaseBoxShapeUtil<IMultmuxShape> {
fontSize: '12px', fontSize: '12px',
fontFamily: 'monospace', fontFamily: 'monospace',
padding: '4px 0', padding: '4px 0',
touchAction: 'manipulation',
minHeight: '44px',
}} }}
onPointerDown={(e) => e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()}
onTouchStart={(e) => e.stopPropagation()}
onTouchEnd={(e) => {
e.stopPropagation()
e.preventDefault()
setShowAdvanced(!showAdvanced)
}}
onMouseDown={(e) => e.stopPropagation()} onMouseDown={(e) => e.stopPropagation()}
> >
{showAdvanced ? '▼' : '▶'} Advanced Settings {showAdvanced ? '▼' : '▶'} Advanced Settings
@ -704,8 +739,11 @@ export class MultmuxShape extends BaseBoxShapeUtil<IMultmuxShape> {
color: '#cdd6f4', color: '#cdd6f4',
fontFamily: 'monospace', fontFamily: 'monospace',
fontSize: '12px', fontSize: '12px',
touchAction: 'manipulation',
minHeight: '44px',
}} }}
onPointerDown={(e) => e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()}
onTouchStart={(e) => e.stopPropagation()}
onMouseDown={(e) => e.stopPropagation()} onMouseDown={(e) => e.stopPropagation()}
/> />
</label> </label>

View File

@ -35,8 +35,9 @@ const AutoResizeTextarea: React.FC<{
style: React.CSSProperties style: React.CSSProperties
placeholder?: string placeholder?: string
onPointerDown?: (e: React.PointerEvent) => void onPointerDown?: (e: React.PointerEvent) => void
onTouchStart?: (e: React.TouchEvent) => void
onWheel?: (e: React.WheelEvent) => void onWheel?: (e: React.WheelEvent) => void
}> = ({ value, onChange, onBlur, onKeyDown, style, placeholder, onPointerDown, onWheel }) => { }> = ({ value, onChange, onBlur, onKeyDown, style, placeholder, onPointerDown, onTouchStart, onWheel }) => {
const textareaRef = useRef<HTMLTextAreaElement>(null) const textareaRef = useRef<HTMLTextAreaElement>(null)
useEffect(() => { useEffect(() => {
@ -56,8 +57,12 @@ const AutoResizeTextarea: React.FC<{
onBlur={onBlur} onBlur={onBlur}
onKeyDown={onKeyDown} onKeyDown={onKeyDown}
onPointerDown={onPointerDown} onPointerDown={onPointerDown}
onTouchStart={onTouchStart}
onWheel={onWheel} onWheel={onWheel}
style={style} style={{
...style,
touchAction: 'manipulation',
}}
placeholder={placeholder} placeholder={placeholder}
autoFocus autoFocus
/> />
@ -638,6 +643,9 @@ export class TranscriptionShape extends BaseBoxShapeUtil<ITranscription> {
zIndex: 1000, zIndex: 1000,
position: 'relative', position: 'relative',
pointerEvents: 'auto', // Ensure button can receive clicks pointerEvents: 'auto', // Ensure button can receive clicks
touchAction: 'manipulation',
minWidth: '44px',
minHeight: '32px',
} }
// Custom header content with status indicators and controls // Custom header content with status indicators and controls
@ -665,6 +673,12 @@ export class TranscriptionShape extends BaseBoxShapeUtil<ITranscription> {
style={buttonStyle} style={buttonStyle}
onClick={handleSaveEdit} onClick={handleSaveEdit}
onPointerDown={(e) => e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()}
onTouchStart={(e) => e.stopPropagation()}
onTouchEnd={(e) => {
e.stopPropagation()
e.preventDefault()
handleSaveEdit()
}}
> >
Save Save
</button> </button>
@ -672,6 +686,12 @@ export class TranscriptionShape extends BaseBoxShapeUtil<ITranscription> {
style={buttonStyle} style={buttonStyle}
onClick={handleCancelEdit} onClick={handleCancelEdit}
onPointerDown={(e) => e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()}
onTouchStart={(e) => e.stopPropagation()}
onTouchEnd={(e) => {
e.stopPropagation()
e.preventDefault()
handleCancelEdit()
}}
> >
Cancel Cancel
</button> </button>
@ -723,6 +743,7 @@ export class TranscriptionShape extends BaseBoxShapeUtil<ITranscription> {
style={textareaStyle} style={textareaStyle}
placeholder="" placeholder=""
onPointerDown={(e) => e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()}
onTouchStart={(e) => e.stopPropagation()}
onWheel={handleWheel} onWheel={handleWheel}
/> />
) : ( ) : (
@ -753,13 +774,13 @@ export class TranscriptionShape extends BaseBoxShapeUtil<ITranscription> {
<button <button
style={{ style={{
...buttonStyle, ...buttonStyle,
background: isRecording background: isRecording
? "#ff4444" // Red when recording ? "#ff4444" // Red when recording
: isPaused : isPaused
? "#ffa500" // Orange when paused ? "#ffa500" // Orange when paused
: (useWebSpeech ? webSpeechSupported : modelLoaded) ? "#007bff" : "#6c757d", // Blue when ready to start, gray when loading : (useWebSpeech ? webSpeechSupported : modelLoaded) ? "#007bff" : "#6c757d", // Blue when ready to start, gray when loading
color: "white", color: "white",
border: isRecording border: isRecording
? "1px solid #cc0000" // Red border when recording ? "1px solid #cc0000" // Red border when recording
: isPaused : isPaused
? "1px solid #cc8500" // Orange border when paused ? "1px solid #cc8500" // Orange border when paused
@ -775,6 +796,16 @@ export class TranscriptionShape extends BaseBoxShapeUtil<ITranscription> {
onPointerDown={(e) => { onPointerDown={(e) => {
e.stopPropagation() e.stopPropagation()
}} }}
onTouchStart={(e) => {
e.stopPropagation()
}}
onTouchEnd={(e) => {
e.stopPropagation()
e.preventDefault()
if (useWebSpeech ? webSpeechSupported : modelLoaded) {
handleTranscriptionToggle()
}
}}
disabled={useWebSpeech ? !webSpeechSupported : !modelLoaded} disabled={useWebSpeech ? !webSpeechSupported : !modelLoaded}
title={useWebSpeech ? (!webSpeechSupported ? "Web Speech API not supported" : "") : (!modelLoaded ? "Whisper model is loading - Please wait..." : "")} title={useWebSpeech ? (!webSpeechSupported ? "Web Speech API not supported" : "") : (!modelLoaded ? "Whisper model is loading - Please wait..." : "")}
> >
@ -782,8 +813,8 @@ export class TranscriptionShape extends BaseBoxShapeUtil<ITranscription> {
if (isPaused) { if (isPaused) {
return "Resume" return "Resume"
} }
const buttonText = isRecording const buttonText = isRecording
? "Stop" ? "Stop"
: "Start" : "Start"
return buttonText return buttonText
})()} })()}
@ -804,6 +835,14 @@ export class TranscriptionShape extends BaseBoxShapeUtil<ITranscription> {
onPointerDown={(e) => { onPointerDown={(e) => {
e.stopPropagation() e.stopPropagation()
}} }}
onTouchStart={(e) => {
e.stopPropagation()
}}
onTouchEnd={(e) => {
e.stopPropagation()
e.preventDefault()
handlePauseToggle()
}}
title="Pause transcription" title="Pause transcription"
> >
Pause Pause

View File

@ -482,13 +482,15 @@ export class VideoGenShape extends BaseBoxShapeUtil<IVideoGen> {
setImageBase64('') setImageBase64('')
}} }}
onPointerDown={(e) => e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()}
onTouchStart={(e) => e.stopPropagation()}
onTouchEnd={(e) => e.stopPropagation()}
disabled={isGenerating} disabled={isGenerating}
style={{ style={{
position: 'absolute', position: 'absolute',
top: '4px', top: '4px',
right: '4px', right: '4px',
width: '24px', width: '32px',
height: '24px', height: '32px',
borderRadius: '50%', borderRadius: '50%',
border: 'none', border: 'none',
backgroundColor: 'rgba(0,0,0,0.6)', backgroundColor: 'rgba(0,0,0,0.6)',
@ -497,7 +499,8 @@ export class VideoGenShape extends BaseBoxShapeUtil<IVideoGen> {
fontSize: '14px', fontSize: '14px',
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center' justifyContent: 'center',
touchAction: 'manipulation',
}} }}
> >
× ×
@ -509,6 +512,12 @@ export class VideoGenShape extends BaseBoxShapeUtil<IVideoGen> {
<button <button
onClick={() => fileInputRef.current?.click()} onClick={() => fileInputRef.current?.click()}
onPointerDown={(e) => e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()}
onTouchStart={(e) => e.stopPropagation()}
onTouchEnd={(e) => {
e.stopPropagation()
e.preventDefault()
fileInputRef.current?.click()
}}
disabled={isGenerating} disabled={isGenerating}
style={{ style={{
flex: 1, flex: 1,
@ -522,7 +531,9 @@ export class VideoGenShape extends BaseBoxShapeUtil<IVideoGen> {
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
gap: '6px' gap: '6px',
touchAction: 'manipulation',
minHeight: '44px',
}} }}
> >
📤 Upload Image 📤 Upload Image
@ -549,6 +560,7 @@ export class VideoGenShape extends BaseBoxShapeUtil<IVideoGen> {
placeholder="Or paste image URL..." placeholder="Or paste image URL..."
disabled={isGenerating} disabled={isGenerating}
onPointerDown={(e) => e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()}
onTouchStart={(e) => e.stopPropagation()}
onMouseDown={(e) => e.stopPropagation()} onMouseDown={(e) => e.stopPropagation()}
style={{ style={{
width: '100%', width: '100%',
@ -558,7 +570,9 @@ export class VideoGenShape extends BaseBoxShapeUtil<IVideoGen> {
border: '1px solid #ddd', border: '1px solid #ddd',
borderRadius: '6px', borderRadius: '6px',
fontSize: '12px', fontSize: '12px',
boxSizing: 'border-box' boxSizing: 'border-box',
touchAction: 'manipulation',
minHeight: '44px',
}} }}
/> />
)} )}
@ -578,6 +592,7 @@ export class VideoGenShape extends BaseBoxShapeUtil<IVideoGen> {
} }
disabled={isGenerating} disabled={isGenerating}
onPointerDown={(e) => e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()}
onTouchStart={(e) => e.stopPropagation()}
onMouseDown={(e) => e.stopPropagation()} onMouseDown={(e) => e.stopPropagation()}
style={{ style={{
width: '100%', width: '100%',
@ -590,7 +605,8 @@ export class VideoGenShape extends BaseBoxShapeUtil<IVideoGen> {
fontSize: '13px', fontSize: '13px',
fontFamily: 'inherit', fontFamily: 'inherit',
resize: 'vertical', resize: 'vertical',
boxSizing: 'border-box' boxSizing: 'border-box',
touchAction: 'manipulation',
}} }}
/> />
</div> </div>
@ -614,6 +630,7 @@ export class VideoGenShape extends BaseBoxShapeUtil<IVideoGen> {
}} }}
disabled={isGenerating} disabled={isGenerating}
onPointerDown={(e) => e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()}
onTouchStart={(e) => e.stopPropagation()}
onMouseDown={(e) => e.stopPropagation()} onMouseDown={(e) => e.stopPropagation()}
style={{ style={{
width: '100%', width: '100%',
@ -623,7 +640,9 @@ export class VideoGenShape extends BaseBoxShapeUtil<IVideoGen> {
border: '1px solid #ddd', border: '1px solid #ddd',
borderRadius: '6px', borderRadius: '6px',
fontSize: '13px', fontSize: '13px',
boxSizing: 'border-box' boxSizing: 'border-box',
touchAction: 'manipulation',
minHeight: '44px',
}} }}
/> />
</div> </div>
@ -632,6 +651,14 @@ export class VideoGenShape extends BaseBoxShapeUtil<IVideoGen> {
onClick={handleGenerate} onClick={handleGenerate}
disabled={isGenerating || !prompt.trim()} disabled={isGenerating || !prompt.trim()}
onPointerDown={(e) => e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()}
onTouchStart={(e) => e.stopPropagation()}
onTouchEnd={(e) => {
e.stopPropagation()
e.preventDefault()
if (!isGenerating && prompt.trim()) {
handleGenerate()
}
}}
onMouseDown={(e) => e.stopPropagation()} onMouseDown={(e) => e.stopPropagation()}
style={{ style={{
padding: '8px 20px', padding: '8px 20px',
@ -644,7 +671,9 @@ export class VideoGenShape extends BaseBoxShapeUtil<IVideoGen> {
cursor: isGenerating ? 'not-allowed' : 'pointer', cursor: isGenerating ? 'not-allowed' : 'pointer',
transition: 'all 0.2s', transition: 'all 0.2s',
whiteSpace: 'nowrap', whiteSpace: 'nowrap',
opacity: isGenerating || !prompt.trim() ? 0.6 : 1 opacity: isGenerating || !prompt.trim() ? 0.6 : 1,
touchAction: 'manipulation',
minHeight: '44px',
}} }}
> >
{isGenerating ? 'Generating...' : (mode === 'i2v' ? 'Animate Image' : 'Generate Video')} {isGenerating ? 'Generating...' : (mode === 'i2v' ? 'Animate Image' : 'Generate Video')}
@ -697,14 +726,17 @@ export class VideoGenShape extends BaseBoxShapeUtil<IVideoGen> {
controls controls
autoPlay autoPlay
loop loop
playsInline
onPointerDown={(e) => e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()}
onTouchStart={(e) => e.stopPropagation()}
onLoadedData={() => console.log('🎬 VideoGen: Video loaded successfully')} onLoadedData={() => console.log('🎬 VideoGen: Video loaded successfully')}
onError={(e) => console.error('🎬 VideoGen: Video load error:', e)} onError={(e) => console.error('🎬 VideoGen: Video load error:', e)}
style={{ style={{
width: '100%', width: '100%',
maxHeight: '280px', maxHeight: '280px',
borderRadius: '6px', borderRadius: '6px',
backgroundColor: '#000' backgroundColor: '#000',
touchAction: 'manipulation',
}} }}
/> />
@ -733,6 +765,8 @@ export class VideoGenShape extends BaseBoxShapeUtil<IVideoGen> {
}) })
}} }}
onPointerDown={(e) => e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()}
onTouchStart={(e) => e.stopPropagation()}
onTouchEnd={(e) => e.stopPropagation()}
onMouseDown={(e) => e.stopPropagation()} onMouseDown={(e) => e.stopPropagation()}
style={{ style={{
flex: 1, flex: 1,
@ -743,7 +777,9 @@ export class VideoGenShape extends BaseBoxShapeUtil<IVideoGen> {
borderRadius: '6px', borderRadius: '6px',
fontSize: '12px', fontSize: '12px',
fontWeight: '500', fontWeight: '500',
cursor: 'pointer' cursor: 'pointer',
touchAction: 'manipulation',
minHeight: '44px',
}} }}
> >
New Video New Video
@ -753,6 +789,8 @@ export class VideoGenShape extends BaseBoxShapeUtil<IVideoGen> {
href={videoUrl} href={videoUrl}
download="generated-video.mp4" download="generated-video.mp4"
onPointerDown={(e) => e.stopPropagation()} onPointerDown={(e) => e.stopPropagation()}
onTouchStart={(e) => e.stopPropagation()}
onTouchEnd={(e) => e.stopPropagation()}
onMouseDown={(e) => e.stopPropagation()} onMouseDown={(e) => e.stopPropagation()}
style={{ style={{
flex: 1, flex: 1,
@ -765,7 +803,12 @@ export class VideoGenShape extends BaseBoxShapeUtil<IVideoGen> {
fontWeight: '600', fontWeight: '600',
textAlign: 'center', textAlign: 'center',
textDecoration: 'none', textDecoration: 'none',
cursor: 'pointer' cursor: 'pointer',
touchAction: 'manipulation',
minHeight: '44px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}} }}
> >
Download Download