ai-browser 0.2.5 → 0.3.1
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.
- package/README.md +10 -1
- package/dist/agent/agent-loop.d.ts +18 -0
- package/dist/agent/agent-loop.d.ts.map +1 -1
- package/dist/agent/agent-loop.js +233 -3
- package/dist/agent/agent-loop.js.map +1 -1
- package/dist/agent/content-budget.d.ts.map +1 -1
- package/dist/agent/content-budget.js +4 -0
- package/dist/agent/content-budget.js.map +1 -1
- package/dist/agent/conversation-manager.d.ts.map +1 -1
- package/dist/agent/conversation-manager.js +20 -6
- package/dist/agent/conversation-manager.js.map +1 -1
- package/dist/agent/index.js +7 -2
- package/dist/agent/index.js.map +1 -1
- package/dist/agent/prompt.d.ts +1 -1
- package/dist/agent/prompt.d.ts.map +1 -1
- package/dist/agent/prompt.js +8 -0
- package/dist/agent/prompt.js.map +1 -1
- package/dist/agent/tool-usage-tracker.d.ts.map +1 -1
- package/dist/agent/tool-usage-tracker.js +7 -3
- package/dist/agent/tool-usage-tracker.js.map +1 -1
- package/dist/agent/types.d.ts +6 -0
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/api/mcp-sse.d.ts +2 -1
- package/dist/api/mcp-sse.d.ts.map +1 -1
- package/dist/api/mcp-sse.js +2 -1
- package/dist/api/mcp-sse.js.map +1 -1
- package/dist/api/routes.d.ts +2 -1
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +289 -14
- package/dist/api/routes.js.map +1 -1
- package/dist/browser/BrowserManager.d.ts.map +1 -1
- package/dist/browser/BrowserManager.js +5 -2
- package/dist/browser/BrowserManager.js.map +1 -1
- package/dist/cli/mcp-stdio.js +3 -0
- package/dist/cli/mcp-stdio.js.map +1 -1
- package/dist/cli/server.js +15 -3
- package/dist/cli/server.js.map +1 -1
- package/dist/mcp/ai-markdown.d.ts.map +1 -1
- package/dist/mcp/ai-markdown.js +106 -38
- package/dist/mcp/ai-markdown.js.map +1 -1
- package/dist/mcp/browser-mcp-server.d.ts +2 -0
- package/dist/mcp/browser-mcp-server.d.ts.map +1 -1
- package/dist/mcp/browser-mcp-server.js +72 -13
- package/dist/mcp/browser-mcp-server.js.map +1 -1
- package/dist/mcp/task-tools.d.ts.map +1 -1
- package/dist/mcp/task-tools.js +1 -0
- package/dist/mcp/task-tools.js.map +1 -1
- package/dist/memory/KnowledgeCardStore.d.ts +35 -0
- package/dist/memory/KnowledgeCardStore.d.ts.map +1 -0
- package/dist/memory/KnowledgeCardStore.js +304 -0
- package/dist/memory/KnowledgeCardStore.js.map +1 -0
- package/dist/memory/MemoryCapturer.d.ts +14 -0
- package/dist/memory/MemoryCapturer.d.ts.map +1 -0
- package/dist/memory/MemoryCapturer.js +183 -0
- package/dist/memory/MemoryCapturer.js.map +1 -0
- package/dist/memory/MemoryInjector.d.ts +23 -0
- package/dist/memory/MemoryInjector.d.ts.map +1 -0
- package/dist/memory/MemoryInjector.js +180 -0
- package/dist/memory/MemoryInjector.js.map +1 -0
- package/dist/memory/RecordingConverter.d.ts +16 -0
- package/dist/memory/RecordingConverter.d.ts.map +1 -0
- package/dist/memory/RecordingConverter.js +108 -0
- package/dist/memory/RecordingConverter.js.map +1 -0
- package/dist/memory/SessionRecorder.d.ts +39 -0
- package/dist/memory/SessionRecorder.d.ts.map +1 -0
- package/dist/memory/SessionRecorder.js +198 -0
- package/dist/memory/SessionRecorder.js.map +1 -0
- package/dist/memory/index.d.ts +8 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +6 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/memory/types.d.ts +39 -0
- package/dist/memory/types.d.ts.map +1 -0
- package/dist/memory/types.js +2 -0
- package/dist/memory/types.js.map +1 -0
- package/dist/semantic/PageAnalyzer.d.ts.map +1 -1
- package/dist/semantic/PageAnalyzer.js +2 -1
- package/dist/semantic/PageAnalyzer.js.map +1 -1
- package/dist/task/templates/login-keep-session.d.ts.map +1 -1
- package/dist/task/templates/login-keep-session.js +2 -4
- package/dist/task/templates/login-keep-session.js.map +1 -1
- package/dist/task/tool-actions.d.ts.map +1 -1
- package/dist/task/tool-actions.js +6 -11
- package/dist/task/tool-actions.js.map +1 -1
- package/dist/utils/safe-page.d.ts +9 -0
- package/dist/utils/safe-page.d.ts.map +1 -0
- package/dist/utils/safe-page.js +14 -0
- package/dist/utils/safe-page.js.map +1 -0
- package/package.json +3 -1
- package/public/index.html +1651 -133
- package/public/task-result.html +107 -12
- package/public/tasks.html +83 -10
package/public/index.html
CHANGED
|
@@ -4,50 +4,71 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>AI Browser</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
7
10
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.2.0/github-markdown.min.css">
|
|
8
11
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
9
12
|
<script src="https://cdn.jsdelivr.net/npm/dompurify@3/dist/purify.min.js"></script>
|
|
10
13
|
<style>
|
|
11
14
|
/* ===== CSS Design System ===== */
|
|
12
15
|
:root {
|
|
13
|
-
--bg-canvas: #
|
|
14
|
-
--bg-surface: #
|
|
15
|
-
--bg-overlay: #
|
|
16
|
-
--
|
|
17
|
-
--
|
|
18
|
-
--
|
|
19
|
-
--
|
|
20
|
-
--
|
|
21
|
-
--
|
|
22
|
-
--
|
|
23
|
-
--
|
|
24
|
-
--
|
|
25
|
-
--
|
|
26
|
-
--
|
|
27
|
-
--
|
|
28
|
-
--
|
|
29
|
-
--
|
|
30
|
-
--
|
|
31
|
-
--
|
|
32
|
-
--
|
|
16
|
+
--bg-canvas: #0a0e14;
|
|
17
|
+
--bg-surface: #12171f;
|
|
18
|
+
--bg-overlay: #1a2030;
|
|
19
|
+
--bg-elevated: #1e2536;
|
|
20
|
+
--border: #252d3a;
|
|
21
|
+
--border-subtle: #1c2333;
|
|
22
|
+
--border-accent: rgba(99,160,255,0.2);
|
|
23
|
+
--text-primary: #d4dae4;
|
|
24
|
+
--text-secondary: #8892a2;
|
|
25
|
+
--text-muted: #4a5568;
|
|
26
|
+
--accent: #63a0ff;
|
|
27
|
+
--accent-dim: rgba(99,160,255,0.12);
|
|
28
|
+
--accent-glow: rgba(99,160,255,0.25);
|
|
29
|
+
--green: #22c55e;
|
|
30
|
+
--green-dim: rgba(34,197,94,0.12);
|
|
31
|
+
--green-hover: #16a34a;
|
|
32
|
+
--red: #ef4444;
|
|
33
|
+
--red-dim: rgba(239,68,68,0.12);
|
|
34
|
+
--blue: #3b82f6;
|
|
35
|
+
--blue-dim: rgba(59,130,246,0.12);
|
|
36
|
+
--purple: #a78bfa;
|
|
37
|
+
--purple-dim: rgba(167,139,250,0.12);
|
|
38
|
+
--orange: #fb923c;
|
|
39
|
+
--orange-dim: rgba(251,146,60,0.12);
|
|
40
|
+
--yellow: #facc15;
|
|
41
|
+
--radius-sm: 6px;
|
|
42
|
+
--radius-md: 8px;
|
|
43
|
+
--radius-lg: 12px;
|
|
44
|
+
--radius-xl: 16px;
|
|
45
|
+
--shadow-sm: 0 1px 2px rgba(0,0,0,0.3), 0 1px 3px rgba(0,0,0,0.15);
|
|
46
|
+
--shadow: 0 2px 8px rgba(0,0,0,0.3), 0 1px 3px rgba(0,0,0,0.2);
|
|
47
|
+
--shadow-lg: 0 4px 16px rgba(0,0,0,0.4), 0 2px 6px rgba(0,0,0,0.2);
|
|
48
|
+
--shadow-glow: 0 0 20px rgba(99,160,255,0.08);
|
|
49
|
+
--transition: 180ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
50
|
+
--transition-slow: 300ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
33
51
|
}
|
|
34
52
|
|
|
35
53
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
36
54
|
|
|
37
55
|
body {
|
|
38
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
56
|
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
39
57
|
background: var(--bg-canvas);
|
|
58
|
+
background-image: radial-gradient(ellipse at 50% 0%, rgba(99,160,255,0.03) 0%, transparent 60%);
|
|
40
59
|
color: var(--text-primary);
|
|
41
60
|
height: 100vh;
|
|
42
61
|
display: flex;
|
|
43
62
|
flex-direction: column;
|
|
44
63
|
overflow: hidden;
|
|
64
|
+
-webkit-font-smoothing: antialiased;
|
|
65
|
+
-moz-osx-font-smoothing: grayscale;
|
|
45
66
|
}
|
|
46
67
|
|
|
47
68
|
/* ===== Scrollbar ===== */
|
|
48
|
-
::-webkit-scrollbar { width:
|
|
69
|
+
::-webkit-scrollbar { width: 5px; height: 5px; }
|
|
49
70
|
::-webkit-scrollbar-track { background: transparent; }
|
|
50
|
-
::-webkit-scrollbar-thumb { background: var(--border); border-radius:
|
|
71
|
+
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; }
|
|
51
72
|
::-webkit-scrollbar-thumb:hover { background: var(--text-muted); }
|
|
52
73
|
|
|
53
74
|
/* ===== Header / Nav ===== */
|
|
@@ -57,9 +78,12 @@ body {
|
|
|
57
78
|
padding: 0 20px;
|
|
58
79
|
display: flex;
|
|
59
80
|
align-items: center;
|
|
60
|
-
height:
|
|
81
|
+
height: 52px;
|
|
61
82
|
flex-shrink: 0;
|
|
62
83
|
gap: 16px;
|
|
84
|
+
box-shadow: 0 1px 8px rgba(0,0,0,0.2);
|
|
85
|
+
position: relative;
|
|
86
|
+
z-index: 10;
|
|
63
87
|
}
|
|
64
88
|
.app-logo {
|
|
65
89
|
color: var(--accent);
|
|
@@ -67,39 +91,40 @@ body {
|
|
|
67
91
|
font-weight: 700;
|
|
68
92
|
white-space: nowrap;
|
|
69
93
|
letter-spacing: -0.3px;
|
|
94
|
+
background: linear-gradient(135deg, var(--accent), var(--purple));
|
|
95
|
+
-webkit-background-clip: text;
|
|
96
|
+
-webkit-text-fill-color: transparent;
|
|
97
|
+
background-clip: text;
|
|
70
98
|
}
|
|
71
99
|
.nav-tabs {
|
|
72
100
|
display: flex;
|
|
73
101
|
gap: 2px;
|
|
74
102
|
margin-left: 8px;
|
|
103
|
+
background: var(--bg-canvas);
|
|
104
|
+
border-radius: var(--radius-md);
|
|
105
|
+
padding: 3px;
|
|
75
106
|
}
|
|
76
107
|
.nav-tab {
|
|
77
108
|
padding: 6px 16px;
|
|
78
109
|
font-size: 13px;
|
|
79
110
|
font-weight: 500;
|
|
80
111
|
border: none;
|
|
81
|
-
border-radius: var(--radius-
|
|
112
|
+
border-radius: var(--radius-sm);
|
|
82
113
|
background: transparent;
|
|
83
114
|
color: var(--text-secondary);
|
|
84
115
|
cursor: pointer;
|
|
85
116
|
transition: all var(--transition);
|
|
86
117
|
position: relative;
|
|
87
118
|
}
|
|
119
|
+
.nav-tab .tab-icon { font-size: 14px; margin-right: 2px; }
|
|
88
120
|
.nav-tab:hover { color: var(--text-primary); background: var(--bg-overlay); }
|
|
89
121
|
.nav-tab.active {
|
|
90
122
|
color: var(--accent);
|
|
91
|
-
background:
|
|
123
|
+
background: var(--bg-overlay);
|
|
124
|
+
box-shadow: var(--shadow-sm);
|
|
92
125
|
}
|
|
93
126
|
.nav-tab.active::after {
|
|
94
|
-
|
|
95
|
-
position: absolute;
|
|
96
|
-
bottom: -7px;
|
|
97
|
-
left: 50%;
|
|
98
|
-
transform: translateX(-50%);
|
|
99
|
-
width: 60%;
|
|
100
|
-
height: 2px;
|
|
101
|
-
background: var(--accent);
|
|
102
|
-
border-radius: 1px;
|
|
127
|
+
display: none;
|
|
103
128
|
}
|
|
104
129
|
.header-right {
|
|
105
130
|
display: flex;
|
|
@@ -109,7 +134,7 @@ body {
|
|
|
109
134
|
|
|
110
135
|
/* ===== Shared Button Styles ===== */
|
|
111
136
|
.btn {
|
|
112
|
-
padding:
|
|
137
|
+
padding: 7px 14px;
|
|
113
138
|
font-size: 13px;
|
|
114
139
|
border: 1px solid var(--border);
|
|
115
140
|
border-radius: var(--radius-md);
|
|
@@ -119,34 +144,39 @@ body {
|
|
|
119
144
|
background: var(--bg-overlay);
|
|
120
145
|
color: var(--text-primary);
|
|
121
146
|
white-space: nowrap;
|
|
147
|
+
box-shadow: var(--shadow-sm);
|
|
122
148
|
}
|
|
123
|
-
.btn:hover { border-color: var(--accent); color: var(--accent); }
|
|
149
|
+
.btn:hover { border-color: var(--accent); color: var(--accent); background: var(--bg-elevated); }
|
|
124
150
|
.btn-primary {
|
|
125
|
-
background: var(--green);
|
|
151
|
+
background: linear-gradient(135deg, var(--green), var(--green-hover));
|
|
126
152
|
color: #fff;
|
|
127
|
-
border-color:
|
|
153
|
+
border-color: transparent;
|
|
154
|
+
box-shadow: 0 2px 8px rgba(34,197,94,0.2);
|
|
128
155
|
}
|
|
129
|
-
.btn-primary:hover { background: var(--green-hover); border-color:
|
|
156
|
+
.btn-primary:hover { background: linear-gradient(135deg, var(--green-hover), #15803d); border-color: transparent; color: #fff; box-shadow: 0 2px 12px rgba(34,197,94,0.3); }
|
|
130
157
|
.btn-primary:disabled {
|
|
131
158
|
background: var(--bg-overlay);
|
|
132
159
|
color: var(--text-muted);
|
|
133
160
|
border-color: var(--border);
|
|
134
161
|
cursor: not-allowed;
|
|
162
|
+
box-shadow: none;
|
|
135
163
|
}
|
|
136
164
|
.btn-danger { border-color: var(--red); color: var(--red); }
|
|
137
|
-
.btn-danger:hover { background: var(--red); color: #fff; }
|
|
165
|
+
.btn-danger:hover { background: var(--red); color: #fff; box-shadow: 0 2px 8px rgba(239,68,68,0.25); }
|
|
138
166
|
.btn-send {
|
|
139
|
-
padding: 10px
|
|
140
|
-
background: var(--
|
|
167
|
+
padding: 10px 24px;
|
|
168
|
+
background: linear-gradient(135deg, var(--accent), #4f8ef7);
|
|
141
169
|
color: #fff;
|
|
142
170
|
border: none;
|
|
143
171
|
border-radius: var(--radius-lg);
|
|
144
172
|
font-size: 14px;
|
|
145
173
|
font-weight: 600;
|
|
146
174
|
cursor: pointer;
|
|
175
|
+
box-shadow: 0 2px 10px rgba(99,160,255,0.25);
|
|
176
|
+
transition: all var(--transition);
|
|
147
177
|
}
|
|
148
|
-
.btn-send:hover {
|
|
149
|
-
.btn-send:disabled { background: var(--bg-overlay); color: var(--text-muted); cursor: not-allowed; }
|
|
178
|
+
.btn-send:hover { box-shadow: 0 4px 16px rgba(99,160,255,0.35); transform: translateY(-1px); }
|
|
179
|
+
.btn-send:disabled { background: var(--bg-overlay); color: var(--text-muted); cursor: not-allowed; box-shadow: none; transform: none; }
|
|
150
180
|
|
|
151
181
|
/* ===== Main Layout ===== */
|
|
152
182
|
.app-main {
|
|
@@ -165,15 +195,17 @@ body {
|
|
|
165
195
|
animation: viewFadeIn 200ms ease;
|
|
166
196
|
}
|
|
167
197
|
@keyframes viewFadeIn {
|
|
168
|
-
from { opacity: 0; }
|
|
169
|
-
to { opacity: 1; }
|
|
198
|
+
from { opacity: 0; transform: translateY(6px); }
|
|
199
|
+
to { opacity: 1; transform: translateY(0); }
|
|
170
200
|
}
|
|
171
201
|
|
|
172
202
|
/* ===== Shared Modal ===== */
|
|
173
203
|
.modal-overlay {
|
|
174
204
|
position: fixed;
|
|
175
205
|
inset: 0;
|
|
176
|
-
background: rgba(0,0,0,0.
|
|
206
|
+
background: rgba(0,0,0,0.75);
|
|
207
|
+
backdrop-filter: blur(8px);
|
|
208
|
+
-webkit-backdrop-filter: blur(8px);
|
|
177
209
|
display: flex;
|
|
178
210
|
align-items: center;
|
|
179
211
|
justify-content: center;
|
|
@@ -183,41 +215,43 @@ body {
|
|
|
183
215
|
.modal {
|
|
184
216
|
background: var(--bg-surface);
|
|
185
217
|
border: 1px solid var(--border);
|
|
186
|
-
border-radius:
|
|
187
|
-
padding:
|
|
218
|
+
border-radius: var(--radius-xl);
|
|
219
|
+
padding: 28px;
|
|
188
220
|
width: 440px;
|
|
189
221
|
max-width: 90vw;
|
|
222
|
+
box-shadow: var(--shadow-lg), var(--shadow-glow);
|
|
190
223
|
}
|
|
191
|
-
.modal h2 { font-size: 16px; color: var(--text-primary); margin-bottom: 16px; }
|
|
224
|
+
.modal h2 { font-size: 16px; color: var(--text-primary); margin-bottom: 16px; font-weight: 600; }
|
|
192
225
|
.modal label {
|
|
193
226
|
display: block;
|
|
194
227
|
font-size: 13px;
|
|
195
228
|
color: var(--text-secondary);
|
|
196
229
|
margin-bottom: 4px;
|
|
197
|
-
margin-top:
|
|
230
|
+
margin-top: 14px;
|
|
198
231
|
}
|
|
199
232
|
.modal label:first-of-type { margin-top: 0; }
|
|
200
233
|
.modal input[type="text"],
|
|
201
234
|
.modal input[type="password"],
|
|
202
235
|
.modal input[type="number"] {
|
|
203
236
|
width: 100%;
|
|
204
|
-
padding:
|
|
237
|
+
padding: 9px 12px;
|
|
205
238
|
font-size: 14px;
|
|
206
239
|
border: 1px solid var(--border);
|
|
207
240
|
border-radius: var(--radius-md);
|
|
208
241
|
background: var(--bg-canvas);
|
|
209
242
|
color: var(--text-primary);
|
|
210
243
|
outline: none;
|
|
244
|
+
transition: all var(--transition);
|
|
211
245
|
}
|
|
212
|
-
.modal input:focus { border-color: var(--accent); }
|
|
246
|
+
.modal input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-dim); }
|
|
213
247
|
.modal-btns {
|
|
214
248
|
display: flex;
|
|
215
249
|
gap: 8px;
|
|
216
250
|
justify-content: flex-end;
|
|
217
251
|
margin-top: 20px;
|
|
218
252
|
}
|
|
219
|
-
.btn-save { background: var(--green); color: #fff; border-color:
|
|
220
|
-
.btn-save:hover { background: var(--green-hover); color: #fff; }
|
|
253
|
+
.btn-save { background: linear-gradient(135deg, var(--green), var(--green-hover)); color: #fff; border-color: transparent; box-shadow: 0 2px 8px rgba(34,197,94,0.2); }
|
|
254
|
+
.btn-save:hover { background: linear-gradient(135deg, var(--green-hover), #15803d); color: #fff; }
|
|
221
255
|
|
|
222
256
|
/* ===== Shared Markdown ===== */
|
|
223
257
|
.markdown-body {
|
|
@@ -241,20 +275,21 @@ body {
|
|
|
241
275
|
}
|
|
242
276
|
#view-semantic .sem-toolbar input[type="text"] {
|
|
243
277
|
flex: 1;
|
|
244
|
-
padding:
|
|
278
|
+
padding: 9px 14px;
|
|
245
279
|
font-size: 14px;
|
|
246
280
|
border: 1px solid var(--border);
|
|
247
281
|
border-radius: var(--radius-md);
|
|
248
282
|
background: var(--bg-canvas);
|
|
249
283
|
color: var(--text-primary);
|
|
250
284
|
outline: none;
|
|
285
|
+
transition: all var(--transition);
|
|
251
286
|
}
|
|
252
|
-
#view-semantic .sem-toolbar input:focus { border-color: var(--accent); }
|
|
287
|
+
#view-semantic .sem-toolbar input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-dim); }
|
|
253
288
|
|
|
254
289
|
.sem-stats-bar {
|
|
255
290
|
background: var(--bg-overlay);
|
|
256
291
|
border-bottom: 1px solid var(--border);
|
|
257
|
-
padding:
|
|
292
|
+
padding: 7px 20px;
|
|
258
293
|
display: flex;
|
|
259
294
|
gap: 16px;
|
|
260
295
|
font-size: 12px;
|
|
@@ -264,7 +299,7 @@ body {
|
|
|
264
299
|
.sem-stats-bar .stat-item {
|
|
265
300
|
display: flex;
|
|
266
301
|
align-items: center;
|
|
267
|
-
gap:
|
|
302
|
+
gap: 5px;
|
|
268
303
|
}
|
|
269
304
|
.sem-stats-bar .stat-val {
|
|
270
305
|
color: var(--accent);
|
|
@@ -301,15 +336,16 @@ body {
|
|
|
301
336
|
flex-shrink: 0;
|
|
302
337
|
}
|
|
303
338
|
.sem-content-tab {
|
|
304
|
-
padding:
|
|
339
|
+
padding: 5px 12px;
|
|
305
340
|
font-size: 12px;
|
|
306
341
|
border: 1px solid var(--border);
|
|
307
342
|
border-radius: var(--radius-sm);
|
|
308
343
|
background: transparent;
|
|
309
344
|
color: var(--text-secondary);
|
|
310
345
|
cursor: pointer;
|
|
346
|
+
transition: all var(--transition);
|
|
311
347
|
}
|
|
312
|
-
.sem-content-tab.active { background: var(--
|
|
348
|
+
.sem-content-tab.active { background: var(--accent-dim); color: var(--accent); border-color: var(--accent); }
|
|
313
349
|
|
|
314
350
|
.sem-content-area {
|
|
315
351
|
flex: 1;
|
|
@@ -358,14 +394,14 @@ body {
|
|
|
358
394
|
display: flex;
|
|
359
395
|
align-items: center;
|
|
360
396
|
justify-content: space-between;
|
|
361
|
-
padding:
|
|
397
|
+
padding: 10px 14px;
|
|
362
398
|
border: 1px solid var(--border);
|
|
363
399
|
border-radius: var(--radius-md);
|
|
364
400
|
margin-bottom: 6px;
|
|
365
401
|
background: var(--bg-canvas);
|
|
366
|
-
transition: all
|
|
402
|
+
transition: all var(--transition);
|
|
367
403
|
}
|
|
368
|
-
.element-item:hover { border-color: var(--accent); background: var(--bg-surface); }
|
|
404
|
+
.element-item:hover { border-color: var(--accent); background: var(--bg-surface); box-shadow: 0 0 0 1px var(--accent-dim); }
|
|
369
405
|
.element-info { flex: 1; min-width: 0; }
|
|
370
406
|
.element-type {
|
|
371
407
|
display: inline-block;
|
|
@@ -374,11 +410,12 @@ body {
|
|
|
374
410
|
font-size: 11px;
|
|
375
411
|
font-weight: 600;
|
|
376
412
|
margin-right: 8px;
|
|
413
|
+
letter-spacing: 0.3px;
|
|
377
414
|
}
|
|
378
|
-
.element-type.button { background: var(--green); color:
|
|
379
|
-
.element-type.link { background: var(--blue); color:
|
|
380
|
-
.element-type.textbox { background: var(--purple); color:
|
|
381
|
-
.element-type.checkbox { background: var(--orange); color:
|
|
415
|
+
.element-type.button { background: var(--green-dim); color: var(--green); }
|
|
416
|
+
.element-type.link { background: var(--blue-dim); color: var(--accent); }
|
|
417
|
+
.element-type.textbox { background: var(--purple-dim); color: var(--purple); }
|
|
418
|
+
.element-type.checkbox { background: var(--orange-dim); color: var(--orange); }
|
|
382
419
|
.element-label { font-weight: 500; color: var(--text-primary); font-size: 13px; }
|
|
383
420
|
.element-id {
|
|
384
421
|
font-size: 11px;
|
|
@@ -416,15 +453,16 @@ body {
|
|
|
416
453
|
}
|
|
417
454
|
.filter-bar.hidden { display: none; }
|
|
418
455
|
.filter-btn {
|
|
419
|
-
padding:
|
|
456
|
+
padding: 4px 10px;
|
|
420
457
|
font-size: 11px;
|
|
421
458
|
background: var(--bg-overlay);
|
|
422
459
|
color: var(--text-secondary);
|
|
423
460
|
border: 1px solid var(--border);
|
|
424
461
|
border-radius: var(--radius-sm);
|
|
425
462
|
cursor: pointer;
|
|
463
|
+
transition: all var(--transition);
|
|
426
464
|
}
|
|
427
|
-
.filter-btn.active { background: var(--blue); color:
|
|
465
|
+
.filter-btn.active { background: var(--blue-dim); color: var(--accent); border-color: var(--accent); }
|
|
428
466
|
|
|
429
467
|
/* Log drawer */
|
|
430
468
|
.sem-log-drawer {
|
|
@@ -474,15 +512,16 @@ body {
|
|
|
474
512
|
display: none;
|
|
475
513
|
}
|
|
476
514
|
.sem-status.visible { display: block; }
|
|
477
|
-
.sem-status.loading { background:
|
|
478
|
-
.sem-status.success { background:
|
|
479
|
-
.sem-status.error { background:
|
|
515
|
+
.sem-status.loading { background: var(--accent-dim); color: var(--accent); }
|
|
516
|
+
.sem-status.success { background: var(--green-dim); color: var(--green); }
|
|
517
|
+
.sem-status.error { background: var(--red-dim); color: var(--red); }
|
|
480
518
|
|
|
481
519
|
.placeholder-text {
|
|
482
520
|
color: var(--text-muted);
|
|
483
521
|
font-size: 14px;
|
|
484
522
|
text-align: center;
|
|
485
|
-
padding:
|
|
523
|
+
padding: 48px 20px;
|
|
524
|
+
line-height: 1.6;
|
|
486
525
|
}
|
|
487
526
|
</style>
|
|
488
527
|
<style>
|
|
@@ -502,22 +541,23 @@ body {
|
|
|
502
541
|
}
|
|
503
542
|
.chat-input-bar {
|
|
504
543
|
border-top: 1px solid var(--border);
|
|
505
|
-
padding:
|
|
544
|
+
padding: 14px 16px;
|
|
506
545
|
display: flex;
|
|
507
|
-
gap:
|
|
546
|
+
gap: 10px;
|
|
508
547
|
background: var(--bg-surface);
|
|
509
548
|
}
|
|
510
549
|
.chat-input-bar input {
|
|
511
550
|
flex: 1;
|
|
512
|
-
padding: 10px
|
|
551
|
+
padding: 10px 16px;
|
|
513
552
|
font-size: 14px;
|
|
514
553
|
border: 1px solid var(--border);
|
|
515
554
|
border-radius: var(--radius-lg);
|
|
516
555
|
background: var(--bg-canvas);
|
|
517
556
|
color: var(--text-primary);
|
|
518
557
|
outline: none;
|
|
558
|
+
transition: all var(--transition);
|
|
519
559
|
}
|
|
520
|
-
.chat-input-bar input:focus { border-color: var(--accent); }
|
|
560
|
+
.chat-input-bar input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-dim); }
|
|
521
561
|
|
|
522
562
|
/* Preview panel */
|
|
523
563
|
.preview-panel {
|
|
@@ -536,15 +576,16 @@ body {
|
|
|
536
576
|
}
|
|
537
577
|
.preview-tabs { display: flex; gap: 4px; }
|
|
538
578
|
.preview-tab {
|
|
539
|
-
padding:
|
|
579
|
+
padding: 5px 14px;
|
|
540
580
|
font-size: 12px;
|
|
541
581
|
border: 1px solid var(--border);
|
|
542
582
|
border-radius: var(--radius-sm);
|
|
543
583
|
background: transparent;
|
|
544
584
|
color: var(--text-secondary);
|
|
545
585
|
cursor: pointer;
|
|
586
|
+
transition: all var(--transition);
|
|
546
587
|
}
|
|
547
|
-
.preview-tab.active { background: var(--
|
|
588
|
+
.preview-tab.active { background: var(--accent-dim); color: var(--accent); border-color: var(--accent); }
|
|
548
589
|
.preview-url {
|
|
549
590
|
font-size: 12px;
|
|
550
591
|
color: var(--accent);
|
|
@@ -635,14 +676,15 @@ body {
|
|
|
635
676
|
.msg-user { text-align: right; }
|
|
636
677
|
.msg-user .bubble {
|
|
637
678
|
display: inline-block;
|
|
638
|
-
background: var(--blue);
|
|
679
|
+
background: linear-gradient(135deg, var(--blue), #2563eb);
|
|
639
680
|
color: #fff;
|
|
640
|
-
padding:
|
|
641
|
-
border-radius:
|
|
681
|
+
padding: 10px 16px;
|
|
682
|
+
border-radius: 16px 16px 4px 16px;
|
|
642
683
|
max-width: 85%;
|
|
643
684
|
text-align: left;
|
|
644
685
|
font-size: 14px;
|
|
645
686
|
animation: fadeIn 200ms ease;
|
|
687
|
+
box-shadow: 0 2px 8px rgba(59,130,246,0.2);
|
|
646
688
|
}
|
|
647
689
|
.msg-system {
|
|
648
690
|
text-align: center;
|
|
@@ -654,19 +696,21 @@ body {
|
|
|
654
696
|
background: var(--bg-surface);
|
|
655
697
|
border: 1px solid var(--border);
|
|
656
698
|
border-radius: var(--radius-lg);
|
|
657
|
-
padding:
|
|
699
|
+
padding: 10px 14px;
|
|
658
700
|
font-size: 13px;
|
|
659
701
|
color: var(--text-secondary);
|
|
660
702
|
font-style: italic;
|
|
661
703
|
animation: fadeIn 200ms ease;
|
|
704
|
+
border-left: 3px solid var(--accent);
|
|
662
705
|
}
|
|
663
706
|
.msg-thinking .markdown-body { font-style: normal; font-size: 13px; }
|
|
664
707
|
.msg .md-inline { font-style: normal; }
|
|
665
708
|
.msg-tool-call {
|
|
666
709
|
background: var(--bg-canvas);
|
|
667
|
-
border: 1px solid var(--
|
|
710
|
+
border: 1px solid var(--border);
|
|
711
|
+
border-left: 3px solid var(--accent);
|
|
668
712
|
border-radius: var(--radius-lg);
|
|
669
|
-
padding:
|
|
713
|
+
padding: 10px 14px;
|
|
670
714
|
font-size: 13px;
|
|
671
715
|
animation: fadeIn 200ms ease;
|
|
672
716
|
}
|
|
@@ -716,8 +760,8 @@ body {
|
|
|
716
760
|
font-size: 13px;
|
|
717
761
|
animation: fadeIn 200ms ease;
|
|
718
762
|
}
|
|
719
|
-
.msg-tool-result.success { border: 1px solid var(--green); }
|
|
720
|
-
.msg-tool-result.fail { border: 1px solid var(--red); }
|
|
763
|
+
.msg-tool-result.success { border: 1px solid var(--border); border-left: 3px solid var(--green); }
|
|
764
|
+
.msg-tool-result.fail { border: 1px solid var(--border); border-left: 3px solid var(--red); }
|
|
721
765
|
.msg-tool-result .result-content {
|
|
722
766
|
max-height: 120px;
|
|
723
767
|
overflow: hidden;
|
|
@@ -756,34 +800,58 @@ body {
|
|
|
756
800
|
}
|
|
757
801
|
|
|
758
802
|
.msg-error {
|
|
759
|
-
background:
|
|
760
|
-
border: 1px solid var(--
|
|
803
|
+
background: var(--red-dim);
|
|
804
|
+
border: 1px solid var(--border);
|
|
805
|
+
border-left: 3px solid var(--red);
|
|
761
806
|
border-radius: var(--radius-lg);
|
|
762
|
-
padding:
|
|
807
|
+
padding: 10px 14px;
|
|
763
808
|
font-size: 13px;
|
|
764
809
|
color: #fca5a5;
|
|
765
810
|
animation: fadeIn 200ms ease;
|
|
766
811
|
}
|
|
767
812
|
.msg-done {
|
|
768
|
-
border-radius:
|
|
769
|
-
padding:
|
|
813
|
+
border-radius: var(--radius-lg);
|
|
814
|
+
padding: 14px 18px;
|
|
770
815
|
font-size: 14px;
|
|
771
816
|
animation: fadeIn 200ms ease;
|
|
772
817
|
}
|
|
773
|
-
.msg-done.success { background:
|
|
774
|
-
.msg-done.fail { background:
|
|
818
|
+
.msg-done.success { background: var(--green-dim); border: 1px solid var(--green); border-left: 4px solid var(--green); }
|
|
819
|
+
.msg-done.fail { background: var(--red-dim); border: 1px solid var(--red); border-left: 4px solid var(--red); }
|
|
775
820
|
.msg-done .title { font-weight: 700; margin-bottom: 4px; }
|
|
776
821
|
.msg-done .markdown-body { font-size: 14px; }
|
|
777
822
|
|
|
823
|
+
.msg-memory-recall {
|
|
824
|
+
background: linear-gradient(135deg, rgba(139, 92, 246, 0.08), rgba(59, 130, 246, 0.08));
|
|
825
|
+
border: 1px solid rgba(139, 92, 246, 0.3);
|
|
826
|
+
border-left: 3px solid #8b5cf6;
|
|
827
|
+
border-radius: var(--radius-lg);
|
|
828
|
+
padding: 10px 14px;
|
|
829
|
+
font-size: 13px;
|
|
830
|
+
color: #c4b5fd;
|
|
831
|
+
animation: fadeIn 200ms ease;
|
|
832
|
+
}
|
|
833
|
+
.memory-recall-hint {
|
|
834
|
+
display: flex;
|
|
835
|
+
align-items: center;
|
|
836
|
+
gap: 6px;
|
|
837
|
+
}
|
|
838
|
+
.memory-recall-hint .memory-icon {
|
|
839
|
+
font-size: 16px;
|
|
840
|
+
}
|
|
841
|
+
.memory-recall-hint strong {
|
|
842
|
+
color: #a78bfa;
|
|
843
|
+
}
|
|
844
|
+
|
|
778
845
|
.step-tag {
|
|
779
846
|
display: inline-block;
|
|
780
|
-
background: var(--
|
|
781
|
-
color: var(--
|
|
847
|
+
background: var(--accent-dim);
|
|
848
|
+
color: var(--accent);
|
|
782
849
|
font-size: 11px;
|
|
783
|
-
padding:
|
|
784
|
-
border-radius:
|
|
850
|
+
padding: 2px 8px;
|
|
851
|
+
border-radius: var(--radius-sm);
|
|
785
852
|
margin-right: 6px;
|
|
786
853
|
font-style: normal;
|
|
854
|
+
font-weight: 600;
|
|
787
855
|
}
|
|
788
856
|
|
|
789
857
|
/* Progress bar */
|
|
@@ -798,10 +866,11 @@ body {
|
|
|
798
866
|
.progress-bar-wrap.active { display: block; }
|
|
799
867
|
.progress-bar-fill {
|
|
800
868
|
height: 100%;
|
|
801
|
-
background: var(--accent);
|
|
869
|
+
background: linear-gradient(90deg, var(--accent), var(--purple));
|
|
802
870
|
border-radius: 2px;
|
|
803
871
|
transition: width 0.4s ease;
|
|
804
872
|
width: 0%;
|
|
873
|
+
box-shadow: 0 0 8px rgba(99,160,255,0.4);
|
|
805
874
|
}
|
|
806
875
|
.progress-info {
|
|
807
876
|
display: none;
|
|
@@ -844,7 +913,9 @@ body {
|
|
|
844
913
|
.type-input-modal {
|
|
845
914
|
position: fixed;
|
|
846
915
|
inset: 0;
|
|
847
|
-
background: rgba(0,0,0,0.
|
|
916
|
+
background: rgba(0,0,0,0.75);
|
|
917
|
+
backdrop-filter: blur(8px);
|
|
918
|
+
-webkit-backdrop-filter: blur(8px);
|
|
848
919
|
display: flex;
|
|
849
920
|
align-items: center;
|
|
850
921
|
justify-content: center;
|
|
@@ -854,9 +925,10 @@ body {
|
|
|
854
925
|
.type-input-modal .modal-content {
|
|
855
926
|
background: var(--bg-surface);
|
|
856
927
|
border: 1px solid var(--border);
|
|
857
|
-
border-radius: var(--radius-
|
|
858
|
-
padding:
|
|
928
|
+
border-radius: var(--radius-xl);
|
|
929
|
+
padding: 28px;
|
|
859
930
|
width: 400px;
|
|
931
|
+
box-shadow: var(--shadow-lg), var(--shadow-glow);
|
|
860
932
|
}
|
|
861
933
|
.type-input-modal .modal-title {
|
|
862
934
|
font-size: 16px;
|
|
@@ -873,8 +945,9 @@ body {
|
|
|
873
945
|
color: var(--text-primary);
|
|
874
946
|
outline: none;
|
|
875
947
|
margin-bottom: 16px;
|
|
948
|
+
transition: all var(--transition);
|
|
876
949
|
}
|
|
877
|
-
.type-input-modal .modal-input:focus { border-color: var(--accent); }
|
|
950
|
+
.type-input-modal .modal-input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-dim); }
|
|
878
951
|
.type-input-modal .modal-buttons {
|
|
879
952
|
display: flex;
|
|
880
953
|
gap: 10px;
|
|
@@ -904,11 +977,12 @@ body {
|
|
|
904
977
|
background: var(--bg-surface);
|
|
905
978
|
border: 1px solid var(--border);
|
|
906
979
|
border-radius: var(--radius-lg);
|
|
907
|
-
padding:
|
|
980
|
+
padding: 16px;
|
|
908
981
|
cursor: pointer;
|
|
909
982
|
transition: all var(--transition);
|
|
983
|
+
box-shadow: var(--shadow-sm);
|
|
910
984
|
}
|
|
911
|
-
.task-card:hover { border-color: var(--accent); }
|
|
985
|
+
.task-card:hover { border-color: var(--accent); box-shadow: var(--shadow), 0 0 0 1px var(--accent-dim); transform: translateY(-1px); }
|
|
912
986
|
.task-card .task-header {
|
|
913
987
|
display: flex;
|
|
914
988
|
justify-content: space-between;
|
|
@@ -922,14 +996,15 @@ body {
|
|
|
922
996
|
}
|
|
923
997
|
.task-status {
|
|
924
998
|
display: inline-block;
|
|
925
|
-
padding:
|
|
999
|
+
padding: 3px 10px;
|
|
926
1000
|
border-radius: var(--radius-sm);
|
|
927
1001
|
font-size: 11px;
|
|
928
1002
|
font-weight: 600;
|
|
1003
|
+
letter-spacing: 0.3px;
|
|
929
1004
|
}
|
|
930
|
-
.task-status.running { background:
|
|
931
|
-
.task-status.done { background:
|
|
932
|
-
.task-status.failed { background:
|
|
1005
|
+
.task-status.running { background: var(--accent-dim); color: var(--accent); }
|
|
1006
|
+
.task-status.done { background: var(--green-dim); color: var(--green); }
|
|
1007
|
+
.task-status.failed { background: var(--red-dim); color: var(--red); }
|
|
933
1008
|
.task-status.pending { background: var(--bg-overlay); color: var(--text-muted); }
|
|
934
1009
|
.task-card .task-goal {
|
|
935
1010
|
font-size: 14px;
|
|
@@ -994,27 +1069,620 @@ body {
|
|
|
994
1069
|
.task-create-form input,
|
|
995
1070
|
.task-create-form textarea {
|
|
996
1071
|
width: 100%;
|
|
997
|
-
padding:
|
|
1072
|
+
padding: 9px 12px;
|
|
998
1073
|
font-size: 14px;
|
|
999
1074
|
border: 1px solid var(--border);
|
|
1000
1075
|
border-radius: var(--radius-md);
|
|
1001
1076
|
background: var(--bg-canvas);
|
|
1002
1077
|
color: var(--text-primary);
|
|
1003
1078
|
outline: none;
|
|
1079
|
+
transition: all var(--transition);
|
|
1004
1080
|
}
|
|
1005
1081
|
.task-create-form input:focus,
|
|
1006
|
-
.task-create-form textarea:focus { border-color: var(--accent); }
|
|
1082
|
+
.task-create-form textarea:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-dim); }
|
|
1007
1083
|
.task-create-form textarea { min-height: 80px; resize: vertical; }
|
|
1008
1084
|
.task-create-form .form-row {
|
|
1009
1085
|
display: grid;
|
|
1010
1086
|
grid-template-columns: 1fr 1fr;
|
|
1011
1087
|
gap: 12px;
|
|
1012
1088
|
}
|
|
1013
|
-
.task-create-form .form-actions {
|
|
1014
|
-
margin-top: 16px;
|
|
1089
|
+
.task-create-form .form-actions {
|
|
1090
|
+
margin-top: 16px;
|
|
1091
|
+
display: flex;
|
|
1092
|
+
gap: 8px;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
/* ===== Memory View Styles ===== */
|
|
1096
|
+
.memory-toolbar {
|
|
1097
|
+
background: var(--bg-surface);
|
|
1098
|
+
border-bottom: 1px solid var(--border);
|
|
1099
|
+
padding: 10px 20px;
|
|
1100
|
+
display: flex;
|
|
1101
|
+
gap: 8px;
|
|
1102
|
+
align-items: center;
|
|
1103
|
+
flex-shrink: 0;
|
|
1104
|
+
}
|
|
1105
|
+
.memory-list {
|
|
1106
|
+
flex: 1;
|
|
1107
|
+
overflow-y: auto;
|
|
1108
|
+
padding: 16px;
|
|
1109
|
+
display: grid;
|
|
1110
|
+
grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
|
|
1111
|
+
gap: 12px;
|
|
1112
|
+
align-content: start;
|
|
1113
|
+
}
|
|
1114
|
+
.memory-card {
|
|
1115
|
+
background: var(--bg-surface);
|
|
1116
|
+
border: 1px solid var(--border);
|
|
1117
|
+
border-radius: var(--radius-lg);
|
|
1118
|
+
padding: 16px;
|
|
1119
|
+
cursor: pointer;
|
|
1120
|
+
transition: all var(--transition);
|
|
1121
|
+
box-shadow: var(--shadow-sm);
|
|
1122
|
+
}
|
|
1123
|
+
.memory-card:hover { border-color: var(--accent); box-shadow: var(--shadow), 0 0 0 1px var(--accent-dim); transform: translateY(-1px); }
|
|
1124
|
+
.memory-card .memory-domain {
|
|
1125
|
+
font-size: 15px;
|
|
1126
|
+
font-weight: 600;
|
|
1127
|
+
color: var(--text-primary);
|
|
1128
|
+
margin-bottom: 6px;
|
|
1129
|
+
display: flex;
|
|
1130
|
+
align-items: center;
|
|
1131
|
+
gap: 8px;
|
|
1132
|
+
}
|
|
1133
|
+
.memory-card .memory-domain .site-type {
|
|
1134
|
+
font-size: 10px;
|
|
1135
|
+
font-weight: 600;
|
|
1136
|
+
padding: 2px 6px;
|
|
1137
|
+
border-radius: var(--radius-sm);
|
|
1138
|
+
background: var(--purple-dim);
|
|
1139
|
+
color: var(--purple);
|
|
1140
|
+
text-transform: uppercase;
|
|
1141
|
+
letter-spacing: 0.5px;
|
|
1142
|
+
}
|
|
1143
|
+
.memory-card .memory-domain .login-badge {
|
|
1144
|
+
font-size: 10px;
|
|
1145
|
+
font-weight: 600;
|
|
1146
|
+
padding: 2px 6px;
|
|
1147
|
+
border-radius: var(--radius-sm);
|
|
1148
|
+
background: var(--orange-dim);
|
|
1149
|
+
color: var(--orange);
|
|
1150
|
+
}
|
|
1151
|
+
.memory-card .memory-meta {
|
|
1152
|
+
font-size: 12px;
|
|
1153
|
+
color: var(--text-muted);
|
|
1154
|
+
margin-bottom: 8px;
|
|
1155
|
+
}
|
|
1156
|
+
.memory-card .memory-patterns {
|
|
1157
|
+
display: flex;
|
|
1158
|
+
flex-wrap: wrap;
|
|
1159
|
+
gap: 4px;
|
|
1160
|
+
}
|
|
1161
|
+
.memory-card .pattern-tag {
|
|
1162
|
+
font-size: 11px;
|
|
1163
|
+
padding: 2px 8px;
|
|
1164
|
+
border-radius: var(--radius-sm);
|
|
1165
|
+
background: var(--bg-overlay);
|
|
1166
|
+
color: var(--text-secondary);
|
|
1167
|
+
border: 1px solid var(--border-subtle);
|
|
1168
|
+
max-width: 200px;
|
|
1169
|
+
overflow: hidden;
|
|
1170
|
+
text-overflow: ellipsis;
|
|
1171
|
+
white-space: nowrap;
|
|
1172
|
+
}
|
|
1173
|
+
.memory-detail {
|
|
1174
|
+
flex: 1;
|
|
1175
|
+
overflow-y: auto;
|
|
1176
|
+
padding: 16px;
|
|
1177
|
+
}
|
|
1178
|
+
.memory-detail-header {
|
|
1179
|
+
display: flex;
|
|
1180
|
+
align-items: center;
|
|
1181
|
+
gap: 12px;
|
|
1182
|
+
margin-bottom: 16px;
|
|
1183
|
+
}
|
|
1184
|
+
.memory-detail .pattern-list {
|
|
1185
|
+
display: flex;
|
|
1186
|
+
flex-direction: column;
|
|
1187
|
+
gap: 8px;
|
|
1188
|
+
}
|
|
1189
|
+
.memory-detail .pattern-item {
|
|
1190
|
+
background: var(--bg-canvas);
|
|
1191
|
+
border: 1px solid var(--border);
|
|
1192
|
+
border-radius: var(--radius-md);
|
|
1193
|
+
padding: 12px 14px;
|
|
1194
|
+
transition: all var(--transition);
|
|
1195
|
+
}
|
|
1196
|
+
.memory-detail .pattern-item:hover { border-color: var(--border); background: var(--bg-surface); }
|
|
1197
|
+
.memory-detail .pattern-type {
|
|
1198
|
+
display: inline-block;
|
|
1199
|
+
font-size: 10px;
|
|
1200
|
+
font-weight: 600;
|
|
1201
|
+
padding: 2px 6px;
|
|
1202
|
+
border-radius: var(--radius-sm);
|
|
1203
|
+
margin-right: 6px;
|
|
1204
|
+
text-transform: uppercase;
|
|
1205
|
+
letter-spacing: 0.5px;
|
|
1206
|
+
}
|
|
1207
|
+
.memory-detail .pattern-type.selector { background: var(--green-dim); color: var(--green); }
|
|
1208
|
+
.memory-detail .pattern-type.navigation_path { background: var(--blue-dim); color: var(--accent); }
|
|
1209
|
+
.memory-detail .pattern-type.login_required { background: var(--orange-dim); color: var(--orange); }
|
|
1210
|
+
.memory-detail .pattern-type.spa_hint { background: var(--purple-dim); color: var(--purple); }
|
|
1211
|
+
.memory-detail .pattern-type.page_structure { background: var(--accent-dim); color: var(--accent); }
|
|
1212
|
+
.memory-detail .pattern-desc { font-size: 13px; color: var(--text-primary); margin-top: 4px; }
|
|
1213
|
+
.memory-detail .pattern-value {
|
|
1214
|
+
font-size: 12px;
|
|
1215
|
+
font-family: 'Monaco', 'Menlo', monospace;
|
|
1216
|
+
color: var(--text-secondary);
|
|
1217
|
+
margin-top: 4px;
|
|
1218
|
+
padding: 4px 8px;
|
|
1219
|
+
background: var(--bg-overlay);
|
|
1220
|
+
border-radius: var(--radius-sm);
|
|
1221
|
+
overflow: hidden;
|
|
1222
|
+
text-overflow: ellipsis;
|
|
1223
|
+
white-space: nowrap;
|
|
1224
|
+
}
|
|
1225
|
+
.memory-detail .pattern-meta {
|
|
1226
|
+
font-size: 11px;
|
|
1227
|
+
color: var(--text-muted);
|
|
1228
|
+
margin-top: 4px;
|
|
1229
|
+
display: flex;
|
|
1230
|
+
gap: 12px;
|
|
1231
|
+
}
|
|
1232
|
+
.memory-empty {
|
|
1233
|
+
display: flex;
|
|
1234
|
+
flex-direction: column;
|
|
1235
|
+
align-items: center;
|
|
1236
|
+
justify-content: center;
|
|
1237
|
+
padding: 60px 20px;
|
|
1238
|
+
color: var(--text-muted);
|
|
1239
|
+
text-align: center;
|
|
1240
|
+
}
|
|
1241
|
+
.memory-empty .empty-icon {
|
|
1242
|
+
font-size: 48px;
|
|
1243
|
+
margin-bottom: 16px;
|
|
1244
|
+
opacity: 0.3;
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
/* ===== Recording View ===== */
|
|
1248
|
+
.rec-state { display: none; }
|
|
1249
|
+
.rec-state.active { display: flex; flex-direction: column; height: 100%; }
|
|
1250
|
+
|
|
1251
|
+
.rec-state.rec-setup {
|
|
1252
|
+
align-items: center;
|
|
1253
|
+
justify-content: center;
|
|
1254
|
+
padding: 60px 20px;
|
|
1255
|
+
gap: 20px;
|
|
1256
|
+
}
|
|
1257
|
+
.rec-setup .rec-url-row {
|
|
1258
|
+
display: flex;
|
|
1259
|
+
gap: 8px;
|
|
1260
|
+
width: 100%;
|
|
1261
|
+
max-width: 600px;
|
|
1262
|
+
}
|
|
1263
|
+
.rec-setup .rec-url-row input {
|
|
1264
|
+
flex: 1;
|
|
1265
|
+
}
|
|
1266
|
+
.rec-setup .rec-hint {
|
|
1267
|
+
font-size: 13px;
|
|
1268
|
+
color: var(--text-muted);
|
|
1269
|
+
text-align: center;
|
|
1270
|
+
max-width: 480px;
|
|
1271
|
+
line-height: 1.6;
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
.rec-split {
|
|
1275
|
+
display: flex;
|
|
1276
|
+
flex: 1;
|
|
1277
|
+
overflow: hidden;
|
|
1278
|
+
}
|
|
1279
|
+
.rec-left-panel {
|
|
1280
|
+
width: 50%;
|
|
1281
|
+
border-right: 1px solid var(--border);
|
|
1282
|
+
display: flex;
|
|
1283
|
+
flex-direction: column;
|
|
1284
|
+
overflow: hidden;
|
|
1285
|
+
}
|
|
1286
|
+
.rec-right-panel {
|
|
1287
|
+
width: 50%;
|
|
1288
|
+
display: flex;
|
|
1289
|
+
flex-direction: column;
|
|
1290
|
+
overflow: hidden;
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
/* Recording indicator bar */
|
|
1294
|
+
.rec-indicator-bar {
|
|
1295
|
+
display: flex;
|
|
1296
|
+
align-items: center;
|
|
1297
|
+
gap: 10px;
|
|
1298
|
+
padding: 10px 16px;
|
|
1299
|
+
background: var(--bg-overlay);
|
|
1300
|
+
border-bottom: 1px solid var(--border);
|
|
1301
|
+
flex-shrink: 0;
|
|
1302
|
+
}
|
|
1303
|
+
.rec-dot {
|
|
1304
|
+
width: 10px;
|
|
1305
|
+
height: 10px;
|
|
1306
|
+
border-radius: 50%;
|
|
1307
|
+
background: var(--red);
|
|
1308
|
+
animation: recPulse 1.2s ease-in-out infinite;
|
|
1309
|
+
}
|
|
1310
|
+
@keyframes recPulse {
|
|
1311
|
+
0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(239,68,68,0.5); }
|
|
1312
|
+
50% { opacity: 0.6; box-shadow: 0 0 0 6px rgba(239,68,68,0); }
|
|
1313
|
+
}
|
|
1314
|
+
.rec-indicator-bar .rec-timer {
|
|
1315
|
+
font-size: 14px;
|
|
1316
|
+
font-weight: 600;
|
|
1317
|
+
color: var(--text-primary);
|
|
1318
|
+
font-variant-numeric: tabular-nums;
|
|
1319
|
+
}
|
|
1320
|
+
.rec-indicator-bar .rec-event-count {
|
|
1321
|
+
font-size: 12px;
|
|
1322
|
+
color: var(--text-secondary);
|
|
1323
|
+
margin-left: auto;
|
|
1324
|
+
}
|
|
1325
|
+
.rec-indicator-bar .btn-danger {
|
|
1326
|
+
flex-shrink: 0;
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
/* Event stream */
|
|
1330
|
+
.rec-event-stream {
|
|
1331
|
+
flex: 1;
|
|
1332
|
+
overflow-y: auto;
|
|
1333
|
+
padding: 8px 0;
|
|
1334
|
+
}
|
|
1335
|
+
.rec-event-item {
|
|
1336
|
+
display: flex;
|
|
1337
|
+
align-items: center;
|
|
1338
|
+
gap: 8px;
|
|
1339
|
+
padding: 6px 16px;
|
|
1340
|
+
font-size: 12px;
|
|
1341
|
+
color: var(--text-secondary);
|
|
1342
|
+
animation: recFadeIn 0.3s ease;
|
|
1343
|
+
}
|
|
1344
|
+
@keyframes recFadeIn {
|
|
1345
|
+
from { opacity: 0; transform: translateY(-4px); }
|
|
1346
|
+
to { opacity: 1; transform: translateY(0); }
|
|
1347
|
+
}
|
|
1348
|
+
.rec-event-item .ev-icon {
|
|
1349
|
+
font-size: 14px;
|
|
1350
|
+
width: 20px;
|
|
1351
|
+
text-align: center;
|
|
1352
|
+
flex-shrink: 0;
|
|
1353
|
+
}
|
|
1354
|
+
.rec-event-item .ev-badge {
|
|
1355
|
+
font-size: 10px;
|
|
1356
|
+
padding: 1px 6px;
|
|
1357
|
+
border-radius: 3px;
|
|
1358
|
+
font-weight: 600;
|
|
1359
|
+
text-transform: uppercase;
|
|
1360
|
+
flex-shrink: 0;
|
|
1361
|
+
}
|
|
1362
|
+
.ev-badge.navigate { background: var(--blue-dim); color: var(--blue); }
|
|
1363
|
+
.ev-badge.click { background: var(--green-dim); color: var(--green); }
|
|
1364
|
+
.ev-badge.type { background: var(--purple-dim); color: var(--purple); }
|
|
1365
|
+
.ev-badge.select { background: var(--orange-dim); color: var(--orange); }
|
|
1366
|
+
.ev-badge.scroll { background: var(--accent-dim); color: var(--accent); }
|
|
1367
|
+
.rec-event-item .ev-desc {
|
|
1368
|
+
flex: 1;
|
|
1369
|
+
overflow: hidden;
|
|
1370
|
+
text-overflow: ellipsis;
|
|
1371
|
+
white-space: nowrap;
|
|
1372
|
+
}
|
|
1373
|
+
.rec-event-item .ev-time {
|
|
1374
|
+
font-size: 10px;
|
|
1375
|
+
color: var(--text-muted);
|
|
1376
|
+
flex-shrink: 0;
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
/* Screenshot preview */
|
|
1380
|
+
.rec-preview-header, .rec-review-header {
|
|
1381
|
+
padding: 10px 16px;
|
|
1382
|
+
font-size: 12px;
|
|
1383
|
+
font-weight: 600;
|
|
1384
|
+
color: var(--text-secondary);
|
|
1385
|
+
border-bottom: 1px solid var(--border);
|
|
1386
|
+
flex-shrink: 0;
|
|
1387
|
+
}
|
|
1388
|
+
.rec-preview-body {
|
|
1389
|
+
flex: 1;
|
|
1390
|
+
overflow: auto;
|
|
1391
|
+
display: flex;
|
|
1392
|
+
align-items: flex-start;
|
|
1393
|
+
justify-content: center;
|
|
1394
|
+
padding: 12px;
|
|
1395
|
+
background: var(--bg-canvas);
|
|
1396
|
+
}
|
|
1397
|
+
.rec-preview-body img {
|
|
1398
|
+
max-width: 100%;
|
|
1399
|
+
border-radius: var(--radius-sm);
|
|
1400
|
+
border: 1px solid var(--border);
|
|
1401
|
+
}
|
|
1402
|
+
.rec-preview-body .skeleton {
|
|
1403
|
+
width: 100%;
|
|
1404
|
+
aspect-ratio: 16/10;
|
|
1405
|
+
background: linear-gradient(90deg, var(--bg-overlay) 25%, var(--bg-elevated) 50%, var(--bg-overlay) 75%);
|
|
1406
|
+
background-size: 200% 100%;
|
|
1407
|
+
animation: shimmer 1.5s infinite;
|
|
1408
|
+
border-radius: var(--radius-sm);
|
|
1409
|
+
}
|
|
1410
|
+
@keyframes shimmer {
|
|
1411
|
+
0% { background-position: 200% 0; }
|
|
1412
|
+
100% { background-position: -200% 0; }
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
/* Review state */
|
|
1416
|
+
.rec-summary {
|
|
1417
|
+
padding: 16px;
|
|
1418
|
+
border-bottom: 1px solid var(--border);
|
|
1419
|
+
display: flex;
|
|
1420
|
+
flex-wrap: wrap;
|
|
1421
|
+
gap: 16px;
|
|
1422
|
+
flex-shrink: 0;
|
|
1423
|
+
}
|
|
1424
|
+
.rec-summary .sum-item {
|
|
1425
|
+
font-size: 12px;
|
|
1426
|
+
color: var(--text-secondary);
|
|
1427
|
+
}
|
|
1428
|
+
.rec-summary .sum-item strong {
|
|
1429
|
+
color: var(--text-primary);
|
|
1430
|
+
font-weight: 600;
|
|
1431
|
+
}
|
|
1432
|
+
.rec-timeline {
|
|
1433
|
+
flex: 1;
|
|
1434
|
+
overflow-y: auto;
|
|
1435
|
+
padding: 8px 0;
|
|
1436
|
+
}
|
|
1437
|
+
.rec-patterns-body {
|
|
1438
|
+
flex: 1;
|
|
1439
|
+
overflow-y: auto;
|
|
1440
|
+
padding: 12px 16px;
|
|
1441
|
+
}
|
|
1442
|
+
.rec-pattern-item {
|
|
1443
|
+
padding: 10px 12px;
|
|
1444
|
+
background: var(--bg-overlay);
|
|
1445
|
+
border-radius: var(--radius-sm);
|
|
1446
|
+
margin-bottom: 8px;
|
|
1447
|
+
font-size: 12px;
|
|
1448
|
+
}
|
|
1449
|
+
.rec-pattern-item .pat-type {
|
|
1450
|
+
font-size: 10px;
|
|
1451
|
+
padding: 1px 6px;
|
|
1452
|
+
border-radius: 3px;
|
|
1453
|
+
font-weight: 600;
|
|
1454
|
+
background: var(--accent-dim);
|
|
1455
|
+
color: var(--accent);
|
|
1456
|
+
margin-right: 6px;
|
|
1457
|
+
}
|
|
1458
|
+
.rec-pattern-item .pat-desc {
|
|
1459
|
+
color: var(--text-primary);
|
|
1460
|
+
margin-top: 4px;
|
|
1461
|
+
}
|
|
1462
|
+
.rec-pattern-item .pat-value {
|
|
1463
|
+
color: var(--text-muted);
|
|
1464
|
+
font-family: monospace;
|
|
1465
|
+
font-size: 11px;
|
|
1466
|
+
margin-top: 4px;
|
|
1467
|
+
word-break: break-all;
|
|
1468
|
+
}
|
|
1469
|
+
.rec-intent-section {
|
|
1470
|
+
padding: 12px 16px;
|
|
1471
|
+
border-bottom: 1px solid var(--border);
|
|
1472
|
+
}
|
|
1473
|
+
.rec-intent-header {
|
|
1474
|
+
display: flex;
|
|
1475
|
+
align-items: center;
|
|
1476
|
+
justify-content: space-between;
|
|
1477
|
+
font-size: 12px;
|
|
1478
|
+
font-weight: 600;
|
|
1479
|
+
color: var(--text-secondary);
|
|
1480
|
+
text-transform: uppercase;
|
|
1481
|
+
letter-spacing: 0.5px;
|
|
1482
|
+
margin-bottom: 8px;
|
|
1483
|
+
}
|
|
1484
|
+
.rec-intent-hint {
|
|
1485
|
+
font-weight: 400;
|
|
1486
|
+
text-transform: none;
|
|
1487
|
+
letter-spacing: 0;
|
|
1488
|
+
color: var(--text-muted);
|
|
1489
|
+
font-size: 11px;
|
|
1490
|
+
}
|
|
1491
|
+
.rec-intent-textarea {
|
|
1492
|
+
width: 100%;
|
|
1493
|
+
min-height: 80px;
|
|
1494
|
+
max-height: 200px;
|
|
1495
|
+
padding: 10px 12px;
|
|
1496
|
+
border: 1px solid var(--border);
|
|
1497
|
+
border-radius: var(--radius-sm);
|
|
1498
|
+
background: var(--bg-canvas);
|
|
1499
|
+
color: var(--text-primary);
|
|
1500
|
+
font-family: inherit;
|
|
1501
|
+
font-size: 13px;
|
|
1502
|
+
line-height: 1.5;
|
|
1503
|
+
resize: vertical;
|
|
1504
|
+
box-sizing: border-box;
|
|
1505
|
+
}
|
|
1506
|
+
.rec-intent-textarea:focus {
|
|
1507
|
+
outline: none;
|
|
1508
|
+
border-color: var(--accent);
|
|
1509
|
+
}
|
|
1510
|
+
.rec-intent-textarea.loading {
|
|
1511
|
+
opacity: 0.5;
|
|
1512
|
+
background: repeating-linear-gradient(
|
|
1513
|
+
-45deg,
|
|
1514
|
+
var(--bg-canvas),
|
|
1515
|
+
var(--bg-canvas) 10px,
|
|
1516
|
+
var(--bg-overlay) 10px,
|
|
1517
|
+
var(--bg-overlay) 20px
|
|
1518
|
+
);
|
|
1519
|
+
background-size: 28px 28px;
|
|
1520
|
+
animation: recShimmer 1s linear infinite;
|
|
1521
|
+
}
|
|
1522
|
+
@keyframes recShimmer {
|
|
1523
|
+
to { background-position: 28px 0; }
|
|
1524
|
+
}
|
|
1525
|
+
.rec-actions {
|
|
1526
|
+
padding: 12px 16px;
|
|
1527
|
+
border-top: 1px solid var(--border);
|
|
1528
|
+
display: flex;
|
|
1529
|
+
gap: 8px;
|
|
1530
|
+
justify-content: flex-end;
|
|
1531
|
+
flex-shrink: 0;
|
|
1532
|
+
}
|
|
1533
|
+
.rec-success-msg {
|
|
1534
|
+
padding: 12px 16px;
|
|
1535
|
+
background: var(--green-dim);
|
|
1536
|
+
color: var(--green);
|
|
1537
|
+
font-size: 13px;
|
|
1538
|
+
border-radius: var(--radius-sm);
|
|
1539
|
+
margin: 12px 16px;
|
|
1540
|
+
display: flex;
|
|
1541
|
+
align-items: center;
|
|
1542
|
+
gap: 8px;
|
|
1543
|
+
}
|
|
1544
|
+
.rec-empty-events {
|
|
1545
|
+
display: flex;
|
|
1546
|
+
flex-direction: column;
|
|
1547
|
+
align-items: center;
|
|
1548
|
+
justify-content: center;
|
|
1549
|
+
flex: 1;
|
|
1550
|
+
color: var(--text-muted);
|
|
1551
|
+
font-size: 13px;
|
|
1552
|
+
gap: 8px;
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
/* ===== Welcome Banner ===== */
|
|
1556
|
+
.welcome-banner {
|
|
1557
|
+
background: linear-gradient(135deg, var(--bg-surface), var(--bg-elevated));
|
|
1558
|
+
border: 1px solid var(--border);
|
|
1559
|
+
border-radius: var(--radius-xl);
|
|
1560
|
+
padding: 28px 32px;
|
|
1561
|
+
margin: 20px;
|
|
1562
|
+
position: relative;
|
|
1563
|
+
box-shadow: var(--shadow-lg), var(--shadow-glow);
|
|
1564
|
+
animation: viewFadeIn 400ms ease;
|
|
1565
|
+
}
|
|
1566
|
+
.welcome-banner .welcome-close {
|
|
1567
|
+
position: absolute;
|
|
1568
|
+
top: 12px;
|
|
1569
|
+
right: 16px;
|
|
1570
|
+
background: none;
|
|
1571
|
+
border: none;
|
|
1572
|
+
color: var(--text-muted);
|
|
1573
|
+
font-size: 18px;
|
|
1574
|
+
cursor: pointer;
|
|
1575
|
+
padding: 4px 8px;
|
|
1576
|
+
border-radius: var(--radius-sm);
|
|
1577
|
+
transition: all var(--transition);
|
|
1578
|
+
}
|
|
1579
|
+
.welcome-banner .welcome-close:hover { color: var(--text-primary); background: var(--bg-overlay); }
|
|
1580
|
+
.welcome-title {
|
|
1581
|
+
font-size: 20px;
|
|
1582
|
+
font-weight: 700;
|
|
1583
|
+
color: var(--text-primary);
|
|
1584
|
+
margin-bottom: 6px;
|
|
1585
|
+
display: flex;
|
|
1586
|
+
align-items: center;
|
|
1587
|
+
gap: 10px;
|
|
1588
|
+
}
|
|
1589
|
+
.welcome-title .logo-icon {
|
|
1590
|
+
font-size: 24px;
|
|
1591
|
+
background: linear-gradient(135deg, var(--accent), var(--purple));
|
|
1592
|
+
-webkit-background-clip: text;
|
|
1593
|
+
-webkit-text-fill-color: transparent;
|
|
1594
|
+
background-clip: text;
|
|
1595
|
+
}
|
|
1596
|
+
.welcome-subtitle {
|
|
1597
|
+
font-size: 13px;
|
|
1598
|
+
color: var(--text-secondary);
|
|
1599
|
+
margin-bottom: 20px;
|
|
1600
|
+
line-height: 1.5;
|
|
1601
|
+
}
|
|
1602
|
+
.welcome-grid {
|
|
1603
|
+
display: grid;
|
|
1604
|
+
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
|
1605
|
+
gap: 12px;
|
|
1606
|
+
}
|
|
1607
|
+
.welcome-card {
|
|
1608
|
+
background: var(--bg-canvas);
|
|
1609
|
+
border: 1px solid var(--border);
|
|
1610
|
+
border-radius: var(--radius-lg);
|
|
1611
|
+
padding: 16px;
|
|
1612
|
+
cursor: pointer;
|
|
1613
|
+
transition: all var(--transition);
|
|
1614
|
+
text-align: left;
|
|
1615
|
+
font-family: inherit;
|
|
1616
|
+
color: inherit;
|
|
1617
|
+
}
|
|
1618
|
+
.welcome-card:hover { border-color: var(--accent); background: var(--bg-overlay); transform: translateY(-2px); box-shadow: var(--shadow); }
|
|
1619
|
+
.welcome-card .wc-icon { font-size: 22px; margin-bottom: 8px; }
|
|
1620
|
+
.welcome-card .wc-name { font-size: 13px; font-weight: 600; color: var(--text-primary); margin-bottom: 4px; }
|
|
1621
|
+
.welcome-card .wc-desc { font-size: 11px; color: var(--text-secondary); line-height: 1.5; }
|
|
1622
|
+
.welcome-tip {
|
|
1623
|
+
margin-top: 16px;
|
|
1624
|
+
padding: 10px 14px;
|
|
1625
|
+
background: var(--accent-dim);
|
|
1626
|
+
border-radius: var(--radius-md);
|
|
1627
|
+
font-size: 12px;
|
|
1628
|
+
color: var(--accent);
|
|
1629
|
+
display: flex;
|
|
1630
|
+
align-items: center;
|
|
1631
|
+
gap: 8px;
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
/* ===== Onboarding Empty States ===== */
|
|
1635
|
+
.empty-state {
|
|
1636
|
+
display: flex;
|
|
1637
|
+
flex-direction: column;
|
|
1638
|
+
align-items: center;
|
|
1639
|
+
justify-content: center;
|
|
1640
|
+
padding: 48px 24px;
|
|
1641
|
+
text-align: center;
|
|
1642
|
+
flex: 1;
|
|
1643
|
+
}
|
|
1644
|
+
.empty-state .es-icon { font-size: 48px; opacity: 0.25; margin-bottom: 12px; }
|
|
1645
|
+
.empty-state .es-title { font-size: 16px; font-weight: 600; color: var(--text-primary); margin-bottom: 6px; }
|
|
1646
|
+
.empty-state .es-desc { font-size: 13px; color: var(--text-secondary); max-width: 420px; line-height: 1.6; margin-bottom: 16px; }
|
|
1647
|
+
.empty-state .es-steps {
|
|
1648
|
+
text-align: left;
|
|
1649
|
+
font-size: 12px;
|
|
1650
|
+
color: var(--text-secondary);
|
|
1651
|
+
line-height: 2;
|
|
1652
|
+
margin-bottom: 16px;
|
|
1653
|
+
}
|
|
1654
|
+
.empty-state .es-steps .step-num {
|
|
1655
|
+
display: inline-flex;
|
|
1656
|
+
align-items: center;
|
|
1657
|
+
justify-content: center;
|
|
1658
|
+
width: 20px;
|
|
1659
|
+
height: 20px;
|
|
1660
|
+
border-radius: 50%;
|
|
1661
|
+
background: var(--accent-dim);
|
|
1662
|
+
color: var(--accent);
|
|
1663
|
+
font-size: 11px;
|
|
1664
|
+
font-weight: 600;
|
|
1665
|
+
margin-right: 8px;
|
|
1666
|
+
}
|
|
1667
|
+
.empty-state .es-examples {
|
|
1015
1668
|
display: flex;
|
|
1016
|
-
|
|
1669
|
+
flex-wrap: wrap;
|
|
1670
|
+
gap: 6px;
|
|
1671
|
+
justify-content: center;
|
|
1672
|
+
margin-top: 4px;
|
|
1017
1673
|
}
|
|
1674
|
+
.empty-state .es-example {
|
|
1675
|
+
font-size: 11px;
|
|
1676
|
+
padding: 4px 10px;
|
|
1677
|
+
background: var(--bg-overlay);
|
|
1678
|
+
border: 1px solid var(--border);
|
|
1679
|
+
border-radius: var(--radius-sm);
|
|
1680
|
+
color: var(--text-secondary);
|
|
1681
|
+
cursor: pointer;
|
|
1682
|
+
transition: all var(--transition);
|
|
1683
|
+
font-family: inherit;
|
|
1684
|
+
}
|
|
1685
|
+
.empty-state .es-example:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-dim); }
|
|
1018
1686
|
|
|
1019
1687
|
/* ===== Responsive ===== */
|
|
1020
1688
|
@media (max-width: 900px) {
|
|
@@ -1025,6 +1693,12 @@ body {
|
|
|
1025
1693
|
.chat-panel, .preview-panel { width: 100%; border-right: none; }
|
|
1026
1694
|
.preview-panel { border-top: 1px solid var(--border); }
|
|
1027
1695
|
.tasks-list { grid-template-columns: 1fr; }
|
|
1696
|
+
.memory-list { grid-template-columns: 1fr !important; }
|
|
1697
|
+
.rec-split { flex-direction: column; }
|
|
1698
|
+
.rec-left-panel, .rec-right-panel { width: 100%; border-right: none; }
|
|
1699
|
+
.rec-right-panel { border-top: 1px solid var(--border); }
|
|
1700
|
+
.welcome-banner { margin: 12px; padding: 20px; }
|
|
1701
|
+
.welcome-grid { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 8px; }
|
|
1028
1702
|
}
|
|
1029
1703
|
@media (max-width: 768px) {
|
|
1030
1704
|
.app-logo { font-size: 13px; }
|
|
@@ -1036,11 +1710,17 @@ body {
|
|
|
1036
1710
|
}
|
|
1037
1711
|
@media (max-width: 480px) {
|
|
1038
1712
|
.preview-panel { display: none; }
|
|
1713
|
+
.rec-right-panel { display: none; }
|
|
1714
|
+
.rec-left-panel { width: 100%; border-right: none; }
|
|
1039
1715
|
.chat-panel { width: 100%; border-right: none; }
|
|
1040
1716
|
.chat-input-bar input { font-size: 16px; padding: 12px 14px; }
|
|
1041
1717
|
.msg-tool-result .result-content { max-height: 80px; }
|
|
1042
1718
|
.header-right { gap: 4px; }
|
|
1043
1719
|
.btn { padding: 5px 8px; font-size: 12px; }
|
|
1720
|
+
.tab-icon { display: none; }
|
|
1721
|
+
.welcome-banner { margin: 8px; padding: 16px; }
|
|
1722
|
+
.welcome-grid { grid-template-columns: 1fr 1fr; }
|
|
1723
|
+
.welcome-tip { flex-direction: column; text-align: center; }
|
|
1044
1724
|
}
|
|
1045
1725
|
</style>
|
|
1046
1726
|
</head>
|
|
@@ -1049,25 +1729,62 @@ body {
|
|
|
1049
1729
|
<header class="app-header">
|
|
1050
1730
|
<span class="app-logo">AI Browser</span>
|
|
1051
1731
|
<div class="nav-tabs">
|
|
1052
|
-
<button class="nav-tab active" onclick="switchView('semantic')">Semantic</button>
|
|
1053
|
-
<button class="nav-tab" onclick="switchView('agent')">Agent</button>
|
|
1054
|
-
<button class="nav-tab" onclick="switchView('tasks')">Tasks</button>
|
|
1732
|
+
<button class="nav-tab active" data-view="semantic" onclick="switchView('semantic')" title="Analyze page structure and elements"><span class="tab-icon">🔎</span> Semantic</button>
|
|
1733
|
+
<button class="nav-tab" data-view="agent" onclick="switchView('agent')" title="AI agent that browses the web for you"><span class="tab-icon">🤖</span> Agent</button>
|
|
1734
|
+
<button class="nav-tab" data-view="tasks" onclick="switchView('tasks')" title="Run deterministic task templates"><span class="tab-icon">📋</span> Tasks</button>
|
|
1735
|
+
<button class="nav-tab" data-view="memory" onclick="switchView('memory')" title="Site knowledge learned from browsing"><span class="tab-icon">🧠</span> Memory</button>
|
|
1736
|
+
<button class="nav-tab" data-view="record" onclick="switchView('record')" title="Record browsing sessions to build memory"><span class="tab-icon">⏺</span> Record</button>
|
|
1055
1737
|
</div>
|
|
1056
1738
|
<div class="header-right" id="headerRight">
|
|
1057
1739
|
<button class="btn" id="btnCloseSession" style="display:none" onclick="Sem.closeSession()">Close Session</button>
|
|
1058
1740
|
<button class="btn btn-danger" id="btnStop" style="display:none" onclick="Agent.stop()">Stop</button>
|
|
1059
1741
|
<button class="btn" id="btnClear" style="display:none" onclick="Agent.clearChat()">Clear</button>
|
|
1060
|
-
<button class="btn" id="btnSettings" onclick="openSettings()"
|
|
1742
|
+
<button class="btn" id="btnSettings" onclick="openSettings()" title="Configure LLM provider (API key, model, base URL)">⚙ Settings</button>
|
|
1061
1743
|
</div>
|
|
1062
1744
|
</header>
|
|
1063
1745
|
|
|
1746
|
+
<!-- ===== Welcome Banner ===== -->
|
|
1747
|
+
<div class="welcome-banner" id="welcomeBanner" style="display:none;">
|
|
1748
|
+
<button class="welcome-close" onclick="dismissWelcome()" title="Dismiss" aria-label="Dismiss welcome banner">×</button>
|
|
1749
|
+
<div class="welcome-title"><span class="logo-icon">🌐</span> Welcome to AI Browser</div>
|
|
1750
|
+
<div class="welcome-subtitle">An AI-powered browser automation platform. Analyze pages, run autonomous agents, create repeatable tasks, and build site memory — all from one interface.</div>
|
|
1751
|
+
<div class="welcome-grid">
|
|
1752
|
+
<button class="welcome-card" onclick="dismissWelcome();switchView('semantic')">
|
|
1753
|
+
<div class="wc-icon">🔎</div>
|
|
1754
|
+
<div class="wc-name">Semantic</div>
|
|
1755
|
+
<div class="wc-desc">Open any URL and inspect its structure — interactive elements, markdown content, and screenshots.</div>
|
|
1756
|
+
</button>
|
|
1757
|
+
<button class="welcome-card" onclick="dismissWelcome();switchView('agent')">
|
|
1758
|
+
<div class="wc-icon">🤖</div>
|
|
1759
|
+
<div class="wc-name">Agent</div>
|
|
1760
|
+
<div class="wc-desc">Give a natural-language task. The AI agent opens a browser and completes it autonomously.</div>
|
|
1761
|
+
</button>
|
|
1762
|
+
<button class="welcome-card" onclick="dismissWelcome();switchView('tasks')">
|
|
1763
|
+
<div class="wc-icon">📋</div>
|
|
1764
|
+
<div class="wc-name">Tasks</div>
|
|
1765
|
+
<div class="wc-desc">Create and run deterministic task templates — repeatable browser automations with artifacts.</div>
|
|
1766
|
+
</button>
|
|
1767
|
+
<button class="welcome-card" onclick="dismissWelcome();switchView('memory')">
|
|
1768
|
+
<div class="wc-icon">🧠</div>
|
|
1769
|
+
<div class="wc-name">Memory</div>
|
|
1770
|
+
<div class="wc-desc">View site knowledge cards — selectors, navigation paths, and patterns the system has learned.</div>
|
|
1771
|
+
</button>
|
|
1772
|
+
<button class="welcome-card" onclick="dismissWelcome();switchView('record')">
|
|
1773
|
+
<div class="wc-icon">⏺</div>
|
|
1774
|
+
<div class="wc-name">Record</div>
|
|
1775
|
+
<div class="wc-desc">Record your own browsing session. Your actions are captured and saved as reusable site memory.</div>
|
|
1776
|
+
</button>
|
|
1777
|
+
</div>
|
|
1778
|
+
<div class="welcome-tip">💡 First time? Start with <strong style="margin:0 4px;">Semantic</strong> to explore a page, or jump to <strong style="margin:0 4px;">Agent</strong> to let AI do the work. Configure your LLM in <strong style="margin:0 4px;">Settings</strong> (top right).</div>
|
|
1779
|
+
</div>
|
|
1780
|
+
|
|
1064
1781
|
<!-- ===== Main ===== -->
|
|
1065
1782
|
<main class="app-main">
|
|
1066
1783
|
<!-- Semantic View -->
|
|
1067
1784
|
<div id="view-semantic" class="view active">
|
|
1068
1785
|
<!-- Toolbar -->
|
|
1069
1786
|
<div class="sem-toolbar">
|
|
1070
|
-
<input type="text" id="semUrlInput" placeholder="Enter URL, e.g. https://www.baidu.com" value="https://www.baidu.com">
|
|
1787
|
+
<input type="text" id="semUrlInput" placeholder="Enter URL to analyze, e.g. https://www.baidu.com" value="https://www.baidu.com">
|
|
1071
1788
|
<button class="btn btn-primary" id="semAnalyzeBtn" onclick="Sem.analyze()">Analyze</button>
|
|
1072
1789
|
<button class="btn" id="semRefreshBtn" style="display:none" onclick="Sem.refreshElements()">Refresh</button>
|
|
1073
1790
|
<button class="btn" id="semSendToAgent" style="display:none" onclick="sendToAgent()">Send to Agent</button>
|
|
@@ -1096,7 +1813,17 @@ body {
|
|
|
1096
1813
|
</div>
|
|
1097
1814
|
<div class="filter-bar hidden" id="semFilterBar"></div>
|
|
1098
1815
|
<div class="sem-content-area" id="semLeftContent">
|
|
1099
|
-
<div class="
|
|
1816
|
+
<div class="empty-state">
|
|
1817
|
+
<div class="es-icon">🔎</div>
|
|
1818
|
+
<div class="es-title">Semantic Page Analysis</div>
|
|
1819
|
+
<div class="es-desc">Enter a URL above and click Analyze. The page will be parsed into structured markdown and interactive elements — buttons, links, inputs — each with a semantic ID you can use to automate interactions.</div>
|
|
1820
|
+
<div class="es-steps">
|
|
1821
|
+
<div><span class="step-num">1</span>Enter a URL in the toolbar above</div>
|
|
1822
|
+
<div><span class="step-num">2</span>Click <strong>Analyze</strong> to open the page</div>
|
|
1823
|
+
<div><span class="step-num">3</span>Browse elements, click or type into them</div>
|
|
1824
|
+
<div><span class="step-num">4</span>Click <strong>Send to Agent</strong> to hand off the session</div>
|
|
1825
|
+
</div>
|
|
1826
|
+
</div>
|
|
1100
1827
|
</div>
|
|
1101
1828
|
</div>
|
|
1102
1829
|
|
|
@@ -1107,7 +1834,7 @@ body {
|
|
|
1107
1834
|
<button class="sem-content-tab" onclick="Sem.switchRightTab('raw', this)">Raw Markdown</button>
|
|
1108
1835
|
</div>
|
|
1109
1836
|
<div class="sem-right-area" id="semRightContent">
|
|
1110
|
-
<div class="placeholder-text">
|
|
1837
|
+
<div class="placeholder-text" style="padding:32px 20px;font-size:13px;">Screenshot and raw markdown will appear here after analysis.</div>
|
|
1111
1838
|
</div>
|
|
1112
1839
|
</div>
|
|
1113
1840
|
</div>
|
|
@@ -1127,7 +1854,22 @@ body {
|
|
|
1127
1854
|
<!-- Left: Chat -->
|
|
1128
1855
|
<div class="chat-panel">
|
|
1129
1856
|
<div class="chat-messages" id="chatMessages">
|
|
1130
|
-
<div class="
|
|
1857
|
+
<div class="empty-state" id="agentEmptyState">
|
|
1858
|
+
<div class="es-icon">🤖</div>
|
|
1859
|
+
<div class="es-title">AI Browsing Agent</div>
|
|
1860
|
+
<div class="es-desc">Describe a task in natural language. The AI agent will open a browser, navigate pages, click buttons, fill forms, and extract information — all autonomously.</div>
|
|
1861
|
+
<div class="es-steps">
|
|
1862
|
+
<div><span class="step-num">1</span>Type a task in the input below</div>
|
|
1863
|
+
<div><span class="step-num">2</span>Click <strong>Send</strong> — the agent starts working</div>
|
|
1864
|
+
<div><span class="step-num">3</span>Watch progress in real-time on the right panel</div>
|
|
1865
|
+
</div>
|
|
1866
|
+
<div style="font-size:12px;color:var(--text-muted);margin-bottom:8px;">Try an example:</div>
|
|
1867
|
+
<div class="es-examples">
|
|
1868
|
+
<button class="es-example" onclick="document.getElementById('taskInput').value=this.textContent;document.getElementById('taskInput').focus()">Open Baidu and search for weather</button>
|
|
1869
|
+
<button class="es-example" onclick="document.getElementById('taskInput').value=this.textContent;document.getElementById('taskInput').focus()">Go to GitHub trending and list top 5 repos</button>
|
|
1870
|
+
<button class="es-example" onclick="document.getElementById('taskInput').value=this.textContent;document.getElementById('taskInput').focus()">Search Amazon for wireless headphones under $50</button>
|
|
1871
|
+
</div>
|
|
1872
|
+
</div>
|
|
1131
1873
|
</div>
|
|
1132
1874
|
<div class="typing-indicator" id="typingIndicator">
|
|
1133
1875
|
<div class="typing-dot"></div>
|
|
@@ -1143,7 +1885,7 @@ body {
|
|
|
1143
1885
|
<span id="progressSteps"></span>
|
|
1144
1886
|
</div>
|
|
1145
1887
|
<div class="chat-input-bar">
|
|
1146
|
-
<input type="text" id="taskInput" placeholder="
|
|
1888
|
+
<input type="text" id="taskInput" placeholder="Describe a task, e.g.: Open Baidu and search for weather, then summarize the results">
|
|
1147
1889
|
<button class="btn-send" id="sendBtn" onclick="Agent.start()">Send</button>
|
|
1148
1890
|
</div>
|
|
1149
1891
|
</div>
|
|
@@ -1159,7 +1901,7 @@ body {
|
|
|
1159
1901
|
<span class="preview-url" id="previewUrl"></span>
|
|
1160
1902
|
</div>
|
|
1161
1903
|
<div class="preview-body" id="previewBody">
|
|
1162
|
-
<div class="placeholder-text">
|
|
1904
|
+
<div class="placeholder-text" style="padding:32px 20px;font-size:13px;">Page markdown and screenshots will appear here as the agent browses.</div>
|
|
1163
1905
|
</div>
|
|
1164
1906
|
</div>
|
|
1165
1907
|
</div>
|
|
@@ -1170,13 +1912,103 @@ body {
|
|
|
1170
1912
|
<div class="tasks-toolbar">
|
|
1171
1913
|
<button class="btn btn-primary" id="btnNewTask" onclick="Tasks.showCreate()">+ New Task</button>
|
|
1172
1914
|
<button class="btn" onclick="Tasks.refreshList()">Refresh</button>
|
|
1915
|
+
<span style="font-size:12px;color:var(--text-muted);margin-left:auto;">Run deterministic task templates — repeatable browser automations</span>
|
|
1173
1916
|
</div>
|
|
1174
1917
|
<div id="tasksList" class="tasks-list">
|
|
1175
|
-
<div class="
|
|
1918
|
+
<div class="empty-state" id="tasksEmptyState">
|
|
1919
|
+
<div class="es-icon">📋</div>
|
|
1920
|
+
<div class="es-title">Task Templates</div>
|
|
1921
|
+
<div class="es-desc">Tasks are deterministic, repeatable browser automations. Create a task template with a URL and goal, then run it anytime. Unlike the Agent (which uses AI reasoning), tasks follow predefined steps.</div>
|
|
1922
|
+
<div class="es-steps">
|
|
1923
|
+
<div><span class="step-num">1</span>Click <strong>+ New Task</strong> to create a template</div>
|
|
1924
|
+
<div><span class="step-num">2</span>Set a URL, goal, and optional parameters</div>
|
|
1925
|
+
<div><span class="step-num">3</span>Run the task and view results with artifacts</div>
|
|
1926
|
+
</div>
|
|
1927
|
+
</div>
|
|
1176
1928
|
</div>
|
|
1177
1929
|
<div id="taskDetail" class="task-detail" style="display:none"></div>
|
|
1178
1930
|
<div id="taskCreate" class="task-detail" style="display:none"></div>
|
|
1179
1931
|
</div>
|
|
1932
|
+
|
|
1933
|
+
<!-- Memory View -->
|
|
1934
|
+
<div id="view-memory" class="view">
|
|
1935
|
+
<div class="memory-toolbar">
|
|
1936
|
+
<button class="btn" onclick="Memory.refreshList()">Refresh</button>
|
|
1937
|
+
<span style="font-size:12px;color:var(--text-muted);margin-left:auto;">Site knowledge cards — selectors, navigation paths, and patterns learned from browsing</span>
|
|
1938
|
+
</div>
|
|
1939
|
+
<div id="memoryList" class="memory-list">
|
|
1940
|
+
<div class="placeholder-text">Loading memory...</div>
|
|
1941
|
+
</div>
|
|
1942
|
+
<div id="memoryDetail" class="memory-detail" style="display:none"></div>
|
|
1943
|
+
</div>
|
|
1944
|
+
|
|
1945
|
+
<!-- Record View -->
|
|
1946
|
+
<div id="view-record" class="view">
|
|
1947
|
+
<!-- Setup State -->
|
|
1948
|
+
<div id="rec-setup" class="rec-state active rec-setup">
|
|
1949
|
+
<div style="font-size:36px;opacity:0.3;margin-bottom:8px">●</div>
|
|
1950
|
+
<div style="font-size:16px;font-weight:600;color:var(--text-primary)">Record a Browsing Session</div>
|
|
1951
|
+
<div class="rec-hint">Enter a URL to start recording. A browser window will open — interact with the page normally. Your actions will be captured and converted to site memory.</div>
|
|
1952
|
+
<div class="rec-url-row">
|
|
1953
|
+
<input type="text" id="recUrlInput" placeholder="https://example.com" value="">
|
|
1954
|
+
<button class="btn btn-primary" id="recStartBtn" onclick="Rec.start()">Start Recording</button>
|
|
1955
|
+
</div>
|
|
1956
|
+
</div>
|
|
1957
|
+
|
|
1958
|
+
<!-- Recording State -->
|
|
1959
|
+
<div id="rec-active" class="rec-state">
|
|
1960
|
+
<div class="rec-split">
|
|
1961
|
+
<div class="rec-left-panel">
|
|
1962
|
+
<div class="rec-indicator-bar">
|
|
1963
|
+
<span class="rec-dot"></span>
|
|
1964
|
+
<span class="rec-timer" id="recTimer">00:00</span>
|
|
1965
|
+
<span class="rec-event-count" id="recEventCount">0 events</span>
|
|
1966
|
+
<button class="btn btn-danger" onclick="Rec.stop()">Stop Recording</button>
|
|
1967
|
+
</div>
|
|
1968
|
+
<div class="rec-event-stream" id="recEventStream">
|
|
1969
|
+
<div class="rec-empty-events">
|
|
1970
|
+
<span style="font-size:24px;opacity:0.3">👁</span>
|
|
1971
|
+
<span>Waiting for interactions...</span>
|
|
1972
|
+
</div>
|
|
1973
|
+
</div>
|
|
1974
|
+
</div>
|
|
1975
|
+
<div class="rec-right-panel">
|
|
1976
|
+
<div class="rec-preview-header">Live Preview</div>
|
|
1977
|
+
<div class="rec-preview-body" id="recPreviewBody">
|
|
1978
|
+
<div class="skeleton"></div>
|
|
1979
|
+
</div>
|
|
1980
|
+
</div>
|
|
1981
|
+
</div>
|
|
1982
|
+
</div>
|
|
1983
|
+
|
|
1984
|
+
<!-- Review State -->
|
|
1985
|
+
<div id="rec-review" class="rec-state">
|
|
1986
|
+
<div class="rec-split">
|
|
1987
|
+
<div class="rec-left-panel">
|
|
1988
|
+
<div class="rec-summary" id="recSummary"></div>
|
|
1989
|
+
<div class="rec-intent-section" id="recIntentSection">
|
|
1990
|
+
<div class="rec-intent-header">
|
|
1991
|
+
<span>Task Intent</span>
|
|
1992
|
+
<span class="rec-intent-hint" id="recIntentHint">AI summarizing...</span>
|
|
1993
|
+
</div>
|
|
1994
|
+
<textarea id="recIntentText" class="rec-intent-textarea" rows="6" maxlength="1000" aria-label="Task Intent" placeholder="LLM is analyzing your recording..."></textarea>
|
|
1995
|
+
</div>
|
|
1996
|
+
<div class="rec-timeline" id="recTimeline"></div>
|
|
1997
|
+
</div>
|
|
1998
|
+
<div class="rec-right-panel">
|
|
1999
|
+
<div class="rec-review-header">Extracted Patterns</div>
|
|
2000
|
+
<div class="rec-patterns-body" id="recPatternsBody">
|
|
2001
|
+
<div class="placeholder-text">No patterns extracted.</div>
|
|
2002
|
+
</div>
|
|
2003
|
+
<div id="recSuccessArea"></div>
|
|
2004
|
+
<div class="rec-actions" id="recActions">
|
|
2005
|
+
<button class="btn" onclick="Rec.discard()">Discard</button>
|
|
2006
|
+
<button class="btn btn-primary" id="recSaveBtn" onclick="Rec.saveToMemory()">Save to Memory</button>
|
|
2007
|
+
</div>
|
|
2008
|
+
</div>
|
|
2009
|
+
</div>
|
|
2010
|
+
</div>
|
|
2011
|
+
</div>
|
|
1180
2012
|
</main>
|
|
1181
2013
|
<div class="modal-overlay hidden" id="settingsModal">
|
|
1182
2014
|
<div class="modal">
|
|
@@ -1189,6 +2021,8 @@ body {
|
|
|
1189
2021
|
<input type="password" id="cfgApiKey" placeholder="sk-...">
|
|
1190
2022
|
<label>Max Iterations</label>
|
|
1191
2023
|
<input type="number" id="cfgMaxIterations" placeholder="Default (server-controlled)" min="1" max="1000">
|
|
2024
|
+
<label>LLM Timeout (seconds)</label>
|
|
2025
|
+
<input type="number" id="cfgTimeout" placeholder="300" min="10" max="600">
|
|
1192
2026
|
<label style="display:flex;align-items:center;gap:8px;margin-top:14px;cursor:pointer;">
|
|
1193
2027
|
<input type="checkbox" id="cfgHeadless" checked style="width:auto;cursor:pointer;">
|
|
1194
2028
|
Headless Mode
|
|
@@ -1247,6 +2081,7 @@ function openSettings() {
|
|
|
1247
2081
|
document.getElementById('cfgModel').value = s.model || '';
|
|
1248
2082
|
document.getElementById('cfgApiKey').value = s.apiKey || '';
|
|
1249
2083
|
document.getElementById('cfgMaxIterations').value = s.maxIterations || '';
|
|
2084
|
+
document.getElementById('cfgTimeout').value = s.timeout || '';
|
|
1250
2085
|
document.getElementById('cfgHeadless').checked = s.headless !== false;
|
|
1251
2086
|
document.getElementById('settingsModal').classList.remove('hidden');
|
|
1252
2087
|
}
|
|
@@ -1261,6 +2096,7 @@ function saveSettings() {
|
|
|
1261
2096
|
model: document.getElementById('cfgModel').value.trim(),
|
|
1262
2097
|
apiKey: document.getElementById('cfgApiKey').value.trim(),
|
|
1263
2098
|
maxIterations: parseInt(document.getElementById('cfgMaxIterations').value) || 0,
|
|
2099
|
+
timeout: parseInt(document.getElementById('cfgTimeout').value) || 0,
|
|
1264
2100
|
headless: document.getElementById('cfgHeadless').checked,
|
|
1265
2101
|
};
|
|
1266
2102
|
localStorage.setItem(SETTINGS_KEY, JSON.stringify(settings));
|
|
@@ -1306,6 +2142,18 @@ function testConnection() {
|
|
|
1306
2142
|
|
|
1307
2143
|
// ========== Navigation ==========
|
|
1308
2144
|
function switchView(view) {
|
|
2145
|
+
// Warn if leaving record view while recording or reviewing
|
|
2146
|
+
if (App.currentView === 'record' && view !== 'record' && Rec.state === 'recording') {
|
|
2147
|
+
if (!confirm('Recording is in progress. Leave and stop recording?')) return;
|
|
2148
|
+
Rec.stopPolling();
|
|
2149
|
+
Rec._stopBackendRecording();
|
|
2150
|
+
Rec.cleanup();
|
|
2151
|
+
Rec.reset();
|
|
2152
|
+
}
|
|
2153
|
+
if (App.currentView === 'record' && view !== 'record' && Rec.state === 'review') {
|
|
2154
|
+
Rec.cleanup();
|
|
2155
|
+
Rec.reset();
|
|
2156
|
+
}
|
|
1309
2157
|
App.currentView = view;
|
|
1310
2158
|
location.hash = view;
|
|
1311
2159
|
// Clean up Tasks polling when leaving
|
|
@@ -1316,11 +2164,20 @@ function switchView(view) {
|
|
|
1316
2164
|
document.getElementById('view-semantic').classList.toggle('active', view === 'semantic');
|
|
1317
2165
|
document.getElementById('view-agent').classList.toggle('active', view === 'agent');
|
|
1318
2166
|
document.getElementById('view-tasks').classList.toggle('active', view === 'tasks');
|
|
2167
|
+
document.getElementById('view-memory').classList.toggle('active', view === 'memory');
|
|
2168
|
+
document.getElementById('view-record').classList.toggle('active', view === 'record');
|
|
1319
2169
|
// Update nav tabs
|
|
1320
2170
|
document.querySelectorAll('.nav-tab').forEach(function(tab) {
|
|
1321
|
-
tab.classList.toggle('active', tab.
|
|
2171
|
+
tab.classList.toggle('active', tab.getAttribute('data-view') === view);
|
|
1322
2172
|
});
|
|
1323
2173
|
if (view === 'tasks') Tasks.refreshList();
|
|
2174
|
+
if (view === 'memory') Memory.refreshList();
|
|
2175
|
+
if (view === 'record') {
|
|
2176
|
+
var saved = localStorage.getItem('rec_last_url');
|
|
2177
|
+
if (saved && !document.getElementById('recUrlInput').value) {
|
|
2178
|
+
document.getElementById('recUrlInput').value = saved;
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
1324
2181
|
updateHeaderButtons();
|
|
1325
2182
|
}
|
|
1326
2183
|
|
|
@@ -1340,7 +2197,7 @@ function updateHeaderButtons() {
|
|
|
1340
2197
|
// Hash routing
|
|
1341
2198
|
window.addEventListener('hashchange', function() {
|
|
1342
2199
|
var hash = location.hash.replace('#', '');
|
|
1343
|
-
var view = (hash === 'agent') ? 'agent' : (hash === 'tasks') ? 'tasks' : 'semantic';
|
|
2200
|
+
var view = (hash === 'agent') ? 'agent' : (hash === 'tasks') ? 'tasks' : (hash === 'memory') ? 'memory' : (hash === 'record') ? 'record' : 'semantic';
|
|
1344
2201
|
if (view !== App.currentView) switchView(view);
|
|
1345
2202
|
});
|
|
1346
2203
|
|
|
@@ -1856,8 +2713,8 @@ var Agent = {
|
|
|
1856
2713
|
document.getElementById('previewUrl').textContent = '';
|
|
1857
2714
|
self.renderPreview();
|
|
1858
2715
|
|
|
1859
|
-
// Remove placeholder
|
|
1860
|
-
var ph = document.getElementById('chatMessages').querySelector('.placeholder-text');
|
|
2716
|
+
// Remove placeholder / empty state
|
|
2717
|
+
var ph = document.getElementById('chatMessages').querySelector('.placeholder-text') || document.getElementById('agentEmptyState');
|
|
1861
2718
|
if (ph) ph.remove();
|
|
1862
2719
|
|
|
1863
2720
|
document.getElementById('sendBtn').disabled = true;
|
|
@@ -1873,6 +2730,7 @@ var Agent = {
|
|
|
1873
2730
|
if (settings.baseURL) body.baseURL = settings.baseURL;
|
|
1874
2731
|
if (settings.model) body.model = settings.model;
|
|
1875
2732
|
if (settings.maxIterations) body.maxIterations = settings.maxIterations;
|
|
2733
|
+
if (settings.timeout && settings.timeout > 0) body.timeout = settings.timeout;
|
|
1876
2734
|
if (settings.headless === false) body.headless = false;
|
|
1877
2735
|
if (self.conversationHistory.length > 0) body.messages = self.conversationHistory;
|
|
1878
2736
|
|
|
@@ -1997,6 +2855,9 @@ Agent.handleEvent = function(ev) {
|
|
|
1997
2855
|
document.getElementById('progressInfo').classList.add('active');
|
|
1998
2856
|
}
|
|
1999
2857
|
break;
|
|
2858
|
+
case 'memory_recall':
|
|
2859
|
+
self.addMsg('memory_recall', ev.domain, ev.iteration, ev.patternCount);
|
|
2860
|
+
break;
|
|
2000
2861
|
case 'done':
|
|
2001
2862
|
self.addMsg('done', ev.success ? (ev.result || 'Done') : (ev.error || 'Failed'), null, ev.success);
|
|
2002
2863
|
var summary = 'Total steps: ' + ev.iterations;
|
|
@@ -2075,6 +2936,14 @@ Agent.addMsg = function(type, content, iteration, extra, extra2) {
|
|
|
2075
2936
|
el.className = 'msg msg-error';
|
|
2076
2937
|
el.innerHTML = (iteration ? this.stepTag(iteration) : '') + 'Error: ' + esc(content);
|
|
2077
2938
|
break;
|
|
2939
|
+
case 'memory_recall':
|
|
2940
|
+
el.className = 'msg msg-memory-recall';
|
|
2941
|
+
el.innerHTML = this.stepTag(iteration) +
|
|
2942
|
+
'<div class="memory-recall-hint">' +
|
|
2943
|
+
'<span class="memory-icon">🧠</span> ' +
|
|
2944
|
+
'<span>Recalled site memory for <strong>' + esc(content) + '</strong> (' + (extra || 0) + ' patterns)</span>' +
|
|
2945
|
+
'</div>';
|
|
2946
|
+
break;
|
|
2078
2947
|
case 'done':
|
|
2079
2948
|
el.className = 'msg msg-done ' + (extra ? 'success' : 'fail');
|
|
2080
2949
|
var title = extra ? 'Task Completed' : 'Task Failed';
|
|
@@ -2367,7 +3236,7 @@ var Tasks = {
|
|
|
2367
3236
|
fetch('/v1/tasks').then(function(r) { return r.json(); }).then(function(data) {
|
|
2368
3237
|
var tasks = data.tasks || [];
|
|
2369
3238
|
if (tasks.length === 0) {
|
|
2370
|
-
list.innerHTML = '<div class="
|
|
3239
|
+
list.innerHTML = '<div class="empty-state"><div class="es-icon">📋</div><div class="es-title">No Tasks Yet</div><div class="es-desc">Click <strong>+ New Task</strong> above to create a deterministic task template. Tasks are repeatable browser automations that run the same steps every time.</div><div class="es-steps"><div><span class="step-num">1</span>Click <strong>+ New Task</strong> to create a template</div><div><span class="step-num">2</span>Set a URL, goal, and optional parameters</div><div><span class="step-num">3</span>Run the task and view results with artifacts</div></div></div>';
|
|
2371
3240
|
return;
|
|
2372
3241
|
}
|
|
2373
3242
|
list.innerHTML = '';
|
|
@@ -2537,13 +3406,662 @@ document.getElementById('typeValue').addEventListener('keypress', function(e) {
|
|
|
2537
3406
|
});
|
|
2538
3407
|
</script>
|
|
2539
3408
|
|
|
3409
|
+
<script>
|
|
3410
|
+
// ===== Memory View =====
|
|
3411
|
+
var Memory = {
|
|
3412
|
+
refreshList: function() {
|
|
3413
|
+
var list = document.getElementById('memoryList');
|
|
3414
|
+
list.innerHTML = '<div class="placeholder-text">Loading...</div>';
|
|
3415
|
+
document.getElementById('memoryDetail').style.display = 'none';
|
|
3416
|
+
list.style.display = '';
|
|
3417
|
+
|
|
3418
|
+
fetch('/v1/memory').then(function(r) { return r.json(); }).then(function(data) {
|
|
3419
|
+
var entries = data.entries || [];
|
|
3420
|
+
if (entries.length === 0) {
|
|
3421
|
+
list.innerHTML = '<div class="empty-state"><div class="es-icon">🧠</div><div class="es-title">No Site Memories Yet</div><div class="es-desc">Memories are automatically learned when the agent completes tasks, or you can manually record browsing sessions in the <strong>Record</strong> tab. Each memory card stores selectors, navigation paths, and interaction patterns for a domain.</div><div class="es-examples"><span class="es-example" onclick="switchView(\'record\')">Go to Record tab</span><span class="es-example" onclick="switchView(\'agent\')">Run an Agent task</span></div></div>';
|
|
3422
|
+
return;
|
|
3423
|
+
}
|
|
3424
|
+
list.innerHTML = '';
|
|
3425
|
+
entries.forEach(function(entry) {
|
|
3426
|
+
var card = document.createElement('div');
|
|
3427
|
+
card.className = 'memory-card';
|
|
3428
|
+
card.onclick = function() { Memory.showDetail(entry.domain); };
|
|
3429
|
+
|
|
3430
|
+
var badges = '';
|
|
3431
|
+
if (entry.siteType && entry.siteType !== 'unknown') {
|
|
3432
|
+
badges += '<span class="site-type">' + esc(entry.siteType) + '</span>';
|
|
3433
|
+
}
|
|
3434
|
+
if (entry.requiresLogin) {
|
|
3435
|
+
badges += '<span class="login-badge">Login</span>';
|
|
3436
|
+
}
|
|
3437
|
+
|
|
3438
|
+
var patterns = (entry.topPatterns || []).slice(0, 3);
|
|
3439
|
+
var patternHtml = patterns.map(function(p) {
|
|
3440
|
+
return '<span class="pattern-tag">' + esc(p) + '</span>';
|
|
3441
|
+
}).join('');
|
|
3442
|
+
|
|
3443
|
+
var lastUsed = entry.lastUsedAt ? new Date(entry.lastUsedAt).toLocaleDateString() : '-';
|
|
3444
|
+
|
|
3445
|
+
card.innerHTML =
|
|
3446
|
+
'<div class="memory-domain">' + esc(entry.domain) + badges + '</div>' +
|
|
3447
|
+
'<div class="memory-meta">' + entry.patternCount + ' patterns · Last used: ' + lastUsed + '</div>' +
|
|
3448
|
+
'<div class="memory-patterns">' + patternHtml + '</div>';
|
|
3449
|
+
list.appendChild(card);
|
|
3450
|
+
});
|
|
3451
|
+
}).catch(function() {
|
|
3452
|
+
list.innerHTML = '<div class="placeholder-text">Failed to load memories.</div>';
|
|
3453
|
+
});
|
|
3454
|
+
},
|
|
3455
|
+
|
|
3456
|
+
showDetail: function(domain) {
|
|
3457
|
+
document.getElementById('memoryList').style.display = 'none';
|
|
3458
|
+
var panel = document.getElementById('memoryDetail');
|
|
3459
|
+
panel.style.display = 'block';
|
|
3460
|
+
panel.innerHTML = '<div class="placeholder-text">Loading...</div>';
|
|
3461
|
+
|
|
3462
|
+
fetch('/v1/memory/' + encodeURIComponent(domain)).then(function(r) { return r.json(); }).then(function(card) {
|
|
3463
|
+
var siteTag = card.siteType && card.siteType !== 'unknown' ? ' [' + card.siteType.toUpperCase() + ']' : '';
|
|
3464
|
+
var html = '<div class="memory-detail-header">' +
|
|
3465
|
+
'<button class="btn" onclick="Memory.backToList()">← Back</button>' +
|
|
3466
|
+
'<span style="font-size:16px;font-weight:600">' + esc(card.domain) + esc(siteTag) + '</span>' +
|
|
3467
|
+
'<span style="font-size:12px;color:var(--text-muted)">v' + card.version + '</span>' +
|
|
3468
|
+
'<button class="btn btn-danger" style="margin-left:auto" onclick="Memory.deleteDomain(\'' + esc(card.domain).replace(/'/g, ''') + '\')">Delete</button>' +
|
|
3469
|
+
'</div>';
|
|
3470
|
+
|
|
3471
|
+
html += '<div style="font-size:13px;color:var(--text-secondary);margin-bottom:16px">' +
|
|
3472
|
+
'Created: ' + new Date(card.createdAt).toLocaleString() +
|
|
3473
|
+
' · Updated: ' + new Date(card.updatedAt).toLocaleString() +
|
|
3474
|
+
(card.requiresLogin ? ' · <span style="color:var(--orange)">Requires Login</span>' : '') +
|
|
3475
|
+
'</div>';
|
|
3476
|
+
|
|
3477
|
+
html += '<div class="pattern-list">';
|
|
3478
|
+
(card.patterns || []).forEach(function(p) {
|
|
3479
|
+
var now = Date.now();
|
|
3480
|
+
var daysSince = (now - p.lastUsedAt) / 86400000;
|
|
3481
|
+
var effConf = (p.confidence * Math.pow(0.95, daysSince)).toFixed(2);
|
|
3482
|
+
|
|
3483
|
+
html += '<div class="pattern-item">' +
|
|
3484
|
+
'<span class="pattern-type ' + esc(p.type) + '">' + esc(p.type) + '</span>' +
|
|
3485
|
+
'<span style="font-size:11px;color:var(--text-muted)">' + esc(p.source || '') + '</span>' +
|
|
3486
|
+
'<div class="pattern-desc">' + esc(p.description) + '</div>' +
|
|
3487
|
+
'<div class="pattern-value">' + esc(p.value) + '</div>' +
|
|
3488
|
+
'<div class="pattern-meta">' +
|
|
3489
|
+
'<span>Confidence: ' + effConf + '</span>' +
|
|
3490
|
+
'<span>Used: ' + p.useCount + 'x</span>' +
|
|
3491
|
+
'<span>Last: ' + new Date(p.lastUsedAt).toLocaleDateString() + '</span>' +
|
|
3492
|
+
'</div>' +
|
|
3493
|
+
'</div>';
|
|
3494
|
+
});
|
|
3495
|
+
html += '</div>';
|
|
3496
|
+
|
|
3497
|
+
panel.innerHTML = html;
|
|
3498
|
+
}).catch(function() {
|
|
3499
|
+
panel.innerHTML = '<div class="placeholder-text">Failed to load card.</div>' +
|
|
3500
|
+
'<button class="btn" onclick="Memory.backToList()" style="margin-top:12px">← Back</button>';
|
|
3501
|
+
});
|
|
3502
|
+
},
|
|
3503
|
+
|
|
3504
|
+
backToList: function() {
|
|
3505
|
+
document.getElementById('memoryDetail').style.display = 'none';
|
|
3506
|
+
document.getElementById('memoryList').style.display = '';
|
|
3507
|
+
Memory.refreshList();
|
|
3508
|
+
},
|
|
3509
|
+
|
|
3510
|
+
deleteDomain: function(domain) {
|
|
3511
|
+
if (!confirm('Delete all memories for ' + domain + '?')) return;
|
|
3512
|
+
fetch('/v1/memory/' + encodeURIComponent(domain), { method: 'DELETE' })
|
|
3513
|
+
.then(function() { Memory.backToList(); })
|
|
3514
|
+
.catch(function(err) { alert('Delete failed: ' + err.message); });
|
|
3515
|
+
}
|
|
3516
|
+
};
|
|
3517
|
+
</script>
|
|
3518
|
+
|
|
3519
|
+
<script>
|
|
3520
|
+
// ========== Recording ==========
|
|
3521
|
+
function recJsonOrThrow(r) {
|
|
3522
|
+
if (!r.ok) return r.text().then(function(text) {
|
|
3523
|
+
try {
|
|
3524
|
+
var body = JSON.parse(text);
|
|
3525
|
+
var err = body.error;
|
|
3526
|
+
var msg = (err && err.message) ? err.message : (body.message || ('HTTP ' + r.status));
|
|
3527
|
+
throw new Error(msg);
|
|
3528
|
+
} catch(e) {
|
|
3529
|
+
if (e instanceof SyntaxError) throw new Error('HTTP ' + r.status + ': ' + (text.slice(0, 200) || 'Unknown error'));
|
|
3530
|
+
throw e;
|
|
3531
|
+
}
|
|
3532
|
+
});
|
|
3533
|
+
return r.json();
|
|
3534
|
+
}
|
|
3535
|
+
|
|
3536
|
+
/** Extract structural patterns from recording events (shared by renderReview and saveToMemory) */
|
|
3537
|
+
function extractPatternsFromEvents(events) {
|
|
3538
|
+
var patterns = [];
|
|
3539
|
+
var seen = {};
|
|
3540
|
+
var now = Date.now();
|
|
3541
|
+
var navEvents = events.filter(function(e) { return e.type === 'navigate' && e.url; });
|
|
3542
|
+
if (navEvents.length >= 2) {
|
|
3543
|
+
var paths = navEvents.map(function(e) {
|
|
3544
|
+
try { return new URL(e.url).pathname; } catch(x) { return e.url; }
|
|
3545
|
+
}).join(' → ');
|
|
3546
|
+
if (!seen[paths]) {
|
|
3547
|
+
seen[paths] = true;
|
|
3548
|
+
patterns.push({ type: 'navigation_path', description: 'Navigation path', value: paths, confidence: 0.8, source: 'human_recording', createdAt: now, lastUsedAt: now, useCount: 0 });
|
|
3549
|
+
}
|
|
3550
|
+
}
|
|
3551
|
+
events.forEach(function(e) {
|
|
3552
|
+
if (e.type === 'click' && e.target && !seen['click:' + e.target]) {
|
|
3553
|
+
seen['click:' + e.target] = true;
|
|
3554
|
+
patterns.push({ type: 'selector', description: e.targetLabel || e.target, value: e.target, confidence: 0.8, source: 'human_recording', createdAt: now, lastUsedAt: now, useCount: 0 });
|
|
3555
|
+
}
|
|
3556
|
+
if ((e.type === 'type' || e.type === 'select') && e.target && !seen['input:' + e.target]) {
|
|
3557
|
+
seen['input:' + e.target] = true;
|
|
3558
|
+
patterns.push({ type: 'page_structure', description: e.targetLabel || e.target, value: e.target, confidence: 0.8, source: 'human_recording', createdAt: now, lastUsedAt: now, useCount: 0 });
|
|
3559
|
+
}
|
|
3560
|
+
});
|
|
3561
|
+
return patterns;
|
|
3562
|
+
}
|
|
3563
|
+
|
|
3564
|
+
var Rec = {
|
|
3565
|
+
state: 'setup',
|
|
3566
|
+
sessionId: null,
|
|
3567
|
+
startTime: null,
|
|
3568
|
+
timers: {},
|
|
3569
|
+
lastEventCount: 0,
|
|
3570
|
+
events: [],
|
|
3571
|
+
_pollFailCount: 0,
|
|
3572
|
+
_pollAbort: null, // AbortController for in-flight poll requests
|
|
3573
|
+
_stopping: false, // guard against double-click on stop
|
|
3574
|
+
_reviewData: null, // stored stop response for saveToMemory
|
|
3575
|
+
_summarizeAbort: null, // AbortController for in-flight LLM summarization
|
|
3576
|
+
_saving: false, // guard against double-click on save
|
|
3577
|
+
|
|
3578
|
+
setState: function(s) {
|
|
3579
|
+
this.state = s;
|
|
3580
|
+
document.getElementById('rec-setup').classList.toggle('active', s === 'setup');
|
|
3581
|
+
document.getElementById('rec-active').classList.toggle('active', s === 'recording');
|
|
3582
|
+
document.getElementById('rec-review').classList.toggle('active', s === 'review');
|
|
3583
|
+
},
|
|
3584
|
+
|
|
3585
|
+
start: function() {
|
|
3586
|
+
var url = document.getElementById('recUrlInput').value.trim();
|
|
3587
|
+
if (!url) { alert('Please enter a URL.'); return; }
|
|
3588
|
+
if (!/^https?:\/\//i.test(url)) url = 'https://' + url;
|
|
3589
|
+
localStorage.setItem('rec_last_url', url);
|
|
3590
|
+
|
|
3591
|
+
var btn = document.getElementById('recStartBtn');
|
|
3592
|
+
btn.disabled = true;
|
|
3593
|
+
btn.textContent = 'Starting...';
|
|
3594
|
+
var self = this;
|
|
3595
|
+
|
|
3596
|
+
// 1. Create headful session
|
|
3597
|
+
fetch('/v1/sessions', {
|
|
3598
|
+
method: 'POST',
|
|
3599
|
+
headers: { 'Content-Type': 'application/json' },
|
|
3600
|
+
body: JSON.stringify({ options: { headless: false } })
|
|
3601
|
+
})
|
|
3602
|
+
.then(recJsonOrThrow)
|
|
3603
|
+
.then(function(data) {
|
|
3604
|
+
self.sessionId = data.sessionId;
|
|
3605
|
+
// 2. Navigate
|
|
3606
|
+
return fetch('/v1/sessions/' + self.sessionId + '/navigate', {
|
|
3607
|
+
method: 'POST',
|
|
3608
|
+
headers: { 'Content-Type': 'application/json' },
|
|
3609
|
+
body: JSON.stringify({ url: url })
|
|
3610
|
+
});
|
|
3611
|
+
})
|
|
3612
|
+
.then(recJsonOrThrow)
|
|
3613
|
+
.then(function() {
|
|
3614
|
+
// 3. Start recording
|
|
3615
|
+
return fetch('/v1/sessions/' + self.sessionId + '/recording/start', {
|
|
3616
|
+
method: 'POST',
|
|
3617
|
+
headers: { 'Content-Type': 'application/json' },
|
|
3618
|
+
body: '{}'
|
|
3619
|
+
});
|
|
3620
|
+
})
|
|
3621
|
+
.then(recJsonOrThrow)
|
|
3622
|
+
.then(function() {
|
|
3623
|
+
self.startTime = Date.now();
|
|
3624
|
+
self.lastEventCount = 0;
|
|
3625
|
+
self.events = [];
|
|
3626
|
+
self._pollFailCount = 0;
|
|
3627
|
+
document.getElementById('recEventStream').innerHTML =
|
|
3628
|
+
'<div class="rec-empty-events"><span style="font-size:24px;opacity:0.3">👁</span><span>Waiting for interactions...</span></div>';
|
|
3629
|
+
document.getElementById('recTimer').textContent = '00:00';
|
|
3630
|
+
document.getElementById('recEventCount').textContent = '0 events';
|
|
3631
|
+
document.getElementById('recPreviewBody').innerHTML = '<div class="skeleton"></div>';
|
|
3632
|
+
self.setState('recording');
|
|
3633
|
+
self.startPolling();
|
|
3634
|
+
})
|
|
3635
|
+
.catch(function(err) {
|
|
3636
|
+
alert('Failed to start recording: ' + err.message);
|
|
3637
|
+
if (self.sessionId) self.cleanup();
|
|
3638
|
+
btn.disabled = false;
|
|
3639
|
+
btn.textContent = 'Start Recording';
|
|
3640
|
+
});
|
|
3641
|
+
},
|
|
3642
|
+
|
|
3643
|
+
stop: function() {
|
|
3644
|
+
if (!this.sessionId || this._stopping) return;
|
|
3645
|
+
this._stopping = true;
|
|
3646
|
+
var self = this;
|
|
3647
|
+
// Abort all in-flight poll requests first so the connection pool is free
|
|
3648
|
+
self.stopPolling();
|
|
3649
|
+
|
|
3650
|
+
fetch('/v1/sessions/' + self.sessionId + '/recording/stop', {
|
|
3651
|
+
method: 'POST',
|
|
3652
|
+
headers: { 'Content-Type': 'application/json' },
|
|
3653
|
+
body: JSON.stringify({ convertToMemory: false })
|
|
3654
|
+
})
|
|
3655
|
+
.then(recJsonOrThrow)
|
|
3656
|
+
.then(function(data) {
|
|
3657
|
+
self._stopping = false;
|
|
3658
|
+
self._reviewData = data;
|
|
3659
|
+
self.renderReview(data);
|
|
3660
|
+
self.setState('review');
|
|
3661
|
+
// Fire LLM summarization in background
|
|
3662
|
+
self.summarizeIntent(data);
|
|
3663
|
+
})
|
|
3664
|
+
.catch(function(err) {
|
|
3665
|
+
self._stopping = false;
|
|
3666
|
+
alert('Failed to stop recording: ' + err.message);
|
|
3667
|
+
if (self.sessionId) {
|
|
3668
|
+
self.setState('recording');
|
|
3669
|
+
self.startPolling();
|
|
3670
|
+
}
|
|
3671
|
+
});
|
|
3672
|
+
},
|
|
3673
|
+
|
|
3674
|
+
discard: function() {
|
|
3675
|
+
this.stopPolling();
|
|
3676
|
+
if (this.state === 'recording') {
|
|
3677
|
+
this._stopBackendRecording();
|
|
3678
|
+
}
|
|
3679
|
+
this.cleanup();
|
|
3680
|
+
this.reset();
|
|
3681
|
+
},
|
|
3682
|
+
|
|
3683
|
+
goToMemory: function() {
|
|
3684
|
+
this.cleanup();
|
|
3685
|
+
this.reset();
|
|
3686
|
+
switchView('memory');
|
|
3687
|
+
},
|
|
3688
|
+
|
|
3689
|
+
summarizeIntent: function(data) {
|
|
3690
|
+
var recording = data.recording || {};
|
|
3691
|
+
var events = recording.events || [];
|
|
3692
|
+
var domain = data.domain || recording.domain || '';
|
|
3693
|
+
var textarea = document.getElementById('recIntentText');
|
|
3694
|
+
var hint = document.getElementById('recIntentHint');
|
|
3695
|
+
var saveBtn = document.getElementById('recSaveBtn');
|
|
3696
|
+
|
|
3697
|
+
if (events.length === 0) {
|
|
3698
|
+
textarea.classList.remove('loading');
|
|
3699
|
+
textarea.value = '';
|
|
3700
|
+
textarea.placeholder = 'No events to analyze.';
|
|
3701
|
+
hint.textContent = '';
|
|
3702
|
+
return;
|
|
3703
|
+
}
|
|
3704
|
+
|
|
3705
|
+
// Disable save while summarizing
|
|
3706
|
+
if (saveBtn) saveBtn.disabled = true;
|
|
3707
|
+
textarea.classList.add('loading');
|
|
3708
|
+
textarea.disabled = true;
|
|
3709
|
+
hint.textContent = 'AI summarizing...';
|
|
3710
|
+
|
|
3711
|
+
// Abort any previous summarize request
|
|
3712
|
+
if (this._summarizeAbort) this._summarizeAbort.abort();
|
|
3713
|
+
this._summarizeAbort = new AbortController();
|
|
3714
|
+
var signal = this._summarizeAbort.signal;
|
|
3715
|
+
|
|
3716
|
+
// Pass LLM config from user settings (same as agent endpoints)
|
|
3717
|
+
var settings = loadSettings();
|
|
3718
|
+
var payload = { events: events, domain: domain };
|
|
3719
|
+
if (settings.apiKey) payload.apiKey = settings.apiKey;
|
|
3720
|
+
if (settings.baseURL) payload.baseURL = settings.baseURL;
|
|
3721
|
+
if (settings.model) payload.model = settings.model;
|
|
3722
|
+
payload.timeout = (settings.timeout && settings.timeout > 0) ? settings.timeout : 300;
|
|
3723
|
+
|
|
3724
|
+
fetch('/v1/recordings/summarize', {
|
|
3725
|
+
method: 'POST',
|
|
3726
|
+
headers: { 'Content-Type': 'application/json' },
|
|
3727
|
+
body: JSON.stringify(payload),
|
|
3728
|
+
signal: signal
|
|
3729
|
+
})
|
|
3730
|
+
// Backend returns 200 with { intent, error? } even on LLM failure — intentional
|
|
3731
|
+
.then(function(r) {
|
|
3732
|
+
if (!r.ok) return { intent: '', error: 'Server error (' + r.status + ')' };
|
|
3733
|
+
return r.json();
|
|
3734
|
+
})
|
|
3735
|
+
.then(function(result) {
|
|
3736
|
+
textarea.classList.remove('loading');
|
|
3737
|
+
textarea.disabled = false;
|
|
3738
|
+
if (saveBtn) saveBtn.disabled = false;
|
|
3739
|
+
if (result.intent) {
|
|
3740
|
+
textarea.value = result.intent;
|
|
3741
|
+
hint.textContent = 'You can edit this before saving.';
|
|
3742
|
+
} else {
|
|
3743
|
+
textarea.value = '';
|
|
3744
|
+
textarea.placeholder = result.error || 'Could not generate summary. You can write one manually.';
|
|
3745
|
+
hint.textContent = result.error ? 'LLM unavailable' : '';
|
|
3746
|
+
if (saveBtn) saveBtn.disabled = false;
|
|
3747
|
+
}
|
|
3748
|
+
})
|
|
3749
|
+
.catch(function(err) {
|
|
3750
|
+
if (err.name === 'AbortError') return; // expected on discard/reset
|
|
3751
|
+
textarea.classList.remove('loading');
|
|
3752
|
+
textarea.disabled = false;
|
|
3753
|
+
if (saveBtn) saveBtn.disabled = false;
|
|
3754
|
+
textarea.placeholder = 'Summarization failed. You can write one manually.';
|
|
3755
|
+
hint.textContent = '';
|
|
3756
|
+
});
|
|
3757
|
+
},
|
|
3758
|
+
|
|
3759
|
+
saveToMemory: function() {
|
|
3760
|
+
var data = this._reviewData;
|
|
3761
|
+
if (!data) { alert('No recording data.'); return; }
|
|
3762
|
+
if (this._saving) return;
|
|
3763
|
+
this._saving = true;
|
|
3764
|
+
|
|
3765
|
+
var recording = data.recording || {};
|
|
3766
|
+
var domain = data.domain || recording.domain || '';
|
|
3767
|
+
if (!domain) { alert('Could not determine domain.'); return; }
|
|
3768
|
+
|
|
3769
|
+
var btn = document.getElementById('recSaveBtn');
|
|
3770
|
+
btn.disabled = true;
|
|
3771
|
+
btn.textContent = 'Saving...';
|
|
3772
|
+
var self = this;
|
|
3773
|
+
|
|
3774
|
+
// Build patterns from RecordingConverter (backend already computed them in stop)
|
|
3775
|
+
// We re-use the existing PUT /v1/memory/:domain endpoint
|
|
3776
|
+
// First, get the patterns that RecordingConverter would produce
|
|
3777
|
+
// We'll send the events to the backend to convert + add intent
|
|
3778
|
+
var intentText = document.getElementById('recIntentText').value.trim();
|
|
3779
|
+
var patterns = [];
|
|
3780
|
+
|
|
3781
|
+
// Add task_intent pattern if user provided/edited intent
|
|
3782
|
+
if (intentText) {
|
|
3783
|
+
patterns.push({
|
|
3784
|
+
type: 'task_intent',
|
|
3785
|
+
description: 'Recording task intent',
|
|
3786
|
+
value: intentText,
|
|
3787
|
+
confidence: 0.9,
|
|
3788
|
+
source: 'human_recording'
|
|
3789
|
+
});
|
|
3790
|
+
}
|
|
3791
|
+
|
|
3792
|
+
// Extract structural patterns from events (shared function)
|
|
3793
|
+
var events = recording.events || [];
|
|
3794
|
+
var structural = extractPatternsFromEvents(events);
|
|
3795
|
+
for (var i = 0; i < structural.length; i++) patterns.push(structural[i]);
|
|
3796
|
+
|
|
3797
|
+
if (patterns.length === 0) {
|
|
3798
|
+
alert('No patterns or intent to save.');
|
|
3799
|
+
btn.disabled = false;
|
|
3800
|
+
btn.textContent = 'Save to Memory';
|
|
3801
|
+
self._saving = false;
|
|
3802
|
+
return;
|
|
3803
|
+
}
|
|
3804
|
+
|
|
3805
|
+
fetch('/v1/memory/' + encodeURIComponent(domain), {
|
|
3806
|
+
method: 'PUT',
|
|
3807
|
+
headers: { 'Content-Type': 'application/json' },
|
|
3808
|
+
body: JSON.stringify({ patterns: patterns })
|
|
3809
|
+
})
|
|
3810
|
+
.then(recJsonOrThrow)
|
|
3811
|
+
.then(function(result) {
|
|
3812
|
+
document.getElementById('recSuccessArea').innerHTML =
|
|
3813
|
+
'<div class="rec-success-msg">✓ Saved to memory for <strong>' + esc(domain) + '</strong> (' + result.patternCount + ' patterns)</div>';
|
|
3814
|
+
btn.textContent = 'Saved!';
|
|
3815
|
+
// Replace buttons with "View Memory" link
|
|
3816
|
+
document.getElementById('recActions').innerHTML =
|
|
3817
|
+
'<button class="btn" onclick="Rec.discard()">New Recording</button>' +
|
|
3818
|
+
'<button class="btn btn-primary" onclick="Rec.goToMemory()">View Memory</button>';
|
|
3819
|
+
})
|
|
3820
|
+
.catch(function(err) {
|
|
3821
|
+
alert('Failed to save: ' + err.message);
|
|
3822
|
+
btn.disabled = false;
|
|
3823
|
+
btn.textContent = 'Save to Memory';
|
|
3824
|
+
self._saving = false;
|
|
3825
|
+
});
|
|
3826
|
+
},
|
|
3827
|
+
|
|
3828
|
+
reset: function() {
|
|
3829
|
+
this.state = 'setup';
|
|
3830
|
+
this.sessionId = null;
|
|
3831
|
+
this.startTime = null;
|
|
3832
|
+
this.lastEventCount = 0;
|
|
3833
|
+
this.events = [];
|
|
3834
|
+
this._pollFailCount = 0;
|
|
3835
|
+
this._pollAbort = null;
|
|
3836
|
+
this._stopping = false;
|
|
3837
|
+
this._reviewData = null;
|
|
3838
|
+
if (this._summarizeAbort) { this._summarizeAbort.abort(); this._summarizeAbort = null; }
|
|
3839
|
+
this._saving = false;
|
|
3840
|
+
this.setState('setup');
|
|
3841
|
+
document.getElementById('recSuccessArea').innerHTML = '';
|
|
3842
|
+
var btn = document.getElementById('recStartBtn');
|
|
3843
|
+
btn.disabled = false;
|
|
3844
|
+
btn.textContent = 'Start Recording';
|
|
3845
|
+
},
|
|
3846
|
+
|
|
3847
|
+
cleanup: function() {
|
|
3848
|
+
if (!this.sessionId) return;
|
|
3849
|
+
fetch('/v1/sessions/' + this.sessionId, { method: 'DELETE' }).catch(function() {});
|
|
3850
|
+
this.sessionId = null;
|
|
3851
|
+
},
|
|
3852
|
+
|
|
3853
|
+
/** Fire-and-forget stop recording on backend to clean up recorders Map */
|
|
3854
|
+
_stopBackendRecording: function() {
|
|
3855
|
+
if (!this.sessionId) return;
|
|
3856
|
+
fetch('/v1/sessions/' + this.sessionId + '/recording/stop', {
|
|
3857
|
+
method: 'POST',
|
|
3858
|
+
headers: { 'Content-Type': 'application/json' },
|
|
3859
|
+
body: JSON.stringify({ convertToMemory: false })
|
|
3860
|
+
}).catch(function() {});
|
|
3861
|
+
},
|
|
3862
|
+
|
|
3863
|
+
startPolling: function() {
|
|
3864
|
+
var self = this;
|
|
3865
|
+
this._pollAbort = new AbortController();
|
|
3866
|
+
this.timers.timerInterval = setInterval(function() { self.updateTimer(); }, 1000);
|
|
3867
|
+
this.timers.statusPoll = setInterval(function() { self.pollStatus(); }, 1000);
|
|
3868
|
+
this.timers.screenshotPoll = setInterval(function() { self.pollScreenshot(); }, 3000);
|
|
3869
|
+
// Immediate first screenshot (tracked for cleanup)
|
|
3870
|
+
this.timers.initialScreenshot = setTimeout(function() { self.pollScreenshot(); }, 500);
|
|
3871
|
+
},
|
|
3872
|
+
|
|
3873
|
+
stopPolling: function() {
|
|
3874
|
+
if (this.timers.timerInterval) clearInterval(this.timers.timerInterval);
|
|
3875
|
+
if (this.timers.statusPoll) clearInterval(this.timers.statusPoll);
|
|
3876
|
+
if (this.timers.screenshotPoll) clearInterval(this.timers.screenshotPoll);
|
|
3877
|
+
if (this.timers.initialScreenshot) clearTimeout(this.timers.initialScreenshot);
|
|
3878
|
+
this.timers = {};
|
|
3879
|
+
// Abort any in-flight poll fetches immediately
|
|
3880
|
+
if (this._pollAbort) {
|
|
3881
|
+
this._pollAbort.abort();
|
|
3882
|
+
this._pollAbort = null;
|
|
3883
|
+
}
|
|
3884
|
+
},
|
|
3885
|
+
|
|
3886
|
+
updateTimer: function() {
|
|
3887
|
+
if (!this.startTime) return;
|
|
3888
|
+
var elapsed = Math.floor((Date.now() - this.startTime) / 1000);
|
|
3889
|
+
var m = String(Math.floor(elapsed / 60)).padStart(2, '0');
|
|
3890
|
+
var s = String(elapsed % 60).padStart(2, '0');
|
|
3891
|
+
document.getElementById('recTimer').textContent = m + ':' + s;
|
|
3892
|
+
},
|
|
3893
|
+
|
|
3894
|
+
pollStatus: function() {
|
|
3895
|
+
if (!this.sessionId || !this._pollAbort) return;
|
|
3896
|
+
var self = this;
|
|
3897
|
+
var signal = this._pollAbort.signal;
|
|
3898
|
+
fetch('/v1/sessions/' + this.sessionId + '/recording', { signal: signal })
|
|
3899
|
+
.then(recJsonOrThrow)
|
|
3900
|
+
.then(function(data) {
|
|
3901
|
+
self._pollFailCount = 0;
|
|
3902
|
+
var count = data.eventCount || 0;
|
|
3903
|
+
document.getElementById('recEventCount').textContent = count + ' event' + (count !== 1 ? 's' : '');
|
|
3904
|
+
if (count > self.lastEventCount) {
|
|
3905
|
+
var stream = document.getElementById('recEventStream');
|
|
3906
|
+
if (self.lastEventCount === 0) stream.innerHTML = '';
|
|
3907
|
+
for (var i = self.lastEventCount; i < count; i++) {
|
|
3908
|
+
var item = document.createElement('div');
|
|
3909
|
+
item.className = 'rec-event-item';
|
|
3910
|
+
item.innerHTML = '<span class="ev-icon">●</span>' +
|
|
3911
|
+
'<span class="ev-badge click">event</span>' +
|
|
3912
|
+
'<span class="ev-desc">Interaction #' + (i + 1) + '</span>' +
|
|
3913
|
+
'<span class="ev-time">' + self.formatTime(Date.now()) + '</span>';
|
|
3914
|
+
stream.appendChild(item);
|
|
3915
|
+
stream.scrollTop = stream.scrollHeight;
|
|
3916
|
+
}
|
|
3917
|
+
self.lastEventCount = count;
|
|
3918
|
+
}
|
|
3919
|
+
})
|
|
3920
|
+
.catch(function(err) {
|
|
3921
|
+
if (err.name === 'AbortError') return; // expected on stop
|
|
3922
|
+
self._pollFailCount++;
|
|
3923
|
+
if (self._pollFailCount >= 5) {
|
|
3924
|
+
self.stopPolling();
|
|
3925
|
+
alert('Lost connection to recording session.');
|
|
3926
|
+
}
|
|
3927
|
+
});
|
|
3928
|
+
},
|
|
3929
|
+
|
|
3930
|
+
pollScreenshot: function() {
|
|
3931
|
+
if (!this.sessionId || !this._pollAbort) return;
|
|
3932
|
+
var container = document.getElementById('recPreviewBody');
|
|
3933
|
+
var signal = this._pollAbort.signal;
|
|
3934
|
+
fetch('/v1/sessions/' + this.sessionId + '/screenshot', { signal: signal })
|
|
3935
|
+
.then(function(r) { return r.ok ? r.json() : null; })
|
|
3936
|
+
.then(function(data) {
|
|
3937
|
+
if (data && data.image) {
|
|
3938
|
+
var imgEl = document.createElement('img');
|
|
3939
|
+
imgEl.src = data.image;
|
|
3940
|
+
imgEl.alt = 'Live preview';
|
|
3941
|
+
container.innerHTML = '';
|
|
3942
|
+
container.appendChild(imgEl);
|
|
3943
|
+
}
|
|
3944
|
+
})
|
|
3945
|
+
.catch(function() {}); // AbortError or network error — silent
|
|
3946
|
+
},
|
|
3947
|
+
|
|
3948
|
+
renderReview: function(data) {
|
|
3949
|
+
var recording = data.recording || {};
|
|
3950
|
+
var events = recording.events || [];
|
|
3951
|
+
var domain = data.domain || recording.domain || '-';
|
|
3952
|
+
var duration = recording.endedAt && recording.startedAt
|
|
3953
|
+
? Math.round((recording.endedAt - recording.startedAt) / 1000)
|
|
3954
|
+
: 0;
|
|
3955
|
+
var dMin = String(Math.floor(duration / 60)).padStart(2, '0');
|
|
3956
|
+
var dSec = String(duration % 60).padStart(2, '0');
|
|
3957
|
+
|
|
3958
|
+
// Summary
|
|
3959
|
+
document.getElementById('recSummary').innerHTML =
|
|
3960
|
+
'<div class="sum-item"><strong>' + esc(domain) + '</strong><br>Domain</div>' +
|
|
3961
|
+
'<div class="sum-item"><strong>' + events.length + '</strong><br>Events</div>' +
|
|
3962
|
+
'<div class="sum-item"><strong>' + dMin + ':' + dSec + '</strong><br>Duration</div>';
|
|
3963
|
+
|
|
3964
|
+
// Intent textarea — reset to loading state (summarizeIntent will fill it)
|
|
3965
|
+
var textarea = document.getElementById('recIntentText');
|
|
3966
|
+
textarea.value = '';
|
|
3967
|
+
textarea.classList.add('loading');
|
|
3968
|
+
textarea.disabled = true;
|
|
3969
|
+
document.getElementById('recIntentHint').textContent = 'AI summarizing...';
|
|
3970
|
+
|
|
3971
|
+
// Timeline
|
|
3972
|
+
var timeline = document.getElementById('recTimeline');
|
|
3973
|
+
if (events.length === 0) {
|
|
3974
|
+
timeline.innerHTML = '<div class="rec-empty-events"><span>No events recorded.</span></div>';
|
|
3975
|
+
} else {
|
|
3976
|
+
var html = '';
|
|
3977
|
+
var self = this;
|
|
3978
|
+
events.forEach(function(evt) {
|
|
3979
|
+
html += self.renderEventItem(evt);
|
|
3980
|
+
});
|
|
3981
|
+
timeline.innerHTML = html;
|
|
3982
|
+
}
|
|
3983
|
+
|
|
3984
|
+
// Patterns preview (show what will be extracted, not yet saved)
|
|
3985
|
+
var patternsBody = document.getElementById('recPatternsBody');
|
|
3986
|
+
var previewPatterns = extractPatternsFromEvents(events);
|
|
3987
|
+
|
|
3988
|
+
if (previewPatterns.length > 0) {
|
|
3989
|
+
var phtml = '';
|
|
3990
|
+
previewPatterns.forEach(function(p) { phtml += Rec.renderPattern(p); });
|
|
3991
|
+
patternsBody.innerHTML = phtml;
|
|
3992
|
+
} else {
|
|
3993
|
+
patternsBody.innerHTML = '<div class="placeholder-text">No structural patterns detected.</div>';
|
|
3994
|
+
}
|
|
3995
|
+
|
|
3996
|
+
// Reset action buttons and success area
|
|
3997
|
+
document.getElementById('recSuccessArea').innerHTML = '';
|
|
3998
|
+
document.getElementById('recActions').innerHTML =
|
|
3999
|
+
'<button class="btn" onclick="Rec.discard()">Discard</button>' +
|
|
4000
|
+
'<button class="btn btn-primary" id="recSaveBtn" onclick="Rec.saveToMemory()">Save to Memory</button>';
|
|
4001
|
+
},
|
|
4002
|
+
|
|
4003
|
+
renderEventItem: function(evt) {
|
|
4004
|
+
var icons = { navigate: '↗', click: '◉', type: '⌨', select: '☰', scroll: '↕' };
|
|
4005
|
+
var icon = icons[evt.type] || '●';
|
|
4006
|
+
var desc = '';
|
|
4007
|
+
if (evt.type === 'navigate') {
|
|
4008
|
+
desc = evt.url || '';
|
|
4009
|
+
} else if (evt.type === 'click') {
|
|
4010
|
+
desc = evt.targetLabel || evt.target || 'element';
|
|
4011
|
+
} else if (evt.type === 'type') {
|
|
4012
|
+
desc = (evt.targetLabel || 'input') + (evt.value ? ': ' + evt.value.slice(0, 30) : '');
|
|
4013
|
+
} else if (evt.type === 'select') {
|
|
4014
|
+
desc = (evt.targetLabel || 'select') + (evt.value ? ' → ' + evt.value : '');
|
|
4015
|
+
} else if (evt.type === 'scroll') {
|
|
4016
|
+
desc = 'scrollY: ' + (evt.value || '0');
|
|
4017
|
+
}
|
|
4018
|
+
return '<div class="rec-event-item">' +
|
|
4019
|
+
'<span class="ev-icon">' + icon + '</span>' +
|
|
4020
|
+
'<span class="ev-badge ' + esc(evt.type) + '">' + esc(evt.type) + '</span>' +
|
|
4021
|
+
'<span class="ev-desc" title="' + esc(desc) + '">' + esc(desc) + '</span>' +
|
|
4022
|
+
'<span class="ev-time">' + this.formatTime(evt.timestamp) + '</span>' +
|
|
4023
|
+
'</div>';
|
|
4024
|
+
},
|
|
4025
|
+
|
|
4026
|
+
renderPattern: function(p) {
|
|
4027
|
+
return '<div class="rec-pattern-item">' +
|
|
4028
|
+
'<span class="pat-type">' + esc(p.type) + '</span>' +
|
|
4029
|
+
'<div class="pat-desc">' + esc(p.description) + '</div>' +
|
|
4030
|
+
'<div class="pat-value">' + esc(p.value) + '</div>' +
|
|
4031
|
+
'</div>';
|
|
4032
|
+
},
|
|
4033
|
+
|
|
4034
|
+
formatTime: function(ts) {
|
|
4035
|
+
if (!ts) return '';
|
|
4036
|
+
var d = new Date(ts);
|
|
4037
|
+
return String(d.getHours()).padStart(2, '0') + ':' +
|
|
4038
|
+
String(d.getMinutes()).padStart(2, '0') + ':' +
|
|
4039
|
+
String(d.getSeconds()).padStart(2, '0');
|
|
4040
|
+
}
|
|
4041
|
+
};
|
|
4042
|
+
</script>
|
|
4043
|
+
|
|
4044
|
+
<script>
|
|
4045
|
+
// ========== Welcome Banner ==========
|
|
4046
|
+
function dismissWelcome() {
|
|
4047
|
+
document.getElementById('welcomeBanner').style.display = 'none';
|
|
4048
|
+
localStorage.setItem('ai_browser_welcomed', '1');
|
|
4049
|
+
}
|
|
4050
|
+
function showWelcomeIfNeeded() {
|
|
4051
|
+
if (!localStorage.getItem('ai_browser_welcomed')) {
|
|
4052
|
+
document.getElementById('welcomeBanner').style.display = '';
|
|
4053
|
+
}
|
|
4054
|
+
}
|
|
4055
|
+
</script>
|
|
4056
|
+
|
|
2540
4057
|
<script>
|
|
2541
4058
|
// ========== Init ==========
|
|
2542
4059
|
(function() {
|
|
2543
4060
|
var hash = location.hash.replace('#', '');
|
|
2544
|
-
var view = (hash === 'agent') ? 'agent' : (hash === 'tasks') ? 'tasks' : 'semantic';
|
|
4061
|
+
var view = (hash === 'agent') ? 'agent' : (hash === 'tasks') ? 'tasks' : (hash === 'memory') ? 'memory' : (hash === 'record') ? 'record' : 'semantic';
|
|
2545
4062
|
switchView(view);
|
|
2546
4063
|
ChatStore.load();
|
|
4064
|
+
showWelcomeIfNeeded();
|
|
2547
4065
|
})();
|
|
2548
4066
|
</script>
|
|
2549
4067
|
</body>
|