@walkthru-earth/objex 0.1.0 → 1.0.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/README.md +1 -1
- package/dist/components/browser/FileBrowser.svelte +37 -1
- package/dist/components/browser/FileRow.svelte +8 -3
- package/dist/components/browser/FileTreeSidebar.svelte +1 -3
- package/dist/components/layout/AboutSheet.svelte +126 -0
- package/dist/components/layout/AboutSheet.svelte.d.ts +6 -0
- package/dist/components/layout/ConnectionDialog.svelte +186 -138
- package/dist/components/layout/ConnectionDialog.svelte.d.ts +1 -0
- package/dist/components/layout/Sidebar.svelte +19 -3
- package/dist/components/layout/TabBar.svelte +4 -7
- package/dist/components/viewers/CodeViewer.svelte +17 -9
- package/dist/components/viewers/ImageViewer.svelte +6 -16
- package/dist/components/viewers/MarkdownViewer.svelte +8 -16
- package/dist/components/viewers/MediaViewer.svelte +6 -17
- package/dist/components/viewers/ModelViewer.svelte +4 -2
- package/dist/components/viewers/NotebookViewer.svelte +90 -40
- package/dist/components/viewers/PdfViewer.svelte +5 -3
- package/dist/components/viewers/RawViewer.svelte +4 -2
- package/dist/components/viewers/TableGrid.svelte +3 -2
- package/dist/components/viewers/ZarrMapViewer.svelte +334 -40
- package/dist/components/viewers/ZarrMapViewer.svelte.d.ts +3 -8
- package/dist/components/viewers/ZarrViewer.svelte +459 -178
- package/dist/components/viewers/map/AttributeTable.svelte +1 -6
- package/dist/components/viewers/pmtiles/PmtilesArchiveView.svelte +2 -6
- package/dist/components/viewers/pmtiles/PmtilesTileInspector.svelte +96 -22
- package/dist/constants.d.ts +28 -0
- package/dist/constants.js +34 -0
- package/dist/file-icons/index.js +6 -0
- package/dist/i18n/ar.js +34 -0
- package/dist/i18n/en.js +34 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +7 -1
- package/dist/query/wasm.js +5 -4
- package/dist/storage/browser-cloud.d.ts +7 -0
- package/dist/storage/browser-cloud.js +74 -7
- package/dist/storage/providers.d.ts +53 -0
- package/dist/storage/providers.js +318 -0
- package/dist/stores/connections.svelte.js +5 -5
- package/dist/stores/query-history.svelte.js +4 -5
- package/dist/stores/settings.svelte.js +4 -4
- package/dist/types.d.ts +2 -2
- package/dist/utils/clipboard.d.ts +13 -0
- package/dist/utils/clipboard.js +38 -0
- package/dist/utils/error.d.ts +8 -0
- package/dist/utils/error.js +12 -0
- package/dist/utils/format.d.ts +10 -0
- package/dist/utils/format.js +22 -0
- package/dist/utils/host-detection.js +78 -18
- package/dist/utils/notebook.d.ts +59 -0
- package/dist/utils/notebook.js +211 -0
- package/dist/utils/parquet-metadata.js +1 -1
- package/dist/utils/pmtiles-tile.js +2 -1
- package/dist/utils/pmtiles.js +2 -1
- package/dist/utils/storage-url.d.ts +1 -1
- package/dist/utils/storage-url.js +82 -24
- package/dist/utils/url-state.js +2 -7
- package/dist/utils/url.d.ts +15 -1
- package/dist/utils/url.js +45 -19
- package/dist/utils/zarr.d.ts +60 -20
- package/dist/utils/zarr.js +450 -103
- package/package.json +64 -52
- package/dist/assets/favicon.svg +0 -17
- package/dist/components/CLAUDE.md +0 -44
- package/dist/components/viewers/CLAUDE.md +0 -60
- package/dist/file-icons/CLAUDE.md +0 -21
- package/dist/i18n/CLAUDE.md +0 -19
- package/dist/query/CLAUDE.md +0 -22
- package/dist/storage/CLAUDE.md +0 -23
- package/dist/stores/CLAUDE.md +0 -29
- package/dist/types/notebookjs.d.ts +0 -14
- package/dist/utils/CLAUDE.md +0 -54
- package/dist/utils/analytics.d.ts +0 -10
- package/dist/utils/analytics.js +0 -38
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ graph LR
|
|
|
13
13
|
|
|
14
14
|
## Features
|
|
15
15
|
|
|
16
|
-
- **Browse** cloud storage (S3, GCS, Azure, R2,
|
|
16
|
+
- **Browse** cloud storage (S3, GCS, Azure, R2, B2, DigitalOcean, Wasabi, Storj, Hetzner, Contabo, Linode, OVHcloud, MinIO, direct URLs)
|
|
17
17
|
- **Query** Parquet, CSV, JSONL with SQL (DuckDB-WASM, cancellable queries)
|
|
18
18
|
- **Visualize** GeoParquet, GeoJSON, COG, PMTiles, FlatGeobuf, Zarr on maps (MapLibre + deck.gl)
|
|
19
19
|
- **View** 100+ file formats: code (30+ languages), Jupyter notebooks, PDF, 3D models, archives, media
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { ArrowDown, ArrowUp, ArrowUpDown, FolderOpen, Loader2 } from '@lucide/svelte';
|
|
2
|
+
import { ArrowDown, ArrowUp, ArrowUpDown, FolderOpen, Layers, Loader2 } from '@lucide/svelte';
|
|
3
3
|
import FolderPlusIcon from '@lucide/svelte/icons/folder-plus';
|
|
4
4
|
import { Button } from '../ui/button/index.js';
|
|
5
5
|
import { ScrollArea } from '../ui/scroll-area/index.js';
|
|
6
6
|
import { t } from '../../i18n/index.svelte.js';
|
|
7
7
|
import { browser } from '../../stores/browser.svelte.js';
|
|
8
8
|
import { safeLock } from '../../stores/safelock.svelte.js';
|
|
9
|
+
import { tabs } from '../../stores/tabs.svelte.js';
|
|
9
10
|
import type { FileEntry } from '../../types.js';
|
|
11
|
+
import { detectZarrMarkers } from '../../utils/zarr.js';
|
|
10
12
|
import Breadcrumb from './Breadcrumb.svelte';
|
|
11
13
|
import CreateFolderDialog from './CreateFolderDialog.svelte';
|
|
12
14
|
import DeleteConfirmDialog from './DeleteConfirmDialog.svelte';
|
|
@@ -31,6 +33,22 @@ let renameTarget = $state<FileEntry | null>(null);
|
|
|
31
33
|
|
|
32
34
|
let showWriteActions = $derived(browser.canWrite && !safeLock.locked);
|
|
33
35
|
|
|
36
|
+
const zarrDetection = $derived(detectZarrMarkers(browser.entries.map((e: FileEntry) => e.name)));
|
|
37
|
+
|
|
38
|
+
function openAsZarr() {
|
|
39
|
+
if (!browser.activeConnection) return;
|
|
40
|
+
const prefix = browser.currentPrefix.replace(/\/+$/, '');
|
|
41
|
+
const name = prefix.split('/').pop() || browser.activeConnection.bucket;
|
|
42
|
+
tabs.open({
|
|
43
|
+
id: `${browser.activeConnection.id}:${prefix}/`,
|
|
44
|
+
name,
|
|
45
|
+
path: `${prefix}/`,
|
|
46
|
+
source: 'remote',
|
|
47
|
+
connectionId: browser.activeConnection.id,
|
|
48
|
+
extension: 'zarr'
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
34
52
|
const sortedAndFilteredEntries = $derived.by(() => {
|
|
35
53
|
let result = [...browser.entries];
|
|
36
54
|
|
|
@@ -172,6 +190,24 @@ function handleRename(entry: FileEntry) {
|
|
|
172
190
|
</button>
|
|
173
191
|
</div>
|
|
174
192
|
|
|
193
|
+
<!-- Zarr detection banner -->
|
|
194
|
+
{#if zarrDetection.detected}
|
|
195
|
+
<div class="border-border flex items-center gap-2 border-b bg-purple-50 px-3 py-1.5 dark:bg-purple-950/30">
|
|
196
|
+
<Layers class="size-3.5 shrink-0 text-purple-500" />
|
|
197
|
+
<span class="flex-1 text-xs text-purple-700 dark:text-purple-300">
|
|
198
|
+
{t('fileBrowser.zarrDetected', { version: String(zarrDetection.version ?? '?') })}
|
|
199
|
+
</span>
|
|
200
|
+
<Button
|
|
201
|
+
variant="outline"
|
|
202
|
+
size="sm"
|
|
203
|
+
class="h-6 gap-1 px-2 text-[11px]"
|
|
204
|
+
onclick={openAsZarr}
|
|
205
|
+
>
|
|
206
|
+
{t('fileBrowser.openAsZarr')}
|
|
207
|
+
</Button>
|
|
208
|
+
</div>
|
|
209
|
+
{/if}
|
|
210
|
+
|
|
175
211
|
<!-- File list -->
|
|
176
212
|
<div class="relative min-h-0 flex-1">
|
|
177
213
|
<DropZone>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import PencilIcon from '@lucide/svelte/icons/pencil';
|
|
3
3
|
import Trash2Icon from '@lucide/svelte/icons/trash-2';
|
|
4
|
+
import { VIEWER_DIR_EXTENSIONS } from '../../constants.js';
|
|
4
5
|
import FileTypeIcon from '../../file-icons/FileTypeIcon.svelte';
|
|
5
6
|
import { getFileTypeInfo } from '../../file-icons/index.js';
|
|
6
7
|
import { t } from '../../i18n/index.svelte.js';
|
|
@@ -18,11 +19,15 @@ interface Props {
|
|
|
18
19
|
|
|
19
20
|
let { entry, onDelete, onRename }: Props = $props();
|
|
20
21
|
|
|
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)));
|
|
22
27
|
let showActions = $derived(browser.canWrite && !safeLock.locked);
|
|
23
28
|
|
|
24
29
|
function handleClick() {
|
|
25
|
-
if (entry.is_dir) {
|
|
30
|
+
if (entry.is_dir && !isViewerDir(entry)) {
|
|
26
31
|
browser.navigateTo(entry.path);
|
|
27
32
|
} else {
|
|
28
33
|
if (browser.activeConnection) {
|
|
@@ -68,7 +73,7 @@ function handleRenameClick(e: MouseEvent) {
|
|
|
68
73
|
>
|
|
69
74
|
<!-- Icon -->
|
|
70
75
|
<div class="flex shrink-0 items-center justify-center">
|
|
71
|
-
<FileTypeIcon extension={entry.extension} isDir={entry.is_dir} class="size-4" />
|
|
76
|
+
<FileTypeIcon extension={entry.extension} isDir={entry.is_dir && !isViewerDir(entry)} class="size-4" />
|
|
72
77
|
</div>
|
|
73
78
|
|
|
74
79
|
<!-- File name -->
|
|
@@ -10,6 +10,7 @@ import Loader2Icon from '@lucide/svelte/icons/loader-2';
|
|
|
10
10
|
import RefreshCwIcon from '@lucide/svelte/icons/refresh-cw';
|
|
11
11
|
import SearchIcon from '@lucide/svelte/icons/search';
|
|
12
12
|
import * as ContextMenu from '../ui/context-menu/index.js';
|
|
13
|
+
import { VIEWER_DIR_EXTENSIONS } from '../../constants.js';
|
|
13
14
|
import FileTypeIcon from '../../file-icons/FileTypeIcon.svelte';
|
|
14
15
|
import { t } from '../../i18n/index.svelte.js';
|
|
15
16
|
import { getAdapter } from '../../storage/index.js';
|
|
@@ -170,9 +171,6 @@ function openFile(entry: FileEntry) {
|
|
|
170
171
|
syncUrlParam(connection, entry.path);
|
|
171
172
|
}
|
|
172
173
|
|
|
173
|
-
/** Extensions that represent "virtual files" — directories that open as viewers. */
|
|
174
|
-
const VIEWER_DIR_EXTENSIONS = new Set(['zarr', 'zr3']);
|
|
175
|
-
|
|
176
174
|
function isViewerDir(entry: FileEntry): boolean {
|
|
177
175
|
return entry.is_dir && VIEWER_DIR_EXTENSIONS.has(entry.extension);
|
|
178
176
|
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
<script module lang="ts">
|
|
2
|
+
declare const __APP_VERSION__: string;
|
|
3
|
+
declare const __THIRD_PARTY_LICENSES__: {
|
|
4
|
+
license: string;
|
|
5
|
+
packages: { name: string; url: string }[];
|
|
6
|
+
}[];
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<script lang="ts">
|
|
10
|
+
import ChevronDownIcon from '@lucide/svelte/icons/chevron-down';
|
|
11
|
+
import ExternalLinkIcon from '@lucide/svelte/icons/external-link';
|
|
12
|
+
import GithubIcon from '@lucide/svelte/icons/github';
|
|
13
|
+
import {
|
|
14
|
+
Sheet,
|
|
15
|
+
SheetContent,
|
|
16
|
+
SheetDescription,
|
|
17
|
+
SheetHeader,
|
|
18
|
+
SheetTitle
|
|
19
|
+
} from '../ui/sheet/index.js';
|
|
20
|
+
import { t } from '../../i18n/index.svelte.js';
|
|
21
|
+
|
|
22
|
+
interface Props {
|
|
23
|
+
open: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let { open = $bindable(false) }: Props = $props();
|
|
27
|
+
|
|
28
|
+
let licensesOpen = $state(false);
|
|
29
|
+
|
|
30
|
+
const version = __APP_VERSION__;
|
|
31
|
+
|
|
32
|
+
const thirdPartyLicenses = __THIRD_PARTY_LICENSES__;
|
|
33
|
+
|
|
34
|
+
$effect(() => {
|
|
35
|
+
if (!open) licensesOpen = false;
|
|
36
|
+
});
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<Sheet bind:open>
|
|
40
|
+
<SheetContent side="bottom" class="max-h-[85vh] sm:mx-auto sm:max-w-lg sm:rounded-t-lg">
|
|
41
|
+
<SheetHeader>
|
|
42
|
+
<SheetTitle>{t('about.title')}</SheetTitle>
|
|
43
|
+
<SheetDescription class="sr-only">
|
|
44
|
+
{t('about.version', { version })}
|
|
45
|
+
</SheetDescription>
|
|
46
|
+
</SheetHeader>
|
|
47
|
+
|
|
48
|
+
<div class="flex flex-col items-center gap-4 overflow-y-auto px-4 py-6 sm:px-6">
|
|
49
|
+
<!-- walkthru.earth logo/link -->
|
|
50
|
+
<a
|
|
51
|
+
href="https://walkthru.earth/links"
|
|
52
|
+
target="_blank"
|
|
53
|
+
rel="noopener noreferrer"
|
|
54
|
+
class="group flex flex-col items-center gap-2 transition-opacity hover:opacity-80"
|
|
55
|
+
>
|
|
56
|
+
<img src="https://walkthru.earth/icon.svg" alt="walkthru.earth" class="size-12" />
|
|
57
|
+
<span class="flex items-center gap-1 text-lg font-semibold text-foreground">
|
|
58
|
+
walkthru.earth
|
|
59
|
+
<ExternalLinkIcon
|
|
60
|
+
class="size-3.5 opacity-0 transition-opacity group-hover:opacity-100"
|
|
61
|
+
/>
|
|
62
|
+
</span>
|
|
63
|
+
</a>
|
|
64
|
+
|
|
65
|
+
<!-- Version + License -->
|
|
66
|
+
<div class="flex flex-col items-center gap-1 text-sm text-muted-foreground">
|
|
67
|
+
<span>{t('about.version', { version })}</span>
|
|
68
|
+
<span>{t('about.license')}</span>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<!-- GitHub link -->
|
|
72
|
+
<a
|
|
73
|
+
href="https://github.com/walkthru-earth/objex"
|
|
74
|
+
target="_blank"
|
|
75
|
+
rel="noopener noreferrer"
|
|
76
|
+
class="inline-flex items-center gap-2 text-sm text-muted-foreground transition-colors hover:text-foreground"
|
|
77
|
+
>
|
|
78
|
+
<GithubIcon class="size-4" />
|
|
79
|
+
{t('about.sourceCode')}
|
|
80
|
+
</a>
|
|
81
|
+
|
|
82
|
+
<!-- Third-party licenses -->
|
|
83
|
+
<div class="w-full border-t pt-3">
|
|
84
|
+
<button
|
|
85
|
+
class="flex w-full items-center justify-between rounded-md px-3 py-1.5 text-xs text-muted-foreground transition-colors hover:bg-muted hover:text-foreground"
|
|
86
|
+
onclick={() => {
|
|
87
|
+
licensesOpen = !licensesOpen;
|
|
88
|
+
}}
|
|
89
|
+
>
|
|
90
|
+
<span>{t('about.openSourceLicenses')}</span>
|
|
91
|
+
<ChevronDownIcon
|
|
92
|
+
class="size-3.5 transition-transform {licensesOpen ? 'rotate-180' : ''}"
|
|
93
|
+
/>
|
|
94
|
+
</button>
|
|
95
|
+
|
|
96
|
+
{#if licensesOpen}
|
|
97
|
+
<div
|
|
98
|
+
class="mt-2 flex max-h-48 flex-col gap-3 overflow-y-auto rounded-lg bg-muted/40 p-3 sm:max-h-60"
|
|
99
|
+
>
|
|
100
|
+
{#each thirdPartyLicenses as group}
|
|
101
|
+
<div>
|
|
102
|
+
<span
|
|
103
|
+
class="inline-block rounded bg-muted px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground"
|
|
104
|
+
>
|
|
105
|
+
{group.license}
|
|
106
|
+
</span>
|
|
107
|
+
<div class="mt-1.5 flex flex-wrap gap-1">
|
|
108
|
+
{#each group.packages as pkg}
|
|
109
|
+
<a
|
|
110
|
+
href={pkg.url}
|
|
111
|
+
target="_blank"
|
|
112
|
+
rel="noopener noreferrer"
|
|
113
|
+
class="rounded-md border border-border/50 bg-background px-2 py-0.5 text-[11px] text-muted-foreground transition-colors hover:border-border hover:text-foreground"
|
|
114
|
+
>
|
|
115
|
+
{pkg.name}
|
|
116
|
+
</a>
|
|
117
|
+
{/each}
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
{/each}
|
|
121
|
+
</div>
|
|
122
|
+
{/if}
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
</SheetContent>
|
|
126
|
+
</Sheet>
|