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/__init__.py +1 -1
- app/console_chat.py +816 -0
- app/console_main.py +58 -0
- app/console_utils.py +195 -0
- app/main.py +246 -126
- app/ui/borders.py +154 -0
- app/ui/chat_interface.py +84 -78
- app/ui/model_selector.py +11 -3
- app/ui/styles.py +231 -137
- {chat_console-0.4.3.dist-info → chat_console-0.4.7.dist-info}/METADATA +2 -2
- chat_console-0.4.7.dist-info/RECORD +28 -0
- {chat_console-0.4.3.dist-info → chat_console-0.4.7.dist-info}/WHEEL +1 -1
- chat_console-0.4.7.dist-info/entry_points.txt +5 -0
- chat_console-0.4.3.dist-info/RECORD +0 -24
- chat_console-0.4.3.dist-info/entry_points.txt +0 -3
- {chat_console-0.4.3.dist-info → chat_console-0.4.7.dist-info}/licenses/LICENSE +0 -0
- {chat_console-0.4.3.dist-info → chat_console-0.4.7.dist-info}/top_level.txt +0 -0
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
|
183
|
-
|
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
|
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:
|
192
|
-
background:
|
193
|
-
color:
|
194
|
-
padding:
|
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
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
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:
|
221
|
-
align-horizontal:
|
222
|
-
background:
|
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:
|
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
|
-
|
231
|
-
|
250
|
+
/* Messages container - Purposeful spacing */
|
251
|
+
#messages-container {
|
252
|
+
width: 100%;
|
232
253
|
height: 1fr;
|
233
|
-
min-height:
|
234
|
-
border-bottom: solid $primary-darken-2;
|
254
|
+
min-height: 15;
|
235
255
|
overflow: auto;
|
236
|
-
padding:
|
256
|
+
padding: 2;
|
257
|
+
background: #0C0C0C;
|
258
|
+
border-bottom: solid #333333 1;
|
237
259
|
}
|
238
260
|
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
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
|
-
|
269
|
+
border-bottom: solid #333333 1;
|
270
|
+
padding: 0 2;
|
247
271
|
}
|
248
272
|
|
249
|
-
#loading-indicator.hidden {
|
273
|
+
#loading-indicator.hidden {
|
250
274
|
display: none;
|
251
275
|
}
|
252
276
|
|
253
277
|
#loading-indicator.model-loading {
|
254
|
-
|
255
|
-
color: $text;
|
278
|
+
color: #33FF33;
|
256
279
|
}
|
257
280
|
|
258
|
-
|
259
|
-
|
281
|
+
/* Input area - Clean and functional */
|
282
|
+
#input-area {
|
283
|
+
width: 100%;
|
260
284
|
height: auto;
|
261
|
-
min-height:
|
262
|
-
max-height:
|
263
|
-
padding:
|
285
|
+
min-height: 5;
|
286
|
+
max-height: 12;
|
287
|
+
padding: 2;
|
288
|
+
background: #0C0C0C;
|
264
289
|
}
|
265
290
|
|
266
|
-
#message-input {
|
267
|
-
width: 1fr;
|
268
|
-
min-height:
|
291
|
+
#message-input {
|
292
|
+
width: 1fr;
|
293
|
+
min-height: 3;
|
269
294
|
height: auto;
|
270
295
|
margin-right: 1;
|
271
|
-
border: solid
|
296
|
+
border: solid #333333 1;
|
297
|
+
background: #0C0C0C;
|
298
|
+
color: #E8E8E8;
|
299
|
+
padding: 1;
|
272
300
|
}
|
273
301
|
|
274
|
-
#message-input:focus {
|
275
|
-
border: solid
|
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
|
-
|
282
|
-
|
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:
|
287
|
-
border:
|
288
|
-
padding:
|
289
|
-
layer: settings;
|
316
|
+
background: #0C0C0C;
|
317
|
+
border: solid #333333 1;
|
318
|
+
padding: 2;
|
319
|
+
layer: settings;
|
290
320
|
}
|
291
321
|
|
292
|
-
#settings-panel.visible {
|
322
|
+
#settings-panel.visible {
|
293
323
|
display: block;
|
294
324
|
}
|
295
325
|
|
296
326
|
#settings-title {
|
297
327
|
width: 100%;
|
298
|
-
content-align:
|
328
|
+
content-align: left middle;
|
299
329
|
padding-bottom: 1;
|
300
|
-
border-bottom:
|
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:
|
307
|
-
padding-top:
|
338
|
+
align: right middle;
|
339
|
+
padding-top: 2;
|
308
340
|
}
|
309
341
|
|
310
|
-
|
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:
|
316
|
-
border:
|
317
|
-
padding:
|
318
|
-
layer: modal;
|
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:
|
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:
|
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:
|
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
|
-
|
342
|
-
Binding("
|
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),
|
346
|
-
Binding("s", "settings", "Settings", show=True, key_display="s", priority=True),
|
347
|
-
|
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
|
-
#
|
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")
|
382
|
-
yield Static(
|
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
|
-
#
|
490
|
+
# Action buttons - Minimal and functional
|
388
491
|
with Horizontal(id="action-buttons"):
|
389
|
-
yield Button("
|
390
|
-
yield Button("✎
|
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
|
-
#
|
398
|
-
yield Static("
|
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
|
-
#
|
487
|
-
title =
|
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
|
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
|
-
|
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 =
|
693
|
+
model_display = display_name
|
694
|
+
else:
|
695
|
+
model_display = self.selected_model
|
594
696
|
|
595
|
-
model_info.update(
|
697
|
+
model_info.update(model_display)
|
596
698
|
except Exception as e:
|
597
|
-
|
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
|
-
"""
|
1617
|
+
"""Minimal loading animation following Rams principles"""
|
1517
1618
|
try:
|
1518
|
-
#
|
1619
|
+
# Minimal loading frames - "Less but better"
|
1519
1620
|
frames = [
|
1520
|
-
"
|
1521
|
-
"
|
1522
|
-
"
|
1523
|
-
"
|
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
|
-
|
1531
|
-
|
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
|
-
#
|
1540
|
-
await asyncio.sleep(0.
|
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("
|
1637
|
+
loading_widget.update("● Generating")
|
1546
1638
|
except:
|
1547
1639
|
pass
|
1548
|
-
await asyncio.sleep(0.
|
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("
|
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(
|
1728
|
-
|
1729
|
-
|
1730
|
-
|
1731
|
-
|
1732
|
-
|
1733
|
-
|
1734
|
-
|
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
|
-
|
1737
|
-
|
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
|