@mongoosejs/studio 0.0.136 → 0.0.138

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.
@@ -38,6 +38,7 @@ const QUERY_SELECTORS = [
38
38
  appendCSS(require('./models.css'));
39
39
 
40
40
  const limit = 20;
41
+ const OUTPUT_TYPE_STORAGE_KEY = 'studio:model-output-type';
41
42
 
42
43
  module.exports = app => app.component('models', {
43
44
  template: template,
@@ -80,6 +81,7 @@ module.exports = app => app.component('models', {
80
81
  created() {
81
82
  this.currentModel = this.model;
82
83
  this.buildAutocompleteTrie();
84
+ this.loadOutputPreference();
83
85
  },
84
86
  beforeDestroy() {
85
87
  document.removeEventListener('scroll', this.onScroll, true);
@@ -108,6 +110,24 @@ module.exports = app => app.component('models', {
108
110
  this.autocompleteTrie.bulkInsert(paths, 10);
109
111
  }
110
112
  },
113
+ loadOutputPreference() {
114
+ if (typeof window === 'undefined' || !window.localStorage) {
115
+ return;
116
+ }
117
+ const storedPreference = window.localStorage.getItem(OUTPUT_TYPE_STORAGE_KEY);
118
+ if (storedPreference === 'json' || storedPreference === 'table') {
119
+ this.outputType = storedPreference;
120
+ }
121
+ },
122
+ setOutputType(type) {
123
+ if (type !== 'json' && type !== 'table') {
124
+ return;
125
+ }
126
+ this.outputType = type;
127
+ if (typeof window !== 'undefined' && window.localStorage) {
128
+ window.localStorage.setItem(OUTPUT_TYPE_STORAGE_KEY, type);
129
+ }
130
+ },
111
131
  buildDocumentFetchParams(options = {}) {
112
132
  const params = {
113
133
  model: this.currentModel,
@@ -547,41 +567,52 @@ module.exports = app => app.component('models', {
547
567
  },
548
568
  handleDocumentClick(document, event) {
549
569
  if (this.selectMultiple) {
550
- const documentIndex = this.documents.findIndex(doc => doc._id.toString() == document._id.toString());
551
- if (event?.shiftKey && this.selectedDocuments.length > 0) {
552
- const anchorIndex = this.lastSelectedIndex;
553
- if (anchorIndex != null && anchorIndex !== -1 && documentIndex !== -1) {
554
- const start = Math.min(anchorIndex, documentIndex);
555
- const end = Math.max(anchorIndex, documentIndex);
556
- const selectedDocumentIds = new Set(this.selectedDocuments.map(doc => doc._id.toString()));
557
- for (let i = start; i <= end; i++) {
558
- const docInRange = this.documents[i];
559
- const existsInRange = selectedDocumentIds.has(docInRange._id.toString());
560
- if (!existsInRange) {
561
- this.selectedDocuments.push(docInRange);
562
- }
570
+ this.handleDocumentSelection(document, event);
571
+ } else {
572
+ this.openDocument(document);
573
+ }
574
+ },
575
+ handleDocumentContainerClick(document, event) {
576
+ if (this.selectMultiple) {
577
+ this.handleDocumentSelection(document, event);
578
+ }
579
+ },
580
+ handleDocumentSelection(document, event) {
581
+ const documentIndex = this.documents.findIndex(doc => doc._id.toString() == document._id.toString());
582
+ if (event?.shiftKey && this.selectedDocuments.length > 0) {
583
+ const anchorIndex = this.lastSelectedIndex;
584
+ if (anchorIndex != null && anchorIndex !== -1 && documentIndex !== -1) {
585
+ const start = Math.min(anchorIndex, documentIndex);
586
+ const end = Math.max(anchorIndex, documentIndex);
587
+ const selectedDocumentIds = new Set(this.selectedDocuments.map(doc => doc._id.toString()));
588
+ for (let i = start; i <= end; i++) {
589
+ const docInRange = this.documents[i];
590
+ const existsInRange = selectedDocumentIds.has(docInRange._id.toString());
591
+ if (!existsInRange) {
592
+ this.selectedDocuments.push(docInRange);
563
593
  }
564
- this.lastSelectedIndex = documentIndex;
565
- return;
566
594
  }
595
+ this.lastSelectedIndex = documentIndex;
596
+ return;
567
597
  }
568
- const index = this.selectedDocuments.findIndex(x => x._id.toString() == document._id.toString());
569
- if (index !== -1) {
570
- this.selectedDocuments.splice(index, 1);
571
- if (this.selectedDocuments.length === 0) {
572
- this.lastSelectedIndex = null;
573
- } else {
574
- const lastDoc = this.selectedDocuments[this.selectedDocuments.length - 1];
575
- this.lastSelectedIndex = this.documents.findIndex(doc => doc._id.toString() == lastDoc._id.toString());
576
- }
598
+ }
599
+ const index = this.selectedDocuments.findIndex(x => x._id.toString() == document._id.toString());
600
+ if (index !== -1) {
601
+ this.selectedDocuments.splice(index, 1);
602
+ if (this.selectedDocuments.length === 0) {
603
+ this.lastSelectedIndex = null;
577
604
  } else {
578
- this.selectedDocuments.push(document);
579
- this.lastSelectedIndex = documentIndex;
605
+ const lastDoc = this.selectedDocuments[this.selectedDocuments.length - 1];
606
+ this.lastSelectedIndex = this.documents.findIndex(doc => doc._id.toString() == lastDoc._id.toString());
580
607
  }
581
608
  } else {
582
- this.$router.push('/model/' + this.currentModel + '/document/' + document._id);
609
+ this.selectedDocuments.push(document);
610
+ this.lastSelectedIndex = documentIndex;
583
611
  }
584
612
  },
613
+ openDocument(document) {
614
+ this.$router.push('/model/' + this.currentModel + '/document/' + document._id);
615
+ },
585
616
  async deleteDocuments() {
586
617
  const documentIds = this.selectedDocuments.map(x => x._id);
587
618
  await api.Model.deleteDocuments({
package/next.js ADDED
@@ -0,0 +1,97 @@
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
6
+ module.exports = withMongooseStudio;
7
+
8
+ /**
9
+ * Copies Mongoose Studio frontend assets and injects rewrites.
10
+ *
11
+ * @param {object} nextConfig - Existing Next.js config
12
+ * @param {object} [options]
13
+ * @param {string} [options.studioPath="/studio"] - Public base path for Studio frontend
14
+ */
15
+ function withMongooseStudio(nextConfig = {}) {
16
+ const studioPath = normalizeBasePath(nextConfig.studioPath || '/studio');
17
+
18
+ return {
19
+ ...nextConfig,
20
+
21
+ async redirects() {
22
+ const userRedirects =
23
+ typeof nextConfig.redirects === 'function'
24
+ ? await nextConfig.redirects()
25
+ : nextConfig.redirects || [];
26
+
27
+ // Permanent redirect ensures browser URL is updated
28
+ const studioRedirect = {
29
+ source: studioPath,
30
+ destination: `${studioPath}/index.html`,
31
+ permanent: true,
32
+ };
33
+
34
+ return [...userRedirects, studioRedirect];
35
+ },
36
+
37
+ webpack(config, { isServer }) {
38
+ if (isServer) {
39
+ try {
40
+ copyStudioFrontend(studioPath);
41
+ console.log(`✅ Mongoose Studio: copied frontend to public${studioPath}`);
42
+ } catch (err) {
43
+ console.error('❌ Mongoose Studio: failed to copy frontend', err);
44
+ }
45
+ }
46
+
47
+ // Preserve user’s webpack config
48
+ if (typeof nextConfig.webpack === 'function') {
49
+ return nextConfig.webpack(config, { isServer });
50
+ }
51
+ return config;
52
+ },
53
+ };
54
+ }
55
+
56
+ /** Ensures path starts with "/" but not ends with "/" */
57
+ function normalizeBasePath(p) {
58
+ let res = p.startsWith('/') ? p : '/' + p;
59
+ return res.endsWith('/') ? res.slice(0, -1) : res;
60
+ }
61
+
62
+ /** Copies all built frontend assets into /public/{studioPath} */
63
+ function copyStudioFrontend(studioPath) {
64
+ const src = path.join(
65
+ path.dirname(require.resolve('@mongoosejs/studio/package.json')),
66
+ 'frontend',
67
+ 'public'
68
+ );
69
+ const dest = path.join(process.cwd(), 'public', studioPath.replace(/^\//, ''));
70
+
71
+ if (!fs.existsSync(src)) {
72
+ throw new Error(`Frontend build not found at ${src}`);
73
+ }
74
+
75
+ fs.mkdirSync(dest, { recursive: true });
76
+
77
+ // Node 16.7+ has fs.cpSync
78
+ if (fs.cpSync) {
79
+ fs.cpSync(src, dest, { recursive: true });
80
+ } else {
81
+ copyRecursiveSync(src, dest);
82
+ }
83
+ }
84
+
85
+ function copyRecursiveSync(src, dest) {
86
+ const entries = fs.readdirSync(src, { withFileTypes: true });
87
+ for (const entry of entries) {
88
+ const srcPath = path.join(src, entry.name);
89
+ const destPath = path.join(dest, entry.name);
90
+ if (entry.isDirectory()) {
91
+ fs.mkdirSync(destPath, { recursive: true });
92
+ copyRecursiveSync(srcPath, destPath);
93
+ } else {
94
+ fs.copyFileSync(srcPath, destPath);
95
+ }
96
+ }
97
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mongoosejs/studio",
3
- "version": "0.0.136",
3
+ "version": "0.0.138",
4
4
  "description": "A sleek, powerful MongoDB UI with built-in dashboarding and auth, seamlessly integrated with your Express, Vercel, or Netlify app.",
5
5
  "homepage": "https://studio.mongoosejs.io/",
6
6
  "repository": {
@@ -1,3 +0,0 @@
1
- .list-json {
2
- width: 100%;
3
- }