cowork-dash 0.2.0__py3-none-any.whl → 0.2.1__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 +18 -23
- cowork_dash/app.py +595 -223
- cowork_dash/assets/styles.css +20 -0
- cowork_dash/components.py +199 -28
- cowork_dash/layout.py +41 -0
- cowork_dash/tools.py +20 -35
- {cowork_dash-0.2.0.dist-info → cowork_dash-0.2.1.dist-info}/METADATA +1 -1
- {cowork_dash-0.2.0.dist-info → cowork_dash-0.2.1.dist-info}/RECORD +11 -11
- {cowork_dash-0.2.0.dist-info → cowork_dash-0.2.1.dist-info}/WHEEL +0 -0
- {cowork_dash-0.2.0.dist-info → cowork_dash-0.2.1.dist-info}/entry_points.txt +0 -0
- {cowork_dash-0.2.0.dist-info → cowork_dash-0.2.1.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:
|
|
@@ -349,24 +439,53 @@ def render_display_inline_result(result: Dict, colors: Dict) -> html.Div:
|
|
|
349
439
|
])
|
|
350
440
|
|
|
351
441
|
elif display_type == "html":
|
|
352
|
-
# Show preview
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
442
|
+
# Show HTML preview directly in iframe (like PDF)
|
|
443
|
+
if not data:
|
|
444
|
+
content_element = html.Div(
|
|
445
|
+
"Error: HTML content is empty or missing",
|
|
446
|
+
style={"color": "red", "padding": "10px"}
|
|
447
|
+
)
|
|
448
|
+
else:
|
|
449
|
+
# Create fullscreen button
|
|
450
|
+
fullscreen_btn = html.Button(
|
|
451
|
+
DashIconify(icon="mdi:fullscreen", width=18),
|
|
452
|
+
id={"type": "fullscreen-btn", "index": item_id},
|
|
453
|
+
className="fullscreen-btn",
|
|
454
|
+
title="View fullscreen",
|
|
361
455
|
style={
|
|
362
|
-
"
|
|
363
|
-
"
|
|
456
|
+
"position": "absolute",
|
|
457
|
+
"top": "8px",
|
|
458
|
+
"right": "8px",
|
|
459
|
+
"background": "rgba(255,255,255,0.9)",
|
|
364
460
|
"border": "1px solid #ddd",
|
|
365
|
-
"borderRadius": "
|
|
366
|
-
"
|
|
461
|
+
"borderRadius": "4px",
|
|
462
|
+
"cursor": "pointer",
|
|
463
|
+
"padding": "4px 6px",
|
|
464
|
+
"display": "flex",
|
|
465
|
+
"alignItems": "center",
|
|
466
|
+
"justifyContent": "center",
|
|
467
|
+
"zIndex": "10",
|
|
367
468
|
}
|
|
368
469
|
)
|
|
369
|
-
|
|
470
|
+
# Store data for fullscreen modal
|
|
471
|
+
fullscreen_store = dcc.Store(
|
|
472
|
+
id={"type": "fullscreen-data", "index": item_id},
|
|
473
|
+
data={"type": "html", "content": data, "title": title or "HTML Preview"}
|
|
474
|
+
)
|
|
475
|
+
content_element = html.Div([
|
|
476
|
+
fullscreen_btn,
|
|
477
|
+
html.Iframe(
|
|
478
|
+
srcDoc=data,
|
|
479
|
+
style={
|
|
480
|
+
"width": "100%",
|
|
481
|
+
"height": "400px",
|
|
482
|
+
"border": "none",
|
|
483
|
+
"borderRadius": "5px",
|
|
484
|
+
"backgroundColor": "white",
|
|
485
|
+
}
|
|
486
|
+
),
|
|
487
|
+
fullscreen_store,
|
|
488
|
+
], style={"position": "relative"})
|
|
370
489
|
|
|
371
490
|
elif display_type == "pdf":
|
|
372
491
|
mime_type = result.get("mime_type", "application/pdf")
|
|
@@ -378,16 +497,46 @@ def render_display_inline_result(result: Dict, colors: Dict) -> html.Div:
|
|
|
378
497
|
)
|
|
379
498
|
else:
|
|
380
499
|
data_url = f"data:{mime_type};base64,{data}"
|
|
381
|
-
#
|
|
382
|
-
|
|
383
|
-
|
|
500
|
+
# Create fullscreen button
|
|
501
|
+
fullscreen_btn = html.Button(
|
|
502
|
+
DashIconify(icon="mdi:fullscreen", width=18),
|
|
503
|
+
id={"type": "fullscreen-btn", "index": item_id},
|
|
504
|
+
className="fullscreen-btn",
|
|
505
|
+
title="View fullscreen",
|
|
384
506
|
style={
|
|
385
|
-
"
|
|
386
|
-
"
|
|
387
|
-
"
|
|
388
|
-
"
|
|
507
|
+
"position": "absolute",
|
|
508
|
+
"top": "8px",
|
|
509
|
+
"right": "8px",
|
|
510
|
+
"background": "rgba(255,255,255,0.9)",
|
|
511
|
+
"border": "1px solid #ddd",
|
|
512
|
+
"borderRadius": "4px",
|
|
513
|
+
"cursor": "pointer",
|
|
514
|
+
"padding": "4px 6px",
|
|
515
|
+
"display": "flex",
|
|
516
|
+
"alignItems": "center",
|
|
517
|
+
"justifyContent": "center",
|
|
518
|
+
"zIndex": "10",
|
|
389
519
|
}
|
|
390
520
|
)
|
|
521
|
+
# Store data for fullscreen modal
|
|
522
|
+
fullscreen_store = dcc.Store(
|
|
523
|
+
id={"type": "fullscreen-data", "index": item_id},
|
|
524
|
+
data={"type": "pdf", "content": data_url, "title": title or "PDF Preview"}
|
|
525
|
+
)
|
|
526
|
+
# Use iframe instead of embed for better browser compatibility
|
|
527
|
+
content_element = html.Div([
|
|
528
|
+
fullscreen_btn,
|
|
529
|
+
html.Iframe(
|
|
530
|
+
src=data_url,
|
|
531
|
+
style={
|
|
532
|
+
"width": "100%",
|
|
533
|
+
"height": "400px",
|
|
534
|
+
"border": "none",
|
|
535
|
+
"borderRadius": "5px",
|
|
536
|
+
}
|
|
537
|
+
),
|
|
538
|
+
fullscreen_store,
|
|
539
|
+
], style={"position": "relative"})
|
|
391
540
|
|
|
392
541
|
elif display_type == "json":
|
|
393
542
|
json_str = json.dumps(data, indent=2) if isinstance(data, (dict, list)) else str(data)
|
|
@@ -477,16 +626,38 @@ def render_display_inline_result(result: Dict, colors: Dict) -> html.Div:
|
|
|
477
626
|
}
|
|
478
627
|
)
|
|
479
628
|
|
|
629
|
+
# Create download button
|
|
630
|
+
download_btn = html.Button(
|
|
631
|
+
DashIconify(icon="mdi:download", width=16),
|
|
632
|
+
id={"type": "download-display-btn", "index": item_id},
|
|
633
|
+
className="download-display-btn",
|
|
634
|
+
title="Download",
|
|
635
|
+
style={
|
|
636
|
+
"background": "transparent",
|
|
637
|
+
"border": "none",
|
|
638
|
+
"cursor": "pointer",
|
|
639
|
+
"padding": "4px",
|
|
640
|
+
"borderRadius": "4px",
|
|
641
|
+
"display": "flex",
|
|
642
|
+
"alignItems": "center",
|
|
643
|
+
"justifyContent": "center",
|
|
644
|
+
"marginLeft": "4px",
|
|
645
|
+
}
|
|
646
|
+
)
|
|
647
|
+
|
|
480
648
|
# Store the result data in a hidden div for the callback to retrieve
|
|
481
649
|
result_store = dcc.Store(
|
|
482
650
|
id={"type": "display-inline-data", "index": item_id},
|
|
483
651
|
data=result
|
|
484
652
|
)
|
|
485
653
|
|
|
486
|
-
# Build summary with text and
|
|
654
|
+
# Build summary with text and buttons
|
|
487
655
|
summary_content = html.Div([
|
|
488
656
|
html.Span(summary_text, className="display-inline-summary-text"),
|
|
489
|
-
|
|
657
|
+
html.Div([
|
|
658
|
+
add_to_canvas_btn,
|
|
659
|
+
download_btn,
|
|
660
|
+
], style={"display": "flex", "alignItems": "center"}),
|
|
490
661
|
], className="display-inline-summary-row", style={
|
|
491
662
|
"display": "flex",
|
|
492
663
|
"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,23 @@ 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": "10px",
|
|
387
|
+
"borderLeft": "1px solid var(--mantine-color-default-border)",
|
|
388
|
+
"background": "var(--mantine-color-body)",
|
|
389
|
+
}),
|
|
349
390
|
], id="main-container", style={"display": "flex", "flex": "1", "overflow": "hidden"}),
|
|
350
391
|
], id="app-container", style={"display": "flex", "flexDirection": "column", "height": "100vh"})
|
|
351
392
|
])
|
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.1
|
|
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=jQZGHrpyhz9c57GgSe7MAo-pohNc8exd0WRw-zJrrV0,6495
|
|
4
|
+
cowork_dash/app.py,sha256=OpUqnwKgobT_9x0QeGWOxq9_ExkFyVNsrYOFvQx3RKc,162756
|
|
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=dUpmav4XWJmlr1CziNXfyoXwSXhTHFj_-TtQIGHYkwI,42468
|
|
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=B4A7tiMnm836HYBAU4J3hirS3HvGBhJHbdlPwGquHB4,18526
|
|
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.1.dist-info/METADATA,sha256=k7EHUdfLUzlhYvlVD0lZfYiLynzQJO3opHEUeo0qxmY,6719
|
|
20
|
+
cowork_dash-0.2.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
21
|
+
cowork_dash-0.2.1.dist-info/entry_points.txt,sha256=lL_9XJINiky3nh13tLqWd61LitKbbyh085ROATH9fck,53
|
|
22
|
+
cowork_dash-0.2.1.dist-info/licenses/LICENSE,sha256=2SFXFfIa_c_g_uwY0JApQDXI1mWqEfJeG87Pn4ehLMQ,1072
|
|
23
|
+
cowork_dash-0.2.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|