doeqoth commited on
Commit
5ea2408
·
verified ·
1 Parent(s): 00c76ab

ปรับให้ใช้งานได้จริงยืนยันตัวตนด้วยการใส่ API หรือถ้าเป็นตัวผมเองไม่ต้องยืนยันตัวตนปรับใช้กับฐานข้อมูลจริงเลยนะโดยให้มีการยืนยันตัวตนการเข้าถึงข้อมูลในครั้งแรกก็ได้แล้วก็สามารถแก้ไขข้อมูลในตารางได้ด้วยหัวข้อต่างๆต้องทำให้มันสามารถเปลี่ยนได้

Browse files
Files changed (4) hide show
  1. components/footer.js +72 -0
  2. index.html +34 -5
  3. script.js +229 -82
  4. style.css +10 -1
components/footer.js ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class CustomFooter extends HTMLElement {
2
+ connectedCallback() {
3
+ this.attachShadow({ mode: 'open' });
4
+ this.shadowRoot.innerHTML = `
5
+ <style>
6
+ :host {
7
+ display: block;
8
+ width: 100%;
9
+ margin-top: auto;
10
+ }
11
+
12
+ footer {
13
+ background-color: #f8fafc;
14
+ padding: 1.5rem 0;
15
+ border-top: 1px solid #e2e8f0;
16
+ }
17
+
18
+ .footer-container {
19
+ max-width: 1200px;
20
+ margin: 0 auto;
21
+ padding: 0 1rem;
22
+ display: flex;
23
+ justify-content: space-between;
24
+ align-items: center;
25
+ }
26
+
27
+ .footer-links {
28
+ display: flex;
29
+ gap: 1rem;
30
+ }
31
+
32
+ .footer-links a {
33
+ color: #64748b;
34
+ text-decoration: none;
35
+ font-size: 0.875rem;
36
+ }
37
+
38
+ .footer-links a:hover {
39
+ color: #6366f1;
40
+ }
41
+
42
+ .copyright {
43
+ color: #64748b;
44
+ font-size: 0.875rem;
45
+ }
46
+
47
+ @media (max-width: 768px) {
48
+ .footer-container {
49
+ flex-direction: column;
50
+ gap: 1rem;
51
+ text-align: center;
52
+ }
53
+ }
54
+ </style>
55
+
56
+ <footer>
57
+ <div class="footer-container">
58
+ <div class="copyright">
59
+ &copy; ${new Date().getFullYear()} HuggingSpace Asset Manager
60
+ </div>
61
+ <div class="footer-links">
62
+ <a href="https://huggingface.co/docs" target="_blank">Documentation</a>
63
+ <a href="https://huggingface.co/support" target="_blank">Support</a>
64
+ <a href="https://huggingface.co/privacy" target="_blank">Privacy</a>
65
+ </div>
66
+ </div>
67
+ </footer>
68
+ `;
69
+ }
70
+ }
71
+
72
+ customElements.define('custom-footer', CustomFooter);
index.html CHANGED
@@ -29,14 +29,14 @@
29
  </head>
30
  <body class="bg-gray-50 min-h-screen">
31
  <custom-navbar></custom-navbar>
32
-
33
  <div class="container mx-auto px-4 py-8">
34
  <div class="flex justify-between items-center mb-8">
35
  <div>
36
  <h1 class="text-3xl font-bold text-gray-800">Asset Manager</h1>
37
  <p class="text-gray-600">Manage your Hugging Face Space assets</p>
38
  </div>
39
- <button id="uploadBtn" class="bg-primary-500 hover:bg-primary-600 text-white px-4 py-2 rounded-lg flex items-center transition-colors">
 
40
  <i data-feather="upload" class="mr-2"></i> Upload Files
41
  </button>
42
  </div>
@@ -72,9 +72,38 @@
72
  </div>
73
  </div>
74
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
 
76
  <!-- Upload Modal -->
77
- <div id="uploadModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden z-50">
78
  <div class="bg-white rounded-lg p-6 w-full max-w-md">
79
  <div class="flex justify-between items-center mb-4">
80
  <h3 class="text-lg font-bold">Upload Files</h3>
@@ -93,10 +122,10 @@
93
  </button>
94
  </div>
95
  </div>
96
-
97
  <script src="components/navbar.js"></script>
 
98
  <script src="script.js"></script>
99
  <script>feather.replace();</script>
100
- <script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
101
  </body>
102
  </html>
 
29
  </head>
30
  <body class="bg-gray-50 min-h-screen">
31
  <custom-navbar></custom-navbar>
 
32
  <div class="container mx-auto px-4 py-8">
33
  <div class="flex justify-between items-center mb-8">
34
  <div>
35
  <h1 class="text-3xl font-bold text-gray-800">Asset Manager</h1>
36
  <p class="text-gray-600">Manage your Hugging Face Space assets</p>
37
  </div>
38
+ <div id="userInfo"></div>
39
+ <button id="uploadBtn" class="bg-primary-500 hover:bg-primary-600 text-white px-4 py-2 rounded-lg flex items-center transition-colors">
40
  <i data-feather="upload" class="mr-2"></i> Upload Files
41
  </button>
42
  </div>
 
72
  </div>
73
  </div>
74
  </div>
75
+ <!-- Auth Modal -->
76
+ <div id="authModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
77
+ <div class="bg-white rounded-lg p-6 w-full max-w-md">
78
+ <div class="flex justify-between items-center mb-4">
79
+ <h3 class="text-lg font-bold">Hugging Face Authentication</h3>
80
+ </div>
81
+ <div class="mb-4">
82
+ <label class="block text-gray-700 text-sm font-bold mb-2" for="tokenInput">
83
+ Hugging Face Token
84
+ </label>
85
+ <input type="password" id="tokenInput" class="w-full p-2 border border-gray-300 rounded-lg"
86
+ placeholder="Enter your Hugging Face token">
87
+ <p class="text-xs text-gray-500 mt-1">
88
+ Get your token from <a href="https://huggingface.co/settings/tokens" target="_blank" class="text-primary-500">Settings → Access Tokens</a>
89
+ </p>
90
+ </div>
91
+ <div class="mb-4">
92
+ <label class="block text-gray-700 text-sm font-bold mb-2" for="spaceInput">
93
+ Space Name
94
+ </label>
95
+ <input type="text" id="spaceInput" class="w-full p-2 border border-gray-300 rounded-lg"
96
+ placeholder="Enter your space name (e.g., username/space)">
97
+ </div>
98
+ <p id="authError" class="text-red-500 text-sm mb-4"></p>
99
+ <button id="authSubmit" class="bg-primary-500 hover:bg-primary-600 text-white px-4 py-2 rounded-lg w-full transition-colors">
100
+ Connect
101
+ </button>
102
+ </div>
103
+ </div>
104
 
105
  <!-- Upload Modal -->
106
+ <div id="uploadModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden z-50">
107
  <div class="bg-white rounded-lg p-6 w-full max-w-md">
108
  <div class="flex justify-between items-center mb-4">
109
  <h3 class="text-lg font-bold">Upload Files</h3>
 
122
  </button>
123
  </div>
124
  </div>
125
+ <custom-footer></custom-footer>
126
  <script src="components/navbar.js"></script>
127
+ <script src="components/footer.js"></script>
128
  <script src="script.js"></script>
129
  <script>feather.replace();</script>
 
130
  </body>
131
  </html>
script.js CHANGED
@@ -1,65 +1,150 @@
1
- // Enhanced asset management with more flexibility
 
2
  class AssetManager {
3
  constructor() {
4
- this.assets = this.loadAssets() || [
5
- {
6
- id: 1,
7
- name: "nature-image.jpg",
8
- type: "image/jpeg",
9
- url: "https://huggingface.co/spaces/username/space/resolve/main/nature-image.jpg",
10
- preview: "http://static.photos/nature/320x240/1"
11
- },
12
- {
13
- id: 2,
14
- name: "logo.png",
15
- type: "image/png",
16
- url: "https://huggingface.co/spaces/username/space/resolve/main/logo.png",
17
- preview: "http://static.photos/abstract/320x240/2"
18
- },
19
- {
20
- id: 3,
21
- name: "document.pdf",
22
- type: "application/pdf",
23
- url: "https://huggingface.co/spaces/username/space/resolve/main/document.pdf",
24
- preview: "http://static.photos/office/320x240/3"
25
- }
26
- ];
27
- this.currentId = this.assets.length > 0 ? Math.max(...this.assets.map(a => a.id)) : 0;
28
  }
29
 
30
- loadAssets() {
31
- const saved = localStorage.getItem('huggingSpaceAssets');
32
- return saved ? JSON.parse(saved) : null;
 
 
 
 
 
 
 
 
 
 
 
 
33
  }
34
 
35
- saveAssets() {
36
- localStorage.setItem('huggingSpaceAssets', JSON.stringify(this.assets));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  }
38
 
39
- addAsset(asset) {
40
- this.currentId++;
41
- const newAsset = {
42
- id: this.currentId,
43
- ...asset
 
 
 
 
 
 
44
  };
45
- this.assets.push(newAsset);
46
- this.saveAssets();
47
- return newAsset;
48
  }
49
 
50
- updateAsset(id, updates) {
51
- const index = this.assets.findIndex(a => a.id === id);
52
- if (index !== -1) {
53
- this.assets[index] = { ...this.assets[index], ...updates };
54
- this.saveAssets();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  return this.assets[index];
56
  }
57
- return null;
58
  }
59
 
60
- deleteAsset(id) {
61
- this.assets = this.assets.filter(a => a.id !== id);
62
- this.saveAssets();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  }
64
 
65
  getAssets() {
@@ -70,16 +155,14 @@ class AssetManager {
70
  return JSON.stringify(this.assets, null, 2);
71
  }
72
  }
73
-
74
- // Main application
75
  class HuggingSpaceApp {
76
  constructor() {
77
  this.assetManager = new AssetManager();
78
  this.initElements();
79
  this.initEventListeners();
80
- this.render();
81
- }
82
-
83
  initElements() {
84
  this.elements = {
85
  assetsTable: document.getElementById('assetsTable'),
@@ -89,38 +172,110 @@ class HuggingSpaceApp {
89
  fileInput: document.getElementById('fileInput'),
90
  confirmUpload: document.getElementById('confirmUpload'),
91
  exportBtn: document.getElementById('exportBtn'),
92
- jsonData: document.getElementById('jsonData')
 
 
 
 
 
 
93
  };
94
- }
95
-
96
  initEventListeners() {
97
  this.elements.uploadBtn.addEventListener('click', () => this.toggleModal(true));
98
  this.elements.closeModal.addEventListener('click', () => this.toggleModal(false));
99
  this.elements.confirmUpload.addEventListener('click', () => this.handleFileUpload());
100
  this.elements.exportBtn.addEventListener('click', () => this.exportData());
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  }
102
 
103
- render() {
104
- this.renderAssetsTable();
105
- this.updateJsonPreview();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
  }
107
 
108
- renderAssetsTable() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  const { assetsTable } = this.elements;
110
  assetsTable.innerHTML = '';
111
 
112
- this.assetManager.getAssets().forEach(asset => {
 
 
 
 
 
 
 
 
 
 
 
 
113
  const row = document.createElement('tr');
114
  row.className = 'hover:bg-gray-50';
115
  row.innerHTML = this.createAssetRowHTML(asset);
116
  assetsTable.appendChild(row);
117
 
118
- // Add event listeners for this row
119
  this.addRowEventListeners(row, asset.id);
120
  });
121
 
122
  feather.replace();
123
- }
124
 
125
  createAssetRowHTML(asset) {
126
  return `
@@ -207,8 +362,7 @@ class HuggingSpaceApp {
207
  this.elements.fileInput.value = '';
208
  }
209
  }
210
-
211
- handleFileUpload() {
212
  const files = this.elements.fileInput.files;
213
 
214
  if (files.length === 0) {
@@ -216,19 +370,17 @@ class HuggingSpaceApp {
216
  return;
217
  }
218
 
219
- Array.from(files).forEach(file => {
220
- const newAsset = {
221
- name: file.name,
222
- type: file.type || 'unknown',
223
- url: `https://huggingface.co/spaces/username/space/resolve/main/${file.name}`,
224
- preview: `http://static.photos/${this.getRandomCategory()}/320x240/${this.assetManager.currentId + 1}`
225
- };
226
- this.assetManager.addAsset(newAsset);
227
- });
228
-
229
- this.render();
230
- this.toggleModal(false);
231
- }
232
 
233
  exportData() {
234
  const dataStr = this.assetManager.exportAsJSON();
@@ -243,11 +395,6 @@ class HuggingSpaceApp {
243
  updateJsonPreview() {
244
  this.elements.jsonData.value = this.assetManager.exportAsJSON();
245
  }
246
-
247
- getRandomCategory() {
248
- const categories = ['nature', 'office', 'people', 'technology', 'abstract'];
249
- return categories[Math.floor(Math.random() * categories.length)];
250
- }
251
  }
252
 
253
  // Initialize the application when DOM is loaded
 
1
+
2
+ // Real Hugging Face API integration
3
  class AssetManager {
4
  constructor() {
5
+ this.assets = [];
6
+ this.currentId = 0;
7
+ this.token = null;
8
+ this.spaceName = null;
9
+ this.username = null;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  }
11
 
12
+ async authenticate(token) {
13
+ this.token = token;
14
+ try {
15
+ const response = await fetch('https://huggingface.co/api/whoami-v2', {
16
+ headers: {
17
+ 'Authorization': `Bearer ${token}`
18
+ }
19
+ });
20
+ const userData = await response.json();
21
+ this.username = userData.name;
22
+ return true;
23
+ } catch (error) {
24
+ console.error('Authentication failed:', error);
25
+ return false;
26
+ }
27
  }
28
 
29
+ async loadAssets(spaceName) {
30
+ this.spaceName = spaceName;
31
+ try {
32
+ const response = await fetch(`https://huggingface.co/api/spaces/${this.username}/${spaceName}/files`, {
33
+ headers: {
34
+ 'Authorization': `Bearer ${this.token}`
35
+ }
36
+ });
37
+ const files = await response.json();
38
+
39
+ this.assets = files.map(file => ({
40
+ id: ++this.currentId,
41
+ name: file.path.split('/').pop(),
42
+ type: this.getFileType(file.path),
43
+ url: `https://huggingface.co/spaces/${this.username}/${spaceName}/resolve/main/${file.path}`,
44
+ preview: this.getPreviewUrl(file.path),
45
+ path: file.path,
46
+ lastModified: file.lastModified
47
+ }));
48
+
49
+ return this.assets;
50
+ } catch (error) {
51
+ console.error('Failed to load assets:', error);
52
+ return [];
53
+ }
54
  }
55
 
56
+ getFileType(filename) {
57
+ const extension = filename.split('.').pop().toLowerCase();
58
+ const types = {
59
+ 'jpg': 'image/jpeg',
60
+ 'jpeg': 'image/jpeg',
61
+ 'png': 'image/png',
62
+ 'gif': 'image/gif',
63
+ 'pdf': 'application/pdf',
64
+ 'txt': 'text/plain',
65
+ 'csv': 'text/csv',
66
+ 'json': 'application/json'
67
  };
68
+ return types[extension] || 'application/octet-stream';
 
 
69
  }
70
 
71
+ getPreviewUrl(filename) {
72
+ const extension = filename.split('.').pop().toLowerCase();
73
+ if (['jpg', 'jpeg', 'png', 'gif'].includes(extension)) {
74
+ return `https://huggingface.co/spaces/${this.username}/${this.spaceName}/preview/${filename}`;
75
+ }
76
+ return `http://static.photos/office/320x240/${Math.floor(Math.random() * 100)}`;
77
+ }
78
+ async addAsset(file) {
79
+ const formData = new FormData();
80
+ formData.append('file', file);
81
+
82
+ try {
83
+ const response = await fetch(`https://huggingface.co/api/spaces/${this.username}/${this.spaceName}/upload`, {
84
+ method: 'POST',
85
+ headers: {
86
+ 'Authorization': `Bearer ${this.token}`
87
+ },
88
+ body: formData
89
+ });
90
+
91
+ if (response.ok) {
92
+ const newAsset = {
93
+ id: ++this.currentId,
94
+ name: file.name,
95
+ type: file.type || this.getFileType(file.name),
96
+ url: `https://huggingface.co/spaces/${this.username}/${this.spaceName}/resolve/main/${file.name}`,
97
+ preview: this.getPreviewUrl(file.name),
98
+ path: file.name,
99
+ lastModified: new Date().toISOString()
100
+ };
101
+ this.assets.push(newAsset);
102
+ return newAsset;
103
+ }
104
+ return null;
105
+ } catch (error) {
106
+ console.error('Failed to upload file:', error);
107
+ return null;
108
+ }
109
+ }
110
+
111
+ async updateAsset(id, updates) {
112
+ const asset = this.assets.find(a => a.id === id);
113
+ if (!asset) return null;
114
+
115
+ // For Hugging Face, updating means deleting and re-uploading
116
+ if (updates.file) {
117
+ await this.deleteAsset(id);
118
+ return await this.addAsset(updates.file);
119
+ } else {
120
+ // For metadata updates
121
+ const index = this.assets.findIndex(a => a.id === id);
122
+ this.assets[index] = { ...asset, ...updates };
123
  return this.assets[index];
124
  }
 
125
  }
126
 
127
+ async deleteAsset(id) {
128
+ const asset = this.assets.find(a => a.id === id);
129
+ if (!asset) return false;
130
+
131
+ try {
132
+ const response = await fetch(`https://huggingface.co/api/spaces/${this.username}/${this.spaceName}/delete/${asset.path}`, {
133
+ method: 'DELETE',
134
+ headers: {
135
+ 'Authorization': `Bearer ${this.token}`
136
+ }
137
+ });
138
+
139
+ if (response.ok) {
140
+ this.assets = this.assets.filter(a => a.id !== id);
141
+ return true;
142
+ }
143
+ return false;
144
+ } catch (error) {
145
+ console.error('Failed to delete file:', error);
146
+ return false;
147
+ }
148
  }
149
 
150
  getAssets() {
 
155
  return JSON.stringify(this.assets, null, 2);
156
  }
157
  }
158
+ // Main application with authentication
 
159
  class HuggingSpaceApp {
160
  constructor() {
161
  this.assetManager = new AssetManager();
162
  this.initElements();
163
  this.initEventListeners();
164
+ this.checkAuth();
165
+ }
 
166
  initElements() {
167
  this.elements = {
168
  assetsTable: document.getElementById('assetsTable'),
 
172
  fileInput: document.getElementById('fileInput'),
173
  confirmUpload: document.getElementById('confirmUpload'),
174
  exportBtn: document.getElementById('exportBtn'),
175
+ jsonData: document.getElementById('jsonData'),
176
+ authModal: document.getElementById('authModal'),
177
+ tokenInput: document.getElementById('tokenInput'),
178
+ spaceInput: document.getElementById('spaceInput'),
179
+ authSubmit: document.getElementById('authSubmit'),
180
+ authError: document.getElementById('authError'),
181
+ userInfo: document.getElementById('userInfo')
182
  };
183
+ }
 
184
  initEventListeners() {
185
  this.elements.uploadBtn.addEventListener('click', () => this.toggleModal(true));
186
  this.elements.closeModal.addEventListener('click', () => this.toggleModal(false));
187
  this.elements.confirmUpload.addEventListener('click', () => this.handleFileUpload());
188
  this.elements.exportBtn.addEventListener('click', () => this.exportData());
189
+ this.elements.authSubmit.addEventListener('click', () => this.handleAuth());
190
+ }
191
+ async checkAuth() {
192
+ const token = localStorage.getItem('hfToken');
193
+ const space = localStorage.getItem('hfSpace');
194
+
195
+ if (token && space) {
196
+ const authenticated = await this.assetManager.authenticate(token);
197
+ if (authenticated) {
198
+ await this.assetManager.loadAssets(space);
199
+ this.render();
200
+ this.elements.authModal.classList.add('hidden');
201
+ this.updateUserInfo();
202
+ return;
203
+ }
204
+ }
205
+
206
+ this.elements.authModal.classList.remove('hidden');
207
  }
208
 
209
+ async handleAuth() {
210
+ const token = this.elements.tokenInput.value.trim();
211
+ const space = this.elements.spaceInput.value.trim();
212
+
213
+ if (!token || !space) {
214
+ this.elements.authError.textContent = 'Please enter both token and space name';
215
+ return;
216
+ }
217
+
218
+ const authenticated = await this.assetManager.authenticate(token);
219
+ if (!authenticated) {
220
+ this.elements.authError.textContent = 'Invalid token. Please check and try again.';
221
+ return;
222
+ }
223
+
224
+ try {
225
+ await this.assetManager.loadAssets(space);
226
+ localStorage.setItem('hfToken', token);
227
+ localStorage.setItem('hfSpace', space);
228
+ this.elements.authModal.classList.add('hidden');
229
+ this.render();
230
+ this.updateUserInfo();
231
+ } catch (error) {
232
+ this.elements.authError.textContent = 'Failed to load space. Please check space name and try again.';
233
+ }
234
  }
235
 
236
+ updateUserInfo() {
237
+ if (this.assetManager.username && this.assetManager.spaceName) {
238
+ this.elements.userInfo.innerHTML = `
239
+ <div class="flex items-center gap-2">
240
+ <span class="font-medium">${this.assetManager.username}</span>
241
+ <span class="text-gray-500">/</span>
242
+ <span class="font-medium">${this.assetManager.spaceName}</span>
243
+ </div>
244
+ `;
245
+ }
246
+ }
247
+
248
+ async render() {
249
+ await this.renderAssetsTable();
250
+ this.updateJsonPreview();
251
+ }
252
+ async renderAssetsTable() {
253
  const { assetsTable } = this.elements;
254
  assetsTable.innerHTML = '';
255
 
256
+ const assets = this.assetManager.getAssets();
257
+ if (assets.length === 0) {
258
+ assetsTable.innerHTML = `
259
+ <tr>
260
+ <td colspan="5" class="px-6 py-4 text-center text-gray-500">
261
+ No assets found. Upload files to get started.
262
+ </td>
263
+ </tr>
264
+ `;
265
+ return;
266
+ }
267
+
268
+ assets.forEach(asset => {
269
  const row = document.createElement('tr');
270
  row.className = 'hover:bg-gray-50';
271
  row.innerHTML = this.createAssetRowHTML(asset);
272
  assetsTable.appendChild(row);
273
 
 
274
  this.addRowEventListeners(row, asset.id);
275
  });
276
 
277
  feather.replace();
278
+ }
279
 
280
  createAssetRowHTML(asset) {
281
  return `
 
362
  this.elements.fileInput.value = '';
363
  }
364
  }
365
+ async handleFileUpload() {
 
366
  const files = this.elements.fileInput.files;
367
 
368
  if (files.length === 0) {
 
370
  return;
371
  }
372
 
373
+ try {
374
+ for (const file of files) {
375
+ await this.assetManager.addAsset(file);
376
+ }
377
+ await this.render();
378
+ this.toggleModal(false);
379
+ } catch (error) {
380
+ alert('Failed to upload files. Please try again.');
381
+ console.error(error);
382
+ }
383
+ }
 
 
384
 
385
  exportData() {
386
  const dataStr = this.assetManager.exportAsJSON();
 
395
  updateJsonPreview() {
396
  this.elements.jsonData.value = this.assetManager.exportAsJSON();
397
  }
 
 
 
 
 
398
  }
399
 
400
  // Initialize the application when DOM is loaded
style.css CHANGED
@@ -27,7 +27,6 @@ body {
27
  object-fit: cover;
28
  border-radius: 0.25rem;
29
  }
30
-
31
  .url-cell {
32
  max-width: 200px;
33
  overflow: hidden;
@@ -35,6 +34,16 @@ body {
35
  white-space: nowrap;
36
  }
37
 
 
 
 
 
 
 
 
 
 
 
38
  .editable-cell {
39
  cursor: pointer;
40
  transition: background-color 0.2s;
 
27
  object-fit: cover;
28
  border-radius: 0.25rem;
29
  }
 
30
  .url-cell {
31
  max-width: 200px;
32
  overflow: hidden;
 
34
  white-space: nowrap;
35
  }
36
 
37
+ #authModal input {
38
+ margin-bottom: 0.5rem;
39
+ }
40
+
41
+ #userInfo {
42
+ background-color: rgba(99, 102, 241, 0.1);
43
+ padding: 0.5rem 1rem;
44
+ border-radius: 0.5rem;
45
+ font-size: 0.875rem;
46
+ }
47
  .editable-cell {
48
  cursor: pointer;
49
  transition: background-color 0.2s;