Spaces:
Runtime error
Runtime error
block painting on ongoing frames
Browse files
frontend/src/lib/App.svelte
CHANGED
@@ -5,7 +5,7 @@
|
|
5 |
import PaintCanvas from '$lib/PaintCanvas.svelte';
|
6 |
import Menu from '$lib/Menu.svelte';
|
7 |
import PromptModal from '$lib/PromptModal.svelte';
|
8 |
-
import { COLORS } from '$lib/constants';
|
9 |
import { PUBLIC_WS_INPAINTING } from '$env/static/public';
|
10 |
import type { PromptImgKey } from '$lib/types';
|
11 |
import { Status } from '$lib/types';
|
@@ -21,39 +21,72 @@
|
|
21 |
|
22 |
const myPresence = useMyPresence({ addToHistory: true });
|
23 |
const others = useOthers();
|
24 |
-
|
25 |
function getKey(position: { x: number; y: number }): PromptImgKey {
|
26 |
return `${position.x}_${position.y}`;
|
27 |
}
|
28 |
|
29 |
const promptImgStorage = useObject('promptImgStorage');
|
30 |
|
31 |
-
let showModal = false;
|
32 |
-
|
33 |
$: isLoading = $myPresence?.status === Status.loading || $isRenderingCanvas || false;
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
myPresence.update({
|
39 |
status: Status.prompting
|
40 |
});
|
|
|
|
|
|
|
|
|
41 |
}
|
42 |
}
|
43 |
-
function onClose() {
|
44 |
-
showModal = false;
|
45 |
-
}
|
46 |
|
47 |
function onPaint() {
|
48 |
-
generateImage();
|
49 |
showModal = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
50 |
}
|
51 |
|
|
|
|
|
|
|
|
|
|
|
52 |
async function generateImage() {
|
53 |
if (isLoading) return;
|
54 |
-
$loadingState = 'Pending';
|
55 |
const prompt = $myPresence.currentPrompt;
|
56 |
const position = $myPresence.frame;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
57 |
const imageKey = getKey(position);
|
58 |
const room = $selectedRoomID || 'default';
|
59 |
console.log('Generating...', prompt, position);
|
@@ -137,9 +170,7 @@
|
|
137 |
$promptImgStorage.set(imageKey, promptImgParams);
|
138 |
console.log(params.image.url);
|
139 |
$loadingState = data.success ? 'Complete' : 'Error';
|
140 |
-
|
141 |
-
$loadingState = '';
|
142 |
-
}, 2000);
|
143 |
myPresence.update({
|
144 |
status: Status.ready,
|
145 |
currentPrompt: ''
|
@@ -150,9 +181,7 @@
|
|
150 |
myPresence.update({
|
151 |
status: Status.ready
|
152 |
});
|
153 |
-
|
154 |
-
$loadingState = '';
|
155 |
-
}, 10000);
|
156 |
}
|
157 |
websocket.close();
|
158 |
return;
|
@@ -173,14 +202,16 @@
|
|
173 |
{$loadingState}
|
174 |
</div>
|
175 |
{#if showModal}
|
176 |
-
<PromptModal
|
|
|
|
|
|
|
|
|
177 |
{/if}
|
178 |
<div class="fixed top-0 left-0 z-0 w-screen h-screen min-h-[600px]">
|
179 |
<PaintCanvas />
|
180 |
|
181 |
<main class="z-10 relative">
|
182 |
-
<PaintFrame on:prompt={onPrompt} transform={$currZoomTransform} />
|
183 |
-
|
184 |
<!-- When others connected, iterate through others and show their cursors -->
|
185 |
{#if $others}
|
186 |
{#each [...$others] as { connectionId, presence } (connectionId)}
|
@@ -201,10 +232,11 @@
|
|
201 |
{/if}
|
202 |
{/each}
|
203 |
{/if}
|
|
|
204 |
</main>
|
205 |
</div>
|
206 |
<div class="fixed bottom-0 md:bottom-16 left-0 right-0 z-10 my-2">
|
207 |
-
<Menu on:
|
208 |
</div>
|
209 |
|
210 |
<style lang="postcss" scoped>
|
|
|
5 |
import PaintCanvas from '$lib/PaintCanvas.svelte';
|
6 |
import Menu from '$lib/Menu.svelte';
|
7 |
import PromptModal from '$lib/PromptModal.svelte';
|
8 |
+
import { COLORS, FRAME_SIZE } from '$lib/constants';
|
9 |
import { PUBLIC_WS_INPAINTING } from '$env/static/public';
|
10 |
import type { PromptImgKey } from '$lib/types';
|
11 |
import { Status } from '$lib/types';
|
|
|
21 |
|
22 |
const myPresence = useMyPresence({ addToHistory: true });
|
23 |
const others = useOthers();
|
24 |
+
let showModal = false;
|
25 |
function getKey(position: { x: number; y: number }): PromptImgKey {
|
26 |
return `${position.x}_${position.y}`;
|
27 |
}
|
28 |
|
29 |
const promptImgStorage = useObject('promptImgStorage');
|
30 |
|
|
|
|
|
31 |
$: isLoading = $myPresence?.status === Status.loading || $isRenderingCanvas || false;
|
32 |
+
function onShowModal(e: CustomEvent) {
|
33 |
+
if (isLoading) return;
|
34 |
+
showModal = e.detail.showModal;
|
35 |
+
if (showModal) {
|
36 |
myPresence.update({
|
37 |
status: Status.prompting
|
38 |
});
|
39 |
+
} else {
|
40 |
+
myPresence.update({
|
41 |
+
status: Status.ready
|
42 |
+
});
|
43 |
}
|
44 |
}
|
|
|
|
|
|
|
45 |
|
46 |
function onPaint() {
|
|
|
47 |
showModal = false;
|
48 |
+
generateImage();
|
49 |
+
}
|
50 |
+
function canPaint(position: { x: number; y: number }): boolean {
|
51 |
+
if (!$others) return true;
|
52 |
+
console.log('P', position);
|
53 |
+
let canPaint = true;
|
54 |
+
for (const { presence } of $others) {
|
55 |
+
if (
|
56 |
+
position.x < presence.frame.x + FRAME_SIZE &&
|
57 |
+
position.x + FRAME_SIZE > presence.frame.x &&
|
58 |
+
position.y < presence.frame.y + FRAME_SIZE &&
|
59 |
+
position.y + FRAME_SIZE > presence.frame.y
|
60 |
+
) {
|
61 |
+
// can paint if presence is only dragging
|
62 |
+
if (presence.status === Status.ready || presence.status === Status.dragging) {
|
63 |
+
canPaint = true;
|
64 |
+
}
|
65 |
+
canPaint = false;
|
66 |
+
break;
|
67 |
+
}
|
68 |
+
}
|
69 |
+
return canPaint;
|
70 |
}
|
71 |
|
72 |
+
function clearStateMsg(t = 5000) {
|
73 |
+
setTimeout(() => {
|
74 |
+
$loadingState = '';
|
75 |
+
}, t);
|
76 |
+
}
|
77 |
async function generateImage() {
|
78 |
if (isLoading) return;
|
|
|
79 |
const prompt = $myPresence.currentPrompt;
|
80 |
const position = $myPresence.frame;
|
81 |
+
$loadingState = 'Pending';
|
82 |
+
if (!canPaint(position)) {
|
83 |
+
$loadingState = 'Someone is already painting here';
|
84 |
+
myPresence.update({
|
85 |
+
status: Status.ready
|
86 |
+
});
|
87 |
+
clearStateMsg();
|
88 |
+
return;
|
89 |
+
}
|
90 |
const imageKey = getKey(position);
|
91 |
const room = $selectedRoomID || 'default';
|
92 |
console.log('Generating...', prompt, position);
|
|
|
170 |
$promptImgStorage.set(imageKey, promptImgParams);
|
171 |
console.log(params.image.url);
|
172 |
$loadingState = data.success ? 'Complete' : 'Error';
|
173 |
+
clearStateMsg();
|
|
|
|
|
174 |
myPresence.update({
|
175 |
status: Status.ready,
|
176 |
currentPrompt: ''
|
|
|
181 |
myPresence.update({
|
182 |
status: Status.ready
|
183 |
});
|
184 |
+
clearStateMsg(10000);
|
|
|
|
|
185 |
}
|
186 |
websocket.close();
|
187 |
return;
|
|
|
202 |
{$loadingState}
|
203 |
</div>
|
204 |
{#if showModal}
|
205 |
+
<PromptModal
|
206 |
+
on:paint={onPaint}
|
207 |
+
initPrompt={$myPresence?.currentPrompt}
|
208 |
+
on:showModal={onShowModal}
|
209 |
+
/>
|
210 |
{/if}
|
211 |
<div class="fixed top-0 left-0 z-0 w-screen h-screen min-h-[600px]">
|
212 |
<PaintCanvas />
|
213 |
|
214 |
<main class="z-10 relative">
|
|
|
|
|
215 |
<!-- When others connected, iterate through others and show their cursors -->
|
216 |
{#if $others}
|
217 |
{#each [...$others] as { connectionId, presence } (connectionId)}
|
|
|
232 |
{/if}
|
233 |
{/each}
|
234 |
{/if}
|
235 |
+
<PaintFrame transform={$currZoomTransform} on:showModal={onShowModal} />
|
236 |
</main>
|
237 |
</div>
|
238 |
<div class="fixed bottom-0 md:bottom-16 left-0 right-0 z-10 my-2">
|
239 |
+
<Menu {isLoading} on:showModal={onShowModal} />
|
240 |
</div>
|
241 |
|
242 |
<style lang="postcss" scoped>
|
frontend/src/lib/Menu.svelte
CHANGED
@@ -1,17 +1,21 @@
|
|
1 |
<script lang="ts">
|
2 |
-
import { createEventDispatcher } from 'svelte';
|
3 |
import RoomsSelector from '$lib/Buttons/RoomsSelector.svelte';
|
4 |
import AboutButton from '$lib/Buttons/AboutButton.svelte';
|
5 |
import { toggleAbout } from '$lib/store';
|
6 |
-
|
7 |
|
8 |
const dispatch = createEventDispatcher();
|
|
|
9 |
|
10 |
export let isLoading = false;
|
11 |
</script>
|
12 |
|
13 |
<svelte:window
|
14 |
-
on:
|
|
|
|
|
|
|
|
|
15 |
/>
|
16 |
<div class="flex flex-col md:flex-row items-center justify-between px-4 md:px-12 gap-3 md:gap-0">
|
17 |
<AboutButton
|
@@ -21,7 +25,7 @@
|
|
21 |
/>
|
22 |
|
23 |
<button
|
24 |
-
on:click={() => dispatch('
|
25 |
title="Click to prompt, and paint. The generated image will show up in the frame."
|
26 |
disabled={isLoading}
|
27 |
class="{isLoading
|
@@ -48,7 +52,5 @@
|
|
48 |
Enter</span
|
49 |
></button
|
50 |
>
|
51 |
-
|
52 |
<RoomsSelector {isLoading} />
|
53 |
-
<!-- <PPButton {isLoading} on:click={() => dispatch('prompt')} /> -->
|
54 |
</div>
|
|
|
1 |
<script lang="ts">
|
|
|
2 |
import RoomsSelector from '$lib/Buttons/RoomsSelector.svelte';
|
3 |
import AboutButton from '$lib/Buttons/AboutButton.svelte';
|
4 |
import { toggleAbout } from '$lib/store';
|
5 |
+
import { createEventDispatcher } from 'svelte';
|
6 |
|
7 |
const dispatch = createEventDispatcher();
|
8 |
+
// const broadcast = useBroadcastEvent();
|
9 |
|
10 |
export let isLoading = false;
|
11 |
</script>
|
12 |
|
13 |
<svelte:window
|
14 |
+
on:keypress={(e) => {
|
15 |
+
if (e.key === 'Enter') {
|
16 |
+
dispatch('showModal', { showModal: true });
|
17 |
+
}
|
18 |
+
}}
|
19 |
/>
|
20 |
<div class="flex flex-col md:flex-row items-center justify-between px-4 md:px-12 gap-3 md:gap-0">
|
21 |
<AboutButton
|
|
|
25 |
/>
|
26 |
|
27 |
<button
|
28 |
+
on:click={() => dispatch('showModal', { showModal: true })}
|
29 |
title="Click to prompt, and paint. The generated image will show up in the frame."
|
30 |
disabled={isLoading}
|
31 |
class="{isLoading
|
|
|
52 |
Enter</span
|
53 |
></button
|
54 |
>
|
|
|
55 |
<RoomsSelector {isLoading} />
|
|
|
56 |
</div>
|
frontend/src/lib/PaintFrame.svelte
CHANGED
@@ -7,10 +7,11 @@
|
|
7 |
import { round } from '$lib/utils';
|
8 |
|
9 |
import type { ZoomTransform } from 'd3-zoom';
|
10 |
-
import { onMount
|
11 |
|
12 |
import { useMyPresence } from '$lib/liveblocks';
|
13 |
import { canvasEl, maskEl, loadingState, isRenderingCanvas } from '$lib/store';
|
|
|
14 |
|
15 |
import { Status } from './types';
|
16 |
const myPresence = useMyPresence({ addToHistory: true });
|
@@ -274,7 +275,7 @@
|
|
274 |
<div class="mx-4 flex flex-col gap-2">
|
275 |
<button
|
276 |
title="Click to prompt and paint"
|
277 |
-
on:click={() => dispatch('
|
278 |
class="w-10 h-10 bg-blue-600 hover:saturate-150 shadow-2xl shadow-blue-500 rounded-lg flex items-center justify-center text-3xl"
|
279 |
>
|
280 |
π
|
|
|
7 |
import { round } from '$lib/utils';
|
8 |
|
9 |
import type { ZoomTransform } from 'd3-zoom';
|
10 |
+
import { onMount } from 'svelte';
|
11 |
|
12 |
import { useMyPresence } from '$lib/liveblocks';
|
13 |
import { canvasEl, maskEl, loadingState, isRenderingCanvas } from '$lib/store';
|
14 |
+
import { createEventDispatcher } from 'svelte';
|
15 |
|
16 |
import { Status } from './types';
|
17 |
const myPresence = useMyPresence({ addToHistory: true });
|
|
|
275 |
<div class="mx-4 flex flex-col gap-2">
|
276 |
<button
|
277 |
title="Click to prompt and paint"
|
278 |
+
on:click={() => dispatch('showModal', { showModal: true })}
|
279 |
class="w-10 h-10 bg-blue-600 hover:saturate-150 shadow-2xl shadow-blue-500 rounded-lg flex items-center justify-center text-3xl"
|
280 |
>
|
281 |
π
|
frontend/src/lib/PromptModal.svelte
CHANGED
@@ -4,18 +4,18 @@
|
|
4 |
import { Status } from '$lib/types';
|
5 |
|
6 |
const dispatch = createEventDispatcher();
|
|
|
7 |
export let initPrompt = '';
|
|
|
8 |
let prompt: string;
|
9 |
let inputEl: HTMLInputElement;
|
10 |
let boxEl: HTMLDivElement;
|
11 |
-
const myPresence =
|
12 |
|
13 |
const onKeyup = (e: KeyboardEvent) => {
|
14 |
if (e.key === 'Escape') {
|
15 |
-
|
16 |
-
|
17 |
-
});
|
18 |
-
dispatch('close');
|
19 |
}
|
20 |
};
|
21 |
|
@@ -41,10 +41,13 @@
|
|
41 |
});
|
42 |
}, 100);
|
43 |
}
|
44 |
-
function onPrompt() {
|
|
|
|
|
|
|
|
|
45 |
if (prompt.trim() !== '') {
|
46 |
dispatch('paint');
|
47 |
-
dispatch('close');
|
48 |
}
|
49 |
}
|
50 |
function onInput(event: Event) {
|
@@ -58,13 +61,13 @@
|
|
58 |
myPresence.update({
|
59 |
status: Status.ready
|
60 |
});
|
61 |
-
dispatch('
|
62 |
}
|
63 |
</script>
|
64 |
|
65 |
<form
|
66 |
class="fixed w-screen top-0 left-0 bottom-0 right-0 h-screen z-50 flex items-center justify-center bg-black bg-opacity-80"
|
67 |
-
on:submit
|
68 |
>
|
69 |
<div
|
70 |
class="flex bg-white overflow-hidden rounded-2xl w-full max-w-lg 2xl:max-w-xl"
|
|
|
4 |
import { Status } from '$lib/types';
|
5 |
|
6 |
const dispatch = createEventDispatcher();
|
7 |
+
|
8 |
export let initPrompt = '';
|
9 |
+
|
10 |
let prompt: string;
|
11 |
let inputEl: HTMLInputElement;
|
12 |
let boxEl: HTMLDivElement;
|
13 |
+
const myPresence = useMyPresence({ addToHistory: true });
|
14 |
|
15 |
const onKeyup = (e: KeyboardEvent) => {
|
16 |
if (e.key === 'Escape') {
|
17 |
+
dispatch('showModal', { showModal: false });
|
18 |
+
console.log('Escape');
|
|
|
|
|
19 |
}
|
20 |
};
|
21 |
|
|
|
41 |
});
|
42 |
}, 100);
|
43 |
}
|
44 |
+
function onPrompt(event: Event) {
|
45 |
+
event.stopPropagation();
|
46 |
+
event.preventDefault();
|
47 |
+
event.stopImmediatePropagation();
|
48 |
+
|
49 |
if (prompt.trim() !== '') {
|
50 |
dispatch('paint');
|
|
|
51 |
}
|
52 |
}
|
53 |
function onInput(event: Event) {
|
|
|
61 |
myPresence.update({
|
62 |
status: Status.ready
|
63 |
});
|
64 |
+
dispatch('showModal', { showModal: false });
|
65 |
}
|
66 |
</script>
|
67 |
|
68 |
<form
|
69 |
class="fixed w-screen top-0 left-0 bottom-0 right-0 h-screen z-50 flex items-center justify-center bg-black bg-opacity-80"
|
70 |
+
on:submit={onPrompt}
|
71 |
>
|
72 |
<div
|
73 |
class="flex bg-white overflow-hidden rounded-2xl w-full max-w-lg 2xl:max-w-xl"
|
frontend/src/lib/liveblocks/useRooms.ts
CHANGED
@@ -3,7 +3,7 @@ import { writable, type Writable } from "svelte/store";
|
|
3 |
import type { RoomResponse } from '$lib/types';
|
4 |
import { PUBLIC_API_BASE } from '$env/static/public';
|
5 |
|
6 |
-
const INTERVAL =
|
7 |
|
8 |
export function useRooms(): Writable<RoomResponse[]> {
|
9 |
const roomsStorage = writable<RoomResponse[]>([]);
|
|
|
3 |
import type { RoomResponse } from '$lib/types';
|
4 |
import { PUBLIC_API_BASE } from '$env/static/public';
|
5 |
|
6 |
+
const INTERVAL = 10000
|
7 |
|
8 |
export function useRooms(): Writable<RoomResponse[]> {
|
9 |
const roomsStorage = writable<RoomResponse[]>([]);
|
frontend/src/lib/store.ts
CHANGED
@@ -7,4 +7,5 @@ export const canvasEl = writable<HTMLCanvasElement>();
|
|
7 |
export const maskEl = writable<HTMLCanvasElement>();
|
8 |
export const selectedRoomID = writable<string | null>();
|
9 |
export const toggleAbout = writable<boolean>(false);
|
10 |
-
export const isRenderingCanvas = writable<boolean>(true);
|
|
|
|
7 |
export const maskEl = writable<HTMLCanvasElement>();
|
8 |
export const selectedRoomID = writable<string | null>();
|
9 |
export const toggleAbout = writable<boolean>(false);
|
10 |
+
export const isRenderingCanvas = writable<boolean>(true);
|
11 |
+
export const showModal = writable<boolean>(false);
|
frontend/src/lib/types.ts
CHANGED
@@ -15,7 +15,7 @@ export type Presence = {
|
|
15 |
frame: {
|
16 |
x: number;
|
17 |
y: number;
|
18 |
-
}
|
19 |
status: Status;
|
20 |
currentPrompt: string
|
21 |
}
|
|
|
15 |
frame: {
|
16 |
x: number;
|
17 |
y: number;
|
18 |
+
};
|
19 |
status: Status;
|
20 |
currentPrompt: string
|
21 |
}
|