@mongoosejs/studio 0.4.1 → 0.5.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.
@@ -683,6 +683,9 @@ client.interceptors.response.use(
683
683
  );
684
684
 
685
685
  if (window.MONGOOSE_STUDIO_CONFIG.isLambda) {
686
+ exports.getCapabilities = function getCapabilities() {
687
+ return client.post('', { action: 'getCapabilities' }).then(res => res.data);
688
+ };
686
689
  exports.status = function status() {
687
690
  return client.post('', { action: 'status' }).then(res => res.data);
688
691
  };
@@ -859,6 +862,9 @@ if (window.MONGOOSE_STUDIO_CONFIG.isLambda) {
859
862
  }
860
863
  };
861
864
  } else {
865
+ exports.getCapabilities = function getCapabilities() {
866
+ return client.get('/getCapabilities').then(res => res.data);
867
+ };
862
868
  exports.status = function status() {
863
869
  return client.get('/status').then(res => res.data);
864
870
  };
@@ -1382,15 +1388,20 @@ const template = __webpack_require__(/*! ./chat-message-script.html */ "./fronte
1382
1388
 
1383
1389
  module.exports = app => app.component('chat-message-script', {
1384
1390
  template,
1385
- props: ['message', 'script', 'language', 'targetDashboardId'],
1391
+ props: ['message', 'script', 'language', 'targetDashboardId', 'capabilities'],
1386
1392
  emits: ['copyMessage'],
1387
1393
  data() {
1388
1394
  return {
1389
1395
  activeTab: 'code',
1396
+ selectedRunMode: 'run',
1390
1397
  showDetailModal: false,
1398
+ showRunInfoModal: false,
1399
+ showDryRunInfoModal: false,
1391
1400
  showCreateDashboardModal: false,
1392
1401
  showOverwriteDashboardConfirmationModal: false,
1402
+ showRunDropdown: false,
1393
1403
  showDropdown: false,
1404
+ isExecuting: false,
1394
1405
  newDashboardTitle: '',
1395
1406
  dashboardCode: '',
1396
1407
  createError: null,
@@ -1406,32 +1417,60 @@ module.exports = app => app.component('chat-message-script', {
1406
1417
  },
1407
1418
  canOverwriteDashboard() {
1408
1419
  return !!this.targetDashboardId;
1420
+ },
1421
+ canUseDryRun() {
1422
+ return this.capabilities?.supportsTransactions !== false;
1423
+ },
1424
+ dryRunDisabledTitle() {
1425
+ return this.canUseDryRun ? null : 'dry run mode requires MongoDB transactions support';
1426
+ },
1427
+ isDryRunResult() {
1428
+ return !!this.message.executionResult?.dryRun;
1429
+ },
1430
+ selectedRunLabel() {
1431
+ return this.selectedRunMode === 'dryRun' ? 'Dry Run' : 'Run';
1409
1432
  }
1410
1433
  },
1411
1434
  methods: {
1412
- async executeScript() {
1435
+ async executeScript(dryRun = this.selectedRunMode === 'dryRun') {
1413
1436
  const scriptToRun = this.isEditing ? this.editedScript : this.script;
1437
+ if (this.isExecuting) {
1438
+ return;
1439
+ }
1440
+ this.showRunDropdown = false;
1414
1441
  this.editedScript = scriptToRun;
1415
- const { chatMessage } = await api.ChatMessage.executeScript({
1416
- chatMessageId: this.message._id,
1417
- script: scriptToRun
1418
- });
1419
- this.message.executionResult = chatMessage.executionResult;
1420
- this.message.script = chatMessage.script;
1421
- this.message.content = chatMessage.content;
1422
- this.editedScript = chatMessage.script;
1423
- if (this.isEditing) {
1424
- this.finishEditing();
1425
- } else {
1426
- this.highlightCode();
1442
+ this.isExecuting = true;
1443
+ try {
1444
+ const { chatMessage } = await api.ChatMessage.executeScript({
1445
+ chatMessageId: this.message._id,
1446
+ script: scriptToRun,
1447
+ dryRun
1448
+ });
1449
+ this.message.executionResult = chatMessage.executionResult;
1450
+ this.message.script = chatMessage.script;
1451
+ this.message.content = chatMessage.content;
1452
+ this.editedScript = chatMessage.script;
1453
+ if (this.isEditing) {
1454
+ this.finishEditing();
1455
+ } else {
1456
+ this.highlightCode();
1457
+ }
1458
+ this.activeTab = 'output';
1459
+ this.$toast.success('Script executed successfully!');
1460
+ return chatMessage;
1461
+ } finally {
1462
+ this.isExecuting = false;
1427
1463
  }
1428
- this.activeTab = 'output';
1429
- this.$toast.success('Script executed successfully!');
1430
- return chatMessage;
1431
1464
  },
1432
1465
  openDetailModal() {
1433
1466
  this.showDetailModal = true;
1434
1467
  },
1468
+ openRunInfoModal() {
1469
+ this.showRunInfoModal = true;
1470
+ },
1471
+ openDryRunInfoModal() {
1472
+ this.showDryRunInfoModal = true;
1473
+ },
1435
1474
  openCreateDashboardModal() {
1436
1475
  this.newDashboardTitle = '';
1437
1476
  this.dashboardCode = this.script;
@@ -1448,6 +1487,21 @@ module.exports = app => app.component('chat-message-script', {
1448
1487
  },
1449
1488
  toggleDropdown() {
1450
1489
  this.showDropdown = !this.showDropdown;
1490
+ this.showRunDropdown = false;
1491
+ },
1492
+ toggleRunDropdown() {
1493
+ this.showRunDropdown = !this.showRunDropdown;
1494
+ this.showDropdown = false;
1495
+ },
1496
+ selectRunMode(mode) {
1497
+ if (this.isExecuting) {
1498
+ return;
1499
+ }
1500
+ if (mode === 'dryRun' && !this.canUseDryRun) {
1501
+ return;
1502
+ }
1503
+ this.selectedRunMode = mode;
1504
+ this.showRunDropdown = false;
1451
1505
  },
1452
1506
  startEditing() {
1453
1507
  this.isEditing = true;
@@ -1473,7 +1527,11 @@ module.exports = app => app.component('chat-message-script', {
1473
1527
  });
1474
1528
  },
1475
1529
  handleBodyClick(event) {
1530
+ const runDropdown = this.$refs.runDropdown;
1476
1531
  const dropdown = this.$refs.dropdown;
1532
+ if (runDropdown && typeof runDropdown.contains === 'function' && !runDropdown.contains(event.target)) {
1533
+ this.showRunDropdown = false;
1534
+ }
1477
1535
  if (dropdown && typeof dropdown.contains === 'function' && !dropdown.contains(event.target)) {
1478
1536
  this.showDropdown = false;
1479
1537
  }
@@ -1548,6 +1606,14 @@ module.exports = app => app.component('chat-message-script', {
1548
1606
  this.editedScript = newScript;
1549
1607
  this.highlightCode();
1550
1608
  }
1609
+ },
1610
+ capabilities: {
1611
+ immediate: true,
1612
+ handler(capabilities) {
1613
+ if (capabilities?.supportsTransactions === false && this.selectedRunMode === 'dryRun') {
1614
+ this.selectedRunMode = 'run';
1615
+ }
1616
+ }
1551
1617
  }
1552
1618
  },
1553
1619
  mounted() {
@@ -1582,7 +1648,7 @@ const template = __webpack_require__(/*! ./chat-message.html */ "./frontend/src/
1582
1648
 
1583
1649
  module.exports = app => app.component('chat-message', {
1584
1650
  template: template,
1585
- props: ['message', 'targetDashboardId'],
1651
+ props: ['message', 'targetDashboardId', 'capabilities'],
1586
1652
  computed: {
1587
1653
  styleForMessage() {
1588
1654
  return this.message.role === 'user' ? 'p-3 bg-muted' : 'py-3 pr-3';
@@ -1638,10 +1704,11 @@ module.exports = app => app.component('chat-message', {
1638
1704
  if (input.limit && input.limit !== 10) parts.push('limit=' + input.limit);
1639
1705
  return parts.length ? '(' + parts.join(', ') + ')' : '';
1640
1706
  },
1641
- async executeScript(message, script) {
1707
+ async executeScript(message, script, dryRun) {
1642
1708
  const { chatMessage } = await api.ChatMessage.executeScript({
1643
1709
  chatMessageId: message._id,
1644
- script
1710
+ script,
1711
+ dryRun
1645
1712
  });
1646
1713
  message.executionResult = chatMessage.executionResult;
1647
1714
  console.log(message);
@@ -1710,7 +1777,12 @@ module.exports = {
1710
1777
  sharingThread: false,
1711
1778
  threadSearch: '',
1712
1779
  showProUpgradeModal: false,
1713
- agentTools: agentToolMetadata
1780
+ agentTools: agentToolMetadata,
1781
+ capabilities: {
1782
+ supportsChangeStreams: false,
1783
+ supportsTransactions: true,
1784
+ supportsAI: false
1785
+ }
1714
1786
  }),
1715
1787
  methods: {
1716
1788
  async sendMessage() {
@@ -1987,8 +2059,12 @@ module.exports = {
1987
2059
 
1988
2060
  this.draftAgentMode = this.getAgentModePreference();
1989
2061
  this.chatThreadId = this.threadId;
1990
- const { chatThreads } = await api.ChatThread.listChatThreads();
2062
+ const [{ chatThreads }, { capabilities }] = await Promise.all([
2063
+ api.ChatThread.listChatThreads(),
2064
+ api.getCapabilities()
2065
+ ]);
1991
2066
  this.chatThreads = chatThreads;
2067
+ this.capabilities = capabilities;
1992
2068
  if (this.chatThreadId) {
1993
2069
  await this.syncCurrentThreadAgentMode();
1994
2070
  const { chatMessages } = await api.ChatThread.getChatThread({ chatThreadId: this.chatThreadId });
@@ -8703,8 +8779,8 @@ module.exports = app => app.component('models', {
8703
8779
  const projectionPaths = this.normalizeProjectionPathsForDisplay(routeProjectionInput);
8704
8780
  const fromProjectionInput = Array.isArray(projectionPaths) ?
8705
8781
  projectionPaths
8706
- .map(path => this.schemaPaths.find(p => p.path === path))
8707
- .filter(Boolean) :
8782
+ .map(path => this.schemaPaths.find(p => p.path === path))
8783
+ .filter(Boolean) :
8708
8784
  [];
8709
8785
  this.filteredPaths = fromProjectionInput;
8710
8786
  } else {
@@ -9259,9 +9335,9 @@ function getBraceDepth(text, pos) {
9259
9335
  let depth = 0;
9260
9336
  for (let i = 0; i < pos; i++) {
9261
9337
  const ch = text[i];
9262
- if (ch === "{") {
9338
+ if (ch === '{') {
9263
9339
  depth++;
9264
- } else if (ch === "}") {
9340
+ } else if (ch === '}') {
9265
9341
  depth--;
9266
9342
  }
9267
9343
  }
@@ -51501,7 +51577,7 @@ module.exports = "<button v-bind=\"attrsToBind\" :disabled=\"isDisabled\" @click
51501
51577
  (module) {
51502
51578
 
51503
51579
  "use strict";
51504
- module.exports = "<div class=\"chat-message-script relative border rounded bg-surface my-1 text-content text-sm overflow-hidden\">\n <div class=\"flex border-b py-1 pl-1 text-xs font-medium bg-surface\">\n <button\n class=\"px-4 py-1 border-r border-edge-strong text-content-secondary font-semibold transition-colors duration-200 focus:outline-none\"\n :class=\"[\n 'rounded-l-md',\n activeTab === 'code'\n ? 'bg-gray-700 text-white shadow'\n : 'bg-muted hover:bg-muted text-gray-600'\n ]\"\n @click=\"activeTab = 'code'\">\n Code\n </button>\n <button\n class=\"px-4 py-1 text-content-secondary font-semibold transition-colors duration-200 focus:outline-none\"\n :class=\"[\n 'rounded-r-md',\n activeTab === 'output'\n ? 'bg-gray-700 text-white shadow'\n : 'bg-muted hover:bg-muted text-gray-600'\n ]\"\n @click=\"activeTab = 'output'\">\n Output\n </button>\n <div class=\"ml-auto mr-1 flex items-center\">\n <button\n v-if=\"activeTab === 'output'\"\n 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\"\n @click=\"openDetailModal\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" style=\"height: 1.2em;\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <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\" />\n </svg>\n </button>\n <button\n v-if=\"activeTab === 'code' && !isEditing\"\n class=\"px-2 py-1 mr-1 text-xs bg-gray-700 text-white border-none rounded cursor-pointer hover:bg-gray-600 transition-colors flex items-center\"\n @click.stop=\"startEditing\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <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\" />\n </svg>\n </button>\n <async-button\n v-if=\"!isEditing\"\n 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\"\n @click=\"executeScript\">\n Run\n </async-button>\n <div class=\"relative ml-1\" ref=\"dropdown\">\n <button\n @click.stop=\"toggleDropdown\"\n class=\"px-1 py-1 text-xs hover:bg-gray-300 rounded flex items-center\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path d=\"M10 6a2 2 0 110-4 2 2 0 010 4zm0 6a2 2 0 110-4 2 2 0 010 4zm0 6a2 2 0 110-4 2 2 0 010 4z\" />\n </svg>\n </button>\n <div\n v-if=\"showDropdown\"\n 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\">\n <button\n class=\"block w-full text-left px-4 py-2 text-xs text-content-secondary hover:bg-muted\"\n @click=\"openCreateDashboardModal(); showDropdown = false\">\n Create Dashboard\n </button>\n <button\n class=\"block w-full text-left px-4 py-2 text-xs text-content-secondary hover:bg-muted\"\n @click=\"copyOutput(); showDropdown = false\">\n Copy Output As Text\n </button>\n <button\n v-if=\"canOverwriteDashboard\"\n class=\"block w-full text-left px-4 py-2 text-xs text-content-secondary hover:bg-muted\"\n @click=\"openOverwriteDashboardConfirmation(); showDropdown = false\">\n Overwrite Dashboard\n </button>\n <button\n class=\"block w-full text-left px-4 py-2 text-xs text-content-secondary hover:bg-muted\"\n @click=\"$emit('copyMessage'); showDropdown = false\">\n Copy Full Message\n </button>\n </div>\n </div>\n </div>\n </div>\n\n <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'\">\n <div v-if=\"isEditing\" class=\"flex flex-col space-y-2 chat-script-editor-wrap\">\n <div class=\"border border-edge\">\n <ace-editor\n v-model=\"editedScript\"\n mode=\"javascript\"\n :line-numbers=\"true\"\n class=\"w-full h-[45vh] min-h-[200px]\"\n />\n </div>\n <div class=\"flex justify-end gap-2 pb-2\">\n <button\n class=\"px-2 py-1 text-xs bg-gray-300 text-gray-800 border-none rounded cursor-pointer hover:bg-gray-400 transition-colors\"\n @click.stop=\"cancelEditing\">\n Cancel\n </button>\n <async-button\n class=\"px-2 py-1 text-xs bg-blue-600 text-white border-none rounded cursor-pointer hover:bg-blue-700 transition-colors disabled:bg-gray-400\"\n @click=\"executeScript\">\n Execute\n </async-button>\n </div>\n </div>\n <pre v-else class=\"whitespace-pre-wrap !my-0 bg-muted\"><code v-text=\"script\" ref=\"code\" :class=\"'language-' + language\"></code></pre>\n </div>\n\n <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'\">\n <dashboard-result\n v-if=\"message.executionResult?.output != null\"\n :result=\"message.executionResult.output\">\n </dashboard-result>\n <pre v-else>No output</pre>\n\n <div v-if=\"message.executionResult?.logs?.length\" class=\"mt-3 pt-3 border-t border-edge\">\n <div class=\"text-xs font-semibold text-gray-600 uppercase tracking-wide\">Console</div>\n <pre class=\"mt-1 bg-muted text-content p-3 rounded whitespace-pre-wrap overflow-x-auto max-h-[280px]\">{{ message.executionResult.logs }}</pre>\n </div>\n </div>\n\n <modal ref=\"outputModal\" v-if=\"showDetailModal\" containerClass=\"!h-[90vh] !w-[90vw]\">\n <template #body>\n <div class=\"absolute font-mono right-1 top-1 cursor-pointer text-xl\" @click=\"showDetailModal = false;\">&times;</div>\n <div class=\"h-full overflow-auto\">\n <dashboard-result\n v-if=\"message.executionResult?.output != null\"\n :result=\"message.executionResult.output\"\n :fullscreen=\"true\">\n </dashboard-result>\n <pre v-else class=\"whitespace-pre-wrap\">No output</pre>\n </div>\n </template>\n </modal>\n <modal v-if=\"showCreateDashboardModal\">\n <template #body>\n <div class=\"modal-exit\" @click=\"showCreateDashboardModal = false\">&times;</div>\n <div>\n <div class=\"mt-4 text-content font-semibold\">Create Dashboard</div>\n <div class=\"mt-4\">\n <label class=\"block text-sm font-medium leading-6 text-content\">Title</label>\n <div class=\"mt-2\">\n <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\">\n <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\">\n </div>\n </div>\n </div>\n <div class=\"my-4\">\n <label class=\"block text-sm font-medium leading-6 text-content\">Code</label>\n <div class=\"border border-edge\">\n <ace-editor\n v-model=\"dashboardCode\"\n mode=\"javascript\"\n :line-numbers=\"true\"\n class=\"h-[300px] w-full\"\n />\n </div>\n </div>\n <async-button\n @click=\"createDashboardFromScript\"\n class=\"rounded-md bg-teal-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-teal-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600\">\n Submit\n </async-button>\n <div v-if=\"createError\" class=\"rounded-md bg-red-50 p-4 mt-1\">\n <div class=\"flex\">\n <div class=\"flex-shrink-0\">\n <svg class=\"h-5 w-5 text-red-400\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z\" clip-rule=\"evenodd\" />\n </svg>\n </div>\n <div class=\"ml-3\">\n <h3 class=\"text-sm font-medium text-red-800\">Error</h3>\n <div class=\"mt-2 text-sm text-red-700\">\n {{createError}}\n </div>\n </div>\n </div>\n </div>\n </div>\n </template>\n </modal>\n <modal v-if=\"showOverwriteDashboardConfirmationModal\">\n <template #body>\n <div class=\"modal-exit\" @click=\"showOverwriteDashboardConfirmationModal = false\">&times;</div>\n <div>\n <div class=\"mt-4 text-content font-semibold\">Overwrite Dashboard</div>\n <p class=\"mt-2 text-sm text-content-secondary\">\n This will replace the linked dashboard's code with the script below.\n </p>\n <p class=\"mt-1 text-xs text-gray-600 break-all\" v-if=\"targetDashboardId\">\n Dashboard ID: {{ targetDashboardId }}\n </p>\n <div class=\"my-4 border border-edge bg-page rounded\">\n <pre class=\"p-2 h-[300px] overflow-auto whitespace-pre-wrap text-xs\">{{ overwriteDashboardCode }}</pre>\n </div>\n <div class=\"flex items-center gap-2\">\n <async-button\n @click=\"confirmOverwriteDashboard\"\n class=\"rounded-md bg-teal-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-teal-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600\">\n Confirm Overwrite\n </async-button>\n <button\n class=\"px-2.5 py-1.5 rounded-md text-sm font-semibold text-content-secondary bg-gray-200 hover:bg-gray-300\"\n @click=\"showOverwriteDashboardConfirmationModal = false\">\n Cancel\n </button>\n </div>\n <div v-if=\"overwriteError\" class=\"rounded-md bg-red-50 p-4 mt-3\">\n <div class=\"flex\">\n <div class=\"flex-shrink-0\">\n <svg class=\"h-5 w-5 text-red-400\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z\" clip-rule=\"evenodd\" />\n </svg>\n </div>\n <div class=\"ml-3\">\n <h3 class=\"text-sm font-medium text-red-800\">Error</h3>\n <div class=\"mt-2 text-sm text-red-700\">\n {{overwriteError}}\n </div>\n </div>\n </div>\n </div>\n </div>\n </template>\n </modal>\n</div>\n";
51580
+ module.exports = "<div class=\"chat-message-script relative border rounded bg-surface my-1 text-content text-sm overflow-hidden\">\n <div class=\"flex border-b py-1 pl-1 text-xs font-medium bg-surface\">\n <button\n class=\"px-4 py-1 border-r border-edge-strong text-content-secondary font-semibold transition-colors duration-200 focus:outline-none\"\n :class=\"[\n 'rounded-l-md',\n activeTab === 'code'\n ? 'bg-gray-700 text-white shadow'\n : 'bg-muted hover:bg-muted text-gray-600'\n ]\"\n @click=\"activeTab = 'code'\">\n Code\n </button>\n <button\n class=\"px-4 py-1 text-content-secondary font-semibold transition-colors duration-200 focus:outline-none\"\n :class=\"[\n 'rounded-r-md',\n activeTab === 'output'\n ? 'bg-gray-700 text-white shadow'\n : 'bg-muted hover:bg-muted text-gray-600'\n ]\"\n @click=\"activeTab = 'output'\">\n <span>Output</span>\n <span v-if=\"isDryRunResult\" class=\"ml-1\">[Dry Run]</span>\n <span\n v-if=\"isDryRunResult\"\n class=\"ml-1 inline-flex align-middle cursor-pointer\"\n title=\"This output was produced in dry run mode\"\n @click.stop=\"openDryRunInfoModal\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-3.5 w-3.5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path fill-rule=\"evenodd\" d=\"M18 10A8 8 0 112 10a8 8 0 0116 0zm-7-3a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 102 0v-3a1 1 0 00-1-1H9z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n </button>\n <div class=\"ml-auto mr-1 flex items-center\">\n <button\n v-if=\"activeTab === 'code' && !isEditing\"\n class=\"px-2 py-1 mr-1 text-xs bg-gray-700 text-white border-none rounded cursor-pointer hover:bg-gray-600 transition-colors flex items-center\"\n @click.stop=\"startEditing\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <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\" />\n </svg>\n </button>\n <button\n v-if=\"activeTab === 'output'\"\n 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\"\n @click=\"openDetailModal\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" style=\"height: 1.2em;\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <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\" />\n </svg>\n </button>\n <div v-if=\"!isEditing\" class=\"relative mr-1\" ref=\"runDropdown\">\n <div class=\"flex items-center\">\n <button\n class=\"px-2 py-1 text-xs bg-primary hover:bg-primary-hover text-primary-text border-none rounded-l-md cursor-pointer transition-colors disabled:bg-gray-400 disabled:cursor-not-allowed\"\n :disabled=\"isExecuting\"\n @click=\"executeScript()\">\n {{ selectedRunLabel }}\n </button>\n <button\n class=\"px-1 py-1 text-xs bg-primary hover:bg-primary-hover text-primary-text border-l border-black/10 rounded-r-md cursor-pointer transition-colors disabled:bg-gray-400 disabled:cursor-not-allowed\"\n :disabled=\"isExecuting\"\n @click.stop=\"toggleRunDropdown\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path fill-rule=\"evenodd\" d=\"M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z\" clip-rule=\"evenodd\" />\n </svg>\n </button>\n <button\n class=\"ml-1 px-1 py-1 text-content-secondary hover:bg-muted rounded cursor-pointer transition-colors\"\n @click=\"openRunInfoModal\"\n title=\"How run works\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path fill-rule=\"evenodd\" d=\"M18 10A8 8 0 112 10a8 8 0 0116 0zm-7-3a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 102 0v-3a1 1 0 00-1-1H9z\" clip-rule=\"evenodd\" />\n </svg>\n </button>\n </div>\n <div\n v-if=\"showRunDropdown\"\n class=\"absolute right-0 z-10 mt-1 w-40 origin-top-right rounded-md bg-surface py-1 shadow-lg ring-1 ring-black/5\">\n <button\n class=\"block w-full text-left px-4 py-2 text-xs text-content-secondary hover:bg-muted\"\n :disabled=\"isExecuting\"\n @click=\"selectRunMode('run')\">\n Run\n </button>\n <button\n class=\"block w-full text-left px-4 py-2 text-xs\"\n :class=\"isExecuting ? 'cursor-not-allowed text-gray-400' : canUseDryRun ? 'text-content-secondary hover:bg-muted' : 'cursor-not-allowed text-gray-400'\"\n :aria-disabled=\"!canUseDryRun\"\n :title=\"dryRunDisabledTitle\"\n @click=\"selectRunMode('dryRun')\">\n Dry Run\n </button>\n </div>\n </div>\n <div class=\"relative ml-1\" ref=\"dropdown\">\n <button\n @click.stop=\"toggleDropdown\"\n class=\"px-1 py-1 text-xs hover:bg-gray-300 rounded flex items-center\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path d=\"M10 6a2 2 0 110-4 2 2 0 010 4zm0 6a2 2 0 110-4 2 2 0 010 4zm0 6a2 2 0 110-4 2 2 0 010 4z\" />\n </svg>\n </button>\n <div\n v-if=\"showDropdown\"\n 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\">\n <button\n class=\"block w-full text-left px-4 py-2 text-xs text-content-secondary hover:bg-muted\"\n @click=\"openCreateDashboardModal(); showDropdown = false\">\n Create Dashboard\n </button>\n <button\n class=\"block w-full text-left px-4 py-2 text-xs text-content-secondary hover:bg-muted\"\n @click=\"copyOutput(); showDropdown = false\">\n Copy Output As Text\n </button>\n <button\n v-if=\"canOverwriteDashboard\"\n class=\"block w-full text-left px-4 py-2 text-xs text-content-secondary hover:bg-muted\"\n @click=\"openOverwriteDashboardConfirmation(); showDropdown = false\">\n Overwrite Dashboard\n </button>\n <button\n class=\"block w-full text-left px-4 py-2 text-xs text-content-secondary hover:bg-muted\"\n @click=\"$emit('copyMessage'); showDropdown = false\">\n Copy Full Message\n </button>\n </div>\n </div>\n </div>\n </div>\n\n <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'\">\n <div v-if=\"isEditing\" class=\"flex flex-col space-y-2 chat-script-editor-wrap\">\n <div class=\"border border-edge\">\n <ace-editor\n v-model=\"editedScript\"\n mode=\"javascript\"\n :line-numbers=\"true\"\n class=\"w-full h-[45vh] min-h-[200px]\"\n />\n </div>\n <div class=\"flex justify-end gap-2 pb-2\">\n <button\n class=\"px-2 py-1 text-xs bg-gray-300 text-gray-800 border-none rounded cursor-pointer hover:bg-gray-400 transition-colors\"\n @click.stop=\"cancelEditing\">\n Cancel\n </button>\n <async-button\n class=\"px-2 py-1 text-xs bg-blue-600 text-white border-none rounded cursor-pointer hover:bg-blue-700 transition-colors disabled:bg-gray-400\"\n @click=\"executeScript(false)\">\n Execute\n </async-button>\n </div>\n </div>\n <pre v-else class=\"whitespace-pre-wrap !my-0 bg-muted\"><code v-text=\"script\" ref=\"code\" :class=\"'language-' + language\"></code></pre>\n </div>\n\n <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'\">\n <dashboard-result\n v-if=\"message.executionResult?.output != null\"\n :result=\"message.executionResult.output\">\n </dashboard-result>\n <pre v-else>No output</pre>\n\n <div v-if=\"message.executionResult?.logs?.length\" class=\"mt-3 pt-3 border-t border-edge\">\n <div class=\"text-xs font-semibold text-gray-600 uppercase tracking-wide\">Console</div>\n <pre class=\"mt-1 bg-muted text-content p-3 rounded whitespace-pre-wrap overflow-x-auto max-h-[280px]\">{{ message.executionResult.logs }}</pre>\n </div>\n </div>\n\n <modal ref=\"outputModal\" v-if=\"showDetailModal\" containerClass=\"!h-[90vh] !w-[90vw]\">\n <template #body>\n <div class=\"absolute font-mono right-1 top-1 cursor-pointer text-xl\" @click=\"showDetailModal = false;\">&times;</div>\n <div class=\"h-full overflow-auto\">\n <dashboard-result\n v-if=\"message.executionResult?.output != null\"\n :result=\"message.executionResult.output\"\n :fullscreen=\"true\">\n </dashboard-result>\n <pre v-else class=\"whitespace-pre-wrap\">No output</pre>\n </div>\n </template>\n </modal>\n <modal v-if=\"showRunInfoModal\">\n <template #body>\n <div class=\"modal-exit\" @click=\"showRunInfoModal = false\">&times;</div>\n <div>\n <div class=\"mt-4 text-content font-semibold\">How Run Works</div>\n <p class=\"mt-4 text-sm text-content-secondary\">\n Run executes the script shown in this message on the server with access to your Mongoose models through <code class=\"bg-muted px-1 rounded\">db.models</code>. The latest script is saved back onto the chat message.\n </p>\n <p class=\"mt-2 text-sm text-content-secondary\">\n Output and console logs are stored on the message, and the Output tab updates to show the latest result.\n </p>\n <p class=\"mt-2 text-sm text-content-secondary\">\n Dry Run executes the same script inside a MongoDB transaction. Studio attaches the transaction session to Mongoose model and collection calls, captures the output and logs, then aborts the transaction so writes are rolled back.\n </p>\n <p class=\"mt-2 text-sm text-content-secondary\">\n Dry Run requires MongoDB transaction support. It is intended for previewing what a script would return before persisting changes.\n </p>\n </div>\n </template>\n </modal>\n <modal v-if=\"showDryRunInfoModal\">\n <template #body>\n <div class=\"modal-exit\" @click=\"showDryRunInfoModal = false\">&times;</div>\n <div>\n <div class=\"mt-4 text-content font-semibold\">Dry Run Output</div>\n <p class=\"mt-4 text-sm text-content-secondary\">\n This output was produced in dry run mode. The script ran on the server inside a MongoDB transaction, and Studio aborted that transaction after the script finished.\n </p>\n <p class=\"mt-2 text-sm text-content-secondary\">\n No data changes from the script were persisted. The displayed output and console logs were saved on the chat message so you can review what the script returned.\n </p>\n </div>\n </template>\n </modal>\n <modal v-if=\"showCreateDashboardModal\">\n <template #body>\n <div class=\"modal-exit\" @click=\"showCreateDashboardModal = false\">&times;</div>\n <div>\n <div class=\"mt-4 text-content font-semibold\">Create Dashboard</div>\n <div class=\"mt-4\">\n <label class=\"block text-sm font-medium leading-6 text-content\">Title</label>\n <div class=\"mt-2\">\n <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\">\n <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\">\n </div>\n </div>\n </div>\n <div class=\"my-4\">\n <label class=\"block text-sm font-medium leading-6 text-content\">Code</label>\n <div class=\"border border-edge\">\n <ace-editor\n v-model=\"dashboardCode\"\n mode=\"javascript\"\n :line-numbers=\"true\"\n class=\"h-[300px] w-full\"\n />\n </div>\n </div>\n <async-button\n @click=\"createDashboardFromScript\"\n class=\"rounded-md bg-teal-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-teal-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600\">\n Submit\n </async-button>\n <div v-if=\"createError\" class=\"rounded-md bg-red-50 p-4 mt-1\">\n <div class=\"flex\">\n <div class=\"flex-shrink-0\">\n <svg class=\"h-5 w-5 text-red-400\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z\" clip-rule=\"evenodd\" />\n </svg>\n </div>\n <div class=\"ml-3\">\n <h3 class=\"text-sm font-medium text-red-800\">Error</h3>\n <div class=\"mt-2 text-sm text-red-700\">\n {{createError}}\n </div>\n </div>\n </div>\n </div>\n </div>\n </template>\n </modal>\n <modal v-if=\"showOverwriteDashboardConfirmationModal\">\n <template #body>\n <div class=\"modal-exit\" @click=\"showOverwriteDashboardConfirmationModal = false\">&times;</div>\n <div>\n <div class=\"mt-4 text-content font-semibold\">Overwrite Dashboard</div>\n <p class=\"mt-2 text-sm text-content-secondary\">\n This will replace the linked dashboard's code with the script below.\n </p>\n <p class=\"mt-1 text-xs text-gray-600 break-all\" v-if=\"targetDashboardId\">\n Dashboard ID: {{ targetDashboardId }}\n </p>\n <div class=\"my-4 border border-edge bg-page rounded\">\n <pre class=\"p-2 h-[300px] overflow-auto whitespace-pre-wrap text-xs\">{{ overwriteDashboardCode }}</pre>\n </div>\n <div class=\"flex items-center gap-2\">\n <async-button\n @click=\"confirmOverwriteDashboard\"\n class=\"rounded-md bg-teal-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-teal-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600\">\n Confirm Overwrite\n </async-button>\n <button\n class=\"px-2.5 py-1.5 rounded-md text-sm font-semibold text-content-secondary bg-gray-200 hover:bg-gray-300\"\n @click=\"showOverwriteDashboardConfirmationModal = false\">\n Cancel\n </button>\n </div>\n <div v-if=\"overwriteError\" class=\"rounded-md bg-red-50 p-4 mt-3\">\n <div class=\"flex\">\n <div class=\"flex-shrink-0\">\n <svg class=\"h-5 w-5 text-red-400\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z\" clip-rule=\"evenodd\" />\n </svg>\n </div>\n <div class=\"ml-3\">\n <h3 class=\"text-sm font-medium text-red-800\">Error</h3>\n <div class=\"mt-2 text-sm text-red-700\">\n {{overwriteError}}\n </div>\n </div>\n </div>\n </div>\n </div>\n </template>\n </modal>\n</div>\n";
51505
51581
 
51506
51582
  /***/ },
51507
51583
 
@@ -51512,7 +51588,7 @@ module.exports = "<div class=\"chat-message-script relative border rounded bg-su
51512
51588
  (module) {
51513
51589
 
51514
51590
  "use strict";
51515
- module.exports = "<div class=\"relative flex items-start\" :class=\"{'justify-end': message.role === 'user'}\">\n <div\n class=\"min-w-0 max-w-full\"\n :class=\"{'text-right': message.role === 'user'}\">\n\n <div v-if=\"message.toolCalls && message.toolCalls.length\" class=\"mb-2 text-xs text-gray-500 dark:text-gray-400\">\n <div\n v-for=\"(tc, i) in message.toolCalls\"\n :key=\"i\"\n class=\"flex items-center gap-1.5 py-0.5 font-mono\">\n <svg :class=\"['w-3.5 h-3.5 shrink-0', tc.status === 'running' ? 'animate-spin text-gray-400 dark:text-gray-500' : 'text-gray-500 dark:text-gray-400']\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M21.75 6.75a4.5 4.5 0 0 1-4.884 4.484c-1.076-.091-2.264.071-2.95.904l-7.152 8.684a2.548 2.548 0 1 1-3.586-3.586l8.684-7.152c.833-.686.995-1.874.904-2.95a4.5 4.5 0 0 1 6.336-4.486l-3.276 3.276a3.004 3.004 0 0 0 2.25 2.25l3.276-3.276c.256.565.398 1.192.398 1.852Z\" /><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M4.867 19.125h.008v.008h-.008v-.008Z\" /></svg>\n <span>{{ tc.toolName }}</span>\n <span v-if=\"tc.toolName !== 'typeCheck'\" class=\"text-gray-400 dark:text-gray-500\">{{ formatToolInput(tc.input) }}</span>\n </div>\n </div>\n\n <div class=\"text-sm text-content rounded-md inline-block relative\" :class=\"styleForMessage\">\n <div v-for=\"part in contentSplitByScripts\">\n <div v-if=\"part.type === 'text'\" v-html=\"marked(part.content)\">\n </div>\n <div v-else-if=\"part.type === 'code'\">\n <chat-message-script\n :message=\"message\"\n :script=\"part.content\"\n :language=\"part.language\"\n :target-dashboard-id=\"targetDashboardId\"\n @copyMessage=\"copyMessage\"></chat-message-script>\n </div>\n </div>\n </div>\n </div>\n</div>\n";
51591
+ module.exports = "<div class=\"relative flex items-start\" :class=\"{'justify-end': message.role === 'user'}\">\n <div\n class=\"min-w-0 max-w-full\"\n :class=\"{'text-right': message.role === 'user'}\">\n\n <div v-if=\"message.toolCalls && message.toolCalls.length\" class=\"mb-2 text-xs text-gray-500 dark:text-gray-400\">\n <div\n v-for=\"(tc, i) in message.toolCalls\"\n :key=\"i\"\n class=\"flex items-center gap-1.5 py-0.5 font-mono\">\n <svg :class=\"['w-3.5 h-3.5 shrink-0', tc.status === 'running' ? 'animate-spin text-gray-400 dark:text-gray-500' : 'text-gray-500 dark:text-gray-400']\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\"><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M21.75 6.75a4.5 4.5 0 0 1-4.884 4.484c-1.076-.091-2.264.071-2.95.904l-7.152 8.684a2.548 2.548 0 1 1-3.586-3.586l8.684-7.152c.833-.686.995-1.874.904-2.95a4.5 4.5 0 0 1 6.336-4.486l-3.276 3.276a3.004 3.004 0 0 0 2.25 2.25l3.276-3.276c.256.565.398 1.192.398 1.852Z\" /><path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M4.867 19.125h.008v.008h-.008v-.008Z\" /></svg>\n <span>{{ tc.toolName }}</span>\n <span v-if=\"tc.toolName !== 'typeCheck'\" class=\"text-gray-400 dark:text-gray-500\">{{ formatToolInput(tc.input) }}</span>\n </div>\n </div>\n\n <div class=\"text-sm text-content rounded-md inline-block relative\" :class=\"styleForMessage\">\n <div v-for=\"part in contentSplitByScripts\">\n <div v-if=\"part.type === 'text'\" v-html=\"marked(part.content)\">\n </div>\n <div v-else-if=\"part.type === 'code'\">\n <chat-message-script\n :message=\"message\"\n :script=\"part.content\"\n :language=\"part.language\"\n :target-dashboard-id=\"targetDashboardId\"\n :capabilities=\"capabilities\"\n @copyMessage=\"copyMessage\"></chat-message-script>\n </div>\n </div>\n </div>\n </div>\n</div>\n";
51516
51592
 
51517
51593
  /***/ },
51518
51594
 
@@ -51523,7 +51599,7 @@ module.exports = "<div class=\"relative flex items-start\" :class=\"{'justify-en
51523
51599
  (module) {
51524
51600
 
51525
51601
  "use strict";
51526
- module.exports = "<div class=\"flex\" style=\"height: calc(100vh - 55px); height: calc(100dvh - 55px)\">\n <button\n type=\"button\"\n class=\"fixed top-[65px] left-0 cursor-pointer bg-muted rounded-r-md p-1\"\n :class=\"hideSidebar === true ? 'z-[10001]' : hideSidebar === false ? 'z-10' : 'z-30 lg:hidden'\"\n @click=\"hideSidebar = hideSidebar === false ? true : false\"\n aria-label=\"Toggle chat history\"\n title=\"Toggle chat history\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"w-5 h-5 text-content-tertiary\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"m5.25 4.5 7.5 7.5-7.5 7.5m6-15 7.5 7.5-7.5 7.5\" />\n </svg>\n </button>\n <!-- Sidebar: Chat Threads -->\n <aside\n class=\"bg-page border-r overflow-hidden h-full transition-all duration-300 ease-in-out z-20 w-64 fixed lg:relative shrink-0 flex flex-col\"\n :class=\"hideSidebar === false ? 'translate-x-0' : hideSidebar === true ? '-translate-x-full lg:translate-x-0 lg:!w-0 lg:!border-r-0' : '-translate-x-full lg:translate-x-0'\"\n style=\"z-index: 1000\">\n <!-- Search + New Thread -->\n <div class=\"p-3 shrink-0\">\n <div class=\"relative flex items-center gap-2\">\n <div class=\"relative flex-1\">\n <svg class=\"absolute left-2.5 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z\" />\n </svg>\n <input\n v-model=\"threadSearch\"\n type=\"text\"\n placeholder=\"Search threads...\"\n @keydown.esc=\"threadSearch = ''\"\n class=\"w-full rounded-md border border-edge bg-surface py-1.5 pl-8 pr-3 text-sm text-content placeholder:text-gray-400 focus:border-edge-strong focus:outline-none focus:ring-1 focus:ring-gray-300\"\n />\n </div>\n <async-button\n @click=\"createNewThread\"\n class=\"shrink-0 rounded-md border border-edge bg-surface p-1.5 text-content-tertiary hover:bg-muted hover:text-content-secondary\"\n title=\"New thread\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\" class=\"w-4 h-4\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 4.5v15m7.5-7.5h-15\" />\n </svg>\n </async-button>\n </div>\n </div>\n <!-- Thread list (scrollable) -->\n <nav class=\"flex-1 overflow-y-auto px-2 pb-2\">\n <div class=\"px-2 py-1 text-xs font-semibold text-gray-400 uppercase tracking-wider\">Threads</div>\n <div v-if=\"status === 'loaded' && filteredThreads.length === 0\" class=\"px-2 py-2 text-sm text-content-tertiary\">\n <template v-if=\"threadSearch.trim()\">No threads match \"{{threadSearch}}\"</template>\n <template v-else>No threads yet</template>\n </div>\n <ul role=\"list\">\n <li\n v-for=\"thread in filteredThreads\"\n :key=\"thread._id\"\n @click=\"selectThread(thread._id)\"\n class=\"rounded-md py-2 px-2 cursor-pointer mb-0.5\"\n :class=\"thread._id === chatThreadId ? 'bg-gray-200 text-content' : 'hover:bg-muted text-content-secondary'\"\n >\n <div class=\"text-sm font-medium truncate\">{{ thread.title || 'Untitled Thread' }}</div>\n <div class=\"text-xs text-gray-400 mt-0.5\">{{ formatThreadDate(thread.updatedAt || thread.createdAt) }}</div>\n </li>\n </ul>\n </nav>\n <!-- Bottom toolbar -->\n <div class=\"shrink-0 border-t border-edge bg-page px-2 py-1.5 flex items-center gap-1\">\n <button\n type=\"button\"\n @click=\"hideSidebar = true\"\n class=\"rounded p-1.5 text-gray-400 hover:text-gray-600 hover:bg-muted\"\n title=\"Hide sidebar\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"w-5 h-5\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"m18.75 4.5-7.5 7.5 7.5 7.5m-6-15L5.25 12l7.5 7.5\" />\n </svg>\n </button>\n <button\n type=\"button\"\n @click=\"hasWorkspace ? toggleShareThread() : (showProUpgradeModal = true)\"\n class=\"rounded p-1.5\"\n :class=\"hasWorkspace && chatThreadId ? 'text-gray-400 hover:text-gray-600 hover:bg-muted' : !hasWorkspace ? 'text-gray-400 hover:text-gray-600 hover:bg-muted' : 'text-gray-300 cursor-not-allowed'\"\n :disabled=\"hasWorkspace && !chatThreadId\"\n aria-label=\"Share thread with workspace\"\n :title=\"'Share thread with workspace' + (!hasWorkspace ? ' (requires a pro workspace)' : !chatThreadId ? ': Open a thread first!' : '')\"\n >\n <svg v-if=\"hasWorkspace\" xmlns=\"http://www.w3.org/2000/svg\" class=\"w-6 h-6\" fill=\"currentColor\" viewBox=\"0 0 24 24\"><path d=\"M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7a2.48 2.48 0 0 0 0-1.39l7.02-4.11a2.5 2.5 0 1 0-.87-1.37L8.04 9.94a2.5 2.5 0 1 0 0 4.12l7.12 4.16a2.5 2.5 0 1 0 .84-1.34l-7.05-4.12c-.04-.02-.08-.05-.11-.07a2.48 2.48 0 0 0 0-1.39c.03-.02.07-.04.11-.07l7.11-4.16c.52.47 1.2.76 1.94.76a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0-1.94.94L7.97 8.43a2.5 2.5 0 1 0 0 7.14l9.09 5.3c.52-.47 1.2-.76 1.94-.76a2.5 2.5 0 1 0 0-5z\"/></svg>\n <svg v-else xmlns=\"http://www.w3.org/2000/svg\" class=\"w-5 h-5\" fill=\"currentColor\" viewBox=\"0 0 24 24\"><path d=\"M12 1a5 5 0 00-5 5v3H6a2 2 0 00-2 2v9a2 2 0 002 2h12a2 2 0 002-2v-9a2 2 0 00-2-2h-1V6a5 5 0 00-5-5zm-3 8V6a3 3 0 016 0v3H9zm9 2v9H6v-9h12z\"/></svg>\n </button>\n <button\n type=\"button\"\n @click=\"toggleAgentMode\"\n class=\"rounded p-1.5 transition-colors\"\n :class=\"isAgentModeEnabled ? 'text-primary bg-primary/10' : 'text-gray-400 hover:text-gray-600 hover:bg-muted'\"\n :aria-label=\"isAgentModeEnabled ? 'Turn agent mode off' : 'Turn agent mode on'\"\n :aria-pressed=\"isAgentModeEnabled\"\n :title=\"isAgentModeEnabled ? 'Agent mode on' : 'Agent mode off'\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"w-5 h-5\" fill=\"currentColor\" viewBox=\"0 0 200 200\"><path d=\"m187 170.9-38.72-13.73-0.55-0.25-11.24-12.18v-9.98c6.3-6.82 6.43-11.03 8.3-23.22 7.34-1.41 10.2-7.7 12.28-18.9 0.97-5.02 2.72-9.88 0.14-13.92-1.38-2.35-3.49-3.08-5.62-3.38 1.65-11.57 5.47-27.07-2.68-42.38-5.47-9.94-14-14.18-21.8-14.92-3.5-9.15-12.32-13.06-27.11-13.06h-1.06c-27.2 0-48.53 17.96-52.18 39.74-2.05 11.38-0.41 20.41 0.74 30.03-2.23 0.19-4.38 1.02-5.84 3.42-2.21 3.83-1.27 8.84 0.06 14.19 2 8.28 2.26 16.18 12.9 18.96 1.47 6.52 0.58 14.07 8.3 22.84v10.77l-12.54 12.83-38.38 12.85v24.18h57.31c-3.11-14.37-6.53-23.75-5.45-42.72l0.44-0.34c5.71 9.04 16.02 15.97 29.22 25.91l-12.68 17.15h38.07l-13.16-17.2c11.44-9.29 22.31-16.55 29.2-25.38 4.68 9.53-1.48 26.7-4.72 42.58h56.77v-23.89zm-49.56-49.09c-1.43 5.43-2.53 6.16-9.69 14.68l-11.68 13.11c-3.48 3.27-4.8 3.27-11.11 3.27h-10.39c-6.25 0-7.45-0.78-11.01-4.46-6.78-7.7-11.76-13.37-15.09-17.32-5.39-6.33-5.85-7.2-7.16-18.89-0.73-4.86-3.02-14.25-2.94-14.91 3.94 3.43 13.59 3.35 16.89 3.35 12.76 0 15.79-2.74 19.92-17.12 0.68-2.98 1.71-3.53 4-3.53 2.04 0 3.04 0.98 3.86 3.71 3.9 13.69 5.2 16.52 16.15 16.76h2.99c8.1 0 15.01-1.52 18.58-2.93-0.5 5.06-2.28 16.7-3.32 24.28zm4.72-39.02c-1.97 10.98-2.94 12.3-17.69 12.91-9.05 0.38-11.35-0.71-14.17-8.69-3.69-11.25-2.17-13 5.55-13.83 6.36-0.83 11.74-0.79 15.92-0.79 10.01 0 12.11 0.27 10.39 10.4zm-84.38-32c0-11.99 9.26-21.63 15.54-23.52 4.46-1.14 7.99 3.61 15.25 5.74 5.06 1.54 10.71 1.32 11.84 1.32 11.49 0 16.71-2.62 21.55-6.91 1.66-1.28 3.22-1.57 4.86-0.67 10.89 6.36 15.34 16.28 14.74 26.07-0.36 5.5-2.18 9.03-1.38 12.23l1.14 3.62c-3.83-0.82-6.2-0.92-10.69-0.92-9.44 0-17.61 1.24-22.14 2.5-3.66 1.24-5.2 1.44-8.35 1.44-2.98 0-4.92-0.2-7.56-1.44-4.97-1.54-14.49-2.5-21.47-2.5-3.69 0-7 0.01-10.9 0.62l-1.84 0.3 0.42-3.07c0.82-3.01-1.01-7.56-1.01-14.81z\"/><path d=\"m116.6 125.4-12.18-2.58c-2.29-0.68-3.7 0.76-5.4 0.76-1.66 0-2.79-1.35-5.4-0.66l-10.88 2.36c-1.35 0.12-1.55 1.44-0.79 3.32 0.38 1.02 1.26 0.7 3.07 0.36 3.25-0.46 4.65-0.84 8.11-1.04 1.85-0.17 2.59 0.41 5.06 0.41h1.14c1.72 0 2.62-0.55 4.15-0.31 3.81 0.31 5.64 0.72 11.64 1.22 2.08 0.38 2.4-1.15 2.21-2.52-0.07-0.65-0.24-1.1-0.73-1.32z\"/><path d=\"m94.08 133.5c-1.96 0-2.49 1.23-2.4 2.39 0.06 1.38 0.93 2.27 2.57 2.27h10.06c2.13 0 2.7-1.25 2.7-2.56 0-1.13-0.65-2.1-2.53-2.1h-10.4z\"/><path d=\"m91.31 106.6c-2.13 0-2.51 3.32-1.24 4.55 1.11 1.08 7.39 3.76 9.34 3.76s8.38-3.24 9.4-3.87c1.9-1.2 0.85-4.82-1.7-4.44-1.84 0.3-6.01 3.39-7.96 3.01-1.53-0.32-6.08-3.01-7.84-3.01z\"/><path d=\"m85.62 73.71c-5.3-1.1-10.53-1.15-14.51-1.15-10.96 0-14.08-0.44-14.33 5.26-0.47 11.15 1.82 15.96 6.69 17.19 4.7 1.26 6.84 1.08 11.71 1.08 8.88 0 10.9-1.18 14.23-10.67 2.38-6.81 1.87-10.59-3.79-11.71z\"/><path d=\"m114.5 73.51c-7.72 0.83-7.35 2.71-6.39 9.73 1.78 7.47 3.64 11.94 10.97 12.49 3.07 0.14 4.97-0.05 8-0.32 10.72-0.96 12.6-2.48 14.45-12.22 1.88-10.29-0.54-10.77-9.79-10.77-4.18 0-10.45 0.26-17.24 1.09z\"/></svg>\n </button>\n </div>\n </aside>\n\n <div class=\"flex-1 min-w-0 flex\">\n <!-- Main Chat Area -->\n <main class=\"flex-1 min-w-0 flex flex-col bg-slate-50\">\n <div class=\"flex-1 overflow-y-auto p-6 space-y-4\" ref=\"messagesContainer\">\n <div v-if=\"chatMessages?.length === 0\">\n <div class=\"flex items-center w-full h-full justify-center py-3 mb-4\">\n <p class=\"mx-4 font-bold text-content\">\n Ask Mongoose Studio to analyze your data or generate a script\n </p>\n </div>\n </div>\n <ul role=\"list\" class=\"space-y-4\" v-else>\n <li v-for=\"message in chatMessages\" :key=\"message._id\">\n <chat-message :message=\"message\" :target-dashboard-id=\"currentThread?.dashboardId\"></chat-message>\n </li>\n </ul>\n </div>\n\n\n <!-- Input Area -->\n <div class=\"border-t p-4\">\n <form @submit.prevent=\"sendMessage\" :disabled=\"sendingMessage\" class=\"flex gap-2 items-end justify-end\">\n <textarea\n v-model=\"newMessage\"\n :placeholder=\"sendingMessage ? 'Sending...' : 'Ask about your data, generate a query, or build a chart…'\"\n class=\"flex-1 border rounded px-4 py-2 resize-none overflow-y-auto shadow-sm\"\n :disabled=\"sendingMessage\"\n rows=\"2\"\n ref=\"messageInput\"\n @input=\"adjustTextareaHeight\"\n @keydown.enter.exact.prevent=\"handleEnter\"\n ></textarea>\n <button class=\"bg-primary hover:bg-primary-hover text-white px-4 h-[42px] rounded disabled:bg-gray-600\" :disabled=\"sendingMessage\">\n <svg v-if=\"sendingMessage\" style=\"height: 1em\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <g>\n <circle cx=\"12\" cy=\"12\" r=\"10\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" opacity=\"0.3\" />\n <path d=\"M12 2a10 10 0 0 1 10 10\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\">\n <animateTransform attributeName=\"transform\" type=\"rotate\" from=\"0 12 12\" to=\"360 12 12\" dur=\"1s\" repeatCount=\"indefinite\" />\n </path>\n </g>\n </svg>\n <span v-else>Send</span>\n </button>\n </form>\n </div>\n </main>\n\n <aside\n v-if=\"isAgentModeEnabled\"\n class=\"hidden lg:flex shrink-0 border-l border-edge bg-page transition-all duration-300 ease-in-out overflow-hidden\"\n :class=\"showAgentSidebar ? 'w-80' : 'w-0 border-l-0'\"\n >\n <div v-if=\"showAgentSidebar\" class=\"flex h-full w-80 flex-col\">\n <div class=\"flex items-center justify-between border-b border-edge px-4 py-3\">\n <div>\n <h2 class=\"text-sm font-semibold text-content\">Agent Mode</h2>\n <p class=\"mt-1 text-xs text-content-tertiary\">Agent mode lets Mongoose Studio inspect your data and validate scripts before it responds.</p>\n </div>\n <button\n type=\"button\"\n @click=\"closeAgentSidebar\"\n class=\"rounded p-1.5 text-gray-400 hover:bg-muted hover:text-gray-600\"\n title=\"Hide agent sidebar\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"w-4 h-4\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6 18 18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n <div class=\"flex-1 overflow-y-auto px-4 py-4\">\n <div class=\"mb-3 text-xs font-semibold uppercase tracking-wider text-gray-400\">Available Tools</div>\n <ul role=\"list\" class=\"space-y-3\">\n <li v-for=\"tool in agentTools\" :key=\"tool.name\" class=\"rounded-md border border-edge bg-surface px-3 py-3\">\n <div class=\"text-sm font-medium text-content\">{{ tool.name }}</div>\n <div class=\"mt-1 text-sm text-content-secondary\">{{ tool.description }}</div>\n </li>\n </ul>\n </div>\n </div>\n </aside>\n </div>\n\n <pro-upgrade-modal\n :show=\"showProUpgradeModal\"\n feature-description=\"Sharing threads lets you collaborate with your team by sharing chat threads within your workspace.\"\n @close=\"showProUpgradeModal = false\"\n ></pro-upgrade-modal>\n</div>\n";
51602
+ module.exports = "<div class=\"flex\" style=\"height: calc(100vh - 55px); height: calc(100dvh - 55px)\">\n <button\n type=\"button\"\n class=\"fixed top-[65px] left-0 cursor-pointer bg-muted rounded-r-md p-1\"\n :class=\"hideSidebar === true ? 'z-[10001]' : hideSidebar === false ? 'z-10' : 'z-30 lg:hidden'\"\n @click=\"hideSidebar = hideSidebar === false ? true : false\"\n aria-label=\"Toggle chat history\"\n title=\"Toggle chat history\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"w-5 h-5 text-content-tertiary\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"m5.25 4.5 7.5 7.5-7.5 7.5m6-15 7.5 7.5-7.5 7.5\" />\n </svg>\n </button>\n <!-- Sidebar: Chat Threads -->\n <aside\n class=\"bg-page border-r overflow-hidden h-full transition-all duration-300 ease-in-out z-20 w-64 fixed lg:relative shrink-0 flex flex-col\"\n :class=\"hideSidebar === false ? 'translate-x-0' : hideSidebar === true ? '-translate-x-full lg:translate-x-0 lg:!w-0 lg:!border-r-0' : '-translate-x-full lg:translate-x-0'\"\n style=\"z-index: 1000\">\n <!-- Search + New Thread -->\n <div class=\"p-3 shrink-0\">\n <div class=\"relative flex items-center gap-2\">\n <div class=\"relative flex-1\">\n <svg class=\"absolute left-2.5 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400\" xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z\" />\n </svg>\n <input\n v-model=\"threadSearch\"\n type=\"text\"\n placeholder=\"Search threads...\"\n @keydown.esc=\"threadSearch = ''\"\n class=\"w-full rounded-md border border-edge bg-surface py-1.5 pl-8 pr-3 text-sm text-content placeholder:text-gray-400 focus:border-edge-strong focus:outline-none focus:ring-1 focus:ring-gray-300\"\n />\n </div>\n <async-button\n @click=\"createNewThread\"\n class=\"shrink-0 rounded-md border border-edge bg-surface p-1.5 text-content-tertiary hover:bg-muted hover:text-content-secondary\"\n title=\"New thread\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"2\" stroke=\"currentColor\" class=\"w-4 h-4\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 4.5v15m7.5-7.5h-15\" />\n </svg>\n </async-button>\n </div>\n </div>\n <!-- Thread list (scrollable) -->\n <nav class=\"flex-1 overflow-y-auto px-2 pb-2\">\n <div class=\"px-2 py-1 text-xs font-semibold text-gray-400 uppercase tracking-wider\">Threads</div>\n <div v-if=\"status === 'loaded' && filteredThreads.length === 0\" class=\"px-2 py-2 text-sm text-content-tertiary\">\n <template v-if=\"threadSearch.trim()\">No threads match \"{{threadSearch}}\"</template>\n <template v-else>No threads yet</template>\n </div>\n <ul role=\"list\">\n <li\n v-for=\"thread in filteredThreads\"\n :key=\"thread._id\"\n @click=\"selectThread(thread._id)\"\n class=\"rounded-md py-2 px-2 cursor-pointer mb-0.5\"\n :class=\"thread._id === chatThreadId ? 'bg-gray-200 text-content' : 'hover:bg-muted text-content-secondary'\"\n >\n <div class=\"text-sm font-medium truncate\">{{ thread.title || 'Untitled Thread' }}</div>\n <div class=\"text-xs text-gray-400 mt-0.5\">{{ formatThreadDate(thread.updatedAt || thread.createdAt) }}</div>\n </li>\n </ul>\n </nav>\n <!-- Bottom toolbar -->\n <div class=\"shrink-0 border-t border-edge bg-page px-2 py-1.5 flex items-center gap-1\">\n <button\n type=\"button\"\n @click=\"hideSidebar = true\"\n class=\"rounded p-1.5 text-gray-400 hover:text-gray-600 hover:bg-muted\"\n title=\"Hide sidebar\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"w-5 h-5\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"m18.75 4.5-7.5 7.5 7.5 7.5m-6-15L5.25 12l7.5 7.5\" />\n </svg>\n </button>\n <button\n type=\"button\"\n @click=\"hasWorkspace ? toggleShareThread() : (showProUpgradeModal = true)\"\n class=\"rounded p-1.5\"\n :class=\"hasWorkspace && chatThreadId ? 'text-gray-400 hover:text-gray-600 hover:bg-muted' : !hasWorkspace ? 'text-gray-400 hover:text-gray-600 hover:bg-muted' : 'text-gray-300 cursor-not-allowed'\"\n :disabled=\"hasWorkspace && !chatThreadId\"\n aria-label=\"Share thread with workspace\"\n :title=\"'Share thread with workspace' + (!hasWorkspace ? ' (requires a pro workspace)' : !chatThreadId ? ': Open a thread first!' : '')\"\n >\n <svg v-if=\"hasWorkspace\" xmlns=\"http://www.w3.org/2000/svg\" class=\"w-6 h-6\" fill=\"currentColor\" viewBox=\"0 0 24 24\"><path d=\"M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7a2.48 2.48 0 0 0 0-1.39l7.02-4.11a2.5 2.5 0 1 0-.87-1.37L8.04 9.94a2.5 2.5 0 1 0 0 4.12l7.12 4.16a2.5 2.5 0 1 0 .84-1.34l-7.05-4.12c-.04-.02-.08-.05-.11-.07a2.48 2.48 0 0 0 0-1.39c.03-.02.07-.04.11-.07l7.11-4.16c.52.47 1.2.76 1.94.76a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0-1.94.94L7.97 8.43a2.5 2.5 0 1 0 0 7.14l9.09 5.3c.52-.47 1.2-.76 1.94-.76a2.5 2.5 0 1 0 0-5z\"/></svg>\n <svg v-else xmlns=\"http://www.w3.org/2000/svg\" class=\"w-5 h-5\" fill=\"currentColor\" viewBox=\"0 0 24 24\"><path d=\"M12 1a5 5 0 00-5 5v3H6a2 2 0 00-2 2v9a2 2 0 002 2h12a2 2 0 002-2v-9a2 2 0 00-2-2h-1V6a5 5 0 00-5-5zm-3 8V6a3 3 0 016 0v3H9zm9 2v9H6v-9h12z\"/></svg>\n </button>\n <button\n type=\"button\"\n @click=\"toggleAgentMode\"\n class=\"rounded p-1.5 transition-colors\"\n :class=\"isAgentModeEnabled ? 'text-primary bg-primary/10' : 'text-gray-400 hover:text-gray-600 hover:bg-muted'\"\n :aria-label=\"isAgentModeEnabled ? 'Turn agent mode off' : 'Turn agent mode on'\"\n :aria-pressed=\"isAgentModeEnabled\"\n :title=\"isAgentModeEnabled ? 'Agent mode on' : 'Agent mode off'\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"w-5 h-5\" fill=\"currentColor\" viewBox=\"0 0 200 200\"><path d=\"m187 170.9-38.72-13.73-0.55-0.25-11.24-12.18v-9.98c6.3-6.82 6.43-11.03 8.3-23.22 7.34-1.41 10.2-7.7 12.28-18.9 0.97-5.02 2.72-9.88 0.14-13.92-1.38-2.35-3.49-3.08-5.62-3.38 1.65-11.57 5.47-27.07-2.68-42.38-5.47-9.94-14-14.18-21.8-14.92-3.5-9.15-12.32-13.06-27.11-13.06h-1.06c-27.2 0-48.53 17.96-52.18 39.74-2.05 11.38-0.41 20.41 0.74 30.03-2.23 0.19-4.38 1.02-5.84 3.42-2.21 3.83-1.27 8.84 0.06 14.19 2 8.28 2.26 16.18 12.9 18.96 1.47 6.52 0.58 14.07 8.3 22.84v10.77l-12.54 12.83-38.38 12.85v24.18h57.31c-3.11-14.37-6.53-23.75-5.45-42.72l0.44-0.34c5.71 9.04 16.02 15.97 29.22 25.91l-12.68 17.15h38.07l-13.16-17.2c11.44-9.29 22.31-16.55 29.2-25.38 4.68 9.53-1.48 26.7-4.72 42.58h56.77v-23.89zm-49.56-49.09c-1.43 5.43-2.53 6.16-9.69 14.68l-11.68 13.11c-3.48 3.27-4.8 3.27-11.11 3.27h-10.39c-6.25 0-7.45-0.78-11.01-4.46-6.78-7.7-11.76-13.37-15.09-17.32-5.39-6.33-5.85-7.2-7.16-18.89-0.73-4.86-3.02-14.25-2.94-14.91 3.94 3.43 13.59 3.35 16.89 3.35 12.76 0 15.79-2.74 19.92-17.12 0.68-2.98 1.71-3.53 4-3.53 2.04 0 3.04 0.98 3.86 3.71 3.9 13.69 5.2 16.52 16.15 16.76h2.99c8.1 0 15.01-1.52 18.58-2.93-0.5 5.06-2.28 16.7-3.32 24.28zm4.72-39.02c-1.97 10.98-2.94 12.3-17.69 12.91-9.05 0.38-11.35-0.71-14.17-8.69-3.69-11.25-2.17-13 5.55-13.83 6.36-0.83 11.74-0.79 15.92-0.79 10.01 0 12.11 0.27 10.39 10.4zm-84.38-32c0-11.99 9.26-21.63 15.54-23.52 4.46-1.14 7.99 3.61 15.25 5.74 5.06 1.54 10.71 1.32 11.84 1.32 11.49 0 16.71-2.62 21.55-6.91 1.66-1.28 3.22-1.57 4.86-0.67 10.89 6.36 15.34 16.28 14.74 26.07-0.36 5.5-2.18 9.03-1.38 12.23l1.14 3.62c-3.83-0.82-6.2-0.92-10.69-0.92-9.44 0-17.61 1.24-22.14 2.5-3.66 1.24-5.2 1.44-8.35 1.44-2.98 0-4.92-0.2-7.56-1.44-4.97-1.54-14.49-2.5-21.47-2.5-3.69 0-7 0.01-10.9 0.62l-1.84 0.3 0.42-3.07c0.82-3.01-1.01-7.56-1.01-14.81z\"/><path d=\"m116.6 125.4-12.18-2.58c-2.29-0.68-3.7 0.76-5.4 0.76-1.66 0-2.79-1.35-5.4-0.66l-10.88 2.36c-1.35 0.12-1.55 1.44-0.79 3.32 0.38 1.02 1.26 0.7 3.07 0.36 3.25-0.46 4.65-0.84 8.11-1.04 1.85-0.17 2.59 0.41 5.06 0.41h1.14c1.72 0 2.62-0.55 4.15-0.31 3.81 0.31 5.64 0.72 11.64 1.22 2.08 0.38 2.4-1.15 2.21-2.52-0.07-0.65-0.24-1.1-0.73-1.32z\"/><path d=\"m94.08 133.5c-1.96 0-2.49 1.23-2.4 2.39 0.06 1.38 0.93 2.27 2.57 2.27h10.06c2.13 0 2.7-1.25 2.7-2.56 0-1.13-0.65-2.1-2.53-2.1h-10.4z\"/><path d=\"m91.31 106.6c-2.13 0-2.51 3.32-1.24 4.55 1.11 1.08 7.39 3.76 9.34 3.76s8.38-3.24 9.4-3.87c1.9-1.2 0.85-4.82-1.7-4.44-1.84 0.3-6.01 3.39-7.96 3.01-1.53-0.32-6.08-3.01-7.84-3.01z\"/><path d=\"m85.62 73.71c-5.3-1.1-10.53-1.15-14.51-1.15-10.96 0-14.08-0.44-14.33 5.26-0.47 11.15 1.82 15.96 6.69 17.19 4.7 1.26 6.84 1.08 11.71 1.08 8.88 0 10.9-1.18 14.23-10.67 2.38-6.81 1.87-10.59-3.79-11.71z\"/><path d=\"m114.5 73.51c-7.72 0.83-7.35 2.71-6.39 9.73 1.78 7.47 3.64 11.94 10.97 12.49 3.07 0.14 4.97-0.05 8-0.32 10.72-0.96 12.6-2.48 14.45-12.22 1.88-10.29-0.54-10.77-9.79-10.77-4.18 0-10.45 0.26-17.24 1.09z\"/></svg>\n </button>\n </div>\n </aside>\n\n <div class=\"flex-1 min-w-0 flex\">\n <!-- Main Chat Area -->\n <main class=\"flex-1 min-w-0 flex flex-col bg-slate-50\">\n <div class=\"flex-1 overflow-y-auto p-6 space-y-4\" ref=\"messagesContainer\">\n <div v-if=\"chatMessages?.length === 0\">\n <div class=\"flex items-center w-full h-full justify-center py-3 mb-4\">\n <p class=\"mx-4 font-bold text-content\">\n Ask Mongoose Studio to analyze your data or generate a script\n </p>\n </div>\n </div>\n <ul role=\"list\" class=\"space-y-4\" v-else>\n <li v-for=\"message in chatMessages\" :key=\"message._id\">\n <chat-message\n :message=\"message\"\n :target-dashboard-id=\"currentThread?.dashboardId\"\n :capabilities=\"capabilities\">\n </chat-message>\n </li>\n </ul>\n </div>\n\n\n <!-- Input Area -->\n <div class=\"border-t p-4\">\n <form @submit.prevent=\"sendMessage\" :disabled=\"sendingMessage\" class=\"flex gap-2 items-end justify-end\">\n <textarea\n v-model=\"newMessage\"\n :placeholder=\"sendingMessage ? 'Sending...' : 'Ask about your data, generate a query, or build a chart…'\"\n class=\"flex-1 border rounded px-4 py-2 resize-none overflow-y-auto shadow-sm\"\n :disabled=\"sendingMessage\"\n rows=\"2\"\n ref=\"messageInput\"\n @input=\"adjustTextareaHeight\"\n @keydown.enter.exact.prevent=\"handleEnter\"\n ></textarea>\n <button class=\"bg-primary hover:bg-primary-hover text-white px-4 h-[42px] rounded disabled:bg-gray-600\" :disabled=\"sendingMessage\">\n <svg v-if=\"sendingMessage\" style=\"height: 1em\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <g>\n <circle cx=\"12\" cy=\"12\" r=\"10\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" opacity=\"0.3\" />\n <path d=\"M12 2a10 10 0 0 1 10 10\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\">\n <animateTransform attributeName=\"transform\" type=\"rotate\" from=\"0 12 12\" to=\"360 12 12\" dur=\"1s\" repeatCount=\"indefinite\" />\n </path>\n </g>\n </svg>\n <span v-else>Send</span>\n </button>\n </form>\n </div>\n </main>\n\n <aside\n v-if=\"isAgentModeEnabled\"\n class=\"hidden lg:flex shrink-0 border-l border-edge bg-page transition-all duration-300 ease-in-out overflow-hidden\"\n :class=\"showAgentSidebar ? 'w-80' : 'w-0 border-l-0'\"\n >\n <div v-if=\"showAgentSidebar\" class=\"flex h-full w-80 flex-col\">\n <div class=\"flex items-center justify-between border-b border-edge px-4 py-3\">\n <div>\n <h2 class=\"text-sm font-semibold text-content\">Agent Mode</h2>\n <p class=\"mt-1 text-xs text-content-tertiary\">Agent mode lets Mongoose Studio inspect your data and validate scripts before it responds.</p>\n </div>\n <button\n type=\"button\"\n @click=\"closeAgentSidebar\"\n class=\"rounded p-1.5 text-gray-400 hover:bg-muted hover:text-gray-600\"\n title=\"Hide agent sidebar\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"w-4 h-4\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6 18 18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n <div class=\"flex-1 overflow-y-auto px-4 py-4\">\n <div class=\"mb-3 text-xs font-semibold uppercase tracking-wider text-gray-400\">Available Tools</div>\n <ul role=\"list\" class=\"space-y-3\">\n <li v-for=\"tool in agentTools\" :key=\"tool.name\" class=\"rounded-md border border-edge bg-surface px-3 py-3\">\n <div class=\"text-sm font-medium text-content\">{{ tool.name }}</div>\n <div class=\"mt-1 text-sm text-content-secondary\">{{ tool.description }}</div>\n </li>\n </ul>\n </div>\n </div>\n </aside>\n </div>\n\n <pro-upgrade-modal\n :show=\"showProUpgradeModal\"\n feature-description=\"Sharing threads lets you collaborate with your team by sharing chat threads within your workspace.\"\n @close=\"showProUpgradeModal = false\"\n ></pro-upgrade-modal>\n</div>\n";
51527
51603
 
51528
51604
  /***/ },
51529
51605
 
@@ -52128,7 +52204,7 @@ module.exports = "<div class=\"list-subdocument tooltip\">\n <div>{{shortenValu
52128
52204
  (module) {
52129
52205
 
52130
52206
  "use strict";
52131
- module.exports = "/** Vue modal */\n\n.modal-mask {\n position: fixed;\n z-index: 9998;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: rgba(0, 0, 0, 0.5);\n display: table;\n transition: opacity 0.3s ease;\n}\n\n.modal-wrapper {\n display: table-cell;\n vertical-align: middle;\n}\n\n.modal-container {\n width: 600px;\n margin: 0px auto;\n padding: 20px 30px;\n padding-bottom: 40px;\n background-color: #fff;\n border-radius: 2px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.33);\n transition: all 0.3s ease;\n font-family: Helvetica, Arial, sans-serif;\n position: relative;\n}\n\n.modal-header {\n margin-top: 0;\n font-size: 18px;\n font-weight: bold;\n}\n\n.modal-header-success {\n color: #42b983;\n}\n\n.modal-header-error {\n color: #ff0000;\n}\n\n.modal-body {\n margin: 20px 0;\n max-height: calc(100vh - 40px - 60px - 10px);\n overflow: auto;\n}\n\n.modal__button--default {\n float: right;\n}\n\n/*\n * The following styles are auto-applied to elements with\n * transition=\"modal\" when their visibility is toggled\n * by Vue.js.\n *\n * You can easily play with the modal transition by editing\n * these styles.\n */\n\n.modal-enter {\n opacity: 0;\n}\n\n.modal-leave-active {\n opacity: 0;\n}\n\n.modal-enter .modal-container,\n.modal-leave-active .modal-container {\n -webkit-transform: scale(1.1);\n transform: scale(1.1);\n}\n\n.modal-container .modal-exit {\n position: absolute;\n right: 0.25em;\n top: 0.25em;\n cursor: pointer;\n font-size: 1.25em;\n height: 1.25em;\n width: 1.25em;\n border-radius: 100%;\n border: 1px solid #ddd;\n display: flex;\n align-items: center;\n justify-content: center;\n padding-bottom: 0.25em;\n}\n\n.modal-container .modal-exit:hover {\n background-color: #f1f5ff;\n}\n\n@media (max-width: 767px) {\n .modal-container {\n width: calc(100vw - 10px);\n margin: 0;\n margin-left: 5px;\n }\n}\n";
52207
+ module.exports = "/** Vue modal */\n\n.modal-mask {\n position: fixed;\n z-index: 9998;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: rgba(0, 0, 0, 0.5);\n display: table;\n transition: opacity 0.3s ease;\n}\n\n.modal-wrapper {\n display: table-cell;\n vertical-align: middle;\n}\n\n.modal-container {\n width: 600px;\n margin: 0px auto;\n padding: 20px 30px;\n padding-bottom: 40px;\n background-color: #fff;\n border-radius: 2px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.33);\n transition: all 0.3s ease;\n font-family: Helvetica, Arial, sans-serif;\n position: relative;\n}\n\n.modal-header {\n margin-top: 0;\n font-size: 18px;\n font-weight: bold;\n}\n\n.modal-header-success {\n color: #42b983;\n}\n\n.modal-header-error {\n color: #ff0000;\n}\n\n.modal-body {\n margin: 20px 0;\n max-height: calc(100vh - 40px - 60px - 10px);\n overflow: auto;\n}\n\n.modal__button--default {\n float: right;\n}\n\n/*\n * The following styles are auto-applied to elements with\n * transition=\"modal\" when their visibility is toggled\n * by Vue.js.\n *\n * You can easily play with the modal transition by editing\n * these styles.\n */\n\n.modal-enter {\n opacity: 0;\n}\n\n.modal-leave-active {\n opacity: 0;\n}\n\n.modal-enter .modal-container,\n.modal-leave-active .modal-container {\n -webkit-transform: scale(1.1);\n transform: scale(1.1);\n}\n\n.modal-container .modal-exit {\n position: absolute;\n right: 0.25em;\n top: 0.25em;\n cursor: pointer;\n font-size: 1.25em;\n height: 1.25em;\n width: 1.25em;\n border-radius: 100%;\n border: 1px solid #ddd;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.modal-container .modal-exit:hover {\n background-color: #f1f5ff;\n}\n\n@media (max-width: 767px) {\n .modal-container {\n width: calc(100vw - 10px);\n margin: 0;\n margin-left: 5px;\n }\n}\n";
52132
52208
 
52133
52209
  /***/ },
52134
52210
 
@@ -63690,7 +63766,7 @@ var src_default = VueToastificationPlugin;
63690
63766
  (module) {
63691
63767
 
63692
63768
  "use strict";
63693
- module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version":"0.4.1","description":"A Mongoose-native MongoDB UI with schema-aware autocomplete, AI-assisted queries, and dashboards that understand your models - not just your data.","homepage":"https://mongoosestudio.app/","repository":{"type":"git","url":"https://github.com/mongoosejs/studio"},"license":"Apache-2.0","dependencies":{"@ai-sdk/anthropic":"2.x","@ai-sdk/google":"2.x","@ai-sdk/openai":"2.x","ace-builds":"^1.43.6","ai":"5.x","archetype":"0.13.1","csv-stringify":"6.3.0","ejson":"^2.2.3","extrovert":"^0.2.0","marked":"15.0.12","node-inspect-extracted":"3.x","regexp.escape":"^2.0.1","tailwindcss":"3.4.0","time-commando":"1.0.1","typescript":"^6.0.2","vue":"3.x","vue-toastification":"^2.0.0-rc.5","webpack":"5.x","xss":"^1.0.15"},"peerDependencies":{"mongoose":"7.x || 8.x || ^9.0.0"},"optionalPeerDependencies":{"@mongoosejs/task":"0.5.x || 0.6.x"},"devDependencies":{"@masteringjs/eslint-config":"0.1.1","axios":"1.2.2","dedent":"^1.6.0","eslint":"9.30.0","express":"4.x","mocha":"10.2.0","mongodb-memory-server":"^11.0.1","mongoose":"9.x","sinon":"^21.0.1"},"scripts":{"lint":"eslint .","seed":"node seed/index.js","start":"node ./local.js","tailwind":"tailwindcss -o ./frontend/public/tw.css","tailwind:watch":"tailwindcss -o ./frontend/public/tw.css --watch","test":"mocha test/*.test.js","test:frontend":"mocha test/frontend/*.test.js"}}');
63769
+ module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version":"0.5.0","description":"AI-Powered Data Workspace for MongoDB: Turn your MongoDB data into dashboards, maps, and interactive workflows - powered by your Mongoose models and AI.","homepage":"https://mongoosestudio.app/","repository":{"type":"git","url":"https://github.com/mongoosejs/studio"},"license":"Apache-2.0","dependencies":{"@ai-sdk/anthropic":"2.x","@ai-sdk/google":"2.x","@ai-sdk/openai":"2.x","ace-builds":"^1.43.6","ai":"5.x","archetype":"0.13.1","csv-stringify":"6.3.0","ejson":"^2.2.3","extrovert":"^0.2.0","marked":"15.0.12","node-inspect-extracted":"3.x","regexp.escape":"^2.0.1","tailwindcss":"3.4.0","time-commando":"1.0.1","typescript":"^6.0.2","vue":"3.x","vue-toastification":"^2.0.0-rc.5","webpack":"5.x","xss":"^1.0.15"},"peerDependencies":{"mongoose":"7.x || 8.x || ^9.0.0"},"optionalPeerDependencies":{"@mongoosejs/task":"0.5.x || 0.6.x"},"devDependencies":{"@masteringjs/eslint-config":"0.1.1","axios":"1.2.2","dedent":"^1.6.0","eslint":"9.30.0","express":"4.x","mocha":"10.2.0","mongodb-memory-server":"^11.0.1","mongoose":"9.x","sinon":"^21.0.1"},"scripts":{"lint":"eslint .","seed":"node seed/index.js","start":"node ./local.js","tailwind":"tailwindcss -o ./frontend/public/tw.css","tailwind:watch":"tailwindcss -o ./frontend/public/tw.css --watch","test":"mocha test/*.test.js","test:frontend":"mocha test/frontend/*.test.js"}}');
63694
63770
 
63695
63771
  /***/ }
63696
63772
 
@@ -1826,6 +1826,10 @@ video {
1826
1826
  border-style: none;
1827
1827
  }
1828
1828
 
1829
+ .border-black\/10 {
1830
+ border-color: rgb(0 0 0 / 0.1);
1831
+ }
1832
+
1829
1833
  .border-blue-300 {
1830
1834
  --tw-border-opacity: 1;
1831
1835
  border-color: rgb(147 197 253 / var(--tw-border-opacity));
@@ -29,6 +29,9 @@ client.interceptors.response.use(
29
29
  );
30
30
 
31
31
  if (window.MONGOOSE_STUDIO_CONFIG.isLambda) {
32
+ exports.getCapabilities = function getCapabilities() {
33
+ return client.post('', { action: 'getCapabilities' }).then(res => res.data);
34
+ };
32
35
  exports.status = function status() {
33
36
  return client.post('', { action: 'status' }).then(res => res.data);
34
37
  };
@@ -205,6 +208,9 @@ if (window.MONGOOSE_STUDIO_CONFIG.isLambda) {
205
208
  }
206
209
  };
207
210
  } else {
211
+ exports.getCapabilities = function getCapabilities() {
212
+ return client.get('/getCapabilities').then(res => res.data);
213
+ };
208
214
  exports.status = function status() {
209
215
  return client.get('/status').then(res => res.data);
210
216
  };
@@ -24,6 +24,7 @@
24
24
  :script="part.content"
25
25
  :language="part.language"
26
26
  :target-dashboard-id="targetDashboardId"
27
+ :capabilities="capabilities"
27
28
  @copyMessage="copyMessage"></chat-message-script>
28
29
  </div>
29
30
  </div>
@@ -6,7 +6,7 @@ const template = require('./chat-message.html');
6
6
 
7
7
  module.exports = app => app.component('chat-message', {
8
8
  template: template,
9
- props: ['message', 'targetDashboardId'],
9
+ props: ['message', 'targetDashboardId', 'capabilities'],
10
10
  computed: {
11
11
  styleForMessage() {
12
12
  return this.message.role === 'user' ? 'p-3 bg-muted' : 'py-3 pr-3';
@@ -62,10 +62,11 @@ module.exports = app => app.component('chat-message', {
62
62
  if (input.limit && input.limit !== 10) parts.push('limit=' + input.limit);
63
63
  return parts.length ? '(' + parts.join(', ') + ')' : '';
64
64
  },
65
- async executeScript(message, script) {
65
+ async executeScript(message, script, dryRun) {
66
66
  const { chatMessage } = await api.ChatMessage.executeScript({
67
67
  chatMessageId: message._id,
68
- script
68
+ script,
69
+ dryRun
69
70
  });
70
71
  message.executionResult = chatMessage.executionResult;
71
72
  console.log(message);