@walkthru-earth/objex 1.3.1 → 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.
- package/LICENSE +5 -0
- package/README.md +20 -12
- package/dist/components/browser/FileTreeSidebar.svelte +32 -17
- package/dist/components/layout/AboutSheet.svelte +5 -2
- package/dist/components/layout/ConnectionDialog.svelte +1 -1
- package/dist/components/layout/SettingsSheet.svelte +237 -0
- package/dist/components/layout/SettingsSheet.svelte.d.ts +6 -0
- package/dist/components/layout/Sidebar.svelte +73 -6
- package/dist/components/layout/Sidebar.svelte.d.ts +4 -1
- package/dist/components/layout/StatusBar.svelte +1 -1
- package/dist/components/layout/TabBar.svelte +2 -2
- package/dist/components/ui/context-menu/context-menu-radio-group.svelte.d.ts +1 -1
- package/dist/components/ui/dropdown-menu/dropdown-menu-checkbox-group.svelte.d.ts +1 -1
- package/dist/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte.d.ts +1 -1
- package/dist/components/ui/input/input.svelte.d.ts +1 -1
- package/dist/components/ui/resizable/index.d.ts +1 -1
- package/dist/components/ui/resizable/index.js +2 -2
- package/dist/components/ui/slider/index.d.ts +3 -0
- package/dist/components/ui/slider/index.js +5 -0
- package/dist/components/ui/slider/range-slider.svelte +94 -0
- package/dist/components/ui/slider/range-slider.svelte.d.ts +21 -0
- package/dist/components/ui/slider/slider.svelte +83 -0
- package/dist/components/ui/slider/slider.svelte.d.ts +7 -0
- package/dist/components/viewers/ArchiveViewer.svelte +2 -2
- package/dist/components/viewers/CodeViewer.svelte +31 -22
- package/dist/components/viewers/CogControls.svelte +338 -184
- package/dist/components/viewers/CogControls.svelte.d.ts +33 -10
- package/dist/components/viewers/CogViewer.svelte +263 -112
- package/dist/components/viewers/CopcViewer.svelte +1 -1
- package/dist/components/viewers/FlatGeobufViewer.svelte +1 -1
- package/dist/components/viewers/GeoParquetMapViewer.svelte +6 -6
- package/dist/components/viewers/GeoParquetMapViewer.svelte.d.ts +1 -1
- package/dist/components/viewers/ImageViewer.svelte +2 -2
- package/dist/components/viewers/MarkdownViewer.svelte +12 -9
- package/dist/components/viewers/MediaViewer.svelte +2 -2
- package/dist/components/viewers/ModelViewer.svelte +1 -1
- package/dist/components/viewers/MultiCogViewer.svelte +467 -102
- package/dist/components/viewers/MultiCogViewer.svelte.d.ts +1 -1
- package/dist/components/viewers/NotebookViewer.svelte +6 -3
- package/dist/components/viewers/PdfViewer.svelte +2 -2
- package/dist/components/viewers/PmtilesViewer.svelte +3 -6
- package/dist/components/viewers/RawViewer.svelte +6 -3
- package/dist/components/viewers/StacMapViewer.svelte +1 -1
- package/dist/components/viewers/StacMosaicViewer.svelte +1760 -408
- package/dist/components/viewers/StacMosaicViewer.svelte.d.ts +1 -1
- package/dist/components/viewers/StacTabViewer.svelte +24 -13
- package/dist/components/viewers/StacTabViewer.svelte.d.ts +1 -1
- package/dist/components/viewers/TableGrid.svelte +4 -4
- package/dist/components/viewers/TableStatusBar.svelte +1 -1
- package/dist/components/viewers/TableToolbar.svelte +1 -1
- package/dist/components/viewers/TableViewer.svelte +25 -17
- package/dist/components/viewers/TableViewer.svelte.d.ts +1 -0
- package/dist/components/viewers/ViewerRouter.svelte +16 -8
- package/dist/components/viewers/ZarrMapViewer.svelte +11 -9
- package/dist/components/viewers/ZarrViewer.svelte +4 -4
- package/dist/components/viewers/cog/ChannelPicker.svelte +83 -0
- package/dist/components/viewers/cog/ChannelPicker.svelte.d.ts +13 -0
- package/dist/components/viewers/cog/PixelInspectorPanel.svelte +87 -0
- package/dist/components/viewers/cog/PixelInspectorPanel.svelte.d.ts +17 -0
- package/dist/components/viewers/cog/buildRgbLayer.d.ts +78 -0
- package/dist/components/viewers/cog/buildRgbLayer.js +176 -0
- package/dist/components/viewers/map/AttributeTable.svelte +1 -1
- package/dist/components/viewers/map/MapContainer.svelte +37 -11
- package/dist/components/viewers/pmtiles/PmtilesArchiveView.svelte +1 -1
- package/dist/components/viewers/pmtiles/PmtilesTileInspector.svelte +1 -1
- package/dist/components/viewers/stac/StacDatetimeBar.svelte +175 -0
- package/dist/components/viewers/stac/StacDatetimeBar.svelte.d.ts +10 -0
- package/dist/components/viewers/stac/StacFilterPanel.svelte +243 -0
- package/dist/components/viewers/stac/StacFilterPanel.svelte.d.ts +14 -0
- package/dist/components/viewers/stac/StacItemInspector.svelte +223 -0
- package/dist/components/viewers/stac/StacItemInspector.svelte.d.ts +10 -0
- package/dist/components/viewers/stac/StacItemStrip.svelte +228 -0
- package/dist/components/viewers/stac/StacItemStrip.svelte.d.ts +12 -0
- package/dist/file-icons/index.d.ts +1 -1
- package/dist/file-icons/index.js +1 -1
- package/dist/i18n/ar.js +110 -2
- package/dist/i18n/en.js +110 -2
- package/dist/index.d.ts +2 -28
- package/dist/index.js +7 -23
- package/dist/query/engine.d.ts +10 -0
- package/dist/query/source.js +1 -1
- package/dist/query/stac-source-factory.d.ts +65 -0
- package/dist/query/stac-source-factory.js +77 -0
- package/dist/query/stac-source-parquet.d.ts +135 -0
- package/dist/query/stac-source-parquet.js +465 -0
- package/dist/query/wasm.d.ts +8 -0
- package/dist/query/wasm.js +304 -2
- package/dist/storage/presign.js +1 -1
- package/dist/storage/providers.js +5 -5
- package/dist/stores/config.svelte.d.ts +15 -0
- package/dist/stores/config.svelte.js +46 -0
- package/dist/stores/connections.svelte.d.ts +2 -2
- package/dist/stores/connections.svelte.js +1 -2
- package/dist/stores/files.svelte.d.ts +1 -1
- package/dist/stores/files.svelte.js +1 -1
- package/dist/stores/query-history.svelte.js +1 -1
- package/dist/stores/settings.svelte.d.ts +16 -1
- package/dist/stores/settings.svelte.js +104 -48
- package/dist/stores/tabs.svelte.d.ts +3 -0
- package/dist/stores/tabs.svelte.js +17 -0
- package/dist/utils/cog-histogram.d.ts +121 -0
- package/dist/utils/cog-histogram.js +424 -0
- package/dist/utils/cog.d.ts +177 -20
- package/dist/utils/cog.js +361 -76
- package/dist/utils/colormap-sprite.d.ts +0 -9
- package/dist/utils/colormap-sprite.js +0 -21
- package/dist/utils/deck.d.ts +16 -12
- package/dist/utils/deck.js +10 -4
- package/dist/utils/pmtiles-tile.js +2 -2
- package/dist/utils/{url.d.ts → signed-url.d.ts} +15 -1
- package/dist/utils/{url.js → signed-url.js} +32 -10
- package/dist/utils/url-state.d.ts +36 -0
- package/dist/utils/url-state.js +72 -2
- package/dist/utils/zarr-tab.d.ts +1 -2
- package/dist/utils/zarr-tab.js +1 -2
- package/dist/utils/zarr.d.ts +0 -17
- package/dist/utils/zarr.js +1 -45
- package/package.json +55 -84
- package/dist/components/browser/Breadcrumb.svelte +0 -50
- package/dist/components/browser/Breadcrumb.svelte.d.ts +0 -7
- package/dist/components/browser/CreateFolderDialog.svelte +0 -98
- package/dist/components/browser/CreateFolderDialog.svelte.d.ts +0 -6
- package/dist/components/browser/DeleteConfirmDialog.svelte +0 -90
- package/dist/components/browser/DeleteConfirmDialog.svelte.d.ts +0 -8
- package/dist/components/browser/DropZone.svelte +0 -83
- package/dist/components/browser/DropZone.svelte.d.ts +0 -7
- package/dist/components/browser/FileBrowser.svelte +0 -252
- package/dist/components/browser/FileBrowser.svelte.d.ts +0 -3
- package/dist/components/browser/FileRow.svelte +0 -117
- package/dist/components/browser/FileRow.svelte.d.ts +0 -9
- package/dist/components/browser/RenameDialog.svelte +0 -101
- package/dist/components/browser/RenameDialog.svelte.d.ts +0 -8
- package/dist/components/browser/SearchBar.svelte +0 -40
- package/dist/components/browser/SearchBar.svelte.d.ts +0 -6
- package/dist/components/browser/UploadButton.svelte +0 -65
- package/dist/components/browser/UploadButton.svelte.d.ts +0 -3
- package/dist/query/stac-geoparquet.d.ts +0 -31
- package/dist/query/stac-geoparquet.js +0 -136
- package/dist/utils/clipboard.d.ts +0 -13
- package/dist/utils/clipboard.js +0 -38
- package/dist/utils/cloud-url.d.ts +0 -27
- package/dist/utils/cloud-url.js +0 -61
- package/dist/utils/cog-pure.d.ts +0 -25
- package/dist/utils/cog-pure.js +0 -35
- package/dist/utils/column-types.d.ts +0 -5
- package/dist/utils/column-types.js +0 -137
- package/dist/utils/connection-identity.d.ts +0 -51
- package/dist/utils/connection-identity.js +0 -97
- package/dist/utils/error.d.ts +0 -8
- package/dist/utils/error.js +0 -12
- package/dist/utils/evidence-context.d.ts +0 -22
- package/dist/utils/evidence-context.js +0 -56
- package/dist/utils/export.d.ts +0 -22
- package/dist/utils/export.js +0 -76
- package/dist/utils/file-sort.d.ts +0 -20
- package/dist/utils/file-sort.js +0 -41
- package/dist/utils/format.d.ts +0 -24
- package/dist/utils/format.js +0 -78
- package/dist/utils/geoarrow.d.ts +0 -32
- package/dist/utils/geoarrow.js +0 -672
- package/dist/utils/geometry-type.d.ts +0 -52
- package/dist/utils/geometry-type.js +0 -76
- package/dist/utils/hex.d.ts +0 -10
- package/dist/utils/hex.js +0 -27
- package/dist/utils/host-detection.d.ts +0 -23
- package/dist/utils/host-detection.js +0 -95
- package/dist/utils/local-storage.d.ts +0 -16
- package/dist/utils/local-storage.js +0 -37
- package/dist/utils/markdown-sql.d.ts +0 -30
- package/dist/utils/markdown-sql.js +0 -72
- package/dist/utils/notebook.d.ts +0 -59
- package/dist/utils/notebook.js +0 -211
- package/dist/utils/parquet-metadata.d.ts +0 -64
- package/dist/utils/parquet-metadata.js +0 -262
- package/dist/utils/stac-geoparquet.d.ts +0 -90
- package/dist/utils/stac-geoparquet.js +0 -223
- package/dist/utils/stac-hydrate.d.ts +0 -38
- package/dist/utils/stac-hydrate.js +0 -243
- package/dist/utils/stac.d.ts +0 -136
- package/dist/utils/stac.js +0 -176
- package/dist/utils/storage-url.d.ts +0 -90
- package/dist/utils/storage-url.js +0 -568
- package/dist/utils/wkb.d.ts +0 -43
- package/dist/utils/wkb.js +0 -359
|
@@ -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,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,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>;
|
|
@@ -1,136 +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 { stacRowToItem } from '../utils/stac-geoparquet.js';
|
|
16
|
-
import { parseWKB } from '../utils/wkb.js';
|
|
17
|
-
import { QueryCancelledError } from './engine.js';
|
|
18
|
-
import { getQueryEngine } from './index.js';
|
|
19
|
-
import { resolveTableSourceAsync } from './source.js';
|
|
20
|
-
const DEFAULT_LIMIT = 2000;
|
|
21
|
-
/**
|
|
22
|
-
* Build the SELECT list. All columns are optional in the stac-geoparquet
|
|
23
|
-
* spec, so we only project what we know we'll use and the spec requires.
|
|
24
|
-
* The optional `proj:*` / `raster:*` / `bands` columns are sniffed from the
|
|
25
|
-
* schema so missing columns don't trigger a DuckDB binder error.
|
|
26
|
-
*/
|
|
27
|
-
function buildSelectList(availableColumns) {
|
|
28
|
-
const required = [
|
|
29
|
-
'id',
|
|
30
|
-
'collection',
|
|
31
|
-
'type',
|
|
32
|
-
'stac_version',
|
|
33
|
-
'stac_extensions',
|
|
34
|
-
'assets',
|
|
35
|
-
'bbox',
|
|
36
|
-
'links'
|
|
37
|
-
];
|
|
38
|
-
const optional = [
|
|
39
|
-
'datetime',
|
|
40
|
-
'proj:code',
|
|
41
|
-
'proj:bbox',
|
|
42
|
-
'proj:transform',
|
|
43
|
-
'proj:shape',
|
|
44
|
-
'raster:spatial_resolution',
|
|
45
|
-
'bands'
|
|
46
|
-
];
|
|
47
|
-
const cols = [];
|
|
48
|
-
for (const name of required) {
|
|
49
|
-
if (availableColumns.has(name))
|
|
50
|
-
cols.push(quoteIdent(name));
|
|
51
|
-
}
|
|
52
|
-
for (const name of optional) {
|
|
53
|
-
if (availableColumns.has(name))
|
|
54
|
-
cols.push(quoteIdent(name));
|
|
55
|
-
}
|
|
56
|
-
// Always project geometry as WKB so parseWKB can decode it regardless of
|
|
57
|
-
// whether DuckDB presents it as the v1.5 GEOMETRY type or a plain BLOB.
|
|
58
|
-
if (availableColumns.has('geometry')) {
|
|
59
|
-
cols.push('ST_AsWKB(geometry) AS geom_wkb');
|
|
60
|
-
}
|
|
61
|
-
return cols.join(', ');
|
|
62
|
-
}
|
|
63
|
-
function quoteIdent(name) {
|
|
64
|
-
return `"${name.replace(/"/g, '""')}"`;
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Query a stac-geoparquet tab and return a STAC FeatureCollection whose
|
|
68
|
-
* features are proper STAC Items (assets absolutized, WKB decoded, bbox
|
|
69
|
-
* flattened).
|
|
70
|
-
*
|
|
71
|
-
* @param tab - the tab pointing at the `.parquet` file
|
|
72
|
-
* @param connId - connection id used for DuckDB's httpfs S3 config; pass
|
|
73
|
-
* an empty string for URL-source tabs (DuckDB will use anonymous httpfs)
|
|
74
|
-
*/
|
|
75
|
-
export async function queryStacGeoparquetFeatureCollection(tab, connId, opts = {}) {
|
|
76
|
-
const { signal, limit = DEFAULT_LIMIT } = opts;
|
|
77
|
-
if (signal?.aborted)
|
|
78
|
-
throw new QueryCancelledError();
|
|
79
|
-
const engine = await getQueryEngine();
|
|
80
|
-
const resolved = await resolveTableSourceAsync(tab);
|
|
81
|
-
if (signal?.aborted)
|
|
82
|
-
throw new QueryCancelledError();
|
|
83
|
-
// Discover which optional columns are present so the SELECT list doesn't
|
|
84
|
-
// reference missing columns.
|
|
85
|
-
const schema = await engine.getSchema(connId, resolved);
|
|
86
|
-
if (signal?.aborted)
|
|
87
|
-
throw new QueryCancelledError();
|
|
88
|
-
const available = new Set(schema.map((f) => f.name));
|
|
89
|
-
const selectList = buildSelectList(available);
|
|
90
|
-
if (!available.has('geometry') || !available.has('assets')) {
|
|
91
|
-
throw new Error('Not a stac-geoparquet file (missing geometry or assets column)');
|
|
92
|
-
}
|
|
93
|
-
const sql = `SELECT ${selectList} FROM ${resolved.ref} LIMIT ${limit}`;
|
|
94
|
-
// Prefer cancellable path when the engine exposes it.
|
|
95
|
-
let resultPromise;
|
|
96
|
-
let cancel = null;
|
|
97
|
-
if (engine.queryCancellable) {
|
|
98
|
-
const handle = engine.queryCancellable(connId, sql);
|
|
99
|
-
cancel = handle.cancel;
|
|
100
|
-
resultPromise = handle.result;
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
resultPromise = engine.query(connId, sql);
|
|
104
|
-
}
|
|
105
|
-
const onAbort = () => {
|
|
106
|
-
cancel?.().catch(() => { });
|
|
107
|
-
};
|
|
108
|
-
signal?.addEventListener('abort', onAbort, { once: true });
|
|
109
|
-
let rows;
|
|
110
|
-
try {
|
|
111
|
-
const result = await resultPromise;
|
|
112
|
-
rows = result.rows ?? [];
|
|
113
|
-
}
|
|
114
|
-
finally {
|
|
115
|
-
signal?.removeEventListener('abort', onAbort);
|
|
116
|
-
}
|
|
117
|
-
if (signal?.aborted)
|
|
118
|
-
throw new QueryCancelledError();
|
|
119
|
-
// Asset hrefs in stac-geoparquet are typically written relative to each
|
|
120
|
-
// item's original `self` URL, not the parquet URL. The stactools default
|
|
121
|
-
// layout places each item JSON at `{catalog_dir}/{item.id}/{item.id}.json`,
|
|
122
|
-
// so a per-row base of `{parquet_dir}/{item.id}/` resolves `./foo.tif` to
|
|
123
|
-
// `{parquet_dir}/{item.id}/foo.tif`. Absolute hrefs pass through unchanged
|
|
124
|
-
// via `resolveStacAssetHref`.
|
|
125
|
-
const parquetUrl = resolved.fileUrl ?? tab.path;
|
|
126
|
-
const parquetDir = parquetUrl.replace(/[^/]*(?:\?.*)?$/, '');
|
|
127
|
-
const features = rows.map((row) => {
|
|
128
|
-
const id = typeof row.id === 'string' ? row.id : String(row.id ?? '');
|
|
129
|
-
const itemBase = id ? `${parquetDir}${id}/` : parquetUrl;
|
|
130
|
-
return stacRowToItem(row, itemBase, { wkbParser: parseWKB });
|
|
131
|
-
});
|
|
132
|
-
return {
|
|
133
|
-
type: 'FeatureCollection',
|
|
134
|
-
features
|
|
135
|
-
};
|
|
136
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copy text to clipboard and run a feedback callback for COPY_FEEDBACK_MS.
|
|
3
|
-
* Silently catches clipboard errors (e.g. insecure context).
|
|
4
|
-
*
|
|
5
|
-
* @returns true if copy succeeded, false otherwise.
|
|
6
|
-
*/
|
|
7
|
-
export declare function copyToClipboard(text: string, onFeedback?: (copied: boolean) => void): Promise<boolean>;
|
|
8
|
-
/**
|
|
9
|
-
* Wire click-to-copy on all elements matching `selector` inside `root`.
|
|
10
|
-
* Each element must have `data-code` (URI-encoded) with the text to copy.
|
|
11
|
-
* Adds/removes a `copied` CSS class for visual feedback.
|
|
12
|
-
*/
|
|
13
|
-
export declare function wireCodeCopyButtons(root: Element, selector: string): void;
|
package/dist/utils/clipboard.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { COPY_FEEDBACK_MS } from '../constants.js';
|
|
2
|
-
/**
|
|
3
|
-
* Copy text to clipboard and run a feedback callback for COPY_FEEDBACK_MS.
|
|
4
|
-
* Silently catches clipboard errors (e.g. insecure context).
|
|
5
|
-
*
|
|
6
|
-
* @returns true if copy succeeded, false otherwise.
|
|
7
|
-
*/
|
|
8
|
-
export async function copyToClipboard(text, onFeedback) {
|
|
9
|
-
try {
|
|
10
|
-
await navigator.clipboard.writeText(text);
|
|
11
|
-
onFeedback?.(true);
|
|
12
|
-
setTimeout(() => onFeedback?.(false), COPY_FEEDBACK_MS);
|
|
13
|
-
return true;
|
|
14
|
-
}
|
|
15
|
-
catch {
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Wire click-to-copy on all elements matching `selector` inside `root`.
|
|
21
|
-
* Each element must have `data-code` (URI-encoded) with the text to copy.
|
|
22
|
-
* Adds/removes a `copied` CSS class for visual feedback.
|
|
23
|
-
*/
|
|
24
|
-
export function wireCodeCopyButtons(root, selector) {
|
|
25
|
-
for (const btn of root.querySelectorAll(selector)) {
|
|
26
|
-
btn.addEventListener('click', async () => {
|
|
27
|
-
const code = decodeURIComponent(btn.dataset.code ?? '');
|
|
28
|
-
try {
|
|
29
|
-
await navigator.clipboard.writeText(code);
|
|
30
|
-
btn.classList.add('copied');
|
|
31
|
-
setTimeout(() => btn.classList.remove('copied'), COPY_FEEDBACK_MS);
|
|
32
|
-
}
|
|
33
|
-
catch {
|
|
34
|
-
// clipboard not available
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cloud storage protocol URL utilities — pure TS, no Svelte dependency.
|
|
3
|
-
*
|
|
4
|
-
* Converts cloud protocol URLs (s3://, gs://) to HTTPS URLs for browser access.
|
|
5
|
-
* Provider-aware native scheme lookup.
|
|
6
|
-
*/
|
|
7
|
-
/**
|
|
8
|
-
* Map provider to its native URI scheme prefix.
|
|
9
|
-
* Derived from the registry's `schemes` array (first entry is the primary scheme).
|
|
10
|
-
* Falls back to 's3' for providers without a scheme (S3-compatible).
|
|
11
|
-
*/
|
|
12
|
-
export declare function getNativeScheme(provider: string): string;
|
|
13
|
-
/**
|
|
14
|
-
* Safely decode a percent-encoded URI component.
|
|
15
|
-
* Returns the original string if decoding fails (malformed sequences).
|
|
16
|
-
*/
|
|
17
|
-
export declare function safeDecodeURIComponent(s: string): string;
|
|
18
|
-
/**
|
|
19
|
-
* Convert a cloud storage protocol URL (s3://, gs://) to an HTTPS URL
|
|
20
|
-
* for browser access. Returns the original URL if already HTTP(S) or unknown.
|
|
21
|
-
*
|
|
22
|
-
* Supported:
|
|
23
|
-
* - `s3://bucket/key` → `https://s3.{region}.amazonaws.com/{bucket}/{key}`
|
|
24
|
-
* (region auto-detected from bucket name when possible)
|
|
25
|
-
* - `gs://bucket/key` → `https://storage.googleapis.com/{bucket}/{key}`
|
|
26
|
-
*/
|
|
27
|
-
export declare function resolveCloudUrl(url: string): string;
|
package/dist/utils/cloud-url.js
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cloud storage protocol URL utilities — pure TS, no Svelte dependency.
|
|
3
|
-
*
|
|
4
|
-
* Converts cloud protocol URLs (s3://, gs://) to HTTPS URLs for browser access.
|
|
5
|
-
* Provider-aware native scheme lookup.
|
|
6
|
-
*/
|
|
7
|
-
import { buildProviderBaseUrl, PROVIDERS } from '../storage/providers.js';
|
|
8
|
-
/** AWS region pattern — matches prefixes like "us-west-2", "eu-central-1", etc. */
|
|
9
|
-
const AWS_REGION_RE = /^(us|eu|ap|sa|ca|me|af|il)-(north|south|east|west|central|northeast|southeast|northwest|southwest)-\d+/;
|
|
10
|
-
/**
|
|
11
|
-
* Map provider to its native URI scheme prefix.
|
|
12
|
-
* Derived from the registry's `schemes` array (first entry is the primary scheme).
|
|
13
|
-
* Falls back to 's3' for providers without a scheme (S3-compatible).
|
|
14
|
-
*/
|
|
15
|
-
export function getNativeScheme(provider) {
|
|
16
|
-
const def = PROVIDERS[provider];
|
|
17
|
-
if (def?.schemes.length)
|
|
18
|
-
return def.schemes[0];
|
|
19
|
-
return 's3';
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Safely decode a percent-encoded URI component.
|
|
23
|
-
* Returns the original string if decoding fails (malformed sequences).
|
|
24
|
-
*/
|
|
25
|
-
export function safeDecodeURIComponent(s) {
|
|
26
|
-
try {
|
|
27
|
-
return decodeURIComponent(s);
|
|
28
|
-
}
|
|
29
|
-
catch {
|
|
30
|
-
return s;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Convert a cloud storage protocol URL (s3://, gs://) to an HTTPS URL
|
|
35
|
-
* for browser access. Returns the original URL if already HTTP(S) or unknown.
|
|
36
|
-
*
|
|
37
|
-
* Supported:
|
|
38
|
-
* - `s3://bucket/key` → `https://s3.{region}.amazonaws.com/{bucket}/{key}`
|
|
39
|
-
* (region auto-detected from bucket name when possible)
|
|
40
|
-
* - `gs://bucket/key` → `https://storage.googleapis.com/{bucket}/{key}`
|
|
41
|
-
*/
|
|
42
|
-
export function resolveCloudUrl(url) {
|
|
43
|
-
// S3 / S3-compatible: s3://, s3a://, s3n://
|
|
44
|
-
const s3Match = url.match(/^s3[an]?:\/\/([^/]+)\/?(.*)$/);
|
|
45
|
-
if (s3Match) {
|
|
46
|
-
const [, bucket, key] = s3Match;
|
|
47
|
-
// Detect region from bucket name (e.g. "us-west-2.opendata.source.coop")
|
|
48
|
-
const regionMatch = bucket.match(AWS_REGION_RE);
|
|
49
|
-
const region = regionMatch ? regionMatch[0] : 'us-east-1';
|
|
50
|
-
const base = buildProviderBaseUrl('s3', '', bucket, region);
|
|
51
|
-
return key ? `${base}/${key}` : base;
|
|
52
|
-
}
|
|
53
|
-
// Google Cloud Storage: gs://, gcs://
|
|
54
|
-
const gcsMatch = url.match(/^gcs?:\/\/([^/]+)\/?(.*)$/);
|
|
55
|
-
if (gcsMatch) {
|
|
56
|
-
const [, bucket, key] = gcsMatch;
|
|
57
|
-
const base = buildProviderBaseUrl('gcs', '', bucket, '');
|
|
58
|
-
return key ? `${base}/${key}` : base;
|
|
59
|
-
}
|
|
60
|
-
return url;
|
|
61
|
-
}
|