Implement "floating windows" style UI (#224)

* Basic transparent pane styling

* HTML and static asset cleanup

* Convert to collapsibles

* Polish up DebugPanel

* Add hotkey support, remove allotment

* Remove allotment css dependency

* Merge in from main

* Add a different resizable package

* Fix tsc errors introduced by merge

* Stream has to have at least z-index of 0

* App header has to be above stream z-index

* Applied z-index to the wrong element

* Scrollable logs, disable UI while dragging

* Fix test errors from importing CSS Modules in Jest

* Persist open panes configuration

* Style tweaks and fix camera step in onboarding

* Kurt review, make click-drag handler declarative
This commit is contained in:
Frank Noirot
2023-08-06 21:29:26 -04:00
committed by GitHub
parent 4c5178ea5c
commit 391f4ba206
21 changed files with 619 additions and 258 deletions

View File

@ -1,16 +1,24 @@
import { MouseEventHandler, useEffect, useRef } from 'react'
import { PanelHeader } from '../components/PanelHeader'
import { v4 as uuidv4 } from 'uuid'
import { useStore } from '../useStore'
import { throttle } from '../lib/utils'
import { EngineCommand } from '../lang/std/engineConnection'
export const Stream = () => {
export const Stream = ({ className = '' }) => {
const videoRef = useRef<HTMLVideoElement>(null)
const cmdId = useRef('')
const { mediaStream, engineCommandManager } = useStore((s) => ({
const {
mediaStream,
engineCommandManager,
setIsMouseDownInStream,
fileId,
setFileId,
setCmdId,
} = useStore((s) => ({
mediaStream: s.mediaStream,
engineCommandManager: s.engineCommandManager,
isMouseDownInStream: s.isMouseDownInStream,
setIsMouseDownInStream: s.setIsMouseDownInStream,
fileId: s.fileId,
setFileId: s.setFileId,
setCmdId: s.setCmdId,
}))
useEffect(() => {
@ -22,36 +30,8 @@ export const Stream = () => {
if (!videoRef.current) return
if (!mediaStream) return
videoRef.current.srcObject = mediaStream
}, [mediaStream, engineCommandManager])
const file_id = uuidv4()
const debounceSocketSend = throttle<EngineCommand>((message) => {
engineCommandManager?.sendSceneCommand(message)
}, 16)
const handleMouseMove: MouseEventHandler<HTMLVideoElement> = ({
clientX,
clientY,
ctrlKey,
}) => {
if (!videoRef.current) return
if (!cmdId.current) return
const { left, top } = videoRef.current.getBoundingClientRect()
const x = clientX - left
const y = clientY - top
const interaction = ctrlKey ? 'pan' : 'rotate'
debounceSocketSend({
type: 'modeling_cmd_req',
cmd: {
type: 'camera_drag_move',
interaction,
window: { x, y },
},
cmd_id: uuidv4(),
file_id: file_id,
})
}
setFileId(uuidv4())
}, [mediaStream, engineCommandManager, setFileId])
const handleMouseDown: MouseEventHandler<HTMLVideoElement> = ({
clientX,
@ -65,7 +45,7 @@ export const Stream = () => {
console.log('click', x, y)
const newId = uuidv4()
cmdId.current = newId
setCmdId(newId)
const interaction = ctrlKey ? 'pan' : 'rotate'
@ -77,9 +57,12 @@ export const Stream = () => {
window: { x, y },
},
cmd_id: newId,
file_id,
file_id: fileId,
})
setIsMouseDownInStream(true)
}
const handleMouseUp: MouseEventHandler<HTMLVideoElement> = ({
clientX,
clientY,
@ -90,9 +73,8 @@ export const Stream = () => {
const x = clientX - left
const y = clientY - top
if (cmdId.current == null) {
return
}
const newCmdId = uuidv4()
setCmdId(newCmdId)
const interaction = ctrlKey ? 'pan' : 'rotate'
@ -103,26 +85,27 @@ export const Stream = () => {
interaction,
window: { x, y },
},
cmd_id: uuidv4(),
file_id: file_id,
cmd_id: newCmdId,
file_id: fileId,
})
cmdId.current = ''
setCmdId('')
setIsMouseDownInStream(false)
}
return (
<div id="stream">
<PanelHeader title="Stream" />
<div id="stream" className={className}>
<video
ref={videoRef}
muted
autoPlay
controls={false}
onMouseMove={handleMouseMove}
onMouseDown={handleMouseDown}
onMouseUp={handleMouseUp}
onMouseLeave={handleMouseUp}
onContextMenu={(e) => e.preventDefault()}
onContextMenuCapture={(e) => e.preventDefault()}
className="w-full h-full"
/>
</div>
)