@mongoosejs/studio 0.5.2 → 0.5.4
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/backend/actions/ChatThread/streamChatMessage.js +12 -12
- package/backend/chatAgent/agentSystemPrompt.js +22 -10
- package/backend/chatAgent/getAgentTools.js +19 -8
- package/backend/chatAgent/runChatAgent.js +17 -0
- package/backend/index.js +3 -0
- package/debugging.js +11 -0
- package/eval/agent/cases.js +168 -0
- package/eval/agent/fixtures/carsharing.js +147 -0
- package/eval/agent/fixtures/ecommerce.js +152 -0
- package/eval/agent/run.js +323 -0
- package/frontend/index.js +1 -2
- package/frontend/public/app.js +395 -23
- package/frontend/public/tw.css +52 -0
- package/frontend/src/_util/recent-pages-history.js +116 -0
- package/frontend/src/chat/chat-message-script/chat-message-script.html +1 -6
- package/frontend/src/chat/chat.js +2 -2
- package/frontend/src/document/document.html +2 -2
- package/frontend/src/index.js +188 -10
- package/frontend/src/modal/modal.css +26 -1
- package/frontend/src/modal/modal.html +2 -2
- package/frontend/src/modal/modal.js +5 -1
- package/frontend/src/navbar/navbar.html +77 -6
- package/frontend/src/navbar/navbar.js +65 -4
- package/index.js +2 -1
- package/package.json +3 -2
package/frontend/public/tw.css
CHANGED
|
@@ -1192,6 +1192,10 @@ video {
|
|
|
1192
1192
|
width: 90vw !important;
|
|
1193
1193
|
}
|
|
1194
1194
|
|
|
1195
|
+
.\!w-\[min\(94vw\2c 28rem\)\] {
|
|
1196
|
+
width: min(94vw,28rem) !important;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1195
1199
|
.w-0 {
|
|
1196
1200
|
width: 0px;
|
|
1197
1201
|
}
|
|
@@ -1676,6 +1680,10 @@ video {
|
|
|
1676
1680
|
overflow: auto;
|
|
1677
1681
|
}
|
|
1678
1682
|
|
|
1683
|
+
.\!overflow-hidden {
|
|
1684
|
+
overflow: hidden !important;
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1679
1687
|
.overflow-hidden {
|
|
1680
1688
|
overflow: hidden;
|
|
1681
1689
|
}
|
|
@@ -1718,6 +1726,10 @@ video {
|
|
|
1718
1726
|
word-break: break-all;
|
|
1719
1727
|
}
|
|
1720
1728
|
|
|
1729
|
+
.\!rounded-lg {
|
|
1730
|
+
border-radius: 0.5rem !important;
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1721
1733
|
.rounded {
|
|
1722
1734
|
border-radius: 0.25rem;
|
|
1723
1735
|
}
|
|
@@ -1772,6 +1784,10 @@ video {
|
|
|
1772
1784
|
border-bottom-right-radius: 0px;
|
|
1773
1785
|
}
|
|
1774
1786
|
|
|
1787
|
+
.\!border {
|
|
1788
|
+
border-width: 1px !important;
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1775
1791
|
.border {
|
|
1776
1792
|
border-width: 1px;
|
|
1777
1793
|
}
|
|
@@ -1836,6 +1852,10 @@ video {
|
|
|
1836
1852
|
border-style: none;
|
|
1837
1853
|
}
|
|
1838
1854
|
|
|
1855
|
+
.\!border-edge {
|
|
1856
|
+
border-color: var(--color-edge) !important;
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1839
1859
|
.border-black\/10 {
|
|
1840
1860
|
border-color: rgb(0 0 0 / 0.1);
|
|
1841
1861
|
}
|
|
@@ -1921,6 +1941,10 @@ video {
|
|
|
1921
1941
|
border-right-color: rgb(17 24 39 / var(--tw-border-opacity));
|
|
1922
1942
|
}
|
|
1923
1943
|
|
|
1944
|
+
.\!bg-surface {
|
|
1945
|
+
background-color: var(--color-surface) !important;
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1924
1948
|
.bg-amber-100 {
|
|
1925
1949
|
--tw-bg-opacity: 1;
|
|
1926
1950
|
background-color: rgb(254 243 199 / var(--tw-bg-opacity));
|
|
@@ -2205,6 +2229,10 @@ video {
|
|
|
2205
2229
|
--tw-gradient-to: #dc2626 var(--tw-gradient-to-position);
|
|
2206
2230
|
}
|
|
2207
2231
|
|
|
2232
|
+
.\!p-0 {
|
|
2233
|
+
padding: 0px !important;
|
|
2234
|
+
}
|
|
2235
|
+
|
|
2208
2236
|
.p-0 {
|
|
2209
2237
|
padding: 0px;
|
|
2210
2238
|
}
|
|
@@ -2396,6 +2424,14 @@ video {
|
|
|
2396
2424
|
padding-left: 2rem;
|
|
2397
2425
|
}
|
|
2398
2426
|
|
|
2427
|
+
.pr-12 {
|
|
2428
|
+
padding-right: 3rem;
|
|
2429
|
+
}
|
|
2430
|
+
|
|
2431
|
+
.pr-2 {
|
|
2432
|
+
padding-right: 0.5rem;
|
|
2433
|
+
}
|
|
2434
|
+
|
|
2399
2435
|
.pr-3 {
|
|
2400
2436
|
padding-right: 0.75rem;
|
|
2401
2437
|
}
|
|
@@ -2810,6 +2846,12 @@ video {
|
|
|
2810
2846
|
opacity: 0.75;
|
|
2811
2847
|
}
|
|
2812
2848
|
|
|
2849
|
+
.\!shadow-xl {
|
|
2850
|
+
--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1) !important;
|
|
2851
|
+
--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color) !important;
|
|
2852
|
+
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow) !important;
|
|
2853
|
+
}
|
|
2854
|
+
|
|
2813
2855
|
.shadow {
|
|
2814
2856
|
--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
|
2815
2857
|
--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);
|
|
@@ -3851,6 +3893,11 @@ video {
|
|
|
3851
3893
|
}
|
|
3852
3894
|
}
|
|
3853
3895
|
|
|
3896
|
+
:is(:where(.dark) .dark\:\!bg-shark-900) {
|
|
3897
|
+
--tw-bg-opacity: 1 !important;
|
|
3898
|
+
background-color: rgb(21 25 30 / var(--tw-bg-opacity)) !important;
|
|
3899
|
+
}
|
|
3900
|
+
|
|
3854
3901
|
:is(:where(.dark) .dark\:bg-shark-800) {
|
|
3855
3902
|
--tw-bg-opacity: 1;
|
|
3856
3903
|
background-color: rgb(33 37 41 / var(--tw-bg-opacity));
|
|
@@ -3864,6 +3911,11 @@ video {
|
|
|
3864
3911
|
background-color: rgb(33 37 41 / 0.8);
|
|
3865
3912
|
}
|
|
3866
3913
|
|
|
3914
|
+
:is(:where(.dark) .dark\:bg-shark-900) {
|
|
3915
|
+
--tw-bg-opacity: 1;
|
|
3916
|
+
background-color: rgb(21 25 30 / var(--tw-bg-opacity));
|
|
3917
|
+
}
|
|
3918
|
+
|
|
3867
3919
|
:is(:where(.dark) .dark\:text-gray-400) {
|
|
3868
3920
|
--tw-text-opacity: 1;
|
|
3869
3921
|
color: rgb(156 163 175 / var(--tw-text-opacity));
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const RECENT_PAGES_STORAGE_KEY = 'studio:recent-pages-history';
|
|
4
|
+
const RECENT_PAGES_CHANGED_EVENT = 'studio-recent-pages-changed';
|
|
5
|
+
const RECENT_PAGES_LOCK_NAME = `studio-lock:${RECENT_PAGES_STORAGE_KEY}`;
|
|
6
|
+
const MAX_RECENT_PAGES = 50;
|
|
7
|
+
|
|
8
|
+
/** Collapses hash-style prefixes (e.g. `/#/`) to a normal path; keeps query string. */
|
|
9
|
+
function normalizeTrackedPath(fullPath) {
|
|
10
|
+
if (!fullPath || typeof fullPath !== 'string') {
|
|
11
|
+
return '/';
|
|
12
|
+
}
|
|
13
|
+
const qIndex = fullPath.indexOf('?');
|
|
14
|
+
const rawMain = qIndex === -1 ? fullPath : fullPath.slice(0, qIndex);
|
|
15
|
+
const qs = qIndex === -1 ? '' : fullPath.slice(qIndex);
|
|
16
|
+
|
|
17
|
+
let pathOnly = rawMain.trim().replace(/#/g, '');
|
|
18
|
+
pathOnly = pathOnly.replace(/^\/+/u, '/');
|
|
19
|
+
if (pathOnly !== '/' && /\/$/u.test(pathOnly)) {
|
|
20
|
+
pathOnly = pathOnly.replace(/\/+$/u, '');
|
|
21
|
+
}
|
|
22
|
+
if (pathOnly === '' || pathOnly === '/') {
|
|
23
|
+
pathOnly = '/';
|
|
24
|
+
} else if (!pathOnly.startsWith('/')) {
|
|
25
|
+
pathOnly = `/${pathOnly}`;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const out = qs ? pathOnly + qs : pathOnly;
|
|
29
|
+
return out;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function safeReadRecentPages() {
|
|
33
|
+
if (typeof window === 'undefined' || !window.localStorage) {
|
|
34
|
+
return [];
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
const raw = window.localStorage.getItem(RECENT_PAGES_STORAGE_KEY);
|
|
38
|
+
if (!raw) {
|
|
39
|
+
return [];
|
|
40
|
+
}
|
|
41
|
+
const parsed = JSON.parse(raw);
|
|
42
|
+
if (!Array.isArray(parsed)) {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
return parsed
|
|
46
|
+
.map(entry => {
|
|
47
|
+
if (!entry || typeof entry.path !== 'string') {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
return { ...entry, path: normalizeTrackedPath(entry.path) };
|
|
51
|
+
})
|
|
52
|
+
.filter(
|
|
53
|
+
entry =>
|
|
54
|
+
entry &&
|
|
55
|
+
typeof entry.label === 'string' &&
|
|
56
|
+
typeof entry.visitedAt === 'number' &&
|
|
57
|
+
entry.path !== ''
|
|
58
|
+
)
|
|
59
|
+
.slice(0, MAX_RECENT_PAGES);
|
|
60
|
+
} catch {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function notifyRecentPagesChangedFromThisTab() {
|
|
66
|
+
if (typeof window === 'undefined') {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
window.dispatchEvent(new CustomEvent(RECENT_PAGES_CHANGED_EVENT));
|
|
71
|
+
} catch {
|
|
72
|
+
/* ignore */
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function saveRecentPages(entries) {
|
|
77
|
+
if (typeof window === 'undefined' || !window.localStorage) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
window.localStorage.setItem(
|
|
81
|
+
RECENT_PAGES_STORAGE_KEY,
|
|
82
|
+
JSON.stringify(entries.slice(0, MAX_RECENT_PAGES))
|
|
83
|
+
);
|
|
84
|
+
notifyRecentPagesChangedFromThisTab();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function withRecentPagesStorageLock(syncFn) {
|
|
88
|
+
if (typeof window === 'undefined' || !window.localStorage) {
|
|
89
|
+
syncFn();
|
|
90
|
+
return Promise.resolve();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
const locksApi =
|
|
95
|
+
typeof navigator !== 'undefined' && navigator.locks !== undefined ? navigator.locks : null;
|
|
96
|
+
|
|
97
|
+
if (locksApi !== null && typeof locksApi.request === 'function') {
|
|
98
|
+
return locksApi.request(RECENT_PAGES_LOCK_NAME, syncFn);
|
|
99
|
+
}
|
|
100
|
+
} catch {
|
|
101
|
+
/* fall through */
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
syncFn();
|
|
105
|
+
return Promise.resolve();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
module.exports = {
|
|
109
|
+
RECENT_PAGES_STORAGE_KEY,
|
|
110
|
+
RECENT_PAGES_CHANGED_EVENT,
|
|
111
|
+
MAX_RECENT_PAGES,
|
|
112
|
+
normalizeTrackedPath,
|
|
113
|
+
safeReadRecentPages,
|
|
114
|
+
saveRecentPages,
|
|
115
|
+
withRecentPagesStorageLock
|
|
116
|
+
};
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 1v4m0 0h-4m4 0l-5-5" />
|
|
50
50
|
</svg>
|
|
51
51
|
</button>
|
|
52
|
-
<div
|
|
52
|
+
<div class="relative mr-1" ref="runDropdown">
|
|
53
53
|
<div class="flex items-center">
|
|
54
54
|
<button
|
|
55
55
|
class="px-2 py-1 text-xs bg-primary hover:bg-primary-hover text-primary-text border-none rounded-l-md cursor-pointer transition-colors disabled:bg-gray-400 disabled:cursor-not-allowed"
|
|
@@ -146,11 +146,6 @@
|
|
|
146
146
|
@click.stop="cancelEditing">
|
|
147
147
|
Cancel
|
|
148
148
|
</button>
|
|
149
|
-
<async-button
|
|
150
|
-
class="px-2 py-1 text-xs bg-blue-600 text-white border-none rounded cursor-pointer hover:bg-blue-700 transition-colors disabled:bg-gray-400"
|
|
151
|
-
@click="executeScript(false)">
|
|
152
|
-
Execute
|
|
153
|
-
</async-button>
|
|
154
149
|
</div>
|
|
155
150
|
</div>
|
|
156
151
|
<pre v-else class="whitespace-pre-wrap !my-0 bg-muted"><code v-text="script" ref="code" :class="'language-' + language"></code></pre>
|
|
@@ -305,9 +305,9 @@ module.exports = {
|
|
|
305
305
|
|
|
306
306
|
this.draftAgentMode = this.getAgentModePreference();
|
|
307
307
|
this.chatThreadId = this.threadId;
|
|
308
|
-
const [{ chatThreads },
|
|
308
|
+
const [{ chatThreads }, capabilities] = await Promise.all([
|
|
309
309
|
api.ChatThread.listChatThreads(),
|
|
310
|
-
api.getCapabilities()
|
|
310
|
+
api.getCapabilities().catch(() => this.capabilities)
|
|
311
311
|
]);
|
|
312
312
|
this.chatThreads = chatThreads;
|
|
313
313
|
this.capabilities = capabilities;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<button
|
|
7
7
|
@click="viewMode = 'fields'"
|
|
8
8
|
:class="viewMode === 'fields'
|
|
9
|
-
? 'bg-
|
|
9
|
+
? 'bg-primary text-white z-10'
|
|
10
10
|
: 'bg-gray-200 text-content-secondary hover:bg-gray-300'"
|
|
11
11
|
class="px-2 py-1.5 text-sm font-medium focus:outline-none focus:ring-2 focus:ring-blue-500 flex items-center gap-2 border border-edge-strong border-r-0 rounded-l-lg rounded-r-none"
|
|
12
12
|
>
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<button
|
|
19
19
|
@click="viewMode = 'json'"
|
|
20
20
|
:class="viewMode === 'json'
|
|
21
|
-
? 'bg-
|
|
21
|
+
? 'bg-primary text-white z-10'
|
|
22
22
|
: 'bg-gray-200 text-content-secondary hover:bg-gray-300'"
|
|
23
23
|
class="px-2 py-1.5 text-sm font-medium focus:outline-none focus:ring-2 focus:ring-blue-500 flex items-center gap-2 border border-edge-strong rounded-r-lg rounded-l-none"
|
|
24
24
|
>
|
package/frontend/src/index.js
CHANGED
|
@@ -15,12 +15,144 @@ const { routes, hasAccess } = require('./routes');
|
|
|
15
15
|
const Toast = require('vue-toastification').default;
|
|
16
16
|
const { useToast } = require('vue-toastification');
|
|
17
17
|
const appendCSS = require('./appendCSS');
|
|
18
|
+
const {
|
|
19
|
+
safeReadRecentPages,
|
|
20
|
+
saveRecentPages,
|
|
21
|
+
MAX_RECENT_PAGES,
|
|
22
|
+
normalizeTrackedPath,
|
|
23
|
+
withRecentPagesStorageLock
|
|
24
|
+
} = require('./_util/recent-pages-history');
|
|
18
25
|
appendCSS(require('vue-toastification/dist/index.css'));
|
|
19
26
|
|
|
27
|
+
const TRACKED_RECENT_PAGE_ROUTE_NAMES = new Set(['model', 'document', 'dashboard', 'chat']);
|
|
28
|
+
const CHAT_ROUTE_NAMES = new Set(['chat index', 'chat']);
|
|
29
|
+
|
|
30
|
+
function formatHistoryLabel(route) {
|
|
31
|
+
if (!route || typeof route.path !== 'string') {
|
|
32
|
+
return 'Unknown page';
|
|
33
|
+
}
|
|
34
|
+
if (route.name === 'model') {
|
|
35
|
+
return route.params?.model ? `Model: ${route.params.model}` : 'Model';
|
|
36
|
+
}
|
|
37
|
+
if (route.name === 'document') {
|
|
38
|
+
const model = route.params?.model ? `${route.params.model} ` : '';
|
|
39
|
+
const documentId = route.params?.documentId ? String(route.params.documentId) : '';
|
|
40
|
+
const shortDocumentId = documentId ? documentId.slice(0, 24) : '';
|
|
41
|
+
return `Document: ${model}${shortDocumentId}`.trim();
|
|
42
|
+
}
|
|
43
|
+
if (route.name === 'dashboard') {
|
|
44
|
+
return route.params?.dashboardId ? `Dashboard: ${route.params.dashboardId}` : 'Dashboard';
|
|
45
|
+
}
|
|
46
|
+
if (route.name === 'chat') {
|
|
47
|
+
return route.params?.threadId ? `Chat: ${route.params.threadId}` : 'Chat';
|
|
48
|
+
}
|
|
49
|
+
const normalizedPath = route.path.replace(/^\//, '');
|
|
50
|
+
return normalizedPath || 'Home';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function trackRecentPage(route) {
|
|
54
|
+
if (!route || typeof route.path !== 'string') {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (!TRACKED_RECENT_PAGE_ROUTE_NAMES.has(route.name)) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (typeof route.path === 'string' && route.path.includes('code=')) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const path = normalizeTrackedPath(route.fullPath || route.path || '/');
|
|
64
|
+
const label = formatHistoryLabel(route);
|
|
65
|
+
const visitedAt = Date.now();
|
|
66
|
+
|
|
67
|
+
void withRecentPagesStorageLock(() => {
|
|
68
|
+
const existing = safeReadRecentPages();
|
|
69
|
+
const deduped = existing.filter(entry => normalizeTrackedPath(entry.path) !== path);
|
|
70
|
+
const next = [...deduped, { path, label, visitedAt }]
|
|
71
|
+
.sort((a, b) => b.visitedAt - a.visitedAt)
|
|
72
|
+
.slice(0, MAX_RECENT_PAGES);
|
|
73
|
+
saveRecentPages(next);
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
20
77
|
const app = Vue.createApp({
|
|
21
78
|
template: '<app-component />'
|
|
22
79
|
});
|
|
23
80
|
|
|
81
|
+
window.drawArrowTo = function drawArrowTo(elementOrSelector) {
|
|
82
|
+
const element = typeof elementOrSelector === 'string' ?
|
|
83
|
+
document.querySelector(elementOrSelector) :
|
|
84
|
+
elementOrSelector;
|
|
85
|
+
|
|
86
|
+
if (!element || typeof element.getBoundingClientRect !== 'function') {
|
|
87
|
+
throw new Error('window.drawArrowTo() requires a DOM element or selector');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const existingArrow = document.getElementById('mongoose-studio-screenshot-arrow');
|
|
91
|
+
if (existingArrow) {
|
|
92
|
+
existingArrow.remove();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const rect = element.getBoundingClientRect();
|
|
96
|
+
const arrowLength = 140;
|
|
97
|
+
const arrowRise = 80;
|
|
98
|
+
const centerX = rect.left + rect.width / 2;
|
|
99
|
+
const centerY = rect.top + rect.height / 2;
|
|
100
|
+
const startX = Math.max(24, Math.min(window.innerWidth - 24, centerX - arrowLength));
|
|
101
|
+
const startY = Math.max(24, Math.min(window.innerHeight - 24, centerY - arrowRise));
|
|
102
|
+
const targetX = Math.max(rect.left, Math.min(rect.right, startX));
|
|
103
|
+
const targetY = Math.max(rect.top, Math.min(rect.bottom, startY));
|
|
104
|
+
const controlX = startX + (targetX - startX) * 0.6;
|
|
105
|
+
const controlY = startY + (targetY - startY) * 0.15;
|
|
106
|
+
const svgNS = 'http://www.w3.org/2000/svg';
|
|
107
|
+
const svg = document.createElementNS(svgNS, 'svg');
|
|
108
|
+
const defs = document.createElementNS(svgNS, 'defs');
|
|
109
|
+
const marker = document.createElementNS(svgNS, 'marker');
|
|
110
|
+
const markerPath = document.createElementNS(svgNS, 'path');
|
|
111
|
+
const path = document.createElementNS(svgNS, 'path');
|
|
112
|
+
|
|
113
|
+
svg.setAttribute('id', 'mongoose-studio-screenshot-arrow');
|
|
114
|
+
svg.setAttribute('viewBox', `0 0 ${window.innerWidth} ${window.innerHeight}`);
|
|
115
|
+
svg.setAttribute('width', '100vw');
|
|
116
|
+
svg.setAttribute('height', '100vh');
|
|
117
|
+
svg.style.position = 'fixed';
|
|
118
|
+
svg.style.left = '0';
|
|
119
|
+
svg.style.top = '0';
|
|
120
|
+
svg.style.zIndex = '2147483647';
|
|
121
|
+
svg.style.pointerEvents = 'none';
|
|
122
|
+
|
|
123
|
+
marker.setAttribute('id', 'mongoose-studio-screenshot-arrow-head');
|
|
124
|
+
marker.setAttribute('markerWidth', '14');
|
|
125
|
+
marker.setAttribute('markerHeight', '14');
|
|
126
|
+
marker.setAttribute('refX', '12');
|
|
127
|
+
marker.setAttribute('refY', '7');
|
|
128
|
+
marker.setAttribute('orient', 'auto');
|
|
129
|
+
marker.setAttribute('markerUnits', 'userSpaceOnUse');
|
|
130
|
+
|
|
131
|
+
markerPath.setAttribute('d', 'M 0 0 L 14 7 L 0 14 z');
|
|
132
|
+
markerPath.setAttribute('fill', '#dc2626');
|
|
133
|
+
|
|
134
|
+
path.setAttribute('d', `M ${startX} ${startY} Q ${controlX} ${controlY} ${targetX} ${targetY}`);
|
|
135
|
+
path.setAttribute('fill', 'none');
|
|
136
|
+
path.setAttribute('stroke', '#dc2626');
|
|
137
|
+
path.setAttribute('stroke-width', '5');
|
|
138
|
+
path.setAttribute('stroke-linecap', 'round');
|
|
139
|
+
path.setAttribute('stroke-linejoin', 'round');
|
|
140
|
+
path.setAttribute('marker-end', 'url(#mongoose-studio-screenshot-arrow-head)');
|
|
141
|
+
|
|
142
|
+
marker.appendChild(markerPath);
|
|
143
|
+
defs.appendChild(marker);
|
|
144
|
+
svg.appendChild(defs);
|
|
145
|
+
svg.appendChild(path);
|
|
146
|
+
document.body.appendChild(svg);
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
element: svg,
|
|
150
|
+
remove() {
|
|
151
|
+
svg.remove();
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
|
|
24
156
|
// https://github.com/Maronato/vue-toastification/tree/main?tab=readme-ov-file#toast-types
|
|
25
157
|
app.use(Toast, { position: 'bottom-right', timeout: 3000 });
|
|
26
158
|
|
|
@@ -109,8 +241,12 @@ app.component('app-component', {
|
|
|
109
241
|
window.localStorage.setItem('_mongooseStudioAccessToken', accessToken._id);
|
|
110
242
|
|
|
111
243
|
try {
|
|
112
|
-
const { nodeEnv } = await
|
|
244
|
+
const [{ nodeEnv }, capabilities] = await Promise.all([
|
|
245
|
+
api.status(),
|
|
246
|
+
loadStudioCapabilities()
|
|
247
|
+
]);
|
|
113
248
|
this.nodeEnv = nodeEnv;
|
|
249
|
+
this.capabilities = capabilities;
|
|
114
250
|
} catch (err) {
|
|
115
251
|
this.authError = 'Error connecting to Mongoose Studio API: ' + err.response?.data?.message ?? err.message;
|
|
116
252
|
this.status = 'loaded';
|
|
@@ -130,12 +266,14 @@ app.component('app-component', {
|
|
|
130
266
|
const { user, roles } = await mothership.me();
|
|
131
267
|
|
|
132
268
|
try {
|
|
133
|
-
const [{ nodeEnv }, { modelSchemaPaths }] = await Promise.all([
|
|
269
|
+
const [{ nodeEnv }, { modelSchemaPaths }, capabilities] = await Promise.all([
|
|
134
270
|
api.status(),
|
|
135
|
-
api.Model.listModels()
|
|
271
|
+
api.Model.listModels(),
|
|
272
|
+
loadStudioCapabilities()
|
|
136
273
|
]);
|
|
137
274
|
this.nodeEnv = nodeEnv;
|
|
138
275
|
this.modelSchemaPaths = modelSchemaPaths;
|
|
276
|
+
this.capabilities = capabilities;
|
|
139
277
|
} catch (err) {
|
|
140
278
|
this.authError = 'Error connecting to Mongoose Studio API: ' + (err.response?.data?.message ?? err.message);
|
|
141
279
|
this.status = 'loaded';
|
|
@@ -148,12 +286,14 @@ app.component('app-component', {
|
|
|
148
286
|
}
|
|
149
287
|
} else {
|
|
150
288
|
try {
|
|
151
|
-
const [{ nodeEnv }, { modelSchemaPaths }] = await Promise.all([
|
|
289
|
+
const [{ nodeEnv }, { modelSchemaPaths }, capabilities] = await Promise.all([
|
|
152
290
|
api.status(),
|
|
153
|
-
api.Model.listModels()
|
|
291
|
+
api.Model.listModels(),
|
|
292
|
+
loadStudioCapabilities()
|
|
154
293
|
]);
|
|
155
294
|
this.nodeEnv = nodeEnv;
|
|
156
295
|
this.modelSchemaPaths = modelSchemaPaths;
|
|
296
|
+
this.capabilities = capabilities;
|
|
157
297
|
} catch (err) {
|
|
158
298
|
this.authError = 'Error connecting to Mongoose Studio API: ' + (err.response?.data?.message ?? err.message);
|
|
159
299
|
}
|
|
@@ -168,8 +308,17 @@ app.component('app-component', {
|
|
|
168
308
|
const nodeEnv = Vue.ref(null);
|
|
169
309
|
const authError = Vue.ref(null);
|
|
170
310
|
const modelSchemaPaths = Vue.ref(null);
|
|
171
|
-
|
|
172
|
-
|
|
311
|
+
const capabilities = Vue.ref(null);
|
|
312
|
+
|
|
313
|
+
const state = Vue.reactive({
|
|
314
|
+
user,
|
|
315
|
+
roles,
|
|
316
|
+
status,
|
|
317
|
+
nodeEnv,
|
|
318
|
+
authError,
|
|
319
|
+
modelSchemaPaths,
|
|
320
|
+
capabilities
|
|
321
|
+
});
|
|
173
322
|
Vue.provide('state', state);
|
|
174
323
|
|
|
175
324
|
return state;
|
|
@@ -220,20 +369,49 @@ router.beforeEach((to, from, next) => {
|
|
|
220
369
|
next();
|
|
221
370
|
});
|
|
222
371
|
|
|
223
|
-
router.beforeEach((to, from, next) => {
|
|
224
|
-
if (
|
|
372
|
+
router.beforeEach(async(to, from, next) => {
|
|
373
|
+
if (CHAT_ROUTE_NAMES.has(to.name) && !(await supportsAI())) {
|
|
225
374
|
next({ name: 'root' });
|
|
226
375
|
return;
|
|
227
376
|
}
|
|
228
377
|
|
|
229
|
-
if (to.name === 'root' && window.state
|
|
378
|
+
if (to.name === 'root' && window.state?.roles && window.state.roles[0] === 'dashboards') {
|
|
230
379
|
return next({ name: 'dashboards' });
|
|
231
380
|
} else {
|
|
232
381
|
next();
|
|
233
382
|
}
|
|
234
383
|
});
|
|
235
384
|
|
|
385
|
+
router.afterEach((to) => {
|
|
386
|
+
trackRecentPage(to);
|
|
387
|
+
});
|
|
388
|
+
|
|
236
389
|
app.config.globalProperties = { format, arrayUtils, $toast: toast };
|
|
237
390
|
app.use(router);
|
|
238
391
|
|
|
239
392
|
app.mount('#content');
|
|
393
|
+
|
|
394
|
+
async function getStudioCapabilities() {
|
|
395
|
+
if (window.__mongooseStudioCapabilitiesPromise == null) {
|
|
396
|
+
window.__mongooseStudioCapabilitiesPromise = api.getCapabilities().catch(err => {
|
|
397
|
+
window.__mongooseStudioCapabilitiesPromise = null;
|
|
398
|
+
throw err;
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
return window.__mongooseStudioCapabilitiesPromise;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
async function loadStudioCapabilities() {
|
|
405
|
+
try {
|
|
406
|
+
return await getStudioCapabilities();
|
|
407
|
+
} catch (err) {
|
|
408
|
+
return null;
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
async function supportsAI() {
|
|
413
|
+
if (window.state?.capabilities == null) {
|
|
414
|
+
return true;
|
|
415
|
+
}
|
|
416
|
+
return window.state.capabilities.supportsAI !== false;
|
|
417
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
.modal-mask {
|
|
4
4
|
position: fixed;
|
|
5
|
-
z-index:
|
|
5
|
+
z-index: 10050;
|
|
6
6
|
top: 0;
|
|
7
7
|
left: 0;
|
|
8
8
|
width: 100%;
|
|
@@ -30,6 +30,11 @@
|
|
|
30
30
|
position: relative;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
.modal-container.modal-container--flush {
|
|
34
|
+
padding: 0;
|
|
35
|
+
padding-bottom: 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
33
38
|
.modal-header {
|
|
34
39
|
margin-top: 0;
|
|
35
40
|
font-size: 18px;
|
|
@@ -50,6 +55,25 @@
|
|
|
50
55
|
overflow: auto;
|
|
51
56
|
}
|
|
52
57
|
|
|
58
|
+
/* Recent pages: one scroll region — inner .modal-recent-pages-scroll only (avoids double scrollbars with .modal-body overflow). */
|
|
59
|
+
.modal-body.modal-recent-pages-body {
|
|
60
|
+
box-sizing: border-box;
|
|
61
|
+
display: flex;
|
|
62
|
+
flex-direction: column;
|
|
63
|
+
margin: 0;
|
|
64
|
+
height: min(60vh, 22.5rem);
|
|
65
|
+
max-height: min(60vh, 22.5rem);
|
|
66
|
+
overflow: hidden;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.modal-body.modal-recent-pages-body .modal-recent-pages-scroll {
|
|
70
|
+
flex: 1 1 0;
|
|
71
|
+
min-height: 0;
|
|
72
|
+
overflow-x: hidden;
|
|
73
|
+
overflow-y: auto;
|
|
74
|
+
-webkit-overflow-scrolling: touch;
|
|
75
|
+
}
|
|
76
|
+
|
|
53
77
|
.modal__button--default {
|
|
54
78
|
float: right;
|
|
55
79
|
}
|
|
@@ -79,6 +103,7 @@
|
|
|
79
103
|
|
|
80
104
|
.modal-container .modal-exit {
|
|
81
105
|
position: absolute;
|
|
106
|
+
z-index: 2;
|
|
82
107
|
right: 0.25em;
|
|
83
108
|
top: 0.25em;
|
|
84
109
|
cursor: pointer;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<transition name="modal">
|
|
2
2
|
<div class="modal-mask">
|
|
3
3
|
<div class="modal-wrapper">
|
|
4
|
-
<div class="modal-container" :class="containerClass">
|
|
5
|
-
<div class="modal-body">
|
|
4
|
+
<div class="modal-container" :class="[containerClass, { 'modal-container--flush': flushContainer }]">
|
|
5
|
+
<div class="modal-body" :class="bodyClass">
|
|
6
6
|
<slot name="body">
|
|
7
7
|
</slot>
|
|
8
8
|
</div>
|
|
@@ -7,7 +7,11 @@ appendCSS(require('./modal.css'));
|
|
|
7
7
|
|
|
8
8
|
module.exports = app => app.component('modal', {
|
|
9
9
|
template,
|
|
10
|
-
props:
|
|
10
|
+
props: {
|
|
11
|
+
containerClass: String,
|
|
12
|
+
bodyClass: String,
|
|
13
|
+
flushContainer: Boolean
|
|
14
|
+
},
|
|
11
15
|
mounted() {
|
|
12
16
|
window.addEventListener('keydown', this.onEscape);
|
|
13
17
|
},
|