@@ -2,18 +2,101 @@ import {
22 FormField ,
33 FormInput ,
44 FormSection ,
5+ FormSelect ,
56 FormSwitch ,
67} from '@/components/forms'
7- import { useFilterEditor , useUI } from '@/services'
8+ import { FormSlider } from '@/components/forms/FormSlider'
9+ import { useFilterEditor , useRenderer , useUI } from '@/services'
10+ import { getValidNumber } from '@aitube/clap'
11+ import { FilterWithParams } from '@aitube/clapper-services'
12+ import { useEffect , useState } from 'react'
13+
14+ // TODO: move this to the renderer service
15+ // also since filters use WebGPU, I think one day we can run them in real-time
16+ // over the video as well (or maybe using WebGL)
17+ function useCurrentlyVisibleStoryboard ( ) : string | undefined {
18+ const { activeStoryboardSegment } = useRenderer ( ( s ) => s . bufferedSegments )
19+
20+ // can't return something if there is nothing
21+ if ( ! activeStoryboardSegment ?. assetUrl . startsWith ( 'data:image/' ) ) {
22+ return undefined
23+ }
24+
25+ return activeStoryboardSegment . assetUrl
26+ }
27+
28+ function useFilteredStoryboard ( input ?: string ) : string | undefined {
29+ const current = useFilterEditor ( ( s ) => s . current )
30+ const runFilterPipeline = useFilterEditor ( ( s ) => s . runFilterPipeline )
31+ const [ result , setResult ] = useState ( '' )
32+
33+ const currentFiltersWithParams : FilterWithParams [ ] = current || [ ]
34+
35+ console . log ( 'current changed?' , current )
36+
37+ useEffect ( ( ) => {
38+ const fn = async ( input ?: string ) => {
39+ if ( ! input ) {
40+ return undefined
41+ }
42+ try {
43+ console . log ( 'running filter using WebGPU..' )
44+ const res = await runFilterPipeline ( input )
45+ setResult ( res )
46+ } catch ( err ) {
47+ console . error ( err )
48+ }
49+ }
50+ fn ( input )
51+ } , [
52+ input ,
53+ // whenever the pipeline, filter, input values.. change,
54+ // we re-generate the output as well
55+ currentFiltersWithParams ,
56+ JSON . stringify ( currentFiltersWithParams ) ,
57+ ] )
58+
59+ return result ? result : undefined
60+ }
861
962export function FilterViewer ( ) {
1063 const current = useFilterEditor ( ( s ) => s . current )
1164 const setCurrent = useFilterEditor ( ( s ) => s . setCurrent )
1265 const undo = useFilterEditor ( ( s ) => s . undo )
1366 const redo = useFilterEditor ( ( s ) => s . redo )
1467
68+ const input = useCurrentlyVisibleStoryboard ( )
69+ const output = useFilteredStoryboard ( input )
70+
1571 const hasBetaAccess = useUI ( ( s ) => s . hasBetaAccess )
1672
73+ const setFilterParamValue = (
74+ filterId : string ,
75+ fieldId : string ,
76+ value : string | number | boolean
77+ ) => {
78+ console . log ( `setFilterParamValue(${ filterId } , ${ fieldId } , ${ value } )` )
79+ setCurrent (
80+ ( current || [ ] ) . map ( ( fwp ) => {
81+ if ( fwp . filter . id === filterId ) {
82+ console . log ( 'match!' , fwp )
83+ return {
84+ ...fwp ,
85+ parameters : {
86+ ...fwp . parameters ,
87+ [ fieldId ] : value ,
88+ } ,
89+ }
90+ } else {
91+ console . log ( 'no match..' , fwp )
92+ console . log ( 'filterId:' , filterId )
93+ console . log ( 'fieldId:' , fieldId )
94+ }
95+ return fwp
96+ } )
97+ )
98+ }
99+
17100 if ( ! hasBetaAccess ) {
18101 return (
19102 < FormSection label = { 'Filter Editor' } className = "p-4" >
@@ -31,40 +114,71 @@ export function FilterViewer() {
31114 }
32115
33116 return (
34- < >
35- { current . map ( ( { filter, parameters } ) => (
36- < FormSection key = { filter . id } label = { filter . label } className = "p-4" >
37- { filter . parameters . map ( ( filter ) => (
38- < FormField key = { filter . id } >
39- { filter . type === 'string' && (
40- < FormInput
41- type = "text"
42- label = { filter . label }
43- value = { parameters [ filter . id ] }
44- defaultValue = { filter . defaultValue }
45- />
46- ) }
47- { filter . type === 'number' && (
48- < FormInput
49- type = "number"
50- label = { filter . label }
51- value = { parameters [ filter . id ] }
52- defaultValue = { filter . defaultValue }
53- />
54- ) }
55- { filter . type === 'boolean' && (
56- < FormSwitch
57- label = { filter . label }
58- checked = { ! ! parameters [ filter . id ] }
59- onCheckedChange = { ( ) => {
60- // TODO
61- } }
62- />
63- ) }
64- </ FormField >
65- ) ) }
66- </ FormSection >
67- ) ) }
68- </ >
117+ < div className = "flex flex-row space-x-2" >
118+ < div className = "flex w-1/3 flex-col" >
119+ { current . map ( ( { filter, parameters } ) => (
120+ < FormSection key = { filter . id } label = { filter . label } className = "p-4" >
121+ { filter . parameters . map ( ( field ) => (
122+ < FormField key = { field . id } >
123+ { field . type === 'string' && (
124+ < FormSelect
125+ label = { field . label }
126+ className = ""
127+ selectedItemId = { parameters [ field . id ] as string }
128+ selectedItemLabel = { parameters [ field . id ] as string }
129+ defaultItemId = { field . defaultValue }
130+ defaultItemLabel = { field . defaultValue }
131+ items = { field . allowedValues . map ( ( value ) => ( {
132+ id : value ,
133+ label : value ,
134+ value,
135+ } ) ) }
136+ onSelect = { ( value ) => {
137+ setFilterParamValue ( filter . id , field . id , value || '' )
138+ } }
139+ />
140+ ) }
141+ { field . type === 'number' && (
142+ < FormSlider
143+ className = "w-full"
144+ label = { field . label }
145+ value = { getValidNumber (
146+ parameters [ field . id ] ,
147+ field . minValue ,
148+ field . maxValue ,
149+ field . defaultValue
150+ ) }
151+ defaultValue = { field . defaultValue }
152+ minValue = { field . minValue }
153+ maxValue = { field . maxValue }
154+ onChange = { ( newValue ) => {
155+ const value = getValidNumber (
156+ newValue ,
157+ field . minValue ,
158+ field . maxValue ,
159+ field . defaultValue
160+ )
161+ setFilterParamValue ( filter . id , field . id , value || '' )
162+ } }
163+ />
164+ ) }
165+ { field . type === 'boolean' && (
166+ < FormSwitch
167+ label = { field . label }
168+ checked = { ! ! parameters [ field . id ] }
169+ onCheckedChange = { ( checked ) => {
170+ setFilterParamValue ( filter . id , field . id , checked || false )
171+ } }
172+ />
173+ ) }
174+ </ FormField >
175+ ) ) }
176+ </ FormSection >
177+ ) ) }
178+ </ div >
179+ < div className = "flex w-2/3 flex-col" >
180+ { output ? < img src = { output } > </ img > : null }
181+ </ div >
182+ </ div >
69183 )
70184}
0 commit comments