@mongoosejs/studio 0.2.12 → 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 (92) 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/backend/actions/Model/getEstimatedDocumentCounts.js +38 -0
  5. package/backend/actions/Model/index.js +1 -0
  6. package/backend/actions/Model/streamDocumentChanges.js +8 -7
  7. package/backend/actions/Task/getTasks.js +9 -6
  8. package/backend/authorize.js +1 -0
  9. package/backend/index.js +11 -3
  10. package/eslint.config.js +5 -1
  11. package/express.js +1 -0
  12. package/frontend/public/app.js +25235 -662
  13. package/frontend/public/dark-theme.css +365 -0
  14. package/frontend/public/images/mongoose-studio.svg +4 -0
  15. package/frontend/public/index.html +21 -1
  16. package/frontend/public/style.css +5 -7
  17. package/frontend/public/theme-variables.css +294 -0
  18. package/frontend/public/tw.css +461 -239
  19. package/frontend/src/ace-editor/ace-editor.html +4 -0
  20. package/frontend/src/ace-editor/ace-editor.js +89 -0
  21. package/frontend/src/aceEditor.js +69 -0
  22. package/frontend/src/api.js +6 -0
  23. package/frontend/src/chat/chat-message/chat-message.html +1 -1
  24. package/frontend/src/chat/chat-message/chat-message.js +1 -1
  25. package/frontend/src/chat/chat-message-script/chat-message-script.html +51 -34
  26. package/frontend/src/chat/chat-message-script/chat-message-script.js +12 -55
  27. package/frontend/src/chat/chat.html +72 -37
  28. package/frontend/src/chat/chat.js +26 -2
  29. package/frontend/src/clone-document/clone-document.html +7 -2
  30. package/frontend/src/clone-document/clone-document.js +1 -8
  31. package/frontend/src/create-dashboard/create-dashboard.html +11 -6
  32. package/frontend/src/create-dashboard/create-dashboard.js +0 -7
  33. package/frontend/src/create-document/create-document.html +15 -9
  34. package/frontend/src/create-document/create-document.js +5 -12
  35. package/frontend/src/dashboard/dashboard.html +14 -12
  36. package/frontend/src/dashboard/dashboard.js +12 -4
  37. package/frontend/src/dashboard/edit-dashboard/edit-dashboard.html +13 -7
  38. package/frontend/src/dashboard/edit-dashboard/edit-dashboard.js +13 -21
  39. package/frontend/src/dashboard-result/dashboard-chart/dashboard-chart.html +19 -17
  40. package/frontend/src/dashboard-result/dashboard-chart/dashboard-chart.js +97 -2
  41. package/frontend/src/dashboard-result/dashboard-map/dashboard-map.js +27 -3
  42. package/frontend/src/dashboard-result/dashboard-result.html +3 -3
  43. package/frontend/src/dashboards/dashboards.html +101 -109
  44. package/frontend/src/dashboards/dashboards.js +25 -1
  45. package/frontend/src/detail-default/detail-default.html +2 -2
  46. package/frontend/src/detail-default/detail-default.js +24 -3
  47. package/frontend/src/document/confirm-changes/confirm-changes.html +1 -1
  48. package/frontend/src/document/confirm-delete/confirm-delete.html +1 -1
  49. package/frontend/src/document/document.css +1 -1
  50. package/frontend/src/document/document.html +53 -27
  51. package/frontend/src/document/document.js +27 -1
  52. package/frontend/src/document/execute-script/execute-script.html +20 -21
  53. package/frontend/src/document/execute-script/execute-script.js +1 -43
  54. package/frontend/src/document-details/document-details.css +4 -9
  55. package/frontend/src/document-details/document-details.html +34 -33
  56. package/frontend/src/document-details/document-details.js +2 -53
  57. package/frontend/src/document-details/document-property/document-property.html +12 -12
  58. package/frontend/src/edit-array/edit-array.html +7 -6
  59. package/frontend/src/edit-array/edit-array.js +10 -50
  60. package/frontend/src/edit-boolean/edit-boolean.html +12 -12
  61. package/frontend/src/edit-date/edit-date.html +2 -2
  62. package/frontend/src/edit-default/edit-default.html +1 -1
  63. package/frontend/src/edit-string/edit-string.html +3 -3
  64. package/frontend/src/edit-subdocument/edit-subdocument.html +5 -3
  65. package/frontend/src/edit-subdocument/edit-subdocument.js +1 -15
  66. package/frontend/src/export-query-results/export-query-results.html +3 -3
  67. package/frontend/src/json-node/json-node.html +3 -3
  68. package/frontend/src/list-json/json-node.html +1 -1
  69. package/frontend/src/models/document-search/document-search.html +3 -3
  70. package/frontend/src/models/model-switcher/model-switcher.html +53 -0
  71. package/frontend/src/models/model-switcher/model-switcher.js +123 -0
  72. package/frontend/src/models/models.css +3 -10
  73. package/frontend/src/models/models.html +146 -74
  74. package/frontend/src/models/models.js +142 -4
  75. package/frontend/src/navbar/navbar.html +157 -97
  76. package/frontend/src/navbar/navbar.js +32 -13
  77. package/frontend/src/routes.js +20 -4
  78. package/frontend/src/splash/splash.html +5 -5
  79. package/frontend/src/task-by-name/task-by-name.html +15 -0
  80. package/frontend/src/task-by-name/task-by-name.js +78 -0
  81. package/frontend/src/task-single/task-single.html +157 -0
  82. package/frontend/src/task-single/task-single.js +116 -0
  83. package/frontend/src/tasks/task-details/task-details.html +124 -73
  84. package/frontend/src/tasks/task-details/task-details.js +166 -10
  85. package/frontend/src/tasks/tasks.html +37 -48
  86. package/frontend/src/tasks/tasks.js +11 -50
  87. package/frontend/src/team/new-invitation/new-invitation.html +8 -8
  88. package/frontend/src/team/team.html +27 -27
  89. package/frontend/src/update-document/update-document.html +7 -2
  90. package/frontend/src/update-document/update-document.js +2 -11
  91. package/package.json +3 -1
  92. package/tailwind.config.js +75 -11
@@ -1,6 +1,6 @@
1
1
  <div class="document-details">
2
2
  <!-- View Toggle and Search/Filter Bar -->
3
- <div class="sticky top-[60px] z-40 bg-white rounded-md p-4 border-b border-gray-200 shadow-sm">
3
+ <div class="sticky top-[60px] z-40 bg-surface rounded-md p-4 border-b border-edge shadow-sm">
4
4
 
5
5
  <!-- Search and Filter Bar (only show in fields view) -->
6
6
  <div v-if="viewMode === 'fields'" class="flex md:gap-3">
@@ -11,7 +11,7 @@
11
11
  v-model="searchQuery"
12
12
  type="text"
13
13
  placeholder="Search fields..."
14
- class="w-full px-4 py-2 pl-10 pr-4 text-sm border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
14
+ class="w-full px-4 py-2 pl-10 pr-4 text-sm border border-edge-strong rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
15
15
  />
16
16
  <div class="absolute inset-y-0 left-0 flex items-center pl-3">
17
17
  <svg class="w-4 h-4 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -24,7 +24,7 @@
24
24
  <div class="relative hidden md:block">
25
25
  <select
26
26
  v-model="selectedType"
27
- class="px-4 py-2 pr-8 text-sm border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-white min-w-[140px] appearance-none"
27
+ class="px-4 py-2 pr-8 text-sm border border-edge-strong rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-surface min-w-[140px] appearance-none"
28
28
  >
29
29
  <option value="">All Types</option>
30
30
  <option v-for="type in availableTypes" :key="type" :value="type">
@@ -62,24 +62,24 @@
62
62
  <div
63
63
  v-for="path in matchedVirtuals"
64
64
  :key="path.name"
65
- class="border border-gray-200 rounded-lg mb-2 transition-all duration-200 ease-in-out mt-4"
65
+ class="border border-edge rounded-lg mb-2 transition-all duration-200 ease-in-out mt-4"
66
66
  >
67
67
  <!-- Virtual Field Header (Always Visible) -->
68
68
  <div
69
69
  @click="toggleVirtualField(path.name)"
70
- class="p-3 bg-amber-100 hover:bg-amber-200 cursor-pointer flex items-center justify-between border-b border-gray-200 transition-colors duration-200 ease-in-out"
70
+ class="p-3 bg-amber-100 hover:bg-amber-200 cursor-pointer flex items-center justify-between border-b border-edge transition-colors duration-200 ease-in-out"
71
71
  >
72
72
  <div class="flex items-center">
73
73
  <svg
74
74
  :class="isVirtualFieldCollapsed(path.name) ? 'rotate-0' : 'rotate-90'"
75
- class="w-4 h-4 text-gray-500 mr-2 transition-transform duration-200"
75
+ class="w-4 h-4 text-content-tertiary mr-2 transition-transform duration-200"
76
76
  fill="none"
77
77
  stroke="currentColor"
78
78
  viewBox="0 0 24 24"
79
79
  >
80
80
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
81
81
  </svg>
82
- <span class="font-medium text-gray-900">{{path.name}}</span>
82
+ <span class="font-medium text-content">{{path.name}}</span>
83
83
  <span v-if="path.isVirtual" class="ml-2 text-sm text-purple-600">(virtual - {{getVirtualFieldType(path)}})</span>
84
84
  <span v-else class="ml-2 text-sm text-blue-600">(user-added - {{getVirtualFieldType(path)}})</span>
85
85
  </div>
@@ -116,24 +116,24 @@
116
116
  <div
117
117
  v-for="path in unmatchedVirtuals"
118
118
  :key="path.name"
119
- class="border border-gray-200 rounded-lg mb-2 transition-all duration-200 ease-in-out"
119
+ class="border border-edge rounded-lg mb-2 transition-all duration-200 ease-in-out"
120
120
  >
121
121
  <!-- Virtual Field Header (Always Visible) -->
122
122
  <div
123
123
  @click="toggleVirtualField(path.name)"
124
- class="p-3 bg-gray-50 hover:bg-gray-100 cursor-pointer flex items-center justify-between border-b border-gray-200 transition-colors duration-200 ease-in-out"
124
+ class="p-3 bg-page hover:bg-muted cursor-pointer flex items-center justify-between border-b border-edge transition-colors duration-200 ease-in-out"
125
125
  >
126
126
  <div class="flex items-center">
127
127
  <svg
128
128
  :class="isVirtualFieldCollapsed(path.name) ? 'rotate-0' : 'rotate-90'"
129
- class="w-4 h-4 text-gray-500 mr-2 transition-transform duration-200"
129
+ class="w-4 h-4 text-content-tertiary mr-2 transition-transform duration-200"
130
130
  fill="none"
131
131
  stroke="currentColor"
132
132
  viewBox="0 0 24 24"
133
133
  >
134
134
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
135
135
  </svg>
136
- <span class="font-medium text-gray-900">{{path.name}}</span>
136
+ <span class="font-medium text-content">{{path.name}}</span>
137
137
  <span v-if="path.isVirtual" class="ml-2 text-sm text-purple-600">(virtual - {{getVirtualFieldType(path)}})</span>
138
138
  <span v-else class="ml-2 text-sm text-blue-600">(user-added - {{getVirtualFieldType(path)}})</span>
139
139
  </div>
@@ -151,7 +151,7 @@
151
151
  </div>
152
152
 
153
153
  <!-- No Results Message -->
154
- <div v-if="searchQuery.trim() && !hasSearchMatches" class="text-center py-8 text-gray-500">
154
+ <div v-if="searchQuery.trim() && !hasSearchMatches" class="text-center py-8 text-content-tertiary">
155
155
  <svg class="w-12 h-12 mx-auto mb-4 text-gray-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
156
156
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.172 16.172a4 4 0 015.656 0M9 12h6m-6-4h6m2 5.291A7.962 7.962 0 0112 15c-2.34 0-4.29-1.009-5.824-2.709M15 6.291A7.962 7.962 0 0012 5c-2.34 0-4.29 1.009-5.824 2.709"></path>
157
157
  </svg>
@@ -161,7 +161,7 @@
161
161
 
162
162
  <!-- JSON View -->
163
163
  <div v-if="viewMode === 'json'" class="json-view">
164
- <div class="border border-gray-300 rounded-lg bg-gray-50 p-4 overflow-auto">
164
+ <div class="border border-edge-strong rounded-lg bg-page p-4 overflow-auto">
165
165
  <pre class="text-sm font-mono text-gray-800 whitespace-pre">{{formattedJson}}</pre>
166
166
  </div>
167
167
  </div>
@@ -172,14 +172,14 @@
172
172
  <div class="modal-exit" @click="closeAddFieldModal">&times;</div>
173
173
  <div class="add-field-modal">
174
174
  <div class="mb-6">
175
- <h2 class="text-xl font-semibold text-gray-900 mb-2">Add New Field</h2>
175
+ <h2 class="text-xl font-semibold text-content mb-2">Add New Field</h2>
176
176
  <p class="text-sm text-gray-600">Create a new field for this document</p>
177
177
  </div>
178
178
 
179
179
  <form @submit.prevent="handleAddFieldSubmit" class="space-y-4">
180
180
  <!-- Field Name -->
181
181
  <div>
182
- <label for="fieldName" class="block text-sm font-medium text-gray-700 mb-1">
182
+ <label for="fieldName" class="block text-sm font-medium text-content-secondary mb-1">
183
183
  Field Name *
184
184
  </label>
185
185
  <input
@@ -188,7 +188,7 @@
188
188
  type="text"
189
189
  required
190
190
  placeholder="Enter field name..."
191
- 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"
191
+ 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"
192
192
  :class="{ 'border-red-500': fieldErrors.name }"
193
193
  />
194
194
  <p v-if="fieldErrors.name" class="mt-1 text-sm text-red-600">{{fieldErrors.name}}</p>
@@ -199,14 +199,14 @@
199
199
 
200
200
  <!-- Field Type -->
201
201
  <div>
202
- <label for="fieldType" class="block text-sm font-medium text-gray-700 mb-1">
202
+ <label for="fieldType" class="block text-sm font-medium text-content-secondary mb-1">
203
203
  Field Type *
204
204
  </label>
205
205
  <select
206
206
  id="fieldType"
207
207
  v-model="fieldData.type"
208
208
  required
209
- 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"
209
+ 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"
210
210
  :class="{ 'border-red-500': fieldErrors.type }"
211
211
  >
212
212
  <option value="">Select field type...</option>
@@ -219,7 +219,7 @@
219
219
 
220
220
  <!-- Field Value -->
221
221
  <div>
222
- <label for="fieldValue" class="block text-sm font-medium text-gray-700 mb-1">
222
+ <label for="fieldValue" class="block text-sm font-medium text-content-secondary mb-1">
223
223
  Initial Value
224
224
  </label>
225
225
 
@@ -229,47 +229,48 @@
229
229
  v-model="fieldData.value"
230
230
  type="date"
231
231
  id="fieldValue"
232
- 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"
232
+ 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"
233
233
  :class="{ 'border-red-500': fieldErrors.value }"
234
234
  />
235
235
 
236
236
  <!-- Simple input for basic types -->
237
237
  <input
238
- v-else-if="!shouldUseCodeMirror"
238
+ v-else-if="!shouldUseAce"
239
239
  v-model="fieldData.value"
240
240
  type="text"
241
241
  id="fieldValue"
242
242
  placeholder="Enter initial value (optional)..."
243
- 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"
243
+ 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"
244
244
  :class="{ 'border-red-500': fieldErrors.value }"
245
245
  />
246
246
 
247
- <!-- CodeMirror textarea for complex types -->
248
- <textarea
247
+ <!-- Ace editor for complex types (Array/Object/Embedded) -->
248
+ <ace-editor
249
249
  v-else
250
- ref="fieldValueEditor"
251
250
  id="fieldValue"
252
- rows="3"
253
- placeholder="Enter initial value (optional)..."
254
- 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"
251
+ v-model="fieldData.value"
252
+ mode="json"
253
+ :line-numbers="true"
254
+ :min-lines="3"
255
+ class="add-field-ace-editor w-full min-h-[100px]"
255
256
  :class="{ 'border-red-500': fieldErrors.value }"
256
- ></textarea>
257
+ />
257
258
 
258
259
  <p v-if="fieldErrors.value" class="mt-1 text-sm text-red-600">{{fieldErrors.value}}</p>
259
- <p class="mt-1 text-xs text-gray-500">
260
+ <p class="mt-1 text-xs text-content-tertiary">
260
261
  <span v-if="shouldUseDatePicker">Select a date or leave empty for null/undefined values.</span>
261
- <span v-else-if="shouldUseCodeMirror">Leave empty for null/undefined values. Use valid JSON format.</span>
262
+ <span v-else-if="shouldUseAce">Leave empty for null/undefined values. Use valid JSON format.</span>
262
263
  <span v-else>Leave empty for null/undefined values.</span>
263
264
  </p>
264
265
  </div>
265
266
 
266
267
 
267
268
  <!-- Action Buttons -->
268
- <div class="flex justify-end gap-3 pt-4 border-t border-gray-200">
269
+ <div class="flex justify-end gap-3 pt-4 border-t border-edge">
269
270
  <button
270
271
  type="button"
271
272
  @click="closeAddFieldModal"
272
- class="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
273
+ class="px-4 py-2 text-sm font-medium text-content-secondary bg-surface border border-edge-strong rounded-md hover:bg-page focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
273
274
  >
274
275
  Cancel
275
276
  </button>
@@ -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>