@sksoftofficial/mindroot 1.1.0 → 1.3.0
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/package.json +9 -2
- package/src/dashboard/favicon.svg +6 -0
- package/src/dashboard/graph.js +280 -24
- package/src/dashboard/index.html +350 -101
- package/src/dashboard/logo.svg +6 -0
- package/src/dashboard/workspace.css +144 -0
- package/src/embedder.js +2 -0
package/src/dashboard/index.html
CHANGED
|
@@ -4,34 +4,28 @@
|
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
6
|
<meta name="theme-color" content="#161616">
|
|
7
|
-
<link rel="icon" type="image/
|
|
7
|
+
<link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40'%3E%3Cpath d='M20 18v17m0-10-8 6m8-6 8 6' fill='none' stroke='%23e9973f' stroke-width='2.8' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M20 21C10 21 5 15 6 7c8-1 14 4 14 14Z' fill='%23e9973f'/%3E%3Cpath d='M22 17C22 9 27 4 35 5c0 8-5 13-13 12Z' fill='%23f2bd7e'/%3E%3C/svg%3E">
|
|
8
8
|
<title>Mindroot</title>
|
|
9
9
|
<style>
|
|
10
10
|
:root { --bg:#1e1e1e; --bg2:#161616; --bg3:#2b2b2b; --hover:#303030; --fg:#dcddde; --dim:#999; --accent:#e9973f; --accent-strong:#d97721; --accent-soft:#442f20; --danger:#ff6b6b; --ok:#53c59f; --border:rgba(255,255,255,.055); --border-soft:rgba(255,255,255,.03); --border-hover:rgba(255,255,255,.11); --border-accent:rgba(233,151,63,.22); }
|
|
11
|
-
* { box-sizing:border-box; margin:0; padding:0; }
|
|
11
|
+
* { box-sizing:border-box; margin:0; padding:0; outline:none !important; }
|
|
12
12
|
* { scrollbar-width:thin; scrollbar-color:#505050 transparent }
|
|
13
13
|
*::-webkit-scrollbar { width:10px; height:10px }
|
|
14
14
|
*::-webkit-scrollbar-track { background:transparent }
|
|
15
15
|
*::-webkit-scrollbar-thumb { background:#484848; border:2px solid transparent; border-radius:999px; background-clip:padding-box }
|
|
16
16
|
*::-webkit-scrollbar-thumb:hover { background:#606060; border:2px solid transparent; background-clip:padding-box }
|
|
17
17
|
*::-webkit-scrollbar-corner { background:transparent }
|
|
18
|
-
body { font:14px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background:var(--bg); color:var(--fg); height:100vh; height:100dvh; display:flex; flex-direction:column; }
|
|
19
|
-
header { position:relative; z-index:2;
|
|
18
|
+
body { font:14px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background:var(--bg); color:var(--fg); min-height:100vh; min-height:100dvh; display:flex; flex-direction:column; }
|
|
19
|
+
header { position:relative; z-index:2; flex:none; display:flex; align-items:center; gap:18px; padding:12px 24px; background:var(--bg2); border-bottom:1px solid var(--border-soft); box-shadow:0 8px 20px rgba(0,0,0,.14); }
|
|
20
20
|
.brand { display:flex; align-items:center; gap:10px; min-width:0 }
|
|
21
21
|
.brand-mark { width:36px; height:36px; flex:none; display:grid; place-items:center; background:#211c19; border:1px solid rgba(233,151,63,.14); border-radius:8px; box-shadow:0 1px 2px rgba(0,0,0,.3), inset 0 1px rgba(255,255,255,.025) }
|
|
22
22
|
.brand-logo { width:26px; height:26px; object-fit:contain; flex:none }
|
|
23
23
|
.brand-copy { min-width:0; line-height:1.15 }
|
|
24
24
|
header h1 { font-size:15px; font-weight:680; letter-spacing:-.015em; color:var(--fg); }
|
|
25
25
|
.brand-subtitle { display:block; margin-top:3px; color:#aaa; font-size:11px; white-space:nowrap }
|
|
26
|
-
.header-context { min-width:0; display:flex; align-items:center; gap:8px; color:var(--dim); font-size:12px }
|
|
27
|
-
.header-context[hidden] { display:none }
|
|
28
|
-
.header-divider { width:1px; height:24px; flex:none; background:var(--border-soft) }
|
|
29
|
-
.header-context svg { width:15px; height:15px; flex:none; stroke:currentColor }
|
|
30
|
-
#headerProject { max-width:280px; overflow:hidden; color:#b9b9b9; text-overflow:ellipsis; white-space:nowrap }
|
|
31
26
|
header .spacer { flex:1 }
|
|
32
|
-
.header-lock {
|
|
33
|
-
.header-lock:hover { color:var(--fg); background
|
|
34
|
-
.header-lock:focus-visible { outline:2px solid var(--accent); outline-offset:2px }
|
|
27
|
+
.header-lock { padding:5px 10px; display:flex; align-items:center; gap:7px; font-size:12px; color:var(--dim); background:transparent; border-color:transparent; transition:background-color 120ms ease-out, border-color 120ms ease-out, color 120ms ease-out }
|
|
28
|
+
.header-lock:hover { color:var(--fg); background:#ffffff0c; border-color:#ffffff40 }
|
|
35
29
|
.header-lock svg { width:16px; height:16px; stroke:currentColor }
|
|
36
30
|
.header-lock-label { font-size:12px }
|
|
37
31
|
button { background:var(--bg3); color:var(--fg); border:1px solid var(--border); border-radius:5px; padding:5px 10px; cursor:pointer; font:inherit; }
|
|
@@ -40,35 +34,23 @@
|
|
|
40
34
|
button.primary:hover { background:#e58732; border-color:#e58732 }
|
|
41
35
|
button.danger { color:var(--danger) }
|
|
42
36
|
input, textarea { background:#242424; color:var(--fg); border:1px solid var(--border); border-radius:5px; padding:6px 9px; font:inherit }
|
|
43
|
-
input:focus, textarea:focus { outline:none;
|
|
37
|
+
input:focus, textarea:focus { outline:none; }
|
|
44
38
|
#gate { margin:auto; text-align:center; display:flex; flex-direction:column; gap:14px; width:min(420px, calc(100% - 32px)); padding:28px; background:var(--bg2); border:1px solid var(--border); border-radius:10px; box-shadow:0 12px 40px rgba(0,0,0,.28) }
|
|
45
39
|
#gate input { width:100% }
|
|
46
|
-
main { display:flex; flex:1
|
|
47
|
-
aside { position:relative; z-index:1; width:230px; background:var(--bg2); border-right:1px solid var(--border-soft);
|
|
48
|
-
.sidebar-toggle { flex:none; height:34px; padding:0; display:flex; align-items:center; justify-content:center }
|
|
49
|
-
.sidebar-toggle svg { width:17px; height:17px; stroke:currentColor; transition:transform 160ms ease-out }
|
|
40
|
+
main { display:flex; flex:1 }
|
|
41
|
+
aside { position:relative; z-index:1; width:230px; background:var(--bg2); border-right:1px solid var(--border-soft); padding:10px 14px; box-shadow:8px 0 20px rgba(0,0,0,.12); display:flex; flex-direction:column; gap:8px; transition:width 160ms ease-out }
|
|
50
42
|
.sidebar-item { width:100%; display:flex; align-items:center; gap:9px; padding:8px 10px; text-align:left; color:var(--dim); background:transparent; border-color:transparent; white-space:nowrap }
|
|
51
43
|
.sidebar-item svg { width:17px; height:17px; flex:none; stroke:currentColor }
|
|
52
44
|
.sidebar-item.active { background:var(--accent-soft); color:#f2b46f }
|
|
53
|
-
.sidebar-item:focus-visible, .proj:focus-visible { outline:2px solid var(--accent); outline-offset:-2px }
|
|
54
45
|
.sidebar-item .menu-chevron { margin-left:auto; width:13px; transition:transform 160ms ease-out }
|
|
55
46
|
.sidebar-item[aria-expanded="true"] .menu-chevron { transform:rotate(90deg) }
|
|
56
47
|
#projectMenu { min-width:0 }
|
|
57
48
|
#projList[hidden] { display:none }
|
|
58
|
-
@media (min-width:701px) {
|
|
59
|
-
aside.collapsed { width:56px; padding:10px 12px }
|
|
60
|
-
aside.collapsed .sidebar-toggle svg { transform:rotate(180deg) }
|
|
61
|
-
aside.collapsed .sidebar-item { padding:8px 0; justify-content:center }
|
|
62
|
-
aside.collapsed .sidebar-label, aside.collapsed .menu-chevron { display:none }
|
|
63
|
-
aside.collapsed #projList { margin-left:0; padding-left:0; border-left:0 }
|
|
64
|
-
aside.collapsed .proj { padding:7px 0; text-align:center; font-size:0 }
|
|
65
|
-
aside.collapsed .proj::before { content:attr(data-initial); font-size:12px; line-height:inherit }
|
|
66
|
-
}
|
|
67
49
|
#projList { display:flex; flex-direction:column; gap:2px; margin:4px 0 0 18px; padding-left:8px; border-left:1px solid var(--border) }
|
|
68
50
|
.proj { padding:7px 10px; border-radius:5px; cursor:pointer; color:var(--dim); background:transparent; border:0; text-align:left; overflow-wrap:anywhere }
|
|
69
51
|
.proj:hover { background:var(--hover); color:var(--fg) }
|
|
70
52
|
.proj.active { background:var(--accent-soft); color:#f2b46f }
|
|
71
|
-
#content { flex:1; min-width:0;
|
|
53
|
+
#content { flex:1; min-width:0; padding:18px 22px; display:flex; flex-direction:column }
|
|
72
54
|
.workspace-head { flex:none; display:flex; align-items:center; gap:12px; min-height:36px; margin-bottom:16px; padding-bottom:12px; border-bottom:1px solid var(--border) }
|
|
73
55
|
.tabs { display:flex; gap:4px; align-items:center }
|
|
74
56
|
.tab { padding:5px 12px; border-radius:5px; cursor:pointer; color:var(--dim); border:1px solid transparent }
|
|
@@ -77,9 +59,9 @@
|
|
|
77
59
|
.tab.active { color:#f2b46f; border-color:var(--border-accent); background:var(--accent-soft) }
|
|
78
60
|
.project-actions { margin-left:auto; display:flex; align-items:center; gap:10px }
|
|
79
61
|
.delete-project { white-space:nowrap }
|
|
80
|
-
.view { flex:1; min-height:0;
|
|
81
|
-
.graph-view { display:flex; flex-direction:column; gap:12px
|
|
82
|
-
.graph-workspace { display:flex; flex:1; min-height:
|
|
62
|
+
.view { flex:1; min-height:0; width:100% }
|
|
63
|
+
.graph-view { display:flex; flex-direction:column; gap:12px }
|
|
64
|
+
.graph-workspace { display:flex; flex:1; min-height:500px; border:1px solid var(--border); border-radius:8px; overflow:hidden; background:var(--bg2) }
|
|
83
65
|
.graph-stage { position:relative; flex:1; min-width:0; overflow:hidden }
|
|
84
66
|
.graph-canvas { position:absolute; inset:0; touch-action:none }
|
|
85
67
|
.graph-legend { position:absolute; left:16px; top:14px; display:flex; gap:16px; color:var(--dim); font-size:12px; pointer-events:none }
|
|
@@ -89,9 +71,8 @@
|
|
|
89
71
|
.graph-controls { position:absolute; bottom:14px; left:14px; display:flex; gap:5px }
|
|
90
72
|
.graph-controls button { min-width:34px }
|
|
91
73
|
.graph-hint { color:var(--dim); font-size:11px; flex:none }
|
|
92
|
-
.graph-view button:focus-visible, .tab:focus-visible { outline:2px solid var(--accent); outline-offset:2px }
|
|
93
74
|
.docrow { padding:11px 12px; border:1px solid var(--border); border-radius:7px; margin-bottom:8px; cursor:pointer; background:#242424 }
|
|
94
|
-
.docrow:hover { background:#282828;
|
|
75
|
+
.docrow:hover { background:#282828; }
|
|
95
76
|
.doc-head { display:flex; align-items:baseline; justify-content:space-between; gap:12px; margin-bottom:5px }
|
|
96
77
|
.docrow .t { color:var(--fg); font-weight:600 }
|
|
97
78
|
.docrow .p { color:var(--dim); font-size:12px }
|
|
@@ -109,7 +90,7 @@
|
|
|
109
90
|
button:disabled { opacity:.5; cursor:default }
|
|
110
91
|
.skill-fields [hidden], .editor-head .viewmode[hidden] { display:none }
|
|
111
92
|
.sec-chip { display:inline-block; background:var(--bg3); border-radius:4px; padding:0 6px; margin:3px 4px 0 0; font-size:12px; color:#aaa }
|
|
112
|
-
textarea.editor { width:100%; min-height:420px; resize:vertical; white-space:pre; line-height:1.5; font-family:ui-monospace, SFMono-Regular, Menlo, monospace }
|
|
93
|
+
textarea.editor { width:100%; min-height:420px; resize:vertical; white-space:pre-wrap; overflow-wrap:anywhere; line-height:1.5; font-family:ui-monospace, SFMono-Regular, Menlo, monospace }
|
|
113
94
|
.rem { border:1px solid var(--border); border-radius:7px; padding:10px 12px; margin-bottom:8px; display:flex; gap:10px; align-items:flex-start; background:#242424 }
|
|
114
95
|
.rem .txt { flex:1 }
|
|
115
96
|
.rem a { color:var(--accent); text-decoration:none; font-size:12px }
|
|
@@ -118,7 +99,7 @@
|
|
|
118
99
|
.err { color:var(--danger) }
|
|
119
100
|
.empty { color:var(--dim); border:1px dashed var(--border); border-radius:7px; padding:28px 20px; text-align:center }
|
|
120
101
|
h2 { font-size:13px; text-transform:uppercase; letter-spacing:.1em; color:var(--dim); margin:18px 0 10px }
|
|
121
|
-
.editor-view { display:flex; flex-direction:column;
|
|
102
|
+
.editor-view { display:flex; flex-direction:column; padding-right:0 }
|
|
122
103
|
.editor-head { flex:none }
|
|
123
104
|
.editorbar { display:grid; grid-template-columns:minmax(0,1fr) auto minmax(0,1fr); align-items:center; gap:14px; min-height:42px; margin-bottom:10px }
|
|
124
105
|
.editor-identity, .editor-actions { display:flex; align-items:center; gap:8px; min-width:0 }
|
|
@@ -127,15 +108,12 @@
|
|
|
127
108
|
.editor-document { min-width:0 }
|
|
128
109
|
.editor-title { max-width:320px; color:var(--fg); font-size:13px; font-weight:600; overflow:hidden; text-overflow:ellipsis; white-space:nowrap }
|
|
129
110
|
.editor-actions { justify-self:end }
|
|
130
|
-
.viewmode { display:inline-flex; gap:
|
|
131
|
-
.
|
|
132
|
-
.
|
|
133
|
-
.
|
|
134
|
-
.split { display:flex; flex:1; min-height:0; gap:12px; align-items:stretch }
|
|
135
|
-
.split textarea.editor { flex:1; height:100%; min-height:0; resize:none; overflow-y:auto }
|
|
136
|
-
.split.preview { display:block; overflow-y:auto }
|
|
111
|
+
.viewmode { display:inline-flex; gap:24px }
|
|
112
|
+
.split { display:flex; flex:none; min-height:0; gap:12px; align-items:stretch }
|
|
113
|
+
.split textarea.editor { flex:1; height:auto; min-height:420px; resize:none; overflow-y:hidden; field-sizing:content }
|
|
114
|
+
.split.preview { display:block }
|
|
137
115
|
.split.preview .md { width:100%; min-height:100%; overflow:visible }
|
|
138
|
-
.md { flex:1;
|
|
116
|
+
.md { flex:1; padding:24px 28px; border:1px solid var(--border); border-radius:7px; background:#242424; font-family:-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif }
|
|
139
117
|
.md h1, .md h2, .md h3, .md h4 { margin:18px 0 10px; line-height:1.3; color:var(--fg) }
|
|
140
118
|
.md h1 { font-size:26px } .md h2 { font-size:20px } .md h3 { font-size:16px } .md h4 { font-size:14px }
|
|
141
119
|
.md h2 { text-transform:none; letter-spacing:normal }
|
|
@@ -166,15 +144,11 @@
|
|
|
166
144
|
.header-lock { min-width:44px; height:44px; justify-content:center }
|
|
167
145
|
}
|
|
168
146
|
@media (max-width:700px) {
|
|
169
|
-
header { gap:10px
|
|
147
|
+
header { gap:10px }
|
|
170
148
|
.brand { gap:8px }
|
|
171
149
|
.brand-mark { width:34px; height:34px }
|
|
172
150
|
.brand-logo { width:24px; height:24px }
|
|
173
151
|
.brand-subtitle { display:none }
|
|
174
|
-
.header-context { flex:1 }
|
|
175
|
-
.header-divider { height:20px }
|
|
176
|
-
#headerProject { max-width:none }
|
|
177
|
-
header .spacer { display:none }
|
|
178
152
|
.header-lock { justify-content:center; padding:0 }
|
|
179
153
|
.header-lock-label { display:none }
|
|
180
154
|
main { flex-direction:column }
|
|
@@ -185,13 +159,10 @@
|
|
|
185
159
|
.workspace-head { align-items:flex-start; flex-wrap:wrap; gap:8px; margin-bottom:12px; padding-bottom:10px }
|
|
186
160
|
button, input, select { min-height:44px; touch-action:manipulation }
|
|
187
161
|
.header-lock, .back-button { width:44px; height:44px }
|
|
188
|
-
.sidebar-toggle { display:none }
|
|
189
162
|
.proj, .tab { min-height:44px; display:flex; align-items:center }
|
|
190
163
|
.tab { padding:5px 8px }
|
|
191
164
|
.delete-project { padding:5px 9px }
|
|
192
|
-
.graph-view { overflow-y:auto }
|
|
193
165
|
.graph-workspace { min-height:300px }
|
|
194
|
-
.editor-view { overflow-y:auto }
|
|
195
166
|
.editorbar { grid-template-columns:minmax(0,1fr) auto; gap:8px }
|
|
196
167
|
.editor-title { max-width:180px }
|
|
197
168
|
.viewmode { grid-column:1 / -1; grid-row:2; justify-self:center }
|
|
@@ -200,6 +171,202 @@
|
|
|
200
171
|
.md { padding:20px }
|
|
201
172
|
.split.preview .md { min-height:calc(100vh - 240px) }
|
|
202
173
|
}
|
|
174
|
+
/* Workspace polish: retain the charcoal and amber identity. */
|
|
175
|
+
:root { color-scheme:dark; --border:rgba(255,255,255,.08); --dim:#a3a3a3 }
|
|
176
|
+
button, input, textarea, select { border-radius:8px }
|
|
177
|
+
button { transition:background-color 140ms, border-color 140ms }
|
|
178
|
+
header { box-shadow:none; border-bottom:1px solid var(--border) }
|
|
179
|
+
.brand { min-width:214px; gap:11px }
|
|
180
|
+
.brand-mark { width:38px; height:38px; background:none; border:0; box-shadow:none }
|
|
181
|
+
.brand-logo { width:38px; height:38px }
|
|
182
|
+
header h1 { font-size:20px; letter-spacing:-.04em }
|
|
183
|
+
.brand-subtitle { font-size:10px; margin-top:4px; color:var(--dim) }
|
|
184
|
+
aside { width:252px; padding:18px 14px; box-shadow:none; border-right:1px solid var(--border); gap:0 }
|
|
185
|
+
#projectMenu { display:flex; flex-direction:column; min-height:0 }
|
|
186
|
+
.sidebar-item { position:relative; font-size:11px; text-transform:uppercase; letter-spacing:.09em; font-weight:600; padding:9px 12px; min-height:40px; flex:none; border:0; border-radius:6px }
|
|
187
|
+
.sidebar-item:hover { background:var(--hover); color:var(--fg) }
|
|
188
|
+
.sidebar-item.active { background:#ffffff08; color:var(--fg) }
|
|
189
|
+
.sidebar-item.active::after { content:""; position:absolute; left:0; top:0; bottom:0; width:2px; border-radius:2px; background:var(--accent) }
|
|
190
|
+
.project-search { width:100%; margin:10px 0 14px; padding:9px 11px; font-size:12px; background:#ffffff04; border-color:transparent }
|
|
191
|
+
.project-search:focus { border-color:transparent; background:#ffffff08; box-shadow:none }
|
|
192
|
+
#projList { margin:0; padding:0 4px 0 0; border:0; gap:3px; min-height:0 }
|
|
193
|
+
.project-search { flex:none }
|
|
194
|
+
.project-empty { padding:12px 8px; color:var(--dim); font-size:12px }
|
|
195
|
+
.proj { position:relative; display:flex; align-items:center; gap:10px; padding:9px 12px; min-height:40px; flex:none; font-size:13px; border:0; border-radius:6px }
|
|
196
|
+
.proj::before { content:attr(data-initial); display:grid; place-items:center; width:24px; height:24px; flex:none; color:#777; font:10px ui-monospace,monospace }
|
|
197
|
+
.project-name { overflow:hidden; white-space:nowrap; text-overflow:ellipsis }
|
|
198
|
+
.proj.active { background:#ffffff08; color:var(--fg) }
|
|
199
|
+
.proj.active::before { color:var(--fg) }
|
|
200
|
+
.proj.active::after { content:""; position:absolute; left:0; top:0; bottom:0; width:2px; border-radius:2px; background:var(--accent) }
|
|
201
|
+
.mobile-project-picker { display:none }
|
|
202
|
+
#content { padding:30px 38px 24px }
|
|
203
|
+
.workspace-intro { flex:none; margin-bottom:24px }
|
|
204
|
+
.workspace-intro .eyebrow { color:var(--dim); font-size:10px; text-transform:uppercase; letter-spacing:.14em }
|
|
205
|
+
.workspace-intro h2 { margin:5px 0 4px; font-size:28px; line-height:1.3; font-weight:600; color:var(--fg); text-transform:none; letter-spacing:-.035em; overflow-wrap:anywhere }
|
|
206
|
+
.workspace-intro p { font-size:12px; color:var(--dim) }
|
|
207
|
+
.workspace-head { padding-bottom:0; margin-bottom:24px; min-height:44px }
|
|
208
|
+
.tabs { gap:24px; align-self:stretch }
|
|
209
|
+
.tab, .viewmode button { padding:10px 1px 13px; border:0; border-bottom:2px solid transparent; border-radius:0; font-size:13px; color:var(--dim); background:transparent }
|
|
210
|
+
button.tab.active, .viewmode button.on { background:transparent; border-color:var(--accent); color:var(--accent) }
|
|
211
|
+
button.tab:hover, .viewmode button:hover { background:transparent; color:var(--fg) }
|
|
212
|
+
button.danger { background:transparent; font-size:12px }
|
|
213
|
+
button.danger:hover { background:#ff6b6b0c; border-color:#ff6b6b40 }
|
|
214
|
+
.delete-project { border-color:transparent }
|
|
215
|
+
.delete-project svg { display:none; width:16px; height:16px }
|
|
216
|
+
.project-actions { padding-bottom:8px }
|
|
217
|
+
.collection-toolbar { display:flex; align-items:center; justify-content:space-between; gap:16px; margin-bottom:20px; background:var(--bg); padding-bottom:8px }
|
|
218
|
+
.collection-toolbar h3 { font-size:14px; font-weight:600; display:flex; align-items:center; gap:9px }
|
|
219
|
+
.collection-toolbar input { width:230px; font-size:12px; padding:8px 11px; background:var(--bg2) }
|
|
220
|
+
.collection-count { font-size:11px; color:var(--dim); background:var(--bg3); padding:1px 7px; border-radius:5px; font-variant-numeric:tabular-nums }
|
|
221
|
+
.collection-grid { display:grid; grid-template-columns:repeat(2,minmax(0,1fr)); grid-auto-rows:max-content; gap:14px; align-content:start }
|
|
222
|
+
.collection-grid .collection-toolbar, .collection-grid .empty { grid-column:1 / -1 }
|
|
223
|
+
.collection-grid .collection-toolbar { margin-bottom:0 }
|
|
224
|
+
.docrow { display:flex; flex-direction:column; align-items:stretch; justify-content:flex-start; width:100%; text-align:left; padding:22px; margin-bottom:0; border-radius:10px; background:#242424; position:relative }
|
|
225
|
+
.docrow:hover { background:#282623 }
|
|
226
|
+
.doc-head { align-items:center; margin-bottom:10px }
|
|
227
|
+
.docrow .t { font-size:15px; letter-spacing:-.015em; overflow-wrap:anywhere }
|
|
228
|
+
.docrow .p { flex:none; font-size:11px }
|
|
229
|
+
.note-path { display:block; margin-bottom:18px; color:var(--dim); font:11px ui-monospace,monospace; overflow-wrap:anywhere }
|
|
230
|
+
.docrow .note-path + div { height:90px; overflow:hidden; flex:none }
|
|
231
|
+
.sec-chip { background:#ffffff03; border:1px solid var(--border); font-size:11px; padding:2px 7px; margin:3px 4px 0 0; max-width:100%; overflow-wrap:anywhere }
|
|
232
|
+
.skill-summary { line-height:1.8; font-size:13px }
|
|
233
|
+
.rem { padding:20px 22px; border-radius:10px; gap:24px; margin-bottom:10px }
|
|
234
|
+
.rem .txt { min-width:0; overflow-wrap:anywhere }
|
|
235
|
+
.txt-main { line-height:1.8; margin-bottom:8px }
|
|
236
|
+
.rem .meta { margin-top:8px; font-size:10px }
|
|
237
|
+
.rem > button { border-color:transparent; color:var(--dim); width:32px; height:32px; padding:7px; flex:none; display:grid; place-items:center }
|
|
238
|
+
.rem > button:hover, .rem > button:focus-visible { color:var(--danger) }
|
|
239
|
+
.rem > button svg { width:16px; height:16px }
|
|
240
|
+
.empty { padding:40px 24px; border-radius:10px }
|
|
241
|
+
.editorbar { margin-bottom:24px; border-bottom:1px solid var(--border); padding-bottom:0; align-items:stretch }
|
|
242
|
+
.editor-identity, .editor-actions { padding-bottom:8px }
|
|
243
|
+
.viewmode { align-self:stretch }
|
|
244
|
+
.editor-actions button { min-height:34px; padding:5px 10px; font-size:12px; line-height:22px; font-weight:400; background:transparent; border-color:transparent }
|
|
245
|
+
.editor-actions .primary { color:var(--accent) }
|
|
246
|
+
.editor-actions .primary:hover { background:var(--hover); border-color:transparent }
|
|
247
|
+
.editor-actions .danger:hover { border-color:transparent }
|
|
248
|
+
.back-button { background:transparent; border-color:transparent; color:var(--dim) }
|
|
249
|
+
.back-button:hover { background:var(--hover); border-color:transparent; color:var(--fg) }
|
|
250
|
+
.editor-title { display:block; max-width:100% }
|
|
251
|
+
.md { padding:32px; line-height:1.85; border-radius:10px }
|
|
252
|
+
.split > .md, .split > textarea { min-width:0 }
|
|
253
|
+
.md p, .md li { overflow-wrap:anywhere }
|
|
254
|
+
.split.preview .md { padding:32px }
|
|
255
|
+
.md h1 { letter-spacing:-.035em; font-size:30px }
|
|
256
|
+
.md h2 { margin-top:30px }
|
|
257
|
+
.md blockquote { padding:8px 16px; background:#e9973f05 }
|
|
258
|
+
textarea.editor { padding:22px; line-height:1.8 }
|
|
259
|
+
textarea.editor:focus { border-color:var(--border); box-shadow:none; outline:none }
|
|
260
|
+
.graph-workspace { border-radius:10px }
|
|
261
|
+
.proj[hidden], .docrow[hidden], .rem[hidden], .empty[hidden] { display:none }
|
|
262
|
+
@media (min-width:1600px) { #content { padding-left:max(38px,calc((100vw - 1600px)/2)); padding-right:max(38px,calc((100vw - 1600px)/2)) } }
|
|
263
|
+
@media (max-width:1000px) { #content { padding:24px } aside { width:220px } .brand { min-width:182px } .collection-grid { grid-template-columns:1fr } }
|
|
264
|
+
@media (max-width:700px) {
|
|
265
|
+
header { gap:12px }
|
|
266
|
+
.brand { min-width:0 } header h1 { font-size:18px }
|
|
267
|
+
.brand-mark, .brand-logo { width:32px; height:32px }
|
|
268
|
+
aside { width:100%; padding:4px 16px; max-height:none; gap:0 }
|
|
269
|
+
#projectMenu { display:none }
|
|
270
|
+
.mobile-project-picker { display:flex; align-items:center; gap:16px; min-width:0 }
|
|
271
|
+
.mobile-project-picker span { font-size:10px; text-transform:uppercase; letter-spacing:.1em; color:var(--dim); flex:none }
|
|
272
|
+
.mobile-project-picker select { flex:1; min-width:0; width:0; background:var(--bg2); color:var(--fg); border:0; padding:8px; font:inherit; font-size:13px; min-height:44px }
|
|
273
|
+
.project-search { margin:4px 0 8px; padding:6px 10px }
|
|
274
|
+
#projList { margin:0; gap:5px; overflow-y:hidden; padding:0 }
|
|
275
|
+
#content { padding:20px 16px 16px }
|
|
276
|
+
.workspace-intro { margin-bottom:16px }
|
|
277
|
+
.workspace-intro h2 { font-size:24px }
|
|
278
|
+
.workspace-intro p { display:none }
|
|
279
|
+
.workspace-head { gap:6px; margin-bottom:18px }
|
|
280
|
+
.tabs, .viewmode { gap:18px } .tab, .viewmode button { padding:10px 0 }
|
|
281
|
+
.project-actions { padding-bottom:0 }
|
|
282
|
+
.delete-project { width:44px; height:44px; display:grid; place-items:center }
|
|
283
|
+
.delete-project svg { display:block }
|
|
284
|
+
.delete-project span { display:none }
|
|
285
|
+
.collection-toolbar { gap:12px }
|
|
286
|
+
.collection-toolbar h3 { flex:none }
|
|
287
|
+
.collection-toolbar input { width:0; flex:1; min-width:0 }
|
|
288
|
+
.docrow, .rem { padding:18px }
|
|
289
|
+
.rem { gap:10px }
|
|
290
|
+
.rem > button { width:44px; height:44px }
|
|
291
|
+
.doc-head { flex-wrap:wrap; gap:6px }
|
|
292
|
+
.split.preview .md, .md { padding:24px 20px }
|
|
293
|
+
.editor-title { max-width:140px }
|
|
294
|
+
.editor-actions { gap:5px }
|
|
295
|
+
.editor-actions button { min-height:44px; padding:7px 10px }
|
|
296
|
+
}
|
|
297
|
+
.home-link { margin-bottom:12px }
|
|
298
|
+
body:has(#app.home-active), body:has(#app.graph-active) { height:100dvh; min-height:0; overflow:hidden }
|
|
299
|
+
#app.home-active, #app.graph-active { min-height:0 }
|
|
300
|
+
#app.home-active > aside, #app.graph-active > aside { overflow-y:auto; overflow-x:hidden; flex-shrink:0 }
|
|
301
|
+
#app.graph-active #content { overflow:auto; padding-bottom:0 }
|
|
302
|
+
.project-graph-view { gap:0; min-height:350px }
|
|
303
|
+
.project-graph-view .graph-workspace { min-height:260px; border:0; border-radius:0 }
|
|
304
|
+
.project-graph-view .brain-footer { padding:18px 0 }
|
|
305
|
+
.project-graph-view .graph-legend { top:auto; bottom:24px; left:0; font-size:10px }
|
|
306
|
+
:root { --bg:#17191b; --bg2:#151719; --bg3:#252a2d; --hover:#2b3236 }
|
|
307
|
+
#content { background:radial-gradient(ellipse at 55% 47%,#162329 0%,#151a1d 37%,#17191b 70%); }
|
|
308
|
+
#content.home-content { padding:0; overflow:auto }
|
|
309
|
+
.collection-toolbar { background:transparent }
|
|
310
|
+
.docrow, .rem, .md { background:#ffffff03 }
|
|
311
|
+
.docrow:hover { background:#ffffff06 }
|
|
312
|
+
.graph-workspace { background:transparent; border-color:#ffffff0a }
|
|
313
|
+
.graph-legend { color:#889498 }
|
|
314
|
+
.graph-legend i { background:#efffff }
|
|
315
|
+
.graph-legend .memory-dot { background:#acf0ff }
|
|
316
|
+
.graph-controls button { background:#ffffff04; color:#b9c2c5; border-color:#ffffff0b }
|
|
317
|
+
.graph-controls button:hover { background:#ffffff09 }
|
|
318
|
+
.home-loading { display:grid; place-items:center; color:var(--dim); font-size:12px; letter-spacing:.02em }
|
|
319
|
+
.brain-view { gap:0; min-height:480px }
|
|
320
|
+
.brain-heading { display:flex; align-items:center; justify-content:space-between; gap:32px; padding:32px 40px 8px; flex:none }
|
|
321
|
+
.brain-eyebrow { display:block; color:#8b9295; font-size:10px; letter-spacing:.16em; text-transform:uppercase; margin-bottom:8px }
|
|
322
|
+
.brain-title h2 { color:#e9e9e5; font-size:30px; font-weight:500; line-height:1.2; letter-spacing:-.045em; text-transform:none; margin:0 }
|
|
323
|
+
.brain-title p { color:#8b9295; font-size:12px; margin-top:10px }
|
|
324
|
+
.brain-stats { display:flex; gap:30px; flex:none }
|
|
325
|
+
.brain-stats > div { display:flex; flex-direction:column-reverse; gap:4px }
|
|
326
|
+
.brain-stats dt { color:#8b9295; font-size:11px }
|
|
327
|
+
.brain-stats dd { color:#d1d9dc; font-size:22px; font-weight:400; letter-spacing:-.035em; font-variant-numeric:tabular-nums }
|
|
328
|
+
.brain-view .graph-workspace { min-height:280px; border:0; border-radius:0; background:transparent }
|
|
329
|
+
.brain-view .graph-legend { top:auto; bottom:24px; left:40px; color:#889498; font-size:10px; gap:14px }
|
|
330
|
+
.brain-view .graph-legend i { width:5px; height:5px; background:#efffff }
|
|
331
|
+
.brain-view .graph-legend .memory-dot { width:4px; height:4px; background:#91dfff }
|
|
332
|
+
.brain-interaction-hint { position:absolute; bottom:22px; left:50%; transform:translateX(-50%); color:#778387; font-size:10px; white-space:nowrap; pointer-events:none }
|
|
333
|
+
.brain-footer { flex:none; display:flex; align-items:center; justify-content:space-between; gap:20px; padding:18px 40px; border-top:1px solid #ffffff07 }
|
|
334
|
+
.brain-provenance { display:flex; align-items:center; gap:8px; color:#889498; font-size:11px }
|
|
335
|
+
.brain-status-dot { width:5px; height:5px; border-radius:50%; background:#85ada3; flex:none }
|
|
336
|
+
.brain-actions { display:flex; align-items:center; gap:16px; min-width:0 }
|
|
337
|
+
.brain-project-picker { max-width:220px; min-width:0; background:#ffffff04; color:#b9c2c5; border:1px solid #ffffff0b; padding:8px 28px 8px 12px; font:inherit; font-size:11px; cursor:pointer }
|
|
338
|
+
.brain-project-picker option { background:#1c2124; color:#dcddde }
|
|
339
|
+
.graph-view .graph-controls { position:static; gap:0; border:1px solid #ffffff0b; border-radius:8px; overflow:hidden; flex:none }
|
|
340
|
+
.graph-view .graph-controls button { min-width:34px; min-height:34px; background:transparent; color:#b9c2c5; border:0; border-radius:0 }
|
|
341
|
+
.graph-view .graph-controls button + button { border-left:1px solid #ffffff0b }
|
|
342
|
+
.graph-view .graph-controls button:hover, .brain-project-picker:hover { background:#ffffff09 }
|
|
343
|
+
.brain-view > .empty { margin:40px; flex:1; display:grid; place-items:center; color:var(--dim); font-size:13px }
|
|
344
|
+
@media (max-width:1000px) {
|
|
345
|
+
.brain-heading { padding:28px 24px 8px; gap:20px }
|
|
346
|
+
.brain-stats { gap:18px }
|
|
347
|
+
.brain-footer { padding:16px 24px }
|
|
348
|
+
.brain-gesture { display:none }
|
|
349
|
+
.brain-view .graph-legend { left:24px }
|
|
350
|
+
.brain-interaction-hint { left:auto; right:24px; transform:none }
|
|
351
|
+
}
|
|
352
|
+
@media (max-width:700px) {
|
|
353
|
+
.brain-view { min-height:510px }
|
|
354
|
+
.brain-heading { flex-wrap:wrap; gap:22px; padding:24px 24px 4px }
|
|
355
|
+
.brain-title h2 { font-size:28px }
|
|
356
|
+
.brain-title p { display:none }
|
|
357
|
+
.brain-stats { width:100%; justify-content:space-between; padding-right:24px }
|
|
358
|
+
.brain-stats dd { font-size:20px }
|
|
359
|
+
.brain-stats dt { font-size:10px }
|
|
360
|
+
.brain-view .graph-workspace { min-height:260px }
|
|
361
|
+
.brain-view .graph-legend { bottom:12px }
|
|
362
|
+
.brain-interaction-hint { display:none }
|
|
363
|
+
.brain-footer { flex-wrap:wrap; gap:12px; padding:14px 24px 20px }
|
|
364
|
+
.brain-provenance { font-size:10px }
|
|
365
|
+
.brain-actions { width:100%; justify-content:space-between }
|
|
366
|
+
.brain-project-picker { flex:1; max-width:none; min-height:44px }
|
|
367
|
+
.graph-view .graph-controls button { min-width:44px; min-height:44px }
|
|
368
|
+
}
|
|
369
|
+
@media (prefers-reduced-motion:reduce) { *, *::before, *::after { transition:none !important } }
|
|
203
370
|
</style>
|
|
204
371
|
<script src="https://cdn.jsdelivr.net/npm/marked@12.0.2/marked.min.js"></script>
|
|
205
372
|
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.1.6/dist/purify.min.js"></script>
|
|
@@ -208,20 +375,13 @@
|
|
|
208
375
|
<header>
|
|
209
376
|
<div class="brand">
|
|
210
377
|
<span class="brand-mark" aria-hidden="true">
|
|
211
|
-
<
|
|
378
|
+
<svg class="brand-logo" viewBox="0 0 40 40" fill="none"><path d="M20 18v17m0-10-8 6m8-6 8 6" stroke="#e9973f" stroke-width="2.8" stroke-linecap="round" stroke-linejoin="round"/><path d="M20 21C10 21 5 15 6 7c8-1 14 4 14 14Z" fill="#e9973f"/><path d="M22 17C22 9 27 4 35 5c0 8-5 13-13 12Z" fill="#f2bd7e"/></svg>
|
|
212
379
|
</span>
|
|
213
380
|
<div class="brand-copy">
|
|
214
381
|
<h1>mindroot</h1>
|
|
215
|
-
<span class="brand-subtitle">
|
|
382
|
+
<span class="brand-subtitle">Your second brain</span>
|
|
216
383
|
</div>
|
|
217
384
|
</div>
|
|
218
|
-
<div class="header-context" hidden>
|
|
219
|
-
<span class="header-divider" aria-hidden="true"></span>
|
|
220
|
-
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
|
221
|
-
<path d="M4 7.5h6l2 2h8v9H4z" stroke-width="1.7" stroke-linejoin="round"/>
|
|
222
|
-
</svg>
|
|
223
|
-
<span id="headerProject" aria-live="polite"></span>
|
|
224
|
-
</div>
|
|
225
385
|
<div class="spacer"></div>
|
|
226
386
|
<button class="header-lock" type="button" onclick="logout()" title="Lock dashboard" aria-label="Lock dashboard">
|
|
227
387
|
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
|
@@ -242,18 +402,20 @@
|
|
|
242
402
|
|
|
243
403
|
<main id="app" style="display:none">
|
|
244
404
|
<aside>
|
|
245
|
-
<
|
|
246
|
-
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true">
|
|
247
|
-
<path d="m14 7-5 5 5 5" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
248
|
-
</svg>
|
|
249
|
-
</button>
|
|
405
|
+
<label class="mobile-project-picker"><span>Project</span><select id="mobileProject" aria-label="Choose project" onchange="selectProject(this.value)"></select></label>
|
|
250
406
|
<div id="projectMenu">
|
|
251
|
-
<button class="sidebar-item" id="
|
|
407
|
+
<button class="sidebar-item home-link" id="homeLink" type="button" title="The Atlas" aria-label="The Atlas" onclick="navTo('#/')">
|
|
408
|
+
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M12 10.8 V21 M12 15 l-4.8 3.6 M12 15 l4.8 3.6" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/><path d="M12 12.6 C6 12.6 3 9 3.6 4.2 c4.8 -.6 8.4 2.4 8.4 8.4 Z" stroke-width="1.6" stroke-linejoin="round"/><path d="M13.2 10.2 C13.2 5.4 16.2 2.4 21 3 c0 4.8 -3 7.8 -7.8 7.2 Z" stroke-width="1.6" stroke-linejoin="round"/></svg>
|
|
409
|
+
<span class="sidebar-label">The Atlas</span>
|
|
410
|
+
</button>
|
|
411
|
+
<button class="sidebar-item" id="projectMenuToggle" type="button" title="Projects" aria-label="Projects" aria-expanded="true" aria-controls="projList" onclick="toggleProjectMenu()">
|
|
252
412
|
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="M3 7h7l2 2h9v11H3zM3 7V4h7l2 3" stroke-width="1.7" stroke-linejoin="round"/></svg>
|
|
253
|
-
<span class="sidebar-label">
|
|
413
|
+
<span class="sidebar-label">Projects</span>
|
|
254
414
|
<svg class="menu-chevron" viewBox="0 0 24 24" fill="none" aria-hidden="true"><path d="m9 6 6 6-6 6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
|
255
415
|
</button>
|
|
416
|
+
<input class="project-search" type="search" placeholder="Find a project…" aria-label="Find a project" oninput="filterProjects()">
|
|
256
417
|
<div id="projList"></div>
|
|
418
|
+
<p class="project-empty" role="status" hidden>No projects match.</p>
|
|
257
419
|
</div>
|
|
258
420
|
</aside>
|
|
259
421
|
<section id="content"></section>
|
|
@@ -283,6 +445,8 @@ async function api(method, url, body) {
|
|
|
283
445
|
function showGate(msg) {
|
|
284
446
|
disposeGraph?.();
|
|
285
447
|
disposeGraph = null;
|
|
448
|
+
$("#content").replaceChildren();
|
|
449
|
+
$("#app").classList.remove("home-active", "graph-active");
|
|
286
450
|
$("#app").style.display = "none";
|
|
287
451
|
$("#gate").style.display = "flex";
|
|
288
452
|
if (msg) $("#gateErr").textContent = msg;
|
|
@@ -304,9 +468,8 @@ async function boot() {
|
|
|
304
468
|
$("#app").style.display = "flex";
|
|
305
469
|
const projects = await renderProjects();
|
|
306
470
|
const r = routeFromHash();
|
|
307
|
-
if (projects.some((p) => p.slug === r.slug)) applyRoute(r);
|
|
308
|
-
else
|
|
309
|
-
else applyRoute(r);
|
|
471
|
+
if (!r.slug || projects.some((p) => p.slug === r.slug)) applyRoute(r);
|
|
472
|
+
else navTo("#/");
|
|
310
473
|
} catch (e) {
|
|
311
474
|
if (e.message !== "unauthorized") showGate(String(e));
|
|
312
475
|
}
|
|
@@ -336,8 +499,12 @@ function applyRoute({ slug = null, tab = "notes", doc = null } = {}) {
|
|
|
336
499
|
disposeGraph = null;
|
|
337
500
|
activeHash = location.hash || "#/";
|
|
338
501
|
currentProject = slug;
|
|
502
|
+
$("#mobileProject").value = slug || "";
|
|
339
503
|
currentTab = tab;
|
|
340
504
|
currentDoc = doc;
|
|
505
|
+
$("#homeLink").classList.toggle("active", !slug);
|
|
506
|
+
if (!slug) $("#homeLink").setAttribute("aria-current", "page");
|
|
507
|
+
else $("#homeLink").removeAttribute("aria-current");
|
|
341
508
|
document.querySelectorAll(".proj").forEach((n) => {
|
|
342
509
|
n.classList.toggle("active", n.dataset.slug === slug);
|
|
343
510
|
if (n.dataset.slug === slug) n.setAttribute("aria-current", "page");
|
|
@@ -345,15 +512,9 @@ function applyRoute({ slug = null, tab = "notes", doc = null } = {}) {
|
|
|
345
512
|
});
|
|
346
513
|
if (slug) setProjectMenu(true);
|
|
347
514
|
if (!slug) {
|
|
348
|
-
|
|
349
|
-
c.innerHTML = '<div class="view"><div class="empty">No projects yet. Agents create them automatically on first write.</div></div>';
|
|
515
|
+
renderHome();
|
|
350
516
|
return;
|
|
351
517
|
}
|
|
352
|
-
$(".header-context").hidden = false;
|
|
353
|
-
const projectLabel = $("#headerProject");
|
|
354
|
-
projectLabel.textContent = slug;
|
|
355
|
-
projectLabel.title = projectLabel.textContent;
|
|
356
|
-
projectLabel.setAttribute("aria-label", "Current project: " + slug);
|
|
357
518
|
renderTabs();
|
|
358
519
|
}
|
|
359
520
|
|
|
@@ -361,6 +522,9 @@ window.addEventListener("hashchange", () => applyRoute(routeFromHash()));
|
|
|
361
522
|
|
|
362
523
|
async function renderProjects() {
|
|
363
524
|
const rows = await api("GET", "/api/projects");
|
|
525
|
+
const picker = $("#mobileProject");
|
|
526
|
+
picker.replaceChildren(new Option("Home · All projects", ""), ...rows.map((r) => new Option(r.slug, r.slug)));
|
|
527
|
+
picker.value = currentProject || "";
|
|
364
528
|
const el = $("#projList");
|
|
365
529
|
el.innerHTML = "";
|
|
366
530
|
for (const r of rows) {
|
|
@@ -368,26 +532,87 @@ async function renderProjects() {
|
|
|
368
532
|
div.type = "button";
|
|
369
533
|
div.className = "proj" + (r.slug === currentProject ? " active" : "");
|
|
370
534
|
div.dataset.slug = r.slug;
|
|
535
|
+
div.setAttribute("aria-label", r.slug);
|
|
371
536
|
div.dataset.initial = r.slug.slice(0, 2).toUpperCase();
|
|
372
|
-
|
|
373
|
-
|
|
537
|
+
const name = document.createElement("span");
|
|
538
|
+
name.className = "project-name";
|
|
539
|
+
name.textContent = r.slug;
|
|
540
|
+
div.appendChild(name);
|
|
541
|
+
div.title = r.slug + " · " + r.docs + " notes, " + r.memories + " memories, " + (r.skills || 0) + " skills";
|
|
374
542
|
div.onclick = () => selectProject(r.slug);
|
|
375
543
|
el.appendChild(div);
|
|
376
544
|
}
|
|
377
545
|
if (!rows.length) el.innerHTML = '<p class="muted">No projects yet</p>';
|
|
546
|
+
filterProjects();
|
|
378
547
|
return rows;
|
|
379
548
|
}
|
|
380
549
|
|
|
550
|
+
function filterProjects() {
|
|
551
|
+
const query = $(".project-search").value.trim().toLowerCase();
|
|
552
|
+
const rows = [...document.querySelectorAll(".proj")];
|
|
553
|
+
rows.forEach((row) => { row.hidden = !row.dataset.slug.toLowerCase().includes(query); });
|
|
554
|
+
$(".project-empty").hidden = !query || rows.some((row) => !row.hidden);
|
|
555
|
+
}
|
|
556
|
+
|
|
381
557
|
function selectProject(slug) {
|
|
382
|
-
navTo(routeUrl(slug, "notes"));
|
|
558
|
+
navTo(slug ? routeUrl(slug, "notes") : "#/");
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
async function renderHome() {
|
|
562
|
+
const c = $("#content");
|
|
563
|
+
c.classList.add("home-content");
|
|
564
|
+
$("#app").classList.remove("graph-active");
|
|
565
|
+
$("#app").classList.add("home-active");
|
|
566
|
+
c.replaceChildren();
|
|
567
|
+
const view = document.createElement("div");
|
|
568
|
+
view.className = "view home-loading";
|
|
569
|
+
view.innerHTML = '<div role="status">Gathering your memories…</div>';
|
|
570
|
+
c.append(view);
|
|
571
|
+
try {
|
|
572
|
+
const projects = await api("GET", "/api/projects");
|
|
573
|
+
if (!view.isConnected) return;
|
|
574
|
+
const entries = new Array(projects.length);
|
|
575
|
+
let next = 0;
|
|
576
|
+
// Bound concurrency so a large workspace does not flood the server.
|
|
577
|
+
await Promise.all(Array.from({ length: Math.min(4, projects.length) }, async () => {
|
|
578
|
+
while (next < projects.length && view.isConnected) {
|
|
579
|
+
const index = next++;
|
|
580
|
+
const { slug } = projects[index];
|
|
581
|
+
entries[index] = { slug, data: await api("GET", "/api/projects/" + encodeURIComponent(slug)) };
|
|
582
|
+
}
|
|
583
|
+
}));
|
|
584
|
+
if (!view.isConnected || $("#app").style.display === "none") return;
|
|
585
|
+
const { mountGraph } = await import("/graph.js");
|
|
586
|
+
if (!view.isConnected) return;
|
|
587
|
+
const dispose = await mountGraph(view, entries, { brain: true, openProject: (slug) => navTo(routeUrl(slug, "graph")) });
|
|
588
|
+
if (!view.isConnected) dispose?.();
|
|
589
|
+
else disposeGraph = dispose;
|
|
590
|
+
} catch (e) {
|
|
591
|
+
if (!view.isConnected) return;
|
|
592
|
+
view.innerHTML = '<div class="empty err"></div>';
|
|
593
|
+
view.firstChild.textContent = "Unable to load the brain: " + e.message;
|
|
594
|
+
const retry = document.createElement("button");
|
|
595
|
+
retry.textContent = "Try again";
|
|
596
|
+
retry.onclick = () => navTo("#/");
|
|
597
|
+
view.append(retry);
|
|
598
|
+
}
|
|
383
599
|
}
|
|
384
600
|
|
|
385
601
|
function renderTabs() {
|
|
602
|
+
window.scrollTo(0, 0);
|
|
386
603
|
disposeGraph?.();
|
|
387
604
|
disposeGraph = null;
|
|
388
605
|
const c = $("#content");
|
|
606
|
+
c.classList.remove("home-content");
|
|
607
|
+
$("#app").classList.remove("home-active");
|
|
608
|
+
$("#app").classList.toggle("graph-active", currentTab === "graph");
|
|
389
609
|
c.innerHTML = "";
|
|
390
610
|
if (!(["notes", "skills"].includes(currentTab) && currentDoc !== null)) {
|
|
611
|
+
const intro = document.createElement("div");
|
|
612
|
+
intro.className = "workspace-intro";
|
|
613
|
+
intro.innerHTML = '<div class="eyebrow">Project workspace</div><h2></h2>';
|
|
614
|
+
intro.querySelector("h2").textContent = currentProject;
|
|
615
|
+
c.appendChild(intro);
|
|
391
616
|
const head = document.createElement("div");
|
|
392
617
|
head.className = "workspace-head";
|
|
393
618
|
const tabs = document.createElement("div");
|
|
@@ -403,7 +628,7 @@ function renderTabs() {
|
|
|
403
628
|
}
|
|
404
629
|
const actions = document.createElement("div");
|
|
405
630
|
actions.className = "project-actions";
|
|
406
|
-
actions.innerHTML = '<button class="danger delete-project">Delete project</button>';
|
|
631
|
+
actions.innerHTML = '<button class="danger delete-project" aria-label="Delete project" title="Delete project"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 7h16M9 7V4h6v3M6 7l1 13h10l1-13M10 11v5M14 11v5"/></svg><span>Delete project</span></button>';
|
|
407
632
|
actions.querySelector("button").onclick = deleteCurrentProject;
|
|
408
633
|
head.append(tabs, actions);
|
|
409
634
|
c.appendChild(head);
|
|
@@ -450,39 +675,52 @@ async function deleteCurrentProject() {
|
|
|
450
675
|
|
|
451
676
|
async function renderNotes(view) {
|
|
452
677
|
const docs = await api("GET", "/api/projects/" + currentProject + "/docs");
|
|
678
|
+
if (!view.isConnected) return;
|
|
453
679
|
if (currentDoc !== null) return renderEditor(view, docs);
|
|
680
|
+
view.classList.add("collection-grid");
|
|
454
681
|
for (const d of docs) {
|
|
455
|
-
const row = document.createElement("
|
|
682
|
+
const row = document.createElement("button");
|
|
683
|
+
row.type = "button";
|
|
456
684
|
row.className = "docrow";
|
|
457
685
|
row.innerHTML = '<div class="doc-head"><div class="t"></div><div class="p">' + d.sections.length + ' sections</div></div>';
|
|
458
686
|
row.querySelector(".t").textContent = d.title + " ";
|
|
687
|
+
const path = document.createElement("span");
|
|
688
|
+
path.className = "note-path";
|
|
689
|
+
path.textContent = d.path;
|
|
690
|
+
row.appendChild(path);
|
|
459
691
|
const chips = d.sections.filter(Boolean).map((s) => '<span class="sec-chip">' + esc(s) + "</span>").join("");
|
|
460
692
|
row.insertAdjacentHTML("beforeend", '<div>' + chips + "</div>");
|
|
461
693
|
row.onclick = () => navTo(routeUrl(currentProject, "notes", d.path));
|
|
462
694
|
view.appendChild(row);
|
|
463
695
|
}
|
|
464
696
|
if (!docs.length) view.innerHTML = '<div class="empty">No notes yet. Agents create them with <b>mindroot_save_note</b>.</div>';
|
|
697
|
+
addCollectionToolbar(view, "Notes", docs.length);
|
|
465
698
|
}
|
|
466
699
|
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
700
|
+
function addCollectionToolbar(view, label, count) {
|
|
701
|
+
const toolbar = document.createElement("div");
|
|
702
|
+
toolbar.className = "collection-toolbar";
|
|
703
|
+
toolbar.innerHTML = '<h3>' + label + '<span class="collection-count">' + count + '</span></h3><input type="search" placeholder="Filter ' + label.toLowerCase() + '…" aria-label="Filter ' + label.toLowerCase() + '">';
|
|
704
|
+
const empty = document.createElement("div");
|
|
705
|
+
empty.className = "empty";
|
|
706
|
+
empty.hidden = true;
|
|
707
|
+
empty.setAttribute("role", "status");
|
|
708
|
+
empty.textContent = "No matches. Try a different search.";
|
|
709
|
+
toolbar.querySelector("input").oninput = (event) => {
|
|
710
|
+
const query = event.target.value.trim().toLowerCase();
|
|
711
|
+
let visible = 0;
|
|
712
|
+
view.querySelectorAll(".docrow, .rem").forEach((row) => {
|
|
713
|
+
row.hidden = !row.textContent.toLowerCase().includes(query);
|
|
714
|
+
if (!row.hidden) visible++;
|
|
715
|
+
});
|
|
716
|
+
toolbar.querySelector(".collection-count").textContent = query ? visible + " / " + count : count;
|
|
717
|
+
empty.hidden = visible > 0 || count === 0;
|
|
718
|
+
};
|
|
719
|
+
view.prepend(toolbar);
|
|
720
|
+
view.appendChild(empty);
|
|
484
721
|
}
|
|
485
|
-
|
|
722
|
+
|
|
723
|
+
let mdMode = localStorage.getItem("mindroot_mdmode") || "preview";
|
|
486
724
|
|
|
487
725
|
let projectMenuExpanded = localStorage.getItem("mindroot_project_menu") !== "collapsed";
|
|
488
726
|
function toggleProjectMenu() { setProjectMenu(!projectMenuExpanded); }
|
|
@@ -491,6 +729,9 @@ function setProjectMenu(expanded) {
|
|
|
491
729
|
localStorage.setItem("mindroot_project_menu", expanded ? "expanded" : "collapsed");
|
|
492
730
|
$("#projectMenuToggle").setAttribute("aria-expanded", String(expanded));
|
|
493
731
|
$("#projList").hidden = !expanded;
|
|
732
|
+
$(".project-search").hidden = !expanded;
|
|
733
|
+
if (!expanded) $(".project-empty").hidden = true;
|
|
734
|
+
else filterProjects();
|
|
494
735
|
}
|
|
495
736
|
setProjectMenu(projectMenuExpanded);
|
|
496
737
|
|
|
@@ -528,6 +769,7 @@ async function renderEditor(view, docs) {
|
|
|
528
769
|
wrap.className = "split " + mdMode;
|
|
529
770
|
const ta = document.createElement("textarea");
|
|
530
771
|
ta.className = "editor";
|
|
772
|
+
ta.setAttribute("aria-label", "Note content");
|
|
531
773
|
ta.value = doc.content;
|
|
532
774
|
ta.spellcheck = false;
|
|
533
775
|
let prev = null;
|
|
@@ -563,6 +805,7 @@ async function renderEditor(view, docs) {
|
|
|
563
805
|
|
|
564
806
|
async function renderMemories(view) {
|
|
565
807
|
const rows = await api("GET", "/api/projects/" + currentProject + "/memories");
|
|
808
|
+
if (!view.isConnected) return;
|
|
566
809
|
for (const r of rows) {
|
|
567
810
|
const div = document.createElement("div");
|
|
568
811
|
div.className = "rem";
|
|
@@ -572,12 +815,16 @@ async function renderMemories(view) {
|
|
|
572
815
|
'<div class="meta">#' + r.id + " · " + r.target_type + " · " + new Date(r.updated_at).toISOString().slice(0,16).replace("T"," ") + "</div></div>";
|
|
573
816
|
div.querySelector(".txt-main").textContent = r.text;
|
|
574
817
|
const del = document.createElement("button");
|
|
575
|
-
del.className = "danger";
|
|
818
|
+
del.className = "danger";
|
|
819
|
+
del.title = "Delete memory";
|
|
820
|
+
del.setAttribute("aria-label", "Delete memory #" + r.id);
|
|
821
|
+
del.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 7h16M9 7V4h6v3M6 7l1 13h10l1-13M10 11v5M14 11v5"/></svg>';
|
|
576
822
|
del.onclick = async () => { await api("DELETE", "/api/projects/" + currentProject + "/memories/" + r.id); renderTabs(); };
|
|
577
823
|
div.appendChild(del);
|
|
578
824
|
view.appendChild(div);
|
|
579
825
|
}
|
|
580
826
|
if (!rows.length) view.insertAdjacentHTML("beforeend", '<div class="empty">No memories yet</div>');
|
|
827
|
+
addCollectionToolbar(view, "Memories", rows.length);
|
|
581
828
|
view.querySelectorAll("a[data-p]").forEach((a) =>
|
|
582
829
|
a.onclick = (e) => { e.preventDefault(); navTo(routeUrl(currentProject, "notes", a.dataset.p)); });
|
|
583
830
|
}
|
|
@@ -587,6 +834,7 @@ async function renderSkills(view) {
|
|
|
587
834
|
if (currentDoc !== null) return renderSkillEditor(view, slug, currentDoc);
|
|
588
835
|
const rows = await api("GET", "/api/projects/" + encodeURIComponent(slug) + "/skills");
|
|
589
836
|
if (!view.isConnected) return;
|
|
837
|
+
view.classList.add("collection-grid");
|
|
590
838
|
for (const skill of rows) {
|
|
591
839
|
const row = document.createElement("button");
|
|
592
840
|
row.className = "docrow skillrow";
|
|
@@ -598,6 +846,7 @@ async function renderSkills(view) {
|
|
|
598
846
|
view.appendChild(row);
|
|
599
847
|
}
|
|
600
848
|
if (!rows.length) view.innerHTML = '<div class="empty">No skills yet. Saved procedures from your agents will appear here.</div>';
|
|
849
|
+
addCollectionToolbar(view, "Skills", rows.length);
|
|
601
850
|
}
|
|
602
851
|
|
|
603
852
|
async function renderSkillEditor(view, slug, name) {
|