Skip to content

Commit 5144058

Browse files
Chore/update dependencies (#106)
* chore(deps): update openai to v2 and add UI dependencies - Upgrade openai from >=1.92.0,<2.0.0 to >=2.8.0 - Add PyYAML, matplotlib, litellm to UI requirements - Improve installation instructions in ui/requirements.txt Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(ui): optimize layout and simplify language selector - Remove JavaScript injection for language loading, use URL params instead - Add render_sidebar_language_selector for standalone language selection - Hide header and reduce top padding for more compact layout - Conditionally show workspace selector based on ENABLE_SHARED_WORKSPACES - Refactor result_panel score card HTML for better readability Co-authored-by: Cursor <cursoragent@cursor.com> * fix(ui): resolve pre-commit linting issues - Fix line-too-long in result_panel.py (195 > 120 chars) - Format imports with isort in app.py - Remove unnecessary pass statements in i18n/core.py Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(ui): move divider outside conditional block for consistency Move the divider after workspace/language selector outside the conditional block so it's always rendered regardless of whether shared workspaces are enabled. Co-authored-by: Cursor <cursoragent@cursor.com> * style(ui): improve HTML readability in result_panel.py Re-introduce indentation within the multi-line f-string for better HTML structure visibility and maintainability. Co-authored-by: Cursor <cursoragent@cursor.com> * refactor(i18n): make unused placeholder function private Make render_header_language_selector a private function (_render_header_language_selector) and remove it from public exports since it's not implemented and not used anywhere. Co-authored-by: Cursor <cursoragent@cursor.com> * docs(css): add warning comment for fragile CSS selector Add a warning comment explaining that the .stMain > div > div > div:first-child selector depends on Streamlit's internal DOM structure and may break with future updates. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent e4a3a57 commit 5144058

7 files changed

Lines changed: 162 additions & 74 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ dependencies = [
4242
"loguru>=0.7.3,<0.8.0",
4343
"json_repair>=0.54.0,<1.0.0",
4444
"pydantic>=2.11.5,<3.0.0",
45-
"openai>=1.92.0,<2.0.0",
45+
"openai>=2.8.0",
4646
"tenacity>=9.1.0,<10.0.0",
4747
"math-verify>=0.7.0,<0.8.0",
4848
"tqdm>=4.66.0,<5.0.0",

ui/app.py

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,15 @@
2727
from features.paper_review import PaperReviewFeature # noqa: E402
2828
from shared.components.common import render_footer # noqa: E402
2929
from shared.components.logo import render_logo_and_title # noqa: E402
30-
from shared.components.workspace_selector import render_workspace_selector # noqa: E402
31-
from shared.i18n import inject_language_loader, t # noqa: E402
30+
from shared.components.workspace_selector import ( # noqa: E402
31+
ENABLE_SHARED_WORKSPACES,
32+
render_workspace_selector,
33+
)
34+
from shared.i18n import ( # noqa: E402
35+
inject_language_loader,
36+
render_language_selector,
37+
t,
38+
)
3239
from shared.services.workspace_manager import ( # noqa: E402
3340
get_storage_manager,
3441
initialize_workspace_from_url,
@@ -99,11 +106,7 @@ def main() -> None:
99106
# Inject custom CSS
100107
inject_css()
101108

102-
# Load language preference from browser localStorage
103-
inject_language_loader()
104-
105-
# Initialize workspace from browser ID
106-
inject_browser_id_loader()
109+
# Initialize workspace from URL (doesn't render anything)
107110
initialize_workspace_from_url()
108111

109112
# Periodic storage cleanup check (runs once per session)
@@ -113,16 +116,27 @@ def main() -> None:
113116
# Sidebar Configuration
114117
# ========================================================================
115118
with st.sidebar:
119+
# Initialize browser ID (inside sidebar to avoid main area spacing)
120+
inject_browser_id_loader()
121+
122+
# Load language preference
123+
inject_language_loader()
124+
116125
# Logo and title
117126
render_logo_and_title()
118127

119128
# Divider
120129
st.markdown('<div class="custom-divider" style="margin: 0.75rem 0;"></div>', unsafe_allow_html=True)
121130

122-
# Workspace selector + Language selector in one row
123-
render_workspace_selector(show_language_selector=True)
131+
# Workspace selector (only if shared workspaces are enabled)
132+
if ENABLE_SHARED_WORKSPACES:
133+
# Workspace selector with language selector in the same row
134+
render_workspace_selector(show_language_selector=True)
135+
else:
136+
# Just show language selector when workspace selector is hidden
137+
render_language_selector(position="sidebar")
124138

125-
# Divider
139+
# Divider after workspace/language selector
126140
st.markdown('<div class="custom-divider" style="margin: 0.75rem 0;"></div>', unsafe_allow_html=True)
127141

128142
# Feature navigation

ui/features/paper_review/components/result_panel.py

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -36,33 +36,35 @@ def _render_score_card(
3636
color = _get_score_color(score, max_score)
3737
pct = (score / max_score) * 100
3838

39-
st.markdown(
40-
f"""<div style="
41-
background: linear-gradient(135deg, rgba(30, 41, 59, 0.8), rgba(15, 23, 42, 0.9));
42-
border: 1px solid #334155;
43-
border-radius: 12px;
44-
padding: 1.25rem;
45-
margin-bottom: 1rem;
46-
">
47-
<div style="display: flex; align-items: center; gap: 0.75rem; margin-bottom: 0.75rem;">
48-
<span style="font-size: 1.5rem;">{icon}</span>
49-
<span style="font-weight: 600; color: #F1F5F9;">{title}</span>
50-
</div>
51-
<div style="display: flex; align-items: baseline; gap: 0.5rem; margin-bottom: 0.5rem;">
52-
<span style="font-size: 2rem; font-weight: 700; color: {color};">{score}</span>
53-
<span style="color: #64748B;">/ {max_score}</span>
54-
</div>
55-
<div style="height: 6px; background: #1E293B; border-radius: 3px; overflow: hidden;">
56-
<div style="width: {pct}%; height: 100%; background: {color}; border-radius: 3px;"></div>
57-
</div>
58-
{f'<div style="color: #94A3B8; font-size: 0.85rem; margin-top: 0.75rem; '
59-
f'line-height: 1.5;">{description[:200]}...</div>'
60-
if description and len(description) > 200
61-
else (f'<div style="color: #94A3B8; font-size: 0.85rem; margin-top: 0.75rem; '
62-
f'line-height: 1.5;">{description}</div>' if description else '')}
63-
</div>""",
64-
unsafe_allow_html=True,
65-
)
39+
# Build description HTML separately to avoid f-string issues
40+
desc_html = ""
41+
if description:
42+
desc_text = f"{description[:200]}..." if len(description) > 200 else description
43+
desc_html = (
44+
'<div style="color: #94A3B8; font-size: 0.85rem; '
45+
f'margin-top: 0.75rem; line-height: 1.5;">{desc_text}</div>'
46+
)
47+
48+
# fmt: off
49+
html_content = f"""
50+
<div style="background: linear-gradient(135deg, rgba(30, 41, 59, 0.8), rgba(15, 23, 42, 0.9));
51+
border: 1px solid #334155; border-radius: 12px; padding: 1.25rem; margin-bottom: 1rem;">
52+
<div style="display: flex; align-items: center; gap: 0.75rem; margin-bottom: 0.75rem;">
53+
<span style="font-size: 1.5rem;">{icon}</span>
54+
<span style="font-weight: 600; color: #F1F5F9;">{title}</span>
55+
</div>
56+
<div style="display: flex; align-items: baseline; gap: 0.5rem; margin-bottom: 0.5rem;">
57+
<span style="font-size: 2rem; font-weight: 700; color: {color};">{score}</span>
58+
<span style="color: #64748B;">/ {max_score}</span>
59+
</div>
60+
<div style="height: 6px; background: #1E293B; border-radius: 3px; overflow: hidden;">
61+
<div style="width: {pct}%; height: 100%; background: {color}; border-radius: 3px;"></div>
62+
</div>
63+
{desc_html}
64+
</div>
65+
"""
66+
# fmt: on
67+
st.markdown(html_content, unsafe_allow_html=True)
6668

6769

6870
def _render_safety_result(result: PaperReviewResult) -> None:

ui/requirements.txt

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# OpenJudge Studio Dependencies
22
# ==============================
3+
# These are additional dependencies required by the UI that are NOT
4+
# included in the main pyproject.toml dependencies.
5+
#
6+
# Installation:
7+
# 1. Install main package: pip install -e .. (from ui/ directory)
8+
# 2. Install UI deps: pip install -r requirements.txt
39

410
# Core Framework
511
streamlit==1.46.1
@@ -10,5 +16,11 @@ altair==5.5.0
1016
# For async support in Streamlit
1117
nest-asyncio==1.6.0
1218

13-
# OpenJudge framework (install from parent directory)
14-
# pip install -e ..
19+
# YAML support (used by Auto Arena feature for config files)
20+
PyYAML>=6.0
21+
22+
# Chart generation for Auto Arena reports (optional but recommended)
23+
matplotlib>=3.7.0
24+
25+
# LiteLLM for Paper Review feature (PDF native support)
26+
litellm>=1.40.0

ui/shared/i18n/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
get_ui_language,
2424
inject_language_loader,
2525
render_language_selector,
26+
render_sidebar_language_selector,
2627
set_ui_language,
2728
t,
2829
)
@@ -34,4 +35,5 @@
3435
"render_language_selector",
3536
"get_available_languages",
3637
"inject_language_loader",
38+
"render_sidebar_language_selector",
3739
]

ui/shared/i18n/core.py

Lines changed: 43 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,14 @@ def get_available_languages() -> dict[str, str]:
3333

3434

3535
def _init_language_from_storage() -> None:
36-
"""Initialize language from localStorage on first load."""
37-
if UI_LANGUAGE_INITIALIZED not in st.session_state:
38-
st.session_state[UI_LANGUAGE_INITIALIZED] = True
39-
# Check query params for language (set by JavaScript from localStorage)
40-
params = st.query_params
41-
if "lang" in params:
42-
lang = params.get("lang")
43-
if lang in SUPPORTED_LANGUAGES:
36+
"""Initialize language from URL query parameters."""
37+
# Always check query params for language changes
38+
params = st.query_params
39+
if "lang" in params:
40+
lang = params.get("lang")
41+
if lang in SUPPORTED_LANGUAGES:
42+
current = st.session_state.get(UI_LANGUAGE_KEY)
43+
if current != lang:
4444
st.session_state[UI_LANGUAGE_KEY] = lang
4545

4646

@@ -166,32 +166,42 @@ def render_language_selector(position: str = "sidebar") -> None:
166166

167167

168168
def inject_language_loader() -> None:
169-
"""Inject JavaScript to load language from localStorage on page load.
169+
"""Initialize language from URL query parameters.
170170
171-
Call this once at the start of the app to restore language preference.
171+
This is a lightweight function that doesn't inject any HTML.
172+
Language loading is handled by _init_language_from_storage().
172173
"""
173-
# Only inject once per session to avoid repeated reloads
174-
if "_lang_loader_injected" in st.session_state:
175-
return
176-
st.session_state["_lang_loader_injected"] = True
174+
# Language is already initialized via _init_language_from_storage()
175+
# which is called by get_ui_language()
177176

178-
js_code = """
179-
<script>
180-
(function() {
181-
const savedLang = localStorage.getItem('openjudge_ui_language');
182-
if (savedLang && (savedLang === 'zh' || savedLang === 'en')) {
183-
const url = new URL(window.location.href);
184-
const currentLang = url.searchParams.get('lang');
185-
if (currentLang !== savedLang) {
186-
url.searchParams.set('lang', savedLang);
187-
window.history.replaceState({}, '', url);
188-
// Only reload if this is the first load (no lang param was set)
189-
if (!currentLang) {
190-
window.location.reload();
191-
}
192-
}
193-
}
194-
})();
195-
</script>
177+
178+
def _render_header_language_selector() -> None:
179+
"""Placeholder for header language selector (not implemented).
180+
181+
Actual rendering is done via render_sidebar_language_selector.
182+
This is kept as a private function for potential future implementation.
196183
"""
197-
st.markdown(js_code, unsafe_allow_html=True)
184+
185+
186+
def render_sidebar_language_selector() -> None:
187+
"""Render a compact language selector in the sidebar.
188+
189+
Uses Streamlit native selectbox for reliability.
190+
"""
191+
current_lang = get_ui_language()
192+
lang_options = list(SUPPORTED_LANGUAGES.keys())
193+
194+
selected_lang = st.selectbox(
195+
"🌐",
196+
options=lang_options,
197+
index=lang_options.index(current_lang) if current_lang in lang_options else 0,
198+
format_func=lambda x: SUPPORTED_LANGUAGES[x],
199+
key="_sidebar_lang_selector",
200+
label_visibility="collapsed",
201+
)
202+
203+
# Handle language change
204+
if selected_lang != current_lang:
205+
set_ui_language(selected_lang)
206+
_save_language_to_storage(selected_lang)
207+
st.rerun()

ui/shared/styles/theme.py

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,60 @@
5252
/* Reduce top padding in main content area */
5353
.stMainBlockContainer,
5454
.block-container {
55-
padding-top: 1rem !important;
55+
padding-top: 0 !important;
56+
margin-top: 0 !important;
5657
}
5758
5859
/* Remove extra top margin from first element */
5960
.stMain .stVerticalBlock > div:first-child {
6061
margin-top: 0 !important;
62+
padding-top: 0 !important;
63+
}
64+
65+
/* Hide the header completely to save space */
66+
[data-testid="stHeader"] {
67+
display: none !important;
68+
}
69+
70+
/* Reduce space between header and main content */
71+
.stAppViewContainer {
72+
padding-top: 0 !important;
73+
margin-top: 0 !important;
74+
}
75+
76+
/* Make the main area start at the very top */
77+
.stMain {
78+
padding-top: 0 !important;
79+
margin-top: 0 !important;
80+
}
81+
82+
/* Reduce top space in the main block container */
83+
[data-testid="stAppViewBlockContainer"] {
84+
padding-top: 0 !important;
85+
margin-top: 0 !important;
86+
}
87+
88+
/* Remove padding from vertical blocks */
89+
.stVerticalBlock {
90+
gap: 0.5rem !important;
91+
}
92+
93+
/* Compact the entire app view */
94+
[data-testid="stAppViewContainer"] {
95+
padding-top: 0 !important;
96+
}
97+
98+
/* Remove top margin from markdown elements */
99+
.stMarkdown {
100+
margin-top: 0 !important;
101+
}
102+
103+
/* First element in main content should have no top spacing */
104+
/* WARNING: This selector depends on Streamlit's internal DOM structure and may
105+
break with future Streamlit updates. Unfortunately, Streamlit doesn't provide
106+
a stable data-testid for this specific element. Monitor after Streamlit upgrades. */
107+
.stMain > div > div > div:first-child {
108+
padding-top: 0.5rem !important;
61109
}
62110
63111
/* =========================================================================

0 commit comments

Comments
 (0)