@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.
Files changed (367) hide show
  1. package/LICENSE +396 -0
  2. package/README.md +114 -0
  3. package/dist/assets/favicon.svg +17 -0
  4. package/dist/components/CLAUDE.md +44 -0
  5. package/dist/components/browser/Breadcrumb.svelte +50 -0
  6. package/dist/components/browser/Breadcrumb.svelte.d.ts +7 -0
  7. package/dist/components/browser/CreateFolderDialog.svelte +98 -0
  8. package/dist/components/browser/CreateFolderDialog.svelte.d.ts +6 -0
  9. package/dist/components/browser/DeleteConfirmDialog.svelte +90 -0
  10. package/dist/components/browser/DeleteConfirmDialog.svelte.d.ts +8 -0
  11. package/dist/components/browser/DropZone.svelte +83 -0
  12. package/dist/components/browser/DropZone.svelte.d.ts +7 -0
  13. package/dist/components/browser/FileBrowser.svelte +229 -0
  14. package/dist/components/browser/FileBrowser.svelte.d.ts +3 -0
  15. package/dist/components/browser/FileRow.svelte +112 -0
  16. package/dist/components/browser/FileRow.svelte.d.ts +9 -0
  17. package/dist/components/browser/FileTreeSidebar.svelte +559 -0
  18. package/dist/components/browser/FileTreeSidebar.svelte.d.ts +8 -0
  19. package/dist/components/browser/RenameDialog.svelte +101 -0
  20. package/dist/components/browser/RenameDialog.svelte.d.ts +8 -0
  21. package/dist/components/browser/SearchBar.svelte +40 -0
  22. package/dist/components/browser/SearchBar.svelte.d.ts +6 -0
  23. package/dist/components/browser/UploadButton.svelte +65 -0
  24. package/dist/components/browser/UploadButton.svelte.d.ts +3 -0
  25. package/dist/components/editor/CodeMirrorEditor.svelte +404 -0
  26. package/dist/components/editor/CodeMirrorEditor.svelte.d.ts +12 -0
  27. package/dist/components/editor/MilkdownEditor.svelte +98 -0
  28. package/dist/components/editor/MilkdownEditor.svelte.d.ts +9 -0
  29. package/dist/components/editor/SqlEditor.svelte +173 -0
  30. package/dist/components/editor/SqlEditor.svelte.d.ts +7 -0
  31. package/dist/components/editor/SqlResultBlock.svelte +199 -0
  32. package/dist/components/editor/SqlResultBlock.svelte.d.ts +9 -0
  33. package/dist/components/layout/ConnectionDialog.svelte +439 -0
  34. package/dist/components/layout/ConnectionDialog.svelte.d.ts +9 -0
  35. package/dist/components/layout/LocaleToggle.svelte +32 -0
  36. package/dist/components/layout/LocaleToggle.svelte.d.ts +3 -0
  37. package/dist/components/layout/SafeLockToggle.svelte +37 -0
  38. package/dist/components/layout/SafeLockToggle.svelte.d.ts +18 -0
  39. package/dist/components/layout/Sidebar.svelte +314 -0
  40. package/dist/components/layout/Sidebar.svelte.d.ts +3 -0
  41. package/dist/components/layout/StatusBar.svelte +73 -0
  42. package/dist/components/layout/StatusBar.svelte.d.ts +3 -0
  43. package/dist/components/layout/TabBar.svelte +102 -0
  44. package/dist/components/layout/TabBar.svelte.d.ts +7 -0
  45. package/dist/components/layout/ThemeToggle.svelte +52 -0
  46. package/dist/components/layout/ThemeToggle.svelte.d.ts +3 -0
  47. package/dist/components/ui/badge/badge.svelte +49 -0
  48. package/dist/components/ui/badge/badge.svelte.d.ts +32 -0
  49. package/dist/components/ui/badge/index.d.ts +1 -0
  50. package/dist/components/ui/badge/index.js +1 -0
  51. package/dist/components/ui/button/button.svelte +82 -0
  52. package/dist/components/ui/button/button.svelte.d.ts +64 -0
  53. package/dist/components/ui/button/index.d.ts +2 -0
  54. package/dist/components/ui/button/index.js +4 -0
  55. package/dist/components/ui/context-menu/context-menu-checkbox-item.svelte +40 -0
  56. package/dist/components/ui/context-menu/context-menu-checkbox-item.svelte.d.ts +9 -0
  57. package/dist/components/ui/context-menu/context-menu-content.svelte +28 -0
  58. package/dist/components/ui/context-menu/context-menu-content.svelte.d.ts +10 -0
  59. package/dist/components/ui/context-menu/context-menu-group-heading.svelte +21 -0
  60. package/dist/components/ui/context-menu/context-menu-group-heading.svelte.d.ts +7 -0
  61. package/dist/components/ui/context-menu/context-menu-group.svelte +7 -0
  62. package/dist/components/ui/context-menu/context-menu-group.svelte.d.ts +4 -0
  63. package/dist/components/ui/context-menu/context-menu-item.svelte +27 -0
  64. package/dist/components/ui/context-menu/context-menu-item.svelte.d.ts +8 -0
  65. package/dist/components/ui/context-menu/context-menu-label.svelte +24 -0
  66. package/dist/components/ui/context-menu/context-menu-label.svelte.d.ts +8 -0
  67. package/dist/components/ui/context-menu/context-menu-portal.svelte +7 -0
  68. package/dist/components/ui/context-menu/context-menu-portal.svelte.d.ts +3 -0
  69. package/dist/components/ui/context-menu/context-menu-radio-group.svelte +16 -0
  70. package/dist/components/ui/context-menu/context-menu-radio-group.svelte.d.ts +4 -0
  71. package/dist/components/ui/context-menu/context-menu-radio-item.svelte +33 -0
  72. package/dist/components/ui/context-menu/context-menu-radio-item.svelte.d.ts +4 -0
  73. package/dist/components/ui/context-menu/context-menu-separator.svelte +17 -0
  74. package/dist/components/ui/context-menu/context-menu-separator.svelte.d.ts +4 -0
  75. package/dist/components/ui/context-menu/context-menu-shortcut.svelte +20 -0
  76. package/dist/components/ui/context-menu/context-menu-shortcut.svelte.d.ts +5 -0
  77. package/dist/components/ui/context-menu/context-menu-sub-content.svelte +20 -0
  78. package/dist/components/ui/context-menu/context-menu-sub-content.svelte.d.ts +4 -0
  79. package/dist/components/ui/context-menu/context-menu-sub-trigger.svelte +29 -0
  80. package/dist/components/ui/context-menu/context-menu-sub-trigger.svelte.d.ts +8 -0
  81. package/dist/components/ui/context-menu/context-menu-sub.svelte +7 -0
  82. package/dist/components/ui/context-menu/context-menu-sub.svelte.d.ts +3 -0
  83. package/dist/components/ui/context-menu/context-menu-trigger.svelte +7 -0
  84. package/dist/components/ui/context-menu/context-menu-trigger.svelte.d.ts +4 -0
  85. package/dist/components/ui/context-menu/context-menu.svelte +7 -0
  86. package/dist/components/ui/context-menu/context-menu.svelte.d.ts +3 -0
  87. package/dist/components/ui/context-menu/index.d.ts +17 -0
  88. package/dist/components/ui/context-menu/index.js +19 -0
  89. package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte +16 -0
  90. package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte.d.ts +4 -0
  91. package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte +43 -0
  92. package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte.d.ts +9 -0
  93. package/dist/components/ui/dropdown-menu/dropdown-menu-content.svelte +29 -0
  94. package/dist/components/ui/dropdown-menu/dropdown-menu-content.svelte.d.ts +10 -0
  95. package/dist/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte +22 -0
  96. package/dist/components/ui/dropdown-menu/dropdown-menu-group-heading.svelte.d.ts +8 -0
  97. package/dist/components/ui/dropdown-menu/dropdown-menu-group.svelte +7 -0
  98. package/dist/components/ui/dropdown-menu/dropdown-menu-group.svelte.d.ts +4 -0
  99. package/dist/components/ui/dropdown-menu/dropdown-menu-item.svelte +27 -0
  100. package/dist/components/ui/dropdown-menu/dropdown-menu-item.svelte.d.ts +8 -0
  101. package/dist/components/ui/dropdown-menu/dropdown-menu-label.svelte +24 -0
  102. package/dist/components/ui/dropdown-menu/dropdown-menu-label.svelte.d.ts +8 -0
  103. package/dist/components/ui/dropdown-menu/dropdown-menu-portal.svelte +7 -0
  104. package/dist/components/ui/dropdown-menu/dropdown-menu-portal.svelte.d.ts +3 -0
  105. package/dist/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte +16 -0
  106. package/dist/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte.d.ts +4 -0
  107. package/dist/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte +33 -0
  108. package/dist/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte.d.ts +4 -0
  109. package/dist/components/ui/dropdown-menu/dropdown-menu-separator.svelte +17 -0
  110. package/dist/components/ui/dropdown-menu/dropdown-menu-separator.svelte.d.ts +4 -0
  111. package/dist/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte +20 -0
  112. package/dist/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte.d.ts +5 -0
  113. package/dist/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte +20 -0
  114. package/dist/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte.d.ts +4 -0
  115. package/dist/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte +29 -0
  116. package/dist/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte.d.ts +7 -0
  117. package/dist/components/ui/dropdown-menu/dropdown-menu-sub.svelte +7 -0
  118. package/dist/components/ui/dropdown-menu/dropdown-menu-sub.svelte.d.ts +3 -0
  119. package/dist/components/ui/dropdown-menu/dropdown-menu-trigger.svelte +7 -0
  120. package/dist/components/ui/dropdown-menu/dropdown-menu-trigger.svelte.d.ts +4 -0
  121. package/dist/components/ui/dropdown-menu/dropdown-menu.svelte +7 -0
  122. package/dist/components/ui/dropdown-menu/dropdown-menu.svelte.d.ts +3 -0
  123. package/dist/components/ui/dropdown-menu/index.d.ts +18 -0
  124. package/dist/components/ui/dropdown-menu/index.js +18 -0
  125. package/dist/components/ui/input/index.d.ts +2 -0
  126. package/dist/components/ui/input/index.js +4 -0
  127. package/dist/components/ui/input/input.svelte +52 -0
  128. package/dist/components/ui/input/input.svelte.d.ts +13 -0
  129. package/dist/components/ui/resizable/index.d.ts +4 -0
  130. package/dist/components/ui/resizable/index.js +6 -0
  131. package/dist/components/ui/resizable/resizable-handle.svelte +30 -0
  132. package/dist/components/ui/resizable/resizable-handle.svelte.d.ts +8 -0
  133. package/dist/components/ui/resizable/resizable-pane-group.svelte +20 -0
  134. package/dist/components/ui/resizable/resizable-pane-group.svelte.d.ts +7 -0
  135. package/dist/components/ui/scroll-area/index.d.ts +3 -0
  136. package/dist/components/ui/scroll-area/index.js +5 -0
  137. package/dist/components/ui/scroll-area/scroll-area-scrollbar.svelte +31 -0
  138. package/dist/components/ui/scroll-area/scroll-area-scrollbar.svelte.d.ts +4 -0
  139. package/dist/components/ui/scroll-area/scroll-area.svelte +47 -0
  140. package/dist/components/ui/scroll-area/scroll-area.svelte.d.ts +11 -0
  141. package/dist/components/ui/separator/index.d.ts +2 -0
  142. package/dist/components/ui/separator/index.js +4 -0
  143. package/dist/components/ui/separator/separator.svelte +21 -0
  144. package/dist/components/ui/separator/separator.svelte.d.ts +4 -0
  145. package/dist/components/ui/sheet/index.d.ts +11 -0
  146. package/dist/components/ui/sheet/index.js +13 -0
  147. package/dist/components/ui/sheet/sheet-close.svelte +7 -0
  148. package/dist/components/ui/sheet/sheet-close.svelte.d.ts +4 -0
  149. package/dist/components/ui/sheet/sheet-content.svelte +62 -0
  150. package/dist/components/ui/sheet/sheet-content.svelte.d.ts +37 -0
  151. package/dist/components/ui/sheet/sheet-description.svelte +17 -0
  152. package/dist/components/ui/sheet/sheet-description.svelte.d.ts +4 -0
  153. package/dist/components/ui/sheet/sheet-footer.svelte +20 -0
  154. package/dist/components/ui/sheet/sheet-footer.svelte.d.ts +5 -0
  155. package/dist/components/ui/sheet/sheet-header.svelte +20 -0
  156. package/dist/components/ui/sheet/sheet-header.svelte.d.ts +5 -0
  157. package/dist/components/ui/sheet/sheet-overlay.svelte +20 -0
  158. package/dist/components/ui/sheet/sheet-overlay.svelte.d.ts +4 -0
  159. package/dist/components/ui/sheet/sheet-portal.svelte +7 -0
  160. package/dist/components/ui/sheet/sheet-portal.svelte.d.ts +3 -0
  161. package/dist/components/ui/sheet/sheet-title.svelte +13 -0
  162. package/dist/components/ui/sheet/sheet-title.svelte.d.ts +4 -0
  163. package/dist/components/ui/sheet/sheet-trigger.svelte +7 -0
  164. package/dist/components/ui/sheet/sheet-trigger.svelte.d.ts +4 -0
  165. package/dist/components/ui/sheet/sheet.svelte +7 -0
  166. package/dist/components/ui/sheet/sheet.svelte.d.ts +3 -0
  167. package/dist/components/ui/switch/index.d.ts +2 -0
  168. package/dist/components/ui/switch/index.js +4 -0
  169. package/dist/components/ui/switch/switch.svelte +28 -0
  170. package/dist/components/ui/switch/switch.svelte.d.ts +8 -0
  171. package/dist/components/ui/tabs/index.d.ts +5 -0
  172. package/dist/components/ui/tabs/index.js +7 -0
  173. package/dist/components/ui/tabs/tabs-content.svelte +17 -0
  174. package/dist/components/ui/tabs/tabs-content.svelte.d.ts +4 -0
  175. package/dist/components/ui/tabs/tabs-list.svelte +16 -0
  176. package/dist/components/ui/tabs/tabs-list.svelte.d.ts +4 -0
  177. package/dist/components/ui/tabs/tabs-trigger.svelte +20 -0
  178. package/dist/components/ui/tabs/tabs-trigger.svelte.d.ts +4 -0
  179. package/dist/components/ui/tabs/tabs.svelte +19 -0
  180. package/dist/components/ui/tabs/tabs.svelte.d.ts +4 -0
  181. package/dist/components/ui/tooltip/index.d.ts +6 -0
  182. package/dist/components/ui/tooltip/index.js +8 -0
  183. package/dist/components/ui/tooltip/tooltip-content.svelte +52 -0
  184. package/dist/components/ui/tooltip/tooltip-content.svelte.d.ts +11 -0
  185. package/dist/components/ui/tooltip/tooltip-portal.svelte +7 -0
  186. package/dist/components/ui/tooltip/tooltip-portal.svelte.d.ts +4 -0
  187. package/dist/components/ui/tooltip/tooltip-provider.svelte +7 -0
  188. package/dist/components/ui/tooltip/tooltip-provider.svelte.d.ts +4 -0
  189. package/dist/components/ui/tooltip/tooltip-trigger.svelte +7 -0
  190. package/dist/components/ui/tooltip/tooltip-trigger.svelte.d.ts +4 -0
  191. package/dist/components/ui/tooltip/tooltip.svelte +7 -0
  192. package/dist/components/ui/tooltip/tooltip.svelte.d.ts +4 -0
  193. package/dist/components/viewers/ArchiveViewer.svelte +586 -0
  194. package/dist/components/viewers/ArchiveViewer.svelte.d.ts +7 -0
  195. package/dist/components/viewers/CLAUDE.md +60 -0
  196. package/dist/components/viewers/CodeViewer.svelte +553 -0
  197. package/dist/components/viewers/CodeViewer.svelte.d.ts +7 -0
  198. package/dist/components/viewers/CogViewer.svelte +1345 -0
  199. package/dist/components/viewers/CogViewer.svelte.d.ts +7 -0
  200. package/dist/components/viewers/CopcViewer.svelte +25 -0
  201. package/dist/components/viewers/CopcViewer.svelte.d.ts +7 -0
  202. package/dist/components/viewers/DatabaseViewer.svelte +169 -0
  203. package/dist/components/viewers/DatabaseViewer.svelte.d.ts +7 -0
  204. package/dist/components/viewers/FileInfo.svelte +174 -0
  205. package/dist/components/viewers/FileInfo.svelte.d.ts +10 -0
  206. package/dist/components/viewers/FlatGeobufViewer.svelte +755 -0
  207. package/dist/components/viewers/FlatGeobufViewer.svelte.d.ts +7 -0
  208. package/dist/components/viewers/GeoParquetMapViewer.svelte +278 -0
  209. package/dist/components/viewers/GeoParquetMapViewer.svelte.d.ts +17 -0
  210. package/dist/components/viewers/ImageViewer.svelte +233 -0
  211. package/dist/components/viewers/ImageViewer.svelte.d.ts +7 -0
  212. package/dist/components/viewers/LoadProgress.svelte +93 -0
  213. package/dist/components/viewers/LoadProgress.svelte.d.ts +15 -0
  214. package/dist/components/viewers/MapViewer.svelte +234 -0
  215. package/dist/components/viewers/MapViewer.svelte.d.ts +7 -0
  216. package/dist/components/viewers/MarkdownViewer.svelte +478 -0
  217. package/dist/components/viewers/MarkdownViewer.svelte.d.ts +7 -0
  218. package/dist/components/viewers/MediaViewer.svelte +121 -0
  219. package/dist/components/viewers/MediaViewer.svelte.d.ts +7 -0
  220. package/dist/components/viewers/ModelViewer.svelte +164 -0
  221. package/dist/components/viewers/ModelViewer.svelte.d.ts +7 -0
  222. package/dist/components/viewers/NotebookViewer.svelte +389 -0
  223. package/dist/components/viewers/NotebookViewer.svelte.d.ts +7 -0
  224. package/dist/components/viewers/PdfViewer.svelte +278 -0
  225. package/dist/components/viewers/PdfViewer.svelte.d.ts +7 -0
  226. package/dist/components/viewers/PmtilesViewer.svelte +191 -0
  227. package/dist/components/viewers/PmtilesViewer.svelte.d.ts +7 -0
  228. package/dist/components/viewers/QueryHistoryPanel.svelte +159 -0
  229. package/dist/components/viewers/QueryHistoryPanel.svelte.d.ts +8 -0
  230. package/dist/components/viewers/RawViewer.svelte +117 -0
  231. package/dist/components/viewers/RawViewer.svelte.d.ts +7 -0
  232. package/dist/components/viewers/StacMapViewer.svelte +20 -0
  233. package/dist/components/viewers/StacMapViewer.svelte.d.ts +7 -0
  234. package/dist/components/viewers/StyleEditorOverlay.svelte +27 -0
  235. package/dist/components/viewers/StyleEditorOverlay.svelte.d.ts +7 -0
  236. package/dist/components/viewers/TableGrid.svelte +355 -0
  237. package/dist/components/viewers/TableGrid.svelte.d.ts +12 -0
  238. package/dist/components/viewers/TableStatusBar.svelte +92 -0
  239. package/dist/components/viewers/TableStatusBar.svelte.d.ts +11 -0
  240. package/dist/components/viewers/TableToolbar.svelte +382 -0
  241. package/dist/components/viewers/TableToolbar.svelte.d.ts +25 -0
  242. package/dist/components/viewers/TableViewer.svelte +923 -0
  243. package/dist/components/viewers/TableViewer.svelte.d.ts +7 -0
  244. package/dist/components/viewers/ViewerRouter.svelte +70 -0
  245. package/dist/components/viewers/ViewerRouter.svelte.d.ts +7 -0
  246. package/dist/components/viewers/ZarrMapViewer.svelte +288 -0
  247. package/dist/components/viewers/ZarrMapViewer.svelte.d.ts +17 -0
  248. package/dist/components/viewers/ZarrViewer.svelte +256 -0
  249. package/dist/components/viewers/ZarrViewer.svelte.d.ts +7 -0
  250. package/dist/components/viewers/map/AttributeTable.svelte +52 -0
  251. package/dist/components/viewers/map/AttributeTable.svelte.d.ts +8 -0
  252. package/dist/components/viewers/map/MapContainer.svelte +158 -0
  253. package/dist/components/viewers/map/MapContainer.svelte.d.ts +12 -0
  254. package/dist/components/viewers/pmtiles/PmtilesArchiveView.svelte +389 -0
  255. package/dist/components/viewers/pmtiles/PmtilesArchiveView.svelte.d.ts +10 -0
  256. package/dist/components/viewers/pmtiles/PmtilesMapView.svelte +332 -0
  257. package/dist/components/viewers/pmtiles/PmtilesMapView.svelte.d.ts +11 -0
  258. package/dist/components/viewers/pmtiles/PmtilesTileInspector.svelte +373 -0
  259. package/dist/components/viewers/pmtiles/PmtilesTileInspector.svelte.d.ts +12 -0
  260. package/dist/components/viewers/pmtiles/SvgTileRenderer.svelte +112 -0
  261. package/dist/components/viewers/pmtiles/SvgTileRenderer.svelte.d.ts +10 -0
  262. package/dist/file-icons/CLAUDE.md +21 -0
  263. package/dist/file-icons/FileTypeIcon.svelte +74 -0
  264. package/dist/file-icons/FileTypeIcon.svelte.d.ts +9 -0
  265. package/dist/file-icons/index.d.ts +56 -0
  266. package/dist/file-icons/index.js +1070 -0
  267. package/dist/i18n/CLAUDE.md +19 -0
  268. package/dist/i18n/ar.d.ts +1 -0
  269. package/dist/i18n/ar.js +404 -0
  270. package/dist/i18n/en.d.ts +1 -0
  271. package/dist/i18n/en.js +404 -0
  272. package/dist/i18n/index.svelte.d.ts +9 -0
  273. package/dist/i18n/index.svelte.js +27 -0
  274. package/dist/index.d.ts +20 -0
  275. package/dist/index.js +13 -0
  276. package/dist/query/CLAUDE.md +22 -0
  277. package/dist/query/engine.d.ts +56 -0
  278. package/dist/query/engine.js +6 -0
  279. package/dist/query/index.d.ts +4 -0
  280. package/dist/query/index.js +19 -0
  281. package/dist/query/wasm.d.ts +20 -0
  282. package/dist/query/wasm.js +890 -0
  283. package/dist/storage/CLAUDE.md +23 -0
  284. package/dist/storage/adapter.d.ts +21 -0
  285. package/dist/storage/adapter.js +1 -0
  286. package/dist/storage/browser-azure.d.ts +25 -0
  287. package/dist/storage/browser-azure.js +271 -0
  288. package/dist/storage/browser-cloud.d.ts +32 -0
  289. package/dist/storage/browser-cloud.js +293 -0
  290. package/dist/storage/index.d.ts +11 -0
  291. package/dist/storage/index.js +37 -0
  292. package/dist/storage/url-adapter.d.ts +19 -0
  293. package/dist/storage/url-adapter.js +51 -0
  294. package/dist/stores/CLAUDE.md +29 -0
  295. package/dist/stores/browser.svelte.d.ts +28 -0
  296. package/dist/stores/browser.svelte.js +160 -0
  297. package/dist/stores/connections.svelte.d.ts +56 -0
  298. package/dist/stores/connections.svelte.js +272 -0
  299. package/dist/stores/credentials.svelte.d.ts +56 -0
  300. package/dist/stores/credentials.svelte.js +79 -0
  301. package/dist/stores/files.svelte.d.ts +20 -0
  302. package/dist/stores/files.svelte.js +76 -0
  303. package/dist/stores/query-history.svelte.d.ts +16 -0
  304. package/dist/stores/query-history.svelte.js +57 -0
  305. package/dist/stores/safelock.svelte.d.ts +8 -0
  306. package/dist/stores/safelock.svelte.js +52 -0
  307. package/dist/stores/settings.svelte.d.ts +11 -0
  308. package/dist/stores/settings.svelte.js +101 -0
  309. package/dist/stores/tab-resources.svelte.d.ts +25 -0
  310. package/dist/stores/tab-resources.svelte.js +61 -0
  311. package/dist/stores/tabs.svelte.d.ts +17 -0
  312. package/dist/stores/tabs.svelte.js +110 -0
  313. package/dist/types/notebookjs.d.ts +14 -0
  314. package/dist/types.d.ts +47 -0
  315. package/dist/types.js +1 -0
  316. package/dist/utils/CLAUDE.md +54 -0
  317. package/dist/utils/analytics.d.ts +10 -0
  318. package/dist/utils/analytics.js +38 -0
  319. package/dist/utils/archive.d.ts +70 -0
  320. package/dist/utils/archive.js +333 -0
  321. package/dist/utils/column-types.d.ts +5 -0
  322. package/dist/utils/column-types.js +137 -0
  323. package/dist/utils/deck.d.ts +98 -0
  324. package/dist/utils/deck.js +208 -0
  325. package/dist/utils/evidence-context.d.ts +22 -0
  326. package/dist/utils/evidence-context.js +56 -0
  327. package/dist/utils/export.d.ts +2 -0
  328. package/dist/utils/export.js +51 -0
  329. package/dist/utils/format.d.ts +14 -0
  330. package/dist/utils/format.js +56 -0
  331. package/dist/utils/geoarrow.d.ts +32 -0
  332. package/dist/utils/geoarrow.js +672 -0
  333. package/dist/utils/hex.d.ts +10 -0
  334. package/dist/utils/hex.js +27 -0
  335. package/dist/utils/host-detection.d.ts +23 -0
  336. package/dist/utils/host-detection.js +289 -0
  337. package/dist/utils/map-selection.d.ts +12 -0
  338. package/dist/utils/map-selection.js +45 -0
  339. package/dist/utils/markdown-sql.d.ts +30 -0
  340. package/dist/utils/markdown-sql.js +73 -0
  341. package/dist/utils/markdown.d.ts +18 -0
  342. package/dist/utils/markdown.js +146 -0
  343. package/dist/utils/model3d.d.ts +13 -0
  344. package/dist/utils/model3d.js +62 -0
  345. package/dist/utils/parquet-metadata.d.ts +58 -0
  346. package/dist/utils/parquet-metadata.js +228 -0
  347. package/dist/utils/pdf.d.ts +8 -0
  348. package/dist/utils/pdf.js +28 -0
  349. package/dist/utils/pmtiles-tile.d.ts +38 -0
  350. package/dist/utils/pmtiles-tile.js +64 -0
  351. package/dist/utils/pmtiles.d.ts +46 -0
  352. package/dist/utils/pmtiles.js +135 -0
  353. package/dist/utils/shiki.d.ts +8 -0
  354. package/dist/utils/shiki.js +98 -0
  355. package/dist/utils/storage-url.d.ts +64 -0
  356. package/dist/utils/storage-url.js +374 -0
  357. package/dist/utils/url-state.d.ts +40 -0
  358. package/dist/utils/url-state.js +113 -0
  359. package/dist/utils/url.d.ts +27 -0
  360. package/dist/utils/url.js +115 -0
  361. package/dist/utils/wkb.d.ts +43 -0
  362. package/dist/utils/wkb.js +345 -0
  363. package/dist/utils/zarr.d.ts +39 -0
  364. package/dist/utils/zarr.js +204 -0
  365. package/dist/utils.d.ts +12 -0
  366. package/dist/utils.js +5 -0
  367. package/package.json +203 -0
@@ -0,0 +1,586 @@
1
+ <script lang="ts">
2
+ import { Archive, ChevronRight, Download, File, Folder, Loader } from '@lucide/svelte';
3
+ import type { Entry } from '@zip.js/zip.js';
4
+ import { onDestroy, untrack } from 'svelte';
5
+ import { Badge } from '../ui/badge/index.js';
6
+ import {
7
+ ResizableHandle,
8
+ ResizablePane,
9
+ ResizablePaneGroup
10
+ } from '../ui/resizable/index.js';
11
+ import { t } from '../../i18n/index.svelte.js';
12
+ import { getAdapter } from '../../storage/index.js';
13
+ import { tabResources } from '../../stores/tab-resources.svelte.js';
14
+ import type { Tab } from '../../types';
15
+ import {
16
+ type ArchiveEntry,
17
+ type ArchiveFormat,
18
+ clampPrefix,
19
+ decompressGzip,
20
+ detectArchiveFormat,
21
+ downloadTarEntryFromBuffer,
22
+ downloadTarEntryFromUrl,
23
+ downloadZipEntry,
24
+ listContents,
25
+ readTarEntriesFromBuffer,
26
+ readZipEntriesFromBuffer,
27
+ streamTarEntriesFromUrl,
28
+ streamTarGzEntriesFromUrl,
29
+ streamZipEntriesFromUrl
30
+ } from '../../utils/archive';
31
+ import { formatFileSize } from '../../utils/format';
32
+ import { buildHttpsUrl } from '../../utils/url.js';
33
+
34
+ let { tab }: { tab: Tab } = $props();
35
+
36
+ const MAX_ITEMS = 500;
37
+
38
+ // ── State ──────────────────────────────────────────────────────────────
39
+
40
+ let error = $state<string | null>(null);
41
+ let entryList = $state<ArchiveEntry[]>([]);
42
+ let prefix = $state('');
43
+ let loadMethod = $state<'range' | 'full' | ''>('');
44
+ let format = $state<ArchiveFormat>('unsupported');
45
+ let downloading = $state<Set<string>>(new Set());
46
+ let remoteUrl = $state('');
47
+ let tarBuffer = $state<Uint8Array | null>(null);
48
+ let initializing = $state(true);
49
+ let scanning = $state(false);
50
+ let scanCount = $state(0);
51
+
52
+ // Column browser state
53
+ let selectedDir = $state<string | null>(null);
54
+ let selectedFile = $state<ArchiveEntry | null>(null);
55
+
56
+ let zipEntryMap = new Map<string, Entry>();
57
+ let abortController: AbortController | null = null;
58
+
59
+ // Derived listing — column 1
60
+ let contents = $derived.by(() => {
61
+ void scanCount;
62
+ return listContents(entryList, prefix);
63
+ });
64
+
65
+ // Column 2 — contents of selected directory
66
+ let selectedDirContents = $derived.by(() => {
67
+ if (!selectedDir) return { directories: [] as string[], files: [] as ArchiveEntry[] };
68
+ void scanCount;
69
+ return listContents(entryList, selectedDir);
70
+ });
71
+
72
+ let breadcrumbs = $derived(prefix.length > 0 ? prefix.split('/').filter(Boolean) : []);
73
+
74
+ // Reset selections when prefix changes
75
+ $effect(() => {
76
+ void prefix;
77
+ selectedDir = null;
78
+ selectedFile = null;
79
+ });
80
+
81
+ // ── Lifecycle ──────────────────────────────────────────────────────────
82
+
83
+ $effect(() => {
84
+ if (!tab) return;
85
+ const _tabId = tab.id;
86
+ untrack(() => {
87
+ loadArchive();
88
+ });
89
+ });
90
+
91
+ function cleanup() {
92
+ abortController?.abort();
93
+ entryList = [];
94
+ zipEntryMap.clear();
95
+ tarBuffer = null;
96
+ }
97
+
98
+ $effect(() => {
99
+ const id = tab.id;
100
+ const unregister = tabResources.register(id, cleanup);
101
+ return unregister;
102
+ });
103
+ onDestroy(cleanup);
104
+
105
+ // ── Navigation ─────────────────────────────────────────────────────────
106
+
107
+ function navigateTo(newPrefix: string) {
108
+ prefix = clampPrefix(newPrefix);
109
+ }
110
+
111
+ function breadcrumbPath(index: number): string {
112
+ return breadcrumbs.slice(0, index + 1).join('/');
113
+ }
114
+
115
+ function selectDirectory(dir: string) {
116
+ selectedDir = dir;
117
+ selectedFile = null;
118
+ }
119
+
120
+ function selectFile(file: ArchiveEntry) {
121
+ selectedFile = file;
122
+ }
123
+
124
+ function navigateIntoDir(dir: string) {
125
+ navigateTo(dir);
126
+ }
127
+
128
+ // ── Loading ────────────────────────────────────────────────────────────
129
+
130
+ function resetState() {
131
+ abortController?.abort();
132
+ abortController = new AbortController();
133
+ error = null;
134
+ prefix = '';
135
+ entryList = [];
136
+ scanCount = 0;
137
+ zipEntryMap.clear();
138
+ tarBuffer = null;
139
+ remoteUrl = '';
140
+ initializing = true;
141
+ scanning = false;
142
+ selectedDir = null;
143
+ selectedFile = null;
144
+ }
145
+
146
+ function pushEntries(batch: ArchiveEntry[]) {
147
+ entryList.push(...batch);
148
+ scanCount = entryList.length;
149
+ if (initializing) initializing = false;
150
+ }
151
+
152
+ async function loadArchive() {
153
+ resetState();
154
+
155
+ try {
156
+ format = detectArchiveFormat(tab.name);
157
+
158
+ if (format === 'zip') {
159
+ await loadZip();
160
+ } else if (format === 'tar') {
161
+ await loadTar();
162
+ } else if (format === 'tar.gz') {
163
+ await loadTarGz();
164
+ } else {
165
+ error = t('archive.unsupported');
166
+ }
167
+ } catch (err) {
168
+ if ((err as DOMException)?.name === 'AbortError') return;
169
+ error = err instanceof Error ? err.message : String(err);
170
+ } finally {
171
+ scanning = false;
172
+ if (initializing) initializing = false;
173
+ }
174
+ }
175
+
176
+ async function loadZip() {
177
+ const signal = abortController!.signal;
178
+
179
+ if (tab.source === 'remote') {
180
+ const url = buildHttpsUrl(tab);
181
+ try {
182
+ scanning = true;
183
+ for await (const batch of streamZipEntriesFromUrl(url, signal)) {
184
+ for (const ze of batch.zipEntries) zipEntryMap.set(ze.filename, ze);
185
+ pushEntries(batch.archiveEntries);
186
+ }
187
+ loadMethod = 'range';
188
+ return;
189
+ } catch (err) {
190
+ if ((err as DOMException)?.name === 'AbortError') throw err;
191
+ entryList = [];
192
+ scanCount = 0;
193
+ zipEntryMap.clear();
194
+ scanning = false;
195
+ }
196
+ }
197
+
198
+ const adapter = getAdapter(tab.source, tab.connectionId);
199
+ const data = await adapter.read(tab.path);
200
+ const result = await readZipEntriesFromBuffer(data);
201
+ for (const e of result.entries) zipEntryMap.set(e.filename, e);
202
+ entryList = result.entryList;
203
+ scanCount = entryList.length;
204
+ loadMethod = 'full';
205
+ }
206
+
207
+ async function loadTar() {
208
+ const signal = abortController!.signal;
209
+
210
+ if (tab.source === 'remote') {
211
+ const url = buildHttpsUrl(tab);
212
+ try {
213
+ scanning = true;
214
+ remoteUrl = url;
215
+ for await (const batch of streamTarEntriesFromUrl(url, signal)) {
216
+ pushEntries(batch);
217
+ }
218
+ loadMethod = 'range';
219
+ return;
220
+ } catch (err) {
221
+ if ((err as DOMException)?.name === 'AbortError') throw err;
222
+ entryList = [];
223
+ scanCount = 0;
224
+ remoteUrl = '';
225
+ scanning = false;
226
+ }
227
+ }
228
+
229
+ const adapter = getAdapter(tab.source, tab.connectionId);
230
+ const data = await adapter.read(tab.path);
231
+ const result = readTarEntriesFromBuffer(data);
232
+ entryList = result.entryList;
233
+ scanCount = entryList.length;
234
+ tarBuffer = data;
235
+ loadMethod = 'full';
236
+ }
237
+
238
+ async function loadTarGz() {
239
+ const signal = abortController!.signal;
240
+
241
+ // For remote URLs: stream-fetch → decompress → parse progressively
242
+ if (tab.source === 'remote' || tab.source === 'url') {
243
+ const url = buildHttpsUrl(tab);
244
+ try {
245
+ scanning = true;
246
+ const decompressedChunks: Uint8Array[] = [];
247
+ for await (const { entries, chunk } of streamTarGzEntriesFromUrl(url, signal)) {
248
+ pushEntries(entries);
249
+ decompressedChunks.push(chunk);
250
+ }
251
+ // Assemble decompressed buffer for file downloads
252
+ const totalLen = decompressedChunks.reduce((sum, c) => sum + c.length, 0);
253
+ const assembled = new Uint8Array(totalLen);
254
+ let pos = 0;
255
+ for (const c of decompressedChunks) {
256
+ assembled.set(c, pos);
257
+ pos += c.length;
258
+ }
259
+ tarBuffer = assembled;
260
+ loadMethod = 'full';
261
+ return;
262
+ } catch (err) {
263
+ if ((err as DOMException)?.name === 'AbortError') throw err;
264
+ // Fall through to full-buffer approach
265
+ entryList = [];
266
+ scanCount = 0;
267
+ scanning = false;
268
+ }
269
+ }
270
+
271
+ // Fallback: full download + in-memory decompress
272
+ const adapter = getAdapter(tab.source, tab.connectionId);
273
+ const compressed = await adapter.read(tab.path);
274
+ const data = await decompressGzip(compressed);
275
+ const result = readTarEntriesFromBuffer(data);
276
+ entryList = result.entryList;
277
+ scanCount = entryList.length;
278
+ tarBuffer = data;
279
+ loadMethod = 'full';
280
+ }
281
+
282
+ // ── Download ───────────────────────────────────────────────────────────
283
+
284
+ async function handleDownload(archiveEntry: ArchiveEntry) {
285
+ if (archiveEntry.directory) return;
286
+ const key = archiveEntry.filename;
287
+ if (downloading.has(key)) return;
288
+
289
+ downloading = new Set([...downloading, key]);
290
+ try {
291
+ if (format === 'zip') {
292
+ const zipEntry = zipEntryMap.get(key);
293
+ if (zipEntry) await downloadZipEntry(zipEntry);
294
+ } else if (format === 'tar' && remoteUrl) {
295
+ await downloadTarEntryFromUrl(remoteUrl, archiveEntry);
296
+ } else if ((format === 'tar' || format === 'tar.gz') && tarBuffer) {
297
+ downloadTarEntryFromBuffer(tarBuffer, archiveEntry);
298
+ }
299
+ } finally {
300
+ const next = new Set(downloading);
301
+ next.delete(key);
302
+ downloading = next;
303
+ }
304
+ }
305
+
306
+ const formatLabel = $derived(format === 'tar.gz' ? 'TAR.GZ' : format.toUpperCase());
307
+ const totalDirs = $derived(contents.directories.length);
308
+ const totalFiles = $derived(contents.files.length);
309
+ </script>
310
+
311
+ {#snippet fileDetails()}
312
+ {#if selectedFile}
313
+ {@const fileName = selectedFile.filename.split('/').pop()}
314
+ <div
315
+ class="shrink-0 border-b border-zinc-200 px-3 py-1.5 text-[11px] font-medium uppercase tracking-wider text-muted-foreground dark:border-zinc-800"
316
+ >
317
+ {t('archive.fileDetails')}
318
+ </div>
319
+ <div class="flex-1 overflow-auto p-3">
320
+ <dl class="space-y-2 text-xs">
321
+ <div>
322
+ <dt class="text-muted-foreground">{t('archive.fileName')}</dt>
323
+ <dd class="break-all font-mono text-[11px]">{fileName}</dd>
324
+ </div>
325
+ <div>
326
+ <dt class="text-muted-foreground">{t('archive.path')}</dt>
327
+ <dd class="break-all font-mono text-[10px] text-muted-foreground">{selectedFile.filename}</dd>
328
+ </div>
329
+ <div>
330
+ <dt class="text-muted-foreground">{t('archive.size')}</dt>
331
+ <dd>{formatFileSize(selectedFile.uncompressedSize)}</dd>
332
+ </div>
333
+ {#if selectedFile.compressedSize > 0 && selectedFile.compressedSize !== selectedFile.uncompressedSize}
334
+ <div>
335
+ <dt class="text-muted-foreground">{t('archive.compressed')}</dt>
336
+ <dd>{formatFileSize(selectedFile.compressedSize)}</dd>
337
+ </div>
338
+ {/if}
339
+ {#if selectedFile.lastModified}
340
+ <div>
341
+ <dt class="text-muted-foreground">{t('archive.modified')}</dt>
342
+ <dd class="text-[11px]">{selectedFile.lastModified.toLocaleString()}</dd>
343
+ </div>
344
+ {/if}
345
+ </dl>
346
+
347
+ <button
348
+ class="mt-4 w-full rounded bg-primary/90 px-3 py-1.5 text-xs text-primary-foreground hover:bg-primary disabled:cursor-not-allowed disabled:opacity-40"
349
+ onclick={() => handleDownload(selectedFile!)}
350
+ disabled={downloading.has(selectedFile.filename)}
351
+ >
352
+ {#if downloading.has(selectedFile.filename)}
353
+ <Loader class="mr-1 inline-block h-3 w-3 animate-spin" />
354
+ Downloading...
355
+ {:else}
356
+ <Download class="mr-1 inline-block h-3 w-3" />
357
+ {t('archive.download')}
358
+ {/if}
359
+ </button>
360
+ </div>
361
+ {:else}
362
+ <div class="flex flex-1 items-center justify-center text-xs text-muted-foreground">
363
+ {t('archive.selectFile')}
364
+ </div>
365
+ {/if}
366
+ {/snippet}
367
+
368
+ <div class="flex h-full flex-col">
369
+ <!-- Header bar -->
370
+ <div class="shrink-0 border-b border-zinc-200 px-3 py-2 sm:px-4 dark:border-zinc-800">
371
+ <div class="flex items-center gap-1.5 sm:gap-2">
372
+ <Archive class="h-4 w-4 shrink-0 text-amber-500" />
373
+ <span class="max-w-[140px] truncate text-sm font-medium text-zinc-700 sm:max-w-none dark:text-zinc-300">
374
+ {tab.name}
375
+ </span>
376
+ <Badge variant="outline" class="text-[10px]">{formatLabel}</Badge>
377
+
378
+ {#if scanCount > 0}
379
+ <span class="text-xs text-muted-foreground">
380
+ {scanCount.toLocaleString()} {t('archive.entries')}
381
+ </span>
382
+ {/if}
383
+
384
+ {#if scanning}
385
+ <span class="flex items-center gap-1 text-xs text-amber-500">
386
+ <Loader class="h-3 w-3 animate-spin" />
387
+ <span class="hidden sm:inline">{t('archive.scanning')}</span>
388
+ </span>
389
+ {/if}
390
+
391
+ {#if loadMethod === 'range' && !scanning}
392
+ <Badge variant="outline" class="border-emerald-200 text-[10px] text-emerald-600 dark:border-emerald-800 dark:text-emerald-400">
393
+ {t('archive.streamed')}
394
+ </Badge>
395
+ {:else if loadMethod === 'full' && format === 'tar.gz' && !scanning}
396
+ <Badge variant="outline" class="border-amber-200 text-[10px] text-amber-600 dark:border-amber-800 dark:text-amber-400">
397
+ {t('archive.fullDownload')}
398
+ </Badge>
399
+ {/if}
400
+ </div>
401
+
402
+ <!-- Breadcrumb -->
403
+ {#if breadcrumbs.length > 0}
404
+ <nav class="mt-1.5">
405
+ <ol class="flex flex-wrap items-center gap-1 text-xs">
406
+ <li>
407
+ <button
408
+ class="text-muted-foreground hover:text-foreground"
409
+ onclick={() => navigateTo('')}
410
+ >
411
+ <Archive class="inline-block h-3 w-3" />
412
+ </button>
413
+ </li>
414
+ {#each breadcrumbs as crumb, i}
415
+ <li class="flex items-center gap-1">
416
+ <ChevronRight class="h-3 w-3 text-muted-foreground/50" />
417
+ {#if i === breadcrumbs.length - 1}
418
+ <span class="font-medium">{crumb}</span>
419
+ {:else}
420
+ <button
421
+ class="text-muted-foreground hover:text-foreground"
422
+ onclick={() => navigateTo(breadcrumbPath(i))}
423
+ >
424
+ {crumb}
425
+ </button>
426
+ {/if}
427
+ </li>
428
+ {/each}
429
+ </ol>
430
+ </nav>
431
+ {/if}
432
+ </div>
433
+
434
+ <!-- Content area -->
435
+ {#if initializing}
436
+ <div class="flex flex-1 items-center justify-center gap-2">
437
+ <Loader class="h-5 w-5 animate-spin text-zinc-400" />
438
+ <span class="text-sm text-zinc-400">{t('archive.loading')}</span>
439
+ </div>
440
+ {:else if error}
441
+ <div class="flex flex-1 items-center justify-center px-4">
442
+ <p class="text-sm text-red-400">{error}</p>
443
+ </div>
444
+ {:else}
445
+ <!-- Column browser (resizable) -->
446
+ <ResizablePaneGroup direction="horizontal" class="min-h-0 flex-1">
447
+ <!-- Column 1: Current path entries -->
448
+ <ResizablePane defaultSize={35} minSize={20}>
449
+ <div class="flex h-full flex-col">
450
+ <div
451
+ class="shrink-0 border-b border-zinc-200 px-3 py-1.5 text-[11px] font-medium uppercase tracking-wider text-muted-foreground dark:border-zinc-800"
452
+ >
453
+ {t('archive.contents')}
454
+ <span class="ms-1 normal-case tracking-normal">({(totalDirs + totalFiles).toLocaleString()})</span>
455
+ </div>
456
+ <div class="flex-1 overflow-auto">
457
+ {#if contents.directories.length === 0 && contents.files.length === 0 && !scanning}
458
+ <div class="p-4 text-center text-xs text-muted-foreground">
459
+ {t('archive.empty')}
460
+ </div>
461
+ {/if}
462
+
463
+ {#each contents.directories as dir, i}
464
+ {#if i < MAX_ITEMS}
465
+ {@const dirName = dir.split('/').pop()}
466
+ <button
467
+ class="flex w-full items-center gap-2 px-3 py-1.5 text-xs hover:bg-zinc-100 dark:hover:bg-zinc-800/50"
468
+ class:bg-zinc-100={selectedDir === dir}
469
+ class:dark:bg-zinc-800={selectedDir === dir}
470
+ onclick={() => selectDirectory(dir)}
471
+ ondblclick={() => navigateIntoDir(dir)}
472
+ >
473
+ <Folder class="size-3.5 shrink-0 text-amber-500/70" />
474
+ <span class="truncate font-medium">{dirName}</span>
475
+ <ChevronRight class="ms-auto size-3 shrink-0 text-muted-foreground" />
476
+ </button>
477
+ {:else if i === MAX_ITEMS}
478
+ <div class="px-3 py-1.5 text-[10px] text-muted-foreground">
479
+ +{contents.directories.length - MAX_ITEMS} more
480
+ </div>
481
+ {/if}
482
+ {/each}
483
+
484
+ {#each contents.files as file, i}
485
+ {#if i < MAX_ITEMS}
486
+ {@const fileName = file.filename.split('/').pop()}
487
+ <button
488
+ class="flex w-full items-center gap-2 px-3 py-1.5 text-xs hover:bg-zinc-100 dark:hover:bg-zinc-800/50"
489
+ class:bg-zinc-100={selectedFile?.filename === file.filename}
490
+ class:dark:bg-zinc-800={selectedFile?.filename === file.filename}
491
+ onclick={() => selectFile(file)}
492
+ >
493
+ <File class="size-3.5 shrink-0 text-muted-foreground/70" />
494
+ <span class="truncate">{fileName}</span>
495
+ <span class="ms-auto shrink-0 text-[10px] tabular-nums text-muted-foreground">
496
+ {formatFileSize(file.uncompressedSize)}
497
+ </span>
498
+ </button>
499
+ {:else if i === MAX_ITEMS}
500
+ <div class="px-3 py-1.5 text-[10px] text-muted-foreground">
501
+ +{contents.files.length - MAX_ITEMS} more
502
+ </div>
503
+ {/if}
504
+ {/each}
505
+
506
+ {#if scanning}
507
+ <div class="flex items-center gap-2 px-3 py-2 text-[10px] text-muted-foreground">
508
+ <Loader class="h-3 w-3 animate-spin" />
509
+ <span>{t('archive.scanningProgress', { count: scanCount.toLocaleString() })}</span>
510
+ </div>
511
+ {/if}
512
+ </div>
513
+ </div>
514
+ </ResizablePane>
515
+
516
+ <ResizableHandle />
517
+
518
+ <!-- Column 2: Selected directory contents -->
519
+ <ResizablePane defaultSize={35} minSize={20}>
520
+ <div class="flex h-full flex-col">
521
+ {#if selectedDir}
522
+ {@const dirName = selectedDir.split('/').pop()}
523
+ <div
524
+ class="shrink-0 border-b border-zinc-200 px-3 py-1.5 text-[11px] font-medium uppercase tracking-wider text-muted-foreground dark:border-zinc-800"
525
+ >
526
+ {dirName}
527
+ <span class="ms-1 normal-case tracking-normal">({(selectedDirContents.directories.length + selectedDirContents.files.length).toLocaleString()})</span>
528
+ </div>
529
+ <div class="flex-1 overflow-auto">
530
+ {#if selectedDirContents.directories.length === 0 && selectedDirContents.files.length === 0}
531
+ <div class="p-4 text-center text-xs text-muted-foreground">
532
+ {t('archive.empty')}
533
+ </div>
534
+ {/if}
535
+
536
+ {#each selectedDirContents.directories as subDir, i}
537
+ {#if i < MAX_ITEMS}
538
+ {@const subDirName = subDir.split('/').pop()}
539
+ <button
540
+ class="flex w-full items-center gap-2 px-3 py-1.5 text-xs hover:bg-zinc-100 dark:hover:bg-zinc-800/50"
541
+ onclick={() => navigateIntoDir(subDir)}
542
+ >
543
+ <Folder class="size-3.5 shrink-0 text-amber-500/70" />
544
+ <span class="truncate font-medium">{subDirName}</span>
545
+ <ChevronRight class="ms-auto size-3 shrink-0 text-muted-foreground" />
546
+ </button>
547
+ {/if}
548
+ {/each}
549
+
550
+ {#each selectedDirContents.files as file, i}
551
+ {#if i < MAX_ITEMS}
552
+ {@const fileName = file.filename.split('/').pop()}
553
+ <button
554
+ class="flex w-full items-center gap-2 px-3 py-1.5 text-xs hover:bg-zinc-100 dark:hover:bg-zinc-800/50"
555
+ class:bg-zinc-100={selectedFile?.filename === file.filename}
556
+ class:dark:bg-zinc-800={selectedFile?.filename === file.filename}
557
+ onclick={() => selectFile(file)}
558
+ >
559
+ <File class="size-3.5 shrink-0 text-muted-foreground/70" />
560
+ <span class="truncate">{fileName}</span>
561
+ <span class="ms-auto shrink-0 text-[10px] tabular-nums text-muted-foreground">
562
+ {formatFileSize(file.uncompressedSize)}
563
+ </span>
564
+ </button>
565
+ {/if}
566
+ {/each}
567
+ </div>
568
+ {:else}
569
+ <div class="flex flex-1 items-center justify-center text-xs text-muted-foreground">
570
+ {t('archive.selectFolder')}
571
+ </div>
572
+ {/if}
573
+ </div>
574
+ </ResizablePane>
575
+
576
+ <ResizableHandle />
577
+
578
+ <!-- Column 3: File details -->
579
+ <ResizablePane defaultSize={30} minSize={15}>
580
+ <div class="flex h-full flex-col">
581
+ {@render fileDetails()}
582
+ </div>
583
+ </ResizablePane>
584
+ </ResizablePaneGroup>
585
+ {/if}
586
+ </div>
@@ -0,0 +1,7 @@
1
+ import type { Tab } from '../../types';
2
+ type $$ComponentProps = {
3
+ tab: Tab;
4
+ };
5
+ declare const ArchiveViewer: import("svelte").Component<$$ComponentProps, {}, "">;
6
+ type ArchiveViewer = ReturnType<typeof ArchiveViewer>;
7
+ export default ArchiveViewer;
@@ -0,0 +1,60 @@
1
+ # viewers/
2
+
3
+ Per-format viewer components. Routed by `ViewerRouter` based on file extension.
4
+
5
+ ```mermaid
6
+ graph TD
7
+ VR[ViewerRouter] -->|parquet,csv,jsonl| TV[TableViewer]
8
+ VR -->|geoparquet map| GPM[GeoParquetMapViewer]
9
+ VR -->|cog,tif| COG[CogViewer]
10
+ VR -->|pmtiles| PMT[PmtilesViewer]
11
+ VR -->|fgb| FGB[FlatGeobufViewer]
12
+ VR -->|zarr| ZV[ZarrViewer]
13
+ VR -->|ipynb| NB[NotebookViewer]
14
+ VR -->|code,json,py| CODE[CodeViewer]
15
+ VR -->|md| MD[MarkdownViewer]
16
+ VR -->|pdf| PDF[PdfViewer]
17
+ VR -->|img| IMG[ImageViewer]
18
+ VR -->|video,audio| MEDIA[MediaViewer]
19
+ VR -->|glb,obj,stl| MODEL[ModelViewer]
20
+ VR -->|zip,tar| ARC[ArchiveViewer]
21
+ VR -->|copc,laz| COPC[CopcViewer]
22
+ VR -->|duckdb,sqlite| DB[DatabaseViewer]
23
+ VR -->|stac| STAC[StacMapViewer]
24
+ VR -->|fallback| RAW[RawViewer]
25
+
26
+ TV --> TG[TableGrid]
27
+ TV --> TTB[TableToolbar]
28
+ TV --> TSB[TableStatusBar]
29
+ TV --> QHP[QueryHistoryPanel]
30
+ GPM --> MC[map/MapContainer]
31
+ GPM --> AT[map/AttributeTable]
32
+ PMT --> PMV[pmtiles/PmtilesMapView]
33
+ PMT --> PMA[pmtiles/PmtilesArchiveView]
34
+ PMT --> PMI[pmtiles/PmtilesTileInspector]
35
+ ```
36
+
37
+ | Viewer | Powered by | Key deps used |
38
+ |--------|-----------|---------------|
39
+ | TableViewer | DuckDB-WASM, Arrow | query/index, utils/wkb, utils/geoarrow, utils/parquet-metadata, utils/url, utils/url-state, file-icons |
40
+ | GeoParquetMapViewer | deck.gl, @geoarrow/deck.gl-layers | query/engine, utils/wkb, utils/geoarrow, utils/deck, map/MapContainer |
41
+ | CogViewer | @developmentseed/deck.gl-geotiff, geotiff v3 | utils/url |
42
+ | PmtilesViewer | pmtiles, MapLibre | utils/pmtiles, utils/url-state, pmtiles/* sub-components |
43
+ | FlatGeobufViewer | flatgeobuf, deck.gl | utils/deck, utils/url |
44
+ | ZarrViewer | zarrita, @carbonplan/zarr-layer | utils/zarr, utils/url-state |
45
+ | CodeViewer | Shiki | utils/shiki, utils/url-state |
46
+ | NotebookViewer | notebookjs, Marked, Shiki | utils/shiki |
47
+ | PdfViewer | pdf.js | utils/pdf |
48
+ | ModelViewer | Babylon.js | utils/model3d |
49
+ | ArchiveViewer | zip.js | utils/archive, utils/url, utils/format |
50
+ | DatabaseViewer | DuckDB-WASM | query/index |
51
+ | MarkdownViewer | Marked, Milkdown | utils/markdown, utils/markdown-sql, editor/MilkdownEditor |
52
+ | RawViewer | custom hex dump | utils/hex, utils/format |
53
+ | ImageViewer | native `<img>` | utils/url |
54
+ | MediaViewer | native `<video>`/`<audio>` | utils/url |
55
+ | StacMapViewer | stac-map iframe | utils/url |
56
+ | CopcViewer | viewer.copc.io iframe | utils/url |
57
+
58
+ All viewers use: `stores/tab-resources` (LRU cleanup), `i18n/t()`, `stores/settings` (theme).
59
+
60
+ Every viewer must follow the pattern in root `CLAUDE.md` (cleanup, tabResources, AbortController, $state.raw).