@mongoosejs/studio 0.3.5 → 0.3.6

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.
@@ -719,10 +719,6 @@ video {
719
719
  right: 0.5rem;
720
720
  }
721
721
 
722
- .right-4 {
723
- right: 1rem;
724
- }
725
-
726
722
  .top-0 {
727
723
  top: 0px;
728
724
  }
@@ -3328,6 +3324,11 @@ video {
3328
3324
  color: var(--color-primary);
3329
3325
  }
3330
3326
 
3327
+ .hover\:text-red-900:hover {
3328
+ --tw-text-opacity: 1;
3329
+ color: rgb(127 29 29 / var(--tw-text-opacity));
3330
+ }
3331
+
3331
3332
  .hover\:text-slate-700:hover {
3332
3333
  --tw-text-opacity: 1;
3333
3334
  color: rgb(51 65 85 / var(--tw-text-opacity));
@@ -10,17 +10,6 @@
10
10
  <path stroke-linecap="round" stroke-linejoin="round" d="m5.25 4.5 7.5 7.5-7.5 7.5m6-15 7.5 7.5-7.5 7.5" />
11
11
  </svg>
12
12
  </button>
13
- <button
14
- class="fixed top-[65px] right-4 z-10 p-2 rounded-md shadow bg-surface"
15
- :class="hasWorkspace ? 'text-content-secondary hover:bg-muted' : 'text-gray-300 cursor-not-allowed bg-page'"
16
- @click="toggleShareThread"
17
- :disabled="!hasWorkspace || !chatThreadId || sharingThread"
18
- aria-label="Share thread with workspace"
19
- title="Share thread with workspace"
20
- >
21
- <svg v-if="hasWorkspace" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7a2.48 2.48 0 0 0 0-1.39l7.02-4.11a2.5 2.5 0 1 0-.87-1.37L8.04 9.94a2.5 2.5 0 1 0 0 4.12l7.12 4.16a2.5 2.5 0 1 0 .84-1.34l-7.05-4.12c-.04-.02-.08-.05-.11-.07a2.48 2.48 0 0 0 0-1.39c.03-.02.07-.04.11-.07l7.11-4.16c.52.47 1.2.76 1.94.76a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0-1.94.94L7.97 8.43a2.5 2.5 0 1 0 0 7.14l9.09 5.3c.52-.47 1.2-.76 1.94-.76a2.5 2.5 0 1 0 0-5z"/></svg>
22
- <svg v-else xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"><path d="M12 1a5 5 0 00-5 5v3H6a2 2 0 00-2 2v9a2 2 0 002 2h12a2 2 0 002-2v-9a2 2 0 00-2-2h-1V6a5 5 0 00-5-5zm-3 8V6a3 3 0 016 0v3H9zm9 2v9H6v-9h12z"/></svg>
23
- </button>
24
13
  <!-- Sidebar: Chat Threads -->
25
14
  <aside
26
15
  class="bg-page border-r overflow-hidden h-full transition-all duration-300 ease-in-out z-20 w-64 fixed lg:relative shrink-0 flex flex-col"
@@ -84,6 +73,18 @@
84
73
  <path stroke-linecap="round" stroke-linejoin="round" d="m18.75 4.5-7.5 7.5 7.5 7.5m-6-15L5.25 12l7.5 7.5" />
85
74
  </svg>
86
75
  </button>
76
+ <button
77
+ type="button"
78
+ @click="hasWorkspace ? toggleShareThread() : (showProUpgradeModal = true)"
79
+ class="rounded p-1.5"
80
+ :class="hasWorkspace && chatThreadId ? 'text-gray-400 hover:text-gray-600 hover:bg-muted' : !hasWorkspace ? 'text-gray-400 hover:text-gray-600 hover:bg-muted' : 'text-gray-300 cursor-not-allowed'"
81
+ :disabled="hasWorkspace && !chatThreadId"
82
+ aria-label="Share thread with workspace"
83
+ :title="'Share thread with workspace' + (!hasWorkspace ? ' (requires a pro workspace)' : !chatThreadId ? ': Open a thread first!' : '')"
84
+ >
85
+ <svg v-if="hasWorkspace" xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7a2.48 2.48 0 0 0 0-1.39l7.02-4.11a2.5 2.5 0 1 0-.87-1.37L8.04 9.94a2.5 2.5 0 1 0 0 4.12l7.12 4.16a2.5 2.5 0 1 0 .84-1.34l-7.05-4.12c-.04-.02-.08-.05-.11-.07a2.48 2.48 0 0 0 0-1.39c.03-.02.07-.04.11-.07l7.11-4.16c.52.47 1.2.76 1.94.76a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0-1.94.94L7.97 8.43a2.5 2.5 0 1 0 0 7.14l9.09 5.3c.52-.47 1.2-.76 1.94-.76a2.5 2.5 0 1 0 0-5z"/></svg>
86
+ <svg v-else xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24"><path d="M12 1a5 5 0 00-5 5v3H6a2 2 0 00-2 2v9a2 2 0 002 2h12a2 2 0 002-2v-9a2 2 0 00-2-2h-1V6a5 5 0 00-5-5zm-3 8V6a3 3 0 016 0v3H9zm9 2v9H6v-9h12z"/></svg>
87
+ </button>
87
88
  </div>
88
89
  </aside>
89
90
 
@@ -118,7 +119,7 @@
118
119
  @input="adjustTextareaHeight"
119
120
  @keydown.enter.exact.prevent="handleEnter"
120
121
  ></textarea>
121
- <button class="bg-blue-600 text-white px-4 h-[42px] rounded disabled:bg-gray-600" :disabled="sendingMessage">
122
+ <button class="bg-primary hover:bg-primary-hover text-white px-4 h-[42px] rounded disabled:bg-gray-600" :disabled="sendingMessage">
122
123
  <svg v-if="sendingMessage" style="height: 1em" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
123
124
  <g>
124
125
  <circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="2" opacity="0.3" />
@@ -132,4 +133,10 @@
132
133
  </form>
133
134
  </div>
134
135
  </main>
136
+
137
+ <pro-upgrade-modal
138
+ :show="showProUpgradeModal"
139
+ feature-description="Sharing threads lets you collaborate with your team by sharing chat threads within your workspace."
140
+ @close="showProUpgradeModal = false"
141
+ ></pro-upgrade-modal>
135
142
  </div>
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  const api = require('../api');
4
+ const getCurrentDateTimeContext = require('../getCurrentDateTimeContext');
4
5
  const template = require('./chat.html');
5
6
 
6
7
  module.exports = {
@@ -15,7 +16,8 @@ module.exports = {
15
16
  chatMessages: [],
16
17
  hideSidebar: null,
17
18
  sharingThread: false,
18
- threadSearch: ''
19
+ threadSearch: '',
20
+ showProUpgradeModal: false
19
21
  }),
20
22
  methods: {
21
23
  async sendMessage() {
@@ -44,7 +46,11 @@ module.exports = {
44
46
  }
45
47
  });
46
48
 
47
- const params = { chatThreadId: this.chatThreadId, content };
49
+ const params = {
50
+ chatThreadId: this.chatThreadId,
51
+ content,
52
+ currentDateTime: getCurrentDateTimeContext()
53
+ };
48
54
  let userChatMessage = null;
49
55
  let assistantChatMessage = null;
50
56
  for await (const event of api.ChatThread.streamChatMessage(params)) {
@@ -151,7 +157,7 @@ module.exports = {
151
157
  },
152
158
  async toggleShareThread() {
153
159
  if (!this.chatThreadId || !this.hasWorkspace) {
154
- return;
160
+ throw new Error('Cannot share thread: chatThreadId or hasWorkspace is missing');
155
161
  }
156
162
  this.sharingThread = true;
157
163
  try {
@@ -11,6 +11,7 @@ const ObjectId = new Proxy(BSON.ObjectId, {
11
11
  });
12
12
 
13
13
  const appendCSS = require('../appendCSS');
14
+ const getCurrentDateTimeContext = require('../getCurrentDateTimeContext');
14
15
 
15
16
  appendCSS(require('./create-document.css'));
16
17
 
@@ -49,7 +50,8 @@ module.exports = app => app.component('create-document', {
49
50
  for await (const event of api.Model.streamChatMessage({
50
51
  model: this.currentModel,
51
52
  content: prompt,
52
- documentData: this.aiOriginalDocument
53
+ documentData: this.aiOriginalDocument,
54
+ currentDateTime: getCurrentDateTimeContext()
53
55
  })) {
54
56
  if (event?.textPart) {
55
57
  this.aiSuggestion += event.textPart;
@@ -0,0 +1,17 @@
1
+ 'use strict';
2
+
3
+ const time = require('time-commando');
4
+
5
+ module.exports = function getCurrentDateTimeContext() {
6
+ const date = time.now();
7
+ const components = [
8
+ date.getFullYear(),
9
+ date.getMonth() + 1,
10
+ date.getDate(),
11
+ date.getHours(),
12
+ date.getMinutes(),
13
+ date.getSeconds()
14
+ ].map(num => num.toString().padStart(2, '0'));
15
+ const [yyyy, mm, dd, hh, mi, ss] = components;
16
+ return `${yyyy}-${mm}-${dd}T${hh}:${mi}:${ss}`;
17
+ };
@@ -28,13 +28,6 @@ module.exports = app => app.component('list-json', {
28
28
  topLevelExpanded: false
29
29
  };
30
30
  },
31
- watch: {
32
- value: {
33
- handler() {
34
- this.resetCollapse();
35
- }
36
- }
37
- },
38
31
  created() {
39
32
  this.resetCollapse();
40
33
  for (const field of this.expandedFields) {
@@ -7,5 +7,29 @@ appendCSS(require('./modal.css'));
7
7
 
8
8
  module.exports = app => app.component('modal', {
9
9
  template,
10
- props: ['containerClass']
10
+ props: ['containerClass'],
11
+ mounted() {
12
+ window.addEventListener('keydown', this.onEscape);
13
+ },
14
+ beforeUnmount() {
15
+ window.removeEventListener('keydown', this.onEscape);
16
+ },
17
+ methods: {
18
+ onEscape(event) {
19
+ if (event.key !== 'Escape') {
20
+ return;
21
+ }
22
+
23
+ const modalMasks = Array.from(document.querySelectorAll('.modal-mask'));
24
+ const currentMask = this.$el?.classList?.contains('modal-mask') ? this.$el : this.$el?.querySelector('.modal-mask') || this.$el;
25
+ const isTopMostModal = modalMasks.length > 0 && modalMasks[modalMasks.length - 1] === currentMask;
26
+
27
+ if (!isTopMostModal) {
28
+ return;
29
+ }
30
+
31
+ const closeButton = currentMask.querySelector('.modal-exit, [data-modal-close]');
32
+ closeButton?.click();
33
+ }
34
+ }
11
35
  });
@@ -41,6 +41,9 @@ module.exports = app => app.component('document-search', {
41
41
  created() {
42
42
  this.buildAutocompleteTrie();
43
43
  },
44
+ mounted() {
45
+ this.$refs.searchInput.focus();
46
+ },
44
47
  methods: {
45
48
  emitSearch() {
46
49
  this.$emit('input', this.searchText);
@@ -280,6 +280,24 @@
280
280
  <div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 relative m-4 rounded-md" role="alert">
281
281
  <span class="block font-bold">Error</span>
282
282
  <span class="block">{{ error }}</span>
283
+ <span class="block mt-2">
284
+ Need help?
285
+ <a
286
+ href="https://discord.gg/P3YCfKYxpy"
287
+ target="_blank"
288
+ rel="noopener noreferrer"
289
+ class="underline font-medium text-red-800 hover:text-red-900"
290
+ >Ask in Discord</a>
291
+ or
292
+ <a
293
+ href="https://github.com/mongoosejs/studio/issues"
294
+ target="_blank"
295
+ rel="noopener noreferrer"
296
+ class="underline font-medium text-red-800 hover:text-red-900"
297
+ >
298
+ open a GitHub issue.
299
+ </a>
300
+ </span>
283
301
  </div>
284
302
  </div>
285
303
  <div v-else-if="outputType === 'table'" class="flex-1 min-h-0 flex flex-col overflow-hidden">
@@ -442,13 +460,15 @@
442
460
  selectedDocuments.some(x => x._id.toString() === document._id.toString()) ? 'bg-blue-200' : 'hover:shadow-sm hover:border-slate-300 bg-surface'
443
461
  ]"
444
462
  >
445
- <button
446
- type="button"
463
+ <router-link
447
464
  class="absolute top-2 right-2 z-10 inline-flex items-center rounded bg-primary px-2 py-1 text-xs font-semibold text-primary-text shadow-sm transition-opacity duration-150 opacity-0 group-hover:opacity-100 focus-visible:opacity-100 hover:bg-primary-hover focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary"
448
- @click.stop="openDocument(document)"
465
+ :to="{ path: '/model/' + currentModel + '/document/' + document._id, query: $route.query }"
466
+ target="_blank"
467
+ rel="noopener noreferrer"
468
+ @click.stop
449
469
  >
450
- Open this Document
451
- </button>
470
+ Open
471
+ </router-link>
452
472
  <list-json :value="filterDocument(document)" :references="referenceMap">
453
473
  </list-json>
454
474
  </div>
@@ -261,7 +261,7 @@ module.exports = app => app.component('models', {
261
261
  computed: {
262
262
  referenceMap() {
263
263
  const map = {};
264
- for (const path of this.filteredPaths) {
264
+ for (const path of this.schemaPaths) {
265
265
  if (path?.ref) {
266
266
  map[path.path] = path.ref;
267
267
  }
@@ -42,7 +42,8 @@
42
42
  </svg>
43
43
  </span>
44
44
  <a
45
- :href="hasTaskVisualizer"
45
+ v-if="hasTaskVisualizer"
46
+ href="#/tasks"
46
47
  class="inline-flex items-center px-1 border-b-2 text-sm font-medium"
47
48
  :class="taskView ? 'text-content border-primary' : 'border-transparent text-content-tertiary hover:text-content'">
48
49
  Tasks
@@ -210,6 +211,10 @@
210
211
  <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" />
211
212
  </svg>
212
213
  </span>
214
+ <a v-if="hasTaskVisualizer"
215
+ href="#/tasks"
216
+ class="block px-3 py-2 rounded-md text-base font-medium"
217
+ :class="taskView ? 'text-content bg-primary-subtle' : 'text-content-secondary hover:bg-muted'">Tasks</a>
213
218
  <div v-if="!user && hasAPIKey" class="mt-4">
214
219
  <button
215
220
  type="button"
@@ -67,12 +67,7 @@ module.exports = app => app.component('navbar', {
67
67
  return this.roles && this.roles[0] === 'dashboards' ? 'dashboards' : 'root';
68
68
  },
69
69
  hasTaskVisualizer() {
70
- if (window.MONGOOSE_STUDIO_CONFIG.enableTaskVisualizer) {
71
- return '#/tasks';
72
- } else {
73
- return 'https://www.npmjs.com/package/@mongoosejs/task';
74
- }
75
-
70
+ return !!window.MONGOOSE_STUDIO_CONFIG.enableTaskVisualizer;
76
71
  }
77
72
  },
78
73
  methods: {
@@ -0,0 +1,38 @@
1
+ <modal v-if="show" containerClass="!max-w-md">
2
+ <template v-slot:body>
3
+ <div @keydown.esc="$emit('close')" tabindex="0" ref="overlay">
4
+ <div class="flex items-center justify-between mb-4">
5
+ <h3 class="text-lg font-semibold text-gray-900">Pro Feature</h3>
6
+ <button
7
+ type="button"
8
+ @click="$emit('close')"
9
+ class="text-gray-400 hover:text-gray-600 cursor-pointer"
10
+ aria-label="Close"
11
+ >
12
+ <svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
13
+ <path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12" />
14
+ </svg>
15
+ </button>
16
+ </div>
17
+ <p class="text-gray-600 mb-6">
18
+ {{ featureDescription }} Upgrade to a Pro workspace to unlock this feature.
19
+ </p>
20
+ <div class="flex justify-end gap-3">
21
+ <button
22
+ type="button"
23
+ @click="$emit('close')"
24
+ class="px-4 py-2 text-sm font-medium text-gray-700 bg-gray-100 rounded-md hover:bg-gray-200 cursor-pointer"
25
+ >
26
+ Cancel
27
+ </button>
28
+ <a
29
+ href="https://studio.mongoosejs.io/pro"
30
+ target="_blank"
31
+ class="px-4 py-2 text-sm font-medium text-white bg-primary rounded-md hover:bg-primary-hover inline-flex items-center"
32
+ >
33
+ Upgrade to Pro
34
+ </a>
35
+ </div>
36
+ </div>
37
+ </template>
38
+ </modal>
@@ -0,0 +1,23 @@
1
+ 'use strict';
2
+
3
+ const template = require('./pro-upgrade-modal.html');
4
+
5
+ module.exports = app => app.component('pro-upgrade-modal', {
6
+ template,
7
+ props: {
8
+ show: { type: Boolean, default: false },
9
+ featureDescription: { type: String, default: 'This feature is available on the Pro plan.' }
10
+ },
11
+ emits: ['close'],
12
+ watch: {
13
+ show(val) {
14
+ if (val) {
15
+ this.$nextTick(() => {
16
+ if (this.$refs.overlay) {
17
+ this.$refs.overlay.focus();
18
+ }
19
+ });
20
+ }
21
+ }
22
+ }
23
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mongoosejs/studio",
3
- "version": "0.3.5",
3
+ "version": "0.3.6",
4
4
  "description": "A Mongoose-native MongoDB UI with schema-aware autocomplete, AI-assisted queries, and dashboards that understand your models - not just your data.",
5
5
  "homepage": "https://mongoosestudio.app/",
6
6
  "repository": {
@@ -25,6 +25,7 @@
25
25
  "vue": "3.x",
26
26
  "vue-toastification": "^2.0.0-rc.5",
27
27
  "webpack": "5.x",
28
+ "time-commando": "1.0.1",
28
29
  "xss": "^1.0.15"
29
30
  },
30
31
  "peerDependencies": {