@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,923 @@
1
+ <script lang="ts">
2
+ import { format as formatSql } from 'sql-formatter';
3
+ import { untrack } from 'svelte';
4
+ import CodeMirrorEditor from '../editor/CodeMirrorEditor.svelte';
5
+ import { buildDuckDbSource, isCloudNativeFormat } from '../../file-icons/index.js';
6
+ import { t } from '../../i18n/index.svelte.js';
7
+ import type { MapQueryResult, SchemaField } from '../../query/engine';
8
+ import { getQueryEngine, QueryCancelledError, type QueryHandle } from '../../query/index.js';
9
+ import { queryHistory } from '../../stores/query-history.svelte.js';
10
+ import { settings } from '../../stores/settings.svelte.js';
11
+ import { tabResources } from '../../stores/tab-resources.svelte.js';
12
+ import type { Tab } from '../../types';
13
+ import type { GeoArrowGeomType } from '../../utils/geoarrow.js';
14
+ import {
15
+ extractBounds,
16
+ extractEpsgFromGeoMeta,
17
+ extractGeometryTypes,
18
+ readParquetMetadata
19
+ } from '../../utils/parquet-metadata.js';
20
+ import {
21
+ buildDuckDbUrl,
22
+ buildHttpsUrl,
23
+ buildStorageUrl,
24
+ canStreamDirectly
25
+ } from '../../utils/url.js';
26
+ import { getUrlView, updateUrlView } from '../../utils/url-state.js';
27
+ import { findGeoColumn, findGeoColumnFromRows, parseWKB, toBinary } from '../../utils/wkb.js';
28
+ import FileInfo from './FileInfo.svelte';
29
+ import LoadProgress, { type ProgressEntry } from './LoadProgress.svelte';
30
+ import QueryHistoryPanel from './QueryHistoryPanel.svelte';
31
+ import TableGrid from './TableGrid.svelte';
32
+ import TableStatusBar from './TableStatusBar.svelte';
33
+ import TableToolbar from './TableToolbar.svelte';
34
+
35
+ let { tab }: { tab: Tab } = $props();
36
+
37
+ let pageSize = $state(settings.featureLimit);
38
+
39
+ let schema = $state<SchemaField[]>([]);
40
+ let columns = $state<string[]>([]);
41
+ let rows = $state.raw<Record<string, any>[]>([]);
42
+ let totalRows = $state<number | null>(null);
43
+ let currentPage = $state(1);
44
+ let loading = $state(true);
45
+ let error = $state<string | null>(null);
46
+ let historyVisible = $state(false);
47
+ let hasGeo = $state(false);
48
+ let isStac = $state(false);
49
+ // Restore view mode from URL hash if present
50
+ const urlView = getUrlView();
51
+ let viewMode = $state<'table' | 'map' | 'stac' | 'info'>(
52
+ urlView === 'map' ? 'map' : urlView === 'stac' ? 'stac' : urlView === 'info' ? 'info' : 'table'
53
+ );
54
+ let sqlQuery = $state('');
55
+ let customSql = $state('');
56
+ let queryRunning = $state(false);
57
+ let executionTimeMs = $state(0);
58
+
59
+ // Progress stage for user feedback
60
+ let loadStage = $state('');
61
+ let loadProgress = $state<ProgressEntry[]>([]);
62
+
63
+ // Load cancellation: incrementing ID so stale loads are ignored
64
+ let loadGeneration = 0;
65
+ let activeHandle: QueryHandle | null = null;
66
+ let forceCancelVisible = $state(false);
67
+ let forceCancelTimer: ReturnType<typeof setTimeout> | null = null;
68
+
69
+ // Sort state
70
+ let sortColumn = $state<string | null>(null);
71
+ let sortDirection = $state<'asc' | 'desc' | null>(null);
72
+
73
+ // Tracks whether the current mapData came from a user-edited query (no system limit)
74
+ let isCustomQuery = $state(false);
75
+
76
+ // Geo column state for unified table+map query
77
+ let geoCol = $state<string | null>(null);
78
+ let geoColType = $state<string>('');
79
+ let sourceCrs = $state<string | null>(null);
80
+ let mapData = $state.raw<MapQueryResult | null>(null);
81
+ let knownGeomType = $state<GeoArrowGeomType | undefined>(undefined);
82
+ let metadataBounds = $state<[number, number, number, number] | null>(null);
83
+
84
+ const totalPages = $derived(totalRows != null ? Math.max(1, Math.ceil(totalRows / pageSize)) : 1);
85
+ const connId = $derived(tab?.connectionId ?? '');
86
+
87
+ // Build column type map from schema
88
+ const columnTypes = $derived(Object.fromEntries(schema.map((f) => [f.name, f.type])));
89
+
90
+ // Columns for display — exclude internal __wkb helper
91
+ const displayColumns = $derived(columns.filter((c) => c !== '__wkb'));
92
+
93
+ function buildDefaultSql(offset = 0): string {
94
+ const fileUrl = buildDuckDbUrl(tab);
95
+ const source = buildDuckDbSource(tab.path, fileUrl);
96
+
97
+ let sql: string;
98
+ if (geoCol) {
99
+ const quoted = `"${geoCol}"`;
100
+ const upper = geoColType.toUpperCase();
101
+ // Spatial types that ST_AsWKB accepts directly (GEOMETRY, WKB_BLOB, etc.).
102
+ // Includes Arrow "Binary"/"LargeBinary" — DuckDB GEOMETRY columns from
103
+ // ST_ReadSHP/ST_Read appear as Arrow Binary but are NOT WKB blobs.
104
+ const isSpatialType =
105
+ upper === 'GEOMETRY' ||
106
+ upper === 'GEOGRAPHY' ||
107
+ upper === 'WKB_BLOB' ||
108
+ upper.includes('POINT') ||
109
+ upper.includes('LINESTRING') ||
110
+ upper.includes('POLYGON') ||
111
+ upper.includes('BINARY'); // Arrow serialization of DuckDB GEOMETRY
112
+ // Actual WKB BLOB columns (e.g. GeoParquet) need explicit ST_GeomFromWKB
113
+ // because DuckDB has no implicit BLOB→GEOMETRY cast.
114
+ const isWkbBlob = upper === 'BLOB' || upper === 'BYTEA';
115
+
116
+ let wkbExpr: string;
117
+ if (isWkbBlob && !sourceCrs) {
118
+ // Already WKB — use directly, no conversion needed
119
+ wkbExpr = `${quoted} AS __wkb`;
120
+ } else {
121
+ let geomExpr = isSpatialType
122
+ ? quoted
123
+ : isWkbBlob
124
+ ? `ST_GeomFromWKB(${quoted})`
125
+ : `ST_GeomFromGeoJSON(${quoted})`;
126
+ if (sourceCrs) {
127
+ geomExpr = `ST_Transform(${geomExpr}, '${sourceCrs}', 'EPSG:4326', always_xy := true)`;
128
+ }
129
+ wkbExpr = `ST_AsWKB(${geomExpr}) AS __wkb`;
130
+ }
131
+ sql = `SELECT * EXCLUDE(${quoted}), ${wkbExpr} FROM ${source}`;
132
+ } else {
133
+ sql = `SELECT * FROM ${source}`;
134
+ }
135
+
136
+ if (sortColumn && sortDirection) {
137
+ sql += ` ORDER BY "${sortColumn}" ${sortDirection.toUpperCase()}`;
138
+ }
139
+
140
+ sql += ` LIMIT ${pageSize} OFFSET ${offset}`;
141
+ return sql;
142
+ }
143
+
144
+ function extractMapData(queryRows: Record<string, any>[]): MapQueryResult | null {
145
+ if (!geoCol || queryRows.length === 0 || !columns.includes('__wkb')) return null;
146
+
147
+ const wkbArrays: Uint8Array[] = [];
148
+ let geometryType = 'POINT';
149
+
150
+ for (const row of queryRows) {
151
+ const bin = toBinary(row.__wkb);
152
+ if (bin) wkbArrays.push(bin);
153
+ }
154
+
155
+ // Detect geometry type from first WKB value
156
+ if (wkbArrays.length > 0) {
157
+ const parsed = parseWKB(wkbArrays[0]);
158
+ if (parsed) geometryType = parsed.type.toUpperCase();
159
+ }
160
+
161
+ // Build attributes map (exclude __wkb)
162
+ const attributes = new Map<string, { values: any[]; type: string }>();
163
+ for (const col of columns) {
164
+ if (col === '__wkb') continue;
165
+ const fieldType = columnTypes[col] ?? 'VARCHAR';
166
+ const values = queryRows.map((r) => r[col]);
167
+ attributes.set(col, { values, type: fieldType });
168
+ }
169
+
170
+ return { wkbArrays, geometryType, attributes, rowCount: wkbArrays.length };
171
+ }
172
+
173
+ // Track last loaded tab to prevent duplicate loads
174
+ let lastLoadedTabId = '';
175
+
176
+ // Register cleanup so tab store can free heavy data on close
177
+ $effect(() => {
178
+ const id = tab.id;
179
+ const unregister = tabResources.register(id, () => {
180
+ loadGeneration++;
181
+ // Cancel in-flight query if any
182
+ if (activeHandle) {
183
+ activeHandle.cancel();
184
+ activeHandle = null;
185
+ }
186
+ if (forceCancelTimer) {
187
+ clearTimeout(forceCancelTimer);
188
+ forceCancelTimer = null;
189
+ }
190
+ forceCancelVisible = false;
191
+ rows = [];
192
+ schema = [];
193
+ columns = [];
194
+ mapData = null;
195
+ geoCol = null;
196
+ knownGeomType = undefined;
197
+ metadataBounds = null;
198
+ error = null;
199
+ });
200
+ return unregister;
201
+ });
202
+
203
+ $effect(() => {
204
+ if (!tab) return;
205
+ const tabId = tab.id;
206
+ untrack(() => {
207
+ if (tabId !== lastLoadedTabId) {
208
+ const isInitialLoad = lastLoadedTabId === '';
209
+ lastLoadedTabId = tabId;
210
+ if (!isInitialLoad) {
211
+ // Reset view mode on tab switch to avoid stale iframe/map views
212
+ viewMode = 'table';
213
+ updateUrlView('');
214
+ }
215
+ loadTable();
216
+ } else {
217
+ console.warn('[TableViewer] $effect re-fired but tab unchanged, skipping loadTable', tabId);
218
+ }
219
+ });
220
+ });
221
+
222
+ function cancelLoad() {
223
+ loadGeneration++;
224
+ loadStage = t('table.cancellingQuery');
225
+
226
+ // Attempt graceful cancel via conn.cancelSent()
227
+ if (activeHandle) {
228
+ activeHandle.cancel();
229
+ // If the query hasn't settled after 5s, show force-cancel button
230
+ forceCancelTimer = setTimeout(() => {
231
+ forceCancelVisible = true;
232
+ }, 5000);
233
+ } else {
234
+ loading = false;
235
+ queryRunning = false;
236
+ loadStage = '';
237
+ error = t('table.queryCancelled');
238
+ }
239
+ }
240
+
241
+ async function forceCancel() {
242
+ if (forceCancelTimer) {
243
+ clearTimeout(forceCancelTimer);
244
+ forceCancelTimer = null;
245
+ }
246
+ forceCancelVisible = false;
247
+ loadStage = '';
248
+
249
+ const engine = await getQueryEngine();
250
+ if (engine.forceCancel) {
251
+ await engine.forceCancel();
252
+ }
253
+
254
+ activeHandle = null;
255
+ loading = false;
256
+ queryRunning = false;
257
+ error = t('table.queryCancelled');
258
+ }
259
+
260
+ async function loadTable() {
261
+ const thisGen = ++loadGeneration;
262
+
263
+ // Cancel in-flight query from a previous load to prevent duplicate concurrent queries
264
+ if (activeHandle) {
265
+ activeHandle.cancel();
266
+ activeHandle = null;
267
+ }
268
+
269
+ loading = true;
270
+ error = null;
271
+ geoCol = null;
272
+ geoColType = '';
273
+ sourceCrs = null;
274
+ mapData = null;
275
+ isCustomQuery = false;
276
+ knownGeomType = undefined;
277
+ metadataBounds = null;
278
+ loadStage = t('table.preparingQuery');
279
+ loadProgress = [];
280
+
281
+ // Set SQL eagerly so editor shows the query while loading
282
+ const initialSql = buildDefaultSql(0);
283
+ sqlQuery = initialSql;
284
+ customSql = initialSql;
285
+
286
+ try {
287
+ const fileUrl = buildDuckDbUrl(tab);
288
+ const httpsUrl = buildHttpsUrl(tab);
289
+ const cloudNative = isCloudNativeFormat(tab.path);
290
+ const isParquet = /\.parquet$/i.test(tab.path);
291
+ const streamable = canStreamDirectly(tab);
292
+
293
+ // Start DuckDB boot immediately (runs in parallel with hyparquet)
294
+ loadStage = t('table.initEngine');
295
+ const enginePromise = getQueryEngine();
296
+
297
+ // ── Fast metadata via hyparquet (runs concurrently with DuckDB boot) ──
298
+ let metaFromHyparquet = false;
299
+ let needsDuckDbCrs = false;
300
+ if (cloudNative && isParquet && streamable) {
301
+ try {
302
+ loadStage = t('table.readingMetadata');
303
+ const meta = await readParquetMetadata(httpsUrl);
304
+ if (thisGen !== loadGeneration) return;
305
+
306
+ // Format detection
307
+ const formatName = meta.geo ? 'GeoParquet' : 'Parquet';
308
+ loadProgress = [
309
+ { label: t('progress.format'), value: formatName },
310
+ { label: t('progress.source'), value: t('progress.rangeRequest') }
311
+ ];
312
+
313
+ // Instant schema display — use hyparquet-detected types as-is.
314
+ // mapParquetType() returns 'GEOMETRY' for native Parquet GEOMETRY
315
+ // logical type (Format 2.11+) and 'BLOB' for plain BYTE_ARRAY.
316
+ // This matches what DuckDB reports with enable_geoparquet_conversion=false.
317
+ schema = meta.schema.map((s) => ({
318
+ name: s.name,
319
+ type: s.type,
320
+ nullable: true
321
+ }));
322
+ columns = schema.map((f) => f.name);
323
+ totalRows = meta.rowCount;
324
+
325
+ // Column names preview (truncated)
326
+ const colPreview =
327
+ columns.length <= 8
328
+ ? columns.join(', ')
329
+ : `${columns.slice(0, 7).join(', ')}, +${columns.length - 7} more`;
330
+ loadProgress = [
331
+ ...loadProgress,
332
+ { label: t('progress.columns'), value: String(columns.length), detail: colPreview },
333
+ { label: t('progress.rows'), value: meta.rowCount.toLocaleString() }
334
+ ];
335
+
336
+ // Row groups & compression
337
+ if (meta.numRowGroups > 0) {
338
+ loadProgress = [
339
+ ...loadProgress,
340
+ { label: t('progress.rowGroups'), value: String(meta.numRowGroups) }
341
+ ];
342
+ }
343
+ if (meta.compression) {
344
+ loadProgress = [
345
+ ...loadProgress,
346
+ { label: t('progress.compression'), value: meta.compression }
347
+ ];
348
+ }
349
+
350
+ // Created by (tool)
351
+ if (meta.createdBy) {
352
+ loadProgress = [
353
+ ...loadProgress,
354
+ { label: t('progress.createdBy'), value: meta.createdBy }
355
+ ];
356
+ }
357
+
358
+ if (meta.geo) {
359
+ geoCol = meta.geo.primaryColumn;
360
+ // With enable_geoparquet_conversion=false, DuckDB reads ALL
361
+ // GeoParquet geometry columns as BLOB regardless of Parquet
362
+ // logical type. Native Parquet GEOMETRY (Format 2.11+) is a
363
+ // DuckDB v1.5 feature not yet in WASM (v1.33).
364
+ geoColType = 'BLOB';
365
+ sourceCrs = extractEpsgFromGeoMeta(meta.geo);
366
+ const geomTypes = extractGeometryTypes(meta.geo);
367
+ if (geomTypes.length === 1) knownGeomType = geomTypes[0];
368
+ metadataBounds = extractBounds(meta.geo);
369
+
370
+ const geomLabel = geomTypes.join(', ') || geoColType;
371
+ const primaryCol = meta.geo.columns[meta.geo.primaryColumn];
372
+ const encodingInfo = primaryCol?.encoding ?? 'WKB';
373
+ loadProgress = [
374
+ ...loadProgress,
375
+ { label: t('progress.geometry'), value: `${geoCol} (${geomLabel})` },
376
+ { label: t('progress.encoding'), value: encodingInfo }
377
+ ];
378
+ if (sourceCrs) {
379
+ loadProgress = [...loadProgress, { label: t('progress.crs'), value: sourceCrs }];
380
+ } else {
381
+ loadProgress = [
382
+ ...loadProgress,
383
+ { label: t('progress.crs'), value: 'EPSG:4326 (WGS84)' }
384
+ ];
385
+ }
386
+ if (metadataBounds) {
387
+ const [minX, minY, maxX, maxY] = metadataBounds;
388
+ loadProgress = [
389
+ ...loadProgress,
390
+ {
391
+ label: t('progress.bounds'),
392
+ value: `${minX.toFixed(2)}, ${minY.toFixed(2)}, ${maxX.toFixed(2)}, ${maxY.toFixed(2)}`
393
+ }
394
+ ];
395
+ }
396
+ }
397
+
398
+ // No GeoParquet "geo" metadata — detect native Parquet GEOMETRY
399
+ // columns (Format 2.11+) via schema column names/types
400
+ if (!geoCol) {
401
+ const detectedGeoCol = findGeoColumn(schema);
402
+ if (detectedGeoCol) {
403
+ geoCol = detectedGeoCol;
404
+ // DuckDB WASM v1.33 doesn't support native Parquet GEOMETRY —
405
+ // columns are always BLOB with enable_geoparquet_conversion=false.
406
+ geoColType = 'BLOB';
407
+ needsDuckDbCrs = true;
408
+ loadProgress = [
409
+ ...loadProgress,
410
+ { label: t('progress.geometry'), value: `${geoCol} (native Parquet)` }
411
+ ];
412
+ }
413
+ }
414
+
415
+ hasGeo = geoCol !== null;
416
+ isStac = schema.some((f) => f.name === 'stac_version');
417
+ if (isStac) {
418
+ loadProgress = [
419
+ ...loadProgress,
420
+ { label: t('progress.format'), value: t('progress.stacDetected') }
421
+ ];
422
+ }
423
+ metaFromHyparquet = true;
424
+ } catch {
425
+ // hyparquet failed (CORS, auth, format) — fall back to DuckDB
426
+ }
427
+ }
428
+
429
+ // Wait for DuckDB engine
430
+ loadStage = metaFromHyparquet ? t('table.bootingEngine') : t('table.initEngine');
431
+ const engine = await enginePromise;
432
+ if (thisGen !== loadGeneration) return;
433
+
434
+ // If hyparquet detected a geo column but couldn't determine CRS
435
+ // (native Parquet GEOMETRY without "geo" KV metadata), use DuckDB
436
+ if (metaFromHyparquet && needsDuckDbCrs && geoCol) {
437
+ try {
438
+ sourceCrs = await engine.detectCrs(connId, fileUrl, geoCol);
439
+ if (thisGen !== loadGeneration) return;
440
+ if (sourceCrs) {
441
+ loadProgress = [...loadProgress, { label: t('progress.crs'), value: sourceCrs }];
442
+ } else {
443
+ loadProgress = [
444
+ ...loadProgress,
445
+ { label: t('progress.crs'), value: 'EPSG:4326 (WGS84)' }
446
+ ];
447
+ }
448
+ } catch {
449
+ // CRS detection failed — continue with WGS84 assumption
450
+ }
451
+ }
452
+
453
+ if (cloudNative && !metaFromHyparquet) {
454
+ // Fallback: DuckDB metadata queries
455
+ loadStage = t('table.loadingSchema');
456
+ loadProgress = [
457
+ ...loadProgress,
458
+ { label: t('progress.source'), value: t('progress.duckdbFallback') }
459
+ ];
460
+
461
+ if (engine.getSchemaAndCrs) {
462
+ const result = await engine.getSchemaAndCrs(connId, fileUrl, findGeoColumn);
463
+ if (thisGen !== loadGeneration) return;
464
+ schema = result.schema;
465
+ columns = schema.map((f) => f.name);
466
+ const colPreview =
467
+ columns.length <= 8
468
+ ? columns.join(', ')
469
+ : `${columns.slice(0, 7).join(', ')}, +${columns.length - 7} more`;
470
+ loadProgress = [
471
+ ...loadProgress,
472
+ { label: t('progress.columns'), value: String(columns.length), detail: colPreview }
473
+ ];
474
+
475
+ geoCol = result.geomCol;
476
+ if (result.geomCol) {
477
+ const geoField = schema.find((f) => f.name === result.geomCol);
478
+ geoColType = geoField?.type ?? 'GEOMETRY';
479
+ loadProgress = [
480
+ ...loadProgress,
481
+ { label: t('progress.geometry'), value: `${result.geomCol} (${geoColType})` }
482
+ ];
483
+ sourceCrs = result.crs;
484
+ if (sourceCrs) {
485
+ loadProgress = [...loadProgress, { label: t('progress.crs'), value: sourceCrs }];
486
+ }
487
+ }
488
+ } else {
489
+ schema = await engine.getSchema(connId, fileUrl);
490
+ if (thisGen !== loadGeneration) return;
491
+ columns = schema.map((f) => f.name);
492
+ const colPreview =
493
+ columns.length <= 8
494
+ ? columns.join(', ')
495
+ : `${columns.slice(0, 7).join(', ')}, +${columns.length - 7} more`;
496
+ loadProgress = [
497
+ ...loadProgress,
498
+ { label: t('progress.columns'), value: String(columns.length), detail: colPreview }
499
+ ];
500
+
501
+ const detectedGeoCol = findGeoColumn(schema);
502
+ geoCol = detectedGeoCol;
503
+ if (detectedGeoCol) {
504
+ const geoField = schema.find((f) => f.name === detectedGeoCol);
505
+ geoColType = geoField?.type ?? 'GEOMETRY';
506
+ loadProgress = [
507
+ ...loadProgress,
508
+ { label: t('progress.geometry'), value: `${detectedGeoCol} (${geoColType})` }
509
+ ];
510
+ sourceCrs = await engine.detectCrs(connId, fileUrl, detectedGeoCol);
511
+ if (thisGen !== loadGeneration) return;
512
+ if (sourceCrs) {
513
+ loadProgress = [...loadProgress, { label: t('progress.crs'), value: sourceCrs }];
514
+ }
515
+ }
516
+ }
517
+ hasGeo = geoCol !== null;
518
+ isStac = schema.some((f) => f.name === 'stac_version');
519
+ if (isStac) {
520
+ loadProgress = [
521
+ ...loadProgress,
522
+ { label: t('progress.format'), value: t('progress.stacDetected') }
523
+ ];
524
+ }
525
+ }
526
+
527
+ // Rebuild SQL now that we know the actual path resolution
528
+ sqlQuery = buildDefaultSql(0);
529
+ customSql = sqlQuery;
530
+
531
+ loadStage = t('table.runningQuery');
532
+ const start = performance.now();
533
+ const result = await executeQuery(sqlQuery);
534
+ if (thisGen !== loadGeneration) return;
535
+ executionTimeMs = Math.round(performance.now() - start);
536
+
537
+ if (!cloudNative && result) {
538
+ // Non-cloud-native (CSV, GeoJSON, etc.): derive schema from the
539
+ // query result so we avoid a second full-file download.
540
+ schema = (result.columns ?? []).map((col, i) => ({
541
+ name: col,
542
+ type: result.types?.[i] ?? 'VARCHAR',
543
+ nullable: true
544
+ }));
545
+ columns = schema.map((f) => f.name);
546
+
547
+ const detectedGeoCol = findGeoColumn(schema);
548
+ geoCol = detectedGeoCol;
549
+ if (detectedGeoCol) {
550
+ const geoField = schema.find((f) => f.name === detectedGeoCol);
551
+ geoColType = geoField?.type ?? 'GEOMETRY';
552
+ }
553
+ hasGeo = detectedGeoCol !== null;
554
+ isStac = schema.some((f) => f.name === 'stac_version');
555
+
556
+ // Re-query with geo-aware SQL if geo column was detected
557
+ if (hasGeo) {
558
+ const geoSql = buildDefaultSql(0);
559
+ sqlQuery = geoSql;
560
+ customSql = geoSql;
561
+ const geoStart = performance.now();
562
+ await executeQuery(geoSql);
563
+ if (thisGen !== loadGeneration) return;
564
+ executionTimeMs = Math.round(performance.now() - geoStart);
565
+ }
566
+ }
567
+
568
+ // If schema-only detection missed geo, try content sniffing on actual rows
569
+ if (!hasGeo && rows.length > 0) {
570
+ hasGeo = findGeoColumnFromRows(rows, schema) !== null;
571
+ }
572
+
573
+ mapData = extractMapData(rows);
574
+
575
+ // Auto-switch to table if URL hash requested map/stac but no geo/stac was detected
576
+ if (!hasGeo && viewMode === 'map') {
577
+ viewMode = 'table';
578
+ }
579
+ if (!isStac && viewMode === 'stac') {
580
+ viewMode = 'table';
581
+ }
582
+
583
+ loading = false;
584
+ loadStage = '';
585
+ updateUrlView(viewMode);
586
+
587
+ // Row count: skip if hyparquet already provided it
588
+ if (totalRows === null) {
589
+ if (!cloudNative && rows.length < pageSize) {
590
+ totalRows = rows.length;
591
+ } else {
592
+ loadStage = t('table.countingRows');
593
+ engine
594
+ .getRowCount(connId, fileUrl)
595
+ .then((count) => {
596
+ if (thisGen === loadGeneration) {
597
+ totalRows = count;
598
+ }
599
+ loadStage = '';
600
+ })
601
+ .catch(() => {
602
+ loadStage = '';
603
+ });
604
+ }
605
+ }
606
+ } catch (err) {
607
+ if (thisGen !== loadGeneration) return;
608
+ console.error('[TableViewer] Error:', err);
609
+ error = err instanceof Error ? err.message : String(err);
610
+ loading = false;
611
+ loadStage = '';
612
+ }
613
+ }
614
+
615
+ async function executeQuery(sql: string) {
616
+ try {
617
+ const engine = await getQueryEngine();
618
+
619
+ if (engine.queryCancellable) {
620
+ const handle = engine.queryCancellable(connId, sql);
621
+ activeHandle = handle;
622
+ try {
623
+ const result = await handle.result;
624
+ columns = result.columns;
625
+ rows = result.rows;
626
+ return result;
627
+ } finally {
628
+ activeHandle = null;
629
+ if (forceCancelTimer) {
630
+ clearTimeout(forceCancelTimer);
631
+ forceCancelTimer = null;
632
+ }
633
+ forceCancelVisible = false;
634
+ }
635
+ }
636
+
637
+ const result = await engine.query(connId, sql);
638
+ columns = result.columns;
639
+ rows = result.rows;
640
+ return result;
641
+ } catch (err) {
642
+ if (err instanceof QueryCancelledError) {
643
+ loading = false;
644
+ queryRunning = false;
645
+ loadStage = '';
646
+ error = t('table.queryCancelled');
647
+ return null;
648
+ }
649
+ error = err instanceof Error ? err.message : String(err);
650
+ return null;
651
+ }
652
+ }
653
+
654
+ async function loadPage(page: number) {
655
+ const offset = (page - 1) * pageSize;
656
+ const sql = buildDefaultSql(offset);
657
+ sqlQuery = sql;
658
+ customSql = sql;
659
+ const start = performance.now();
660
+ await executeQuery(sql);
661
+ executionTimeMs = Math.round(performance.now() - start);
662
+ mapData = extractMapData(rows);
663
+ currentPage = page;
664
+ }
665
+
666
+ async function runCustomSql() {
667
+ queryRunning = true;
668
+ error = null;
669
+ isCustomQuery = true;
670
+ loadStage = t('table.runningCustomQuery');
671
+ const start = performance.now();
672
+ try {
673
+ sqlQuery = customSql;
674
+ await executeQuery(customSql);
675
+ executionTimeMs = Math.round(performance.now() - start);
676
+ currentPage = 1;
677
+ totalRows = null;
678
+ updateUrlView('query');
679
+
680
+ // Pass custom query results to the map (respects user's LIMIT or lack thereof)
681
+ mapData = extractMapData(rows);
682
+
683
+ // Record in history
684
+ queryHistory.add({
685
+ sql: customSql,
686
+ timestamp: Date.now(),
687
+ durationMs: executionTimeMs,
688
+ rowCount: rows.length,
689
+ connectionId: connId || undefined
690
+ });
691
+ } catch (err) {
692
+ executionTimeMs = Math.round(performance.now() - start);
693
+ error = err instanceof Error ? err.message : String(err);
694
+
695
+ queryHistory.add({
696
+ sql: customSql,
697
+ timestamp: Date.now(),
698
+ durationMs: executionTimeMs,
699
+ rowCount: 0,
700
+ error: error ?? undefined,
701
+ connectionId: connId || undefined
702
+ });
703
+ } finally {
704
+ queryRunning = false;
705
+ loadStage = '';
706
+ }
707
+ }
708
+
709
+ function handleSqlChange(val: string) {
710
+ customSql = val;
711
+ }
712
+
713
+ function handleFormatSql() {
714
+ try {
715
+ customSql = formatSql(customSql, { language: 'sql' });
716
+ } catch {
717
+ // ignore format errors
718
+ }
719
+ }
720
+
721
+ function handleSort(column: string, direction: 'asc' | 'desc' | null) {
722
+ sortColumn = direction ? column : null;
723
+ sortDirection = direction;
724
+ // Rebuild the default sql and reload
725
+ const sql = buildDefaultSql(0);
726
+ sqlQuery = sql;
727
+ customSql = sql;
728
+ currentPage = 1;
729
+ const start = performance.now();
730
+ executeQuery(sql).then(() => {
731
+ executionTimeMs = Math.round(performance.now() - start);
732
+ mapData = extractMapData(rows);
733
+ });
734
+ }
735
+
736
+ function handleHistorySelect(sql: string) {
737
+ customSql = sql;
738
+ runCustomSql();
739
+ }
740
+
741
+ function handlePageSizeChange(size: number) {
742
+ pageSize = size;
743
+ settings.setFeatureLimit(size);
744
+ currentPage = 1;
745
+ const sql = buildDefaultSql(0);
746
+ sqlQuery = sql;
747
+ customSql = sql;
748
+ const start = performance.now();
749
+ executeQuery(sql).then(() => {
750
+ executionTimeMs = Math.round(performance.now() - start);
751
+ mapData = extractMapData(rows);
752
+ });
753
+ }
754
+
755
+ function prevPage() {
756
+ if (currentPage > 1) loadPage(currentPage - 1);
757
+ }
758
+
759
+ function nextPage() {
760
+ if (totalRows != null && currentPage < totalPages) loadPage(currentPage + 1);
761
+ else if (totalRows == null && rows.length === pageSize) loadPage(currentPage + 1);
762
+ }
763
+
764
+ function goToPage(page: number) {
765
+ if (page >= 1 && page <= totalPages) loadPage(page);
766
+ }
767
+
768
+ function toggleInfo() {
769
+ viewMode = viewMode === 'info' ? 'table' : 'info';
770
+ updateUrlView(viewMode);
771
+ }
772
+
773
+ function toggleHistory() {
774
+ historyVisible = !historyVisible;
775
+ }
776
+
777
+ function toggleView() {
778
+ viewMode = viewMode === 'map' ? 'table' : 'map';
779
+ updateUrlView(viewMode);
780
+ }
781
+
782
+ function setStacView() {
783
+ viewMode = viewMode === 'stac' ? 'table' : 'stac';
784
+ updateUrlView(viewMode);
785
+ }
786
+ </script>
787
+
788
+ <div class="flex h-full flex-col">
789
+ <!-- Toolbar — always visible -->
790
+ <TableToolbar
791
+ {tab}
792
+ fileName={tab.name}
793
+ columnCount={displayColumns.length}
794
+ rowCount={totalRows ?? 0}
795
+ {currentPage}
796
+ {totalPages}
797
+ {pageSize}
798
+ {historyVisible}
799
+ {hasGeo}
800
+ {isStac}
801
+ {viewMode}
802
+ onPrevPage={prevPage}
803
+ onNextPage={nextPage}
804
+ onGoToPage={goToPage}
805
+ onToggleInfo={toggleInfo}
806
+ onToggleHistory={toggleHistory}
807
+ onToggleView={toggleView}
808
+ onToggleStac={setStacView}
809
+ onPageSizeChange={handlePageSizeChange}
810
+ />
811
+
812
+ {#if viewMode === 'table'}
813
+ <!-- SQL Query Bar — hidden during schema/CRS detection, shown once query starts running -->
814
+ <div class="border-b border-zinc-200 px-2 py-1.5 sm:px-4 dark:border-zinc-800" class:hidden={loading && loadStage !== t('table.runningQuery')}>
815
+ <div class="flex items-start gap-1.5 sm:gap-2">
816
+ <div class="min-w-0 flex-1">
817
+ <CodeMirrorEditor
818
+ value={customSql}
819
+ onChange={handleSqlChange}
820
+ onExecute={runCustomSql}
821
+ placeholder={t('table.enterSql')}
822
+ schemaColumns={displayColumns}
823
+ />
824
+ </div>
825
+ <div class="flex shrink-0 flex-col gap-1">
826
+ <button
827
+ class="rounded bg-primary px-2 py-1 text-xs text-primary-foreground hover:bg-primary/90 disabled:opacity-50 sm:px-3"
828
+ onclick={runCustomSql}
829
+ disabled={queryRunning || loading}
830
+ >
831
+ {queryRunning ? t('table.running') : t('table.run')}
832
+ </button>
833
+ <button
834
+ class="rounded px-2 py-1 text-xs text-zinc-400 hover:bg-zinc-100 sm:px-3 dark:hover:bg-zinc-800"
835
+ onclick={handleFormatSql}
836
+ >
837
+ {t('table.format')}
838
+ </button>
839
+ </div>
840
+ </div>
841
+ </div>
842
+
843
+ {#if error}
844
+ <div
845
+ class="border-b border-red-200 bg-red-50 px-4 py-2 dark:border-red-800 dark:bg-red-950"
846
+ >
847
+ <p class="text-xs text-red-600 dark:text-red-400">{error}</p>
848
+ {#if tab.source === 'remote'}
849
+ <p class="mt-1 text-[10px] text-zinc-400 break-all">{buildStorageUrl(tab)}</p>
850
+ {/if}
851
+ </div>
852
+ {/if}
853
+
854
+ <!-- Content area: loading / table + side panels -->
855
+ <div class="relative flex flex-1 overflow-hidden">
856
+ {#if loading || queryRunning}
857
+ <LoadProgress
858
+ stage={loadStage}
859
+ entries={loadProgress}
860
+ onCancel={cancelLoad}
861
+ {forceCancelVisible}
862
+ onForceCancel={forceCancel}
863
+ />
864
+ {:else if error && rows.length === 0}
865
+ <div class="flex flex-1 items-center justify-center">
866
+ <div
867
+ class="max-w-lg rounded-lg border border-red-300 bg-red-50 px-6 py-4 text-center dark:border-red-800 dark:bg-red-950"
868
+ >
869
+ <p class="text-sm text-red-600 dark:text-red-400">{error}</p>
870
+ </div>
871
+ </div>
872
+ {:else}
873
+ <TableGrid
874
+ columns={displayColumns}
875
+ {rows}
876
+ totalRows={totalRows ?? rows.length}
877
+ {columnTypes}
878
+ {sortColumn}
879
+ {sortDirection}
880
+ onSort={handleSort}
881
+ />
882
+ {/if}
883
+ <QueryHistoryPanel
884
+ visible={historyVisible}
885
+ onSelect={handleHistorySelect}
886
+ onClose={toggleHistory}
887
+ />
888
+ </div>
889
+
890
+ <!-- Status bar — table mode only -->
891
+ <TableStatusBar
892
+ rowCount={rows.length}
893
+ {executionTimeMs}
894
+ loading={loading || queryRunning}
895
+ columns={displayColumns}
896
+ {rows}
897
+ fileName={tab.name}
898
+ />
899
+ {:else if viewMode === 'info'}
900
+ <!-- Info mode — file metadata & schema -->
901
+ <div class="min-h-0 flex-1 overflow-auto">
902
+ <FileInfo
903
+ entries={loadProgress}
904
+ {schema}
905
+ parquetUrl={/\.parquet$/i.test(tab.path) ? buildHttpsUrl(tab) : ''}
906
+ />
907
+ </div>
908
+ {:else if viewMode === 'stac'}
909
+ <!-- STAC Map mode — full size -->
910
+ <div class="flex-1 overflow-hidden">
911
+ {#await import('./StacMapViewer.svelte') then StacMapViewer}
912
+ <StacMapViewer.default {tab} />
913
+ {/await}
914
+ </div>
915
+ {:else if viewMode === 'map'}
916
+ <!-- Map mode — full size -->
917
+ <div class="flex-1 overflow-hidden">
918
+ {#await import('./GeoParquetMapViewer.svelte') then GeoParquetMapViewer}
919
+ <GeoParquetMapViewer.default {tab} {schema} {mapData} {sourceCrs} {knownGeomType} {metadataBounds} {isCustomQuery} progressEntries={loadProgress} />
920
+ {/await}
921
+ </div>
922
+ {/if}
923
+ </div>