@gradio/model3d 0.15.1 → 0.16.1

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/CHANGELOG.md CHANGED
@@ -1,4 +1,36 @@
1
1
  # @gradio/model3d
2
+
3
+ ## 0.16.1
4
+
5
+ ### Fixes
6
+
7
+ - [#12688](https://github.com/gradio-app/gradio/pull/12688) [`5206d47`](https://github.com/gradio-app/gradio/commit/5206d47b7730e6ffc8efd90d8157dd5388c18dc5) - Migrate Model3D to Svelte 5. Thanks @dawoodkhan82!
8
+
9
+ ### Dependency updates
10
+
11
+ - @gradio/utils@0.11.1
12
+ - @gradio/client@2.0.2
13
+
14
+ ## 0.16.0
15
+
16
+ ### Features
17
+
18
+ - [#12539](https://github.com/gradio-app/gradio/pull/12539) [`f1d83fa`](https://github.com/gradio-app/gradio/commit/f1d83fac3d6e4bad60cf896a026fa2d572f26073) - Add ability to add custom buttons to components. Thanks @abidlabs!
19
+
20
+ ### Dependency updates
21
+
22
+ - @gradio/atoms@0.20.0
23
+ - @gradio/utils@0.11.0
24
+ - @gradio/client@2.0.1
25
+ - @gradio/statustracker@0.12.1
26
+ - @gradio/upload@0.17.3
27
+
28
+ ## 0.15.1
29
+
30
+ ### Dependency updates
31
+
32
+ - @gradio/utils@0.10.4
33
+
2
34
  ## 0.15.1
3
35
 
4
36
  ### Features
package/Index.svelte CHANGED
@@ -11,7 +11,13 @@
11
11
  import { Gradio } from "@gradio/utils";
12
12
  import Model3D from "./shared/Model3D.svelte";
13
13
  import Model3DUpload from "./shared/Model3DUpload.svelte";
14
- import { BlockLabel, Block, Empty, UploadText } from "@gradio/atoms";
14
+ import {
15
+ BlockLabel,
16
+ Block,
17
+ Empty,
18
+ UploadText,
19
+ IconButtonWrapper
20
+ } from "@gradio/atoms";
15
21
  import { File } from "@gradio/icons";
16
22
  import { StatusTracker } from "@gradio/statustracker";
17
23
 
@@ -106,6 +112,14 @@
106
112
  {has_change_history}
107
113
  />
108
114
  {:else}
115
+ {#if gradio.shared.show_label && gradio.props.buttons && gradio.props.buttons.length > 0}
116
+ <IconButtonWrapper
117
+ buttons={gradio.props.buttons}
118
+ on_custom_button_click={(id) => {
119
+ gradio.dispatch("custom_button_click", { id });
120
+ }}
121
+ />
122
+ {/if}
109
123
  <BlockLabel
110
124
  show_label={gradio.shared.show_label}
111
125
  Icon={File}
@@ -143,15 +157,15 @@
143
157
  root={gradio.shared.root}
144
158
  display_mode={gradio.props.display_mode}
145
159
  clear_color={gradio.props.clear_color}
146
- value={gradio.props.value}
160
+ bind:value={gradio.props.value}
147
161
  camera_position={gradio.props.camera_position}
148
162
  zoom_speed={gradio.props.zoom_speed}
149
163
  bind:uploading
150
- on:change={({ detail }) => handle_change(detail)}
151
- on:drag={({ detail }) => handle_drag(detail)}
152
- on:clear={handle_clear}
153
- on:load={({ detail }) => handle_load(detail)}
154
- on:error={({ detail }) => handle_error(detail)}
164
+ onchange={handle_change}
165
+ ondrag={handle_drag}
166
+ onclear={handle_clear}
167
+ onload={handle_load}
168
+ onerror={handle_error}
155
169
  i18n={gradio.i18n}
156
170
  max_file_size={gradio.shared.max_file_size}
157
171
  upload={(...args) => gradio.shared.client.upload(...args)}
package/dist/Index.svelte CHANGED
@@ -11,7 +11,13 @@
11
11
  import { Gradio } from "@gradio/utils";
12
12
  import Model3D from "./shared/Model3D.svelte";
13
13
  import Model3DUpload from "./shared/Model3DUpload.svelte";
14
- import { BlockLabel, Block, Empty, UploadText } from "@gradio/atoms";
14
+ import {
15
+ BlockLabel,
16
+ Block,
17
+ Empty,
18
+ UploadText,
19
+ IconButtonWrapper
20
+ } from "@gradio/atoms";
15
21
  import { File } from "@gradio/icons";
16
22
  import { StatusTracker } from "@gradio/statustracker";
17
23
 
@@ -106,6 +112,14 @@
106
112
  {has_change_history}
107
113
  />
108
114
  {:else}
115
+ {#if gradio.shared.show_label && gradio.props.buttons && gradio.props.buttons.length > 0}
116
+ <IconButtonWrapper
117
+ buttons={gradio.props.buttons}
118
+ on_custom_button_click={(id) => {
119
+ gradio.dispatch("custom_button_click", { id });
120
+ }}
121
+ />
122
+ {/if}
109
123
  <BlockLabel
110
124
  show_label={gradio.shared.show_label}
111
125
  Icon={File}
@@ -143,15 +157,15 @@
143
157
  root={gradio.shared.root}
144
158
  display_mode={gradio.props.display_mode}
145
159
  clear_color={gradio.props.clear_color}
146
- value={gradio.props.value}
160
+ bind:value={gradio.props.value}
147
161
  camera_position={gradio.props.camera_position}
148
162
  zoom_speed={gradio.props.zoom_speed}
149
163
  bind:uploading
150
- on:change={({ detail }) => handle_change(detail)}
151
- on:drag={({ detail }) => handle_drag(detail)}
152
- on:clear={handle_clear}
153
- on:load={({ detail }) => handle_load(detail)}
154
- on:error={({ detail }) => handle_error(detail)}
164
+ onchange={handle_change}
165
+ ondrag={handle_drag}
166
+ onclear={handle_clear}
167
+ onload={handle_load}
168
+ onerror={handle_error}
155
169
  i18n={gradio.i18n}
156
170
  max_file_size={gradio.shared.max_file_size}
157
171
  upload={(...args) => gradio.shared.client.upload(...args)}
@@ -5,19 +5,28 @@
5
5
 
6
6
  let BABYLON_VIEWER: typeof import("@babylonjs/viewer");
7
7
 
8
- export let value: FileData;
9
- export let display_mode: "solid" | "point_cloud" | "wireframe";
10
- export let clear_color: [number, number, number, number];
11
- export let camera_position: [number | null, number | null, number | null];
12
- export let zoom_speed: number;
13
- export let pan_speed: number;
8
+ let {
9
+ value,
10
+ display_mode,
11
+ clear_color,
12
+ camera_position,
13
+ zoom_speed,
14
+ pan_speed
15
+ }: {
16
+ value: FileData;
17
+ display_mode: "solid" | "point_cloud" | "wireframe";
18
+ clear_color: [number, number, number, number];
19
+ camera_position: [number | null, number | null, number | null];
20
+ zoom_speed: number;
21
+ pan_speed: number;
22
+ } = $props();
14
23
 
15
- $: url = value.url;
24
+ let url = $derived(value.url);
16
25
 
17
26
  let canvas: HTMLCanvasElement;
18
- let viewer: Viewer;
19
- let viewerDetails: Readonly<ViewerDetails>;
20
- let mounted = false;
27
+ let viewer = $state<Viewer>();
28
+ let viewerDetails = $state<Readonly<ViewerDetails>>();
29
+ let mounted = $state(false);
21
30
 
22
31
  onMount(() => {
23
32
  const initViewer = async (): Promise<void> => {
@@ -43,9 +52,14 @@
43
52
  };
44
53
  });
45
54
 
46
- $: mounted && load_model(url);
55
+ $effect(() => {
56
+ if (mounted) {
57
+ load_model(url);
58
+ }
59
+ });
47
60
 
48
61
  function setRenderingMode(pointsCloud: boolean, wireframe: boolean): void {
62
+ if (!viewerDetails) return;
49
63
  viewerDetails.scene.forcePointsCloud = pointsCloud;
50
64
  viewerDetails.scene.forceWireframe = wireframe;
51
65
  }
@@ -81,6 +95,7 @@
81
95
  zoom_speed: number,
82
96
  pan_speed: number
83
97
  ): void {
98
+ if (!viewerDetails) return;
84
99
  const camera = viewerDetails.camera;
85
100
  if (camera_position[0] !== null) {
86
101
  camera.alpha = (camera_position[0] * Math.PI) / 180;
@@ -101,7 +116,7 @@
101
116
  }
102
117
 
103
118
  export function reset_camera_position(): void {
104
- if (viewerDetails) {
119
+ if (viewerDetails && viewer) {
105
120
  viewer.resetCamera();
106
121
  }
107
122
  }
@@ -1,31 +1,15 @@
1
1
  import type { FileData } from "@gradio/client";
2
- interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
- new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
- $$bindings?: Bindings;
5
- } & Exports;
6
- (internal: unknown, props: Props & {
7
- $$events?: Events;
8
- $$slots?: Slots;
9
- }): Exports & {
10
- $set?: any;
11
- $on?: any;
12
- };
13
- z_$$bindings?: Bindings;
14
- }
15
- declare const Canvas3D: $$__sveltets_2_IsomorphicComponent<{
2
+ type $$ComponentProps = {
16
3
  value: FileData;
17
4
  display_mode: "solid" | "point_cloud" | "wireframe";
18
5
  clear_color: [number, number, number, number];
19
6
  camera_position: [number | null, number | null, number | null];
20
7
  zoom_speed: number;
21
8
  pan_speed: number;
22
- update_camera?: (camera_position: [number | null, number | null, number | null], zoom_speed: number, pan_speed: number) => void;
23
- reset_camera_position?: () => void;
24
- }, {
25
- [evt: string]: CustomEvent<any>;
26
- }, {}, {
9
+ };
10
+ declare const Canvas3D: import("svelte").Component<$$ComponentProps, {
27
11
  update_camera: (camera_position: [number | null, number | null, number | null], zoom_speed: number, pan_speed: number) => void;
28
12
  reset_camera_position: () => void;
29
- }, string>;
30
- type Canvas3D = InstanceType<typeof Canvas3D>;
13
+ }, "">;
14
+ type Canvas3D = ReturnType<typeof Canvas3D>;
31
15
  export default Canvas3D;
@@ -3,19 +3,25 @@
3
3
  import * as SPLAT from "gsplat";
4
4
  import type { FileData } from "@gradio/client";
5
5
 
6
- export let value: FileData;
7
- export let zoom_speed: number;
8
- export let pan_speed: number;
9
-
10
- $: url = value.url;
6
+ let {
7
+ value,
8
+ zoom_speed,
9
+ pan_speed
10
+ }: {
11
+ value: FileData;
12
+ zoom_speed: number;
13
+ pan_speed: number;
14
+ } = $props();
15
+
16
+ let url = $derived(value.url);
11
17
 
12
18
  let canvas: HTMLCanvasElement;
13
19
  let scene: SPLAT.Scene;
14
20
  let camera: SPLAT.Camera;
15
- let renderer: SPLAT.WebGLRenderer | null = null;
21
+ let renderer = $state<SPLAT.WebGLRenderer | null>(null);
16
22
  let controls: SPLAT.OrbitControls;
17
- let mounted = false;
18
- let frameId: number | null = null;
23
+ let mounted = $state(false);
24
+ let frameId = $state<number | null>(null);
19
25
 
20
26
  function reset_scene(): void {
21
27
  if (frameId !== null) {
@@ -92,11 +98,13 @@
92
98
  };
93
99
  });
94
100
 
95
- $: ({ path } = value || {
96
- path: undefined
97
- });
101
+ let path = $derived(value?.path);
98
102
 
99
- $: canvas && mounted && path && reset_scene();
103
+ $effect(() => {
104
+ if (canvas && mounted && path) {
105
+ reset_scene();
106
+ }
107
+ });
100
108
  </script>
101
109
 
102
110
  <canvas bind:this={canvas}></canvas>
@@ -1,23 +1,9 @@
1
1
  import type { FileData } from "@gradio/client";
2
- interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
- new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
- $$bindings?: Bindings;
5
- } & Exports;
6
- (internal: unknown, props: Props & {
7
- $$events?: Events;
8
- $$slots?: Slots;
9
- }): Exports & {
10
- $set?: any;
11
- $on?: any;
12
- };
13
- z_$$bindings?: Bindings;
14
- }
15
- declare const Canvas3DGS: $$__sveltets_2_IsomorphicComponent<{
2
+ type $$ComponentProps = {
16
3
  value: FileData;
17
4
  zoom_speed: number;
18
5
  pan_speed: number;
19
- }, {
20
- [evt: string]: CustomEvent<any>;
21
- }, {}, {}, string>;
22
- type Canvas3DGS = InstanceType<typeof Canvas3DGS>;
6
+ };
7
+ declare const Canvas3DGS: import("svelte").Component<$$ComponentProps, {}, "">;
8
+ type Canvas3DGS = ReturnType<typeof Canvas3DGS>;
23
9
  export default Canvas3DGS;
@@ -7,27 +7,36 @@
7
7
  import type Canvas3DGS from "./Canvas3DGS.svelte";
8
8
  import type Canvas3D from "./Canvas3D.svelte";
9
9
 
10
- export let value: FileData | null;
11
- export let display_mode: "solid" | "point_cloud" | "wireframe" = "solid";
12
- export let clear_color: [number, number, number, number] = [0, 0, 0, 0];
13
- export let label = "";
14
- export let show_label: boolean;
15
- export let i18n: I18nFormatter;
16
- export let zoom_speed = 1;
17
- export let pan_speed = 1;
18
- // alpha, beta, radius
19
- export let camera_position: [number | null, number | null, number | null] = [
20
- null,
21
- null,
22
- null
23
- ];
24
- export let has_change_history = false;
10
+ let {
11
+ value,
12
+ display_mode = "solid",
13
+ clear_color = [0, 0, 0, 0],
14
+ label = "",
15
+ show_label,
16
+ i18n,
17
+ zoom_speed = 1,
18
+ pan_speed = 1,
19
+ camera_position = [null, null, null],
20
+ has_change_history = false
21
+ }: {
22
+ value: FileData | null;
23
+ display_mode?: "solid" | "point_cloud" | "wireframe";
24
+ clear_color?: [number, number, number, number];
25
+ label?: string;
26
+ show_label: boolean;
27
+ i18n: I18nFormatter;
28
+ zoom_speed?: number;
29
+ pan_speed?: number;
30
+ camera_position?: [number | null, number | null, number | null];
31
+ has_change_history?: boolean;
32
+ } = $props();
25
33
 
26
- let current_settings = { camera_position, zoom_speed, pan_speed };
34
+ let current_settings = $state({ camera_position, zoom_speed, pan_speed });
35
+ let use_3dgs = $state(false);
36
+ let Canvas3DGSComponent = $state<typeof Canvas3DGS>();
37
+ let Canvas3DComponent = $state<typeof Canvas3D>();
38
+ let canvas3d = $state<Canvas3D | undefined>();
27
39
 
28
- let use_3dgs = false;
29
- let Canvas3DGSComponent: typeof Canvas3DGS;
30
- let Canvas3DComponent: typeof Canvas3D;
31
40
  async function loadCanvas3D(): Promise<typeof Canvas3D> {
32
41
  const module = await import("./Canvas3D.svelte");
33
42
  return module.default;
@@ -36,25 +45,27 @@
36
45
  const module = await import("./Canvas3DGS.svelte");
37
46
  return module.default;
38
47
  }
39
- $: if (value) {
40
- use_3dgs = value.path.endsWith(".splat") || value.path.endsWith(".ply");
41
- if (use_3dgs) {
42
- loadCanvas3DGS().then((component) => {
43
- Canvas3DGSComponent = component;
44
- });
45
- } else {
46
- loadCanvas3D().then((component) => {
47
- Canvas3DComponent = component;
48
- });
48
+
49
+ $effect(() => {
50
+ if (value) {
51
+ use_3dgs = value.path.endsWith(".splat") || value.path.endsWith(".ply");
52
+ if (use_3dgs) {
53
+ loadCanvas3DGS().then((component) => {
54
+ Canvas3DGSComponent = component;
55
+ });
56
+ } else {
57
+ loadCanvas3D().then((component) => {
58
+ Canvas3DComponent = component;
59
+ });
60
+ }
49
61
  }
50
- }
62
+ });
51
63
 
52
- let canvas3d: Canvas3D | undefined;
53
64
  function handle_undo(): void {
54
65
  canvas3d?.reset_camera_position();
55
66
  }
56
67
 
57
- $: {
68
+ $effect(() => {
58
69
  if (
59
70
  !dequal(current_settings.camera_position, camera_position) ||
60
71
  current_settings.zoom_speed !== zoom_speed ||
@@ -63,7 +74,7 @@
63
74
  canvas3d?.update_camera(camera_position, zoom_speed, pan_speed);
64
75
  current_settings = { camera_position, zoom_speed, pan_speed };
65
76
  }
66
- }
77
+ });
67
78
  </script>
68
79
 
69
80
  <BlockLabel
@@ -79,7 +90,7 @@
79
90
  <IconButton
80
91
  Icon={Undo}
81
92
  label="Undo"
82
- on:click={() => handle_undo()}
93
+ onclick={() => handle_undo()}
83
94
  disabled={!has_change_history}
84
95
  />
85
96
  {/if}
@@ -1,19 +1,6 @@
1
1
  import type { FileData } from "@gradio/client";
2
2
  import type { I18nFormatter } from "@gradio/utils";
3
- interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
4
- new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
5
- $$bindings?: Bindings;
6
- } & Exports;
7
- (internal: unknown, props: Props & {
8
- $$events?: Events;
9
- $$slots?: Slots;
10
- }): Exports & {
11
- $set?: any;
12
- $on?: any;
13
- };
14
- z_$$bindings?: Bindings;
15
- }
16
- declare const Model3D: $$__sveltets_2_IsomorphicComponent<{
3
+ type $$ComponentProps = {
17
4
  value: FileData | null;
18
5
  display_mode?: "solid" | "point_cloud" | "wireframe";
19
6
  clear_color?: [number, number, number, number];
@@ -24,8 +11,7 @@ declare const Model3D: $$__sveltets_2_IsomorphicComponent<{
24
11
  pan_speed?: number;
25
12
  camera_position?: [number | null, number | null, number | null];
26
13
  has_change_history?: boolean;
27
- }, {
28
- [evt: string]: CustomEvent<any>;
29
- }, {}, {}, string>;
30
- type Model3D = InstanceType<typeof Model3D>;
14
+ };
15
+ declare const Model3D: import("svelte").Component<$$ComponentProps, {}, "">;
16
+ type Model3D = ReturnType<typeof Model3D>;
31
17
  export default Model3D;
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { createEventDispatcher, tick } from "svelte";
2
+ import { tick } from "svelte";
3
3
  import { Upload, ModifyUpload } from "@gradio/upload";
4
4
  import type { FileData, Client } from "@gradio/client";
5
5
  import { BlockLabel } from "@gradio/atoms";
@@ -8,83 +8,107 @@
8
8
  import type Canvas3DGS from "./Canvas3DGS.svelte";
9
9
  import type Canvas3D from "./Canvas3D.svelte";
10
10
 
11
- export let value: null | FileData;
12
- export let display_mode: "solid" | "point_cloud" | "wireframe" = "solid";
13
- export let clear_color: [number, number, number, number] = [0, 0, 0, 0];
14
- export let label = "";
15
- export let show_label: boolean;
16
- export let root: string;
17
- export let i18n: I18nFormatter;
18
- export let zoom_speed = 1;
19
- export let pan_speed = 1;
20
- export let max_file_size: number | null = null;
21
- export let uploading = false;
22
- export let upload_promise: Promise<(FileData | null)[]> | null = null;
11
+ let {
12
+ value = $bindable(),
13
+ display_mode = "solid",
14
+ clear_color = [0, 0, 0, 0],
15
+ label = "",
16
+ show_label,
17
+ root,
18
+ i18n,
19
+ zoom_speed = 1,
20
+ pan_speed = 1,
21
+ max_file_size = null,
22
+ uploading = $bindable(),
23
+ upload_promise = $bindable(),
24
+ camera_position = [null, null, null],
25
+ upload,
26
+ stream_handler,
27
+ onchange,
28
+ onclear,
29
+ ondrag,
30
+ onload,
31
+ onerror
32
+ }: {
33
+ value?: FileData | null;
34
+ display_mode?: "solid" | "point_cloud" | "wireframe";
35
+ clear_color?: [number, number, number, number];
36
+ label?: string;
37
+ show_label: boolean;
38
+ root: string;
39
+ i18n: I18nFormatter;
40
+ zoom_speed?: number;
41
+ pan_speed?: number;
42
+ max_file_size?: number | null;
43
+ uploading?: boolean;
44
+ upload_promise?: Promise<(FileData | null)[]> | null;
45
+ camera_position?: [number | null, number | null, number | null];
46
+ upload: Client["upload"];
47
+ stream_handler: Client["stream"];
48
+ onchange?: (value: FileData | null) => void;
49
+ onclear?: () => void;
50
+ ondrag?: (dragging: boolean) => void;
51
+ onload?: (value: FileData) => void;
52
+ onerror?: (error: string) => void;
53
+ } = $props();
23
54
 
24
- // alpha, beta, radius
25
- export let camera_position: [number | null, number | null, number | null] = [
26
- null,
27
- null,
28
- null
29
- ];
30
- export let upload: Client["upload"];
31
- export let stream_handler: Client["stream"];
55
+ let use_3dgs = $state(false);
56
+ let Canvas3DGSComponent = $state<typeof Canvas3DGS>();
57
+ let Canvas3DComponent = $state<typeof Canvas3D>();
58
+ let canvas3d = $state<Canvas3D | undefined>();
59
+ let dragging = $state(false);
60
+
61
+ async function loadCanvas3D(): Promise<typeof Canvas3D> {
62
+ const module = await import("./Canvas3D.svelte");
63
+ return module.default;
64
+ }
65
+ async function loadCanvas3DGS(): Promise<typeof Canvas3DGS> {
66
+ const module = await import("./Canvas3DGS.svelte");
67
+ return module.default;
68
+ }
69
+
70
+ $effect(() => {
71
+ if (value) {
72
+ use_3dgs = value.path.endsWith(".splat") || value.path.endsWith(".ply");
73
+ if (use_3dgs) {
74
+ loadCanvas3DGS().then((component) => {
75
+ Canvas3DGSComponent = component;
76
+ });
77
+ } else {
78
+ loadCanvas3D().then((component) => {
79
+ Canvas3DComponent = component;
80
+ });
81
+ }
82
+ }
83
+ });
84
+
85
+ $effect(() => {
86
+ ondrag?.(dragging);
87
+ });
32
88
 
33
89
  async function handle_upload({
34
90
  detail
35
91
  }: CustomEvent<FileData>): Promise<void> {
36
92
  value = detail;
37
93
  await tick();
38
- dispatch("change", value);
39
- dispatch("load", value);
94
+ onchange?.(value);
95
+ onload?.(value as FileData);
40
96
  }
41
97
 
42
98
  async function handle_clear(): Promise<void> {
43
99
  value = null;
44
100
  await tick();
45
- dispatch("clear");
46
- dispatch("change");
47
- }
48
-
49
- let use_3dgs = false;
50
- let Canvas3DGSComponent: typeof Canvas3DGS;
51
- let Canvas3DComponent: typeof Canvas3D;
52
- async function loadCanvas3D(): Promise<typeof Canvas3D> {
53
- const module = await import("./Canvas3D.svelte");
54
- return module.default;
55
- }
56
- async function loadCanvas3DGS(): Promise<typeof Canvas3DGS> {
57
- const module = await import("./Canvas3DGS.svelte");
58
- return module.default;
59
- }
60
- $: if (value) {
61
- use_3dgs = value.path.endsWith(".splat") || value.path.endsWith(".ply");
62
- if (use_3dgs) {
63
- loadCanvas3DGS().then((component) => {
64
- Canvas3DGSComponent = component;
65
- });
66
- } else {
67
- loadCanvas3D().then((component) => {
68
- Canvas3DComponent = component;
69
- });
70
- }
101
+ onclear?.();
102
+ onchange?.(null);
71
103
  }
72
104
 
73
- let canvas3d: Canvas3D | undefined;
74
105
  async function handle_undo(): Promise<void> {
75
106
  canvas3d?.reset_camera_position();
76
107
  }
77
108
 
78
- const dispatch = createEventDispatcher<{
79
- change: FileData | null;
80
- clear: undefined;
81
- drag: boolean;
82
- load: FileData;
83
- }>();
84
-
85
- let dragging = false;
86
-
87
- $: dispatch("drag", dragging);
109
+ function handle_error({ detail }: CustomEvent<string>): void {
110
+ onerror?.(detail);
111
+ }
88
112
  </script>
89
113
 
90
114
  <BlockLabel {show_label} Icon={File} label={label || "3D Model"} />
@@ -100,7 +124,7 @@
100
124
  filetype={[".stl", ".obj", ".gltf", ".glb", "model/obj", ".splat", ".ply"]}
101
125
  bind:dragging
102
126
  bind:uploading
103
- on:error
127
+ on:error={handle_error}
104
128
  aria_label={i18n("model3d.drop_to_upload")}
105
129
  >
106
130
  <slot />
@@ -1,5 +1,27 @@
1
1
  import type { FileData, Client } from "@gradio/client";
2
2
  import type { I18nFormatter } from "@gradio/utils";
3
+ type $$ComponentProps = {
4
+ value?: FileData | null;
5
+ display_mode?: "solid" | "point_cloud" | "wireframe";
6
+ clear_color?: [number, number, number, number];
7
+ label?: string;
8
+ show_label: boolean;
9
+ root: string;
10
+ i18n: I18nFormatter;
11
+ zoom_speed?: number;
12
+ pan_speed?: number;
13
+ max_file_size?: number | null;
14
+ uploading?: boolean;
15
+ upload_promise?: Promise<(FileData | null)[]> | null;
16
+ camera_position?: [number | null, number | null, number | null];
17
+ upload: Client["upload"];
18
+ stream_handler: Client["stream"];
19
+ onchange?: (value: FileData | null) => void;
20
+ onclear?: () => void;
21
+ ondrag?: (dragging: boolean) => void;
22
+ onload?: (value: FileData) => void;
23
+ onerror?: (error: string) => void;
24
+ };
3
25
  interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
4
26
  new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
5
27
  $$bindings?: Bindings;
@@ -18,34 +40,12 @@ type $$__sveltets_2_PropsWithChildren<Props, Slots> = Props & (Slots extends {
18
40
  } ? Props extends Record<string, never> ? any : {
19
41
  children?: any;
20
42
  } : {});
21
- declare const Model3DUpload: $$__sveltets_2_IsomorphicComponent<$$__sveltets_2_PropsWithChildren<{
22
- value: null | FileData;
23
- display_mode?: "solid" | "point_cloud" | "wireframe";
24
- clear_color?: [number, number, number, number];
25
- label?: string;
26
- show_label: boolean;
27
- root: string;
28
- i18n: I18nFormatter;
29
- zoom_speed?: number;
30
- pan_speed?: number;
31
- max_file_size?: number | null;
32
- uploading?: boolean;
33
- upload_promise?: Promise<(FileData | null)[]> | null;
34
- camera_position?: [number | null, number | null, number | null];
35
- upload: Client["upload"];
36
- stream_handler: Client["stream"];
37
- }, {
43
+ declare const Model3DUpload: $$__sveltets_2_IsomorphicComponent<$$__sveltets_2_PropsWithChildren<$$ComponentProps, {
38
44
  default: {};
39
45
  }>, {
40
- error: CustomEvent<any>;
41
- change: CustomEvent<FileData | null>;
42
- clear: CustomEvent<undefined>;
43
- drag: CustomEvent<boolean>;
44
- load: CustomEvent<FileData>;
45
- } & {
46
46
  [evt: string]: CustomEvent<any>;
47
47
  }, {
48
48
  default: {};
49
- }, {}, string>;
49
+ }, {}, "value" | "uploading" | "upload_promise">;
50
50
  type Model3DUpload = InstanceType<typeof Model3DUpload>;
51
51
  export default Model3DUpload;
package/dist/types.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { FileData } from "@gradio/client";
2
2
  import type { LoadingStatus } from "js/statustracker";
3
+ import type { CustomButton } from "@gradio/utils";
3
4
  export interface Model3DProps {
4
5
  value: null | FileData;
5
6
  display_mode: "solid" | "point_cloud" | "wireframe";
@@ -8,6 +9,7 @@ export interface Model3DProps {
8
9
  zoom_speed: number;
9
10
  has_change_history: boolean;
10
11
  camera_position: [number | null, number | null, number | null];
12
+ buttons: (string | CustomButton)[] | null;
11
13
  }
12
14
  export interface Model3DEvents {
13
15
  change: FileData | null;
@@ -16,4 +18,7 @@ export interface Model3DEvents {
16
18
  clear: never;
17
19
  clear_status: LoadingStatus;
18
20
  error: string;
21
+ custom_button_click: {
22
+ id: number;
23
+ };
19
24
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gradio/model3d",
3
- "version": "0.15.1",
3
+ "version": "0.16.1",
4
4
  "description": "Gradio UI packages",
5
5
  "type": "module",
6
6
  "author": "",
@@ -13,15 +13,15 @@
13
13
  "@types/babylon": "^6.16.9",
14
14
  "dequal": "^2.0.3",
15
15
  "gsplat": "^1.2.9",
16
- "@gradio/atoms": "^0.19.0",
17
- "@gradio/client": "^2.0.0",
18
- "@gradio/statustracker": "^0.12.0",
16
+ "@gradio/atoms": "^0.20.0",
17
+ "@gradio/client": "^2.0.2",
18
+ "@gradio/statustracker": "^0.12.1",
19
19
  "@gradio/icons": "^0.15.0",
20
- "@gradio/upload": "^0.17.2",
21
- "@gradio/utils": "^0.10.3"
20
+ "@gradio/upload": "^0.17.3",
21
+ "@gradio/utils": "^0.11.1"
22
22
  },
23
23
  "devDependencies": {
24
- "@gradio/preview": "^0.15.0"
24
+ "@gradio/preview": "^0.15.1"
25
25
  },
26
26
  "main_changeset": true,
27
27
  "main": "./Index.svelte",
@@ -5,19 +5,28 @@
5
5
 
6
6
  let BABYLON_VIEWER: typeof import("@babylonjs/viewer");
7
7
 
8
- export let value: FileData;
9
- export let display_mode: "solid" | "point_cloud" | "wireframe";
10
- export let clear_color: [number, number, number, number];
11
- export let camera_position: [number | null, number | null, number | null];
12
- export let zoom_speed: number;
13
- export let pan_speed: number;
8
+ let {
9
+ value,
10
+ display_mode,
11
+ clear_color,
12
+ camera_position,
13
+ zoom_speed,
14
+ pan_speed
15
+ }: {
16
+ value: FileData;
17
+ display_mode: "solid" | "point_cloud" | "wireframe";
18
+ clear_color: [number, number, number, number];
19
+ camera_position: [number | null, number | null, number | null];
20
+ zoom_speed: number;
21
+ pan_speed: number;
22
+ } = $props();
14
23
 
15
- $: url = value.url;
24
+ let url = $derived(value.url);
16
25
 
17
26
  let canvas: HTMLCanvasElement;
18
- let viewer: Viewer;
19
- let viewerDetails: Readonly<ViewerDetails>;
20
- let mounted = false;
27
+ let viewer = $state<Viewer>();
28
+ let viewerDetails = $state<Readonly<ViewerDetails>>();
29
+ let mounted = $state(false);
21
30
 
22
31
  onMount(() => {
23
32
  const initViewer = async (): Promise<void> => {
@@ -43,9 +52,14 @@
43
52
  };
44
53
  });
45
54
 
46
- $: mounted && load_model(url);
55
+ $effect(() => {
56
+ if (mounted) {
57
+ load_model(url);
58
+ }
59
+ });
47
60
 
48
61
  function setRenderingMode(pointsCloud: boolean, wireframe: boolean): void {
62
+ if (!viewerDetails) return;
49
63
  viewerDetails.scene.forcePointsCloud = pointsCloud;
50
64
  viewerDetails.scene.forceWireframe = wireframe;
51
65
  }
@@ -81,6 +95,7 @@
81
95
  zoom_speed: number,
82
96
  pan_speed: number
83
97
  ): void {
98
+ if (!viewerDetails) return;
84
99
  const camera = viewerDetails.camera;
85
100
  if (camera_position[0] !== null) {
86
101
  camera.alpha = (camera_position[0] * Math.PI) / 180;
@@ -101,7 +116,7 @@
101
116
  }
102
117
 
103
118
  export function reset_camera_position(): void {
104
- if (viewerDetails) {
119
+ if (viewerDetails && viewer) {
105
120
  viewer.resetCamera();
106
121
  }
107
122
  }
@@ -3,19 +3,25 @@
3
3
  import * as SPLAT from "gsplat";
4
4
  import type { FileData } from "@gradio/client";
5
5
 
6
- export let value: FileData;
7
- export let zoom_speed: number;
8
- export let pan_speed: number;
9
-
10
- $: url = value.url;
6
+ let {
7
+ value,
8
+ zoom_speed,
9
+ pan_speed
10
+ }: {
11
+ value: FileData;
12
+ zoom_speed: number;
13
+ pan_speed: number;
14
+ } = $props();
15
+
16
+ let url = $derived(value.url);
11
17
 
12
18
  let canvas: HTMLCanvasElement;
13
19
  let scene: SPLAT.Scene;
14
20
  let camera: SPLAT.Camera;
15
- let renderer: SPLAT.WebGLRenderer | null = null;
21
+ let renderer = $state<SPLAT.WebGLRenderer | null>(null);
16
22
  let controls: SPLAT.OrbitControls;
17
- let mounted = false;
18
- let frameId: number | null = null;
23
+ let mounted = $state(false);
24
+ let frameId = $state<number | null>(null);
19
25
 
20
26
  function reset_scene(): void {
21
27
  if (frameId !== null) {
@@ -92,11 +98,13 @@
92
98
  };
93
99
  });
94
100
 
95
- $: ({ path } = value || {
96
- path: undefined
97
- });
101
+ let path = $derived(value?.path);
98
102
 
99
- $: canvas && mounted && path && reset_scene();
103
+ $effect(() => {
104
+ if (canvas && mounted && path) {
105
+ reset_scene();
106
+ }
107
+ });
100
108
  </script>
101
109
 
102
110
  <canvas bind:this={canvas}></canvas>
@@ -7,27 +7,36 @@
7
7
  import type Canvas3DGS from "./Canvas3DGS.svelte";
8
8
  import type Canvas3D from "./Canvas3D.svelte";
9
9
 
10
- export let value: FileData | null;
11
- export let display_mode: "solid" | "point_cloud" | "wireframe" = "solid";
12
- export let clear_color: [number, number, number, number] = [0, 0, 0, 0];
13
- export let label = "";
14
- export let show_label: boolean;
15
- export let i18n: I18nFormatter;
16
- export let zoom_speed = 1;
17
- export let pan_speed = 1;
18
- // alpha, beta, radius
19
- export let camera_position: [number | null, number | null, number | null] = [
20
- null,
21
- null,
22
- null
23
- ];
24
- export let has_change_history = false;
10
+ let {
11
+ value,
12
+ display_mode = "solid",
13
+ clear_color = [0, 0, 0, 0],
14
+ label = "",
15
+ show_label,
16
+ i18n,
17
+ zoom_speed = 1,
18
+ pan_speed = 1,
19
+ camera_position = [null, null, null],
20
+ has_change_history = false
21
+ }: {
22
+ value: FileData | null;
23
+ display_mode?: "solid" | "point_cloud" | "wireframe";
24
+ clear_color?: [number, number, number, number];
25
+ label?: string;
26
+ show_label: boolean;
27
+ i18n: I18nFormatter;
28
+ zoom_speed?: number;
29
+ pan_speed?: number;
30
+ camera_position?: [number | null, number | null, number | null];
31
+ has_change_history?: boolean;
32
+ } = $props();
25
33
 
26
- let current_settings = { camera_position, zoom_speed, pan_speed };
34
+ let current_settings = $state({ camera_position, zoom_speed, pan_speed });
35
+ let use_3dgs = $state(false);
36
+ let Canvas3DGSComponent = $state<typeof Canvas3DGS>();
37
+ let Canvas3DComponent = $state<typeof Canvas3D>();
38
+ let canvas3d = $state<Canvas3D | undefined>();
27
39
 
28
- let use_3dgs = false;
29
- let Canvas3DGSComponent: typeof Canvas3DGS;
30
- let Canvas3DComponent: typeof Canvas3D;
31
40
  async function loadCanvas3D(): Promise<typeof Canvas3D> {
32
41
  const module = await import("./Canvas3D.svelte");
33
42
  return module.default;
@@ -36,25 +45,27 @@
36
45
  const module = await import("./Canvas3DGS.svelte");
37
46
  return module.default;
38
47
  }
39
- $: if (value) {
40
- use_3dgs = value.path.endsWith(".splat") || value.path.endsWith(".ply");
41
- if (use_3dgs) {
42
- loadCanvas3DGS().then((component) => {
43
- Canvas3DGSComponent = component;
44
- });
45
- } else {
46
- loadCanvas3D().then((component) => {
47
- Canvas3DComponent = component;
48
- });
48
+
49
+ $effect(() => {
50
+ if (value) {
51
+ use_3dgs = value.path.endsWith(".splat") || value.path.endsWith(".ply");
52
+ if (use_3dgs) {
53
+ loadCanvas3DGS().then((component) => {
54
+ Canvas3DGSComponent = component;
55
+ });
56
+ } else {
57
+ loadCanvas3D().then((component) => {
58
+ Canvas3DComponent = component;
59
+ });
60
+ }
49
61
  }
50
- }
62
+ });
51
63
 
52
- let canvas3d: Canvas3D | undefined;
53
64
  function handle_undo(): void {
54
65
  canvas3d?.reset_camera_position();
55
66
  }
56
67
 
57
- $: {
68
+ $effect(() => {
58
69
  if (
59
70
  !dequal(current_settings.camera_position, camera_position) ||
60
71
  current_settings.zoom_speed !== zoom_speed ||
@@ -63,7 +74,7 @@
63
74
  canvas3d?.update_camera(camera_position, zoom_speed, pan_speed);
64
75
  current_settings = { camera_position, zoom_speed, pan_speed };
65
76
  }
66
- }
77
+ });
67
78
  </script>
68
79
 
69
80
  <BlockLabel
@@ -79,7 +90,7 @@
79
90
  <IconButton
80
91
  Icon={Undo}
81
92
  label="Undo"
82
- on:click={() => handle_undo()}
93
+ onclick={() => handle_undo()}
83
94
  disabled={!has_change_history}
84
95
  />
85
96
  {/if}
@@ -1,5 +1,5 @@
1
1
  <script lang="ts">
2
- import { createEventDispatcher, tick } from "svelte";
2
+ import { tick } from "svelte";
3
3
  import { Upload, ModifyUpload } from "@gradio/upload";
4
4
  import type { FileData, Client } from "@gradio/client";
5
5
  import { BlockLabel } from "@gradio/atoms";
@@ -8,83 +8,107 @@
8
8
  import type Canvas3DGS from "./Canvas3DGS.svelte";
9
9
  import type Canvas3D from "./Canvas3D.svelte";
10
10
 
11
- export let value: null | FileData;
12
- export let display_mode: "solid" | "point_cloud" | "wireframe" = "solid";
13
- export let clear_color: [number, number, number, number] = [0, 0, 0, 0];
14
- export let label = "";
15
- export let show_label: boolean;
16
- export let root: string;
17
- export let i18n: I18nFormatter;
18
- export let zoom_speed = 1;
19
- export let pan_speed = 1;
20
- export let max_file_size: number | null = null;
21
- export let uploading = false;
22
- export let upload_promise: Promise<(FileData | null)[]> | null = null;
11
+ let {
12
+ value = $bindable(),
13
+ display_mode = "solid",
14
+ clear_color = [0, 0, 0, 0],
15
+ label = "",
16
+ show_label,
17
+ root,
18
+ i18n,
19
+ zoom_speed = 1,
20
+ pan_speed = 1,
21
+ max_file_size = null,
22
+ uploading = $bindable(),
23
+ upload_promise = $bindable(),
24
+ camera_position = [null, null, null],
25
+ upload,
26
+ stream_handler,
27
+ onchange,
28
+ onclear,
29
+ ondrag,
30
+ onload,
31
+ onerror
32
+ }: {
33
+ value?: FileData | null;
34
+ display_mode?: "solid" | "point_cloud" | "wireframe";
35
+ clear_color?: [number, number, number, number];
36
+ label?: string;
37
+ show_label: boolean;
38
+ root: string;
39
+ i18n: I18nFormatter;
40
+ zoom_speed?: number;
41
+ pan_speed?: number;
42
+ max_file_size?: number | null;
43
+ uploading?: boolean;
44
+ upload_promise?: Promise<(FileData | null)[]> | null;
45
+ camera_position?: [number | null, number | null, number | null];
46
+ upload: Client["upload"];
47
+ stream_handler: Client["stream"];
48
+ onchange?: (value: FileData | null) => void;
49
+ onclear?: () => void;
50
+ ondrag?: (dragging: boolean) => void;
51
+ onload?: (value: FileData) => void;
52
+ onerror?: (error: string) => void;
53
+ } = $props();
23
54
 
24
- // alpha, beta, radius
25
- export let camera_position: [number | null, number | null, number | null] = [
26
- null,
27
- null,
28
- null
29
- ];
30
- export let upload: Client["upload"];
31
- export let stream_handler: Client["stream"];
55
+ let use_3dgs = $state(false);
56
+ let Canvas3DGSComponent = $state<typeof Canvas3DGS>();
57
+ let Canvas3DComponent = $state<typeof Canvas3D>();
58
+ let canvas3d = $state<Canvas3D | undefined>();
59
+ let dragging = $state(false);
60
+
61
+ async function loadCanvas3D(): Promise<typeof Canvas3D> {
62
+ const module = await import("./Canvas3D.svelte");
63
+ return module.default;
64
+ }
65
+ async function loadCanvas3DGS(): Promise<typeof Canvas3DGS> {
66
+ const module = await import("./Canvas3DGS.svelte");
67
+ return module.default;
68
+ }
69
+
70
+ $effect(() => {
71
+ if (value) {
72
+ use_3dgs = value.path.endsWith(".splat") || value.path.endsWith(".ply");
73
+ if (use_3dgs) {
74
+ loadCanvas3DGS().then((component) => {
75
+ Canvas3DGSComponent = component;
76
+ });
77
+ } else {
78
+ loadCanvas3D().then((component) => {
79
+ Canvas3DComponent = component;
80
+ });
81
+ }
82
+ }
83
+ });
84
+
85
+ $effect(() => {
86
+ ondrag?.(dragging);
87
+ });
32
88
 
33
89
  async function handle_upload({
34
90
  detail
35
91
  }: CustomEvent<FileData>): Promise<void> {
36
92
  value = detail;
37
93
  await tick();
38
- dispatch("change", value);
39
- dispatch("load", value);
94
+ onchange?.(value);
95
+ onload?.(value as FileData);
40
96
  }
41
97
 
42
98
  async function handle_clear(): Promise<void> {
43
99
  value = null;
44
100
  await tick();
45
- dispatch("clear");
46
- dispatch("change");
47
- }
48
-
49
- let use_3dgs = false;
50
- let Canvas3DGSComponent: typeof Canvas3DGS;
51
- let Canvas3DComponent: typeof Canvas3D;
52
- async function loadCanvas3D(): Promise<typeof Canvas3D> {
53
- const module = await import("./Canvas3D.svelte");
54
- return module.default;
55
- }
56
- async function loadCanvas3DGS(): Promise<typeof Canvas3DGS> {
57
- const module = await import("./Canvas3DGS.svelte");
58
- return module.default;
59
- }
60
- $: if (value) {
61
- use_3dgs = value.path.endsWith(".splat") || value.path.endsWith(".ply");
62
- if (use_3dgs) {
63
- loadCanvas3DGS().then((component) => {
64
- Canvas3DGSComponent = component;
65
- });
66
- } else {
67
- loadCanvas3D().then((component) => {
68
- Canvas3DComponent = component;
69
- });
70
- }
101
+ onclear?.();
102
+ onchange?.(null);
71
103
  }
72
104
 
73
- let canvas3d: Canvas3D | undefined;
74
105
  async function handle_undo(): Promise<void> {
75
106
  canvas3d?.reset_camera_position();
76
107
  }
77
108
 
78
- const dispatch = createEventDispatcher<{
79
- change: FileData | null;
80
- clear: undefined;
81
- drag: boolean;
82
- load: FileData;
83
- }>();
84
-
85
- let dragging = false;
86
-
87
- $: dispatch("drag", dragging);
109
+ function handle_error({ detail }: CustomEvent<string>): void {
110
+ onerror?.(detail);
111
+ }
88
112
  </script>
89
113
 
90
114
  <BlockLabel {show_label} Icon={File} label={label || "3D Model"} />
@@ -100,7 +124,7 @@
100
124
  filetype={[".stl", ".obj", ".gltf", ".glb", "model/obj", ".splat", ".ply"]}
101
125
  bind:dragging
102
126
  bind:uploading
103
- on:error
127
+ on:error={handle_error}
104
128
  aria_label={i18n("model3d.drop_to_upload")}
105
129
  >
106
130
  <slot />
package/types.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { FileData } from "@gradio/client";
2
2
  import type { LoadingStatus } from "js/statustracker";
3
+ import type { CustomButton } from "@gradio/utils";
3
4
 
4
5
  export interface Model3DProps {
5
6
  value: null | FileData;
@@ -9,6 +10,7 @@ export interface Model3DProps {
9
10
  zoom_speed: number;
10
11
  has_change_history: boolean;
11
12
  camera_position: [number | null, number | null, number | null];
13
+ buttons: (string | CustomButton)[] | null;
12
14
  }
13
15
 
14
16
  export interface Model3DEvents {
@@ -18,4 +20,5 @@ export interface Model3DEvents {
18
20
  clear: never;
19
21
  clear_status: LoadingStatus;
20
22
  error: string;
23
+ custom_button_click: { id: number };
21
24
  }