DD009 commited on
Commit
caf1f01
Β·
verified Β·
1 Parent(s): ff9e1b0

Update src/streamlit_app.py

Browse files
Files changed (1) hide show
  1. src/streamlit_app.py +296 -204
src/streamlit_app.py CHANGED
@@ -50,13 +50,20 @@ st.markdown("""
50
  padding-top: 0rem;
51
  }
52
 
53
- /* Brand Header */
 
 
 
 
 
 
 
54
  .brand-header {
55
  background: linear-gradient(135deg, #0f0f0f 0%, #1a1a2e 50%, #16213e 100%);
56
- padding: 2rem 2rem 1.5rem 2rem;
57
- border-radius: 0 0 24px 24px;
58
- margin: -1rem -1rem 2rem -1rem;
59
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
60
  position: relative;
61
  overflow: hidden;
62
  }
@@ -66,8 +73,8 @@ st.markdown("""
66
  position: absolute;
67
  top: -50%;
68
  right: -10%;
69
- width: 300px;
70
- height: 300px;
71
  background: radial-gradient(circle, rgba(64, 224, 208, 0.15) 0%, transparent 70%);
72
  border-radius: 50%;
73
  }
@@ -75,22 +82,23 @@ st.markdown("""
75
  .brand-container {
76
  display: flex;
77
  align-items: center;
78
- gap: 1.5rem;
79
  position: relative;
80
  z-index: 1;
81
  }
82
 
83
  .brand-logo {
84
- width: 70px;
85
- height: 70px;
 
86
  display: flex;
87
  align-items: center;
88
  justify-content: center;
89
  background: linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 25%, #20B2AA 60%, #40E0D0 100%);
90
- border-radius: 16px;
91
  box-shadow:
92
- 0 0 30px rgba(64, 224, 208, 0.6),
93
- 0 4px 20px rgba(0, 0, 0, 0.8),
94
  inset 0 1px 0 rgba(255, 255, 255, 0.1);
95
  flex-shrink: 0;
96
  border: 2px solid rgba(64, 224, 208, 0.3);
@@ -98,112 +106,95 @@ st.markdown("""
98
  }
99
 
100
  @keyframes glow {
101
- 0%, 100% { box-shadow: 0 0 30px rgba(64, 224, 208, 0.6), 0 4px 20px rgba(0, 0, 0, 0.8), inset 0 1px 0 rgba(255, 255, 255, 0.1); }
102
- 50% { box-shadow: 0 0 50px rgba(64, 224, 208, 0.9), 0 4px 20px rgba(0, 0, 0, 0.8), inset 0 1px 0 rgba(255, 255, 255, 0.1); }
103
  }
104
 
105
  .brand-logo svg {
106
- width: 45px;
107
- height: 45px;
108
- filter: drop-shadow(0 2px 8px rgba(255, 255, 255, 0.3));
109
  }
110
 
111
  .brand-info {
112
  flex: 1;
 
113
  }
114
 
115
  .brand-title {
116
- font-size: 2.5rem;
117
  font-weight: 900;
118
  background: linear-gradient(135deg, #ffffff 0%, #40E0D0 50%, #20B2AA 100%);
119
  -webkit-background-clip: text;
120
  -webkit-text-fill-color: transparent;
121
  background-clip: text;
122
- margin: 0 0 0.3rem 0;
123
- letter-spacing: -1px;
124
  line-height: 1.2;
 
125
  }
126
 
127
  .brand-subtitle {
128
  color: rgba(255, 255, 255, 0.7);
129
- font-size: 0.95rem;
130
  font-weight: 500;
131
  margin: 0;
132
- letter-spacing: 0.5px;
133
  }
134
 
135
- /* Metric Cards */
136
- .metric-card {
137
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
138
- padding: 1.8rem;
139
- border-radius: 16px;
140
  color: white;
141
  text-align: center;
142
  margin-bottom: 1rem;
143
- box-shadow: 0 8px 24px rgba(102, 126, 234, 0.3);
144
  transition: transform 0.3s ease, box-shadow 0.3s ease;
145
  border: 1px solid rgba(255, 255, 255, 0.1);
146
  }
147
 
148
- .metric-card:hover {
149
- transform: translateY(-4px);
150
- box-shadow: 0 12px 32px rgba(102, 126, 234, 0.4);
151
- }
152
-
153
- .metric-value {
154
- font-size: 2.2rem;
155
- font-weight: 900;
156
- margin-bottom: 0.3rem;
157
- text-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
158
- }
159
-
160
- .metric-label {
161
- font-size: 0.9rem;
162
- font-weight: 600;
163
- opacity: 0.95;
164
- letter-spacing: 0.5px;
165
  }
166
 
167
  .savings-card {
168
  background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
169
- padding: 1.8rem;
170
- border-radius: 16px;
171
- color: white;
172
- text-align: center;
173
- box-shadow: 0 8px 24px rgba(56, 239, 125, 0.3);
174
- transition: transform 0.3s ease, box-shadow 0.3s ease;
175
- border: 1px solid rgba(255, 255, 255, 0.1);
176
- }
177
-
178
- .savings-card:hover {
179
- transform: translateY(-4px);
180
- box-shadow: 0 12px 32px rgba(56, 239, 125, 0.4);
181
  }
182
 
183
  .cost-card {
184
  background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
185
- padding: 1.8rem;
186
- border-radius: 16px;
187
- color: white;
188
- text-align: center;
189
- box-shadow: 0 8px 24px rgba(245, 87, 108, 0.3);
190
- transition: transform 0.3s ease, box-shadow 0.3s ease;
191
- border: 1px solid rgba(255, 255, 255, 0.1);
192
  }
193
 
194
- .cost-card:hover {
195
- transform: translateY(-4px);
196
- box-shadow: 0 12px 32px rgba(245, 87, 108, 0.4);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
197
  }
198
 
199
- /* Chat Messages */
200
  .chat-message {
201
- padding: 1.2rem;
202
- border-radius: 12px;
203
- margin-bottom: 1rem;
204
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
205
  transition: transform 0.2s ease;
206
  animation: slideIn 0.3s ease;
 
 
207
  }
208
 
209
  @keyframes slideIn {
@@ -218,127 +209,226 @@ st.markdown("""
218
  }
219
 
220
  .chat-message:hover {
221
- transform: translateX(4px);
222
  }
223
 
224
  .user-message {
225
  background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);
226
- border-left: 4px solid #2196f3;
227
  }
228
 
229
  .assistant-message {
230
  background: linear-gradient(135deg, #f5f5f5 0%, #e0e0e0 100%);
231
- border-left: 4px solid #4caf50;
 
 
 
 
 
 
 
 
 
232
  }
233
 
234
- /* Info Cards */
235
  .info-card {
236
  background: linear-gradient(135deg, #f0f4f8 0%, #d9e2ec 100%);
237
- padding: 1.5rem;
238
- border-radius: 12px;
239
- border-left: 4px solid #3b82f6;
240
- margin-bottom: 1.5rem;
241
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
242
  }
243
 
244
  .warning-card {
245
  background: linear-gradient(135deg, #fff4e6 0%, #ffe0b2 100%);
246
- padding: 1.5rem;
247
- border-radius: 12px;
248
- border-left: 4px solid #ff9800;
249
- margin-bottom: 1.5rem;
250
- box-shadow: 0 4px 12px rgba(255, 152, 0, 0.1);
251
  }
252
 
253
  .success-card {
254
  background: linear-gradient(135deg, #e8f5e9 0%, #c8e6c9 100%);
255
- padding: 1.5rem;
256
- border-radius: 12px;
257
- border-left: 4px solid #4caf50;
258
- margin-bottom: 1.5rem;
259
- box-shadow: 0 4px 12px rgba(76, 175, 80, 0.1);
260
  }
261
 
262
- /* Tabs */
263
  .stTabs [data-baseweb="tab-list"] {
264
- gap: 8px;
265
  background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
266
- padding: 0.5rem;
267
- border-radius: 12px;
 
 
268
  }
269
 
270
  .stTabs [data-baseweb="tab"] {
271
  border-radius: 8px;
272
- padding: 0.75rem 1.5rem;
273
  font-weight: 600;
274
  transition: all 0.3s ease;
 
275
  }
276
 
277
  .stTabs [aria-selected="true"] {
278
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
279
  color: white !important;
280
- box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
281
  }
282
 
283
- /* Buttons */
284
  .stButton > button {
285
- border-radius: 10px;
286
  font-weight: 600;
287
  transition: all 0.3s ease;
288
  border: none;
289
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
 
 
 
290
  }
291
 
292
  .stButton > button:hover {
293
- transform: translateY(-2px);
294
- box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15);
295
  }
296
 
297
- /* Code blocks */
298
- .stCodeBlock {
299
- border-radius: 12px;
300
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
301
  }
302
 
303
- /* Expanders */
304
- .streamlit-expanderHeader {
305
- background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
306
  border-radius: 10px;
307
- font-weight: 600;
308
- padding: 1rem;
309
  }
310
 
311
- /* Sidebar */
312
- .css-1d391kg {
313
  background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
 
 
 
 
314
  }
315
 
316
- /* Status badges */
317
  .status-badge {
318
  display: inline-block;
319
- padding: 0.4rem 1rem;
320
- border-radius: 20px;
321
- font-size: 0.85rem;
322
  font-weight: 600;
323
- margin: 0.25rem;
 
324
  }
325
 
326
  .status-success {
327
  background: linear-gradient(135deg, #4caf50 0%, #66bb6a 100%);
328
  color: white;
329
- box-shadow: 0 2px 8px rgba(76, 175, 80, 0.3);
330
  }
331
 
332
  .status-info {
333
  background: linear-gradient(135deg, #2196f3 0%, #42a5f5 100%);
334
  color: white;
335
- box-shadow: 0 2px 8px rgba(33, 150, 243, 0.3);
336
  }
337
 
338
  .status-warning {
339
  background: linear-gradient(135deg, #ff9800 0%, #ffa726 100%);
340
  color: white;
341
- box-shadow: 0 2px 8px rgba(255, 152, 0, 0.3);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
342
  }
343
  </style>
344
  """, unsafe_allow_html=True)
@@ -362,9 +452,6 @@ if "indexed" not in st.session_state:
362
  if "auto_indexed" not in st.session_state:
363
  st.session_state.auto_indexed = False
364
 
365
- if "saved_api_keys" not in st.session_state:
366
- st.session_state.saved_api_keys = [] # List of {name, openai_key, pinecone_key}
367
-
368
  # ============================================================================
369
  # Sidebar
370
  # ============================================================================
@@ -406,83 +493,23 @@ with st.sidebar:
406
 
407
  st.markdown("---")
408
 
409
- # API Keys Section with Dropdown
410
  st.markdown("### πŸ”‘ API Configuration")
411
 
412
- # Dropdown for saved API keys
413
- if st.session_state.saved_api_keys:
414
- key_options = ["Add New Configuration..."] + [key["name"] for key in st.session_state.saved_api_keys]
415
- selected_key = st.selectbox(
416
- "Select API Configuration",
417
- key_options,
418
- key="api_key_selector"
419
- )
420
-
421
- if selected_key and selected_key != "Add New Configuration...":
422
- # Load selected keys
423
- selected_config = next((k for k in st.session_state.saved_api_keys if k["name"] == selected_key), None)
424
- if selected_config:
425
- default_openai = selected_config["openai_key"]
426
- default_pinecone = selected_config["pinecone_key"]
427
- else:
428
- default_openai = st.session_state.get("openai_key", "")
429
- default_pinecone = st.session_state.get("pinecone_key", "")
430
- else:
431
- default_openai = st.session_state.get("openai_key", "")
432
- default_pinecone = st.session_state.get("pinecone_key", "")
433
- else:
434
- selected_key = None
435
- default_openai = st.session_state.get("openai_key", "")
436
- default_pinecone = st.session_state.get("pinecone_key", "")
437
-
438
  openai_key = st.text_input(
439
  "OpenAI API Key",
440
  type="password",
441
- value=default_openai,
442
- help="Your OpenAI API key for LLM and embeddings",
443
- key="openai_key_input"
444
  )
445
 
446
  pinecone_key = st.text_input(
447
  "Pinecone API Key",
448
  type="password",
449
- value=default_pinecone,
450
- help="Your Pinecone API key for vector storage",
451
- key="pinecone_key_input"
452
  )
453
 
454
- # Save/Delete configuration
455
- if openai_key and pinecone_key:
456
- with st.form("save_api_keys_form"):
457
- col1, col2 = st.columns(2)
458
- with col1:
459
- key_name = st.text_input("Save as (optional)", placeholder="e.g., Production, Dev", key="save_key_name")
460
- with col2:
461
- save_submitted = st.form_submit_button("πŸ’Ύ Save", use_container_width=True)
462
-
463
- if save_submitted and key_name:
464
- # Check if name already exists
465
- existing = next((k for k in st.session_state.saved_api_keys if k["name"] == key_name), None)
466
- if existing:
467
- existing["openai_key"] = openai_key
468
- existing["pinecone_key"] = pinecone_key
469
- st.success(f"βœ… Updated '{key_name}'")
470
- else:
471
- st.session_state.saved_api_keys.append({
472
- "name": key_name,
473
- "openai_key": openai_key,
474
- "pinecone_key": pinecone_key
475
- })
476
- st.success(f"βœ… Saved as '{key_name}'")
477
- st.rerun()
478
-
479
- # Delete button for selected configuration
480
- if selected_key and selected_key != "Add New Configuration...":
481
- if st.button("πŸ—‘οΈ Delete Selected", use_container_width=True):
482
- st.session_state.saved_api_keys = [k for k in st.session_state.saved_api_keys if k["name"] != selected_key]
483
- st.success(f"βœ… Deleted '{selected_key}'")
484
- st.rerun()
485
-
486
  if openai_key and pinecone_key:
487
  st.session_state.openai_key = openai_key
488
  st.session_state.pinecone_key = pinecone_key
@@ -514,6 +541,37 @@ with st.sidebar:
514
 
515
  st.markdown("---")
516
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
517
  # Quick Actions
518
  st.markdown("### ⚑ Quick Actions")
519
 
@@ -629,20 +687,53 @@ with tab1:
629
  with tab2:
630
  st.markdown("### 🎫 JIRA Ticket to Implementation")
631
 
632
- st.markdown("#### Ticket Details")
633
- ticket_id = st.text_input("πŸ“‹ Ticket ID", placeholder="PROJ-123")
634
- ticket_title = st.text_input("πŸ“ Title", placeholder="Add user preferences feature")
635
- ticket_desc = st.text_area(
636
- "πŸ“„ Description",
637
- height=150,
638
- placeholder="Detailed description of the feature..."
639
- )
640
- acceptance = st.text_area(
641
- "βœ… Acceptance Criteria",
642
- height=100,
643
- placeholder="List the acceptance criteria..."
644
- )
645
- labels = st.text_input("🏷️ Labels", placeholder="backend, frontend (comma-separated)")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
646
 
647
  st.markdown("---")
648
 
@@ -666,6 +757,7 @@ with tab2:
666
  st.session_state.current_plan = plan
667
  st.session_state.last_ticket = ticket
668
  st.success("βœ… Plan generated! Check the 'Implementation Plan' tab")
 
669
  except Exception as e:
670
  st.error(f"❌ Error: {str(e)}")
671
 
 
50
  padding-top: 0rem;
51
  }
52
 
53
+ /* Responsive container */
54
+ .block-container {
55
+ padding-top: 1rem;
56
+ padding-bottom: 1rem;
57
+ max-width: 100%;
58
+ }
59
+
60
+ /* Brand Header - Responsive */
61
  .brand-header {
62
  background: linear-gradient(135deg, #0f0f0f 0%, #1a1a2e 50%, #16213e 100%);
63
+ padding: 1.5rem 1rem;
64
+ border-radius: 0 0 16px 16px;
65
+ margin: -1rem -1rem 1.5rem -1rem;
66
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.4);
67
  position: relative;
68
  overflow: hidden;
69
  }
 
73
  position: absolute;
74
  top: -50%;
75
  right: -10%;
76
+ width: 200px;
77
+ height: 200px;
78
  background: radial-gradient(circle, rgba(64, 224, 208, 0.15) 0%, transparent 70%);
79
  border-radius: 50%;
80
  }
 
82
  .brand-container {
83
  display: flex;
84
  align-items: center;
85
+ gap: 1rem;
86
  position: relative;
87
  z-index: 1;
88
  }
89
 
90
  .brand-logo {
91
+ width: 50px;
92
+ height: 50px;
93
+ min-width: 50px;
94
  display: flex;
95
  align-items: center;
96
  justify-content: center;
97
  background: linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 25%, #20B2AA 60%, #40E0D0 100%);
98
+ border-radius: 12px;
99
  box-shadow:
100
+ 0 0 20px rgba(64, 224, 208, 0.6),
101
+ 0 4px 16px rgba(0, 0, 0, 0.8),
102
  inset 0 1px 0 rgba(255, 255, 255, 0.1);
103
  flex-shrink: 0;
104
  border: 2px solid rgba(64, 224, 208, 0.3);
 
106
  }
107
 
108
  @keyframes glow {
109
+ 0%, 100% { box-shadow: 0 0 20px rgba(64, 224, 208, 0.6), 0 4px 16px rgba(0, 0, 0, 0.8), inset 0 1px 0 rgba(255, 255, 255, 0.1); }
110
+ 50% { box-shadow: 0 0 30px rgba(64, 224, 208, 0.9), 0 4px 16px rgba(0, 0, 0, 0.8), inset 0 1px 0 rgba(255, 255, 255, 0.1); }
111
  }
112
 
113
  .brand-logo svg {
114
+ width: 30px;
115
+ height: 30px;
116
+ filter: drop-shadow(0 2px 4px rgba(255, 255, 255, 0.3));
117
  }
118
 
119
  .brand-info {
120
  flex: 1;
121
+ min-width: 0;
122
  }
123
 
124
  .brand-title {
125
+ font-size: clamp(1.2rem, 4vw, 2rem);
126
  font-weight: 900;
127
  background: linear-gradient(135deg, #ffffff 0%, #40E0D0 50%, #20B2AA 100%);
128
  -webkit-background-clip: text;
129
  -webkit-text-fill-color: transparent;
130
  background-clip: text;
131
+ margin: 0 0 0.2rem 0;
132
+ letter-spacing: -0.5px;
133
  line-height: 1.2;
134
+ word-break: break-word;
135
  }
136
 
137
  .brand-subtitle {
138
  color: rgba(255, 255, 255, 0.7);
139
+ font-size: clamp(0.7rem, 2vw, 0.9rem);
140
  font-weight: 500;
141
  margin: 0;
142
+ letter-spacing: 0.3px;
143
  }
144
 
145
+ /* Metric Cards - Responsive */
146
+ .metric-card, .savings-card, .cost-card {
147
+ padding: 1.2rem;
148
+ border-radius: 12px;
 
149
  color: white;
150
  text-align: center;
151
  margin-bottom: 1rem;
152
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
153
  transition: transform 0.3s ease, box-shadow 0.3s ease;
154
  border: 1px solid rgba(255, 255, 255, 0.1);
155
  }
156
 
157
+ .metric-card {
158
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  }
160
 
161
  .savings-card {
162
  background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
 
 
 
 
 
 
 
 
 
 
 
 
163
  }
164
 
165
  .cost-card {
166
  background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
 
 
 
 
 
 
 
167
  }
168
 
169
+ .metric-card:hover, .savings-card:hover, .cost-card:hover {
170
+ transform: translateY(-2px);
171
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
172
+ }
173
+
174
+ .metric-value {
175
+ font-size: clamp(1.5rem, 5vw, 2rem);
176
+ font-weight: 900;
177
+ margin-bottom: 0.3rem;
178
+ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
179
+ }
180
+
181
+ .metric-label {
182
+ font-size: clamp(0.75rem, 2vw, 0.9rem);
183
+ font-weight: 600;
184
+ opacity: 0.95;
185
+ letter-spacing: 0.3px;
186
  }
187
 
188
+ /* Chat Messages - Responsive */
189
  .chat-message {
190
+ padding: 0.8rem 1rem;
191
+ border-radius: 10px;
192
+ margin-bottom: 0.8rem;
193
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
194
  transition: transform 0.2s ease;
195
  animation: slideIn 0.3s ease;
196
+ word-wrap: break-word;
197
+ overflow-wrap: break-word;
198
  }
199
 
200
  @keyframes slideIn {
 
209
  }
210
 
211
  .chat-message:hover {
212
+ transform: translateX(2px);
213
  }
214
 
215
  .user-message {
216
  background: linear-gradient(135deg, #e3f2fd 0%, #bbdefb 100%);
217
+ border-left: 3px solid #2196f3;
218
  }
219
 
220
  .assistant-message {
221
  background: linear-gradient(135deg, #f5f5f5 0%, #e0e0e0 100%);
222
+ border-left: 3px solid #4caf50;
223
+ }
224
+
225
+ /* Info Cards - Responsive */
226
+ .info-card, .warning-card, .success-card {
227
+ padding: 1rem;
228
+ border-radius: 10px;
229
+ margin-bottom: 1rem;
230
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
231
+ font-size: clamp(0.85rem, 2vw, 1rem);
232
  }
233
 
 
234
  .info-card {
235
  background: linear-gradient(135deg, #f0f4f8 0%, #d9e2ec 100%);
236
+ border-left: 3px solid #3b82f6;
 
 
 
 
237
  }
238
 
239
  .warning-card {
240
  background: linear-gradient(135deg, #fff4e6 0%, #ffe0b2 100%);
241
+ border-left: 3px solid #ff9800;
 
 
 
 
242
  }
243
 
244
  .success-card {
245
  background: linear-gradient(135deg, #e8f5e9 0%, #c8e6c9 100%);
246
+ border-left: 3px solid #4caf50;
 
 
 
 
247
  }
248
 
249
+ /* Tabs - Responsive */
250
  .stTabs [data-baseweb="tab-list"] {
251
+ gap: 4px;
252
  background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
253
+ padding: 0.4rem;
254
+ border-radius: 10px;
255
+ overflow-x: auto;
256
+ white-space: nowrap;
257
  }
258
 
259
  .stTabs [data-baseweb="tab"] {
260
  border-radius: 8px;
261
+ padding: 0.6rem 1rem;
262
  font-weight: 600;
263
  transition: all 0.3s ease;
264
+ font-size: clamp(0.8rem, 2vw, 0.95rem);
265
  }
266
 
267
  .stTabs [aria-selected="true"] {
268
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
269
  color: white !important;
270
+ box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
271
  }
272
 
273
+ /* Buttons - Responsive */
274
  .stButton > button {
275
+ border-radius: 8px;
276
  font-weight: 600;
277
  transition: all 0.3s ease;
278
  border: none;
279
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
280
+ font-size: clamp(0.85rem, 2vw, 1rem);
281
+ padding: 0.5rem 1rem;
282
+ width: 100%;
283
  }
284
 
285
  .stButton > button:hover {
286
+ transform: translateY(-1px);
287
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
288
  }
289
 
290
+ /* Forms - Responsive */
291
+ .stTextInput input, .stTextArea textarea {
292
+ font-size: clamp(0.9rem, 2vw, 1rem);
293
+ border-radius: 8px;
294
  }
295
 
296
+ /* Code blocks - Responsive */
297
+ .stCodeBlock {
 
298
  border-radius: 10px;
299
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
300
+ font-size: clamp(0.75rem, 1.5vw, 0.9rem);
301
  }
302
 
303
+ /* Expanders - Responsive */
304
+ .streamlit-expanderHeader {
305
  background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
306
+ border-radius: 8px;
307
+ font-weight: 600;
308
+ padding: 0.8rem;
309
+ font-size: clamp(0.9rem, 2vw, 1rem);
310
  }
311
 
312
+ /* Status badges - Responsive */
313
  .status-badge {
314
  display: inline-block;
315
+ padding: 0.3rem 0.8rem;
316
+ border-radius: 16px;
317
+ font-size: clamp(0.75rem, 1.5vw, 0.85rem);
318
  font-weight: 600;
319
+ margin: 0.2rem;
320
+ white-space: nowrap;
321
  }
322
 
323
  .status-success {
324
  background: linear-gradient(135deg, #4caf50 0%, #66bb6a 100%);
325
  color: white;
326
+ box-shadow: 0 2px 6px rgba(76, 175, 80, 0.3);
327
  }
328
 
329
  .status-info {
330
  background: linear-gradient(135deg, #2196f3 0%, #42a5f5 100%);
331
  color: white;
332
+ box-shadow: 0 2px 6px rgba(33, 150, 243, 0.3);
333
  }
334
 
335
  .status-warning {
336
  background: linear-gradient(135deg, #ff9800 0%, #ffa726 100%);
337
  color: white;
338
+ box-shadow: 0 2px 6px rgba(255, 152, 0, 0.3);
339
+ }
340
+
341
+ /* Mobile-specific adjustments */
342
+ @media only screen and (max-width: 768px) {
343
+ .main {
344
+ padding: 0.5rem;
345
+ }
346
+
347
+ .block-container {
348
+ padding: 0.5rem;
349
+ }
350
+
351
+ .brand-header {
352
+ padding: 1rem 0.8rem;
353
+ margin: -0.5rem -0.5rem 1rem -0.5rem;
354
+ }
355
+
356
+ .brand-container {
357
+ gap: 0.8rem;
358
+ }
359
+
360
+ .brand-logo {
361
+ width: 40px;
362
+ height: 40px;
363
+ min-width: 40px;
364
+ }
365
+
366
+ .brand-logo svg {
367
+ width: 24px;
368
+ height: 24px;
369
+ }
370
+
371
+ .metric-card, .savings-card, .cost-card {
372
+ padding: 1rem;
373
+ margin-bottom: 0.8rem;
374
+ }
375
+
376
+ .chat-message {
377
+ padding: 0.7rem 0.8rem;
378
+ font-size: 0.9rem;
379
+ }
380
+
381
+ .stTabs [data-baseweb="tab"] {
382
+ padding: 0.5rem 0.8rem;
383
+ font-size: 0.85rem;
384
+ }
385
+
386
+ /* Stack columns on mobile */
387
+ .row-widget.stHorizontal {
388
+ flex-direction: column;
389
+ }
390
+
391
+ /* Full width buttons on mobile */
392
+ .stButton > button {
393
+ width: 100%;
394
+ margin-bottom: 0.5rem;
395
+ }
396
+ }
397
+
398
+ /* Tablet adjustments */
399
+ @media only screen and (min-width: 769px) and (max-width: 1024px) {
400
+ .brand-title {
401
+ font-size: 1.8rem;
402
+ }
403
+
404
+ .metric-value {
405
+ font-size: 1.8rem;
406
+ }
407
+ }
408
+
409
+ /* Large desktop */
410
+ @media only screen and (min-width: 1920px) {
411
+ .block-container {
412
+ max-width: 1600px;
413
+ margin: 0 auto;
414
+ }
415
+ }
416
+
417
+ /* Improve scrolling on mobile */
418
+ @media only screen and (max-width: 768px) {
419
+ .stTabs [data-baseweb="tab-list"] {
420
+ -webkit-overflow-scrolling: touch;
421
+ scrollbar-width: thin;
422
+ }
423
+
424
+ .stTabs [data-baseweb="tab-list"]::-webkit-scrollbar {
425
+ height: 4px;
426
+ }
427
+
428
+ .stTabs [data-baseweb="tab-list"]::-webkit-scrollbar-thumb {
429
+ background: #667eea;
430
+ border-radius: 4px;
431
+ }
432
  }
433
  </style>
434
  """, unsafe_allow_html=True)
 
452
  if "auto_indexed" not in st.session_state:
453
  st.session_state.auto_indexed = False
454
 
 
 
 
455
  # ============================================================================
456
  # Sidebar
457
  # ============================================================================
 
493
 
494
  st.markdown("---")
495
 
496
+ # API Keys Section
497
  st.markdown("### πŸ”‘ API Configuration")
498
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
499
  openai_key = st.text_input(
500
  "OpenAI API Key",
501
  type="password",
502
+ value=st.session_state.get("openai_key", ""),
503
+ help="Your OpenAI API key for LLM and embeddings"
 
504
  )
505
 
506
  pinecone_key = st.text_input(
507
  "Pinecone API Key",
508
  type="password",
509
+ value=st.session_state.get("pinecone_key", ""),
510
+ help="Your Pinecone API key for vector storage"
 
511
  )
512
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
513
  if openai_key and pinecone_key:
514
  st.session_state.openai_key = openai_key
515
  st.session_state.pinecone_key = pinecone_key
 
541
 
542
  st.markdown("---")
543
 
544
+ # Codebase Status
545
+ st.markdown("### πŸ“‚ Codebase Status")
546
+
547
+ try:
548
+ stats = st.session_state.agent.indexer.get_stats()
549
+ total_chunks = stats.get('total_chunks', 0)
550
+ if total_chunks > 0:
551
+ st.markdown(f"""
552
+ <div class="success-card">
553
+ <strong>βœ… Codebase Indexed</strong><br>
554
+ <span style="font-size: 1.5rem; font-weight: 700;">{total_chunks:,}</span> chunks ready
555
+ </div>
556
+ """, unsafe_allow_html=True)
557
+ st.session_state.indexed = True
558
+ else:
559
+ st.markdown("""
560
+ <div class="info-card">
561
+ <strong>ℹ️ Ready to Index</strong><br>
562
+ Set API keys to auto-index
563
+ </div>
564
+ """, unsafe_allow_html=True)
565
+ except:
566
+ st.markdown("""
567
+ <div class="info-card">
568
+ <strong>ℹ️ Awaiting Configuration</strong><br>
569
+ Configure API keys above
570
+ </div>
571
+ """, unsafe_allow_html=True)
572
+
573
+ st.markdown("---")
574
+
575
  # Quick Actions
576
  st.markdown("### ⚑ Quick Actions")
577
 
 
687
  with tab2:
688
  st.markdown("### 🎫 JIRA Ticket to Implementation")
689
 
690
+ col1, col2 = st.columns([2, 1])
691
+
692
+ with col1:
693
+ st.markdown("#### Ticket Details")
694
+ ticket_id = st.text_input("πŸ“‹ Ticket ID", placeholder="PROJ-123")
695
+ ticket_title = st.text_input("πŸ“ Title", placeholder="Add user preferences feature")
696
+ ticket_desc = st.text_area(
697
+ "πŸ“„ Description",
698
+ height=150,
699
+ placeholder="Detailed description of the feature..."
700
+ )
701
+ acceptance = st.text_area(
702
+ "βœ… Acceptance Criteria",
703
+ height=100,
704
+ placeholder="List the acceptance criteria..."
705
+ )
706
+ labels = st.text_input("🏷️ Labels", placeholder="backend, frontend (comma-separated)")
707
+
708
+ with col2:
709
+ st.markdown("#### Quick Actions")
710
+
711
+ if st.button("πŸ“ Load Example", use_container_width=True):
712
+ st.session_state.example = {
713
+ "id": "PROJ-456",
714
+ "title": "Add notification preferences",
715
+ "desc": "As a user, I want to customize my notification settings.\n\nFeatures:\n- Email notification toggle\n- Push notification toggle\n- Notification frequency settings\n- Save preferences to database",
716
+ "accept": "- User can toggle email notifications\n- User can toggle push notifications\n- Settings persist across sessions\n- Changes take effect immediately",
717
+ "labels": "backend, frontend, notifications"
718
+ }
719
+ st.rerun()
720
+
721
+ if "example" in st.session_state:
722
+ ticket_id = st.session_state.example["id"]
723
+ ticket_title = st.session_state.example["title"]
724
+ ticket_desc = st.session_state.example["desc"]
725
+ acceptance = st.session_state.example["accept"]
726
+ labels = st.session_state.example["labels"]
727
+ del st.session_state.example
728
+
729
+ st.markdown("---")
730
+
731
+ st.markdown("""
732
+ <div class="info-card">
733
+ <strong>πŸ’‘ Tip</strong><br>
734
+ Provide detailed descriptions for better code generation
735
+ </div>
736
+ """, unsafe_allow_html=True)
737
 
738
  st.markdown("---")
739
 
 
757
  st.session_state.current_plan = plan
758
  st.session_state.last_ticket = ticket
759
  st.success("βœ… Plan generated! Check the 'Implementation Plan' tab")
760
+ st.balloons()
761
  except Exception as e:
762
  st.error(f"❌ Error: {str(e)}")
763