@mongoosejs/studio 0.2.5 → 0.2.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.
- package/backend/actions/ChatMessage/executeScript.js +2 -1
- package/backend/integrations/callLLM.js +1 -1
- package/backend/integrations/streamLLM.js +1 -1
- package/docs/user_stories.md +13 -0
- package/frontend/public/app.js +1142 -7
- package/frontend/public/tw.css +72 -0
- package/frontend/src/chat/chat.js +6 -2
- package/frontend/src/detail-default/detail-default.html +15 -2
- package/frontend/src/detail-default/detail-default.js +1066 -2
- package/frontend/src/document-details/document-property/document-property.html +71 -3
- package/frontend/src/document-details/document-property/document-property.js +67 -1
- package/package.json +1 -1
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
<div class="border border-gray-200 bg-white rounded-lg mb-2">
|
|
1
|
+
<div class="border border-gray-200 bg-white rounded-lg mb-2" style="overflow: visible;">
|
|
2
2
|
<!-- Collapsible Header -->
|
|
3
3
|
<div
|
|
4
4
|
@click="toggleCollapse"
|
|
5
5
|
class="p-1 cursor-pointer flex items-center justify-between border-b border-gray-200 transition-colors duration-200 ease-in-out"
|
|
6
6
|
:class="{ 'bg-amber-100 hover:bg-amber-200': highlight, 'bg-slate-100 hover:bg-gray-100': !highlight }"
|
|
7
|
+
style="overflow: visible; position: relative;"
|
|
7
8
|
>
|
|
8
9
|
<div class="flex items-center" >
|
|
9
10
|
<svg
|
|
@@ -17,6 +18,56 @@
|
|
|
17
18
|
</svg>
|
|
18
19
|
<span class="font-medium text-gray-900">{{path.path}}</span>
|
|
19
20
|
<span class="ml-2 text-sm text-gray-500">({{(path.instance || 'unknown').toLowerCase()}})</span>
|
|
21
|
+
<div v-if="isGeoJsonGeometry" class="ml-3 inline-flex items-center gap-2">
|
|
22
|
+
<div class="inline-flex items-center rounded-full bg-gray-200 p-0.5 text-xs font-semibold">
|
|
23
|
+
<button
|
|
24
|
+
type="button"
|
|
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'"
|
|
27
|
+
:style="detailViewMode === 'text' ? 'color: white !important; background-color: #2563eb !important;' : ''"
|
|
28
|
+
@click.stop="setDetailViewMode('text')">
|
|
29
|
+
Text
|
|
30
|
+
</button>
|
|
31
|
+
<button
|
|
32
|
+
type="button"
|
|
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'"
|
|
35
|
+
:style="detailViewMode === 'map' ? 'color: white !important; background-color: #2563eb !important;' : ''"
|
|
36
|
+
@click.stop="setDetailViewMode('map')">
|
|
37
|
+
Map
|
|
38
|
+
</button>
|
|
39
|
+
</div>
|
|
40
|
+
<!-- Info icon with tooltip -->
|
|
41
|
+
<div v-if="editting" class="relative inline-block" style="z-index: 10002;" @mouseenter="showTooltip = true" @mouseleave="showTooltip = false" @click.stop>
|
|
42
|
+
<svg
|
|
43
|
+
ref="infoIcon"
|
|
44
|
+
class="w-6 h-6 text-gray-400 hover:text-gray-600 cursor-help"
|
|
45
|
+
fill="none"
|
|
46
|
+
stroke="currentColor"
|
|
47
|
+
viewBox="0 0 24 24"
|
|
48
|
+
>
|
|
49
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
|
50
|
+
</svg>
|
|
51
|
+
<div
|
|
52
|
+
v-show="showTooltip"
|
|
53
|
+
ref="tooltip"
|
|
54
|
+
class="absolute left-full top-0 ml-2 w-64 p-3 text-white text-xs rounded-lg shadow-xl"
|
|
55
|
+
style="z-index: 99999; pointer-events: none; white-space: normal; position: fixed; background-color: #111827;"
|
|
56
|
+
:style="getTooltipStyle()"
|
|
57
|
+
>
|
|
58
|
+
<div class="font-semibold mb-2">Map Controls:</div>
|
|
59
|
+
<div v-if="isGeoJsonPoint" class="space-y-1">
|
|
60
|
+
<div>• Drag pin to move location</div>
|
|
61
|
+
</div>
|
|
62
|
+
<div v-else-if="isGeoJsonPolygon" class="space-y-1">
|
|
63
|
+
<div>• Drag vertices to reshape polygon</div>
|
|
64
|
+
<div v-if="isMultiPolygon">• Right-click edge to add new vertex</div>
|
|
65
|
+
<div>• Right-click vertex to delete</div>
|
|
66
|
+
</div>
|
|
67
|
+
<div class="absolute top-2 -left-1 w-0 h-0 border-t-4 border-b-4 border-r-4 border-transparent border-r-gray-900"></div>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
20
71
|
</div>
|
|
21
72
|
<div class="flex items-center gap-2">
|
|
22
73
|
<button
|
|
@@ -69,7 +120,18 @@
|
|
|
69
120
|
|
|
70
121
|
<!-- Field Content -->
|
|
71
122
|
<div v-if="editting && path.path !== '_id'">
|
|
123
|
+
<!-- Use detail-default with map editing for GeoJSON geometries -->
|
|
124
|
+
<component
|
|
125
|
+
v-if="isGeoJsonGeometry"
|
|
126
|
+
:is="getComponentForPath(path)"
|
|
127
|
+
:value="getEditValueForPath(path)"
|
|
128
|
+
:view-mode="detailViewMode"
|
|
129
|
+
:on-change="handleInputChange"
|
|
130
|
+
>
|
|
131
|
+
</component>
|
|
132
|
+
<!-- Use standard edit components for other types -->
|
|
72
133
|
<component
|
|
134
|
+
v-else
|
|
73
135
|
:is="getEditComponentForPath(path)"
|
|
74
136
|
:value="getEditValueForPath(path)"
|
|
75
137
|
:format="dateType"
|
|
@@ -131,7 +193,10 @@
|
|
|
131
193
|
</div>
|
|
132
194
|
<!-- Expanded view -->
|
|
133
195
|
<div v-else-if="needsTruncation && isValueExpanded" class="relative">
|
|
134
|
-
<component
|
|
196
|
+
<component
|
|
197
|
+
:is="getComponentForPath(path)"
|
|
198
|
+
:value="getValueForPath(path.path)"
|
|
199
|
+
:view-mode="detailViewMode"></component>
|
|
135
200
|
<button
|
|
136
201
|
@click="toggleValueExpansion"
|
|
137
202
|
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"
|
|
@@ -144,7 +209,10 @@
|
|
|
144
209
|
</div>
|
|
145
210
|
<!-- Full view (no truncation needed) -->
|
|
146
211
|
<div v-else>
|
|
147
|
-
<component
|
|
212
|
+
<component
|
|
213
|
+
:is="getComponentForPath(path)"
|
|
214
|
+
:value="getValueForPath(path.path)"
|
|
215
|
+
:view-mode="detailViewMode"></component>
|
|
148
216
|
</div>
|
|
149
217
|
</div>
|
|
150
218
|
</div>
|
|
@@ -17,8 +17,10 @@ module.exports = app => app.component('document-property', {
|
|
|
17
17
|
dateType: 'picker', // picker, iso
|
|
18
18
|
isCollapsed: false, // Start uncollapsed by default
|
|
19
19
|
isValueExpanded: false, // Track if the value is expanded
|
|
20
|
+
detailViewMode: 'text',
|
|
20
21
|
copyButtonLabel: 'Copy',
|
|
21
|
-
copyResetTimeoutId: null
|
|
22
|
+
copyResetTimeoutId: null,
|
|
23
|
+
showTooltip: false
|
|
22
24
|
};
|
|
23
25
|
},
|
|
24
26
|
beforeDestroy() {
|
|
@@ -92,9 +94,58 @@ module.exports = app => app.component('document-property', {
|
|
|
92
94
|
return this.arrayValue.length - 2;
|
|
93
95
|
}
|
|
94
96
|
return 0;
|
|
97
|
+
},
|
|
98
|
+
isGeoJsonGeometry() {
|
|
99
|
+
const value = this.getValueForPath(this.path.path);
|
|
100
|
+
return value != null
|
|
101
|
+
&& typeof value === 'object'
|
|
102
|
+
&& !Array.isArray(value)
|
|
103
|
+
&& Object.prototype.hasOwnProperty.call(value, 'type')
|
|
104
|
+
&& Object.prototype.hasOwnProperty.call(value, 'coordinates');
|
|
105
|
+
},
|
|
106
|
+
isGeoJsonPoint() {
|
|
107
|
+
const value = this.getValueForPath(this.path.path);
|
|
108
|
+
return this.isGeoJsonGeometry && value.type === 'Point';
|
|
109
|
+
},
|
|
110
|
+
isGeoJsonPolygon() {
|
|
111
|
+
const value = this.getValueForPath(this.path.path);
|
|
112
|
+
return this.isGeoJsonGeometry && (value.type === 'Polygon' || value.type === 'MultiPolygon');
|
|
113
|
+
},
|
|
114
|
+
isMultiPolygon() {
|
|
115
|
+
const value = this.getValueForPath(this.path.path);
|
|
116
|
+
return this.isGeoJsonGeometry && value.type === 'MultiPolygon';
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
watch: {
|
|
120
|
+
isGeoJsonGeometry(newValue) {
|
|
121
|
+
if (!newValue) {
|
|
122
|
+
this.detailViewMode = 'text';
|
|
123
|
+
} else if (this.editting) {
|
|
124
|
+
// Default to map view when editing GeoJSON
|
|
125
|
+
this.detailViewMode = 'map';
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
editting(newValue) {
|
|
129
|
+
// When entering edit mode for GeoJSON, default to map view
|
|
130
|
+
if (newValue && this.isGeoJsonGeometry) {
|
|
131
|
+
this.detailViewMode = 'map';
|
|
132
|
+
}
|
|
95
133
|
}
|
|
96
134
|
},
|
|
97
135
|
methods: {
|
|
136
|
+
setDetailViewMode(mode) {
|
|
137
|
+
this.detailViewMode = mode;
|
|
138
|
+
|
|
139
|
+
// When switching to map view, expand the container and value so the map is visible
|
|
140
|
+
if (mode === 'map' && this.isGeoJsonGeometry) {
|
|
141
|
+
if (this.isCollapsed) {
|
|
142
|
+
this.isCollapsed = false;
|
|
143
|
+
}
|
|
144
|
+
if (this.needsTruncation && !this.isValueExpanded) {
|
|
145
|
+
this.isValueExpanded = true;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
},
|
|
98
149
|
handleInputChange(newValue) {
|
|
99
150
|
const currentValue = this.getValueForPath(this.path.path);
|
|
100
151
|
|
|
@@ -165,6 +216,11 @@ module.exports = app => app.component('document-property', {
|
|
|
165
216
|
if (!this.document) {
|
|
166
217
|
return;
|
|
167
218
|
}
|
|
219
|
+
// If there are unsaved changes for this path, use the changed value
|
|
220
|
+
if (Object.prototype.hasOwnProperty.call(this.changes, path)) {
|
|
221
|
+
return this.changes[path];
|
|
222
|
+
}
|
|
223
|
+
// Otherwise, use the document value
|
|
168
224
|
const documentValue = mpath.get(path, this.document);
|
|
169
225
|
return documentValue;
|
|
170
226
|
},
|
|
@@ -184,6 +240,16 @@ module.exports = app => app.component('document-property', {
|
|
|
184
240
|
this.copyResetTimeoutId = null;
|
|
185
241
|
}, 5000);
|
|
186
242
|
},
|
|
243
|
+
getTooltipStyle() {
|
|
244
|
+
if (!this.$refs.infoIcon || !this.showTooltip) {
|
|
245
|
+
return {};
|
|
246
|
+
}
|
|
247
|
+
const rect = this.$refs.infoIcon.getBoundingClientRect();
|
|
248
|
+
return {
|
|
249
|
+
left: (rect.right + 8) + 'px',
|
|
250
|
+
top: rect.top + 'px'
|
|
251
|
+
};
|
|
252
|
+
},
|
|
187
253
|
copyPropertyValue() {
|
|
188
254
|
const textToCopy = this.valueAsString;
|
|
189
255
|
if (textToCopy == null) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mongoosejs/studio",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.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": {
|