mop-agent 0.1.12 → 0.1.13
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 +3 -2
- package/apps/web/app/assistant/page.tsx +3 -4
- package/apps/web/app/globals.css +80 -153
- package/apps/web/components/AppShell.tsx +59 -108
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,8 +5,9 @@ through MOP-FLOW. It stores project memory, performs semantic recall and
|
|
|
5
5
|
consolidation, serves grounded chat, and can request approved actions from a
|
|
6
6
|
linked FLOW node.
|
|
7
7
|
|
|
8
|
-
> **Release status:**
|
|
9
|
-
> installer, one-time Admin setup/login flow, and shared
|
|
8
|
+
> **Release status:** release candidate `mop-agent@0.1.13` contains the corrected VPS
|
|
9
|
+
> installer, one-time Admin setup/login flow, and simplified shared application shell
|
|
10
|
+
> with centered page titles and ChatGPT-inspired navigation.
|
|
10
11
|
> The canonical installation command is exactly `npx mop-agent`.
|
|
11
12
|
|
|
12
13
|
## Current status
|
|
@@ -7,7 +7,7 @@ import { useMemoryCore } from "@/components/AppShell";
|
|
|
7
7
|
type Turn = { role: "user" | "assistant"; content: string };
|
|
8
8
|
|
|
9
9
|
export default function AssistantPage() {
|
|
10
|
-
const {
|
|
10
|
+
const { projects } = useMemoryCore();
|
|
11
11
|
const [turns, setTurns] = useState<Turn[]>([]);
|
|
12
12
|
const [name, setName] = useState("Admin");
|
|
13
13
|
const [input, setInput] = useState("");
|
|
@@ -34,8 +34,7 @@ export default function AssistantPage() {
|
|
|
34
34
|
headers: { "content-type": "application/json" },
|
|
35
35
|
body: JSON.stringify({
|
|
36
36
|
message,
|
|
37
|
-
|
|
38
|
-
allowCrossProject: !selectedProject,
|
|
37
|
+
allowCrossProject: true,
|
|
39
38
|
}),
|
|
40
39
|
});
|
|
41
40
|
|
|
@@ -120,7 +119,7 @@ export default function AssistantPage() {
|
|
|
120
119
|
<button onClick={() => send()} disabled={busy || !input.trim()} style={{ ...sendButton, opacity: busy || !input.trim() ? .45 : 1 }}>↑</button>
|
|
121
120
|
</div>
|
|
122
121
|
<div style={{ textAlign: "center", color: "rgba(45,74,62,.62)", fontSize: 11, marginTop: 8 }}>
|
|
123
|
-
{providerUsed ? `Answered by ${providerUsed} · ` : ""}
|
|
122
|
+
{providerUsed ? `Answered by ${providerUsed} · ` : ""}Cross-project memory
|
|
124
123
|
</div>
|
|
125
124
|
</div>
|
|
126
125
|
</section>
|
package/apps/web/app/globals.css
CHANGED
|
@@ -97,7 +97,7 @@ button {
|
|
|
97
97
|
.mop-app-frame {
|
|
98
98
|
min-height: 100vh;
|
|
99
99
|
display: grid;
|
|
100
|
-
grid-template-columns:
|
|
100
|
+
grid-template-columns: 260px minmax(0, 1fr);
|
|
101
101
|
grid-template-rows: 70px minmax(0, 1fr);
|
|
102
102
|
grid-template-areas:
|
|
103
103
|
"topbar topbar"
|
|
@@ -111,7 +111,7 @@ button {
|
|
|
111
111
|
top: 0;
|
|
112
112
|
z-index: 50;
|
|
113
113
|
display: grid;
|
|
114
|
-
grid-template-columns:
|
|
114
|
+
grid-template-columns: 260px minmax(0, 1fr);
|
|
115
115
|
min-width: 0;
|
|
116
116
|
color: var(--mop-cream);
|
|
117
117
|
background:
|
|
@@ -153,6 +153,7 @@ button {
|
|
|
153
153
|
min-width: 0;
|
|
154
154
|
display: flex;
|
|
155
155
|
align-items: center;
|
|
156
|
+
justify-content: center;
|
|
156
157
|
gap: 14px;
|
|
157
158
|
padding: 0 18px;
|
|
158
159
|
}
|
|
@@ -167,18 +168,8 @@ button {
|
|
|
167
168
|
cursor: pointer;
|
|
168
169
|
}
|
|
169
170
|
|
|
170
|
-
.mop-topbar-title {
|
|
171
|
-
display: flex;
|
|
172
|
-
align-items: center;
|
|
173
|
-
gap: 8px;
|
|
174
|
-
min-width: 130px;
|
|
175
|
-
font-family: "SFMono-Regular", Consolas, monospace;
|
|
176
|
-
font-size: 13px;
|
|
177
|
-
letter-spacing: .08em;
|
|
178
|
-
text-transform: uppercase;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
171
|
.mop-live-dot {
|
|
172
|
+
flex: 0 0 auto;
|
|
182
173
|
width: 8px;
|
|
183
174
|
height: 8px;
|
|
184
175
|
background: #78e19b;
|
|
@@ -186,37 +177,21 @@ button {
|
|
|
186
177
|
}
|
|
187
178
|
|
|
188
179
|
.mop-topbar-center {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
180
|
+
min-width: 240px;
|
|
181
|
+
display: flex;
|
|
182
|
+
align-items: center;
|
|
183
|
+
justify-content: center;
|
|
184
|
+
gap: 10px;
|
|
194
185
|
padding: 9px 28px;
|
|
195
186
|
text-align: center;
|
|
196
187
|
color: rgba(254, 249, 225, .86);
|
|
197
188
|
border: 1px solid rgba(254, 249, 225, .12);
|
|
198
189
|
background: rgba(254, 249, 225, .07);
|
|
199
190
|
font-family: "SFMono-Regular", Consolas, monospace;
|
|
200
|
-
font-size:
|
|
201
|
-
font-weight: 800;
|
|
202
|
-
letter-spacing: .18em;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
.mop-topbar-meta {
|
|
206
|
-
display: flex;
|
|
207
|
-
align-items: center;
|
|
208
|
-
gap: 9px;
|
|
209
|
-
margin-left: auto;
|
|
210
|
-
font-family: "SFMono-Regular", Consolas, monospace;
|
|
211
|
-
font-size: 10px;
|
|
191
|
+
font-size: 12px;
|
|
212
192
|
font-weight: 800;
|
|
213
|
-
letter-spacing: .
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
.mop-version {
|
|
217
|
-
padding: 4px 6px;
|
|
218
|
-
border: 1px solid rgba(254, 249, 225, .22);
|
|
219
|
-
background: rgba(254, 249, 225, .07);
|
|
193
|
+
letter-spacing: .14em;
|
|
194
|
+
text-transform: uppercase;
|
|
220
195
|
}
|
|
221
196
|
|
|
222
197
|
.mop-app-sidebar {
|
|
@@ -228,60 +203,68 @@ button {
|
|
|
228
203
|
display: flex;
|
|
229
204
|
flex-direction: column;
|
|
230
205
|
overflow-y: auto;
|
|
231
|
-
padding:
|
|
206
|
+
padding: 10px 9px 9px;
|
|
232
207
|
color: var(--mop-cream);
|
|
233
208
|
background:
|
|
234
|
-
linear-gradient(rgba(255,255,255,.
|
|
209
|
+
linear-gradient(rgba(255,255,255,.028), rgba(0,0,0,.035)),
|
|
235
210
|
var(--mop-green);
|
|
236
211
|
border-right: 2px solid #20362e;
|
|
237
212
|
}
|
|
238
213
|
|
|
239
|
-
.mop-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
font-family: "SFMono-Regular", Consolas, monospace;
|
|
244
|
-
font-size: 9px;
|
|
245
|
-
font-weight: 900;
|
|
246
|
-
letter-spacing: .22em;
|
|
214
|
+
.mop-sidebar-primary {
|
|
215
|
+
display: grid;
|
|
216
|
+
gap: 3px;
|
|
217
|
+
margin-bottom: 22px;
|
|
247
218
|
}
|
|
248
219
|
|
|
249
|
-
.mop-
|
|
220
|
+
.mop-sidebar-primary a,
|
|
250
221
|
.mop-nav-section a,
|
|
251
222
|
.mop-nav-section button {
|
|
252
223
|
display: flex;
|
|
253
224
|
align-items: center;
|
|
254
225
|
gap: 11px;
|
|
255
226
|
width: 100%;
|
|
256
|
-
min-height:
|
|
257
|
-
padding: 8px
|
|
258
|
-
color: rgba(254, 249, 225, .
|
|
227
|
+
min-height: 40px;
|
|
228
|
+
padding: 8px 11px;
|
|
229
|
+
color: rgba(254, 249, 225, .82);
|
|
259
230
|
border: 1px solid transparent;
|
|
260
231
|
background: transparent;
|
|
261
232
|
text-align: left;
|
|
262
233
|
text-decoration: none;
|
|
263
|
-
font-
|
|
264
|
-
font-
|
|
265
|
-
|
|
266
|
-
letter-spacing: .055em;
|
|
267
|
-
text-transform: uppercase;
|
|
234
|
+
font-size: 14px;
|
|
235
|
+
font-weight: 540;
|
|
236
|
+
letter-spacing: 0;
|
|
268
237
|
cursor: pointer;
|
|
269
238
|
}
|
|
270
239
|
|
|
240
|
+
.mop-sidebar-primary a:hover,
|
|
271
241
|
.mop-nav-section a:hover,
|
|
272
242
|
.mop-nav-section button:hover {
|
|
273
243
|
color: var(--mop-cream);
|
|
274
|
-
background: rgba(254, 249, 225, .
|
|
244
|
+
background: rgba(254, 249, 225, .075);
|
|
275
245
|
}
|
|
276
246
|
|
|
247
|
+
.mop-sidebar-primary a.is-active,
|
|
277
248
|
.mop-nav-section a.is-active,
|
|
278
249
|
.mop-nav-section button.is-active {
|
|
279
|
-
color:
|
|
280
|
-
border-color: rgba(254, 249, 225, .
|
|
281
|
-
background: rgba(254, 249, 225, .
|
|
282
|
-
box-shadow:
|
|
250
|
+
color: var(--mop-cream);
|
|
251
|
+
border-color: rgba(254, 249, 225, .12);
|
|
252
|
+
background: rgba(254, 249, 225, .1);
|
|
253
|
+
box-shadow: 2px 2px 0 rgba(18, 38, 30, .22);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.mop-nav-section { margin-bottom: 18px; }
|
|
257
|
+
.mop-nav-section > p {
|
|
258
|
+
margin: 0 11px 7px;
|
|
259
|
+
color: rgba(254, 249, 225, .46);
|
|
260
|
+
font-family: "SFMono-Regular", Consolas, monospace;
|
|
261
|
+
font-size: 10px;
|
|
262
|
+
font-weight: 750;
|
|
263
|
+
letter-spacing: .11em;
|
|
283
264
|
}
|
|
284
265
|
|
|
266
|
+
.mop-nav-section nav { display: grid; gap: 2px; }
|
|
267
|
+
|
|
285
268
|
.mop-nav-icon {
|
|
286
269
|
width: 21px;
|
|
287
270
|
text-align: center;
|
|
@@ -289,6 +272,32 @@ button {
|
|
|
289
272
|
font-size: 16px;
|
|
290
273
|
}
|
|
291
274
|
|
|
275
|
+
.mop-project-memory {
|
|
276
|
+
min-height: 0;
|
|
277
|
+
overflow: hidden;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.mop-project-memory nav {
|
|
281
|
+
max-height: min(34vh, 280px);
|
|
282
|
+
overflow-y: auto;
|
|
283
|
+
scrollbar-width: thin;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.mop-project-memory a { min-height: 34px; padding-block: 5px; font-size: 13px; }
|
|
287
|
+
.mop-project-dot {
|
|
288
|
+
flex: 0 0 auto;
|
|
289
|
+
width: 7px;
|
|
290
|
+
height: 7px;
|
|
291
|
+
background: rgba(254, 249, 225, .34);
|
|
292
|
+
}
|
|
293
|
+
.mop-project-dot[data-online="true"] { background: #78e19b; box-shadow: 0 0 0 2px rgba(120, 225, 155, .12); }
|
|
294
|
+
.mop-nav-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
295
|
+
.mop-sidebar-empty { display: block; padding: 7px 11px; color: rgba(254, 249, 225, .42); font-size: 12px; }
|
|
296
|
+
.mop-admin-nav { margin-top: 2px; }
|
|
297
|
+
.mop-settings-subnav { display: grid; gap: 1px; margin: 1px 0 2px 31px; border-left: 1px solid rgba(254, 249, 225, .18); }
|
|
298
|
+
.mop-settings-subnav button { min-height: 32px; padding: 5px 12px; color: rgba(254, 249, 225, .6); font-size: 12px; }
|
|
299
|
+
.mop-settings-subnav button.is-active { color: #ff9a56; border-color: transparent; background: transparent; box-shadow: none; }
|
|
300
|
+
|
|
292
301
|
.mop-sidebar-spacer { flex: 1; }
|
|
293
302
|
.mop-account-card {
|
|
294
303
|
width: 100%;
|
|
@@ -297,11 +306,12 @@ button {
|
|
|
297
306
|
gap: 9px;
|
|
298
307
|
padding: 10px 8px;
|
|
299
308
|
color: var(--mop-cream);
|
|
300
|
-
border: 1px solid
|
|
301
|
-
background:
|
|
309
|
+
border: 1px solid transparent;
|
|
310
|
+
background: transparent;
|
|
302
311
|
text-align: left;
|
|
303
312
|
cursor: pointer;
|
|
304
313
|
}
|
|
314
|
+
.mop-account-card:hover { border-color: rgba(254, 249, 225, .1); background: rgba(254, 249, 225, .07); }
|
|
305
315
|
|
|
306
316
|
.mop-account-avatar {
|
|
307
317
|
flex: 0 0 auto;
|
|
@@ -385,63 +395,16 @@ button {
|
|
|
385
395
|
flex-direction: column;
|
|
386
396
|
}
|
|
387
397
|
|
|
388
|
-
|
|
389
|
-
.mop-assistant-toolbar {
|
|
390
|
-
width: 100%;
|
|
391
|
-
display: flex;
|
|
392
|
-
align-items: center;
|
|
393
|
-
justify-content: space-between;
|
|
394
|
-
gap: 16px;
|
|
395
|
-
}
|
|
396
|
-
.mop-assistant-status {
|
|
397
|
-
display: flex;
|
|
398
|
-
align-items: center;
|
|
399
|
-
gap: 9px;
|
|
400
|
-
min-width: 0;
|
|
401
|
-
}
|
|
402
|
-
.mop-assistant-status strong {
|
|
403
|
-
font-family: "SFMono-Regular", Consolas, monospace;
|
|
404
|
-
font-size: 13px;
|
|
405
|
-
letter-spacing: .08em;
|
|
406
|
-
text-transform: uppercase;
|
|
407
|
-
color: var(--mop-cream);
|
|
408
|
-
}
|
|
409
|
-
.mop-assistant-provider {
|
|
410
|
-
overflow: hidden;
|
|
411
|
-
text-overflow: ellipsis;
|
|
412
|
-
white-space: nowrap;
|
|
413
|
-
color: rgba(254, 249, 225, .66);
|
|
414
|
-
font-size: 12px;
|
|
415
|
-
}
|
|
416
|
-
.mop-assistant-scope {
|
|
417
|
-
display: flex;
|
|
418
|
-
align-items: center;
|
|
419
|
-
gap: 8px;
|
|
420
|
-
flex: 0 0 auto;
|
|
421
|
-
color: rgba(254, 249, 225, .82);
|
|
422
|
-
font-family: "SFMono-Regular", Consolas, monospace;
|
|
423
|
-
font-size: 10px;
|
|
424
|
-
font-weight: 800;
|
|
425
|
-
letter-spacing: .12em;
|
|
426
|
-
text-transform: uppercase;
|
|
427
|
-
}
|
|
428
|
-
.mop-assistant-scope select {
|
|
429
|
-
color: var(--mop-green);
|
|
430
|
-
background: var(--mop-paper);
|
|
431
|
-
border: 1px solid rgba(254, 249, 225, .32);
|
|
432
|
-
padding: 6px 8px;
|
|
433
|
-
font-family: "SFMono-Regular", Consolas, monospace;
|
|
434
|
-
font-size: 12px;
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
.mop-assistant-conversation { flex: 1; overflow-y: auto; padding: 0 28px; }
|
|
398
|
+
.mop-assistant-conversation { flex: 1; overflow-y: auto; padding: 0 clamp(18px, 5vw, 64px); }
|
|
438
399
|
.mop-assistant-welcome {
|
|
439
|
-
|
|
400
|
+
width: min(100%, 760px);
|
|
401
|
+
min-height: calc(100vh - 190px);
|
|
402
|
+
margin: 0 auto;
|
|
440
403
|
display: flex;
|
|
441
404
|
flex-direction: column;
|
|
442
405
|
align-items: center;
|
|
443
406
|
justify-content: center;
|
|
444
|
-
padding:
|
|
407
|
+
padding: 38px 0 110px;
|
|
445
408
|
text-align: center;
|
|
446
409
|
}
|
|
447
410
|
|
|
@@ -497,11 +460,10 @@ button {
|
|
|
497
460
|
.mop-app-topbar { grid-template-columns: 66px minmax(0, 1fr); }
|
|
498
461
|
.mop-app-brand { justify-content: center; padding: 5px; }
|
|
499
462
|
.mop-app-brand img { width: 49px; height: 49px; }
|
|
500
|
-
.mop-app-brand span
|
|
463
|
+
.mop-app-brand span { display: none; }
|
|
501
464
|
.mop-app-topbar-main { padding: 0 10px; gap: 9px; }
|
|
502
465
|
.mop-menu-toggle { display: block; }
|
|
503
|
-
.mop-topbar-
|
|
504
|
-
.mop-topbar-meta > span:first-child { display: none; }
|
|
466
|
+
.mop-topbar-center { min-width: 0; flex: 1; padding: 8px 12px; font-size: 10px; }
|
|
505
467
|
.mop-app-sidebar {
|
|
506
468
|
position: fixed;
|
|
507
469
|
top: 62px;
|
|
@@ -523,43 +485,8 @@ button {
|
|
|
523
485
|
}
|
|
524
486
|
.mop-app-main { min-height: calc(100vh - 62px); }
|
|
525
487
|
.mop-assistant-page { min-height: calc(100vh - 62px); }
|
|
526
|
-
.mop-assistant-toolbar { gap: 9px; }
|
|
527
|
-
.mop-assistant-status { display: none; }
|
|
528
488
|
.mop-assistant-conversation { padding: 0 16px; }
|
|
529
489
|
.mop-assistant-composer-wrap { padding: 26px 12px 12px; }
|
|
530
490
|
.mop-settings-grid { grid-template-columns: 1fr; }
|
|
531
491
|
.mop-user-invite-form { grid-template-columns: 1fr !important; }
|
|
532
492
|
}
|
|
533
|
-
|
|
534
|
-
.mop-back-workspace-btn {
|
|
535
|
-
display: flex;
|
|
536
|
-
align-items: center;
|
|
537
|
-
justify-content: center;
|
|
538
|
-
gap: 8px;
|
|
539
|
-
width: 100%;
|
|
540
|
-
min-height: 40px;
|
|
541
|
-
margin-bottom: 9px;
|
|
542
|
-
padding: 9px 12px;
|
|
543
|
-
border: 1px solid var(--mop-red);
|
|
544
|
-
background: var(--mop-red);
|
|
545
|
-
color: var(--mop-cream);
|
|
546
|
-
font-family: "SFMono-Regular", Consolas, monospace;
|
|
547
|
-
font-size: 11px;
|
|
548
|
-
font-weight: 900;
|
|
549
|
-
text-decoration: none;
|
|
550
|
-
cursor: pointer;
|
|
551
|
-
transition: transform 80ms steps(2, end), box-shadow 80ms steps(2, end);
|
|
552
|
-
box-shadow: 2px 2px 0 rgba(45, 74, 62, .17);
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
.mop-back-workspace-btn:hover {
|
|
556
|
-
transform: translate(-1px, -1px);
|
|
557
|
-
box-shadow: 3px 3px 0 rgba(45, 74, 62, .24);
|
|
558
|
-
color: var(--mop-cream);
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
.mop-back-workspace-btn:active {
|
|
562
|
-
transform: translate(1px, 1px);
|
|
563
|
-
box-shadow: 0 0 0 rgba(45, 74, 62, 0);
|
|
564
|
-
}
|
|
565
|
-
|
|
@@ -12,13 +12,9 @@ export type AppViewer = {
|
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
export type Project = { id: string; name: string; status: string };
|
|
15
|
-
export type ProviderState = { configured: boolean; provider?: string; model?: string | null };
|
|
16
15
|
|
|
17
16
|
interface MemoryCoreContextType {
|
|
18
|
-
selectedProject: string;
|
|
19
|
-
setSelectedProject: (id: string) => void;
|
|
20
17
|
projects: Project[];
|
|
21
|
-
provider: ProviderState;
|
|
22
18
|
settingsSection: "providers" | "users";
|
|
23
19
|
setSettingsSection: (section: "providers" | "users") => void;
|
|
24
20
|
}
|
|
@@ -27,9 +23,7 @@ const MemoryCoreContext = createContext<MemoryCoreContextType | undefined>(undef
|
|
|
27
23
|
|
|
28
24
|
export function useMemoryCore() {
|
|
29
25
|
const context = useContext(MemoryCoreContext);
|
|
30
|
-
if (!context)
|
|
31
|
-
throw new Error("useMemoryCore must be used within a MemoryCoreProvider");
|
|
32
|
-
}
|
|
26
|
+
if (!context) throw new Error("useMemoryCore must be used within a MemoryCoreProvider");
|
|
33
27
|
return context;
|
|
34
28
|
}
|
|
35
29
|
|
|
@@ -46,22 +40,17 @@ function pageTitle(pathname: string): string {
|
|
|
46
40
|
export function AppShell({ viewer, children }: { viewer: AppViewer; children: ReactNode }) {
|
|
47
41
|
const pathname = usePathname();
|
|
48
42
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
49
|
-
const isAdmin = viewer.role === "owner";
|
|
50
|
-
const title = pageTitle(pathname);
|
|
51
|
-
|
|
52
43
|
const [projects, setProjects] = useState<Project[]>([]);
|
|
53
|
-
const [provider, setProvider] = useState<ProviderState>({ configured: false });
|
|
54
|
-
const [selectedProject, setSelectedProject] = useState("");
|
|
55
44
|
const [settingsSection, setSettingsSection] = useState<"providers" | "users">("providers");
|
|
45
|
+
const isAdmin = viewer.role === "owner";
|
|
46
|
+
const isSettings = pathname.startsWith("/settings");
|
|
47
|
+
const title = pageTitle(pathname);
|
|
56
48
|
|
|
57
49
|
useEffect(() => {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
setProjects(projectData.projects ?? []);
|
|
63
|
-
setProvider(providerData.config ?? { configured: false });
|
|
64
|
-
}).catch(() => {});
|
|
50
|
+
fetch("/api/projects")
|
|
51
|
+
.then((response) => response.json())
|
|
52
|
+
.then((data) => setProjects(data.projects ?? []))
|
|
53
|
+
.catch(() => {});
|
|
65
54
|
|
|
66
55
|
const requested = new URLSearchParams(window.location.search).get("section");
|
|
67
56
|
if (requested === "users") setSettingsSection("users");
|
|
@@ -72,21 +61,13 @@ export function AppShell({ viewer, children }: { viewer: AppViewer; children: Re
|
|
|
72
61
|
window.location.replace("/login");
|
|
73
62
|
}
|
|
74
63
|
|
|
75
|
-
|
|
76
|
-
setSettingsSection(
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
const isSettings = pathname.startsWith("/settings");
|
|
82
|
-
|
|
83
|
-
const nav = [
|
|
84
|
-
{ href: "/assistant", label: "Assistant", icon: "✦", active: pathname.startsWith("/assistant") || pathname.startsWith("/chat/") },
|
|
85
|
-
{ href: "/brain", label: "Brain", icon: "◉", active: pathname.startsWith("/brain") },
|
|
86
|
-
];
|
|
64
|
+
function selectSection(section: "providers" | "users") {
|
|
65
|
+
setSettingsSection(section);
|
|
66
|
+
window.history.replaceState(null, "", section === "providers" ? "/settings" : "/settings?section=users");
|
|
67
|
+
}
|
|
87
68
|
|
|
88
69
|
return (
|
|
89
|
-
<MemoryCoreContext.Provider value={{
|
|
70
|
+
<MemoryCoreContext.Provider value={{ projects, settingsSection, setSettingsSection }}>
|
|
90
71
|
<div className="mop-app-frame">
|
|
91
72
|
<header className="mop-app-topbar">
|
|
92
73
|
<a className="mop-app-brand" href="/assistant" aria-label="MOP-AGENT home">
|
|
@@ -103,99 +84,70 @@ export function AppShell({ viewer, children }: { viewer: AppViewer; children: Re
|
|
|
103
84
|
>
|
|
104
85
|
☰
|
|
105
86
|
</button>
|
|
106
|
-
|
|
107
|
-
<
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
<strong>LIVE ASSISTANT</strong>
|
|
111
|
-
<span className="mop-assistant-provider">
|
|
112
|
-
{provider.configured ? `${provider.provider}${provider.model ? ` · ${provider.model}` : ""}` : "offline demo"}
|
|
113
|
-
</span>
|
|
114
|
-
</div>
|
|
115
|
-
<label className="mop-assistant-scope">
|
|
116
|
-
MEMORY SCOPE
|
|
117
|
-
<select value={selectedProject} onChange={(e) => setSelectedProject(e.target.value)}>
|
|
118
|
-
<option value="">All memory</option>
|
|
119
|
-
{projects.map((project) => <option key={project.id} value={project.id}>{project.name}</option>)}
|
|
120
|
-
</select>
|
|
121
|
-
</label>
|
|
122
|
-
</div>
|
|
123
|
-
) : (
|
|
124
|
-
<>
|
|
125
|
-
<div className="mop-topbar-title">
|
|
126
|
-
<span className="mop-live-dot" />
|
|
127
|
-
<strong>{title}</strong>
|
|
128
|
-
</div>
|
|
129
|
-
<div className="mop-topbar-center">MOP MEMORYCORE</div>
|
|
130
|
-
<div className="mop-topbar-meta">
|
|
131
|
-
<span>{isAdmin ? "ADMIN" : "MEMBER"}</span>
|
|
132
|
-
<span className="mop-version">v0.1.12</span>
|
|
133
|
-
</div>
|
|
134
|
-
</>
|
|
135
|
-
)}
|
|
87
|
+
<div className="mop-topbar-center">
|
|
88
|
+
<span className="mop-live-dot" />
|
|
89
|
+
<strong>{title}</strong>
|
|
90
|
+
</div>
|
|
136
91
|
</div>
|
|
137
92
|
</header>
|
|
138
93
|
|
|
139
94
|
{menuOpen && <button className="mop-sidebar-scrim" aria-label="Close navigation" onClick={() => setMenuOpen(false)} />}
|
|
140
95
|
|
|
141
96
|
<aside className={`mop-app-sidebar${menuOpen ? " is-open" : ""}`}>
|
|
142
|
-
|
|
143
|
-
<
|
|
144
|
-
<
|
|
97
|
+
<nav className="mop-sidebar-primary" aria-label="Workspace">
|
|
98
|
+
<a href="/assistant" className={pathname.startsWith("/assistant") || pathname.startsWith("/chat/") ? "is-active" : ""} onClick={() => setMenuOpen(false)}>
|
|
99
|
+
<span className="mop-nav-icon">✎</span>
|
|
100
|
+
<span>New chat</span>
|
|
101
|
+
</a>
|
|
102
|
+
<a href="/brain" className={pathname.startsWith("/brain") ? "is-active" : ""} onClick={() => setMenuOpen(false)}>
|
|
103
|
+
<span className="mop-nav-icon">◉</span>
|
|
104
|
+
<span>Brain</span>
|
|
105
|
+
</a>
|
|
106
|
+
</nav>
|
|
107
|
+
|
|
108
|
+
<div className="mop-nav-section mop-project-memory">
|
|
109
|
+
<p>PROJECT MEMORY</p>
|
|
110
|
+
<nav>
|
|
111
|
+
{projects.slice(0, 8).map((project) => (
|
|
112
|
+
<a key={project.id} href={`/brain/${project.id}`} className={pathname === `/brain/${project.id}` ? "is-active" : ""} onClick={() => setMenuOpen(false)}>
|
|
113
|
+
<span className="mop-project-dot" data-online={project.status === "online"} />
|
|
114
|
+
<span className="mop-nav-label">{project.name}</span>
|
|
115
|
+
</a>
|
|
116
|
+
))}
|
|
117
|
+
{projects.length === 0 && <span className="mop-sidebar-empty">No linked projects yet</span>}
|
|
118
|
+
</nav>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
{isAdmin && (
|
|
122
|
+
<div className="mop-nav-section mop-admin-nav">
|
|
123
|
+
<p>ADMIN</p>
|
|
145
124
|
<nav>
|
|
146
|
-
<
|
|
147
|
-
<span className="mop-nav-icon"
|
|
148
|
-
<span>
|
|
149
|
-
</
|
|
150
|
-
|
|
151
|
-
<
|
|
152
|
-
|
|
153
|
-
|
|
125
|
+
<a href="/settings" className={isSettings ? "is-active" : ""} onClick={() => setMenuOpen(false)}>
|
|
126
|
+
<span className="mop-nav-icon">⚙</span>
|
|
127
|
+
<span>Settings</span>
|
|
128
|
+
</a>
|
|
129
|
+
{isSettings && (
|
|
130
|
+
<div className="mop-settings-subnav">
|
|
131
|
+
<button className={settingsSection === "providers" ? "is-active" : ""} onClick={() => { selectSection("providers"); setMenuOpen(false); }}>
|
|
132
|
+
<span>Providers</span>
|
|
133
|
+
</button>
|
|
134
|
+
<button className={settingsSection === "users" ? "is-active" : ""} onClick={() => { selectSection("users"); setMenuOpen(false); }}>
|
|
135
|
+
<span>Users</span>
|
|
136
|
+
</button>
|
|
137
|
+
</div>
|
|
138
|
+
)}
|
|
154
139
|
</nav>
|
|
155
140
|
</div>
|
|
156
|
-
) : (
|
|
157
|
-
<>
|
|
158
|
-
<div className="mop-nav-section">
|
|
159
|
-
<p>WORKSPACE</p>
|
|
160
|
-
<nav>
|
|
161
|
-
{nav.map((item) => (
|
|
162
|
-
<a key={item.href} href={item.href} className={item.active ? "is-active" : ""} onClick={() => setMenuOpen(false)}>
|
|
163
|
-
<span className="mop-nav-icon">{item.icon}</span>
|
|
164
|
-
<span>{item.label}</span>
|
|
165
|
-
</a>
|
|
166
|
-
))}
|
|
167
|
-
</nav>
|
|
168
|
-
</div>
|
|
169
|
-
|
|
170
|
-
{isAdmin && (
|
|
171
|
-
<div className="mop-nav-section">
|
|
172
|
-
<p>ADMIN</p>
|
|
173
|
-
<nav>
|
|
174
|
-
<a href="/settings" className={pathname.startsWith("/settings") ? "is-active" : ""} onClick={() => setMenuOpen(false)}>
|
|
175
|
-
<span className="mop-nav-icon">⚙</span>
|
|
176
|
-
<span>Settings</span>
|
|
177
|
-
</a>
|
|
178
|
-
</nav>
|
|
179
|
-
</div>
|
|
180
|
-
)}
|
|
181
|
-
</>
|
|
182
141
|
)}
|
|
183
142
|
|
|
184
143
|
<div className="mop-sidebar-spacer" />
|
|
185
|
-
|
|
186
|
-
{isSettings && (
|
|
187
|
-
<a href="/assistant" className="mop-back-workspace-btn">
|
|
188
|
-
<span>← BACK TO WORKSPACE</span>
|
|
189
|
-
</a>
|
|
190
|
-
)}
|
|
191
|
-
|
|
192
144
|
<button className="mop-account-card" type="button" onClick={logout} title="Sign out">
|
|
193
145
|
<span className="mop-account-avatar">{viewer.name.slice(0, 1).toUpperCase()}</span>
|
|
194
146
|
<span className="mop-account-copy">
|
|
195
147
|
<strong>{viewer.name}</strong>
|
|
196
148
|
<small>{isAdmin ? "Administrator" : "Member"}</small>
|
|
197
149
|
</span>
|
|
198
|
-
<span aria-hidden="true"
|
|
150
|
+
<span aria-hidden="true">•••</span>
|
|
199
151
|
</button>
|
|
200
152
|
</aside>
|
|
201
153
|
|
|
@@ -204,4 +156,3 @@ export function AppShell({ viewer, children }: { viewer: AppViewer; children: Re
|
|
|
204
156
|
</MemoryCoreContext.Provider>
|
|
205
157
|
);
|
|
206
158
|
}
|
|
207
|
-
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mop-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "mop-agent",
|
|
9
|
-
"version": "0.1.
|
|
9
|
+
"version": "0.1.13",
|
|
10
10
|
"license": "UNLICENSED",
|
|
11
11
|
"workspaces": [
|
|
12
12
|
"packages/*",
|
package/package.json
CHANGED