@onivoro/app-server-bucketvore 24.31.3 → 24.31.5
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.
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
(()=>{"use strict";function e(){return{selectedBucket:"",currentPrefix:"",buckets:[],files:[],folders:[],viewMode:"list",bucketsHtml:'<div class="loading"><div class="spinner"></div><p>Loading buckets...</p></div>',filesHtml:"",breadcrumbsHtml:"",bucketFilter:"",fileFilter:"",showUpload:!1,showPreview:!1,uploadProgress:[],uploadProgressHtml:"",previewHtml:"",activeTab:"data",get filteredBucketsHtml(){if(!this.bucketFilter.trim())return this.bucketsHtml;const e=(new DOMParser).parseFromString(this.bucketsHtml,"text/html");return e.querySelectorAll(".bucket-item").forEach(e=>{(e.querySelector(".bucket-name")?.textContent?.toLowerCase()||"").includes(this.bucketFilter.toLowerCase())?e.style.display="":e.style.display="none"}),0===e.querySelectorAll('.bucket-item:not([style*="display: none"])').length?'<div class="empty-state"><div class="empty-state-icon">🔍</div><p>No buckets match your filter</p></div>':e.body.innerHTML},get filteredFilesHtml(){if(!this.fileFilter.trim()||!this.filesHtml)return this.filesHtml;const e=(new DOMParser).parseFromString(this.filesHtml,"text/html");return e.querySelectorAll(".file-item").forEach(e=>{(e.querySelector(".file-name")?.textContent?.toLowerCase()||"").includes(this.fileFilter.toLowerCase())?e.style.display="":e.style.display="none"}),0===e.querySelectorAll('.file-item:not([style*="display: none"])').length?'<div class="empty-state"><div class="empty-state-icon">🔍</div><p>No files match your filter</p></div>':e.body.innerHTML},async init(){await this.loadBuckets()},async loadBuckets(){try{const e=await fetch("/api/buckets");this.bucketsHtml=await e.text()}catch(e){this.bucketsHtml='<div class="error"><div class="error-icon">⚠️</div><p>Error loading buckets</p></div>'}},async selectBucket(e){this.selectedBucket=e,this.currentPrefix="",this.fileFilter="",await this.loadFiles()},async loadFiles(){try{const e=new URLSearchParams({bucket:this.selectedBucket,prefix:this.currentPrefix}),t=await fetch(`/api/files?${e}`),i=await t.json();this.filesHtml=i.filesHtml||"",this.breadcrumbsHtml=i.breadcrumbsHtml||""}catch(e){this.filesHtml='<div class="error"><div class="error-icon">⚠️</div><p>Error loading files</p></div>'}},async navigateToFolder(e){this.currentPrefix=e,this.fileFilter="",await this.loadFiles()},async previewFile(e){try{const t=new URLSearchParams({bucket:this.selectedBucket,key:e}),i=await fetch(`/api/files/preview?${t}`);this.previewHtml=await i.text(),this.showPreview=!0}catch(e){alert("Error loading preview")}},closePreview(){this.showPreview=!1,this.previewHtml=""},async downloadFile(e){try{const t=new URLSearchParams({bucket:this.selectedBucket,key:e}),i=await fetch(`/api/files/download?${t}`),s=await i.json();window.open(s.url,"_blank")}catch(e){alert("Error generating download link")}},async deleteFile(e){if(confirm(`Delete ${e}?`))try{await fetch("/api/files/delete",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({bucket:this.selectedBucket,key:e})}),await this.loadFiles()}catch(e){alert("Error deleting file")}},async copyS3Path(e){const t=event?.target;await async function(e,t,i){try{const s=await fetch(`/api/buckets/${encodeURIComponent(e)}/region`),
|
|
1
|
+
(()=>{"use strict";function e(){return{selectedBucket:"",currentPrefix:"",buckets:[],files:[],folders:[],viewMode:"list",bucketsHtml:'<div class="loading"><div class="spinner"></div><p>Loading buckets...</p></div>',filesHtml:"",breadcrumbsHtml:"",bucketFilter:"",fileFilter:"",showUpload:!1,showPreview:!1,uploadProgress:[],uploadProgressHtml:"",previewHtml:"",activeTab:"data",get filteredBucketsHtml(){if(!this.bucketFilter.trim())return this.bucketsHtml;const e=(new DOMParser).parseFromString(this.bucketsHtml,"text/html");return e.querySelectorAll(".bucket-item").forEach(e=>{(e.querySelector(".bucket-name")?.textContent?.toLowerCase()||"").includes(this.bucketFilter.toLowerCase())?e.style.display="":e.style.display="none"}),0===e.querySelectorAll('.bucket-item:not([style*="display: none"])').length?'<div class="empty-state"><div class="empty-state-icon">🔍</div><p>No buckets match your filter</p></div>':e.body.innerHTML},get filteredFilesHtml(){if(!this.fileFilter.trim()||!this.filesHtml)return this.filesHtml;const e=(new DOMParser).parseFromString(this.filesHtml,"text/html");return e.querySelectorAll(".file-item").forEach(e=>{(e.querySelector(".file-name")?.textContent?.toLowerCase()||"").includes(this.fileFilter.toLowerCase())?e.style.display="":e.style.display="none"}),0===e.querySelectorAll('.file-item:not([style*="display: none"])').length?'<div class="empty-state"><div class="empty-state-icon">🔍</div><p>No files match your filter</p></div>':e.body.innerHTML},async init(){await this.loadBuckets(),this.restoreStateFromUrl(),this.setupUrlSync()},restoreStateFromUrl(){const e=new URLSearchParams(window.location.search),t=e.get("bucket"),i=e.get("prefix")||"";t&&(this.selectedBucket=t,this.currentPrefix=i,this.loadFiles())},updateUrl(){const e=new URLSearchParams;this.selectedBucket&&(e.set("bucket",this.selectedBucket),this.currentPrefix&&e.set("prefix",this.currentPrefix));const t=e.toString()?`${window.location.pathname}?${e.toString()}`:window.location.pathname;window.history.pushState({},"",t)},setupUrlSync(){window.addEventListener("popstate",()=>{this.restoreStateFromUrl()})},async loadBuckets(){try{const e=await fetch("/api/buckets");this.bucketsHtml=await e.text()}catch(e){this.bucketsHtml='<div class="error"><div class="error-icon">⚠️</div><p>Error loading buckets</p></div>'}},async selectBucket(e){this.selectedBucket=e,this.currentPrefix="",this.fileFilter="",this.updateUrl(),await this.loadFiles()},async loadFiles(){try{const e=new URLSearchParams({bucket:this.selectedBucket,prefix:this.currentPrefix}),t=await fetch(`/api/files?${e}`),i=await t.json();this.filesHtml=i.filesHtml||"",this.breadcrumbsHtml=i.breadcrumbsHtml||""}catch(e){this.filesHtml='<div class="error"><div class="error-icon">⚠️</div><p>Error loading files</p></div>'}},async navigateToFolder(e){this.currentPrefix=e,this.fileFilter="",this.updateUrl(),await this.loadFiles()},async previewFile(e){try{const t=new URLSearchParams({bucket:this.selectedBucket,key:e}),i=await fetch(`/api/files/preview?${t}`);this.previewHtml=await i.text(),this.showPreview=!0}catch(e){alert("Error loading preview")}},closePreview(){this.showPreview=!1,this.previewHtml=""},async downloadFile(e){try{const t=new URLSearchParams({bucket:this.selectedBucket,key:e}),i=await fetch(`/api/files/download?${t}`),s=await i.json();window.open(s.url,"_blank")}catch(e){alert("Error generating download link")}},async deleteFile(e){if(confirm(`Delete ${e}?`))try{await fetch("/api/files/delete",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({bucket:this.selectedBucket,key:e})}),await this.loadFiles()}catch(e){alert("Error deleting file")}},async copyS3Path(e){const t=event?.target;await async function(e,t,i){try{const s=await fetch(`/api/buckets/${encodeURIComponent(e)}/region`),r=`https://s3.${(await s.json()).region||"us-east-1"}.amazonaws.com/${e}/${t}`;await async function(e,t){try{if(await navigator.clipboard.writeText(e),t){const e=t.textContent||"",i=t.style.backgroundColor;t.textContent="✓ Copied!",t.style.backgroundColor="var(--color-success)",setTimeout(()=>{t.textContent=e,t.style.backgroundColor=i},1500)}return!0}catch(t){return console.error("Clipboard API failed:",t),null!==prompt("Copy to clipboard:",e)}}(r,i)}catch(i){console.error("Error getting S3 URL:",i),prompt("Copy S3 path (fallback):",`s3://${e}/${t}`)}}(this.selectedBucket,e,t)},async deleteFolder(e){if(confirm(`Delete folder ${e} and all its contents?`))try{await fetch("/api/files/delete-folder",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({bucket:this.selectedBucket,prefix:e})}),await this.loadFiles()}catch(e){alert("Error deleting folder")}},handleFileSelect(e){const t=e.target;if(t.files){const e=Array.from(t.files);this.uploadFiles(e)}},handleDrop(e){if(e.target.classList.remove("drag-over"),e.dataTransfer?.files){const t=Array.from(e.dataTransfer.files);this.uploadFiles(t)}},async uploadFiles(e){this.uploadProgress=e.map(e=>({name:e.name,status:"Uploading..."}));for(let t=0;t<e.length;t++){const i=e[t],s=new FormData;s.append("file",i),s.append("bucket",this.selectedBucket),s.append("prefix",this.currentPrefix);try{await fetch("/api/upload",{method:"POST",body:s}),this.uploadProgress[t].status="✓ Complete"}catch(e){this.uploadProgress[t].status="✗ Failed"}}setTimeout(async()=>{this.uploadProgress=[],this.showUpload=!1,await this.loadFiles()},2e3)},async refresh(){await this.loadFiles()},filterBuckets(){},filterFiles(){}}}"undefined"!=typeof window&&(window.Alpine?window.Alpine.data("s3Explorer",e):document.addEventListener("alpine:init",()=>{window.Alpine.data("s3Explorer",e)}))})();
|
|
2
2
|
//# sourceMappingURL=s3-explorer.bundle.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"s3-explorer.bundle.js","mappings":"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","sources":["webpack://onivoro/./apps/server/bucketvore/src/app/client/s3-explorer.client.ts","webpack://onivoro/./apps/server/bucketvore/src/app/client/utils/clipboard.client.ts"],"sourcesContent":["/**\n * S3 Explorer Alpine.js Component\n * Client-side TypeScript with full IDE support\n */\n\nimport { copyS3PathToClipboard } from './utils/clipboard.client';\nimport type { S3ClientState, UploadProgress } from '../shared/types.shared';\n\ndeclare global {\n interface Window {\n Alpine: any;\n }\n}\n\nexport function s3Explorer(): S3ClientState & Record<string, any> {\n return {\n // State\n selectedBucket: '',\n currentPrefix: '',\n buckets: [],\n files: [],\n folders: [],\n viewMode: 'list',\n bucketsHtml: '<div class=\"loading\"><div class=\"spinner\"></div><p>Loading buckets...</p></div>',\n filesHtml: '',\n breadcrumbsHtml: '',\n bucketFilter: '',\n fileFilter: '',\n showUpload: false,\n showPreview: false,\n uploadProgress: [] as UploadProgress[],\n uploadProgressHtml: '',\n previewHtml: '',\n activeTab: 'data',\n\n // Computed properties\n get filteredBucketsHtml(): string {\n if (!this.bucketFilter.trim()) {\n return this.bucketsHtml;\n }\n\n const parser = new DOMParser();\n const doc = parser.parseFromString(this.bucketsHtml, 'text/html');\n const bucketItems = doc.querySelectorAll('.bucket-item');\n\n bucketItems.forEach((item) => {\n const bucketName = item.querySelector('.bucket-name')?.textContent?.toLowerCase() || '';\n if (bucketName.includes(this.bucketFilter.toLowerCase())) {\n (item as HTMLElement).style.display = '';\n } else {\n (item as HTMLElement).style.display = 'none';\n }\n });\n\n const visibleCount = doc.querySelectorAll('.bucket-item:not([style*=\"display: none\"])').length;\n if (visibleCount === 0) {\n return '<div class=\"empty-state\"><div class=\"empty-state-icon\">🔍</div><p>No buckets match your filter</p></div>';\n }\n\n return doc.body.innerHTML;\n },\n\n get filteredFilesHtml(): string {\n if (!this.fileFilter.trim() || !this.filesHtml) {\n return this.filesHtml;\n }\n\n const parser = new DOMParser();\n const doc = parser.parseFromString(this.filesHtml, 'text/html');\n const fileItems = doc.querySelectorAll('.file-item');\n\n fileItems.forEach((item) => {\n const fileName = item.querySelector('.file-name')?.textContent?.toLowerCase() || '';\n if (fileName.includes(this.fileFilter.toLowerCase())) {\n (item as HTMLElement).style.display = '';\n } else {\n (item as HTMLElement).style.display = 'none';\n }\n });\n\n const visibleCount = doc.querySelectorAll('.file-item:not([style*=\"display: none\"])').length;\n if (visibleCount === 0) {\n return '<div class=\"empty-state\"><div class=\"empty-state-icon\">🔍</div><p>No files match your filter</p></div>';\n }\n\n return doc.body.innerHTML;\n },\n\n // Initialization\n async init(): Promise<void> {\n await this.loadBuckets();\n },\n\n // API Methods\n async loadBuckets(): Promise<void> {\n try {\n const response = await fetch('/api/buckets');\n this.bucketsHtml = await response.text();\n } catch (error) {\n this.bucketsHtml = '<div class=\"error\"><div class=\"error-icon\">⚠️</div><p>Error loading buckets</p></div>';\n }\n },\n\n async selectBucket(bucketName: string): Promise<void> {\n this.selectedBucket = bucketName;\n this.currentPrefix = '';\n this.fileFilter = '';\n await this.loadFiles();\n },\n\n async loadFiles(): Promise<void> {\n try {\n const params = new URLSearchParams({\n bucket: this.selectedBucket,\n prefix: this.currentPrefix\n });\n\n const response = await fetch(`/api/files?${params}`);\n const data = await response.json();\n\n this.filesHtml = data.filesHtml || '';\n this.breadcrumbsHtml = data.breadcrumbsHtml || '';\n } catch (error) {\n this.filesHtml = '<div class=\"error\"><div class=\"error-icon\">⚠️</div><p>Error loading files</p></div>';\n }\n },\n\n async navigateToFolder(prefix: string): Promise<void> {\n this.currentPrefix = prefix;\n this.fileFilter = '';\n await this.loadFiles();\n },\n\n async previewFile(key: string): Promise<void> {\n try {\n const params = new URLSearchParams({\n bucket: this.selectedBucket,\n key: key\n });\n\n const response = await fetch(`/api/files/preview?${params}`);\n this.previewHtml = await response.text();\n this.showPreview = true;\n } catch (error) {\n alert('Error loading preview');\n }\n },\n\n closePreview(): void {\n this.showPreview = false;\n this.previewHtml = '';\n },\n\n async downloadFile(key: string): Promise<void> {\n try {\n const params = new URLSearchParams({\n bucket: this.selectedBucket,\n key: key\n });\n\n const response = await fetch(`/api/files/download?${params}`);\n const data = await response.json();\n window.open(data.url, '_blank');\n } catch (error) {\n alert('Error generating download link');\n }\n },\n\n async deleteFile(key: string): Promise<void> {\n if (!confirm(`Delete ${key}?`)) return;\n\n try {\n await fetch(`/api/files/delete`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ bucket: this.selectedBucket, key })\n });\n await this.loadFiles();\n } catch (error) {\n alert('Error deleting file');\n }\n },\n\n async copyS3Path(key: string): Promise<void> {\n const element = (event as any)?.target as HTMLElement | undefined;\n await copyS3PathToClipboard(this.selectedBucket, key, element);\n },\n\n async deleteFolder(prefix: string): Promise<void> {\n if (!confirm(`Delete folder ${prefix} and all its contents?`)) return;\n\n try {\n await fetch(`/api/files/delete-folder`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ bucket: this.selectedBucket, prefix })\n });\n await this.loadFiles();\n } catch (error) {\n alert('Error deleting folder');\n }\n },\n\n // Upload functionality\n handleFileSelect(event: Event): void {\n const input = event.target as HTMLInputElement;\n if (input.files) {\n const files = Array.from(input.files);\n this.uploadFiles(files);\n }\n },\n\n handleDrop(event: DragEvent): void {\n (event.target as HTMLElement).classList.remove('drag-over');\n if (event.dataTransfer?.files) {\n const files = Array.from(event.dataTransfer.files);\n this.uploadFiles(files);\n }\n },\n\n async uploadFiles(files: File[]): Promise<void> {\n this.uploadProgress = files.map(f => ({ name: f.name, status: 'Uploading...' }));\n\n for (let i = 0; i < files.length; i++) {\n const file = files[i];\n const formData = new FormData();\n formData.append('file', file);\n formData.append('bucket', this.selectedBucket);\n formData.append('prefix', this.currentPrefix);\n\n try {\n await fetch('/api/upload', {\n method: 'POST',\n body: formData\n });\n this.uploadProgress[i].status = '✓ Complete';\n } catch (error) {\n this.uploadProgress[i].status = '✗ Failed';\n }\n }\n\n setTimeout(async () => {\n this.uploadProgress = [];\n this.showUpload = false;\n await this.loadFiles();\n }, 2000);\n },\n\n async refresh(): Promise<void> {\n await this.loadFiles();\n },\n\n filterBuckets(): void {\n // Reactive computed property handles this\n },\n\n filterFiles(): void {\n // Reactive computed property handles this\n }\n };\n}\n\n// Auto-register with Alpine when it initializes\nif (typeof window !== 'undefined') {\n // Register immediately if Alpine is already available\n if ((window as any).Alpine) {\n (window as any).Alpine.data('s3Explorer', s3Explorer);\n } else {\n // Otherwise wait for Alpine to initialize\n document.addEventListener('alpine:init', () => {\n (window as any).Alpine.data('s3Explorer', s3Explorer);\n });\n }\n}\n","/**\n * Client-side utility for clipboard operations\n */\n\nexport async function copyToClipboard(text: string, element?: HTMLElement): Promise<boolean> {\n try {\n await navigator.clipboard.writeText(text);\n\n // Provide visual feedback if element is provided\n if (element) {\n const originalText = element.textContent || '';\n const originalBg = element.style.backgroundColor;\n\n element.textContent = '✓ Copied!';\n element.style.backgroundColor = 'var(--color-success)';\n\n setTimeout(() => {\n element.textContent = originalText;\n element.style.backgroundColor = originalBg;\n }, 1500);\n }\n\n return true;\n } catch (error) {\n console.error('Clipboard API failed:', error);\n\n // Fallback to prompt\n const userCopy = prompt('Copy to clipboard:', text);\n return userCopy !== null;\n }\n}\n\nexport async function copyS3PathToClipboard(\n bucket: string,\n key: string,\n element?: HTMLElement\n): Promise<void> {\n try {\n // Fetch bucket region\n const response = await fetch(`/api/buckets/${encodeURIComponent(bucket)}/region`);\n const data = await response.json();\n const region = data.region || 'us-east-1';\n\n // Construct HTTPS URL\n const url = `https://s3.${region}.amazonaws.com/${bucket}/${key}`;\n\n await copyToClipboard(url, element);\n } catch (error) {\n console.error('Error getting S3 URL:', error);\n\n // Fallback to s3:// protocol\n const s3Path = `s3://${bucket}/${key}`;\n prompt('Copy S3 path (fallback):', s3Path);\n }\n}\n"],"names":["s3Explorer","selectedBucket","currentPrefix","buckets","files","folders","viewMode","bucketsHtml","filesHtml","breadcrumbsHtml","bucketFilter","fileFilter","showUpload","showPreview","uploadProgress","uploadProgressHtml","previewHtml","activeTab","filteredBucketsHtml","this","trim","doc","DOMParser","parseFromString","querySelectorAll","forEach","item","querySelector","textContent","toLowerCase","includes","style","display","length","body","innerHTML","filteredFilesHtml","init","loadBuckets","response","fetch","text","error","selectBucket","bucketName","loadFiles","params","URLSearchParams","bucket","prefix","data","json","navigateToFolder","previewFile","key","alert","closePreview","downloadFile","window","open","url","deleteFile","confirm","method","headers","JSON","stringify","copyS3Path","element","event","target","async","encodeURIComponent","region","navigator","clipboard","writeText","originalText","originalBg","backgroundColor","setTimeout","console","prompt","copyToClipboard","copyS3PathToClipboard","deleteFolder","handleFileSelect","input","Array","from","uploadFiles","handleDrop","classList","remove","dataTransfer","map","f","name","status","i","file","formData","FormData","append","refresh","filterBuckets","filterFiles","Alpine","document","addEventListener"],"sourceRoot":""}
|
|
1
|
+
{"version":3,"file":"s3-explorer.bundle.js","mappings":"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","sources":["webpack://onivoro/./apps/server/bucketvore/src/app/client/s3-explorer.client.ts","webpack://onivoro/./apps/server/bucketvore/src/app/client/utils/clipboard.client.ts"],"sourcesContent":["/**\n * S3 Explorer Alpine.js Component\n * Client-side TypeScript with full IDE support\n */\n\nimport { copyS3PathToClipboard } from './utils/clipboard.client';\nimport type { S3ClientState, UploadProgress } from '../shared/types.shared';\n\ndeclare global {\n interface Window {\n Alpine: any;\n }\n}\n\nexport function s3Explorer(): S3ClientState & Record<string, any> {\n return {\n // State\n selectedBucket: '',\n currentPrefix: '',\n buckets: [],\n files: [],\n folders: [],\n viewMode: 'list',\n bucketsHtml: '<div class=\"loading\"><div class=\"spinner\"></div><p>Loading buckets...</p></div>',\n filesHtml: '',\n breadcrumbsHtml: '',\n bucketFilter: '',\n fileFilter: '',\n showUpload: false,\n showPreview: false,\n uploadProgress: [] as UploadProgress[],\n uploadProgressHtml: '',\n previewHtml: '',\n activeTab: 'data',\n\n // Computed properties\n get filteredBucketsHtml(): string {\n if (!this.bucketFilter.trim()) {\n return this.bucketsHtml;\n }\n\n const parser = new DOMParser();\n const doc = parser.parseFromString(this.bucketsHtml, 'text/html');\n const bucketItems = doc.querySelectorAll('.bucket-item');\n\n bucketItems.forEach((item) => {\n const bucketName = item.querySelector('.bucket-name')?.textContent?.toLowerCase() || '';\n if (bucketName.includes(this.bucketFilter.toLowerCase())) {\n (item as HTMLElement).style.display = '';\n } else {\n (item as HTMLElement).style.display = 'none';\n }\n });\n\n const visibleCount = doc.querySelectorAll('.bucket-item:not([style*=\"display: none\"])').length;\n if (visibleCount === 0) {\n return '<div class=\"empty-state\"><div class=\"empty-state-icon\">🔍</div><p>No buckets match your filter</p></div>';\n }\n\n return doc.body.innerHTML;\n },\n\n get filteredFilesHtml(): string {\n if (!this.fileFilter.trim() || !this.filesHtml) {\n return this.filesHtml;\n }\n\n const parser = new DOMParser();\n const doc = parser.parseFromString(this.filesHtml, 'text/html');\n const fileItems = doc.querySelectorAll('.file-item');\n\n fileItems.forEach((item) => {\n const fileName = item.querySelector('.file-name')?.textContent?.toLowerCase() || '';\n if (fileName.includes(this.fileFilter.toLowerCase())) {\n (item as HTMLElement).style.display = '';\n } else {\n (item as HTMLElement).style.display = 'none';\n }\n });\n\n const visibleCount = doc.querySelectorAll('.file-item:not([style*=\"display: none\"])').length;\n if (visibleCount === 0) {\n return '<div class=\"empty-state\"><div class=\"empty-state-icon\">🔍</div><p>No files match your filter</p></div>';\n }\n\n return doc.body.innerHTML;\n },\n\n // Initialization\n async init(): Promise<void> {\n await this.loadBuckets();\n this.restoreStateFromUrl();\n this.setupUrlSync();\n },\n\n // Deep linking: restore state from URL\n restoreStateFromUrl(): void {\n const params = new URLSearchParams(window.location.search);\n const bucket = params.get('bucket');\n const prefix = params.get('prefix') || '';\n\n if (bucket) {\n this.selectedBucket = bucket;\n this.currentPrefix = prefix;\n this.loadFiles();\n }\n },\n\n // Deep linking: update URL when state changes\n updateUrl(): void {\n const params = new URLSearchParams();\n\n if (this.selectedBucket) {\n params.set('bucket', this.selectedBucket);\n\n if (this.currentPrefix) {\n params.set('prefix', this.currentPrefix);\n }\n }\n\n const newUrl = params.toString()\n ? `${window.location.pathname}?${params.toString()}`\n : window.location.pathname;\n\n window.history.pushState({}, '', newUrl);\n },\n\n // Setup browser back/forward button support\n setupUrlSync(): void {\n window.addEventListener('popstate', () => {\n this.restoreStateFromUrl();\n });\n },\n\n // API Methods\n async loadBuckets(): Promise<void> {\n try {\n const response = await fetch('/api/buckets');\n this.bucketsHtml = await response.text();\n } catch (error) {\n this.bucketsHtml = '<div class=\"error\"><div class=\"error-icon\">⚠️</div><p>Error loading buckets</p></div>';\n }\n },\n\n async selectBucket(bucketName: string): Promise<void> {\n this.selectedBucket = bucketName;\n this.currentPrefix = '';\n this.fileFilter = '';\n this.updateUrl();\n await this.loadFiles();\n },\n\n async loadFiles(): Promise<void> {\n try {\n const params = new URLSearchParams({\n bucket: this.selectedBucket,\n prefix: this.currentPrefix\n });\n\n const response = await fetch(`/api/files?${params}`);\n const data = await response.json();\n\n this.filesHtml = data.filesHtml || '';\n this.breadcrumbsHtml = data.breadcrumbsHtml || '';\n } catch (error) {\n this.filesHtml = '<div class=\"error\"><div class=\"error-icon\">⚠️</div><p>Error loading files</p></div>';\n }\n },\n\n async navigateToFolder(prefix: string): Promise<void> {\n this.currentPrefix = prefix;\n this.fileFilter = '';\n this.updateUrl();\n await this.loadFiles();\n },\n\n async previewFile(key: string): Promise<void> {\n try {\n const params = new URLSearchParams({\n bucket: this.selectedBucket,\n key: key\n });\n\n const response = await fetch(`/api/files/preview?${params}`);\n this.previewHtml = await response.text();\n this.showPreview = true;\n } catch (error) {\n alert('Error loading preview');\n }\n },\n\n closePreview(): void {\n this.showPreview = false;\n this.previewHtml = '';\n },\n\n async downloadFile(key: string): Promise<void> {\n try {\n const params = new URLSearchParams({\n bucket: this.selectedBucket,\n key: key\n });\n\n const response = await fetch(`/api/files/download?${params}`);\n const data = await response.json();\n window.open(data.url, '_blank');\n } catch (error) {\n alert('Error generating download link');\n }\n },\n\n async deleteFile(key: string): Promise<void> {\n if (!confirm(`Delete ${key}?`)) return;\n\n try {\n await fetch(`/api/files/delete`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ bucket: this.selectedBucket, key })\n });\n await this.loadFiles();\n } catch (error) {\n alert('Error deleting file');\n }\n },\n\n async copyS3Path(key: string): Promise<void> {\n const element = (event as any)?.target as HTMLElement | undefined;\n await copyS3PathToClipboard(this.selectedBucket, key, element);\n },\n\n async deleteFolder(prefix: string): Promise<void> {\n if (!confirm(`Delete folder ${prefix} and all its contents?`)) return;\n\n try {\n await fetch(`/api/files/delete-folder`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ bucket: this.selectedBucket, prefix })\n });\n await this.loadFiles();\n } catch (error) {\n alert('Error deleting folder');\n }\n },\n\n // Upload functionality\n handleFileSelect(event: Event): void {\n const input = event.target as HTMLInputElement;\n if (input.files) {\n const files = Array.from(input.files);\n this.uploadFiles(files);\n }\n },\n\n handleDrop(event: DragEvent): void {\n (event.target as HTMLElement).classList.remove('drag-over');\n if (event.dataTransfer?.files) {\n const files = Array.from(event.dataTransfer.files);\n this.uploadFiles(files);\n }\n },\n\n async uploadFiles(files: File[]): Promise<void> {\n this.uploadProgress = files.map(f => ({ name: f.name, status: 'Uploading...' }));\n\n for (let i = 0; i < files.length; i++) {\n const file = files[i];\n const formData = new FormData();\n formData.append('file', file);\n formData.append('bucket', this.selectedBucket);\n formData.append('prefix', this.currentPrefix);\n\n try {\n await fetch('/api/upload', {\n method: 'POST',\n body: formData\n });\n this.uploadProgress[i].status = '✓ Complete';\n } catch (error) {\n this.uploadProgress[i].status = '✗ Failed';\n }\n }\n\n setTimeout(async () => {\n this.uploadProgress = [];\n this.showUpload = false;\n await this.loadFiles();\n }, 2000);\n },\n\n async refresh(): Promise<void> {\n await this.loadFiles();\n },\n\n filterBuckets(): void {\n // Reactive computed property handles this\n },\n\n filterFiles(): void {\n // Reactive computed property handles this\n }\n };\n}\n\n// Auto-register with Alpine when it initializes\nif (typeof window !== 'undefined') {\n // Register immediately if Alpine is already available\n if ((window as any).Alpine) {\n (window as any).Alpine.data('s3Explorer', s3Explorer);\n } else {\n // Otherwise wait for Alpine to initialize\n document.addEventListener('alpine:init', () => {\n (window as any).Alpine.data('s3Explorer', s3Explorer);\n });\n }\n}\n","/**\n * Client-side utility for clipboard operations\n */\n\nexport async function copyToClipboard(text: string, element?: HTMLElement): Promise<boolean> {\n try {\n await navigator.clipboard.writeText(text);\n\n // Provide visual feedback if element is provided\n if (element) {\n const originalText = element.textContent || '';\n const originalBg = element.style.backgroundColor;\n\n element.textContent = '✓ Copied!';\n element.style.backgroundColor = 'var(--color-success)';\n\n setTimeout(() => {\n element.textContent = originalText;\n element.style.backgroundColor = originalBg;\n }, 1500);\n }\n\n return true;\n } catch (error) {\n console.error('Clipboard API failed:', error);\n\n // Fallback to prompt\n const userCopy = prompt('Copy to clipboard:', text);\n return userCopy !== null;\n }\n}\n\nexport async function copyS3PathToClipboard(\n bucket: string,\n key: string,\n element?: HTMLElement\n): Promise<void> {\n try {\n // Fetch bucket region\n const response = await fetch(`/api/buckets/${encodeURIComponent(bucket)}/region`);\n const data = await response.json();\n const region = data.region || 'us-east-1';\n\n // Construct HTTPS URL\n const url = `https://s3.${region}.amazonaws.com/${bucket}/${key}`;\n\n await copyToClipboard(url, element);\n } catch (error) {\n console.error('Error getting S3 URL:', error);\n\n // Fallback to s3:// protocol\n const s3Path = `s3://${bucket}/${key}`;\n prompt('Copy S3 path (fallback):', s3Path);\n }\n}\n"],"names":["s3Explorer","selectedBucket","currentPrefix","buckets","files","folders","viewMode","bucketsHtml","filesHtml","breadcrumbsHtml","bucketFilter","fileFilter","showUpload","showPreview","uploadProgress","uploadProgressHtml","previewHtml","activeTab","filteredBucketsHtml","this","trim","doc","DOMParser","parseFromString","querySelectorAll","forEach","item","querySelector","textContent","toLowerCase","includes","style","display","length","body","innerHTML","filteredFilesHtml","init","loadBuckets","restoreStateFromUrl","setupUrlSync","params","URLSearchParams","window","location","search","bucket","get","prefix","loadFiles","updateUrl","set","newUrl","toString","pathname","history","pushState","addEventListener","response","fetch","text","error","selectBucket","bucketName","data","json","navigateToFolder","previewFile","key","alert","closePreview","downloadFile","open","url","deleteFile","confirm","method","headers","JSON","stringify","copyS3Path","element","event","target","async","encodeURIComponent","region","navigator","clipboard","writeText","originalText","originalBg","backgroundColor","setTimeout","console","prompt","copyToClipboard","copyS3PathToClipboard","deleteFolder","handleFileSelect","input","Array","from","uploadFiles","handleDrop","classList","remove","dataTransfer","map","f","name","status","i","file","formData","FormData","append","refresh","filterBuckets","filterFiles","Alpine","document"],"sourceRoot":""}
|