@mongoosejs/studio 0.2.13 → 0.3.1

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 +4 -1
  3. package/backend/actions/ChatThread/streamChatMessage.js +4 -2
  4. package/backend/actions/Dashboard/updateDashboard.js +2 -2
  5. package/backend/actions/Task/getTaskOverview.js +102 -0
  6. package/backend/actions/Task/getTasks.js +85 -45
  7. package/backend/actions/Task/index.js +1 -0
  8. package/eslint.config.js +4 -1
  9. package/frontend/public/app.js +25025 -762
  10. package/frontend/public/dark-theme.css +365 -0
  11. package/frontend/public/images/mongoose-studio.svg +4 -0
  12. package/frontend/public/index.html +21 -1
  13. package/frontend/public/style.css +5 -7
  14. package/frontend/public/theme-variables.css +294 -0
  15. package/frontend/public/tw.css +348 -213
  16. package/frontend/src/_util/dateRange.js +82 -0
  17. package/frontend/src/ace-editor/ace-editor.html +4 -0
  18. package/frontend/src/ace-editor/ace-editor.js +95 -0
  19. package/frontend/src/aceEditor.js +69 -0
  20. package/frontend/src/api.js +6 -0
  21. package/frontend/src/chat/chat-message/chat-message.html +1 -1
  22. package/frontend/src/chat/chat-message/chat-message.js +1 -1
  23. package/frontend/src/chat/chat-message-script/chat-message-script.html +54 -42
  24. package/frontend/src/chat/chat-message-script/chat-message-script.js +6 -55
  25. package/frontend/src/chat/chat.html +68 -39
  26. package/frontend/src/chat/chat.js +26 -2
  27. package/frontend/src/clone-document/clone-document.html +7 -2
  28. package/frontend/src/clone-document/clone-document.js +1 -8
  29. package/frontend/src/create-dashboard/create-dashboard.html +11 -6
  30. package/frontend/src/create-dashboard/create-dashboard.js +0 -7
  31. package/frontend/src/create-document/create-document.html +15 -9
  32. package/frontend/src/create-document/create-document.js +5 -12
  33. package/frontend/src/dashboard/dashboard.html +14 -12
  34. package/frontend/src/dashboard/dashboard.js +21 -4
  35. package/frontend/src/dashboard/edit-dashboard/edit-dashboard.html +13 -7
  36. package/frontend/src/dashboard/edit-dashboard/edit-dashboard.js +16 -23
  37. package/frontend/src/dashboard-result/dashboard-chart/dashboard-chart.html +19 -17
  38. package/frontend/src/dashboard-result/dashboard-chart/dashboard-chart.js +97 -2
  39. package/frontend/src/dashboard-result/dashboard-map/dashboard-map.js +27 -3
  40. package/frontend/src/dashboard-result/dashboard-result.html +3 -3
  41. package/frontend/src/dashboard-result/dashboard-result.js +3 -0
  42. package/frontend/src/dashboard-result/dashboard-table/dashboard-table.html +34 -0
  43. package/frontend/src/dashboard-result/dashboard-table/dashboard-table.js +37 -0
  44. package/frontend/src/dashboards/dashboards.html +101 -109
  45. package/frontend/src/dashboards/dashboards.js +25 -1
  46. package/frontend/src/detail-default/detail-default.html +2 -2
  47. package/frontend/src/detail-default/detail-default.js +24 -3
  48. package/frontend/src/document/confirm-changes/confirm-changes.html +1 -1
  49. package/frontend/src/document/confirm-delete/confirm-delete.html +1 -1
  50. package/frontend/src/document/document.css +1 -1
  51. package/frontend/src/document/document.html +28 -28
  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 -80
  74. package/frontend/src/models/models.js +116 -7
  75. package/frontend/src/navbar/navbar.html +157 -97
  76. package/frontend/src/navbar/navbar.js +31 -12
  77. package/frontend/src/routes.js +1 -1
  78. package/frontend/src/splash/splash.html +5 -5
  79. package/frontend/src/task-by-name/task-by-name.html +77 -7
  80. package/frontend/src/task-by-name/task-by-name.js +84 -9
  81. package/frontend/src/task-single/task-single.html +29 -29
  82. package/frontend/src/task-single/task-single.js +10 -10
  83. package/frontend/src/tasks/task-details/task-details.html +43 -43
  84. package/frontend/src/tasks/task-details/task-details.js +9 -3
  85. package/frontend/src/tasks/tasks.html +36 -35
  86. package/frontend/src/tasks/tasks.js +27 -143
  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
@@ -0,0 +1,82 @@
1
+ 'use strict';
2
+
3
+ const DATE_FILTERS = [
4
+ { value: 'last_hour', label: 'Last Hour' },
5
+ { value: 'today', label: 'Today' },
6
+ { value: 'yesterday', label: 'Yesterday' },
7
+ { value: 'thisWeek', label: 'This Week' },
8
+ { value: 'lastWeek', label: 'Last Week' },
9
+ { value: 'thisMonth', label: 'This Month' },
10
+ { value: 'lastMonth', label: 'Last Month' }
11
+ ];
12
+
13
+ const DATE_FILTER_VALUES = DATE_FILTERS.map(f => f.value);
14
+
15
+ /**
16
+ * Returns { start, end } Date objects for a given range key (e.g. 'last_hour', 'today').
17
+ * Month ranges use UTC boundaries.
18
+ * @param {string} selectedRange - One of DATE_FILTER_VALUES
19
+ * @returns {{ start: Date, end: Date }}
20
+ */
21
+ function getDateRangeForRange(selectedRange) {
22
+ const now = new Date();
23
+ let start, end;
24
+ switch (selectedRange) {
25
+ case 'last_hour':
26
+ start = new Date();
27
+ start.setHours(start.getHours() - 1);
28
+ end = new Date();
29
+ break;
30
+ case 'today':
31
+ start = new Date();
32
+ start.setHours(0, 0, 0, 0);
33
+ end = new Date();
34
+ end.setHours(23, 59, 59, 999);
35
+ break;
36
+ case 'yesterday':
37
+ start = new Date(now);
38
+ start.setDate(start.getDate() - 1);
39
+ start.setHours(0, 0, 0, 0);
40
+ end = new Date(start);
41
+ end.setHours(23, 59, 59, 999);
42
+ break;
43
+ case 'thisWeek':
44
+ start = new Date(now.getTime() - (7 * 86400000));
45
+ start.setHours(0, 0, 0, 0);
46
+ end = new Date();
47
+ end.setHours(23, 59, 59, 999);
48
+ break;
49
+ case 'lastWeek':
50
+ start = new Date(now.getTime() - (14 * 86400000));
51
+ start.setHours(0, 0, 0, 0);
52
+ end = new Date(now.getTime() - (7 * 86400000));
53
+ end.setHours(23, 59, 59, 999);
54
+ break;
55
+ case 'thisMonth': {
56
+ const y = now.getUTCFullYear();
57
+ const m = now.getUTCMonth();
58
+ start = new Date(Date.UTC(y, m, 1, 0, 0, 0, 0));
59
+ end = new Date(Date.UTC(y, m + 1, 0, 23, 59, 59, 999));
60
+ break;
61
+ }
62
+ case 'lastMonth': {
63
+ const y = now.getUTCFullYear();
64
+ const m = now.getUTCMonth();
65
+ start = new Date(Date.UTC(y, m - 1, 1, 0, 0, 0, 0));
66
+ end = new Date(Date.UTC(y, m, 0, 23, 59, 59, 999));
67
+ break;
68
+ }
69
+ default:
70
+ start = new Date();
71
+ start.setHours(start.getHours() - 1);
72
+ end = new Date();
73
+ break;
74
+ }
75
+ return { start, end };
76
+ }
77
+
78
+ module.exports = {
79
+ DATE_FILTERS,
80
+ DATE_FILTER_VALUES,
81
+ getDateRangeForRange
82
+ };
@@ -0,0 +1,4 @@
1
+ <div
2
+ ref="container"
3
+ class="ace-editor-container w-full min-h-[120px] border border-edge-strong rounded-md focus-within:ring-2 focus-within:ring-blue-500 focus-within:border-transparent font-mono"
4
+ ></div>
@@ -0,0 +1,95 @@
1
+ 'use strict';
2
+
3
+ const template = require('./ace-editor.html');
4
+ const { createAceEditor, destroyAceEditor } = require('../aceEditor');
5
+
6
+ module.exports = app => app.component('ace-editor', {
7
+ template,
8
+ props: {
9
+ modelValue: {
10
+ type: String,
11
+ default: ''
12
+ },
13
+ // Support :value for explicit binding
14
+ value: {
15
+ type: String,
16
+ default: ''
17
+ },
18
+ mode: {
19
+ type: String,
20
+ default: 'javascript',
21
+ validator: (v) => ['javascript', 'json'].includes(v)
22
+ },
23
+ lineNumbers: {
24
+ type: Boolean,
25
+ default: true
26
+ },
27
+ readOnly: {
28
+ type: Boolean,
29
+ default: false
30
+ },
31
+ wrap: {
32
+ type: Boolean,
33
+ default: false
34
+ },
35
+ minLines: { type: Number, default: null },
36
+ maxLines: { type: Number, default: null }
37
+ },
38
+ emits: ['input', 'update:modelValue'],
39
+ data() {
40
+ return { editor: null };
41
+ },
42
+ mounted() {
43
+ this.$nextTick(() => {
44
+ const container = this.$refs.container;
45
+ if (!container) return;
46
+ this.editor = createAceEditor(container, {
47
+ value: this.modelValue !== '' ? this.modelValue : this.value,
48
+ mode: this.mode,
49
+ lineNumbers: this.lineNumbers,
50
+ readOnly: this.readOnly,
51
+ wrap: this.wrap,
52
+ minLines: this.minLines,
53
+ maxLines: this.maxLines
54
+ });
55
+ this.editor.session.on('change', () => {
56
+ const val = this.editor.getValue();
57
+ this.$emit('input', val);
58
+ this.$emit('update:modelValue', val);
59
+ });
60
+ });
61
+ },
62
+ beforeDestroy() {
63
+ if (this.editor) {
64
+ destroyAceEditor(this.editor);
65
+ this.editor = null;
66
+ }
67
+ },
68
+ watch: {
69
+ modelValue(newVal) {
70
+ const val = newVal ?? '';
71
+ if (this.editor && this.editor.getValue() !== val) {
72
+ this.editor.setValue(val, -1);
73
+ }
74
+ },
75
+ value(newVal) {
76
+ const val = newVal ?? '';
77
+ if (this.editor && this.editor.getValue() !== val) {
78
+ this.editor.setValue(val, -1);
79
+ }
80
+ }
81
+ },
82
+ methods: {
83
+ getValue() {
84
+ if (this.editor) {
85
+ return this.editor.getValue();
86
+ }
87
+ return this.modelValue !== '' ? this.modelValue : this.value;
88
+ },
89
+ setValue(val) {
90
+ if (this.editor) {
91
+ this.editor.setValue(val ?? '', -1);
92
+ }
93
+ }
94
+ }
95
+ });
@@ -0,0 +1,69 @@
1
+ 'use strict';
2
+
3
+ const ace = require('ace-builds');
4
+ require('ace-builds/src-noconflict/mode-javascript');
5
+ require('ace-builds/src-noconflict/mode-json');
6
+ require('ace-builds/src-noconflict/theme-chrome');
7
+ require('ace-builds/src-noconflict/theme-one_dark');
8
+
9
+ const LIGHT_THEME = 'ace/theme/chrome';
10
+ const DARK_THEME = 'ace/theme/one_dark';
11
+
12
+ function isDarkMode() {
13
+ return typeof document !== 'undefined' && document.documentElement.classList.contains('dark');
14
+ }
15
+
16
+ /**
17
+ * Create an Ace editor on a container element (div). The container must have
18
+ * explicit dimensions (e.g. height/min-height and width).
19
+ * @param {HTMLElement} container - A div element to attach the editor to
20
+ * @param {Object} options - { value: string, mode: 'javascript'|'json', lineNumbers: boolean, ... }
21
+ * @returns {ace.Ace.Editor} The Ace editor instance
22
+ */
23
+ function createAceEditor(container, options = {}) {
24
+ const {
25
+ value = '',
26
+ mode = 'javascript',
27
+ lineNumbers = true,
28
+ minLines,
29
+ maxLines,
30
+ readOnly = false,
31
+ wrap = false
32
+ } = options;
33
+
34
+ const editor = ace.edit(container);
35
+ editor.setTheme(isDarkMode() ? DARK_THEME : LIGHT_THEME);
36
+ editor.session.setMode(mode === 'json' ? 'ace/mode/json' : 'ace/mode/javascript');
37
+ editor.setValue(value, -1);
38
+ editor.setOptions({
39
+ showLineNumbers: lineNumbers,
40
+ readOnly,
41
+ wrap
42
+ });
43
+ if (minLines != null) editor.setOption('minLines', minLines);
44
+ if (maxLines != null) editor.setOption('maxLines', maxLines);
45
+
46
+ // Listen for theme toggles
47
+ const onThemeChanged = (e) => {
48
+ editor.setTheme(e.detail?.dark ? DARK_THEME : LIGHT_THEME);
49
+ };
50
+ document.documentElement.addEventListener('studio-theme-changed', onThemeChanged);
51
+ editor._studioThemeHandler = onThemeChanged;
52
+
53
+ return editor;
54
+ }
55
+
56
+ /**
57
+ * Destroy an Ace editor and release resources.
58
+ * @param {ace.Ace.Editor|null} editor - The editor instance from createAceEditor
59
+ */
60
+ function destroyAceEditor(editor) {
61
+ if (editor) {
62
+ if (editor._studioThemeHandler) {
63
+ document.documentElement.removeEventListener('studio-theme-changed', editor._studioThemeHandler);
64
+ }
65
+ editor.destroy();
66
+ }
67
+ }
68
+
69
+ module.exports = { createAceEditor, destroyAceEditor };
@@ -185,6 +185,9 @@ if (window.MONGOOSE_STUDIO_CONFIG.isLambda) {
185
185
  getTasks: function getTasks(params) {
186
186
  return client.post('', { action: 'Task.getTasks', ...params }).then(res => res.data);
187
187
  },
188
+ getTaskOverview: function getTaskOverview(params) {
189
+ return client.post('', { action: 'Task.getTaskOverview', ...params }).then(res => res.data);
190
+ },
188
191
  rescheduleTask: function rescheduleTask(params) {
189
192
  return client.post('', { action: 'Task.rescheduleTask', ...params }).then(res => res.data);
190
193
  },
@@ -519,6 +522,9 @@ if (window.MONGOOSE_STUDIO_CONFIG.isLambda) {
519
522
  getTasks: function getTasks(params) {
520
523
  return client.post('/Task/getTasks', params).then(res => res.data);
521
524
  },
525
+ getTaskOverview: function getTaskOverview(params) {
526
+ return client.post('/Task/getTaskOverview', params).then(res => res.data);
527
+ },
522
528
  rescheduleTask: function rescheduleTask(params) {
523
529
  return client.post('/Task/rescheduleTask', params).then(res => res.data);
524
530
  },
@@ -3,7 +3,7 @@
3
3
  class="min-w-0 max-w-[calc(100vw-3rem)] lg:max-w-[calc(100vw-15rem)]"
4
4
  :class="{'text-right': message.role === 'user'}">
5
5
 
6
- <div class="text-sm text-gray-900 rounded-md inline-block relative" :class="styleForMessage">
6
+ <div class="text-sm text-content rounded-md inline-block relative" :class="styleForMessage">
7
7
  <div v-for="part in contentSplitByScripts">
8
8
  <div v-if="part.type === 'text'" v-html="marked(part.content)">
9
9
  </div>
@@ -9,7 +9,7 @@ module.exports = app => app.component('chat-message', {
9
9
  props: ['message', 'targetDashboardId'],
10
10
  computed: {
11
11
  styleForMessage() {
12
- return this.message.role === 'user' ? 'p-3 bg-gray-100' : 'py-3 pr-3';
12
+ return this.message.role === 'user' ? 'p-3 bg-muted' : 'py-3 pr-3';
13
13
  },
14
14
  contentSplitByScripts() {
15
15
  const content = this.message.content;
@@ -1,39 +1,39 @@
1
- <div class="relative border rounded bg-white my-1 text-black text-sm overflow-hidden">
2
- <div class="flex border-b py-1 pl-1 text-xs font-medium bg-white">
1
+ <div class="chat-message-script relative border rounded bg-surface my-1 text-content text-sm overflow-hidden">
2
+ <div class="flex border-b py-1 pl-1 text-xs font-medium bg-surface">
3
3
  <button
4
- class="px-4 py-1 border-r border-gray-300 text-gray-700 font-semibold transition-colors duration-200 focus:outline-none"
4
+ class="px-4 py-1 border-r border-edge-strong text-content-secondary font-semibold transition-colors duration-200 focus:outline-none"
5
5
  :class="[
6
6
  'rounded-l-md',
7
7
  activeTab === 'code'
8
8
  ? 'bg-gray-700 text-white shadow'
9
- : 'bg-gray-100 hover:bg-gray-200 text-gray-600'
9
+ : 'bg-muted hover:bg-muted text-gray-600'
10
10
  ]"
11
11
  @click="activeTab = 'code'">
12
12
  Code
13
13
  </button>
14
14
  <button
15
- class="px-4 py-1 text-gray-700 font-semibold transition-colors duration-200 focus:outline-none"
15
+ class="px-4 py-1 text-content-secondary font-semibold transition-colors duration-200 focus:outline-none"
16
16
  :class="[
17
17
  'rounded-r-md',
18
18
  activeTab === 'output'
19
19
  ? 'bg-gray-700 text-white shadow'
20
- : 'bg-gray-100 hover:bg-gray-200 text-gray-600'
20
+ : 'bg-muted hover:bg-muted text-gray-600'
21
21
  ]"
22
22
  @click="activeTab = 'output'">
23
23
  Output
24
24
  </button>
25
- <div class="ml-auto mr-1 flex">
25
+ <div class="ml-auto mr-1 flex items-center">
26
26
  <button
27
27
  v-if="activeTab === 'output'"
28
- class="px-2 py-1 mr-1 text-xs bg-blue-500 text-white border-none rounded cursor-pointer hover:bg-blue-600 transition-colors flex items-center"
28
+ class="px-2 py-1 mr-1 text-xs bg-gray-700 text-white border-none rounded cursor-pointer hover:bg-primary-hover transition-colors flex items-center"
29
29
  @click="openDetailModal">
30
- <svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
30
+ <svg xmlns="http://www.w3.org/2000/svg" style="height: 1.2em;" fill="none" viewBox="0 0 24 24" stroke="currentColor">
31
31
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 1v4m0 0h-4m4 0l-5-5" />
32
32
  </svg>
33
33
  </button>
34
34
  <button
35
35
  v-if="activeTab === 'code' && !isEditing"
36
- class="px-2 py-1 mr-1 text-xs bg-gray-500 text-white border-none rounded cursor-pointer hover:bg-gray-600 transition-colors flex items-center"
36
+ class="px-2 py-1 mr-1 text-xs bg-page0 text-white border-none rounded cursor-pointer hover:bg-gray-600 transition-colors flex items-center"
37
37
  @click.stop="startEditing">
38
38
  <svg xmlns="http://www.w3.org/2000/svg" class="h-3 w-3" viewBox="0 0 20 20" fill="currentColor">
39
39
  <path d="M13.586 3.586a2 2 0 112.828 2.828l-.793.793-2.828-2.828.793-.793zM4 13.5V16h2.5l7.086-7.086-2.828-2.828L4 13.5z" />
@@ -41,7 +41,7 @@
41
41
  </button>
42
42
  <async-button
43
43
  v-if="!isEditing"
44
- class="px-2 py-1 text-xs bg-blue-600 hover:bg-blue-700 text-white border-none rounded cursor-pointer transition-colors disabled:bg-gray-400"
44
+ class="px-2 py-1 text-xs bg-primary hover:bg-primary-hover text-primary-text border-none rounded cursor-pointer transition-colors disabled:bg-gray-400"
45
45
  @click="executeScript">
46
46
  Run
47
47
  </async-button>
@@ -55,25 +55,25 @@
55
55
  </button>
56
56
  <div
57
57
  v-if="showDropdown"
58
- class="absolute right-0 z-10 mt-1 w-64 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black/5">
58
+ class="absolute right-0 z-10 mt-1 w-64 origin-top-right rounded-md bg-surface py-1 shadow-lg ring-1 ring-black/5">
59
59
  <button
60
- class="block w-full text-left px-4 py-2 text-xs text-gray-700 hover:bg-gray-100"
60
+ class="block w-full text-left px-4 py-2 text-xs text-content-secondary hover:bg-muted"
61
61
  @click="openCreateDashboardModal(); showDropdown = false">
62
62
  Create Dashboard
63
63
  </button>
64
64
  <button
65
- class="block w-full text-left px-4 py-2 text-xs text-gray-700 hover:bg-gray-100"
65
+ class="block w-full text-left px-4 py-2 text-xs text-content-secondary hover:bg-muted"
66
66
  @click="copyOutput(); showDropdown = false">
67
67
  Copy Output As Text
68
68
  </button>
69
69
  <button
70
70
  v-if="canOverwriteDashboard"
71
- class="block w-full text-left px-4 py-2 text-xs text-gray-700 hover:bg-gray-100"
71
+ class="block w-full text-left px-4 py-2 text-xs text-content-secondary hover:bg-muted"
72
72
  @click="openOverwriteDashboardConfirmation(); showDropdown = false">
73
73
  Overwrite Dashboard
74
74
  </button>
75
75
  <button
76
- class="block w-full text-left px-4 py-2 text-xs text-gray-700 hover:bg-gray-100"
76
+ class="block w-full text-left px-4 py-2 text-xs text-content-secondary hover:bg-muted"
77
77
  @click="$emit('copyMessage'); showDropdown = false">
78
78
  Copy Full Message
79
79
  </button>
@@ -83,9 +83,14 @@
83
83
  </div>
84
84
 
85
85
  <div class="p-0 max-h-[50vh] max-w-[calc(100vw-4rem)] lg:max-w-[calc(100vw-20rem)] overflow-y-auto" v-show="activeTab === 'code'">
86
- <div v-if="isEditing" class="flex flex-col space-y-2">
87
- <div class="border border-gray-200">
88
- <textarea ref="scriptEditor" class="w-full h-[45vh]" @input="handleScriptInput"></textarea>
86
+ <div v-if="isEditing" class="flex flex-col space-y-2 chat-script-editor-wrap">
87
+ <div class="border border-edge">
88
+ <ace-editor
89
+ v-model="editedScript"
90
+ mode="javascript"
91
+ :line-numbers="true"
92
+ class="w-full h-[45vh] min-h-[200px]"
93
+ />
89
94
  </div>
90
95
  <div class="flex justify-end gap-2 pb-2">
91
96
  <button
@@ -100,17 +105,19 @@
100
105
  </async-button>
101
106
  </div>
102
107
  </div>
103
- <pre v-else class="whitespace-pre-wrap !my-0 !bg-zinc-50"><code v-text="script" ref="code" :class="'language-' + language"></code></pre>
108
+ <pre v-else class="whitespace-pre-wrap !my-0 bg-muted"><code v-text="script" ref="code" :class="'language-' + language"></code></pre>
104
109
  </div>
105
110
 
106
- <div class="p-3 whitespace-pre-wrap max-h-[50vh] overflow-y-auto bg-white border-t max-w-[calc(100vw-4rem)] lg:max-w-[calc(100vw-20rem)] relative" v-show="activeTab === 'output'">
107
- <dashboard-chart v-if="message.executionResult?.output?.$chart" :value="message.executionResult?.output" />
108
- <dashboard-map v-else-if="message.executionResult?.output?.$featureCollection" :value="message.executionResult?.output" />
109
- <pre v-else>{{ message.executionResult?.output || 'No output' }}</pre>
111
+ <div class="p-3 whitespace-pre-wrap max-h-[50vh] overflow-y-auto bg-surface border-t max-w-[calc(100vw-4rem)] lg:max-w-[calc(100vw-20rem)] relative" v-show="activeTab === 'output'">
112
+ <dashboard-result
113
+ v-if="message.executionResult?.output != null"
114
+ :result="message.executionResult.output">
115
+ </dashboard-result>
116
+ <pre v-else>No output</pre>
110
117
 
111
- <div v-if="message.executionResult?.logs?.length" class="mt-3 pt-3 border-t border-gray-200">
118
+ <div v-if="message.executionResult?.logs?.length" class="mt-3 pt-3 border-t border-edge">
112
119
  <div class="text-xs font-semibold text-gray-600 uppercase tracking-wide">Console</div>
113
- <pre class="mt-1 bg-gray-100 text-gray-900 p-3 rounded whitespace-pre-wrap overflow-x-auto max-h-[280px]">{{ message.executionResult.logs }}</pre>
120
+ <pre class="mt-1 bg-muted text-content p-3 rounded whitespace-pre-wrap overflow-x-auto max-h-[280px]">{{ message.executionResult.logs }}</pre>
114
121
  </div>
115
122
  </div>
116
123
 
@@ -118,12 +125,12 @@
118
125
  <template #body>
119
126
  <div class="absolute font-mono right-1 top-1 cursor-pointer text-xl" @click="showDetailModal = false;">&times;</div>
120
127
  <div class="h-full overflow-auto">
121
- <dashboard-chart v-if="message.executionResult?.output?.$chart" :value="message.executionResult?.output" :responsive="true" />
122
- <dashboard-map
123
- v-else-if="message.executionResult?.output?.$featureCollection"
124
- :value="message.executionResult?.output"
125
- height="80vh" />
126
- <pre v-else class="whitespace-pre-wrap">{{ message.executionResult?.output || 'No output' }}</pre>
128
+ <dashboard-result
129
+ v-if="message.executionResult?.output != null"
130
+ :result="message.executionResult.output"
131
+ :fullscreen="true">
132
+ </dashboard-result>
133
+ <pre v-else class="whitespace-pre-wrap">No output</pre>
127
134
  </div>
128
135
  </template>
129
136
  </modal>
@@ -131,19 +138,24 @@
131
138
  <template #body>
132
139
  <div class="modal-exit" @click="showCreateDashboardModal = false">&times;</div>
133
140
  <div>
134
- <div class="mt-4 text-gray-900 font-semibold">Create Dashboard</div>
141
+ <div class="mt-4 text-content font-semibold">Create Dashboard</div>
135
142
  <div class="mt-4">
136
- <label class="block text-sm font-medium leading-6 text-gray-900">Title</label>
143
+ <label class="block text-sm font-medium leading-6 text-content">Title</label>
137
144
  <div class="mt-2">
138
145
  <div class="w-full flex rounded-md shadow-sm ring-1 ring-inset ring-gray-300 focus-within:ring-2 focus-within:ring-inset focus-within:ring-teal-600">
139
- <input type="text" v-model="newDashboardTitle" class="outline-none block flex-1 border-0 bg-transparent py-1.5 pl-1 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6" placeholder="My Dashboard">
146
+ <input type="text" v-model="newDashboardTitle" class="outline-none block flex-1 border-0 bg-transparent py-1.5 pl-1 text-content placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6" placeholder="My Dashboard">
140
147
  </div>
141
148
  </div>
142
149
  </div>
143
150
  <div class="my-4">
144
- <label class="block text-sm font-medium leading-6 text-gray-900">Code</label>
145
- <div class="border border-gray-200">
146
- <textarea class="p-2 h-[300px] w-full" ref="dashboardCodeEditor"></textarea>
151
+ <label class="block text-sm font-medium leading-6 text-content">Code</label>
152
+ <div class="border border-edge">
153
+ <ace-editor
154
+ v-model="dashboardCode"
155
+ mode="javascript"
156
+ :line-numbers="true"
157
+ class="h-[300px] w-full"
158
+ />
147
159
  </div>
148
160
  </div>
149
161
  <async-button
@@ -173,14 +185,14 @@
173
185
  <template #body>
174
186
  <div class="modal-exit" @click="showOverwriteDashboardConfirmationModal = false">&times;</div>
175
187
  <div>
176
- <div class="mt-4 text-gray-900 font-semibold">Overwrite Dashboard</div>
177
- <p class="mt-2 text-sm text-gray-700">
188
+ <div class="mt-4 text-content font-semibold">Overwrite Dashboard</div>
189
+ <p class="mt-2 text-sm text-content-secondary">
178
190
  This will replace the linked dashboard's code with the script below.
179
191
  </p>
180
192
  <p class="mt-1 text-xs text-gray-600 break-all" v-if="targetDashboardId">
181
193
  Dashboard ID: {{ targetDashboardId }}
182
194
  </p>
183
- <div class="my-4 border border-gray-200 bg-gray-50 rounded">
195
+ <div class="my-4 border border-edge bg-page rounded">
184
196
  <pre class="p-2 h-[300px] overflow-auto whitespace-pre-wrap text-xs">{{ overwriteDashboardCode }}</pre>
185
197
  </div>
186
198
  <div class="flex items-center gap-2">
@@ -190,7 +202,7 @@
190
202
  Confirm Overwrite
191
203
  </async-button>
192
204
  <button
193
- class="px-2.5 py-1.5 rounded-md text-sm font-semibold text-gray-700 bg-gray-200 hover:bg-gray-300"
205
+ class="px-2.5 py-1.5 rounded-md text-sm font-semibold text-content-secondary bg-gray-200 hover:bg-gray-300"
194
206
  @click="showOverwriteDashboardConfirmationModal = false">
195
207
  Cancel
196
208
  </button>
@@ -1,4 +1,4 @@
1
- /* global CodeMirror, Prism */
1
+ /* global Prism */
2
2
  'use strict';
3
3
 
4
4
  const api = require('../../api');
@@ -18,9 +18,7 @@ module.exports = app => app.component('chat-message-script', {
18
18
  newDashboardTitle: '',
19
19
  dashboardCode: '',
20
20
  createError: null,
21
- dashboardEditor: null,
22
21
  isEditing: false,
23
- codeEditor: null,
24
22
  editedScript: null,
25
23
  overwriteDashboardCode: '',
26
24
  overwriteError: null
@@ -28,7 +26,7 @@ module.exports = app => app.component('chat-message-script', {
28
26
  },
29
27
  computed: {
30
28
  styleForMessage() {
31
- return this.message.role === 'user' ? 'bg-gray-100' : '';
29
+ return this.message.role === 'user' ? 'bg-muted' : '';
32
30
  },
33
31
  canOverwriteDashboard() {
34
32
  return !!this.targetDashboardId;
@@ -36,10 +34,7 @@ module.exports = app => app.component('chat-message-script', {
36
34
  },
37
35
  methods: {
38
36
  async executeScript() {
39
- let scriptToRun = this.script;
40
- if (this.isEditing) {
41
- scriptToRun = this.codeEditor ? this.codeEditor.getValue() : this.editedScript;
42
- }
37
+ const scriptToRun = this.isEditing ? this.editedScript : this.script;
43
38
  this.editedScript = scriptToRun;
44
39
  const { chatMessage } = await api.ChatMessage.executeScript({
45
40
  chatMessageId: this.message._id,
@@ -66,25 +61,12 @@ module.exports = app => app.component('chat-message-script', {
66
61
  this.dashboardCode = this.script;
67
62
  this.createError = null;
68
63
  this.showCreateDashboardModal = true;
69
- this.$nextTick(() => {
70
- if (this.dashboardEditor) {
71
- this.dashboardEditor.toTextArea();
72
- }
73
- this.$refs.dashboardCodeEditor.value = this.dashboardCode;
74
- this.dashboardEditor = CodeMirror.fromTextArea(this.$refs.dashboardCodeEditor, {
75
- mode: 'javascript',
76
- lineNumbers: true
77
- });
78
- this.dashboardEditor.on('change', () => {
79
- this.dashboardCode = this.dashboardEditor.getValue();
80
- });
81
- });
82
64
  },
83
65
  openOverwriteDashboardConfirmation() {
84
66
  if (!this.canOverwriteDashboard) {
85
67
  return;
86
68
  }
87
- this.overwriteDashboardCode = this.codeEditor?.getValue?.() ?? this.script;
69
+ this.overwriteDashboardCode = this.isEditing ? this.editedScript : this.script;
88
70
  this.overwriteError = null;
89
71
  this.showOverwriteDashboardConfirmationModal = true;
90
72
  },
@@ -94,39 +76,16 @@ module.exports = app => app.component('chat-message-script', {
94
76
  startEditing() {
95
77
  this.isEditing = true;
96
78
  this.editedScript = this.script;
97
- this.$nextTick(() => {
98
- if (!this.$refs.scriptEditor) {
99
- return;
100
- }
101
- this.$refs.scriptEditor.value = this.editedScript;
102
- if (typeof CodeMirror === 'undefined') {
103
- return;
104
- }
105
- this.destroyCodeMirror();
106
- this.codeEditor = CodeMirror.fromTextArea(this.$refs.scriptEditor, {
107
- mode: 'javascript',
108
- lineNumbers: true,
109
- smartIndent: false
110
- });
111
- });
112
79
  },
113
80
  cancelEditing() {
114
81
  this.isEditing = false;
115
- this.destroyCodeMirror();
116
82
  this.editedScript = this.script;
117
83
  this.highlightCode();
118
84
  },
119
85
  finishEditing() {
120
86
  this.isEditing = false;
121
- this.destroyCodeMirror();
122
87
  this.highlightCode();
123
88
  },
124
- destroyCodeMirror() {
125
- if (this.codeEditor) {
126
- this.codeEditor.toTextArea();
127
- this.codeEditor = null;
128
- }
129
- },
130
89
  handleScriptInput(event) {
131
90
  this.editedScript = event?.target?.value || '';
132
91
  },
@@ -144,9 +103,8 @@ module.exports = app => app.component('chat-message-script', {
144
103
  }
145
104
  },
146
105
  async createDashboardFromScript() {
147
- this.dashboardCode = this.dashboardEditor.getValue();
148
106
  const { dashboard } = await api.Dashboard.createDashboard({
149
- code: this.dashboardCode,
107
+ code: this.dashboardCode || '',
150
108
  title: this.newDashboardTitle
151
109
  }).catch(err => {
152
110
  if (err.response?.data?.message) {
@@ -167,7 +125,7 @@ module.exports = app => app.component('chat-message-script', {
167
125
  return;
168
126
  }
169
127
 
170
- this.overwriteDashboardCode = this.codeEditor?.getValue?.() ?? this.script;
128
+ this.overwriteDashboardCode = this.isEditing ? this.editedScript : this.script;
171
129
 
172
130
  const params = {
173
131
  dashboardId: this.targetDashboardId,
@@ -209,12 +167,6 @@ module.exports = app => app.component('chat-message-script', {
209
167
  }
210
168
  },
211
169
  watch: {
212
- showCreateDashboardModal(val) {
213
- if (!val && this.dashboardEditor) {
214
- this.dashboardEditor.toTextArea();
215
- this.dashboardEditor = null;
216
- }
217
- },
218
170
  script(newScript) {
219
171
  if (!this.isEditing) {
220
172
  this.editedScript = newScript;
@@ -232,7 +184,6 @@ module.exports = app => app.component('chat-message-script', {
232
184
  }
233
185
  },
234
186
  unmounted() {
235
- this.destroyCodeMirror();
236
187
  document.body.removeEventListener('click', this.handleBodyClick);
237
188
  }
238
189
  });