@mongoosejs/studio 0.5.14 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/AGENTS.md +69 -2
  2. package/README.md +24 -0
  3. package/backend/actions/ChatMessage/executeScript.js +14 -6
  4. package/backend/actions/ChatThread/createChatMessage.js +7 -4
  5. package/backend/actions/ChatThread/getChatThread.js +7 -4
  6. package/backend/actions/ChatThread/listChatThreads.js +5 -2
  7. package/backend/actions/ChatThread/shareChatThread.js +5 -2
  8. package/backend/actions/ChatThread/streamChatMessage.js +7 -4
  9. package/backend/actions/ChatThread/toggleAgentMode.js +5 -2
  10. package/backend/actions/Dashboard/createDashboard.js +3 -0
  11. package/backend/actions/Dashboard/deleteDashboard.js +9 -3
  12. package/backend/actions/Dashboard/getDashboard.js +23 -11
  13. package/backend/actions/Dashboard/getDashboards.js +8 -2
  14. package/backend/actions/Dashboard/updateDashboard.js +8 -2
  15. package/backend/actions/Model/addField.js +8 -2
  16. package/backend/actions/Model/analyzeSchema.js +15 -5
  17. package/backend/actions/Model/createChatMessage.js +3 -0
  18. package/backend/actions/Model/createDocument.js +3 -0
  19. package/backend/actions/Model/deleteDocument.js +8 -2
  20. package/backend/actions/Model/deleteDocuments.js +8 -2
  21. package/backend/actions/Model/dropCollection.js +3 -0
  22. package/backend/actions/Model/dropIndex.js +3 -0
  23. package/backend/actions/Model/executeDocumentScript.js +14 -3
  24. package/backend/actions/Model/exportQueryResults.js +12 -2
  25. package/backend/actions/Model/getCollectionInfo.js +14 -4
  26. package/backend/actions/Model/getDocument.js +12 -2
  27. package/backend/actions/Model/getDocuments.js +13 -3
  28. package/backend/actions/Model/getDocumentsStream.js +13 -3
  29. package/backend/actions/Model/getEstimatedDocumentCounts.js +12 -2
  30. package/backend/actions/Model/getIndexes.js +3 -0
  31. package/backend/actions/Model/getSuggestedProjection.js +3 -0
  32. package/backend/actions/Model/listModels.js +3 -0
  33. package/backend/actions/Model/streamChatMessage.js +3 -0
  34. package/backend/actions/Model/streamDocumentChanges.js +3 -0
  35. package/backend/actions/Model/updateDocument.js +8 -2
  36. package/backend/actions/Model/updateDocuments.js +8 -2
  37. package/backend/actions/Model/validateDocument.js +12 -2
  38. package/backend/actions/Script/createScript.js +36 -0
  39. package/backend/actions/Script/executeScript.js +89 -0
  40. package/backend/actions/Script/getScript.js +43 -0
  41. package/backend/actions/Script/index.js +5 -0
  42. package/backend/actions/Task/cancelTask.js +5 -2
  43. package/backend/actions/Task/getTaskOverview.js +6 -3
  44. package/backend/actions/Task/getTasks.js +6 -3
  45. package/backend/actions/Task/getTasksOverTime.js +6 -3
  46. package/backend/actions/Task/rescheduleTask.js +5 -2
  47. package/backend/actions/Task/runTask.js +5 -2
  48. package/backend/actions/index.js +1 -0
  49. package/backend/authorize.js +6 -1
  50. package/backend/db/scriptSchema.js +29 -0
  51. package/backend/helpers/operationOptions.js +26 -0
  52. package/backend/helpers/readOperationOptions.js +31 -0
  53. package/backend/index.js +2 -0
  54. package/backend/integrations/mothership.js +28 -0
  55. package/backend/mcp.js +140 -0
  56. package/backend/netlify.js +5 -3
  57. package/backend/next.js +5 -3
  58. package/backend/sandbox/createSandbox.js +2 -2
  59. package/backend/sandbox/createScriptDb.js +40 -23
  60. package/backend/util/archetypeToZodSchema.js +74 -0
  61. package/docs/mcp-oauth.md +106 -0
  62. package/eslint.config.js +1 -0
  63. package/eval/agent/run.js +1 -1
  64. package/express.js +183 -41
  65. package/frontend/index.js +3 -0
  66. package/frontend/public/app.js +441 -48
  67. package/frontend/public/tw.css +8 -0
  68. package/frontend/src/api.js +22 -0
  69. package/frontend/src/index.js +9 -1
  70. package/frontend/src/mcp/mcp.html +55 -0
  71. package/frontend/src/mcp/mcp.js +27 -0
  72. package/frontend/src/navbar/navbar.html +8 -0
  73. package/frontend/src/navbar/navbar.js +3 -0
  74. package/frontend/src/routes.js +16 -0
  75. package/frontend/src/script/script.html +105 -0
  76. package/frontend/src/script/script.js +98 -0
  77. package/index.d.ts +10 -1
  78. package/nest.d.ts +3 -0
  79. package/package.json +5 -3
  80. package/vercel.d.ts +2 -0
  81. package/CLAUDE.md +0 -7
@@ -1204,6 +1204,17 @@ if (window.MONGOOSE_STUDIO_CONFIG.isLambda) {
1204
1204
  return client.post('', { action: 'ChatMessage.executeScript', ...params }).then(res => res.data);
1205
1205
  }
1206
1206
  };
1207
+ exports.Script = {
1208
+ createScript(params) {
1209
+ return client.post('', { action: 'Script.createScript', ...params }).then(res => res.data);
1210
+ },
1211
+ executeScript(params) {
1212
+ return client.post('', { action: 'Script.executeScript', ...params }).then(res => res.data);
1213
+ },
1214
+ getScript(params) {
1215
+ return client.post('', { action: 'Script.getScript', ...params }).then(res => res.data);
1216
+ }
1217
+ };
1207
1218
  exports.Model = {
1208
1219
  addField(params) {
1209
1220
  return client.post('', { action: 'Model.addField', ...params }).then(res => res.data);
@@ -1403,6 +1414,17 @@ if (window.MONGOOSE_STUDIO_CONFIG.isLambda) {
1403
1414
  return client.post('/ChatMessage/executeScript', params).then(res => res.data);
1404
1415
  }
1405
1416
  };
1417
+ exports.Script = {
1418
+ createScript: function createScript(params) {
1419
+ return client.post('/Script/createScript', params).then(res => res.data);
1420
+ },
1421
+ executeScript: function executeScript(params) {
1422
+ return client.post('/Script/executeScript', params).then(res => res.data);
1423
+ },
1424
+ getScript: function getScript(params) {
1425
+ return client.post('/Script/getScript', params).then(res => res.data);
1426
+ }
1427
+ };
1406
1428
  exports.Model = {
1407
1429
  addField(params) {
1408
1430
  return client.post('/Model/addField', params).then(res => res.data);
@@ -7851,7 +7873,7 @@ const {
7851
7873
  } = __webpack_require__(/*! ./_util/recent-pages-history */ "./frontend/src/_util/recent-pages-history.js");
7852
7874
  appendCSS(__webpack_require__(/*! vue-toastification/dist/index.css */ "./node_modules/vue-toastification/dist/index.css"));
7853
7875
 
7854
- const TRACKED_RECENT_PAGE_ROUTE_NAMES = new Set(['model', 'document', 'dashboard', 'chat']);
7876
+ const TRACKED_RECENT_PAGE_ROUTE_NAMES = new Set(['model', 'document', 'dashboard', 'chat', 'script']);
7855
7877
  const CHAT_ROUTE_NAMES = new Set(['chat index', 'chat']);
7856
7878
 
7857
7879
  function formatHistoryLabel(route) {
@@ -7873,6 +7895,9 @@ function formatHistoryLabel(route) {
7873
7895
  if (route.name === 'chat') {
7874
7896
  return route.params?.threadId ? `Chat: ${route.params.threadId}` : 'Chat';
7875
7897
  }
7898
+ if (route.name === 'script') {
7899
+ return route.params?.scriptId ? `Script: ${route.params.scriptId}` : 'Script';
7900
+ }
7876
7901
  const normalizedPath = route.path.replace(/^\//, '');
7877
7902
  return normalizedPath || 'Home';
7878
7903
  }
@@ -8160,6 +8185,11 @@ const router = VueRouter.createRouter({
8160
8185
 
8161
8186
  // Add global navigation guard
8162
8187
  router.beforeEach((to, from, next) => {
8188
+ if (to.name === 'mcp' && window.MONGOOSE_STUDIO_CONFIG.mcp === false) {
8189
+ next({ name: window.state?.roles?.[0] === 'dashboards' ? 'dashboards' : 'root' });
8190
+ return;
8191
+ }
8192
+
8163
8193
  // Skip auth check for authorized (public) routes
8164
8194
  if (to.meta.authorized) {
8165
8195
  next();
@@ -8888,6 +8918,44 @@ module.exports = app => app.component('list-subdocument', {
8888
8918
  });
8889
8919
 
8890
8920
 
8921
+ /***/ },
8922
+
8923
+ /***/ "./frontend/src/mcp/mcp.js"
8924
+ /*!*********************************!*\
8925
+ !*** ./frontend/src/mcp/mcp.js ***!
8926
+ \*********************************/
8927
+ (module, __unused_webpack_exports, __webpack_require__) {
8928
+
8929
+ "use strict";
8930
+
8931
+
8932
+ const template = __webpack_require__(/*! ./mcp.html */ "./frontend/src/mcp/mcp.html");
8933
+
8934
+ module.exports = {
8935
+ template,
8936
+ computed: {
8937
+ mcpUrl() {
8938
+ const configuredUrl = window.MONGOOSE_STUDIO_CONFIG.mcpUrl;
8939
+ if (configuredUrl) {
8940
+ return configuredUrl;
8941
+ }
8942
+
8943
+ const url = new URL(window.location.href);
8944
+ url.hash = '';
8945
+ url.search = '';
8946
+ url.pathname = `${url.pathname.replace(/\/+$/, '')}/mcp`;
8947
+ return url.toString();
8948
+ }
8949
+ },
8950
+ methods: {
8951
+ async copyMCPUrl() {
8952
+ await navigator.clipboard.writeText(this.mcpUrl);
8953
+ this.$toast.success('MCP URL copied!');
8954
+ }
8955
+ }
8956
+ };
8957
+
8958
+
8891
8959
  /***/ },
8892
8960
 
8893
8961
  /***/ "./frontend/src/modal/modal.js"
@@ -11739,6 +11807,9 @@ module.exports = app => app.component('navbar', {
11739
11807
  hasTaskVisualizer() {
11740
11808
  return !!window.MONGOOSE_STUDIO_CONFIG.enableTaskVisualizer;
11741
11809
  },
11810
+ mcpEnabled() {
11811
+ return window.MONGOOSE_STUDIO_CONFIG.mcp !== false;
11812
+ },
11742
11813
  recentPagesList() {
11743
11814
  return this.localRecentPages;
11744
11815
  },
@@ -11938,6 +12009,14 @@ module.exports = {
11938
12009
  authorized: false
11939
12010
  }
11940
12011
  },
12012
+ {
12013
+ path: '/mcp',
12014
+ name: 'mcp',
12015
+ component: 'mcp',
12016
+ meta: {
12017
+ authorized: true
12018
+ }
12019
+ },
11941
12020
  {
11942
12021
  path: '/tasks',
11943
12022
  name: 'tasks',
@@ -11977,6 +12056,14 @@ module.exports = {
11977
12056
  meta: {
11978
12057
  authorized: true
11979
12058
  }
12059
+ },
12060
+ {
12061
+ path: '/script/:scriptId',
12062
+ name: 'script',
12063
+ component: 'script',
12064
+ meta: {
12065
+ authorized: true
12066
+ }
11980
12067
  }
11981
12068
  ],
11982
12069
  roleAccess,
@@ -11984,6 +12071,115 @@ module.exports = {
11984
12071
  };
11985
12072
 
11986
12073
 
12074
+ /***/ },
12075
+
12076
+ /***/ "./frontend/src/script/script.js"
12077
+ /*!***************************************!*\
12078
+ !*** ./frontend/src/script/script.js ***!
12079
+ \***************************************/
12080
+ (module, __unused_webpack_exports, __webpack_require__) {
12081
+
12082
+ "use strict";
12083
+ /* global Prism */
12084
+
12085
+
12086
+ const api = __webpack_require__(/*! ../api */ "./frontend/src/api.js");
12087
+ const baseComponent = __webpack_require__(/*! ../_util/baseComponent */ "./frontend/src/_util/baseComponent.js");
12088
+ const template = __webpack_require__(/*! ./script.html */ "./frontend/src/script/script.html");
12089
+
12090
+ module.exports = {
12091
+ template,
12092
+ extends: baseComponent,
12093
+ props: ['scriptId'],
12094
+ inject: ['state'],
12095
+ data() {
12096
+ return {
12097
+ status: 'loading',
12098
+ script: null,
12099
+ editedScript: '',
12100
+ isEditing: false,
12101
+ activeTab: 'code',
12102
+ selectedRunMode: 'run',
12103
+ showRunDropdown: false
12104
+ };
12105
+ },
12106
+ computed: {
12107
+ canUseDryRun() {
12108
+ return this.state.capabilities?.supportsTransactions !== false;
12109
+ },
12110
+ selectedRunLabel() {
12111
+ return this.selectedRunMode === 'dryRun' ? 'Dry Run' : 'Run';
12112
+ },
12113
+ isDryRunResult() {
12114
+ return this.script?.executionFinishedAt != null && this.script?.dryRun === true;
12115
+ }
12116
+ },
12117
+ methods: {
12118
+ async loadScript() {
12119
+ this.status = 'loading';
12120
+ try {
12121
+ const { script } = await api.Script.getScript({ scriptId: this.scriptId });
12122
+ this.script = script;
12123
+ this.editedScript = script.script;
12124
+ this.activeTab = script.executionResult == null ? 'code' : 'output';
12125
+ } finally {
12126
+ this.status = 'loaded';
12127
+ this.highlightCode();
12128
+ }
12129
+ },
12130
+ startEditing() {
12131
+ this.editedScript = this.script.script;
12132
+ this.isEditing = true;
12133
+ this.activeTab = 'code';
12134
+ },
12135
+ cancelEditing() {
12136
+ this.editedScript = this.script.script;
12137
+ this.isEditing = false;
12138
+ this.highlightCode();
12139
+ },
12140
+ selectRunMode(mode) {
12141
+ if (mode === 'dryRun' && !this.canUseDryRun) {
12142
+ return;
12143
+ }
12144
+ this.selectedRunMode = mode;
12145
+ this.showRunDropdown = false;
12146
+ },
12147
+ async executeScript() {
12148
+ this.showRunDropdown = false;
12149
+ try {
12150
+ const { script } = await api.Script.executeScript({
12151
+ scriptId: this.scriptId,
12152
+ script: this.isEditing ? this.editedScript : this.script.script,
12153
+ dryRun: this.selectedRunMode === 'dryRun'
12154
+ });
12155
+ this.script = script;
12156
+ this.editedScript = script.script;
12157
+ this.isEditing = false;
12158
+ this.activeTab = 'output';
12159
+ this.$toast.success(this.script.dryRun ? 'Dry run completed!' : 'Script executed successfully!');
12160
+ } catch (err) {
12161
+ const { script } = await api.Script.getScript({ scriptId: this.scriptId });
12162
+ this.script = script;
12163
+ this.editedScript = script.script;
12164
+ this.isEditing = false;
12165
+ this.activeTab = 'output';
12166
+ throw err;
12167
+ }
12168
+ },
12169
+ highlightCode() {
12170
+ this.$nextTick(() => {
12171
+ if (this.$refs.code) {
12172
+ Prism.highlightElement(this.$refs.code);
12173
+ }
12174
+ });
12175
+ }
12176
+ },
12177
+ mounted() {
12178
+ return this.loadScript();
12179
+ }
12180
+ };
12181
+
12182
+
11987
12183
  /***/ },
11988
12184
 
11989
12185
  /***/ "./frontend/src/splash/splash.js"
@@ -13573,6 +13769,9 @@ const map = {
13573
13769
  "./list-subdocument/list-subdocument.css": "./frontend/src/list-subdocument/list-subdocument.css",
13574
13770
  "./list-subdocument/list-subdocument.html": "./frontend/src/list-subdocument/list-subdocument.html",
13575
13771
  "./list-subdocument/list-subdocument.js": "./frontend/src/list-subdocument/list-subdocument.js",
13772
+ "./mcp/mcp": "./frontend/src/mcp/mcp.js",
13773
+ "./mcp/mcp.html": "./frontend/src/mcp/mcp.html",
13774
+ "./mcp/mcp.js": "./frontend/src/mcp/mcp.js",
13576
13775
  "./modal/modal": "./frontend/src/modal/modal.js",
13577
13776
  "./modal/modal.css": "./frontend/src/modal/modal.css",
13578
13777
  "./modal/modal.html": "./frontend/src/modal/modal.html",
@@ -13606,6 +13805,9 @@ const map = {
13606
13805
  "./pro-upgrade-modal/pro-upgrade-modal.js": "./frontend/src/pro-upgrade-modal/pro-upgrade-modal.js",
13607
13806
  "./routes": "./frontend/src/routes.js",
13608
13807
  "./routes.js": "./frontend/src/routes.js",
13808
+ "./script/script": "./frontend/src/script/script.js",
13809
+ "./script/script.html": "./frontend/src/script/script.html",
13810
+ "./script/script.js": "./frontend/src/script/script.js",
13609
13811
  "./splash/splash": "./frontend/src/splash/splash.js",
13610
13812
  "./splash/splash.html": "./frontend/src/splash/splash.html",
13611
13813
  "./splash/splash.js": "./frontend/src/splash/splash.js",
@@ -13721,7 +13923,7 @@ __webpack_require__.r(__webpack_exports__);
13721
13923
  /* harmony export */ });
13722
13924
  /* harmony import */ var _vue_shared__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @vue/shared */ "./node_modules/@vue/shared/dist/shared.esm-bundler.js");
13723
13925
  /**
13724
- * @vue/reactivity v3.5.42
13926
+ * @vue/reactivity v3.5.43
13725
13927
  * (c) 2018-present Yuxi (Evan) You and Vue contributors
13726
13928
  * @license MIT
13727
13929
  **/
@@ -14496,7 +14698,9 @@ function reactiveReadArray(array) {
14496
14698
  const raw = toRaw(array);
14497
14699
  if (raw === array) return raw;
14498
14700
  track(raw, "iterate", ARRAY_ITERATE_KEY);
14499
- return isShallow(array) ? raw : raw.map(toReactive);
14701
+ if (isShallow(array)) return raw;
14702
+ if (!isReadonly(array)) return raw.map(toReactive);
14703
+ return isReactive(array) ? raw.map((item) => toReadonly(toReactive(item))) : raw.map(toReadonly);
14500
14704
  }
14501
14705
  function shallowReadArray(arr) {
14502
14706
  track(arr = toRaw(arr), "iterate", ARRAY_ITERATE_KEY);
@@ -15894,7 +16098,7 @@ __webpack_require__.r(__webpack_exports__);
15894
16098
  /* harmony import */ var _vue_reactivity__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @vue/reactivity */ "./node_modules/@vue/reactivity/dist/reactivity.esm-bundler.js");
15895
16099
  /* harmony import */ var _vue_shared__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @vue/shared */ "./node_modules/@vue/shared/dist/shared.esm-bundler.js");
15896
16100
  /**
15897
- * @vue/runtime-core v3.5.42
16101
+ * @vue/runtime-core v3.5.43
15898
16102
  * (c) 2018-present Yuxi (Evan) You and Vue contributors
15899
16103
  * @license MIT
15900
16104
  **/
@@ -17962,13 +18166,15 @@ function createHydrationFunctions(rendererInternals) {
17962
18166
  getContainerType(container),
17963
18167
  optimized
17964
18168
  );
17965
- if (isAsyncWrapper(vnode) && !vnode.component.subTree) {
18169
+ if ((isAsyncWrapper(vnode) || vnode.component.asyncDep) && !vnode.component.subTree) {
17966
18170
  let subTree;
17967
18171
  if (isFragmentStart) {
17968
18172
  subTree = createVNode(Static);
17969
18173
  subTree.anchor = nextNode ? nextNode.previousSibling : container.lastChild;
17970
18174
  } else {
17971
- subTree = node.nodeType === 3 ? createTextVNode("") : createVNode("div");
18175
+ subTree = node.nodeType === 3 ? createTextVNode("") : createVNode(
18176
+ node.nodeType === 8 ? Comment : "div"
18177
+ );
17972
18178
  }
17973
18179
  subTree.el = node;
17974
18180
  vnode.component.subTree = subTree;
@@ -21446,6 +21652,12 @@ function baseCreateRenderer(options, createHydrationFns) {
21446
21652
  optimized = false;
21447
21653
  n2.dynamicChildren = null;
21448
21654
  }
21655
+ if (n2.dynamicChildren && n1 && n1.dynamicChildren && n1.dynamicChildren.hasOnce) {
21656
+ if (n2.dynamicChildren === _vue_shared__WEBPACK_IMPORTED_MODULE_1__.EMPTY_ARR) {
21657
+ n2.dynamicChildren = [];
21658
+ }
21659
+ n2.dynamicChildren.hasOnce = true;
21660
+ }
21449
21661
  const { type, ref, shapeFlag } = n2;
21450
21662
  switch (type) {
21451
21663
  case Text:
@@ -22052,6 +22264,7 @@ function baseCreateRenderer(options, createHydrationFns) {
22052
22264
  if (true) {
22053
22265
  pushWarningContext(n2);
22054
22266
  }
22267
+ n2.el = n1.el;
22055
22268
  updateComponentPreRender(instance, n2, optimized);
22056
22269
  if (true) {
22057
22270
  popWarningContext();
@@ -22643,7 +22856,7 @@ function baseCreateRenderer(options, createHydrationFns) {
22643
22856
  cacheIndex,
22644
22857
  memo
22645
22858
  } = vnode;
22646
- if (patchFlag === -2) {
22859
+ if (patchFlag === -2 || dynamicChildren && dynamicChildren.hasOnce) {
22647
22860
  optimized = false;
22648
22861
  }
22649
22862
  if (ref != null) {
@@ -22651,7 +22864,7 @@ function baseCreateRenderer(options, createHydrationFns) {
22651
22864
  setRef(ref, null, parentSuspense, vnode, true);
22652
22865
  (0,_vue_reactivity__WEBPACK_IMPORTED_MODULE_0__.resetTracking)();
22653
22866
  }
22654
- if (cacheIndex != null) {
22867
+ if (cacheIndex != null && (!vnode.ctx || vnode.ctx === parentComponent)) {
22655
22868
  parentComponent.renderCache[cacheIndex] = void 0;
22656
22869
  }
22657
22870
  if (shapeFlag & 256) {
@@ -22732,6 +22945,9 @@ function baseCreateRenderer(options, createHydrationFns) {
22732
22945
  }
22733
22946
  if (type === Static) {
22734
22947
  removeStaticNode(vnode);
22948
+ if (transition && !transition.persisted && transition.afterLeave) {
22949
+ transition.afterLeave();
22950
+ }
22735
22951
  return;
22736
22952
  }
22737
22953
  const performRemove = () => {
@@ -22775,6 +22991,9 @@ function baseCreateRenderer(options, createHydrationFns) {
22775
22991
  if (job) {
22776
22992
  job.flags |= 8;
22777
22993
  unmount(subTree, instance, parentSuspense, doRemove);
22994
+ } else if (instance.vnode.el && subTree) {
22995
+ subTree.transition = instance.vnode.transition;
22996
+ unmount(subTree, instance, parentSuspense, doRemove);
22778
22997
  }
22779
22998
  if (um) {
22780
22999
  queuePostRenderEffect(um, parentSuspense);
@@ -22989,7 +23208,7 @@ const SuspenseImpl = {
22989
23208
  rendererInternals
22990
23209
  );
22991
23210
  } else {
22992
- if (parentSuspense && parentSuspense.deps > 0 && !n1.suspense.isInFallback) {
23211
+ if (parentSuspense && parentSuspense.deps > 0 && !n1.suspense.isInFallback && !parentSuspense.isHydrating) {
22993
23212
  n2.suspense = n1.suspense;
22994
23213
  n2.suspense.vnode = n2;
22995
23214
  n2.el = n1.el;
@@ -23075,10 +23294,12 @@ function patchSuspense(n1, n2, container, anchor, parentComponent, namespace, sl
23075
23294
  if (pendingBranch) {
23076
23295
  suspense.pendingBranch = newBranch;
23077
23296
  if (isSameVNodeType(pendingBranch, newBranch)) {
23297
+ suspense.deps++;
23078
23298
  patch(
23079
23299
  pendingBranch,
23080
23300
  newBranch,
23081
- suspense.hiddenContainer,
23301
+ // a hydrating pending branch is adopted SSR DOM, already in place
23302
+ isHydrating ? container : suspense.hiddenContainer,
23082
23303
  null,
23083
23304
  parentComponent,
23084
23305
  suspense,
@@ -23086,6 +23307,7 @@ function patchSuspense(n1, n2, container, anchor, parentComponent, namespace, sl
23086
23307
  slotScopeIds,
23087
23308
  optimized
23088
23309
  );
23310
+ suspense.deps--;
23089
23311
  if (suspense.deps <= 0) {
23090
23312
  suspense.resolve();
23091
23313
  } else if (isInFallback) {
@@ -23351,6 +23573,7 @@ function createSuspenseBoundary(vnode, parentSuspense, parentComponent, containe
23351
23573
  suspense.effects = [];
23352
23574
  if (isSuspensible) {
23353
23575
  if (parentSuspense && parentSuspense.pendingBranch && parentSuspenseId === parentSuspense.pendingId) {
23576
+ parentSuspenseId = void 0;
23354
23577
  parentSuspense.deps--;
23355
23578
  if (parentSuspense.deps === 0 && !sync) {
23356
23579
  parentSuspense.resolve();
@@ -23424,6 +23647,12 @@ function createSuspenseBoundary(vnode, parentSuspense, parentComponent, containe
23424
23647
  return;
23425
23648
  }
23426
23649
  unsetCurrentInstance();
23650
+ if (hydratedEl && !instance.scope.active) {
23651
+ if (isInPendingSuspense && --suspense.deps === 0) {
23652
+ suspense.resolve();
23653
+ }
23654
+ return;
23655
+ }
23427
23656
  instance.asyncResolved = true;
23428
23657
  const { vnode: vnode2 } = instance;
23429
23658
  if (true) {
@@ -23845,7 +24074,8 @@ function cloneVNode(vnode, extraProps, mergeRef = false, cloneTransition = false
23845
24074
  el: vnode.el,
23846
24075
  anchor: vnode.anchor,
23847
24076
  ctx: vnode.ctx,
23848
- ce: vnode.ce
24077
+ ce: vnode.ce,
24078
+ cacheIndex: vnode.cacheIndex
23849
24079
  };
23850
24080
  if (transition && cloneTransition) {
23851
24081
  setTransitionHooks(
@@ -24652,7 +24882,7 @@ function isMemoSame(cached, memo) {
24652
24882
  return true;
24653
24883
  }
24654
24884
 
24655
- const version = "3.5.42";
24885
+ const version = "3.5.43";
24656
24886
  const warn = true ? warn$1 : 0;
24657
24887
  const ErrorTypeStrings = ErrorTypeStrings$1 ;
24658
24888
  const devtools = true ? devtools$1 : 0;
@@ -24863,7 +25093,7 @@ __webpack_require__.r(__webpack_exports__);
24863
25093
  /* harmony import */ var _vue_runtime_core__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @vue/runtime-core */ "./node_modules/@vue/reactivity/dist/reactivity.esm-bundler.js");
24864
25094
  /* harmony import */ var _vue_runtime_core__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @vue/shared */ "./node_modules/@vue/shared/dist/shared.esm-bundler.js");
24865
25095
  /**
24866
- * @vue/runtime-dom v3.5.42
25096
+ * @vue/runtime-dom v3.5.43
24867
25097
  * (c) 2018-present Yuxi (Evan) You and Vue contributors
24868
25098
  * @license MIT
24869
25099
  **/
@@ -27011,7 +27241,7 @@ __webpack_require__.r(__webpack_exports__);
27011
27241
  /* harmony export */ toTypeString: () => (/* binding */ toTypeString)
27012
27242
  /* harmony export */ });
27013
27243
  /**
27014
- * @vue/shared v3.5.42
27244
+ * @vue/shared v3.5.43
27015
27245
  * (c) 2018-present Yuxi (Evan) You and Vue contributors
27016
27246
  * @license MIT
27017
27247
  **/
@@ -27279,10 +27509,10 @@ function normalizeStyle(value) {
27279
27509
  }
27280
27510
  const listDelimiterRE = /;(?![^(]*\))/g;
27281
27511
  const propertyDelimiterRE = /:([^]+)/;
27282
- const styleCommentRE = /\/\*[^]*?\*\//g;
27512
+ const styleCommentRE = /"(?:[^"\\]|\\[^])*"|'(?:[^'\\]|\\[^])*'|\\[^]|\/\*[^]*?\*\//g;
27283
27513
  function parseStringStyle(cssText) {
27284
27514
  const ret = {};
27285
- cssText.replace(styleCommentRE, "").split(listDelimiterRE).forEach((item) => {
27515
+ cssText.replace(styleCommentRE, (match) => match.startsWith("/*") ? "" : match).split(listDelimiterRE).forEach((item) => {
27286
27516
  if (item) {
27287
27517
  const tmp = item.split(propertyDelimiterRE);
27288
27518
  tmp.length > 1 && (ret[tmp[0].trim()] = tmp[1].trim());
@@ -27443,22 +27673,22 @@ function getEscapedCssVarName(key, doubleEscape) {
27443
27673
  );
27444
27674
  }
27445
27675
 
27446
- function looseCompareArrays(a, b) {
27676
+ function looseCompareArrays(a, b, seen) {
27447
27677
  if (a.length !== b.length) return false;
27448
27678
  let equal = true;
27449
27679
  for (let i = 0; equal && i < a.length; i++) {
27450
- equal = looseEqual(a[i], b[i]);
27680
+ equal = looseEqual(a[i], b[i], seen);
27451
27681
  }
27452
27682
  return equal;
27453
27683
  }
27454
- function looseCompareCollections(a, b) {
27684
+ function looseCompareCollections(a, b, seen) {
27455
27685
  if (a.size !== b.size) return false;
27456
27686
  const candidates = Array.from(b);
27457
27687
  const matched = new Uint8Array(candidates.length);
27458
27688
  for (const item of a) {
27459
27689
  let index = -1;
27460
27690
  for (let i = 0; i < candidates.length; i++) {
27461
- if (!matched[i] && looseEqual(item, candidates[i])) {
27691
+ if (!matched[i] && looseEqual(item, candidates[i], seen)) {
27462
27692
  index = i;
27463
27693
  break;
27464
27694
  }
@@ -27468,7 +27698,47 @@ function looseCompareCollections(a, b) {
27468
27698
  }
27469
27699
  return true;
27470
27700
  }
27471
- function looseEqual(a, b) {
27701
+ function looseCompareObjects(a, b, seen) {
27702
+ let aValidType = isMap(a);
27703
+ let bValidType = isMap(b);
27704
+ if (aValidType || bValidType) {
27705
+ return aValidType && bValidType ? looseCompareCollections(a, b, seen) : false;
27706
+ }
27707
+ aValidType = isSet(a);
27708
+ bValidType = isSet(b);
27709
+ if (aValidType || bValidType) {
27710
+ return aValidType && bValidType ? looseCompareCollections(a, b, seen) : false;
27711
+ }
27712
+ const aKeysCount = Object.keys(a).length;
27713
+ const bKeysCount = Object.keys(b).length;
27714
+ if (aKeysCount !== bKeysCount) {
27715
+ return false;
27716
+ }
27717
+ for (const key in a) {
27718
+ const aHasKey = a.hasOwnProperty(key);
27719
+ const bHasKey = b.hasOwnProperty(key);
27720
+ if (aHasKey && !bHasKey || !aHasKey && bHasKey || !looseEqual(a[key], b[key], seen)) {
27721
+ return false;
27722
+ }
27723
+ }
27724
+ return String(a) === String(b);
27725
+ }
27726
+ function looseCompareNested(a, b, seen, compare) {
27727
+ if (!seen) {
27728
+ seen = [/* @__PURE__ */ new Map(), /* @__PURE__ */ new Map()];
27729
+ }
27730
+ const [seenA, seenB] = seen;
27731
+ if (seenA.has(a) || seenB.has(b)) {
27732
+ return seenA.get(a) === b && seenB.get(b) === a;
27733
+ }
27734
+ seenA.set(a, b);
27735
+ seenB.set(b, a);
27736
+ const equal = compare(a, b, seen);
27737
+ seenA.delete(a);
27738
+ seenB.delete(b);
27739
+ return equal;
27740
+ }
27741
+ function looseEqual(a, b, seen) {
27472
27742
  if (a === b) return true;
27473
27743
  let aValidType = isDate(a);
27474
27744
  let bValidType = isDate(b);
@@ -27483,7 +27753,7 @@ function looseEqual(a, b) {
27483
27753
  aValidType = isArray(a);
27484
27754
  bValidType = isArray(b);
27485
27755
  if (aValidType || bValidType) {
27486
- return aValidType && bValidType ? looseCompareArrays(a, b) : false;
27756
+ return aValidType && bValidType ? looseCompareNested(a, b, seen, looseCompareArrays) : false;
27487
27757
  }
27488
27758
  aValidType = isObject(a);
27489
27759
  bValidType = isObject(b);
@@ -27491,28 +27761,7 @@ function looseEqual(a, b) {
27491
27761
  if (!aValidType || !bValidType) {
27492
27762
  return false;
27493
27763
  }
27494
- aValidType = isMap(a);
27495
- bValidType = isMap(b);
27496
- if (aValidType || bValidType) {
27497
- return aValidType && bValidType ? looseCompareCollections(a, b) : false;
27498
- }
27499
- aValidType = isSet(a);
27500
- bValidType = isSet(b);
27501
- if (aValidType || bValidType) {
27502
- return aValidType && bValidType ? looseCompareCollections(a, b) : false;
27503
- }
27504
- const aKeysCount = Object.keys(a).length;
27505
- const bKeysCount = Object.keys(b).length;
27506
- if (aKeysCount !== bKeysCount) {
27507
- return false;
27508
- }
27509
- for (const key in a) {
27510
- const aHasKey = a.hasOwnProperty(key);
27511
- const bHasKey = b.hasOwnProperty(key);
27512
- if (aHasKey && !bHasKey || !aHasKey && bHasKey || !looseEqual(a[key], b[key])) {
27513
- return false;
27514
- }
27515
- }
27764
+ return looseCompareNested(a, b, seen, looseCompareObjects);
27516
27765
  }
27517
27766
  return String(a) === String(b);
27518
27767
  }
@@ -52001,6 +52250,128 @@ function parseUtf8ValidationOption(options) {
52001
52250
 
52002
52251
  /***/ },
52003
52252
 
52253
+ /***/ "./node_modules/mongoose/node_modules/mongodb/lib/bson.js"
52254
+ /*!****************************************************************!*\
52255
+ !*** ./node_modules/mongoose/node_modules/mongodb/lib/bson.js ***!
52256
+ \****************************************************************/
52257
+ (__unused_webpack_module, exports, __webpack_require__) {
52258
+
52259
+ "use strict";
52260
+
52261
+ Object.defineProperty(exports, "__esModule", ({ value: true }));
52262
+ exports.setUint32LE = exports.readUint8 = exports.readInt32LE = exports.UUID = exports.Timestamp = exports.serialize = exports.ObjectId = exports.NumberUtils = exports.MinKey = exports.MaxKey = exports.Long = exports.Int32 = exports.EJSON = exports.Double = exports.deserialize = exports.Decimal128 = exports.DBRef = exports.Code = exports.calculateObjectSize = exports.ByteUtils = exports.BSONType = exports.BSONSymbol = exports.BSONRegExp = exports.BSONError = exports.BSON = exports.Binary = void 0;
52263
+ exports.parseToElementsToArray = parseToElementsToArray;
52264
+ exports.pluckBSONSerializeOptions = pluckBSONSerializeOptions;
52265
+ exports.resolveBSONOptions = resolveBSONOptions;
52266
+ exports.parseUtf8ValidationOption = parseUtf8ValidationOption;
52267
+ /* eslint-disable no-restricted-imports */
52268
+ const bson_1 = __webpack_require__(/*! bson */ "./node_modules/bson/lib/bson.mjs");
52269
+ var bson_2 = __webpack_require__(/*! bson */ "./node_modules/bson/lib/bson.mjs");
52270
+ Object.defineProperty(exports, "Binary", ({ enumerable: true, get: function () { return bson_2.Binary; } }));
52271
+ Object.defineProperty(exports, "BSON", ({ enumerable: true, get: function () { return bson_2.BSON; } }));
52272
+ Object.defineProperty(exports, "BSONError", ({ enumerable: true, get: function () { return bson_2.BSONError; } }));
52273
+ Object.defineProperty(exports, "BSONRegExp", ({ enumerable: true, get: function () { return bson_2.BSONRegExp; } }));
52274
+ Object.defineProperty(exports, "BSONSymbol", ({ enumerable: true, get: function () { return bson_2.BSONSymbol; } }));
52275
+ Object.defineProperty(exports, "BSONType", ({ enumerable: true, get: function () { return bson_2.BSONType; } }));
52276
+ Object.defineProperty(exports, "ByteUtils", ({ enumerable: true, get: function () { return bson_2.ByteUtils; } }));
52277
+ Object.defineProperty(exports, "calculateObjectSize", ({ enumerable: true, get: function () { return bson_2.calculateObjectSize; } }));
52278
+ Object.defineProperty(exports, "Code", ({ enumerable: true, get: function () { return bson_2.Code; } }));
52279
+ Object.defineProperty(exports, "DBRef", ({ enumerable: true, get: function () { return bson_2.DBRef; } }));
52280
+ Object.defineProperty(exports, "Decimal128", ({ enumerable: true, get: function () { return bson_2.Decimal128; } }));
52281
+ Object.defineProperty(exports, "deserialize", ({ enumerable: true, get: function () { return bson_2.deserialize; } }));
52282
+ Object.defineProperty(exports, "Double", ({ enumerable: true, get: function () { return bson_2.Double; } }));
52283
+ Object.defineProperty(exports, "EJSON", ({ enumerable: true, get: function () { return bson_2.EJSON; } }));
52284
+ Object.defineProperty(exports, "Int32", ({ enumerable: true, get: function () { return bson_2.Int32; } }));
52285
+ Object.defineProperty(exports, "Long", ({ enumerable: true, get: function () { return bson_2.Long; } }));
52286
+ Object.defineProperty(exports, "MaxKey", ({ enumerable: true, get: function () { return bson_2.MaxKey; } }));
52287
+ Object.defineProperty(exports, "MinKey", ({ enumerable: true, get: function () { return bson_2.MinKey; } }));
52288
+ Object.defineProperty(exports, "NumberUtils", ({ enumerable: true, get: function () { return bson_2.NumberUtils; } }));
52289
+ Object.defineProperty(exports, "ObjectId", ({ enumerable: true, get: function () { return bson_2.ObjectId; } }));
52290
+ Object.defineProperty(exports, "serialize", ({ enumerable: true, get: function () { return bson_2.serialize; } }));
52291
+ Object.defineProperty(exports, "Timestamp", ({ enumerable: true, get: function () { return bson_2.Timestamp; } }));
52292
+ Object.defineProperty(exports, "UUID", ({ enumerable: true, get: function () { return bson_2.UUID; } }));
52293
+ function parseToElementsToArray(bytes, offset) {
52294
+ const res = bson_1.BSON.onDemand.parseToElements(bytes, offset);
52295
+ return Array.isArray(res) ? res : [...res];
52296
+ }
52297
+ // validates buffer inputs, used for read operations
52298
+ const validateBufferInputs = (buffer, offset, length) => {
52299
+ if (offset < 0 || offset + length > buffer.length) {
52300
+ throw new RangeError(`Attempt to access memory outside buffer bounds: buffer length: ${buffer.length}, offset: ${offset}, length: ${length}`);
52301
+ }
52302
+ };
52303
+ // readInt32LE, reads a 32-bit integer from buffer at given offset
52304
+ // throws if offset is out of bounds
52305
+ const readInt32LE = (buffer, offset) => {
52306
+ validateBufferInputs(buffer, offset, 4);
52307
+ return bson_1.NumberUtils.getInt32LE(buffer, offset);
52308
+ };
52309
+ exports.readInt32LE = readInt32LE;
52310
+ // readUint8, reads a single unsigned byte from buffer at given offset
52311
+ const readUint8 = (buffer, offset) => {
52312
+ validateBufferInputs(buffer, offset, 1);
52313
+ return buffer[offset];
52314
+ };
52315
+ exports.readUint8 = readUint8;
52316
+ const setUint32LE = (destination, offset, value) => {
52317
+ destination[offset] = value;
52318
+ value >>>= 8;
52319
+ destination[offset + 1] = value;
52320
+ value >>>= 8;
52321
+ destination[offset + 2] = value;
52322
+ value >>>= 8;
52323
+ destination[offset + 3] = value;
52324
+ return 4;
52325
+ };
52326
+ exports.setUint32LE = setUint32LE;
52327
+ function pluckBSONSerializeOptions(options) {
52328
+ const { fieldsAsRaw, useBigInt64, promoteValues, promoteBuffers, promoteLongs, serializeFunctions, ignoreUndefined, bsonRegExp, raw, enableUtf8Validation } = options;
52329
+ return {
52330
+ fieldsAsRaw,
52331
+ useBigInt64,
52332
+ promoteValues,
52333
+ promoteBuffers,
52334
+ promoteLongs,
52335
+ serializeFunctions,
52336
+ ignoreUndefined,
52337
+ bsonRegExp,
52338
+ raw,
52339
+ enableUtf8Validation
52340
+ };
52341
+ }
52342
+ /**
52343
+ * Merge the given BSONSerializeOptions, preferring options over the parent's options, and
52344
+ * substituting defaults for values not set.
52345
+ *
52346
+ * @internal
52347
+ */
52348
+ function resolveBSONOptions(options, parent) {
52349
+ const parentOptions = parent?.bsonOptions;
52350
+ return {
52351
+ raw: options?.raw ?? parentOptions?.raw ?? false,
52352
+ useBigInt64: options?.useBigInt64 ?? parentOptions?.useBigInt64 ?? false,
52353
+ promoteLongs: options?.promoteLongs ?? parentOptions?.promoteLongs ?? true,
52354
+ promoteValues: options?.promoteValues ?? parentOptions?.promoteValues ?? true,
52355
+ promoteBuffers: options?.promoteBuffers ?? parentOptions?.promoteBuffers ?? false,
52356
+ ignoreUndefined: options?.ignoreUndefined ?? parentOptions?.ignoreUndefined ?? false,
52357
+ bsonRegExp: options?.bsonRegExp ?? parentOptions?.bsonRegExp ?? false,
52358
+ serializeFunctions: options?.serializeFunctions ?? parentOptions?.serializeFunctions ?? false,
52359
+ fieldsAsRaw: options?.fieldsAsRaw ?? parentOptions?.fieldsAsRaw ?? {},
52360
+ enableUtf8Validation: options?.enableUtf8Validation ?? parentOptions?.enableUtf8Validation ?? true
52361
+ };
52362
+ }
52363
+ /** @internal */
52364
+ function parseUtf8ValidationOption(options) {
52365
+ const enableUtf8Validation = options?.enableUtf8Validation;
52366
+ if (enableUtf8Validation === false) {
52367
+ return { utf8: false };
52368
+ }
52369
+ return { utf8: { writeErrors: false } };
52370
+ }
52371
+ //# sourceMappingURL=bson.js.map
52372
+
52373
+ /***/ },
52374
+
52004
52375
  /***/ "./node_modules/mpath/index.js"
52005
52376
  /*!*************************************!*\
52006
52377
  !*** ./node_modules/mpath/index.js ***!
@@ -52758,7 +53129,7 @@ __webpack_require__.r(__webpack_exports__);
52758
53129
  /* harmony import */ var _vue_runtime_dom__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @vue/runtime-dom */ "./node_modules/@vue/runtime-core/dist/runtime-core.esm-bundler.js");
52759
53130
  /* harmony import */ var _vue_runtime_dom__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @vue/runtime-dom */ "./node_modules/@vue/runtime-dom/dist/runtime-dom.esm-bundler.js");
52760
53131
  /**
52761
- * vue v3.5.42
53132
+ * vue v3.5.43
52762
53133
  * (c) 2018-present Yuxi (Evan) You and Vue contributors
52763
53134
  * @license MIT
52764
53135
  **/
@@ -54543,6 +54914,17 @@ module.exports = "<div class=\"list-subdocument tooltip\">\n <div>{{shortenValu
54543
54914
 
54544
54915
  /***/ },
54545
54916
 
54917
+ /***/ "./frontend/src/mcp/mcp.html"
54918
+ /*!***********************************!*\
54919
+ !*** ./frontend/src/mcp/mcp.html ***!
54920
+ \***********************************/
54921
+ (module) {
54922
+
54923
+ "use strict";
54924
+ module.exports = "<div class=\"mx-auto max-w-5xl py-6 px-2\">\n <h1 class=\"text-xl font-bold text-content\">MCP</h1>\n <p class=\"mt-2 text-sm text-content-secondary\">\n Connect an MCP client such as Claude or ChatGPT to this Mongoose Studio workspace.\n </p>\n\n <div class=\"mt-6 rounded-lg border border-edge bg-surface p-4\">\n <label for=\"mcp-url\" class=\"block text-sm font-medium text-content\">MCP URL</label>\n <div class=\"mt-2 flex gap-2\">\n <input\n id=\"mcp-url\"\n type=\"text\"\n readonly\n :value=\"mcpUrl\"\n class=\"min-w-0 flex-1 rounded-md bg-muted px-3 py-2 font-mono text-sm text-content outline outline-1 -outline-offset-1 outline-edge-strong\"\n />\n <async-button\n type=\"button\"\n @click=\"copyMCPUrl\"\n class=\"rounded-md bg-primary px-4 py-2 text-sm font-semibold text-primary-text shadow-sm hover:bg-primary-hover\">\n Copy\n </async-button>\n </div>\n </div>\n\n <div class=\"mt-8 grid gap-6 text-sm text-content-secondary\">\n <section>\n <h2 class=\"text-base font-semibold text-content\">Authentication and network access</h2>\n <p class=\"mt-2\">\n Without a Mongoose Studio API key, the MCP endpoint uses the deployment's\n <code class=\"rounded bg-muted px-1 py-0.5\">bindIp</code> restrictions. By default, this means an MCP client can only connect from localhost.\n </p>\n <p class=\"mt-2\">\n With an API key, MCP clients such as Claude and ChatGPT connect through OAuth. You will be prompted to sign in to Mongoose Studio and approve the connection before the client receives access.\n </p>\n </section>\n\n <section>\n <h2 class=\"text-base font-semibold text-content\">Permissions</h2>\n <p class=\"mt-2\">\n When an API key is configured, MCP access is scoped to the signed-in user's workspace permissions. During authorization, the user can explicitly grant fewer permissions, such as read-only access or a lower execution time limit. An MCP connection can never receive more access than that user already has.\n </p>\n </section>\n\n <section>\n <h2 class=\"text-base font-semibold text-content\">Supported operations</h2>\n <p class=\"mt-2\">\n The MCP server supports database CRUD operations, dashboards, and script creation. It does not execute standalone scripts directly.\n </p>\n <p class=\"mt-2\">\n An MCP client can create a script and return a Mongoose Studio URL. A person can then open that URL, review or edit the code, and choose whether to dry run or execute it.\n </p>\n </section>\n </div>\n</div>\n";
54925
+
54926
+ /***/ },
54927
+
54546
54928
  /***/ "./frontend/src/modal/modal.css"
54547
54929
  /*!**************************************!*\
54548
54930
  !*** ./frontend/src/modal/modal.css ***!
@@ -54649,7 +55031,7 @@ module.exports = ".active {\n text-decoration: underline;\n}\n\n.navbar .nav-le
54649
55031
  (module) {
54650
55032
 
54651
55033
  "use strict";
54652
- module.exports = "<div>\n<div class=\"navbar w-full bg-page border-b border-edge !h-[55px] hidden md:grid grid-cols-[1fr_auto_1fr] items-stretch px-4\">\n <!-- Left: Logo + Brand -->\n <div class=\"flex items-center gap-2 self-center\">\n <router-link class=\"flex items-center gap-2\" :to=\"{ name: defaultRoute }\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-10 text-primary\" viewBox=\"0 0 250 250\" fill=\"currentColor\" aria-label=\"Mongoose Studio Logo\">\n <path d=\"M 241.66,125.01 C 237.81,112.98 227.41,111.95 216.76,110.36 C 215.12,95.83 210.99,83.09 202.29,70.33 C 202.99,68.37 201.92,66.65 200.74,65.11 C 183.14,41.46 157.52,27 124.66,27 C 92.62,27 66.09,40.77 48.28,63.38 C 46.21,65.85 46.26,67.71 47.12,69.36 C 37.81,81.15 33.21,93.78 30.99,109.86 C 20.19,110.58 11.71,115.92 9.04,124.94 C 3.28,145.66 5.62,157.95 10.01,169.16 C 16.14,184.65 29.12,186.31 36.04,185.81 C 42.48,185.68 44.94,184.31 44.72,182.11 C 44.26,177.79 41.01,165.31 41.01,149.12 C 41.01,138.22 43.19,127.74 46.21,117.7 C 47.51,113.33 44.66,112.05 39.16,110.28 C 41.01,96.88 45.88,84.85 52.61,73.79 C 55.17,75.56 58.07,74.88 59.61,72.71 C 75.86,54.31 97.89,45.16 124.61,45.16 C 149.71,45.16 171.91,55.91 189.29,73.47 C 191.95,76.59 193.91,76.67 197.6,74.42 C 204.93,86.26 208.28,98.24 208.98,110.39 C 203.84,111.81 201.28,113.19 202.92,118.61 C 206.51,128.99 207.92,137.93 207.92,148.73 C 207.92,162.11 204.8,174.71 202.84,182.71 C 201.92,186.23 204.31,186.91 210.79,186.99 H 213.91 C 225.87,186.61 235.39,180.64 239.49,169.05 C 244.83,155.26 245.08,138.01 241.66,125.01 Z\"/>\n <path d=\"M 124.71,70.81 C 79.63,70.81 47.49,107.12 47.49,148.72 C 47.49,169.16 56.41,188.19 73.71,200.61 L 73.96,200.77 C 77.43,177.81 84.16,162.21 95.29,145.42 C 92.04,137.09 91.71,130.87 92.62,120.83 C 93.81,111.23 96.08,107.12 102.19,107.68 C 110.04,108.87 117.98,116.18 126.22,124.96 C 139.89,124.05 157.81,131.25 178.33,139.09 C 183.11,140.86 183.72,142.63 182.97,148.33 C 179.45,160.93 170.86,166.63 158.11,174.05 C 143.09,183.07 141.13,199.42 153.88,220.09 L 153.96,221.01 L 154.71,221.17 C 181.31,208.43 201.34,181.01 201.34,148.72 C 201.34,108.15 169.29,70.81 124.71,70.81 Z\"/>\n </svg>\n <span class=\"text-base font-semibold text-content whitespace-nowrap\">Mongoose Studio</span>\n </router-link>\n </div>\n <!-- Center: Nav Links -->\n <nav class=\"flex items-stretch gap-6\">\n <a v-if=\"hasAccess(roles, 'root')\"\n href=\"#/\"\n class=\"inline-flex items-center px-1 border-b-2 text-sm font-medium\"\n :class=\"documentView ? 'text-content border-primary' : 'border-transparent text-content-tertiary hover:text-content'\">Documents</a>\n <span v-else class=\"inline-flex items-center px-1 border-b-2 border-transparent text-sm font-medium text-gray-300 cursor-not-allowed\" aria-disabled=\"true\">\n Documents\n <svg class=\"h-4 w-4 ml-1\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <a v-if=\"hasAccess(roles, 'dashboards')\"\n href=\"#/dashboards\"\n class=\"inline-flex items-center px-1 border-b-2 text-sm font-medium\"\n :class=\"dashboardView ? 'text-content border-primary' : 'border-transparent text-content-tertiary hover:text-content'\">Dashboards</a>\n <span v-else class=\"inline-flex items-center px-1 border-b-2 border-transparent text-sm font-medium text-gray-300 cursor-not-allowed\">\n Dashboards\n <svg class=\"h-4 w-4 ml-1\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <a v-if=\"hasAccess(roles, 'chat') && supportsAI\"\n href=\"#/chat\"\n class=\"inline-flex items-center px-1 border-b-2 text-sm font-medium\"\n :class=\"chatView ? 'text-content border-primary' : 'border-transparent text-content-tertiary hover:text-content'\">Chat</a>\n <span v-else-if=\"hasAccess(roles, 'chat') && !supportsAI\"\n class=\"inline-flex items-center px-1 border-b-2 border-transparent text-sm font-medium text-gray-300 cursor-not-allowed\"\n :title=\"chatDisabledReason\">\n Chat\n <svg class=\"h-4 w-4 ml-1\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <span v-else class=\"inline-flex items-center px-1 border-b-2 border-transparent text-sm font-medium text-gray-300 cursor-not-allowed\">\n Chat\n <svg class=\"h-4 w-4 ml-1\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <a\n v-if=\"hasTaskVisualizer\"\n href=\"#/tasks\"\n class=\"inline-flex items-center px-1 border-b-2 text-sm font-medium\"\n :class=\"taskView ? 'text-content border-primary' : 'border-transparent text-content-tertiary hover:text-content'\">\n Tasks\n </a>\n <a\n href=\"https://studio.mongoosejs.io/docs\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"inline-flex items-center px-1 border-b-2 border-transparent text-sm font-medium text-content-tertiary hover:text-content\"\n >\n Docs\n </a>\n </nav>\n <!-- Right: Env Badge + User -->\n <div class=\"flex items-center justify-end gap-3 self-center\">\n <button\n type=\"button\"\n class=\"inline-flex items-center justify-center rounded-md p-2 text-content-tertiary hover:bg-muted hover:text-content-secondary\"\n title=\"Recent pages\"\n aria-label=\"Recent pages\"\n aria-haspopup=\"dialog\"\n :aria-expanded=\"showRecentPagesModal ? 'true' : 'false'\"\n @click=\"openRecentPagesModal\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"h-5 w-5\" aria-hidden=\"true\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z\" />\n </svg>\n </button>\n <div\n v-if=\"!!state.nodeEnv\"\n title=\"NODE_ENV\"\n class=\"inline-flex items-center rounded px-2 py-1 text-xs text-content-tertiary bg-surface border border-edge gap-2\"\n >\n <span\n :class=\"warnEnv ? 'bg-red-400' : 'bg-yellow-400'\"\n class=\"inline-block rounded-full\"\n style=\"width: 0.5em; height: 0.5em;\"\n ></span>\n <span>{{state.nodeEnv}}</span>\n </div>\n <button\n v-if=\"!user || !hasAPIKey\"\n type=\"button\"\n @click=\"toggleDarkMode\"\n :title=\"darkMode ? 'Switch to light mode' : 'Switch to dark mode'\"\n class=\"inline-flex items-center justify-center rounded-md p-2 text-content-tertiary hover:text-content-secondary hover:bg-muted\"\n aria-label=\"Toggle dark mode\">\n <svg v-if=\"darkMode\" xmlns=\"http://www.w3.org/2000/svg\" class=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z\"/>\n </svg>\n <svg v-else xmlns=\"http://www.w3.org/2000/svg\" class=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z\"/>\n </svg>\n </button>\n <div class=\"flex items-center\" v-if=\"!user && hasAPIKey\">\n <button\n type=\"button\"\n @click=\"loginWithGithub\"\n class=\"rounded bg-primary px-2 py-2 text-sm font-semibold text-primary-text shadow-sm hover:bg-primary-hover focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary\">\n Login\n </button>\n </div>\n <div v-if=\"user && hasAPIKey\" class=\"flex items-center relative\" v-clickOutside=\"hideFlyout\">\n <button type=\"button\" @click=\"showFlyout = !showFlyout\" class=\"relative flex rounded-full text-sm focus:outline-none focus:ring-2 focus:ring-gray-300\" id=\"user-menu-button\" aria-expanded=\"false\" aria-haspopup=\"true\">\n <span class=\"absolute -inset-1.5\"></span>\n <span class=\"sr-only\">Open user menu</span>\n <img class=\"size-8 rounded-full\" :src=\"user.picture\" alt=\"\">\n </button>\n\n <div\n v-if=\"showFlyout\"\n class=\"absolute right-0 top-[90%] w-48 origin-top-right rounded-md bg-surface py-1 shadow-lg ring-1 ring-black/5 focus:outline-none\"\n role=\"menu\"\n aria-orientation=\"vertical\"\n aria-labelledby=\"user-menu-button\"\n style=\"z-index: 10000\"\n tabindex=\"-1\">\n <button\n type=\"button\"\n @click=\"toggleDarkMode(); showFlyout = false\"\n class=\"w-full cursor-pointer block px-4 py-2 text-sm text-content-secondary hover:bg-primary-subtle text-left flex items-center gap-2\"\n role=\"menuitem\"\n tabindex=\"-1\">\n <svg v-if=\"darkMode\" xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" viewBox=\"0 0 20 20\" fill=\"currentColor\"><path d=\"M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z\"/></svg>\n <svg v-else xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" viewBox=\"0 0 20 20\" fill=\"currentColor\"><path d=\"M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z\"/></svg>\n {{ darkMode ? 'Light mode' : 'Dark mode' }}\n </button>\n <router-link to=\"/team\" v-if=\"hasAccess(roles, 'team')\" @click=\"showFlyout = false\" class=\"cursor-pointer block px-4 py-2 text-sm text-content-secondary hover:bg-primary-subtle\" role=\"menuitem\" tabindex=\"-1\" id=\"user-menu-item-2\">Team</router-link>\n <span v-else class=\"block px-4 py-2 text-sm text-gray-300 cursor-not-allowed\" role=\"menuitem\" tabindex=\"-1\" id=\"user-menu-item-2\">\n Team\n <svg class=\"h-4 w-4 ml-1 inline\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <span @click=\"logout\" class=\"cursor-pointer block px-4 py-2 text-sm text-content-secondary hover:bg-primary-subtle\" role=\"menuitem\" tabindex=\"-1\" id=\"user-menu-item-2\">Sign out</span>\n </div>\n </div>\n </div>\n</div>\n<!-- Mobile navbar -->\n<div class=\"navbar w-full bg-page flex justify-between border-b border-edge !h-[55px] md:hidden\">\n <div class=\"flex items-center gap-2 pl-4\">\n <router-link class=\"flex items-center gap-2\" :to=\"{ name: defaultRoute }\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-8 text-primary\" viewBox=\"0 0 250 250\" fill=\"currentColor\" aria-label=\"Mongoose Studio Logo\">\n <path d=\"M 241.66,125.01 C 237.81,112.98 227.41,111.95 216.76,110.36 C 215.12,95.83 210.99,83.09 202.29,70.33 C 202.99,68.37 201.92,66.65 200.74,65.11 C 183.14,41.46 157.52,27 124.66,27 C 92.62,27 66.09,40.77 48.28,63.38 C 46.21,65.85 46.26,67.71 47.12,69.36 C 37.81,81.15 33.21,93.78 30.99,109.86 C 20.19,110.58 11.71,115.92 9.04,124.94 C 3.28,145.66 5.62,157.95 10.01,169.16 C 16.14,184.65 29.12,186.31 36.04,185.81 C 42.48,185.68 44.94,184.31 44.72,182.11 C 44.26,177.79 41.01,165.31 41.01,149.12 C 41.01,138.22 43.19,127.74 46.21,117.7 C 47.51,113.33 44.66,112.05 39.16,110.28 C 41.01,96.88 45.88,84.85 52.61,73.79 C 55.17,75.56 58.07,74.88 59.61,72.71 C 75.86,54.31 97.89,45.16 124.61,45.16 C 149.71,45.16 171.91,55.91 189.29,73.47 C 191.95,76.59 193.91,76.67 197.6,74.42 C 204.93,86.26 208.28,98.24 208.98,110.39 C 203.84,111.81 201.28,113.19 202.92,118.61 C 206.51,128.99 207.92,137.93 207.92,148.73 C 207.92,162.11 204.8,174.71 202.84,182.71 C 201.92,186.23 204.31,186.91 210.79,186.99 H 213.91 C 225.87,186.61 235.39,180.64 239.49,169.05 C 244.83,155.26 245.08,138.01 241.66,125.01 Z\"/>\n <path d=\"M 124.71,70.81 C 79.63,70.81 47.49,107.12 47.49,148.72 C 47.49,169.16 56.41,188.19 73.71,200.61 L 73.96,200.77 C 77.43,177.81 84.16,162.21 95.29,145.42 C 92.04,137.09 91.71,130.87 92.62,120.83 C 93.81,111.23 96.08,107.12 102.19,107.68 C 110.04,108.87 117.98,116.18 126.22,124.96 C 139.89,124.05 157.81,131.25 178.33,139.09 C 183.11,140.86 183.72,142.63 182.97,148.33 C 179.45,160.93 170.86,166.63 158.11,174.05 C 143.09,183.07 141.13,199.42 153.88,220.09 L 153.96,221.01 L 154.71,221.17 C 181.31,208.43 201.34,181.01 201.34,148.72 C 201.34,108.15 169.29,70.81 124.71,70.81 Z\"/>\n </svg>\n <span class=\"text-base font-semibold text-content\">Mongoose Studio</span>\n </router-link>\n </div>\n <div class=\"flex items-center gap-1 pr-2\">\n <button\n type=\"button\"\n class=\"rounded-md p-2 text-content-tertiary hover:bg-muted hover:text-content-secondary md:hidden\"\n title=\"Recent pages\"\n aria-label=\"Recent pages\"\n aria-haspopup=\"dialog\"\n :aria-expanded=\"showRecentPagesModal ? 'true' : 'false'\"\n @click=\"openRecentPagesModal\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"h-6 w-6\" aria-hidden=\"true\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z\" />\n </svg>\n </button>\n <!-- Mobile menu toggle, controls the 'mobileMenuOpen' state. -->\n <button type=\"button\" id=\"open-mobile-menu\" class=\"-ml-2 rounded-md p-2 text-gray-400\">\n <span class=\"sr-only\">Open menu</span>\n <svg class=\"h-6 w-6\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" aria-hidden=\"true\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5\" />\n </svg>\n </button>\n </div>\n\n <!-- Mobile menu mask -->\n <div id=\"mobile-menu-mask\" class=\"fixed inset-0 bg-black bg-opacity-40 z-40 hidden\"></div>\n <!-- Mobile menu drawer -->\n <div id=\"mobile-menu\" style=\"z-index: 100000\" class=\"fixed inset-0 bg-page shadow-lg transform translate-x-full transition-transform duration-200 ease-in-out flex flex-col\">\n <div class=\"flex items-center justify-between px-4 !h-[55px] border-b border-edge\">\n <router-link :to=\"{ name: defaultRoute }\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-[32px] text-primary\" viewBox=\"0 0 250 250\" fill=\"currentColor\" aria-label=\"Mongoose Studio Logo\">\n <path d=\"M 241.66,125.01 C 237.81,112.98 227.41,111.95 216.76,110.36 C 215.12,95.83 210.99,83.09 202.29,70.33 C 202.99,68.37 201.92,66.65 200.74,65.11 C 183.14,41.46 157.52,27 124.66,27 C 92.62,27 66.09,40.77 48.28,63.38 C 46.21,65.85 46.26,67.71 47.12,69.36 C 37.81,81.15 33.21,93.78 30.99,109.86 C 20.19,110.58 11.71,115.92 9.04,124.94 C 3.28,145.66 5.62,157.95 10.01,169.16 C 16.14,184.65 29.12,186.31 36.04,185.81 C 42.48,185.68 44.94,184.31 44.72,182.11 C 44.26,177.79 41.01,165.31 41.01,149.12 C 41.01,138.22 43.19,127.74 46.21,117.7 C 47.51,113.33 44.66,112.05 39.16,110.28 C 41.01,96.88 45.88,84.85 52.61,73.79 C 55.17,75.56 58.07,74.88 59.61,72.71 C 75.86,54.31 97.89,45.16 124.61,45.16 C 149.71,45.16 171.91,55.91 189.29,73.47 C 191.95,76.59 193.91,76.67 197.6,74.42 C 204.93,86.26 208.28,98.24 208.98,110.39 C 203.84,111.81 201.28,113.19 202.92,118.61 C 206.51,128.99 207.92,137.93 207.92,148.73 C 207.92,162.11 204.8,174.71 202.84,182.71 C 201.92,186.23 204.31,186.91 210.79,186.99 H 213.91 C 225.87,186.61 235.39,180.64 239.49,169.05 C 244.83,155.26 245.08,138.01 241.66,125.01 Z\"/>\n <path d=\"M 124.71,70.81 C 79.63,70.81 47.49,107.12 47.49,148.72 C 47.49,169.16 56.41,188.19 73.71,200.61 L 73.96,200.77 C 77.43,177.81 84.16,162.21 95.29,145.42 C 92.04,137.09 91.71,130.87 92.62,120.83 C 93.81,111.23 96.08,107.12 102.19,107.68 C 110.04,108.87 117.98,116.18 126.22,124.96 C 139.89,124.05 157.81,131.25 178.33,139.09 C 183.11,140.86 183.72,142.63 182.97,148.33 C 179.45,160.93 170.86,166.63 158.11,174.05 C 143.09,183.07 141.13,199.42 153.88,220.09 L 153.96,221.01 L 154.71,221.17 C 181.31,208.43 201.34,181.01 201.34,148.72 C 201.34,108.15 169.29,70.81 124.71,70.81 Z\"/>\n </svg>\n </router-link>\n <button type=\"button\" id=\"close-mobile-menu\" class=\"text-gray-400 p-2 rounded-md\">\n <span class=\"sr-only\">Close menu</span>\n <svg class=\"h-6 w-6\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" aria-hidden=\"true\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n <nav class=\"flex-1 px-4 py-4 space-y-2\">\n <div class=\"flex items-center gap-2 px-3 py-2\" v-if=\"!user || !hasAPIKey\">\n <button\n type=\"button\"\n @click=\"toggleDarkMode\"\n class=\"inline-flex items-center gap-2 rounded-md px-3 py-2 text-base font-medium text-content-secondary hover:bg-muted\"\n aria-label=\"Toggle dark mode\">\n <svg v-if=\"darkMode\" xmlns=\"http://www.w3.org/2000/svg\" class=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\"><path d=\"M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z\"/></svg>\n <svg v-else xmlns=\"http://www.w3.org/2000/svg\" class=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\"><path d=\"M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z\"/></svg>\n <span>{{ darkMode ? 'Light mode' : 'Dark mode' }}</span>\n </button>\n </div>\n <a v-if=\"hasAccess(roles, 'root')\"\n href=\"#/\"\n class=\"block px-3 py-2 rounded-md text-base font-medium\"\n :class=\"documentView ? 'text-content bg-primary-subtle' : 'text-content-secondary hover:bg-muted'\">Documents</a>\n <span v-else class=\"block px-3 py-2 rounded-md text-base font-medium text-gray-300 cursor-not-allowed\">\n Documents\n <svg class=\"h-4 w-4 ml-1 inline\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <a v-if=\"hasAccess(roles, 'dashboards')\"\n href=\"#/dashboards\"\n class=\"block px-3 py-2 rounded-md text-base font-medium\"\n :class=\"dashboardView ? 'text-content bg-primary-subtle' : 'text-content-secondary hover:bg-muted'\">Dashboards</a>\n <span v-else class=\"block px-3 py-2 rounded-md text-base font-medium text-gray-300 cursor-not-allowed\">\n Dashboards\n <svg class=\"h-4 w-4 ml-1 inline\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <a v-if=\"hasAccess(roles, 'chat') && supportsAI\"\n href=\"#/chat\"\n class=\"block px-3 py-2 rounded-md text-base font-medium\"\n :class=\"chatView ? 'text-content bg-primary-subtle' : 'text-content-secondary hover:bg-muted'\">Chat</a>\n <span v-else-if=\"hasAccess(roles, 'chat') && !supportsAI\"\n class=\"block px-3 py-2 rounded-md text-base font-medium text-gray-300 cursor-not-allowed\"\n :title=\"chatDisabledReason\">\n Chat\n <svg class=\"h-4 w-4 ml-1 inline\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <span v-else class=\"block px-3 py-2 rounded-md text-base font-medium text-gray-300 cursor-not-allowed\">\n Chat\n <svg class=\"h-4 w-4 ml-1 inline\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <a v-if=\"hasTaskVisualizer\"\n href=\"#/tasks\"\n class=\"block px-3 py-2 rounded-md text-base font-medium\"\n :class=\"taskView ? 'text-content bg-primary-subtle' : 'text-content-secondary hover:bg-muted'\">Tasks</a>\n <div v-if=\"!user && hasAPIKey\" class=\"mt-4\">\n <button\n type=\"button\"\n @click=\"loginWithGithub\"\n class=\"w-full rounded bg-muted text-content px-3 py-2 text-base font-semibold shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary\">\n Login\n </button>\n </div>\n <div v-if=\"user && hasAPIKey\" class=\"mt-4\">\n <div class=\"flex items-center gap-3 px-3 py-2 bg-page rounded-md\">\n <img class=\"size-8 rounded-full\" :src=\"user.picture\" alt=\"\">\n <span class=\"text-content font-medium\">{{ user.name }}</span>\n </div>\n <div class=\"mt-2 space-y-1\">\n <button\n type=\"button\"\n @click=\"toggleDarkMode\"\n class=\"w-full inline-flex items-center gap-2 px-3 py-2 rounded-md text-base text-content-secondary hover:bg-primary-subtle text-left\">\n <svg v-if=\"darkMode\" xmlns=\"http://www.w3.org/2000/svg\" class=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\"><path d=\"M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z\"/></svg>\n <svg v-else xmlns=\"http://www.w3.org/2000/svg\" class=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\"><path d=\"M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z\"/></svg>\n {{ darkMode ? 'Light mode' : 'Dark mode' }}\n </button>\n <router-link to=\"/team\" v-if=\"hasAccess(roles, 'team')\" class=\"block px-3 py-2 rounded-md text-base text-content-secondary hover:bg-primary-subtle\">Team</router-link>\n <span v-else class=\"block px-3 py-2 rounded-md text-base text-gray-300 cursor-not-allowed\">\n Team\n <svg class=\"h-4 w-4 ml-1 inline\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <span @click=\"logout\" class=\"block px-3 py-2 rounded-md text-base text-content-secondary hover:bg-primary-subtle cursor-pointer\">Sign out</span>\n </div>\n </div>\n </nav>\n </div>\n</div>\n\n<modal\n v-if=\"showRecentPagesModal\"\n flush-container\n containerClass=\"!max-w-md !w-[min(94vw,28rem)] !overflow-hidden !rounded-lg !border !border-edge !bg-surface !shadow-xl dark:!bg-shark-900 text-content\"\n bodyClass=\"modal-recent-pages-body !p-0\"\n>\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"closeRecentPagesModal\" role=\"button\" aria-label=\"Close recent pages\">&times;</div>\n <div\n class=\"shrink-0 flex items-center gap-3 border-b border-edge bg-surface px-4 py-4 pr-12 dark:bg-shark-900\"\n >\n <h2 class=\"m-0 text-lg font-semibold text-content\">Recent pages</h2>\n <button\n type=\"button\"\n class=\"rounded px-2 py-1 text-sm font-medium text-content-secondary hover:bg-muted disabled:opacity-50\"\n @click=\"clearRecentPagesHistory\"\n :disabled=\"recentPagesList.length === 0\"\n >\n Clear\n </button>\n </div>\n <div class=\"modal-recent-pages-scroll\">\n <div\n v-if=\"recentPagesList.length === 0\"\n class=\"m-4 rounded border border-dashed border-edge px-3 py-6 text-center text-sm text-content-tertiary\"\n >\n No recent pages yet.\n </div>\n <button\n v-for=\"entry in recentPagesList\"\n :key=\"entry.path + '-' + entry.visitedAt\"\n type=\"button\"\n class=\"w-full px-4 py-2.5 text-left hover:bg-muted\"\n :class=\"entry.path === $route.fullPath ? 'bg-primary-subtle' : ''\"\n @click=\"goToRecentPage(entry)\"\n >\n <div class=\"truncate text-sm font-medium text-content\">{{ entry.label }}</div>\n <div class=\"truncate font-mono text-xs text-content-tertiary\">{{ entry.path }}</div>\n </button>\n </div>\n </template>\n</modal>\n</div>\n";
55034
+ module.exports = "<div>\n<div class=\"navbar w-full bg-page border-b border-edge !h-[55px] hidden md:grid grid-cols-[1fr_auto_1fr] items-stretch px-4\">\n <!-- Left: Logo + Brand -->\n <div class=\"flex items-center gap-2 self-center\">\n <router-link class=\"flex items-center gap-2\" :to=\"{ name: defaultRoute }\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-10 text-primary\" viewBox=\"0 0 250 250\" fill=\"currentColor\" aria-label=\"Mongoose Studio Logo\">\n <path d=\"M 241.66,125.01 C 237.81,112.98 227.41,111.95 216.76,110.36 C 215.12,95.83 210.99,83.09 202.29,70.33 C 202.99,68.37 201.92,66.65 200.74,65.11 C 183.14,41.46 157.52,27 124.66,27 C 92.62,27 66.09,40.77 48.28,63.38 C 46.21,65.85 46.26,67.71 47.12,69.36 C 37.81,81.15 33.21,93.78 30.99,109.86 C 20.19,110.58 11.71,115.92 9.04,124.94 C 3.28,145.66 5.62,157.95 10.01,169.16 C 16.14,184.65 29.12,186.31 36.04,185.81 C 42.48,185.68 44.94,184.31 44.72,182.11 C 44.26,177.79 41.01,165.31 41.01,149.12 C 41.01,138.22 43.19,127.74 46.21,117.7 C 47.51,113.33 44.66,112.05 39.16,110.28 C 41.01,96.88 45.88,84.85 52.61,73.79 C 55.17,75.56 58.07,74.88 59.61,72.71 C 75.86,54.31 97.89,45.16 124.61,45.16 C 149.71,45.16 171.91,55.91 189.29,73.47 C 191.95,76.59 193.91,76.67 197.6,74.42 C 204.93,86.26 208.28,98.24 208.98,110.39 C 203.84,111.81 201.28,113.19 202.92,118.61 C 206.51,128.99 207.92,137.93 207.92,148.73 C 207.92,162.11 204.8,174.71 202.84,182.71 C 201.92,186.23 204.31,186.91 210.79,186.99 H 213.91 C 225.87,186.61 235.39,180.64 239.49,169.05 C 244.83,155.26 245.08,138.01 241.66,125.01 Z\"/>\n <path d=\"M 124.71,70.81 C 79.63,70.81 47.49,107.12 47.49,148.72 C 47.49,169.16 56.41,188.19 73.71,200.61 L 73.96,200.77 C 77.43,177.81 84.16,162.21 95.29,145.42 C 92.04,137.09 91.71,130.87 92.62,120.83 C 93.81,111.23 96.08,107.12 102.19,107.68 C 110.04,108.87 117.98,116.18 126.22,124.96 C 139.89,124.05 157.81,131.25 178.33,139.09 C 183.11,140.86 183.72,142.63 182.97,148.33 C 179.45,160.93 170.86,166.63 158.11,174.05 C 143.09,183.07 141.13,199.42 153.88,220.09 L 153.96,221.01 L 154.71,221.17 C 181.31,208.43 201.34,181.01 201.34,148.72 C 201.34,108.15 169.29,70.81 124.71,70.81 Z\"/>\n </svg>\n <span class=\"text-base font-semibold text-content whitespace-nowrap\">Mongoose Studio</span>\n </router-link>\n </div>\n <!-- Center: Nav Links -->\n <nav class=\"flex items-stretch gap-6\">\n <a v-if=\"hasAccess(roles, 'root')\"\n href=\"#/\"\n class=\"inline-flex items-center px-1 border-b-2 text-sm font-medium\"\n :class=\"documentView ? 'text-content border-primary' : 'border-transparent text-content-tertiary hover:text-content'\">Documents</a>\n <span v-else class=\"inline-flex items-center px-1 border-b-2 border-transparent text-sm font-medium text-gray-300 cursor-not-allowed\" aria-disabled=\"true\">\n Documents\n <svg class=\"h-4 w-4 ml-1\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <a v-if=\"hasAccess(roles, 'dashboards')\"\n href=\"#/dashboards\"\n class=\"inline-flex items-center px-1 border-b-2 text-sm font-medium\"\n :class=\"dashboardView ? 'text-content border-primary' : 'border-transparent text-content-tertiary hover:text-content'\">Dashboards</a>\n <span v-else class=\"inline-flex items-center px-1 border-b-2 border-transparent text-sm font-medium text-gray-300 cursor-not-allowed\">\n Dashboards\n <svg class=\"h-4 w-4 ml-1\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <a v-if=\"hasAccess(roles, 'chat') && supportsAI\"\n href=\"#/chat\"\n class=\"inline-flex items-center px-1 border-b-2 text-sm font-medium\"\n :class=\"chatView ? 'text-content border-primary' : 'border-transparent text-content-tertiary hover:text-content'\">Chat</a>\n <span v-else-if=\"hasAccess(roles, 'chat') && !supportsAI\"\n class=\"inline-flex items-center px-1 border-b-2 border-transparent text-sm font-medium text-gray-300 cursor-not-allowed\"\n :title=\"chatDisabledReason\">\n Chat\n <svg class=\"h-4 w-4 ml-1\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <span v-else class=\"inline-flex items-center px-1 border-b-2 border-transparent text-sm font-medium text-gray-300 cursor-not-allowed\">\n Chat\n <svg class=\"h-4 w-4 ml-1\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <a\n v-if=\"hasTaskVisualizer\"\n href=\"#/tasks\"\n class=\"inline-flex items-center px-1 border-b-2 text-sm font-medium\"\n :class=\"taskView ? 'text-content border-primary' : 'border-transparent text-content-tertiary hover:text-content'\">\n Tasks\n </a>\n <a\n href=\"https://studio.mongoosejs.io/docs\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"inline-flex items-center px-1 border-b-2 border-transparent text-sm font-medium text-content-tertiary hover:text-content\"\n >\n Docs\n </a>\n </nav>\n <!-- Right: Env Badge + User -->\n <div class=\"flex items-center justify-end gap-3 self-center\">\n <button\n type=\"button\"\n class=\"inline-flex items-center justify-center rounded-md p-2 text-content-tertiary hover:bg-muted hover:text-content-secondary\"\n title=\"Recent pages\"\n aria-label=\"Recent pages\"\n aria-haspopup=\"dialog\"\n :aria-expanded=\"showRecentPagesModal ? 'true' : 'false'\"\n @click=\"openRecentPagesModal\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"h-5 w-5\" aria-hidden=\"true\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z\" />\n </svg>\n </button>\n <div\n v-if=\"!!state.nodeEnv\"\n title=\"NODE_ENV\"\n class=\"inline-flex items-center rounded px-2 py-1 text-xs text-content-tertiary bg-surface border border-edge gap-2\"\n >\n <span\n :class=\"warnEnv ? 'bg-red-400' : 'bg-yellow-400'\"\n class=\"inline-block rounded-full\"\n style=\"width: 0.5em; height: 0.5em;\"\n ></span>\n <span>{{state.nodeEnv}}</span>\n </div>\n <button\n v-if=\"!user || !hasAPIKey\"\n type=\"button\"\n @click=\"toggleDarkMode\"\n :title=\"darkMode ? 'Switch to light mode' : 'Switch to dark mode'\"\n class=\"inline-flex items-center justify-center rounded-md p-2 text-content-tertiary hover:text-content-secondary hover:bg-muted\"\n aria-label=\"Toggle dark mode\">\n <svg v-if=\"darkMode\" xmlns=\"http://www.w3.org/2000/svg\" class=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z\"/>\n </svg>\n <svg v-else xmlns=\"http://www.w3.org/2000/svg\" class=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z\"/>\n </svg>\n </button>\n <div class=\"flex items-center\" v-if=\"!user && hasAPIKey\">\n <button\n type=\"button\"\n @click=\"loginWithGithub\"\n class=\"rounded bg-primary px-2 py-2 text-sm font-semibold text-primary-text shadow-sm hover:bg-primary-hover focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary\">\n Login\n </button>\n </div>\n <div v-if=\"user && hasAPIKey\" class=\"flex items-center relative\" v-clickOutside=\"hideFlyout\">\n <button type=\"button\" @click=\"showFlyout = !showFlyout\" class=\"relative flex rounded-full text-sm focus:outline-none focus:ring-2 focus:ring-gray-300\" id=\"user-menu-button\" aria-expanded=\"false\" aria-haspopup=\"true\">\n <span class=\"absolute -inset-1.5\"></span>\n <span class=\"sr-only\">Open user menu</span>\n <img class=\"size-8 rounded-full\" :src=\"user.picture\" alt=\"\">\n </button>\n\n <div\n v-if=\"showFlyout\"\n class=\"absolute right-0 top-[90%] w-48 origin-top-right rounded-md bg-surface py-1 shadow-lg ring-1 ring-black/5 focus:outline-none\"\n role=\"menu\"\n aria-orientation=\"vertical\"\n aria-labelledby=\"user-menu-button\"\n style=\"z-index: 10000\"\n tabindex=\"-1\">\n <button\n type=\"button\"\n @click=\"toggleDarkMode(); showFlyout = false\"\n class=\"w-full cursor-pointer block px-4 py-2 text-sm text-content-secondary hover:bg-primary-subtle text-left flex items-center gap-2\"\n role=\"menuitem\"\n tabindex=\"-1\">\n <svg v-if=\"darkMode\" xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" viewBox=\"0 0 20 20\" fill=\"currentColor\"><path d=\"M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z\"/></svg>\n <svg v-else xmlns=\"http://www.w3.org/2000/svg\" class=\"h-4 w-4\" viewBox=\"0 0 20 20\" fill=\"currentColor\"><path d=\"M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z\"/></svg>\n {{ darkMode ? 'Light mode' : 'Dark mode' }}\n </button>\n <router-link to=\"/team\" v-if=\"hasAccess(roles, 'team')\" @click=\"showFlyout = false\" class=\"cursor-pointer block px-4 py-2 text-sm text-content-secondary hover:bg-primary-subtle\" role=\"menuitem\" tabindex=\"-1\" id=\"user-menu-item-2\">Team</router-link>\n <span v-else class=\"block px-4 py-2 text-sm text-gray-300 cursor-not-allowed\" role=\"menuitem\" tabindex=\"-1\" id=\"user-menu-item-2\">\n Team\n <svg class=\"h-4 w-4 ml-1 inline\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <router-link v-if=\"mcpEnabled\" to=\"/mcp\" @click=\"showFlyout = false\" class=\"cursor-pointer block px-4 py-2 text-sm text-content-secondary hover:bg-primary-subtle\" role=\"menuitem\" tabindex=\"-1\">MCP</router-link>\n <span v-else class=\"block px-4 py-2 text-sm text-gray-300 cursor-not-allowed\" role=\"menuitem\" title=\"MCP is disabled for this deployment\">\n MCP\n </span>\n <span @click=\"logout\" class=\"cursor-pointer block px-4 py-2 text-sm text-content-secondary hover:bg-primary-subtle\" role=\"menuitem\" tabindex=\"-1\" id=\"user-menu-item-2\">Sign out</span>\n </div>\n </div>\n </div>\n</div>\n<!-- Mobile navbar -->\n<div class=\"navbar w-full bg-page flex justify-between border-b border-edge !h-[55px] md:hidden\">\n <div class=\"flex items-center gap-2 pl-4\">\n <router-link class=\"flex items-center gap-2\" :to=\"{ name: defaultRoute }\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-8 text-primary\" viewBox=\"0 0 250 250\" fill=\"currentColor\" aria-label=\"Mongoose Studio Logo\">\n <path d=\"M 241.66,125.01 C 237.81,112.98 227.41,111.95 216.76,110.36 C 215.12,95.83 210.99,83.09 202.29,70.33 C 202.99,68.37 201.92,66.65 200.74,65.11 C 183.14,41.46 157.52,27 124.66,27 C 92.62,27 66.09,40.77 48.28,63.38 C 46.21,65.85 46.26,67.71 47.12,69.36 C 37.81,81.15 33.21,93.78 30.99,109.86 C 20.19,110.58 11.71,115.92 9.04,124.94 C 3.28,145.66 5.62,157.95 10.01,169.16 C 16.14,184.65 29.12,186.31 36.04,185.81 C 42.48,185.68 44.94,184.31 44.72,182.11 C 44.26,177.79 41.01,165.31 41.01,149.12 C 41.01,138.22 43.19,127.74 46.21,117.7 C 47.51,113.33 44.66,112.05 39.16,110.28 C 41.01,96.88 45.88,84.85 52.61,73.79 C 55.17,75.56 58.07,74.88 59.61,72.71 C 75.86,54.31 97.89,45.16 124.61,45.16 C 149.71,45.16 171.91,55.91 189.29,73.47 C 191.95,76.59 193.91,76.67 197.6,74.42 C 204.93,86.26 208.28,98.24 208.98,110.39 C 203.84,111.81 201.28,113.19 202.92,118.61 C 206.51,128.99 207.92,137.93 207.92,148.73 C 207.92,162.11 204.8,174.71 202.84,182.71 C 201.92,186.23 204.31,186.91 210.79,186.99 H 213.91 C 225.87,186.61 235.39,180.64 239.49,169.05 C 244.83,155.26 245.08,138.01 241.66,125.01 Z\"/>\n <path d=\"M 124.71,70.81 C 79.63,70.81 47.49,107.12 47.49,148.72 C 47.49,169.16 56.41,188.19 73.71,200.61 L 73.96,200.77 C 77.43,177.81 84.16,162.21 95.29,145.42 C 92.04,137.09 91.71,130.87 92.62,120.83 C 93.81,111.23 96.08,107.12 102.19,107.68 C 110.04,108.87 117.98,116.18 126.22,124.96 C 139.89,124.05 157.81,131.25 178.33,139.09 C 183.11,140.86 183.72,142.63 182.97,148.33 C 179.45,160.93 170.86,166.63 158.11,174.05 C 143.09,183.07 141.13,199.42 153.88,220.09 L 153.96,221.01 L 154.71,221.17 C 181.31,208.43 201.34,181.01 201.34,148.72 C 201.34,108.15 169.29,70.81 124.71,70.81 Z\"/>\n </svg>\n <span class=\"text-base font-semibold text-content\">Mongoose Studio</span>\n </router-link>\n </div>\n <div class=\"flex items-center gap-1 pr-2\">\n <button\n type=\"button\"\n class=\"rounded-md p-2 text-content-tertiary hover:bg-muted hover:text-content-secondary md:hidden\"\n title=\"Recent pages\"\n aria-label=\"Recent pages\"\n aria-haspopup=\"dialog\"\n :aria-expanded=\"showRecentPagesModal ? 'true' : 'false'\"\n @click=\"openRecentPagesModal\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"h-6 w-6\" aria-hidden=\"true\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z\" />\n </svg>\n </button>\n <!-- Mobile menu toggle, controls the 'mobileMenuOpen' state. -->\n <button type=\"button\" id=\"open-mobile-menu\" class=\"-ml-2 rounded-md p-2 text-gray-400\">\n <span class=\"sr-only\">Open menu</span>\n <svg class=\"h-6 w-6\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" aria-hidden=\"true\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5\" />\n </svg>\n </button>\n </div>\n\n <!-- Mobile menu mask -->\n <div id=\"mobile-menu-mask\" class=\"fixed inset-0 bg-black bg-opacity-40 z-40 hidden\"></div>\n <!-- Mobile menu drawer -->\n <div id=\"mobile-menu\" style=\"z-index: 100000\" class=\"fixed inset-0 bg-page shadow-lg transform translate-x-full transition-transform duration-200 ease-in-out flex flex-col\">\n <div class=\"flex items-center justify-between px-4 !h-[55px] border-b border-edge\">\n <router-link :to=\"{ name: defaultRoute }\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-[32px] text-primary\" viewBox=\"0 0 250 250\" fill=\"currentColor\" aria-label=\"Mongoose Studio Logo\">\n <path d=\"M 241.66,125.01 C 237.81,112.98 227.41,111.95 216.76,110.36 C 215.12,95.83 210.99,83.09 202.29,70.33 C 202.99,68.37 201.92,66.65 200.74,65.11 C 183.14,41.46 157.52,27 124.66,27 C 92.62,27 66.09,40.77 48.28,63.38 C 46.21,65.85 46.26,67.71 47.12,69.36 C 37.81,81.15 33.21,93.78 30.99,109.86 C 20.19,110.58 11.71,115.92 9.04,124.94 C 3.28,145.66 5.62,157.95 10.01,169.16 C 16.14,184.65 29.12,186.31 36.04,185.81 C 42.48,185.68 44.94,184.31 44.72,182.11 C 44.26,177.79 41.01,165.31 41.01,149.12 C 41.01,138.22 43.19,127.74 46.21,117.7 C 47.51,113.33 44.66,112.05 39.16,110.28 C 41.01,96.88 45.88,84.85 52.61,73.79 C 55.17,75.56 58.07,74.88 59.61,72.71 C 75.86,54.31 97.89,45.16 124.61,45.16 C 149.71,45.16 171.91,55.91 189.29,73.47 C 191.95,76.59 193.91,76.67 197.6,74.42 C 204.93,86.26 208.28,98.24 208.98,110.39 C 203.84,111.81 201.28,113.19 202.92,118.61 C 206.51,128.99 207.92,137.93 207.92,148.73 C 207.92,162.11 204.8,174.71 202.84,182.71 C 201.92,186.23 204.31,186.91 210.79,186.99 H 213.91 C 225.87,186.61 235.39,180.64 239.49,169.05 C 244.83,155.26 245.08,138.01 241.66,125.01 Z\"/>\n <path d=\"M 124.71,70.81 C 79.63,70.81 47.49,107.12 47.49,148.72 C 47.49,169.16 56.41,188.19 73.71,200.61 L 73.96,200.77 C 77.43,177.81 84.16,162.21 95.29,145.42 C 92.04,137.09 91.71,130.87 92.62,120.83 C 93.81,111.23 96.08,107.12 102.19,107.68 C 110.04,108.87 117.98,116.18 126.22,124.96 C 139.89,124.05 157.81,131.25 178.33,139.09 C 183.11,140.86 183.72,142.63 182.97,148.33 C 179.45,160.93 170.86,166.63 158.11,174.05 C 143.09,183.07 141.13,199.42 153.88,220.09 L 153.96,221.01 L 154.71,221.17 C 181.31,208.43 201.34,181.01 201.34,148.72 C 201.34,108.15 169.29,70.81 124.71,70.81 Z\"/>\n </svg>\n </router-link>\n <button type=\"button\" id=\"close-mobile-menu\" class=\"text-gray-400 p-2 rounded-md\">\n <span class=\"sr-only\">Close menu</span>\n <svg class=\"h-6 w-6\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" aria-hidden=\"true\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n </div>\n <nav class=\"flex-1 px-4 py-4 space-y-2\">\n <div class=\"flex items-center gap-2 px-3 py-2\" v-if=\"!user || !hasAPIKey\">\n <button\n type=\"button\"\n @click=\"toggleDarkMode\"\n class=\"inline-flex items-center gap-2 rounded-md px-3 py-2 text-base font-medium text-content-secondary hover:bg-muted\"\n aria-label=\"Toggle dark mode\">\n <svg v-if=\"darkMode\" xmlns=\"http://www.w3.org/2000/svg\" class=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\"><path d=\"M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z\"/></svg>\n <svg v-else xmlns=\"http://www.w3.org/2000/svg\" class=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\"><path d=\"M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z\"/></svg>\n <span>{{ darkMode ? 'Light mode' : 'Dark mode' }}</span>\n </button>\n </div>\n <a v-if=\"hasAccess(roles, 'root')\"\n href=\"#/\"\n class=\"block px-3 py-2 rounded-md text-base font-medium\"\n :class=\"documentView ? 'text-content bg-primary-subtle' : 'text-content-secondary hover:bg-muted'\">Documents</a>\n <span v-else class=\"block px-3 py-2 rounded-md text-base font-medium text-gray-300 cursor-not-allowed\">\n Documents\n <svg class=\"h-4 w-4 ml-1 inline\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <a v-if=\"hasAccess(roles, 'dashboards')\"\n href=\"#/dashboards\"\n class=\"block px-3 py-2 rounded-md text-base font-medium\"\n :class=\"dashboardView ? 'text-content bg-primary-subtle' : 'text-content-secondary hover:bg-muted'\">Dashboards</a>\n <span v-else class=\"block px-3 py-2 rounded-md text-base font-medium text-gray-300 cursor-not-allowed\">\n Dashboards\n <svg class=\"h-4 w-4 ml-1 inline\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <a v-if=\"hasAccess(roles, 'chat') && supportsAI\"\n href=\"#/chat\"\n class=\"block px-3 py-2 rounded-md text-base font-medium\"\n :class=\"chatView ? 'text-content bg-primary-subtle' : 'text-content-secondary hover:bg-muted'\">Chat</a>\n <span v-else-if=\"hasAccess(roles, 'chat') && !supportsAI\"\n class=\"block px-3 py-2 rounded-md text-base font-medium text-gray-300 cursor-not-allowed\"\n :title=\"chatDisabledReason\">\n Chat\n <svg class=\"h-4 w-4 ml-1 inline\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <span v-else class=\"block px-3 py-2 rounded-md text-base font-medium text-gray-300 cursor-not-allowed\">\n Chat\n <svg class=\"h-4 w-4 ml-1 inline\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <a v-if=\"hasTaskVisualizer\"\n href=\"#/tasks\"\n class=\"block px-3 py-2 rounded-md text-base font-medium\"\n :class=\"taskView ? 'text-content bg-primary-subtle' : 'text-content-secondary hover:bg-muted'\">Tasks</a>\n <div v-if=\"!user && hasAPIKey\" class=\"mt-4\">\n <button\n type=\"button\"\n @click=\"loginWithGithub\"\n class=\"w-full rounded bg-muted text-content px-3 py-2 text-base font-semibold shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary\">\n Login\n </button>\n </div>\n <div v-if=\"user && hasAPIKey\" class=\"mt-4\">\n <div class=\"flex items-center gap-3 px-3 py-2 bg-page rounded-md\">\n <img class=\"size-8 rounded-full\" :src=\"user.picture\" alt=\"\">\n <span class=\"text-content font-medium\">{{ user.name }}</span>\n </div>\n <div class=\"mt-2 space-y-1\">\n <button\n type=\"button\"\n @click=\"toggleDarkMode\"\n class=\"w-full inline-flex items-center gap-2 px-3 py-2 rounded-md text-base text-content-secondary hover:bg-primary-subtle text-left\">\n <svg v-if=\"darkMode\" xmlns=\"http://www.w3.org/2000/svg\" class=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\"><path d=\"M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z\"/></svg>\n <svg v-else xmlns=\"http://www.w3.org/2000/svg\" class=\"h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\"><path d=\"M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z\"/></svg>\n {{ darkMode ? 'Light mode' : 'Dark mode' }}\n </button>\n <router-link to=\"/team\" v-if=\"hasAccess(roles, 'team')\" class=\"block px-3 py-2 rounded-md text-base text-content-secondary hover:bg-primary-subtle\">Team</router-link>\n <span v-else class=\"block px-3 py-2 rounded-md text-base text-gray-300 cursor-not-allowed\">\n Team\n <svg class=\"h-4 w-4 ml-1 inline\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 2a4 4 0 00-4 4v2H5a2 2 0 00-2 2v6a2 2 0 002 2h10a2 2 0 002-2v-6a2 2 0 00-2-2h-1V6a4 4 0 00-4-4zm-3 6V6a3 3 0 116 0v2H7z\" clip-rule=\"evenodd\" />\n </svg>\n </span>\n <router-link v-if=\"mcpEnabled\" to=\"/mcp\" class=\"block px-3 py-2 rounded-md text-base text-content-secondary hover:bg-primary-subtle\">MCP</router-link>\n <span v-else class=\"block px-3 py-2 rounded-md text-base text-gray-300 cursor-not-allowed\" title=\"MCP is disabled for this deployment\">\n MCP\n </span>\n <span @click=\"logout\" class=\"block px-3 py-2 rounded-md text-base text-content-secondary hover:bg-primary-subtle cursor-pointer\">Sign out</span>\n </div>\n </div>\n </nav>\n </div>\n</div>\n\n<modal\n v-if=\"showRecentPagesModal\"\n flush-container\n containerClass=\"!max-w-md !w-[min(94vw,28rem)] !overflow-hidden !rounded-lg !border !border-edge !bg-surface !shadow-xl dark:!bg-shark-900 text-content\"\n bodyClass=\"modal-recent-pages-body !p-0\"\n>\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"closeRecentPagesModal\" role=\"button\" aria-label=\"Close recent pages\">&times;</div>\n <div\n class=\"shrink-0 flex items-center gap-3 border-b border-edge bg-surface px-4 py-4 pr-12 dark:bg-shark-900\"\n >\n <h2 class=\"m-0 text-lg font-semibold text-content\">Recent pages</h2>\n <button\n type=\"button\"\n class=\"rounded px-2 py-1 text-sm font-medium text-content-secondary hover:bg-muted disabled:opacity-50\"\n @click=\"clearRecentPagesHistory\"\n :disabled=\"recentPagesList.length === 0\"\n >\n Clear\n </button>\n </div>\n <div class=\"modal-recent-pages-scroll\">\n <div\n v-if=\"recentPagesList.length === 0\"\n class=\"m-4 rounded border border-dashed border-edge px-3 py-6 text-center text-sm text-content-tertiary\"\n >\n No recent pages yet.\n </div>\n <button\n v-for=\"entry in recentPagesList\"\n :key=\"entry.path + '-' + entry.visitedAt\"\n type=\"button\"\n class=\"w-full px-4 py-2.5 text-left hover:bg-muted\"\n :class=\"entry.path === $route.fullPath ? 'bg-primary-subtle' : ''\"\n @click=\"goToRecentPage(entry)\"\n >\n <div class=\"truncate text-sm font-medium text-content\">{{ entry.label }}</div>\n <div class=\"truncate font-mono text-xs text-content-tertiary\">{{ entry.path }}</div>\n </button>\n </div>\n </template>\n</modal>\n</div>\n";
54653
55035
 
54654
55036
  /***/ },
54655
55037
 
@@ -54664,6 +55046,17 @@ module.exports = "<modal v-if=\"show\" containerClass=\"!max-w-md\">\n <templat
54664
55046
 
54665
55047
  /***/ },
54666
55048
 
55049
+ /***/ "./frontend/src/script/script.html"
55050
+ /*!*****************************************!*\
55051
+ !*** ./frontend/src/script/script.html ***!
55052
+ \*****************************************/
55053
+ (module) {
55054
+
55055
+ "use strict";
55056
+ module.exports = "<div class=\"px-4 py-6\">\n <div v-if=\"status === 'loading'\" class=\"max-w-5xl mx-auto text-center\">\n <img src=\"images/loader.gif\" class=\"inline mt-10\" alt=\"Loading script\">\n </div>\n\n <div v-else-if=\"script\" class=\"max-w-5xl mx-auto\">\n <div class=\"flex items-center justify-between mb-4\">\n <div>\n <h1 class=\"text-xl font-semibold text-content\">Script</h1>\n <p class=\"text-xs text-content-tertiary font-mono\">{{ script._id }}</p>\n </div>\n <div class=\"flex items-center\">\n <button\n v-if=\"!isEditing\"\n class=\"px-3 py-1.5 mr-2 text-sm bg-gray-700 text-white rounded hover:bg-gray-600\"\n @click=\"startEditing\">\n Edit\n </button>\n <div class=\"relative flex items-center\">\n <async-button\n class=\"px-3 py-1.5 text-sm bg-primary hover:bg-primary-hover text-primary-text rounded-l-md\"\n @click=\"executeScript\">\n {{ selectedRunLabel }}\n </async-button>\n <button\n class=\"px-1.5 py-1.5 text-sm bg-primary hover:bg-primary-hover text-primary-text border-l border-black/10 rounded-r-md\"\n @click.stop=\"showRunDropdown = !showRunDropdown\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-5 w-5\" 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 <div\n v-if=\"showRunDropdown\"\n class=\"absolute right-0 top-full z-10 mt-1 w-40 rounded-md bg-surface py-1 shadow-lg ring-1 ring-black/5\">\n <button class=\"block w-full px-4 py-2 text-left text-xs text-content-secondary hover:bg-muted\" @click=\"selectRunMode('run')\">\n Run\n </button>\n <button\n class=\"block w-full px-4 py-2 text-left text-xs\"\n :class=\"canUseDryRun ? 'text-content-secondary hover:bg-muted' : 'cursor-not-allowed text-gray-400'\"\n :disabled=\"!canUseDryRun\"\n :title=\"canUseDryRun ? null : 'Dry run mode requires MongoDB transaction support'\"\n @click=\"selectRunMode('dryRun')\">\n Dry Run\n </button>\n </div>\n </div>\n </div>\n </div>\n\n <div class=\"relative border rounded bg-surface 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 rounded-l-md border-r border-edge-strong font-semibold\"\n :class=\"activeTab === 'code' ? 'bg-gray-700 text-white shadow' : 'bg-muted text-gray-600'\"\n @click=\"activeTab = 'code'\">\n Code\n </button>\n <button\n class=\"px-4 py-1 rounded-r-md font-semibold\"\n :class=\"activeTab === 'output' ? 'bg-gray-700 text-white shadow' : 'bg-muted text-gray-600'\"\n @click=\"activeTab = 'output'\">\n Output<span v-if=\"isDryRunResult\" class=\"ml-1\">[Dry Run]</span>\n </button>\n </div>\n\n <div v-show=\"activeTab === 'code'\" class=\"max-h-[65vh] overflow-y-auto\">\n <div v-if=\"isEditing\" class=\"flex flex-col\">\n <ace-editor\n v-model=\"editedScript\"\n mode=\"javascript\"\n :line-numbers=\"true\"\n class=\"w-full h-[55vh] min-h-[300px]\"\n />\n <div class=\"flex justify-end p-2 border-t border-edge\">\n <button class=\"px-3 py-1.5 text-xs bg-gray-300 text-gray-800 rounded hover:bg-gray-400\" @click=\"cancelEditing\">\n Cancel\n </button>\n </div>\n </div>\n <pre v-else class=\"whitespace-pre-wrap !my-0 bg-muted\"><code ref=\"code\" class=\"language-javascript\" v-text=\"script.script\"></code></pre>\n </div>\n\n <div v-show=\"activeTab === 'output'\" class=\"p-3 max-h-[65vh] overflow-y-auto border-t\">\n <div v-if=\"script.executionResult?.error\" class=\"rounded-md bg-red-50 p-4 mb-3 text-sm text-red-800\">\n {{ script.executionResult.error }}\n </div>\n <dashboard-result\n v-if=\"script.executionResult?.output != null\"\n :result=\"script.executionResult.output\">\n </dashboard-result>\n <pre v-else-if=\"!script.executionResult?.error\">No output</pre>\n\n <div v-if=\"script.executionResult?.logs\" 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]\">{{ script.executionResult.logs }}</pre>\n </div>\n </div>\n </div>\n </div>\n\n <div v-else class=\"max-w-5xl mx-auto text-content-secondary\">\n No script with the given id could be found.\n </div>\n</div>\n";
55057
+
55058
+ /***/ },
55059
+
54667
55060
  /***/ "./frontend/src/splash/splash.html"
54668
55061
  /*!*****************************************!*\
54669
55062
  !*** ./frontend/src/splash/splash.html ***!
@@ -60234,7 +60627,7 @@ exports.validatorErrorSymbol = Symbol('mongoose#validatorError');
60234
60627
 
60235
60628
 
60236
60629
 
60237
- const ObjectId = (__webpack_require__(/*! mongodb/lib/bson */ "./node_modules/mongodb/lib/bson.js").ObjectId);
60630
+ const ObjectId = (__webpack_require__(/*! mongodb/lib/bson */ "./node_modules/mongoose/node_modules/mongodb/lib/bson.js").ObjectId);
60238
60631
  const objectIdSymbol = (__webpack_require__(/*! ../helpers/symbols */ "./node_modules/mongoose/lib/helpers/symbols.js").objectIdSymbol);
60239
60632
 
60240
60633
  /**
@@ -66262,7 +66655,7 @@ var src_default = VueToastificationPlugin;
66262
66655
  (module) {
66263
66656
 
66264
66657
  "use strict";
66265
- module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version":"0.5.14","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","mpath":"^0.9.0","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":{"@nestjs/common":"10.x || 11.x","mongoose":"7.x || 8.x || ^9.0.0"},"peerDependenciesMeta":{"@nestjs/common":{"optional":true}},"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","@nestjs/common":"^11.1.9","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","eval:agent":"node eval/agent/run.js"}}');
66658
+ module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version":"0.6.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","@modelcontextprotocol/sdk":"^1.30.1","ace-builds":"^1.43.6","ai":"5.x","archetype":"0.13.1","csv-stringify":"6.3.0","ejson":"^2.2.3","extrovert":"^0.2.1","marked":"15.0.12","mpath":"^0.9.0","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","zod":"^4.6.5"},"peerDependencies":{"@nestjs/common":"10.x || 11.x","mongoose":"7.x || 8.x || ^9.0.0"},"peerDependenciesMeta":{"@nestjs/common":{"optional":true}},"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","@nestjs/common":"^11.1.9","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","eval:agent":"node eval/agent/run.js"}}');
66266
66659
 
66267
66660
  /***/ }
66268
66661