@dollhousemcp/mcp-server 2.0.13 → 2.0.15
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/CHANGELOG.md +16 -0
- package/dist/di/Container.d.ts.map +1 -1
- package/dist/di/Container.js +19 -9
- package/dist/elements/BaseElement.js +2 -2
- package/dist/elements/memories/Memory.d.ts.map +1 -1
- package/dist/elements/memories/Memory.js +3 -3
- package/dist/elements/skills/Skill.d.ts.map +1 -1
- package/dist/elements/skills/Skill.js +4 -4
- package/dist/elements/templates/Template.d.ts.map +1 -1
- package/dist/elements/templates/Template.js +4 -4
- package/dist/generated/version.d.ts +2 -2
- package/dist/generated/version.js +3 -3
- package/dist/handlers/ElementCRUDHandler.d.ts +10 -0
- package/dist/handlers/ElementCRUDHandler.d.ts.map +1 -1
- package/dist/handlers/ElementCRUDHandler.js +123 -1
- package/dist/handlers/mcp-aql/MCPAQLHandler.d.ts +1 -0
- package/dist/handlers/mcp-aql/MCPAQLHandler.d.ts.map +1 -1
- package/dist/handlers/mcp-aql/MCPAQLHandler.js +31 -2
- package/dist/services/ActivationStore.d.ts +20 -0
- package/dist/services/ActivationStore.d.ts.map +1 -1
- package/dist/services/ActivationStore.js +104 -1
- package/dist/web/console/IngestRoutes.d.ts +1 -0
- package/dist/web/console/IngestRoutes.d.ts.map +1 -1
- package/dist/web/console/IngestRoutes.js +4 -1
- package/dist/web/console/UnifiedConsole.js +2 -1
- package/dist/web/public/permissions.css +224 -16
- package/dist/web/public/permissions.js +326 -63
- package/dist/web/public/sessions.js +218 -98
- package/dist/web/public/styles.css +15 -10
- package/dist/web/routes/permissionRoutes.d.ts.map +1 -1
- package/dist/web/routes/permissionRoutes.js +57 -19
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +2 -1
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Shows a labeled "N sessions" box in the header. Clicking opens a
|
|
5
5
|
* dropdown with selectable sessions. Selecting a session filters
|
|
6
|
-
* logs and
|
|
6
|
+
* logs and refreshes any session-aware dashboard tabs.
|
|
7
7
|
*
|
|
8
8
|
* @security-audit-suppress DMCP-SEC-004 Client-side JS — all session data is
|
|
9
9
|
* pre-normalized server-side via UnicodeValidator. Browser String.normalize('NFC')
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
var SESSION_FILTER_INJECTION_RETRY_INTERVAL = getConfiguredNumber('sessionFilterInjectionRetryIntervalMs', 500);
|
|
25
25
|
var SESSION_FILTER_INJECTION_MAX_RETRIES = getConfiguredNumber('sessionFilterInjectionMaxRetries', 20);
|
|
26
26
|
var sessions = [];
|
|
27
|
+
var policySessions = [];
|
|
27
28
|
var filterSessionId = '';
|
|
28
29
|
var dropdownBuilt = false;
|
|
29
30
|
var lastSessionKey = ''; // tracks session list identity to avoid unnecessary rebuilds
|
|
@@ -47,6 +48,10 @@
|
|
|
47
48
|
/** NFC-normalize a string safely */
|
|
48
49
|
function nfc(s) { try { return s.normalize('NFC'); } catch(e) { return s; } }
|
|
49
50
|
|
|
51
|
+
function isPolicyOnlySession(session) {
|
|
52
|
+
return !!(session && session.isPolicyOnly);
|
|
53
|
+
}
|
|
54
|
+
|
|
50
55
|
function displayName(session) {
|
|
51
56
|
if (typeof session === 'object' && session.displayName) return nfc(session.displayName);
|
|
52
57
|
var id = typeof session === 'string' ? nfc(session) : nfc((session && session.sessionId) || '');
|
|
@@ -54,9 +59,72 @@
|
|
|
54
59
|
return parts.length >= 2 ? parts[1] : id.slice(0, 8);
|
|
55
60
|
}
|
|
56
61
|
|
|
62
|
+
function getLiveSessions() {
|
|
63
|
+
return sessions.filter(function(s) { return s.status === 'active'; });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getSelectableSessions() {
|
|
67
|
+
var live = getLiveSessions();
|
|
68
|
+
var liveIds = new Set(live.map(function(s) { return s.sessionId; }));
|
|
69
|
+
var merged = live.slice();
|
|
70
|
+
for (var i = 0; i < policySessions.length; i++) {
|
|
71
|
+
if (!liveIds.has(policySessions[i].sessionId)) merged.push(policySessions[i]);
|
|
72
|
+
}
|
|
73
|
+
return merged;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function getActiveTabName() {
|
|
77
|
+
var activeTab = document.querySelector('.console-tab.active');
|
|
78
|
+
return activeTab ? activeTab.dataset.tab || '' : '';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function normalizePolicySessions(list) {
|
|
82
|
+
if (!Array.isArray(list)) return [];
|
|
83
|
+
|
|
84
|
+
var seen = new Set();
|
|
85
|
+
var normalized = [];
|
|
86
|
+
for (var i = 0; i < list.length; i++) {
|
|
87
|
+
var item = list[i];
|
|
88
|
+
if (!item || typeof item.sessionId !== 'string') continue;
|
|
89
|
+
var sessionId = nfc(item.sessionId).trim();
|
|
90
|
+
if (!sessionId || seen.has(sessionId)) continue;
|
|
91
|
+
seen.add(sessionId);
|
|
92
|
+
normalized.push({
|
|
93
|
+
sessionId: sessionId,
|
|
94
|
+
displayName: nfc(typeof item.displayName === 'string' && item.displayName ? item.displayName : sessionId),
|
|
95
|
+
color: '#94a3b8',
|
|
96
|
+
pid: 0,
|
|
97
|
+
startedAt: '',
|
|
98
|
+
lastHeartbeat: '',
|
|
99
|
+
status: 'policy',
|
|
100
|
+
isLeader: false,
|
|
101
|
+
authenticated: false,
|
|
102
|
+
kind: 'policy',
|
|
103
|
+
isPolicyOnly: true,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
normalized.sort(function(a, b) { return a.sessionId.localeCompare(b.sessionId); });
|
|
108
|
+
return normalized;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function setPolicySessions(nextSessions) {
|
|
112
|
+
policySessions = normalizePolicySessions(nextSessions);
|
|
113
|
+
updateSessionIndicator();
|
|
114
|
+
updateSessionFilterOptions();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function allSessionsSummary(liveCount, policyCount) {
|
|
118
|
+
if (policyCount <= 0) return liveCount + ' total';
|
|
119
|
+
if (liveCount <= 0) return policyCount + ' saved';
|
|
120
|
+
return liveCount + ' live, ' + policyCount + ' saved';
|
|
121
|
+
}
|
|
122
|
+
|
|
57
123
|
// Build a key from current sessions to detect changes
|
|
58
124
|
function sessionListKey(list) {
|
|
59
|
-
return list.map(function(s) {
|
|
125
|
+
return list.map(function(s) {
|
|
126
|
+
return s.sessionId + ':' + s.status + ':' + (isPolicyOnlySession(s) ? 'policy' : 'live');
|
|
127
|
+
}).join(',');
|
|
60
128
|
}
|
|
61
129
|
|
|
62
130
|
// Apply session filter and update all UI to reflect it
|
|
@@ -67,8 +135,21 @@
|
|
|
67
135
|
var logSelect = document.getElementById('log-session-filter');
|
|
68
136
|
if (logSelect) logSelect.value = sessionId;
|
|
69
137
|
|
|
70
|
-
|
|
71
|
-
|
|
138
|
+
if (window.DollhouseConsole && window.DollhouseConsole.permissions) {
|
|
139
|
+
if (window.DollhouseConsole.permissions.onSessionChange) {
|
|
140
|
+
window.DollhouseConsole.permissions.onSessionChange(sessionId);
|
|
141
|
+
} else if (window.DollhouseConsole.permissions.refresh) {
|
|
142
|
+
window.DollhouseConsole.permissions.refresh();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Trigger log re-filter only when the Logs tab is active. Refiltering the
|
|
147
|
+
// virtualized log buffer can be expensive, and it should not delay session
|
|
148
|
+
// switching on other tabs like Permissions.
|
|
149
|
+
if (getActiveTabName() === 'logs'
|
|
150
|
+
&& window.DollhouseConsole
|
|
151
|
+
&& window.DollhouseConsole.logs
|
|
152
|
+
&& window.DollhouseConsole.logs.refilter) {
|
|
72
153
|
window.DollhouseConsole.logs.refilter(sessionId);
|
|
73
154
|
}
|
|
74
155
|
|
|
@@ -117,7 +198,7 @@
|
|
|
117
198
|
// Tick uptimes
|
|
118
199
|
var uptimes = document.querySelectorAll('.session-dropdown-uptime');
|
|
119
200
|
for (var j = 0; j < uptimes.length; j++) {
|
|
120
|
-
uptimes[j].textContent = formatUptime(uptimes[j].dataset.startedAt);
|
|
201
|
+
uptimes[j].textContent = uptimes[j].dataset.startedAt ? formatUptime(uptimes[j].dataset.startedAt) : 'saved';
|
|
121
202
|
}
|
|
122
203
|
|
|
123
204
|
// Update box label
|
|
@@ -125,7 +206,18 @@
|
|
|
125
206
|
var labelEl = document.querySelector('.session-box-label');
|
|
126
207
|
if (!countEl || !labelEl) return;
|
|
127
208
|
|
|
128
|
-
var active =
|
|
209
|
+
var active = getLiveSessions();
|
|
210
|
+
var selectable = getSelectableSessions();
|
|
211
|
+
|
|
212
|
+
if (filterSessionId) {
|
|
213
|
+
var filtered = selectable.find(function(s) { return s.sessionId === filterSessionId; });
|
|
214
|
+
if (filtered) {
|
|
215
|
+
countEl.textContent = displayName(filtered);
|
|
216
|
+
if (filtered.color) countEl.style.color = filtered.color;
|
|
217
|
+
labelEl.textContent = isPolicyOnlySession(filtered) ? 'policy only' : ('1/' + active.length);
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
129
221
|
|
|
130
222
|
if (active.length === 1) {
|
|
131
223
|
countEl.textContent = displayName(active[0]);
|
|
@@ -136,24 +228,16 @@
|
|
|
136
228
|
|
|
137
229
|
// Reset color when showing count
|
|
138
230
|
countEl.style.color = '';
|
|
139
|
-
|
|
140
|
-
if (filterSessionId) {
|
|
141
|
-
var filtered = active.find(function(s) { return s.sessionId === filterSessionId; });
|
|
142
|
-
if (filtered) {
|
|
143
|
-
countEl.textContent = displayName(filtered);
|
|
144
|
-
if (filtered.color) countEl.style.color = filtered.color;
|
|
145
|
-
labelEl.textContent = '1/' + active.length;
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
231
|
countEl.textContent = String(active.length);
|
|
150
232
|
labelEl.textContent = active.length === 1 ? 'session' : 'sessions';
|
|
151
233
|
}
|
|
152
234
|
|
|
153
235
|
// Build or rebuild the session indicator — only when session list actually changes
|
|
154
236
|
function updateSessionIndicator() {
|
|
155
|
-
var active =
|
|
156
|
-
var
|
|
237
|
+
var active = getLiveSessions();
|
|
238
|
+
var selectable = getSelectableSessions();
|
|
239
|
+
var policyOnly = selectable.filter(function(s) { return isPolicyOnlySession(s); });
|
|
240
|
+
var key = sessionListKey(selectable);
|
|
157
241
|
|
|
158
242
|
// If sessions haven't changed, just refresh selection state
|
|
159
243
|
if (key === lastSessionKey && dropdownBuilt) {
|
|
@@ -209,7 +293,7 @@
|
|
|
209
293
|
|
|
210
294
|
var allCount = document.createElement('span');
|
|
211
295
|
allCount.className = 'session-dropdown-role';
|
|
212
|
-
allCount.textContent = count
|
|
296
|
+
allCount.textContent = allSessionsSummary(count, policyOnly.length);
|
|
213
297
|
allItem.appendChild(allCount);
|
|
214
298
|
|
|
215
299
|
allItem.addEventListener('click', function(e) {
|
|
@@ -218,10 +302,18 @@
|
|
|
218
302
|
});
|
|
219
303
|
dropdown.appendChild(allItem);
|
|
220
304
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
305
|
+
function appendDivider() {
|
|
306
|
+
var divider = document.createElement('div');
|
|
307
|
+
divider.className = 'session-dropdown-divider';
|
|
308
|
+
dropdown.appendChild(divider);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function appendHeading(text) {
|
|
312
|
+
var heading = document.createElement('div');
|
|
313
|
+
heading.className = 'session-dropdown-heading';
|
|
314
|
+
heading.textContent = text;
|
|
315
|
+
dropdown.appendChild(heading);
|
|
316
|
+
}
|
|
225
317
|
|
|
226
318
|
// Session items — leader first, then followers
|
|
227
319
|
var sorted = active.slice().sort(function(a, b) {
|
|
@@ -230,70 +322,78 @@
|
|
|
230
322
|
return 0;
|
|
231
323
|
});
|
|
232
324
|
|
|
233
|
-
|
|
234
|
-
(
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
authBadge.
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
325
|
+
function appendSessionItem(s) {
|
|
326
|
+
var item = document.createElement('div');
|
|
327
|
+
item.className = 'session-dropdown-item';
|
|
328
|
+
item.dataset.sessionId = s.sessionId;
|
|
329
|
+
item.setAttribute('role', 'option');
|
|
330
|
+
|
|
331
|
+
var check = document.createElement('span');
|
|
332
|
+
check.className = 'session-dropdown-check';
|
|
333
|
+
item.appendChild(check);
|
|
334
|
+
|
|
335
|
+
var dot = document.createElement('span');
|
|
336
|
+
dot.className = 'session-dot';
|
|
337
|
+
if (s.color) dot.style.background = s.color;
|
|
338
|
+
item.appendChild(dot);
|
|
339
|
+
|
|
340
|
+
var nameEl = document.createElement('span');
|
|
341
|
+
nameEl.className = 'session-dropdown-name';
|
|
342
|
+
nameEl.textContent = displayName(s);
|
|
343
|
+
if (s.color) nameEl.style.color = s.color;
|
|
344
|
+
item.appendChild(nameEl);
|
|
345
|
+
|
|
346
|
+
// Session status badges (#1805) — for persisted policy sessions we
|
|
347
|
+
// switch from "live/authenticated" semantics to "saved/no client".
|
|
348
|
+
var authBadge = document.createElement('span');
|
|
349
|
+
authBadge.className = 'session-status-badge';
|
|
350
|
+
if (isPolicyOnlySession(s)) {
|
|
351
|
+
authBadge.textContent = 'Saved';
|
|
352
|
+
authBadge.dataset.status = 'positive';
|
|
353
|
+
authBadge.title = 'Persisted policy state from a prior session';
|
|
354
|
+
} else if (s.authenticated) {
|
|
355
|
+
authBadge.textContent = '\u25CF Auth';
|
|
356
|
+
authBadge.dataset.status = 'positive';
|
|
357
|
+
authBadge.title = 'Authenticated session';
|
|
358
|
+
} else {
|
|
359
|
+
authBadge.textContent = '\u25CB No auth';
|
|
360
|
+
authBadge.dataset.status = 'negative';
|
|
361
|
+
authBadge.title = 'Unauthenticated session';
|
|
362
|
+
}
|
|
363
|
+
item.appendChild(authBadge);
|
|
364
|
+
|
|
365
|
+
var clientBadge = document.createElement('span');
|
|
366
|
+
clientBadge.className = 'session-status-badge';
|
|
367
|
+
if (isPolicyOnlySession(s)) {
|
|
368
|
+
clientBadge.textContent = 'No client';
|
|
369
|
+
clientBadge.dataset.status = 'negative';
|
|
370
|
+
clientBadge.title = 'No live MCP client is currently attached';
|
|
371
|
+
} else if (s.kind === 'mcp') {
|
|
372
|
+
clientBadge.textContent = '\u2713 Client';
|
|
373
|
+
clientBadge.dataset.status = 'positive';
|
|
374
|
+
clientBadge.title = 'MCP client attached';
|
|
375
|
+
} else {
|
|
376
|
+
clientBadge.textContent = '\u2717 No client';
|
|
377
|
+
clientBadge.dataset.status = 'negative';
|
|
378
|
+
clientBadge.title = 'No MCP client attached';
|
|
379
|
+
}
|
|
380
|
+
item.appendChild(clientBadge);
|
|
381
|
+
|
|
382
|
+
var uptimeEl = document.createElement('span');
|
|
383
|
+
uptimeEl.className = 'session-dropdown-uptime';
|
|
384
|
+
uptimeEl.dataset.startedAt = s.startedAt;
|
|
385
|
+
uptimeEl.textContent = isPolicyOnlySession(s) ? 'saved' : formatUptime(s.startedAt);
|
|
386
|
+
item.appendChild(uptimeEl);
|
|
387
|
+
|
|
388
|
+
var killBtn = document.createElement('button');
|
|
389
|
+
killBtn.className = 'session-kill-btn';
|
|
390
|
+
killBtn.type = 'button';
|
|
391
|
+
killBtn.textContent = '\u00D7';
|
|
392
|
+
if (isPolicyOnlySession(s)) {
|
|
393
|
+
killBtn.disabled = true;
|
|
394
|
+
killBtn.style.visibility = 'hidden';
|
|
395
|
+
} else {
|
|
295
396
|
killBtn.title = 'Stop ' + displayName(s);
|
|
296
|
-
killBtn.textContent = '\u00D7';
|
|
297
397
|
killBtn.addEventListener('click', function(e) {
|
|
298
398
|
e.stopPropagation();
|
|
299
399
|
if (!confirm('Stop session ' + displayName(s) + '?')) return;
|
|
@@ -317,15 +417,31 @@
|
|
|
317
417
|
alert('Failed to stop session ' + displayName(s) + ': ' + (err.message || 'network error'));
|
|
318
418
|
});
|
|
319
419
|
});
|
|
320
|
-
|
|
420
|
+
}
|
|
421
|
+
item.appendChild(killBtn);
|
|
321
422
|
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
423
|
+
item.addEventListener('click', function(e) {
|
|
424
|
+
e.stopPropagation();
|
|
425
|
+
applyFilter(filterSessionId === s.sessionId ? '' : s.sessionId);
|
|
426
|
+
});
|
|
326
427
|
|
|
327
|
-
|
|
328
|
-
|
|
428
|
+
dropdown.appendChild(item);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
if (sorted.length > 0) {
|
|
432
|
+
appendDivider();
|
|
433
|
+
appendHeading('Live Sessions');
|
|
434
|
+
for (var i = 0; i < sorted.length; i++) {
|
|
435
|
+
appendSessionItem(sorted[i]);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
if (policyOnly.length > 0) {
|
|
440
|
+
appendDivider();
|
|
441
|
+
appendHeading('Persisted Policy State (Debug Info)');
|
|
442
|
+
for (var j = 0; j < policyOnly.length; j++) {
|
|
443
|
+
appendSessionItem(policyOnly[j]);
|
|
444
|
+
}
|
|
329
445
|
}
|
|
330
446
|
|
|
331
447
|
var wrapper = document.createElement('div');
|
|
@@ -391,14 +507,16 @@
|
|
|
391
507
|
if (!select) return;
|
|
392
508
|
|
|
393
509
|
var current = select.value;
|
|
394
|
-
var
|
|
510
|
+
var selectable = getSelectableSessions();
|
|
395
511
|
|
|
396
512
|
select.innerHTML = '<option value="">All Sessions</option>';
|
|
397
|
-
for (var i = 0; i <
|
|
513
|
+
for (var i = 0; i < selectable.length; i++) {
|
|
398
514
|
var opt = document.createElement('option');
|
|
399
|
-
opt.value =
|
|
400
|
-
opt.textContent = displayName(
|
|
401
|
-
|
|
515
|
+
opt.value = selectable[i].sessionId;
|
|
516
|
+
opt.textContent = displayName(selectable[i])
|
|
517
|
+
+ (selectable[i].isLeader ? ' (leader)' : '')
|
|
518
|
+
+ (isPolicyOnlySession(selectable[i]) ? ' (policy only)' : '');
|
|
519
|
+
if (selectable[i].sessionId === current) opt.selected = true;
|
|
402
520
|
select.appendChild(opt);
|
|
403
521
|
}
|
|
404
522
|
}
|
|
@@ -432,6 +550,8 @@
|
|
|
432
550
|
getFilterSessionId: function() { return filterSessionId; },
|
|
433
551
|
displayName: displayName,
|
|
434
552
|
getSessions: function() { return sessions; },
|
|
553
|
+
getSelectableSessions: getSelectableSessions,
|
|
554
|
+
setPolicySessions: setPolicySessions,
|
|
435
555
|
};
|
|
436
556
|
|
|
437
557
|
function init() {
|
|
@@ -69,6 +69,8 @@ html { scroll-behavior: smooth; }
|
|
|
69
69
|
body {
|
|
70
70
|
margin: 0;
|
|
71
71
|
min-height: 100vh;
|
|
72
|
+
display: flex;
|
|
73
|
+
flex-direction: column;
|
|
72
74
|
color-scheme: light;
|
|
73
75
|
color: var(--ink-900);
|
|
74
76
|
background:
|
|
@@ -249,6 +251,7 @@ p, li { max-width: 69ch; }
|
|
|
249
251
|
.site-main {
|
|
250
252
|
width: min(var(--max-width), calc(100% - 2 * var(--gutter)));
|
|
251
253
|
margin: 0 auto;
|
|
254
|
+
flex: 1 0 auto;
|
|
252
255
|
padding: clamp(1.1rem, 3vw, 2rem) 0 3rem;
|
|
253
256
|
}
|
|
254
257
|
|
|
@@ -949,7 +952,7 @@ dialog.modal {
|
|
|
949
952
|
display: flex;
|
|
950
953
|
align-items: flex-start;
|
|
951
954
|
justify-content: center;
|
|
952
|
-
padding: 1rem var(--gutter) 0;
|
|
955
|
+
padding: 1rem var(--gutter) calc(var(--site-footer-height, 4.5rem) + 0.5rem);
|
|
953
956
|
width: 100%;
|
|
954
957
|
}
|
|
955
958
|
dialog.modal::backdrop { display: none; }
|
|
@@ -969,14 +972,12 @@ body.modal-open { overflow: hidden; }
|
|
|
969
972
|
position: relative;
|
|
970
973
|
width: min(72rem, 100%);
|
|
971
974
|
margin: 0 auto;
|
|
972
|
-
/*
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
border-radius: var(--radius-lg) var(--radius-lg) 0 0;
|
|
975
|
+
/* Keep the dialog fully inside the viewport and above the fixed footer. */
|
|
976
|
+
height: calc(100dvh - var(--site-footer-height, 4.5rem) - 1.5rem);
|
|
977
|
+
max-height: calc(100dvh - var(--site-footer-height, 4.5rem) - 1.5rem);
|
|
978
|
+
border-radius: var(--radius-lg);
|
|
977
979
|
background: var(--paper-strong);
|
|
978
980
|
border: 1px solid var(--line);
|
|
979
|
-
border-bottom: none;
|
|
980
981
|
box-shadow: var(--shadow-card);
|
|
981
982
|
overflow: hidden;
|
|
982
983
|
display: flex;
|
|
@@ -1375,10 +1376,14 @@ body.modal-open { overflow: hidden; }
|
|
|
1375
1376
|
.site-footer {
|
|
1376
1377
|
border-top: 1px solid var(--line);
|
|
1377
1378
|
padding: 0.85rem 0 1.35rem;
|
|
1378
|
-
|
|
1379
|
+
margin-top: 0;
|
|
1380
|
+
position: fixed;
|
|
1381
|
+
left: 0;
|
|
1382
|
+
right: 0;
|
|
1379
1383
|
bottom: 0;
|
|
1380
|
-
background: var(--paper);
|
|
1381
|
-
|
|
1384
|
+
background: color-mix(in srgb, var(--paper) 92%, transparent);
|
|
1385
|
+
backdrop-filter: blur(10px);
|
|
1386
|
+
z-index: 40;
|
|
1382
1387
|
}
|
|
1383
1388
|
|
|
1384
1389
|
.footer-inner {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissionRoutes.d.ts","sourceRoot":"","sources":["../../../src/web/routes/permissionRoutes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAgB,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAE1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;
|
|
1
|
+
{"version":3,"file":"permissionRoutes.d.ts","sourceRoot":"","sources":["../../../src/web/routes/permissionRoutes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAgB,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAE1C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AA4F7E;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,GAAG,IAAI,CAoHrF"}
|