Spaces:
Running
Running
style(ui): polish visuals - localize tabs/tooltips, progress bars, light theme, hide empty cards, mobile layout
Browse files- app.py +151 -136
- src/i18n.py +4 -0
- src/prompt_analyzer.py +11 -11
app.py
CHANGED
|
@@ -105,14 +105,21 @@ COPY_JS = """(text) => {
|
|
| 105 |
}"""
|
| 106 |
|
| 107 |
|
| 108 |
-
def on_generate(prompt, model, rating, num_variations, creativity, weight_mode, mode, lang, artist_style, selected_artists, use_tandems, selected_tandem_idx, web_enrich, seed, current_preset_state, fx_count, blacklist_text, mirror_blacklist, strip_quality, strip_artist, strip_lora, strip_meta, min_tags, user_preset_state, output_format, *checks) -> tuple
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
try:
|
| 110 |
if not prompt or not prompt.strip():
|
| 111 |
-
return tuple([""] * (MAX_OUTPUTS * 2))
|
| 112 |
parsed = parse_prompt(prompt)
|
| 113 |
if parsed is None:
|
| 114 |
msg = t("error_parse", lang)
|
| 115 |
-
|
|
|
|
| 116 |
active = [c for c, on in zip(ALL_CATEGORIES, checks) if on]
|
| 117 |
if not active:
|
| 118 |
active = ["quality", "lighting", "expression"]
|
|
@@ -191,12 +198,13 @@ def on_generate(prompt, model, rating, num_variations, creativity, weight_mode,
|
|
| 191 |
categories=active,
|
| 192 |
seed=int(seed) if seed is not None and str(seed).strip() else None,
|
| 193 |
)
|
| 194 |
-
return tuple(out[:MAX_OUTPUTS]) + tuple(neg_out[:MAX_OUTPUTS])
|
| 195 |
except Exception as exc:
|
| 196 |
import traceback
|
| 197 |
traceback.print_exc()
|
| 198 |
err_msg = t("generation_error", lang).format(exc=html.escape(str(exc)))
|
| 199 |
-
|
|
|
|
| 200 |
|
| 201 |
|
| 202 |
def on_suggest(prompt, model, rating, lang) -> str:
|
|
@@ -262,22 +270,22 @@ def on_web_search(query: str, lang: str) -> str:
|
|
| 262 |
from src.tag_searcher import search_tags
|
| 263 |
lc = "ru" if lang == "RU" else "en"
|
| 264 |
if not query or not query.strip():
|
| 265 |
-
return f'<div class="whyx-info-text" style="color:
|
| 266 |
try:
|
| 267 |
results = search_tags(query, warehouse)
|
| 268 |
parts = []
|
| 269 |
if results["local"]:
|
| 270 |
-
parts.append(f'<div style="margin-top:6px;font-size:13px;font-weight:600;">🔍 {t("web_search_offline", lc)} ({len(results["local"])})</div>')
|
| 271 |
for r in results["local"][:15]:
|
| 272 |
safe_tag = html.escape(r["tag"])
|
| 273 |
safe_cat = html.escape(r["category"])
|
| 274 |
-
parts.append(f'<span style="display:inline-block;background:
|
| 275 |
if results["web"]:
|
| 276 |
-
parts.append(f'<div style="margin-top:8px;font-size:13px;font-weight:600;">🌐 {t("web_search_online", lc)} ({len(results["web"])})</div>')
|
| 277 |
for r in results["web"][:MAX_OUTPUTS]:
|
| 278 |
safe_tag = html.escape(r.get("tag", ""))
|
| 279 |
count = r.get("post_count", 0)
|
| 280 |
-
parts.append(f'<span style="display:inline-block;background:
|
| 281 |
if not parts:
|
| 282 |
return f'<div class="whyx-info-text" style="font-size:12px;">{t("web_enrich_no_results", lc)}</div>'
|
| 283 |
return '<div style="line-height:1.8;">' + "".join(parts) + '</div>'
|
|
@@ -289,7 +297,7 @@ def _format_history_html(lang: str) -> str:
|
|
| 289 |
lc = "ru" if lang == "RU" else "en"
|
| 290 |
history = get_history()
|
| 291 |
lines = []
|
| 292 |
-
lines.append("<div style='background:
|
| 293 |
|
| 294 |
entries = history.get_all()[:5]
|
| 295 |
if not entries:
|
|
@@ -490,7 +498,7 @@ def _build_tagger_html(result: dict, lc: str) -> str:
|
|
| 490 |
nl_caption = (result.get("nl_caption") or "").strip()
|
| 491 |
if nl_caption:
|
| 492 |
lines.append(
|
| 493 |
-
f"<div class='whyx-tagger-panel-title'>
|
| 494 |
f"<div class='whyx-tagger-nl-caption'>{html.escape(nl_caption)}</div>"
|
| 495 |
)
|
| 496 |
|
|
@@ -534,8 +542,12 @@ def _build_tagger_html(result: dict, lc: str) -> str:
|
|
| 534 |
for name, score in result["ratings"].items():
|
| 535 |
color = rating_colors.get(name, "var(--text-dim)")
|
| 536 |
pct = int(score * 100)
|
| 537 |
-
|
| 538 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 539 |
|
| 540 |
if result["characters"]:
|
| 541 |
lines.append(f"<div class='whyx-tagger-panel-title' style='margin-top:10px;'>{t('tagger_characters', lc)}</div>")
|
|
@@ -549,8 +561,12 @@ def _build_tagger_html(result: dict, lc: str) -> str:
|
|
| 549 |
for name, score in list(result["general"].items())[:14]:
|
| 550 |
safe = html.escape(name.replace("_", " ").replace("(", "\\(").replace(")", "\\)"))
|
| 551 |
pct = int(score * 100)
|
| 552 |
-
|
| 553 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 554 |
lines.append("</div>")
|
| 555 |
lines.append("</div>")
|
| 556 |
return "\n".join(lines)
|
|
@@ -884,6 +900,29 @@ body.whyx-light, body.whyx-light .gradio-container, body.whyx-light .dark {
|
|
| 884 |
--button-secondary-background-fill: transparent !important; --button-secondary-background-fill-hover: rgba(15,23,42,0.05) !important;
|
| 885 |
--button-secondary-border-color: rgba(15,23,42,0.16) !important;
|
| 886 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 887 |
@keyframes whyxBreath { 0%, 100% { opacity: 0.65; } 50% { opacity: 1; } }
|
| 888 |
@keyframes whyxFadeIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
|
| 889 |
@keyframes progressShimmer { 0% { background-position: -200% 0; } 100% { background-position: 200% 0; } }
|
|
@@ -893,25 +932,10 @@ body::before { content: ""; position: fixed; inset: 0; z-index: -1; pointer-even
|
|
| 893 |
:focus-visible { outline: 2px solid rgba(90,200,245,0.6) !important; outline-offset: 2px !important; }
|
| 894 |
*::-webkit-scrollbar { width: 5px; height: 5px; } *::-webkit-scrollbar-track { background: rgba(15,23,42,0.25); } *::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.08); border-radius: 3px; } *::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.14); }
|
| 895 |
|
| 896 |
-
.whyx-section-card { background: var(--surface) !important; border: 1px solid var(--glass-border) !important; border-left: 3px solid rgba(110,231,210,0.18) !important; border-radius: var(--radius-lg) !important; box-shadow: var(--shadow-md), inset 0 1px 0 rgba(255,255,255,0.03) !important; transition: border-color var(--transition), box-shadow var(--transition), transform var(--transition) !important; overflow: visible !important; }
|
| 897 |
-
.whyx-section-card:hover { border-color: var(--glass-border-hover) !important; border-left-color: rgba(16,185,129,0.4) !important; box-shadow: var(--shadow-lg), var(--shadow-glow) !important; transform: translateY(-1px); }
|
| 898 |
-
.whyx-section-header { display: flex !important; align-items: flex-start !important; gap: 10px !important; margin: 0 0 14px !important; padding: 0 0 10px !important; border-bottom: 1px solid var(--glass-border) !important; animation: whyxFadeIn 0.3s ease both !important; }
|
| 899 |
-
.whyx-section-icon { font-size: 18px !important; line-height: 1.4 !important; flex-shrink: 0; }
|
| 900 |
-
.whyx-section-title { color: var(--text) !important; background: none !important; -webkit-background-clip: unset !important; -webkit-text-fill-color: unset !important; background-clip: unset !important; font-weight: 700 !important; font-size: 11px !important; letter-spacing: 1px !important; text-transform: uppercase !important; line-height: 1.4 !important; }
|
| 901 |
-
.whyx-section-desc { color: var(--text-dim) !important; font-size: 11px !important; line-height: 1.45 !important; margin-top: 2px !important; max-width: 640px !important; }
|
| 902 |
.whyx-title { background: linear-gradient(135deg, #7C3AED, #4F46E5 38%, #2563EB 70%, #10B981) !important; -webkit-background-clip: text !important; -webkit-text-fill-color: transparent !important; background-clip: text !important; color: transparent !important; display: inline-block !important; font-weight: 800 !important; font-size: 32px !important; letter-spacing: -0.6px !important; line-height: 1.2 !important; }
|
| 903 |
.whyx-subtitle { font-size: 12px !important; color: var(--text-dim) !important; margin-top: 2px !important; border-left: 2px solid rgba(110,231,210,0.45) !important; padding-left: 8px !important; }
|
| 904 |
.whyx-divider { height: 1px !important; background: linear-gradient(90deg, transparent, rgba(110,231,210,0.2), rgba(176,124,240,0.2), transparent) !important; margin: 10px 0 !important; border: none !important; }
|
| 905 |
|
| 906 |
-
.whyx-stepper { counter-reset: whyx-step; position: relative; gap: 4px; }
|
| 907 |
-
.whyx-stepper::before { content: ""; position: absolute; left: 25px; top: 30px; bottom: 44px; width: 2px; background: linear-gradient(180deg, var(--c0), var(--c1) 50%, var(--c3)); border-radius: 2px; opacity: 0.35; }
|
| 908 |
-
.whyx-step { position: relative; counter-increment: whyx-step; background: var(--surface) !important; border: 1px solid var(--glass-border) !important; border-radius: var(--radius-xl) !important; padding: 16px 18px 16px 60px !important; margin-bottom: 10px !important; box-shadow: var(--shadow-sm) !important; transition: border-color var(--transition), box-shadow var(--transition), transform var(--transition) !important; }
|
| 909 |
-
.whyx-step:hover { border-color: var(--glass-border-hover) !important; box-shadow: var(--shadow-md), 0 0 16px rgba(79,70,229,0.08) !important; transform: translateY(-1px); }
|
| 910 |
-
.whyx-step::before { content: counter(whyx-step); position: absolute; left: 10px; top: 50%; transform: translateY(-50%); width: 32px; height: 32px; border-radius: 50%; background: var(--accent); color: #06121A; font-weight: 800; font-size: 14px; display: flex; align-items: center; justify-content: center; box-shadow: 0 4px 14px rgba(79,70,229,0.25); z-index: 2; }
|
| 911 |
-
.whyx-step:hover::before { box-shadow: 0 6px 20px rgba(79,70,229,0.4); }
|
| 912 |
-
.whyx-step .whyx-field, .whyx-step .whyx-field:hover { border: none !important; box-shadow: none !important; transform: none !important; }
|
| 913 |
-
.whyx-step .whyx-field:focus-within { box-shadow: 0 0 0 2px rgba(79,70,229,0.14) !important; }
|
| 914 |
-
|
| 915 |
.whyx-field { border: 1px solid var(--input-border) !important; border-radius: var(--radius-md) !important; box-shadow: var(--shadow-sm) !important; transition: border-color var(--transition), box-shadow var(--transition), transform var(--transition) !important; }
|
| 916 |
.whyx-field:hover { border-color: var(--accent-border) !important; box-shadow: 0 0 0 3px var(--accent-glow), var(--shadow-sm) !important; transform: translateY(-1px); }
|
| 917 |
.whyx-field textarea, .whyx-field input[type="text"] { border: 1px solid var(--input-border) !important; border-radius: var(--radius-md) !important; box-shadow: var(--shadow-sm) !important; transition: border-color var(--transition), box-shadow var(--transition) !important; }
|
|
@@ -979,7 +1003,6 @@ input[type="range"]::-moz-range-thumb { width: 16px !important; height: 16px !im
|
|
| 979 |
.whyx-theme-btn label.selected { background: var(--accent-glow) !important; border-color: var(--accent-border) !important; color: var(--text-accent) !important; }
|
| 980 |
|
| 981 |
.whyx-info-text, .gradio-container .info { color: var(--text-dim) !important; font-size: 11px !important; line-height: 1.45 !important; border-left: 2px solid rgba(110,231,210,0.35) !important; background: var(--chip-bg) !important; padding: 6px 10px !important; border-radius: 0 var(--radius-sm) var(--radius-sm) 0 !important; margin-top: 4px !important; }
|
| 982 |
-
.whyx-info-highlight { color: var(--text-accent) !important; font-weight: 600 !important; }
|
| 983 |
.whyx-tooltip { position: relative !important; cursor: pointer !important; }
|
| 984 |
.whyx-tooltip:hover .whyx-tooltip-text, .whyx-tooltip:focus-within .whyx-tooltip-text { visibility: visible !important; opacity: 1 !important; }
|
| 985 |
.whyx-tooltip-text { visibility: hidden !important; opacity: 0 !important; position: absolute !important; z-index: 9999 !important; padding: 4px 8px !important; background: var(--surface) !important; color: var(--text) !important; font-size: 10px !important; font-weight: 600 !important; border-radius: var(--radius-sm) !important; border: 1px solid var(--glass-border) !important; white-space: nowrap !important; pointer-events: none !important; transition: opacity 0.15s ease !important; box-shadow: var(--shadow-md) !important; border-left: 2px solid rgba(110,231,210,0.35) !important; bottom: 110% !important; left: 50% !important; transform: translateX(-50%) !important; }
|
|
@@ -992,7 +1015,6 @@ input[type="range"]::-moz-range-thumb { width: 16px !important; height: 16px !im
|
|
| 992 |
.whyx-tagger-apply-btn button:hover { background: linear-gradient(135deg, #9D7CFA, #5B6BFA, #3B8BE0) !important; box-shadow: 0 6px 20px rgba(79,70,229,0.35) !important; transform: translateY(-1px) !important; }
|
| 993 |
.whyx-tagger-copy-btn button { background: var(--chip-bg) !important; border: 1px solid var(--chip-border) !important; color: var(--chip-text) !important; font-weight: 600 !important; box-shadow: none !important; }
|
| 994 |
.whyx-tagger-copy-btn button:hover:not(:disabled) { background: var(--accent-glow) !important; border-color: var(--accent-border) !important; color: var(--text-accent) !important; box-shadow: 0 4px 14px rgba(79,70,229,0.15) !important; }
|
| 995 |
-
.whyx-tagger-tags-card { border-radius: var(--radius-lg) !important; border: 1px solid rgba(129,140,248,0.14) !important; box-shadow: var(--shadow-md) !important; background: var(--surface) !important; }
|
| 996 |
.whyx-tagger-tags-box textarea { font-family: 'JetBrains Mono', ui-monospace, monospace !important; font-size: 12px !important; line-height: 1.5 !important; color: var(--input-text) !important; background: var(--input-bg) !important; border-radius: var(--radius-md) !important; }
|
| 997 |
.whyx-tagger-chips .wrap, .whyx-tagger-chips .grid { display: flex !important; flex-wrap: wrap !important; gap: 6px !important; max-height: 260px !important; overflow-y: auto !important; padding: 4px 2px 8px !important; }
|
| 998 |
.whyx-tagger-chips label { display: inline-flex !important; align-items: center !important; gap: 6px !important; border: 1px solid var(--chip-border) !important; background: var(--chip-bg) !important; border-radius: var(--radius-pill) !important; padding: 5px 12px !important; font-size: 12px !important; font-weight: 500 !important; color: var(--chip-text) !important; cursor: pointer !important; transition: all var(--transition-fast) !important; user-select: none !important; }
|
|
@@ -1001,16 +1023,18 @@ input[type="range"]::-moz-range-thumb { width: 16px !important; height: 16px !im
|
|
| 1001 |
.whyx-tagger-chips label:has(input:checked) { background: var(--accent-soft) !important; border-color: rgba(110,231,210,0.55) !important; color: #E6FBF5 !important; box-shadow: 0 0 10px rgba(79,70,229,0.18) !important; }
|
| 1002 |
.whyx-tagger-panel { background: var(--input-bg) !important; border: 1px solid rgba(79,70,229,0.1) !important; border-radius: var(--radius-md) !important; padding: 12px 16px !important; margin-top: 4px !important; animation: whyxFadeIn 0.3s ease both !important; }
|
| 1003 |
.whyx-tagger-panel-title { font-size: 12px !important; font-weight: 600 !important; color: var(--text) !important; margin-bottom: 6px !important; }
|
| 1004 |
-
.whyx-tagger-rating-row { font-size: 11px !important; color: var(--text-dim) !important; margin-bottom:
|
| 1005 |
-
.whyx-tagger-rating-name { width: 92px !important; display: inline-block !important; font-weight: 600 !important; }
|
| 1006 |
-
.whyx-tagger-rating-
|
| 1007 |
-
.whyx-tagger-rating-
|
|
|
|
| 1008 |
.whyx-tagger-badges { display: flex !important; flex-wrap: wrap !important; gap: 6px !important; }
|
| 1009 |
.whyx-tagger-char-badge { display: inline-block !important; background: linear-gradient(135deg, rgba(129,140,248,0.18), rgba(99,102,241,0.14)) !important; border: 1px solid rgba(129,140,248,0.3) !important; border-radius: var(--radius-pill) !important; padding: 3px 10px !important; font-size: 12px !important; color: #E0E7FF !important; }
|
| 1010 |
.whyx-tagger-badge-pct { color: #A5B4FC !important; font-size: 10px !important; margin-left: 2px !important; }
|
| 1011 |
.whyx-tagger-gen-list { display: flex !important; flex-direction: column !important; gap: 1px !important; }
|
| 1012 |
.whyx-tagger-gen-row { display: flex !important; align-items: center !important; gap: 8px !important; font-size: 11px !important; color: var(--text-dim) !important; white-space: nowrap !important; }
|
| 1013 |
-
.whyx-tagger-gen-
|
|
|
|
| 1014 |
.whyx-tagger-gen-name { overflow: hidden !important; text-overflow: ellipsis !important; }
|
| 1015 |
.whyx-tagger-pose-tags { font-size: 12px !important; color: var(--text-dim) !important; line-height: 1.4 !important; background: var(--input-bg) !important; border: 1px solid rgba(79,70,229,0.1) !important; border-radius: var(--radius-sm) !important; padding: 6px 10px !important; margin-top: 4px !important; }
|
| 1016 |
.whyx-tagger-pose-people { font-size: 11px !important; color: var(--text) !important; font-weight: 600 !important; }
|
|
@@ -1018,21 +1042,29 @@ input[type="range"]::-moz-range-thumb { width: 16px !important; height: 16px !im
|
|
| 1018 |
.whyx-tagger-anatomy-tags { font-size: 12px !important; color: var(--text-dim) !important; line-height: 1.4 !important; background: var(--input-bg) !important; border: 1px solid rgba(79,70,229,0.1) !important; border-radius: var(--radius-sm) !important; padding: 6px 10px !important; margin-top: 4px !important; }
|
| 1019 |
.whyx-tagger-expr-tags { font-size: 12px !important; color: var(--text-dim) !important; line-height: 1.4 !important; background: var(--input-bg) !important; border: 1px solid rgba(79,70,229,0.1) !important; border-radius: var(--radius-sm) !important; padding: 6px 10px !important; margin-top: 4px !important; }
|
| 1020 |
.whyx-tagger-bg-tags { font-size: 12px !important; color: var(--text-dim) !important; line-height: 1.4 !important; background: var(--input-bg) !important; border: 1px solid rgba(79,70,229,0.1) !important; border-radius: var(--radius-sm) !important; padding: 6px 10px !important; margin-top: 4px !important; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1021 |
|
| 1022 |
.progress-level .progress-bar { background: var(--accent) !important; background-size: 200% auto !important; animation: progressShimmer 2s linear infinite !important; }
|
| 1023 |
.progress-level .progress-text { color: var(--text) !important; font-size: 12px !important; }
|
| 1024 |
.progress-level { background: rgba(15,23,42,0.5) !important; border: 1px solid rgba(124,58,237,0.12) !important; border-radius: var(--radius-md) !important; padding: 8px 12px !important; }
|
| 1025 |
|
| 1026 |
-
.
|
| 1027 |
-
.whyx-accordion-group > .gradio-accordion { flex: 1 !important; min-width: 0 !important; }
|
| 1028 |
-
.whyx-accordion-group .gradio-accordion { background: var(--surface) !important; border: 1px solid var(--glass-border) !important; border-radius: var(--radius-md) !important; }
|
| 1029 |
-
.whyx-accordion-group .gradio-accordion label[class*="accordion"],
|
| 1030 |
-
.whyx-accordion-group .gradio-accordion button,
|
| 1031 |
-
.whyx-accordion-group .gradio-accordion summary {
|
| 1032 |
-
color: var(--text) !important; font-weight: 600 !important; font-size: 12px !important; padding: 8px 12px !important; border-bottom: 1px solid var(--glass-border) !important; cursor: pointer !important; background: transparent !important;
|
| 1033 |
-
}
|
| 1034 |
-
.whyx-accordion-group .gradio-accordion > div:not(label) { padding: 8px 12px !important; }
|
| 1035 |
-
.whyx-section-card, .gradio-dropdown, .gradio-dropdown > div, .tabs, .tabitem, [data-testid="tab-item"] { overflow: visible !important; }
|
| 1036 |
|
| 1037 |
/* --- Dropdown menu styling — opaque panel, themed --- */
|
| 1038 |
[role="listbox"], ul.options, ul.dropdown-options, [data-testid="dropdown-options"] {
|
|
@@ -1107,7 +1139,6 @@ ul.options::-webkit-scrollbar-thumb:hover { background: rgba(79,70,229,0.4) !imp
|
|
| 1107 |
.whyx-artist-preview-empty { color: var(--text-dim) !important; font-size: 12px !important; font-style: italic !important; }
|
| 1108 |
.whyx-artist-link { display: inline-flex !important; align-items: center !important; gap: 4px !important; border: 1px solid var(--chip-border) !important; border-radius: var(--radius-sm) !important; padding: 4px 10px !important; background: var(--chip-bg) !important; color: var(--chip-text) !important; text-decoration: none !important; font-size: 11px !important; font-weight: 600 !important; transition: all var(--transition-fast) !important; }
|
| 1109 |
.whyx-artist-link:hover { background: var(--accent-glow) !important; border-color: rgba(124,58,237,0.25) !important; box-shadow: 0 2px 8px rgba(79,70,229,0.08) !important; color: var(--text-accent) !important; }
|
| 1110 |
-
.whyx-tag-bar { width: 80px !important; display: inline-block !important; white-space: nowrap !important; font-family: monospace !important; }
|
| 1111 |
|
| 1112 |
@media (max-width: 767px) {
|
| 1113 |
.whyx-title { font-size: 24px !important; } .whyx-section-title { font-size: 11px !important; }
|
|
@@ -1125,6 +1156,14 @@ ul.options::-webkit-scrollbar-thumb:hover { background: rgba(79,70,229,0.4) !imp
|
|
| 1125 |
.tabs button { flex-shrink: 0 !important; font-size: 11px !important; padding: 6px 10px !important; }
|
| 1126 |
.whyx-tooltip-text { bottom: auto !important; top: 110% !important; }
|
| 1127 |
.whyx-tooltip-text::after { top: -8px !important; border-color: transparent transparent var(--surface) transparent !important; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1128 |
}
|
| 1129 |
@media (min-width: 768px) and (max-width: 1023px) {
|
| 1130 |
.whyx-title { font-size: 26px !important; }
|
|
@@ -1212,72 +1251,6 @@ ul.options::-webkit-scrollbar-thumb:hover { background: rgba(79,70,229,0.4) !imp
|
|
| 1212 |
to { opacity: 1; transform: translateY(0); }
|
| 1213 |
}
|
| 1214 |
|
| 1215 |
-
.whyx-tree-leaf {
|
| 1216 |
-
padding: 10px 14px !important;
|
| 1217 |
-
border-top: 1px solid var(--tree-node-border) !important;
|
| 1218 |
-
transition: background var(--tree-transition) !important;
|
| 1219 |
-
}
|
| 1220 |
-
|
| 1221 |
-
.whyx-tree-leaf:hover {
|
| 1222 |
-
background: var(--tree-leaf-hover) !important;
|
| 1223 |
-
}
|
| 1224 |
-
|
| 1225 |
-
.whyx-tree-leaf:first-child {
|
| 1226 |
-
border-top: none !important;
|
| 1227 |
-
}
|
| 1228 |
-
|
| 1229 |
-
.whyx-tree-indent {
|
| 1230 |
-
margin-left: var(--tree-indent) !important;
|
| 1231 |
-
}
|
| 1232 |
-
|
| 1233 |
-
.whyx-tree-connector {
|
| 1234 |
-
position: absolute !important;
|
| 1235 |
-
left: -8px !important;
|
| 1236 |
-
top: 0 !important;
|
| 1237 |
-
bottom: 0 !important;
|
| 1238 |
-
width: 2px !important;
|
| 1239 |
-
background: var(--tree-connector) !important;
|
| 1240 |
-
border-radius: 1px !important;
|
| 1241 |
-
}
|
| 1242 |
-
|
| 1243 |
-
.whyx-tree-connector:hover {
|
| 1244 |
-
background: var(--tree-connector-hover) !important;
|
| 1245 |
-
}
|
| 1246 |
-
|
| 1247 |
-
.whyx-tree-item {
|
| 1248 |
-
display: flex !important;
|
| 1249 |
-
align-items: center !important;
|
| 1250 |
-
gap: 8px !important;
|
| 1251 |
-
padding: 6px 0 !important;
|
| 1252 |
-
}
|
| 1253 |
-
|
| 1254 |
-
.whyx-tree-item-icon {
|
| 1255 |
-
font-size: 14px !important;
|
| 1256 |
-
line-height: 1 !important;
|
| 1257 |
-
flex-shrink: 0 !important;
|
| 1258 |
-
color: var(--text-accent) !important;
|
| 1259 |
-
}
|
| 1260 |
-
|
| 1261 |
-
.whyx-tree-item-label {
|
| 1262 |
-
color: var(--text) !important;
|
| 1263 |
-
font-size: 12px !important;
|
| 1264 |
-
font-weight: 500 !important;
|
| 1265 |
-
flex: 1 !important;
|
| 1266 |
-
}
|
| 1267 |
-
|
| 1268 |
-
.whyx-tree-item-value {
|
| 1269 |
-
color: var(--text-dim) !important;
|
| 1270 |
-
font-size: 11px !important;
|
| 1271 |
-
font-family: 'JetBrains Mono', ui-monospace, monospace !important;
|
| 1272 |
-
}
|
| 1273 |
-
|
| 1274 |
-
.whyx-tree-divider {
|
| 1275 |
-
height: 1px !important;
|
| 1276 |
-
background: linear-gradient(90deg, transparent, var(--tree-connector), transparent) !important;
|
| 1277 |
-
margin: 8px 0 !important;
|
| 1278 |
-
border: none !important;
|
| 1279 |
-
}
|
| 1280 |
-
|
| 1281 |
/* Tree node header (replaces _section for tree) */
|
| 1282 |
.whyx-tree-header {
|
| 1283 |
display: flex !important;
|
|
@@ -1451,7 +1424,8 @@ with gr.Blocks(**_blocks_kw) as demo:
|
|
| 1451 |
|
| 1452 |
with gr.Tabs():
|
| 1453 |
# ===== TAB 1: Generate =====
|
| 1454 |
-
|
|
|
|
| 1455 |
with gr.Column(elem_classes=["whyx-tree"]):
|
| 1456 |
# Tree root: Prompt
|
| 1457 |
with gr.Column(elem_classes=["whyx-tree-node"]):
|
|
@@ -1711,11 +1685,14 @@ with gr.Blocks(**_blocks_kw) as demo:
|
|
| 1711 |
result_boxes = []
|
| 1712 |
neg_boxes = []
|
| 1713 |
heart_btns = []
|
|
|
|
|
|
|
| 1714 |
for i in range(MAX_OUTPUTS):
|
| 1715 |
-
with gr.Row(elem_classes=["whyx-result-card"]) as card:
|
| 1716 |
-
|
|
|
|
| 1717 |
gr.HTML(f'<div class="whyx-result-num">#{i+1}</div>')
|
| 1718 |
-
with gr.Column(scale=1):
|
| 1719 |
rb = gr.Textbox(
|
| 1720 |
label="",
|
| 1721 |
show_label=False,
|
|
@@ -1725,7 +1702,7 @@ with gr.Blocks(**_blocks_kw) as demo:
|
|
| 1725 |
placeholder=t("result_placeholder", "en"),
|
| 1726 |
elem_classes=["whyx-result-text"],
|
| 1727 |
)
|
| 1728 |
-
with gr.Column(scale=0, min_width=36):
|
| 1729 |
cb_pos = gr.Button(
|
| 1730 |
"📋",
|
| 1731 |
size="sm",
|
|
@@ -1733,11 +1710,12 @@ with gr.Blocks(**_blocks_kw) as demo:
|
|
| 1733 |
scale=0,
|
| 1734 |
min_width=32,
|
| 1735 |
elem_classes=["whyx-copy-btn", "whyx-tooltip"],
|
| 1736 |
-
elem_id="copy-btn-pos",
|
| 1737 |
)
|
| 1738 |
cb_pos.click(fn=None, inputs=[rb], outputs=[], js=COPY_JS)
|
| 1739 |
-
gr.HTML('<span class="whyx-tooltip-text">
|
| 1740 |
-
|
|
|
|
| 1741 |
hb = gr.Button(
|
| 1742 |
"♡",
|
| 1743 |
size="sm",
|
|
@@ -1745,11 +1723,12 @@ with gr.Blocks(**_blocks_kw) as demo:
|
|
| 1745 |
scale=0,
|
| 1746 |
min_width=36,
|
| 1747 |
elem_classes=["whyx-heart-btn", "whyx-tooltip"],
|
| 1748 |
-
elem_id="heart-btn",
|
| 1749 |
)
|
| 1750 |
heart_btns.append(hb)
|
| 1751 |
-
gr.HTML('<span class="whyx-tooltip-text">
|
| 1752 |
-
|
|
|
|
| 1753 |
nb = gr.Textbox(
|
| 1754 |
label="",
|
| 1755 |
show_label=False,
|
|
@@ -1759,7 +1738,7 @@ with gr.Blocks(**_blocks_kw) as demo:
|
|
| 1759 |
placeholder=t("neg_placeholder", "en"),
|
| 1760 |
elem_classes=["whyx-neg-text"],
|
| 1761 |
)
|
| 1762 |
-
with gr.Column(scale=0, min_width=36):
|
| 1763 |
cb_neg = gr.Button(
|
| 1764 |
"📋",
|
| 1765 |
size="sm",
|
|
@@ -1767,15 +1746,17 @@ with gr.Blocks(**_blocks_kw) as demo:
|
|
| 1767 |
scale=0,
|
| 1768 |
min_width=32,
|
| 1769 |
elem_classes=["whyx-copy-btn", "whyx-tooltip"],
|
| 1770 |
-
elem_id="copy-btn-neg",
|
| 1771 |
)
|
| 1772 |
cb_neg.click(fn=None, inputs=[nb], outputs=[], js=COPY_JS)
|
| 1773 |
-
gr.HTML('<span class="whyx-tooltip-text">
|
|
|
|
| 1774 |
result_boxes.append(rb)
|
| 1775 |
neg_boxes.append(nb)
|
| 1776 |
|
| 1777 |
# ===== TAB 2: Tagger =====
|
| 1778 |
-
|
|
|
|
| 1779 |
with gr.Column(elem_classes=["whyx-tree"]):
|
| 1780 |
# Tree root: Tagger
|
| 1781 |
with gr.Column(elem_classes=["whyx-tree-node"]):
|
|
@@ -1912,7 +1893,8 @@ with gr.Blocks(**_blocks_kw) as demo:
|
|
| 1912 |
tagger_esc_state = gr.State("")
|
| 1913 |
|
| 1914 |
# ===== TAB 3: Tools =====
|
| 1915 |
-
|
|
|
|
| 1916 |
with gr.Column(elem_classes=["whyx-tree"]):
|
| 1917 |
# Tree root: Analyzer
|
| 1918 |
with gr.Column(elem_classes=["whyx-tree-node"]):
|
|
@@ -2025,7 +2007,8 @@ with gr.Blocks(**_blocks_kw) as demo:
|
|
| 2025 |
)
|
| 2026 |
|
| 2027 |
# ===== TAB 4: History =====
|
| 2028 |
-
|
|
|
|
| 2029 |
with gr.Column(elem_classes=["whyx-tree-node"]):
|
| 2030 |
history_header_html = gr.HTML(_tree_header("📜", "history_section", "en"))
|
| 2031 |
history_md = gr.HTML(
|
|
@@ -2049,7 +2032,7 @@ with gr.Blocks(**_blocks_kw) as demo:
|
|
| 2049 |
outputs=[],
|
| 2050 |
)
|
| 2051 |
web_search_clear.click(
|
| 2052 |
-
fn=lambda lang: f'<div class="whyx-info-text" style="color:
|
| 2053 |
inputs=[lang_state],
|
| 2054 |
outputs=[web_search_output],
|
| 2055 |
)
|
|
@@ -2206,7 +2189,7 @@ with gr.Blocks(**_blocks_kw) as demo:
|
|
| 2206 |
generate_btn.click(
|
| 2207 |
fn=on_generate,
|
| 2208 |
inputs=inputs_list,
|
| 2209 |
-
outputs=result_boxes + neg_boxes,
|
| 2210 |
).then(
|
| 2211 |
fn=lambda lang: gr.update(value=_format_history_html("ru" if lang == "RU" else "en")),
|
| 2212 |
inputs=[lang_state],
|
|
@@ -2400,6 +2383,13 @@ with gr.Blocks(**_blocks_kw) as demo:
|
|
| 2400 |
updates.append(gr.update(value=_format_artist_info([], lc)))
|
| 2401 |
updates.append(gr.update(value=f'<div class="whyx-info-text">{t("analyzer_desc", lc)}</div>'))
|
| 2402 |
updates.append(gr.update(value=_format_history_html(lc)))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2403 |
updates.append(choice)
|
| 2404 |
return updates
|
| 2405 |
|
|
@@ -2437,19 +2427,44 @@ with gr.Blocks(**_blocks_kw) as demo:
|
|
| 2437 |
presets_acc, user_preset_name, user_preset_tags, user_preset_save, user_preset_delete,
|
| 2438 |
user_preset_trigger, user_preset_dropdown, user_preset_apply, theme_toggle,
|
| 2439 |
] + category_checks + group_components + result_boxes + neg_boxes + [categories_desc_md, presets_desc_md, results_desc_md, hotkeys_hint_md, artist_info_md, analyzer_md, history_md,
|
| 2440 |
-
lang_state]
|
| 2441 |
|
| 2442 |
lang_btn.change(
|
| 2443 |
fn=update_lang,
|
| 2444 |
inputs=[lang_btn],
|
| 2445 |
outputs=lang_outputs,
|
| 2446 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2447 |
|
| 2448 |
theme_toggle.change(
|
| 2449 |
fn=None,
|
| 2450 |
inputs=[theme_toggle],
|
| 2451 |
outputs=[],
|
| 2452 |
-
js="(v) => { document.body.classList.toggle('whyx-light', v === 'light'); }",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2453 |
)
|
| 2454 |
|
| 2455 |
|
|
|
|
| 105 |
}"""
|
| 106 |
|
| 107 |
|
| 108 |
+
def on_generate(prompt, model, rating, num_variations, creativity, weight_mode, mode, lang, artist_style, selected_artists, use_tandems, selected_tandem_idx, web_enrich, seed, current_preset_state, fx_count, blacklist_text, mirror_blacklist, strip_quality, strip_artist, strip_lora, strip_meta, min_tags, user_preset_state, output_format, *checks) -> tuple:
|
| 109 |
+
def _card_updates(positives):
|
| 110 |
+
return tuple(
|
| 111 |
+
gr.update(visible=bool((positives[i] or "").strip()))
|
| 112 |
+
for i in range(MAX_OUTPUTS)
|
| 113 |
+
)
|
| 114 |
+
|
| 115 |
try:
|
| 116 |
if not prompt or not prompt.strip():
|
| 117 |
+
return tuple([""] * (MAX_OUTPUTS * 2)) + _card_updates([""] * MAX_OUTPUTS)
|
| 118 |
parsed = parse_prompt(prompt)
|
| 119 |
if parsed is None:
|
| 120 |
msg = t("error_parse", lang)
|
| 121 |
+
outs = [msg] + [""] * (MAX_OUTPUTS - 1)
|
| 122 |
+
return tuple(outs) + tuple([""] * MAX_OUTPUTS) + _card_updates(outs)
|
| 123 |
active = [c for c, on in zip(ALL_CATEGORIES, checks) if on]
|
| 124 |
if not active:
|
| 125 |
active = ["quality", "lighting", "expression"]
|
|
|
|
| 198 |
categories=active,
|
| 199 |
seed=int(seed) if seed is not None and str(seed).strip() else None,
|
| 200 |
)
|
| 201 |
+
return tuple(out[:MAX_OUTPUTS]) + tuple(neg_out[:MAX_OUTPUTS]) + _card_updates(out[:MAX_OUTPUTS])
|
| 202 |
except Exception as exc:
|
| 203 |
import traceback
|
| 204 |
traceback.print_exc()
|
| 205 |
err_msg = t("generation_error", lang).format(exc=html.escape(str(exc)))
|
| 206 |
+
outs = [err_msg] + [""] * (MAX_OUTPUTS - 1)
|
| 207 |
+
return tuple(outs) + tuple([""] * MAX_OUTPUTS) + _card_updates(outs)
|
| 208 |
|
| 209 |
|
| 210 |
def on_suggest(prompt, model, rating, lang) -> str:
|
|
|
|
| 270 |
from src.tag_searcher import search_tags
|
| 271 |
lc = "ru" if lang == "RU" else "en"
|
| 272 |
if not query or not query.strip():
|
| 273 |
+
return f'<div class="whyx-info-text" style="color:var(--text-faint);font-size:12px;">{t("web_search_empty", lc)}</div>'
|
| 274 |
try:
|
| 275 |
results = search_tags(query, warehouse)
|
| 276 |
parts = []
|
| 277 |
if results["local"]:
|
| 278 |
+
parts.append(f'<div style="margin-top:6px;font-size:13px;font-weight:600;color:var(--text);">🔍 {t("web_search_offline", lc)} ({len(results["local"])})</div>')
|
| 279 |
for r in results["local"][:15]:
|
| 280 |
safe_tag = html.escape(r["tag"])
|
| 281 |
safe_cat = html.escape(r["category"])
|
| 282 |
+
parts.append(f'<span style="display:inline-block;background:var(--accent-glow);border:1px solid var(--accent-border);border-radius:4px;padding:1px 6px;margin:2px;font-size:12px;color:var(--text);">{safe_tag} <span style="color:var(--text-faint);font-size:10px;">[{safe_cat}]</span></span>')
|
| 283 |
if results["web"]:
|
| 284 |
+
parts.append(f'<div style="margin-top:8px;font-size:13px;font-weight:600;color:var(--text);">🌐 {t("web_search_online", lc)} ({len(results["web"])})</div>')
|
| 285 |
for r in results["web"][:MAX_OUTPUTS]:
|
| 286 |
safe_tag = html.escape(r.get("tag", ""))
|
| 287 |
count = r.get("post_count", 0)
|
| 288 |
+
parts.append(f'<span style="display:inline-block;background:var(--accent-glow);border:1px solid var(--accent-border);border-radius:4px;padding:1px 6px;margin:2px;font-size:12px;color:var(--text);">{safe_tag} <span style="color:var(--text-faint);font-size:10px;">({count} posts)</span></span>')
|
| 289 |
if not parts:
|
| 290 |
return f'<div class="whyx-info-text" style="font-size:12px;">{t("web_enrich_no_results", lc)}</div>'
|
| 291 |
return '<div style="line-height:1.8;">' + "".join(parts) + '</div>'
|
|
|
|
| 297 |
lc = "ru" if lang == "RU" else "en"
|
| 298 |
history = get_history()
|
| 299 |
lines = []
|
| 300 |
+
lines.append("<div style='background:var(--input-bg);border:1px solid rgba(124,58,237,0.12);border-radius:10px;padding:10px 14px;margin-top:4px;'>")
|
| 301 |
|
| 302 |
entries = history.get_all()[:5]
|
| 303 |
if not entries:
|
|
|
|
| 498 |
nl_caption = (result.get("nl_caption") or "").strip()
|
| 499 |
if nl_caption:
|
| 500 |
lines.append(
|
| 501 |
+
f"<div class='whyx-tagger-panel-title'>{t('tagger_nl_caption', lc)}</div>"
|
| 502 |
f"<div class='whyx-tagger-nl-caption'>{html.escape(nl_caption)}</div>"
|
| 503 |
)
|
| 504 |
|
|
|
|
| 542 |
for name, score in result["ratings"].items():
|
| 543 |
color = rating_colors.get(name, "var(--text-dim)")
|
| 544 |
pct = int(score * 100)
|
| 545 |
+
lines.append(
|
| 546 |
+
f"<div class='whyx-tagger-rating-row'>"
|
| 547 |
+
f"<span class='whyx-tagger-rating-name' style='color:{color};'>{name}</span>"
|
| 548 |
+
f"<span class='whyx-tagger-rating-track'><span class='whyx-tagger-rating-fill' style='width:{pct}%;background:{color};'></span></span>"
|
| 549 |
+
f"<span class='whyx-tagger-rating-pct'>({score:.1%})</span></div>"
|
| 550 |
+
)
|
| 551 |
|
| 552 |
if result["characters"]:
|
| 553 |
lines.append(f"<div class='whyx-tagger-panel-title' style='margin-top:10px;'>{t('tagger_characters', lc)}</div>")
|
|
|
|
| 561 |
for name, score in list(result["general"].items())[:14]:
|
| 562 |
safe = html.escape(name.replace("_", " ").replace("(", "\\(").replace(")", "\\)"))
|
| 563 |
pct = int(score * 100)
|
| 564 |
+
lines.append(
|
| 565 |
+
f"<div class='whyx-tagger-gen-row'>"
|
| 566 |
+
f"<span class='whyx-tagger-gen-track'><span class='whyx-tagger-gen-fill' style='width:{pct}%;'></span></span>"
|
| 567 |
+
f"<span class='whyx-tagger-gen-name'>{safe}</span>"
|
| 568 |
+
f"<span class='whyx-tagger-badge-pct'>{score:.0%}</span></div>"
|
| 569 |
+
)
|
| 570 |
lines.append("</div>")
|
| 571 |
lines.append("</div>")
|
| 572 |
return "\n".join(lines)
|
|
|
|
| 900 |
--button-secondary-background-fill: transparent !important; --button-secondary-background-fill-hover: rgba(15,23,42,0.05) !important;
|
| 901 |
--button-secondary-border-color: rgba(15,23,42,0.16) !important;
|
| 902 |
}
|
| 903 |
+
/* --- Light theme component overrides --- */
|
| 904 |
+
body.whyx-light {
|
| 905 |
+
--tree-node-bg: rgba(255,255,255,0.92);
|
| 906 |
+
--tree-node-border: rgba(15,23,42,0.08);
|
| 907 |
+
--tree-node-border-hover: rgba(15,23,42,0.16);
|
| 908 |
+
--tree-branch-hover: rgba(79,70,229,0.06);
|
| 909 |
+
--tree-leaf-hover: rgba(241,245,249,0.9);
|
| 910 |
+
}
|
| 911 |
+
body.whyx-light::before { opacity: 0.45 !important; }
|
| 912 |
+
body.whyx-light [role="listbox"], body.whyx-light ul.options, body.whyx-light ul.dropdown-options, body.whyx-light [data-testid="dropdown-options"] {
|
| 913 |
+
background: rgba(255,255,255,0.98) !important;
|
| 914 |
+
border: 1px solid rgba(15,23,42,0.12) !important;
|
| 915 |
+
box-shadow: 0 8px 32px rgba(15,23,42,0.16), 0 0 0 1px rgba(79,70,229,0.05) !important;
|
| 916 |
+
}
|
| 917 |
+
body.whyx-light .gradio-dropdown { background: #FFFFFF !important; }
|
| 918 |
+
body.whyx-light .whyx-tagger-char-badge { background: linear-gradient(135deg, rgba(129,140,248,0.14), rgba(99,102,241,0.1)) !important; border-color: rgba(99,102,241,0.35) !important; color: #3730A3 !important; }
|
| 919 |
+
body.whyx-light .whyx-tagger-badge-pct { color: #6366F1 !important; }
|
| 920 |
+
body.whyx-light .progress-level { background: rgba(255,255,255,0.85) !important; border-color: rgba(124,58,237,0.18) !important; }
|
| 921 |
+
body.whyx-light .whyx-tagger-chips label:has(input:checked) { color: #0F766E !important; }
|
| 922 |
+
body.whyx-light .whyx-slide-trigger { background: linear-gradient(135deg, rgba(255,255,255,0.85), rgba(241,245,249,0.65)) !important; border-color: rgba(15,23,42,0.12) !important; }
|
| 923 |
+
body.whyx-light *::-webkit-scrollbar-track { background: rgba(15,23,42,0.05) !important; }
|
| 924 |
+
body.whyx-light *::-webkit-scrollbar-thumb { background: rgba(15,23,42,0.18) !important; }
|
| 925 |
+
body.whyx-light *::-webkit-scrollbar-thumb:hover { background: rgba(15,23,42,0.3) !important; }
|
| 926 |
@keyframes whyxBreath { 0%, 100% { opacity: 0.65; } 50% { opacity: 1; } }
|
| 927 |
@keyframes whyxFadeIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
|
| 928 |
@keyframes progressShimmer { 0% { background-position: -200% 0; } 100% { background-position: 200% 0; } }
|
|
|
|
| 932 |
:focus-visible { outline: 2px solid rgba(90,200,245,0.6) !important; outline-offset: 2px !important; }
|
| 933 |
*::-webkit-scrollbar { width: 5px; height: 5px; } *::-webkit-scrollbar-track { background: rgba(15,23,42,0.25); } *::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.08); border-radius: 3px; } *::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.14); }
|
| 934 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 935 |
.whyx-title { background: linear-gradient(135deg, #7C3AED, #4F46E5 38%, #2563EB 70%, #10B981) !important; -webkit-background-clip: text !important; -webkit-text-fill-color: transparent !important; background-clip: text !important; color: transparent !important; display: inline-block !important; font-weight: 800 !important; font-size: 32px !important; letter-spacing: -0.6px !important; line-height: 1.2 !important; }
|
| 936 |
.whyx-subtitle { font-size: 12px !important; color: var(--text-dim) !important; margin-top: 2px !important; border-left: 2px solid rgba(110,231,210,0.45) !important; padding-left: 8px !important; }
|
| 937 |
.whyx-divider { height: 1px !important; background: linear-gradient(90deg, transparent, rgba(110,231,210,0.2), rgba(176,124,240,0.2), transparent) !important; margin: 10px 0 !important; border: none !important; }
|
| 938 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 939 |
.whyx-field { border: 1px solid var(--input-border) !important; border-radius: var(--radius-md) !important; box-shadow: var(--shadow-sm) !important; transition: border-color var(--transition), box-shadow var(--transition), transform var(--transition) !important; }
|
| 940 |
.whyx-field:hover { border-color: var(--accent-border) !important; box-shadow: 0 0 0 3px var(--accent-glow), var(--shadow-sm) !important; transform: translateY(-1px); }
|
| 941 |
.whyx-field textarea, .whyx-field input[type="text"] { border: 1px solid var(--input-border) !important; border-radius: var(--radius-md) !important; box-shadow: var(--shadow-sm) !important; transition: border-color var(--transition), box-shadow var(--transition) !important; }
|
|
|
|
| 1003 |
.whyx-theme-btn label.selected { background: var(--accent-glow) !important; border-color: var(--accent-border) !important; color: var(--text-accent) !important; }
|
| 1004 |
|
| 1005 |
.whyx-info-text, .gradio-container .info { color: var(--text-dim) !important; font-size: 11px !important; line-height: 1.45 !important; border-left: 2px solid rgba(110,231,210,0.35) !important; background: var(--chip-bg) !important; padding: 6px 10px !important; border-radius: 0 var(--radius-sm) var(--radius-sm) 0 !important; margin-top: 4px !important; }
|
|
|
|
| 1006 |
.whyx-tooltip { position: relative !important; cursor: pointer !important; }
|
| 1007 |
.whyx-tooltip:hover .whyx-tooltip-text, .whyx-tooltip:focus-within .whyx-tooltip-text { visibility: visible !important; opacity: 1 !important; }
|
| 1008 |
.whyx-tooltip-text { visibility: hidden !important; opacity: 0 !important; position: absolute !important; z-index: 9999 !important; padding: 4px 8px !important; background: var(--surface) !important; color: var(--text) !important; font-size: 10px !important; font-weight: 600 !important; border-radius: var(--radius-sm) !important; border: 1px solid var(--glass-border) !important; white-space: nowrap !important; pointer-events: none !important; transition: opacity 0.15s ease !important; box-shadow: var(--shadow-md) !important; border-left: 2px solid rgba(110,231,210,0.35) !important; bottom: 110% !important; left: 50% !important; transform: translateX(-50%) !important; }
|
|
|
|
| 1015 |
.whyx-tagger-apply-btn button:hover { background: linear-gradient(135deg, #9D7CFA, #5B6BFA, #3B8BE0) !important; box-shadow: 0 6px 20px rgba(79,70,229,0.35) !important; transform: translateY(-1px) !important; }
|
| 1016 |
.whyx-tagger-copy-btn button { background: var(--chip-bg) !important; border: 1px solid var(--chip-border) !important; color: var(--chip-text) !important; font-weight: 600 !important; box-shadow: none !important; }
|
| 1017 |
.whyx-tagger-copy-btn button:hover:not(:disabled) { background: var(--accent-glow) !important; border-color: var(--accent-border) !important; color: var(--text-accent) !important; box-shadow: 0 4px 14px rgba(79,70,229,0.15) !important; }
|
|
|
|
| 1018 |
.whyx-tagger-tags-box textarea { font-family: 'JetBrains Mono', ui-monospace, monospace !important; font-size: 12px !important; line-height: 1.5 !important; color: var(--input-text) !important; background: var(--input-bg) !important; border-radius: var(--radius-md) !important; }
|
| 1019 |
.whyx-tagger-chips .wrap, .whyx-tagger-chips .grid { display: flex !important; flex-wrap: wrap !important; gap: 6px !important; max-height: 260px !important; overflow-y: auto !important; padding: 4px 2px 8px !important; }
|
| 1020 |
.whyx-tagger-chips label { display: inline-flex !important; align-items: center !important; gap: 6px !important; border: 1px solid var(--chip-border) !important; background: var(--chip-bg) !important; border-radius: var(--radius-pill) !important; padding: 5px 12px !important; font-size: 12px !important; font-weight: 500 !important; color: var(--chip-text) !important; cursor: pointer !important; transition: all var(--transition-fast) !important; user-select: none !important; }
|
|
|
|
| 1023 |
.whyx-tagger-chips label:has(input:checked) { background: var(--accent-soft) !important; border-color: rgba(110,231,210,0.55) !important; color: #E6FBF5 !important; box-shadow: 0 0 10px rgba(79,70,229,0.18) !important; }
|
| 1024 |
.whyx-tagger-panel { background: var(--input-bg) !important; border: 1px solid rgba(79,70,229,0.1) !important; border-radius: var(--radius-md) !important; padding: 12px 16px !important; margin-top: 4px !important; animation: whyxFadeIn 0.3s ease both !important; }
|
| 1025 |
.whyx-tagger-panel-title { font-size: 12px !important; font-weight: 600 !important; color: var(--text) !important; margin-bottom: 6px !important; }
|
| 1026 |
+
.whyx-tagger-rating-row { font-size: 11px !important; color: var(--text-dim) !important; margin-bottom: 4px !important; display: flex !important; align-items: center !important; gap: 8px !important; }
|
| 1027 |
+
.whyx-tagger-rating-name { width: 92px !important; display: inline-block !important; font-weight: 600 !important; flex-shrink: 0 !important; }
|
| 1028 |
+
.whyx-tagger-rating-track { flex: 1 !important; height: 6px !important; background: var(--chip-bg) !important; border: 1px solid var(--chip-border) !important; border-radius: var(--radius-pill) !important; overflow: hidden !important; }
|
| 1029 |
+
.whyx-tagger-rating-fill { display: block !important; height: 100% !important; border-radius: var(--radius-pill) !important; transition: width 0.4s ease !important; }
|
| 1030 |
+
.whyx-tagger-rating-pct { color: var(--text-faint) !important; flex-shrink: 0 !important; }
|
| 1031 |
.whyx-tagger-badges { display: flex !important; flex-wrap: wrap !important; gap: 6px !important; }
|
| 1032 |
.whyx-tagger-char-badge { display: inline-block !important; background: linear-gradient(135deg, rgba(129,140,248,0.18), rgba(99,102,241,0.14)) !important; border: 1px solid rgba(129,140,248,0.3) !important; border-radius: var(--radius-pill) !important; padding: 3px 10px !important; font-size: 12px !important; color: #E0E7FF !important; }
|
| 1033 |
.whyx-tagger-badge-pct { color: #A5B4FC !important; font-size: 10px !important; margin-left: 2px !important; }
|
| 1034 |
.whyx-tagger-gen-list { display: flex !important; flex-direction: column !important; gap: 1px !important; }
|
| 1035 |
.whyx-tagger-gen-row { display: flex !important; align-items: center !important; gap: 8px !important; font-size: 11px !important; color: var(--text-dim) !important; white-space: nowrap !important; }
|
| 1036 |
+
.whyx-tagger-gen-track { width: 90px !important; height: 5px !important; background: var(--chip-bg) !important; border: 1px solid var(--chip-border) !important; border-radius: var(--radius-pill) !important; overflow: hidden !important; flex-shrink: 0 !important; }
|
| 1037 |
+
.whyx-tagger-gen-fill { display: block !important; height: 100% !important; background: linear-gradient(90deg, #5AC8F5, #38BDF8) !important; border-radius: var(--radius-pill) !important; transition: width 0.4s ease !important; }
|
| 1038 |
.whyx-tagger-gen-name { overflow: hidden !important; text-overflow: ellipsis !important; }
|
| 1039 |
.whyx-tagger-pose-tags { font-size: 12px !important; color: var(--text-dim) !important; line-height: 1.4 !important; background: var(--input-bg) !important; border: 1px solid rgba(79,70,229,0.1) !important; border-radius: var(--radius-sm) !important; padding: 6px 10px !important; margin-top: 4px !important; }
|
| 1040 |
.whyx-tagger-pose-people { font-size: 11px !important; color: var(--text) !important; font-weight: 600 !important; }
|
|
|
|
| 1042 |
.whyx-tagger-anatomy-tags { font-size: 12px !important; color: var(--text-dim) !important; line-height: 1.4 !important; background: var(--input-bg) !important; border: 1px solid rgba(79,70,229,0.1) !important; border-radius: var(--radius-sm) !important; padding: 6px 10px !important; margin-top: 4px !important; }
|
| 1043 |
.whyx-tagger-expr-tags { font-size: 12px !important; color: var(--text-dim) !important; line-height: 1.4 !important; background: var(--input-bg) !important; border: 1px solid rgba(79,70,229,0.1) !important; border-radius: var(--radius-sm) !important; padding: 6px 10px !important; margin-top: 4px !important; }
|
| 1044 |
.whyx-tagger-bg-tags { font-size: 12px !important; color: var(--text-dim) !important; line-height: 1.4 !important; background: var(--input-bg) !important; border: 1px solid rgba(79,70,229,0.1) !important; border-radius: var(--radius-sm) !important; padding: 6px 10px !important; margin-top: 4px !important; }
|
| 1045 |
+
.whyx-tagger-seg-tags { font-size: 12px !important; color: var(--text-dim) !important; line-height: 1.4 !important; background: var(--input-bg) !important; border: 1px solid rgba(16,185,129,0.16) !important; border-radius: var(--radius-sm) !important; padding: 6px 10px !important; margin-top: 4px !important; }
|
| 1046 |
+
.whyx-tagger-depth-tags { font-size: 12px !important; color: var(--text-dim) !important; line-height: 1.4 !important; background: var(--input-bg) !important; border: 1px solid rgba(37,99,235,0.18) !important; border-radius: var(--radius-sm) !important; padding: 6px 10px !important; margin-top: 4px !important; }
|
| 1047 |
+
.whyx-tagger-control-img { border-radius: var(--radius-md) !important; overflow: hidden !important; border: 1px solid var(--glass-border) !important; }
|
| 1048 |
+
.whyx-tagger-control-img img { border-radius: var(--radius-md) !important; }
|
| 1049 |
+
|
| 1050 |
+
.whyx-suggest-box { background: var(--input-bg) !important; border: 1px solid rgba(124,58,237,0.14) !important; border-radius: var(--radius-md) !important; padding: 10px 14px !important; margin-top: 6px !important; animation: whyxFadeIn 0.3s ease both !important; }
|
| 1051 |
+
.whyx-suggest-title { font-size: 12px !important; font-weight: 600 !important; color: var(--text) !important; margin-bottom: 6px !important; }
|
| 1052 |
+
.whyx-suggest-box ul { margin: 0 !important; padding-left: 18px !important; }
|
| 1053 |
+
.whyx-suggest-item { font-size: 12px !important; color: var(--text-dim) !important; line-height: 1.5 !important; padding: 1px 0 !important; }
|
| 1054 |
+
|
| 1055 |
+
.whyx-chip-group { display: flex !important; flex-wrap: wrap !important; gap: 6px !important; }
|
| 1056 |
+
.whyx-chip-group label { display: inline-flex !important; align-items: center !important; border: 1px solid var(--chip-border) !important; background: var(--chip-bg) !important; border-radius: var(--radius-pill) !important; padding: 5px 12px !important; font-size: 11px !important; cursor: pointer !important; transition: all var(--transition-fast) !important; user-select: none !important; }
|
| 1057 |
+
.whyx-chip-group label:hover { background: var(--accent-glow) !important; border-color: var(--accent-border) !important; }
|
| 1058 |
+
.whyx-chip-group label:has(input:checked) { background: var(--accent-glow) !important; border-color: var(--accent-border) !important; color: var(--text-accent) !important; }
|
| 1059 |
+
.whyx-checkbox label { font-size: 12px !important; color: var(--text-dim) !important; }
|
| 1060 |
+
.whyx-strip-row { gap: 10px !important; flex-wrap: wrap !important; }
|
| 1061 |
+
.whyx-strip-row .gradio-checkbox { min-width: 0 !important; }
|
| 1062 |
|
| 1063 |
.progress-level .progress-bar { background: var(--accent) !important; background-size: 200% auto !important; animation: progressShimmer 2s linear infinite !important; }
|
| 1064 |
.progress-level .progress-text { color: var(--text) !important; font-size: 12px !important; }
|
| 1065 |
.progress-level { background: rgba(15,23,42,0.5) !important; border: 1px solid rgba(124,58,237,0.12) !important; border-radius: var(--radius-md) !important; padding: 8px 12px !important; }
|
| 1066 |
|
| 1067 |
+
.gradio-dropdown, .gradio-dropdown > div, .tabs, .tabitem, [data-testid="tab-item"] { overflow: visible !important; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1068 |
|
| 1069 |
/* --- Dropdown menu styling — opaque panel, themed --- */
|
| 1070 |
[role="listbox"], ul.options, ul.dropdown-options, [data-testid="dropdown-options"] {
|
|
|
|
| 1139 |
.whyx-artist-preview-empty { color: var(--text-dim) !important; font-size: 12px !important; font-style: italic !important; }
|
| 1140 |
.whyx-artist-link { display: inline-flex !important; align-items: center !important; gap: 4px !important; border: 1px solid var(--chip-border) !important; border-radius: var(--radius-sm) !important; padding: 4px 10px !important; background: var(--chip-bg) !important; color: var(--chip-text) !important; text-decoration: none !important; font-size: 11px !important; font-weight: 600 !important; transition: all var(--transition-fast) !important; }
|
| 1141 |
.whyx-artist-link:hover { background: var(--accent-glow) !important; border-color: rgba(124,58,237,0.25) !important; box-shadow: 0 2px 8px rgba(79,70,229,0.08) !important; color: var(--text-accent) !important; }
|
|
|
|
| 1142 |
|
| 1143 |
@media (max-width: 767px) {
|
| 1144 |
.whyx-title { font-size: 24px !important; } .whyx-section-title { font-size: 11px !important; }
|
|
|
|
| 1156 |
.tabs button { flex-shrink: 0 !important; font-size: 11px !important; padding: 6px 10px !important; }
|
| 1157 |
.whyx-tooltip-text { bottom: auto !important; top: 110% !important; }
|
| 1158 |
.whyx-tooltip-text::after { top: -8px !important; border-color: transparent transparent var(--surface) transparent !important; }
|
| 1159 |
+
/* Result cards: compact wrapped layout instead of a tall vertical stack */
|
| 1160 |
+
#root .whyx-result-card { flex-direction: row !important; flex-wrap: wrap !important; align-items: flex-start !important; gap: 6px !important; }
|
| 1161 |
+
#root .whyx-result-card > .whyx-rc-num { width: auto !important; flex: 0 0 auto !important; }
|
| 1162 |
+
#root .whyx-result-card > .whyx-rc-pos { width: auto !important; flex: 1 1 60% !important; min-width: 0 !important; }
|
| 1163 |
+
#root .whyx-result-card > .whyx-rc-copypos,
|
| 1164 |
+
#root .whyx-result-card > .whyx-rc-heart { width: auto !important; flex: 0 0 auto !important; }
|
| 1165 |
+
#root .whyx-result-card > .whyx-rc-neg { width: auto !important; flex: 1 1 70% !important; min-width: 0 !important; }
|
| 1166 |
+
#root .whyx-result-card > .whyx-rc-copyneg { width: auto !important; flex: 0 0 auto !important; }
|
| 1167 |
}
|
| 1168 |
@media (min-width: 768px) and (max-width: 1023px) {
|
| 1169 |
.whyx-title { font-size: 26px !important; }
|
|
|
|
| 1251 |
to { opacity: 1; transform: translateY(0); }
|
| 1252 |
}
|
| 1253 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1254 |
/* Tree node header (replaces _section for tree) */
|
| 1255 |
.whyx-tree-header {
|
| 1256 |
display: flex !important;
|
|
|
|
| 1424 |
|
| 1425 |
with gr.Tabs():
|
| 1426 |
# ===== TAB 1: Generate =====
|
| 1427 |
+
tab_generate = gr.TabItem(f"✨ {t('tab_generate', 'en')}")
|
| 1428 |
+
with tab_generate:
|
| 1429 |
with gr.Column(elem_classes=["whyx-tree"]):
|
| 1430 |
# Tree root: Prompt
|
| 1431 |
with gr.Column(elem_classes=["whyx-tree-node"]):
|
|
|
|
| 1685 |
result_boxes = []
|
| 1686 |
neg_boxes = []
|
| 1687 |
heart_btns = []
|
| 1688 |
+
result_cards = []
|
| 1689 |
+
tooltip_spans = [] # (component, "copy" | "fav") for i18n updates
|
| 1690 |
for i in range(MAX_OUTPUTS):
|
| 1691 |
+
with gr.Row(elem_classes=["whyx-result-card"], visible=False) as card:
|
| 1692 |
+
result_cards.append(card)
|
| 1693 |
+
with gr.Column(scale=0, min_width=40, elem_classes=["whyx-rc-num"]):
|
| 1694 |
gr.HTML(f'<div class="whyx-result-num">#{i+1}</div>')
|
| 1695 |
+
with gr.Column(scale=1, elem_classes=["whyx-rc-pos"]):
|
| 1696 |
rb = gr.Textbox(
|
| 1697 |
label="",
|
| 1698 |
show_label=False,
|
|
|
|
| 1702 |
placeholder=t("result_placeholder", "en"),
|
| 1703 |
elem_classes=["whyx-result-text"],
|
| 1704 |
)
|
| 1705 |
+
with gr.Column(scale=0, min_width=36, elem_classes=["whyx-rc-copypos"]):
|
| 1706 |
cb_pos = gr.Button(
|
| 1707 |
"📋",
|
| 1708 |
size="sm",
|
|
|
|
| 1710 |
scale=0,
|
| 1711 |
min_width=32,
|
| 1712 |
elem_classes=["whyx-copy-btn", "whyx-tooltip"],
|
| 1713 |
+
elem_id=f"copy-btn-pos-{i}",
|
| 1714 |
)
|
| 1715 |
cb_pos.click(fn=None, inputs=[rb], outputs=[], js=COPY_JS)
|
| 1716 |
+
tt_copy_pos = gr.HTML(f'<span class="whyx-tooltip-text">{t("tooltip_copy", "en")}</span>')
|
| 1717 |
+
tooltip_spans.append((tt_copy_pos, "copy"))
|
| 1718 |
+
with gr.Column(scale=0, min_width=46, elem_classes=["whyx-rc-heart"]):
|
| 1719 |
hb = gr.Button(
|
| 1720 |
"♡",
|
| 1721 |
size="sm",
|
|
|
|
| 1723 |
scale=0,
|
| 1724 |
min_width=36,
|
| 1725 |
elem_classes=["whyx-heart-btn", "whyx-tooltip"],
|
| 1726 |
+
elem_id=f"heart-btn-{i}",
|
| 1727 |
)
|
| 1728 |
heart_btns.append(hb)
|
| 1729 |
+
tt_fav = gr.HTML(f'<span class="whyx-tooltip-text">{t("tooltip_favorite", "en")}</span>')
|
| 1730 |
+
tooltip_spans.append((tt_fav, "fav"))
|
| 1731 |
+
with gr.Column(scale=1, elem_classes=["whyx-rc-neg"]):
|
| 1732 |
nb = gr.Textbox(
|
| 1733 |
label="",
|
| 1734 |
show_label=False,
|
|
|
|
| 1738 |
placeholder=t("neg_placeholder", "en"),
|
| 1739 |
elem_classes=["whyx-neg-text"],
|
| 1740 |
)
|
| 1741 |
+
with gr.Column(scale=0, min_width=36, elem_classes=["whyx-rc-copyneg"]):
|
| 1742 |
cb_neg = gr.Button(
|
| 1743 |
"📋",
|
| 1744 |
size="sm",
|
|
|
|
| 1746 |
scale=0,
|
| 1747 |
min_width=32,
|
| 1748 |
elem_classes=["whyx-copy-btn", "whyx-tooltip"],
|
| 1749 |
+
elem_id=f"copy-btn-neg-{i}",
|
| 1750 |
)
|
| 1751 |
cb_neg.click(fn=None, inputs=[nb], outputs=[], js=COPY_JS)
|
| 1752 |
+
tt_copy_neg = gr.HTML(f'<span class="whyx-tooltip-text">{t("tooltip_copy", "en")}</span>')
|
| 1753 |
+
tooltip_spans.append((tt_copy_neg, "copy"))
|
| 1754 |
result_boxes.append(rb)
|
| 1755 |
neg_boxes.append(nb)
|
| 1756 |
|
| 1757 |
# ===== TAB 2: Tagger =====
|
| 1758 |
+
tab_tagger = gr.TabItem(f"🖼️ {t('tab_tagger', 'en')}")
|
| 1759 |
+
with tab_tagger:
|
| 1760 |
with gr.Column(elem_classes=["whyx-tree"]):
|
| 1761 |
# Tree root: Tagger
|
| 1762 |
with gr.Column(elem_classes=["whyx-tree-node"]):
|
|
|
|
| 1893 |
tagger_esc_state = gr.State("")
|
| 1894 |
|
| 1895 |
# ===== TAB 3: Tools =====
|
| 1896 |
+
tab_tools = gr.TabItem(f"🔧 {t('tab_tools', 'en')}")
|
| 1897 |
+
with tab_tools:
|
| 1898 |
with gr.Column(elem_classes=["whyx-tree"]):
|
| 1899 |
# Tree root: Analyzer
|
| 1900 |
with gr.Column(elem_classes=["whyx-tree-node"]):
|
|
|
|
| 2007 |
)
|
| 2008 |
|
| 2009 |
# ===== TAB 4: History =====
|
| 2010 |
+
tab_history = gr.TabItem(f"📜 {t('tab_history', 'en')}")
|
| 2011 |
+
with tab_history:
|
| 2012 |
with gr.Column(elem_classes=["whyx-tree-node"]):
|
| 2013 |
history_header_html = gr.HTML(_tree_header("📜", "history_section", "en"))
|
| 2014 |
history_md = gr.HTML(
|
|
|
|
| 2032 |
outputs=[],
|
| 2033 |
)
|
| 2034 |
web_search_clear.click(
|
| 2035 |
+
fn=lambda lang: f'<div class="whyx-info-text" style="color:var(--text-faint);font-size:12px;">{t("web_search_empty", "ru" if lang == "RU" else "en")}</div>',
|
| 2036 |
inputs=[lang_state],
|
| 2037 |
outputs=[web_search_output],
|
| 2038 |
)
|
|
|
|
| 2189 |
generate_btn.click(
|
| 2190 |
fn=on_generate,
|
| 2191 |
inputs=inputs_list,
|
| 2192 |
+
outputs=result_boxes + neg_boxes + result_cards,
|
| 2193 |
).then(
|
| 2194 |
fn=lambda lang: gr.update(value=_format_history_html("ru" if lang == "RU" else "en")),
|
| 2195 |
inputs=[lang_state],
|
|
|
|
| 2383 |
updates.append(gr.update(value=_format_artist_info([], lc)))
|
| 2384 |
updates.append(gr.update(value=f'<div class="whyx-info-text">{t("analyzer_desc", lc)}</div>'))
|
| 2385 |
updates.append(gr.update(value=_format_history_html(lc)))
|
| 2386 |
+
updates.append(gr.update(label=f"✨ {t('tab_generate', lc)}"))
|
| 2387 |
+
updates.append(gr.update(label=f"🖼️ {t('tab_tagger', lc)}"))
|
| 2388 |
+
updates.append(gr.update(label=f"🔧 {t('tab_tools', lc)}"))
|
| 2389 |
+
updates.append(gr.update(label=f"📜 {t('tab_history', lc)}"))
|
| 2390 |
+
for _comp, kind in tooltip_spans:
|
| 2391 |
+
key = "tooltip_copy" if kind == "copy" else "tooltip_favorite"
|
| 2392 |
+
updates.append(gr.update(value=f'<span class="whyx-tooltip-text">{t(key, lc)}</span>'))
|
| 2393 |
updates.append(choice)
|
| 2394 |
return updates
|
| 2395 |
|
|
|
|
| 2427 |
presets_acc, user_preset_name, user_preset_tags, user_preset_save, user_preset_delete,
|
| 2428 |
user_preset_trigger, user_preset_dropdown, user_preset_apply, theme_toggle,
|
| 2429 |
] + category_checks + group_components + result_boxes + neg_boxes + [categories_desc_md, presets_desc_md, results_desc_md, hotkeys_hint_md, artist_info_md, analyzer_md, history_md,
|
| 2430 |
+
tab_generate, tab_tagger, tab_tools, tab_history] + [c for c, _k in tooltip_spans] + [lang_state]
|
| 2431 |
|
| 2432 |
lang_btn.change(
|
| 2433 |
fn=update_lang,
|
| 2434 |
inputs=[lang_btn],
|
| 2435 |
outputs=lang_outputs,
|
| 2436 |
)
|
| 2437 |
+
lang_btn.change(
|
| 2438 |
+
fn=None,
|
| 2439 |
+
inputs=[lang_btn],
|
| 2440 |
+
outputs=[],
|
| 2441 |
+
js="(v) => { try { localStorage.setItem('whyx-lang', v); } catch(e) {} }",
|
| 2442 |
+
)
|
| 2443 |
|
| 2444 |
theme_toggle.change(
|
| 2445 |
fn=None,
|
| 2446 |
inputs=[theme_toggle],
|
| 2447 |
outputs=[],
|
| 2448 |
+
js="(v) => { document.body.classList.toggle('whyx-light', v === 'light'); try { localStorage.setItem('whyx-theme', v); } catch(e) {} }",
|
| 2449 |
+
)
|
| 2450 |
+
|
| 2451 |
+
# Restore persisted theme + language on page load: the JS reads
|
| 2452 |
+
# localStorage, applies the body class / radio value, then the standard
|
| 2453 |
+
# update_lang chain re-renders every localized label.
|
| 2454 |
+
demo.load(
|
| 2455 |
+
fn=None,
|
| 2456 |
+
inputs=[],
|
| 2457 |
+
outputs=[theme_toggle],
|
| 2458 |
+
js="() => { let t = 'dark'; try { t = localStorage.getItem('whyx-theme') || 'dark'; } catch(e) {} document.body.classList.toggle('whyx-light', t === 'light'); return t; }",
|
| 2459 |
+
).then(
|
| 2460 |
+
fn=None,
|
| 2461 |
+
inputs=[],
|
| 2462 |
+
outputs=[lang_btn],
|
| 2463 |
+
js="() => { try { return localStorage.getItem('whyx-lang') || 'EN'; } catch(e) { return 'EN'; } }",
|
| 2464 |
+
).then(
|
| 2465 |
+
fn=update_lang,
|
| 2466 |
+
inputs=[lang_btn],
|
| 2467 |
+
outputs=lang_outputs,
|
| 2468 |
)
|
| 2469 |
|
| 2470 |
|
src/i18n.py
CHANGED
|
@@ -361,6 +361,8 @@ L10N = {
|
|
| 361 |
"tagger_tags_desc": "Edit the list, then Apply or Copy. Built from the toggled tags above.",
|
| 362 |
"tagger_tags_ph": "Recognized tags will appear here…",
|
| 363 |
"tagger_copy": "Copy",
|
|
|
|
|
|
|
| 364 |
"tab_generate": "Generate",
|
| 365 |
"tab_tagger": "Tagger",
|
| 366 |
"tab_tools": "Tools",
|
|
@@ -750,6 +752,8 @@ L10N = {
|
|
| 750 |
"tagger_tags_desc": "Отредактируйте список, затем «Применить» или «Копировать». Собран из отмеченных выше тегов.",
|
| 751 |
"tagger_tags_ph": "Здесь появятся распознанные теги…",
|
| 752 |
"tagger_copy": "Копировать",
|
|
|
|
|
|
|
| 753 |
"tab_generate": "Генерация",
|
| 754 |
"tab_tagger": "Теггер",
|
| 755 |
"tab_tools": "Инструменты",
|
|
|
|
| 361 |
"tagger_tags_desc": "Edit the list, then Apply or Copy. Built from the toggled tags above.",
|
| 362 |
"tagger_tags_ph": "Recognized tags will appear here…",
|
| 363 |
"tagger_copy": "Copy",
|
| 364 |
+
"tooltip_copy": "Copy to clipboard",
|
| 365 |
+
"tooltip_favorite": "Toggle favorite",
|
| 366 |
"tab_generate": "Generate",
|
| 367 |
"tab_tagger": "Tagger",
|
| 368 |
"tab_tools": "Tools",
|
|
|
|
| 752 |
"tagger_tags_desc": "Отредактируйте список, затем «Применить» или «Копировать». Собран из отмеченных выше тегов.",
|
| 753 |
"tagger_tags_ph": "Здесь появятся распознанные теги…",
|
| 754 |
"tagger_copy": "Копировать",
|
| 755 |
+
"tooltip_copy": "Скопировать в буфер",
|
| 756 |
+
"tooltip_favorite": "Добавить в избранное",
|
| 757 |
"tab_generate": "Генерация",
|
| 758 |
"tab_tagger": "Теггер",
|
| 759 |
"tab_tools": "Инструменты",
|
src/prompt_analyzer.py
CHANGED
|
@@ -241,7 +241,7 @@ def format_analysis_html(data: dict, lang: str) -> str:
|
|
| 241 |
if data.get("error") == "parse":
|
| 242 |
msg = t("analyzer_parse_error", lang)
|
| 243 |
return f"""
|
| 244 |
-
<div style="background:
|
| 245 |
<div style="color:#F87171;font-size:12px;">⚠ {msg}</div>
|
| 246 |
</div>
|
| 247 |
"""
|
|
@@ -266,22 +266,22 @@ def format_analysis_html(data: dict, lang: str) -> str:
|
|
| 266 |
|
| 267 |
lines = []
|
| 268 |
|
| 269 |
-
lines.append("<div style='background:
|
| 270 |
|
| 271 |
-
lines.append(f"<div style='display:flex;gap:12px;flex-wrap:wrap;font-size:11px;color:
|
| 272 |
-
lines.append(f"<span><strong style='color:
|
| 273 |
-
lines.append(f"<span><strong style='color:
|
| 274 |
-
lines.append(f"<span><strong style='color:
|
| 275 |
if s['artists']:
|
| 276 |
-
lines.append(f"<span><strong style='color:
|
| 277 |
lines.append("</div>")
|
| 278 |
|
| 279 |
bar_color = {"good": "#34D399", "missing": "#F87171", "conflict": "#FBBF24", "low": "#FBBF24"}.get(q["status"], "#94A3B8")
|
| 280 |
|
| 281 |
-
lines.append(f"<div style='display:flex;gap:10px;flex-wrap:wrap;font-size:11px;color:
|
| 282 |
-
lines.append(f"<span><strong style='color:
|
| 283 |
-
lines.append(f"<span><strong style='color:
|
| 284 |
-
lines.append(f"<span><strong style='color:
|
| 285 |
if s['n_language']:
|
| 286 |
lines.append(f"<span style='color:#818CF8;'>{t('analyzer_natural_lang', lang)}</span>")
|
| 287 |
lines.append("</div>")
|
|
|
|
| 241 |
if data.get("error") == "parse":
|
| 242 |
msg = t("analyzer_parse_error", lang)
|
| 243 |
return f"""
|
| 244 |
+
<div style="background:var(--input-bg);border:1px solid rgba(239,68,68,0.25);border-radius:10px;padding:10px 14px;margin-top:4px;">
|
| 245 |
<div style="color:#F87171;font-size:12px;">⚠ {msg}</div>
|
| 246 |
</div>
|
| 247 |
"""
|
|
|
|
| 266 |
|
| 267 |
lines = []
|
| 268 |
|
| 269 |
+
lines.append("<div style='background:var(--input-bg);border:1px solid rgba(56,189,248,0.12);border-radius:10px;padding:10px 14px;margin-top:4px;'>")
|
| 270 |
|
| 271 |
+
lines.append(f"<div style='display:flex;gap:12px;flex-wrap:wrap;font-size:11px;color:var(--text-dim);margin-bottom:6px;'>")
|
| 272 |
+
lines.append(f"<span><strong style='color:var(--text);'>{t('analyzer_subject', lang)}:</strong> {html.escape(s['subject'])}</span>")
|
| 273 |
+
lines.append(f"<span><strong style='color:var(--text);'>{t('analyzer_character', lang)}:</strong> {html.escape(s['character'])}</span>")
|
| 274 |
+
lines.append(f"<span><strong style='color:var(--text);'>{t('analyzer_series', lang)}:</strong> {html.escape(s['series'])}</span>")
|
| 275 |
if s['artists']:
|
| 276 |
+
lines.append(f"<span><strong style='color:var(--text);'>{t('analyzer_artists', lang)}:</strong> {html.escape(', '.join(s['artists']))}</span>")
|
| 277 |
lines.append("</div>")
|
| 278 |
|
| 279 |
bar_color = {"good": "#34D399", "missing": "#F87171", "conflict": "#FBBF24", "low": "#FBBF24"}.get(q["status"], "#94A3B8")
|
| 280 |
|
| 281 |
+
lines.append(f"<div style='display:flex;gap:10px;flex-wrap:wrap;font-size:11px;color:var(--text-dim);margin-bottom:6px;'>")
|
| 282 |
+
lines.append(f"<span><strong style='color:var(--text);'>{t('analyzer_tags', lang)}:</strong> {s['total_tags']} ({t('analyzer_quality', lang)}: {s['num_quality']}, {t('analyzer_meta', lang)}: {s['num_meta']}, {t('analyzer_general', lang)}: {s['num_general']})</span>")
|
| 283 |
+
lines.append(f"<span><strong style='color:var(--text);'>{t('analyzer_quality', lang)}:</strong> <span style='color:{bar_color};'>{quality_label}</span></span>")
|
| 284 |
+
lines.append(f"<span><strong style='color:var(--text);'>{t('analyzer_tokens', lang)}:</strong> ≈{tok['clip']} CLIP {'⚠️' if tok['clip_warning'] else ''} / ≈{tok['t5']} T5 {'⚠️' if tok['t5_warning'] else ''}</span>")
|
| 285 |
if s['n_language']:
|
| 286 |
lines.append(f"<span style='color:#818CF8;'>{t('analyzer_natural_lang', lang)}</span>")
|
| 287 |
lines.append("</div>")
|