@ -56,7 +56,7 @@
 | 
			
		||||
  },
 | 
			
		||||
  "jest": {
 | 
			
		||||
    "transformIgnorePatterns": [
 | 
			
		||||
      "node_modules/(?!(three|allotment|@tauri-apps\/api)/)"
 | 
			
		||||
      "node_modules/(?!(three|allotment|@tauri-apps/api)/)"
 | 
			
		||||
    ]
 | 
			
		||||
  },
 | 
			
		||||
  "prettier": {
 | 
			
		||||
@ -93,6 +93,7 @@
 | 
			
		||||
    "@tauri-apps/cli": "^1.3.1",
 | 
			
		||||
    "@types/crypto-js": "^4.1.1",
 | 
			
		||||
    "@types/three": "^0.146.0",
 | 
			
		||||
    "@types/uuid": "^9.0.2",
 | 
			
		||||
    "autoprefixer": "^10.4.13",
 | 
			
		||||
    "postcss": "^8.4.19",
 | 
			
		||||
    "prettier": "^2.8.0",
 | 
			
		||||
 | 
			
		||||
@ -209,7 +209,7 @@ function App() {
 | 
			
		||||
          <MemoryPanel />
 | 
			
		||||
          <Logs />
 | 
			
		||||
        </Allotment>
 | 
			
		||||
        <Allotment vertical defaultSizes={[400, 1]} minSize={20}>
 | 
			
		||||
        <Allotment vertical defaultSizes={[1, 400]} minSize={20}>
 | 
			
		||||
          <div className="h-full">
 | 
			
		||||
            <PanelHeader title="Drafting Board" />
 | 
			
		||||
            <Toolbar />
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
import { useEffect, useRef } from 'react'
 | 
			
		||||
import { PanelHeader } from '../components/PanelHeader'
 | 
			
		||||
import { v4 as uuidv4 } from 'uuid'
 | 
			
		||||
 | 
			
		||||
export const Stream = () => {
 | 
			
		||||
  const videoRef = useRef<HTMLVideoElement>(null)
 | 
			
		||||
@ -11,6 +12,8 @@ export const Stream = () => {
 | 
			
		||||
    )
 | 
			
		||||
      return
 | 
			
		||||
    const url = 'wss://api.dev.kittycad.io/ws/modeling/commands'
 | 
			
		||||
    const file_id = uuidv4()
 | 
			
		||||
    let currentCmdId: null | string = null
 | 
			
		||||
    const [pc, socket] = [new RTCPeerConnection(), new WebSocket(url)]
 | 
			
		||||
    // Connection opened
 | 
			
		||||
    socket.addEventListener('open', (event) => {
 | 
			
		||||
@ -41,13 +44,13 @@ export const Stream = () => {
 | 
			
		||||
        if (message.type === 'SDPAnswer') {
 | 
			
		||||
          pc.setRemoteDescription(new RTCSessionDescription(message.answer))
 | 
			
		||||
        } else if (message.type === 'TrickleIce') {
 | 
			
		||||
          console.log("got remote trickle ice");
 | 
			
		||||
          pc.addIceCandidate(message.candidate);
 | 
			
		||||
          console.log('got remote trickle ice')
 | 
			
		||||
          pc.addIceCandidate(message.candidate)
 | 
			
		||||
        } else if (message.type === 'IceServerInfo') {
 | 
			
		||||
          console.log('received IceServerInfo')
 | 
			
		||||
          pc.setConfiguration({
 | 
			
		||||
            iceServers: message.ice_servers,
 | 
			
		||||
            iceTransportPolicy: "relay",
 | 
			
		||||
            iceTransportPolicy: 'relay',
 | 
			
		||||
          })
 | 
			
		||||
          pc.ontrack = function (event) {
 | 
			
		||||
            if (videoRef.current) {
 | 
			
		||||
@ -69,14 +72,14 @@ export const Stream = () => {
 | 
			
		||||
                })
 | 
			
		||||
              )
 | 
			
		||||
            } else {
 | 
			
		||||
              console.log("sending trickle ice candidate");
 | 
			
		||||
              const {
 | 
			
		||||
                candidate
 | 
			
		||||
              } = event;
 | 
			
		||||
              socket.send(JSON.stringify({
 | 
			
		||||
                type: "TrickleIce",
 | 
			
		||||
                candidate: candidate.toJSON(),
 | 
			
		||||
              }));
 | 
			
		||||
              console.log('sending trickle ice candidate')
 | 
			
		||||
              const { candidate } = event
 | 
			
		||||
              socket.send(
 | 
			
		||||
                JSON.stringify({
 | 
			
		||||
                  type: 'TrickleIce',
 | 
			
		||||
                  candidate: candidate.toJSON(),
 | 
			
		||||
                })
 | 
			
		||||
              )
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
@ -87,11 +90,13 @@ export const Stream = () => {
 | 
			
		||||
          pc.createOffer()
 | 
			
		||||
            .then((d) => pc.setLocalDescription(d))
 | 
			
		||||
            .then(() => {
 | 
			
		||||
              console.log("sent SDPOffer begin");
 | 
			
		||||
              socket.send(JSON.stringify({
 | 
			
		||||
                type: "SDPOffer",
 | 
			
		||||
                offer: pc.localDescription,
 | 
			
		||||
              }));
 | 
			
		||||
              console.log('sent SDPOffer begin')
 | 
			
		||||
              socket.send(
 | 
			
		||||
                JSON.stringify({
 | 
			
		||||
                  type: 'SDPOffer',
 | 
			
		||||
                  offer: pc.localDescription,
 | 
			
		||||
                })
 | 
			
		||||
              )
 | 
			
		||||
            })
 | 
			
		||||
            .catch(console.log)
 | 
			
		||||
        }
 | 
			
		||||
@ -101,15 +106,89 @@ export const Stream = () => {
 | 
			
		||||
    const debounceSocketSend = throttle((message) => {
 | 
			
		||||
      socket.send(JSON.stringify(message))
 | 
			
		||||
    }, 100)
 | 
			
		||||
    const handleClick = ({ clientX, clientY }: MouseEvent) => {
 | 
			
		||||
      if (!videoRef.current) return
 | 
			
		||||
      const { left, top } = videoRef.current.getBoundingClientRect()
 | 
			
		||||
      const x = clientX - left
 | 
			
		||||
      const y = clientY - top
 | 
			
		||||
      console.log('click', x, y)
 | 
			
		||||
 | 
			
		||||
      if (currentCmdId == null) {
 | 
			
		||||
        currentCmdId = uuidv4()
 | 
			
		||||
 | 
			
		||||
        debounceSocketSend({
 | 
			
		||||
          type: 'ModelingCmdReq',
 | 
			
		||||
          cmd: {
 | 
			
		||||
            CameraDragStart: {
 | 
			
		||||
              interaction: 'rotate',
 | 
			
		||||
              window: {
 | 
			
		||||
                x: x,
 | 
			
		||||
                y: y,
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
          cmd_id: uuidv4(),
 | 
			
		||||
          file_id: file_id,
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const handleMouseUp = ({ clientX, clientY }: MouseEvent) => {
 | 
			
		||||
      if (!videoRef.current) return
 | 
			
		||||
      const { left, top } = videoRef.current.getBoundingClientRect()
 | 
			
		||||
      const x = clientX - left
 | 
			
		||||
      const y = clientY - top
 | 
			
		||||
      console.log('click', x, y)
 | 
			
		||||
 | 
			
		||||
      if (currentCmdId == null) {
 | 
			
		||||
        return
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      debounceSocketSend({
 | 
			
		||||
        type: 'ModelingCmdReq',
 | 
			
		||||
        cmd: {
 | 
			
		||||
          CameraDragEnd: {
 | 
			
		||||
            interaction: 'rotate',
 | 
			
		||||
            window: {
 | 
			
		||||
              x: x,
 | 
			
		||||
              y: y,
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        cmd_id: uuidv4(),
 | 
			
		||||
        file_id: file_id,
 | 
			
		||||
      })
 | 
			
		||||
        currentCmdId   = null
 | 
			
		||||
    }
 | 
			
		||||
    const handleMouseMove = ({ clientX, clientY }: MouseEvent) => {
 | 
			
		||||
      if (!videoRef.current) return
 | 
			
		||||
      const { left, top } = videoRef.current.getBoundingClientRect()
 | 
			
		||||
      const x = clientX - left
 | 
			
		||||
      const y = clientY - top
 | 
			
		||||
      debounceSocketSend({ type: 'MouseMove', x: x, y: y })
 | 
			
		||||
      if (currentCmdId == null) {
 | 
			
		||||
        return
 | 
			
		||||
      } else {
 | 
			
		||||
      console.log('mouse move', x, y)
 | 
			
		||||
        debounceSocketSend({
 | 
			
		||||
          type: 'ModelingCmdReq',
 | 
			
		||||
          cmd: {
 | 
			
		||||
            CameraDragMove: {
 | 
			
		||||
              interaction: 'rotate',
 | 
			
		||||
              window: {
 | 
			
		||||
                x: x,
 | 
			
		||||
                y: y,
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
          cmd_id: uuidv4(),
 | 
			
		||||
          file_id: file_id,
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    if (videoRef.current) {
 | 
			
		||||
      videoRef.current.addEventListener('mousemove', handleMouseMove)
 | 
			
		||||
      videoRef.current.addEventListener('mousedown', handleClick)
 | 
			
		||||
      videoRef.current.addEventListener('mouseup', handleMouseUp)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return () => {
 | 
			
		||||
@ -117,6 +196,8 @@ export const Stream = () => {
 | 
			
		||||
      pc.close()
 | 
			
		||||
      if (!videoRef.current) return
 | 
			
		||||
      videoRef.current.removeEventListener('mousemove', handleMouseMove)
 | 
			
		||||
      videoRef.current.removeEventListener('mousedown', handleClick)
 | 
			
		||||
      videoRef.current.removeEventListener('mouseup', handleMouseUp)
 | 
			
		||||
    }
 | 
			
		||||
  }, [])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -2695,6 +2695,11 @@
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.2.tgz#fc25ad9943bcac11cceb8168db4f275e0e72e756"
 | 
			
		||||
  integrity sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==
 | 
			
		||||
 | 
			
		||||
"@types/uuid@^9.0.2":
 | 
			
		||||
  version "9.0.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.2.tgz#ede1d1b1e451548d44919dc226253e32a6952c4b"
 | 
			
		||||
  integrity sha512-kNnC1GFBLuhImSnV7w4njQkUiJi0ZXUycu1rUaouPqiKlXkh77JKgdRnTAp1x5eBwcIwbtI+3otwzuIDEuDoxQ==
 | 
			
		||||
 | 
			
		||||
"@types/webxr@*":
 | 
			
		||||
  version "0.5.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/@types/webxr/-/webxr-0.5.0.tgz#aae1cef3210d88fd4204f8c33385a0bbc4da07c9"
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user