Spaces:
Sleeping
Sleeping
Dark violet theme matching the cover art
Browse files- README.md +2 -1
- tonesearch.py +50 -2
README.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
| 1 |
---
|
| 2 |
title: tonesearch
|
| 3 |
colorFrom: indigo
|
| 4 |
-
colorTo:
|
|
|
|
| 5 |
sdk: gradio
|
| 6 |
sdk_version: 6.30.0
|
| 7 |
python_version: "3.12"
|
|
|
|
| 1 |
---
|
| 2 |
title: tonesearch
|
| 3 |
colorFrom: indigo
|
| 4 |
+
colorTo: purple
|
| 5 |
+
thumbnail: https://raw.githubusercontent.com/aipdv/tonesearch/main/assets/banner.jpg
|
| 6 |
sdk: gradio
|
| 7 |
sdk_version: 6.30.0
|
| 8 |
python_version: "3.12"
|
tonesearch.py
CHANGED
|
@@ -101,6 +101,49 @@ def search(index_dir, query="", tone="angry", w=1.0, k=10):
|
|
| 101 |
return top, score[top]
|
| 102 |
|
| 103 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
MAX_UPLOAD_S = 300 # ponytail: 5 min keeps one upload inside a free visitor's daily ZeroGPU quota
|
| 105 |
|
| 106 |
|
|
@@ -149,7 +192,7 @@ def ui(index_dir):
|
|
| 149 |
return plot, pd.DataFrame({"start": ux["start"][top].round(1), "end": ux["end"][top].round(1), "score": score.round(2)})
|
| 150 |
|
| 151 |
with gr.Blocks(title="tonesearch") as app:
|
| 152 |
-
gr.
|
| 153 |
with gr.Tab("Search"):
|
| 154 |
with gr.Row():
|
| 155 |
q = gr.Textbox(label="What was said (optional)", placeholder="customer wants a refund")
|
|
@@ -161,6 +204,7 @@ def ui(index_dir):
|
|
| 161 |
go.click(run_search, [q, tone, w], [hits, seg])
|
| 162 |
q.submit(run_search, [q, tone, w], [hits, seg])
|
| 163 |
hits.select(on_select, hits, seg)
|
|
|
|
| 164 |
with gr.Tab("Timeline"):
|
| 165 |
with gr.Row():
|
| 166 |
call = gr.Dropdown([(os.path.basename(c), c) for c in calls], value=calls[0], label="Call")
|
|
@@ -179,7 +223,11 @@ def ui(index_dir):
|
|
| 179 |
up_plot = gr.LinePlot()
|
| 180 |
up_hits = gr.Dataframe(label="Top segments (seconds)", interactive=False)
|
| 181 |
up_go.click(run_upload, [up, up_q, up_tone], [up_plot, up_hits])
|
| 182 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 183 |
|
| 184 |
|
| 185 |
def main():
|
|
|
|
| 101 |
return top, score[top]
|
| 102 |
|
| 103 |
|
| 104 |
+
# Look: dark violet, faint grid, film grain, one glowing lens. Matches assets/banner.svg.
|
| 105 |
+
HERO = """<div id="hero">
|
| 106 |
+
<div class="mark"><svg width="34" height="34" viewBox="0 0 38 38" fill="none" stroke="#fff" stroke-linecap="round">
|
| 107 |
+
<circle cx="17" cy="17" r="14" stroke-width="2.6"/><line x1="27.5" y1="27.5" x2="36" y2="36" stroke-width="3.6"/>
|
| 108 |
+
<line x1="11" y1="14" x2="11" y2="20" stroke-width="2.6"/><line x1="17" y1="9.5" x2="17" y2="24.5" stroke="#fb7185" stroke-width="2.6"/>
|
| 109 |
+
<line x1="23" y1="13" x2="23" y2="21" stroke-width="2.6"/></svg><span>tonesearch</span></div>
|
| 110 |
+
<p class="tag">Find the angry minute in your call recordings.</p>
|
| 111 |
+
<p class="sub">Search calls by <b>what</b> was said and <b>how</b> it was said. Or upload your own call.</p>
|
| 112 |
+
<div class="lens"><svg viewBox="0 0 200 100" preserveAspectRatio="none"><polyline points="0,78 20,74 35,80 50,72 62,76 75,70 85,62 92,30 100,14 108,30 115,62 125,70 140,74 155,68 170,75 185,70 200,74"/></svg></div>
|
| 113 |
+
</div>"""
|
| 114 |
+
CSS = """
|
| 115 |
+
html { background: #110e22 !important; }
|
| 116 |
+
body { background: radial-gradient(900px 520px at 88% -8%, rgba(99,102,241,.38), transparent 60%),
|
| 117 |
+
radial-gradient(700px 420px at 105% 45%, rgba(124,58,237,.18), transparent 60%),
|
| 118 |
+
linear-gradient(135deg, #2b1f57 0%, #1b1638 55%, #110e22 100%) fixed !important; }
|
| 119 |
+
body::before { content: ""; position: fixed; inset: 0; pointer-events: none; z-index: 0;
|
| 120 |
+
background-image: linear-gradient(rgba(255,255,255,.035) 1px, transparent 1px), linear-gradient(90deg, rgba(255,255,255,.035) 1px, transparent 1px);
|
| 121 |
+
background-size: 40px 40px; }
|
| 122 |
+
body::after { content: ""; position: fixed; inset: 0; pointer-events: none; z-index: 1000; opacity: .09;
|
| 123 |
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E"); }
|
| 124 |
+
.gradio-container { background: transparent !important; max-width: 1080px !important; position: relative; z-index: 1; }
|
| 125 |
+
#hero { position: relative; padding: 36px 8px 28px; overflow: visible; }
|
| 126 |
+
#hero .mark { display: flex; align-items: center; gap: 14px; }
|
| 127 |
+
#hero .mark span { font-size: 46px; font-weight: 300; letter-spacing: -1.2px; color: #fff; }
|
| 128 |
+
#hero .tag { margin: 44px 0 4px; font-size: 20px; color: #eef2ff; }
|
| 129 |
+
#hero .sub { margin: 0; font-size: 15px; font-weight: 300; color: #a5a3c8; }
|
| 130 |
+
#hero .lens { position: absolute; right: 40px; top: 22px; width: 170px; height: 170px; border-radius: 50%;
|
| 131 |
+
border: 2px solid #e0e7ff; background: rgba(15,12,36,.55); overflow: hidden;
|
| 132 |
+
box-shadow: 0 0 36px rgba(129,140,248,.55), inset 0 0 28px rgba(99,102,241,.35); }
|
| 133 |
+
#hero .lens svg { position: absolute; left: 0; top: 30px; width: 100%; height: 100px; }
|
| 134 |
+
#hero .lens polyline { fill: none; stroke: #fb7185; stroke-width: 2.2; vector-effect: non-scaling-stroke;
|
| 135 |
+
filter: drop-shadow(0 0 6px rgba(244,63,94,.8)); }
|
| 136 |
+
@media (max-width: 720px) { #hero .lens { display: none; } }
|
| 137 |
+
"""
|
| 138 |
+
DARK = dict(body_background_fill="transparent", body_text_color="#e0e7ff", body_text_color_subdued="#a5a3c8",
|
| 139 |
+
background_fill_primary="#15122e", background_fill_secondary="#1b1638",
|
| 140 |
+
block_background_fill="rgba(21,18,46,.72)", block_border_color="rgba(165,180,252,.14)",
|
| 141 |
+
block_label_text_color="#a5a3c8", block_title_text_color="#c7d2fe", border_color_primary="rgba(165,180,252,.14)",
|
| 142 |
+
input_background_fill="rgba(15,12,36,.8)", input_border_color="rgba(165,180,252,.22)",
|
| 143 |
+
button_primary_background_fill="linear-gradient(90deg, #6366f1, #8b5cf6)", button_primary_text_color="#ffffff",
|
| 144 |
+
button_secondary_background_fill="rgba(99,102,241,.14)", button_secondary_text_color="#e0e7ff",
|
| 145 |
+
table_even_background_fill="#17142f", table_odd_background_fill="#1b1638", color_accent_soft="rgba(99,102,241,.25)")
|
| 146 |
+
|
| 147 |
MAX_UPLOAD_S = 300 # ponytail: 5 min keeps one upload inside a free visitor's daily ZeroGPU quota
|
| 148 |
|
| 149 |
|
|
|
|
| 192 |
return plot, pd.DataFrame({"start": ux["start"][top].round(1), "end": ux["end"][top].round(1), "score": score.round(2)})
|
| 193 |
|
| 194 |
with gr.Blocks(title="tonesearch") as app:
|
| 195 |
+
gr.HTML(HERO)
|
| 196 |
with gr.Tab("Search"):
|
| 197 |
with gr.Row():
|
| 198 |
q = gr.Textbox(label="What was said (optional)", placeholder="customer wants a refund")
|
|
|
|
| 204 |
go.click(run_search, [q, tone, w], [hits, seg])
|
| 205 |
q.submit(run_search, [q, tone, w], [hits, seg])
|
| 206 |
hits.select(on_select, hits, seg)
|
| 207 |
+
app.load(run_search, [q, tone, w], [hits, seg]) # empty query = tone only, no GPU call
|
| 208 |
with gr.Tab("Timeline"):
|
| 209 |
with gr.Row():
|
| 210 |
call = gr.Dropdown([(os.path.basename(c), c) for c in calls], value=calls[0], label="Call")
|
|
|
|
| 223 |
up_plot = gr.LinePlot()
|
| 224 |
up_hits = gr.Dataframe(label="Top segments (seconds)", interactive=False)
|
| 225 |
up_go.click(run_upload, [up, up_q, up_tone], [up_plot, up_hits])
|
| 226 |
+
theme = gr.themes.Base(primary_hue="indigo", secondary_hue="rose", neutral_hue="slate",
|
| 227 |
+
font=[gr.themes.GoogleFont("Inter", weights=(300, 400, 600)), "Helvetica Neue", "sans-serif"])
|
| 228 |
+
theme.set(**DARK, **{k + "_dark": v for k, v in DARK.items()}) # same values both modes: always dark
|
| 229 |
+
app.launch(allowed_paths=sorted({os.path.dirname(c) for c in calls}), theme=theme, css=CSS,
|
| 230 |
+
js="() => document.body.classList.add('dark')")
|
| 231 |
|
| 232 |
|
| 233 |
def main():
|