@walkthru-earth/objex 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +396 -0
- package/README.md +114 -0
- package/dist/assets/favicon.svg +17 -0
- package/dist/components/CLAUDE.md +44 -0
- package/dist/components/browser/Breadcrumb.svelte +50 -0
- package/dist/components/browser/Breadcrumb.svelte.d.ts +7 -0
- package/dist/components/browser/CreateFolderDialog.svelte +98 -0
- package/dist/components/browser/CreateFolderDialog.svelte.d.ts +6 -0
- package/dist/components/browser/DeleteConfirmDialog.svelte +90 -0
- package/dist/components/browser/DeleteConfirmDialog.svelte.d.ts +8 -0
- package/dist/components/browser/DropZone.svelte +83 -0
- package/dist/components/browser/DropZone.svelte.d.ts +7 -0
- package/dist/components/browser/FileBrowser.svelte +229 -0
- package/dist/components/browser/FileBrowser.svelte.d.ts +3 -0
- package/dist/components/browser/FileRow.svelte +112 -0
- package/dist/components/browser/FileRow.svelte.d.ts +9 -0
- package/dist/components/browser/FileTreeSidebar.svelte +559 -0
- package/dist/components/browser/FileTreeSidebar.svelte.d.ts +8 -0
- package/dist/components/browser/RenameDialog.svelte +101 -0
- package/dist/components/browser/RenameDialog.svelte.d.ts +8 -0
- package/dist/components/browser/SearchBar.svelte +40 -0
- package/dist/components/browser/SearchBar.svelte.d.ts +6 -0
- package/dist/components/browser/UploadButton.svelte +65 -0
- package/dist/components/browser/UploadButton.svelte.d.ts +3 -0
- package/dist/components/editor/CodeMirrorEditor.svelte +404 -0
- package/dist/components/editor/CodeMirrorEditor.svelte.d.ts +12 -0
- package/dist/components/editor/MilkdownEditor.svelte +98 -0
- package/dist/components/editor/MilkdownEditor.svelte.d.ts +9 -0
- package/dist/components/editor/SqlEditor.svelte +173 -0
- package/dist/components/editor/SqlEditor.svelte.d.ts +7 -0
- package/dist/components/editor/SqlResultBlock.svelte +199 -0
- package/dist/components/editor/SqlResultBlock.svelte.d.ts +9 -0
- package/dist/components/layout/ConnectionDialog.svelte +439 -0
- package/dist/components/layout/ConnectionDialog.svelte.d.ts +9 -0
- package/dist/components/layout/LocaleToggle.svelte +32 -0
- package/dist/components/layout/LocaleToggle.svelte.d.ts +3 -0
- package/dist/components/layout/SafeLockToggle.svelte +37 -0
- package/dist/components/layout/SafeLockToggle.svelte.d.ts +18 -0
- package/dist/components/layout/Sidebar.svelte +314 -0
- package/dist/components/layout/Sidebar.svelte.d.ts +3 -0
- package/dist/components/layout/StatusBar.svelte +73 -0
- package/dist/components/layout/StatusBar.svelte.d.ts +3 -0
- package/dist/components/layout/TabBar.svelte +102 -0
- package/dist/components/layout/TabBar.svelte.d.ts +7 -0
- package/dist/components/layout/ThemeToggle.svelte +52 -0
- package/dist/components/layout/ThemeToggle.svelte.d.ts +3 -0
- package/dist/components/ui/badge/badge.svelte +49 -0
- package/dist/components/ui/badge/badge.svelte.d.ts +32 -0
- package/dist/components/ui/badge/index.d.ts +1 -0
- package/dist/components/ui/badge/index.js +1 -0
- package/dist/components/ui/button/button.svelte +82 -0
- package/dist/components/ui/button/button.svelte.d.ts +64 -0
- package/dist/components/ui/button/index.d.ts +2 -0
- package/dist/components/ui/button/index.js +4 -0
- package/dist/components/ui/context-menu/context-menu-checkbox-item.svelte +40 -0
- package/dist/components/ui/context-menu/context-menu-checkbox-item.svelte.d.ts +9 -0
- package/dist/components/ui/context-menu/context-menu-content.svelte +28 -0
- package/dist/components/ui/context-menu/context-menu-content.svelte.d.ts +10 -0
- package/dist/components/ui/context-menu/context-menu-group-heading.svelte +21 -0
- package/dist/components/ui/context-menu/context-menu-group-heading.svelte.d.ts +7 -0
- package/dist/components/ui/context-menu/context-menu-group.svelte +7 -0
- package/dist/components/ui/context-menu/context-menu-group.svelte.d.ts +4 -0
- package/dist/components/ui/context-menu/context-menu-item.svelte +27 -0
- package/dist/components/ui/context-menu/context-menu-item.svelte.d.ts +8 -0
- package/dist/components/ui/context-menu/context-menu-label.svelte +24 -0
- package/dist/components/ui/context-menu/context-menu-label.svelte.d.ts +8 -0
- package/dist/components/ui/context-menu/context-menu-portal.svelte +7 -0
- package/dist/components/ui/context-menu/context-menu-portal.svelte.d.ts +3 -0
- package/dist/components/ui/context-menu/context-menu-radio-group.svelte +16 -0
- package/dist/components/ui/context-menu/context-menu-radio-group.svelte.d.ts +4 -0
- package/dist/components/ui/context-menu/context-menu-radio-item.svelte +33 -0
- package/dist/components/ui/context-menu/context-menu-radio-item.svelte.d.ts +4 -0
- package/dist/components/ui/context-menu/context-menu-separator.svelte +17 -0
- package/dist/components/ui/context-menu/context-menu-separator.svelte.d.ts +4 -0
- package/dist/components/ui/context-menu/context-menu-shortcut.svelte +20 -0
- package/dist/components/ui/context-menu/context-menu-shortcut.svelte.d.ts +5 -0
- package/dist/components/ui/context-menu/context-menu-sub-content.svelte +20 -0
- package/dist/components/ui/context-menu/context-menu-sub-content.svelte.d.ts +4 -0
- package/dist/components/ui/context-menu/context-menu-sub-trigger.svelte +29 -0
- package/dist/components/ui/context-menu/context-menu-sub-trigger.svelte.d.ts +8 -0
- package/dist/components/ui/context-menu/context-menu-sub.svelte +7 -0
- package/dist/components/ui/context-menu/context-menu-sub.svelte.d.ts +3 -0
- package/dist/components/ui/context-menu/context-menu-trigger.svelte +7 -0
- package/dist/components/ui/context-menu/context-menu-trigger.svelte.d.ts +4 -0
- package/dist/components/ui/context-menu/context-menu.svelte +7 -0
- package/dist/components/ui/context-menu/context-menu.svelte.d.ts +3 -0
- package/dist/components/ui/context-menu/index.d.ts +17 -0
- package/dist/components/ui/context-menu/index.js +19 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte +16 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte.d.ts +4 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte +43 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte.d.ts +9 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-content.svelte +29 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-content.svelte.d.ts +10 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte +22 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte.d.ts +8 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-group.svelte +7 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-group.svelte.d.ts +4 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-item.svelte +27 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-item.svelte.d.ts +8 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-label.svelte +24 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-label.svelte.d.ts +8 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-portal.svelte +7 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-portal.svelte.d.ts +3 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte +16 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte.d.ts +4 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte +33 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte.d.ts +4 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-separator.svelte +17 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-separator.svelte.d.ts +4 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte +20 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte.d.ts +5 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte +20 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte.d.ts +4 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte +29 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte.d.ts +7 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-sub.svelte +7 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-sub.svelte.d.ts +3 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-trigger.svelte +7 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu-trigger.svelte.d.ts +4 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu.svelte +7 -0
- package/dist/components/ui/dropdown-menu/dropdown-menu.svelte.d.ts +3 -0
- package/dist/components/ui/dropdown-menu/index.d.ts +18 -0
- package/dist/components/ui/dropdown-menu/index.js +18 -0
- package/dist/components/ui/input/index.d.ts +2 -0
- package/dist/components/ui/input/index.js +4 -0
- package/dist/components/ui/input/input.svelte +52 -0
- package/dist/components/ui/input/input.svelte.d.ts +13 -0
- package/dist/components/ui/resizable/index.d.ts +4 -0
- package/dist/components/ui/resizable/index.js +6 -0
- package/dist/components/ui/resizable/resizable-handle.svelte +30 -0
- package/dist/components/ui/resizable/resizable-handle.svelte.d.ts +8 -0
- package/dist/components/ui/resizable/resizable-pane-group.svelte +20 -0
- package/dist/components/ui/resizable/resizable-pane-group.svelte.d.ts +7 -0
- package/dist/components/ui/scroll-area/index.d.ts +3 -0
- package/dist/components/ui/scroll-area/index.js +5 -0
- package/dist/components/ui/scroll-area/scroll-area-scrollbar.svelte +31 -0
- package/dist/components/ui/scroll-area/scroll-area-scrollbar.svelte.d.ts +4 -0
- package/dist/components/ui/scroll-area/scroll-area.svelte +47 -0
- package/dist/components/ui/scroll-area/scroll-area.svelte.d.ts +11 -0
- package/dist/components/ui/separator/index.d.ts +2 -0
- package/dist/components/ui/separator/index.js +4 -0
- package/dist/components/ui/separator/separator.svelte +21 -0
- package/dist/components/ui/separator/separator.svelte.d.ts +4 -0
- package/dist/components/ui/sheet/index.d.ts +11 -0
- package/dist/components/ui/sheet/index.js +13 -0
- package/dist/components/ui/sheet/sheet-close.svelte +7 -0
- package/dist/components/ui/sheet/sheet-close.svelte.d.ts +4 -0
- package/dist/components/ui/sheet/sheet-content.svelte +62 -0
- package/dist/components/ui/sheet/sheet-content.svelte.d.ts +37 -0
- package/dist/components/ui/sheet/sheet-description.svelte +17 -0
- package/dist/components/ui/sheet/sheet-description.svelte.d.ts +4 -0
- package/dist/components/ui/sheet/sheet-footer.svelte +20 -0
- package/dist/components/ui/sheet/sheet-footer.svelte.d.ts +5 -0
- package/dist/components/ui/sheet/sheet-header.svelte +20 -0
- package/dist/components/ui/sheet/sheet-header.svelte.d.ts +5 -0
- package/dist/components/ui/sheet/sheet-overlay.svelte +20 -0
- package/dist/components/ui/sheet/sheet-overlay.svelte.d.ts +4 -0
- package/dist/components/ui/sheet/sheet-portal.svelte +7 -0
- package/dist/components/ui/sheet/sheet-portal.svelte.d.ts +3 -0
- package/dist/components/ui/sheet/sheet-title.svelte +13 -0
- package/dist/components/ui/sheet/sheet-title.svelte.d.ts +4 -0
- package/dist/components/ui/sheet/sheet-trigger.svelte +7 -0
- package/dist/components/ui/sheet/sheet-trigger.svelte.d.ts +4 -0
- package/dist/components/ui/sheet/sheet.svelte +7 -0
- package/dist/components/ui/sheet/sheet.svelte.d.ts +3 -0
- package/dist/components/ui/switch/index.d.ts +2 -0
- package/dist/components/ui/switch/index.js +4 -0
- package/dist/components/ui/switch/switch.svelte +28 -0
- package/dist/components/ui/switch/switch.svelte.d.ts +8 -0
- package/dist/components/ui/tabs/index.d.ts +5 -0
- package/dist/components/ui/tabs/index.js +7 -0
- package/dist/components/ui/tabs/tabs-content.svelte +17 -0
- package/dist/components/ui/tabs/tabs-content.svelte.d.ts +4 -0
- package/dist/components/ui/tabs/tabs-list.svelte +16 -0
- package/dist/components/ui/tabs/tabs-list.svelte.d.ts +4 -0
- package/dist/components/ui/tabs/tabs-trigger.svelte +20 -0
- package/dist/components/ui/tabs/tabs-trigger.svelte.d.ts +4 -0
- package/dist/components/ui/tabs/tabs.svelte +19 -0
- package/dist/components/ui/tabs/tabs.svelte.d.ts +4 -0
- package/dist/components/ui/tooltip/index.d.ts +6 -0
- package/dist/components/ui/tooltip/index.js +8 -0
- package/dist/components/ui/tooltip/tooltip-content.svelte +52 -0
- package/dist/components/ui/tooltip/tooltip-content.svelte.d.ts +11 -0
- package/dist/components/ui/tooltip/tooltip-portal.svelte +7 -0
- package/dist/components/ui/tooltip/tooltip-portal.svelte.d.ts +4 -0
- package/dist/components/ui/tooltip/tooltip-provider.svelte +7 -0
- package/dist/components/ui/tooltip/tooltip-provider.svelte.d.ts +4 -0
- package/dist/components/ui/tooltip/tooltip-trigger.svelte +7 -0
- package/dist/components/ui/tooltip/tooltip-trigger.svelte.d.ts +4 -0
- package/dist/components/ui/tooltip/tooltip.svelte +7 -0
- package/dist/components/ui/tooltip/tooltip.svelte.d.ts +4 -0
- package/dist/components/viewers/ArchiveViewer.svelte +586 -0
- package/dist/components/viewers/ArchiveViewer.svelte.d.ts +7 -0
- package/dist/components/viewers/CLAUDE.md +60 -0
- package/dist/components/viewers/CodeViewer.svelte +553 -0
- package/dist/components/viewers/CodeViewer.svelte.d.ts +7 -0
- package/dist/components/viewers/CogViewer.svelte +1345 -0
- package/dist/components/viewers/CogViewer.svelte.d.ts +7 -0
- package/dist/components/viewers/CopcViewer.svelte +25 -0
- package/dist/components/viewers/CopcViewer.svelte.d.ts +7 -0
- package/dist/components/viewers/DatabaseViewer.svelte +169 -0
- package/dist/components/viewers/DatabaseViewer.svelte.d.ts +7 -0
- package/dist/components/viewers/FileInfo.svelte +174 -0
- package/dist/components/viewers/FileInfo.svelte.d.ts +10 -0
- package/dist/components/viewers/FlatGeobufViewer.svelte +755 -0
- package/dist/components/viewers/FlatGeobufViewer.svelte.d.ts +7 -0
- package/dist/components/viewers/GeoParquetMapViewer.svelte +278 -0
- package/dist/components/viewers/GeoParquetMapViewer.svelte.d.ts +17 -0
- package/dist/components/viewers/ImageViewer.svelte +233 -0
- package/dist/components/viewers/ImageViewer.svelte.d.ts +7 -0
- package/dist/components/viewers/LoadProgress.svelte +93 -0
- package/dist/components/viewers/LoadProgress.svelte.d.ts +15 -0
- package/dist/components/viewers/MapViewer.svelte +234 -0
- package/dist/components/viewers/MapViewer.svelte.d.ts +7 -0
- package/dist/components/viewers/MarkdownViewer.svelte +478 -0
- package/dist/components/viewers/MarkdownViewer.svelte.d.ts +7 -0
- package/dist/components/viewers/MediaViewer.svelte +121 -0
- package/dist/components/viewers/MediaViewer.svelte.d.ts +7 -0
- package/dist/components/viewers/ModelViewer.svelte +164 -0
- package/dist/components/viewers/ModelViewer.svelte.d.ts +7 -0
- package/dist/components/viewers/NotebookViewer.svelte +389 -0
- package/dist/components/viewers/NotebookViewer.svelte.d.ts +7 -0
- package/dist/components/viewers/PdfViewer.svelte +278 -0
- package/dist/components/viewers/PdfViewer.svelte.d.ts +7 -0
- package/dist/components/viewers/PmtilesViewer.svelte +191 -0
- package/dist/components/viewers/PmtilesViewer.svelte.d.ts +7 -0
- package/dist/components/viewers/QueryHistoryPanel.svelte +159 -0
- package/dist/components/viewers/QueryHistoryPanel.svelte.d.ts +8 -0
- package/dist/components/viewers/RawViewer.svelte +117 -0
- package/dist/components/viewers/RawViewer.svelte.d.ts +7 -0
- package/dist/components/viewers/StacMapViewer.svelte +20 -0
- package/dist/components/viewers/StacMapViewer.svelte.d.ts +7 -0
- package/dist/components/viewers/StyleEditorOverlay.svelte +27 -0
- package/dist/components/viewers/StyleEditorOverlay.svelte.d.ts +7 -0
- package/dist/components/viewers/TableGrid.svelte +355 -0
- package/dist/components/viewers/TableGrid.svelte.d.ts +12 -0
- package/dist/components/viewers/TableStatusBar.svelte +92 -0
- package/dist/components/viewers/TableStatusBar.svelte.d.ts +11 -0
- package/dist/components/viewers/TableToolbar.svelte +382 -0
- package/dist/components/viewers/TableToolbar.svelte.d.ts +25 -0
- package/dist/components/viewers/TableViewer.svelte +923 -0
- package/dist/components/viewers/TableViewer.svelte.d.ts +7 -0
- package/dist/components/viewers/ViewerRouter.svelte +70 -0
- package/dist/components/viewers/ViewerRouter.svelte.d.ts +7 -0
- package/dist/components/viewers/ZarrMapViewer.svelte +288 -0
- package/dist/components/viewers/ZarrMapViewer.svelte.d.ts +17 -0
- package/dist/components/viewers/ZarrViewer.svelte +256 -0
- package/dist/components/viewers/ZarrViewer.svelte.d.ts +7 -0
- package/dist/components/viewers/map/AttributeTable.svelte +52 -0
- package/dist/components/viewers/map/AttributeTable.svelte.d.ts +8 -0
- package/dist/components/viewers/map/MapContainer.svelte +158 -0
- package/dist/components/viewers/map/MapContainer.svelte.d.ts +12 -0
- package/dist/components/viewers/pmtiles/PmtilesArchiveView.svelte +389 -0
- package/dist/components/viewers/pmtiles/PmtilesArchiveView.svelte.d.ts +10 -0
- package/dist/components/viewers/pmtiles/PmtilesMapView.svelte +332 -0
- package/dist/components/viewers/pmtiles/PmtilesMapView.svelte.d.ts +11 -0
- package/dist/components/viewers/pmtiles/PmtilesTileInspector.svelte +373 -0
- package/dist/components/viewers/pmtiles/PmtilesTileInspector.svelte.d.ts +12 -0
- package/dist/components/viewers/pmtiles/SvgTileRenderer.svelte +112 -0
- package/dist/components/viewers/pmtiles/SvgTileRenderer.svelte.d.ts +10 -0
- package/dist/file-icons/CLAUDE.md +21 -0
- package/dist/file-icons/FileTypeIcon.svelte +74 -0
- package/dist/file-icons/FileTypeIcon.svelte.d.ts +9 -0
- package/dist/file-icons/index.d.ts +56 -0
- package/dist/file-icons/index.js +1070 -0
- package/dist/i18n/CLAUDE.md +19 -0
- package/dist/i18n/ar.d.ts +1 -0
- package/dist/i18n/ar.js +404 -0
- package/dist/i18n/en.d.ts +1 -0
- package/dist/i18n/en.js +404 -0
- package/dist/i18n/index.svelte.d.ts +9 -0
- package/dist/i18n/index.svelte.js +27 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +13 -0
- package/dist/query/CLAUDE.md +22 -0
- package/dist/query/engine.d.ts +56 -0
- package/dist/query/engine.js +6 -0
- package/dist/query/index.d.ts +4 -0
- package/dist/query/index.js +19 -0
- package/dist/query/wasm.d.ts +20 -0
- package/dist/query/wasm.js +890 -0
- package/dist/storage/CLAUDE.md +23 -0
- package/dist/storage/adapter.d.ts +21 -0
- package/dist/storage/adapter.js +1 -0
- package/dist/storage/browser-azure.d.ts +25 -0
- package/dist/storage/browser-azure.js +271 -0
- package/dist/storage/browser-cloud.d.ts +32 -0
- package/dist/storage/browser-cloud.js +293 -0
- package/dist/storage/index.d.ts +11 -0
- package/dist/storage/index.js +37 -0
- package/dist/storage/url-adapter.d.ts +19 -0
- package/dist/storage/url-adapter.js +51 -0
- package/dist/stores/CLAUDE.md +29 -0
- package/dist/stores/browser.svelte.d.ts +28 -0
- package/dist/stores/browser.svelte.js +160 -0
- package/dist/stores/connections.svelte.d.ts +56 -0
- package/dist/stores/connections.svelte.js +272 -0
- package/dist/stores/credentials.svelte.d.ts +56 -0
- package/dist/stores/credentials.svelte.js +79 -0
- package/dist/stores/files.svelte.d.ts +20 -0
- package/dist/stores/files.svelte.js +76 -0
- package/dist/stores/query-history.svelte.d.ts +16 -0
- package/dist/stores/query-history.svelte.js +57 -0
- package/dist/stores/safelock.svelte.d.ts +8 -0
- package/dist/stores/safelock.svelte.js +52 -0
- package/dist/stores/settings.svelte.d.ts +11 -0
- package/dist/stores/settings.svelte.js +101 -0
- package/dist/stores/tab-resources.svelte.d.ts +25 -0
- package/dist/stores/tab-resources.svelte.js +61 -0
- package/dist/stores/tabs.svelte.d.ts +17 -0
- package/dist/stores/tabs.svelte.js +110 -0
- package/dist/types/notebookjs.d.ts +14 -0
- package/dist/types.d.ts +47 -0
- package/dist/types.js +1 -0
- package/dist/utils/CLAUDE.md +54 -0
- package/dist/utils/analytics.d.ts +10 -0
- package/dist/utils/analytics.js +38 -0
- package/dist/utils/archive.d.ts +70 -0
- package/dist/utils/archive.js +333 -0
- package/dist/utils/column-types.d.ts +5 -0
- package/dist/utils/column-types.js +137 -0
- package/dist/utils/deck.d.ts +98 -0
- package/dist/utils/deck.js +208 -0
- package/dist/utils/evidence-context.d.ts +22 -0
- package/dist/utils/evidence-context.js +56 -0
- package/dist/utils/export.d.ts +2 -0
- package/dist/utils/export.js +51 -0
- package/dist/utils/format.d.ts +14 -0
- package/dist/utils/format.js +56 -0
- package/dist/utils/geoarrow.d.ts +32 -0
- package/dist/utils/geoarrow.js +672 -0
- package/dist/utils/hex.d.ts +10 -0
- package/dist/utils/hex.js +27 -0
- package/dist/utils/host-detection.d.ts +23 -0
- package/dist/utils/host-detection.js +289 -0
- package/dist/utils/map-selection.d.ts +12 -0
- package/dist/utils/map-selection.js +45 -0
- package/dist/utils/markdown-sql.d.ts +30 -0
- package/dist/utils/markdown-sql.js +73 -0
- package/dist/utils/markdown.d.ts +18 -0
- package/dist/utils/markdown.js +146 -0
- package/dist/utils/model3d.d.ts +13 -0
- package/dist/utils/model3d.js +62 -0
- package/dist/utils/parquet-metadata.d.ts +58 -0
- package/dist/utils/parquet-metadata.js +228 -0
- package/dist/utils/pdf.d.ts +8 -0
- package/dist/utils/pdf.js +28 -0
- package/dist/utils/pmtiles-tile.d.ts +38 -0
- package/dist/utils/pmtiles-tile.js +64 -0
- package/dist/utils/pmtiles.d.ts +46 -0
- package/dist/utils/pmtiles.js +135 -0
- package/dist/utils/shiki.d.ts +8 -0
- package/dist/utils/shiki.js +98 -0
- package/dist/utils/storage-url.d.ts +64 -0
- package/dist/utils/storage-url.js +374 -0
- package/dist/utils/url-state.d.ts +40 -0
- package/dist/utils/url-state.js +113 -0
- package/dist/utils/url.d.ts +27 -0
- package/dist/utils/url.js +115 -0
- package/dist/utils/wkb.d.ts +43 -0
- package/dist/utils/wkb.js +345 -0
- package/dist/utils/zarr.d.ts +39 -0
- package/dist/utils/zarr.js +204 -0
- package/dist/utils.d.ts +12 -0
- package/dist/utils.js +5 -0
- package/package.json +203 -0
|
@@ -0,0 +1,559 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import ChevronDownIcon from '@lucide/svelte/icons/chevron-down';
|
|
3
|
+
import ChevronRightIcon from '@lucide/svelte/icons/chevron-right';
|
|
4
|
+
import ClipboardCopyIcon from '@lucide/svelte/icons/clipboard-copy';
|
|
5
|
+
import ExternalLinkIcon from '@lucide/svelte/icons/external-link';
|
|
6
|
+
import FileTextIcon from '@lucide/svelte/icons/file-text';
|
|
7
|
+
import FolderOpenIcon from '@lucide/svelte/icons/folder-open';
|
|
8
|
+
import LinkIcon from '@lucide/svelte/icons/link';
|
|
9
|
+
import Loader2Icon from '@lucide/svelte/icons/loader-2';
|
|
10
|
+
import RefreshCwIcon from '@lucide/svelte/icons/refresh-cw';
|
|
11
|
+
import SearchIcon from '@lucide/svelte/icons/search';
|
|
12
|
+
import * as ContextMenu from '../ui/context-menu/index.js';
|
|
13
|
+
import FileTypeIcon from '../../file-icons/FileTypeIcon.svelte';
|
|
14
|
+
import { t } from '../../i18n/index.svelte.js';
|
|
15
|
+
import { getAdapter } from '../../storage/index.js';
|
|
16
|
+
import { browser } from '../../stores/browser.svelte.js';
|
|
17
|
+
import { tabs } from '../../stores/tabs.svelte.js';
|
|
18
|
+
import type { Connection, FileEntry } from '../../types.js';
|
|
19
|
+
import { getNativeScheme } from '../../utils/url.js';
|
|
20
|
+
import { syncUrlParam } from '../../utils/url-state.js';
|
|
21
|
+
|
|
22
|
+
let {
|
|
23
|
+
connection,
|
|
24
|
+
initialPath = ''
|
|
25
|
+
}: {
|
|
26
|
+
connection: Connection;
|
|
27
|
+
initialPath?: string;
|
|
28
|
+
} = $props();
|
|
29
|
+
|
|
30
|
+
/** Entries per API call. S3 default is 1000 — we use 200 for fast first paint. */
|
|
31
|
+
const PAGE_SIZE = 200;
|
|
32
|
+
|
|
33
|
+
interface TreeNode {
|
|
34
|
+
entry: FileEntry;
|
|
35
|
+
expanded: boolean;
|
|
36
|
+
children: TreeNode[];
|
|
37
|
+
loaded: boolean;
|
|
38
|
+
loading: boolean;
|
|
39
|
+
continuationToken?: string;
|
|
40
|
+
hasMore: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let rootNodes = $state<TreeNode[]>([]);
|
|
44
|
+
let rootLoading = $state(true);
|
|
45
|
+
let rootLoadingMore = $state(false);
|
|
46
|
+
let rootContinuationToken = $state<string | undefined>();
|
|
47
|
+
let rootHasMore = $state(false);
|
|
48
|
+
let filterQuery = $state('');
|
|
49
|
+
let scrollEl = $state<HTMLElement>();
|
|
50
|
+
|
|
51
|
+
const filteredNodes = $derived(
|
|
52
|
+
filterQuery ? filterTree(rootNodes, filterQuery.toLowerCase()) : rootNodes
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
function filterTree(nodes: TreeNode[], query: string): TreeNode[] {
|
|
56
|
+
const result: TreeNode[] = [];
|
|
57
|
+
for (const node of nodes) {
|
|
58
|
+
if (node.entry.name.toLowerCase().includes(query)) {
|
|
59
|
+
result.push(node);
|
|
60
|
+
} else if (node.entry.is_dir && node.children.length > 0) {
|
|
61
|
+
const filteredChildren = filterTree(node.children, query);
|
|
62
|
+
if (filteredChildren.length > 0) {
|
|
63
|
+
result.push({ ...node, children: filteredChildren, expanded: true });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function toTreeNode(entry: FileEntry): TreeNode {
|
|
71
|
+
return {
|
|
72
|
+
entry,
|
|
73
|
+
expanded: false,
|
|
74
|
+
children: [],
|
|
75
|
+
loaded: !entry.is_dir,
|
|
76
|
+
loading: false,
|
|
77
|
+
continuationToken: undefined,
|
|
78
|
+
hasMore: false
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ---------- IntersectionObserver sentinel action ----------
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Svelte action: fires `callback` when the element scrolls into view.
|
|
86
|
+
* Uses the tree scroll container as the observer root for correct detection.
|
|
87
|
+
*/
|
|
88
|
+
function lazyLoad(el: HTMLElement, callback: () => void) {
|
|
89
|
+
const observer = new IntersectionObserver(
|
|
90
|
+
(entries) => {
|
|
91
|
+
if (entries[0]?.isIntersecting) {
|
|
92
|
+
callback();
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
{ root: scrollEl, rootMargin: '200px' }
|
|
96
|
+
);
|
|
97
|
+
observer.observe(el);
|
|
98
|
+
return {
|
|
99
|
+
destroy() {
|
|
100
|
+
observer.disconnect();
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ---------- Data loading ----------
|
|
106
|
+
|
|
107
|
+
async function loadChildren(node: TreeNode) {
|
|
108
|
+
if (node.children.length > 0 || node.loading || !node.entry.is_dir) return;
|
|
109
|
+
node.loading = true;
|
|
110
|
+
try {
|
|
111
|
+
const adapter = getAdapter('remote', connection.id);
|
|
112
|
+
if (adapter.listPage) {
|
|
113
|
+
const firstPage = await adapter.listPage(node.entry.path, undefined, PAGE_SIZE);
|
|
114
|
+
node.children = firstPage.entries.map(toTreeNode);
|
|
115
|
+
node.continuationToken = firstPage.continuationToken;
|
|
116
|
+
node.hasMore = firstPage.hasMore;
|
|
117
|
+
node.loaded = !firstPage.hasMore;
|
|
118
|
+
} else {
|
|
119
|
+
const entries = await adapter.list(node.entry.path);
|
|
120
|
+
node.children = entries.map(toTreeNode);
|
|
121
|
+
node.loaded = true;
|
|
122
|
+
}
|
|
123
|
+
} catch (err) {
|
|
124
|
+
console.error('[FileTree] Error loading:', err);
|
|
125
|
+
} finally {
|
|
126
|
+
node.loading = false;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async function loadMoreChildren(node: TreeNode) {
|
|
131
|
+
if (!node.hasMore || node.loading || !node.continuationToken) return;
|
|
132
|
+
node.loading = true;
|
|
133
|
+
try {
|
|
134
|
+
const adapter = getAdapter('remote', connection.id);
|
|
135
|
+
if (adapter.listPage) {
|
|
136
|
+
const page = await adapter.listPage(node.entry.path, node.continuationToken, PAGE_SIZE);
|
|
137
|
+
node.children = [...node.children, ...page.entries.map(toTreeNode)];
|
|
138
|
+
node.continuationToken = page.continuationToken;
|
|
139
|
+
node.hasMore = page.hasMore;
|
|
140
|
+
node.loaded = !page.hasMore;
|
|
141
|
+
}
|
|
142
|
+
} catch (err) {
|
|
143
|
+
console.error('[FileTree] Error loading more:', err);
|
|
144
|
+
} finally {
|
|
145
|
+
node.loading = false;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async function toggleFolder(node: TreeNode) {
|
|
150
|
+
if (!node.entry.is_dir) return;
|
|
151
|
+
|
|
152
|
+
if (node.children.length === 0) {
|
|
153
|
+
await loadChildren(node);
|
|
154
|
+
}
|
|
155
|
+
node.expanded = !node.expanded;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function openFile(entry: FileEntry) {
|
|
159
|
+
tabs.open({
|
|
160
|
+
id: `${connection.id}:${entry.path}`,
|
|
161
|
+
name: entry.name,
|
|
162
|
+
path: entry.path,
|
|
163
|
+
source: 'remote',
|
|
164
|
+
connectionId: connection.id,
|
|
165
|
+
extension: entry.extension,
|
|
166
|
+
size: entry.size
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// Update URL for shareable links
|
|
170
|
+
syncUrlParam(connection, entry.path);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** Extensions that represent "virtual files" — directories that open as viewers. */
|
|
174
|
+
const VIEWER_DIR_EXTENSIONS = new Set(['zarr', 'zr3']);
|
|
175
|
+
|
|
176
|
+
function isViewerDir(entry: FileEntry): boolean {
|
|
177
|
+
return entry.is_dir && VIEWER_DIR_EXTENSIONS.has(entry.extension);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function handleNodeClick(node: TreeNode) {
|
|
181
|
+
if (isViewerDir(node.entry)) {
|
|
182
|
+
// .zarr directories open in the viewer (clicking chevron expands)
|
|
183
|
+
openFile(node.entry);
|
|
184
|
+
} else if (node.entry.is_dir) {
|
|
185
|
+
toggleFolder(node);
|
|
186
|
+
} else {
|
|
187
|
+
openFile(node.entry);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function handleChevronClick(e: MouseEvent, node: TreeNode) {
|
|
192
|
+
e.stopPropagation();
|
|
193
|
+
toggleFolder(node);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ---------- URL builders ----------
|
|
197
|
+
|
|
198
|
+
/** Build HTTPS URL for a file path. */
|
|
199
|
+
function buildHttpUrl(path: string): string {
|
|
200
|
+
const conn = connection;
|
|
201
|
+
if (conn.endpoint) {
|
|
202
|
+
const base = conn.endpoint.replace(/\/$/, '');
|
|
203
|
+
return `${base}/${conn.bucket}/${encodeKeyPath(path)}`;
|
|
204
|
+
}
|
|
205
|
+
// Default AWS S3
|
|
206
|
+
return `https://s3.${conn.region}.amazonaws.com/${conn.bucket}/${encodeKeyPath(path)}`;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** Build provider-native URI (s3://, gs://, r2://, az://). */
|
|
210
|
+
function buildNativeUri(path: string): string {
|
|
211
|
+
const conn = connection;
|
|
212
|
+
const scheme = getNativeScheme(conn.provider);
|
|
213
|
+
return `${scheme}://${conn.bucket}/${path}`;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// getNativeScheme imported from $lib/utils/url.js
|
|
217
|
+
|
|
218
|
+
function encodeKeyPath(key: string): string {
|
|
219
|
+
return key
|
|
220
|
+
.split('/')
|
|
221
|
+
.map((s) => encodeURIComponent(s))
|
|
222
|
+
.join('/');
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// ---------- Clipboard ----------
|
|
226
|
+
|
|
227
|
+
async function copyToClipboard(text: string) {
|
|
228
|
+
try {
|
|
229
|
+
await navigator.clipboard.writeText(text);
|
|
230
|
+
} catch {
|
|
231
|
+
// clipboard not available
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// ---------- Size formatting ----------
|
|
236
|
+
|
|
237
|
+
function formatSize(bytes: number): string {
|
|
238
|
+
if (bytes === 0) return '';
|
|
239
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
240
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
241
|
+
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
242
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// ---------- Date formatting ----------
|
|
246
|
+
|
|
247
|
+
function formatDate(timestamp: number): string {
|
|
248
|
+
if (!timestamp) return '';
|
|
249
|
+
return new Date(timestamp).toLocaleDateString(undefined, {
|
|
250
|
+
year: 'numeric',
|
|
251
|
+
month: 'short',
|
|
252
|
+
day: 'numeric',
|
|
253
|
+
hour: '2-digit',
|
|
254
|
+
minute: '2-digit'
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// ---------- Path expansion (for shared URLs) ----------
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Search for a node at a given level, loading more pages if needed.
|
|
262
|
+
* Used by expandToPath to navigate to a deep path that may be beyond the first page.
|
|
263
|
+
*/
|
|
264
|
+
async function findNodeAtRoot(targetPath: string): Promise<TreeNode | undefined> {
|
|
265
|
+
let node = rootNodes.find((n) => n.entry.path === targetPath);
|
|
266
|
+
if (node) return node;
|
|
267
|
+
|
|
268
|
+
const adapter = getAdapter('remote', connection.id);
|
|
269
|
+
const prefix = connection.rootPrefix ?? '';
|
|
270
|
+
while (rootHasMore && rootContinuationToken && adapter.listPage) {
|
|
271
|
+
const page = await adapter.listPage(prefix, rootContinuationToken, PAGE_SIZE);
|
|
272
|
+
const newNodes = page.entries.map(toTreeNode);
|
|
273
|
+
rootNodes = [...rootNodes, ...newNodes];
|
|
274
|
+
rootContinuationToken = page.continuationToken;
|
|
275
|
+
rootHasMore = page.hasMore;
|
|
276
|
+
|
|
277
|
+
node = newNodes.find((n) => n.entry.path === targetPath);
|
|
278
|
+
if (node) return node;
|
|
279
|
+
}
|
|
280
|
+
return undefined;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async function findNodeInParent(
|
|
284
|
+
parent: TreeNode,
|
|
285
|
+
targetPath: string
|
|
286
|
+
): Promise<TreeNode | undefined> {
|
|
287
|
+
if (parent.children.length === 0) {
|
|
288
|
+
await loadChildren(parent);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
let node = parent.children.find((n) => n.entry.path === targetPath);
|
|
292
|
+
if (node) return node;
|
|
293
|
+
|
|
294
|
+
const adapter = getAdapter('remote', connection.id);
|
|
295
|
+
while (parent.hasMore && parent.continuationToken && adapter.listPage) {
|
|
296
|
+
const page = await adapter.listPage(parent.entry.path, parent.continuationToken, PAGE_SIZE);
|
|
297
|
+
const newNodes = page.entries.map(toTreeNode);
|
|
298
|
+
parent.children = [...parent.children, ...newNodes];
|
|
299
|
+
parent.continuationToken = page.continuationToken;
|
|
300
|
+
parent.hasMore = page.hasMore;
|
|
301
|
+
parent.loaded = !page.hasMore;
|
|
302
|
+
|
|
303
|
+
node = newNodes.find((n) => n.entry.path === targetPath);
|
|
304
|
+
if (node) return node;
|
|
305
|
+
}
|
|
306
|
+
return undefined;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Auto-expand tree directories along a given file path.
|
|
311
|
+
* Loads additional pages as needed to find each path segment.
|
|
312
|
+
*/
|
|
313
|
+
async function expandToPath(path: string) {
|
|
314
|
+
if (!path) return;
|
|
315
|
+
|
|
316
|
+
const prefix = connection.rootPrefix ?? '';
|
|
317
|
+
const relativePath = path.startsWith(prefix) ? path.slice(prefix.length) : path;
|
|
318
|
+
const segments = relativePath.split('/').filter(Boolean);
|
|
319
|
+
|
|
320
|
+
let accumulatedPath = prefix;
|
|
321
|
+
let parentNode: TreeNode | null = null;
|
|
322
|
+
|
|
323
|
+
// Expand each directory segment (skip the last segment — it's the file)
|
|
324
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
325
|
+
accumulatedPath += `${segments[i]}/`;
|
|
326
|
+
|
|
327
|
+
const node: TreeNode | undefined =
|
|
328
|
+
parentNode === null
|
|
329
|
+
? await findNodeAtRoot(accumulatedPath)
|
|
330
|
+
: await findNodeInParent(parentNode, accumulatedPath);
|
|
331
|
+
|
|
332
|
+
if (!node || !node.entry.is_dir) break;
|
|
333
|
+
|
|
334
|
+
if (node.children.length === 0) {
|
|
335
|
+
await loadChildren(node);
|
|
336
|
+
}
|
|
337
|
+
node.expanded = true;
|
|
338
|
+
parentNode = node;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// ---------- Root loading ----------
|
|
343
|
+
|
|
344
|
+
// Load root entries when connection changes
|
|
345
|
+
$effect(() => {
|
|
346
|
+
const _connId = connection.id;
|
|
347
|
+
loadRoot();
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
async function loadRoot() {
|
|
351
|
+
rootLoading = true;
|
|
352
|
+
rootContinuationToken = undefined;
|
|
353
|
+
rootHasMore = false;
|
|
354
|
+
try {
|
|
355
|
+
const adapter = getAdapter('remote', connection.id);
|
|
356
|
+
const prefix = connection.rootPrefix ?? '';
|
|
357
|
+
|
|
358
|
+
if (adapter.listPage) {
|
|
359
|
+
const firstPage = await adapter.listPage(prefix, undefined, PAGE_SIZE);
|
|
360
|
+
rootNodes = firstPage.entries.map(toTreeNode);
|
|
361
|
+
rootContinuationToken = firstPage.continuationToken;
|
|
362
|
+
rootHasMore = firstPage.hasMore;
|
|
363
|
+
} else {
|
|
364
|
+
const entries = await adapter.list(prefix);
|
|
365
|
+
rootNodes = entries.map(toTreeNode);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Auto-expand to the initial shared path
|
|
369
|
+
if (initialPath) {
|
|
370
|
+
await expandToPath(initialPath);
|
|
371
|
+
}
|
|
372
|
+
} catch (err) {
|
|
373
|
+
console.error('[FileTree] Error loading root:', err);
|
|
374
|
+
} finally {
|
|
375
|
+
rootLoading = false;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
async function loadMoreRoot() {
|
|
380
|
+
if (rootLoadingMore || !rootHasMore || !rootContinuationToken) return;
|
|
381
|
+
rootLoadingMore = true;
|
|
382
|
+
try {
|
|
383
|
+
const adapter = getAdapter('remote', connection.id);
|
|
384
|
+
const prefix = connection.rootPrefix ?? '';
|
|
385
|
+
if (adapter.listPage) {
|
|
386
|
+
const page = await adapter.listPage(prefix, rootContinuationToken, PAGE_SIZE);
|
|
387
|
+
rootNodes = [...rootNodes, ...page.entries.map(toTreeNode)];
|
|
388
|
+
rootContinuationToken = page.continuationToken;
|
|
389
|
+
rootHasMore = page.hasMore;
|
|
390
|
+
}
|
|
391
|
+
} catch (err) {
|
|
392
|
+
console.error('[FileTree] Error loading more root:', err);
|
|
393
|
+
} finally {
|
|
394
|
+
rootLoadingMore = false;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
</script>
|
|
398
|
+
|
|
399
|
+
<div class="flex h-full flex-col bg-sidebar text-sidebar-foreground">
|
|
400
|
+
<!-- Header -->
|
|
401
|
+
<div class="flex items-center gap-2 border-b border-sidebar-border px-3 py-2">
|
|
402
|
+
<span class="truncate text-xs font-medium text-muted-foreground">{connection.name}</span>
|
|
403
|
+
<button
|
|
404
|
+
class="ms-auto shrink-0 rounded p-0.5 text-muted-foreground hover:bg-accent hover:text-foreground"
|
|
405
|
+
onclick={loadRoot}
|
|
406
|
+
title={t('fileTree.refresh')}
|
|
407
|
+
>
|
|
408
|
+
<RefreshCwIcon class="size-3" />
|
|
409
|
+
</button>
|
|
410
|
+
</div>
|
|
411
|
+
|
|
412
|
+
<!-- Search filter -->
|
|
413
|
+
<div class="border-b border-sidebar-border px-2 py-1.5">
|
|
414
|
+
<div class="flex items-center gap-1.5 rounded bg-sidebar-accent/50 px-2 py-1">
|
|
415
|
+
<SearchIcon class="size-3 shrink-0 text-muted-foreground" />
|
|
416
|
+
<input
|
|
417
|
+
type="text"
|
|
418
|
+
class="w-full bg-transparent text-xs outline-none placeholder:text-muted-foreground"
|
|
419
|
+
placeholder={t('fileTree.filterPlaceholder')}
|
|
420
|
+
bind:value={filterQuery}
|
|
421
|
+
/>
|
|
422
|
+
</div>
|
|
423
|
+
</div>
|
|
424
|
+
|
|
425
|
+
<!-- Tree (scrollable both axes) -->
|
|
426
|
+
<div class="min-h-0 flex-1 overflow-auto" bind:this={scrollEl}>
|
|
427
|
+
{#if rootLoading}
|
|
428
|
+
<div class="flex items-center justify-center py-8">
|
|
429
|
+
<Loader2Icon class="size-4 animate-spin text-muted-foreground" />
|
|
430
|
+
</div>
|
|
431
|
+
{:else if filteredNodes.length === 0}
|
|
432
|
+
<div class="px-3 py-6 text-center text-xs text-muted-foreground">
|
|
433
|
+
{filterQuery ? t('fileTree.noMatch') : t('fileTree.emptyBucket')}
|
|
434
|
+
</div>
|
|
435
|
+
{:else}
|
|
436
|
+
<div class="min-w-max py-1">
|
|
437
|
+
{#each filteredNodes as node (node.entry.path)}
|
|
438
|
+
{@render treeItem(node, 0)}
|
|
439
|
+
{/each}
|
|
440
|
+
|
|
441
|
+
<!-- Root-level scroll sentinel -->
|
|
442
|
+
{#if rootHasMore && !rootLoadingMore && !filterQuery}
|
|
443
|
+
<div use:lazyLoad={loadMoreRoot} class="h-1"></div>
|
|
444
|
+
{/if}
|
|
445
|
+
{#if rootLoadingMore}
|
|
446
|
+
<div class="flex items-center justify-center py-2">
|
|
447
|
+
<Loader2Icon class="size-3 animate-spin text-muted-foreground" />
|
|
448
|
+
</div>
|
|
449
|
+
{/if}
|
|
450
|
+
</div>
|
|
451
|
+
{/if}
|
|
452
|
+
</div>
|
|
453
|
+
</div>
|
|
454
|
+
|
|
455
|
+
{#snippet treeItem(node: TreeNode, depth: number)}
|
|
456
|
+
{@const entry = node.entry}
|
|
457
|
+
{@const sizeStr = !entry.is_dir && entry.size > 0 ? formatSize(entry.size) : ''}
|
|
458
|
+
{@const dateStr = entry.modified ? formatDate(entry.modified) : ''}
|
|
459
|
+
{@const scheme = getNativeScheme(connection.provider)}
|
|
460
|
+
|
|
461
|
+
<ContextMenu.Root>
|
|
462
|
+
<ContextMenu.Trigger>
|
|
463
|
+
<div
|
|
464
|
+
class="group flex w-full items-center gap-1 whitespace-nowrap rounded-sm px-2 py-0.5 text-start text-xs hover:bg-accent/50"
|
|
465
|
+
style="padding-inline-start: {8 + depth * 16}px; height: 24px;"
|
|
466
|
+
title={dateStr}
|
|
467
|
+
>
|
|
468
|
+
<!-- Expand/collapse chevron for folders -->
|
|
469
|
+
{#if entry.is_dir}
|
|
470
|
+
<button
|
|
471
|
+
class="shrink-0 rounded hover:bg-accent"
|
|
472
|
+
onclick={(e) => handleChevronClick(e, node)}
|
|
473
|
+
title={isViewerDir(entry) ? t('fileTree.expandDir') : undefined}
|
|
474
|
+
>
|
|
475
|
+
{#if node.loading && node.children.length === 0}
|
|
476
|
+
<Loader2Icon class="size-3 animate-spin text-muted-foreground" />
|
|
477
|
+
{:else if node.expanded}
|
|
478
|
+
<ChevronDownIcon class="size-3 text-muted-foreground" />
|
|
479
|
+
{:else}
|
|
480
|
+
<ChevronRightIcon class="size-3 text-muted-foreground rtl:-scale-x-100" />
|
|
481
|
+
{/if}
|
|
482
|
+
</button>
|
|
483
|
+
{:else}
|
|
484
|
+
<span class="size-3 shrink-0"></span>
|
|
485
|
+
{/if}
|
|
486
|
+
|
|
487
|
+
<!-- Clickable area: icon + name -->
|
|
488
|
+
<button
|
|
489
|
+
class="flex min-w-0 flex-1 items-center gap-1"
|
|
490
|
+
onclick={() => handleNodeClick(node)}
|
|
491
|
+
>
|
|
492
|
+
<FileTypeIcon
|
|
493
|
+
extension={entry.extension}
|
|
494
|
+
isDir={entry.is_dir && !isViewerDir(entry)}
|
|
495
|
+
isOpen={node.expanded}
|
|
496
|
+
class="size-3.5 shrink-0"
|
|
497
|
+
/>
|
|
498
|
+
<span class="text-foreground">{entry.name}</span>
|
|
499
|
+
</button>
|
|
500
|
+
|
|
501
|
+
<!-- File size -->
|
|
502
|
+
{#if sizeStr}
|
|
503
|
+
<span class="shrink-0 text-[10px] tabular-nums text-muted-foreground/60">{sizeStr}</span>
|
|
504
|
+
{/if}
|
|
505
|
+
</div>
|
|
506
|
+
</ContextMenu.Trigger>
|
|
507
|
+
|
|
508
|
+
<ContextMenu.Content class="w-52">
|
|
509
|
+
{#if !entry.is_dir || isViewerDir(entry)}
|
|
510
|
+
<ContextMenu.Item onclick={() => openFile(entry)}>
|
|
511
|
+
<ExternalLinkIcon class="me-2 size-3.5" />
|
|
512
|
+
{t('fileTree.open')}
|
|
513
|
+
</ContextMenu.Item>
|
|
514
|
+
<ContextMenu.Separator />
|
|
515
|
+
{/if}
|
|
516
|
+
|
|
517
|
+
<ContextMenu.Item onclick={() => copyToClipboard(buildHttpUrl(entry.path))}>
|
|
518
|
+
<LinkIcon class="me-2 size-3.5" />
|
|
519
|
+
{t('fileTree.copyHttpUrl')}
|
|
520
|
+
</ContextMenu.Item>
|
|
521
|
+
|
|
522
|
+
<ContextMenu.Item onclick={() => copyToClipboard(buildNativeUri(entry.path))}>
|
|
523
|
+
<ClipboardCopyIcon class="me-2 size-3.5" />
|
|
524
|
+
{t('fileTree.copyNativeUri', { scheme: scheme.toUpperCase() })}
|
|
525
|
+
</ContextMenu.Item>
|
|
526
|
+
|
|
527
|
+
<ContextMenu.Separator />
|
|
528
|
+
|
|
529
|
+
<ContextMenu.Item onclick={() => copyToClipboard(entry.path)}>
|
|
530
|
+
<FolderOpenIcon class="me-2 size-3.5" />
|
|
531
|
+
{t('fileTree.copyPath')}
|
|
532
|
+
</ContextMenu.Item>
|
|
533
|
+
|
|
534
|
+
<ContextMenu.Item onclick={() => copyToClipboard(entry.name)}>
|
|
535
|
+
<FileTextIcon class="me-2 size-3.5" />
|
|
536
|
+
{t('fileTree.copyName')}
|
|
537
|
+
</ContextMenu.Item>
|
|
538
|
+
</ContextMenu.Content>
|
|
539
|
+
</ContextMenu.Root>
|
|
540
|
+
|
|
541
|
+
{#if entry.is_dir && node.expanded}
|
|
542
|
+
{#each node.children as child (child.entry.path)}
|
|
543
|
+
{@render treeItem(child, depth + 1)}
|
|
544
|
+
{/each}
|
|
545
|
+
|
|
546
|
+
<!-- Folder-level scroll sentinel -->
|
|
547
|
+
{#if node.hasMore && !node.loading}
|
|
548
|
+
<div use:lazyLoad={() => loadMoreChildren(node)} class="h-1"></div>
|
|
549
|
+
{/if}
|
|
550
|
+
{#if node.hasMore && node.loading}
|
|
551
|
+
<div
|
|
552
|
+
class="flex items-center gap-1 py-0.5"
|
|
553
|
+
style="padding-inline-start: {8 + (depth + 1) * 16}px;"
|
|
554
|
+
>
|
|
555
|
+
<Loader2Icon class="size-3 animate-spin text-muted-foreground" />
|
|
556
|
+
</div>
|
|
557
|
+
{/if}
|
|
558
|
+
{/if}
|
|
559
|
+
{/snippet}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Connection } from '../../types.js';
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
connection: Connection;
|
|
4
|
+
initialPath?: string;
|
|
5
|
+
};
|
|
6
|
+
declare const FileTreeSidebar: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
7
|
+
type FileTreeSidebar = ReturnType<typeof FileTreeSidebar>;
|
|
8
|
+
export default FileTreeSidebar;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import Loader2Icon from '@lucide/svelte/icons/loader-2';
|
|
3
|
+
import PencilIcon from '@lucide/svelte/icons/pencil';
|
|
4
|
+
import { Button } from '../ui/button/index.js';
|
|
5
|
+
import { Input } from '../ui/input/index.js';
|
|
6
|
+
import {
|
|
7
|
+
Sheet,
|
|
8
|
+
SheetContent,
|
|
9
|
+
SheetDescription,
|
|
10
|
+
SheetFooter,
|
|
11
|
+
SheetHeader,
|
|
12
|
+
SheetTitle
|
|
13
|
+
} from '../ui/sheet/index.js';
|
|
14
|
+
import { t } from '../../i18n/index.svelte.js';
|
|
15
|
+
import { browser } from '../../stores/browser.svelte.js';
|
|
16
|
+
import type { FileEntry } from '../../types.js';
|
|
17
|
+
|
|
18
|
+
interface Props {
|
|
19
|
+
open: boolean;
|
|
20
|
+
entry: FileEntry | null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let { open = $bindable(false), entry }: Props = $props();
|
|
24
|
+
|
|
25
|
+
let newName = $state('');
|
|
26
|
+
let renaming = $state(false);
|
|
27
|
+
let error = $state<string | null>(null);
|
|
28
|
+
|
|
29
|
+
let canRename = $derived(
|
|
30
|
+
newName.trim() !== '' && !newName.includes('/') && newName.trim() !== entry?.name
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
$effect(() => {
|
|
34
|
+
if (open && entry) {
|
|
35
|
+
newName = entry.name;
|
|
36
|
+
renaming = false;
|
|
37
|
+
error = null;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
async function handleRename() {
|
|
42
|
+
if (!canRename || !entry) return;
|
|
43
|
+
renaming = true;
|
|
44
|
+
error = null;
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
await browser.renameEntry(entry, newName.trim());
|
|
48
|
+
open = false;
|
|
49
|
+
} catch (e) {
|
|
50
|
+
error = e instanceof Error ? e.message : String(e);
|
|
51
|
+
} finally {
|
|
52
|
+
renaming = false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function handleKeydown(e: KeyboardEvent) {
|
|
57
|
+
if (e.key === 'Enter' && canRename) {
|
|
58
|
+
handleRename();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
</script>
|
|
62
|
+
|
|
63
|
+
<Sheet bind:open>
|
|
64
|
+
<SheetContent side="bottom" class="sm:max-w-lg sm:mx-auto sm:rounded-t-lg">
|
|
65
|
+
<SheetHeader>
|
|
66
|
+
<div class="flex items-center gap-2">
|
|
67
|
+
<PencilIcon class="size-5 text-primary" />
|
|
68
|
+
<SheetTitle>{t('rename.title')}</SheetTitle>
|
|
69
|
+
</div>
|
|
70
|
+
<SheetDescription>
|
|
71
|
+
Enter a new name for <span class="font-medium">{entry?.name}</span>.
|
|
72
|
+
</SheetDescription>
|
|
73
|
+
</SheetHeader>
|
|
74
|
+
|
|
75
|
+
<div class="py-4">
|
|
76
|
+
<Input
|
|
77
|
+
placeholder={t('rename.placeholder')}
|
|
78
|
+
bind:value={newName}
|
|
79
|
+
onkeydown={handleKeydown}
|
|
80
|
+
/>
|
|
81
|
+
{#if error}
|
|
82
|
+
<p class="mt-2 text-sm text-destructive">{error}</p>
|
|
83
|
+
{/if}
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<SheetFooter class="flex-row gap-2">
|
|
87
|
+
<div class="flex-1"></div>
|
|
88
|
+
<Button variant="ghost" size="sm" onclick={() => { open = false; }} disabled={renaming}>
|
|
89
|
+
{t('rename.cancel')}
|
|
90
|
+
</Button>
|
|
91
|
+
<Button size="sm" disabled={!canRename || renaming} onclick={handleRename}>
|
|
92
|
+
{#if renaming}
|
|
93
|
+
<Loader2Icon class="me-1.5 size-4 animate-spin" />
|
|
94
|
+
{t('rename.renaming')}
|
|
95
|
+
{:else}
|
|
96
|
+
{t('rename.rename')}
|
|
97
|
+
{/if}
|
|
98
|
+
</Button>
|
|
99
|
+
</SheetFooter>
|
|
100
|
+
</SheetContent>
|
|
101
|
+
</Sheet>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FileEntry } from '../../types.js';
|
|
2
|
+
interface Props {
|
|
3
|
+
open: boolean;
|
|
4
|
+
entry: FileEntry | null;
|
|
5
|
+
}
|
|
6
|
+
declare const RenameDialog: import("svelte").Component<Props, {}, "open">;
|
|
7
|
+
type RenameDialog = ReturnType<typeof RenameDialog>;
|
|
8
|
+
export default RenameDialog;
|