lamgamepro1 commited on
Commit
ee85042
·
verified ·
1 Parent(s): e112614

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +96 -99
app.py CHANGED
@@ -19,7 +19,6 @@ DB_FILE = "nodeai_pro_memory.json"
19
 
20
  # 2. HỆ THỐNG TRÍ NHỚ THEO USER
21
  def get_user_id(username):
22
- """Tạo ID unique cho mỗi user dựa trên username"""
23
  if not username: return "guest"
24
  return hashlib.md5(username.encode()).hexdigest()[:8]
25
 
@@ -38,38 +37,33 @@ def save_db(data):
38
  custom_css = """
39
  @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;800&family=Plus+Jakarta+Sans:wght@300;400;500;600;700&display=swap');
40
 
41
- /* RESET & BASE */
42
  * { box-sizing: border-box; }
43
  body { margin: 0; font-family: 'Plus Jakarta Sans', sans-serif !important; }
44
  .gradio-container {
45
  background: linear-gradient(135deg, #0a0a0f 0%, #1a0d1a 50%, #0f0a1a 100%) !important;
46
- min-height: 100vh !important;
47
- position: relative !important;
48
- overflow-x: hidden !important;
49
  }
50
 
51
- /* MOBILE HAMBURGER MENU */
52
  .hamburger {
53
- position: fixed !important; top: 20px !important; left: 20px !important;
54
- z-index: 3000 !important; background: rgba(20,20,30,0.95) !important;
55
- border: 1px solid rgba(0,242,254,0.3) !important; border-radius: 12px !important;
56
- width: 56px !important; height: 56px !important; cursor: pointer !important;
57
  backdrop-filter: blur(20px) !important; transition: all 0.3s ease !important;
58
  }
59
  .hamburger:hover { box-shadow: 0 8px 25px rgba(0,242,254,0.3) !important; }
60
 
61
- /* SIDEBAR - MOBILE SLIDE IN */
62
  .sidebar-mobile {
63
- position: fixed !important; top: 0 !important; left: -320px !important;
64
- width: 320px !important; height: 100vh !important; z-index: 2500 !important;
65
- background: rgba(15,15,25,0.98) !important; backdrop-filter: blur(25px) !important;
66
- border-right: 1px solid rgba(255,255,255,0.08) !important;
67
- transition: left 0.4s cubic-bezier(0.25,0.46,0.45,0.94) !important;
68
- padding: 80px 25px 25px !important; overflow-y: auto !important;
69
  }
70
  .sidebar-mobile.open { left: 0 !important; }
71
 
72
- /* LOGO CYBERPUNK */
73
  .logo-text {
74
  font-family: 'Orbitron', monospace !important; font-weight: 800 !important;
75
  background: linear-gradient(135deg, #00f2fe 0%, #4facfe 50%, #ff6b6b 100%) !important;
@@ -78,14 +72,10 @@ body { margin: 0; font-family: 'Plus Jakarta Sans', sans-serif !important; }
78
  text-align: center !important; margin-bottom: 35px !important;
79
  animation: gradientShift 4s ease infinite !important;
80
  }
81
- @keyframes gradientShift {
82
- 0%, 100% { background-position: 0% 50%; }
83
- 50% { background-position: 100% 50%; }
84
- }
85
 
86
- /* USER INFO CARD */
87
- .user-card {
88
- background: rgba(255,107,107,0.15) !important; border: 1px solid rgba(255,107,107,0.3) !important;
89
  border-radius: 16px !important; padding: 20px !important; margin-bottom: 25px !important;
90
  text-align: center !important; backdrop-filter: blur(15px) !important;
91
  }
@@ -93,74 +83,50 @@ body { margin: 0; font-family: 'Plus Jakarta Sans', sans-serif !important; }
93
 
94
  /* BUTTONS */
95
  .orange-btn, .hist-btn {
96
- width: 100% !important; border-radius: 12px !important; border: none !important;
97
- font-weight: 600 !important; cursor: pointer !important; transition: all 0.3s ease !important;
98
- margin-bottom: 12px !important; padding: 14px 18px !important; font-size: 15px !important;
99
  }
100
  .orange-btn {
101
  background: linear-gradient(135deg, #FF6B6B 0%, #FF8E53 50%, #FF6B6B 100%) !important;
102
  box-shadow: 0 6px 20px rgba(255,107,107,0.4) !important; color: white !important;
103
  }
104
  .orange-btn:hover { transform: translateY(-2px) !important; box-shadow: 0 10px 30px rgba(255,107,107,0.6) !important; }
105
- .hist-btn {
106
- background: rgba(255,255,255,0.05) !important; color: #a0aec0 !important;
107
  border: 1px solid rgba(255,255,255,0.1) !important; text-align: left !important;
108
  }
109
- .hist-btn:hover, .hist-btn.active {
110
- background: rgba(0,242,254,0.15) !important; color: #00f2fe !important;
111
  border-color: rgba(0,242,254,0.4) !important; box-shadow: 0 4px 15px rgba(0,242,254,0.2) !important;
112
  }
113
 
114
- /* MAIN CHAT - FULLSCREEN MOBILE */
115
- .chat-container {
116
- padding: 20px !important; max-width: 100% !important; margin: 0 auto !important;
117
  min-height: 100vh !important; display: flex !important; flex-direction: column !important;
118
  }
119
- .chatbot-box {
120
- flex: 1 !important; background: rgba(20,20,30,0.6) !important;
121
- border: 1px solid rgba(0,242,254,0.2) !important; border-radius: 24px !important;
122
- padding: 25px !important; margin-bottom: 120px !important; max-height: 70vh !important;
123
- overflow-y: auto !important; backdrop-filter: blur(20px) !important;
124
  box-shadow: 0 20px 60px rgba(0,0,0,0.5) !important;
125
  }
126
-
127
- /* FIXED INPUT BAR */
128
- .input-bar {
129
- position: fixed !important; bottom: 20px !important; left: 20px !important; right: 20px !important;
130
- z-index: 1000 !important; background: rgba(20,20,30,0.95) !important;
131
- backdrop-filter: blur(25px) !important; border: 1px solid rgba(0,242,254,0.3) !important;
132
- border-radius: 24px !important; padding: 12px 20px !important;
133
- box-shadow: 0 15px 50px rgba(0,0,0,0.6) !important; transition: all 0.3s ease !important;
134
- }
135
- .input-bar:focus-within {
136
- border-color: rgba(0,242,254,0.6) !important; box-shadow: 0 20px 60px rgba(0,242,254,0.2) !important;
137
  }
138
-
139
- /* HIDE DEFAULT ELEMENTS */
140
  footer, .gr-button-secondary { display: none !important; }
141
 
142
- /* DESKTOP SIDEBAR */
143
- @media screen and (min-width: 1024px) {
144
  .hamburger { display: none !important; }
145
  .sidebar-mobile { position: static !important; left: 0 !important; width: 340px !important; }
146
  .chat-container { margin-left: 340px !important; max-width: calc(100% - 360px) !important; }
147
  }
148
-
149
- /* TABLET */
150
- @media screen and (max-width: 1023px) and (min-width: 769px) {
151
- .sidebar-mobile { width: 300px !important; }
152
- .chat-container { margin-left: 0 !important; }
153
- }
154
-
155
- /* PERFECT MOBILE */
156
- @media screen and (max-width: 768px) {
157
  .chatbot-box { margin-bottom: 140px !important; padding: 20px !important; border-radius: 20px !important; }
158
  .input-bar { padding: 10px 16px !important; bottom: 15px !important; }
159
- .logo-text { font-size: 24px !important; }
160
  }
161
  """
162
 
163
- # 4. CORE LOGIC CHAT - PER USER
164
  def chat_fn(message, history, session_id, store, username):
165
  user_text = message.get("text", "")
166
  if not user_text and not message.get("files", []):
@@ -195,7 +161,6 @@ def chat_fn(message, history, session_id, store, username):
195
  store[user_id]["workspaces"][session_id]["history"] = final_h
196
  store[user_id]["workspaces"][session_id]["title"] = user_text[:30] if user_text else store[user_id]["workspaces"][session_id]["title"]
197
  save_db(store)
198
-
199
  yield {"text": ""}, final_h, session_id, store, ans
200
  except Exception as e:
201
  yield {"text": f"Lỗi: {str(e)}"}, history + [{"role": "assistant", "content": str(e)}], session_id, store, ""
@@ -208,30 +173,68 @@ def tts_engine(text):
208
  return "voice.mp3"
209
  except: return None
210
 
211
- # 5. GIAO DIỆN CHÍNH - GRADIO 6.0 SYNTAX
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  with gr.Blocks(title="NodeAI Genesis Pro") as demo:
213
  chat_store = gr.State(load_db())
214
  session_id = gr.State(str(uuid.uuid4()))
215
  current_user = gr.State("guest")
216
  tts_buffer = gr.Textbox(visible=False)
217
 
218
- # HAMBURGER MENU MOBILE
219
  hamburger = gr.Button("☰", elem_classes="hamburger", scale=1)
220
 
221
- # SIDEBAR - MOBILE SLIDE
222
  with gr.Column(elem_classes="sidebar-mobile", visible=False) as sidebar:
223
  gr.HTML("<div class='logo-text'>NODEAI GENESIS</div>")
224
 
225
- with gr.Row():
226
- gr.HTML("<h3>👤 NGƯỜI DÙNG</h3>")
 
 
 
227
  user_card = gr.HTML(value="<div class='user-card'><h3>Guest</h3><p>Chưa đăng nhập</p></div>")
228
 
 
229
  new_btn = gr.Button("+ TẠO KHÔNG GIAN MỚI", elem_classes="orange-btn", scale=1)
230
 
231
- gr.HTML("<h3>📚 LỊCH SỬ</h3>")
 
232
  history_container = gr.Column()
233
-
234
- gr.HTML("<div style='height: 200px;'></div>") # Spacer
235
 
236
  # MAIN CHAT
237
  with gr.Column(elem_classes="chat-container"):
@@ -249,30 +252,27 @@ with gr.Blocks(title="NodeAI Genesis Pro") as demo:
249
  container=False
250
  )
251
 
252
- # EVENTS - GRADIO 6.0 EVENT SYSTEM
253
- def toggle_sidebar():
254
- return gr.update(visible=True, elem_classes="sidebar-mobile open")
255
-
256
- def login_event(username):
257
- if username:
258
- user_id = get_user_id(username)
259
- user_html = f"""
260
- <div class='user-card'>
261
- <h3>👋 Xin chào</h3>
262
- <p style='color: #ff8e53; font-weight: 600;'>@{username}</p>
263
- <small>ID: {user_id}</small>
264
- </div>
265
- """
266
- return gr.update(value=user_html), gr.update(visible=True)
267
- return gr.update(value="<div class='user-card'><h3>Guest</h3><p>Chưa đăng nhập</p></div>"), gr.update(visible=False)
268
-
269
- # Event listeners
270
  hamburger.click(toggle_sidebar, outputs=sidebar)
 
 
 
 
 
 
 
 
 
 
 
 
 
271
  new_btn.click(
272
- lambda: ([], str(uuid.uuid4())),
273
  outputs=[chatbot, session_id]
274
  )
275
 
 
276
  msg_input.submit(
277
  chat_fn,
278
  inputs=[msg_input, chatbot, session_id, chat_store, current_user],
@@ -283,9 +283,6 @@ with gr.Blocks(title="NodeAI Genesis Pro") as demo:
283
 
284
  if __name__ == "__main__":
285
  demo.launch(
286
- share=True,
287
- server_name="0.0.0.0",
288
- server_port=7860,
289
- theme=gr.themes.Base(),
290
- css=custom_css
291
  )
 
19
 
20
  # 2. HỆ THỐNG TRÍ NHỚ THEO USER
21
  def get_user_id(username):
 
22
  if not username: return "guest"
23
  return hashlib.md5(username.encode()).hexdigest()[:8]
24
 
 
37
  custom_css = """
38
  @import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;800&family=Plus+Jakarta+Sans:wght@300;400;500;600;700&display=swap');
39
 
 
40
  * { box-sizing: border-box; }
41
  body { margin: 0; font-family: 'Plus Jakarta Sans', sans-serif !important; }
42
  .gradio-container {
43
  background: linear-gradient(135deg, #0a0a0f 0%, #1a0d1a 50%, #0f0a1a 100%) !important;
44
+ min-height: 100vh !important; position: relative !important; overflow-x: hidden !important;
 
 
45
  }
46
 
47
+ /* MOBILE HAMBURGER */
48
  .hamburger {
49
+ position: fixed !important; top: 20px !important; left: 20px !important; z-index: 3000 !important;
50
+ background: rgba(20,20,30,0.95) !important; border: 1px solid rgba(0,242,254,0.3) !important;
51
+ border-radius: 12px !important; width: 56px !important; height: 56px !important;
 
52
  backdrop-filter: blur(20px) !important; transition: all 0.3s ease !important;
53
  }
54
  .hamburger:hover { box-shadow: 0 8px 25px rgba(0,242,254,0.3) !important; }
55
 
56
+ /* SIDEBAR MOBILE */
57
  .sidebar-mobile {
58
+ position: fixed !important; top: 0 !important; left: -320px !important; width: 320px !important;
59
+ height: 100vh !important; z-index: 2500 !important; background: rgba(15,15,25,0.98) !important;
60
+ backdrop-filter: blur(25px) !important; border-right: 1px solid rgba(255,255,255,0.08) !important;
61
+ transition: left 0.4s cubic-bezier(0.25,0.46,0.45,0.94) !important; padding: 80px 25px 25px !important;
62
+ overflow-y: auto !important;
 
63
  }
64
  .sidebar-mobile.open { left: 0 !important; }
65
 
66
+ /* LOGO */
67
  .logo-text {
68
  font-family: 'Orbitron', monospace !important; font-weight: 800 !important;
69
  background: linear-gradient(135deg, #00f2fe 0%, #4facfe 50%, #ff6b6b 100%) !important;
 
72
  text-align: center !important; margin-bottom: 35px !important;
73
  animation: gradientShift 4s ease infinite !important;
74
  }
75
+ @keyframes gradientShift { 0%,100%{background-position:0%50%} 50%{background-position:100%50%} }
 
 
 
76
 
77
+ /* USER CARD */
78
+ .user-card { background: rgba(255,107,107,0.15) !important; border: 1px solid rgba(255,107,107,0.3) !important;
 
79
  border-radius: 16px !important; padding: 20px !important; margin-bottom: 25px !important;
80
  text-align: center !important; backdrop-filter: blur(15px) !important;
81
  }
 
83
 
84
  /* BUTTONS */
85
  .orange-btn, .hist-btn {
86
+ width: 100% !important; border-radius: 12px !important; border: none !important; font-weight: 600 !important;
87
+ cursor: pointer !important; transition: all 0.3s ease !important; margin-bottom: 12px !important;
88
+ padding: 14px 18px !important; font-size: 15px !important;
89
  }
90
  .orange-btn {
91
  background: linear-gradient(135deg, #FF6B6B 0%, #FF8E53 50%, #FF6B6B 100%) !important;
92
  box-shadow: 0 6px 20px rgba(255,107,107,0.4) !important; color: white !important;
93
  }
94
  .orange-btn:hover { transform: translateY(-2px) !important; box-shadow: 0 10px 30px rgba(255,107,107,0.6) !important; }
95
+ .hist-btn { background: rgba(255,255,255,0.05) !important; color: #a0aec0 !important;
 
96
  border: 1px solid rgba(255,255,255,0.1) !important; text-align: left !important;
97
  }
98
+ .hist-btn:hover { background: rgba(0,242,254,0.15) !important; color: #00f2fe !important;
 
99
  border-color: rgba(0,242,254,0.4) !important; box-shadow: 0 4px 15px rgba(0,242,254,0.2) !important;
100
  }
101
 
102
+ /* CHAT */
103
+ .chat-container { padding: 20px !important; max-width: 100% !important; margin: 0 auto !important;
 
104
  min-height: 100vh !important; display: flex !important; flex-direction: column !important;
105
  }
106
+ .chatbot-box { flex: 1 !important; background: rgba(20,20,30,0.6) !important; border: 1px solid rgba(0,242,254,0.2) !important;
107
+ border-radius: 24px !important; padding: 25px !important; margin-bottom: 120px !important;
108
+ max-height: 70vh !important; overflow-y: auto !important; backdrop-filter: blur(20px) !important;
 
 
109
  box-shadow: 0 20px 60px rgba(0,0,0,0.5) !important;
110
  }
111
+ .input-bar { position: fixed !important; bottom: 20px !important; left: 20px !important; right: 20px !important;
112
+ z-index: 1000 !important; background: rgba(20,20,30,0.95) !important; backdrop-filter: blur(25px) !important;
113
+ border: 1px solid rgba(0,242,254,0.3) !important; border-radius: 24px !important; padding: 12px 20px !important;
114
+ box-shadow: 0 15px 50px rgba(0,0,0,0.6) !important;
 
 
 
 
 
 
 
115
  }
 
 
116
  footer, .gr-button-secondary { display: none !important; }
117
 
118
+ @media (min-width: 1024px) {
 
119
  .hamburger { display: none !important; }
120
  .sidebar-mobile { position: static !important; left: 0 !important; width: 340px !important; }
121
  .chat-container { margin-left: 340px !important; max-width: calc(100% - 360px) !important; }
122
  }
123
+ @media (max-width: 768px) {
 
 
 
 
 
 
 
 
124
  .chatbot-box { margin-bottom: 140px !important; padding: 20px !important; border-radius: 20px !important; }
125
  .input-bar { padding: 10px 16px !important; bottom: 15px !important; }
 
126
  }
127
  """
128
 
129
+ # 4. CORE FUNCTIONS
130
  def chat_fn(message, history, session_id, store, username):
131
  user_text = message.get("text", "")
132
  if not user_text and not message.get("files", []):
 
161
  store[user_id]["workspaces"][session_id]["history"] = final_h
162
  store[user_id]["workspaces"][session_id]["title"] = user_text[:30] if user_text else store[user_id]["workspaces"][session_id]["title"]
163
  save_db(store)
 
164
  yield {"text": ""}, final_h, session_id, store, ans
165
  except Exception as e:
166
  yield {"text": f"Lỗi: {str(e)}"}, history + [{"role": "assistant", "content": str(e)}], session_id, store, ""
 
173
  return "voice.mp3"
174
  except: return None
175
 
176
+ def toggle_sidebar():
177
+ return gr.update(visible=True, elem_classes="sidebar-mobile open")
178
+
179
+ def login_event(username):
180
+ if username:
181
+ user_id = get_user_id(username)
182
+ user_html = f"""
183
+ <div class='user-card'>
184
+ <h3>👋 Xin chào</h3>
185
+ <p style='color: #ff8e53; font-weight: 600;'>@{username}</p>
186
+ <small>ID: {user_id}</small>
187
+ </div>
188
+ """
189
+ return gr.update(value=user_html), username
190
+ return gr.update(value="<div class='user-card'><h3>Guest</h3><p>Chưa đăng nhập</p></div>"), "guest"
191
+
192
+ def load_history_buttons(store, username):
193
+ user_id = get_user_id(username)
194
+ if user_id in store and "workspaces" in store[user_id]:
195
+ return [
196
+ gr.Button(
197
+ f"💬 {data['title']} ({len(data['history'])//2} tin)",
198
+ elem_classes="hist-btn"
199
+ ) for sid, data in sorted(store[user_id]["workspaces"].items(),
200
+ key=lambda x: x[1]["created"], reverse=True)
201
+ ]
202
+ return []
203
+
204
+ def load_workspace(store, session_id, username):
205
+ user_id = get_user_id(username)
206
+ if user_id in store and session_id in store[user_id]["workspaces"]:
207
+ history = store[user_id]["workspaces"][session_id]["history"]
208
+ return history, session_id
209
+ return [], session_id
210
+
211
+ # 5. GIAO DIỆN CHÍNH
212
  with gr.Blocks(title="NodeAI Genesis Pro") as demo:
213
  chat_store = gr.State(load_db())
214
  session_id = gr.State(str(uuid.uuid4()))
215
  current_user = gr.State("guest")
216
  tts_buffer = gr.Textbox(visible=False)
217
 
218
+ # HAMBURGER MENU
219
  hamburger = gr.Button("☰", elem_classes="hamburger", scale=1)
220
 
221
+ # SIDEBAR
222
  with gr.Column(elem_classes="sidebar-mobile", visible=False) as sidebar:
223
  gr.HTML("<div class='logo-text'>NODEAI GENESIS</div>")
224
 
225
+ # ĐĂNG NHẬP BUTTON Ở ĐÂY 👈
226
+ login_btn = gr.LoginButton(value="🔐 ĐĂNG NHẬP HF", elem_classes="orange-btn", scale=1)
227
+
228
+ # USER INFO
229
+ gr.HTML("<h3 style='color: #a0aec0; margin: 25px 0 15px 0;'>👤 NGƯỜI DÙNG</h3>")
230
  user_card = gr.HTML(value="<div class='user-card'><h3>Guest</h3><p>Chưa đăng nhập</p></div>")
231
 
232
+ # TẠO WORKSPACE MỚI
233
  new_btn = gr.Button("+ TẠO KHÔNG GIAN MỚI", elem_classes="orange-btn", scale=1)
234
 
235
+ # LỊCH SỬ CHAT Ở ĐÂY 👈
236
+ gr.HTML("<h3 style='color: #a0aec0; margin: 25px 0 15px 0;'>📚 LỊCH SỬ</h3>")
237
  history_container = gr.Column()
 
 
238
 
239
  # MAIN CHAT
240
  with gr.Column(elem_classes="chat-container"):
 
252
  container=False
253
  )
254
 
255
+ # EVENTS
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
256
  hamburger.click(toggle_sidebar, outputs=sidebar)
257
+
258
+ # ĐĂNG NHẬP → CẬP NHẬT USER + LỊCH SỬ
259
+ login_btn.change(
260
+ login_event,
261
+ inputs=current_user,
262
+ outputs=[user_card, current_user]
263
+ ).then(
264
+ load_history_buttons,
265
+ inputs=[chat_store, current_user],
266
+ outputs=history_container
267
+ )
268
+
269
+ # TẠO MỚI
270
  new_btn.click(
271
+ lambda: ([], str(uuid.uuid4())),
272
  outputs=[chatbot, session_id]
273
  )
274
 
275
+ # CHAT
276
  msg_input.submit(
277
  chat_fn,
278
  inputs=[msg_input, chatbot, session_id, chat_store, current_user],
 
283
 
284
  if __name__ == "__main__":
285
  demo.launch(
286
+ share=True, server_name="0.0.0.0", server_port=7860,
287
+ theme=gr.themes.Base(), css=custom_css
 
 
 
288
  )