elismasilva commited on
Commit
185559e
·
verified ·
1 Parent(s): 51bac51

Upload folder using huggingface_hub

Browse files
README.md CHANGED
@@ -10,7 +10,7 @@ app_file: space.py
10
  ---
11
 
12
  # `gradio_propertysheet`
13
- <img alt="Static Badge" src="https://img.shields.io/badge/version%20-%200.0.15%20-%20blue"> <a href="https://huggingface.co/spaces/elismasilva/gradio_propertysheet"><img src="https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Demo-blue"></a><p><span>💻 <a href='https://github.com/DEVAIEXP/gradio_component_propertysheet'>Component GitHub Code</a></span></p>
14
 
15
  The **PropertySheet** component for Gradio allows you to automatically generate a complete and interactive settings panel from a standard Python `dataclass`. It's designed to bring the power of IDE-like property editors directly into your Gradio applications.
16
 
@@ -57,6 +57,45 @@ from gradio_htmlinjector import HTMLInjector
57
 
58
  # --- 1. Dataclass Definitions (unchanged) ---
59
  @dataclass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  class APISettings:
61
  api_key: str = field(
62
  default="ab123cd45ef67890ghij123klmno456p",
@@ -112,7 +151,8 @@ class ModelSettings:
112
  metadata={
113
  "component": "dropdown",
114
  "label": "Base Model",
115
- "help": "Select the base diffusion model."
 
116
  }
117
  )
118
  custom_model_path: str = field(
@@ -364,6 +404,8 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="PropertySheet Demos") as demo:
364
  gr.Markdown(
365
  "An example of using the `PropertySheet` component as a traditional sidebar for settings."
366
  )
 
 
367
  render_state = gr.State(value=initial_render_config)
368
  env_state = gr.State(value=initial_env_config)
369
  sidebar_visible = gr.State(False)
@@ -373,6 +415,7 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="PropertySheet Demos") as demo:
373
  with gr.Row():
374
  output_render_json = gr.JSON(label="Live Render State")
375
  output_env_json = gr.JSON(label="Live Environment State")
 
376
  with gr.Column(scale=1):
377
  render_sheet = PropertySheet(
378
  value=initial_render_config,
@@ -393,14 +436,22 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="PropertySheet Demos") as demo:
393
  interactive=True,
394
  root_properties_first=False
395
  )
 
 
 
 
 
 
 
396
 
397
- def change_visibility(is_visible, render_cfg, env_cfg):
398
  new_visibility = not is_visible
399
  button_text = "Hide Settings" if new_visibility else "Show Settings"
400
  return (
401
  new_visibility,
402
  gr.update(visible=new_visibility, value=render_cfg),
403
  gr.update(visible=new_visibility, value=env_cfg),
 
404
  gr.update(value=button_text),
405
  )
406
 
@@ -421,10 +472,11 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="PropertySheet Demos") as demo:
421
  return updated_config, asdict(updated_config), current_state
422
 
423
  generate.click(
424
- fn=change_visibility,
425
- inputs=[sidebar_visible, render_state, env_state],
426
- outputs=[sidebar_visible, render_sheet, environment_sheet, generate],
427
  )
 
428
  render_sheet.change(
429
  fn=handle_render_change,
430
  inputs=[render_sheet, render_state],
@@ -456,12 +508,26 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="PropertySheet Demos") as demo:
456
  inputs=[environment_sheet, env_state],
457
  outputs=[environment_sheet, output_env_json, env_state],
458
  )
 
 
 
 
459
 
 
 
 
 
 
 
 
 
 
 
460
 
461
  demo.load(
462
- fn=lambda r_cfg, e_cfg: (asdict(r_cfg), asdict(e_cfg)),
463
- inputs=[render_state, env_state],
464
- outputs=[output_render_json, output_env_json],
465
  )
466
 
467
  with gr.TabItem("Flyout Popup Demo"):
 
10
  ---
11
 
12
  # `gradio_propertysheet`
13
+ <img alt="Static Badge" src="https://img.shields.io/badge/version%20-%200.0.16%20-%20blue"> <a href="https://huggingface.co/spaces/elismasilva/gradio_propertysheet"><img src="https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Demo-blue"></a><p><span>💻 <a href='https://github.com/DEVAIEXP/gradio_component_propertysheet'>Component GitHub Code</a></span></p>
14
 
15
  The **PropertySheet** component for Gradio allows you to automatically generate a complete and interactive settings panel from a standard Python `dataclass`. It's designed to bring the power of IDE-like property editors directly into your Gradio applications.
16
 
 
57
 
58
  # --- 1. Dataclass Definitions (unchanged) ---
59
  @dataclass
60
+ class EffectBase:
61
+ """Classe base com configurações de efeito comuns."""
62
+ strength: float = field(
63
+ default=0.5,
64
+ metadata={
65
+ "component": "slider",
66
+ "label": "Effect Strength",
67
+ "minimum": 0.0,
68
+ "maximum": 1.0,
69
+ "step": 0.01,
70
+ # Esta regra depende do 'is_active' da classe filha
71
+ "interactive_if": {"field": "is_active", "value": True}
72
+ }
73
+ )
74
+
75
+ @dataclass
76
+ class EffectSettings(EffectBase):
77
+ """Classe filha que adiciona o controle de ativação."""
78
+ is_active: bool = field(
79
+ default=True,
80
+ metadata={"label": "Enable Effect"}
81
+ )
82
+
83
+ @dataclass
84
+ class EffectsConfig:
85
+ """Dataclass principal que contém múltiplos efeitos com campos de controle de mesmo nome."""
86
+ blur_effect: EffectSettings = field(
87
+ default_factory=EffectSettings,
88
+ metadata={"label": "Blur Effect"}
89
+ )
90
+ sharpen_effect: EffectSettings = field(
91
+ default_factory=EffectSettings,
92
+ metadata={"label": "Sharpen Effect"}
93
+ )
94
+ vignette_effect: EffectSettings = field(
95
+ default_factory=EffectSettings,
96
+ metadata={"label": "Vignette Effect"}
97
+ )
98
+ @dataclass
99
  class APISettings:
100
  api_key: str = field(
101
  default="ab123cd45ef67890ghij123klmno456p",
 
151
  metadata={
152
  "component": "dropdown",
153
  "label": "Base Model",
154
+ "help": "Select the base diffusion model.",
155
+ "visible": True #change here to test visibility
156
  }
157
  )
158
  custom_model_path: str = field(
 
404
  gr.Markdown(
405
  "An example of using the `PropertySheet` component as a traditional sidebar for settings."
406
  )
407
+ initial_effects_config = EffectsConfig()
408
+ effects_state = gr.State(value=initial_effects_config)
409
  render_state = gr.State(value=initial_render_config)
410
  env_state = gr.State(value=initial_env_config)
411
  sidebar_visible = gr.State(False)
 
415
  with gr.Row():
416
  output_render_json = gr.JSON(label="Live Render State")
417
  output_env_json = gr.JSON(label="Live Environment State")
418
+ output_effects_json = gr.JSON(label="Live Effects State")
419
  with gr.Column(scale=1):
420
  render_sheet = PropertySheet(
421
  value=initial_render_config,
 
436
  interactive=True,
437
  root_properties_first=False
438
  )
439
+ effects_sheet = PropertySheet(
440
+ value=initial_effects_config,
441
+ label="Post-Processing Effects",
442
+ width=400,
443
+ visible=False,
444
+ interactive=True
445
+ )
446
 
447
+ def change_visibility(is_visible, render_cfg, env_cfg, effects_cfg):
448
  new_visibility = not is_visible
449
  button_text = "Hide Settings" if new_visibility else "Show Settings"
450
  return (
451
  new_visibility,
452
  gr.update(visible=new_visibility, value=render_cfg),
453
  gr.update(visible=new_visibility, value=env_cfg),
454
+ gr.update(visible=new_visibility, value=effects_cfg),
455
  gr.update(value=button_text),
456
  )
457
 
 
472
  return updated_config, asdict(updated_config), current_state
473
 
474
  generate.click(
475
+ fn=change_visibility,
476
+ inputs=[sidebar_visible, render_state, env_state, effects_state],
477
+ outputs=[sidebar_visible, render_sheet, environment_sheet, effects_sheet, generate],
478
  )
479
+
480
  render_sheet.change(
481
  fn=handle_render_change,
482
  inputs=[render_sheet, render_state],
 
508
  inputs=[environment_sheet, env_state],
509
  outputs=[environment_sheet, output_env_json, env_state],
510
  )
511
+ def handle_effects_change(updated_config: EffectsConfig, current_state: EffectsConfig):
512
+ if updated_config is None:
513
+ return current_state, asdict(current_state), current_state
514
+ return updated_config, asdict(updated_config), updated_config
515
 
516
+ effects_sheet.change(
517
+ fn=handle_effects_change,
518
+ inputs=[effects_sheet, effects_state],
519
+ outputs=[effects_sheet, output_effects_json, effects_state]
520
+ )
521
+ effects_sheet.undo(
522
+ fn=handle_effects_change,
523
+ inputs=[effects_sheet, effects_state],
524
+ outputs=[effects_sheet, output_effects_json, effects_state]
525
+ )
526
 
527
  demo.load(
528
+ fn=lambda r_cfg, e_cfg, ef_cfg: (asdict(r_cfg), asdict(e_cfg), asdict(ef_cfg)),
529
+ inputs=[render_state, env_state, effects_state],
530
+ outputs=[output_render_json, output_env_json, output_effects_json],
531
  )
532
 
533
  with gr.TabItem("Flyout Popup Demo"):
app.py CHANGED
@@ -9,6 +9,45 @@ from gradio_htmlinjector import HTMLInjector
9
 
10
  # --- 1. Dataclass Definitions (unchanged) ---
11
  @dataclass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  class APISettings:
13
  api_key: str = field(
14
  default="ab123cd45ef67890ghij123klmno456p",
@@ -139,6 +178,7 @@ class SamplingSettings:
139
  "minimum": 0.0,
140
  "maximum": 1.0,
141
  "step": 0.01,
 
142
  "interactive_if": {"field": "enable_advanced", "value": True},
143
  "help": "An example of an advanced setting that is only visible when the corresponding checkbox is enabled."
144
  },
@@ -151,6 +191,7 @@ class SamplingSettings:
151
  "minimum": 0.1,
152
  "maximum": 2.0,
153
  "step": 0.1,
 
154
  "help": "Controls the randomness of the sampling process. A value of 1.0 is standard. Higher values increase diversity at the risk of artifacts."
155
  }
156
  )
@@ -317,6 +358,8 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="PropertySheet Demos") as demo:
317
  gr.Markdown(
318
  "An example of using the `PropertySheet` component as a traditional sidebar for settings."
319
  )
 
 
320
  render_state = gr.State(value=initial_render_config)
321
  env_state = gr.State(value=initial_env_config)
322
  sidebar_visible = gr.State(False)
@@ -326,6 +369,7 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="PropertySheet Demos") as demo:
326
  with gr.Row():
327
  output_render_json = gr.JSON(label="Live Render State")
328
  output_env_json = gr.JSON(label="Live Environment State")
 
329
  with gr.Column(scale=1):
330
  render_sheet = PropertySheet(
331
  value=initial_render_config,
@@ -346,14 +390,22 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="PropertySheet Demos") as demo:
346
  interactive=True,
347
  root_properties_first=False
348
  )
 
 
 
 
 
 
 
349
 
350
- def change_visibility(is_visible, render_cfg, env_cfg):
351
  new_visibility = not is_visible
352
  button_text = "Hide Settings" if new_visibility else "Show Settings"
353
  return (
354
  new_visibility,
355
  gr.update(visible=new_visibility, value=render_cfg),
356
  gr.update(visible=new_visibility, value=env_cfg),
 
357
  gr.update(value=button_text),
358
  )
359
 
@@ -374,10 +426,11 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="PropertySheet Demos") as demo:
374
  return updated_config, asdict(updated_config), current_state
375
 
376
  generate.click(
377
- fn=change_visibility,
378
- inputs=[sidebar_visible, render_state, env_state],
379
- outputs=[sidebar_visible, render_sheet, environment_sheet, generate],
380
  )
 
381
  render_sheet.change(
382
  fn=handle_render_change,
383
  inputs=[render_sheet, render_state],
@@ -409,12 +462,26 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="PropertySheet Demos") as demo:
409
  inputs=[environment_sheet, env_state],
410
  outputs=[environment_sheet, output_env_json, env_state],
411
  )
 
 
 
 
412
 
 
 
 
 
 
 
 
 
 
 
413
 
414
  demo.load(
415
- fn=lambda r_cfg, e_cfg: (asdict(r_cfg), asdict(e_cfg)),
416
- inputs=[render_state, env_state],
417
- outputs=[output_render_json, output_env_json],
418
  )
419
 
420
  with gr.TabItem("Flyout Popup Demo"):
 
9
 
10
  # --- 1. Dataclass Definitions (unchanged) ---
11
  @dataclass
12
+ class EffectBase:
13
+ """Classe base com configurações de efeito comuns."""
14
+ strength: float = field(
15
+ default=0.5,
16
+ metadata={
17
+ "component": "slider",
18
+ "label": "Effect Strength",
19
+ "minimum": 0.0,
20
+ "maximum": 1.0,
21
+ "step": 0.01,
22
+ # Esta regra depende do 'is_active' da classe filha
23
+ "interactive_if": {"field": "is_active", "value": True}
24
+ }
25
+ )
26
+
27
+ @dataclass
28
+ class EffectSettings(EffectBase):
29
+ """Classe filha que adiciona o controle de ativação."""
30
+ is_active: bool = field(
31
+ default=True,
32
+ metadata={"label": "Enable Effect"}
33
+ )
34
+
35
+ @dataclass
36
+ class EffectsConfig:
37
+ """Dataclass principal que contém múltiplos efeitos com campos de controle de mesmo nome."""
38
+ blur_effect: EffectSettings = field(
39
+ default_factory=EffectSettings,
40
+ metadata={"label": "Blur Effect"}
41
+ )
42
+ sharpen_effect: EffectSettings = field(
43
+ default_factory=EffectSettings,
44
+ metadata={"label": "Sharpen Effect"}
45
+ )
46
+ vignette_effect: EffectSettings = field(
47
+ default_factory=EffectSettings,
48
+ metadata={"label": "Vignette Effect"}
49
+ )
50
+ @dataclass
51
  class APISettings:
52
  api_key: str = field(
53
  default="ab123cd45ef67890ghij123klmno456p",
 
178
  "minimum": 0.0,
179
  "maximum": 1.0,
180
  "step": 0.01,
181
+ "visible_if": {"field": "enable_advanced", "value": True},
182
  "interactive_if": {"field": "enable_advanced", "value": True},
183
  "help": "An example of an advanced setting that is only visible when the corresponding checkbox is enabled."
184
  },
 
191
  "minimum": 0.1,
192
  "maximum": 2.0,
193
  "step": 0.1,
194
+ "visible_if": {"field": "enable_advanced", "value": True},
195
  "help": "Controls the randomness of the sampling process. A value of 1.0 is standard. Higher values increase diversity at the risk of artifacts."
196
  }
197
  )
 
358
  gr.Markdown(
359
  "An example of using the `PropertySheet` component as a traditional sidebar for settings."
360
  )
361
+ initial_effects_config = EffectsConfig()
362
+ effects_state = gr.State(value=initial_effects_config)
363
  render_state = gr.State(value=initial_render_config)
364
  env_state = gr.State(value=initial_env_config)
365
  sidebar_visible = gr.State(False)
 
369
  with gr.Row():
370
  output_render_json = gr.JSON(label="Live Render State")
371
  output_env_json = gr.JSON(label="Live Environment State")
372
+ output_effects_json = gr.JSON(label="Live Effects State")
373
  with gr.Column(scale=1):
374
  render_sheet = PropertySheet(
375
  value=initial_render_config,
 
390
  interactive=True,
391
  root_properties_first=False
392
  )
393
+ effects_sheet = PropertySheet(
394
+ value=initial_effects_config,
395
+ label="Post-Processing Effects",
396
+ width=400,
397
+ visible=False,
398
+ interactive=True
399
+ )
400
 
401
+ def change_visibility(is_visible, render_cfg, env_cfg, effects_cfg):
402
  new_visibility = not is_visible
403
  button_text = "Hide Settings" if new_visibility else "Show Settings"
404
  return (
405
  new_visibility,
406
  gr.update(visible=new_visibility, value=render_cfg),
407
  gr.update(visible=new_visibility, value=env_cfg),
408
+ gr.update(visible=new_visibility, value=effects_cfg),
409
  gr.update(value=button_text),
410
  )
411
 
 
426
  return updated_config, asdict(updated_config), current_state
427
 
428
  generate.click(
429
+ fn=change_visibility,
430
+ inputs=[sidebar_visible, render_state, env_state, effects_state],
431
+ outputs=[sidebar_visible, render_sheet, environment_sheet, effects_sheet, generate],
432
  )
433
+
434
  render_sheet.change(
435
  fn=handle_render_change,
436
  inputs=[render_sheet, render_state],
 
462
  inputs=[environment_sheet, env_state],
463
  outputs=[environment_sheet, output_env_json, env_state],
464
  )
465
+ def handle_effects_change(updated_config: EffectsConfig, current_state: EffectsConfig):
466
+ if updated_config is None:
467
+ return current_state, asdict(current_state), current_state
468
+ return updated_config, asdict(updated_config), updated_config
469
 
470
+ effects_sheet.change(
471
+ fn=handle_effects_change,
472
+ inputs=[effects_sheet, effects_state],
473
+ outputs=[effects_sheet, output_effects_json, effects_state]
474
+ )
475
+ effects_sheet.undo(
476
+ fn=handle_effects_change,
477
+ inputs=[effects_sheet, effects_state],
478
+ outputs=[effects_sheet, output_effects_json, effects_state]
479
+ )
480
 
481
  demo.load(
482
+ fn=lambda r_cfg, e_cfg, ef_cfg: (asdict(r_cfg), asdict(e_cfg), asdict(ef_cfg)),
483
+ inputs=[render_state, env_state, effects_state],
484
+ outputs=[output_render_json, output_env_json, output_effects_json],
485
  )
486
 
487
  with gr.TabItem("Flyout Popup Demo"):
space.py CHANGED
@@ -49,6 +49,45 @@ from gradio_htmlinjector import HTMLInjector
49
 
50
  # --- 1. Dataclass Definitions (unchanged) ---
51
  @dataclass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  class APISettings:
53
  api_key: str = field(
54
  default="ab123cd45ef67890ghij123klmno456p",
@@ -104,7 +143,8 @@ class ModelSettings:
104
  metadata={
105
  "component": "dropdown",
106
  "label": "Base Model",
107
- "help": "Select the base diffusion model."
 
108
  }
109
  )
110
  custom_model_path: str = field(
@@ -356,6 +396,8 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="PropertySheet Demos") as demo:
356
  gr.Markdown(
357
  "An example of using the `PropertySheet` component as a traditional sidebar for settings."
358
  )
 
 
359
  render_state = gr.State(value=initial_render_config)
360
  env_state = gr.State(value=initial_env_config)
361
  sidebar_visible = gr.State(False)
@@ -365,6 +407,7 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="PropertySheet Demos") as demo:
365
  with gr.Row():
366
  output_render_json = gr.JSON(label="Live Render State")
367
  output_env_json = gr.JSON(label="Live Environment State")
 
368
  with gr.Column(scale=1):
369
  render_sheet = PropertySheet(
370
  value=initial_render_config,
@@ -385,14 +428,22 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="PropertySheet Demos") as demo:
385
  interactive=True,
386
  root_properties_first=False
387
  )
 
 
 
 
 
 
 
388
 
389
- def change_visibility(is_visible, render_cfg, env_cfg):
390
  new_visibility = not is_visible
391
  button_text = "Hide Settings" if new_visibility else "Show Settings"
392
  return (
393
  new_visibility,
394
  gr.update(visible=new_visibility, value=render_cfg),
395
  gr.update(visible=new_visibility, value=env_cfg),
 
396
  gr.update(value=button_text),
397
  )
398
 
@@ -413,10 +464,11 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="PropertySheet Demos") as demo:
413
  return updated_config, asdict(updated_config), current_state
414
 
415
  generate.click(
416
- fn=change_visibility,
417
- inputs=[sidebar_visible, render_state, env_state],
418
- outputs=[sidebar_visible, render_sheet, environment_sheet, generate],
419
  )
 
420
  render_sheet.change(
421
  fn=handle_render_change,
422
  inputs=[render_sheet, render_state],
@@ -448,12 +500,26 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="PropertySheet Demos") as demo:
448
  inputs=[environment_sheet, env_state],
449
  outputs=[environment_sheet, output_env_json, env_state],
450
  )
 
 
 
 
451
 
 
 
 
 
 
 
 
 
 
 
452
 
453
  demo.load(
454
- fn=lambda r_cfg, e_cfg: (asdict(r_cfg), asdict(e_cfg)),
455
- inputs=[render_state, env_state],
456
- outputs=[output_render_json, output_env_json],
457
  )
458
 
459
  with gr.TabItem("Flyout Popup Demo"):
 
49
 
50
  # --- 1. Dataclass Definitions (unchanged) ---
51
  @dataclass
52
+ class EffectBase:
53
+ \"\"\"Classe base com configurações de efeito comuns.\"\"\"
54
+ strength: float = field(
55
+ default=0.5,
56
+ metadata={
57
+ "component": "slider",
58
+ "label": "Effect Strength",
59
+ "minimum": 0.0,
60
+ "maximum": 1.0,
61
+ "step": 0.01,
62
+ # Esta regra depende do 'is_active' da classe filha
63
+ "interactive_if": {"field": "is_active", "value": True}
64
+ }
65
+ )
66
+
67
+ @dataclass
68
+ class EffectSettings(EffectBase):
69
+ \"\"\"Classe filha que adiciona o controle de ativação.\"\"\"
70
+ is_active: bool = field(
71
+ default=True,
72
+ metadata={"label": "Enable Effect"}
73
+ )
74
+
75
+ @dataclass
76
+ class EffectsConfig:
77
+ \"\"\"Dataclass principal que contém múltiplos efeitos com campos de controle de mesmo nome.\"\"\"
78
+ blur_effect: EffectSettings = field(
79
+ default_factory=EffectSettings,
80
+ metadata={"label": "Blur Effect"}
81
+ )
82
+ sharpen_effect: EffectSettings = field(
83
+ default_factory=EffectSettings,
84
+ metadata={"label": "Sharpen Effect"}
85
+ )
86
+ vignette_effect: EffectSettings = field(
87
+ default_factory=EffectSettings,
88
+ metadata={"label": "Vignette Effect"}
89
+ )
90
+ @dataclass
91
  class APISettings:
92
  api_key: str = field(
93
  default="ab123cd45ef67890ghij123klmno456p",
 
143
  metadata={
144
  "component": "dropdown",
145
  "label": "Base Model",
146
+ "help": "Select the base diffusion model.",
147
+ "visible": True #change here to test visibility
148
  }
149
  )
150
  custom_model_path: str = field(
 
396
  gr.Markdown(
397
  "An example of using the `PropertySheet` component as a traditional sidebar for settings."
398
  )
399
+ initial_effects_config = EffectsConfig()
400
+ effects_state = gr.State(value=initial_effects_config)
401
  render_state = gr.State(value=initial_render_config)
402
  env_state = gr.State(value=initial_env_config)
403
  sidebar_visible = gr.State(False)
 
407
  with gr.Row():
408
  output_render_json = gr.JSON(label="Live Render State")
409
  output_env_json = gr.JSON(label="Live Environment State")
410
+ output_effects_json = gr.JSON(label="Live Effects State")
411
  with gr.Column(scale=1):
412
  render_sheet = PropertySheet(
413
  value=initial_render_config,
 
428
  interactive=True,
429
  root_properties_first=False
430
  )
431
+ effects_sheet = PropertySheet(
432
+ value=initial_effects_config,
433
+ label="Post-Processing Effects",
434
+ width=400,
435
+ visible=False,
436
+ interactive=True
437
+ )
438
 
439
+ def change_visibility(is_visible, render_cfg, env_cfg, effects_cfg):
440
  new_visibility = not is_visible
441
  button_text = "Hide Settings" if new_visibility else "Show Settings"
442
  return (
443
  new_visibility,
444
  gr.update(visible=new_visibility, value=render_cfg),
445
  gr.update(visible=new_visibility, value=env_cfg),
446
+ gr.update(visible=new_visibility, value=effects_cfg),
447
  gr.update(value=button_text),
448
  )
449
 
 
464
  return updated_config, asdict(updated_config), current_state
465
 
466
  generate.click(
467
+ fn=change_visibility,
468
+ inputs=[sidebar_visible, render_state, env_state, effects_state],
469
+ outputs=[sidebar_visible, render_sheet, environment_sheet, effects_sheet, generate],
470
  )
471
+
472
  render_sheet.change(
473
  fn=handle_render_change,
474
  inputs=[render_sheet, render_state],
 
500
  inputs=[environment_sheet, env_state],
501
  outputs=[environment_sheet, output_env_json, env_state],
502
  )
503
+ def handle_effects_change(updated_config: EffectsConfig, current_state: EffectsConfig):
504
+ if updated_config is None:
505
+ return current_state, asdict(current_state), current_state
506
+ return updated_config, asdict(updated_config), updated_config
507
 
508
+ effects_sheet.change(
509
+ fn=handle_effects_change,
510
+ inputs=[effects_sheet, effects_state],
511
+ outputs=[effects_sheet, output_effects_json, effects_state]
512
+ )
513
+ effects_sheet.undo(
514
+ fn=handle_effects_change,
515
+ inputs=[effects_sheet, effects_state],
516
+ outputs=[effects_sheet, output_effects_json, effects_state]
517
+ )
518
 
519
  demo.load(
520
+ fn=lambda r_cfg, e_cfg, ef_cfg: (asdict(r_cfg), asdict(e_cfg), asdict(ef_cfg)),
521
+ inputs=[render_state, env_state, effects_state],
522
+ outputs=[output_render_json, output_env_json, output_effects_json],
523
  )
524
 
525
  with gr.TabItem("Flyout Popup Demo"):
src/README.md CHANGED
@@ -10,7 +10,7 @@ app_file: space.py
10
  ---
11
 
12
  # `gradio_propertysheet`
13
- <img alt="Static Badge" src="https://img.shields.io/badge/version%20-%200.0.15%20-%20blue"> <a href="https://huggingface.co/spaces/elismasilva/gradio_propertysheet"><img src="https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Demo-blue"></a><p><span>💻 <a href='https://github.com/DEVAIEXP/gradio_component_propertysheet'>Component GitHub Code</a></span></p>
14
 
15
  The **PropertySheet** component for Gradio allows you to automatically generate a complete and interactive settings panel from a standard Python `dataclass`. It's designed to bring the power of IDE-like property editors directly into your Gradio applications.
16
 
@@ -57,6 +57,45 @@ from gradio_htmlinjector import HTMLInjector
57
 
58
  # --- 1. Dataclass Definitions (unchanged) ---
59
  @dataclass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  class APISettings:
61
  api_key: str = field(
62
  default="ab123cd45ef67890ghij123klmno456p",
@@ -112,7 +151,8 @@ class ModelSettings:
112
  metadata={
113
  "component": "dropdown",
114
  "label": "Base Model",
115
- "help": "Select the base diffusion model."
 
116
  }
117
  )
118
  custom_model_path: str = field(
@@ -364,6 +404,8 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="PropertySheet Demos") as demo:
364
  gr.Markdown(
365
  "An example of using the `PropertySheet` component as a traditional sidebar for settings."
366
  )
 
 
367
  render_state = gr.State(value=initial_render_config)
368
  env_state = gr.State(value=initial_env_config)
369
  sidebar_visible = gr.State(False)
@@ -373,6 +415,7 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="PropertySheet Demos") as demo:
373
  with gr.Row():
374
  output_render_json = gr.JSON(label="Live Render State")
375
  output_env_json = gr.JSON(label="Live Environment State")
 
376
  with gr.Column(scale=1):
377
  render_sheet = PropertySheet(
378
  value=initial_render_config,
@@ -393,14 +436,22 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="PropertySheet Demos") as demo:
393
  interactive=True,
394
  root_properties_first=False
395
  )
 
 
 
 
 
 
 
396
 
397
- def change_visibility(is_visible, render_cfg, env_cfg):
398
  new_visibility = not is_visible
399
  button_text = "Hide Settings" if new_visibility else "Show Settings"
400
  return (
401
  new_visibility,
402
  gr.update(visible=new_visibility, value=render_cfg),
403
  gr.update(visible=new_visibility, value=env_cfg),
 
404
  gr.update(value=button_text),
405
  )
406
 
@@ -421,10 +472,11 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="PropertySheet Demos") as demo:
421
  return updated_config, asdict(updated_config), current_state
422
 
423
  generate.click(
424
- fn=change_visibility,
425
- inputs=[sidebar_visible, render_state, env_state],
426
- outputs=[sidebar_visible, render_sheet, environment_sheet, generate],
427
  )
 
428
  render_sheet.change(
429
  fn=handle_render_change,
430
  inputs=[render_sheet, render_state],
@@ -456,12 +508,26 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="PropertySheet Demos") as demo:
456
  inputs=[environment_sheet, env_state],
457
  outputs=[environment_sheet, output_env_json, env_state],
458
  )
 
 
 
 
459
 
 
 
 
 
 
 
 
 
 
 
460
 
461
  demo.load(
462
- fn=lambda r_cfg, e_cfg: (asdict(r_cfg), asdict(e_cfg)),
463
- inputs=[render_state, env_state],
464
- outputs=[output_render_json, output_env_json],
465
  )
466
 
467
  with gr.TabItem("Flyout Popup Demo"):
 
10
  ---
11
 
12
  # `gradio_propertysheet`
13
+ <img alt="Static Badge" src="https://img.shields.io/badge/version%20-%200.0.16%20-%20blue"> <a href="https://huggingface.co/spaces/elismasilva/gradio_propertysheet"><img src="https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Demo-blue"></a><p><span>💻 <a href='https://github.com/DEVAIEXP/gradio_component_propertysheet'>Component GitHub Code</a></span></p>
14
 
15
  The **PropertySheet** component for Gradio allows you to automatically generate a complete and interactive settings panel from a standard Python `dataclass`. It's designed to bring the power of IDE-like property editors directly into your Gradio applications.
16
 
 
57
 
58
  # --- 1. Dataclass Definitions (unchanged) ---
59
  @dataclass
60
+ class EffectBase:
61
+ """Classe base com configurações de efeito comuns."""
62
+ strength: float = field(
63
+ default=0.5,
64
+ metadata={
65
+ "component": "slider",
66
+ "label": "Effect Strength",
67
+ "minimum": 0.0,
68
+ "maximum": 1.0,
69
+ "step": 0.01,
70
+ # Esta regra depende do 'is_active' da classe filha
71
+ "interactive_if": {"field": "is_active", "value": True}
72
+ }
73
+ )
74
+
75
+ @dataclass
76
+ class EffectSettings(EffectBase):
77
+ """Classe filha que adiciona o controle de ativação."""
78
+ is_active: bool = field(
79
+ default=True,
80
+ metadata={"label": "Enable Effect"}
81
+ )
82
+
83
+ @dataclass
84
+ class EffectsConfig:
85
+ """Dataclass principal que contém múltiplos efeitos com campos de controle de mesmo nome."""
86
+ blur_effect: EffectSettings = field(
87
+ default_factory=EffectSettings,
88
+ metadata={"label": "Blur Effect"}
89
+ )
90
+ sharpen_effect: EffectSettings = field(
91
+ default_factory=EffectSettings,
92
+ metadata={"label": "Sharpen Effect"}
93
+ )
94
+ vignette_effect: EffectSettings = field(
95
+ default_factory=EffectSettings,
96
+ metadata={"label": "Vignette Effect"}
97
+ )
98
+ @dataclass
99
  class APISettings:
100
  api_key: str = field(
101
  default="ab123cd45ef67890ghij123klmno456p",
 
151
  metadata={
152
  "component": "dropdown",
153
  "label": "Base Model",
154
+ "help": "Select the base diffusion model.",
155
+ "visible": True #change here to test visibility
156
  }
157
  )
158
  custom_model_path: str = field(
 
404
  gr.Markdown(
405
  "An example of using the `PropertySheet` component as a traditional sidebar for settings."
406
  )
407
+ initial_effects_config = EffectsConfig()
408
+ effects_state = gr.State(value=initial_effects_config)
409
  render_state = gr.State(value=initial_render_config)
410
  env_state = gr.State(value=initial_env_config)
411
  sidebar_visible = gr.State(False)
 
415
  with gr.Row():
416
  output_render_json = gr.JSON(label="Live Render State")
417
  output_env_json = gr.JSON(label="Live Environment State")
418
+ output_effects_json = gr.JSON(label="Live Effects State")
419
  with gr.Column(scale=1):
420
  render_sheet = PropertySheet(
421
  value=initial_render_config,
 
436
  interactive=True,
437
  root_properties_first=False
438
  )
439
+ effects_sheet = PropertySheet(
440
+ value=initial_effects_config,
441
+ label="Post-Processing Effects",
442
+ width=400,
443
+ visible=False,
444
+ interactive=True
445
+ )
446
 
447
+ def change_visibility(is_visible, render_cfg, env_cfg, effects_cfg):
448
  new_visibility = not is_visible
449
  button_text = "Hide Settings" if new_visibility else "Show Settings"
450
  return (
451
  new_visibility,
452
  gr.update(visible=new_visibility, value=render_cfg),
453
  gr.update(visible=new_visibility, value=env_cfg),
454
+ gr.update(visible=new_visibility, value=effects_cfg),
455
  gr.update(value=button_text),
456
  )
457
 
 
472
  return updated_config, asdict(updated_config), current_state
473
 
474
  generate.click(
475
+ fn=change_visibility,
476
+ inputs=[sidebar_visible, render_state, env_state, effects_state],
477
+ outputs=[sidebar_visible, render_sheet, environment_sheet, effects_sheet, generate],
478
  )
479
+
480
  render_sheet.change(
481
  fn=handle_render_change,
482
  inputs=[render_sheet, render_state],
 
508
  inputs=[environment_sheet, env_state],
509
  outputs=[environment_sheet, output_env_json, env_state],
510
  )
511
+ def handle_effects_change(updated_config: EffectsConfig, current_state: EffectsConfig):
512
+ if updated_config is None:
513
+ return current_state, asdict(current_state), current_state
514
+ return updated_config, asdict(updated_config), updated_config
515
 
516
+ effects_sheet.change(
517
+ fn=handle_effects_change,
518
+ inputs=[effects_sheet, effects_state],
519
+ outputs=[effects_sheet, output_effects_json, effects_state]
520
+ )
521
+ effects_sheet.undo(
522
+ fn=handle_effects_change,
523
+ inputs=[effects_sheet, effects_state],
524
+ outputs=[effects_sheet, output_effects_json, effects_state]
525
+ )
526
 
527
  demo.load(
528
+ fn=lambda r_cfg, e_cfg, ef_cfg: (asdict(r_cfg), asdict(e_cfg), asdict(ef_cfg)),
529
+ inputs=[render_state, env_state, effects_state],
530
+ outputs=[output_render_json, output_env_json, output_effects_json],
531
  )
532
 
533
  with gr.TabItem("Flyout Popup Demo"):
src/backend/gradio_propertysheet/helpers.py CHANGED
@@ -57,7 +57,7 @@ def extract_prop_metadata(cls: Type, field: dataclasses.Field) -> Dict[str, Any]
57
 
58
  Args:
59
  cls: The dataclass instance containing the field.
60
- field: The dataclasses.Field object to inspect.
61
  Returns:
62
  A dictionary of metadata for the frontend to render a property control.
63
  """
 
57
 
58
  Args:
59
  cls: The dataclass instance containing the field.
60
+ field: The dataclasses.Field object to inspect.
61
  Returns:
62
  A dictionary of metadata for the frontend to render a property control.
63
  """
src/backend/gradio_propertysheet/propertysheet.py CHANGED
@@ -1,13 +1,14 @@
1
  from __future__ import annotations
2
  import copy
3
  import json
 
4
  from typing import Any, Dict, List, get_type_hints
5
  import dataclasses
6
  from gradio.components.base import Component
7
  from gradio_propertysheet.helpers import extract_prop_metadata, infer_type
8
  from gradio_client.documentation import document
9
  from gradio.events import Events, EventListener
10
-
11
  def prop_meta(**kwargs) -> dataclasses.Field:
12
  """
13
  A helper function to create a dataclass field with Gradio-specific metadata.
@@ -151,13 +152,25 @@ class PropertySheet(Component):
151
  reordered_fields = dataclasses.fields(group_type)
152
 
153
  for group_field in reordered_fields:
154
- metadata = extract_prop_metadata(group_obj, group_field)
155
- if "interactive_if" in metadata and "." not in metadata["interactive_if"]["field"]:
156
- relative_field_name = metadata["interactive_if"]["field"]
157
- metadata["interactive_if"]["field"] = f"{field.name}.{relative_field_name}"
 
 
 
 
 
 
 
158
 
159
- metadata["name"] = f"{field.name}.{group_field.name}"
160
- group_props.append(metadata)
 
 
 
 
 
161
 
162
  base_group_name = field.metadata.get("label", field.name.replace("_", " ").title())
163
  unique_group_name = base_group_name
@@ -169,7 +182,8 @@ class PropertySheet(Component):
169
  used_group_names.add(unique_group_name)
170
  json_schema.append({"group_name": unique_group_name, "properties": group_props})
171
  else:
172
- root_properties.append(extract_prop_metadata(current_value, field))
 
173
 
174
  # Process root properties, if any exist
175
  if root_properties:
@@ -185,8 +199,7 @@ class PropertySheet(Component):
185
  if self.root_properties_first:
186
  json_schema.insert(0, root_group)
187
  else:
188
- json_schema.append(root_group)
189
-
190
  return json_schema
191
 
192
  @document()
 
1
  from __future__ import annotations
2
  import copy
3
  import json
4
+ import logging
5
  from typing import Any, Dict, List, get_type_hints
6
  import dataclasses
7
  from gradio.components.base import Component
8
  from gradio_propertysheet.helpers import extract_prop_metadata, infer_type
9
  from gradio_client.documentation import document
10
  from gradio.events import Events, EventListener
11
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - [BACKEND] %(message)s')
12
  def prop_meta(**kwargs) -> dataclasses.Field:
13
  """
14
  A helper function to create a dataclass field with Gradio-specific metadata.
 
152
  reordered_fields = dataclasses.fields(group_type)
153
 
154
  for group_field in reordered_fields:
155
+ original_metadata = group_field.metadata
156
+ prop_data = extract_prop_metadata(group_obj, group_field)
157
+ group_prefix = f"{field.name}."
158
+ prop_data["name"] = f"{group_prefix}{group_field.name}"
159
+
160
+ if "interactive_if" in original_metadata:
161
+ new_condition = copy.deepcopy(original_metadata["interactive_if"])
162
+ if "field" in new_condition:
163
+ base_condition_field = new_condition["field"].split('.')[-1]
164
+ new_condition["field"] = f"{group_prefix}{base_condition_field}"
165
+ prop_data["interactive_if"] = new_condition
166
 
167
+ if "visible_if" in original_metadata:
168
+ new_condition = copy.deepcopy(original_metadata["visible_if"])
169
+ if "field" in new_condition:
170
+ base_condition_field = new_condition["field"].split('.')[-1]
171
+ new_condition["field"] = f"{group_prefix}{base_condition_field}"
172
+ prop_data["visible_if"] = new_condition
173
+ group_props.append(prop_data)
174
 
175
  base_group_name = field.metadata.get("label", field.name.replace("_", " ").title())
176
  unique_group_name = base_group_name
 
182
  used_group_names.add(unique_group_name)
183
  json_schema.append({"group_name": unique_group_name, "properties": group_props})
184
  else:
185
+ metadata = extract_prop_metadata(current_value, field)
186
+ root_properties.append(metadata)
187
 
188
  # Process root properties, if any exist
189
  if root_properties:
 
199
  if self.root_properties_first:
200
  json_schema.insert(0, root_group)
201
  else:
202
+ json_schema.append(root_group)
 
203
  return json_schema
204
 
205
  @document()
src/backend/gradio_propertysheet/templates/component/index.js CHANGED
The diff for this file is too large to render. See raw diff
 
src/demo/app.py CHANGED
@@ -9,6 +9,45 @@ from gradio_htmlinjector import HTMLInjector
9
 
10
  # --- 1. Dataclass Definitions (unchanged) ---
11
  @dataclass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  class APISettings:
13
  api_key: str = field(
14
  default="ab123cd45ef67890ghij123klmno456p",
@@ -139,6 +178,7 @@ class SamplingSettings:
139
  "minimum": 0.0,
140
  "maximum": 1.0,
141
  "step": 0.01,
 
142
  "interactive_if": {"field": "enable_advanced", "value": True},
143
  "help": "An example of an advanced setting that is only visible when the corresponding checkbox is enabled."
144
  },
@@ -151,6 +191,7 @@ class SamplingSettings:
151
  "minimum": 0.1,
152
  "maximum": 2.0,
153
  "step": 0.1,
 
154
  "help": "Controls the randomness of the sampling process. A value of 1.0 is standard. Higher values increase diversity at the risk of artifacts."
155
  }
156
  )
@@ -317,6 +358,8 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="PropertySheet Demos") as demo:
317
  gr.Markdown(
318
  "An example of using the `PropertySheet` component as a traditional sidebar for settings."
319
  )
 
 
320
  render_state = gr.State(value=initial_render_config)
321
  env_state = gr.State(value=initial_env_config)
322
  sidebar_visible = gr.State(False)
@@ -326,6 +369,7 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="PropertySheet Demos") as demo:
326
  with gr.Row():
327
  output_render_json = gr.JSON(label="Live Render State")
328
  output_env_json = gr.JSON(label="Live Environment State")
 
329
  with gr.Column(scale=1):
330
  render_sheet = PropertySheet(
331
  value=initial_render_config,
@@ -346,14 +390,22 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="PropertySheet Demos") as demo:
346
  interactive=True,
347
  root_properties_first=False
348
  )
 
 
 
 
 
 
 
349
 
350
- def change_visibility(is_visible, render_cfg, env_cfg):
351
  new_visibility = not is_visible
352
  button_text = "Hide Settings" if new_visibility else "Show Settings"
353
  return (
354
  new_visibility,
355
  gr.update(visible=new_visibility, value=render_cfg),
356
  gr.update(visible=new_visibility, value=env_cfg),
 
357
  gr.update(value=button_text),
358
  )
359
 
@@ -374,10 +426,11 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="PropertySheet Demos") as demo:
374
  return updated_config, asdict(updated_config), current_state
375
 
376
  generate.click(
377
- fn=change_visibility,
378
- inputs=[sidebar_visible, render_state, env_state],
379
- outputs=[sidebar_visible, render_sheet, environment_sheet, generate],
380
  )
 
381
  render_sheet.change(
382
  fn=handle_render_change,
383
  inputs=[render_sheet, render_state],
@@ -409,12 +462,26 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="PropertySheet Demos") as demo:
409
  inputs=[environment_sheet, env_state],
410
  outputs=[environment_sheet, output_env_json, env_state],
411
  )
 
 
 
 
412
 
 
 
 
 
 
 
 
 
 
 
413
 
414
  demo.load(
415
- fn=lambda r_cfg, e_cfg: (asdict(r_cfg), asdict(e_cfg)),
416
- inputs=[render_state, env_state],
417
- outputs=[output_render_json, output_env_json],
418
  )
419
 
420
  with gr.TabItem("Flyout Popup Demo"):
 
9
 
10
  # --- 1. Dataclass Definitions (unchanged) ---
11
  @dataclass
12
+ class EffectBase:
13
+ """Classe base com configurações de efeito comuns."""
14
+ strength: float = field(
15
+ default=0.5,
16
+ metadata={
17
+ "component": "slider",
18
+ "label": "Effect Strength",
19
+ "minimum": 0.0,
20
+ "maximum": 1.0,
21
+ "step": 0.01,
22
+ # Esta regra depende do 'is_active' da classe filha
23
+ "interactive_if": {"field": "is_active", "value": True}
24
+ }
25
+ )
26
+
27
+ @dataclass
28
+ class EffectSettings(EffectBase):
29
+ """Classe filha que adiciona o controle de ativação."""
30
+ is_active: bool = field(
31
+ default=True,
32
+ metadata={"label": "Enable Effect"}
33
+ )
34
+
35
+ @dataclass
36
+ class EffectsConfig:
37
+ """Dataclass principal que contém múltiplos efeitos com campos de controle de mesmo nome."""
38
+ blur_effect: EffectSettings = field(
39
+ default_factory=EffectSettings,
40
+ metadata={"label": "Blur Effect"}
41
+ )
42
+ sharpen_effect: EffectSettings = field(
43
+ default_factory=EffectSettings,
44
+ metadata={"label": "Sharpen Effect"}
45
+ )
46
+ vignette_effect: EffectSettings = field(
47
+ default_factory=EffectSettings,
48
+ metadata={"label": "Vignette Effect"}
49
+ )
50
+ @dataclass
51
  class APISettings:
52
  api_key: str = field(
53
  default="ab123cd45ef67890ghij123klmno456p",
 
178
  "minimum": 0.0,
179
  "maximum": 1.0,
180
  "step": 0.01,
181
+ "visible_if": {"field": "enable_advanced", "value": True},
182
  "interactive_if": {"field": "enable_advanced", "value": True},
183
  "help": "An example of an advanced setting that is only visible when the corresponding checkbox is enabled."
184
  },
 
191
  "minimum": 0.1,
192
  "maximum": 2.0,
193
  "step": 0.1,
194
+ "visible_if": {"field": "enable_advanced", "value": True},
195
  "help": "Controls the randomness of the sampling process. A value of 1.0 is standard. Higher values increase diversity at the risk of artifacts."
196
  }
197
  )
 
358
  gr.Markdown(
359
  "An example of using the `PropertySheet` component as a traditional sidebar for settings."
360
  )
361
+ initial_effects_config = EffectsConfig()
362
+ effects_state = gr.State(value=initial_effects_config)
363
  render_state = gr.State(value=initial_render_config)
364
  env_state = gr.State(value=initial_env_config)
365
  sidebar_visible = gr.State(False)
 
369
  with gr.Row():
370
  output_render_json = gr.JSON(label="Live Render State")
371
  output_env_json = gr.JSON(label="Live Environment State")
372
+ output_effects_json = gr.JSON(label="Live Effects State")
373
  with gr.Column(scale=1):
374
  render_sheet = PropertySheet(
375
  value=initial_render_config,
 
390
  interactive=True,
391
  root_properties_first=False
392
  )
393
+ effects_sheet = PropertySheet(
394
+ value=initial_effects_config,
395
+ label="Post-Processing Effects",
396
+ width=400,
397
+ visible=False,
398
+ interactive=True
399
+ )
400
 
401
+ def change_visibility(is_visible, render_cfg, env_cfg, effects_cfg):
402
  new_visibility = not is_visible
403
  button_text = "Hide Settings" if new_visibility else "Show Settings"
404
  return (
405
  new_visibility,
406
  gr.update(visible=new_visibility, value=render_cfg),
407
  gr.update(visible=new_visibility, value=env_cfg),
408
+ gr.update(visible=new_visibility, value=effects_cfg),
409
  gr.update(value=button_text),
410
  )
411
 
 
426
  return updated_config, asdict(updated_config), current_state
427
 
428
  generate.click(
429
+ fn=change_visibility,
430
+ inputs=[sidebar_visible, render_state, env_state, effects_state],
431
+ outputs=[sidebar_visible, render_sheet, environment_sheet, effects_sheet, generate],
432
  )
433
+
434
  render_sheet.change(
435
  fn=handle_render_change,
436
  inputs=[render_sheet, render_state],
 
462
  inputs=[environment_sheet, env_state],
463
  outputs=[environment_sheet, output_env_json, env_state],
464
  )
465
+ def handle_effects_change(updated_config: EffectsConfig, current_state: EffectsConfig):
466
+ if updated_config is None:
467
+ return current_state, asdict(current_state), current_state
468
+ return updated_config, asdict(updated_config), updated_config
469
 
470
+ effects_sheet.change(
471
+ fn=handle_effects_change,
472
+ inputs=[effects_sheet, effects_state],
473
+ outputs=[effects_sheet, output_effects_json, effects_state]
474
+ )
475
+ effects_sheet.undo(
476
+ fn=handle_effects_change,
477
+ inputs=[effects_sheet, effects_state],
478
+ outputs=[effects_sheet, output_effects_json, effects_state]
479
+ )
480
 
481
  demo.load(
482
+ fn=lambda r_cfg, e_cfg, ef_cfg: (asdict(r_cfg), asdict(e_cfg), asdict(ef_cfg)),
483
+ inputs=[render_state, env_state, effects_state],
484
+ outputs=[output_render_json, output_env_json, output_effects_json],
485
  )
486
 
487
  with gr.TabItem("Flyout Popup Demo"):
src/demo/space.py CHANGED
@@ -49,6 +49,45 @@ from gradio_htmlinjector import HTMLInjector
49
 
50
  # --- 1. Dataclass Definitions (unchanged) ---
51
  @dataclass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  class APISettings:
53
  api_key: str = field(
54
  default="ab123cd45ef67890ghij123klmno456p",
@@ -104,7 +143,8 @@ class ModelSettings:
104
  metadata={
105
  "component": "dropdown",
106
  "label": "Base Model",
107
- "help": "Select the base diffusion model."
 
108
  }
109
  )
110
  custom_model_path: str = field(
@@ -356,6 +396,8 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="PropertySheet Demos") as demo:
356
  gr.Markdown(
357
  "An example of using the `PropertySheet` component as a traditional sidebar for settings."
358
  )
 
 
359
  render_state = gr.State(value=initial_render_config)
360
  env_state = gr.State(value=initial_env_config)
361
  sidebar_visible = gr.State(False)
@@ -365,6 +407,7 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="PropertySheet Demos") as demo:
365
  with gr.Row():
366
  output_render_json = gr.JSON(label="Live Render State")
367
  output_env_json = gr.JSON(label="Live Environment State")
 
368
  with gr.Column(scale=1):
369
  render_sheet = PropertySheet(
370
  value=initial_render_config,
@@ -385,14 +428,22 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="PropertySheet Demos") as demo:
385
  interactive=True,
386
  root_properties_first=False
387
  )
 
 
 
 
 
 
 
388
 
389
- def change_visibility(is_visible, render_cfg, env_cfg):
390
  new_visibility = not is_visible
391
  button_text = "Hide Settings" if new_visibility else "Show Settings"
392
  return (
393
  new_visibility,
394
  gr.update(visible=new_visibility, value=render_cfg),
395
  gr.update(visible=new_visibility, value=env_cfg),
 
396
  gr.update(value=button_text),
397
  )
398
 
@@ -413,10 +464,11 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="PropertySheet Demos") as demo:
413
  return updated_config, asdict(updated_config), current_state
414
 
415
  generate.click(
416
- fn=change_visibility,
417
- inputs=[sidebar_visible, render_state, env_state],
418
- outputs=[sidebar_visible, render_sheet, environment_sheet, generate],
419
  )
 
420
  render_sheet.change(
421
  fn=handle_render_change,
422
  inputs=[render_sheet, render_state],
@@ -448,12 +500,26 @@ with gr.Blocks(theme=gr.themes.Ocean(), title="PropertySheet Demos") as demo:
448
  inputs=[environment_sheet, env_state],
449
  outputs=[environment_sheet, output_env_json, env_state],
450
  )
 
 
 
 
451
 
 
 
 
 
 
 
 
 
 
 
452
 
453
  demo.load(
454
- fn=lambda r_cfg, e_cfg: (asdict(r_cfg), asdict(e_cfg)),
455
- inputs=[render_state, env_state],
456
- outputs=[output_render_json, output_env_json],
457
  )
458
 
459
  with gr.TabItem("Flyout Popup Demo"):
 
49
 
50
  # --- 1. Dataclass Definitions (unchanged) ---
51
  @dataclass
52
+ class EffectBase:
53
+ \"\"\"Classe base com configurações de efeito comuns.\"\"\"
54
+ strength: float = field(
55
+ default=0.5,
56
+ metadata={
57
+ "component": "slider",
58
+ "label": "Effect Strength",
59
+ "minimum": 0.0,
60
+ "maximum": 1.0,
61
+ "step": 0.01,
62
+ # Esta regra depende do 'is_active' da classe filha
63
+ "interactive_if": {"field": "is_active", "value": True}
64
+ }
65
+ )
66
+
67
+ @dataclass
68
+ class EffectSettings(EffectBase):
69
+ \"\"\"Classe filha que adiciona o controle de ativação.\"\"\"
70
+ is_active: bool = field(
71
+ default=True,
72
+ metadata={"label": "Enable Effect"}
73
+ )
74
+
75
+ @dataclass
76
+ class EffectsConfig:
77
+ \"\"\"Dataclass principal que contém múltiplos efeitos com campos de controle de mesmo nome.\"\"\"
78
+ blur_effect: EffectSettings = field(
79
+ default_factory=EffectSettings,
80
+ metadata={"label": "Blur Effect"}
81
+ )
82
+ sharpen_effect: EffectSettings = field(
83
+ default_factory=EffectSettings,
84
+ metadata={"label": "Sharpen Effect"}
85
+ )
86
+ vignette_effect: EffectSettings = field(
87
+ default_factory=EffectSettings,
88
+ metadata={"label": "Vignette Effect"}
89
+ )
90
+ @dataclass
91
  class APISettings:
92
  api_key: str = field(
93
  default="ab123cd45ef67890ghij123klmno456p",
 
143
  metadata={
144
  "component": "dropdown",
145
  "label": "Base Model",
146
+ "help": "Select the base diffusion model.",
147
+ "visible": True #change here to test visibility
148
  }
149
  )
150
  custom_model_path: str = field(
 
396
  gr.Markdown(
397
  "An example of using the `PropertySheet` component as a traditional sidebar for settings."
398
  )
399
+ initial_effects_config = EffectsConfig()
400
+ effects_state = gr.State(value=initial_effects_config)
401
  render_state = gr.State(value=initial_render_config)
402
  env_state = gr.State(value=initial_env_config)
403
  sidebar_visible = gr.State(False)
 
407
  with gr.Row():
408
  output_render_json = gr.JSON(label="Live Render State")
409
  output_env_json = gr.JSON(label="Live Environment State")
410
+ output_effects_json = gr.JSON(label="Live Effects State")
411
  with gr.Column(scale=1):
412
  render_sheet = PropertySheet(
413
  value=initial_render_config,
 
428
  interactive=True,
429
  root_properties_first=False
430
  )
431
+ effects_sheet = PropertySheet(
432
+ value=initial_effects_config,
433
+ label="Post-Processing Effects",
434
+ width=400,
435
+ visible=False,
436
+ interactive=True
437
+ )
438
 
439
+ def change_visibility(is_visible, render_cfg, env_cfg, effects_cfg):
440
  new_visibility = not is_visible
441
  button_text = "Hide Settings" if new_visibility else "Show Settings"
442
  return (
443
  new_visibility,
444
  gr.update(visible=new_visibility, value=render_cfg),
445
  gr.update(visible=new_visibility, value=env_cfg),
446
+ gr.update(visible=new_visibility, value=effects_cfg),
447
  gr.update(value=button_text),
448
  )
449
 
 
464
  return updated_config, asdict(updated_config), current_state
465
 
466
  generate.click(
467
+ fn=change_visibility,
468
+ inputs=[sidebar_visible, render_state, env_state, effects_state],
469
+ outputs=[sidebar_visible, render_sheet, environment_sheet, effects_sheet, generate],
470
  )
471
+
472
  render_sheet.change(
473
  fn=handle_render_change,
474
  inputs=[render_sheet, render_state],
 
500
  inputs=[environment_sheet, env_state],
501
  outputs=[environment_sheet, output_env_json, env_state],
502
  )
503
+ def handle_effects_change(updated_config: EffectsConfig, current_state: EffectsConfig):
504
+ if updated_config is None:
505
+ return current_state, asdict(current_state), current_state
506
+ return updated_config, asdict(updated_config), updated_config
507
 
508
+ effects_sheet.change(
509
+ fn=handle_effects_change,
510
+ inputs=[effects_sheet, effects_state],
511
+ outputs=[effects_sheet, output_effects_json, effects_state]
512
+ )
513
+ effects_sheet.undo(
514
+ fn=handle_effects_change,
515
+ inputs=[effects_sheet, effects_state],
516
+ outputs=[effects_sheet, output_effects_json, effects_state]
517
+ )
518
 
519
  demo.load(
520
+ fn=lambda r_cfg, e_cfg, ef_cfg: (asdict(r_cfg), asdict(e_cfg), asdict(ef_cfg)),
521
+ inputs=[render_state, env_state, effects_state],
522
+ outputs=[output_render_json, output_env_json, output_effects_json],
523
  )
524
 
525
  with gr.TabItem("Flyout Popup Demo"):
src/frontend/Index.svelte CHANGED
@@ -100,15 +100,22 @@
100
  }
101
 
102
  /**
103
- * Reactive block that triggers whenever the `value` prop changes from the backend.
104
- * It initializes group visibility, validates properties, and updates slider visuals.
 
 
105
  */
106
- $: if (Array.isArray(value) && JSON.stringify(value) !== lastValue) {
107
- lastValue = JSON.stringify(value);
108
- for (const group of value) {
109
- if (groupVisibility[group.group_name] === undefined) {
110
- groupVisibility[group.group_name] = true;
 
 
111
  }
 
 
 
112
  if (Array.isArray(group.properties)) {
113
  for (const prop of group.properties) {
114
  if (prop.component?.startsWith("number") || prop.component === 'slider') {
@@ -117,9 +124,9 @@
117
  }
118
  }
119
  }
120
- updateAllSliders();
 
121
  }
122
-
123
  /**
124
  * Updates a slider's track background to visually represent its value as a percentage.
125
  * It sets the `--slider-progress` CSS custom property.
@@ -159,11 +166,14 @@
159
  * @param {string} prop_name - The name of the property to find.
160
  */
161
  function get_prop_value(prop_name: string) {
162
- if (!Array.isArray(value)) return undefined;
 
163
  for (const group of value) {
164
- if (!Array.isArray(group.properties)) continue;
165
  const found_prop = group.properties.find(p => p.name === prop_name);
166
- if (found_prop) return found_prop.value;
 
 
167
  }
168
  return undefined;
169
  }
@@ -320,161 +330,177 @@
320
  {#each group.properties as prop (prop.name)}
321
  {#if prop.visible ?? true}
322
  <!-- Conditional interactivity based on another property's value -->
323
- {@const condition = prop.interactive_if}
324
- {@const parent_value = condition ? get_prop_value(condition.field) : null}
 
 
 
325
 
326
  {@const is_interactive = interactive && (
327
- !condition ?
328
- true // No condition, so it's interactive by default.
329
  :
330
- condition.value !== undefined ?
331
- // Case 1: `value` key exists (equality check)
332
- Array.isArray(condition.value) ?
333
- // If `value` is an array, check if parent_value is IN the array.
334
- condition.value.includes(parent_value)
335
  :
336
- // If `value` is a single item, do a strict equality check.
337
- parent_value === condition.value
338
  :
339
- condition.neq !== undefined ?
340
- // Case 2: `neq` key exists (inequality check)
341
- parent_value !== condition.neq
342
  :
343
- true // Fallback if `interactive_if` is malformed.
344
  )}
345
-
346
- <label class="prop-label" for={prop.name}>
347
- <div class="prop-label-wrapper">
348
- <span>{prop.label}</span>
349
- <!-- Help tooltip -->
350
- {#if prop.help}
351
- <div class="tooltip-container">
352
- <span class="tooltip-icon">?</span>
353
- <span class="tooltip-text">{prop.help}</span>
354
- </div>
355
- {/if}
356
- </div>
357
- </label>
358
 
359
- <div class="prop-control">
360
- <!-- Dynamically render the correct input component based on `prop.component` -->
361
- {#if prop.component === 'string'}
362
- <input
363
- type="text"
364
- bind:value={prop.value}
365
- disabled={!is_interactive}
366
- on:change={() => dispatch_update("change", prop)}
367
- on:input={() => dispatch_update("input", prop)}
368
- />
369
- {:else if prop.component === 'password'}
370
- <input
371
- type="password"
372
- bind:value={prop.value}
373
- disabled={!is_interactive}
374
- on:change={() => dispatch_update("change", prop)}
375
- on:input={() => dispatch_update("input", prop)}
376
- />
377
- {:else if prop.component === 'checkbox'}
378
- <input
379
- type="checkbox"
380
- bind:checked={prop.value}
381
- disabled={!is_interactive}
382
- on:change={() => dispatch_update("change", prop)}
383
- />
384
- {:else if prop.component === 'number_integer' || prop.component === 'number_float'}
385
- <input
386
- class:invalid={validationState[prop.name] === false}
387
- class:disabled={!is_interactive}
388
- type="number"
389
- step={prop.step || 1}
390
- bind:value={prop.value}
391
- disabled={!is_interactive}
392
- on:change={() => dispatch_update("change", prop)}
393
- on:input={() => {
394
- validate_prop(prop);
395
- dispatch_update("input", prop);
396
- }}
397
- />
398
- {:else if prop.component === 'slider'}
399
- <div class="slider-container" class:disabled={!is_interactive}>
400
  <input
401
- type="range"
402
- min={prop.minimum}
403
- max={prop.maximum}
404
- step={prop.step || 1}
405
  bind:value={prop.value}
406
- bind:this={sliderElements[prop.name]}
407
  disabled={!is_interactive}
408
- on:input={() => {
409
- validate_prop(prop);
410
- updateSliderBackground(prop, sliderElements[prop.name]);
411
- dispatch_update("input", prop);
412
- }}
413
  on:change={() => dispatch_update("change", prop)}
 
414
  />
415
- <span class="slider-value">{prop.value}</span>
416
- </div>
417
- {:else if prop.component === 'colorpicker'}
418
- <div class="color-picker-container" class:disabled={!is_interactive}>
419
  <input
420
- type="color"
421
- class="color-picker-input"
422
- bind:value={prop.value}
423
- disabled={!is_interactive}
424
- on:change={() => dispatch_update("change", prop)}
425
  />
426
- <span class="color-picker-value">{prop.value}</span>
427
- </div>
428
- {:else if prop.component === 'dropdown'}
429
- <div class="dropdown-wrapper" class:disabled={!is_interactive}>
430
- <select
431
  disabled={!is_interactive}
432
- value={prop.value}
433
- on:change={(e) => handle_dropdown_change(e, prop)}
434
- >
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
435
  {#if Array.isArray(prop.choices)}
436
  {#each prop.choices as choice}
437
- <option value={choice} selected={prop.value === choice}>
438
- {choice}
439
- </option>
 
 
 
 
 
 
 
 
440
  {/each}
441
  {/if}
442
- </select>
443
- <div class="dropdown-arrow-icon"></div>
444
- </div>
445
- {:else if prop.component === 'radio'}
446
- <div class="radio-group" class:disabled={!is_interactive} on:change={() => dispatch_update('change', prop)}>
447
- {#if Array.isArray(prop.choices)}
448
- {#each prop.choices as choice}
449
- <div class="radio-item">
450
- <input
451
- type="radio"
452
- id="{prop.name}-{choice}"
453
- name={prop.name}
454
- value={choice}
455
- bind:group={prop.value}
456
- disabled={!is_interactive}
457
- >
458
- <label for="{prop.name}-{choice}">{choice}</label>
459
- </div>
460
- {/each}
461
- {/if}
462
- </div>
463
- {/if}
464
 
465
- <!-- Reset button, visible only when the current value differs from the initial value -->
466
- {#if prop.component !== 'checkbox'}
467
- <button
468
- class="reset-button-prop"
469
- class:visible={initialValues[prop.name] !== prop.value}
470
- title="Reset to default"
471
- on:click|stopPropagation={() => handle_reset_prop(prop.name)}
472
- disabled={!is_interactive}
473
- >
474
-
475
- </button>
476
- {/if}
477
- </div>
 
478
  {/if}
479
  {/each}
480
  {/if}
 
100
  }
101
 
102
  /**
103
+ * Reactive block that triggers whenever the `value` prop changes from the backend
104
+ * OR from a user interaction within the component (like a checkbox toggle).
105
+ * It initializes group visibility, validates properties, and critically,
106
+ * re-updates slider visuals after the DOM has been updated.
107
  */
108
+ $: if (Array.isArray(value)) {
109
+ if (JSON.stringify(value) !== lastValue) {
110
+ lastValue = JSON.stringify(value);
111
+ for (const group of value) {
112
+ if (groupVisibility[group.group_name] === undefined) {
113
+ groupVisibility[group.group_name] = true;
114
+ }
115
  }
116
+ }
117
+
118
+ for (const group of value) {
119
  if (Array.isArray(group.properties)) {
120
  for (const prop of group.properties) {
121
  if (prop.component?.startsWith("number") || prop.component === 'slider') {
 
124
  }
125
  }
126
  }
127
+
128
+ tick().then(updateAllSliders);
129
  }
 
130
  /**
131
  * Updates a slider's track background to visually represent its value as a percentage.
132
  * It sets the `--slider-progress` CSS custom property.
 
166
  * @param {string} prop_name - The name of the property to find.
167
  */
168
  function get_prop_value(prop_name: string) {
169
+ if (!Array.isArray(value) || !prop_name) return undefined;
170
+
171
  for (const group of value) {
172
+ if (!Array.isArray(group.properties)) continue;
173
  const found_prop = group.properties.find(p => p.name === prop_name);
174
+ if (found_prop) {
175
+ return found_prop.value;
176
+ }
177
  }
178
  return undefined;
179
  }
 
330
  {#each group.properties as prop (prop.name)}
331
  {#if prop.visible ?? true}
332
  <!-- Conditional interactivity based on another property's value -->
333
+ {@const i_condition = prop.interactive_if}
334
+ {@const v_condition = prop.visible_if}
335
+
336
+ {@const i_parent_value = i_condition ? get_prop_value(i_condition.field) : null}
337
+ {@const v_parent_value = v_condition ? get_prop_value(v_condition.field) : null}
338
 
339
  {@const is_interactive = interactive && (
340
+ !i_condition ?
341
+ true
342
  :
343
+ i_condition.value !== undefined ?
344
+ Array.isArray(i_condition.value) ?
345
+ i_condition.value.includes(i_parent_value)
 
 
346
  :
347
+ i_parent_value === i_condition.value
 
348
  :
349
+ i_condition.neq !== undefined ?
350
+ i_parent_value !== i_condition.neq
 
351
  :
352
+ true
353
  )}
 
 
 
 
 
 
 
 
 
 
 
 
 
354
 
355
+ {@const is_visible = (prop.visible ?? true) && (
356
+ !v_condition ?
357
+ true
358
+ :
359
+ v_condition.value !== undefined ?
360
+ Array.isArray(v_condition.value) ?
361
+ v_condition.value.includes(v_parent_value)
362
+ :
363
+ v_parent_value === v_condition.value
364
+ :
365
+ v_condition.neq !== undefined ?
366
+ v_parent_value !== v_condition.neq
367
+ :
368
+ true // Fallback
369
+ )}
370
+ {#if is_visible}
371
+ <label class="prop-label" for={prop.name}>
372
+ <div class="prop-label-wrapper">
373
+ <span>{prop.label}</span>
374
+ <!-- Help tooltip -->
375
+ {#if prop.help}
376
+ <div class="tooltip-container">
377
+ <span class="tooltip-icon">?</span>
378
+ <span class="tooltip-text">{prop.help}</span>
379
+ </div>
380
+ {/if}
381
+ </div>
382
+ </label>
383
+
384
+ <div class="prop-control">
385
+ <!-- Dynamically render the correct input component based on `prop.component` -->
386
+ {#if prop.component === 'string'}
 
 
 
 
 
 
 
 
 
387
  <input
388
+ type="text"
 
 
 
389
  bind:value={prop.value}
 
390
  disabled={!is_interactive}
 
 
 
 
 
391
  on:change={() => dispatch_update("change", prop)}
392
+ on:input={() => dispatch_update("input", prop)}
393
  />
394
+ {:else if prop.component === 'password'}
 
 
 
395
  <input
396
+ type="password"
397
+ bind:value={prop.value}
398
+ disabled={!is_interactive}
399
+ on:change={() => dispatch_update("change", prop)}
400
+ on:input={() => dispatch_update("input", prop)}
401
  />
402
+ {:else if prop.component === 'checkbox'}
403
+ <input
404
+ type="checkbox"
405
+ bind:checked={prop.value}
 
406
  disabled={!is_interactive}
407
+ on:change={() => dispatch_update("change", prop)}
408
+ />
409
+ {:else if prop.component === 'number_integer' || prop.component === 'number_float'}
410
+ <input
411
+ class:invalid={validationState[prop.name] === false}
412
+ class:disabled={!is_interactive}
413
+ type="number"
414
+ step={prop.step || 1}
415
+ bind:value={prop.value}
416
+ disabled={!is_interactive}
417
+ on:change={() => dispatch_update("change", prop)}
418
+ on:input={() => {
419
+ validate_prop(prop);
420
+ dispatch_update("input", prop);
421
+ }}
422
+ />
423
+ {:else if prop.component === 'slider'}
424
+ <div class="slider-container" class:disabled={!is_interactive}>
425
+ <input
426
+ type="range"
427
+ min={prop.minimum}
428
+ max={prop.maximum}
429
+ step={prop.step || 1}
430
+ bind:value={prop.value}
431
+ bind:this={sliderElements[prop.name]}
432
+ disabled={!is_interactive}
433
+ on:input={() => {
434
+ validate_prop(prop);
435
+ updateSliderBackground(prop, sliderElements[prop.name]);
436
+ dispatch_update("input", prop);
437
+ }}
438
+ on:change={() => dispatch_update("change", prop)}
439
+ />
440
+ <span class="slider-value">{prop.value}</span>
441
+ </div>
442
+ {:else if prop.component === 'colorpicker'}
443
+ <div class="color-picker-container" class:disabled={!is_interactive}>
444
+ <input
445
+ type="color"
446
+ class="color-picker-input"
447
+ bind:value={prop.value}
448
+ disabled={!is_interactive}
449
+ on:change={() => dispatch_update("change", prop)}
450
+ />
451
+ <span class="color-picker-value">{prop.value}</span>
452
+ </div>
453
+ {:else if prop.component === 'dropdown'}
454
+ <div class="dropdown-wrapper" class:disabled={!is_interactive}>
455
+ <select
456
+ disabled={!is_interactive}
457
+ value={prop.value}
458
+ on:change={(e) => handle_dropdown_change(e, prop)}
459
+ >
460
+ {#if Array.isArray(prop.choices)}
461
+ {#each prop.choices as choice}
462
+ <option value={choice} selected={prop.value === choice}>
463
+ {choice}
464
+ </option>
465
+ {/each}
466
+ {/if}
467
+ </select>
468
+ <div class="dropdown-arrow-icon"></div>
469
+ </div>
470
+ {:else if prop.component === 'radio'}
471
+ <div class="radio-group" class:disabled={!is_interactive} on:change={() => dispatch_update('change', prop)}>
472
  {#if Array.isArray(prop.choices)}
473
  {#each prop.choices as choice}
474
+ <div class="radio-item">
475
+ <input
476
+ type="radio"
477
+ id="{prop.name}-{choice}"
478
+ name={prop.name}
479
+ value={choice}
480
+ bind:group={prop.value}
481
+ disabled={!is_interactive}
482
+ >
483
+ <label for="{prop.name}-{choice}">{choice}</label>
484
+ </div>
485
  {/each}
486
  {/if}
487
+ </div>
488
+ {/if}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
489
 
490
+ <!-- Reset button, visible only when the current value differs from the initial value -->
491
+ {#if prop.component !== 'checkbox'}
492
+ <button
493
+ class="reset-button-prop"
494
+ class:visible={initialValues[prop.name] !== prop.value}
495
+ title="Reset to default"
496
+ on:click|stopPropagation={() => handle_reset_prop(prop.name)}
497
+ disabled={!is_interactive}
498
+ >
499
+
500
+ </button>
501
+ {/if}
502
+ </div>
503
+ {/if}
504
  {/if}
505
  {/each}
506
  {/if}
src/pyproject.toml CHANGED
@@ -8,7 +8,7 @@ build-backend = "hatchling.build"
8
 
9
  [project]
10
  name = "gradio_propertysheet"
11
- version = "0.0.15"
12
  description = "Property sheet"
13
  readme = "README.md"
14
  license = "apache-2.0"
 
8
 
9
  [project]
10
  name = "gradio_propertysheet"
11
+ version = "0.0.16"
12
  description = "Property sheet"
13
  readme = "README.md"
14
  license = "apache-2.0"