@walkthru-earth/objex 0.1.0 → 1.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 (84) hide show
  1. package/README.md +9 -2
  2. package/dist/components/browser/FileBrowser.svelte +53 -41
  3. package/dist/components/browser/FileRow.svelte +8 -3
  4. package/dist/components/browser/FileTreeSidebar.svelte +2 -4
  5. package/dist/components/layout/AboutSheet.svelte +126 -0
  6. package/dist/components/layout/AboutSheet.svelte.d.ts +6 -0
  7. package/dist/components/layout/ConnectionDialog.svelte +186 -138
  8. package/dist/components/layout/ConnectionDialog.svelte.d.ts +1 -0
  9. package/dist/components/layout/Sidebar.svelte +19 -3
  10. package/dist/components/layout/TabBar.svelte +4 -7
  11. package/dist/components/viewers/CodeViewer.svelte +17 -9
  12. package/dist/components/viewers/ImageViewer.svelte +6 -16
  13. package/dist/components/viewers/MarkdownViewer.svelte +8 -16
  14. package/dist/components/viewers/MediaViewer.svelte +6 -17
  15. package/dist/components/viewers/ModelViewer.svelte +4 -2
  16. package/dist/components/viewers/NotebookViewer.svelte +90 -40
  17. package/dist/components/viewers/PdfViewer.svelte +5 -3
  18. package/dist/components/viewers/RawViewer.svelte +4 -2
  19. package/dist/components/viewers/TableGrid.svelte +3 -2
  20. package/dist/components/viewers/ZarrMapViewer.svelte +334 -40
  21. package/dist/components/viewers/ZarrMapViewer.svelte.d.ts +3 -8
  22. package/dist/components/viewers/ZarrViewer.svelte +459 -178
  23. package/dist/components/viewers/map/AttributeTable.svelte +1 -6
  24. package/dist/components/viewers/pmtiles/PmtilesArchiveView.svelte +2 -6
  25. package/dist/components/viewers/pmtiles/PmtilesTileInspector.svelte +96 -22
  26. package/dist/constants.d.ts +28 -0
  27. package/dist/constants.js +34 -0
  28. package/dist/file-icons/index.js +6 -0
  29. package/dist/i18n/ar.js +34 -0
  30. package/dist/i18n/en.js +34 -0
  31. package/dist/index.d.ts +13 -1
  32. package/dist/index.js +16 -1
  33. package/dist/query/wasm.js +5 -4
  34. package/dist/storage/browser-cloud.d.ts +7 -0
  35. package/dist/storage/browser-cloud.js +74 -7
  36. package/dist/storage/providers.d.ts +53 -0
  37. package/dist/storage/providers.js +318 -0
  38. package/dist/stores/connections.svelte.js +8 -34
  39. package/dist/stores/files.svelte.d.ts +1 -6
  40. package/dist/stores/files.svelte.js +4 -36
  41. package/dist/stores/query-history.svelte.js +5 -28
  42. package/dist/stores/settings.svelte.d.ts +1 -0
  43. package/dist/stores/settings.svelte.js +11 -31
  44. package/dist/types.d.ts +2 -2
  45. package/dist/utils/clipboard.d.ts +13 -0
  46. package/dist/utils/clipboard.js +38 -0
  47. package/dist/utils/cloud-url.d.ts +27 -0
  48. package/dist/utils/cloud-url.js +61 -0
  49. package/dist/utils/error.d.ts +8 -0
  50. package/dist/utils/error.js +12 -0
  51. package/dist/utils/export.d.ts +22 -2
  52. package/dist/utils/export.js +35 -10
  53. package/dist/utils/file-sort.d.ts +20 -0
  54. package/dist/utils/file-sort.js +41 -0
  55. package/dist/utils/format.d.ts +10 -0
  56. package/dist/utils/format.js +22 -0
  57. package/dist/utils/host-detection.js +78 -18
  58. package/dist/utils/local-storage.d.ts +16 -0
  59. package/dist/utils/local-storage.js +37 -0
  60. package/dist/utils/notebook.d.ts +59 -0
  61. package/dist/utils/notebook.js +211 -0
  62. package/dist/utils/parquet-metadata.js +1 -1
  63. package/dist/utils/pmtiles-tile.js +2 -1
  64. package/dist/utils/pmtiles.js +2 -1
  65. package/dist/utils/storage-url.d.ts +1 -1
  66. package/dist/utils/storage-url.js +82 -24
  67. package/dist/utils/url-state.js +2 -7
  68. package/dist/utils/url.d.ts +0 -2
  69. package/dist/utils/url.js +3 -29
  70. package/dist/utils/zarr.d.ts +60 -20
  71. package/dist/utils/zarr.js +450 -103
  72. package/package.json +66 -54
  73. package/dist/assets/favicon.svg +0 -17
  74. package/dist/components/CLAUDE.md +0 -44
  75. package/dist/components/viewers/CLAUDE.md +0 -60
  76. package/dist/file-icons/CLAUDE.md +0 -21
  77. package/dist/i18n/CLAUDE.md +0 -19
  78. package/dist/query/CLAUDE.md +0 -22
  79. package/dist/storage/CLAUDE.md +0 -23
  80. package/dist/stores/CLAUDE.md +0 -29
  81. package/dist/types/notebookjs.d.ts +0 -14
  82. package/dist/utils/CLAUDE.md +0 -54
  83. package/dist/utils/analytics.d.ts +0 -10
  84. package/dist/utils/analytics.js +0 -38
package/README.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # objex
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/@walkthru-earth/objex?label=%40walkthru-earth%2Fobjex&color=cb3837)](https://www.npmjs.com/package/@walkthru-earth/objex)
4
+ [![npm](https://img.shields.io/npm/v/@walkthru-earth/objex-utils?label=%40walkthru-earth%2Fobjex-utils&color=cb3837)](https://www.npmjs.com/package/@walkthru-earth/objex-utils)
5
+ [![CI](https://github.com/walkthru-earth/objex/actions/workflows/ci.yml/badge.svg)](https://github.com/walkthru-earth/objex/actions/workflows/ci.yml)
6
+ [![License: CC BY 4.0](https://img.shields.io/badge/license-CC%20BY%204.0-lightgrey.svg)](https://creativecommons.org/licenses/by/4.0/)
7
+
3
8
  Cloud storage explorer that runs entirely in the browser. Connect to S3, Azure, GCS, R2, MinIO -- browse files, query data with SQL, and visualize geospatial formats on interactive maps. No backend required.
4
9
 
5
10
  ```mermaid
@@ -13,7 +18,7 @@ graph LR
13
18
 
14
19
  ## Features
15
20
 
16
- - **Browse** cloud storage (S3, GCS, Azure, R2, MinIO, Wasabi, Storj, direct URLs)
21
+ - **Browse** cloud storage (S3, GCS, Azure, R2, B2, DigitalOcean, Wasabi, Storj, Hetzner, Contabo, Linode, OVHcloud, MinIO, direct URLs)
17
22
  - **Query** Parquet, CSV, JSONL with SQL (DuckDB-WASM, cancellable queries)
18
23
  - **Visualize** GeoParquet, GeoJSON, COG, PMTiles, FlatGeobuf, Zarr on maps (MapLibre + deck.gl)
19
24
  - **View** 100+ file formats: code (30+ languages), Jupyter notebooks, PDF, 3D models, archives, media
@@ -92,12 +97,14 @@ import {
92
97
  | `./utils/geoarrow` | `buildGeoArrowTables`, `normalizeGeomType` |
93
98
  | `./utils/storage-url` | `parseStorageUrl`, `looksLikeUrl` |
94
99
  | `./utils/parquet-metadata` | `readParquetMetadata`, `extractEpsgFromGeoMeta` |
95
- | `./utils/format` | `formatFileSize`, `formatDate`, `getFileExtension` |
100
+ | `./utils/format` | `formatFileSize`, `formatDate`, `formatValue`, `getFileExtension`, `jsonReplacerBigInt` |
96
101
  | `./utils/hex` | `generateHexDump` |
97
102
  | `./utils/column-types` | `classifyType`, `typeColor`, `typeBadgeClass` |
98
103
  | `./file-icons` | `getFileTypeInfo`, `getDuckDbReadFn`, `getViewerKind` |
99
104
  | `./types` | `FileEntry`, `Connection`, `Tab`, `WriteResult`, `Theme` |
100
105
 
106
+ The main export also includes `copyToClipboard`, `handleLoadError`, and shared constants (`WGS84_CODES`, `STORAGE_KEYS`, `DEFAULT_TARGET_CRS`, etc.).
107
+
101
108
  ## Quick Start (Development)
102
109
 
103
110
  ```bash
@@ -1,12 +1,20 @@
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 {
12
+ type SortConfig,
13
+ type SortField,
14
+ sortFileEntries,
15
+ toggleSortField
16
+ } from '../../utils/file-sort.js';
17
+ import { detectZarrMarkers } from '../../utils/zarr.js';
10
18
  import Breadcrumb from './Breadcrumb.svelte';
11
19
  import CreateFolderDialog from './CreateFolderDialog.svelte';
12
20
  import DeleteConfirmDialog from './DeleteConfirmDialog.svelte';
@@ -16,12 +24,8 @@ import RenameDialog from './RenameDialog.svelte';
16
24
  import SearchBar from './SearchBar.svelte';
17
25
  import UploadButton from './UploadButton.svelte';
18
26
 
19
- type SortField = 'name' | 'size' | 'modified' | 'extension';
20
- type SortDirection = 'asc' | 'desc';
21
-
22
27
  let filterQuery = $state('');
23
- let sortField = $state<SortField>('name');
24
- let sortDirection = $state<SortDirection>('asc');
28
+ let sortConfig = $state<SortConfig>({ field: 'name', direction: 'asc' });
25
29
 
26
30
  let deleteDialogOpen = $state(false);
27
31
  let deleteTarget = $state<FileEntry | null>(null);
@@ -31,8 +35,24 @@ let renameTarget = $state<FileEntry | null>(null);
31
35
 
32
36
  let showWriteActions = $derived(browser.canWrite && !safeLock.locked);
33
37
 
38
+ const zarrDetection = $derived(detectZarrMarkers(browser.entries.map((e: FileEntry) => e.name)));
39
+
40
+ function openAsZarr() {
41
+ if (!browser.activeConnection) return;
42
+ const prefix = browser.currentPrefix.replace(/\/+$/, '');
43
+ const name = prefix.split('/').pop() || browser.activeConnection.bucket;
44
+ tabs.open({
45
+ id: `${browser.activeConnection.id}:${prefix}/`,
46
+ name,
47
+ path: `${prefix}/`,
48
+ source: 'remote',
49
+ connectionId: browser.activeConnection.id,
50
+ extension: 'zarr'
51
+ });
52
+ }
53
+
34
54
  const sortedAndFilteredEntries = $derived.by(() => {
35
- let result = [...browser.entries];
55
+ let result = browser.entries;
36
56
 
37
57
  // Filter
38
58
  if (filterQuery) {
@@ -40,28 +60,7 @@ const sortedAndFilteredEntries = $derived.by(() => {
40
60
  result = result.filter((entry: FileEntry) => entry.name.toLowerCase().includes(q));
41
61
  }
42
62
 
43
- // Sort
44
- const dir = sortDirection === 'asc' ? 1 : -1;
45
- result.sort((a, b) => {
46
- // Directories always come first
47
- if (a.is_dir && !b.is_dir) return -1;
48
- if (!a.is_dir && b.is_dir) return 1;
49
-
50
- switch (sortField) {
51
- case 'name':
52
- return dir * a.name.localeCompare(b.name, undefined, { sensitivity: 'base' });
53
- case 'size':
54
- return dir * (a.size - b.size);
55
- case 'modified':
56
- return dir * (a.modified - b.modified);
57
- case 'extension':
58
- return dir * a.extension.localeCompare(b.extension, undefined, { sensitivity: 'base' });
59
- default:
60
- return 0;
61
- }
62
- });
63
-
64
- return result;
63
+ return sortFileEntries(result, sortConfig);
65
64
  });
66
65
 
67
66
  function handleFilter(query: string) {
@@ -73,12 +72,7 @@ function handleNavigate(path: string) {
73
72
  }
74
73
 
75
74
  function handleSort(field: SortField) {
76
- if (sortField === field) {
77
- sortDirection = sortDirection === 'asc' ? 'desc' : 'asc';
78
- } else {
79
- sortField = field;
80
- sortDirection = 'asc';
81
- }
75
+ sortConfig = toggleSortField(sortConfig, field);
82
76
  }
83
77
 
84
78
  function handleDelete(entry: FileEntry) {
@@ -134,8 +128,8 @@ function handleRename(entry: FileEntry) {
134
128
  onclick={() => handleSort('name')}
135
129
  >
136
130
  {t('fileBrowser.name')}
137
- {#if sortField === 'name'}
138
- {#if sortDirection === 'asc'}
131
+ {#if sortConfig.field === 'name'}
132
+ {#if sortConfig.direction === 'asc'}
139
133
  <ArrowUp class="size-3" />
140
134
  {:else}
141
135
  <ArrowDown class="size-3" />
@@ -148,8 +142,8 @@ function handleRename(entry: FileEntry) {
148
142
  class="text-muted-foreground hover:text-foreground flex w-20 shrink-0 items-center justify-end gap-1 transition-colors"
149
143
  onclick={() => handleSort('size')}
150
144
  >
151
- {#if sortField === 'size'}
152
- {#if sortDirection === 'asc'}
145
+ {#if sortConfig.field === 'size'}
146
+ {#if sortConfig.direction === 'asc'}
153
147
  <ArrowUp class="size-3" />
154
148
  {:else}
155
149
  <ArrowDown class="size-3" />
@@ -161,8 +155,8 @@ function handleRename(entry: FileEntry) {
161
155
  class="text-muted-foreground hover:text-foreground flex w-24 shrink-0 items-center justify-end gap-1 transition-colors"
162
156
  onclick={() => handleSort('modified')}
163
157
  >
164
- {#if sortField === 'modified'}
165
- {#if sortDirection === 'asc'}
158
+ {#if sortConfig.field === 'modified'}
159
+ {#if sortConfig.direction === 'asc'}
166
160
  <ArrowUp class="size-3" />
167
161
  {:else}
168
162
  <ArrowDown class="size-3" />
@@ -172,6 +166,24 @@ function handleRename(entry: FileEntry) {
172
166
  </button>
173
167
  </div>
174
168
 
169
+ <!-- Zarr detection banner -->
170
+ {#if zarrDetection.detected}
171
+ <div class="border-border flex items-center gap-2 border-b bg-purple-50 px-3 py-1.5 dark:bg-purple-950/30">
172
+ <Layers class="size-3.5 shrink-0 text-purple-500" />
173
+ <span class="flex-1 text-xs text-purple-700 dark:text-purple-300">
174
+ {t('fileBrowser.zarrDetected', { version: String(zarrDetection.version ?? '?') })}
175
+ </span>
176
+ <Button
177
+ variant="outline"
178
+ size="sm"
179
+ class="h-6 gap-1 px-2 text-[11px]"
180
+ onclick={openAsZarr}
181
+ >
182
+ {t('fileBrowser.openAsZarr')}
183
+ </Button>
184
+ </div>
185
+ {/if}
186
+
175
187
  <!-- File list -->
176
188
  <div class="relative min-h-0 flex-1">
177
189
  <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
- const info = $derived(getFileTypeInfo(entry.extension, entry.is_dir));
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,13 +10,14 @@ 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';
16
17
  import { browser } from '../../stores/browser.svelte.js';
17
18
  import { tabs } from '../../stores/tabs.svelte.js';
18
19
  import type { Connection, FileEntry } from '../../types.js';
19
- import { getNativeScheme } from '../../utils/url.js';
20
+ import { getNativeScheme } from '../../utils/cloud-url.js';
20
21
  import { syncUrlParam } from '../../utils/url-state.js';
21
22
 
22
23
  let {
@@ -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>
@@ -0,0 +1,6 @@
1
+ interface Props {
2
+ open: boolean;
3
+ }
4
+ declare const AboutSheet: import("svelte").Component<Props, {}, "open">;
5
+ type AboutSheet = ReturnType<typeof AboutSheet>;
6
+ export default AboutSheet;