diff --git a/demo/blocks_scroll/run.py b/demo/blocks_scroll/run.py new file mode 100644 index 0000000000..2b2194dfb8 --- /dev/null +++ b/demo/blocks_scroll/run.py @@ -0,0 +1,24 @@ +import gradio as gr + + +demo = gr.Blocks() + +with demo: + inp = gr.Textbox(placeholder="Enter text.") + scroll_btn = gr.Button("Scroll") + no_scroll_btn = gr.Button("No Scroll") + big_block = gr.HTML(""" +
+ """) + out = gr.Textbox() + + scroll_btn.click(lambda x: x, + inputs=inp, + outputs=out, + scroll_to_output=True) + no_scroll_btn.click(lambda x: x, + inputs=inp, + outputs=out) + +if __name__ == "__main__": + demo.launch() \ No newline at end of file diff --git a/gradio/blocks.py b/gradio/blocks.py index 2318ef7ebc..41cebbf00f 100644 --- a/gradio/blocks.py +++ b/gradio/blocks.py @@ -85,6 +85,8 @@ class Block: outputs: Optional[Component | List[Component]], preprocess: bool = True, postprocess: bool = True, + scroll_to_output: bool = False, + show_progress: bool = True, api_name: Optional[AnyStr] = None, js: Optional[str] = False, no_target: bool = False, @@ -100,6 +102,8 @@ class Block: outputs: output list preprocess: whether to run the preprocess methods of components postprocess: whether to run the postprocess methods of components + scroll_to_output: whether to scroll to output of dependency on trigger + show_progress: whether to show progress animation while running. api_name: Defining this parameter exposes the endpoint in the api docs js: Optional frontend js method to run before running 'fn'. Input arguments for js method are values of 'inputs' and 'outputs', return should be a list of values for output components no_target: if True, sets "targets" to [], used for Blocks "load" event @@ -129,6 +133,8 @@ class Block: else None, "queue": queue, "api_name": api_name, + "scroll_to_output": scroll_to_output, + "show_progress": show_progress, } if api_name is not None: dependency["documentation"] = [ diff --git a/gradio/events.py b/gradio/events.py index a9199ca738..456d3df12f 100644 --- a/gradio/events.py +++ b/gradio/events.py @@ -14,8 +14,10 @@ class Changeable(Block): fn: Callable, inputs: List[Component], outputs: List[Component], - status_tracker: Optional[StatusTracker] = None, api_name: AnyStr = None, + status_tracker: Optional[StatusTracker] = None, + scroll_to_output: bool = False, + show_progress: bool = True, queue: Optional[bool] = None, _js: Optional[str] = None, _preprocess: bool = True, @@ -26,12 +28,14 @@ class Changeable(Block): or uploads an image) Parameters: - fn (Callable): Callable function - inputs (List[Component]): List of inputs - outputs (List[Component]): List of outputs - status_tracker (StatusTracker): StatusTracker to visualize function progress - api_name (str): Defining this parameter exposes the endpoint in the api docs - _js (str): Optional frontend js method to run before running 'fn'. Input arguments for js method are values of input and outputs components, return should be a list of values for output component. + fn: Callable function + inputs: List of inputs + outputs: List of outputs + api_name: Defining this parameter exposes the endpoint in the api docs + status_tracker: StatusTracker to visualize function progress + scroll_to_output: If True, will scroll to output component on completion + show_progress: If True, will show progress animation while pending + _js: Optional frontend js method to run before running 'fn'. Input arguments for js method are values of input and outputs components, return should be a list of values for output component. Returns: None """ self.set_event_trigger( @@ -39,8 +43,10 @@ class Changeable(Block): fn, inputs, outputs, - status_tracker=status_tracker, api_name=api_name, + status_tracker=status_tracker, + scroll_to_output=scroll_to_output, + show_progress=show_progress, js=_js, preprocess=_preprocess, postprocess=_postprocess, @@ -54,8 +60,10 @@ class Clickable(Block): fn: Callable, inputs: List[Component], outputs: List[Component], - status_tracker: Optional[StatusTracker] = None, api_name: AnyStr = None, + status_tracker: Optional[StatusTracker] = None, + scroll_to_output: bool = False, + show_progress: bool = True, queue=None, _js: Optional[str] = None, _preprocess: bool = True, @@ -68,8 +76,10 @@ class Clickable(Block): fn: Callable function inputs: List of inputs outputs: List of outputs - status_tracker: StatusTracker to visualize function progress api_name: Defining this parameter exposes the endpoint in the api docs + status_tracker: StatusTracker to visualize function progress + scroll_to_output: If True, will scroll to output component on completion + show_progress: If True, will show progress animation while pending _js: Optional frontend js method to run before running 'fn'. Input arguments for js method are values of 'inputs' and 'outputs', return should be a list of values for output components. _preprocess: If False, will not run preprocessing of component data before running 'fn'. _postprocess: If False, will not run postprocessing of component data before returning 'fn' output. @@ -80,8 +90,10 @@ class Clickable(Block): fn, inputs, outputs, - status_tracker=status_tracker, api_name=api_name, + status_tracker=status_tracker, + scroll_to_output=scroll_to_output, + show_progress=show_progress, queue=queue, js=_js, preprocess=_preprocess, @@ -95,8 +107,10 @@ class Submittable(Block): fn: Callable, inputs: List[Component], outputs: List[Component], - status_tracker: Optional[StatusTracker] = None, api_name: AnyStr = None, + status_tracker: Optional[StatusTracker] = None, + scroll_to_output: bool = False, + show_progress: bool = True, queue: Optional[bool] = None, _js: Optional[str] = None, _preprocess: bool = True, @@ -109,8 +123,10 @@ class Submittable(Block): fn: Callable function inputs: List of inputs outputs: List of outputs - status_tracker: StatusTracker to visualize function progress api_name: Defining this parameter exposes the endpoint in the api docs + status_tracker: StatusTracker to visualize function progress + scroll_to_output: If True, will scroll to output component on completion + show_progress: If True, will show progress animation while pending _js: Optional frontend js method to run before running 'fn'. Input arguments for js method are values of 'inputs' and 'outputs', return should be a list of values for output components. Returns: None """ @@ -121,6 +137,8 @@ class Submittable(Block): outputs, status_tracker=status_tracker, api_name=api_name, + scroll_to_output=scroll_to_output, + show_progress=show_progress, js=_js, preprocess=_preprocess, postprocess=_postprocess, @@ -313,6 +331,7 @@ class Streamable(Block): inputs: List[Component], outputs: List[Component], api_name: AnyStr = None, + show_progress: bool = False, queue: Optional[bool] = None, _js: Optional[str] = None, _preprocess: bool = True, @@ -341,4 +360,5 @@ class Streamable(Block): preprocess=_preprocess, postprocess=_postprocess, queue=queue, + show_progress=False, ) diff --git a/gradio/interface.py b/gradio/interface.py index 623ba1c85c..b709309757 100644 --- a/gradio/interface.py +++ b/gradio/interface.py @@ -545,6 +545,7 @@ class Interface(Blocks): self.input_components, self.output_components, api_name="predict", + scroll_to_output=True, status_tracker=status_tracker, ) clear_btn.click( diff --git a/ui/packages/app/src/Blocks.svelte b/ui/packages/app/src/Blocks.svelte index 85964e97ec..9c94f57546 100644 --- a/ui/packages/app/src/Blocks.svelte +++ b/ui/packages/app/src/Blocks.svelte @@ -3,7 +3,7 @@ import { onMount } from "svelte"; import { component_map } from "./components/directory"; import { loading_status } from "./stores"; - import type { LoadingStatus } from "./stores"; + import type { LoadingStatus } from "./components/StatusTracker/types"; import { _ } from "svelte-i18n"; import { setupi18n } from "./i18n"; @@ -42,6 +42,8 @@ outputs: Array; backend_fn: boolean; js: string | null; + scroll_to_output: boolean; + show_progress: boolean; frontend_fn?: Function; status_tracker: number | null; status?: string; @@ -62,6 +64,7 @@ export let analytics_enabled: boolean = false; export let target: HTMLElement; export let css: string; + export let is_space: boolean; export let id: number = 0; let rootNode: Component = { id: layout.id, type: "column", props: {} }; @@ -326,14 +329,16 @@ loading_status.register(i, v.inputs, v.outputs); }); - function set_status( - statuses: Record> - ) { + function set_status(statuses: Record) { if (window.__gradio_mode__ === "website") { return; } for (const id in statuses) { - set_prop(instance_map[id], "loading_status", statuses[id]); + let loading_status = statuses[id]; + let dependency = dependencies[loading_status.fn_index]; + loading_status.scroll_to_output = dependency.scroll_to_output; + loading_status.visible = dependency.show_progress; + set_prop(instance_map[id], "loading_status", loading_status); } const inputs_to_update = loading_status.get_inputs_to_update(); for (const [id, pending_status] of inputs_to_update) { diff --git a/ui/packages/app/src/Render.svelte b/ui/packages/app/src/Render.svelte index c32c8b0dd7..587d380745 100644 --- a/ui/packages/app/src/Render.svelte +++ b/ui/packages/app/src/Render.svelte @@ -86,7 +86,7 @@ } } - children = + $: children = children && children.filter((v) => instance_map[v.id].type !== "statustracker"); diff --git a/ui/packages/app/src/components/StatusTracker/StatusTracker.svelte b/ui/packages/app/src/components/StatusTracker/StatusTracker.svelte index 712e851d61..dbd20091d1 100644 --- a/ui/packages/app/src/components/StatusTracker/StatusTracker.svelte +++ b/ui/packages/app/src/components/StatusTracker/StatusTracker.svelte @@ -44,6 +44,7 @@ export let eta: number | null = null; export let queue_position: number | null; export let status: "complete" | "pending" | "error"; + export let scroll_to_output: boolean = false; export let timer: boolean = true; let el: HTMLDivElement; @@ -101,6 +102,7 @@ } $: el && + scroll_to_output && (status === "pending" || status === "complete") && scroll_into_view(el); diff --git a/ui/packages/app/src/components/StatusTracker/types.ts b/ui/packages/app/src/components/StatusTracker/types.ts index 4bf8592d39..7fae5a0e23 100644 --- a/ui/packages/app/src/components/StatusTracker/types.ts +++ b/ui/packages/app/src/components/StatusTracker/types.ts @@ -2,4 +2,7 @@ export interface LoadingStatus { eta: number; queue_position: number; status: "pending" | "error" | "complete"; + scroll_to_output: boolean; + visible: boolean; + fn_index: number; } diff --git a/ui/packages/app/src/stores.ts b/ui/packages/app/src/stores.ts index 8e7449ade3..d75471d8b5 100644 --- a/ui/packages/app/src/stores.ts +++ b/ui/packages/app/src/stores.ts @@ -4,6 +4,7 @@ export interface LoadingStatus { eta: number | null; status: "pending" | "error" | "complete"; queue_position: number | null; + fn_index: number; outputs: Array; } @@ -19,7 +20,7 @@ function create_loading_status_store() { const fn_status: Array = []; function update( - fn_index: number, + fn_index: LoadingStatus["fn_index"], status: LoadingStatus["status"], position: LoadingStatus["queue_position"], eta: LoadingStatus["eta"] @@ -82,7 +83,8 @@ function create_loading_status_store() { outputs[id] = { queue_position, eta: eta || outputs[id]?.eta, - status + status, + fn_index }; }); diff --git a/ui/packages/atoms/src/Block.svelte b/ui/packages/atoms/src/Block.svelte index 4dddf67ce8..d05830b590 100644 --- a/ui/packages/atoms/src/Block.svelte +++ b/ui/packages/atoms/src/Block.svelte @@ -73,7 +73,7 @@ this={tag} data-testid={test_id} id={elem_id} - class="overflow-hidden {styles[variant]} {rounded + class="relative w-full overflow-hidden {styles[variant]} {rounded ? styles[color] : ''} {form_class} {classes} {rounded_style}" class:gr-panel={padding}