@selvajs/ui 0.9.1 → 0.9.2

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.
@@ -63,7 +63,8 @@
63
63
 
64
64
  function buildTree(files: FileData[]): SvelteMap<string, TreeNode> {
65
65
  const root = new SvelteMap<string, TreeNode>();
66
- for (const file of files) {
66
+ for (let i = 0; i < files.length; i++) {
67
+ const file = files[i];
67
68
  const parts = (file.subFolder || '').split('/').filter(Boolean);
68
69
  let current = root;
69
70
  for (const part of parts) {
@@ -73,7 +74,9 @@
73
74
  const node = current.get(part)!;
74
75
  if (node.type === 'folder') current = node.children;
75
76
  }
76
- current.set(file.fileName + file.fileType, { type: 'file', file });
77
+ const baseKey = `${file.fileName}${file.fileType ?? ''}`;
78
+ const key = current.has(baseKey) ? `${baseKey}#${i}` : baseKey;
79
+ current.set(key, { type: 'file', file });
77
80
  }
78
81
  return root;
79
82
  }
@@ -93,6 +96,24 @@
93
96
  const hasSubFolders = $derived(filesArray.some((f) => f.subFolder && f.subFolder.length > 0));
94
97
  const fileTree = $derived(hasSubFolders ? buildTree(filesArray) : null);
95
98
 
99
+ function fullPath(f: FileData): string {
100
+ const folder = (f.subFolder || '').replace(/^\/+|\/+$/g, '');
101
+ const name = `${f.fileName}${f.fileType ?? ''}`;
102
+ return folder ? `${folder}/${name}` : name;
103
+ }
104
+
105
+ const duplicatePaths = $derived.by(() => {
106
+ const seen = new Map<string, number>();
107
+ for (const f of filesArray) {
108
+ const key = fullPath(f);
109
+ seen.set(key, (seen.get(key) ?? 0) + 1);
110
+ }
111
+ return Array.from(seen.entries())
112
+ .filter(([, n]) => n > 1)
113
+ .map(([path]) => path);
114
+ });
115
+ const hasDuplicates = $derived(duplicatePaths.length > 0);
116
+
96
117
  // All folders expanded by default
97
118
  let expandedFolders = new SvelteSet<string>();
98
119
 
@@ -220,7 +241,7 @@
220
241
  {@render treeNodes(fileTree, '')}
221
242
  {:else}
222
243
  <div class="gap-1 flex flex-col">
223
- {#each filesArray as file (file.fileName + file.fileType)}
244
+ {#each filesArray as file, i (fullPath(file) + '#' + i)}
224
245
  <div
225
246
  class="gap-2 text-xs flex items-center justify-between text-muted-foreground"
226
247
  >
@@ -233,6 +254,24 @@
233
254
  </div>
234
255
  {/if}
235
256
  </div>
257
+ {#if hasDuplicates}
258
+ <div
259
+ class="rounded px-3 py-2 text-sm border border-destructive bg-destructive/10 text-destructive"
260
+ >
261
+ <div class="font-medium">Duplicate file names detected</div>
262
+ <div class="mt-1 text-xs">
263
+ The following {duplicatePaths.length === 1 ? 'path appears' : 'paths appear'} more than once:
264
+ </div>
265
+ <ul class="mt-1 ml-4 text-xs list-disc">
266
+ {#each duplicatePaths as path (path)}
267
+ <li class="truncate"><span class="font-mono">{path}</span></li>
268
+ {/each}
269
+ </ul>
270
+ <div class="mt-1 text-xs">
271
+ Rename outputs in your Grasshopper definition so each file has a unique name.
272
+ </div>
273
+ </div>
274
+ {/if}
236
275
  {#if downloadError}
237
276
  <div
238
277
  class="rounded px-3 py-2 text-sm border border-destructive bg-destructive/10 text-destructive"
@@ -240,7 +279,11 @@
240
279
  {downloadError}
241
280
  </div>
242
281
  {/if}
243
- <Button onclick={handleDownload} disabled={downloading} class="w-full">
282
+ <Button
283
+ onclick={handleDownload}
284
+ disabled={downloading || hasDuplicates}
285
+ class="w-full"
286
+ >
244
287
  {downloading
245
288
  ? 'Downloading...'
246
289
  : `Download ${fileCount === 1 ? 'File' : `${fileCount} Files`}`}
package/package.json CHANGED
@@ -1,102 +1,100 @@
1
1
  {
2
- "name": "@selvajs/ui",
3
- "version": "0.9.1",
4
- "description": "Shared UI components and utilities for Selva applications",
5
- "license": "MIT",
6
- "publishConfig": {
7
- "access": "public"
8
- },
9
- "type": "module",
10
- "svelte": "./dist/index.js",
11
- "types": "./dist/index.d.ts",
12
- "exports": {
13
- ".": {
14
- "types": "./dist/index.d.ts",
15
- "svelte": "./dist/index.js"
16
- },
17
- "./primitives": {
18
- "types": "./dist/components/primitives/index.d.ts",
19
- "svelte": "./dist/components/primitives/index.js"
20
- },
21
- "./layout": {
22
- "types": "./dist/components/layout/index.d.ts",
23
- "svelte": "./dist/components/layout/index.js"
24
- },
25
- "./compute": {
26
- "types": "./dist/components/compute/index.d.ts",
27
- "svelte": "./dist/components/compute/index.js"
28
- },
29
- "./viewer": {
30
- "types": "./dist/components/viewer/index.d.ts",
31
- "svelte": "./dist/components/viewer/index.js"
32
- },
33
- "./footer": {
34
- "types": "./dist/contexts/footerContext.svelte.d.ts",
35
- "svelte": "./dist/contexts/footerContext.svelte.js"
36
- },
37
- "./utils": {
38
- "types": "./dist/utils.d.ts",
39
- "import": "./dist/utils.js"
40
- },
41
- "./styles/base.css": "./src/lib/styles/base.css",
42
- "./styles/themes/*": "./src/lib/styles/themes/*"
43
- },
44
- "files": [
45
- "dist",
46
- "src/lib",
47
- "!dist/**/*.test.*",
48
- "!dist/**/*.spec.*",
49
- "!src/lib/**/*.test.*",
50
- "!src/lib/**/*.spec.*"
51
- ],
52
- "sideEffects": [
53
- "**/*.css"
54
- ],
55
- "scripts": {
56
- "dev": "vite dev",
57
- "build": "rm -rf dist && vite build && pnpm prepack",
58
- "build:fast": "svelte-kit sync && svelte-package",
59
- "build:watch": "svelte-kit sync && svelte-package --watch",
60
- "preview": "vite preview",
61
- "prepack": "svelte-kit sync && svelte-package && publint",
62
- "prepare": "svelte-kit sync || echo ''",
63
- "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
64
- "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
65
- "format": "prettier --write .",
66
- "lint": "prettier --check . && eslint ."
67
- },
68
- "peerDependencies": {
69
- "@selvajs/compute": "catalog:",
70
- "@sveltejs/kit": "^2",
71
- "bits-ui": "^2.15.4",
72
- "svelte": "^5",
73
- "tailwind-variants": "^3.2.2",
74
- "three": "catalog:"
75
- },
76
- "peerDependenciesMeta": {
77
- "three": {
78
- "optional": true
79
- }
80
- },
81
- "dependencies": {
82
- "@floating-ui/dom": "catalog:",
83
- "@iconify/svelte": "catalog:",
84
- "@lucide/svelte": "catalog:",
85
- "@selvajs/schemas": "workspace:*",
86
- "clsx": "catalog:",
87
- "mode-watcher": "catalog:",
88
- "paneforge": "^1.0.2",
89
- "svelte-sonner": "catalog:",
90
- "tailwind-merge": "catalog:",
91
- "tw-animate-css": "catalog:"
92
- },
93
- "devDependencies": {
94
- "@internationalized/date": "^3.12.1",
95
- "@selvajs/config": "workspace:*",
96
- "@sveltejs/kit": "catalog:",
97
- "@types/three": "catalog:",
98
- "bits-ui": "^2.18.0",
99
- "svelte": "catalog:",
100
- "tailwind-variants": "^3.2.2"
101
- }
102
- }
2
+ "name": "@selvajs/ui",
3
+ "version": "0.9.2",
4
+ "description": "Shared UI components and utilities for Selva applications",
5
+ "license": "MIT",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "type": "module",
10
+ "svelte": "./dist/index.js",
11
+ "types": "./dist/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "svelte": "./dist/index.js"
16
+ },
17
+ "./primitives": {
18
+ "types": "./dist/components/primitives/index.d.ts",
19
+ "svelte": "./dist/components/primitives/index.js"
20
+ },
21
+ "./layout": {
22
+ "types": "./dist/components/layout/index.d.ts",
23
+ "svelte": "./dist/components/layout/index.js"
24
+ },
25
+ "./compute": {
26
+ "types": "./dist/components/compute/index.d.ts",
27
+ "svelte": "./dist/components/compute/index.js"
28
+ },
29
+ "./viewer": {
30
+ "types": "./dist/components/viewer/index.d.ts",
31
+ "svelte": "./dist/components/viewer/index.js"
32
+ },
33
+ "./footer": {
34
+ "types": "./dist/contexts/footerContext.svelte.d.ts",
35
+ "svelte": "./dist/contexts/footerContext.svelte.js"
36
+ },
37
+ "./utils": {
38
+ "types": "./dist/utils.d.ts",
39
+ "import": "./dist/utils.js"
40
+ },
41
+ "./styles/base.css": "./src/lib/styles/base.css",
42
+ "./styles/themes/*": "./src/lib/styles/themes/*"
43
+ },
44
+ "files": [
45
+ "dist",
46
+ "src/lib",
47
+ "!dist/**/*.test.*",
48
+ "!dist/**/*.spec.*",
49
+ "!src/lib/**/*.test.*",
50
+ "!src/lib/**/*.spec.*"
51
+ ],
52
+ "sideEffects": [
53
+ "**/*.css"
54
+ ],
55
+ "peerDependencies": {
56
+ "@selvajs/compute": "1.5.2-beta.7",
57
+ "@sveltejs/kit": "^2",
58
+ "bits-ui": "^2.15.4",
59
+ "svelte": "^5",
60
+ "tailwind-variants": "^3.2.2",
61
+ "three": "^0.184.0"
62
+ },
63
+ "peerDependenciesMeta": {
64
+ "three": {
65
+ "optional": true
66
+ }
67
+ },
68
+ "dependencies": {
69
+ "@floating-ui/dom": "^1.7.6",
70
+ "@iconify/svelte": "^5.2.1",
71
+ "@lucide/svelte": "^1.11.0",
72
+ "clsx": "^2.1.1",
73
+ "mode-watcher": "^1.1.0",
74
+ "paneforge": "^1.0.2",
75
+ "svelte-sonner": "^1.1.1",
76
+ "tailwind-merge": "^3.5.0",
77
+ "tw-animate-css": "^1.4.0",
78
+ "@selvajs/schemas": "1.1.1"
79
+ },
80
+ "devDependencies": {
81
+ "@internationalized/date": "^3.12.1",
82
+ "@sveltejs/kit": "2.58.0",
83
+ "@types/three": "^0.184.0",
84
+ "bits-ui": "^2.18.0",
85
+ "svelte": "5.55.5",
86
+ "tailwind-variants": "^3.2.2",
87
+ "@selvajs/config": "0.0.0"
88
+ },
89
+ "scripts": {
90
+ "dev": "vite dev",
91
+ "build": "rm -rf dist && vite build && pnpm prepack",
92
+ "build:fast": "svelte-kit sync && svelte-package",
93
+ "build:watch": "svelte-kit sync && svelte-package --watch",
94
+ "preview": "vite preview",
95
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
96
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
97
+ "format": "prettier --write .",
98
+ "lint": "prettier --check . && eslint ."
99
+ }
100
+ }
@@ -63,7 +63,8 @@
63
63
 
64
64
  function buildTree(files: FileData[]): SvelteMap<string, TreeNode> {
65
65
  const root = new SvelteMap<string, TreeNode>();
66
- for (const file of files) {
66
+ for (let i = 0; i < files.length; i++) {
67
+ const file = files[i];
67
68
  const parts = (file.subFolder || '').split('/').filter(Boolean);
68
69
  let current = root;
69
70
  for (const part of parts) {
@@ -73,7 +74,9 @@
73
74
  const node = current.get(part)!;
74
75
  if (node.type === 'folder') current = node.children;
75
76
  }
76
- current.set(file.fileName + file.fileType, { type: 'file', file });
77
+ const baseKey = `${file.fileName}${file.fileType ?? ''}`;
78
+ const key = current.has(baseKey) ? `${baseKey}#${i}` : baseKey;
79
+ current.set(key, { type: 'file', file });
77
80
  }
78
81
  return root;
79
82
  }
@@ -238,7 +241,7 @@
238
241
  {@render treeNodes(fileTree, '')}
239
242
  {:else}
240
243
  <div class="gap-1 flex flex-col">
241
- {#each filesArray as file (file.fileName + file.fileType)}
244
+ {#each filesArray as file, i (fullPath(file) + '#' + i)}
242
245
  <div
243
246
  class="gap-2 text-xs flex items-center justify-between text-muted-foreground"
244
247
  >
@@ -251,6 +254,24 @@
251
254
  </div>
252
255
  {/if}
253
256
  </div>
257
+ {#if hasDuplicates}
258
+ <div
259
+ class="rounded px-3 py-2 text-sm border border-destructive bg-destructive/10 text-destructive"
260
+ >
261
+ <div class="font-medium">Duplicate file names detected</div>
262
+ <div class="mt-1 text-xs">
263
+ The following {duplicatePaths.length === 1 ? 'path appears' : 'paths appear'} more than once:
264
+ </div>
265
+ <ul class="mt-1 ml-4 text-xs list-disc">
266
+ {#each duplicatePaths as path (path)}
267
+ <li class="truncate"><span class="font-mono">{path}</span></li>
268
+ {/each}
269
+ </ul>
270
+ <div class="mt-1 text-xs">
271
+ Rename outputs in your Grasshopper definition so each file has a unique name.
272
+ </div>
273
+ </div>
274
+ {/if}
254
275
  {#if downloadError}
255
276
  <div
256
277
  class="rounded px-3 py-2 text-sm border border-destructive bg-destructive/10 text-destructive"
@@ -258,7 +279,11 @@
258
279
  {downloadError}
259
280
  </div>
260
281
  {/if}
261
- <Button onclick={handleDownload} disabled={downloading} class="w-full">
282
+ <Button
283
+ onclick={handleDownload}
284
+ disabled={downloading || hasDuplicates}
285
+ class="w-full"
286
+ >
262
287
  {downloading
263
288
  ? 'Downloading...'
264
289
  : `Download ${fileCount === 1 ? 'File' : `${fileCount} Files`}`}