@mongoosejs/studio 0.0.96 → 0.0.98

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.
@@ -16,49 +16,13 @@ const CreateChatMessageParams = new Archetype({
16
16
  $type: String
17
17
  },
18
18
  authorization: {
19
- $type: 'string',
20
- $required: true
19
+ $type: 'string'
21
20
  },
22
21
  roles: {
23
22
  $type: ['string']
24
23
  }
25
24
  }).compile('CreateChatMessageParams');
26
25
 
27
- const systemPrompt = `
28
- You are a data querying assistant who writes scripts for users accessing MongoDB data using Node.js and Mongoose.
29
-
30
- Keep scripts concise. Avoid unnecessary comments, error handling, and temporary variables.
31
-
32
- Do not write any imports or require() statements, that will cause the script to break.
33
-
34
- If the user approves the script, the script will run in the Node.js server and then send the response via JSON to the client. Be aware that the result of the query will be serialized to JSON before being displayed to the user.
35
-
36
- Assume the user has pre-defined schemas and models. Do not define any new schemas or models for the user.
37
-
38
- Use async/await where possible. Assume top-level await is allowed.
39
-
40
- Think carefully about the user's input and identify the models referred to by the user's query.
41
-
42
- Format output as Markdown, including code fences for any scripts the user requested.
43
-
44
- Add a brief text description of what the script does.
45
-
46
- If the user's query is best answered with a chart, return a Chart.js 4 configuration as \`return { $chart: chartJSConfig };\`. Disable ChartJS animation by default unless user asks for it. Set responsive: true, maintainAspectRatio: false options unless the user explicitly asks.
47
-
48
- Example output:
49
-
50
- The following script counts the number of users which are not deleted.
51
-
52
- \`\`\`javascript
53
- const users = await db.model('User').find({ isDeleted: false });
54
- return { numUsers: users.length };
55
- \`\`\`
56
-
57
- -----------
58
-
59
- Here is a description of the user's models. Assume these are the only models available in the system unless explicitly instructed otherwise by the user.
60
- `.trim();
61
-
62
26
  module.exports = ({ db, studioConnection, options }) => async function createChatMessage(params) {
63
27
  const { chatThreadId, userId, content, script, authorization, roles } = new CreateChatMessageParams(params);
64
28
  const ChatThread = studioConnection.model('__Studio_ChatThread');
@@ -83,20 +47,13 @@ module.exports = ({ db, studioConnection, options }) => async function createCha
83
47
  llmMessages.push({ role: 'user', content });
84
48
 
85
49
  if (chatThread.title == null) {
86
- getChatCompletion([
87
- { role: 'system', content: 'Summarize the following chat thread in 6 words or less, as a helpful thread title' },
88
- ...llmMessages
89
- ], authorization).then(res => {
50
+ summarizeChatThread(llmMessages).then(res => {
90
51
  const title = res.response;
91
52
  chatThread.title = title;
92
53
  return chatThread.save();
93
54
  }).catch(() => {});
94
55
  }
95
56
 
96
- llmMessages.unshift({
97
- role: 'system',
98
- content: systemPrompt + getModelDescriptions(db)
99
- });
100
57
  if (options.context) {
101
58
  llmMessages.unshift({
102
59
  role: 'system',
@@ -113,7 +70,7 @@ module.exports = ({ db, studioConnection, options }) => async function createCha
113
70
  script,
114
71
  executionResult: null
115
72
  }),
116
- getChatCompletion(llmMessages, authorization).then(res => {
73
+ createChatMessageCore(llmMessages, getModelDescriptions(db), authorization).then(res => {
117
74
  const content = res.response;
118
75
  return ChatMessage.create({
119
76
  chatThreadId,
@@ -126,16 +83,44 @@ module.exports = ({ db, studioConnection, options }) => async function createCha
126
83
  return { chatMessages, chatThread };
127
84
  };
128
85
 
129
- async function getChatCompletion(messages, authorization) {
130
- const response = await fetch('https://mongoose-js.netlify.app/.netlify/functions/createChatMessage', {
86
+ async function summarizeChatThread(messages, authorization) {
87
+ const headers = { 'Content-Type': 'application/json' };
88
+ if (authorization) {
89
+ headers.Authorization = authorization;
90
+ }
91
+ const response = await fetch('https://mongoose-js.netlify.app/.netlify/functions/summarizeChatThread', {
131
92
  method: 'POST',
132
- headers: {
133
- Authorization: authorization,
134
- 'Content-Type': 'application/json'
135
- },
93
+ headers,
136
94
  body: JSON.stringify({
137
95
  messages
138
96
  })
97
+ }).then(response => {
98
+ if (response.status < 200 || response.status >= 400) {
99
+ return response.json().then(data => {
100
+ throw new Error(`Mongoose Studio chat thread summarization error: ${data.message}`);
101
+ });
102
+ }
103
+ return response;
104
+ });
105
+
106
+ return await response.json().then(res => {
107
+ console.log('Response', res);
108
+ return res;
109
+ });
110
+ }
111
+
112
+ async function createChatMessageCore(messages, modelDescriptions, authorization) {
113
+ const headers = { 'Content-Type': 'application/json' };
114
+ if (authorization) {
115
+ headers.Authorization = authorization;
116
+ }
117
+ const response = await fetch('https://mongoose-js.netlify.app/.netlify/functions/createChatMessage', {
118
+ method: 'POST',
119
+ headers,
120
+ body: JSON.stringify({
121
+ messages,
122
+ modelDescriptions
123
+ })
139
124
  }).then(response => {
140
125
  if (response.status < 200 || response.status >= 400) {
141
126
  return response.json().then(data => {
@@ -500,6 +500,11 @@ module.exports = app => app.component('chat', {
500
500
  this.chatMessages.push(chatMessages[1]);
501
501
 
502
502
  this.newMessage = '';
503
+ this.$nextTick(() => {
504
+ if (this.$refs.messageInput) {
505
+ this.$refs.messageInput.style.height = 'auto';
506
+ }
507
+ });
503
508
 
504
509
  this.$nextTick(() => {
505
510
  if (this.$refs.messagesContainer) {
@@ -510,6 +515,18 @@ module.exports = app => app.component('chat', {
510
515
  this.sendingMessage = false;
511
516
  }
512
517
  },
518
+ handleEnter(ev) {
519
+ if (!ev.shiftKey) {
520
+ this.sendMessage();
521
+ }
522
+ },
523
+ adjustTextareaHeight(ev) {
524
+ const textarea = ev.target;
525
+ textarea.style.height = 'auto';
526
+ const lineHeight = parseInt(window.getComputedStyle(textarea).lineHeight, 10);
527
+ const maxHeight = lineHeight * 5;
528
+ textarea.style.height = Math.min(textarea.scrollHeight, maxHeight) + 'px';
529
+ },
513
530
  selectThread(threadId) {
514
531
  this.$router.push('/chat/' + threadId);
515
532
  },
@@ -1756,6 +1773,7 @@ if (typeof process === 'undefined') {
1756
1773
 
1757
1774
  const api = __webpack_require__(/*! ./api */ "./frontend/src/api.js");
1758
1775
  const mothership = __webpack_require__(/*! ./mothership */ "./frontend/src/mothership.js");
1776
+ const { routes } = __webpack_require__(/*! ./routes */ "./frontend/src/routes.js");
1759
1777
  const vanillatoasts = __webpack_require__(/*! vanillatoasts */ "./node_modules/vanillatoasts/vanillatoasts.js");
1760
1778
 
1761
1779
  const app = Vue.createApp({
@@ -1814,7 +1832,7 @@ app.component('app-component', {
1814
1832
  window.state = this;
1815
1833
 
1816
1834
  if (mothership.hasAPIKey) {
1817
- const hash = window.location.hash.replace(/^#?\/?\??/, '') || '';
1835
+ const hash = window.location.hash.replace(/^#?\/?/, '') || '';
1818
1836
  const hashQuery = hash.split('?')[1] || '';
1819
1837
  const hashParams = new URLSearchParams(hashQuery);
1820
1838
  if (hashParams.has('code')) {
@@ -1857,6 +1875,7 @@ app.component('app-component', {
1857
1875
  const { nodeEnv } = await api.status();
1858
1876
  this.nodeEnv = nodeEnv;
1859
1877
  }
1878
+
1860
1879
  this.status = 'loaded';
1861
1880
  },
1862
1881
  setup() {
@@ -1873,7 +1892,6 @@ app.component('app-component', {
1873
1892
  }
1874
1893
  });
1875
1894
 
1876
- const { routes } = __webpack_require__(/*! ./routes */ "./frontend/src/routes.js");
1877
1895
  const router = VueRouter.createRouter({
1878
1896
  history: VueRouter.createWebHashHistory(),
1879
1897
  routes: routes.map(route => ({
@@ -1883,6 +1901,14 @@ const router = VueRouter.createRouter({
1883
1901
  }))
1884
1902
  });
1885
1903
 
1904
+ router.beforeEach((to, from, next) => {
1905
+ if (to.name === 'root' && window.state.roles && window.state.roles[0] === 'dashboards') {
1906
+ return next({ name: 'dashboards' });
1907
+ } else {
1908
+ next();
1909
+ }
1910
+ });
1911
+
1886
1912
  app.use(router);
1887
1913
 
1888
1914
  app.mount('#content');
@@ -2693,14 +2719,6 @@ module.exports = app => app.component('navbar', {
2693
2719
  inject: ['state'],
2694
2720
  data: () => ({ showFlyout: false }),
2695
2721
  mounted: function() {
2696
- // Redirect to first allowed route if current route is not allowed
2697
- if (!this.hasAccess(this.roles, this.$route.name)) {
2698
- const firstAllowedRoute = this.allowedRoutes[0];
2699
- if (firstAllowedRoute) {
2700
- this.$router.push({ name: firstAllowedRoute.name });
2701
- }
2702
- }
2703
-
2704
2722
  const mobileMenuMask = document.querySelector('#mobile-menu-mask');
2705
2723
  const mobileMenu = document.querySelector('#mobile-menu');
2706
2724
 
@@ -2739,11 +2757,8 @@ module.exports = app => app.component('navbar', {
2739
2757
  canViewTeam() {
2740
2758
  return this.hasAccess(this.roles, 'team');
2741
2759
  },
2742
- allowedRoutes() {
2743
- return routes.filter(route => this.hasAccess(this.roles, route.name));
2744
- },
2745
2760
  defaultRoute() {
2746
- return this.allowedRoutes[0]?.name || 'dashboards';
2761
+ return this.roles && this.roles[0] === 'dashboards' ? 'dashboards' : 'root';
2747
2762
  }
2748
2763
  },
2749
2764
  methods: {
@@ -2804,10 +2819,12 @@ const roleAccess = {
2804
2819
  dashboards: ['dashboards', 'dashboard']
2805
2820
  };
2806
2821
 
2822
+ const allowedRoutesForLocalDev = ['document', 'root', 'chat'];
2823
+
2807
2824
  // Helper function to check if a role has access to a route
2808
2825
  function hasAccess(roles, routeName) {
2809
2826
  // change to true for local development
2810
- if (!roles) return false;
2827
+ if (!roles) return allowedRoutesForLocalDev.includes(routeName);
2811
2828
  return roles.some(role => roleAccess[role]?.includes(routeName));
2812
2829
  }
2813
2830
 
@@ -3897,7 +3914,7 @@ module.exports = "<button v-bind=\"attrsToBind\" :disabled=\"isDisabled\" @click
3897
3914
  /***/ ((module) => {
3898
3915
 
3899
3916
  "use strict";
3900
- module.exports = "<div class=\"relative border rounded bg-gray-100 text-black text-sm overflow-hidden\">\n <div class=\"flex border-b pt-[1px] text-xs font-medium bg-gray-200\">\n <button\n class=\"px-3 py-1 border-r border-gray-300 hover:bg-green-300\"\n :class=\"{'bg-gray-300': activeTab === 'code', 'bg-green-300': activeTab === 'code'}\"\n @click=\"activeTab = 'code'\">\n Code\n </button>\n <button\n class=\"px-3 py-1 hover:bg-green-300\"\n :class=\"{'bg-green-300': activeTab === 'output'}\"\n @click=\"activeTab = 'output'\">\n Output\n </button>\n <div class=\"ml-auto mr-1 flex\">\n <button\n v-if=\"activeTab === 'output'\"\n class=\"px-2 py-1 mr-1 text-xs bg-gray-500 text-white border-none rounded cursor-pointer hover:bg-gray-600 transition-colors flex items-center\"\n @click=\"copyOutput\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-3 w-3\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3\" />\n </svg>\n </button>\n <button\n v-if=\"activeTab === 'output'\"\n class=\"px-2 py-1 mr-1 text-xs bg-blue-500 text-white border-none rounded cursor-pointer hover:bg-blue-600 transition-colors flex items-center\"\n @click=\"openDetailModal\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-3 w-3\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 1v4m0 0h-4m4 0l-5-5\" />\n </svg>\n </button>\n <async-button\n class=\"px-2 py-1 text-xs bg-green-500 text-white border-none rounded cursor-pointer hover:bg-green-600 transition-colors disabled:bg-gray-400\"\n @click=\"executeScript(message, script)\">\n Execute\n </async-button>\n </div>\n </div>\n\n <pre class=\"p-3 whitespace-pre-wrap max-h-[30vh] max-w-[calc(100vw-4rem)] overflow-y-auto\" v-show=\"activeTab === 'code'\"><code v-text=\"script\" ref=\"code\" :class=\"'language-' + language\"></code></pre>\n\n <div class=\"p-3 whitespace-pre-wrap max-h-[30vh] overflow-y-auto bg-white border-t max-w-[calc(100vw-4rem)] relative\" v-show=\"activeTab === 'output'\">\n <dashboard-chart v-if=\"message.executionResult?.output?.$chart\" :value=\"message.executionResult?.output\" />\n <pre v-else>{{ message.executionResult?.output || 'No output' }}</pre>\n </div>\n\n <modal ref=\"outputModal\" v-if=\"showDetailModal\" containerClass=\"!h-[90vh] !w-[90vw]\">\n <template #body>\n <div class=\"absolute font-mono right-1 top-1 cursor-pointer text-xl\" @click=\"showDetailModal = false;\">&times;</div>\n <div class=\"h-full overflow-auto\">\n <dashboard-chart v-if=\"message.executionResult?.output?.$chart\" :value=\"message.executionResult?.output\" />\n <pre v-else class=\"whitespace-pre-wrap\">{{ message.executionResult?.output || 'No output' }}</pre>\n </div>\n </template>\n </modal>\n</div>\n";
3917
+ module.exports = "<div class=\"relative border rounded bg-gray-100 text-black text-sm overflow-hidden\">\n <div class=\"flex border-b pt-[1px] text-xs font-medium bg-gray-200\">\n <button\n class=\"px-3 py-1 border-r border-gray-300 hover:bg-green-300\"\n :class=\"{'bg-gray-300': activeTab === 'code', 'bg-green-300': activeTab === 'code'}\"\n @click=\"activeTab = 'code'\">\n Code\n </button>\n <button\n class=\"px-3 py-1 hover:bg-green-300\"\n :class=\"{'bg-green-300': activeTab === 'output'}\"\n @click=\"activeTab = 'output'\">\n Output\n </button>\n <div class=\"ml-auto mr-1 flex\">\n <button\n v-if=\"activeTab === 'output'\"\n class=\"px-2 py-1 mr-1 text-xs bg-gray-500 text-white border-none rounded cursor-pointer hover:bg-gray-600 transition-colors flex items-center\"\n @click=\"copyOutput\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-3 w-3\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3\" />\n </svg>\n </button>\n <button\n v-if=\"activeTab === 'output'\"\n class=\"px-2 py-1 mr-1 text-xs bg-blue-500 text-white border-none rounded cursor-pointer hover:bg-blue-600 transition-colors flex items-center\"\n @click=\"openDetailModal\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-3 w-3\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 1v4m0 0h-4m4 0l-5-5\" />\n </svg>\n </button>\n <async-button\n class=\"px-2 py-1 text-xs bg-green-500 text-white border-none rounded cursor-pointer hover:bg-green-600 transition-colors disabled:bg-gray-400\"\n @click=\"executeScript(message, script)\">\n Execute\n </async-button>\n </div>\n </div>\n\n <pre class=\"p-3 whitespace-pre-wrap max-h-[50vh] max-w-[calc(100vw-4rem)] lg:max-w-[calc(100vw-20rem)] overflow-y-auto\" v-show=\"activeTab === 'code'\"><code v-text=\"script\" ref=\"code\" :class=\"'language-' + language\"></code></pre>\n\n <div class=\"p-3 whitespace-pre-wrap max-h-[50vh] overflow-y-auto bg-white border-t max-w-[calc(100vw-4rem)] lg:max-w-[calc(100vw-20rem)] relative\" v-show=\"activeTab === 'output'\">\n <dashboard-chart v-if=\"message.executionResult?.output?.$chart\" :value=\"message.executionResult?.output\" />\n <pre v-else>{{ message.executionResult?.output || 'No output' }}</pre>\n </div>\n\n <modal ref=\"outputModal\" v-if=\"showDetailModal\" containerClass=\"!h-[90vh] !w-[90vw]\">\n <template #body>\n <div class=\"absolute font-mono right-1 top-1 cursor-pointer text-xl\" @click=\"showDetailModal = false;\">&times;</div>\n <div class=\"h-full overflow-auto\">\n <dashboard-chart v-if=\"message.executionResult?.output?.$chart\" :value=\"message.executionResult?.output\" />\n <pre v-else class=\"whitespace-pre-wrap\">{{ message.executionResult?.output || 'No output' }}</pre>\n </div>\n </template>\n </modal>\n</div>\n";
3901
3918
 
3902
3919
  /***/ }),
3903
3920
 
@@ -3908,7 +3925,7 @@ module.exports = "<div class=\"relative border rounded bg-gray-100 text-black te
3908
3925
  /***/ ((module) => {
3909
3926
 
3910
3927
  "use strict";
3911
- module.exports = "<div class=\"relative flex items-start space-x-3\" :class=\"{'justify-end': message.role === 'user'}\">\n <div\n class=\"min-w-0 max-w-[calc(100vw-4rem)]\"\n :class=\"{'text-right': message.role === 'user'}\">\n\n <div class=\"text-sm text-gray-900 p-3 rounded-md inline-block\" :class=\"styleForMessage\">\n <div v-for=\"part in contentSplitByScripts\">\n <div v-if=\"part.type === 'text'\" v-html=\"marked(part.content)\">\n </div>\n <div v-else-if=\"part.type === 'code'\">\n <chat-message-script :message=\"message\" :script=\"part.content\" :language=\"part.language\"></chat-message-script>\n </div>\n </div>\n </div>\n </div>\n</div>\n";
3928
+ module.exports = "<div class=\"relative flex items-start\" :class=\"{'justify-end': message.role === 'user'}\">\n <div\n class=\"min-w-0 max-w-[calc(100vw-4rem)] lg:max-w-[calc(100vw-20rem)]\"\n :class=\"{'text-right': message.role === 'user'}\">\n\n <div class=\"text-sm text-gray-900 md:p-3 rounded-md inline-block\" :class=\"styleForMessage\">\n <div v-for=\"part in contentSplitByScripts\">\n <div v-if=\"part.type === 'text'\" v-html=\"marked(part.content)\">\n </div>\n <div v-else-if=\"part.type === 'code'\">\n <chat-message-script :message=\"message\" :script=\"part.content\" :language=\"part.language\"></chat-message-script>\n </div>\n </div>\n </div>\n </div>\n</div>\n";
3912
3929
 
3913
3930
  /***/ }),
3914
3931
 
@@ -3919,7 +3936,7 @@ module.exports = "<div class=\"relative flex items-start space-x-3\" :class=\"{'
3919
3936
  /***/ ((module) => {
3920
3937
 
3921
3938
  "use strict";
3922
- module.exports = "<div class=\"flex\" style=\"height: calc(100vh - 55px)\">\n <div class=\"fixed top-[65px] cursor-pointer bg-gray-100 rounded-r-md z-10\" @click=\"hideSidebar = false\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" style=\"h-5 w-5\" viewBox=\"0 -960 960 960\" width=\"24px\" fill=\"#5f6368\"><path d=\"M360-120v-720h80v720h-80Zm160-160v-400l200 200-200 200Z\"/></svg>\n </div>\n <!-- Sidebar: Chat Threads -->\n <aside class=\"bg-gray-50 border-r overflow-y-auto overflow-x-hidden h-full transition-all duration-300 ease-in-out z-20 w-0 md:w-64 fixed md:relative\" :class=\"hideSidebar === true ? '!w-0' : hideSidebar === false ? '!w-64' : ''\">\n <div class=\"flex items-center border-b border-gray-100 w-64 overflow-x-hidden\">\n <div class=\"p-4 font-bold text-lg\">Chat Threads</div>\n <button\n @click=\"hideSidebar = true\"\n class=\"ml-auto mr-2 p-2 rounded hover:bg-gray-200 focus:outline-none\"\n aria-label=\"Close sidebar\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" style=\"h-5 w-5\" viewBox=\"0 -960 960 960\" width=\"24px\" fill=\"currentColor\"><path d=\"M660-320v-320L500-480l160 160ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm120-80v-560H200v560h120Zm80 0h360v-560H400v560Zm-80 0H200h120Z\"/></svg>\n </button>\n </div>\n <div class=\"p-4 w-64\">\n <async-button\n @click=\"createNewThread\"\n class=\"w-full bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700\"\n >\n Create New Thread\n </async-button>\n </div>\n <div v-if=\"status === 'loaded' && chatThreads.length === 0\" class=\"p-4 text-sm text-gray-700\">\n No threads yet\n </div>\n <ul v-if=\"status === 'loaded'\" class=\"w-64\">\n <li\n v-for=\"thread in chatThreads\"\n :key=\"thread._id\"\n @click=\"selectThread(thread._id)\"\n class=\"p-4 hover:bg-gray-200 cursor-pointer w-64\"\n :class=\"{ 'bg-gray-300': thread._id === chatThreadId }\"\n >\n {{ thread.title || 'Untitled Thread' }}\n </li>\n </ul>\n </aside>\n\n <!-- Main Chat Area -->\n <main class=\"flex-1 flex flex-col\">\n <div class=\"flex-1 overflow-y-auto p-6 space-y-4\" ref=\"messagesContainer\">\n <ul role=\"list\" class=\"space-y-4\">\n <div v-if=\"true\">\n <div class=\"flex items-center justify-center py-3 mb-4\">\n <div class=\"bg-gray-300 h-px flex-grow max-w-xs\"></div>\n <p class=\"mx-4 text-sm font-medium text-gray-500\">This is the beginning of the message thread</p>\n <div class=\"bg-gray-300 h-px flex-grow max-w-xs\"></div>\n </div>\n </div>\n <li v-for=\"message in chatMessages\" :key=\"message._id\">\n <chat-message :message=\"message\"></chat-message>\n </li>\n </ul>\n </div>\n\n\n <!-- Input Area -->\n <div class=\"border-t p-4\">\n <form @submit.prevent=\"sendMessage\" :disabled=\"sendingMessage\" class=\"flex gap-2\">\n <input\n v-model=\"newMessage\"\n placeholder=\"Ask something...\"\n class=\"flex-1 border rounded px-4 py-2\"\n />\n <button class=\"bg-blue-600 text-white px-4 py-2 rounded disabled:bg-gray-600\" :disabled=\"sendingMessage\">\n <svg v-if=\"sendingMessage\" style=\"height: 1em\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <g>\n <circle cx=\"12\" cy=\"12\" r=\"10\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" opacity=\"0.3\" />\n <path d=\"M12 2a10 10 0 0 1 10 10\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\">\n <animateTransform attributeName=\"transform\" type=\"rotate\" from=\"0 12 12\" to=\"360 12 12\" dur=\"1s\" repeatCount=\"indefinite\" />\n </path>\n </g>\n </svg>\n <span v-else>Send</span>\n </button>\n </form>\n </div>\n </main>\n</div>\n";
3939
+ module.exports = "<div class=\"flex\" style=\"height: calc(100vh - 55px)\">\n <div class=\"fixed top-[65px] cursor-pointer bg-gray-100 rounded-r-md z-10\" @click=\"hideSidebar = false\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" style=\"h-5 w-5\" viewBox=\"0 -960 960 960\" class=\"w-5\" fill=\"#5f6368\"><path d=\"M360-120v-720h80v720h-80Zm160-160v-400l200 200-200 200Z\"/></svg>\n </div>\n <!-- Sidebar: Chat Threads -->\n <aside class=\"bg-gray-50 border-r overflow-y-auto overflow-x-hidden h-full transition-all duration-300 ease-in-out z-20 w-0 lg:w-64 fixed lg:relative\" :class=\"hideSidebar === true ? '!w-0' : hideSidebar === false ? '!w-64' : ''\">\n <div class=\"flex items-center border-b border-gray-100 w-64 overflow-x-hidden\">\n <div class=\"p-4 font-bold text-lg\">Chat Threads</div>\n <button\n @click=\"hideSidebar = true\"\n class=\"ml-auto mr-2 p-2 rounded hover:bg-gray-200 focus:outline-none\"\n aria-label=\"Close sidebar\"\n >\n <svg xmlns=\"http://www.w3.org/2000/svg\" style=\"h-5 w-5\" viewBox=\"0 -960 960 960\" class=\"w-5\" fill=\"currentColor\"><path d=\"M660-320v-320L500-480l160 160ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm120-80v-560H200v560h120Zm80 0h360v-560H400v560Zm-80 0H200h120Z\"/></svg>\n </button>\n </div>\n <div class=\"p-4 w-64\">\n <async-button\n @click=\"createNewThread\"\n class=\"w-full bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700\"\n >\n Create New Thread\n </async-button>\n </div>\n <div v-if=\"status === 'loaded' && chatThreads.length === 0\" class=\"p-4 text-sm text-gray-700\">\n No threads yet\n </div>\n <ul v-if=\"status === 'loaded'\" class=\"w-64\">\n <li\n v-for=\"thread in chatThreads\"\n :key=\"thread._id\"\n @click=\"selectThread(thread._id)\"\n class=\"p-4 hover:bg-gray-200 cursor-pointer w-64\"\n :class=\"{ 'bg-gray-300': thread._id === chatThreadId }\"\n >\n {{ thread.title || 'Untitled Thread' }}\n </li>\n </ul>\n </aside>\n\n <!-- Main Chat Area -->\n <main class=\"flex-1 flex flex-col\">\n <div class=\"flex-1 overflow-y-auto p-6 space-y-4\" ref=\"messagesContainer\">\n <ul role=\"list\" class=\"space-y-4\">\n <div v-if=\"true\">\n <div class=\"flex items-center justify-center py-3 mb-4\">\n <div class=\"bg-gray-300 h-px flex-grow max-w-xs\"></div>\n <p class=\"mx-4 text-sm font-medium text-gray-500\">This is the beginning of the message thread</p>\n <div class=\"bg-gray-300 h-px flex-grow max-w-xs\"></div>\n </div>\n </div>\n <li v-for=\"message in chatMessages\" :key=\"message._id\">\n <chat-message :message=\"message\"></chat-message>\n </li>\n </ul>\n </div>\n\n\n <!-- Input Area -->\n <div class=\"border-t p-4\">\n <form @submit.prevent=\"sendMessage\" :disabled=\"sendingMessage\" class=\"flex gap-2 items-end justify-end\">\n <textarea\n v-model=\"newMessage\"\n placeholder=\"Ask something...\"\n class=\"flex-1 border rounded px-4 py-2 resize-none overflow-y-auto\"\n rows=\"1\"\n ref=\"messageInput\"\n @input=\"adjustTextareaHeight\"\n @keydown.enter.exact.prevent=\"handleEnter\"\n ></textarea>\n <button class=\"bg-blue-600 text-white px-4 h-[42px] rounded disabled:bg-gray-600\" :disabled=\"sendingMessage\">\n <svg v-if=\"sendingMessage\" style=\"height: 1em\" viewBox=\"0 0 24 24\" xmlns=\"http://www.w3.org/2000/svg\">\n <g>\n <circle cx=\"12\" cy=\"12\" r=\"10\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" opacity=\"0.3\" />\n <path d=\"M12 2a10 10 0 0 1 10 10\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\">\n <animateTransform attributeName=\"transform\" type=\"rotate\" from=\"0 12 12\" to=\"360 12 12\" dur=\"1s\" repeatCount=\"indefinite\" />\n </path>\n </g>\n </svg>\n <span v-else>Send</span>\n </button>\n </form>\n </div>\n </main>\n</div>\n";
3923
3940
 
3924
3941
  /***/ }),
3925
3942
 
@@ -4172,7 +4189,7 @@ module.exports = ".document {\n max-width: 1200px;\n margin-left: auto;\n mar
4172
4189
  /***/ ((module) => {
4173
4190
 
4174
4191
  "use strict";
4175
- module.exports = "<div class=\"document\">\n <div class=\"document-menu\">\n <div class=\"left\">\n <button\n @click=\"$router.push('/model/' + this.model)\"\n class=\"rounded-md bg-gray-400 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-slate-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-slate-600\">\n &lsaquo; Back\n </button>\n </div>\n\n <div class=\"right\">\n <button\n v-if=\"!editting\"\n @click=\"editting = true\"\n :disabled=\"!canManipulate\"\n :class=\"{'cursor-not-allowed opacity-50': !canManipulate}\"\n type=\"button\"\n class=\"rounded-md bg-ultramarine-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600\">\n <img src=\"images/edit.svg\" class=\"inline\" /> Edit\n </button>\n <button\n v-if=\"editting\"\n @click=\"editting = false\"\n type=\"button\"\n class=\"rounded-md bg-slate-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-slate-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-slate-600\">\n &times; Cancel\n </button>\n <button\n v-if=\"editting\"\n :disabled=\"!canManipulate\"\n :class=\"{'cursor-not-allowed opacity-50': !canManipulate}\"\n @click=\"shouldShowConfirmModal=true;\"\n type=\"button\"\n class=\"rounded-md bg-forest-green-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600\">\n <img src=\"images/save.svg\" class=\"inline\" /> Save\n </button>\n <button\n @click=\"shouldShowDeleteModal=true;\"\n :disabled=\"!canManipulate\"\n :class=\"{'cursor-not-allowed opacity-50': !canManipulate}\"\n type=\"button\"\n class=\"rounded-md bg-valencia-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-valencia-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600\">\n <img src=\"images/delete.svg\" class=\"inline\" /> Delete\n </button>\n <button\n @click=\"shouldShowCloneModal=true;\"\n :disabled=\"!canManipulate\"\n :class=\"{'cursor-not-allowed opacity-50': !canManipulate}\"\n type=\"button\"\n class=\"rounded-md bg-pink-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-valencia-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600\">\n <img src=\"images/duplicate.svg\" class=\"inline\" /> Clone\n </button>\n </div>\n </div>\n <div v-if=\"status === 'loaded'\">\n <document-details\n :document=\"document\"\n :schemaPaths=\"schemaPaths\"\n :editting=\"editting\"\n :changes=\"changes\"\n :invalid=\"invalid\"></document-details>\n <modal v-if=\"shouldShowConfirmModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowConfirmModal = false;\">&times;</div>\n <confirm-changes @close=\"shouldShowConfirmModal = false;\" @save=\"save\" :value=\"changes\"></confirm-changes>\n </template>\n </modal>\n <modal v-if=\"shouldShowDeleteModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowConfirmModal = false;\">&times;</div>\n <confirm-delete @close=\"shouldShowConfirmModal = false;\" @remove=\"remove\" :value=\"document\"></confirm-delete>\n </template>\n </modal>\n <modal v-if=\"shouldShowCloneModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowCloneModal = false;\">&times;</div>\n <clone-document :currentModel=\"model\" :doc=\"document\" :schemaPaths=\"schemaPaths\" @close=\"showClonedDocument\"></clone-document>\n </template>\n </modal>\n </div>\n</div>\n";
4192
+ module.exports = "<div class=\"document\">\n <div class=\"document-menu\">\n <div class=\"left\">\n <button\n @click=\"$router.push('/model/' + this.model)\"\n class=\"rounded-md bg-gray-400 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-slate-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-slate-600\">\n &lsaquo; Back\n </button>\n </div>\n\n <div class=\"right\">\n <button\n v-if=\"!editting\"\n @click=\"editting = true\"\n :disabled=\"!canManipulate\"\n :class=\"{'cursor-not-allowed opacity-50': !canManipulate}\"\n type=\"button\"\n class=\"rounded-md bg-ultramarine-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600\">\n <img src=\"images/edit.svg\" class=\"inline\" /> Edit\n </button>\n <button\n v-if=\"editting\"\n @click=\"editting = false\"\n type=\"button\"\n class=\"rounded-md bg-slate-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-slate-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-slate-600\">\n &times; Cancel\n </button>\n <button\n v-if=\"editting\"\n :disabled=\"!canManipulate\"\n :class=\"{'cursor-not-allowed opacity-50': !canManipulate}\"\n @click=\"shouldShowConfirmModal=true;\"\n type=\"button\"\n class=\"rounded-md bg-forest-green-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-green-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600\">\n <img src=\"images/save.svg\" class=\"inline\" /> Save\n </button>\n <button\n @click=\"shouldShowDeleteModal=true;\"\n :disabled=\"!canManipulate\"\n :class=\"{'cursor-not-allowed opacity-50': !canManipulate}\"\n type=\"button\"\n class=\"rounded-md bg-valencia-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-valencia-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600\">\n <img src=\"images/delete.svg\" class=\"inline\" /> Delete\n </button>\n <button\n @click=\"shouldShowCloneModal=true;\"\n :disabled=\"!canManipulate\"\n :class=\"{'cursor-not-allowed opacity-50': !canManipulate}\"\n type=\"button\"\n class=\"rounded-md bg-pink-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-valencia-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600\">\n <img src=\"images/duplicate.svg\" class=\"inline\" /> Clone\n </button>\n </div>\n </div>\n <div v-if=\"status === 'loaded'\">\n <document-details\n :document=\"document\"\n :schemaPaths=\"schemaPaths\"\n :editting=\"editting\"\n :changes=\"changes\"\n :invalid=\"invalid\"></document-details>\n <modal v-if=\"shouldShowConfirmModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowConfirmModal = false;\">&times;</div>\n <confirm-changes @close=\"shouldShowConfirmModal = false;\" @save=\"save\" :value=\"changes\"></confirm-changes>\n </template>\n </modal>\n <modal v-if=\"shouldShowDeleteModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowDeleteModal = false;\">&times;</div>\n <confirm-delete @close=\"shouldShowDeleteModal = false;\" @remove=\"remove\" :value=\"document\"></confirm-delete>\n </template>\n </modal>\n <modal v-if=\"shouldShowCloneModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowCloneModal = false;\">&times;</div>\n <clone-document :currentModel=\"model\" :doc=\"document\" :schemaPaths=\"schemaPaths\" @close=\"showClonedDocument\"></clone-document>\n </template>\n </modal>\n </div>\n</div>\n";
4176
4193
 
4177
4194
  /***/ }),
4178
4195
 
@@ -4458,7 +4475,7 @@ module.exports = ".active {\n text-decoration: underline;\n}\n\n.navbar .nav-le
4458
4475
  /***/ ((module) => {
4459
4476
 
4460
4477
  "use strict";
4461
- module.exports = "<div class=\"navbar w-full bg-gray-50 flex justify-between border-b border-gray-200 !h-[55px]\">\n <div class=\"flex items-center gap-4 h-full pl-4\">\n <router-link :to=\"{ name: defaultRoute }\">\n <img src=\"images/logo.svg\" class=\"h-[32px] mr-1\" alt=\"Mongoose Studio Logo\" />\n </router-link>\n <div v-if=\"!!state.nodeEnv\" class=\"inline-flex items-center rounded-md px-2 py-1 text-sm font-medium text-gray-900\" :class=\"warnEnv ? 'bg-red-300' : 'bg-yellow-300'\">\n {{state.nodeEnv}}\n </div>\n </div>\n <div class=\"h-full pr-4 hidden md:block\">\n <div class=\"sm:ml-6 sm:flex sm:space-x-8 h-full\">\n <a v-if=\"hasAccess(roles, 'root')\"\n href=\"#/\"\n class=\"inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium\"\n :class=\"documentView ? 'text-gray-900 border-ultramarine-500' : 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700'\">Documents</a>\n <a v-if=\"hasAccess(roles, 'dashboards')\"\n href=\"#/dashboards\"\n class=\"inline-flex items-center border-b-2 px-1 pt-1 text-sm font-medium\"\n :class=\"dashboardView ? 'text-gray-900 border-ultramarine-500' : 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700'\">Dashboards</a>\n <a v-if=\"hasAccess(roles, 'chat')\"\n href=\"#/chat\"\n class=\"inline-flex items-center border-b-2 px-1 pt-1 text-sm font-medium\"\n :class=\"chatView ? 'text-gray-900 border-ultramarine-500' : 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700'\">Chat</a>\n\n <div class=\"h-full flex items-center\" v-if=\"!user && hasAPIKey\">\n <button\n type=\"button\"\n @click=\"loginWithGithub\"\n class=\"rounded bg-ultramarine-600 px-2 py-2 text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600\">\n Login\n </button>\n </div>\n <div v-if=\"user && hasAPIKey\" class=\"h-full flex items-center relative\" v-clickOutside=\"hideFlyout\">\n <div>\n <button type=\"button\" @click=\"showFlyout = !showFlyout\" class=\"relative flex rounded-full bg-gray-800 text-sm focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800\" 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 </div>\n\n <div v-if=\"showFlyout\" class=\"absolute right-0 z-10 top-[90%] w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black/5 focus:outline-none\" role=\"menu\" aria-orientation=\"vertical\" aria-labelledby=\"user-menu-button\" tabindex=\"-1\">\n <router-link to=\"/team\" v-if=\"hasAccess(roles, 'team')\" @click=\"showFlyout = false\" class=\"cursor-pointer block px-4 py-2 text-sm text-gray-700 hover:bg-ultramarine-200\" role=\"menuitem\" tabindex=\"-1\" id=\"user-menu-item-2\">Team</router-link>\n <span @click=\"logout\" class=\"cursor-pointer block px-4 py-2 text-sm text-gray-700 hover:bg-ultramarine-200\" role=\"menuitem\" tabindex=\"-1\" id=\"user-menu-item-2\">Sign out</span>\n </div>\n </div>\n\n </div>\n </div>\n <div class=\"md:hidden flex items-center\">\n <!-- Mobile menu toggle, controls the 'mobileMenuOpen' state. -->\n <button type=\"button\" id=\"open-mobile-menu\" class=\"-ml-2 rounded-md p-2 pr-4 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\" class=\"fixed inset-0 bg-white shadow-lg z-50 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-gray-200\">\n <router-link :to=\"{ name: defaultRoute }\">\n <img src=\"images/logo.svg\" class=\"h-[32px]\" alt=\"Mongoose Studio Logo\" />\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 <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-ultramarine-700 bg-ultramarine-100' : 'text-gray-700 hover:bg-gray-100'\">Documents</a>\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-ultramarine-700 bg-ultramarine-100' : 'text-gray-700 hover:bg-gray-100'\">Dashboards</a>\n <a v-if=\"hasAccess(roles, 'chat')\"\n href=\"#/chat\"\n class=\"block px-3 py-2 rounded-md text-base font-medium\"\n :class=\"chatView ? 'text-ultramarine-700 bg-ultramarine-100' : 'text-gray-700 hover:bg-gray-100'\">Chat</a>\n <div v-if=\"!user && hasAPIKey\" class=\"mt-4\">\n <button\n type=\"button\"\n @click=\"loginWithGithub\"\n class=\"w-full rounded bg-ultramarine-600 px-3 py-2 text-base font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600\">\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-gray-50 rounded-md\">\n <img class=\"size-8 rounded-full\" :src=\"user.picture\" alt=\"\">\n <span class=\"text-gray-900 font-medium\">{{ user.name }}</span>\n </div>\n <div class=\"mt-2 space-y-1\">\n <router-link to=\"/team\" v-if=\"hasAccess(roles, 'team')\" class=\"block px-3 py-2 rounded-md text-base text-gray-700 hover:bg-ultramarine-100\">Team</router-link>\n <span @click=\"logout\" class=\"block px-3 py-2 rounded-md text-base text-gray-700 hover:bg-ultramarine-100 cursor-pointer\">Sign out</span>\n </div>\n </div>\n </nav>\n </div>\n</div>\n";
4478
+ module.exports = "<div class=\"navbar w-full bg-gray-50 flex justify-between border-b border-gray-200 !h-[55px]\">\n <div class=\"flex items-center gap-4 h-full pl-4\">\n <router-link :to=\"{ name: defaultRoute }\">\n <img src=\"images/logo.svg\" class=\"h-[32px] mr-1\" alt=\"Mongoose Studio Logo\" />\n </router-link>\n <div v-if=\"!!state.nodeEnv\" title=\"NODE_ENV\" class=\"inline-flex items-center rounded-md px-2 py-1 text-sm font-medium text-gray-900\" :class=\"warnEnv ? 'bg-red-300' : 'bg-yellow-300'\">\n {{state.nodeEnv}}\n </div>\n </div>\n <div class=\"h-full pr-4 hidden md:block\">\n <div class=\"sm:ml-6 sm:flex sm:space-x-8 h-full\">\n <a v-if=\"hasAccess(roles, 'root')\"\n href=\"#/\"\n class=\"inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium\"\n :class=\"documentView ? 'text-gray-900 border-ultramarine-500' : 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700'\">Documents</a>\n <span v-else class=\"inline-flex items-center px-1 pt-1 border-b-2 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 border-b-2 px-1 pt-1 text-sm font-medium\"\n :class=\"dashboardView ? 'text-gray-900 border-ultramarine-500' : 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700'\">Dashboards</a>\n <span v-else class=\"inline-flex items-center border-b-2 px-1 pt-1 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')\"\n href=\"#/chat\"\n class=\"inline-flex items-center border-b-2 px-1 pt-1 text-sm font-medium\"\n :class=\"chatView ? 'text-gray-900 border-ultramarine-500' : 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700'\">Chat</a>\n <span v-else class=\"inline-flex items-center border-b-2 px-1 pt-1 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\n <div class=\"h-full flex items-center\" v-if=\"!user && hasAPIKey\">\n <button\n type=\"button\"\n @click=\"loginWithGithub\"\n class=\"rounded bg-ultramarine-600 px-2 py-2 text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600\">\n Login\n </button>\n </div>\n <div v-if=\"user && hasAPIKey\" class=\"h-full flex items-center relative\" v-clickOutside=\"hideFlyout\">\n <div>\n <button type=\"button\" @click=\"showFlyout = !showFlyout\" class=\"relative flex rounded-full bg-gray-800 text-sm focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800\" 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 </div>\n\n <div v-if=\"showFlyout\" class=\"absolute right-0 z-10 top-[90%] w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black/5 focus:outline-none\" role=\"menu\" aria-orientation=\"vertical\" aria-labelledby=\"user-menu-button\" tabindex=\"-1\">\n <router-link to=\"/team\" v-if=\"hasAccess(roles, 'team')\" @click=\"showFlyout = false\" class=\"cursor-pointer block px-4 py-2 text-sm text-gray-700 hover:bg-ultramarine-200\" 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-gray-700 hover:bg-ultramarine-200\" role=\"menuitem\" tabindex=\"-1\" id=\"user-menu-item-2\">Sign out</span>\n </div>\n </div>\n\n </div>\n </div>\n <div class=\"md:hidden flex items-center\">\n <!-- Mobile menu toggle, controls the 'mobileMenuOpen' state. -->\n <button type=\"button\" id=\"open-mobile-menu\" class=\"-ml-2 rounded-md p-2 pr-4 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\" class=\"fixed inset-0 bg-white shadow-lg z-50 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-gray-200\">\n <router-link :to=\"{ name: defaultRoute }\">\n <img src=\"images/logo.svg\" class=\"h-[32px]\" alt=\"Mongoose Studio Logo\" />\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 <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-ultramarine-700 bg-ultramarine-100' : 'text-gray-700 hover:bg-gray-100'\">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-ultramarine-700 bg-ultramarine-100' : 'text-gray-700 hover:bg-gray-100'\">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')\"\n href=\"#/chat\"\n class=\"block px-3 py-2 rounded-md text-base font-medium\"\n :class=\"chatView ? 'text-ultramarine-700 bg-ultramarine-100' : 'text-gray-700 hover:bg-gray-100'\">Chat</a>\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 <div v-if=\"!user && hasAPIKey\" class=\"mt-4\">\n <button\n type=\"button\"\n @click=\"loginWithGithub\"\n class=\"w-full rounded bg-ultramarine-600 px-3 py-2 text-base font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600\">\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-gray-50 rounded-md\">\n <img class=\"size-8 rounded-full\" :src=\"user.picture\" alt=\"\">\n <span class=\"text-gray-900 font-medium\">{{ user.name }}</span>\n </div>\n <div class=\"mt-2 space-y-1\">\n <router-link to=\"/team\" v-if=\"hasAccess(roles, 'team')\" class=\"block px-3 py-2 rounded-md text-base text-gray-700 hover:bg-ultramarine-100\">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-gray-700 hover:bg-ultramarine-100 cursor-pointer\">Sign out</span>\n </div>\n </div>\n </nav>\n </div>\n</div>\n";
4462
4479
 
4463
4480
  /***/ }),
4464
4481