mop-agent 0.1.9 → 0.1.11
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 +1 -1
- package/apps/web/app/assistant/page.tsx +5 -29
- package/apps/web/app/globals.css +63 -1
- package/apps/web/app/settings/page.tsx +2 -18
- package/apps/web/components/AppShell.tsx +165 -68
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ 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:** npm package `mop-agent@0.1.
|
|
8
|
+
> **Release status:** npm package `mop-agent@0.1.10` contains the corrected VPS
|
|
9
9
|
> installer, one-time Admin setup/login flow, and shared retro application shell.
|
|
10
10
|
> The canonical installation command is exactly `npx mop-agent`.
|
|
11
11
|
|
|
@@ -2,16 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
import type { CSSProperties } from "react";
|
|
4
4
|
import { useEffect, useRef, useState } from "react";
|
|
5
|
+
import { useMemoryCore } from "@/components/AppShell";
|
|
5
6
|
|
|
6
7
|
type Turn = { role: "user" | "assistant"; content: string };
|
|
7
|
-
type Project = { id: string; name: string; status: string };
|
|
8
|
-
type ProviderState = { configured: boolean; provider?: string; model?: string | null };
|
|
9
8
|
|
|
10
9
|
export default function AssistantPage() {
|
|
10
|
+
const { selectedProject, setSelectedProject, projects, provider } = useMemoryCore();
|
|
11
11
|
const [turns, setTurns] = useState<Turn[]>([]);
|
|
12
|
-
const [projects, setProjects] = useState<Project[]>([]);
|
|
13
|
-
const [selectedProject, setSelectedProject] = useState("");
|
|
14
|
-
const [provider, setProvider] = useState<ProviderState>({ configured: false });
|
|
15
12
|
const [name, setName] = useState("Admin");
|
|
16
13
|
const [input, setInput] = useState("");
|
|
17
14
|
const [busy, setBusy] = useState(false);
|
|
@@ -19,17 +16,12 @@ export default function AssistantPage() {
|
|
|
19
16
|
const endRef = useRef<HTMLDivElement>(null);
|
|
20
17
|
|
|
21
18
|
useEffect(() => {
|
|
22
|
-
|
|
23
|
-
fetch("/api/projects").then((r) => r.json()),
|
|
24
|
-
fetch("/api/providers").then((r) => r.json()),
|
|
25
|
-
fetch("/api/me").then((r) => r.json()),
|
|
26
|
-
]).then(([projectData, providerData, me]) => {
|
|
27
|
-
setProjects(projectData.projects ?? []);
|
|
28
|
-
setProvider(providerData.config ?? { configured: false });
|
|
19
|
+
fetch("/api/me").then((r) => r.json()).then((me) => {
|
|
29
20
|
setName(me.user?.name || me.user?.email || "Admin");
|
|
30
21
|
}).catch(() => {});
|
|
31
22
|
}, []);
|
|
32
23
|
|
|
24
|
+
|
|
33
25
|
async function send(prefill?: string) {
|
|
34
26
|
const message = (prefill ?? input).trim();
|
|
35
27
|
if (!message || busy) return;
|
|
@@ -77,22 +69,6 @@ export default function AssistantPage() {
|
|
|
77
69
|
|
|
78
70
|
return (
|
|
79
71
|
<section className="mop-assistant-page">
|
|
80
|
-
<div className="mop-assistant-toolbar">
|
|
81
|
-
<div>
|
|
82
|
-
<strong style={{ fontFamily: '"SFMono-Regular", Consolas, monospace', color: "#742220" }}>LIVE ASSISTANT</strong>
|
|
83
|
-
<span style={{ color: "rgba(45,74,62,.62)", marginLeft: 10, fontSize: 12 }}>
|
|
84
|
-
{provider.configured ? `${provider.provider}${provider.model ? ` · ${provider.model}` : ""}` : "offline demo"}
|
|
85
|
-
</span>
|
|
86
|
-
</div>
|
|
87
|
-
<label style={{ color: "#2d4a3e", fontSize: 12 }}>
|
|
88
|
-
MEMORY SCOPE
|
|
89
|
-
<select value={selectedProject} onChange={(e) => setSelectedProject(e.target.value)} style={selectStyle}>
|
|
90
|
-
<option value="">All memory</option>
|
|
91
|
-
{projects.map((project) => <option key={project.id} value={project.id}>{project.name}</option>)}
|
|
92
|
-
</select>
|
|
93
|
-
</label>
|
|
94
|
-
</div>
|
|
95
|
-
|
|
96
72
|
<div className="mop-assistant-conversation">
|
|
97
73
|
{turns.length === 0 ? (
|
|
98
74
|
<div className="mop-assistant-welcome">
|
|
@@ -152,7 +128,7 @@ export default function AssistantPage() {
|
|
|
152
128
|
}
|
|
153
129
|
|
|
154
130
|
const selectStyle: CSSProperties = { color: "#2d4a3e", border: "1px solid rgba(45,74,62,.42)", padding: "6px 8px", background: "#fffdf2" };
|
|
155
|
-
const assistantLogo: CSSProperties = { width: 86, height: 86, display: "grid", placeItems: "center"
|
|
131
|
+
const assistantLogo: CSSProperties = { width: 86, height: 86, display: "grid", placeItems: "center" };
|
|
156
132
|
const promptGrid: CSSProperties = { width: "min(100%, 650px)", display: "grid", gridTemplateColumns: "repeat(2,minmax(0,1fr))", gap: 10, marginTop: 28 };
|
|
157
133
|
const promptCard: CSSProperties = { display: "flex", justifyContent: "space-between", padding: "14px 15px", border: "1px solid rgba(45,74,62,.38)", borderBottomWidth: 3, background: "#fffdf2", color: "#2d4a3e", cursor: "pointer", textAlign: "left" };
|
|
158
134
|
const botAvatar: CSSProperties = { width: 32, height: 32, display: "grid", placeItems: "center", background: "#742220", color: "#fef9e1" };
|
package/apps/web/app/globals.css
CHANGED
|
@@ -411,11 +411,12 @@ button {
|
|
|
411
411
|
|
|
412
412
|
.mop-settings-grid {
|
|
413
413
|
display: grid;
|
|
414
|
-
grid-template-columns:
|
|
414
|
+
grid-template-columns: 1fr;
|
|
415
415
|
gap: 18px;
|
|
416
416
|
align-items: start;
|
|
417
417
|
}
|
|
418
418
|
|
|
419
|
+
|
|
419
420
|
.mop-settings-nav { padding: 9px; }
|
|
420
421
|
.mop-settings-nav button {
|
|
421
422
|
width: 100%;
|
|
@@ -504,4 +505,65 @@ button {
|
|
|
504
505
|
.mop-settings-nav { display: flex; gap: 7px; }
|
|
505
506
|
.mop-settings-nav button { justify-content: center; }
|
|
506
507
|
.mop-user-invite-form { grid-template-columns: 1fr !important; }
|
|
508
|
+
.mop-settings-sidebar {
|
|
509
|
+
position: fixed;
|
|
510
|
+
top: 62px;
|
|
511
|
+
left: 0;
|
|
512
|
+
bottom: 0;
|
|
513
|
+
width: min(82vw, 280px);
|
|
514
|
+
height: auto;
|
|
515
|
+
transform: translateX(-105%);
|
|
516
|
+
transition: transform 160ms steps(4, end);
|
|
517
|
+
display: flex !important;
|
|
518
|
+
flex-direction: column !important;
|
|
519
|
+
}
|
|
520
|
+
.mop-settings-sidebar.is-open {
|
|
521
|
+
transform: translateX(0);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
.mop-settings-sidebar {
|
|
526
|
+
grid-area: sidebar;
|
|
527
|
+
position: sticky;
|
|
528
|
+
top: 70px;
|
|
529
|
+
height: calc(100vh - 70px);
|
|
530
|
+
z-index: 40;
|
|
531
|
+
display: flex;
|
|
532
|
+
flex-direction: column;
|
|
533
|
+
overflow-y: auto;
|
|
534
|
+
padding: 15px 9px 12px;
|
|
535
|
+
border-right: 2px solid rgba(45, 74, 62, .46);
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
.mop-back-workspace-btn {
|
|
539
|
+
display: flex;
|
|
540
|
+
align-items: center;
|
|
541
|
+
justify-content: center;
|
|
542
|
+
gap: 8px;
|
|
543
|
+
width: 100%;
|
|
544
|
+
min-height: 40px;
|
|
545
|
+
margin-bottom: 9px;
|
|
546
|
+
padding: 9px 12px;
|
|
547
|
+
border: 1px solid var(--mop-red);
|
|
548
|
+
background: var(--mop-red);
|
|
549
|
+
color: var(--mop-cream);
|
|
550
|
+
font-family: "SFMono-Regular", Consolas, monospace;
|
|
551
|
+
font-size: 11px;
|
|
552
|
+
font-weight: 900;
|
|
553
|
+
text-decoration: none;
|
|
554
|
+
cursor: pointer;
|
|
555
|
+
transition: transform 80ms steps(2, end), box-shadow 80ms steps(2, end);
|
|
556
|
+
box-shadow: 2px 2px 0 rgba(45, 74, 62, .17);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
.mop-back-workspace-btn:hover {
|
|
560
|
+
transform: translate(-1px, -1px);
|
|
561
|
+
box-shadow: 3px 3px 0 rgba(45, 74, 62, .24);
|
|
562
|
+
color: var(--mop-cream);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
.mop-back-workspace-btn:active {
|
|
566
|
+
transform: translate(1px, 1px);
|
|
567
|
+
box-shadow: 0 0 0 rgba(45, 74, 62, 0);
|
|
507
568
|
}
|
|
569
|
+
|
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
import type { CSSProperties } from "react";
|
|
4
4
|
import { useEffect, useState } from "react";
|
|
5
|
+
import { useMemoryCore } from "@/components/AppShell";
|
|
5
6
|
|
|
6
|
-
type Section = "providers" | "users";
|
|
7
7
|
type Masked = { configured: boolean; provider?: string; model?: string | null; keyHint?: string };
|
|
8
8
|
type Member = { id: string; email: string; name: string; role: string };
|
|
9
9
|
|
|
10
10
|
export default function SettingsPage() {
|
|
11
|
-
const
|
|
11
|
+
const { settingsSection: section } = useMemoryCore();
|
|
12
12
|
const [config, setConfig] = useState<Masked>({ configured: false });
|
|
13
13
|
const [env, setEnv] = useState<{ anthropic: boolean; openrouter: boolean }>({ anthropic: false, openrouter: false });
|
|
14
14
|
const [provider, setProvider] = useState<"anthropic" | "openrouter">("openrouter");
|
|
@@ -34,17 +34,10 @@ export default function SettingsPage() {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
useEffect(() => {
|
|
37
|
-
const requested = new URLSearchParams(window.location.search).get("section");
|
|
38
|
-
if (requested === "users") setSection("users");
|
|
39
37
|
loadProvider();
|
|
40
38
|
loadUsers();
|
|
41
39
|
}, []);
|
|
42
40
|
|
|
43
|
-
function chooseSection(next: Section) {
|
|
44
|
-
setSection(next);
|
|
45
|
-
const url = next === "providers" ? "/settings" : "/settings?section=users";
|
|
46
|
-
window.history.replaceState(null, "", url);
|
|
47
|
-
}
|
|
48
41
|
|
|
49
42
|
async function saveProvider(e: React.FormEvent) {
|
|
50
43
|
e.preventDefault();
|
|
@@ -90,15 +83,6 @@ export default function SettingsPage() {
|
|
|
90
83
|
</header>
|
|
91
84
|
|
|
92
85
|
<div className="mop-settings-grid">
|
|
93
|
-
<aside className="mop-settings-nav mop-panel" aria-label="Settings sections">
|
|
94
|
-
<button className={section === "providers" ? "is-active" : ""} onClick={() => chooseSection("providers")}>
|
|
95
|
-
<span>◇</span><strong>Providers</strong>
|
|
96
|
-
</button>
|
|
97
|
-
<button className={section === "users" ? "is-active" : ""} onClick={() => chooseSection("users")}>
|
|
98
|
-
<span>♙</span><strong>Users</strong>
|
|
99
|
-
</button>
|
|
100
|
-
</aside>
|
|
101
|
-
|
|
102
86
|
<section className="mop-settings-content mop-panel">
|
|
103
87
|
{section === "providers" ? (
|
|
104
88
|
<>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import type { ReactNode } from "react";
|
|
4
4
|
import { usePathname } from "next/navigation";
|
|
5
|
-
import { useState } from "react";
|
|
5
|
+
import { useState, useEffect, createContext, useContext } from "react";
|
|
6
6
|
import { signOut } from "@/lib/auth-client";
|
|
7
7
|
|
|
8
8
|
export type AppViewer = {
|
|
@@ -11,6 +11,31 @@ export type AppViewer = {
|
|
|
11
11
|
role: "owner" | "member";
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
+
export type Project = { id: string; name: string; status: string };
|
|
15
|
+
export type ProviderState = { configured: boolean; provider?: string; model?: string | null };
|
|
16
|
+
|
|
17
|
+
interface MemoryCoreContextType {
|
|
18
|
+
selectedProject: string;
|
|
19
|
+
setSelectedProject: (id: string) => void;
|
|
20
|
+
projects: Project[];
|
|
21
|
+
provider: ProviderState;
|
|
22
|
+
settingsSection: "providers" | "users";
|
|
23
|
+
setSettingsSection: (section: "providers" | "users") => void;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const MemoryCoreContext = createContext<MemoryCoreContextType | undefined>(undefined);
|
|
27
|
+
|
|
28
|
+
export function useMemoryCore() {
|
|
29
|
+
const context = useContext(MemoryCoreContext);
|
|
30
|
+
if (!context) {
|
|
31
|
+
throw new Error("useMemoryCore must be used within a MemoryCoreProvider");
|
|
32
|
+
}
|
|
33
|
+
return context;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const selectStyle = { color: "#2d4a3e", border: "1px solid rgba(45,74,62,.42)", padding: "6px 8px", background: "#fffdf2" };
|
|
37
|
+
|
|
38
|
+
|
|
14
39
|
function pageTitle(pathname: string): string {
|
|
15
40
|
if (pathname === "/assistant") return "Assistant";
|
|
16
41
|
if (pathname === "/brain/graph") return "Knowledge Graph";
|
|
@@ -27,84 +52,156 @@ export function AppShell({ viewer, children }: { viewer: AppViewer; children: Re
|
|
|
27
52
|
const isAdmin = viewer.role === "owner";
|
|
28
53
|
const title = pageTitle(pathname);
|
|
29
54
|
|
|
55
|
+
const [projects, setProjects] = useState<Project[]>([]);
|
|
56
|
+
const [provider, setProvider] = useState<ProviderState>({ configured: false });
|
|
57
|
+
const [selectedProject, setSelectedProject] = useState("");
|
|
58
|
+
const [settingsSection, setSettingsSection] = useState<"providers" | "users">("providers");
|
|
59
|
+
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
Promise.all([
|
|
62
|
+
fetch("/api/projects").then((r) => r.json()),
|
|
63
|
+
fetch("/api/providers").then((r) => r.json()),
|
|
64
|
+
]).then(([projectData, providerData]) => {
|
|
65
|
+
setProjects(projectData.projects ?? []);
|
|
66
|
+
setProvider(providerData.config ?? { configured: false });
|
|
67
|
+
}).catch(() => {});
|
|
68
|
+
|
|
69
|
+
const requested = new URLSearchParams(window.location.search).get("section");
|
|
70
|
+
if (requested === "users") setSettingsSection("users");
|
|
71
|
+
}, []);
|
|
72
|
+
|
|
30
73
|
async function logout() {
|
|
31
74
|
await signOut();
|
|
32
75
|
window.location.replace("/login");
|
|
33
76
|
}
|
|
34
77
|
|
|
78
|
+
const selectSection = (sec: "providers" | "users") => {
|
|
79
|
+
setSettingsSection(sec);
|
|
80
|
+
const url = sec === "providers" ? "/settings" : "/settings?section=users";
|
|
81
|
+
window.history.replaceState(null, "", url);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const isSettings = pathname.startsWith("/settings");
|
|
85
|
+
|
|
35
86
|
const nav = [
|
|
36
87
|
{ href: "/assistant", label: "Assistant", icon: "✦", active: pathname.startsWith("/assistant") || pathname.startsWith("/chat/") },
|
|
37
88
|
{ href: "/brain", label: "Brain", icon: "◉", active: pathname.startsWith("/brain") },
|
|
38
89
|
];
|
|
39
90
|
|
|
40
91
|
return (
|
|
41
|
-
<
|
|
42
|
-
<
|
|
43
|
-
<
|
|
44
|
-
<
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
<
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
<nav>
|
|
88
|
-
<a href="/settings" className={pathname.startsWith("/settings") ? "is-active" : ""} onClick={() => setMenuOpen(false)}>
|
|
89
|
-
<span className="mop-nav-icon">⚙</span>
|
|
90
|
-
<span>Settings</span>
|
|
91
|
-
</a>
|
|
92
|
-
</nav>
|
|
92
|
+
<MemoryCoreContext.Provider value={{ selectedProject, setSelectedProject, projects, provider, settingsSection, setSettingsSection }}>
|
|
93
|
+
<div className="mop-app-frame">
|
|
94
|
+
<header className="mop-app-topbar">
|
|
95
|
+
<a className="mop-app-brand" href="/assistant" aria-label="MOP-AGENT home">
|
|
96
|
+
<img src="/icon.svg" alt="" />
|
|
97
|
+
<span>MOP-AGENT</span>
|
|
98
|
+
</a>
|
|
99
|
+
<div className="mop-app-topbar-main">
|
|
100
|
+
<button
|
|
101
|
+
className="mop-menu-toggle"
|
|
102
|
+
type="button"
|
|
103
|
+
aria-label="Toggle navigation"
|
|
104
|
+
aria-expanded={menuOpen}
|
|
105
|
+
onClick={() => setMenuOpen((open) => !open)}
|
|
106
|
+
>
|
|
107
|
+
☰
|
|
108
|
+
</button>
|
|
109
|
+
{pathname === "/assistant" ? (
|
|
110
|
+
<div className="mop-assistant-toolbar" style={{ border: 0, padding: 0, margin: 0, background: "transparent", width: "100%", display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
|
111
|
+
<div>
|
|
112
|
+
<strong style={{ fontFamily: '"SFMono-Regular", Consolas, monospace', color: "#742220" }}>LIVE ASSISTANT</strong>
|
|
113
|
+
<span style={{ color: "rgba(45,74,62,.62)", marginLeft: 10, fontSize: 12 }}>
|
|
114
|
+
{provider.configured ? `${provider.provider}${provider.model ? ` · ${provider.model}` : ""}` : "offline demo"}
|
|
115
|
+
</span>
|
|
116
|
+
</div>
|
|
117
|
+
<label style={{ color: "#2d4a3e", fontSize: 12 }}>
|
|
118
|
+
MEMORY SCOPE
|
|
119
|
+
<select value={selectedProject} onChange={(e) => setSelectedProject(e.target.value)} style={selectStyle}>
|
|
120
|
+
<option value="">All memory</option>
|
|
121
|
+
{projects.map((project) => <option key={project.id} value={project.id}>{project.name}</option>)}
|
|
122
|
+
</select>
|
|
123
|
+
</label>
|
|
124
|
+
</div>
|
|
125
|
+
) : (
|
|
126
|
+
<>
|
|
127
|
+
<div className="mop-topbar-title">
|
|
128
|
+
<span className="mop-live-dot" />
|
|
129
|
+
<strong>{title}</strong>
|
|
130
|
+
</div>
|
|
131
|
+
<div className="mop-topbar-center">MOP MEMORYCORE</div>
|
|
132
|
+
<div className="mop-topbar-meta">
|
|
133
|
+
<span>{isAdmin ? "ADMIN" : "MEMBER"}</span>
|
|
134
|
+
<span className="mop-version">v0.1.10</span>
|
|
135
|
+
</div>
|
|
136
|
+
</>
|
|
137
|
+
)}
|
|
93
138
|
</div>
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
<
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
139
|
+
</header>
|
|
140
|
+
|
|
141
|
+
{menuOpen && <button className="mop-sidebar-scrim" aria-label="Close navigation" onClick={() => setMenuOpen(false)} />}
|
|
142
|
+
|
|
143
|
+
<aside className={isSettings
|
|
144
|
+
? `mop-settings-nav mop-panel mop-settings-sidebar${menuOpen ? " is-open" : ""}`
|
|
145
|
+
: `mop-app-sidebar${menuOpen ? " is-open" : ""}`}
|
|
146
|
+
>
|
|
147
|
+
{isSettings ? (
|
|
148
|
+
<>
|
|
149
|
+
<button className={settingsSection === "providers" ? "is-active" : ""} onClick={() => { selectSection("providers"); setMenuOpen(false); }}>
|
|
150
|
+
<span>◇</span><strong>Providers</strong>
|
|
151
|
+
</button>
|
|
152
|
+
<button className={settingsSection === "users" ? "is-active" : ""} onClick={() => { selectSection("users"); setMenuOpen(false); }}>
|
|
153
|
+
<span>♙</span><strong>Users</strong>
|
|
154
|
+
</button>
|
|
155
|
+
</>
|
|
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
|
+
)}
|
|
183
|
+
|
|
184
|
+
<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
|
+
<button className="mop-account-card" type="button" onClick={logout} title="Sign out">
|
|
193
|
+
<span className="mop-account-avatar">{viewer.name.slice(0, 1).toUpperCase()}</span>
|
|
194
|
+
<span className="mop-account-copy">
|
|
195
|
+
<strong>{viewer.name}</strong>
|
|
196
|
+
<small>{isAdmin ? "Administrator" : "Member"}</small>
|
|
197
|
+
</span>
|
|
198
|
+
<span aria-hidden="true">↪</span>
|
|
199
|
+
</button>
|
|
200
|
+
</aside>
|
|
201
|
+
|
|
202
|
+
<main className="mop-app-main">{children}</main>
|
|
203
|
+
</div>
|
|
204
|
+
</MemoryCoreContext.Provider>
|
|
109
205
|
);
|
|
110
206
|
}
|
|
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.11",
|
|
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.11",
|
|
10
10
|
"license": "UNLICENSED",
|
|
11
11
|
"workspaces": [
|
|
12
12
|
"packages/*",
|
package/package.json
CHANGED