@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.
- package/backend/actions/ChatMessage/executeScript.js +5 -1
- package/backend/actions/ChatThread/createChatMessage.js +2 -1
- package/backend/actions/ChatThread/streamChatMessage.js +2 -2
- package/backend/actions/Model/getEstimatedDocumentCounts.js +38 -0
- package/backend/actions/Model/index.js +1 -0
- package/backend/actions/Model/streamDocumentChanges.js +8 -7
- package/backend/actions/Task/getTasks.js +9 -6
- package/backend/authorize.js +1 -0
- package/backend/index.js +11 -3
- package/eslint.config.js +5 -1
- package/express.js +1 -0
- package/frontend/public/app.js +25235 -662
- package/frontend/public/dark-theme.css +365 -0
- package/frontend/public/images/mongoose-studio.svg +4 -0
- package/frontend/public/index.html +21 -1
- package/frontend/public/style.css +5 -7
- package/frontend/public/theme-variables.css +294 -0
- package/frontend/public/tw.css +461 -239
- package/frontend/src/ace-editor/ace-editor.html +4 -0
- package/frontend/src/ace-editor/ace-editor.js +89 -0
- package/frontend/src/aceEditor.js +69 -0
- package/frontend/src/api.js +6 -0
- package/frontend/src/chat/chat-message/chat-message.html +1 -1
- package/frontend/src/chat/chat-message/chat-message.js +1 -1
- package/frontend/src/chat/chat-message-script/chat-message-script.html +51 -34
- package/frontend/src/chat/chat-message-script/chat-message-script.js +12 -55
- package/frontend/src/chat/chat.html +72 -37
- package/frontend/src/chat/chat.js +26 -2
- package/frontend/src/clone-document/clone-document.html +7 -2
- package/frontend/src/clone-document/clone-document.js +1 -8
- package/frontend/src/create-dashboard/create-dashboard.html +11 -6
- package/frontend/src/create-dashboard/create-dashboard.js +0 -7
- package/frontend/src/create-document/create-document.html +15 -9
- package/frontend/src/create-document/create-document.js +5 -12
- package/frontend/src/dashboard/dashboard.html +14 -12
- package/frontend/src/dashboard/dashboard.js +12 -4
- package/frontend/src/dashboard/edit-dashboard/edit-dashboard.html +13 -7
- package/frontend/src/dashboard/edit-dashboard/edit-dashboard.js +13 -21
- package/frontend/src/dashboard-result/dashboard-chart/dashboard-chart.html +19 -17
- package/frontend/src/dashboard-result/dashboard-chart/dashboard-chart.js +97 -2
- package/frontend/src/dashboard-result/dashboard-map/dashboard-map.js +27 -3
- package/frontend/src/dashboard-result/dashboard-result.html +3 -3
- package/frontend/src/dashboards/dashboards.html +101 -109
- package/frontend/src/dashboards/dashboards.js +25 -1
- package/frontend/src/detail-default/detail-default.html +2 -2
- package/frontend/src/detail-default/detail-default.js +24 -3
- package/frontend/src/document/confirm-changes/confirm-changes.html +1 -1
- package/frontend/src/document/confirm-delete/confirm-delete.html +1 -1
- package/frontend/src/document/document.css +1 -1
- package/frontend/src/document/document.html +53 -27
- package/frontend/src/document/document.js +27 -1
- package/frontend/src/document/execute-script/execute-script.html +20 -21
- package/frontend/src/document/execute-script/execute-script.js +1 -43
- package/frontend/src/document-details/document-details.css +4 -9
- package/frontend/src/document-details/document-details.html +34 -33
- package/frontend/src/document-details/document-details.js +2 -53
- package/frontend/src/document-details/document-property/document-property.html +12 -12
- package/frontend/src/edit-array/edit-array.html +7 -6
- package/frontend/src/edit-array/edit-array.js +10 -50
- package/frontend/src/edit-boolean/edit-boolean.html +12 -12
- package/frontend/src/edit-date/edit-date.html +2 -2
- package/frontend/src/edit-default/edit-default.html +1 -1
- package/frontend/src/edit-string/edit-string.html +3 -3
- package/frontend/src/edit-subdocument/edit-subdocument.html +5 -3
- package/frontend/src/edit-subdocument/edit-subdocument.js +1 -15
- package/frontend/src/export-query-results/export-query-results.html +3 -3
- package/frontend/src/json-node/json-node.html +3 -3
- package/frontend/src/list-json/json-node.html +1 -1
- package/frontend/src/models/document-search/document-search.html +3 -3
- package/frontend/src/models/model-switcher/model-switcher.html +53 -0
- package/frontend/src/models/model-switcher/model-switcher.js +123 -0
- package/frontend/src/models/models.css +3 -10
- package/frontend/src/models/models.html +146 -74
- package/frontend/src/models/models.js +142 -4
- package/frontend/src/navbar/navbar.html +157 -97
- package/frontend/src/navbar/navbar.js +32 -13
- package/frontend/src/routes.js +20 -4
- package/frontend/src/splash/splash.html +5 -5
- package/frontend/src/task-by-name/task-by-name.html +15 -0
- package/frontend/src/task-by-name/task-by-name.js +78 -0
- package/frontend/src/task-single/task-single.html +157 -0
- package/frontend/src/task-single/task-single.js +116 -0
- package/frontend/src/tasks/task-details/task-details.html +124 -73
- package/frontend/src/tasks/task-details/task-details.js +166 -10
- package/frontend/src/tasks/tasks.html +37 -48
- package/frontend/src/tasks/tasks.js +11 -50
- package/frontend/src/team/new-invitation/new-invitation.html +8 -8
- package/frontend/src/team/team.html +27 -27
- package/frontend/src/update-document/update-document.html +7 -2
- package/frontend/src/update-document/update-document.js +2 -11
- package/package.json +3 -1
- package/tailwind.config.js +75 -11
|
@@ -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:
|
|
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-
|
|
8
|
-
<div class="ml-2 text-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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>
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const template = require('./model-switcher.html');
|
|
4
|
+
const xss = require('xss');
|
|
5
|
+
|
|
6
|
+
module.exports = app => app.component('model-switcher', {
|
|
7
|
+
template: template,
|
|
8
|
+
props: ['show', 'models', 'recentlyViewedModels', 'modelDocumentCounts'],
|
|
9
|
+
emits: ['close', 'select'],
|
|
10
|
+
data: () => ({
|
|
11
|
+
search: '',
|
|
12
|
+
selectedIndex: 0
|
|
13
|
+
}),
|
|
14
|
+
mounted() {
|
|
15
|
+
this.lastMouseMove = 0;
|
|
16
|
+
this.trackLastMouseMove = () => {
|
|
17
|
+
this.lastMouseMove = Date.now();
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
window.addEventListener('mousemove', this.trackLastMouseMove);
|
|
21
|
+
},
|
|
22
|
+
beforeUnmount() {
|
|
23
|
+
window.removeEventListener('mousemove', this.trackLastMouseMove);
|
|
24
|
+
},
|
|
25
|
+
watch: {
|
|
26
|
+
search() {
|
|
27
|
+
this.selectedIndex = 0;
|
|
28
|
+
},
|
|
29
|
+
show(val) {
|
|
30
|
+
if (val) {
|
|
31
|
+
this.search = '';
|
|
32
|
+
this.selectedIndex = 0;
|
|
33
|
+
this.$nextTick(() => {
|
|
34
|
+
if (this.$refs.searchInput) this.$refs.searchInput.focus();
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
computed: {
|
|
40
|
+
items() {
|
|
41
|
+
const items = [];
|
|
42
|
+
const search = this.search.trim().toLowerCase();
|
|
43
|
+
const recent = (this.recentlyViewedModels || []).filter(m => this.models.includes(m));
|
|
44
|
+
|
|
45
|
+
if (!search && recent.length > 0) {
|
|
46
|
+
for (const model of recent) {
|
|
47
|
+
items.push({ model, section: 'Recently Viewed' });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const filtered = search
|
|
52
|
+
? this.models.filter(m => m.toLowerCase().includes(search))
|
|
53
|
+
: this.models;
|
|
54
|
+
|
|
55
|
+
for (const model of filtered) {
|
|
56
|
+
items.push({ model, section: search ? 'Search Results' : 'All Models' });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return items;
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
methods: {
|
|
63
|
+
selectModel(model) {
|
|
64
|
+
this.$emit('select', model);
|
|
65
|
+
},
|
|
66
|
+
onMouseEnter(index) {
|
|
67
|
+
// If user hasn't moved mouse recently, then this event was likely triggered by scroll
|
|
68
|
+
if (this.lastMouseMove <= Date.now() - 500) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
this.selectedIndex = index;
|
|
72
|
+
},
|
|
73
|
+
handleKeydown(event) {
|
|
74
|
+
const items = this.items;
|
|
75
|
+
if (event.key === 'ArrowDown') {
|
|
76
|
+
event.preventDefault();
|
|
77
|
+
this.selectedIndex = Math.min(this.selectedIndex + 1, items.length - 1);
|
|
78
|
+
this.scrollSelectedIntoView();
|
|
79
|
+
} else if (event.key === 'ArrowUp') {
|
|
80
|
+
event.preventDefault();
|
|
81
|
+
this.selectedIndex = Math.max(this.selectedIndex - 1, 0);
|
|
82
|
+
this.scrollSelectedIntoView();
|
|
83
|
+
} else if (event.key === 'Enter') {
|
|
84
|
+
event.preventDefault();
|
|
85
|
+
if (items.length > 0 && items[this.selectedIndex]) {
|
|
86
|
+
this.selectModel(items[this.selectedIndex].model);
|
|
87
|
+
}
|
|
88
|
+
} else if (event.key === 'Escape') {
|
|
89
|
+
event.preventDefault();
|
|
90
|
+
this.$emit('close');
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
scrollSelectedIntoView() {
|
|
94
|
+
this.$nextTick(() => {
|
|
95
|
+
const container = this.$refs.list;
|
|
96
|
+
if (!container) return;
|
|
97
|
+
const selected = container.querySelector('[data-selected="true"]');
|
|
98
|
+
if (selected) selected.scrollIntoView({ block: 'nearest' });
|
|
99
|
+
});
|
|
100
|
+
},
|
|
101
|
+
highlightMatch(model) {
|
|
102
|
+
const search = this.search.trim();
|
|
103
|
+
if (!search) return model;
|
|
104
|
+
const idx = model.toLowerCase().indexOf(search.toLowerCase());
|
|
105
|
+
if (idx === -1) return model;
|
|
106
|
+
const before = model.slice(0, idx);
|
|
107
|
+
const match = model.slice(idx, idx + search.length);
|
|
108
|
+
const after = model.slice(idx + search.length);
|
|
109
|
+
return `${xss(before)}<strong>${xss(match)}</strong>${xss(after)}`;
|
|
110
|
+
},
|
|
111
|
+
formatCompactCount(value) {
|
|
112
|
+
if (typeof value !== 'number') return '—';
|
|
113
|
+
if (value < 1000) return `${value}`;
|
|
114
|
+
const formatValue = (number, suffix) => {
|
|
115
|
+
const rounded = (Math.round(number * 10) / 10).toFixed(1).replace(/\.0$/, '');
|
|
116
|
+
return `${rounded}${suffix}`;
|
|
117
|
+
};
|
|
118
|
+
if (value < 1000000) return formatValue(value / 1000, 'k');
|
|
119
|
+
if (value < 1000000000) return formatValue(value / 1000000, 'M');
|
|
120
|
+
return formatValue(value / 1000000000, 'B');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
});
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
.models .model-selector {
|
|
14
|
-
background-color: #eee;
|
|
15
14
|
flex-grow: 0;
|
|
16
15
|
padding: 15px;
|
|
17
16
|
padding-top: 0px;
|
|
@@ -82,17 +81,11 @@
|
|
|
82
81
|
}
|
|
83
82
|
|
|
84
83
|
.models .documents-menu {
|
|
85
|
-
position:
|
|
86
|
-
|
|
84
|
+
position: sticky;
|
|
85
|
+
top: 0;
|
|
86
|
+
z-index: 2;
|
|
87
87
|
padding: 4px;
|
|
88
88
|
display: flex;
|
|
89
|
-
width: 100vw;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
@media (min-width: 1024px) {
|
|
93
|
-
.models .documents-menu {
|
|
94
|
-
width: calc(100vw - 12rem);
|
|
95
|
-
}
|
|
96
89
|
}
|
|
97
90
|
|
|
98
91
|
.models .documents-menu .search-input {
|