FeilongTang commited on
Commit
8c97d5b
Β·
1 Parent(s): 2482664

Third UI pass: pipeline flow, primary outputs, animations, empty states

Browse files

Hero
- "by lmms-lab" chip above the title.
- Larger 2.7rem gradient title with animated background-position
drift (ovc-shift, 9s loop) so the gradient feels alive.
- Subtle 28px grid-pattern background reinforces the patch-grid
metaphor without distracting from text.

Pipeline flow strip
- New dashed card under the hero showing the six pipeline stages:
Sample -> Score -> GOP -> Top-K -> Visualize -> Pack canvas.
Gives first-time visitors a one-glance map of what the demo does.

Cards
- Two tiers: regular ovc-card and accented ovc-card-primary used on
the visualization video and the codec-vs-uniform charts. Primary
cards carry an indigo ring + soft shadow so the eye lands on
outputs first.
- Section headers re-styled as pill chips (uppercase, tinted
background, gradient bullet) for stronger visual hierarchy.
- Card fade-in keyframe so newly rendered components animate in.

Run button
- Idle pulse (2.6s) draws attention before the first run; pauses on
hover so the interaction state stays predictable.
- Background also drifts on the same shift keyframe.

Empty states
- Vis video / chart / canvas now show an "ovc-empty" placeholder
explaining what will appear after a run, so a fresh page does not
look broken.

Files changed (1) hide show
  1. app.py +164 -29
app.py CHANGED
@@ -754,19 +754,36 @@ CUSTOM_CSS = """
754
  :root, .gradio-container, .gradio-container.dark {
755
  --ovc-grad: linear-gradient(135deg, #4f46e5 0%, #2563eb 50%, #06b6d4 100%);
756
  --ovc-grad-soft: linear-gradient(135deg, rgba(79,70,229,0.10), rgba(6,182,212,0.10));
 
 
757
  }
758
  .gradio-container { max-width: 1320px !important; margin: 0 auto !important; }
 
 
 
 
 
 
 
 
 
 
 
 
 
759
 
760
  /* Hero */
761
  #ovc-hero {
762
  text-align: center;
763
- padding: 36px 16px 18px;
764
- border-radius: 18px;
765
  background:
766
- radial-gradient(120% 80% at 50% -10%, rgba(79,70,229,0.18), transparent 60%),
767
- linear-gradient(180deg, rgba(79,70,229,0.06), rgba(6,182,212,0.03));
768
- border: 1px solid rgba(99,102,241,0.18);
769
- margin-bottom: 14px;
 
 
770
  position: relative;
771
  overflow: hidden;
772
  }
@@ -774,24 +791,37 @@ CUSTOM_CSS = """
774
  content: "";
775
  position: absolute; inset: auto -20% -40% -20%;
776
  height: 60%;
777
- background: radial-gradient(60% 80% at 50% 0%, rgba(6,182,212,0.18), transparent 70%);
778
  pointer-events: none;
779
  }
780
  #ovc-hero h1 {
781
- font-size: 2.4rem;
782
  font-weight: 800;
783
  background: var(--ovc-grad);
 
 
784
  -webkit-background-clip: text;
785
  background-clip: text;
786
  color: transparent;
787
- margin: 0 0 8px;
788
- letter-spacing: -0.025em;
789
- line-height: 1.05;
 
 
 
 
 
 
 
 
 
 
 
790
  }
791
  #ovc-hero p.tagline {
792
- font-size: 1.04rem;
793
  color: var(--body-text-color-subdued);
794
- margin: 0 auto 14px;
795
  max-width: 760px;
796
  line-height: 1.6;
797
  }
@@ -841,50 +871,69 @@ CUSTOM_CSS = """
841
  /* Cards */
842
  .ovc-card {
843
  border-radius: 16px !important;
844
- padding: 14px 16px !important;
845
- border: 1px solid rgba(148,163,184,0.28) !important;
846
  background: var(--background-fill-primary) !important;
847
  box-shadow: 0 1px 3px rgba(15,23,42,0.04);
848
- transition: box-shadow 0.18s ease, border-color 0.18s ease;
 
849
  }
850
  .ovc-card:hover {
851
  border-color: rgba(99,102,241,0.32) !important;
852
- box-shadow: 0 6px 18px rgba(15,23,42,0.06);
 
 
 
 
 
 
 
 
 
 
 
 
853
  }
854
  .ovc-card h3 {
855
- font-size: 0.78rem !important;
 
 
 
856
  font-weight: 700 !important;
857
  text-transform: uppercase;
858
- letter-spacing: 0.08em;
859
  color: #4f46e5 !important;
860
- margin: 0 0 10px !important;
 
 
 
861
  }
862
  .ovc-card h3::before {
863
  content: "";
864
  display: inline-block;
865
  width: 6px; height: 6px; border-radius: 50%;
866
  background: var(--ovc-grad);
867
- margin-right: 8px; vertical-align: middle;
868
- transform: translateY(-1px);
869
  }
870
 
871
  /* Run button */
872
  #ovc-run button {
873
  width: 100%;
874
- height: 52px !important;
875
- font-size: 1.05rem !important;
876
  font-weight: 700 !important;
877
  letter-spacing: 0.01em;
878
  background: var(--ovc-grad) !important;
 
 
879
  border: none !important;
880
  color: #fff !important;
881
  border-radius: 14px !important;
882
- box-shadow: 0 6px 18px rgba(37, 99, 235, 0.32);
883
- transition: transform 0.06s ease, box-shadow 0.2s ease;
884
  }
885
  #ovc-run button:hover {
886
  transform: translateY(-1px);
887
- box-shadow: 0 8px 22px rgba(37, 99, 235, 0.40);
888
  }
889
  #ovc-run button:active { transform: translateY(0); }
890
 
@@ -920,6 +969,52 @@ CUSTOM_CSS = """
920
 
921
  /* Tighter spacing for sliders inside cards */
922
  .ovc-card .gradio-slider { margin-bottom: 4px !important; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
923
  """
924
 
925
  THEME = gr.themes.Soft(
@@ -936,6 +1031,7 @@ THEME = gr.themes.Soft(
936
 
937
  HERO_HTML = """
938
  <div id="ovc-hero">
 
939
  <h1>OneVision Encoder</h1>
940
  <p class="tagline">
941
  Codec-style patch saliency for video understanding &mdash; see which
@@ -984,8 +1080,44 @@ SIGNAL_CHOICES = [
984
  ]
985
 
986
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
987
  with gr.Blocks(**_BLOCK_KW) as demo:
988
  gr.HTML(HERO_HTML)
 
989
 
990
  with gr.Row(equal_height=False):
991
  # ─── Controls (narrow column) ────────────────────────────────────
@@ -1085,13 +1217,14 @@ with gr.Blocks(**_BLOCK_KW) as demo:
1085
 
1086
  # ─── Outputs (wide column) ───────────────────────────────────────
1087
  with gr.Column(scale=6, min_width=420):
1088
- with gr.Group(elem_classes="ovc-card"):
1089
  gr.Markdown("### Patch selection visualization")
 
1090
  vis_out = gr.Video(
1091
  label="", show_label=False, autoplay=True, height=420,
1092
  )
1093
 
1094
- with gr.Group(elem_classes="ovc-card"):
1095
  gr.Markdown("### Codec selection vs uniform baseline")
1096
  gr.Markdown(
1097
  "<small>Same total patch budget on both sides. "
@@ -1101,12 +1234,14 @@ with gr.Blocks(**_BLOCK_KW) as demo:
1101
  "spend the same budget β€” spread evenly in time, every "
1102
  "patch in the chosen frames kept.</small>"
1103
  )
 
1104
  chart_out = gr.Plot(label="", show_label=False)
1105
 
1106
  with gr.Row():
1107
  with gr.Column(scale=1):
1108
  with gr.Group(elem_classes="ovc-card"):
1109
  gr.Markdown("### Packed canvas")
 
1110
  canvas_out = gr.Image(
1111
  label="", show_label=False, height=320,
1112
  )
 
754
  :root, .gradio-container, .gradio-container.dark {
755
  --ovc-grad: linear-gradient(135deg, #4f46e5 0%, #2563eb 50%, #06b6d4 100%);
756
  --ovc-grad-soft: linear-gradient(135deg, rgba(79,70,229,0.10), rgba(6,182,212,0.10));
757
+ --ovc-ring: rgba(99,102,241,0.32);
758
+ --ovc-ring-strong: rgba(99,102,241,0.55);
759
  }
760
  .gradio-container { max-width: 1320px !important; margin: 0 auto !important; }
761
+ @keyframes ovc-shift {
762
+ 0% { background-position: 0% 50%; }
763
+ 50% { background-position: 100% 50%; }
764
+ 100% { background-position: 0% 50%; }
765
+ }
766
+ @keyframes ovc-pulse {
767
+ 0%, 100% { box-shadow: 0 6px 18px rgba(37, 99, 235, 0.32); }
768
+ 50% { box-shadow: 0 8px 26px rgba(37, 99, 235, 0.50); }
769
+ }
770
+ @keyframes ovc-fade-in {
771
+ from { opacity: 0; transform: translateY(4px); }
772
+ to { opacity: 1; transform: translateY(0); }
773
+ }
774
 
775
  /* Hero */
776
  #ovc-hero {
777
  text-align: center;
778
+ padding: 44px 16px 22px;
779
+ border-radius: 22px;
780
  background:
781
+ radial-gradient(120% 80% at 50% -10%, rgba(79,70,229,0.20), transparent 60%),
782
+ linear-gradient(180deg, rgba(79,70,229,0.06), rgba(6,182,212,0.03)),
783
+ repeating-linear-gradient(0deg, rgba(99,102,241,0.05) 0 1px, transparent 1px 28px),
784
+ repeating-linear-gradient(90deg, rgba(99,102,241,0.05) 0 1px, transparent 1px 28px);
785
+ border: 1px solid rgba(99,102,241,0.22);
786
+ margin-bottom: 18px;
787
  position: relative;
788
  overflow: hidden;
789
  }
 
791
  content: "";
792
  position: absolute; inset: auto -20% -40% -20%;
793
  height: 60%;
794
+ background: radial-gradient(60% 80% at 50% 0%, rgba(6,182,212,0.22), transparent 70%);
795
  pointer-events: none;
796
  }
797
  #ovc-hero h1 {
798
+ font-size: 2.7rem;
799
  font-weight: 800;
800
  background: var(--ovc-grad);
801
+ background-size: 200% 200%;
802
+ animation: ovc-shift 9s ease-in-out infinite;
803
  -webkit-background-clip: text;
804
  background-clip: text;
805
  color: transparent;
806
+ margin: 0 0 6px;
807
+ letter-spacing: -0.028em;
808
+ line-height: 1.04;
809
+ }
810
+ #ovc-hero .by-line {
811
+ display: inline-block;
812
+ font-size: 0.78rem;
813
+ font-weight: 600;
814
+ color: #4f46e5;
815
+ background: rgba(79,70,229,0.10);
816
+ padding: 3px 10px;
817
+ border-radius: 999px;
818
+ margin-bottom: 10px;
819
+ letter-spacing: 0.04em;
820
  }
821
  #ovc-hero p.tagline {
822
+ font-size: 1.05rem;
823
  color: var(--body-text-color-subdued);
824
+ margin: 0 auto 16px;
825
  max-width: 760px;
826
  line-height: 1.6;
827
  }
 
871
  /* Cards */
872
  .ovc-card {
873
  border-radius: 16px !important;
874
+ padding: 16px 18px !important;
875
+ border: 1px solid rgba(148,163,184,0.26) !important;
876
  background: var(--background-fill-primary) !important;
877
  box-shadow: 0 1px 3px rgba(15,23,42,0.04);
878
+ transition: box-shadow 0.18s ease, border-color 0.18s ease, transform 0.18s ease;
879
+ animation: ovc-fade-in 0.32s ease-out;
880
  }
881
  .ovc-card:hover {
882
  border-color: rgba(99,102,241,0.32) !important;
883
+ box-shadow: 0 6px 22px rgba(15,23,42,0.07);
884
+ }
885
+ /* Primary outputs: subtle accent ring + lift */
886
+ .ovc-card-primary {
887
+ border: 1px solid var(--ovc-ring) !important;
888
+ background:
889
+ linear-gradient(180deg, rgba(79,70,229,0.025), rgba(6,182,212,0.012)),
890
+ var(--background-fill-primary) !important;
891
+ box-shadow: 0 4px 18px rgba(79,70,229,0.08) !important;
892
+ }
893
+ .ovc-card-primary:hover {
894
+ border-color: var(--ovc-ring-strong) !important;
895
+ box-shadow: 0 10px 28px rgba(79,70,229,0.14) !important;
896
  }
897
  .ovc-card h3 {
898
+ display: inline-flex;
899
+ align-items: center;
900
+ gap: 8px;
901
+ font-size: 0.74rem !important;
902
  font-weight: 700 !important;
903
  text-transform: uppercase;
904
+ letter-spacing: 0.10em;
905
  color: #4f46e5 !important;
906
+ background: rgba(79,70,229,0.08);
907
+ padding: 4px 10px !important;
908
+ border-radius: 999px;
909
+ margin: 0 0 12px !important;
910
  }
911
  .ovc-card h3::before {
912
  content: "";
913
  display: inline-block;
914
  width: 6px; height: 6px; border-radius: 50%;
915
  background: var(--ovc-grad);
916
+ transform: translateY(0);
 
917
  }
918
 
919
  /* Run button */
920
  #ovc-run button {
921
  width: 100%;
922
+ height: 54px !important;
923
+ font-size: 1.06rem !important;
924
  font-weight: 700 !important;
925
  letter-spacing: 0.01em;
926
  background: var(--ovc-grad) !important;
927
+ background-size: 200% 200% !important;
928
+ animation: ovc-shift 6s ease-in-out infinite, ovc-pulse 2.6s ease-in-out infinite;
929
  border: none !important;
930
  color: #fff !important;
931
  border-radius: 14px !important;
932
+ transition: transform 0.06s ease;
 
933
  }
934
  #ovc-run button:hover {
935
  transform: translateY(-1px);
936
+ animation-play-state: paused;
937
  }
938
  #ovc-run button:active { transform: translateY(0); }
939
 
 
969
 
970
  /* Tighter spacing for sliders inside cards */
971
  .ovc-card .gradio-slider { margin-bottom: 4px !important; }
972
+
973
+ /* Pipeline mini-flow strip */
974
+ #ovc-flow {
975
+ display: flex; flex-wrap: wrap; gap: 6px;
976
+ align-items: center; justify-content: center;
977
+ padding: 14px 16px; margin-bottom: 18px;
978
+ border-radius: 14px;
979
+ border: 1px dashed rgba(99,102,241,0.28);
980
+ background: linear-gradient(180deg, rgba(99,102,241,0.04), transparent);
981
+ }
982
+ #ovc-flow .step {
983
+ display: inline-flex; align-items: center; gap: 6px;
984
+ font-size: 0.83rem; font-weight: 600;
985
+ color: #475569;
986
+ padding: 6px 12px;
987
+ background: var(--background-fill-primary);
988
+ border: 1px solid rgba(148,163,184,0.30);
989
+ border-radius: 999px;
990
+ box-shadow: 0 1px 2px rgba(15,23,42,0.04);
991
+ }
992
+ #ovc-flow .step .dot {
993
+ width: 7px; height: 7px; border-radius: 50%;
994
+ background: var(--ovc-grad);
995
+ }
996
+ #ovc-flow .arrow { color: #94a3b8; font-size: 0.95rem; user-select: none; }
997
+
998
+ /* Empty-state placeholder for outputs */
999
+ .ovc-empty {
1000
+ text-align: center;
1001
+ color: #94a3b8;
1002
+ font-size: 0.88rem;
1003
+ padding: 26px 18px;
1004
+ border: 1px dashed rgba(148,163,184,0.45);
1005
+ border-radius: 12px;
1006
+ background: rgba(148,163,184,0.04);
1007
+ }
1008
+ .ovc-empty b { color: #64748b; font-weight: 600; }
1009
+ .ovc-empty kbd {
1010
+ background: var(--ovc-grad);
1011
+ color: #fff;
1012
+ padding: 1px 7px;
1013
+ border-radius: 6px;
1014
+ font-size: 0.78rem;
1015
+ font-family: inherit;
1016
+ font-weight: 600;
1017
+ }
1018
  """
1019
 
1020
  THEME = gr.themes.Soft(
 
1031
 
1032
  HERO_HTML = """
1033
  <div id="ovc-hero">
1034
+ <span class="by-line">by lmms-lab</span>
1035
  <h1>OneVision Encoder</h1>
1036
  <p class="tagline">
1037
  Codec-style patch saliency for video understanding &mdash; see which
 
1080
  ]
1081
 
1082
 
1083
+ FLOW_HTML = """
1084
+ <div id="ovc-flow">
1085
+ <span class="step"><span class="dot"></span>Sample</span>
1086
+ <span class="arrow">β€Ί</span>
1087
+ <span class="step"><span class="dot"></span>Score patches</span>
1088
+ <span class="arrow">β€Ί</span>
1089
+ <span class="step"><span class="dot"></span>GOP grouping</span>
1090
+ <span class="arrow">β€Ί</span>
1091
+ <span class="step"><span class="dot"></span>Top-K select</span>
1092
+ <span class="arrow">β€Ί</span>
1093
+ <span class="step"><span class="dot"></span>Visualize</span>
1094
+ <span class="arrow">β€Ί</span>
1095
+ <span class="step"><span class="dot"></span>Pack canvas</span>
1096
+ </div>
1097
+ """
1098
+
1099
+ EMPTY_VIS = (
1100
+ '<div class="ovc-empty">'
1101
+ 'Upload a video on the left, hit <kbd>Run pipeline</kbd> '
1102
+ 'β€” or click any row in <b>Demo video</b> below to try the bundled clip. '
1103
+ 'The selected patches will animate here.'
1104
+ '</div>'
1105
+ )
1106
+ EMPTY_CHART = (
1107
+ '<div class="ovc-empty">'
1108
+ 'Codec-vs-uniform timeline charts will appear here after a run.'
1109
+ '</div>'
1110
+ )
1111
+ EMPTY_CANVAS = (
1112
+ '<div class="ovc-empty">'
1113
+ 'The packed canvas (LLaVA-OneVision input) will appear here.'
1114
+ '</div>'
1115
+ )
1116
+
1117
+
1118
  with gr.Blocks(**_BLOCK_KW) as demo:
1119
  gr.HTML(HERO_HTML)
1120
+ gr.HTML(FLOW_HTML)
1121
 
1122
  with gr.Row(equal_height=False):
1123
  # ─── Controls (narrow column) ────────────────────────────────────
 
1217
 
1218
  # ─── Outputs (wide column) ───────────────────────────────────────
1219
  with gr.Column(scale=6, min_width=420):
1220
+ with gr.Group(elem_classes="ovc-card ovc-card-primary"):
1221
  gr.Markdown("### Patch selection visualization")
1222
+ gr.HTML(EMPTY_VIS)
1223
  vis_out = gr.Video(
1224
  label="", show_label=False, autoplay=True, height=420,
1225
  )
1226
 
1227
+ with gr.Group(elem_classes="ovc-card ovc-card-primary"):
1228
  gr.Markdown("### Codec selection vs uniform baseline")
1229
  gr.Markdown(
1230
  "<small>Same total patch budget on both sides. "
 
1234
  "spend the same budget β€” spread evenly in time, every "
1235
  "patch in the chosen frames kept.</small>"
1236
  )
1237
+ gr.HTML(EMPTY_CHART)
1238
  chart_out = gr.Plot(label="", show_label=False)
1239
 
1240
  with gr.Row():
1241
  with gr.Column(scale=1):
1242
  with gr.Group(elem_classes="ovc-card"):
1243
  gr.Markdown("### Packed canvas")
1244
+ gr.HTML(EMPTY_CANVAS)
1245
  canvas_out = gr.Image(
1246
  label="", show_label=False, height=320,
1247
  )