cowork-dash 0.1.4__py3-none-any.whl → 0.1.5__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.
- cowork_dash/agent.py +4 -4
- cowork_dash/app.py +668 -106
- cowork_dash/assets/app.js +12 -1
- cowork_dash/assets/favicon.ico +0 -0
- cowork_dash/assets/styles.css +144 -2
- cowork_dash/canvas.py +194 -80
- cowork_dash/cli.py +7 -0
- cowork_dash/components.py +194 -104
- cowork_dash/config.py +13 -0
- cowork_dash/file_utils.py +17 -6
- cowork_dash/layout.py +115 -23
- cowork_dash/tools.py +88 -11
- {cowork_dash-0.1.4.dist-info → cowork_dash-0.1.5.dist-info}/METADATA +31 -40
- cowork_dash-0.1.5.dist-info/RECORD +20 -0
- cowork_dash-0.1.4.dist-info/RECORD +0 -19
- {cowork_dash-0.1.4.dist-info → cowork_dash-0.1.5.dist-info}/WHEEL +0 -0
- {cowork_dash-0.1.4.dist-info → cowork_dash-0.1.5.dist-info}/entry_points.txt +0 -0
- {cowork_dash-0.1.4.dist-info → cowork_dash-0.1.5.dist-info}/licenses/LICENSE +0 -0
cowork_dash/components.py
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
"""UI components for rendering messages, canvas items, and other UI elements."""
|
|
2
2
|
|
|
3
3
|
import json
|
|
4
|
-
from
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from typing import Dict, List, Optional
|
|
5
6
|
from dash import html, dcc
|
|
7
|
+
import dash_mantine_components as dmc
|
|
8
|
+
from dash_iconify import DashIconify
|
|
6
9
|
|
|
7
10
|
|
|
8
11
|
def format_message(role: str, content: str, colors: Dict, styles: Dict, is_new: bool = False, response_time: float = None):
|
|
@@ -415,8 +418,49 @@ def format_interrupt(interrupt_data: Dict, colors: Dict):
|
|
|
415
418
|
return html.Div(children, className="interrupt-container")
|
|
416
419
|
|
|
417
420
|
|
|
418
|
-
def
|
|
419
|
-
"""
|
|
421
|
+
def _format_timestamp(iso_timestamp: str) -> str:
|
|
422
|
+
"""Format ISO timestamp to human-readable format."""
|
|
423
|
+
try:
|
|
424
|
+
dt = datetime.fromisoformat(iso_timestamp)
|
|
425
|
+
return dt.strftime("%b %d, %H:%M")
|
|
426
|
+
except (ValueError, TypeError):
|
|
427
|
+
return ""
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
def _get_type_badge(item_type: str) -> dmc.Badge:
|
|
431
|
+
"""Get a badge component for the item type."""
|
|
432
|
+
type_colors = {
|
|
433
|
+
"markdown": "gray",
|
|
434
|
+
"dataframe": "blue",
|
|
435
|
+
"matplotlib": "green",
|
|
436
|
+
"image": "green",
|
|
437
|
+
"plotly": "violet",
|
|
438
|
+
"mermaid": "cyan",
|
|
439
|
+
}
|
|
440
|
+
type_labels = {
|
|
441
|
+
"markdown": "Text",
|
|
442
|
+
"dataframe": "Table",
|
|
443
|
+
"matplotlib": "Chart",
|
|
444
|
+
"image": "Image",
|
|
445
|
+
"plotly": "Plot",
|
|
446
|
+
"mermaid": "Diagram",
|
|
447
|
+
}
|
|
448
|
+
color = type_colors.get(item_type, "gray")
|
|
449
|
+
label = type_labels.get(item_type, item_type.title())
|
|
450
|
+
return dmc.Badge(label, color=color, size="xs", variant="light")
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
def render_canvas_items(canvas_items: List[Dict], colors: Dict, collapsed_ids: Optional[List[str]] = None) -> html.Div:
|
|
454
|
+
"""Render all canvas items using CSS classes for theme awareness.
|
|
455
|
+
|
|
456
|
+
Args:
|
|
457
|
+
canvas_items: List of canvas item dictionaries
|
|
458
|
+
colors: Color scheme dictionary
|
|
459
|
+
collapsed_ids: List of item IDs that should be rendered collapsed
|
|
460
|
+
"""
|
|
461
|
+
if collapsed_ids is None:
|
|
462
|
+
collapsed_ids = []
|
|
463
|
+
|
|
420
464
|
if not canvas_items:
|
|
421
465
|
return html.Div([
|
|
422
466
|
html.P("Canvas empty", className="canvas-empty-text", style={
|
|
@@ -441,126 +485,172 @@ def render_canvas_items(canvas_items: List[Dict], colors: Dict) -> html.Div:
|
|
|
441
485
|
|
|
442
486
|
for i, item in enumerate(canvas_items):
|
|
443
487
|
item_type = item.get("type", "unknown")
|
|
488
|
+
item_id = item.get("id", f"canvas_item_{i}")
|
|
489
|
+
is_collapsed = item_id in collapsed_ids
|
|
444
490
|
title = item.get("title")
|
|
445
|
-
|
|
446
|
-
|
|
491
|
+
created_at = item.get("created_at", "")
|
|
492
|
+
|
|
493
|
+
# Build item header left side with collapse toggle, title, badge, and time
|
|
494
|
+
header_left = [
|
|
495
|
+
# Collapse/expand toggle - icon depends on collapsed state
|
|
496
|
+
dmc.ActionIcon(
|
|
497
|
+
DashIconify(icon="mdi:chevron-right" if is_collapsed else "mdi:chevron-down", width=16),
|
|
498
|
+
id={"type": "canvas-collapse-btn", "index": item_id},
|
|
499
|
+
variant="subtle",
|
|
500
|
+
color="gray",
|
|
501
|
+
size="sm",
|
|
502
|
+
className="canvas-collapse-btn",
|
|
503
|
+
),
|
|
504
|
+
]
|
|
447
505
|
if title:
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
"fontSize": "15px",
|
|
451
|
-
"fontWeight": "600",
|
|
452
|
-
"marginBottom": "8px",
|
|
453
|
-
"marginTop": "15px" if i > 0 else "0",
|
|
454
|
-
})
|
|
506
|
+
header_left.append(
|
|
507
|
+
dmc.Text(title, fw=600, size="sm", className="canvas-item-title-text")
|
|
455
508
|
)
|
|
509
|
+
header_left.append(_get_type_badge(item_type))
|
|
510
|
+
if created_at:
|
|
511
|
+
formatted_time = _format_timestamp(created_at)
|
|
512
|
+
if formatted_time:
|
|
513
|
+
header_left.append(
|
|
514
|
+
dmc.Text(formatted_time, size="xs", c="dimmed", className="canvas-item-time")
|
|
515
|
+
)
|
|
516
|
+
|
|
517
|
+
# Header right side with delete button (shows confirmation on first click)
|
|
518
|
+
header_right = dmc.Group([
|
|
519
|
+
# Delete button - first click shows confirm, second click deletes
|
|
520
|
+
dmc.ActionIcon(
|
|
521
|
+
DashIconify(icon="mdi:close", width=14),
|
|
522
|
+
id={"type": "canvas-delete-btn", "index": item_id},
|
|
523
|
+
variant="subtle",
|
|
524
|
+
color="gray",
|
|
525
|
+
size="sm",
|
|
526
|
+
className="canvas-delete-btn",
|
|
527
|
+
),
|
|
528
|
+
], gap="xs")
|
|
456
529
|
|
|
457
|
-
|
|
530
|
+
item_header = html.Div([
|
|
531
|
+
dmc.Group(header_left, gap="xs"),
|
|
532
|
+
header_right,
|
|
533
|
+
], className="canvas-item-header")
|
|
534
|
+
|
|
535
|
+
# Render content based on type
|
|
458
536
|
if item_type == "markdown":
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
], className="canvas-item canvas-item-markdown")
|
|
472
|
-
)
|
|
537
|
+
content = html.Div([
|
|
538
|
+
dcc.Markdown(
|
|
539
|
+
item.get("data", ""),
|
|
540
|
+
className="canvas-markdown",
|
|
541
|
+
style={
|
|
542
|
+
"fontSize": "15px",
|
|
543
|
+
"lineHeight": "1.5",
|
|
544
|
+
"wordBreak": "break-word",
|
|
545
|
+
"overflowWrap": "break-word",
|
|
546
|
+
}
|
|
547
|
+
)
|
|
548
|
+
], className="canvas-item-content canvas-item-markdown", style={"padding": "10px"})
|
|
473
549
|
|
|
474
550
|
elif item_type == "dataframe":
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
)
|
|
551
|
+
content = html.Div([
|
|
552
|
+
dcc.Markdown(
|
|
553
|
+
item.get("html", ""),
|
|
554
|
+
dangerously_allow_html=True,
|
|
555
|
+
style={"fontSize": "14px"}
|
|
556
|
+
)
|
|
557
|
+
], className="canvas-item-content canvas-item-dataframe", style={
|
|
558
|
+
"overflowX": "auto",
|
|
559
|
+
"padding": "10px",
|
|
560
|
+
})
|
|
486
561
|
|
|
487
562
|
elif item_type == "matplotlib" or item_type == "image":
|
|
488
563
|
img_data = item.get("data", "")
|
|
489
|
-
|
|
490
|
-
html.
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
)
|
|
564
|
+
content = html.Div([
|
|
565
|
+
html.Img(
|
|
566
|
+
src=f"data:image/png;base64,{img_data}",
|
|
567
|
+
style={
|
|
568
|
+
"maxWidth": "100%",
|
|
569
|
+
"width": "100%",
|
|
570
|
+
"height": "auto",
|
|
571
|
+
"borderRadius": "5px",
|
|
572
|
+
"objectFit": "contain",
|
|
573
|
+
}
|
|
574
|
+
)
|
|
575
|
+
], className="canvas-item-content canvas-item-image", style={
|
|
576
|
+
"textAlign": "center",
|
|
577
|
+
"padding": "10px",
|
|
578
|
+
})
|
|
505
579
|
|
|
506
580
|
elif item_type == "plotly":
|
|
507
581
|
fig_data = item.get("data", {})
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
], className="canvas-item canvas-item-plotly")
|
|
522
|
-
)
|
|
582
|
+
content = html.Div([
|
|
583
|
+
dcc.Graph(
|
|
584
|
+
figure=fig_data,
|
|
585
|
+
style={"height": "400px", "width": "100%"},
|
|
586
|
+
responsive=True,
|
|
587
|
+
config={
|
|
588
|
+
"displayModeBar": True,
|
|
589
|
+
"displaylogo": False,
|
|
590
|
+
"modeBarButtonsToRemove": ["lasso2d", "select2d"],
|
|
591
|
+
"responsive": True,
|
|
592
|
+
}
|
|
593
|
+
)
|
|
594
|
+
], className="canvas-item-content canvas-item-plotly")
|
|
523
595
|
|
|
524
596
|
elif item_type == "mermaid":
|
|
525
|
-
# Mermaid diagram
|
|
526
597
|
mermaid_code = item.get("data", "")
|
|
527
|
-
|
|
528
|
-
html.Div(
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
})
|
|
544
|
-
)
|
|
598
|
+
content = html.Div([
|
|
599
|
+
html.Div(
|
|
600
|
+
mermaid_code,
|
|
601
|
+
className="mermaid-diagram",
|
|
602
|
+
style={
|
|
603
|
+
"textAlign": "center",
|
|
604
|
+
"padding": "25px",
|
|
605
|
+
"width": "100%",
|
|
606
|
+
"overflow": "auto",
|
|
607
|
+
"whiteSpace": "pre",
|
|
608
|
+
}
|
|
609
|
+
)
|
|
610
|
+
], className="canvas-item-content canvas-item-mermaid", style={
|
|
611
|
+
"textAlign": "center",
|
|
612
|
+
"overflow": "auto",
|
|
613
|
+
})
|
|
545
614
|
|
|
546
615
|
else:
|
|
547
616
|
# Unknown type
|
|
548
|
-
|
|
549
|
-
html.
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
617
|
+
content = html.Div([
|
|
618
|
+
html.Code(
|
|
619
|
+
str(item),
|
|
620
|
+
className="canvas-code",
|
|
621
|
+
style={
|
|
622
|
+
"fontSize": "15px",
|
|
623
|
+
"display": "block",
|
|
624
|
+
"whiteSpace": "pre-wrap",
|
|
625
|
+
"wordBreak": "break-word",
|
|
626
|
+
}
|
|
627
|
+
)
|
|
628
|
+
], className="canvas-item-content canvas-item-code", style={
|
|
629
|
+
"overflow": "auto",
|
|
630
|
+
"padding": "10px",
|
|
631
|
+
})
|
|
632
|
+
|
|
633
|
+
# Wrap content in collapsible container - respect collapsed state
|
|
634
|
+
content_wrapper = html.Div(
|
|
635
|
+
content,
|
|
636
|
+
id={"type": "canvas-item-content", "index": item_id},
|
|
637
|
+
className="canvas-item-content-wrapper",
|
|
638
|
+
style={"display": "none" if is_collapsed else "block"}
|
|
639
|
+
)
|
|
640
|
+
|
|
641
|
+
# Wrap header and content in item container
|
|
642
|
+
rendered_items.append(
|
|
643
|
+
html.Div([
|
|
644
|
+
item_header,
|
|
645
|
+
content_wrapper,
|
|
646
|
+
], id={"type": "canvas-item", "index": item_id}, className="canvas-item-container", style={
|
|
647
|
+
"border": "1px solid var(--mantine-color-default-border)",
|
|
648
|
+
"borderRadius": "6px",
|
|
649
|
+
"marginBottom": "12px",
|
|
650
|
+
"overflow": "hidden",
|
|
651
|
+
"background": "var(--mantine-color-body)",
|
|
652
|
+
})
|
|
653
|
+
)
|
|
564
654
|
|
|
565
655
|
return html.Div(rendered_items, style={
|
|
566
656
|
"maxWidth": "100%",
|
cowork_dash/config.py
CHANGED
|
@@ -94,3 +94,16 @@ DEBUG = get_config(
|
|
|
94
94
|
default=False,
|
|
95
95
|
type_cast=lambda x: str(x).lower() in ("true", "1", "yes")
|
|
96
96
|
)
|
|
97
|
+
|
|
98
|
+
# Welcome message shown when the app starts
|
|
99
|
+
# Environment variable: DEEPAGENT_WELCOME_MESSAGE
|
|
100
|
+
# Supports markdown formatting
|
|
101
|
+
_default_welcome = """This is your AI-powered workspace. I can help you write code, analyze files, create visualizations, and more.
|
|
102
|
+
|
|
103
|
+
**Getting Started:**
|
|
104
|
+
- Type a message below to chat with me
|
|
105
|
+
- Browse files on the right (click to view, ↓ to download)
|
|
106
|
+
- Switch to **Canvas** tab to see charts and diagrams I create
|
|
107
|
+
|
|
108
|
+
Let's get started!"""
|
|
109
|
+
WELCOME_MESSAGE = get_config("welcome_message", default=_default_welcome)
|
cowork_dash/file_utils.py
CHANGED
|
@@ -90,24 +90,35 @@ def render_file_tree(items: List[Dict], colors: Dict, styles: Dict, level: int =
|
|
|
90
90
|
folder_id = item["path"].replace("/", "_").replace("\\", "_")
|
|
91
91
|
children = item.get("children", [])
|
|
92
92
|
|
|
93
|
-
# Folder header
|
|
93
|
+
# Folder header with expand icon and selectable name
|
|
94
94
|
components.append(
|
|
95
95
|
html.Div([
|
|
96
|
+
# Expand/collapse icon (left side)
|
|
96
97
|
html.Span(
|
|
97
98
|
"▶",
|
|
98
99
|
id={"type": "folder-icon", "path": folder_id},
|
|
99
|
-
className="folder-icon",
|
|
100
|
+
className="folder-icon folder-expand-toggle",
|
|
100
101
|
style={
|
|
101
102
|
"marginRight": "5px",
|
|
102
103
|
"fontSize": "10px",
|
|
103
104
|
"transition": "transform 0.15s",
|
|
104
105
|
"display": "inline-block",
|
|
106
|
+
"padding": "2px",
|
|
105
107
|
}
|
|
106
108
|
),
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
"
|
|
110
|
-
|
|
109
|
+
# Folder name (clickable for selection)
|
|
110
|
+
html.Span(item["name"],
|
|
111
|
+
id={"type": "folder-select", "path": folder_id},
|
|
112
|
+
className="folder-name folder-select-target",
|
|
113
|
+
**{"data-folderpath": item["path"]},
|
|
114
|
+
style={
|
|
115
|
+
"fontWeight": "500",
|
|
116
|
+
"fontSize": "14px",
|
|
117
|
+
"flex": "1",
|
|
118
|
+
"padding": "2px 4px",
|
|
119
|
+
"borderRadius": "3px",
|
|
120
|
+
}
|
|
121
|
+
)
|
|
111
122
|
],
|
|
112
123
|
id={"type": "folder-header", "path": folder_id},
|
|
113
124
|
**{"data-realpath": item["path"]}, # Store actual path for lazy loading
|
cowork_dash/layout.py
CHANGED
|
@@ -5,9 +5,10 @@ import dash_mantine_components as dmc
|
|
|
5
5
|
from dash_iconify import DashIconify
|
|
6
6
|
|
|
7
7
|
from .file_utils import build_file_tree, render_file_tree
|
|
8
|
+
from .config import WELCOME_MESSAGE as DEFAULT_WELCOME_MESSAGE
|
|
8
9
|
|
|
9
10
|
|
|
10
|
-
def create_layout(workspace_root, app_title, app_subtitle, colors, styles, agent):
|
|
11
|
+
def create_layout(workspace_root, app_title, app_subtitle, colors, styles, agent, welcome_message=None):
|
|
11
12
|
"""
|
|
12
13
|
Create the app layout with current configuration.
|
|
13
14
|
|
|
@@ -18,10 +19,14 @@ def create_layout(workspace_root, app_title, app_subtitle, colors, styles, agent
|
|
|
18
19
|
colors: Color scheme dictionary
|
|
19
20
|
styles: Styles dictionary
|
|
20
21
|
agent: Agent instance (or None)
|
|
22
|
+
welcome_message: Optional welcome message (uses default if not provided)
|
|
21
23
|
|
|
22
24
|
Returns:
|
|
23
25
|
Dash layout component
|
|
24
26
|
"""
|
|
27
|
+
# Use provided welcome message or fall back to default
|
|
28
|
+
message = welcome_message if welcome_message is not None else DEFAULT_WELCOME_MESSAGE
|
|
29
|
+
|
|
25
30
|
return dmc.MantineProvider(
|
|
26
31
|
id="mantine-provider",
|
|
27
32
|
forceColorScheme="light",
|
|
@@ -29,20 +34,15 @@ def create_layout(workspace_root, app_title, app_subtitle, colors, styles, agent
|
|
|
29
34
|
# State stores
|
|
30
35
|
dcc.Store(id="chat-history", data=[{
|
|
31
36
|
"role": "assistant",
|
|
32
|
-
"content":
|
|
33
|
-
|
|
34
|
-
**Getting Started:**
|
|
35
|
-
- Type a message below to chat with me
|
|
36
|
-
- Browse files on the right (click to view, ↓ to download)
|
|
37
|
-
- Switch to **Canvas** tab to see charts and diagrams I create
|
|
38
|
-
|
|
39
|
-
Let's get started!"""
|
|
37
|
+
"content": message
|
|
40
38
|
}]),
|
|
41
39
|
dcc.Store(id="pending-message", data=None),
|
|
42
40
|
dcc.Store(id="expanded-folders", data=[]),
|
|
43
41
|
dcc.Store(id="file-to-view", data=None),
|
|
44
42
|
dcc.Store(id="file-click-tracker", data={}),
|
|
45
43
|
dcc.Store(id="theme-store", data="light", storage_type="local"),
|
|
44
|
+
dcc.Store(id="current-workspace-path", data=""), # Relative path from original workspace root
|
|
45
|
+
dcc.Store(id="collapsed-canvas-items", data=[]), # Track which canvas items are collapsed
|
|
46
46
|
dcc.Download(id="file-download"),
|
|
47
47
|
|
|
48
48
|
# Interval for polling agent updates (disabled by default)
|
|
@@ -68,6 +68,62 @@ Let's get started!"""
|
|
|
68
68
|
opened=False,
|
|
69
69
|
),
|
|
70
70
|
|
|
71
|
+
# Create folder modal
|
|
72
|
+
dmc.Modal(
|
|
73
|
+
id="create-folder-modal",
|
|
74
|
+
title="Create New Folder",
|
|
75
|
+
size="sm",
|
|
76
|
+
children=[
|
|
77
|
+
dmc.TextInput(
|
|
78
|
+
id="new-folder-name",
|
|
79
|
+
label="Folder name",
|
|
80
|
+
placeholder="Enter folder name",
|
|
81
|
+
style={"marginBottom": "16px"},
|
|
82
|
+
),
|
|
83
|
+
dmc.Text(id="create-folder-error", c="red", size="sm", style={"marginBottom": "8px"}),
|
|
84
|
+
dmc.Group([
|
|
85
|
+
dmc.Button("Cancel", id="cancel-folder-btn", variant="outline", color="gray"),
|
|
86
|
+
dmc.Button("Create", id="confirm-folder-btn", color="blue"),
|
|
87
|
+
], justify="flex-end"),
|
|
88
|
+
],
|
|
89
|
+
opened=False,
|
|
90
|
+
),
|
|
91
|
+
|
|
92
|
+
# Delete canvas item confirmation modal
|
|
93
|
+
dmc.Modal(
|
|
94
|
+
id="delete-canvas-item-modal",
|
|
95
|
+
title="Delete Canvas Item",
|
|
96
|
+
size="sm",
|
|
97
|
+
children=[
|
|
98
|
+
dmc.Text("Are you sure you want to delete this canvas item? This action cannot be undone.",
|
|
99
|
+
size="sm", style={"marginBottom": "16px"}),
|
|
100
|
+
dmc.Group([
|
|
101
|
+
dmc.Button("Cancel", id="cancel-delete-canvas-btn", variant="outline", color="gray"),
|
|
102
|
+
dmc.Button("Delete", id="confirm-delete-canvas-btn", color="red"),
|
|
103
|
+
], justify="flex-end"),
|
|
104
|
+
],
|
|
105
|
+
opened=False,
|
|
106
|
+
),
|
|
107
|
+
|
|
108
|
+
# Store for canvas item ID pending deletion
|
|
109
|
+
dcc.Store(id="delete-canvas-item-id", data=None),
|
|
110
|
+
|
|
111
|
+
# Clear canvas confirmation modal
|
|
112
|
+
dmc.Modal(
|
|
113
|
+
id="clear-canvas-modal",
|
|
114
|
+
title="Clear Canvas",
|
|
115
|
+
size="sm",
|
|
116
|
+
children=[
|
|
117
|
+
dmc.Text("Are you sure you want to clear the entire canvas? The current canvas will be archived with a timestamp.",
|
|
118
|
+
size="sm", style={"marginBottom": "16px"}),
|
|
119
|
+
dmc.Group([
|
|
120
|
+
dmc.Button("Cancel", id="cancel-clear-canvas-btn", variant="outline", color="gray"),
|
|
121
|
+
dmc.Button("Clear", id="confirm-clear-canvas-btn", color="red"),
|
|
122
|
+
], justify="flex-end"),
|
|
123
|
+
],
|
|
124
|
+
opened=False,
|
|
125
|
+
),
|
|
126
|
+
|
|
71
127
|
html.Div([
|
|
72
128
|
# Compact Header
|
|
73
129
|
html.Header([
|
|
@@ -121,17 +177,6 @@ Let's get started!"""
|
|
|
121
177
|
|
|
122
178
|
# Compact Input
|
|
123
179
|
html.Div([
|
|
124
|
-
dcc.Upload(
|
|
125
|
-
id="file-upload",
|
|
126
|
-
children=dmc.ActionIcon(
|
|
127
|
-
DashIconify(icon="radix-icons:plus", width=18),
|
|
128
|
-
id="upload-plus",
|
|
129
|
-
variant="default",
|
|
130
|
-
size="md",
|
|
131
|
-
),
|
|
132
|
-
style={"cursor": "pointer"},
|
|
133
|
-
multiple=True
|
|
134
|
-
),
|
|
135
180
|
dmc.TextInput(
|
|
136
181
|
id="chat-input",
|
|
137
182
|
placeholder="Type a message...",
|
|
@@ -140,14 +185,20 @@ Let's get started!"""
|
|
|
140
185
|
size="md",
|
|
141
186
|
),
|
|
142
187
|
dmc.Button("Send", id="send-btn", className="send-btn", size="md"),
|
|
188
|
+
dmc.ActionIcon(
|
|
189
|
+
DashIconify(icon="mdi:stop", width=20),
|
|
190
|
+
id="stop-btn",
|
|
191
|
+
variant="filled",
|
|
192
|
+
color="red",
|
|
193
|
+
size="lg",
|
|
194
|
+
radius="sm",
|
|
195
|
+
style={"display": "none"}, # Hidden by default, shown when agent is running
|
|
196
|
+
),
|
|
143
197
|
], id="chat-input-area", style={
|
|
144
198
|
"display": "flex", "gap": "8px", "padding": "10px 15px",
|
|
145
199
|
"borderTop": "1px solid var(--mantine-color-default-border)",
|
|
146
200
|
"background": "var(--mantine-color-body)",
|
|
147
201
|
}),
|
|
148
|
-
dmc.Text(id="upload-status", size="sm", c="dimmed", style={
|
|
149
|
-
"padding": "0 15px 8px",
|
|
150
|
-
}),
|
|
151
202
|
], id="chat-panel", style={
|
|
152
203
|
"flex": "1", "display": "flex", "flexDirection": "column",
|
|
153
204
|
"background": "var(--mantine-color-body)", "minWidth": "0",
|
|
@@ -175,6 +226,22 @@ Let's get started!"""
|
|
|
175
226
|
size="sm",
|
|
176
227
|
),
|
|
177
228
|
dmc.Group([
|
|
229
|
+
dmc.ActionIcon(
|
|
230
|
+
DashIconify(icon="mdi:folder-plus-outline", width=18),
|
|
231
|
+
id="create-folder-btn",
|
|
232
|
+
variant="default",
|
|
233
|
+
size="md",
|
|
234
|
+
),
|
|
235
|
+
dcc.Upload(
|
|
236
|
+
id="file-upload-sidebar",
|
|
237
|
+
children=dmc.ActionIcon(
|
|
238
|
+
DashIconify(icon="mdi:file-upload-outline", width=18),
|
|
239
|
+
id="upload-btn",
|
|
240
|
+
variant="default",
|
|
241
|
+
size="md",
|
|
242
|
+
),
|
|
243
|
+
multiple=True,
|
|
244
|
+
),
|
|
178
245
|
dmc.ActionIcon(
|
|
179
246
|
DashIconify(icon="mdi:console", width=18),
|
|
180
247
|
id="open-terminal-btn",
|
|
@@ -196,6 +263,31 @@ Let's get started!"""
|
|
|
196
263
|
|
|
197
264
|
# Files view
|
|
198
265
|
html.Div([
|
|
266
|
+
# Workspace path breadcrumb navigation
|
|
267
|
+
html.Div([
|
|
268
|
+
html.Div(id="workspace-breadcrumb", children=[
|
|
269
|
+
html.Span([
|
|
270
|
+
DashIconify(icon="mdi:home", width=14, style={"marginRight": "4px"}),
|
|
271
|
+
"root"
|
|
272
|
+
], id="breadcrumb-root", className="breadcrumb-item breadcrumb-clickable", style={
|
|
273
|
+
"display": "inline-flex",
|
|
274
|
+
"alignItems": "center",
|
|
275
|
+
"cursor": "pointer",
|
|
276
|
+
"padding": "2px 6px",
|
|
277
|
+
"borderRadius": "3px",
|
|
278
|
+
}),
|
|
279
|
+
], style={
|
|
280
|
+
"display": "flex",
|
|
281
|
+
"alignItems": "center",
|
|
282
|
+
"flexWrap": "wrap",
|
|
283
|
+
"gap": "2px",
|
|
284
|
+
"fontSize": "13px",
|
|
285
|
+
}),
|
|
286
|
+
], className="breadcrumb-bar", style={
|
|
287
|
+
"padding": "6px 10px",
|
|
288
|
+
"borderBottom": "1px solid var(--mantine-color-default-border)",
|
|
289
|
+
"background": "var(--mantine-color-gray-0)",
|
|
290
|
+
}),
|
|
199
291
|
html.Div(
|
|
200
292
|
id="file-tree",
|
|
201
293
|
children=render_file_tree(build_file_tree(workspace_root, workspace_root), colors, styles),
|