✨ feat: support message from webui extension
parent
60ed854add
commit
e928d7b1c5
|
|
@ -0,0 +1,38 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>open pose editor</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/message-test.ts"></script>
|
||||
<div>
|
||||
<iframe
|
||||
id="open-pose-editor"
|
||||
src="http://localhost:5173/open-pose-editor/"
|
||||
style="resize: both"
|
||||
width="600px"
|
||||
height="900px"
|
||||
></iframe>
|
||||
</div>
|
||||
<button id="GetAPIs">GetAPIs</button>
|
||||
<button id="GetAppVersion">GetAppVersion</button>
|
||||
<button id="MakeImages">MakeImages</button>
|
||||
<button id="Pause">Pause</button>
|
||||
<button id="Resume">Resume</button>
|
||||
<button id="OutputWidth">OutputWidth</button>
|
||||
<button id="OutputHeight">OutputHeight</button>
|
||||
<button id="OnlyHand">OnlyHand</button>
|
||||
<button id="MoveMode">MoveMode</button>
|
||||
<button id="GetWidth">GetWidth</button>
|
||||
<button id="GetHeight">GetHeight</button>
|
||||
<button id="GetSceneData">GetSceneData</button>
|
||||
<button id="LockView">LockView</button>
|
||||
<button id="UnlockView">UnlockView</button>
|
||||
<button id="RestoreView">RestoreView</button>
|
||||
<div id="output" style="background-color: gray;"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -11,6 +11,7 @@ import {
|
|||
ResumeIcon,
|
||||
} from '@radix-ui/react-icons'
|
||||
import { getCurrentTime } from '../../utils/time'
|
||||
import useMessageDispatch from '../../hooks/useMessageDispatch'
|
||||
|
||||
const { app, threejsCanvas, gallery, background } = classes
|
||||
|
||||
|
|
@ -140,6 +141,43 @@ function App() {
|
|||
}
|
||||
}, [editor])
|
||||
|
||||
useMessageDispatch({
|
||||
GetAppVersion: () => __APP_VERSION__,
|
||||
MakeImages: () => editor?.MakeImages(),
|
||||
Pause: () => editor?.pause(),
|
||||
Resume: () => editor?.resume(),
|
||||
OutputWidth: (value: number) => {
|
||||
if (editor && typeof value === 'number') {
|
||||
editor.OutputWidth = value
|
||||
return true
|
||||
} else return false
|
||||
},
|
||||
OutputHeight: (value: number) => {
|
||||
if (editor && typeof value === 'number') {
|
||||
editor.OutputHeight = value
|
||||
return true
|
||||
} else return false
|
||||
},
|
||||
OnlyHand(value: boolean) {
|
||||
if (editor && typeof value === 'boolean') {
|
||||
editor.OnlyHand = value
|
||||
return true
|
||||
} else return false
|
||||
},
|
||||
MoveMode(value: boolean) {
|
||||
if (editor && typeof value === 'boolean') {
|
||||
editor.MoveMode = value
|
||||
return true
|
||||
} else return false
|
||||
},
|
||||
GetWidth: () => editor?.Width,
|
||||
GetHeight: () => editor?.Height,
|
||||
GetSceneData: () => editor?.GetSceneData(),
|
||||
LockView: () => editor?.LockView(),
|
||||
UnlockView: () => editor?.UnlockView(),
|
||||
RestoreView: () => editor?.RestoreView(),
|
||||
})
|
||||
|
||||
return (
|
||||
<div ref={backgroundRef} className={background}>
|
||||
<canvas
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
// https://github.com/Volune/use-event-callback/blob/master/src/index.ts
|
||||
|
||||
import { useLayoutEffect, useMemo, useRef } from 'react'
|
||||
|
||||
type Fn<ARGS extends any[], R> = (...args: ARGS) => R
|
||||
|
||||
const useEventCallback = <A extends any[], R>(fn: Fn<A, R>): Fn<A, R> => {
|
||||
const ref = useRef<Fn<A, R>>(fn)
|
||||
useLayoutEffect(() => {
|
||||
ref.current = fn
|
||||
})
|
||||
return useMemo(
|
||||
() =>
|
||||
(...args: A): R => {
|
||||
const { current } = ref
|
||||
return current(...args)
|
||||
},
|
||||
[]
|
||||
)
|
||||
}
|
||||
|
||||
export default useEventCallback
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
// https://github.com/rottitime/react-hook-window-message-event/blob/main/src/useMessage.ts
|
||||
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import useEventCallback from './useEventCall'
|
||||
|
||||
export type IPostMessage = {
|
||||
method: string
|
||||
type: 'call' | 'return'
|
||||
payload: any
|
||||
}
|
||||
|
||||
export type EventHandler = (
|
||||
callback: (data: IPostMessage) => unknown,
|
||||
payload: IPostMessage['payload']
|
||||
) => void
|
||||
|
||||
const postMessage = (
|
||||
data: IPostMessage & {
|
||||
cmd?: string // Just avoid webui exception
|
||||
},
|
||||
target: MessageEvent['source'],
|
||||
origin = '*'
|
||||
) => {
|
||||
console.log('return', { target, origin, data })
|
||||
target?.postMessage(
|
||||
{ cmd: 'openpose-3d', ...data },
|
||||
{ targetOrigin: origin }
|
||||
)
|
||||
}
|
||||
|
||||
export default function useMessageDispatch(
|
||||
dispatch: Record<string, (...args: any[]) => any>
|
||||
) {
|
||||
const originRef = useRef<string>()
|
||||
const sourceRef = useRef<MessageEvent['source']>(null)
|
||||
|
||||
originRef.current = ''
|
||||
sourceRef.current = null as MessageEvent['source']
|
||||
|
||||
const sendToSender = (data: IPostMessage) =>
|
||||
postMessage(data, sourceRef.current, originRef.current)
|
||||
|
||||
const sendToParent = (data: IPostMessage) => {
|
||||
const { opener } = window
|
||||
if (!opener) throw new Error('Parent window has closed')
|
||||
postMessage(data, opener)
|
||||
}
|
||||
|
||||
const onWatchEventHandler = useEventCallback(
|
||||
// tslint:disable-next-line: no-shadowed-variable
|
||||
({ origin, source, data }: MessageEvent) => {
|
||||
if (!data) return
|
||||
|
||||
const { method, payload, type } = data as IPostMessage
|
||||
// It is invalid message, not from webui extension.
|
||||
if (type != 'call') return
|
||||
|
||||
console.log('method', method, payload)
|
||||
|
||||
if (payload && Array.isArray(payload) === false) {
|
||||
console.error('payload is not array')
|
||||
return
|
||||
}
|
||||
|
||||
sourceRef.current = source
|
||||
originRef.current = origin
|
||||
|
||||
if (method in dispatch) {
|
||||
const eventHandler = dispatch[method]
|
||||
if (typeof eventHandler === 'function') {
|
||||
const ret = eventHandler(...(payload ?? []))
|
||||
if (ret instanceof Promise) {
|
||||
ret.then((value) => {
|
||||
sendToSender({
|
||||
method,
|
||||
type: 'return',
|
||||
payload: value,
|
||||
})
|
||||
})
|
||||
} else {
|
||||
sendToSender({
|
||||
method,
|
||||
type: 'return',
|
||||
payload: ret,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else if (method === 'GetAPIs') {
|
||||
sendToSender({
|
||||
method,
|
||||
type: 'return',
|
||||
payload: Object.keys(dispatch),
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener('message', onWatchEventHandler)
|
||||
return () => window.removeEventListener('message', onWatchEventHandler)
|
||||
}, [onWatchEventHandler])
|
||||
|
||||
return { history, sendToParent }
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
export type IPostMessage = {
|
||||
method: string
|
||||
type: 'call' | 'return'
|
||||
payload: any
|
||||
}
|
||||
|
||||
const iframe = document.getElementById('open-pose-editor') as HTMLIFrameElement
|
||||
|
||||
const poseMessage = (message: IPostMessage) => {
|
||||
iframe.contentWindow?.postMessage(message)
|
||||
}
|
||||
|
||||
const MessageReturnHandler: Record<string, (arg: any) => void> = {}
|
||||
|
||||
window.addEventListener('message', (event) => {
|
||||
const { data } = event
|
||||
if (data && data.cmd && data.cmd == 'openpose-3d' && data.method) {
|
||||
const method = data.method
|
||||
if (method in MessageReturnHandler) {
|
||||
MessageReturnHandler[method]?.(data.payload)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function InvokeOnlineOpenPose3D(method: string, ...args: any[]) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const id = setTimeout(() => {
|
||||
delete MessageReturnHandler[method]
|
||||
|
||||
reject({
|
||||
method,
|
||||
status: 'Timeout',
|
||||
})
|
||||
}, 1000)
|
||||
|
||||
const onReutrn = (arg: any) => {
|
||||
clearTimeout(id)
|
||||
resolve(arg)
|
||||
}
|
||||
MessageReturnHandler[method] = onReutrn
|
||||
|
||||
poseMessage({
|
||||
method,
|
||||
type: 'call',
|
||||
payload: args,
|
||||
})
|
||||
})
|
||||
}
|
||||
function CreateClick(name: string, ...args: any[]) {
|
||||
const ele = document.getElementById(name)
|
||||
|
||||
ele?.addEventListener('click', async () => {
|
||||
console.log(name)
|
||||
const value = await InvokeOnlineOpenPose3D(name, ...args)
|
||||
console.log('return', value)
|
||||
})
|
||||
}
|
||||
|
||||
CreateClick('GetAPIs')
|
||||
CreateClick('GetAppVersion')
|
||||
CreateClick('MakeImages')
|
||||
CreateClick('Pause')
|
||||
CreateClick('Resume')
|
||||
CreateClick('OutputWidth', 512)
|
||||
CreateClick('OutputHeight', 512)
|
||||
CreateClick('OnlyHand', true)
|
||||
CreateClick('MoveMode', true)
|
||||
CreateClick('GetWidth')
|
||||
CreateClick('GetHeight')
|
||||
CreateClick('GetSceneData')
|
||||
CreateClick('LockView')
|
||||
CreateClick('UnlockView')
|
||||
CreateClick('RestoreView')
|
||||
|
|
@ -42,7 +42,7 @@ const config: UserConfigFn = ({ command, mode, ssrBuild }) => {
|
|||
base: mode === 'singlefile' ? './' : '/open-pose-editor/',
|
||||
define: {
|
||||
global: {},
|
||||
__APP_VERSION__: JSON.stringify('v0.0.2'),
|
||||
__APP_VERSION__: JSON.stringify('0.1.2'),
|
||||
__APP_BUILD_TIME__: Date.now(),
|
||||
},
|
||||
build: {
|
||||
|
|
|
|||
Loading…
Reference in New Issue