cowork-dash 0.1.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/__init__.py +35 -0
- cowork_dash/__main__.py +9 -0
- cowork_dash/agent.py +117 -0
- cowork_dash/app.py +1776 -0
- cowork_dash/assets/app.js +237 -0
- cowork_dash/assets/favicon.svg +1 -0
- cowork_dash/assets/styles.css +915 -0
- cowork_dash/canvas.py +318 -0
- cowork_dash/cli.py +273 -0
- cowork_dash/components.py +568 -0
- cowork_dash/config.py +91 -0
- cowork_dash/file_utils.py +226 -0
- cowork_dash/layout.py +250 -0
- cowork_dash/tools.py +699 -0
- cowork_dash-0.1.2.dist-info/METADATA +238 -0
- cowork_dash-0.1.2.dist-info/RECORD +19 -0
- cowork_dash-0.1.2.dist-info/WHEEL +4 -0
- cowork_dash-0.1.2.dist-info/entry_points.txt +2 -0
- cowork_dash-0.1.2.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"""File tree and file operations utilities."""
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import List, Dict, Tuple
|
|
6
|
+
from dash import html
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
TEXT_EXTENSIONS = {
|
|
10
|
+
".py", ".js", ".ts", ".jsx", ".tsx", ".html", ".css", ".json", ".md", ".txt",
|
|
11
|
+
".yaml", ".yml", ".toml", ".xml", ".csv", ".sh", ".bash", ".sql", ".env",
|
|
12
|
+
".gitignore", ".dockerignore", ".cfg", ".ini", ".conf", ".log"
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def is_text_file(filename: str) -> bool:
|
|
17
|
+
"""Check if a file can be viewed as text."""
|
|
18
|
+
ext = Path(filename).suffix.lower()
|
|
19
|
+
return ext in TEXT_EXTENSIONS or ext == ""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def build_file_tree(root: Path, workspace_root: Path, lazy_load: bool = True) -> List[Dict]:
|
|
23
|
+
"""
|
|
24
|
+
Build file tree structure.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
root: Directory to scan
|
|
28
|
+
workspace_root: Root workspace directory for relative paths
|
|
29
|
+
lazy_load: If True, only load immediate children (subdirs not expanded)
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
List of file/folder items
|
|
33
|
+
"""
|
|
34
|
+
items = []
|
|
35
|
+
try:
|
|
36
|
+
entries = sorted(root.iterdir(), key=lambda x: (not x.is_dir(), x.name.lower()))
|
|
37
|
+
for entry in entries:
|
|
38
|
+
if entry.name.startswith('.'):
|
|
39
|
+
continue
|
|
40
|
+
rel_path = str(entry.relative_to(workspace_root))
|
|
41
|
+
if entry.is_dir():
|
|
42
|
+
# Count immediate children to show if folder is empty
|
|
43
|
+
try:
|
|
44
|
+
has_children = any(not item.name.startswith('.') for item in entry.iterdir())
|
|
45
|
+
except (PermissionError, OSError):
|
|
46
|
+
has_children = False
|
|
47
|
+
|
|
48
|
+
items.append({
|
|
49
|
+
"type": "folder",
|
|
50
|
+
"name": entry.name,
|
|
51
|
+
"path": rel_path,
|
|
52
|
+
"has_children": has_children,
|
|
53
|
+
# Only recursively load children if not lazy loading
|
|
54
|
+
"children": [] if lazy_load else build_file_tree(entry, workspace_root, lazy_load=False)
|
|
55
|
+
})
|
|
56
|
+
else:
|
|
57
|
+
items.append({
|
|
58
|
+
"type": "file",
|
|
59
|
+
"name": entry.name,
|
|
60
|
+
"path": rel_path,
|
|
61
|
+
"viewable": is_text_file(entry.name)
|
|
62
|
+
})
|
|
63
|
+
except PermissionError:
|
|
64
|
+
pass
|
|
65
|
+
return items
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def load_folder_contents(folder_path: str, workspace_root: Path) -> List[Dict]:
|
|
69
|
+
"""
|
|
70
|
+
Load contents of a specific folder (for lazy loading).
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
folder_path: Relative path to the folder from workspace root
|
|
74
|
+
workspace_root: Root workspace directory
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
List of file/folder items in the specified folder
|
|
78
|
+
"""
|
|
79
|
+
full_path = workspace_root / folder_path
|
|
80
|
+
return build_file_tree(full_path, workspace_root, lazy_load=True)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def render_file_tree(items: List[Dict], colors: Dict, styles: Dict, level: int = 0, parent_path: str = "") -> List:
|
|
84
|
+
"""Render file tree with collapsible folders using CSS classes for theming."""
|
|
85
|
+
components = []
|
|
86
|
+
indent = level * 15 # Scaled up indent
|
|
87
|
+
|
|
88
|
+
for item in items:
|
|
89
|
+
if item["type"] == "folder":
|
|
90
|
+
folder_id = item["path"].replace("/", "_").replace("\\", "_")
|
|
91
|
+
children = item.get("children", [])
|
|
92
|
+
|
|
93
|
+
# Folder header (clickable to expand/collapse)
|
|
94
|
+
components.append(
|
|
95
|
+
html.Div([
|
|
96
|
+
html.Span(
|
|
97
|
+
"▶",
|
|
98
|
+
id={"type": "folder-icon", "path": folder_id},
|
|
99
|
+
className="folder-icon",
|
|
100
|
+
style={
|
|
101
|
+
"marginRight": "5px",
|
|
102
|
+
"fontSize": "10px",
|
|
103
|
+
"transition": "transform 0.15s",
|
|
104
|
+
"display": "inline-block",
|
|
105
|
+
}
|
|
106
|
+
),
|
|
107
|
+
html.Span(item["name"], className="folder-name", style={
|
|
108
|
+
"fontWeight": "500",
|
|
109
|
+
"fontSize": "14px",
|
|
110
|
+
})
|
|
111
|
+
],
|
|
112
|
+
id={"type": "folder-header", "path": folder_id},
|
|
113
|
+
**{"data-realpath": item["path"]}, # Store actual path for lazy loading
|
|
114
|
+
className="folder-header file-tree-item",
|
|
115
|
+
style={
|
|
116
|
+
"display": "flex",
|
|
117
|
+
"alignItems": "center",
|
|
118
|
+
"padding": "5px 10px",
|
|
119
|
+
"paddingLeft": f"{10 + indent}px",
|
|
120
|
+
"cursor": "pointer",
|
|
121
|
+
"userSelect": "none",
|
|
122
|
+
},
|
|
123
|
+
)
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
# Folder children (hidden by default) - always create even if empty
|
|
127
|
+
# Show different content based on whether children are loaded
|
|
128
|
+
has_children = item.get("has_children", True)
|
|
129
|
+
|
|
130
|
+
if children:
|
|
131
|
+
# Children are loaded, render them
|
|
132
|
+
child_content = render_file_tree(children, colors, styles, level + 1, item["path"])
|
|
133
|
+
elif not has_children:
|
|
134
|
+
# Folder is known to be empty
|
|
135
|
+
child_content = [
|
|
136
|
+
html.Div("(empty)", className="file-tree-empty", style={
|
|
137
|
+
"padding": "4px 10px",
|
|
138
|
+
"paddingLeft": f"{25 + (level + 1) * 15}px",
|
|
139
|
+
"fontSize": "12px",
|
|
140
|
+
"fontStyle": "italic",
|
|
141
|
+
})
|
|
142
|
+
]
|
|
143
|
+
else:
|
|
144
|
+
# Children not yet loaded (lazy loading)
|
|
145
|
+
child_content = [
|
|
146
|
+
html.Div("Loading...",
|
|
147
|
+
id={"type": "folder-loading", "path": folder_id},
|
|
148
|
+
className="file-tree-loading",
|
|
149
|
+
style={
|
|
150
|
+
"padding": "4px 10px",
|
|
151
|
+
"paddingLeft": f"{25 + (level + 1) * 15}px",
|
|
152
|
+
"fontSize": "12px",
|
|
153
|
+
"fontStyle": "italic",
|
|
154
|
+
}
|
|
155
|
+
)
|
|
156
|
+
]
|
|
157
|
+
|
|
158
|
+
components.append(
|
|
159
|
+
html.Div(
|
|
160
|
+
child_content,
|
|
161
|
+
id={"type": "folder-children", "path": folder_id},
|
|
162
|
+
style={"display": "none"} # Collapsed by default
|
|
163
|
+
)
|
|
164
|
+
)
|
|
165
|
+
else:
|
|
166
|
+
# File item
|
|
167
|
+
components.append(
|
|
168
|
+
html.Div(
|
|
169
|
+
item["name"],
|
|
170
|
+
id={"type": "file-item", "path": item["path"]},
|
|
171
|
+
className="file-item file-tree-item",
|
|
172
|
+
style={
|
|
173
|
+
"fontSize": "14px",
|
|
174
|
+
"padding": "5px 10px",
|
|
175
|
+
"paddingLeft": f"{25 + indent}px",
|
|
176
|
+
"cursor": "pointer",
|
|
177
|
+
},
|
|
178
|
+
**{"data-viewable": "true" if item["viewable"] else "false"}
|
|
179
|
+
)
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
return components
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def read_file_content(workspace_root: Path, path: str) -> Tuple[str, bool, str]:
|
|
186
|
+
"""Read file content. Returns (content, is_text, error)."""
|
|
187
|
+
full_path = workspace_root / path
|
|
188
|
+
if not full_path.exists() or not full_path.is_file():
|
|
189
|
+
return None, False, "File not found"
|
|
190
|
+
|
|
191
|
+
if is_text_file(path):
|
|
192
|
+
try:
|
|
193
|
+
content = full_path.read_text(encoding="utf-8")
|
|
194
|
+
return content, True, None
|
|
195
|
+
except UnicodeDecodeError:
|
|
196
|
+
return None, False, "Binary file - cannot display"
|
|
197
|
+
except Exception as e:
|
|
198
|
+
return None, False, str(e)
|
|
199
|
+
else:
|
|
200
|
+
return None, False, "Binary file - download to view"
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def get_file_download_data(workspace_root: Path, path: str) -> Tuple[str, str, str]:
|
|
204
|
+
"""Get file data for download. Returns (base64_content, filename, mime_type)."""
|
|
205
|
+
full_path = workspace_root / path
|
|
206
|
+
if not full_path.exists():
|
|
207
|
+
return None, None, None
|
|
208
|
+
|
|
209
|
+
try:
|
|
210
|
+
content = full_path.read_bytes()
|
|
211
|
+
b64 = base64.b64encode(content).decode('utf-8')
|
|
212
|
+
|
|
213
|
+
# Determine MIME type
|
|
214
|
+
ext = full_path.suffix.lower()
|
|
215
|
+
mime_types = {
|
|
216
|
+
".txt": "text/plain", ".py": "text/x-python", ".js": "text/javascript",
|
|
217
|
+
".json": "application/json", ".html": "text/html", ".css": "text/css",
|
|
218
|
+
".md": "text/markdown", ".csv": "text/csv", ".xml": "text/xml",
|
|
219
|
+
".pdf": "application/pdf", ".png": "image/png", ".jpg": "image/jpeg",
|
|
220
|
+
".gif": "image/gif", ".zip": "application/zip",
|
|
221
|
+
}
|
|
222
|
+
mime = mime_types.get(ext, "application/octet-stream")
|
|
223
|
+
|
|
224
|
+
return b64, full_path.name, mime
|
|
225
|
+
except Exception:
|
|
226
|
+
return None, None, None
|
cowork_dash/layout.py
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"""Layout components for DeepAgent Dash."""
|
|
2
|
+
|
|
3
|
+
from dash import html, dcc
|
|
4
|
+
import dash_mantine_components as dmc
|
|
5
|
+
from dash_iconify import DashIconify
|
|
6
|
+
|
|
7
|
+
from .file_utils import build_file_tree, render_file_tree
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def create_layout(workspace_root, app_title, app_subtitle, colors, styles, agent):
|
|
11
|
+
"""
|
|
12
|
+
Create the app layout with current configuration.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
workspace_root: Path to workspace directory
|
|
16
|
+
app_title: Application title
|
|
17
|
+
app_subtitle: Application subtitle
|
|
18
|
+
colors: Color scheme dictionary
|
|
19
|
+
styles: Styles dictionary
|
|
20
|
+
agent: Agent instance (or None)
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
Dash layout component
|
|
24
|
+
"""
|
|
25
|
+
return dmc.MantineProvider(
|
|
26
|
+
id="mantine-provider",
|
|
27
|
+
forceColorScheme="light",
|
|
28
|
+
children=[
|
|
29
|
+
# State stores
|
|
30
|
+
dcc.Store(id="chat-history", data=[{
|
|
31
|
+
"role": "assistant",
|
|
32
|
+
"content": f"""This is your AI-powered workspace. I can help you write code, analyze files, create visualizations, and more.
|
|
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!"""
|
|
40
|
+
}]),
|
|
41
|
+
dcc.Store(id="pending-message", data=None),
|
|
42
|
+
dcc.Store(id="expanded-folders", data=[]),
|
|
43
|
+
dcc.Store(id="file-to-view", data=None),
|
|
44
|
+
dcc.Store(id="file-click-tracker", data={}),
|
|
45
|
+
dcc.Store(id="theme-store", data="light", storage_type="local"),
|
|
46
|
+
dcc.Download(id="file-download"),
|
|
47
|
+
|
|
48
|
+
# Interval for polling agent updates (disabled by default)
|
|
49
|
+
dcc.Interval(id="poll-interval", interval=250, disabled=True),
|
|
50
|
+
|
|
51
|
+
# File viewer modal
|
|
52
|
+
dmc.Modal(
|
|
53
|
+
id="file-modal",
|
|
54
|
+
title="",
|
|
55
|
+
size="xl",
|
|
56
|
+
children=[
|
|
57
|
+
html.Div(id="modal-content"),
|
|
58
|
+
html.Div([
|
|
59
|
+
dmc.Button(
|
|
60
|
+
"Download",
|
|
61
|
+
id="modal-download-btn",
|
|
62
|
+
variant="outline",
|
|
63
|
+
color="blue",
|
|
64
|
+
style={"marginTop": "16px"}
|
|
65
|
+
)
|
|
66
|
+
], style={"textAlign": "right"})
|
|
67
|
+
],
|
|
68
|
+
opened=False,
|
|
69
|
+
),
|
|
70
|
+
|
|
71
|
+
html.Div([
|
|
72
|
+
# Compact Header
|
|
73
|
+
html.Header([
|
|
74
|
+
html.Div([
|
|
75
|
+
html.Div([
|
|
76
|
+
html.H1(app_title or "DeepAgent Dash", id="app-title", style={
|
|
77
|
+
"fontSize": "17px", "fontWeight": "600", "margin": "0",
|
|
78
|
+
}),
|
|
79
|
+
html.Span(app_subtitle or "AI-Powered Workspace", id="app-subtitle", style={
|
|
80
|
+
"fontSize": "14px", "color": "var(--mantine-color-dimmed)", "marginLeft": "10px",
|
|
81
|
+
})
|
|
82
|
+
], style={"display": "flex", "alignItems": "baseline"}),
|
|
83
|
+
html.Div([
|
|
84
|
+
dmc.ActionIcon(
|
|
85
|
+
DashIconify(icon="radix-icons:moon", width=18),
|
|
86
|
+
id="theme-toggle-btn",
|
|
87
|
+
variant="subtle",
|
|
88
|
+
color="gray",
|
|
89
|
+
size="md",
|
|
90
|
+
radius="sm",
|
|
91
|
+
style={"marginRight": "8px"},
|
|
92
|
+
),
|
|
93
|
+
html.Div(style={
|
|
94
|
+
"width": "8px", "height": "8px",
|
|
95
|
+
"borderRadius": "50%",
|
|
96
|
+
"background": "var(--mantine-color-green-6)" if agent else "var(--mantine-color-red-6)",
|
|
97
|
+
"marginRight": "5px",
|
|
98
|
+
}, id="agent-status-indicator"),
|
|
99
|
+
dmc.Text("Ready" if agent else "No Agent", size="sm", c="dimmed", id="agent-status-text")
|
|
100
|
+
], style={"display": "flex", "alignItems": "center"})
|
|
101
|
+
], style={
|
|
102
|
+
"display": "flex", "justifyContent": "space-between",
|
|
103
|
+
"alignItems": "center", "maxWidth": "1600px",
|
|
104
|
+
"margin": "0 auto", "padding": "0 12px",
|
|
105
|
+
})
|
|
106
|
+
], id="header", style={
|
|
107
|
+
"background": "var(--mantine-color-body)",
|
|
108
|
+
"borderBottom": "1px solid var(--mantine-color-default-border)",
|
|
109
|
+
"padding": "8px 0",
|
|
110
|
+
}),
|
|
111
|
+
|
|
112
|
+
# Main content
|
|
113
|
+
html.Main([
|
|
114
|
+
# Chat panel (no header)
|
|
115
|
+
html.Div([
|
|
116
|
+
# Messages
|
|
117
|
+
html.Div(id="chat-messages", style={
|
|
118
|
+
"flex": "1", "overflowY": "auto", "padding": "15px",
|
|
119
|
+
"display": "flex", "flexDirection": "column", "gap": "10px",
|
|
120
|
+
}),
|
|
121
|
+
|
|
122
|
+
# Compact Input
|
|
123
|
+
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
|
+
dmc.TextInput(
|
|
136
|
+
id="chat-input",
|
|
137
|
+
placeholder="Type a message...",
|
|
138
|
+
className="chat-input",
|
|
139
|
+
style={"flex": "1"},
|
|
140
|
+
size="md",
|
|
141
|
+
),
|
|
142
|
+
dmc.Button("Send", id="send-btn", className="send-btn", size="md"),
|
|
143
|
+
], id="chat-input-area", style={
|
|
144
|
+
"display": "flex", "gap": "8px", "padding": "10px 15px",
|
|
145
|
+
"borderTop": "1px solid var(--mantine-color-default-border)",
|
|
146
|
+
"background": "var(--mantine-color-body)",
|
|
147
|
+
}),
|
|
148
|
+
dmc.Text(id="upload-status", size="sm", c="dimmed", style={
|
|
149
|
+
"padding": "0 15px 8px",
|
|
150
|
+
}),
|
|
151
|
+
], id="chat-panel", style={
|
|
152
|
+
"flex": "1", "display": "flex", "flexDirection": "column",
|
|
153
|
+
"background": "var(--mantine-color-body)", "minWidth": "0",
|
|
154
|
+
}),
|
|
155
|
+
|
|
156
|
+
# Resize handle
|
|
157
|
+
html.Div(id="resize-handle", className="resize-handle", style={
|
|
158
|
+
"width": "3px",
|
|
159
|
+
"cursor": "col-resize",
|
|
160
|
+
"background": "transparent",
|
|
161
|
+
"flexShrink": "0",
|
|
162
|
+
}),
|
|
163
|
+
|
|
164
|
+
# Sidebar (Files/Canvas toggle)
|
|
165
|
+
html.Div([
|
|
166
|
+
# Compact header with toggle
|
|
167
|
+
html.Div([
|
|
168
|
+
dmc.SegmentedControl(
|
|
169
|
+
id="sidebar-view-toggle",
|
|
170
|
+
data=[
|
|
171
|
+
{"value": "files", "label": "Files"},
|
|
172
|
+
{"value": "canvas", "label": "Canvas"},
|
|
173
|
+
],
|
|
174
|
+
value="files",
|
|
175
|
+
size="sm",
|
|
176
|
+
),
|
|
177
|
+
dmc.Group([
|
|
178
|
+
dmc.ActionIcon(
|
|
179
|
+
DashIconify(icon="mdi:console", width=18),
|
|
180
|
+
id="open-terminal-btn",
|
|
181
|
+
variant="default",
|
|
182
|
+
size="md",
|
|
183
|
+
),
|
|
184
|
+
dmc.ActionIcon(
|
|
185
|
+
DashIconify(icon="mdi:refresh", width=18),
|
|
186
|
+
id="refresh-btn",
|
|
187
|
+
variant="default",
|
|
188
|
+
size="md",
|
|
189
|
+
),
|
|
190
|
+
], id="files-actions", gap=5)
|
|
191
|
+
], id="sidebar-header", style={
|
|
192
|
+
"display": "flex", "justifyContent": "space-between",
|
|
193
|
+
"alignItems": "center", "padding": "8px 12px",
|
|
194
|
+
"borderBottom": "1px solid var(--mantine-color-default-border)",
|
|
195
|
+
}),
|
|
196
|
+
|
|
197
|
+
# Files view
|
|
198
|
+
html.Div([
|
|
199
|
+
html.Div(
|
|
200
|
+
id="file-tree",
|
|
201
|
+
children=render_file_tree(build_file_tree(workspace_root, workspace_root), colors, styles),
|
|
202
|
+
style={
|
|
203
|
+
"flex": "1",
|
|
204
|
+
"overflowY": "auto",
|
|
205
|
+
"minHeight": "0",
|
|
206
|
+
}
|
|
207
|
+
),
|
|
208
|
+
], id="files-view", style={
|
|
209
|
+
"flex": "1",
|
|
210
|
+
"minHeight": "0",
|
|
211
|
+
"display": "flex",
|
|
212
|
+
"flexDirection": "column",
|
|
213
|
+
}),
|
|
214
|
+
|
|
215
|
+
# Canvas view (hidden by default)
|
|
216
|
+
html.Div([
|
|
217
|
+
html.Div(id="canvas-content", style={
|
|
218
|
+
"flex": "1",
|
|
219
|
+
"minHeight": "0",
|
|
220
|
+
"overflowY": "auto",
|
|
221
|
+
"padding": "15px",
|
|
222
|
+
"background": "var(--mantine-color-body)",
|
|
223
|
+
}),
|
|
224
|
+
# Canvas action button
|
|
225
|
+
dmc.Group([
|
|
226
|
+
dmc.Button("Clear", id="clear-canvas-btn", size="sm", color="red", variant="light"),
|
|
227
|
+
], id="canvas-actions", justify="center", style={
|
|
228
|
+
"padding": "8px 15px",
|
|
229
|
+
"borderTop": "1px solid var(--mantine-color-default-border)",
|
|
230
|
+
"background": "var(--mantine-color-body)",
|
|
231
|
+
})
|
|
232
|
+
], id="canvas-view", style={
|
|
233
|
+
"flex": "1",
|
|
234
|
+
"minHeight": "0",
|
|
235
|
+
"display": "none",
|
|
236
|
+
"flexDirection": "column",
|
|
237
|
+
"overflow": "hidden"
|
|
238
|
+
}),
|
|
239
|
+
], id="sidebar-panel", style={
|
|
240
|
+
"flex": "1",
|
|
241
|
+
"minWidth": "0",
|
|
242
|
+
"minHeight": "0",
|
|
243
|
+
"display": "flex",
|
|
244
|
+
"flexDirection": "column",
|
|
245
|
+
"background": "var(--mantine-color-body)",
|
|
246
|
+
"borderLeft": "1px solid var(--mantine-color-default-border)",
|
|
247
|
+
}),
|
|
248
|
+
], id="main-container", style={"display": "flex", "flex": "1", "overflow": "hidden"}),
|
|
249
|
+
], id="app-container", style={"display": "flex", "flexDirection": "column", "height": "100vh"})
|
|
250
|
+
])
|