@mongoosejs/studio 0.5.13 → 0.5.14
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/AGENTS.md +7 -0
- package/CLAUDE.md +7 -0
- package/README.md +3 -0
- package/backend/actions/ChatMessage/executeScript.js +7 -5
- package/backend/actions/ChatThread/createChatMessage.js +4 -2
- package/backend/actions/ChatThread/getChatThread.js +5 -2
- package/backend/actions/ChatThread/listChatThreads.js +3 -1
- package/backend/actions/ChatThread/shareChatThread.js +5 -2
- package/backend/actions/ChatThread/streamChatMessage.js +4 -2
- package/backend/actions/ChatThread/toggleAgentMode.js +5 -2
- package/backend/actions/Dashboard/deleteDashboard.js +7 -3
- package/backend/actions/Dashboard/getDashboard.js +21 -10
- package/backend/actions/Dashboard/getDashboards.js +3 -1
- package/backend/actions/Dashboard/updateDashboard.js +3 -2
- package/backend/actions/Model/addField.js +3 -2
- package/backend/actions/Model/analyzeSchema.js +6 -4
- package/backend/actions/Model/deleteDocument.js +3 -3
- package/backend/actions/Model/deleteDocuments.js +3 -1
- package/backend/actions/Model/executeDocumentScript.js +25 -29
- package/backend/actions/Model/exportQueryResults.js +3 -1
- package/backend/actions/Model/getCollectionInfo.js +5 -3
- package/backend/actions/Model/getDocument.js +3 -2
- package/backend/actions/Model/getDocuments.js +15 -5
- package/backend/actions/Model/getDocumentsStream.js +28 -18
- package/backend/actions/Model/getEstimatedDocumentCounts.js +5 -2
- package/backend/actions/Model/updateDocument.js +4 -2
- package/backend/actions/Model/updateDocuments.js +4 -2
- package/backend/actions/Model/validateDocument.js +5 -2
- package/backend/actions/Task/cancelTask.js +6 -3
- package/backend/actions/Task/getTaskOverview.js +3 -2
- package/backend/actions/Task/getTasks.js +3 -2
- package/backend/actions/Task/getTasksOverTime.js +3 -2
- package/backend/actions/Task/rescheduleTask.js +6 -3
- package/backend/actions/Task/runTask.js +6 -3
- package/backend/helpers/omitNullish.js +5 -0
- package/backend/index.js +4 -4
- package/backend/sandbox/createSandbox.js +2 -2
- package/backend/sandbox/createScriptDb.js +42 -19
- package/frontend/public/app.js +319 -96
- package/frontend/public/tw.css +68 -0
- package/frontend/src/api.js +5 -1
- package/frontend/src/models/models.html +29 -8
- package/frontend/src/models/models.js +30 -2
- package/frontend/src/tooltip/tooltip.html +28 -0
- package/frontend/src/tooltip/tooltip.js +94 -0
- package/index.d.ts +1 -1
- package/local.js +2 -1
- package/nest.d.ts +1 -0
- package/package.json +1 -1
- package/vercel.d.ts +2 -0
package/frontend/public/tw.css
CHANGED
|
@@ -664,6 +664,10 @@ video {
|
|
|
664
664
|
bottom: 0px;
|
|
665
665
|
}
|
|
666
666
|
|
|
667
|
+
.-bottom-1 {
|
|
668
|
+
bottom: -0.25rem;
|
|
669
|
+
}
|
|
670
|
+
|
|
667
671
|
.-left-1 {
|
|
668
672
|
left: -0.25rem;
|
|
669
673
|
}
|
|
@@ -672,6 +676,10 @@ video {
|
|
|
672
676
|
left: -0.75rem;
|
|
673
677
|
}
|
|
674
678
|
|
|
679
|
+
.-top-1 {
|
|
680
|
+
top: -0.25rem;
|
|
681
|
+
}
|
|
682
|
+
|
|
675
683
|
.bottom-0 {
|
|
676
684
|
bottom: 0px;
|
|
677
685
|
}
|
|
@@ -792,6 +800,10 @@ video {
|
|
|
792
800
|
z-index: 10001;
|
|
793
801
|
}
|
|
794
802
|
|
|
803
|
+
.z-\[1000\] {
|
|
804
|
+
z-index: 1000;
|
|
805
|
+
}
|
|
806
|
+
|
|
795
807
|
.z-\[100\] {
|
|
796
808
|
z-index: 100;
|
|
797
809
|
}
|
|
@@ -1085,6 +1097,10 @@ video {
|
|
|
1085
1097
|
height: 4rem;
|
|
1086
1098
|
}
|
|
1087
1099
|
|
|
1100
|
+
.h-2 {
|
|
1101
|
+
height: 0.5rem;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1088
1104
|
.h-20 {
|
|
1089
1105
|
height: 5rem;
|
|
1090
1106
|
}
|
|
@@ -1265,6 +1281,10 @@ video {
|
|
|
1265
1281
|
width: 4rem;
|
|
1266
1282
|
}
|
|
1267
1283
|
|
|
1284
|
+
.w-2 {
|
|
1285
|
+
width: 0.5rem;
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1268
1288
|
.w-20 {
|
|
1269
1289
|
width: 5rem;
|
|
1270
1290
|
}
|
|
@@ -1365,6 +1385,11 @@ video {
|
|
|
1365
1385
|
width: 100%;
|
|
1366
1386
|
}
|
|
1367
1387
|
|
|
1388
|
+
.w-max {
|
|
1389
|
+
width: -moz-max-content;
|
|
1390
|
+
width: max-content;
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1368
1393
|
.min-w-0 {
|
|
1369
1394
|
min-width: 0px;
|
|
1370
1395
|
}
|
|
@@ -1441,6 +1466,10 @@ video {
|
|
|
1441
1466
|
max-width: calc(100vw - 4rem);
|
|
1442
1467
|
}
|
|
1443
1468
|
|
|
1469
|
+
.max-w-\[min\(20rem\2c calc\(100vw-1rem\)\)\] {
|
|
1470
|
+
max-width: min(20rem,calc(100vw - 1rem));
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1444
1473
|
.max-w-full {
|
|
1445
1474
|
max-width: 100%;
|
|
1446
1475
|
}
|
|
@@ -1555,6 +1584,11 @@ video {
|
|
|
1555
1584
|
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
|
1556
1585
|
}
|
|
1557
1586
|
|
|
1587
|
+
.rotate-45 {
|
|
1588
|
+
--tw-rotate: 45deg;
|
|
1589
|
+
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1558
1592
|
.rotate-90 {
|
|
1559
1593
|
--tw-rotate: 90deg;
|
|
1560
1594
|
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
|
|
@@ -1608,6 +1642,10 @@ video {
|
|
|
1608
1642
|
resize: vertical;
|
|
1609
1643
|
}
|
|
1610
1644
|
|
|
1645
|
+
.resize {
|
|
1646
|
+
resize: both;
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1611
1649
|
.snap-y {
|
|
1612
1650
|
scroll-snap-type: y var(--tw-scroll-snap-strictness);
|
|
1613
1651
|
}
|
|
@@ -1861,6 +1899,10 @@ video {
|
|
|
1861
1899
|
text-overflow: ellipsis;
|
|
1862
1900
|
}
|
|
1863
1901
|
|
|
1902
|
+
.whitespace-normal {
|
|
1903
|
+
white-space: normal;
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1864
1906
|
.whitespace-nowrap {
|
|
1865
1907
|
white-space: nowrap;
|
|
1866
1908
|
}
|
|
@@ -2184,6 +2226,11 @@ video {
|
|
|
2184
2226
|
background-color: rgb(55 65 81 / var(--tw-bg-opacity));
|
|
2185
2227
|
}
|
|
2186
2228
|
|
|
2229
|
+
.bg-gray-900 {
|
|
2230
|
+
--tw-bg-opacity: 1;
|
|
2231
|
+
background-color: rgb(17 24 39 / var(--tw-bg-opacity));
|
|
2232
|
+
}
|
|
2233
|
+
|
|
2187
2234
|
.bg-green-100 {
|
|
2188
2235
|
--tw-bg-opacity: 1;
|
|
2189
2236
|
background-color: rgb(220 252 231 / var(--tw-bg-opacity));
|
|
@@ -2747,6 +2794,10 @@ video {
|
|
|
2747
2794
|
font-weight: 500;
|
|
2748
2795
|
}
|
|
2749
2796
|
|
|
2797
|
+
.font-normal {
|
|
2798
|
+
font-weight: 400;
|
|
2799
|
+
}
|
|
2800
|
+
|
|
2750
2801
|
.font-semibold {
|
|
2751
2802
|
font-weight: 600;
|
|
2752
2803
|
}
|
|
@@ -2763,6 +2814,10 @@ video {
|
|
|
2763
2814
|
font-style: italic;
|
|
2764
2815
|
}
|
|
2765
2816
|
|
|
2817
|
+
.leading-4 {
|
|
2818
|
+
line-height: 1rem;
|
|
2819
|
+
}
|
|
2820
|
+
|
|
2766
2821
|
.leading-6 {
|
|
2767
2822
|
line-height: 1.5rem;
|
|
2768
2823
|
}
|
|
@@ -3216,6 +3271,11 @@ video {
|
|
|
3216
3271
|
--tw-ring-color: rgb(176 32 43 / var(--tw-ring-opacity));
|
|
3217
3272
|
}
|
|
3218
3273
|
|
|
3274
|
+
.ring-red-200 {
|
|
3275
|
+
--tw-ring-opacity: 1;
|
|
3276
|
+
--tw-ring-color: rgb(254 202 202 / var(--tw-ring-opacity));
|
|
3277
|
+
}
|
|
3278
|
+
|
|
3219
3279
|
.ring-red-400 {
|
|
3220
3280
|
--tw-ring-opacity: 1;
|
|
3221
3281
|
--tw-ring-color: rgb(248 113 113 / var(--tw-ring-opacity));
|
|
@@ -3837,6 +3897,10 @@ video {
|
|
|
3837
3897
|
--tw-ring-offset-width: 2px;
|
|
3838
3898
|
}
|
|
3839
3899
|
|
|
3900
|
+
.focus-visible\:rounded-md:focus-visible {
|
|
3901
|
+
border-radius: 0.375rem;
|
|
3902
|
+
}
|
|
3903
|
+
|
|
3840
3904
|
.focus-visible\:opacity-100:focus-visible {
|
|
3841
3905
|
opacity: 1;
|
|
3842
3906
|
}
|
|
@@ -3853,6 +3917,10 @@ video {
|
|
|
3853
3917
|
outline-offset: 2px;
|
|
3854
3918
|
}
|
|
3855
3919
|
|
|
3920
|
+
.focus-visible\:outline-blue-600:focus-visible {
|
|
3921
|
+
outline-color: #2563eb;
|
|
3922
|
+
}
|
|
3923
|
+
|
|
3856
3924
|
.focus-visible\:outline-edge-strong:focus-visible {
|
|
3857
3925
|
outline-color: var(--color-edge-strong);
|
|
3858
3926
|
}
|
package/frontend/src/api.js
CHANGED
|
@@ -229,7 +229,11 @@ if (window.MONGOOSE_STUDIO_CONFIG.isLambda) {
|
|
|
229
229
|
getDocumentsStream: async function* getDocumentsStream(params) {
|
|
230
230
|
const data = await client.post('', { action: 'Model.getDocuments', ...params }).then(res => res.data);
|
|
231
231
|
yield { schemaPaths: data.schemaPaths, suggestedFields: data.suggestedFields };
|
|
232
|
-
|
|
232
|
+
if (data.numDocsError) {
|
|
233
|
+
yield { numDocsError: data.numDocsError };
|
|
234
|
+
} else {
|
|
235
|
+
yield { numDocs: data.numDocs };
|
|
236
|
+
}
|
|
233
237
|
for (const doc of data.docs) {
|
|
234
238
|
yield { document: doc };
|
|
235
239
|
}
|
|
@@ -110,7 +110,18 @@
|
|
|
110
110
|
>
|
|
111
111
|
</document-search>
|
|
112
112
|
<div>
|
|
113
|
-
<
|
|
113
|
+
<tooltip
|
|
114
|
+
v-if="numDocumentsError"
|
|
115
|
+
:text="'Error counting documents: ' + numDocumentsError"
|
|
116
|
+
>
|
|
117
|
+
<span
|
|
118
|
+
class="inline-flex rounded-md bg-red-50 px-2 py-2 text-sm text-red-700 ring-1 ring-inset ring-red-200"
|
|
119
|
+
role="alert"
|
|
120
|
+
>
|
|
121
|
+
Count Error
|
|
122
|
+
</span>
|
|
123
|
+
</tooltip>
|
|
124
|
+
<span v-else-if="numDocuments == null">Loading...</span>
|
|
114
125
|
<span v-else-if="typeof numDocuments === 'number'">{{documents.length}}/{{numDocuments === 1 ? numDocuments + ' document' : numDocuments + ' documents'}}</span>
|
|
115
126
|
</div>
|
|
116
127
|
<button
|
|
@@ -453,11 +464,16 @@
|
|
|
453
464
|
</tbody>
|
|
454
465
|
</table>
|
|
455
466
|
</div>
|
|
456
|
-
<div
|
|
457
|
-
|
|
458
|
-
|
|
467
|
+
<div
|
|
468
|
+
v-if="outputType === 'table' && (documentsError || loadingMore || (status === 'loading' && documents.length > 0))"
|
|
469
|
+
class="flex items-center justify-center gap-2 py-3 text-sm border-t"
|
|
470
|
+
:class="documentsError ? 'border-red-200 bg-red-50 text-red-700' : 'border-edge bg-surface text-content-tertiary'"
|
|
471
|
+
:role="documentsError ? 'alert' : null"
|
|
472
|
+
>
|
|
473
|
+
<img v-if="!documentsError" src="images/loader.gif" alt="" class="h-5 w-5">
|
|
474
|
+
<span>{{documentsError || 'Loading documents…'}}</span>
|
|
459
475
|
</div>
|
|
460
|
-
<p v-if="outputType === 'table' && documents.length === 0 && status === 'loaded'" class="mt-2 text-sm text-content-tertiary px-1">
|
|
476
|
+
<p v-if="outputType === 'table' && documents.length === 0 && status === 'loaded' && !documentsError" class="mt-2 text-sm text-content-tertiary px-1">
|
|
461
477
|
No documents to show. Use Projection in the menu to choose columns.
|
|
462
478
|
</p>
|
|
463
479
|
</div>
|
|
@@ -483,9 +499,14 @@
|
|
|
483
499
|
<list-json :value="filterDocument(document)" :references="referenceMap">
|
|
484
500
|
</list-json>
|
|
485
501
|
</div>
|
|
486
|
-
<div
|
|
487
|
-
|
|
488
|
-
|
|
502
|
+
<div
|
|
503
|
+
v-if="outputType === 'json' && (documentsError || loadingMore || (status === 'loading' && documents.length > 0))"
|
|
504
|
+
class="flex items-center justify-center gap-2 py-3 text-sm"
|
|
505
|
+
:class="documentsError ? 'rounded-md border border-red-200 bg-red-50 text-red-700' : 'text-content-tertiary'"
|
|
506
|
+
:role="documentsError ? 'alert' : null"
|
|
507
|
+
>
|
|
508
|
+
<img v-if="!documentsError" src="images/loader.gif" alt="" class="h-5 w-5">
|
|
509
|
+
<span>{{documentsError || 'Loading documents…'}}</span>
|
|
489
510
|
</div>
|
|
490
511
|
</div>
|
|
491
512
|
<div v-else-if="outputType === 'map'" class="flex flex-col h-full">
|
|
@@ -151,6 +151,8 @@ module.exports = app => app.component('models', {
|
|
|
151
151
|
filteredPaths: [],
|
|
152
152
|
selectedPaths: [],
|
|
153
153
|
numDocuments: null,
|
|
154
|
+
numDocumentsError: null,
|
|
155
|
+
documentsError: null,
|
|
154
156
|
mongoDBIndexes: [],
|
|
155
157
|
schemaIndexes: [],
|
|
156
158
|
status: 'loading',
|
|
@@ -838,7 +840,8 @@ module.exports = app => app.component('models', {
|
|
|
838
840
|
return filteredDoc;
|
|
839
841
|
},
|
|
840
842
|
async checkIfScrolledToBottom() {
|
|
841
|
-
if
|
|
843
|
+
// Stop infinite scroll if the last load failed, otherwise we would retry the same failing request forever.
|
|
844
|
+
if (this.status === 'loading' || this.loadedAllDocs || this.documentsError) {
|
|
842
845
|
return;
|
|
843
846
|
}
|
|
844
847
|
// Infinite scroll only applies to table/json views.
|
|
@@ -864,11 +867,20 @@ module.exports = app => app.component('models', {
|
|
|
864
867
|
try {
|
|
865
868
|
const skip = this.documents.length;
|
|
866
869
|
const params = this.buildDocumentFetchParams({ skip });
|
|
867
|
-
const { docs } = await api.Model.getDocuments(params);
|
|
870
|
+
const { docs, numDocs, numDocsError } = await api.Model.getDocuments(params);
|
|
871
|
+
if (numDocsError) {
|
|
872
|
+
this.numDocumentsError = numDocsError;
|
|
873
|
+
} else if (numDocs !== undefined) {
|
|
874
|
+
this.numDocuments = numDocs;
|
|
875
|
+
this.numDocumentsError = null;
|
|
876
|
+
}
|
|
868
877
|
if (docs.length < limit) {
|
|
869
878
|
this.loadedAllDocs = true;
|
|
870
879
|
}
|
|
871
880
|
this.documents.push(...docs);
|
|
881
|
+
this.documentsError = null;
|
|
882
|
+
} catch (err) {
|
|
883
|
+
this.documentsError = err?.message;
|
|
872
884
|
} finally {
|
|
873
885
|
this.loadingMore = false;
|
|
874
886
|
this.status = 'loaded';
|
|
@@ -1081,6 +1093,8 @@ module.exports = app => app.component('models', {
|
|
|
1081
1093
|
// Clear previous data
|
|
1082
1094
|
this.documents = [];
|
|
1083
1095
|
this.numDocuments = null;
|
|
1096
|
+
this.numDocumentsError = null;
|
|
1097
|
+
this.documentsError = null;
|
|
1084
1098
|
this.loadedAllDocs = false;
|
|
1085
1099
|
this.lastSelectedIndex = null;
|
|
1086
1100
|
|
|
@@ -1130,6 +1144,10 @@ module.exports = app => app.component('models', {
|
|
|
1130
1144
|
}
|
|
1131
1145
|
if (event.numDocs !== undefined) {
|
|
1132
1146
|
this.numDocuments = event.numDocs;
|
|
1147
|
+
this.numDocumentsError = null;
|
|
1148
|
+
}
|
|
1149
|
+
if (event.numDocsError) {
|
|
1150
|
+
this.numDocumentsError = event.numDocsError;
|
|
1133
1151
|
}
|
|
1134
1152
|
if (event.document) {
|
|
1135
1153
|
this.documents.push(event.document);
|
|
@@ -1143,6 +1161,9 @@ module.exports = app => app.component('models', {
|
|
|
1143
1161
|
if (docsCount < limit) {
|
|
1144
1162
|
this.loadedAllDocs = true;
|
|
1145
1163
|
}
|
|
1164
|
+
this.documentsError = null;
|
|
1165
|
+
} catch (err) {
|
|
1166
|
+
this.documentsError = err?.message;
|
|
1146
1167
|
} finally {
|
|
1147
1168
|
this.status = 'loaded';
|
|
1148
1169
|
}
|
|
@@ -1169,8 +1190,12 @@ module.exports = app => app.component('models', {
|
|
|
1169
1190
|
for await (const event of api.Model.getDocumentsStream(params)) {
|
|
1170
1191
|
if (event.numDocs !== undefined && !numDocsReceived) {
|
|
1171
1192
|
this.numDocuments = event.numDocs;
|
|
1193
|
+
this.numDocumentsError = null;
|
|
1172
1194
|
numDocsReceived = true;
|
|
1173
1195
|
}
|
|
1196
|
+
if (event.numDocsError) {
|
|
1197
|
+
this.numDocumentsError = event.numDocsError;
|
|
1198
|
+
}
|
|
1174
1199
|
if (event.document) {
|
|
1175
1200
|
this.documents.push(event.document);
|
|
1176
1201
|
docsCount++;
|
|
@@ -1183,6 +1208,9 @@ module.exports = app => app.component('models', {
|
|
|
1183
1208
|
if (docsCount < limit) {
|
|
1184
1209
|
this.loadedAllDocs = true;
|
|
1185
1210
|
}
|
|
1211
|
+
this.documentsError = null;
|
|
1212
|
+
} catch (err) {
|
|
1213
|
+
this.documentsError = err?.message;
|
|
1186
1214
|
} finally {
|
|
1187
1215
|
this.loadingMore = false;
|
|
1188
1216
|
this.status = 'loaded';
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<span
|
|
2
|
+
ref="trigger"
|
|
3
|
+
class="inline-flex outline-none focus-visible:rounded-md focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"
|
|
4
|
+
tabindex="0"
|
|
5
|
+
:aria-describedby="isOpen ? tooltipId : null"
|
|
6
|
+
@mouseenter="show"
|
|
7
|
+
@mouseleave="hideUnlessPinned"
|
|
8
|
+
@focusin="show"
|
|
9
|
+
@focusout="handleFocusOut"
|
|
10
|
+
@click="toggle"
|
|
11
|
+
@keydown.esc.stop="hide"
|
|
12
|
+
>
|
|
13
|
+
<slot></slot>
|
|
14
|
+
<span
|
|
15
|
+
v-show="isOpen"
|
|
16
|
+
ref="tooltip"
|
|
17
|
+
:id="tooltipId"
|
|
18
|
+
class="pointer-events-none fixed z-[1000] w-max max-w-[min(20rem,calc(100vw-1rem))] whitespace-normal rounded-md bg-gray-900 px-2.5 py-1.5 text-xs font-normal leading-4 text-white shadow-lg"
|
|
19
|
+
:style="tooltipStyle"
|
|
20
|
+
role="tooltip"
|
|
21
|
+
>
|
|
22
|
+
{{ text }}
|
|
23
|
+
<span
|
|
24
|
+
class="absolute left-1/2 h-2 w-2 -translate-x-1/2 rotate-45 bg-gray-900"
|
|
25
|
+
:class="placement === 'bottom' ? '-top-1' : '-bottom-1'"
|
|
26
|
+
></span>
|
|
27
|
+
</span>
|
|
28
|
+
</span>
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const template = require('./tooltip.html');
|
|
4
|
+
|
|
5
|
+
let nextTooltipId = 0;
|
|
6
|
+
|
|
7
|
+
module.exports = app => app.component('tooltip', {
|
|
8
|
+
props: {
|
|
9
|
+
text: {
|
|
10
|
+
type: String,
|
|
11
|
+
required: true
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
template,
|
|
15
|
+
data() {
|
|
16
|
+
return {
|
|
17
|
+
isOpen: false,
|
|
18
|
+
isPinned: false,
|
|
19
|
+
placement: 'bottom',
|
|
20
|
+
tooltipId: `tooltip-${++nextTooltipId}`,
|
|
21
|
+
tooltipStyle: {
|
|
22
|
+
visibility: 'hidden'
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
mounted() {
|
|
27
|
+
document.addEventListener('click', this.handleOutsideClick);
|
|
28
|
+
window.addEventListener('resize', this.updatePosition);
|
|
29
|
+
window.addEventListener('scroll', this.updatePosition, true);
|
|
30
|
+
},
|
|
31
|
+
beforeUnmount() {
|
|
32
|
+
document.removeEventListener('click', this.handleOutsideClick);
|
|
33
|
+
window.removeEventListener('resize', this.updatePosition);
|
|
34
|
+
window.removeEventListener('scroll', this.updatePosition, true);
|
|
35
|
+
},
|
|
36
|
+
methods: {
|
|
37
|
+
show() {
|
|
38
|
+
this.isOpen = true;
|
|
39
|
+
this.tooltipStyle = { visibility: 'hidden' };
|
|
40
|
+
this.$nextTick(this.updatePosition);
|
|
41
|
+
},
|
|
42
|
+
hide() {
|
|
43
|
+
this.isOpen = false;
|
|
44
|
+
this.isPinned = false;
|
|
45
|
+
},
|
|
46
|
+
hideUnlessPinned() {
|
|
47
|
+
if (!this.isPinned) {
|
|
48
|
+
this.isOpen = false;
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
toggle() {
|
|
52
|
+
if (this.isPinned) {
|
|
53
|
+
this.hide();
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
this.isPinned = true;
|
|
58
|
+
this.show();
|
|
59
|
+
},
|
|
60
|
+
handleFocusOut(event) {
|
|
61
|
+
if (!this.$el.contains(event.relatedTarget)) {
|
|
62
|
+
this.hideUnlessPinned();
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
handleOutsideClick(event) {
|
|
66
|
+
if (!this.$el.contains(event.target)) {
|
|
67
|
+
this.hide();
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
updatePosition() {
|
|
71
|
+
if (!this.isOpen || !this.$refs.trigger || !this.$refs.tooltip) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const gap = 8;
|
|
76
|
+
const viewportPadding = 8;
|
|
77
|
+
const triggerRect = this.$refs.trigger.getBoundingClientRect();
|
|
78
|
+
const tooltipRect = this.$refs.tooltip.getBoundingClientRect();
|
|
79
|
+
const fitsBelow = triggerRect.bottom + gap + tooltipRect.height <= window.innerHeight - viewportPadding;
|
|
80
|
+
const top = fitsBelow ?
|
|
81
|
+
triggerRect.bottom + gap :
|
|
82
|
+
Math.max(viewportPadding, triggerRect.top - gap - tooltipRect.height);
|
|
83
|
+
const centeredLeft = triggerRect.left + triggerRect.width / 2 - tooltipRect.width / 2;
|
|
84
|
+
const maxLeft = Math.max(viewportPadding, window.innerWidth - tooltipRect.width - viewportPadding);
|
|
85
|
+
const left = Math.min(Math.max(viewportPadding, centeredLeft), maxLeft);
|
|
86
|
+
|
|
87
|
+
this.placement = fitsBelow ? 'bottom' : 'top';
|
|
88
|
+
this.tooltipStyle = {
|
|
89
|
+
left: `${Math.round(left)}px`,
|
|
90
|
+
top: `${Math.round(top)}px`
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
});
|
package/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ declare module '@mongoosejs/studio' {
|
|
|
5
5
|
const express: (
|
|
6
6
|
path?: string,
|
|
7
7
|
connOrMongoose?: Connection | Mongoose,
|
|
8
|
-
options?: { apiKey?: string; bindIp?: string | string[] | null }
|
|
8
|
+
options?: { apiKey?: string; bindIp?: string | string[] | null; maxTimeMS?: number }
|
|
9
9
|
) => Promise<RequestHandler>;
|
|
10
10
|
|
|
11
11
|
const studio: {
|
package/local.js
CHANGED
|
@@ -30,7 +30,8 @@ async function run() {
|
|
|
30
30
|
_mothershipUrl: 'http://localhost:7777/.netlify/functions',
|
|
31
31
|
// apiKey: 'TACO',
|
|
32
32
|
openAIAPIKey: process.env.OPENAI_API_KEY,
|
|
33
|
-
googleGeminiAPIKey: process.env.GEMINI_API_KEY
|
|
33
|
+
googleGeminiAPIKey: process.env.GEMINI_API_KEY,
|
|
34
|
+
maxTimeMS: 1000
|
|
34
35
|
})
|
|
35
36
|
);
|
|
36
37
|
|
package/nest.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mongoosejs/studio",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.14",
|
|
4
4
|
"description": "AI-Powered Data Workspace for MongoDB: Turn your MongoDB data into dashboards, maps, and interactive workflows - powered by your Mongoose models and AI.",
|
|
5
5
|
"homepage": "https://mongoosestudio.app/",
|
|
6
6
|
"repository": {
|
package/vercel.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ declare module '@mongoosejs/studio/vercel' {
|
|
|
8
8
|
studioConnection?: Connection;
|
|
9
9
|
/** Watch the connection for changes and stream them to the Studio frontend. */
|
|
10
10
|
changeStream?: boolean;
|
|
11
|
+
/** Maximum execution time in milliseconds for MongoDB operations. */
|
|
12
|
+
maxTimeMS?: number;
|
|
11
13
|
/** Chat model name, e.g. 'gpt-4o-mini' */
|
|
12
14
|
model?: string;
|
|
13
15
|
openAIAPIKey?: string;
|