flock-core 0.4.0b42__py3-none-any.whl → 0.4.0b44__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.
Potentially problematic release.
This version of flock-core might be problematic. Click here for more details.
- flock/core/api/__init__.py +1 -2
- flock/core/api/endpoints.py +149 -217
- flock/core/api/main.py +134 -653
- flock/core/api/service.py +214 -0
- flock/core/flock.py +192 -133
- flock/webapp/app/api/agent_management.py +135 -164
- flock/webapp/app/api/execution.py +76 -85
- flock/webapp/app/api/flock_management.py +60 -33
- flock/webapp/app/chat.py +233 -0
- flock/webapp/app/config.py +6 -3
- flock/webapp/app/dependencies.py +95 -0
- flock/webapp/app/main.py +320 -906
- flock/webapp/app/services/flock_service.py +183 -161
- flock/webapp/run.py +178 -97
- flock/webapp/static/css/chat.css +227 -0
- flock/webapp/static/css/components.css +167 -0
- flock/webapp/static/css/header.css +39 -0
- flock/webapp/static/css/layout.css +46 -0
- flock/webapp/static/css/sidebar.css +127 -0
- flock/webapp/templates/base.html +6 -1
- flock/webapp/templates/chat.html +60 -0
- flock/webapp/templates/chat_settings.html +20 -0
- flock/webapp/templates/flock_editor.html +1 -1
- flock/webapp/templates/partials/_agent_detail_form.html +4 -4
- flock/webapp/templates/partials/_agent_list.html +2 -2
- flock/webapp/templates/partials/_agent_manager_view.html +3 -4
- flock/webapp/templates/partials/_chat_container.html +9 -0
- flock/webapp/templates/partials/_chat_messages.html +13 -0
- flock/webapp/templates/partials/_chat_settings_form.html +65 -0
- flock/webapp/templates/partials/_execution_form.html +2 -2
- flock/webapp/templates/partials/_execution_view_container.html +1 -1
- flock/webapp/templates/partials/_flock_properties_form.html +2 -2
- flock/webapp/templates/partials/_registry_viewer_content.html +3 -3
- flock/webapp/templates/partials/_sidebar.html +17 -1
- flock/webapp/templates/registry_viewer.html +3 -3
- {flock_core-0.4.0b42.dist-info → flock_core-0.4.0b44.dist-info}/METADATA +1 -1
- {flock_core-0.4.0b42.dist-info → flock_core-0.4.0b44.dist-info}/RECORD +40 -29
- flock/webapp/static/css/custom.css +0 -612
- flock/webapp/templates/partials/_agent_manager_view_old.html +0 -19
- {flock_core-0.4.0b42.dist-info → flock_core-0.4.0b44.dist-info}/WHEEL +0 -0
- {flock_core-0.4.0b42.dist-info → flock_core-0.4.0b44.dist-info}/entry_points.txt +0 -0
- {flock_core-0.4.0b42.dist-info → flock_core-0.4.0b44.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/* ========================================================================
|
|
2
|
+
components.css — Reusable component / utility styles
|
|
3
|
+
======================================================================== */
|
|
4
|
+
|
|
5
|
+
/* ---------- Message container (toast notifications) ---------- */
|
|
6
|
+
.message-container {
|
|
7
|
+
position: fixed;
|
|
8
|
+
bottom: 3rem;
|
|
9
|
+
right: 1rem;
|
|
10
|
+
z-index: 1002;
|
|
11
|
+
max-width: 400px;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.message-container > div {
|
|
15
|
+
margin-top: 0.5rem;
|
|
16
|
+
box-shadow: var(--pico-card-box-shadow);
|
|
17
|
+
display: flex;
|
|
18
|
+
justify-content: space-between;
|
|
19
|
+
align-items: center;
|
|
20
|
+
padding: 0.75rem;
|
|
21
|
+
border-radius: var(--pico-border-radius);
|
|
22
|
+
font-weight: 500;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.message-container .success {
|
|
26
|
+
background-color: var(--flock-success-color, var(--pico-ins-color));
|
|
27
|
+
color: var(--pico-primary-inverse);
|
|
28
|
+
border: 1px solid var(--flock-success-color, var(--pico-ins-color));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.message-container .error {
|
|
32
|
+
background-color: var(--flock-error-color, var(--pico-del-color));
|
|
33
|
+
color: var(--pico-primary-inverse);
|
|
34
|
+
border: 1px solid var(--flock-error-color, var(--pico-del-color));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.message-container .close {
|
|
38
|
+
background: none;
|
|
39
|
+
border: none;
|
|
40
|
+
color: inherit;
|
|
41
|
+
font-size: 1.2rem;
|
|
42
|
+
cursor: pointer;
|
|
43
|
+
padding: 0 0.5rem;
|
|
44
|
+
margin-left: 1rem;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* ---------- HTMX loading indicator ---------- */
|
|
48
|
+
.htmx-indicator { display: none; opacity: 0; transition: opacity 0.3s ease-in-out; }
|
|
49
|
+
.htmx-request .htmx-indicator { display: inline-block; opacity: 1; margin-left: 0.5em; }
|
|
50
|
+
.htmx-request.htmx-indicator { display: inline-block; opacity: 1; }
|
|
51
|
+
|
|
52
|
+
/* ---------- Form & validation helpers ---------- */
|
|
53
|
+
.field-error { color: var(--flock-error-color, var(--pico-del-color)); font-size: var(--pico-font-size-small); margin-top: -0.5rem; margin-bottom: 0.5rem; }
|
|
54
|
+
|
|
55
|
+
/* ---------- Main content headings ---------- */
|
|
56
|
+
main.main-content h1, main.main-content h2, main.main-content h3 {
|
|
57
|
+
color: var(--pico-color);
|
|
58
|
+
font-weight: 600;
|
|
59
|
+
line-height: 1.2;
|
|
60
|
+
margin-top: 0;
|
|
61
|
+
}
|
|
62
|
+
main.main-content h1 { font-size: 2rem; margin-bottom: 1.5rem; }
|
|
63
|
+
main.main-content h2 { font-size: 1.75rem; margin-bottom: 1.25rem; }
|
|
64
|
+
main.main-content h3 { font-size: 1.5rem; margin-bottom: 1rem; }
|
|
65
|
+
|
|
66
|
+
.panel-title { margin-top: 0; margin-bottom: 1.25rem; }
|
|
67
|
+
|
|
68
|
+
/* ---------- Two-pane flex container (Execution, Agents, etc.) ---------- */
|
|
69
|
+
.two-pane-flex-container {
|
|
70
|
+
display: flex;
|
|
71
|
+
gap: 1.5rem;
|
|
72
|
+
border: 1px solid var(--pico-muted-border-color);
|
|
73
|
+
background-color: var(--pico-card-background-color);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.left-pane { flex: 1; min-width: 300px; display: flex; flex-direction: column; }
|
|
77
|
+
.left-pane > .scrollable-content { flex-grow: 1; }
|
|
78
|
+
|
|
79
|
+
.right-pane-framed { flex: 2; border-left: 1px solid var(--pico-muted-border-color); padding-left: 1.5rem; }
|
|
80
|
+
.right-pane-framed > header { padding-bottom: 0.75rem; border-bottom: 1px solid var(--pico-muted-border-color); margin-bottom: 1rem; }
|
|
81
|
+
.right-pane-framed > header > h5 { margin-bottom: 0; font-size: 1.1rem; }
|
|
82
|
+
|
|
83
|
+
/* ---------- Item-list utility ---------- */
|
|
84
|
+
.item-list-container { flex-grow: 1; }
|
|
85
|
+
ul.item-list {
|
|
86
|
+
list-style: none;
|
|
87
|
+
padding: 0;
|
|
88
|
+
margin: 0;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* Guard against ::marker still appearing in some browsers */
|
|
92
|
+
ul.item-list li::marker { content: none; }
|
|
93
|
+
|
|
94
|
+
ul.item-list li { padding: 0.75rem 1rem; border-bottom: 1px solid var(--pico-muted-border-color); cursor: pointer; transition: background-color 0.15s ease-in-out, color 0.15s ease-in-out; }
|
|
95
|
+
ul.item-list li:last-child { border-bottom: none; }
|
|
96
|
+
ul.item-list li:hover, ul.item-list li.selected-item { background-color: var(--pico-primary-focus); color: var(--pico-primary-inverse); }
|
|
97
|
+
|
|
98
|
+
/* ---------- Misc ---------- */
|
|
99
|
+
progress { border-radius: 10px; height: 8px; }
|
|
100
|
+
progress:indeterminate { background: rgba(var(--pico-primary-rgb, 0, 0, 0), 0.2); }
|
|
101
|
+
|
|
102
|
+
/* ---------- Tool checklist (Agent form) ---------- */
|
|
103
|
+
.tool-checklist {
|
|
104
|
+
max-height: 150px;
|
|
105
|
+
overflow-y: auto;
|
|
106
|
+
border: 1px solid var(--pico-muted-border-color);
|
|
107
|
+
padding: 0.5rem;
|
|
108
|
+
margin-bottom: 0.75rem;
|
|
109
|
+
border-radius: var(--pico-border-radius);
|
|
110
|
+
background-color: var(--pico-form-element-background-color);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.tool-checklist label { display: block; margin-bottom: 0.25rem; font-weight: normal; }
|
|
114
|
+
.tool-checklist input[type="checkbox"] { margin-right: 0.5rem; }
|
|
115
|
+
.tool-checklist label small { color: var(--pico-muted-color); }
|
|
116
|
+
|
|
117
|
+
/* ---------- Execution / Results display ---------- */
|
|
118
|
+
#results-display {
|
|
119
|
+
background-color: var(--pico-code-background-color);
|
|
120
|
+
color: var(--pico-code-color);
|
|
121
|
+
padding: 1rem;
|
|
122
|
+
border-radius: var(--pico-border-radius);
|
|
123
|
+
overflow-x: auto;
|
|
124
|
+
margin-top: 1rem;
|
|
125
|
+
min-height: 100px;
|
|
126
|
+
border: 1px solid var(--pico-muted-border-color);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
#results-display pre { margin: 0; white-space: pre-wrap; word-break: break-all; }
|
|
130
|
+
|
|
131
|
+
#results-display .structured-table {
|
|
132
|
+
border: 1px solid var(--pico-muted-border-color);
|
|
133
|
+
font-size: 0.9em;
|
|
134
|
+
width: 100%;
|
|
135
|
+
margin-bottom: 0.75rem !important;
|
|
136
|
+
border-collapse: collapse;
|
|
137
|
+
}
|
|
138
|
+
#results-display .structured-table td,
|
|
139
|
+
#results-display .structured-table th {
|
|
140
|
+
padding: 0.4em 0.6em !important;
|
|
141
|
+
vertical-align: top;
|
|
142
|
+
border-bottom: 1px solid var(--pico-muted-border-color);
|
|
143
|
+
}
|
|
144
|
+
#results-display .structured-table tr:last-child td { border-bottom: none; }
|
|
145
|
+
#results-display .structured-table td[style*="font-weight: bold"] {
|
|
146
|
+
color: var(--pico-secondary);
|
|
147
|
+
min-width: 120px;
|
|
148
|
+
max-width: 250px;
|
|
149
|
+
word-break: break-word;
|
|
150
|
+
border-right: 1px solid var(--pico-muted-border-color);
|
|
151
|
+
}
|
|
152
|
+
#results-display .structured-table ul { margin-left: 1em; padding-left: 1em; }
|
|
153
|
+
|
|
154
|
+
/* ---------- Registry viewer tweaks ---------- */
|
|
155
|
+
nav ul[role="group"] { margin-bottom: 1rem; }
|
|
156
|
+
nav ul[role="group"] li button { width: 100%; }
|
|
157
|
+
#registry-table-container table { margin-top: 1rem; }
|
|
158
|
+
|
|
159
|
+
/* ---------- Progress elements ---------- */
|
|
160
|
+
.sidebar progress { width: 80%; margin: 1rem auto; display: block; }
|
|
161
|
+
progress { border-radius: 10px; height: 8px; }
|
|
162
|
+
progress:indeterminate { background: rgba(var(--pico-primary-rgb, 0, 0, 0), 0.2); }
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/* ========================================================================
|
|
2
|
+
header.css — Top header bar styles
|
|
3
|
+
======================================================================== */
|
|
4
|
+
|
|
5
|
+
header.top-header {
|
|
6
|
+
grid-area: header;
|
|
7
|
+
border-bottom: 1px solid var(--pico-muted-border-color);
|
|
8
|
+
padding: 0.7rem 1.5rem;
|
|
9
|
+
background-color: var(--pico-card-sectioning-background-color);
|
|
10
|
+
display: flex;
|
|
11
|
+
justify-content: space-between;
|
|
12
|
+
align-items: center;
|
|
13
|
+
position: fixed;
|
|
14
|
+
top: 0;
|
|
15
|
+
left: 0;
|
|
16
|
+
right: 0;
|
|
17
|
+
z-index: 1001;
|
|
18
|
+
box-shadow: var(--pico-card-box-shadow, 0 2px 10px rgba(0, 0, 0, 0.2));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
header.top-header strong {
|
|
22
|
+
color: var(--pico-color);
|
|
23
|
+
font-weight: 600;
|
|
24
|
+
font-size: 1.2rem;
|
|
25
|
+
letter-spacing: 0.02em;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
header.top-header small {
|
|
29
|
+
color: var(--pico-muted-color);
|
|
30
|
+
font-size: 0.85rem;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
header.top-header code {
|
|
34
|
+
background: var(--pico-code-background-color);
|
|
35
|
+
color: var(--pico-code-color);
|
|
36
|
+
padding: 0.2em 0.5em;
|
|
37
|
+
border-radius: 4px;
|
|
38
|
+
font-size: 0.85rem;
|
|
39
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/* ========================================================================
|
|
2
|
+
layout.css — Global layout & structure styles for Flock web UI
|
|
3
|
+
(extracted from legacy custom.css)
|
|
4
|
+
======================================================================== */
|
|
5
|
+
|
|
6
|
+
/* ---------- Global grid layout ---------- */
|
|
7
|
+
body {
|
|
8
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;
|
|
9
|
+
display: grid;
|
|
10
|
+
/* Sidebar column (300px) + main flexible column */
|
|
11
|
+
grid-template-columns: 300px 1fr;
|
|
12
|
+
/* Header (auto height) + main (flex) + footer (auto) rows */
|
|
13
|
+
grid-template-rows: auto 1fr auto;
|
|
14
|
+
grid-template-areas:
|
|
15
|
+
"header header"
|
|
16
|
+
"sidebar main"
|
|
17
|
+
"footer footer";
|
|
18
|
+
min-height: 100vh;
|
|
19
|
+
margin: 0;
|
|
20
|
+
background-color: var(--pico-background-color);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* ---------- Main content area ---------- */
|
|
24
|
+
main.main-content {
|
|
25
|
+
grid-area: main;
|
|
26
|
+
padding: 1.5rem;
|
|
27
|
+
overflow-y: auto;
|
|
28
|
+
/* account for fixed header height so content starts below */
|
|
29
|
+
margin-top: 3.5rem;
|
|
30
|
+
background-color: var(--pico-background-color);
|
|
31
|
+
/* ensure footer does not overlap scrolling content */
|
|
32
|
+
padding-bottom: 50px;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/* ---------- Footer ---------- */
|
|
36
|
+
footer.main-footer {
|
|
37
|
+
position: fixed;
|
|
38
|
+
left: 0;
|
|
39
|
+
bottom: 0;
|
|
40
|
+
width: 100%;
|
|
41
|
+
background-color: var(--pico-card-background-color);
|
|
42
|
+
border-top: 1px solid var(--pico-muted-border-color);
|
|
43
|
+
padding: 0.5rem 1rem;
|
|
44
|
+
text-align: center;
|
|
45
|
+
z-index: 1000;
|
|
46
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/* ========================================================================
|
|
2
|
+
sidebar.css — Left navigation sidebar styles
|
|
3
|
+
======================================================================== */
|
|
4
|
+
|
|
5
|
+
aside.sidebar {
|
|
6
|
+
grid-area: sidebar;
|
|
7
|
+
background: var(--flock-sidebar-background, var(--pico-card-background-color));
|
|
8
|
+
padding: 1.5rem 0;
|
|
9
|
+
border-right: 1px solid var(--pico-muted-border-color);
|
|
10
|
+
margin-top: 3.2rem; /* header height compensation */
|
|
11
|
+
height: calc(100vh - 3.5rem - 2.1rem); /* header + footer */
|
|
12
|
+
position: fixed;
|
|
13
|
+
left: 0;
|
|
14
|
+
bottom: 2.1rem; /* footer height */
|
|
15
|
+
width: 300px;
|
|
16
|
+
z-index: 1000;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.sidebar nav h5 {
|
|
20
|
+
margin-bottom: 0.9rem;
|
|
21
|
+
padding-left: 1.5rem;
|
|
22
|
+
color: var(--pico-color);
|
|
23
|
+
font-size: 0.85em;
|
|
24
|
+
text-transform: uppercase;
|
|
25
|
+
letter-spacing: 0.08em;
|
|
26
|
+
font-weight: 600;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.sidebar nav h5:first-of-type {
|
|
30
|
+
margin-top: 0;
|
|
31
|
+
margin-bottom: 0.9rem;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.sidebar nav ul {
|
|
35
|
+
padding-left: 0.5rem;
|
|
36
|
+
padding-right: 0.5rem;
|
|
37
|
+
list-style: none;
|
|
38
|
+
margin-bottom: 1.5rem;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.sidebar nav li {
|
|
42
|
+
margin-bottom: 0.2rem;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.sidebar hr {
|
|
46
|
+
margin: 1.25rem 1.5rem;
|
|
47
|
+
border-color: var(--pico-muted-border-color);
|
|
48
|
+
opacity: 0.5;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* Links & buttons */
|
|
52
|
+
.sidebar nav a,
|
|
53
|
+
.sidebar nav button {
|
|
54
|
+
display: block;
|
|
55
|
+
width: 100%;
|
|
56
|
+
text-align: left;
|
|
57
|
+
margin-bottom: 0;
|
|
58
|
+
padding: 0.7rem 1rem;
|
|
59
|
+
border: none;
|
|
60
|
+
background-color: transparent;
|
|
61
|
+
color: var(--pico-button-base-color);
|
|
62
|
+
text-decoration: none;
|
|
63
|
+
border-radius: 8px;
|
|
64
|
+
font-size: 0.9em;
|
|
65
|
+
cursor: pointer;
|
|
66
|
+
transition: all 0.2s ease-in-out;
|
|
67
|
+
font-weight: 500;
|
|
68
|
+
white-space: nowrap;
|
|
69
|
+
overflow: hidden;
|
|
70
|
+
text-overflow: ellipsis;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.sidebar nav a i,
|
|
74
|
+
.sidebar nav button i {
|
|
75
|
+
width: 24px;
|
|
76
|
+
margin-right: 8px;
|
|
77
|
+
text-align: center;
|
|
78
|
+
color: var(--pico-h2-color);
|
|
79
|
+
transition: all 0.2s ease-in-out;
|
|
80
|
+
flex-shrink: 0;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.sidebar nav a:hover i,
|
|
84
|
+
.sidebar nav button:hover i,
|
|
85
|
+
.sidebar nav a.active-nav i,
|
|
86
|
+
.sidebar nav button.active-nav i {
|
|
87
|
+
color: var(--pico-primary-inverse);
|
|
88
|
+
transform: scale(1.1);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* Button variations / states */
|
|
92
|
+
.sidebar nav button.outline,
|
|
93
|
+
.sidebar nav button.contrast {
|
|
94
|
+
border: none;
|
|
95
|
+
background-color: transparent;
|
|
96
|
+
color: var(--pico-muted-color);
|
|
97
|
+
box-shadow: none;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.sidebar nav button:focus {
|
|
101
|
+
box-shadow: 0 0 0 3px var(--pico-primary-focus);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.sidebar nav a:hover,
|
|
105
|
+
.sidebar nav button:hover {
|
|
106
|
+
background-color: var(--pico-primary-hover-background, var(--pico-primary-focus));
|
|
107
|
+
color: var(--pico-primary-inverse);
|
|
108
|
+
transform: translateX(3px);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* Active navigation */
|
|
112
|
+
.sidebar nav a[aria-current="page"],
|
|
113
|
+
.sidebar nav button[aria-current="page"],
|
|
114
|
+
.sidebar nav a.active-nav,
|
|
115
|
+
.sidebar nav button.active-nav {
|
|
116
|
+
background-color: var(--pico-primary);
|
|
117
|
+
color: var(--pico-primary-inverse);
|
|
118
|
+
font-weight: 600;
|
|
119
|
+
box-shadow: var(--pico-card-box-shadow);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/* Progress bar inside sidebar (used as loading indicator) */
|
|
123
|
+
.sidebar progress {
|
|
124
|
+
width: 80%;
|
|
125
|
+
margin: 1rem auto;
|
|
126
|
+
display: block;
|
|
127
|
+
}
|
flock/webapp/templates/base.html
CHANGED
|
@@ -6,7 +6,12 @@
|
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
7
|
<title>{% block title %}Flock UI{% endblock %}</title>
|
|
8
8
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css" />
|
|
9
|
-
|
|
9
|
+
<!-- Split modular CSS files -->
|
|
10
|
+
<link rel="stylesheet" href="/static/css/layout.css">
|
|
11
|
+
<link rel="stylesheet" href="/static/css/header.css">
|
|
12
|
+
<link rel="stylesheet" href="/static/css/sidebar.css">
|
|
13
|
+
<link rel="stylesheet" href="/static/css/components.css">
|
|
14
|
+
<link rel="stylesheet" href="/static/css/chat.css">
|
|
10
15
|
<!-- Font Awesome for icons -->
|
|
11
16
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
|
|
12
17
|
{# Inject Theme CSS Variables #}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en" data-theme="dark">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Flock Chat</title>
|
|
7
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css" />
|
|
8
|
+
<link rel="stylesheet" href="/static/css/chat.css">
|
|
9
|
+
{# Inject active theme variables #}
|
|
10
|
+
{% if theme_css %}
|
|
11
|
+
<style>
|
|
12
|
+
/* Start Theme CSS */
|
|
13
|
+
/* stylelint-disable */
|
|
14
|
+
{{ theme_css | safe }}
|
|
15
|
+
/* stylelint-enable */
|
|
16
|
+
/* End Theme CSS */
|
|
17
|
+
</style>
|
|
18
|
+
{% endif %}
|
|
19
|
+
<script src="https://unpkg.com/htmx.org@1.9.10" integrity="sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC" crossorigin="anonymous"></script>
|
|
20
|
+
</head>
|
|
21
|
+
<body class="chat-page">
|
|
22
|
+
<div id="chat-container">
|
|
23
|
+
<div class="chat-header" style="justify-content: space-between;">
|
|
24
|
+
<hgroup>
|
|
25
|
+
<h2>Flock Chat</h2>
|
|
26
|
+
<h3>{{ chat_subtitle }}</h3>
|
|
27
|
+
</hgroup>
|
|
28
|
+
<button class="secondary outline" hx-get="/chat/htmx/settings-form" hx-target="#chat-content-area" hx-swap="innerHTML" style="min-width:auto;">Settings</button>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<div id="chat-content-area">
|
|
32
|
+
<div id="chat-log" hx-get="/chat/messages" hx-trigger="load" hx-swap="innerHTML">
|
|
33
|
+
<p><em>Loading chat…</em></p>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<form id="chat-form" hx-post="/chat/send" hx-target="#chat-log" hx-swap="innerHTML" hx-on::after-request="this.reset()">
|
|
37
|
+
<input type="text" name="message" placeholder="Type a message…" required autofocus>
|
|
38
|
+
<button type="submit">Send</button>
|
|
39
|
+
</form>
|
|
40
|
+
</div>
|
|
41
|
+
|
|
42
|
+
<div class="chat-footer">
|
|
43
|
+
<small>Built with FastAPI • HTMX • Pico.css – Theme: {{ active_theme_name | default('default') }}</small>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<script>
|
|
48
|
+
(function() {
|
|
49
|
+
const log = document.getElementById('chat-log');
|
|
50
|
+
function scrollBottom() {
|
|
51
|
+
log.scrollTop = log.scrollHeight;
|
|
52
|
+
}
|
|
53
|
+
document.addEventListener('htmx:afterSwap', e => {
|
|
54
|
+
if (e.detail.target.id === 'chat-log') scrollBottom();
|
|
55
|
+
});
|
|
56
|
+
window.addEventListener('load', scrollBottom);
|
|
57
|
+
})();
|
|
58
|
+
</script>
|
|
59
|
+
</body>
|
|
60
|
+
</html>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en" data-theme="dark">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Chat Settings</title>
|
|
7
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css" />
|
|
8
|
+
<link rel="stylesheet" href="/static/css/chat.css">
|
|
9
|
+
<link rel="stylesheet" href="/static/css/header.css">
|
|
10
|
+
{# Active theme CSS #}
|
|
11
|
+
{% if theme_css %}<style>{{ theme_css | safe }}</style>{% endif %}
|
|
12
|
+
<script src="https://unpkg.com/htmx.org@1.9.10" crossorigin="anonymous"></script>
|
|
13
|
+
</head>
|
|
14
|
+
<body class="chat-page">
|
|
15
|
+
<main style="max-width: 700px; margin: 2rem auto;">
|
|
16
|
+
{% include 'partials/_chat_settings_form.html' %}
|
|
17
|
+
<p style="text-align:center; margin-top: 1rem;"><a href="/chat">← Back to Chat</a></p>
|
|
18
|
+
</main>
|
|
19
|
+
</body>
|
|
20
|
+
</html>
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
{# Main content area is now loaded dynamically based on sidebar clicks #}
|
|
7
7
|
{# We can load the properties form by default when this page is hit #}
|
|
8
8
|
<div id="editor-main-content"
|
|
9
|
-
hx-get="/api/
|
|
9
|
+
hx-get="/ui/api/flock/htmx/flock-properties-form"
|
|
10
10
|
hx-trigger="load"
|
|
11
11
|
hx-swap="innerHTML">
|
|
12
12
|
<article>
|
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
{% endif %}
|
|
15
15
|
|
|
16
16
|
<form {% if is_new %}
|
|
17
|
-
hx-post="/api/
|
|
17
|
+
hx-post="/api/flock/htmx/agents"
|
|
18
18
|
{% else %}
|
|
19
|
-
hx-put="/api/
|
|
19
|
+
hx-put="/api/flock/htmx/agents/{{ agent.name if agent else '' }}"
|
|
20
20
|
{% endif %}
|
|
21
21
|
hx-target="#agent-detail-form-content"
|
|
22
22
|
hx-swap="innerHTML"
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
</button>
|
|
77
77
|
{% if not is_new and agent %}
|
|
78
78
|
<button type="button" role="button" class="secondary outline"
|
|
79
|
-
hx-delete="/api/
|
|
79
|
+
hx-delete="/api/flock/htmx/agents/{{ agent.name }}"
|
|
80
80
|
hx-target="#agent-detail-form-content"
|
|
81
81
|
hx-confirm="Are you sure you want to delete agent '{{ agent.name }}'?"
|
|
82
82
|
hx-indicator="#agent-detail-loading-indicator">
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
</button>
|
|
85
85
|
{% endif %}
|
|
86
86
|
<button type="button" class="outline"
|
|
87
|
-
hx-get="/api/
|
|
87
|
+
hx-get="/ui/api/flock/htmx/agents/new-agent-form"
|
|
88
88
|
hx-target="#agent-detail-panel"
|
|
89
89
|
hx-swap="innerHTML"
|
|
90
90
|
hx-indicator="#agent-detail-loading-indicator">
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
<div class="form-message {{ 'success' if success else 'error' }}">{{ message }}</div>
|
|
5
5
|
{% endif %}
|
|
6
6
|
{% if flock.agents %}
|
|
7
|
-
<ul>
|
|
7
|
+
<ul class="item-list">
|
|
8
8
|
{% for agent_name, agent in flock.agents.items() %}
|
|
9
|
-
<li hx-get="/api/
|
|
9
|
+
<li hx-get="/ui/api/flock/htmx/agents/{{ agent.name }}/details-form" hx-target="#agent-detail-panel" hx-swap="innerHTML" hx-indicator="#agent-detail-loading" onclick="this.closest('ul').querySelectorAll('li').forEach(li => li.classList.remove('selected-item')); this.classList.add('selected-item');">
|
|
10
10
|
<strong>{{ agent.name }}</strong><br>
|
|
11
11
|
<small>{{ agent.description|truncate(80) if agent.description else 'No description' }}</small>
|
|
12
12
|
</li>
|
|
@@ -20,19 +20,18 @@
|
|
|
20
20
|
<header class="grid">
|
|
21
21
|
<h2>Agents ({{ flock.agents|length }}) </h2>
|
|
22
22
|
<div style="text-align: right;">
|
|
23
|
-
<button role="button" class="outline" hx-get="/api/
|
|
23
|
+
<button role="button" class="outline" hx-get="/ui/api/flock/htmx/agents/new-agent-form" hx-target="#agent-detail-panel" hx-swap="innerHTML">Add New Agent</button>
|
|
24
24
|
</div>
|
|
25
25
|
|
|
26
26
|
</header>
|
|
27
27
|
|
|
28
|
-
|
|
29
28
|
{# Content of the left pane now goes into this article #}
|
|
30
29
|
<div style="padding: var(--pico-block-spacing-vertical) var(--pico-block-spacing-horizontal); flex-grow: 1; display: flex; flex-direction: column;">
|
|
31
|
-
<div hx-get="/api/
|
|
30
|
+
<div hx-get="/ui/api/flock/htmx/agent-list"
|
|
32
31
|
hx-trigger="load, agentListChanged from:body"
|
|
33
32
|
hx-swap="innerHTML"
|
|
34
33
|
id="agent-list-panel"
|
|
35
|
-
class="item-list-container" style="flex-grow: 1; ">
|
|
34
|
+
class="item-list-container" style="flex-grow: 1; overflow-y: auto;border: 1px solid var(--pico-border-color); border-radius: var(--pico-border-radius);">
|
|
36
35
|
<p style="padding:1rem;">Loading Agents...</p><progress indeterminate></progress>
|
|
37
36
|
</div>
|
|
38
37
|
</div>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<article id="chat-container" class="chat-container">
|
|
2
|
+
<div id="chat-log" hx-get="/chat/messages" hx-trigger="load" hx-swap="innerHTML">
|
|
3
|
+
<p><em>Loading chat…</em></p>
|
|
4
|
+
</div>
|
|
5
|
+
<form id="chat-form" class="chat-form" hx-post="/chat/send" hx-target="#chat-log" hx-swap="innerHTML" hx-on::after-request="this.reset()">
|
|
6
|
+
<input type="text" name="message" placeholder="Type a message…" required>
|
|
7
|
+
<button type="submit">Send</button>
|
|
8
|
+
</form>
|
|
9
|
+
</article>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{% for entry in history %}
|
|
2
|
+
{% if entry.role == 'user' %}
|
|
3
|
+
<div class="bubble user">
|
|
4
|
+
{{ entry.text }}
|
|
5
|
+
<span class="chat-timestamp">{{ entry.timestamp|default(now().strftime('%H:%M')) }}</span>
|
|
6
|
+
</div>
|
|
7
|
+
{% else %}
|
|
8
|
+
<div class="bubble bot">
|
|
9
|
+
{{ entry.text }}
|
|
10
|
+
<span class="chat-timestamp">{{ entry.timestamp|default(now().strftime('%H:%M')) }}{% if entry.agent %} - {{ entry.agent }}{% endif %}{% if entry.duration_ms is defined %} - {{ entry.duration_ms }}ms{% endif %}</span>
|
|
11
|
+
</div>
|
|
12
|
+
{% endif %}
|
|
13
|
+
{% endfor %}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
<form class="chat-settings-form" hx-post="/chat/settings" hx-target="this" hx-swap="outerHTML" hx-indicator="#chat-settings-saving" style="max-width: 500px; margin: 1rem auto;">
|
|
2
|
+
<hgroup style="margin-bottom: 1rem;">
|
|
3
|
+
<h3>Chat Settings</h3>
|
|
4
|
+
<h4>Select agent & field mappings</h4>
|
|
5
|
+
</hgroup>
|
|
6
|
+
|
|
7
|
+
<label for="agent_name_field">Chat Agent</label>
|
|
8
|
+
<select id="agent_name_field" name="agent_name"
|
|
9
|
+
hx-get="/chat/htmx/settings-form"
|
|
10
|
+
hx-target="closest form"
|
|
11
|
+
hx-trigger="change"
|
|
12
|
+
hx-include="[name=agent_name]">
|
|
13
|
+
<option value="" {% if not chat_cfg.agent_name %}selected{% endif %}>-- Echo (no agent) --</option>
|
|
14
|
+
{% if current_flock and current_flock.agents %}
|
|
15
|
+
{% for agent_name in current_flock.agents.keys() %}
|
|
16
|
+
<option value="{{ agent_name }}" {% if chat_cfg.agent_name == agent_name %}selected{% endif %}>{{ agent_name }}</option>
|
|
17
|
+
{% endfor %}
|
|
18
|
+
{% else %}
|
|
19
|
+
<option disabled>(no agents loaded)</option>
|
|
20
|
+
{% endif %}
|
|
21
|
+
</select>
|
|
22
|
+
|
|
23
|
+
<label for="message_key_field">Message Key</label>
|
|
24
|
+
{% if input_fields %}
|
|
25
|
+
<select id="message_key_field" name="message_key">
|
|
26
|
+
<option value="" {% if not chat_cfg.message_key %}selected{% endif %}>-- None --</option>
|
|
27
|
+
{% for field in input_fields %}
|
|
28
|
+
<option value="{{ field }}" {% if chat_cfg.message_key == field %}selected{% endif %}>{{ field }}</option>
|
|
29
|
+
{% endfor %}
|
|
30
|
+
</select>
|
|
31
|
+
{% else %}
|
|
32
|
+
<input type="text" id="message_key_field" name="message_key" value="{{ chat_cfg.message_key }}" required>
|
|
33
|
+
{% endif %}
|
|
34
|
+
|
|
35
|
+
<label for="history_key_field">History Key</label>
|
|
36
|
+
{% if input_fields %}
|
|
37
|
+
<select id="history_key_field" name="history_key">
|
|
38
|
+
<option value="" {% if not chat_cfg.history_key %}selected{% endif %}>-- None --</option>
|
|
39
|
+
{% for field in input_fields %}
|
|
40
|
+
<option value="{{ field }}" {% if chat_cfg.history_key == field %}selected{% endif %}>{{ field }}</option>
|
|
41
|
+
{% endfor %}
|
|
42
|
+
</select>
|
|
43
|
+
{% else %}
|
|
44
|
+
<input type="text" id="history_key_field" name="history_key" value="{{ chat_cfg.history_key }}" required>
|
|
45
|
+
{% endif %}
|
|
46
|
+
|
|
47
|
+
<label for="response_key_field">Response Key</label>
|
|
48
|
+
{% if output_fields %}
|
|
49
|
+
<select id="response_key_field" name="response_key">
|
|
50
|
+
<option value="" {% if not chat_cfg.response_key %}selected{% endif %}>-- None --</option>
|
|
51
|
+
{% for field in output_fields %}
|
|
52
|
+
<option value="{{ field }}" {% if chat_cfg.response_key == field %}selected{% endif %}>{{ field }}</option>
|
|
53
|
+
{% endfor %}
|
|
54
|
+
</select>
|
|
55
|
+
{% else %}
|
|
56
|
+
<input type="text" id="response_key_field" name="response_key" value="{{ chat_cfg.response_key }}" required>
|
|
57
|
+
{% endif %}
|
|
58
|
+
|
|
59
|
+
<div class="grid" style="margin-top: 1rem;">
|
|
60
|
+
<button type="submit">Save Settings</button>
|
|
61
|
+
<button type="reset" class="outline">Reset</button>
|
|
62
|
+
<button type="button" class="secondary outline" hx-get="/chat/htmx/chat-view" hx-target="#chat-content-area" hx-swap="innerHTML">Cancel</button>
|
|
63
|
+
</div>
|
|
64
|
+
<div id="chat-settings-saving" class="htmx-indicator" style="text-align:center; margin-top:0.5rem;"><progress indeterminate></progress></div>
|
|
65
|
+
</form>
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<h2>Run Flock</h2>
|
|
4
4
|
</header>
|
|
5
5
|
{% if flock and flock.agents %}
|
|
6
|
-
<form hx-post="/api/
|
|
6
|
+
<form hx-post="/ui/api/flock/htmx/run"
|
|
7
7
|
hx-target="#results-display"
|
|
8
8
|
hx-swap="innerHTML"
|
|
9
9
|
hx-indicator="#run-loading-indicator"
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
x-model="selectedAgentForInput"
|
|
17
17
|
@change="
|
|
18
18
|
if ($event.target.value) {
|
|
19
|
-
htmx.ajax('GET', '/api/
|
|
19
|
+
htmx.ajax('GET', '/ui/api/flock/htmx/agents/' + $event.target.value + '/input-form', {target: '#dynamic-input-form-fields', swap: 'innerHTML', indicator: '#input-form-loading-indicator'});
|
|
20
20
|
} else {
|
|
21
21
|
document.getElementById('dynamic-input-form-fields').innerHTML = '<p><small>Select an agent to see its input fields.</small></p>';
|
|
22
22
|
}
|