@mongoosejs/studio 0.2.13 → 0.3.0

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.
Files changed (81) hide show
  1. package/backend/actions/ChatMessage/executeScript.js +5 -1
  2. package/backend/actions/ChatThread/createChatMessage.js +2 -1
  3. package/backend/actions/ChatThread/streamChatMessage.js +2 -2
  4. package/eslint.config.js +4 -1
  5. package/frontend/public/app.js +24642 -543
  6. package/frontend/public/dark-theme.css +365 -0
  7. package/frontend/public/images/mongoose-studio.svg +4 -0
  8. package/frontend/public/index.html +21 -1
  9. package/frontend/public/style.css +5 -7
  10. package/frontend/public/theme-variables.css +294 -0
  11. package/frontend/public/tw.css +305 -252
  12. package/frontend/src/ace-editor/ace-editor.html +4 -0
  13. package/frontend/src/ace-editor/ace-editor.js +89 -0
  14. package/frontend/src/aceEditor.js +69 -0
  15. package/frontend/src/chat/chat-message/chat-message.html +1 -1
  16. package/frontend/src/chat/chat-message/chat-message.js +1 -1
  17. package/frontend/src/chat/chat-message-script/chat-message-script.html +51 -34
  18. package/frontend/src/chat/chat-message-script/chat-message-script.js +12 -55
  19. package/frontend/src/chat/chat.html +68 -39
  20. package/frontend/src/chat/chat.js +26 -2
  21. package/frontend/src/clone-document/clone-document.html +7 -2
  22. package/frontend/src/clone-document/clone-document.js +1 -8
  23. package/frontend/src/create-dashboard/create-dashboard.html +11 -6
  24. package/frontend/src/create-dashboard/create-dashboard.js +0 -7
  25. package/frontend/src/create-document/create-document.html +15 -9
  26. package/frontend/src/create-document/create-document.js +5 -12
  27. package/frontend/src/dashboard/dashboard.html +14 -12
  28. package/frontend/src/dashboard/dashboard.js +12 -4
  29. package/frontend/src/dashboard/edit-dashboard/edit-dashboard.html +13 -7
  30. package/frontend/src/dashboard/edit-dashboard/edit-dashboard.js +13 -21
  31. package/frontend/src/dashboard-result/dashboard-chart/dashboard-chart.html +19 -17
  32. package/frontend/src/dashboard-result/dashboard-chart/dashboard-chart.js +97 -2
  33. package/frontend/src/dashboard-result/dashboard-map/dashboard-map.js +27 -3
  34. package/frontend/src/dashboard-result/dashboard-result.html +3 -3
  35. package/frontend/src/dashboards/dashboards.html +101 -109
  36. package/frontend/src/dashboards/dashboards.js +25 -1
  37. package/frontend/src/detail-default/detail-default.html +2 -2
  38. package/frontend/src/detail-default/detail-default.js +24 -3
  39. package/frontend/src/document/confirm-changes/confirm-changes.html +1 -1
  40. package/frontend/src/document/confirm-delete/confirm-delete.html +1 -1
  41. package/frontend/src/document/document.css +1 -1
  42. package/frontend/src/document/document.html +28 -28
  43. package/frontend/src/document/execute-script/execute-script.html +20 -21
  44. package/frontend/src/document/execute-script/execute-script.js +1 -43
  45. package/frontend/src/document-details/document-details.css +4 -9
  46. package/frontend/src/document-details/document-details.html +34 -33
  47. package/frontend/src/document-details/document-details.js +2 -53
  48. package/frontend/src/document-details/document-property/document-property.html +12 -12
  49. package/frontend/src/edit-array/edit-array.html +7 -6
  50. package/frontend/src/edit-array/edit-array.js +10 -50
  51. package/frontend/src/edit-boolean/edit-boolean.html +12 -12
  52. package/frontend/src/edit-date/edit-date.html +2 -2
  53. package/frontend/src/edit-default/edit-default.html +1 -1
  54. package/frontend/src/edit-string/edit-string.html +3 -3
  55. package/frontend/src/edit-subdocument/edit-subdocument.html +5 -3
  56. package/frontend/src/edit-subdocument/edit-subdocument.js +1 -15
  57. package/frontend/src/export-query-results/export-query-results.html +3 -3
  58. package/frontend/src/json-node/json-node.html +3 -3
  59. package/frontend/src/list-json/json-node.html +1 -1
  60. package/frontend/src/models/document-search/document-search.html +3 -3
  61. package/frontend/src/models/model-switcher/model-switcher.html +53 -0
  62. package/frontend/src/models/model-switcher/model-switcher.js +123 -0
  63. package/frontend/src/models/models.css +3 -10
  64. package/frontend/src/models/models.html +146 -80
  65. package/frontend/src/models/models.js +108 -4
  66. package/frontend/src/navbar/navbar.html +157 -97
  67. package/frontend/src/navbar/navbar.js +31 -12
  68. package/frontend/src/routes.js +1 -1
  69. package/frontend/src/splash/splash.html +5 -5
  70. package/frontend/src/task-single/task-single.html +29 -29
  71. package/frontend/src/task-single/task-single.js +10 -10
  72. package/frontend/src/tasks/task-details/task-details.html +38 -38
  73. package/frontend/src/tasks/task-details/task-details.js +7 -2
  74. package/frontend/src/tasks/tasks.html +36 -35
  75. package/frontend/src/tasks/tasks.js +2 -25
  76. package/frontend/src/team/new-invitation/new-invitation.html +8 -8
  77. package/frontend/src/team/team.html +27 -27
  78. package/frontend/src/update-document/update-document.html +7 -2
  79. package/frontend/src/update-document/update-document.js +2 -11
  80. package/package.json +2 -1
  81. package/tailwind.config.js +75 -11
@@ -22,8 +22,7 @@ module.exports = app => app.component('document-details', {
22
22
  value: ''
23
23
  },
24
24
  fieldErrors: {},
25
- isSubmittingField: false,
26
- fieldValueEditor: null
25
+ isSubmittingField: false
27
26
  };
28
27
  },
29
28
  mounted() {
@@ -32,32 +31,11 @@ module.exports = app => app.component('document-details', {
32
31
  if (this.$refs.searchInput) {
33
32
  this.$refs.searchInput.focus();
34
33
  }
35
-
36
- if (this.showAddFieldModal) {
37
- this.initializeFieldValueEditor();
38
- }
39
34
  });
40
35
 
41
36
  this.searchQuery = this.getSearchQueryFromRoute();
42
37
  },
43
- beforeDestroy() {
44
- this.destroyFieldValueEditor();
45
- },
46
38
  watch: {
47
- 'fieldData.type'(newType, oldType) {
48
- // When field type changes, we need to handle the transition
49
- if (newType !== oldType) {
50
- // Destroy existing CodeMirror if it exists
51
- this.destroyFieldValueEditor();
52
-
53
- // If switching to a type that needs CodeMirror, initialize it
54
- if (this.shouldUseCodeMirror) {
55
- this.$nextTick(() => {
56
- this.initializeFieldValueEditor();
57
- });
58
- }
59
- }
60
- },
61
39
  searchQuery(newValue) {
62
40
  this.syncSearchQueryToUrl(newValue);
63
41
  },
@@ -122,7 +100,7 @@ module.exports = app => app.component('document-details', {
122
100
  const allTypes = new Set([...schemaTypes, ...commonTypes]);
123
101
  return Array.from(allTypes).sort();
124
102
  },
125
- shouldUseCodeMirror() {
103
+ shouldUseAce() {
126
104
  return ['Array', 'Object', 'Embedded'].includes(this.fieldData.type);
127
105
  },
128
106
  shouldUseDatePicker() {
@@ -313,15 +291,9 @@ module.exports = app => app.component('document-details', {
313
291
  },
314
292
  openAddFieldModal() {
315
293
  this.showAddFieldModal = true;
316
- this.$nextTick(() => {
317
- if (this.shouldUseCodeMirror) {
318
- this.initializeFieldValueEditor();
319
- }
320
- });
321
294
  },
322
295
  closeAddFieldModal() {
323
296
  this.showAddFieldModal = false;
324
- this.destroyFieldValueEditor();
325
297
  this.resetFieldForm();
326
298
  },
327
299
  async addNewField(fieldData) {
@@ -430,29 +402,6 @@ module.exports = app => app.component('document-details', {
430
402
  };
431
403
  this.fieldErrors = {};
432
404
  this.isSubmittingField = false;
433
- // Reset CodeMirror editor if it exists
434
- if (this.fieldValueEditor) {
435
- this.fieldValueEditor.setValue('');
436
- }
437
- },
438
- initializeFieldValueEditor() {
439
- if (this.$refs.fieldValueEditor && !this.fieldValueEditor && this.shouldUseCodeMirror) {
440
- this.$refs.fieldValueEditor.value = this.fieldData.value || '';
441
- this.fieldValueEditor = CodeMirror.fromTextArea(this.$refs.fieldValueEditor, {
442
- mode: 'javascript',
443
- lineNumbers: true,
444
- height: 'auto'
445
- });
446
- this.fieldValueEditor.on('change', () => {
447
- this.fieldData.value = this.fieldValueEditor.getValue();
448
- });
449
- }
450
- },
451
- destroyFieldValueEditor() {
452
- if (this.fieldValueEditor) {
453
- this.fieldValueEditor.toTextArea();
454
- this.fieldValueEditor = null;
455
- }
456
405
  },
457
406
  getVirtualFieldType(virtual) {
458
407
  const value = virtual.value;
@@ -1,29 +1,29 @@
1
- <div class="border border-gray-200 bg-white rounded-lg mb-2" style="overflow: visible;">
1
+ <div class="border border-edge bg-surface rounded-lg mb-2" style="overflow: visible;">
2
2
  <!-- Collapsible Header -->
3
3
  <div
4
4
  @click="toggleCollapse"
5
- class="p-1 cursor-pointer flex items-center justify-between border-b border-gray-200 transition-colors duration-200 ease-in-out"
6
- :class="{ 'bg-amber-100 hover:bg-amber-200': highlight, 'bg-slate-100 hover:bg-gray-100': !highlight }"
5
+ class="p-1 cursor-pointer flex items-center justify-between border-b border-edge transition-colors duration-200 ease-in-out"
6
+ :class="{ 'bg-amber-100 hover:bg-amber-200': highlight, 'bg-slate-100 hover:bg-muted': !highlight }"
7
7
  style="overflow: visible; position: relative;"
8
8
  >
9
9
  <div class="flex items-center" >
10
10
  <svg
11
11
  :class="isCollapsed ? 'rotate-0' : 'rotate-90'"
12
- class="w-4 h-4 text-gray-500 mr-2 transition-transform duration-200"
12
+ class="w-4 h-4 text-content-tertiary mr-2 transition-transform duration-200"
13
13
  fill="none"
14
14
  stroke="currentColor"
15
15
  viewBox="0 0 24 24"
16
16
  >
17
17
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
18
18
  </svg>
19
- <span class="font-medium text-gray-900">{{path.path}}</span>
20
- <span class="ml-2 text-sm text-gray-500">({{(path.instance || 'unknown').toLowerCase()}})</span>
19
+ <span class="font-medium text-content">{{path.path}}</span>
20
+ <span class="ml-2 text-sm text-content-tertiary">({{(path.instance || 'unknown').toLowerCase()}})</span>
21
21
  <div v-if="isGeoJsonGeometry" class="ml-3 inline-flex items-center gap-2">
22
22
  <div class="inline-flex items-center rounded-full bg-gray-200 p-0.5 text-xs font-semibold">
23
23
  <button
24
24
  type="button"
25
25
  class="rounded-full px-2.5 py-0.5 transition"
26
- :class="detailViewMode === 'text' ? 'bg-blue-600 text-white shadow' : 'text-gray-700 hover:text-gray-900'"
26
+ :class="detailViewMode === 'text' ? 'bg-blue-600 text-white shadow' : 'text-content-secondary hover:text-content'"
27
27
  :style="detailViewMode === 'text' ? 'color: white !important; background-color: #2563eb !important;' : ''"
28
28
  @click.stop="setDetailViewMode('text')">
29
29
  Text
@@ -31,7 +31,7 @@
31
31
  <button
32
32
  type="button"
33
33
  class="rounded-full px-2.5 py-0.5 transition"
34
- :class="detailViewMode === 'map' ? 'bg-blue-600 text-white shadow' : 'text-gray-700 hover:text-gray-900'"
34
+ :class="detailViewMode === 'map' ? 'bg-blue-600 text-white shadow' : 'text-content-secondary hover:text-content'"
35
35
  :style="detailViewMode === 'map' ? 'color: white !important; background-color: #2563eb !important;' : ''"
36
36
  @click.stop="setDetailViewMode('map')">
37
37
  Map
@@ -72,7 +72,7 @@
72
72
  <div class="flex items-center gap-2">
73
73
  <button
74
74
  type="button"
75
- class="flex items-center gap-1 text-sm text-gray-600 hover:text-gray-800 px-2 py-1 rounded-md border border-transparent hover:border-gray-300 bg-white"
75
+ class="flex items-center gap-1 text-sm text-gray-600 hover:text-gray-800 px-2 py-1 rounded-md border border-transparent hover:border-edge-strong bg-surface"
76
76
  @click.stop.prevent="copyPropertyValue"
77
77
  title="Copy value"
78
78
  aria-label="Copy property value"
@@ -85,7 +85,7 @@
85
85
  <router-link
86
86
  v-if="path.ref && getValueForPath(path.path)"
87
87
  :to="`/model/${path.ref}/document/${getValueForPath(path.path)}`"
88
- class="bg-ultramarine-600 hover:bg-ultramarine-500 text-white px-2 py-1 text-sm rounded-md"
88
+ class="bg-primary hover:bg-primary-hover text-primary-text px-2 py-1 text-sm rounded-md"
89
89
  @click.stop
90
90
  >View Document
91
91
  </router-link>
@@ -163,7 +163,7 @@
163
163
  <div v-else class="text-xs py-1.5 px-2 font-mono text-gray-800 break-words whitespace-pre-wrap mt-1">{{ arrayUtils.formatValue(item) }}</div>
164
164
  </div>
165
165
  <div class="mb-1.5 py-2.5 px-3 pl-4 bg-transparent border-none border-l-[3px] border-l-blue-500 rounded-none transition-all duration-200 cursor-pointer relative opacity-70 hover:opacity-100">
166
- <div class="text-xs py-1.5 px-2 font-mono text-gray-500 italic break-words whitespace-pre-wrap mt-1">
166
+ <div class="text-xs py-1.5 px-2 font-mono text-content-tertiary italic break-words whitespace-pre-wrap mt-1">
167
167
  ... and {{ remainingArrayCount }} more item{{ remainingArrayCount !== 1 ? 's' : '' }}
168
168
  </div>
169
169
  </div>
@@ -180,7 +180,7 @@
180
180
  </div>
181
181
  <!-- Non-array truncated view -->
182
182
  <div v-else-if="shouldShowTruncated && !isArray" class="relative">
183
- <div class="text-gray-700 whitespace-pre-wrap break-words font-mono text-sm">{{truncatedString}}</div>
183
+ <div class="text-content-secondary whitespace-pre-wrap break-words font-mono text-sm">{{truncatedString}}</div>
184
184
  <button
185
185
  @click="toggleValueExpansion"
186
186
  class="mt-2 text-blue-600 hover:text-blue-800 text-sm font-medium flex items-center gap-1 transform transition-all duration-200 ease-in-out hover:translate-x-0.5"
@@ -1,8 +1,9 @@
1
1
  <div class="w-full">
2
- <!-- CodeMirror editor for the entire array -->
3
- <textarea
4
- ref="arrayEditor"
5
- class="w-full border border-gray-300 p-2 font-mono"
6
- :style="{ minHeight: '300px' }">
7
- </textarea>
2
+ <ace-editor
3
+ :value="arrayStr"
4
+ @input="onEditorInput"
5
+ mode="json"
6
+ :line-numbers="true"
7
+ class="min-h-[300px]"
8
+ />
8
9
  </div>
@@ -1,7 +1,6 @@
1
1
  'use strict';
2
2
 
3
3
  const template = require('./edit-array.html');
4
-
5
4
  const { BSON } = require('mongodb/lib/bson');
6
5
 
7
6
  const ObjectId = new Proxy(BSON.ObjectId, {
@@ -16,10 +15,14 @@ module.exports = app => app.component('edit-array', {
16
15
  props: ['value'],
17
16
  data() {
18
17
  return {
19
- arrayValue: [],
20
- arrayEditor: null
18
+ arrayValue: []
21
19
  };
22
20
  },
21
+ computed: {
22
+ arrayStr() {
23
+ return JSON.stringify(this.arrayValue, null, 2);
24
+ }
25
+ },
23
26
  methods: {
24
27
  initializeArray() {
25
28
  if (this.value == null) {
@@ -29,42 +32,13 @@ module.exports = app => app.component('edit-array', {
29
32
  } else {
30
33
  this.arrayValue = [];
31
34
  }
32
-
33
- // Update CodeMirror editor if it exists
34
- this.$nextTick(() => {
35
- if (this.arrayEditor) {
36
- const arrayStr = JSON.stringify(this.arrayValue, null, 2);
37
- this.arrayEditor.setValue(arrayStr);
38
- }
39
- });
40
- },
41
- initializeArrayEditor() {
42
- this.$nextTick(() => {
43
- const textareaRef = this.$refs.arrayEditor;
44
- const textarea = Array.isArray(textareaRef) ? textareaRef[0] : textareaRef;
45
- if (textarea && !this.arrayEditor) {
46
- const arrayStr = JSON.stringify(this.arrayValue, null, 2);
47
- textarea.value = arrayStr;
48
- this.arrayEditor = CodeMirror.fromTextArea(textarea, {
49
- mode: 'javascript',
50
- lineNumbers: true
51
- });
52
- this.arrayEditor.on('change', () => {
53
- this.updateArrayFromEditor();
54
- });
55
- }
56
- });
57
35
  },
58
- updateArrayFromEditor() {
59
- if (!this.arrayEditor) {
60
- return;
61
- }
36
+ onEditorInput(str) {
62
37
  try {
63
- const value = this.arrayEditor.getValue();
64
- if (value.trim() === '') {
38
+ if (str.trim() === '') {
65
39
  this.arrayValue = [];
66
40
  } else {
67
- this.arrayValue = JSON.parse(value);
41
+ this.arrayValue = JSON.parse(str);
68
42
  }
69
43
  this.emitUpdate();
70
44
  } catch (err) {
@@ -81,25 +55,11 @@ module.exports = app => app.component('edit-array', {
81
55
  },
82
56
  mounted() {
83
57
  this.initializeArray();
84
- this.initializeArrayEditor();
85
- },
86
- beforeDestroy() {
87
- if (this.arrayEditor) {
88
- this.arrayEditor.toTextArea();
89
- }
90
58
  },
91
59
  watch: {
92
60
  value: {
93
- handler(newValue, oldValue) {
94
- // Initialize array when value prop changes
61
+ handler() {
95
62
  this.initializeArray();
96
- // Update array editor if it exists
97
- if (this.arrayEditor) {
98
- this.$nextTick(() => {
99
- const arrayStr = JSON.stringify(this.arrayValue, null, 2);
100
- this.arrayEditor.setValue(arrayStr);
101
- });
102
- }
103
63
  },
104
64
  deep: true,
105
65
  immediate: true
@@ -1,45 +1,45 @@
1
1
  <div class="edit-boolean">
2
2
  <div class="flex flex-wrap gap-2">
3
- <label class="flex items-center gap-2 px-3 py-2 border rounded cursor-pointer hover:bg-gray-50"
4
- :class="selectedValue === true ? 'bg-blue-100 border-blue-300 text-blue-800' : 'border-gray-300 text-gray-700'">
3
+ <label class="flex items-center gap-2 px-3 py-2 border rounded cursor-pointer hover:bg-page"
4
+ :class="selectedValue === true ? 'bg-blue-100 border-blue-300 text-blue-800' : 'border-edge-strong text-content-secondary'">
5
5
  <input
6
6
  type="radio"
7
7
  :checked="selectedValue === true"
8
8
  @change="selectValue(true)"
9
- class="w-4 h-4 text-blue-600 border-gray-300 focus:ring-blue-500"
9
+ class="w-4 h-4 text-blue-600 border-edge-strong focus:ring-blue-500"
10
10
  />
11
11
  <span class="text-sm font-medium">true</span>
12
12
  </label>
13
13
 
14
- <label class="flex items-center gap-2 px-3 py-2 border rounded cursor-pointer hover:bg-gray-50"
15
- :class="selectedValue === false ? 'bg-blue-100 border-blue-300 text-blue-800' : 'border-gray-300 text-gray-700'">
14
+ <label class="flex items-center gap-2 px-3 py-2 border rounded cursor-pointer hover:bg-page"
15
+ :class="selectedValue === false ? 'bg-blue-100 border-blue-300 text-blue-800' : 'border-edge-strong text-content-secondary'">
16
16
  <input
17
17
  type="radio"
18
18
  :checked="selectedValue === false"
19
19
  @change="selectValue(false)"
20
- class="w-4 h-4 text-blue-600 border-gray-300 focus:ring-blue-500"
20
+ class="w-4 h-4 text-blue-600 border-edge-strong focus:ring-blue-500"
21
21
  />
22
22
  <span class="text-sm font-medium">false</span>
23
23
  </label>
24
24
 
25
- <label class="flex items-center gap-2 px-3 py-2 border rounded cursor-pointer hover:bg-gray-50"
26
- :class="selectedValue === null ? 'bg-blue-100 border-blue-300 text-blue-800' : 'border-gray-300 text-gray-700'">
25
+ <label class="flex items-center gap-2 px-3 py-2 border rounded cursor-pointer hover:bg-page"
26
+ :class="selectedValue === null ? 'bg-blue-100 border-blue-300 text-blue-800' : 'border-edge-strong text-content-secondary'">
27
27
  <input
28
28
  type="radio"
29
29
  :checked="selectedValue === null"
30
30
  @change="selectValue(null)"
31
- class="w-4 h-4 text-blue-600 border-gray-300 focus:ring-blue-500"
31
+ class="w-4 h-4 text-blue-600 border-edge-strong focus:ring-blue-500"
32
32
  />
33
33
  <span class="text-sm font-medium">null</span>
34
34
  </label>
35
35
 
36
- <label class="flex items-center gap-2 px-3 py-2 border rounded cursor-pointer hover:bg-gray-50"
37
- :class="selectedValue === undefined ? 'bg-blue-100 border-blue-300 text-blue-800' : 'border-gray-300 text-gray-700'">
36
+ <label class="flex items-center gap-2 px-3 py-2 border rounded cursor-pointer hover:bg-page"
37
+ :class="selectedValue === undefined ? 'bg-blue-100 border-blue-300 text-blue-800' : 'border-edge-strong text-content-secondary'">
38
38
  <input
39
39
  type="radio"
40
40
  :checked="selectedValue === undefined"
41
41
  @change="selectValue(undefined)"
42
- class="w-4 h-4 text-blue-600 border-gray-300 focus:ring-blue-500"
42
+ class="w-4 h-4 text-blue-600 border-edge-strong focus:ring-blue-500"
43
43
  />
44
44
  <span class="text-sm font-medium">undefined</span>
45
45
  </label>
@@ -1,14 +1,14 @@
1
1
  <div>
2
2
  <input
3
3
  v-if="dateSelection == 'picker'"
4
- class="w-64 h-8 border border-gray-300 outline-0"
4
+ class="w-64 h-8 border border-edge-strong outline-0"
5
5
  type="datetime-local"
6
6
  :value="valueAsLocalString"
7
7
  @input="$emit('input', $event.target.value)">
8
8
  <input
9
9
  v-if="dateSelection == 'iso'"
10
10
  type="text"
11
- class="w-64 h-8 border border-gray-300 outline-0"
11
+ class="w-64 h-8 border border-edge-strong outline-0"
12
12
  :value="valueAsISOString"
13
13
  @input="updateFromISO">
14
14
  </div>
@@ -1,3 +1,3 @@
1
1
  <div>
2
- <input type="text" :value="value" @input="$emit('input', $event.target.value)" class="w-full p-1 border border-gray-300 outline-0">
2
+ <input type="text" :value="value" @input="$emit('input', $event.target.value)" class="w-full p-1 border border-edge-strong outline-0">
3
3
  </div>
@@ -1,7 +1,7 @@
1
1
  <div>
2
2
  <div v-if="hasEnumValues" class="space-y-2">
3
3
  <select
4
- class="w-full px-3 py-2 border border-gray-300 bg-white rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
4
+ class="w-full px-3 py-2 border border-edge-strong bg-surface rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
5
5
  :value="selectedOption"
6
6
  @change="onSelectChange"
7
7
  >
@@ -14,7 +14,7 @@
14
14
  <input
15
15
  v-if="selectedOption === '__other'"
16
16
  type="text"
17
- class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
17
+ class="w-full px-3 py-2 border border-edge-strong rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
18
18
  :value="otherValue"
19
19
  @input="onOtherInput"
20
20
  placeholder="Enter a value"
@@ -23,7 +23,7 @@
23
23
  <div v-else>
24
24
  <input
25
25
  type="text"
26
- class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
26
+ class="w-full px-3 py-2 border border-edge-strong rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
27
27
  :value="value != null ? value : ''"
28
28
  @input="onTextInput"
29
29
  placeholder="Enter a value"
@@ -1,6 +1,8 @@
1
1
  <div class="edit-subdocument">
2
- <textarea
3
- ref="editor"
2
+ <ace-editor
4
3
  v-model="currentValue"
5
- class="w-full border border-gray-300 p-1 h-[300px]"></textarea>
4
+ mode="javascript"
5
+ :line-numbers="true"
6
+ class="w-full h-[300px] min-h-[200px]"
7
+ />
6
8
  </div>
@@ -1,7 +1,6 @@
1
1
  'use strict';
2
2
 
3
3
  const template = require('./edit-subdocument.html');
4
-
5
4
  const { BSON, EJSON } = require('mongodb/lib/bson');
6
5
 
7
6
  const ObjectId = new Proxy(BSON.ObjectId, {
@@ -13,19 +12,11 @@ const ObjectId = new Proxy(BSON.ObjectId, {
13
12
  module.exports = app => app.component('edit-subdocument', {
14
13
  template: template,
15
14
  props: ['value'],
16
- data: () => ({ currentValue: null, status: 'init' }),
15
+ data: () => ({ currentValue: '', status: 'init' }),
17
16
  mounted() {
18
17
  this.currentValue = this.value == null
19
18
  ? '' + this.value
20
19
  : JSON.stringify(this.value, null, ' ').trim();
21
- this.$refs.editor.value = this.currentValue;
22
- this.editor = CodeMirror.fromTextArea(this.$refs.editor, {
23
- mode: 'javascript',
24
- lineNumbers: true
25
- });
26
- this.editor.on('change', ev => {
27
- this.currentValue = this.editor.getValue();
28
- });
29
20
  this.status = 'loaded';
30
21
  },
31
22
  watch: {
@@ -41,10 +32,5 @@ module.exports = app => app.component('edit-subdocument', {
41
32
  }
42
33
  }
43
34
  },
44
- beforeDestroy() {
45
- if (this.editor) {
46
- this.editor.toTextArea();
47
- }
48
- },
49
35
  emits: ['input', 'error']
50
36
  });
@@ -4,10 +4,10 @@
4
4
  Choose fields to export
5
5
  </div>
6
6
  <div v-for="(schemaPath,index) in schemaPaths" class="w-5 flex items-center">
7
- <input type="checkbox" class="mt-0 h-4 w-4 rounded border-gray-300 text-sky-600 focus:ring-sky-600 accent-sky-600" v-model="shouldExport[schemaPath.path]" :id="'schemaPath.path'+index">
8
- <div class="ml-2 text-gray-700 grow shrink text-left">
7
+ <input type="checkbox" class="mt-0 h-4 w-4 rounded border-edge-strong text-sky-600 focus:ring-sky-600 accent-sky-600" v-model="shouldExport[schemaPath.path]" :id="'schemaPath.path'+index">
8
+ <div class="ml-2 text-content-secondary grow shrink text-left">
9
9
  <label :for="'schemaPath.path'+index">{{schemaPath.path}}</label>
10
10
  </div>
11
11
  </div>
12
- <async-button class="rounded-md bg-ultramarine-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600" @click="exportQueryResults">Export</async-button>
12
+ <async-button class="rounded-md bg-primary px-2.5 py-1.5 text-sm font-semibold text-primary-text shadow-sm hover:bg-primary-hover focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600" @click="exportQueryResults">Export</async-button>
13
13
  </div>
@@ -3,14 +3,14 @@
3
3
  <button
4
4
  v-if="showToggle"
5
5
  type="button"
6
- class="w-4 h-4 mr-1 inline-flex items-center justify-center leading-none text-gray-500 hover:text-gray-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-slate-400 cursor-pointer"
6
+ class="w-4 h-4 mr-1 inline-flex items-center justify-center leading-none text-content-tertiary hover:text-content-secondary focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-slate-400 cursor-pointer"
7
7
  @click.stop="handleToggle"
8
8
  >
9
9
  {{ isCollapsedNode ? '+' : '-' }}
10
10
  </button>
11
11
  <span v-else class="w-4 h-4 mr-1 inline-flex items-center justify-center invisible flex-shrink-0"></span>
12
12
  <template v-if="hasKey">
13
- <span class="text-blue-600">"{{ nodeKey }}"</span><span>: </span>
13
+ <span class="text-primary">"{{ nodeKey }}"</span><span>: </span>
14
14
  </template>
15
15
  <template v-if="isComplex">
16
16
  <template v-if="hasChildren">
@@ -52,7 +52,7 @@
52
52
  </span>
53
53
  <a
54
54
  href="#"
55
- class="ml-1 text-sm text-sky-700 opacity-0 group-hover:opacity-100 focus:opacity-100 transition-opacity"
55
+ class="ml-1 text-sm text-primary opacity-0 group-hover:opacity-100 focus:opacity-100 transition-opacity"
56
56
  @click.stop.prevent="goToReference()"
57
57
  >
58
58
  View Document
@@ -3,7 +3,7 @@
3
3
  <button
4
4
  v-if="showToggle"
5
5
  type="button"
6
- class="w-4 h-4 mr-1 inline-flex items-center justify-center leading-none text-gray-500 hover:text-gray-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-slate-400 cursor-pointer"
6
+ class="w-4 h-4 mr-1 inline-flex items-center justify-center leading-none text-content-tertiary hover:text-content-secondary focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-slate-400 cursor-pointer"
7
7
  @click.stop="handleToggle"
8
8
  >
9
9
  {{ isCollapsedNode ? '+' : '-' }}
@@ -1,7 +1,7 @@
1
1
  <form @submit.prevent="emitSearch" class="relative flex-grow m-0">
2
2
  <input
3
3
  ref="searchInput"
4
- class="w-full font-mono rounded-md p-1 border border-gray-300 outline-gray-300 text-lg focus:ring-1 focus:ring-ultramarine-200 focus:ring-offset-0 focus:outline-none"
4
+ class="w-full font-mono rounded-md p-1 border border-edge-strong outline-edge-strong text-lg focus:ring-1 focus:ring-primary-subtle focus:ring-offset-0 focus:outline-none"
5
5
  type="text"
6
6
  placeholder="Filter (supports JS, dates, ObjectIds, and more)"
7
7
  v-model="searchText"
@@ -9,12 +9,12 @@
9
9
  @input="updateAutocomplete"
10
10
  @keydown="handleKeyDown"
11
11
  />
12
- <ul v-if="autocompleteSuggestions.length" class="absolute z-[9999] bg-white border border-gray-300 rounded mt-1 w-full max-h-40 overflow-y-auto shadow">
12
+ <ul v-if="autocompleteSuggestions.length" class="absolute z-[9999] bg-surface border border-edge-strong rounded mt-1 w-full max-h-40 overflow-y-auto shadow">
13
13
  <li
14
14
  v-for="(suggestion, index) in autocompleteSuggestions"
15
15
  :key="suggestion"
16
16
  class="px-2 py-1 cursor-pointer"
17
- :class="{ 'bg-ultramarine-100': index === autocompleteIndex }"
17
+ :class="{ 'bg-primary-subtle': index === autocompleteIndex }"
18
18
  @mousedown.prevent="applySuggestion(index)"
19
19
  >
20
20
  {{ suggestion }}
@@ -0,0 +1,53 @@
1
+ <div v-if="show" class="fixed inset-0 z-[9999]">
2
+ <div class="fixed inset-0 bg-black/40" @click="$emit('close')"></div>
3
+ <div
4
+ class="fixed top-[15%] left-1/2 -translate-x-1/2 w-full max-w-lg bg-surface rounded-lg shadow-2xl border border-edge overflow-hidden"
5
+ role="dialog"
6
+ aria-modal="true"
7
+ aria-label="Model switcher"
8
+ >
9
+ <div class="p-3 border-b border-gray-100">
10
+ <input
11
+ ref="searchInput"
12
+ v-model="search"
13
+ type="text"
14
+ placeholder="Type a model name..."
15
+ @keydown="handleKeydown"
16
+ class="w-full rounded-md border border-edge bg-page py-2 px-3 text-sm text-content placeholder:text-gray-400 focus:border-edge-strong focus:outline-none focus:ring-1 focus:ring-gray-300"
17
+ />
18
+ </div>
19
+ <div ref="list" class="max-h-80 overflow-y-auto">
20
+ <template v-for="(item, index) in items" :key="item.section + '-' + item.model">
21
+ <div
22
+ v-if="index === 0 || items[index - 1].section !== item.section"
23
+ class="px-3 pt-2 pb-1 text-xs font-semibold text-gray-400 uppercase tracking-wider"
24
+ >
25
+ {{ item.section }}
26
+ </div>
27
+ <div
28
+ :data-selected="index === selectedIndex"
29
+ @click="selectModel(item.model)"
30
+ @mouseenter="onMouseEnter(index)"
31
+ class="flex items-center px-3 py-1.5 mx-2 rounded-md cursor-pointer text-sm"
32
+ :class="index === selectedIndex ? 'bg-muted text-content' : 'text-content-secondary hover:bg-page'"
33
+ >
34
+ <span class="truncate" v-html="highlightMatch(item.model)"></span>
35
+ <span
36
+ v-if="modelDocumentCounts && modelDocumentCounts[item.model] !== undefined"
37
+ class="ml-auto text-xs text-gray-400 bg-muted rounded px-1.5 py-[1px]"
38
+ >
39
+ {{ formatCompactCount(modelDocumentCounts[item.model]) }}
40
+ </span>
41
+ </div>
42
+ </template>
43
+ <div v-if="items.length === 0" class="px-3 py-4 text-sm text-content-tertiary text-center">
44
+ No models match "{{ search }}"
45
+ </div>
46
+ </div>
47
+ <div class="border-t border-gray-100 px-3 py-2 text-xs text-gray-400 flex gap-3">
48
+ <span>↑↓ navigate</span>
49
+ <span>↵ select</span>
50
+ <span>esc close</span>
51
+ </div>
52
+ </div>
53
+ </div>