@walkthru-earth/objex 1.3.0 → 1.4.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 (182) hide show
  1. package/LICENSE +5 -0
  2. package/README.md +20 -12
  3. package/dist/components/browser/FileTreeSidebar.svelte +32 -17
  4. package/dist/components/layout/AboutSheet.svelte +5 -2
  5. package/dist/components/layout/ConnectionDialog.svelte +1 -1
  6. package/dist/components/layout/SettingsSheet.svelte +237 -0
  7. package/dist/components/layout/SettingsSheet.svelte.d.ts +6 -0
  8. package/dist/components/layout/Sidebar.svelte +73 -6
  9. package/dist/components/layout/Sidebar.svelte.d.ts +4 -1
  10. package/dist/components/layout/StatusBar.svelte +1 -1
  11. package/dist/components/layout/TabBar.svelte +2 -2
  12. package/dist/components/ui/context-menu/context-menu-radio-group.svelte.d.ts +1 -1
  13. package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte.d.ts +1 -1
  14. package/dist/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte.d.ts +1 -1
  15. package/dist/components/ui/input/input.svelte.d.ts +1 -1
  16. package/dist/components/ui/resizable/index.d.ts +1 -1
  17. package/dist/components/ui/resizable/index.js +2 -2
  18. package/dist/components/ui/slider/index.d.ts +3 -0
  19. package/dist/components/ui/slider/index.js +5 -0
  20. package/dist/components/ui/slider/range-slider.svelte +94 -0
  21. package/dist/components/ui/slider/range-slider.svelte.d.ts +21 -0
  22. package/dist/components/ui/slider/slider.svelte +83 -0
  23. package/dist/components/ui/slider/slider.svelte.d.ts +7 -0
  24. package/dist/components/viewers/ArchiveViewer.svelte +2 -2
  25. package/dist/components/viewers/CodeViewer.svelte +31 -22
  26. package/dist/components/viewers/CogControls.svelte +338 -184
  27. package/dist/components/viewers/CogControls.svelte.d.ts +33 -10
  28. package/dist/components/viewers/CogViewer.svelte +320 -119
  29. package/dist/components/viewers/CopcViewer.svelte +1 -1
  30. package/dist/components/viewers/FlatGeobufViewer.svelte +1 -1
  31. package/dist/components/viewers/GeoParquetMapViewer.svelte +6 -6
  32. package/dist/components/viewers/GeoParquetMapViewer.svelte.d.ts +1 -1
  33. package/dist/components/viewers/ImageViewer.svelte +2 -2
  34. package/dist/components/viewers/MarkdownViewer.svelte +12 -9
  35. package/dist/components/viewers/MediaViewer.svelte +2 -2
  36. package/dist/components/viewers/ModelViewer.svelte +1 -1
  37. package/dist/components/viewers/MultiCogViewer.svelte +467 -102
  38. package/dist/components/viewers/MultiCogViewer.svelte.d.ts +1 -1
  39. package/dist/components/viewers/NotebookViewer.svelte +6 -3
  40. package/dist/components/viewers/PdfViewer.svelte +2 -2
  41. package/dist/components/viewers/PmtilesViewer.svelte +3 -6
  42. package/dist/components/viewers/RawViewer.svelte +6 -3
  43. package/dist/components/viewers/StacMapViewer.svelte +10 -2
  44. package/dist/components/viewers/StacMosaicViewer.svelte +1800 -362
  45. package/dist/components/viewers/StacMosaicViewer.svelte.d.ts +1 -1
  46. package/dist/components/viewers/StacTabViewer.svelte +24 -13
  47. package/dist/components/viewers/StacTabViewer.svelte.d.ts +1 -1
  48. package/dist/components/viewers/TableGrid.svelte +4 -4
  49. package/dist/components/viewers/TableStatusBar.svelte +1 -1
  50. package/dist/components/viewers/TableToolbar.svelte +1 -1
  51. package/dist/components/viewers/TableViewer.svelte +25 -17
  52. package/dist/components/viewers/TableViewer.svelte.d.ts +1 -0
  53. package/dist/components/viewers/ViewerRouter.svelte +16 -8
  54. package/dist/components/viewers/ZarrMapViewer.svelte +11 -9
  55. package/dist/components/viewers/ZarrViewer.svelte +4 -4
  56. package/dist/components/viewers/cog/ChannelPicker.svelte +83 -0
  57. package/dist/components/viewers/cog/ChannelPicker.svelte.d.ts +13 -0
  58. package/dist/components/viewers/cog/PixelInspectorPanel.svelte +87 -0
  59. package/dist/components/viewers/cog/PixelInspectorPanel.svelte.d.ts +17 -0
  60. package/dist/components/viewers/cog/buildRgbLayer.d.ts +78 -0
  61. package/dist/components/viewers/cog/buildRgbLayer.js +176 -0
  62. package/dist/components/viewers/map/AttributeTable.svelte +1 -1
  63. package/dist/components/viewers/map/MapContainer.svelte +37 -11
  64. package/dist/components/viewers/pmtiles/PmtilesArchiveView.svelte +1 -1
  65. package/dist/components/viewers/pmtiles/PmtilesTileInspector.svelte +1 -1
  66. package/dist/components/viewers/stac/StacDatetimeBar.svelte +175 -0
  67. package/dist/components/viewers/stac/StacDatetimeBar.svelte.d.ts +10 -0
  68. package/dist/components/viewers/stac/StacFilterPanel.svelte +243 -0
  69. package/dist/components/viewers/stac/StacFilterPanel.svelte.d.ts +14 -0
  70. package/dist/components/viewers/stac/StacItemInspector.svelte +223 -0
  71. package/dist/components/viewers/stac/StacItemInspector.svelte.d.ts +10 -0
  72. package/dist/components/viewers/stac/StacItemStrip.svelte +228 -0
  73. package/dist/components/viewers/stac/StacItemStrip.svelte.d.ts +12 -0
  74. package/dist/file-icons/index.d.ts +1 -1
  75. package/dist/file-icons/index.js +1 -1
  76. package/dist/i18n/ar.js +110 -2
  77. package/dist/i18n/en.js +110 -2
  78. package/dist/index.d.ts +2 -28
  79. package/dist/index.js +7 -23
  80. package/dist/query/engine.d.ts +10 -0
  81. package/dist/query/source.js +1 -1
  82. package/dist/query/stac-source-factory.d.ts +65 -0
  83. package/dist/query/stac-source-factory.js +77 -0
  84. package/dist/query/stac-source-parquet.d.ts +135 -0
  85. package/dist/query/stac-source-parquet.js +465 -0
  86. package/dist/query/wasm.d.ts +8 -0
  87. package/dist/query/wasm.js +304 -2
  88. package/dist/storage/presign.js +1 -1
  89. package/dist/storage/providers.js +5 -5
  90. package/dist/stores/config.svelte.d.ts +15 -0
  91. package/dist/stores/config.svelte.js +46 -0
  92. package/dist/stores/connections.svelte.d.ts +2 -2
  93. package/dist/stores/connections.svelte.js +1 -2
  94. package/dist/stores/files.svelte.d.ts +1 -1
  95. package/dist/stores/files.svelte.js +1 -1
  96. package/dist/stores/query-history.svelte.js +1 -1
  97. package/dist/stores/settings.svelte.d.ts +16 -1
  98. package/dist/stores/settings.svelte.js +104 -48
  99. package/dist/stores/tabs.svelte.d.ts +3 -0
  100. package/dist/stores/tabs.svelte.js +17 -0
  101. package/dist/utils/cog-histogram.d.ts +121 -0
  102. package/dist/utils/cog-histogram.js +424 -0
  103. package/dist/utils/cog.d.ts +200 -60
  104. package/dist/utils/cog.js +377 -114
  105. package/dist/utils/colormap-sprite.d.ts +0 -9
  106. package/dist/utils/colormap-sprite.js +0 -21
  107. package/dist/utils/deck.d.ts +16 -12
  108. package/dist/utils/deck.js +10 -4
  109. package/dist/utils/pmtiles-tile.js +2 -2
  110. package/dist/utils/{url.d.ts → signed-url.d.ts} +15 -1
  111. package/dist/utils/{url.js → signed-url.js} +32 -10
  112. package/dist/utils/url-state.d.ts +36 -0
  113. package/dist/utils/url-state.js +72 -2
  114. package/dist/utils/zarr-tab.d.ts +1 -2
  115. package/dist/utils/zarr-tab.js +1 -2
  116. package/dist/utils/zarr.d.ts +0 -17
  117. package/dist/utils/zarr.js +1 -45
  118. package/package.json +55 -84
  119. package/dist/components/browser/Breadcrumb.svelte +0 -50
  120. package/dist/components/browser/Breadcrumb.svelte.d.ts +0 -7
  121. package/dist/components/browser/CreateFolderDialog.svelte +0 -98
  122. package/dist/components/browser/CreateFolderDialog.svelte.d.ts +0 -6
  123. package/dist/components/browser/DeleteConfirmDialog.svelte +0 -90
  124. package/dist/components/browser/DeleteConfirmDialog.svelte.d.ts +0 -8
  125. package/dist/components/browser/DropZone.svelte +0 -83
  126. package/dist/components/browser/DropZone.svelte.d.ts +0 -7
  127. package/dist/components/browser/FileBrowser.svelte +0 -252
  128. package/dist/components/browser/FileBrowser.svelte.d.ts +0 -3
  129. package/dist/components/browser/FileRow.svelte +0 -117
  130. package/dist/components/browser/FileRow.svelte.d.ts +0 -9
  131. package/dist/components/browser/RenameDialog.svelte +0 -101
  132. package/dist/components/browser/RenameDialog.svelte.d.ts +0 -8
  133. package/dist/components/browser/SearchBar.svelte +0 -40
  134. package/dist/components/browser/SearchBar.svelte.d.ts +0 -6
  135. package/dist/components/browser/UploadButton.svelte +0 -65
  136. package/dist/components/browser/UploadButton.svelte.d.ts +0 -3
  137. package/dist/query/stac-geoparquet.d.ts +0 -31
  138. package/dist/query/stac-geoparquet.js +0 -136
  139. package/dist/utils/clipboard.d.ts +0 -13
  140. package/dist/utils/clipboard.js +0 -38
  141. package/dist/utils/cloud-url.d.ts +0 -27
  142. package/dist/utils/cloud-url.js +0 -61
  143. package/dist/utils/column-types.d.ts +0 -5
  144. package/dist/utils/column-types.js +0 -137
  145. package/dist/utils/connection-identity.d.ts +0 -51
  146. package/dist/utils/connection-identity.js +0 -97
  147. package/dist/utils/error.d.ts +0 -8
  148. package/dist/utils/error.js +0 -12
  149. package/dist/utils/evidence-context.d.ts +0 -22
  150. package/dist/utils/evidence-context.js +0 -56
  151. package/dist/utils/export.d.ts +0 -22
  152. package/dist/utils/export.js +0 -76
  153. package/dist/utils/file-sort.d.ts +0 -20
  154. package/dist/utils/file-sort.js +0 -41
  155. package/dist/utils/format.d.ts +0 -24
  156. package/dist/utils/format.js +0 -78
  157. package/dist/utils/geoarrow.d.ts +0 -32
  158. package/dist/utils/geoarrow.js +0 -672
  159. package/dist/utils/geometry-type.d.ts +0 -52
  160. package/dist/utils/geometry-type.js +0 -76
  161. package/dist/utils/hex.d.ts +0 -10
  162. package/dist/utils/hex.js +0 -27
  163. package/dist/utils/host-detection.d.ts +0 -23
  164. package/dist/utils/host-detection.js +0 -95
  165. package/dist/utils/local-storage.d.ts +0 -16
  166. package/dist/utils/local-storage.js +0 -37
  167. package/dist/utils/markdown-sql.d.ts +0 -30
  168. package/dist/utils/markdown-sql.js +0 -72
  169. package/dist/utils/notebook.d.ts +0 -59
  170. package/dist/utils/notebook.js +0 -211
  171. package/dist/utils/parquet-metadata.d.ts +0 -64
  172. package/dist/utils/parquet-metadata.js +0 -262
  173. package/dist/utils/stac-geoparquet.d.ts +0 -90
  174. package/dist/utils/stac-geoparquet.js +0 -223
  175. package/dist/utils/stac-hydrate.d.ts +0 -38
  176. package/dist/utils/stac-hydrate.js +0 -243
  177. package/dist/utils/stac.d.ts +0 -136
  178. package/dist/utils/stac.js +0 -176
  179. package/dist/utils/storage-url.d.ts +0 -90
  180. package/dist/utils/storage-url.js +0 -568
  181. package/dist/utils/wkb.d.ts +0 -43
  182. package/dist/utils/wkb.js +0 -359
@@ -1,252 +0,0 @@
1
- <script lang="ts">
2
- import { ArrowDown, ArrowUp, ArrowUpDown, FolderOpen, Layers, Loader2 } from '@lucide/svelte';
3
- import FolderPlusIcon from '@lucide/svelte/icons/folder-plus';
4
- import { Button } from '../ui/button/index.js';
5
- import { ScrollArea } from '../ui/scroll-area/index.js';
6
- import { t } from '../../i18n/index.svelte.js';
7
- import { browser } from '../../stores/browser.svelte.js';
8
- import { safeLock } from '../../stores/safelock.svelte.js';
9
- import { tabs } from '../../stores/tabs.svelte.js';
10
- import type { FileEntry } from '../../types.js';
11
- import {
12
- type SortConfig,
13
- type SortField,
14
- sortFileEntries,
15
- toggleSortField
16
- } from '../../utils/file-sort.js';
17
- import { detectZarrMarkers } from '../../utils/zarr.js';
18
- import { openZarrTab } from '../../utils/zarr-tab.js';
19
- import Breadcrumb from './Breadcrumb.svelte';
20
- import CreateFolderDialog from './CreateFolderDialog.svelte';
21
- import DeleteConfirmDialog from './DeleteConfirmDialog.svelte';
22
- import DropZone from './DropZone.svelte';
23
- import FileRow from './FileRow.svelte';
24
- import RenameDialog from './RenameDialog.svelte';
25
- import SearchBar from './SearchBar.svelte';
26
- import UploadButton from './UploadButton.svelte';
27
-
28
- let filterQuery = $state('');
29
- let sortConfig = $state<SortConfig>({ field: 'name', direction: 'asc' });
30
-
31
- let deleteDialogOpen = $state(false);
32
- let deleteTarget = $state<FileEntry | null>(null);
33
- let createFolderOpen = $state(false);
34
- let renameDialogOpen = $state(false);
35
- let renameTarget = $state<FileEntry | null>(null);
36
-
37
- let showWriteActions = $derived(browser.canWrite && !safeLock.locked);
38
-
39
- const zarrDetection = $derived(detectZarrMarkers(browser.entries.map((e: FileEntry) => e.name)));
40
-
41
- // Auto-open Zarr viewer when markers are detected in the current directory.
42
- // Uses a Set to prevent re-triggering when navigating back to a previously opened store.
43
- const autoOpenedPrefixes = new Set<string>();
44
- $effect(() => {
45
- if (zarrDetection.detected && browser.activeConnection) {
46
- const prefix = browser.currentPrefix;
47
- if (!autoOpenedPrefixes.has(prefix)) {
48
- autoOpenedPrefixes.add(prefix);
49
- openZarrTab(prefix, {
50
- source: 'remote',
51
- connectionId: browser.activeConnection.id,
52
- bucketFallback: browser.activeConnection.bucket
53
- });
54
- }
55
- }
56
- });
57
-
58
- const sortedAndFilteredEntries = $derived.by(() => {
59
- let result = browser.entries;
60
-
61
- // Filter
62
- if (filterQuery) {
63
- const q = filterQuery.toLowerCase();
64
- result = result.filter((entry: FileEntry) => entry.name.toLowerCase().includes(q));
65
- }
66
-
67
- return sortFileEntries(result, sortConfig);
68
- });
69
-
70
- function handleFilter(query: string) {
71
- filterQuery = query;
72
- }
73
-
74
- function handleNavigate(path: string) {
75
- browser.navigateTo(path);
76
- }
77
-
78
- function handleSort(field: SortField) {
79
- sortConfig = toggleSortField(sortConfig, field);
80
- }
81
-
82
- function handleDelete(entry: FileEntry) {
83
- deleteTarget = entry;
84
- deleteDialogOpen = true;
85
- }
86
-
87
- function handleRename(entry: FileEntry) {
88
- renameTarget = entry;
89
- renameDialogOpen = true;
90
- }
91
- </script>
92
-
93
- <div class="flex h-full w-full flex-col">
94
- <!-- Header: connection name + breadcrumb + search + actions -->
95
- <div class="border-border flex flex-col gap-2 border-b px-3 py-2">
96
- {#if browser.activeConnection}
97
- <div class="flex items-center gap-2">
98
- <span class="text-xs font-medium text-muted-foreground">
99
- {browser.activeConnection.name}
100
- </span>
101
- <span class="text-muted-foreground/50 text-xs">/</span>
102
- <span class="text-xs text-muted-foreground">
103
- {browser.activeConnection.bucket}
104
- </span>
105
- </div>
106
- {/if}
107
- <Breadcrumb path={browser.currentPrefix} onNavigate={handleNavigate} />
108
- <div class="flex items-center gap-2">
109
- <div class="flex-1">
110
- <SearchBar onFilter={handleFilter} />
111
- </div>
112
- {#if showWriteActions}
113
- <UploadButton />
114
- <Button
115
- variant="outline"
116
- size="sm"
117
- class="h-7 gap-1.5 px-2 text-xs"
118
- onclick={() => { createFolderOpen = true; }}
119
- >
120
- <FolderPlusIcon class="size-3.5" />
121
- {t('fileBrowser.newFolder')}
122
- </Button>
123
- {/if}
124
- </div>
125
- </div>
126
-
127
- <!-- Column headers -->
128
- <div class="border-border bg-muted/30 flex items-center gap-3 border-b px-3 py-1.5 text-xs">
129
- <div class="w-4 shrink-0"></div>
130
- <button
131
- class="text-muted-foreground hover:text-foreground flex min-w-0 flex-1 items-center gap-1 transition-colors"
132
- onclick={() => handleSort('name')}
133
- >
134
- {t('fileBrowser.name')}
135
- {#if sortConfig.field === 'name'}
136
- {#if sortConfig.direction === 'asc'}
137
- <ArrowUp class="size-3" />
138
- {:else}
139
- <ArrowDown class="size-3" />
140
- {/if}
141
- {:else}
142
- <ArrowUpDown class="size-3 opacity-0 group-hover:opacity-100" />
143
- {/if}
144
- </button>
145
- <button
146
- class="text-muted-foreground hover:text-foreground flex w-20 shrink-0 items-center justify-end gap-1 transition-colors"
147
- onclick={() => handleSort('size')}
148
- >
149
- {#if sortConfig.field === 'size'}
150
- {#if sortConfig.direction === 'asc'}
151
- <ArrowUp class="size-3" />
152
- {:else}
153
- <ArrowDown class="size-3" />
154
- {/if}
155
- {/if}
156
- {t('fileBrowser.size')}
157
- </button>
158
- <button
159
- class="text-muted-foreground hover:text-foreground flex w-24 shrink-0 items-center justify-end gap-1 transition-colors"
160
- onclick={() => handleSort('modified')}
161
- >
162
- {#if sortConfig.field === 'modified'}
163
- {#if sortConfig.direction === 'asc'}
164
- <ArrowUp class="size-3" />
165
- {:else}
166
- <ArrowDown class="size-3" />
167
- {/if}
168
- {/if}
169
- {t('fileBrowser.modified')}
170
- </button>
171
- </div>
172
-
173
- <!-- Zarr detection banner -->
174
- {#if zarrDetection.detected}
175
- <div class="border-border flex items-center gap-2 border-b bg-purple-50 px-3 py-1.5 dark:bg-purple-950/30">
176
- <Layers class="size-3.5 shrink-0 text-purple-500" />
177
- <span class="flex-1 text-xs text-purple-700 dark:text-purple-300">
178
- {t('fileBrowser.zarrDetected', { version: String(zarrDetection.version ?? '?') })}
179
- </span>
180
- <Button
181
- variant="outline"
182
- size="sm"
183
- class="h-6 gap-1 px-2 text-[11px]"
184
- onclick={() => {
185
- if (!browser.activeConnection) return;
186
- openZarrTab(browser.currentPrefix, {
187
- source: 'remote',
188
- connectionId: browser.activeConnection.id,
189
- bucketFallback: browser.activeConnection.bucket
190
- });
191
- }}
192
- >
193
- {t('fileBrowser.openAsZarr')}
194
- </Button>
195
- </div>
196
- {/if}
197
-
198
- <!-- File list -->
199
- <div class="relative min-h-0 flex-1">
200
- <DropZone>
201
- {#if browser.loading}
202
- <div class="flex h-full items-center justify-center">
203
- <Loader2 class="text-muted-foreground size-6 animate-spin" />
204
- </div>
205
- {:else if browser.error}
206
- <div class="flex h-full items-center justify-center px-4">
207
- <p class="text-destructive text-sm">{browser.error}</p>
208
- </div>
209
- {:else if sortedAndFilteredEntries.length === 0}
210
- <div class="flex h-full flex-col items-center justify-center gap-2 px-4">
211
- <FolderOpen class="text-muted-foreground/50 size-10" />
212
- {#if filterQuery}
213
- <p class="text-muted-foreground text-sm">{t('fileBrowser.noMatch', { query: filterQuery })}</p>
214
- {:else}
215
- <p class="text-muted-foreground text-sm">{t('fileBrowser.empty')}</p>
216
- {/if}
217
- </div>
218
- {:else}
219
- <ScrollArea class="h-full">
220
- <div role="table" aria-label="File list">
221
- {#each sortedAndFilteredEntries as entry (entry.path)}
222
- <FileRow
223
- {entry}
224
- onDelete={handleDelete}
225
- onRename={handleRename}
226
- />
227
- {/each}
228
- </div>
229
- </ScrollArea>
230
- {/if}
231
- </DropZone>
232
- </div>
233
-
234
- <!-- Upload progress bar -->
235
- {#if browser.uploading}
236
- <div class="border-border border-t px-3 py-1.5">
237
- <div class="flex items-center gap-2 text-xs text-muted-foreground">
238
- <div class="h-1.5 flex-1 overflow-hidden rounded-full bg-muted">
239
- <div
240
- class="h-full rounded-full bg-primary transition-all"
241
- style="width: {browser.uploadProgress.total > 0 ? (browser.uploadProgress.current / browser.uploadProgress.total) * 100 : 0}%"
242
- ></div>
243
- </div>
244
- <span>{browser.uploadProgress.current}/{browser.uploadProgress.total}</span>
245
- </div>
246
- </div>
247
- {/if}
248
- </div>
249
-
250
- <DeleteConfirmDialog bind:open={deleteDialogOpen} entry={deleteTarget} />
251
- <CreateFolderDialog bind:open={createFolderOpen} />
252
- <RenameDialog bind:open={renameDialogOpen} entry={renameTarget} />
@@ -1,3 +0,0 @@
1
- declare const FileBrowser: import("svelte").Component<Record<string, never>, {}, "">;
2
- type FileBrowser = ReturnType<typeof FileBrowser>;
3
- export default FileBrowser;
@@ -1,117 +0,0 @@
1
- <script lang="ts">
2
- import PencilIcon from '@lucide/svelte/icons/pencil';
3
- import Trash2Icon from '@lucide/svelte/icons/trash-2';
4
- import { VIEWER_DIR_EXTENSIONS } from '../../constants.js';
5
- import FileTypeIcon from '../../file-icons/FileTypeIcon.svelte';
6
- import { getFileTypeInfo } from '../../file-icons/index.js';
7
- import { t } from '../../i18n/index.svelte.js';
8
- import { browser } from '../../stores/browser.svelte.js';
9
- import { safeLock } from '../../stores/safelock.svelte.js';
10
- import { tabs } from '../../stores/tabs.svelte.js';
11
- import type { FileEntry } from '../../types.js';
12
- import { formatDate, formatFileSize } from '../../utils/format.js';
13
-
14
- interface Props {
15
- entry: FileEntry;
16
- onDelete?: (entry: FileEntry) => void;
17
- onRename?: (entry: FileEntry) => void;
18
- }
19
-
20
- let { entry, onDelete, onRename }: Props = $props();
21
-
22
- function isViewerDir(e: FileEntry): boolean {
23
- return e.is_dir && VIEWER_DIR_EXTENSIONS.has(e.extension);
24
- }
25
-
26
- const info = $derived(getFileTypeInfo(entry.extension, entry.is_dir && !isViewerDir(entry)));
27
- let showActions = $derived(browser.canWrite && !safeLock.locked);
28
-
29
- function handleClick() {
30
- if (entry.is_dir && !isViewerDir(entry)) {
31
- browser.navigateTo(entry.path);
32
- } else {
33
- if (browser.activeConnection) {
34
- tabs.open({
35
- id: `${browser.activeConnection.id}:${entry.path}`,
36
- name: entry.name,
37
- path: entry.path,
38
- source: 'remote',
39
- connectionId: browser.activeConnection.id,
40
- extension: entry.extension,
41
- size: entry.size
42
- });
43
- }
44
- }
45
- }
46
-
47
- function handleKeydown(e: KeyboardEvent) {
48
- if (e.key === 'Enter' || e.key === ' ') {
49
- e.preventDefault();
50
- handleClick();
51
- }
52
- }
53
-
54
- function handleDeleteClick(e: MouseEvent) {
55
- e.stopPropagation();
56
- onDelete?.(entry);
57
- }
58
-
59
- function handleRenameClick(e: MouseEvent) {
60
- e.stopPropagation();
61
- onRename?.(entry);
62
- }
63
- </script>
64
-
65
- <!-- svelte-ignore a11y_no_static_element_interactions -->
66
- <div
67
- class="hover:bg-accent/50 border-border/40 group flex w-full cursor-pointer items-center gap-3 border-b px-3 py-2 text-start transition-colors last:border-b-0"
68
- onclick={handleClick}
69
- onkeydown={handleKeydown}
70
- role="row"
71
- tabindex="0"
72
- title={info.label}
73
- >
74
- <!-- Icon -->
75
- <div class="flex shrink-0 items-center justify-center">
76
- <FileTypeIcon extension={entry.extension} isDir={entry.is_dir && !isViewerDir(entry)} class="size-4" />
77
- </div>
78
-
79
- <!-- File name -->
80
- <span class="text-foreground min-w-0 flex-1 truncate text-sm">
81
- {entry.name}
82
- </span>
83
-
84
- <!-- Action buttons (hover-reveal) -->
85
- {#if showActions}
86
- <div class="flex shrink-0 items-center gap-0.5 opacity-0 transition-opacity group-hover:opacity-100">
87
- <button
88
- class="inline-flex size-6 items-center justify-center rounded text-muted-foreground hover:bg-accent hover:text-foreground"
89
- aria-label={t('fileRow.rename', { name: entry.name })}
90
- onclick={handleRenameClick}
91
- >
92
- <PencilIcon class="size-3" />
93
- </button>
94
- <button
95
- class="inline-flex size-6 items-center justify-center rounded text-muted-foreground hover:bg-destructive/10 hover:text-destructive"
96
- aria-label={t('fileRow.delete', { name: entry.name })}
97
- onclick={handleDeleteClick}
98
- >
99
- <Trash2Icon class="size-3" />
100
- </button>
101
- </div>
102
- {/if}
103
-
104
- <!-- File size -->
105
- <span class="text-muted-foreground w-20 shrink-0 text-end text-xs">
106
- {#if entry.is_dir}
107
- --
108
- {:else}
109
- {formatFileSize(entry.size)}
110
- {/if}
111
- </span>
112
-
113
- <!-- Modified date -->
114
- <span class="text-muted-foreground w-24 shrink-0 text-end text-xs">
115
- {formatDate(entry.modified)}
116
- </span>
117
- </div>
@@ -1,9 +0,0 @@
1
- import type { FileEntry } from '../../types.js';
2
- interface Props {
3
- entry: FileEntry;
4
- onDelete?: (entry: FileEntry) => void;
5
- onRename?: (entry: FileEntry) => void;
6
- }
7
- declare const FileRow: import("svelte").Component<Props, {}, "">;
8
- type FileRow = ReturnType<typeof FileRow>;
9
- export default FileRow;
@@ -1,101 +0,0 @@
1
- <script lang="ts">
2
- import Loader2Icon from '@lucide/svelte/icons/loader-2';
3
- import PencilIcon from '@lucide/svelte/icons/pencil';
4
- import { Button } from '../ui/button/index.js';
5
- import { Input } from '../ui/input/index.js';
6
- import {
7
- Sheet,
8
- SheetContent,
9
- SheetDescription,
10
- SheetFooter,
11
- SheetHeader,
12
- SheetTitle
13
- } from '../ui/sheet/index.js';
14
- import { t } from '../../i18n/index.svelte.js';
15
- import { browser } from '../../stores/browser.svelte.js';
16
- import type { FileEntry } from '../../types.js';
17
-
18
- interface Props {
19
- open: boolean;
20
- entry: FileEntry | null;
21
- }
22
-
23
- let { open = $bindable(false), entry }: Props = $props();
24
-
25
- let newName = $state('');
26
- let renaming = $state(false);
27
- let error = $state<string | null>(null);
28
-
29
- let canRename = $derived(
30
- newName.trim() !== '' && !newName.includes('/') && newName.trim() !== entry?.name
31
- );
32
-
33
- $effect(() => {
34
- if (open && entry) {
35
- newName = entry.name;
36
- renaming = false;
37
- error = null;
38
- }
39
- });
40
-
41
- async function handleRename() {
42
- if (!canRename || !entry) return;
43
- renaming = true;
44
- error = null;
45
-
46
- try {
47
- await browser.renameEntry(entry, newName.trim());
48
- open = false;
49
- } catch (e) {
50
- error = e instanceof Error ? e.message : String(e);
51
- } finally {
52
- renaming = false;
53
- }
54
- }
55
-
56
- function handleKeydown(e: KeyboardEvent) {
57
- if (e.key === 'Enter' && canRename) {
58
- handleRename();
59
- }
60
- }
61
- </script>
62
-
63
- <Sheet bind:open>
64
- <SheetContent side="bottom" class="sm:max-w-lg sm:mx-auto sm:rounded-t-lg">
65
- <SheetHeader>
66
- <div class="flex items-center gap-2">
67
- <PencilIcon class="size-5 text-primary" />
68
- <SheetTitle>{t('rename.title')}</SheetTitle>
69
- </div>
70
- <SheetDescription>
71
- Enter a new name for <span class="font-medium">{entry?.name}</span>.
72
- </SheetDescription>
73
- </SheetHeader>
74
-
75
- <div class="py-4">
76
- <Input
77
- placeholder={t('rename.placeholder')}
78
- bind:value={newName}
79
- onkeydown={handleKeydown}
80
- />
81
- {#if error}
82
- <p class="mt-2 text-sm text-destructive">{error}</p>
83
- {/if}
84
- </div>
85
-
86
- <SheetFooter class="flex-row gap-2">
87
- <div class="flex-1"></div>
88
- <Button variant="ghost" size="sm" onclick={() => { open = false; }} disabled={renaming}>
89
- {t('rename.cancel')}
90
- </Button>
91
- <Button size="sm" disabled={!canRename || renaming} onclick={handleRename}>
92
- {#if renaming}
93
- <Loader2Icon class="me-1.5 size-4 animate-spin" />
94
- {t('rename.renaming')}
95
- {:else}
96
- {t('rename.rename')}
97
- {/if}
98
- </Button>
99
- </SheetFooter>
100
- </SheetContent>
101
- </Sheet>
@@ -1,8 +0,0 @@
1
- import type { FileEntry } from '../../types.js';
2
- interface Props {
3
- open: boolean;
4
- entry: FileEntry | null;
5
- }
6
- declare const RenameDialog: import("svelte").Component<Props, {}, "open">;
7
- type RenameDialog = ReturnType<typeof RenameDialog>;
8
- export default RenameDialog;
@@ -1,40 +0,0 @@
1
- <script lang="ts">
2
- import { Search, X } from '@lucide/svelte';
3
- import { Input } from '../ui/input/index.js';
4
- import { t } from '../../i18n/index.svelte.js';
5
-
6
- let { onFilter }: { onFilter: (query: string) => void } = $props();
7
-
8
- let query = $state('');
9
-
10
- function handleInput() {
11
- onFilter(query);
12
- }
13
-
14
- function clearSearch() {
15
- query = '';
16
- onFilter('');
17
- }
18
- </script>
19
-
20
- <div class="relative flex items-center">
21
- <label for="file-filter" class="sr-only">{t('searchBar.label')}</label>
22
- <Search class="text-muted-foreground pointer-events-none absolute start-2.5 size-4" />
23
- <Input
24
- id="file-filter"
25
- type="text"
26
- placeholder={t('searchBar.placeholder')}
27
- class="h-8 ps-8 pe-8 text-sm"
28
- bind:value={query}
29
- oninput={handleInput}
30
- />
31
- {#if query.length > 0}
32
- <button
33
- class="text-muted-foreground hover:text-foreground absolute end-2.5 flex items-center justify-center"
34
- onclick={clearSearch}
35
- aria-label={t('searchBar.clear')}
36
- >
37
- <X class="size-3.5" />
38
- </button>
39
- {/if}
40
- </div>
@@ -1,6 +0,0 @@
1
- type $$ComponentProps = {
2
- onFilter: (query: string) => void;
3
- };
4
- declare const SearchBar: import("svelte").Component<$$ComponentProps, {}, "">;
5
- type SearchBar = ReturnType<typeof SearchBar>;
6
- export default SearchBar;
@@ -1,65 +0,0 @@
1
- <script lang="ts">
2
- import Loader2Icon from '@lucide/svelte/icons/loader-2';
3
- import UploadIcon from '@lucide/svelte/icons/upload';
4
- import { Button } from '../ui/button/index.js';
5
- import { t } from '../../i18n/index.svelte.js';
6
- import { browser } from '../../stores/browser.svelte.js';
7
-
8
- let fileInput: HTMLInputElement;
9
-
10
- let uploading = $derived(browser.uploading);
11
-
12
- function handleClick() {
13
- fileInput?.click();
14
- }
15
-
16
- async function handleFileSelect(e: Event) {
17
- const input = e.target as HTMLInputElement;
18
- const fileList = input.files;
19
- if (!fileList || fileList.length === 0) return;
20
-
21
- const files: Array<{ name: string; data: Uint8Array; type?: string }> = [];
22
-
23
- for (const file of fileList) {
24
- const buffer = await file.arrayBuffer();
25
- files.push({
26
- name: file.name,
27
- data: new Uint8Array(buffer),
28
- type: file.type || undefined
29
- });
30
- }
31
-
32
- try {
33
- await browser.uploadFiles(files);
34
- } catch (err) {
35
- console.error('Upload failed:', err);
36
- }
37
-
38
- // Reset input so the same file can be re-selected
39
- input.value = '';
40
- }
41
- </script>
42
-
43
- <input
44
- bind:this={fileInput}
45
- type="file"
46
- multiple
47
- class="hidden"
48
- onchange={handleFileSelect}
49
- />
50
-
51
- <Button
52
- variant="outline"
53
- size="sm"
54
- class="h-7 gap-1.5 px-2 text-xs"
55
- disabled={uploading}
56
- onclick={handleClick}
57
- >
58
- {#if uploading}
59
- <Loader2Icon class="size-3.5 animate-spin" />
60
- {t('upload.uploading')}
61
- {:else}
62
- <UploadIcon class="size-3.5" />
63
- {t('upload.upload')}
64
- {/if}
65
- </Button>
@@ -1,3 +0,0 @@
1
- declare const UploadButton: import("svelte").Component<Record<string, never>, {}, "">;
2
- type UploadButton = ReturnType<typeof UploadButton>;
3
- export default UploadButton;
@@ -1,31 +0,0 @@
1
- /**
2
- * Read a stac-geoparquet file through the existing DuckDB-WASM engine and
3
- * materialize a standard STAC FeatureCollection in memory.
4
- *
5
- * Reuses:
6
- * - `getQueryEngine()` + `queryCancellable`/`query` for the single worker
7
- * - `resolveTableSourceAsync(tab)` for presigned `signed-s3` URL handling
8
- * - `stacRowToItem` from `utils/stac-geoparquet.js` for the pure transform
9
- * - `parseWKB` from `utils/wkb.js` for geometry decoding
10
- *
11
- * The returned `FeatureCollection` is the same shape `classifyStac()` returns
12
- * as `{ kind: 'item-collection', fc }`, so downstream viewers
13
- * (`StacMosaicViewer`, `MultiCogViewer`) consume it unchanged.
14
- */
15
- import type { Tab } from '../types.js';
16
- import type { StacFeatureCollection } from '../utils/stac.js';
17
- export interface QueryStacGeoparquetOptions {
18
- signal?: AbortSignal;
19
- /** Hard cap on rows. Matches `hydrateStacItems` default. */
20
- limit?: number;
21
- }
22
- /**
23
- * Query a stac-geoparquet tab and return a STAC FeatureCollection whose
24
- * features are proper STAC Items (assets absolutized, WKB decoded, bbox
25
- * flattened).
26
- *
27
- * @param tab - the tab pointing at the `.parquet` file
28
- * @param connId - connection id used for DuckDB's httpfs S3 config; pass
29
- * an empty string for URL-source tabs (DuckDB will use anonymous httpfs)
30
- */
31
- export declare function queryStacGeoparquetFeatureCollection(tab: Tab, connId: string, opts?: QueryStacGeoparquetOptions): Promise<StacFeatureCollection>;