"""Portrait-3D — єдиний Gradio ZeroGPU Space (сценарій C, див. ../docs/01). Чому C замість split (A): щоб списувалась квота ВІДВІДУВАЧА, запит має нести його HF- ідентичність. HF впорскує її лише у НАТИВНИЙ Gradio-виклик, не в наш сирий fetch. Тож: three.js-в'ювер живе в ' ) # міст батька: приймає паси з iframe → приховані inputs → клік прихованого тригера BRIDGE = """ """ # ⚠️ НЕ visible=False — у Gradio 5 такі компоненти не потрапляють у DOM, і міст їх не знайде. # Рендеримо нормально, ховаємо CSS-ом → лишаються в DOM, подія тригера все одно спрацьовує. _HIDE = ("#h_normal,#h_depth,#h_beauty,#h_params,#h_seg,#h_result,#h_trigger" "{display:none!important} footer{display:none!important} " ".gradio-container{max-width:100%!important;padding:0!important}") # Gradio 6.0 переніс head/css із Blocks() у launch(). Передаємо туди, куди треба за версією # (HF пиниться на 5.x, Mac-dev = 6.x) — інакше на 6.x міст (head) мовчки не застосується. _GR_MAJOR = int(gr.__version__.split(".")[0]) _HEADCSS = {"head": BRIDGE, "css": _HIDE} with gr.Blocks(title="Portrait-3D", **({} if _GR_MAJOR >= 6 else _HEADCSS)) as demo: gr.HTML(IFRAME) h_normal = gr.Textbox(elem_id="h_normal") h_depth = gr.Textbox(elem_id="h_depth") h_beauty = gr.Textbox(elem_id="h_beauty") h_params = gr.Textbox(elem_id="h_params") h_seg = gr.Textbox(elem_id="h_seg") h_result = gr.Textbox(elem_id="h_result") h_trigger = gr.Button(elem_id="h_trigger") # порядок ВІДПОВІДАЄ data-масиву фронту: [normal, depth, beauty, params, seg] h_trigger.click(portrait, [h_normal, h_depth, h_beauty, h_params, h_seg], h_result, api_name="portrait") # результат → назад у iframe (js-only подія) h_result.change( None, h_result, None, js="(v)=>{ if(!v) return; var f=document.querySelector('#pv'); " "if(f&&f.contentWindow) f.contentWindow.postMessage({type:'portrait-result', payload: JSON.parse(v)}, '*'); }", ) demo.queue(max_size=8).launch(**(_HEADCSS if _GR_MAJOR >= 6 else {}))