cowork-dash 0.2.0__py3-none-any.whl → 0.2.2__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 +19 -24
- cowork_dash/app.py +612 -223
- cowork_dash/assets/styles.css +20 -0
- cowork_dash/components.py +213 -29
- cowork_dash/layout.py +43 -0
- cowork_dash/tools.py +20 -35
- {cowork_dash-0.2.0.dist-info → cowork_dash-0.2.2.dist-info}/METADATA +1 -1
- {cowork_dash-0.2.0.dist-info → cowork_dash-0.2.2.dist-info}/RECORD +11 -11
- {cowork_dash-0.2.0.dist-info → cowork_dash-0.2.2.dist-info}/WHEEL +0 -0
- {cowork_dash-0.2.0.dist-info → cowork_dash-0.2.2.dist-info}/entry_points.txt +0 -0
- {cowork_dash-0.2.0.dist-info → cowork_dash-0.2.2.dist-info}/licenses/LICENSE +0 -0
cowork_dash/assets/styles.css
CHANGED
|
@@ -156,6 +156,15 @@ code, pre, .mono {
|
|
|
156
156
|
line-height: 1.6;
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
/* Tighter spacing for consecutive AI text blocks */
|
|
160
|
+
#chat-messages .ai-text-block p {
|
|
161
|
+
margin: 0;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
#chat-messages .ai-text-block p:last-child {
|
|
165
|
+
margin-bottom: 0;
|
|
166
|
+
}
|
|
167
|
+
|
|
159
168
|
#chat-messages ul, #chat-messages ol {
|
|
160
169
|
margin: 6px 0;
|
|
161
170
|
padding-left: 20px;
|
|
@@ -1158,3 +1167,14 @@ details.display-inline-container[open] > .display-inline-summary::before {
|
|
|
1158
1167
|
text-overflow: ellipsis;
|
|
1159
1168
|
white-space: nowrap;
|
|
1160
1169
|
}
|
|
1170
|
+
|
|
1171
|
+
/* Fullscreen button for HTML/PDF preview */
|
|
1172
|
+
.fullscreen-btn {
|
|
1173
|
+
opacity: 0.7;
|
|
1174
|
+
transition: opacity 0.15s ease, background-color 0.15s ease;
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
.fullscreen-btn:hover {
|
|
1178
|
+
opacity: 1;
|
|
1179
|
+
background-color: rgba(255,255,255,1) !important;
|
|
1180
|
+
}
|
cowork_dash/components.py
CHANGED
|
@@ -82,20 +82,110 @@ def format_loading(colors: Dict):
|
|
|
82
82
|
|
|
83
83
|
|
|
84
84
|
def format_thinking(thinking_text: str, colors: Dict):
|
|
85
|
-
"""Format thinking as an inline subordinate message."""
|
|
85
|
+
"""Format thinking as an inline subordinate message (non-collapsible)."""
|
|
86
86
|
if not thinking_text:
|
|
87
87
|
return None
|
|
88
88
|
|
|
89
|
-
return html.
|
|
90
|
-
html.
|
|
91
|
-
|
|
89
|
+
return html.Div([
|
|
90
|
+
html.Div("Thinking", style={
|
|
91
|
+
"fontSize": "12px",
|
|
92
|
+
"fontWeight": "500",
|
|
93
|
+
"color": colors.get("thinking", colors.get("text_muted", "#888")),
|
|
94
|
+
"marginBottom": "2px",
|
|
95
|
+
}),
|
|
96
|
+
html.Div(thinking_text, style={
|
|
92
97
|
"whiteSpace": "pre-wrap",
|
|
98
|
+
"fontSize": "13px",
|
|
99
|
+
"color": colors.get("text_muted", "#666"),
|
|
100
|
+
"paddingLeft": "8px",
|
|
101
|
+
"borderLeft": f"2px solid {colors.get('thinking', '#7c4dff')}",
|
|
93
102
|
})
|
|
94
|
-
],
|
|
103
|
+
], style={
|
|
95
104
|
"marginBottom": "4px",
|
|
96
105
|
})
|
|
97
106
|
|
|
98
107
|
|
|
108
|
+
def format_ai_text(text: str, colors: Dict, is_new: bool = False):
|
|
109
|
+
"""Format AI text response (without the full message wrapper).
|
|
110
|
+
|
|
111
|
+
This is used when rendering ordered content items where thinking
|
|
112
|
+
and text are interleaved.
|
|
113
|
+
"""
|
|
114
|
+
if not text:
|
|
115
|
+
return None
|
|
116
|
+
|
|
117
|
+
return dcc.Markdown(
|
|
118
|
+
text,
|
|
119
|
+
className="ai-text-block",
|
|
120
|
+
style={
|
|
121
|
+
"fontSize": "15px",
|
|
122
|
+
"lineHeight": "1.5",
|
|
123
|
+
"margin": "0",
|
|
124
|
+
"padding": "0",
|
|
125
|
+
}
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def render_ordered_content_items(content_items: List[Dict], colors: Dict, styles: Dict = None, response_time: float = None) -> List:
|
|
130
|
+
"""Render content items in their original emission order.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
content_items: List of {"type": "text"|"thinking"|"display_inline", "content": ...}
|
|
134
|
+
colors: Color scheme dict
|
|
135
|
+
styles: Optional styles dict
|
|
136
|
+
response_time: Optional response time to show after the last text item
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
List of rendered Dash components in order
|
|
140
|
+
"""
|
|
141
|
+
if not content_items:
|
|
142
|
+
return []
|
|
143
|
+
|
|
144
|
+
rendered = []
|
|
145
|
+
last_text_index = None
|
|
146
|
+
|
|
147
|
+
# Find the last text item index for response time display
|
|
148
|
+
for i, item in enumerate(content_items):
|
|
149
|
+
if item.get("type") == "text":
|
|
150
|
+
last_text_index = i
|
|
151
|
+
|
|
152
|
+
for i, item in enumerate(content_items):
|
|
153
|
+
item_type = item.get("type")
|
|
154
|
+
content = item.get("content", "")
|
|
155
|
+
|
|
156
|
+
if not content:
|
|
157
|
+
continue
|
|
158
|
+
|
|
159
|
+
if item_type == "thinking":
|
|
160
|
+
block = format_thinking(content, colors)
|
|
161
|
+
if block:
|
|
162
|
+
rendered.append(block)
|
|
163
|
+
elif item_type == "text":
|
|
164
|
+
# For the last text item, we might want to show response time
|
|
165
|
+
is_last_text = (i == last_text_index)
|
|
166
|
+
block = format_ai_text(content, colors)
|
|
167
|
+
if block:
|
|
168
|
+
rendered.append(block)
|
|
169
|
+
# Add response time after the last text block
|
|
170
|
+
if is_last_text and response_time is not None:
|
|
171
|
+
time_display = f"{response_time:.1f}s" if response_time >= 1 else f"{response_time*1000:.0f}ms"
|
|
172
|
+
rendered.append(html.Span(
|
|
173
|
+
time_display,
|
|
174
|
+
style={
|
|
175
|
+
"fontSize": "11px",
|
|
176
|
+
"color": colors.get("text_muted", "#888"),
|
|
177
|
+
"marginLeft": "4px",
|
|
178
|
+
}
|
|
179
|
+
))
|
|
180
|
+
elif item_type == "display_inline":
|
|
181
|
+
# content is the full display_inline item dict
|
|
182
|
+
block = render_display_inline_result(content, colors)
|
|
183
|
+
if block:
|
|
184
|
+
rendered.append(block)
|
|
185
|
+
|
|
186
|
+
return rendered
|
|
187
|
+
|
|
188
|
+
|
|
99
189
|
def format_todos(todos, colors: Dict):
|
|
100
190
|
"""Format todo list. Handles both list format [{"content": ..., "status": ...}] and dict format {task_name: status}."""
|
|
101
191
|
if not todos:
|
|
@@ -201,13 +291,23 @@ def format_tool_call(tool_call: Dict, colors: Dict, is_completed: bool = False):
|
|
|
201
291
|
|
|
202
292
|
# Format args for display (truncate if too long)
|
|
203
293
|
args_display = ""
|
|
294
|
+
args_preview = "" # Short preview for header
|
|
204
295
|
if tool_args:
|
|
205
296
|
try:
|
|
297
|
+
# Compact JSON for preview (no indentation)
|
|
298
|
+
args_compact = json.dumps(tool_args, separators=(',', ':'))
|
|
299
|
+
if len(args_compact) > 100:
|
|
300
|
+
args_preview = args_compact[:100] + "..."
|
|
301
|
+
else:
|
|
302
|
+
args_preview = args_compact
|
|
303
|
+
|
|
304
|
+
# Full JSON for expanded view
|
|
206
305
|
args_str = json.dumps(tool_args, indent=2)
|
|
207
306
|
if len(args_str) > 500:
|
|
208
307
|
args_str = args_str[:500] + "..."
|
|
209
308
|
args_display = args_str
|
|
210
309
|
except:
|
|
310
|
+
args_preview = str(tool_args)[:100]
|
|
211
311
|
args_display = str(tool_args)[:500]
|
|
212
312
|
|
|
213
313
|
# Build inner content (shown when expanded)
|
|
@@ -235,11 +335,14 @@ def format_tool_call(tool_call: Dict, colors: Dict, is_completed: bool = False):
|
|
|
235
335
|
html.Pre(result_display, className="tool-call-result")
|
|
236
336
|
]))
|
|
237
337
|
|
|
338
|
+
# Build header text: tool_name(args_preview)
|
|
339
|
+
header_text = f"{tool_name}({args_preview})" if args_preview else tool_name
|
|
340
|
+
|
|
238
341
|
# Wrap entire tool call in a details element
|
|
239
342
|
return html.Details([
|
|
240
343
|
html.Summary([
|
|
241
344
|
html.Span(className="tool-call-status-dot"),
|
|
242
|
-
html.Span(
|
|
345
|
+
html.Span(header_text, className="tool-call-name"),
|
|
243
346
|
], className="tool-call-header"),
|
|
244
347
|
html.Div(inner_children, className="tool-call-body") if inner_children else None
|
|
245
348
|
], className=f"tool-call {status_class}")
|
|
@@ -349,24 +452,53 @@ def render_display_inline_result(result: Dict, colors: Dict) -> html.Div:
|
|
|
349
452
|
])
|
|
350
453
|
|
|
351
454
|
elif display_type == "html":
|
|
352
|
-
# Show preview
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
455
|
+
# Show HTML preview directly in iframe (like PDF)
|
|
456
|
+
if not data:
|
|
457
|
+
content_element = html.Div(
|
|
458
|
+
"Error: HTML content is empty or missing",
|
|
459
|
+
style={"color": "red", "padding": "10px"}
|
|
460
|
+
)
|
|
461
|
+
else:
|
|
462
|
+
# Create fullscreen button
|
|
463
|
+
fullscreen_btn = html.Button(
|
|
464
|
+
DashIconify(icon="mdi:fullscreen", width=18),
|
|
465
|
+
id={"type": "fullscreen-btn", "index": item_id},
|
|
466
|
+
className="fullscreen-btn",
|
|
467
|
+
title="View fullscreen",
|
|
361
468
|
style={
|
|
362
|
-
"
|
|
363
|
-
"
|
|
469
|
+
"position": "absolute",
|
|
470
|
+
"top": "8px",
|
|
471
|
+
"right": "8px",
|
|
472
|
+
"background": "rgba(255,255,255,0.9)",
|
|
364
473
|
"border": "1px solid #ddd",
|
|
365
|
-
"borderRadius": "
|
|
366
|
-
"
|
|
474
|
+
"borderRadius": "4px",
|
|
475
|
+
"cursor": "pointer",
|
|
476
|
+
"padding": "4px 6px",
|
|
477
|
+
"display": "flex",
|
|
478
|
+
"alignItems": "center",
|
|
479
|
+
"justifyContent": "center",
|
|
480
|
+
"zIndex": "10",
|
|
367
481
|
}
|
|
368
482
|
)
|
|
369
|
-
|
|
483
|
+
# Store data for fullscreen modal
|
|
484
|
+
fullscreen_store = dcc.Store(
|
|
485
|
+
id={"type": "fullscreen-data", "index": item_id},
|
|
486
|
+
data={"type": "html", "content": data, "title": title or "HTML Preview"}
|
|
487
|
+
)
|
|
488
|
+
content_element = html.Div([
|
|
489
|
+
fullscreen_btn,
|
|
490
|
+
html.Iframe(
|
|
491
|
+
srcDoc=data,
|
|
492
|
+
style={
|
|
493
|
+
"width": "100%",
|
|
494
|
+
"height": "400px",
|
|
495
|
+
"border": "none",
|
|
496
|
+
"borderRadius": "5px",
|
|
497
|
+
"backgroundColor": "white",
|
|
498
|
+
}
|
|
499
|
+
),
|
|
500
|
+
fullscreen_store,
|
|
501
|
+
], style={"position": "relative"})
|
|
370
502
|
|
|
371
503
|
elif display_type == "pdf":
|
|
372
504
|
mime_type = result.get("mime_type", "application/pdf")
|
|
@@ -378,16 +510,46 @@ def render_display_inline_result(result: Dict, colors: Dict) -> html.Div:
|
|
|
378
510
|
)
|
|
379
511
|
else:
|
|
380
512
|
data_url = f"data:{mime_type};base64,{data}"
|
|
381
|
-
#
|
|
382
|
-
|
|
383
|
-
|
|
513
|
+
# Create fullscreen button
|
|
514
|
+
fullscreen_btn = html.Button(
|
|
515
|
+
DashIconify(icon="mdi:fullscreen", width=18),
|
|
516
|
+
id={"type": "fullscreen-btn", "index": item_id},
|
|
517
|
+
className="fullscreen-btn",
|
|
518
|
+
title="View fullscreen",
|
|
384
519
|
style={
|
|
385
|
-
"
|
|
386
|
-
"
|
|
387
|
-
"
|
|
388
|
-
"
|
|
520
|
+
"position": "absolute",
|
|
521
|
+
"top": "8px",
|
|
522
|
+
"right": "8px",
|
|
523
|
+
"background": "rgba(255,255,255,0.9)",
|
|
524
|
+
"border": "1px solid #ddd",
|
|
525
|
+
"borderRadius": "4px",
|
|
526
|
+
"cursor": "pointer",
|
|
527
|
+
"padding": "4px 6px",
|
|
528
|
+
"display": "flex",
|
|
529
|
+
"alignItems": "center",
|
|
530
|
+
"justifyContent": "center",
|
|
531
|
+
"zIndex": "10",
|
|
389
532
|
}
|
|
390
533
|
)
|
|
534
|
+
# Store data for fullscreen modal
|
|
535
|
+
fullscreen_store = dcc.Store(
|
|
536
|
+
id={"type": "fullscreen-data", "index": item_id},
|
|
537
|
+
data={"type": "pdf", "content": data_url, "title": title or "PDF Preview"}
|
|
538
|
+
)
|
|
539
|
+
# Use iframe instead of embed for better browser compatibility
|
|
540
|
+
content_element = html.Div([
|
|
541
|
+
fullscreen_btn,
|
|
542
|
+
html.Iframe(
|
|
543
|
+
src=data_url,
|
|
544
|
+
style={
|
|
545
|
+
"width": "100%",
|
|
546
|
+
"height": "400px",
|
|
547
|
+
"border": "none",
|
|
548
|
+
"borderRadius": "5px",
|
|
549
|
+
}
|
|
550
|
+
),
|
|
551
|
+
fullscreen_store,
|
|
552
|
+
], style={"position": "relative"})
|
|
391
553
|
|
|
392
554
|
elif display_type == "json":
|
|
393
555
|
json_str = json.dumps(data, indent=2) if isinstance(data, (dict, list)) else str(data)
|
|
@@ -477,16 +639,38 @@ def render_display_inline_result(result: Dict, colors: Dict) -> html.Div:
|
|
|
477
639
|
}
|
|
478
640
|
)
|
|
479
641
|
|
|
642
|
+
# Create download button
|
|
643
|
+
download_btn = html.Button(
|
|
644
|
+
DashIconify(icon="mdi:download", width=16),
|
|
645
|
+
id={"type": "download-display-btn", "index": item_id},
|
|
646
|
+
className="download-display-btn",
|
|
647
|
+
title="Download",
|
|
648
|
+
style={
|
|
649
|
+
"background": "transparent",
|
|
650
|
+
"border": "none",
|
|
651
|
+
"cursor": "pointer",
|
|
652
|
+
"padding": "4px",
|
|
653
|
+
"borderRadius": "4px",
|
|
654
|
+
"display": "flex",
|
|
655
|
+
"alignItems": "center",
|
|
656
|
+
"justifyContent": "center",
|
|
657
|
+
"marginLeft": "4px",
|
|
658
|
+
}
|
|
659
|
+
)
|
|
660
|
+
|
|
480
661
|
# Store the result data in a hidden div for the callback to retrieve
|
|
481
662
|
result_store = dcc.Store(
|
|
482
663
|
id={"type": "display-inline-data", "index": item_id},
|
|
483
664
|
data=result
|
|
484
665
|
)
|
|
485
666
|
|
|
486
|
-
# Build summary with text and
|
|
667
|
+
# Build summary with text and buttons
|
|
487
668
|
summary_content = html.Div([
|
|
488
669
|
html.Span(summary_text, className="display-inline-summary-text"),
|
|
489
|
-
|
|
670
|
+
html.Div([
|
|
671
|
+
add_to_canvas_btn,
|
|
672
|
+
download_btn,
|
|
673
|
+
], style={"display": "flex", "alignItems": "center"}),
|
|
490
674
|
], className="display-inline-summary-row", style={
|
|
491
675
|
"display": "flex",
|
|
492
676
|
"alignItems": "center",
|
cowork_dash/layout.py
CHANGED
|
@@ -53,6 +53,7 @@ def create_layout(workspace_root, app_title, app_subtitle, colors, styles, agent
|
|
|
53
53
|
dcc.Store(id="theme-store", data="light", storage_type="local"),
|
|
54
54
|
dcc.Store(id="current-workspace-path", data=""), # Relative path from original workspace root
|
|
55
55
|
dcc.Store(id="collapsed-canvas-items", data=[]), # Track which canvas items are collapsed
|
|
56
|
+
dcc.Store(id="sidebar-collapsed", data=False), # Track if sidebar is collapsed
|
|
56
57
|
dcc.Download(id="file-download"),
|
|
57
58
|
|
|
58
59
|
# Interval for polling agent updates (disabled by default)
|
|
@@ -134,6 +135,22 @@ def create_layout(workspace_root, app_title, app_subtitle, colors, styles, agent
|
|
|
134
135
|
opened=False,
|
|
135
136
|
),
|
|
136
137
|
|
|
138
|
+
# Fullscreen preview modal for HTML/PDF
|
|
139
|
+
dmc.Modal(
|
|
140
|
+
id="fullscreen-preview-modal",
|
|
141
|
+
title="Preview",
|
|
142
|
+
size="100%",
|
|
143
|
+
children=[
|
|
144
|
+
html.Div(id="fullscreen-preview-content", style={"height": "calc(100vh - 120px)"})
|
|
145
|
+
],
|
|
146
|
+
opened=False,
|
|
147
|
+
styles={
|
|
148
|
+
"content": {"height": "95vh", "maxHeight": "95vh"},
|
|
149
|
+
"body": {"height": "calc(100% - 60px)", "padding": "0"},
|
|
150
|
+
},
|
|
151
|
+
),
|
|
152
|
+
dcc.Store(id="fullscreen-preview-data", data=None),
|
|
153
|
+
|
|
137
154
|
html.Div([
|
|
138
155
|
# Compact Header
|
|
139
156
|
html.Header([
|
|
@@ -264,6 +281,13 @@ def create_layout(workspace_root, app_title, app_subtitle, colors, styles, agent
|
|
|
264
281
|
variant="default",
|
|
265
282
|
size="md",
|
|
266
283
|
),
|
|
284
|
+
dmc.ActionIcon(
|
|
285
|
+
DashIconify(icon="mdi:chevron-right", width=18),
|
|
286
|
+
id="collapse-sidebar-btn",
|
|
287
|
+
variant="subtle",
|
|
288
|
+
size="md",
|
|
289
|
+
**{"aria-label": "Collapse sidebar"},
|
|
290
|
+
),
|
|
267
291
|
], id="files-actions", gap=5)
|
|
268
292
|
], id="sidebar-header", style={
|
|
269
293
|
"display": "flex", "justifyContent": "space-between",
|
|
@@ -346,6 +370,25 @@ def create_layout(workspace_root, app_title, app_subtitle, colors, styles, agent
|
|
|
346
370
|
"background": "var(--mantine-color-body)",
|
|
347
371
|
"borderLeft": "1px solid var(--mantine-color-default-border)",
|
|
348
372
|
}),
|
|
373
|
+
|
|
374
|
+
# Expand button (shown when sidebar is collapsed)
|
|
375
|
+
html.Div([
|
|
376
|
+
dmc.ActionIcon(
|
|
377
|
+
DashIconify(icon="mdi:chevron-left", width=18),
|
|
378
|
+
id="expand-sidebar-btn",
|
|
379
|
+
variant="subtle",
|
|
380
|
+
size="md",
|
|
381
|
+
**{"aria-label": "Expand sidebar"},
|
|
382
|
+
),
|
|
383
|
+
], id="sidebar-expand-btn", style={
|
|
384
|
+
"display": "none",
|
|
385
|
+
"alignItems": "flex-start",
|
|
386
|
+
"paddingTop": "8px",
|
|
387
|
+
"paddingLeft": "2px",
|
|
388
|
+
"paddingRight": "2px",
|
|
389
|
+
"borderLeft": "1px solid var(--mantine-color-default-border)",
|
|
390
|
+
"background": "var(--mantine-color-body)",
|
|
391
|
+
}),
|
|
349
392
|
], id="main-container", style={"display": "flex", "flex": "1", "overflow": "hidden"}),
|
|
350
393
|
], id="app-container", style={"display": "flex", "flexDirection": "column", "height": "100vh"})
|
|
351
394
|
])
|
cowork_dash/tools.py
CHANGED
|
@@ -230,37 +230,6 @@ except (ImportError, AttributeError):
|
|
|
230
230
|
if VIRTUAL_FS:
|
|
231
231
|
self._inject_virtual_fs_helpers()
|
|
232
232
|
|
|
233
|
-
# Inject add_to_canvas function that captures items
|
|
234
|
-
def _add_to_canvas_wrapper(content: Any) -> Dict[str, Any]:
|
|
235
|
-
"""Add content to the canvas for visualization.
|
|
236
|
-
|
|
237
|
-
Supports: DataFrames, matplotlib figures, plotly figures,
|
|
238
|
-
PIL images, and markdown strings.
|
|
239
|
-
"""
|
|
240
|
-
try:
|
|
241
|
-
# Use session's VirtualFilesystem in virtual FS mode, otherwise physical path
|
|
242
|
-
if VIRTUAL_FS and self._session_id:
|
|
243
|
-
from .virtual_fs import get_session_manager
|
|
244
|
-
workspace_root = get_session_manager().get_filesystem(self._session_id)
|
|
245
|
-
if workspace_root is None:
|
|
246
|
-
raise RuntimeError(f"Session {self._session_id} not found")
|
|
247
|
-
else:
|
|
248
|
-
workspace_root = WORKSPACE_ROOT
|
|
249
|
-
|
|
250
|
-
parsed = parse_canvas_object(content, workspace_root=workspace_root)
|
|
251
|
-
self._canvas_items.append(parsed)
|
|
252
|
-
return parsed
|
|
253
|
-
except Exception as e:
|
|
254
|
-
error_result = {
|
|
255
|
-
"type": "error",
|
|
256
|
-
"data": f"Failed to add to canvas: {str(e)}",
|
|
257
|
-
"error": str(e)
|
|
258
|
-
}
|
|
259
|
-
self._canvas_items.append(error_result)
|
|
260
|
-
return error_result
|
|
261
|
-
|
|
262
|
-
self._namespace["add_to_canvas"] = _add_to_canvas_wrapper
|
|
263
|
-
|
|
264
233
|
def _inject_virtual_fs_helpers(self):
|
|
265
234
|
"""Inject virtual filesystem helper functions into the namespace."""
|
|
266
235
|
from .virtual_fs import get_session_manager
|
|
@@ -1089,6 +1058,9 @@ def _display_inline_impl(
|
|
|
1089
1058
|
# Handle explicit display_type for strings
|
|
1090
1059
|
if isinstance(content, str) and display_type:
|
|
1091
1060
|
if display_type == "html":
|
|
1061
|
+
# Check if it's a file path first
|
|
1062
|
+
if _is_file_path(content, workspace_root):
|
|
1063
|
+
return _process_file_for_display(content, workspace_root, title, "html")
|
|
1092
1064
|
result["display_type"] = "html"
|
|
1093
1065
|
result["data"] = content
|
|
1094
1066
|
result["preview"] = content[:500] + "..." if len(content) > 500 else content
|
|
@@ -1098,6 +1070,9 @@ def _display_inline_impl(
|
|
|
1098
1070
|
result["data"] = content
|
|
1099
1071
|
return result
|
|
1100
1072
|
elif display_type in ("csv", "dataframe"):
|
|
1073
|
+
# Check if it's a file path first
|
|
1074
|
+
if _is_file_path(content, workspace_root):
|
|
1075
|
+
return _process_file_for_display(content, workspace_root, title, "csv")
|
|
1101
1076
|
# Parse CSV string
|
|
1102
1077
|
try:
|
|
1103
1078
|
import pandas as pd
|
|
@@ -1109,6 +1084,9 @@ def _display_inline_impl(
|
|
|
1109
1084
|
result["error"] = f"Could not parse as CSV: {e}"
|
|
1110
1085
|
return result
|
|
1111
1086
|
elif display_type == "json":
|
|
1087
|
+
# Check if it's a file path first
|
|
1088
|
+
if _is_file_path(content, workspace_root):
|
|
1089
|
+
return _process_file_for_display(content, workspace_root, title, "json")
|
|
1112
1090
|
result["display_type"] = "json"
|
|
1113
1091
|
try:
|
|
1114
1092
|
result["data"] = json.loads(content) if isinstance(content, str) else content
|
|
@@ -1123,6 +1101,9 @@ def _display_inline_impl(
|
|
|
1123
1101
|
result["data"] = content # Assume base64
|
|
1124
1102
|
return result
|
|
1125
1103
|
elif display_type == "plotly":
|
|
1104
|
+
# Check if it's a file path first
|
|
1105
|
+
if _is_file_path(content, workspace_root):
|
|
1106
|
+
return _process_file_for_display(content, workspace_root, title, "plotly")
|
|
1126
1107
|
result["display_type"] = "plotly"
|
|
1127
1108
|
if isinstance(content, str):
|
|
1128
1109
|
result["data"] = json.loads(content)
|
|
@@ -1406,14 +1387,18 @@ def _process_file_for_display(
|
|
|
1406
1387
|
result["error"] = f"Could not parse as CSV: {e}"
|
|
1407
1388
|
return result
|
|
1408
1389
|
|
|
1409
|
-
# JSON files (check for Plotly)
|
|
1410
|
-
if ext == '.json' or display_type
|
|
1390
|
+
# JSON files (check for Plotly) or explicit plotly display_type
|
|
1391
|
+
if ext == '.json' or display_type in ("json", "plotly"):
|
|
1411
1392
|
content, is_text, error = read_file_content(workspace_root, file_path)
|
|
1412
1393
|
if content:
|
|
1413
1394
|
try:
|
|
1414
1395
|
data = json.loads(content)
|
|
1415
|
-
#
|
|
1416
|
-
if
|
|
1396
|
+
# Force plotly if display_type is explicitly set, otherwise auto-detect
|
|
1397
|
+
if display_type == "plotly":
|
|
1398
|
+
result["display_type"] = "plotly"
|
|
1399
|
+
result["data"] = data
|
|
1400
|
+
# Check if it's Plotly JSON (auto-detect)
|
|
1401
|
+
elif isinstance(data, dict) and 'data' in data and isinstance(data.get('data'), list):
|
|
1417
1402
|
result["display_type"] = "plotly"
|
|
1418
1403
|
result["data"] = data
|
|
1419
1404
|
else:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cowork-dash
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: AI Agent Web Interface with Filesystem and Canvas Visualization
|
|
5
5
|
Project-URL: Homepage, https://github.com/dkedar7/cowork-dash
|
|
6
6
|
Project-URL: Documentation, https://github.com/dkedar7/cowork-dash/blob/main/README.md
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
cowork_dash/__init__.py,sha256=37qBKl7g12Zos8GFukXLXligCdpD32e2qe9F8cd8Qdk,896
|
|
2
2
|
cowork_dash/__main__.py,sha256=CCM9VIkWuwh7hwVGNBBgCCbeVAcHj1soyBVXUaPgABk,131
|
|
3
|
-
cowork_dash/agent.py,sha256=
|
|
4
|
-
cowork_dash/app.py,sha256=
|
|
3
|
+
cowork_dash/agent.py,sha256=ebbqie02mbk7iemnQ3EFUap6Ywfc9iGfuJJjEYTJF8A,6497
|
|
4
|
+
cowork_dash/app.py,sha256=1xgY3HfXtEZE4D8yrcHGWTlza5-JNpHeVzemnyFhUOg,163453
|
|
5
5
|
cowork_dash/backends.py,sha256=YQE8f65o2qGxIIfvBITAS6krCLjl8D9UixW-pgbdgZk,15050
|
|
6
6
|
cowork_dash/canvas.py,sha256=sYOQ5WBLm29UazA5KO7j8jvIeQpx55LToz1o44o2U-k,17261
|
|
7
7
|
cowork_dash/cli.py,sha256=HRxu_k_AphxD8JuLOVTIb6a1lmSdX7ziGCxnEBrW2gA,7561
|
|
8
|
-
cowork_dash/components.py,sha256=
|
|
8
|
+
cowork_dash/components.py,sha256=DtJn7vQH5AKvbRQ4xCa5jfd3XeSLFe07wNJp5r8y-vc,43029
|
|
9
9
|
cowork_dash/config.py,sha256=vVyDiX9Cdjk1wiZ_ktC3I2aiFC-GndNvLXWGW4aXUOM,5094
|
|
10
10
|
cowork_dash/file_utils.py,sha256=qIpTycCNRIQWhdGcO8OIAKmehrxwkMMumgqw-5MjbSg,15431
|
|
11
|
-
cowork_dash/layout.py,sha256=
|
|
11
|
+
cowork_dash/layout.py,sha256=x38qpNmk7Ry69-YjnQwyiusrJtVsBech3Q7RvegEHSc,18610
|
|
12
12
|
cowork_dash/sandbox.py,sha256=T0TMnZefPJj4OV1D1GNaYrircSG4MhXHk0IPizHXqME,11766
|
|
13
|
-
cowork_dash/tools.py,sha256=
|
|
13
|
+
cowork_dash/tools.py,sha256=JXpYcE7AjBXki1HxZjDrSzMzyHBk0qdscbIEOl1AIZw,58150
|
|
14
14
|
cowork_dash/virtual_fs.py,sha256=PAAdRiMkxgJ4xPpGUyAUd69KbH-nFlt-FjldfB1FrQ4,16296
|
|
15
15
|
cowork_dash/assets/app.js,sha256=Rln4MQPxfyOcyH4lkSbT4RAP-fqF1lT_Qw25cHHUFS8,10157
|
|
16
16
|
cowork_dash/assets/favicon.ico,sha256=IiP0rVr0m-TBGGmCY88SyFC14drNwDhLRqb0n2ZufKk,54252
|
|
17
17
|
cowork_dash/assets/favicon.svg,sha256=MdT50APCvIlWh3HSwW5SNXYWB3q_wKfuLP-JV53SnKg,1065
|
|
18
|
-
cowork_dash/assets/styles.css,sha256=
|
|
19
|
-
cowork_dash-0.2.
|
|
20
|
-
cowork_dash-0.2.
|
|
21
|
-
cowork_dash-0.2.
|
|
22
|
-
cowork_dash-0.2.
|
|
23
|
-
cowork_dash-0.2.
|
|
18
|
+
cowork_dash/assets/styles.css,sha256=hUmw_dYhZJJQQ20iuz7sosrOiGouNE1tkWuyEdBMvaI,29335
|
|
19
|
+
cowork_dash-0.2.2.dist-info/METADATA,sha256=fe67fO8mfql-IIaxV6gCoOejPg0K3Xg5ojiWWj6CwF4,6719
|
|
20
|
+
cowork_dash-0.2.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
21
|
+
cowork_dash-0.2.2.dist-info/entry_points.txt,sha256=lL_9XJINiky3nh13tLqWd61LitKbbyh085ROATH9fck,53
|
|
22
|
+
cowork_dash-0.2.2.dist-info/licenses/LICENSE,sha256=2SFXFfIa_c_g_uwY0JApQDXI1mWqEfJeG87Pn4ehLMQ,1072
|
|
23
|
+
cowork_dash-0.2.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|