chat-console 0.4.3__py3-none-any.whl → 0.4.7__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
app/main.py CHANGED
@@ -179,173 +179,276 @@ class SimpleChatApp(App): # Keep SimpleChatApp class definition
179
179
  os.makedirs(log_dir, exist_ok=True)
180
180
  LOG_FILE = os.path.join(log_dir, "textual.log") # Use absolute path
181
181
 
182
- CSS = """ # Keep SimpleChatApp CSS start
183
- #main-content { # Keep SimpleChatApp CSS
182
+ # Rams-inspired CSS following "As little design as possible"
183
+ CSS = """
184
+ /* Main layout - Clean foundation */
185
+ #main-content {
184
186
  width: 100%;
185
187
  height: 100%;
186
- padding: 0 1;
188
+ padding: 0;
189
+ background: #0C0C0C;
187
190
  }
188
191
 
192
+ /* App header with ASCII border aesthetic */
189
193
  #app-info-bar {
190
194
  width: 100%;
191
- height: 1;
192
- background: $surface-darken-3;
193
- color: $text-muted;
194
- padding: 0 1;
195
+ height: 3;
196
+ background: #0C0C0C;
197
+ color: #E8E8E8;
198
+ padding: 1 2;
199
+ border-bottom: solid #333333 1;
195
200
  }
196
201
 
197
202
  #version-info {
198
203
  width: auto;
199
204
  text-align: left;
205
+ color: #E8E8E8;
200
206
  }
201
207
 
202
208
  #model-info {
203
209
  width: 1fr;
204
210
  text-align: right;
211
+ color: #666666;
205
212
  }
206
213
 
207
- #conversation-title { # Keep SimpleChatApp CSS
208
- width: 100%; # Keep SimpleChatApp CSS
209
- height: 2;
210
- background: $surface-darken-2;
211
- color: $text;
212
- content-align: center middle;
213
- text-align: center;
214
- border-bottom: solid $primary-darken-2;
214
+ /* Conversation title - Functional hierarchy */
215
+ #conversation-title {
216
+ width: 100%;
217
+ height: 3;
218
+ background: #0C0C0C;
219
+ color: #E8E8E8;
220
+ content-align: left middle;
221
+ text-align: left;
222
+ padding: 0 2;
223
+ border-bottom: solid #333333 1;
215
224
  }
216
225
 
226
+ /* Action buttons - Minimal design */
217
227
  #action-buttons {
218
228
  width: 100%;
219
229
  height: auto;
220
- padding: 0 1; /* Corrected padding: 0 vertical, 1 horizontal */
221
- align-horizontal: center;
222
- background: $surface-darken-1;
230
+ padding: 2;
231
+ align-horizontal: left;
232
+ background: #0C0C0C;
233
+ border-bottom: solid #333333 1;
223
234
  }
224
235
 
225
236
  #new-chat-button, #change-title-button {
226
- margin: 0 1;
227
- min-width: 15;
237
+ margin: 0 1 0 0;
238
+ min-width: 12;
239
+ background: transparent;
240
+ color: #E8E8E8;
241
+ border: solid #333333 1;
242
+ padding: 1 2;
243
+ }
244
+
245
+ #new-chat-button:hover, #change-title-button:hover {
246
+ background: #1A1A1A;
247
+ border: solid #33FF33 1;
228
248
  }
229
249
 
230
- #messages-container { # Keep SimpleChatApp CSS
231
- width: 100%; # Keep SimpleChatApp CSS
250
+ /* Messages container - Purposeful spacing */
251
+ #messages-container {
252
+ width: 100%;
232
253
  height: 1fr;
233
- min-height: 10;
234
- border-bottom: solid $primary-darken-2;
254
+ min-height: 15;
235
255
  overflow: auto;
236
- padding: 0 1;
256
+ padding: 2;
257
+ background: #0C0C0C;
258
+ border-bottom: solid #333333 1;
237
259
  }
238
260
 
239
- #loading-indicator { # Keep SimpleChatApp CSS
240
- width: 100%; # Keep SimpleChatApp CSS
241
- height: 1;
242
- background: $primary-darken-1;
243
- color: $text;
261
+ /* Loading indicator - Unobtrusive */
262
+ #loading-indicator {
263
+ width: 100%;
264
+ height: 2;
265
+ background: #0C0C0C;
266
+ color: #666666;
244
267
  content-align: center middle;
245
268
  text-align: center;
246
- text-style: bold;
269
+ border-bottom: solid #333333 1;
270
+ padding: 0 2;
247
271
  }
248
272
 
249
- #loading-indicator.hidden { # Keep SimpleChatApp CSS
273
+ #loading-indicator.hidden {
250
274
  display: none;
251
275
  }
252
276
 
253
277
  #loading-indicator.model-loading {
254
- background: $warning;
255
- color: $text;
278
+ color: #33FF33;
256
279
  }
257
280
 
258
- #input-area { # Keep SimpleChatApp CSS
259
- width: 100%; # Keep SimpleChatApp CSS
281
+ /* Input area - Clean and functional */
282
+ #input-area {
283
+ width: 100%;
260
284
  height: auto;
261
- min-height: 4;
262
- max-height: 10;
263
- padding: 1;
285
+ min-height: 5;
286
+ max-height: 12;
287
+ padding: 2;
288
+ background: #0C0C0C;
264
289
  }
265
290
 
266
- #message-input { # Keep SimpleChatApp CSS
267
- width: 1fr; # Keep SimpleChatApp CSS
268
- min-height: 2;
291
+ #message-input {
292
+ width: 1fr;
293
+ min-height: 3;
269
294
  height: auto;
270
295
  margin-right: 1;
271
- border: solid $primary-darken-2;
296
+ border: solid #333333 1;
297
+ background: #0C0C0C;
298
+ color: #E8E8E8;
299
+ padding: 1;
272
300
  }
273
301
 
274
- #message-input:focus { # Keep SimpleChatApp CSS
275
- border: solid $primary;
302
+ #message-input:focus {
303
+ border: solid #33FF33 1;
304
+ outline: none;
276
305
  }
277
306
 
278
307
  /* Removed CSS for #send-button, #new-chat-button, #view-history-button, #settings-button */ # Keep SimpleChatApp CSS comment
279
308
  /* Removed CSS for #button-row */ # Keep SimpleChatApp CSS comment
280
309
 
281
- #settings-panel { /* Add CSS for the new settings panel */
282
- display: none; /* Hidden by default */
310
+ /* Settings panel - Clean modal design */
311
+ #settings-panel {
312
+ display: none;
283
313
  align: center middle;
284
314
  width: 60;
285
315
  height: auto;
286
- background: $surface;
287
- border: thick $primary;
288
- padding: 1 2;
289
- layer: settings; /* Ensure it's above other elements */
316
+ background: #0C0C0C;
317
+ border: solid #333333 1;
318
+ padding: 2;
319
+ layer: settings;
290
320
  }
291
321
 
292
- #settings-panel.visible { /* Class to show the panel */
322
+ #settings-panel.visible {
293
323
  display: block;
294
324
  }
295
325
 
296
326
  #settings-title {
297
327
  width: 100%;
298
- content-align: center middle;
328
+ content-align: left middle;
299
329
  padding-bottom: 1;
300
- border-bottom: thick $primary-darken-2; /* Correct syntax for bottom border */
330
+ border-bottom: solid #333333 1;
331
+ color: #E8E8E8;
332
+ margin-bottom: 2;
301
333
  }
302
334
 
303
335
  #settings-buttons {
304
336
  width: 100%;
305
337
  height: auto;
306
- align: center middle;
307
- padding-top: 1;
338
+ align: right middle;
339
+ padding-top: 2;
308
340
  }
309
341
 
310
- /* --- Title Input Modal CSS --- */
342
+ #settings-save-button, #settings-cancel-button {
343
+ background: transparent;
344
+ color: #E8E8E8;
345
+ border: solid #333333 1;
346
+ margin-left: 1;
347
+ padding: 1 2;
348
+ }
349
+
350
+ #settings-save-button:hover {
351
+ background: #1A1A1A;
352
+ border: solid #33FF33 1;
353
+ }
354
+
355
+ #settings-cancel-button:hover {
356
+ background: #1A1A1A;
357
+ border: solid #FF4444 1;
358
+ }
359
+
360
+ /* Title Input Modal - Minimal and focused */
311
361
  TitleInputModal {
312
362
  align: center middle;
313
363
  width: 60;
314
364
  height: auto;
315
- background: $surface;
316
- border: thick $primary;
317
- padding: 1 2;
318
- layer: modal; /* Ensure it's above other elements */
365
+ background: #0C0C0C;
366
+ border: solid #333333 1;
367
+ padding: 2;
368
+ layer: modal;
319
369
  }
320
370
 
321
371
  #modal-label {
322
372
  width: 100%;
323
- content-align: center middle;
373
+ content-align: left middle;
324
374
  padding-bottom: 1;
375
+ color: #E8E8E8;
376
+ border-bottom: solid #333333 1;
377
+ margin-bottom: 2;
325
378
  }
326
379
 
327
380
  #title-input {
328
381
  width: 100%;
329
- margin-bottom: 1;
382
+ margin-bottom: 2;
383
+ background: #0C0C0C;
384
+ color: #E8E8E8;
385
+ border: solid #333333 1;
386
+ padding: 1;
387
+ }
388
+
389
+ #title-input:focus {
390
+ border: solid #33FF33 1;
391
+ outline: none;
330
392
  }
331
393
 
332
394
  TitleInputModal Horizontal {
333
395
  width: 100%;
334
396
  height: auto;
335
- align: center middle;
397
+ align: right middle;
398
+ padding-top: 2;
399
+ }
400
+
401
+ #update-button, #cancel-button {
402
+ background: transparent;
403
+ color: #E8E8E8;
404
+ border: solid #333333 1;
405
+ margin-left: 1;
406
+ padding: 1 2;
407
+ }
408
+
409
+ #update-button:hover {
410
+ background: #1A1A1A;
411
+ border: solid #33FF33 1;
412
+ }
413
+
414
+ #cancel-button:hover {
415
+ background: #1A1A1A;
416
+ border: solid #FF4444 1;
417
+ }
418
+
419
+ /* Message Display - Clean typography */
420
+ MessageDisplay {
421
+ width: 100%;
422
+ height: auto;
423
+ margin: 1 0;
424
+ padding: 2;
425
+ text-wrap: wrap;
426
+ background: transparent;
427
+ }
428
+
429
+ MessageDisplay.user-message {
430
+ background: #1A1A1A;
431
+ border-left: solid #33FF33 2;
432
+ margin-left: 2;
433
+ margin-right: 8;
434
+ }
435
+
436
+ MessageDisplay.assistant-message {
437
+ background: transparent;
438
+ border-left: solid #666666 1;
439
+ margin-right: 2;
440
+ margin-left: 8;
336
441
  }
337
442
  """
338
443
 
339
444
  BINDINGS = [ # Keep SimpleChatApp BINDINGS, ensure Enter is not globally bound for settings
340
445
  Binding("q", "quit", "Quit", show=True, key_display="q"),
341
- # Removed binding for "n" (new chat) since there's a dedicated button
342
- Binding("c", "action_new_conversation", "New Chat", show=False, key_display="c", priority=True), # Keep alias with priority
343
- Binding("escape", "action_escape", "Cancel / Stop", show=True, key_display="esc"), # Updated to call our async method
446
+ Binding("c", "action_new_conversation", "New", show=True, key_display="c", priority=True),
447
+ Binding("escape", "action_escape", "Cancel", show=True, key_display="esc"),
344
448
  Binding("ctrl+c", "quit", "Quit", show=False),
345
- Binding("h", "view_history", "History", show=True, key_display="h", priority=True), # Add priority
346
- Binding("s", "settings", "Settings", show=True, key_display="s", priority=True), # Add priority
347
- # Removed binding for "t" (title update) since there's a dedicated button
348
- Binding("m", "model_browser", "Model Browser", show=True, key_display="m", priority=True), # Add model browser binding
449
+ Binding("h", "view_history", "History", show=True, key_display="h", priority=True),
450
+ Binding("s", "settings", "Settings", show=True, key_display="s", priority=True),
451
+ Binding("m", "model_browser", "Models", show=True, key_display="m", priority=True),
349
452
  ] # Keep SimpleChatApp BINDINGS end
350
453
 
351
454
  current_conversation = reactive(None) # Keep SimpleChatApp reactive var
@@ -376,26 +479,26 @@ class SimpleChatApp(App): # Keep SimpleChatApp class definition
376
479
  yield Header()
377
480
 
378
481
  with Vertical(id="main-content"):
379
- # Add app info bar with version and model info
482
+ # Clean app header following Rams principles
380
483
  with Horizontal(id="app-info-bar"):
381
- yield Static(f"Chat Console v{__version__}", id="version-info") # Use imported version
382
- yield Static(f"Model: {self.selected_model}", id="model-info")
484
+ yield Static(f"Chat Console v{__version__}", id="version-info")
485
+ yield Static(self.selected_model, id="model-info")
383
486
 
384
487
  # Conversation title
385
488
  yield Static("New Conversation", id="conversation-title")
386
489
 
387
- # Add action buttons at the top for visibility
490
+ # Action buttons - Minimal and functional
388
491
  with Horizontal(id="action-buttons"):
389
- yield Button("+ New Chat", id="new-chat-button", variant="success")
390
- yield Button("✎ Change Title", id="change-title-button", variant="primary")
492
+ yield Button("", id="new-chat-button") # Solid circle for "new"
493
+ yield Button("✎", id="change-title-button") # Pencil for "edit"
391
494
 
392
495
  # Messages area
393
496
  with ScrollableContainer(id="messages-container"):
394
497
  # Will be populated with messages
395
498
  pass
396
499
 
397
- # Loading indicator
398
- yield Static("▪▪▪ Generating response...", id="loading-indicator", classes="hidden", markup=False)
500
+ # Minimal loading indicator
501
+ yield Static(" Generating", id="loading-indicator", classes="hidden", markup=False)
399
502
 
400
503
  # Input area
401
504
  with Container(id="input-area"):
@@ -483,8 +586,8 @@ class SimpleChatApp(App): # Keep SimpleChatApp class definition
483
586
  model = self.selected_model # Keep SimpleChatApp create_new_conversation
484
587
  style = self.selected_style # Keep SimpleChatApp create_new_conversation
485
588
 
486
- # Create a title for the new conversation # Keep SimpleChatApp create_new_conversation
487
- title = f"New conversation ({datetime.now().strftime('%Y-%m-%d %H:%M')})" # Keep SimpleChatApp create_new_conversation
589
+ # Clean title following Rams principles
590
+ title = "New Conversation"
488
591
 
489
592
  # Create conversation in database using the correct method # Keep SimpleChatApp create_new_conversation
490
593
  log(f"Creating conversation with title: {title}, model: {model}, style: {style}") # Added log
@@ -580,22 +683,20 @@ class SimpleChatApp(App): # Keep SimpleChatApp class definition
580
683
  # Optionally add other escape behaviors here if needed
581
684
 
582
685
  def update_app_info(self) -> None:
583
- """Update the displayed app information."""
686
+ """Update app info following clean information architecture"""
584
687
  try:
585
- # Update model info
586
688
  model_info = self.query_one("#model-info", Static)
587
- model_display = self.selected_model
588
-
589
- # Try to get a more readable name from config if available
689
+
690
+ # Clean model display - no unnecessary decoration
590
691
  if self.selected_model in CONFIG["available_models"]:
591
- provider = CONFIG["available_models"][self.selected_model]["provider"]
592
692
  display_name = CONFIG["available_models"][self.selected_model]["display_name"]
593
- model_display = f"{display_name} ({provider.capitalize()})"
693
+ model_display = display_name
694
+ else:
695
+ model_display = self.selected_model
594
696
 
595
- model_info.update(f"Model: {model_display}")
697
+ model_info.update(model_display)
596
698
  except Exception as e:
597
- # Silently handle errors to prevent crashes
598
- log.error(f"Error updating app info: {e}") # Log error instead of passing silently
699
+ log.error(f"Error updating app info: {e}")
599
700
  pass
600
701
 
601
702
  async def update_messages_ui(self) -> None: # Keep SimpleChatApp update_messages_ui
@@ -1513,49 +1614,37 @@ class SimpleChatApp(App): # Keep SimpleChatApp class definition
1513
1614
  self.push_screen(ModelBrowserScreen())
1514
1615
 
1515
1616
  async def _animate_loading_task(self, loading_widget: Static) -> None:
1516
- """Animate the loading indicator with a simple text animation"""
1617
+ """Minimal loading animation following Rams principles"""
1517
1618
  try:
1518
- # Animation frames (simple text animation)
1619
+ # Minimal loading frames - "Less but better"
1519
1620
  frames = [
1520
- "▪▫▫ Generating response...",
1521
- "▪▪▫ Generating response...",
1522
- "▪▪▪ Generating response...",
1523
- "▫▪▪ Generating response...",
1524
- "▫▫▪ Generating response...",
1525
- "▫▫▫ Generating response..."
1621
+ " Generating",
1622
+ " Generating",
1623
+ " Generating",
1624
+ " Generating"
1526
1625
  ]
1527
1626
 
1528
1627
  while self.is_generating:
1529
1628
  try:
1530
- # Update the loading text with safety checks
1531
- if frames and len(frames) > 0:
1532
- frame_idx = self._loading_frame % len(frames)
1533
- loading_widget.update(frames[frame_idx])
1534
- else:
1535
- # Fallback if frames is empty
1536
- loading_widget.update("▪▪▪ Generating response...")
1537
-
1629
+ frame_idx = self._loading_frame % len(frames)
1630
+ loading_widget.update(frames[frame_idx])
1538
1631
  self._loading_frame += 1
1539
- # Small delay between frames
1540
- await asyncio.sleep(0.3)
1632
+ # Slower, less distracting animation
1633
+ await asyncio.sleep(0.8)
1541
1634
  except Exception as e:
1542
- # If any error occurs, use a simple fallback and continue
1543
1635
  log.error(f"Animation frame error: {str(e)}")
1544
1636
  try:
1545
- loading_widget.update("▪▪▪ Generating response...")
1637
+ loading_widget.update(" Generating")
1546
1638
  except:
1547
1639
  pass
1548
- await asyncio.sleep(0.3)
1640
+ await asyncio.sleep(0.8)
1549
1641
 
1550
1642
  except asyncio.CancelledError:
1551
- # Normal cancellation
1552
1643
  pass
1553
1644
  except Exception as e:
1554
- # Log any errors but don't crash
1555
1645
  log.error(f"Error in loading animation: {str(e)}")
1556
- # Reset to basic text
1557
1646
  try:
1558
- loading_widget.update("▪▪▪ Generating response...")
1647
+ loading_widget.update(" Generating")
1559
1648
  except:
1560
1649
  pass
1561
1650
 
@@ -1724,17 +1813,48 @@ class TitleInputModal(Static):
1724
1813
  self.notify(f"Failed to update title: {str(e)}", severity="error")
1725
1814
 
1726
1815
 
1727
- def main(initial_text: Optional[str] = typer.Argument(None, help="Initial text to start the chat with")): # Keep main function
1728
- """Entry point for the chat-cli application""" # Keep main function docstring
1729
- # When no argument is provided, typer passes the ArgumentInfo object # Keep main function
1730
- # When an argument is provided, typer passes the actual value # Keep main function
1731
- if isinstance(initial_text, typer.models.ArgumentInfo): # Keep main function
1732
- initial_value = None # No argument provided # Keep main function
1733
- else: # Keep main function
1734
- initial_value = str(initial_text) if initial_text is not None else None # Keep main function
1816
+ def main(
1817
+ initial_text: Optional[str] = typer.Argument(None, help="Initial text to start the chat with"),
1818
+ console: bool = typer.Option(False, "--console", "-c", help="Use pure console mode (no Textual)")
1819
+ ):
1820
+ """Entry point for the chat-cli application"""
1821
+ if console:
1822
+ # Launch pure console version
1823
+ import asyncio
1824
+ import sys
1825
+ import os
1826
+
1827
+ # Add current directory to path for console_chat import
1828
+ sys.path.insert(0, os.path.dirname(os.path.dirname(__file__)))
1735
1829
 
1736
- app = SimpleChatApp(initial_text=initial_value) # Keep main function
1737
- app.run() # Keep main function
1830
+ try:
1831
+ from console_chat import ConsoleUI
1832
+
1833
+ async def run_console():
1834
+ console_ui = ConsoleUI()
1835
+ if initial_text and not isinstance(initial_text, typer.models.ArgumentInfo):
1836
+ # If initial text provided, create conversation and add message
1837
+ await console_ui.create_new_conversation()
1838
+ await console_ui.generate_response(str(initial_text))
1839
+ await console_ui.run()
1840
+
1841
+ asyncio.run(run_console())
1842
+
1843
+ except ImportError:
1844
+ print("Error: Could not import console version. Make sure all dependencies are installed.")
1845
+ sys.exit(1)
1846
+ except Exception as e:
1847
+ print(f"Error running console version: {e}")
1848
+ sys.exit(1)
1849
+ else:
1850
+ # Original Textual version
1851
+ if isinstance(initial_text, typer.models.ArgumentInfo):
1852
+ initial_value = None
1853
+ else:
1854
+ initial_value = str(initial_text) if initial_text is not None else None
1855
+
1856
+ app = SimpleChatApp(initial_text=initial_value)
1857
+ app.run()
1738
1858
 
1739
1859
  if __name__ == "__main__": # Keep main function entry point
1740
1860
  typer.run(main) # Keep main function entry point