@mongoosejs/studio 0.5.0 → 0.5.2
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/Dashboard/getDashboard.js +29 -2
- package/backend/actions/Model/dropCollection.js +29 -0
- package/backend/actions/Model/exportQueryResults.js +2 -4
- package/backend/actions/Model/index.js +1 -0
- package/backend/authorize.js +1 -0
- package/backend/db/dashboardSchema.js +0 -32
- package/backend/index.js +1 -1
- package/backend/integrations/callLLM.js +5 -20
- package/backend/integrations/streamLLM.js +5 -24
- package/backend/next.js +5 -1
- package/backend/sandbox/createSandbox.js +6 -0
- package/backend/vercel.js +3 -0
- package/frontend/index.js +2 -1
- package/frontend/public/app.js +272 -214
- package/frontend/public/tw.css +29 -0
- package/frontend/src/api.js +96 -164
- package/frontend/src/chat/chat.html +1 -1
- package/frontend/src/document/confirm-changes/confirm-changes.js +20 -0
- package/frontend/src/index.js +5 -0
- package/frontend/src/json-node/json-node.html +41 -39
- package/frontend/src/json-node/json-node.js +38 -0
- package/frontend/src/list-json/list-json.html +3 -2
- package/frontend/src/list-json/list-json.js +5 -0
- package/frontend/src/models/models.html +45 -5
- package/frontend/src/models/models.js +26 -0
- package/frontend/src/navbar/navbar.html +19 -3
- package/frontend/src/navbar/navbar.js +6 -0
- package/package.json +1 -1
|
@@ -117,7 +117,7 @@
|
|
|
117
117
|
@click="stagingSelect"
|
|
118
118
|
type="button"
|
|
119
119
|
:class="{
|
|
120
|
-
'bg-gray-700 ring-inset ring-2 ring-gray-300 hover:bg-gray-
|
|
120
|
+
'bg-gray-700 ring-inset ring-2 ring-gray-300 text-white hover:bg-gray-600': selectMultiple,
|
|
121
121
|
'bg-primary hover:bg-primary-hover text-primary-text': !selectMultiple
|
|
122
122
|
}"
|
|
123
123
|
class="rounded px-2 py-2 text-sm font-semibold shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary"
|
|
@@ -190,6 +190,13 @@
|
|
|
190
190
|
>
|
|
191
191
|
Find oldest document
|
|
192
192
|
</button>
|
|
193
|
+
<button
|
|
194
|
+
@click="openDropCollectionModal"
|
|
195
|
+
type="button"
|
|
196
|
+
class="block w-full px-4 py-2 text-left text-sm text-red-700 hover:bg-red-50"
|
|
197
|
+
>
|
|
198
|
+
Drop Collection
|
|
199
|
+
</button>
|
|
193
200
|
<button
|
|
194
201
|
@click="toggleRowNumbers()"
|
|
195
202
|
type="button"
|
|
@@ -404,8 +411,10 @@
|
|
|
404
411
|
v-for="(document, docIndex) in documents"
|
|
405
412
|
:key="document._id"
|
|
406
413
|
@click="handleDocumentClick(document, $event)"
|
|
407
|
-
class="border-b border-edge cursor-pointer transition-colors
|
|
408
|
-
:class="
|
|
414
|
+
class="border-b border-edge cursor-pointer transition-colors"
|
|
415
|
+
:class="selectedDocuments.some(x => x._id.toString() === document._id.toString())
|
|
416
|
+
? 'bg-blue-200'
|
|
417
|
+
: 'hover:bg-muted'"
|
|
409
418
|
>
|
|
410
419
|
<td v-if="showRowNumbers" class="px-3 py-2 border-r border-edge align-top text-content-tertiary whitespace-nowrap">
|
|
411
420
|
{{ docIndex + 1 }}
|
|
@@ -448,13 +457,13 @@
|
|
|
448
457
|
No documents to show. Use Projection in the menu to choose columns.
|
|
449
458
|
</p>
|
|
450
459
|
</div>
|
|
451
|
-
<div v-else-if="outputType === 'json'" class="flex flex-col space-y-2 p-1 mt-1">
|
|
460
|
+
<div v-else-if="outputType === 'json'" class="flex min-w-0 flex-col space-y-2 p-1 mt-1">
|
|
452
461
|
<div
|
|
453
462
|
v-for="document in documents"
|
|
454
463
|
:key="document._id"
|
|
455
464
|
@click="handleDocumentContainerClick(document, $event)"
|
|
456
465
|
:class="[
|
|
457
|
-
'group relative transition-colors rounded-md border border-slate-100',
|
|
466
|
+
'group relative min-w-0 max-w-full transition-colors rounded-md border border-slate-100',
|
|
458
467
|
selectedDocuments.some(x => x._id.toString() === document._id.toString()) ? 'bg-blue-200' : 'hover:shadow-sm hover:border-slate-300 bg-surface'
|
|
459
468
|
]"
|
|
460
469
|
>
|
|
@@ -609,6 +618,37 @@
|
|
|
609
618
|
</div>
|
|
610
619
|
</template>
|
|
611
620
|
</modal>
|
|
621
|
+
<modal v-if="shouldShowDropCollectionModal">
|
|
622
|
+
<template v-slot:body>
|
|
623
|
+
<div class="modal-exit" @click="shouldShowDropCollectionModal = false">×</div>
|
|
624
|
+
<h2 class="text-xl font-bold mb-3">Are you sure?</h2>
|
|
625
|
+
<p class="text-content-secondary mb-3">
|
|
626
|
+
This will permanently drop the <span class="font-semibold">{{ currentModel }}</span> collection and all its documents.
|
|
627
|
+
</p>
|
|
628
|
+
<div v-if="requiresDropCollectionNameConfirmation()" class="rounded border border-red-300 bg-red-50 px-3 py-2 mb-4">
|
|
629
|
+
<p class="text-sm font-semibold text-red-700">Danger zone</p>
|
|
630
|
+
<p class="text-sm text-red-700 mb-2">Type <span class="font-mono font-semibold">{{ currentModel }}</span> to confirm.</p>
|
|
631
|
+
<input
|
|
632
|
+
v-model="dropCollectionConfirmName"
|
|
633
|
+
type="text"
|
|
634
|
+
:placeholder="currentModel"
|
|
635
|
+
class="w-full rounded border border-red-300 px-2 py-1 text-sm focus:outline-none focus:ring-2 focus:ring-red-200"
|
|
636
|
+
/>
|
|
637
|
+
</div>
|
|
638
|
+
<div class="flex gap-4">
|
|
639
|
+
<async-button
|
|
640
|
+
@click="dropCollection"
|
|
641
|
+
:disabled="requiresDropCollectionNameConfirmation() && dropCollectionConfirmName !== currentModel"
|
|
642
|
+
class="rounded bg-red-600 px-2 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
643
|
+
>
|
|
644
|
+
Drop Collection
|
|
645
|
+
</async-button>
|
|
646
|
+
<button @click="shouldShowDropCollectionModal = false" class="rounded bg-gray-400 px-2 py-2 text-sm font-semibold text-white shadow-sm hover:bg-page0">
|
|
647
|
+
Cancel
|
|
648
|
+
</button>
|
|
649
|
+
</div>
|
|
650
|
+
</template>
|
|
651
|
+
</modal>
|
|
612
652
|
<model-switcher
|
|
613
653
|
:show="showModelSwitcher"
|
|
614
654
|
:models="models"
|
|
@@ -168,6 +168,8 @@ module.exports = app => app.component('models', {
|
|
|
168
168
|
showAddFieldDropdown: false,
|
|
169
169
|
shouldShowIndexModal: false,
|
|
170
170
|
shouldShowCollectionInfoModal: false,
|
|
171
|
+
shouldShowDropCollectionModal: false,
|
|
172
|
+
dropCollectionConfirmName: '',
|
|
171
173
|
shouldShowUpdateMultipleModal: false,
|
|
172
174
|
shouldShowDeleteMultipleModal: false,
|
|
173
175
|
shouldExport: {},
|
|
@@ -923,6 +925,30 @@ module.exports = app => app.component('models', {
|
|
|
923
925
|
const { info } = await api.Model.getCollectionInfo({ model: this.currentModel });
|
|
924
926
|
this.collectionInfo = info;
|
|
925
927
|
},
|
|
928
|
+
openDropCollectionModal() {
|
|
929
|
+
this.closeActionsMenu();
|
|
930
|
+
this.dropCollectionConfirmName = '';
|
|
931
|
+
this.shouldShowDropCollectionModal = true;
|
|
932
|
+
},
|
|
933
|
+
requiresDropCollectionNameConfirmation() {
|
|
934
|
+
const nodeEnv = window?.state?.nodeEnv;
|
|
935
|
+
return nodeEnv !== 'local';
|
|
936
|
+
},
|
|
937
|
+
async dropCollection() {
|
|
938
|
+
if (this.requiresDropCollectionNameConfirmation() && this.dropCollectionConfirmName !== this.currentModel) {
|
|
939
|
+
this.$toast.error('Please type the collection name exactly to confirm.');
|
|
940
|
+
return;
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
await api.Model.dropCollection({ model: this.currentModel });
|
|
944
|
+
this.shouldShowDropCollectionModal = false;
|
|
945
|
+
this.dropCollectionConfirmName = '';
|
|
946
|
+
this.documents = [];
|
|
947
|
+
this.numDocuments = 0;
|
|
948
|
+
this.selectedDocuments = [];
|
|
949
|
+
this.$toast.success(`Collection ${this.currentModel} dropped.`);
|
|
950
|
+
await this.getDocuments();
|
|
951
|
+
},
|
|
926
952
|
async findOldestDocument() {
|
|
927
953
|
this.closeActionsMenu();
|
|
928
954
|
const { docs } = await api.Model.getDocuments({
|
|
@@ -31,10 +31,18 @@
|
|
|
31
31
|
<path fill-rule="evenodd" d="M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z" clip-rule="evenodd" />
|
|
32
32
|
</svg>
|
|
33
33
|
</span>
|
|
34
|
-
<a v-if="hasAccess(roles, 'chat')"
|
|
34
|
+
<a v-if="hasAccess(roles, 'chat') && hasLLMAPIKey"
|
|
35
35
|
href="#/chat"
|
|
36
36
|
class="inline-flex items-center px-1 border-b-2 text-sm font-medium"
|
|
37
37
|
:class="chatView ? 'text-content border-primary' : 'border-transparent text-content-tertiary hover:text-content'">Chat</a>
|
|
38
|
+
<span v-else-if="hasAccess(roles, 'chat') && !hasLLMAPIKey"
|
|
39
|
+
class="inline-flex items-center px-1 border-b-2 border-transparent text-sm font-medium text-gray-300 cursor-not-allowed"
|
|
40
|
+
:title="chatDisabledReason">
|
|
41
|
+
Chat
|
|
42
|
+
<svg class="h-4 w-4 ml-1" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
|
43
|
+
<path fill-rule="evenodd" d="M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z" clip-rule="evenodd" />
|
|
44
|
+
</svg>
|
|
45
|
+
</span>
|
|
38
46
|
<span v-else class="inline-flex items-center px-1 border-b-2 border-transparent text-sm font-medium text-gray-300 cursor-not-allowed">
|
|
39
47
|
Chat
|
|
40
48
|
<svg class="h-4 w-4 ml-1" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
|
@@ -154,7 +162,7 @@
|
|
|
154
162
|
<!-- Mobile menu mask -->
|
|
155
163
|
<div id="mobile-menu-mask" class="fixed inset-0 bg-black bg-opacity-40 z-40 hidden"></div>
|
|
156
164
|
<!-- Mobile menu drawer -->
|
|
157
|
-
<div id="mobile-menu" style="z-index:
|
|
165
|
+
<div id="mobile-menu" style="z-index: 100000" class="fixed inset-0 bg-page shadow-lg transform translate-x-full transition-transform duration-200 ease-in-out flex flex-col">
|
|
158
166
|
<div class="flex items-center justify-between px-4 !h-[55px] border-b border-edge">
|
|
159
167
|
<router-link :to="{ name: defaultRoute }">
|
|
160
168
|
<svg xmlns="http://www.w3.org/2000/svg" class="h-[32px] text-primary" viewBox="0 0 250 250" fill="currentColor" aria-label="Mongoose Studio Logo">
|
|
@@ -201,10 +209,18 @@
|
|
|
201
209
|
<path fill-rule="evenodd" d="M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z" clip-rule="evenodd" />
|
|
202
210
|
</svg>
|
|
203
211
|
</span>
|
|
204
|
-
<a v-if="hasAccess(roles, 'chat')"
|
|
212
|
+
<a v-if="hasAccess(roles, 'chat') && hasLLMAPIKey"
|
|
205
213
|
href="#/chat"
|
|
206
214
|
class="block px-3 py-2 rounded-md text-base font-medium"
|
|
207
215
|
:class="chatView ? 'text-content bg-primary-subtle' : 'text-content-secondary hover:bg-muted'">Chat</a>
|
|
216
|
+
<span v-else-if="hasAccess(roles, 'chat') && !hasLLMAPIKey"
|
|
217
|
+
class="block px-3 py-2 rounded-md text-base font-medium text-gray-300 cursor-not-allowed"
|
|
218
|
+
:title="chatDisabledReason">
|
|
219
|
+
Chat
|
|
220
|
+
<svg class="h-4 w-4 ml-1 inline" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
|
221
|
+
<path fill-rule="evenodd" d="M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z" clip-rule="evenodd" />
|
|
222
|
+
</svg>
|
|
223
|
+
</span>
|
|
208
224
|
<span v-else class="block px-3 py-2 rounded-md text-base font-medium text-gray-300 cursor-not-allowed">
|
|
209
225
|
Chat
|
|
210
226
|
<svg class="h-4 w-4 ml-1 inline" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
|
@@ -68,6 +68,12 @@ module.exports = app => app.component('navbar', {
|
|
|
68
68
|
},
|
|
69
69
|
hasTaskVisualizer() {
|
|
70
70
|
return !!window.MONGOOSE_STUDIO_CONFIG.enableTaskVisualizer;
|
|
71
|
+
},
|
|
72
|
+
hasLLMAPIKey() {
|
|
73
|
+
return !!window.MONGOOSE_STUDIO_CONFIG.hasLLMAPIKey;
|
|
74
|
+
},
|
|
75
|
+
chatDisabledReason() {
|
|
76
|
+
return 'Chat requires an Anthropic, Gemini, or OpenAI API key.';
|
|
71
77
|
}
|
|
72
78
|
},
|
|
73
79
|
methods: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mongoosejs/studio",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2",
|
|
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": {
|