Rishabh2095 commited on
Commit
1a8b673
·
1 Parent(s): 1049dc4

Refactor Gradio UI layout and improve CSS styles for better responsiveness and aesthetics

Browse files
Files changed (2) hide show
  1. job_writer_ui/app.py +85 -64
  2. job_writer_ui/static/custom.css +157 -33
job_writer_ui/app.py CHANGED
@@ -36,9 +36,10 @@ with gr.Blocks(title="Job Application Assistant") as demo:
36
  # Phase 1: Form Interface
37
  # ========================================================================
38
  with gr.Group(visible=True) as form_phase:
39
- with gr.Row(elem_classes=["container", "section"]):
40
- with gr.Column(scale=1, elem_classes=["form-container", "card"]):
41
- gr.Markdown(
 
42
  """
43
  <div class="page-header">
44
  <h1 class="page-title">Job Application Writer</h1>
@@ -49,54 +50,71 @@ with gr.Blocks(title="Job Application Assistant") as demo:
49
  </div>
50
  """
51
  )
52
- job_url = gr.Textbox(
53
- label="Job Posting URL",
54
- placeholder="https://example.com/job-posting",
55
- lines=1,
56
- )
57
- email = gr.Textbox(
58
- label="Email ID",
59
- placeholder="you@example.com",
60
- lines=1,
61
- )
62
- resume_url = gr.Textbox(
63
- label="Resume File URL",
64
- placeholder="https://example.com/resume.pdf",
65
- lines=1,
66
- info=(
67
- "URL where your resume has been uploaded "
68
- "(optional - uses default if empty)"
69
- ),
70
- )
71
- content_type = gr.Dropdown(
72
- choices=["cover_letter", "bullets", "linkedin_note"],
73
- value=None,
74
- label="Content Type",
75
- info="Select the type of content to generate",
76
- )
77
- form_error = gr.Markdown("", elem_classes=["form-error"])
78
- generate_btn = gr.Button(
79
- "Generate Content",
80
- variant="primary",
81
- size="lg",
82
- elem_classes=["btn-primary"],
83
- interactive=False,
84
- )
 
 
 
 
85
 
86
  # ========================================================================
87
  # Phase 2: Chat Interface
88
  # ========================================================================
89
  with gr.Group(visible=False) as chat_phase:
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  with gr.Row(elem_classes=["container", "section"]):
91
  with gr.Column(elem_classes=["chat-phase-container", "card"]):
92
  with gr.Row(elem_classes=["chat-header-row"]):
93
- gr.Markdown(
94
- (
95
- "<div class='chat-page-header'>"
96
- "<h1 class='page-title'>Job Application Writer</h1>"
97
- "</div>"
98
- ),
99
- elem_classes=["chat-title"],
100
  )
101
  new_session_btn = gr.Button(
102
  "+",
@@ -113,34 +131,37 @@ with gr.Blocks(title="Job Application Assistant") as demo:
113
  show_label=False,
114
  allow_file_downloads=False,
115
  line_breaks=False,
116
- buttons=[],
117
  )
118
 
119
  with gr.Group(
120
- visible=True, elem_classes=["section"]
121
  ) as feedback_section:
122
- cancel_run_btn = gr.Button(
123
- "Cancel Run",
124
- variant="stop",
125
- size="sm",
126
- elem_classes=["btn-secondary"],
127
- visible=False, # Hidden by default, shown when run is active
128
- )
129
- feedback_input = gr.Textbox(
130
- label="Your Feedback",
131
- placeholder=(
132
- "Provide feedback to improve the content, "
133
- "or leave empty to approve..."
134
- ),
135
- lines=3,
136
- )
137
- with gr.Row():
 
138
  send_btn = gr.Button(
139
- "Send Feedback",
140
  variant="primary",
141
- elem_classes=["btn-primary"],
142
  interactive=False,
 
143
  )
 
144
  approve_btn = gr.Button(
145
  "Approve Final Version",
146
  variant="secondary",
@@ -253,7 +274,7 @@ if __name__ == "__main__":
253
  demo.launch(
254
  server_name="0.0.0.0",
255
  server_port=settings.app_port,
256
- theme=gr.themes.Ocean(),
257
  css_paths=[_css_path],
258
  share=True,
259
  )
 
36
  # Phase 1: Form Interface
37
  # ========================================================================
38
  with gr.Group(visible=True) as form_phase:
39
+ with gr.Row(
40
+ variant="panel",
41
+ elem_classes=["container", "section"]):
42
+ gr.Markdown(
43
  """
44
  <div class="page-header">
45
  <h1 class="page-title">Job Application Writer</h1>
 
50
  </div>
51
  """
52
  )
53
+ with gr.Row(
54
+ variant="panel",
55
+ elem_classes=["container", "section"]):
56
+ with gr.Column(scale=1, elem_classes=["form-container", "card"]):
57
+ job_url = gr.Textbox(
58
+ label="Job Posting URL",
59
+ placeholder="https://example.com/job-posting",
60
+ lines=1,
61
+ )
62
+ email = gr.Textbox(
63
+ label="Email ID",
64
+ placeholder="you@example.com",
65
+ lines=1,
66
+ )
67
+ resume_url = gr.Textbox(
68
+ label="Resume File URL",
69
+ placeholder="https://example.com/resume.pdf",
70
+ lines=1,
71
+ info=(
72
+ "URL where your resume has been uploaded "
73
+ "(optional - uses default if empty)"
74
+ ),
75
+ )
76
+ content_type = gr.Dropdown(
77
+ choices=["cover_letter", "bullets", "linkedin_note"],
78
+ value=None,
79
+ label="Content Type",
80
+ info="Select the type of content to generate",
81
+ )
82
+ form_error = gr.Markdown("", elem_classes=["form-error"])
83
+ generate_btn = gr.Button(
84
+ "Generate Content",
85
+ variant="primary",
86
+ size="lg",
87
+ elem_classes=["btn-primary"],
88
+ interactive=False,
89
+ )
90
 
91
  # ========================================================================
92
  # Phase 2: Chat Interface
93
  # ========================================================================
94
  with gr.Group(visible=False) as chat_phase:
95
+ with gr.Row(
96
+ variant="panel",
97
+ elem_classes=["container", "section"]):
98
+ gr.Markdown(
99
+ (
100
+ "<div class='chat-page-header'>"
101
+ "<h1 class='page-title'>Job Application Writer</h1>"
102
+ "</div>"
103
+ ),
104
+ elem_classes=["chat-title"],
105
+ padding=True,
106
+
107
+ )
108
  with gr.Row(elem_classes=["container", "section"]):
109
  with gr.Column(elem_classes=["chat-phase-container", "card"]):
110
  with gr.Row(elem_classes=["chat-header-row"]):
111
+ cancel_run_btn = gr.Button(
112
+ "⏹",
113
+ variant="stop",
114
+ size="sm",
115
+ elem_classes=["stop-button"],
116
+ min_width=40,
117
+ visible=False,
118
  )
119
  new_session_btn = gr.Button(
120
  "+",
 
131
  show_label=False,
132
  allow_file_downloads=False,
133
  line_breaks=False,
134
+ buttons=['copy'],
135
  )
136
 
137
  with gr.Group(
138
+ visible=True, elem_classes=["section", "feedback-section"]
139
  ) as feedback_section:
140
+ with gr.Row(elem_classes=["feedback-row"], scale=1):
141
+ upload_btn = gr.UploadButton(
142
+ "📎",
143
+ file_types=[".pdf", ".docx", ".txt"],
144
+ file_count="single",
145
+ elem_classes=["upload-button"],
146
+ min_width=40,
147
+ variant="primary",
148
+ size="lg"
149
+ )
150
+ feedback_input = gr.Textbox(
151
+ placeholder="Type your feedback or leave empty to approve...",
152
+ lines=1,
153
+ show_label=False,
154
+ elem_classes=["feedback-input"],
155
+ scale=4,
156
+ )
157
  send_btn = gr.Button(
158
+ "",
159
  variant="primary",
160
+ elem_classes=["send-button"],
161
  interactive=False,
162
+ min_width=40,
163
  )
164
+ with gr.Row():
165
  approve_btn = gr.Button(
166
  "Approve Final Version",
167
  variant="secondary",
 
274
  demo.launch(
275
  server_name="0.0.0.0",
276
  server_port=settings.app_port,
277
+ theme=gr.themes.Glass(),
278
  css_paths=[_css_path],
279
  share=True,
280
  )
job_writer_ui/static/custom.css CHANGED
@@ -22,11 +22,11 @@ body {
22
  }
23
 
24
  .gradio-container {
25
- min-height: 100vh !important;
26
- width: 100vw !important;
27
- background-color: var(--bg-page) !important;
28
- margin: 0 !important;
29
- padding: 0 !important;
30
  }
31
 
32
  .container {
@@ -36,13 +36,17 @@ body {
36
  }
37
 
38
  .page-header {
39
- padding: 0.75rem 0 1rem;
 
 
 
 
40
  }
41
 
42
  .chat-page-header {
43
  width: 100%;
44
  text-align: center;
45
- padding: 0;
46
  margin: 0 0 0.5rem;
47
  }
48
 
@@ -66,11 +70,11 @@ body {
66
 
67
  .container.section,
68
  .section:first-of-type {
69
- margin-top: 0 !important;
70
  }
71
 
72
  .container.section + .container.section {
73
- margin-top: 2rem !important;
74
  }
75
 
76
  .section-title {
@@ -107,19 +111,19 @@ body {
107
  .gradio-chatbot .copy-btn-wrapper,
108
  .gradio-chatbot .copy-btn-wrapper button,
109
  .gradio-chatbot button.copy-message-btn {
110
- display: none !important;
111
  }
112
 
113
  .btn-primary button {
114
- background: var(--brand) !important;
115
- color: #FFFFFF !important;
116
- border-color: var(--brand) !important;
117
  }
118
 
119
  .btn-secondary button {
120
- background: transparent !important;
121
- color: var(--brand) !important;
122
- border-color: var(--brand) !important;
123
  }
124
 
125
  @media (max-width: 768px) {
@@ -139,10 +143,6 @@ body {
139
  }
140
  }
141
 
142
- * {
143
- font-family: 'Inter', sans-serif;
144
- }
145
-
146
  .form-container {
147
  max-width: 800px !important;
148
  width: 90% !important;
@@ -161,22 +161,22 @@ body {
161
  }
162
 
163
  .chat-phase-container {
164
- display: flex !important;
165
- flex-direction: column !important;
166
- justify-content: flex-start !important;
167
- min-height: auto !important;
168
- padding: 0.75rem 0 1.25rem !important;
169
  }
170
 
171
  [data-testid="bot"] .markdown,
172
  [data-testid="user"] .markdown,
173
  .message-wrap .markdown {
174
- padding: 0.5rem !important;
175
- margin: 0.25rem 0 !important;
176
  }
177
 
178
  .gradio-chatbot {
179
- margin: 0 !important;
180
  }
181
 
182
  .primary-button {
@@ -201,11 +201,11 @@ body {
201
 
202
 
203
  .chat-header-row {
204
- display: flex !important;
205
- justify-content: space-between !important;
206
- align-items: center !important;
207
- margin-bottom: 0.5rem !important;
208
- gap: 0.75rem !important;
209
  }
210
 
211
  .chat-title {
@@ -243,4 +243,128 @@ body {
243
  background-color: #4338ca !important;
244
  }
245
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
246
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  }
23
 
24
  .gradio-container {
25
+ min-height: 100vh;
26
+ width: 100vw;
27
+ background-color: var(--bg-page);
28
+ margin: 0;
29
+ padding: 0;
30
  }
31
 
32
  .container {
 
36
  }
37
 
38
  .page-header {
39
+ font-family: Arial, Helvetica, sans-serif;
40
+ font-size: 2.5rem;
41
+ font-style: normal;
42
+ text-align: center;
43
+ padding: 0rem 0 0rem;
44
  }
45
 
46
  .chat-page-header {
47
  width: 100%;
48
  text-align: center;
49
+ padding: 5px;
50
  margin: 0 0 0.5rem;
51
  }
52
 
 
70
 
71
  .container.section,
72
  .section:first-of-type {
73
+ margin-top: 0;
74
  }
75
 
76
  .container.section + .container.section {
77
+ margin-top: 2rem;
78
  }
79
 
80
  .section-title {
 
111
  .gradio-chatbot .copy-btn-wrapper,
112
  .gradio-chatbot .copy-btn-wrapper button,
113
  .gradio-chatbot button.copy-message-btn {
114
+ display: none;
115
  }
116
 
117
  .btn-primary button {
118
+ background: var(--brand);
119
+ color: #FFFFFF;
120
+ border-color: var(--brand);
121
  }
122
 
123
  .btn-secondary button {
124
+ background: transparent;
125
+ color: var(--brand);
126
+ border-color: var(--brand);
127
  }
128
 
129
  @media (max-width: 768px) {
 
143
  }
144
  }
145
 
 
 
 
 
146
  .form-container {
147
  max-width: 800px !important;
148
  width: 90% !important;
 
161
  }
162
 
163
  .chat-phase-container {
164
+ display: flex;
165
+ flex-direction: column;
166
+ justify-content: flex-start;
167
+ min-height: auto;
168
+ padding: 0.75rem 0 1.25rem;
169
  }
170
 
171
  [data-testid="bot"] .markdown,
172
  [data-testid="user"] .markdown,
173
  .message-wrap .markdown {
174
+ padding: 0.5rem;
175
+ margin: 0.25rem 0;
176
  }
177
 
178
  .gradio-chatbot {
179
+ margin: 0;
180
  }
181
 
182
  .primary-button {
 
201
 
202
 
203
  .chat-header-row {
204
+ display: flex;
205
+ justify-content: space-between;
206
+ align-items: center;
207
+ margin-bottom: 2.5rem;
208
+ gap: 0.75rem;
209
  }
210
 
211
  .chat-title {
 
243
  background-color: #4338ca !important;
244
  }
245
 
246
+ /* Stop button - circular red */
247
+ .stop-button button {
248
+ min-width: 40px !important;
249
+ min-height: 40px !important;
250
+ max-width: 40px !important;
251
+ max-height: 40px !important;
252
+ width: 40px !important;
253
+ height: 40px !important;
254
+ padding: 0 !important;
255
+ border-radius: 9999px !important;
256
+ background-color: #EF4444 !important;
257
+ color: white !important;
258
+ border: none !important;
259
+ font-size: 1rem !important;
260
+ font-weight: 500 !important;
261
+ line-height: 1 !important;
262
+ display: inline-flex !important;
263
+ align-items: center !important;
264
+ justify-content: center !important;
265
+ cursor: pointer !important;
266
+ transition: background-color 0.2s ease !important;
267
+ flex-shrink: 0 !important;
268
+ flex-grow: 0 !important;
269
+ box-sizing: border-box !important;
270
+ }
271
+
272
+ .stop-button button:hover {
273
+ background-color: #DC2626 !important;
274
+ }
275
+
276
+ /* Send button - circular */
277
+ .send-button button {
278
+ min-width: 40px !important;
279
+ min-height: 40px !important;
280
+ max-width: 40px !important;
281
+ max-height: 40px !important;
282
+ width: 40px !important;
283
+ height: 40px !important;
284
+ padding: 0 !important;
285
+ border-radius: 9999px !important;
286
+ background-color: var(--brand) !important;
287
+ color: white !important;
288
+ border: none !important;
289
+ font-size: 1.25rem !important;
290
+ line-height: 1 !important;
291
+ display: inline-flex !important;
292
+ align-items: center !important;
293
+ justify-content: center !important;
294
+ cursor: pointer !important;
295
+ transition: background-color 0.2s ease !important;
296
+ flex-shrink: 0 !important;
297
+ }
298
+
299
+ .send-button button:hover {
300
+ background-color: #4338ca !important;
301
+ }
302
+
303
+ .send-button button:disabled {
304
+ background-color: #9CA3AF !important;
305
+ cursor: not-allowed !important;
306
+ }
307
 
308
+ /* Feedback input row - modern chat bar style */
309
+ .feedback-row {
310
+ display: flex !important;
311
+ align-items: center !important;
312
+ gap: 0.5rem !important;
313
+ padding: 0.5rem !important;
314
+ border: 1px solid #E5E7EB !important;
315
+ border-radius: 24px !important;
316
+ background: #FFFFFF !important;
317
+ }
318
+
319
+ /* Upload button - compact circular */
320
+ .upload-button {
321
+ margin: 0 !important;
322
+ padding: 0 !important;
323
+ }
324
+
325
+ .upload-button button:hover {
326
+ background-color: #F3F4F6 !important;
327
+ }
328
+
329
+ .feedback-input textarea {
330
+ border: none !important;
331
+ outline: none !important;
332
+ box-shadow: none !important;
333
+ background: transparent !important;
334
+ resize: none !important;
335
+ padding: 0.5rem 1rem !important;
336
+ }
337
+
338
+ .feedback-input textarea:focus {
339
+ border: none !important;
340
+ outline: none !important;
341
+ box-shadow: none !important;
342
+ }
343
+
344
+ .feedback-section {
345
+ margin-top: 1rem !important;
346
+ }
347
+
348
+ /* Tighter message spacing in chatbot */
349
+ .chat-container .message-wrap {
350
+ padding: 0.25rem 0 !important;
351
+ }
352
+
353
+ .chat-container [data-testid="bot"],
354
+ .chat-container [data-testid="user"] {
355
+ padding: 0.25rem 0 !important;
356
+ }
357
+
358
+ /* Reduce section margin */
359
+ .section {
360
+ margin-top: 1rem !important;
361
+ }
362
+
363
+ /* Header row alignment */
364
+ .chat-header-row {
365
+ display: flex !important;
366
+ justify-content: flex-end !important;
367
+ align-items: center !important;
368
+ margin-bottom: 0.5rem !important;
369
+ gap: 0.5rem !important;
370
+ }