@mongoosejs/studio 0.5.8 → 0.5.10

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.
@@ -220,6 +220,16 @@ function applySuggestion(searchText, cursorPos, suggestion) {
220
220
  return null;
221
221
  }
222
222
 
223
+ function insertClosingBrace(searchText, selectionStart, selectionEnd) {
224
+ const start = typeof selectionStart === 'number' ? selectionStart : searchText.length;
225
+ const end = typeof selectionEnd === 'number' ? selectionEnd : start;
226
+ const selectedText = searchText.slice(start, end);
227
+ return {
228
+ text: searchText.slice(0, start) + `{${selectedText}}` + searchText.slice(end),
229
+ newCursorPos: start + 1 + selectedText.length
230
+ };
231
+ }
232
+
223
233
  /**
224
234
  * When the cursor is inside a Date(…) or new Date(…) argument, returns the
225
235
  * slice indices of that argument so a picker can replace it with a quoted ISO string.
@@ -301,6 +311,7 @@ module.exports = {
301
311
  getAutocompleteContext,
302
312
  getAutocompleteSuggestions,
303
313
  applySuggestion,
314
+ insertClosingBrace,
304
315
  getDatePickerInsertionRange,
305
316
  dateArgumentSliceToDatetimeLocal,
306
317
  formatDateArgumentValue,
@@ -1,9 +1,5 @@
1
1
  'use strict';
2
2
 
3
- if (typeof process === 'undefined') {
4
- global.process = { env: {} }; // To make `util` package work
5
- }
6
-
7
3
  const { version } = require('../../package.json');
8
4
  console.log(`Mongoose Studio Version ${version}`);
9
5
 
@@ -195,7 +191,7 @@ app.component('app-component', {
195
191
  <div v-else-if="!hasAPIKey || user">
196
192
  <navbar :user="user" :roles="roles" />
197
193
  <div class="view">
198
- <router-view :key="$route.fullPath" :user="user" :roles="roles" :hasAPIKey="hasAPIKey" />
194
+ <router-view :key="$route.meta.preserveStateKey || $route.fullPath" :user="user" :roles="roles" :hasAPIKey="hasAPIKey" />
199
195
  </div>
200
196
  </div>
201
197
  </div>
@@ -5,6 +5,7 @@ const {
5
5
  buildAutocompleteTrie,
6
6
  getAutocompleteSuggestions,
7
7
  applySuggestion,
8
+ insertClosingBrace,
8
9
  getDatePickerInsertionRange,
9
10
  dateArgumentSliceToDatetimeLocal,
10
11
  insertDateInDateArgument
@@ -139,6 +140,21 @@ module.exports = app => app.component('document-search', {
139
140
  });
140
141
  },
141
142
  handleKeyDown(ev) {
143
+ if (ev.key === '{') {
144
+ ev.preventDefault();
145
+ const input = this.$refs.searchInput;
146
+ const selectionStart = input ? input.selectionStart : this.searchText.length;
147
+ const selectionEnd = input ? input.selectionEnd : selectionStart;
148
+ const result = insertClosingBrace(this.searchText, selectionStart, selectionEnd);
149
+ this.searchText = result.text;
150
+ this.$nextTick(() => {
151
+ if (input) {
152
+ input.setSelectionRange(result.newCursorPos, result.newCursorPos);
153
+ }
154
+ this.updateAutocomplete();
155
+ });
156
+ return;
157
+ }
142
158
  if (this.autocompleteSuggestions.length === 0) {
143
159
  return;
144
160
  }
@@ -146,6 +146,7 @@ module.exports = app => app.component('models', {
146
146
  currentModel: null,
147
147
  modelDocumentCounts: {},
148
148
  documents: [],
149
+ allSchemaPaths: null,
149
150
  schemaPaths: [],
150
151
  filteredPaths: [],
151
152
  selectedPaths: [],
@@ -263,8 +264,9 @@ module.exports = app => app.component('models', {
263
264
  this.query = Object.assign({}, this.$route.query);
264
265
  // Keep UI mode in sync with the URL on remounts.
265
266
  this.isProjectionMenuSelected = this.$route?.query?.[PROJECTION_MODE_QUERY_KEY] === '1';
266
- const { models, readyState } = await api.Model.listModels();
267
+ const { models, modelSchemaPaths, readyState } = await api.Model.listModels();
267
268
  this.models = models;
269
+ this.allSchemaPaths = modelSchemaPaths;
268
270
  await this.loadModelCounts();
269
271
  if (this.currentModel == null && this.models.length > 0) {
270
272
  this.currentModel = this.models[0];
@@ -275,6 +277,29 @@ module.exports = app => app.component('models', {
275
277
  if (readyState === 0) {
276
278
  this.error = 'No models found and Mongoose is not connected. Check our documentation for more information.';
277
279
  }
280
+ } else {
281
+ this._refreshSidebar = setInterval(async() => {
282
+ try {
283
+ const { models, modelSchemaPaths, readyState } = await api.Model.listModels();
284
+ this.models = models;
285
+ this.allSchemaPaths = modelSchemaPaths;
286
+ await this.loadModelCounts();
287
+ if (this.currentModel) {
288
+ const schemaPaths = this.allSchemaPaths?.[this.currentModel] ?? {};
289
+ this.schemaPaths = Object.keys(schemaPaths).sort((k1, k2) => {
290
+ if (k1 === '_id' && k2 !== '_id') {
291
+ return -1;
292
+ }
293
+ if (k1 !== '_id' && k2 === '_id') {
294
+ return 1;
295
+ }
296
+ return 0;
297
+ }).map(key => schemaPaths[key]);
298
+ }
299
+ } catch (e) {
300
+ console.error(e);
301
+ }
302
+ }, 20000);
278
303
  }
279
304
 
280
305
  await this.initSearchFromUrl();
@@ -285,25 +310,33 @@ module.exports = app => app.component('models', {
285
310
  this.$nextTick(() => {
286
311
  const focusIntent = takeModelsRemountFocusIntent();
287
312
  if (focusIntent === 'documentSearch') {
288
- const ds = this.$refs.documentSearch;
289
- if (ds && typeof ds.focusInput === 'function') {
290
- ds.focusInput();
291
- }
313
+ this.$refs.documentSearch?.focusInput();
292
314
  return;
293
315
  }
294
316
  if (focusIntent === 'projection' && this.isProjectionMenuSelected) {
295
- const projectionSearch = this.$refs.projectionSearch;
296
- if (projectionSearch && typeof projectionSearch.focusInput === 'function') {
297
- projectionSearch.focusInput();
298
- }
317
+ this.$refs.projectionSearch?.focusInput();
299
318
  }
300
319
  });
301
320
  },
321
+ unmounted() {
322
+ clearInterval(this._refreshSidebar);
323
+ },
302
324
  watch: {
303
325
  model(newModel) {
304
326
  if (newModel !== this.currentModel) {
305
327
  this.currentModel = newModel;
306
328
  if (this.currentModel != null) {
329
+ this.$refs.documentSearch?.focusInput();
330
+ const schemaPaths = this.allSchemaPaths?.[newModel] ?? {};
331
+ this.schemaPaths = Object.keys(schemaPaths).sort((k1, k2) => {
332
+ if (k1 === '_id' && k2 !== '_id') {
333
+ return -1;
334
+ }
335
+ if (k1 !== '_id' && k2 === '_id') {
336
+ return 1;
337
+ }
338
+ return 0;
339
+ }).map(key => schemaPaths[key]);
307
340
  this.initSearchFromUrl();
308
341
  }
309
342
  }
@@ -747,6 +780,8 @@ module.exports = app => app.component('models', {
747
780
  this.query.sortKey = sortKey;
748
781
  this.query.sortDirection = sortDirection;
749
782
  delete this.query.sort;
783
+ } else {
784
+ this.sortBy = {};
750
785
  }
751
786
  if (this.currentModel != null) {
752
787
  await this.getDocuments();
@@ -762,6 +797,8 @@ module.exports = app => app.component('models', {
762
797
  this.selectedPaths = [...this.filteredPaths];
763
798
  this.syncProjectionInputFromQueryOrPaths();
764
799
  }
800
+ } else {
801
+ this.selectedPaths = [];
765
802
  }
766
803
  this.status = 'loaded';
767
804
 
@@ -1043,7 +1080,6 @@ module.exports = app => app.component('models', {
1043
1080
 
1044
1081
  // Clear previous data
1045
1082
  this.documents = [];
1046
- this.schemaPaths = [];
1047
1083
  this.numDocuments = null;
1048
1084
  this.loadedAllDocs = false;
1049
1085
  this.lastSelectedIndex = null;
@@ -33,7 +33,8 @@ module.exports = {
33
33
  name: 'model',
34
34
  component: 'models',
35
35
  meta: {
36
- authorized: false
36
+ authorized: false,
37
+ preserveStateKey: 'model'
37
38
  }
38
39
  },
39
40
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mongoosejs/studio",
3
- "version": "0.5.8",
3
+ "version": "0.5.10",
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": {