@unicitylabs/sphere-ui 0.1.22 → 0.1.24

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.
@@ -1,4 +1,4 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
1
+ import * as react from 'react';
2
2
  import { ReactNode } from 'react';
3
3
 
4
4
  type DateRangePreset = '1d' | '7d' | '30d' | '90d';
@@ -25,7 +25,7 @@ declare const PRESET_LABELS: Record<DateRangePreset, string>;
25
25
  * Emits a normalized DateRangeValue that maps 1:1 to the backend
26
26
  * ?range=... query: presets pass the label, "custom" passes from+to.
27
27
  */
28
- declare function DateRangePicker({ value, onChange, presets, className, }: DateRangePickerProps): react_jsx_runtime.JSX.Element;
28
+ declare function DateRangePicker({ value, onChange, presets, className, }: DateRangePickerProps): react.JSX.Element;
29
29
 
30
30
  interface TimeseriesSeries {
31
31
  /** Key inside each data point (e.g. 'installs'). */
@@ -55,7 +55,7 @@ interface TimeseriesChartProps {
55
55
  * etc. Encapsulates the Recharts wiring so both admin and developer
56
56
  * views render charts identically.
57
57
  */
58
- declare function TimeseriesChart({ data, series, variant, height, stacked, showLegend, showGrid, className, emptyState, }: TimeseriesChartProps): react_jsx_runtime.JSX.Element;
58
+ declare function TimeseriesChart({ data, series, variant, height, stacked, showLegend, showGrid, className, emptyState, }: TimeseriesChartProps): react.JSX.Element;
59
59
 
60
60
  interface KPICardProps {
61
61
  /** Short uppercase label above the value. */
@@ -84,7 +84,7 @@ interface KPICardProps {
84
84
  * Same shape as dev-portal's admin-card pattern so both frontends
85
85
  * render KPIs identically.
86
86
  */
87
- declare function KPICard({ label, value, icon, previousValue, hint, title, format, accentColor, className, }: KPICardProps): react_jsx_runtime.JSX.Element;
87
+ declare function KPICard({ label, value, icon, previousValue, hint, title, format, accentColor, className, }: KPICardProps): react.JSX.Element;
88
88
 
89
89
  interface TopEntity {
90
90
  id: string;
@@ -116,7 +116,7 @@ interface TopEntitiesTableProps {
116
116
  * Ranked list with a horizontal bar fill per row. Used for "top quests",
117
117
  * "top installed projects", "top users", etc. — any cross-entity ranking.
118
118
  */
119
- declare function TopEntitiesTable({ entities, title, valueLabel, secondaryLabel, hideBars, emptyState, accentColor, className, }: TopEntitiesTableProps): react_jsx_runtime.JSX.Element;
119
+ declare function TopEntitiesTable({ entities, title, valueLabel, secondaryLabel, hideBars, emptyState, accentColor, className, }: TopEntitiesTableProps): react.JSX.Element;
120
120
 
121
121
  interface AnalyticsSkeletonProps {
122
122
  /** Rendered above the grid — usually the existing page header (title + DateRangePicker). */
@@ -137,6 +137,6 @@ interface AnalyticsSkeletonProps {
137
137
  * `header` they render in the real state so the date-range picker
138
138
  * stays interactive while data loads.
139
139
  */
140
- declare function AnalyticsSkeleton({ header, showKpiRows, showCharts, showBottomTables, className, }: AnalyticsSkeletonProps): react_jsx_runtime.JSX.Element;
140
+ declare function AnalyticsSkeleton({ header, showKpiRows, showCharts, showBottomTables, className, }: AnalyticsSkeletonProps): react.JSX.Element;
141
141
 
142
142
  export { AnalyticsSkeleton, type AnalyticsSkeletonProps, type DateRangeLabel, DateRangePicker, type DateRangePickerProps, type DateRangePreset, type DateRangeValue, KPICard, type KPICardProps, TimeseriesChart, type TimeseriesChartProps, type TimeseriesSeries, TopEntitiesTable, type TopEntitiesTableProps, type TopEntity, PRESET_LABELS as dateRangePresetLabels };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
1
  import * as react from 'react';
3
2
  import { ReactNode, ButtonHTMLAttributes, InputHTMLAttributes, SelectHTMLAttributes, TextareaHTMLAttributes, MouseEvent, Ref } from 'react';
4
3
  import { ColumnDef } from '@tanstack/react-table';
@@ -17,7 +16,7 @@ interface DashboardLayoutProps {
17
16
  * Dashboard shell with sidebar + main content area.
18
17
  * Used by sphere-backoffice and sphere-dev-portal.
19
18
  */
20
- declare function DashboardLayout({ logo, nav, footer, children }: DashboardLayoutProps): react_jsx_runtime.JSX.Element;
19
+ declare function DashboardLayout({ logo, nav, footer, children }: DashboardLayoutProps): react.JSX.Element;
21
20
 
22
21
  interface AppLogoProps {
23
22
  /** Short code displayed in the icon (e.g. "SQ", "SD") */
@@ -33,7 +32,7 @@ interface AppLogoProps {
33
32
  * App logo for dashboard sidebar.
34
33
  * Orange icon + Anton title + Geist subtitle.
35
34
  */
36
- declare function AppLogo({ icon, title, subtitle, onClick }: AppLogoProps): react_jsx_runtime.JSX.Element;
35
+ declare function AppLogo({ icon, title, subtitle, onClick }: AppLogoProps): react.JSX.Element;
37
36
 
38
37
  interface NavItem {
39
38
  to: string;
@@ -60,7 +59,7 @@ interface SidebarNavProps {
60
59
  * Sidebar navigation with grouped items, icons, and badges.
61
60
  * Matches the admin panel sidebar style exactly.
62
61
  */
63
- declare function SidebarNav({ groups, currentPath, onNavigate }: SidebarNavProps): react_jsx_runtime.JSX.Element;
62
+ declare function SidebarNav({ groups, currentPath, onNavigate }: SidebarNavProps): react.JSX.Element;
64
63
 
65
64
  interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
66
65
  error?: boolean;
@@ -89,12 +88,12 @@ interface FieldProps {
89
88
  className?: string;
90
89
  children: ReactNode;
91
90
  }
92
- declare function Field({ label, required, error, hint, className, children }: FieldProps): react_jsx_runtime.JSX.Element;
91
+ declare function Field({ label, required, error, hint, className, children }: FieldProps): react.JSX.Element;
93
92
  interface SectionProps {
94
93
  title: string;
95
94
  children: ReactNode;
96
95
  }
97
- declare function Section({ title, children }: SectionProps): react_jsx_runtime.JSX.Element;
96
+ declare function Section({ title, children }: SectionProps): react.JSX.Element;
98
97
 
99
98
  interface FormModalProps {
100
99
  title: string;
@@ -121,21 +120,21 @@ interface StatusBadgeProps {
121
120
  status: string;
122
121
  className?: string;
123
122
  }
124
- declare function StatusBadge({ status, className }: StatusBadgeProps): react_jsx_runtime.JSX.Element;
123
+ declare function StatusBadge({ status, className }: StatusBadgeProps): react.JSX.Element;
125
124
 
126
125
  interface SearchInputProps {
127
126
  value: string;
128
127
  onChange: (value: string) => void;
129
128
  placeholder?: string;
130
129
  }
131
- declare function SearchInput({ value, onChange, placeholder }: SearchInputProps): react_jsx_runtime.JSX.Element;
130
+ declare function SearchInput({ value, onChange, placeholder }: SearchInputProps): react.JSX.Element;
132
131
 
133
132
  interface EmptyStateProps {
134
133
  title: string;
135
134
  description?: string;
136
135
  action?: ReactNode;
137
136
  }
138
- declare function EmptyState({ title, description, action }: EmptyStateProps): react_jsx_runtime.JSX.Element;
137
+ declare function EmptyState({ title, description, action }: EmptyStateProps): react.JSX.Element;
139
138
 
140
139
  interface SkeletonProps {
141
140
  width?: string;
@@ -143,7 +142,7 @@ interface SkeletonProps {
143
142
  radius?: string;
144
143
  className?: string;
145
144
  }
146
- declare function Skeleton({ width, height, radius, className, }: SkeletonProps): react_jsx_runtime.JSX.Element;
145
+ declare function Skeleton({ width, height, radius, className, }: SkeletonProps): react.JSX.Element;
147
146
 
148
147
  interface SkeletonTextProps {
149
148
  lines?: number;
@@ -151,14 +150,14 @@ interface SkeletonTextProps {
151
150
  gap?: string;
152
151
  className?: string;
153
152
  }
154
- declare function SkeletonText({ lines, lineHeight, gap, className, }: SkeletonTextProps): react_jsx_runtime.JSX.Element;
153
+ declare function SkeletonText({ lines, lineHeight, gap, className, }: SkeletonTextProps): react.JSX.Element;
155
154
 
156
155
  type SkeletonCircleSize = 'sm' | 'md' | 'lg' | (string & {});
157
156
  interface SkeletonCircleProps {
158
157
  size?: SkeletonCircleSize;
159
158
  className?: string;
160
159
  }
161
- declare function SkeletonCircle({ size, className }: SkeletonCircleProps): react_jsx_runtime.JSX.Element;
160
+ declare function SkeletonCircle({ size, className }: SkeletonCircleProps): react.JSX.Element;
162
161
 
163
162
  interface SelectOption {
164
163
  value: string;
@@ -172,7 +171,7 @@ interface CustomSelectProps {
172
171
  className?: string;
173
172
  size?: 'sm' | 'md';
174
173
  }
175
- declare function CustomSelect({ options, value, onChange, placeholder, className, size, }: CustomSelectProps): react_jsx_runtime.JSX.Element;
174
+ declare function CustomSelect({ options, value, onChange, placeholder, className, size, }: CustomSelectProps): react.JSX.Element;
176
175
 
177
176
  interface PageShellProps {
178
177
  title: string;
@@ -181,7 +180,7 @@ interface PageShellProps {
181
180
  maxWidth?: string;
182
181
  children: ReactNode;
183
182
  }
184
- declare function PageShell({ title, subtitle, action, maxWidth, children }: PageShellProps): react_jsx_runtime.JSX.Element;
183
+ declare function PageShell({ title, subtitle, action, maxWidth, children }: PageShellProps): react.JSX.Element;
185
184
 
186
185
  interface DataTableProps<T> {
187
186
  columns: ColumnDef<T, unknown>[];
@@ -192,21 +191,21 @@ interface DataTableProps<T> {
192
191
  enableSearch?: boolean;
193
192
  onRowClick?: (row: T) => void;
194
193
  }
195
- declare function DataTable<T>({ columns, data, isLoading, emptyMessage, searchPlaceholder, enableSearch, onRowClick, }: DataTableProps<T>): react_jsx_runtime.JSX.Element;
194
+ declare function DataTable<T>({ columns, data, isLoading, emptyMessage, searchPlaceholder, enableSearch, onRowClick, }: DataTableProps<T>): react.JSX.Element;
196
195
 
197
196
  interface AlertBannerProps {
198
197
  type: 'warning' | 'info';
199
198
  title: string;
200
199
  children: ReactNode;
201
200
  }
202
- declare function AlertBanner({ type, title, children }: AlertBannerProps): react_jsx_runtime.JSX.Element;
201
+ declare function AlertBanner({ type, title, children }: AlertBannerProps): react.JSX.Element;
203
202
 
204
203
  interface AddressDisplayProps {
205
204
  address: string;
206
205
  nametag?: string | null;
207
206
  truncate?: boolean;
208
207
  }
209
- declare function AddressDisplay({ address, nametag, truncate }: AddressDisplayProps): react_jsx_runtime.JSX.Element;
208
+ declare function AddressDisplay({ address, nametag, truncate }: AddressDisplayProps): react.JSX.Element;
210
209
 
211
210
  interface JsonPanelProps<T> {
212
211
  /** Current form state to display as JSON */
@@ -218,11 +217,11 @@ interface JsonPanelProps<T> {
218
217
  /** Panel title */
219
218
  title?: string;
220
219
  }
221
- declare function JsonPanel<T extends Record<string, unknown>>({ value, onChange, excludeKeys, title, }: JsonPanelProps<T>): react_jsx_runtime.JSX.Element;
220
+ declare function JsonPanel<T extends Record<string, unknown>>({ value, onChange, excludeKeys, title, }: JsonPanelProps<T>): react.JSX.Element;
222
221
  declare function JsonToggleButton({ active, onClick }: {
223
222
  active: boolean;
224
223
  onClick: () => void;
225
- }): react_jsx_runtime.JSX.Element;
224
+ }): react.JSX.Element;
226
225
 
227
226
  declare function tagColor(tag: string): {
228
227
  bg: string;
@@ -234,7 +233,7 @@ declare function ChainInput({ chains, suggestions, onChange, size }: {
234
233
  suggestions: string[];
235
234
  onChange: (chains: Record<string, number>) => void;
236
235
  size?: 'sm' | 'md';
237
- }): react_jsx_runtime.JSX.Element;
236
+ }): react.JSX.Element;
238
237
 
239
238
  interface MemoCondition {
240
239
  key: string;
@@ -245,7 +244,7 @@ interface MemoCondition {
245
244
  declare function MemoConditionsEditor({ conditions, onChange }: {
246
245
  conditions: MemoCondition[];
247
246
  onChange: (conditions: MemoCondition[]) => void;
248
- }): react_jsx_runtime.JSX.Element;
247
+ }): react.JSX.Element;
249
248
 
250
249
  /**
251
250
  * Consistent SVG icon set for the admin panel.
@@ -256,27 +255,27 @@ interface IconProps {
256
255
  className?: string;
257
256
  style?: React.CSSProperties;
258
257
  }
259
- declare const IconBack: (p: IconProps) => react_jsx_runtime.JSX.Element;
260
- declare const IconUndo: (p: IconProps) => react_jsx_runtime.JSX.Element;
261
- declare const IconQuests: (p: IconProps) => react_jsx_runtime.JSX.Element;
262
- declare const IconTracks: (p: IconProps) => react_jsx_runtime.JSX.Element;
263
- declare const IconSettings: (p: IconProps) => react_jsx_runtime.JSX.Element;
264
- declare const IconChain: (p: IconProps) => react_jsx_runtime.JSX.Element;
265
- declare const IconPlus: (p: IconProps) => react_jsx_runtime.JSX.Element;
266
- declare const IconEdit: (p: IconProps) => react_jsx_runtime.JSX.Element;
267
- declare const IconTrash: (p: IconProps) => react_jsx_runtime.JSX.Element;
268
- declare const IconX: (p: IconProps) => react_jsx_runtime.JSX.Element;
269
- declare const IconCheck: (p: IconProps) => react_jsx_runtime.JSX.Element;
270
- declare const IconSearch: (p: IconProps) => react_jsx_runtime.JSX.Element;
271
- declare const IconChevronUp: (p: IconProps) => react_jsx_runtime.JSX.Element;
272
- declare const IconChevronDown: (p: IconProps) => react_jsx_runtime.JSX.Element;
273
- declare const IconChevronsDown: (p: IconProps) => react_jsx_runtime.JSX.Element;
274
- declare const IconChevronsRight: (p: IconProps) => react_jsx_runtime.JSX.Element;
275
- declare const IconArrowRight: (p: IconProps) => react_jsx_runtime.JSX.Element;
276
- declare const IconPlay: (p: IconProps) => react_jsx_runtime.JSX.Element;
277
- declare const IconStar: (p: IconProps) => react_jsx_runtime.JSX.Element;
278
- declare const IconDiamond: (p: IconProps) => react_jsx_runtime.JSX.Element;
279
- declare const IconCircle: (p: IconProps) => react_jsx_runtime.JSX.Element;
258
+ declare const IconBack: (p: IconProps) => react.JSX.Element;
259
+ declare const IconUndo: (p: IconProps) => react.JSX.Element;
260
+ declare const IconQuests: (p: IconProps) => react.JSX.Element;
261
+ declare const IconTracks: (p: IconProps) => react.JSX.Element;
262
+ declare const IconSettings: (p: IconProps) => react.JSX.Element;
263
+ declare const IconChain: (p: IconProps) => react.JSX.Element;
264
+ declare const IconPlus: (p: IconProps) => react.JSX.Element;
265
+ declare const IconEdit: (p: IconProps) => react.JSX.Element;
266
+ declare const IconTrash: (p: IconProps) => react.JSX.Element;
267
+ declare const IconX: (p: IconProps) => react.JSX.Element;
268
+ declare const IconCheck: (p: IconProps) => react.JSX.Element;
269
+ declare const IconSearch: (p: IconProps) => react.JSX.Element;
270
+ declare const IconChevronUp: (p: IconProps) => react.JSX.Element;
271
+ declare const IconChevronDown: (p: IconProps) => react.JSX.Element;
272
+ declare const IconChevronsDown: (p: IconProps) => react.JSX.Element;
273
+ declare const IconChevronsRight: (p: IconProps) => react.JSX.Element;
274
+ declare const IconArrowRight: (p: IconProps) => react.JSX.Element;
275
+ declare const IconPlay: (p: IconProps) => react.JSX.Element;
276
+ declare const IconStar: (p: IconProps) => react.JSX.Element;
277
+ declare const IconDiamond: (p: IconProps) => react.JSX.Element;
278
+ declare const IconCircle: (p: IconProps) => react.JSX.Element;
280
279
 
281
280
  type MediaKind = 'logo' | 'banner' | 'screenshot' | 'image' | 'background';
282
281
  type MediaMime = 'image/png' | 'image/jpeg' | 'image/webp' | 'image/svg+xml';
@@ -322,7 +321,7 @@ interface MediaUploaderProps {
322
321
  deferUpload?: boolean;
323
322
  onFileSelected?: (file: File | null) => void;
324
323
  }
325
- declare function MediaUploader({ kind, ownerType, ownerId, value, onChange, uploadFn, label, deferUpload, onFileSelected, }: MediaUploaderProps): react_jsx_runtime.JSX.Element;
324
+ declare function MediaUploader({ kind, ownerType, ownerId, value, onChange, uploadFn, label, deferUpload, onFileSelected, }: MediaUploaderProps): react.JSX.Element;
326
325
 
327
326
  interface MediaItem {
328
327
  type: 'screenshot' | 'video';
@@ -336,7 +335,7 @@ interface MediaGalleryProps {
336
335
  uploadFn: MediaUploadFn;
337
336
  max?: number;
338
337
  }
339
- declare function MediaGallery({ ownerType, ownerId, items, onChange, uploadFn, max }: MediaGalleryProps): react_jsx_runtime.JSX.Element;
338
+ declare function MediaGallery({ ownerType, ownerId, items, onChange, uploadFn, max }: MediaGalleryProps): react.JSX.Element;
340
339
 
341
340
  interface MarketplaceProjectCardProps {
342
341
  name: string;
@@ -365,7 +364,7 @@ interface MarketplaceProjectCardProps {
365
364
  *
366
365
  * No `<Link>` dependency — wrap externally if router navigation is needed.
367
366
  */
368
- declare function MarketplaceProjectCard({ name, tagline, logoUrl, bannerUrl, accentColor, category, users, quests, positivePercent, ratingCount, installState, onInstallClick, onClick, }: MarketplaceProjectCardProps): react_jsx_runtime.JSX.Element;
367
+ declare function MarketplaceProjectCard({ name, tagline, logoUrl, bannerUrl, accentColor, category, users, quests, positivePercent, ratingCount, installState, onInstallClick, onClick, }: MarketplaceProjectCardProps): react.JSX.Element;
369
368
 
370
369
  interface FeaturedProjectCardProps {
371
370
  name: string;
@@ -386,7 +385,7 @@ interface FeaturedProjectCardProps {
386
385
  * Wide hero-style card used in marketplace featured rails.
387
386
  * No `<Link>` dependency — wrap externally if router navigation is needed.
388
387
  */
389
- declare function FeaturedProjectCard({ name, tagline, logoUrl, bannerUrl, accentColor, users, quests, positivePercent, ratingCount, onClick, }: FeaturedProjectCardProps): react_jsx_runtime.JSX.Element;
388
+ declare function FeaturedProjectCard({ name, tagline, logoUrl, bannerUrl, accentColor, users, quests, positivePercent, ratingCount, onClick, }: FeaturedProjectCardProps): react.JSX.Element;
390
389
 
391
390
  /** Loose typing — dnd-kit listeners/attributes don't fit React's HTMLButtonAttributes due to motion.button overrides. */
392
391
  type ButtonExtraProps = {
@@ -419,7 +418,7 @@ interface InstalledProjectIconProps {
419
418
  * primary action, `onContextMenu` for right-click, and
420
419
  * `topRightAction` to overlay a small action button on the tile.
421
420
  */
422
- declare function InstalledProjectIcon({ name, logoUrl, accentColor, onClick, onContextMenu, topRightAction, showLabel, buttonRef, buttonProps, }: InstalledProjectIconProps): react_jsx_runtime.JSX.Element;
421
+ declare function InstalledProjectIcon({ name, logoUrl, accentColor, onClick, onContextMenu, topRightAction, showLabel, buttonRef, buttonProps, }: InstalledProjectIconProps): react.JSX.Element;
423
422
 
424
423
  type ProjectLogoSize = 'sm' | 'md' | 'lg';
425
424
  interface ProjectLogoProps {
@@ -443,7 +442,7 @@ interface ProjectLogoProps {
443
442
  * cards, install previews) so the icon looks the same regardless of
444
443
  * surrounding chrome.
445
444
  */
446
- declare function ProjectLogo({ name, logoUrl, accentColor, size, className, children, }: ProjectLogoProps): react_jsx_runtime.JSX.Element;
445
+ declare function ProjectLogo({ name, logoUrl, accentColor, size, className, children, }: ProjectLogoProps): react.JSX.Element;
447
446
 
448
447
  interface QuestPreviewSummary {
449
448
  slug: string;
@@ -490,7 +489,7 @@ interface ProjectPagePreviewProps {
490
489
  * Used by dev-portal & backoffice as a live preview while editing a project.
491
490
  * No router / hooks / data fetching — every value comes via props.
492
491
  */
493
- declare function ProjectPagePreview({ name, tagline, description, logoUrl, bannerUrl, accentColor, category, websiteUrl, discordUrl, twitterUrl, media, users, activeQuests, positivePercent, ratingCount, quests, achievements, tags, }: ProjectPagePreviewProps): react_jsx_runtime.JSX.Element;
492
+ declare function ProjectPagePreview({ name, tagline, description, logoUrl, bannerUrl, accentColor, category, websiteUrl, discordUrl, twitterUrl, media, users, activeQuests, positivePercent, ratingCount, quests, achievements, tags, }: ProjectPagePreviewProps): react.JSX.Element;
494
493
 
495
494
  declare const MEDIA_LIMITS: Record<MediaKind, MediaLimit>;
496
495
  declare function isMimeAllowed(kind: MediaKind, mime: string): mime is MediaMime;
package/dist/index.js CHANGED
@@ -1411,6 +1411,17 @@ import { Fragment as Fragment4, jsx as jsx23, jsxs as jsxs18 } from "react/jsx-r
1411
1411
  function formatExtensions(mimes) {
1412
1412
  return mimes.map((m) => m.split("/")[1].toUpperCase()).join(", ");
1413
1413
  }
1414
+ async function readImageSize(file) {
1415
+ if (typeof createImageBitmap !== "function") return null;
1416
+ try {
1417
+ const bitmap = await createImageBitmap(file);
1418
+ const size = { width: bitmap.width, height: bitmap.height };
1419
+ bitmap.close?.();
1420
+ return size;
1421
+ } catch {
1422
+ return null;
1423
+ }
1424
+ }
1414
1425
  function MediaUploader({
1415
1426
  kind,
1416
1427
  ownerType,
@@ -1453,6 +1464,30 @@ function MediaUploader({
1453
1464
  });
1454
1465
  return;
1455
1466
  }
1467
+ if (file.type !== "image/svg+xml") {
1468
+ const size = await readImageSize(file);
1469
+ if (size) {
1470
+ const { width, height } = size;
1471
+ if (limit.maxWidth && width > limit.maxWidth || limit.maxHeight && height > limit.maxHeight) {
1472
+ setState({
1473
+ phase: "error",
1474
+ message: `Image too large (max ${limit.maxWidth}\xD7${limit.maxHeight}px \u2014 this is ${width}\xD7${height})`
1475
+ });
1476
+ return;
1477
+ }
1478
+ if (limit.aspectRatio) {
1479
+ const ratio = width / height;
1480
+ const tolerance = limit.aspectTolerance ?? 0;
1481
+ if (Math.abs(ratio - limit.aspectRatio) > limit.aspectRatio * tolerance) {
1482
+ setState({
1483
+ phase: "error",
1484
+ message: `Wrong aspect ratio (need ~${limit.aspectRatio}:1 \u2014 this is ${ratio.toFixed(2)}:1)`
1485
+ });
1486
+ return;
1487
+ }
1488
+ }
1489
+ }
1490
+ }
1456
1491
  if (deferUpload) {
1457
1492
  if (previewRef.current) URL.revokeObjectURL(previewRef.current);
1458
1493
  previewRef.current = URL.createObjectURL(file);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unicitylabs/sphere-ui",
3
- "version": "0.1.22",
3
+ "version": "0.1.24",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -43,16 +43,18 @@
43
43
  "test": "vitest",
44
44
  "test:run": "vitest run"
45
45
  },
46
+ "dependencies": {
47
+ "framer-motion": "^11.18.2",
48
+ "lucide-react": ">=0.400.0",
49
+ "react-dropzone": "^14.4.1"
50
+ },
46
51
  "peerDependencies": {
47
52
  "@dnd-kit/core": "^6.0.0",
48
53
  "@dnd-kit/sortable": "^8.0.0 || ^10.0.0",
49
54
  "@tanstack/react-query": "^5.0.0",
50
55
  "@tanstack/react-table": "^8.0.0",
51
- "framer-motion": "^11.18.2",
52
- "lucide-react": ">=0.400.0",
53
56
  "react": "^19.0.0",
54
57
  "react-dom": "^19.0.0",
55
- "react-dropzone": "^14.4.1",
56
58
  "recharts": "^3.0.0"
57
59
  },
58
60
  "peerDependenciesMeta": {