radames commited on
Commit
b4efffa
1 Parent(s): d26cd8c

click to pain

Browse files
.vscode/extensions.json CHANGED
@@ -2,5 +2,5 @@
2
  "recommendations": [
3
  "bradlc.vscode-tailwindcss",
4
  "svelte.svelte-vscode"
5
- ]
6
  }
 
2
  "recommendations": [
3
  "bradlc.vscode-tailwindcss",
4
  "svelte.svelte-vscode"
5
+ ],
6
  }
.vscode/settings.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ {
2
+ "terminal.integrated.defaultProfile.linux": "zsh"
3
+ }
frontend/src/lib/App.svelte CHANGED
@@ -8,14 +8,7 @@
8
  import { COLORS, EMOJIS } from '$lib/constants';
9
  import { PUBLIC_WS_INPAINTING } from '$env/static/public';
10
  import type { PromptImgObject, PromptImgKey, Presence } from '$lib/types';
11
- import {
12
- isLoading,
13
- loadingState,
14
- currZoomTransform,
15
- isPrompting,
16
- clickedPosition,
17
- showFrames
18
- } from '$lib/store';
19
 
20
  import { useMyPresence, useObject, useOthers } from '$lib/liveblocks';
21
 
@@ -34,6 +27,7 @@
34
  // Set a default value for presence
35
  const initialPresence: Presence = {
36
  cursor: null,
 
37
  isPrompting: false,
38
  isLoading: false,
39
  isMoving: true,
@@ -54,25 +48,29 @@
54
  }
55
  return [];
56
  }
 
57
  let promptImgList: PromptImgObject[] = [];
58
  $: promptImgList = getpromptImgList($promptImgStorage?.toObject());
59
 
 
 
60
  let canvasEl: HTMLCanvasElement;
61
 
62
  function onPaintMode(e: CustomEvent) {
63
- console.log('onPaintMode', e.detail);
64
- // const mode = e.detail.mode;
 
 
65
  }
66
- async function onClose(e: CustomEvent) {
67
- console.log('onClose', e.detail);
68
- $isPrompting = false;
69
  }
70
- async function onPrompt(e: CustomEvent) {
71
- const prompt = e.detail.prompt;
72
- const imgURLs = await generateImage(prompt);
73
- $isPrompting = false;
74
- console.log('prompt', prompt, imgURLs);
75
  }
 
76
  function getImageCrop(cursor: { x: number; y: number }) {
77
  const canvasCrop = document.createElement('canvas');
78
 
@@ -92,19 +90,20 @@
92
 
93
  return base64Crop;
94
  }
95
- async function generateImage(_prompt: string) {
96
- if (!_prompt || $isLoading == true) return;
97
  $loadingState = 'Pending';
98
- $isLoading = true;
 
 
99
  myPresence.update({
100
- currentPrompt: _prompt,
101
  isPrompting: true,
102
- cursor: $clickedPosition
103
  });
104
  const sessionHash = crypto.randomUUID();
105
  const payload = {
106
  fn_index: 0,
107
- data: [getImageCrop($clickedPosition), _prompt, 0.75, 7.5, 30, 'patchmatch'],
108
  session_hash: sessionHash
109
  };
110
  console.log('payload', payload);
@@ -116,7 +115,10 @@
116
  websocket.onclose = (evt) => {
117
  if (!evt.wasClean) {
118
  $loadingState = 'Error';
119
- $isLoading = false;
 
 
 
120
  }
121
  };
122
  websocket.onmessage = async function (event) {
@@ -131,7 +133,9 @@
131
  case 'queue_full':
132
  $loadingState = 'Queue full';
133
  websocket.close();
134
- $isLoading = false;
 
 
135
  return;
136
  case 'estimation':
137
  const { rank, queue_size } = data;
@@ -148,11 +152,11 @@
148
  throw new Error('Potential NFSW content, please try again');
149
  }
150
  const imgBlob = await base64ToBlob(imgBase64);
151
- const imgURL = await uploadImage(imgBlob, _prompt);
152
  const promptImg = {
153
- prompt: _prompt,
154
  imgURL: imgURL,
155
- position: $clickedPosition,
156
  date: new Date().getTime(),
157
  id: nanoid()
158
  };
@@ -165,7 +169,9 @@
165
  $loadingState = tError?.message;
166
  }
167
  websocket.close();
168
- $isLoading = false;
 
 
169
  return;
170
  case 'process_starts':
171
  $loadingState = 'Processing';
@@ -173,7 +179,6 @@
173
  }
174
  } catch (e) {
175
  console.error(e);
176
- $isLoading = false;
177
  $loadingState = 'Error';
178
  }
179
  };
@@ -183,9 +188,8 @@
183
  <!-- Show the current user's cursor location -->
184
  <div class="text touch-none pointer-events-none">
185
  {$loadingState}
186
- {$isLoading}
187
  </div>
188
- {#if $isPrompting}
189
  <PromptModal on:prompt={onPrompt} on:close={onClose} />
190
  {/if}
191
  <div class="fixed top-0 left-0 z-0 w-screen h-screen">
@@ -204,18 +208,6 @@
204
  />
205
  {/each}
206
  {/if}
207
- <!-- {#if $clickedPosition}
208
- <Frame color={COLORS[0]} position={$clickedPosition} transform={$currZoomTransform} />
209
- {/if} -->
210
- {#if $myPresence?.cursor}
211
- <!-- <Frame color={COLORS[0]} position={$myPresence.cursor} transform={$currZoomTransform} />
212
- <Cursor
213
- color={COLORS[0]}
214
- position={$myPresence.cursor}
215
- transform={$currZoomTransform}
216
- /> -->
217
- {/if}
218
-
219
  <!-- When others connected, iterate through others and show their cursors -->
220
  {#if $others}
221
  {#each [...$others] as { connectionId, presence } (connectionId)}
 
8
  import { COLORS, EMOJIS } from '$lib/constants';
9
  import { PUBLIC_WS_INPAINTING } from '$env/static/public';
10
  import type { PromptImgObject, PromptImgKey, Presence } from '$lib/types';
11
+ import { loadingState, currZoomTransform, showFrames } from '$lib/store';
 
 
 
 
 
 
 
12
 
13
  import { useMyPresence, useObject, useOthers } from '$lib/liveblocks';
14
 
 
27
  // Set a default value for presence
28
  const initialPresence: Presence = {
29
  cursor: null,
30
+ frame: null,
31
  isPrompting: false,
32
  isLoading: false,
33
  isMoving: true,
 
48
  }
49
  return [];
50
  }
51
+ let showModal = false;
52
  let promptImgList: PromptImgObject[] = [];
53
  $: promptImgList = getpromptImgList($promptImgStorage?.toObject());
54
 
55
+ $: isPrompting = $myPresence?.isPrompting || false;
56
+
57
  let canvasEl: HTMLCanvasElement;
58
 
59
  function onPaintMode(e: CustomEvent) {
60
+ const mode = e.detail.mode;
61
+ if (mode == 'paint') {
62
+ showModal = true;
63
+ }
64
  }
65
+ function onClose() {
66
+ showModal = false;
 
67
  }
68
+
69
+ function onPrompt(e: CustomEvent) {
70
+ generateImage();
71
+ showModal = false;
 
72
  }
73
+
74
  function getImageCrop(cursor: { x: number; y: number }) {
75
  const canvasCrop = document.createElement('canvas');
76
 
 
90
 
91
  return base64Crop;
92
  }
93
+ async function generateImage() {
94
+ // if (isPrompting) return;
95
  $loadingState = 'Pending';
96
+ const prompt = $myPresence.currentPrompt;
97
+ const position = $myPresence.frame;
98
+ console.log('Generating...', prompt, position);
99
  myPresence.update({
 
100
  isPrompting: true,
101
+ isLoading: true
102
  });
103
  const sessionHash = crypto.randomUUID();
104
  const payload = {
105
  fn_index: 0,
106
+ data: [getImageCrop(position), prompt, 0.75, 7.5, 30, 'patchmatch'],
107
  session_hash: sessionHash
108
  };
109
  console.log('payload', payload);
 
115
  websocket.onclose = (evt) => {
116
  if (!evt.wasClean) {
117
  $loadingState = 'Error';
118
+ myPresence.update({
119
+ isPrompting: false,
120
+ isLoading: false
121
+ });
122
  }
123
  };
124
  websocket.onmessage = async function (event) {
 
133
  case 'queue_full':
134
  $loadingState = 'Queue full';
135
  websocket.close();
136
+ myPresence.update({
137
+ isPrompting: false
138
+ });
139
  return;
140
  case 'estimation':
141
  const { rank, queue_size } = data;
 
152
  throw new Error('Potential NFSW content, please try again');
153
  }
154
  const imgBlob = await base64ToBlob(imgBase64);
155
+ const imgURL = await uploadImage(imgBlob, prompt);
156
  const promptImg = {
157
+ prompt,
158
  imgURL: imgURL,
159
+ position,
160
  date: new Date().getTime(),
161
  id: nanoid()
162
  };
 
169
  $loadingState = tError?.message;
170
  }
171
  websocket.close();
172
+ myPresence.update({
173
+ isPrompting: false
174
+ });
175
  return;
176
  case 'process_starts':
177
  $loadingState = 'Processing';
 
179
  }
180
  } catch (e) {
181
  console.error(e);
 
182
  $loadingState = 'Error';
183
  }
184
  };
 
188
  <!-- Show the current user's cursor location -->
189
  <div class="text touch-none pointer-events-none">
190
  {$loadingState}
 
191
  </div>
192
+ {#if showModal}
193
  <PromptModal on:prompt={onPrompt} on:close={onClose} />
194
  {/if}
195
  <div class="fixed top-0 left-0 z-0 w-screen h-screen">
 
208
  />
209
  {/each}
210
  {/if}
 
 
 
 
 
 
 
 
 
 
 
 
211
  <!-- When others connected, iterate through others and show their cursors -->
212
  {#if $others}
213
  {#each [...$others] as { connectionId, presence } (connectionId)}
frontend/src/lib/Canvas.svelte CHANGED
@@ -3,7 +3,7 @@
3
  import { select } from 'd3-selection';
4
  import { onMount } from 'svelte';
5
  import { PUBLIC_UPLOADS } from '$env/static/public';
6
- import { currZoomTransform, isPrompting, clickedPosition } from '$lib/store';
7
  import { round } from '$lib/utils';
8
 
9
  import { useMyPresence, useObject } from '$lib/liveblocks';
@@ -39,28 +39,28 @@
39
  }
40
 
41
  onMount(() => {
 
 
 
 
 
 
42
  const scale = width / containerEl.clientWidth;
43
  const zoomHandler = zoom()
44
- .scaleExtent([1 / scale / 2, 1])
45
  // .extent([
46
  // [0, 0],
47
  // [width, height]
48
  // ])
49
  .translateExtent([
50
- [-width * 0.3, -height * 0.3],
51
- [width * 1.3, height * 1.3]
52
  ])
53
  .tapDistance(10)
54
  .on('zoom', zoomed);
55
 
56
  const selection = select(canvasEl.parentElement)
57
  .call(zoomHandler as any)
58
- .on('dblclick.zoom', () => {
59
- $isPrompting = true;
60
- $clickedPosition = $myPresence.cursor;
61
- console.log('clicked', $clickedPosition);
62
- return null;
63
- })
64
  .call(zoomHandler.scaleTo as any, 1 / scale / 1.5)
65
  .on('pointermove', handlePointerMove)
66
  .on('pointerleave', handlePointerLeave);
 
3
  import { select } from 'd3-selection';
4
  import { onMount } from 'svelte';
5
  import { PUBLIC_UPLOADS } from '$env/static/public';
6
+ import { currZoomTransform } from '$lib/store';
7
  import { round } from '$lib/utils';
8
 
9
  import { useMyPresence, useObject } from '$lib/liveblocks';
 
39
  }
40
 
41
  onMount(() => {
42
+ const margin = {
43
+ top: 0,
44
+ right: 0,
45
+ bottom: 0,
46
+ left: 0
47
+ };
48
  const scale = width / containerEl.clientWidth;
49
  const zoomHandler = zoom()
50
+ .scaleExtent([1 / scale, 1])
51
  // .extent([
52
  // [0, 0],
53
  // [width, height]
54
  // ])
55
  .translateExtent([
56
+ [-margin.left, -margin.top],
57
+ [width + margin.right, height + margin.bottom]
58
  ])
59
  .tapDistance(10)
60
  .on('zoom', zoomed);
61
 
62
  const selection = select(canvasEl.parentElement)
63
  .call(zoomHandler as any)
 
 
 
 
 
 
64
  .call(zoomHandler.scaleTo as any, 1 / scale / 1.5)
65
  .on('pointermove', handlePointerMove)
66
  .on('pointerleave', handlePointerLeave);
frontend/src/lib/Menu.svelte CHANGED
@@ -5,8 +5,8 @@
5
  const dispatch = createEventDispatcher();
6
  </script>
7
 
8
- <div class="grid grid-cols-4 gap-3 text-sm w-max mx-auto">
9
- <div class="flex items-center">
10
  <input
11
  id="showframes"
12
  type="checkbox"
@@ -16,28 +16,28 @@
16
  <label for="showframes" class="text-white dark:text-white cursor-pointer ml-2">
17
  Show Frames
18
  </label>
19
- </div>
20
- <button class="button" title="Move" on:click={() => dispatch('paintMode', { mode: 'move' })}>
21
  Move
22
- </button>
23
  <button
24
  class="button-paint bg-violet-100 text-violet-900"
25
  title="New Paint Frame"
26
  on:click={() => dispatch('paintMode', { mode: 'paint' })}
27
  >
28
  <span
29
- class="rounded-sm h-5 w-5 m-1 flex justify-center items-center border-2 border-dashed border-violet-700 mr-2"
30
  >+</span
31
  >
32
- Paint
33
  </button>
34
  </div>
35
 
36
  <style lang="postcss" scoped>
37
- .button {
38
  @apply disabled:opacity-50 dark:bg-white dark:text-black bg-black text-white rounded-2xl text-xs shadow-sm focus:outline-none focus:border-gray-400;
39
- }
40
  .button-paint {
41
- @apply flex justify-center items-center disabled:opacity-50 dark:bg-white dark:text-black rounded-2xl shadow-sm focus:outline-none focus:border-gray-400;
42
  }
43
  </style>
 
5
  const dispatch = createEventDispatcher();
6
  </script>
7
 
8
+ <div class="grid grid-cols-1 gap-3 w-max mx-auto">
9
+ <!-- <div class="flex items-center">
10
  <input
11
  id="showframes"
12
  type="checkbox"
 
16
  <label for="showframes" class="text-white dark:text-white cursor-pointer ml-2">
17
  Show Frames
18
  </label>
19
+ </div> -->
20
+ <!-- <button class="button" title="Move" on:click={() => dispatch('paintMode', { mode: 'move' })}>
21
  Move
22
+ </button> -->
23
  <button
24
  class="button-paint bg-violet-100 text-violet-900"
25
  title="New Paint Frame"
26
  on:click={() => dispatch('paintMode', { mode: 'paint' })}
27
  >
28
  <span
29
+ class="rounded-sm h-6 w-6 flex justify-center items-center border-2 border-dashed border-violet-700 mr-2"
30
  >+</span
31
  >
32
+ <span>Paint</span>
33
  </button>
34
  </div>
35
 
36
  <style lang="postcss" scoped>
37
+ /* .button {
38
  @apply disabled:opacity-50 dark:bg-white dark:text-black bg-black text-white rounded-2xl text-xs shadow-sm focus:outline-none focus:border-gray-400;
39
+ } */
40
  .button-paint {
41
+ @apply flex justify-center items-center disabled:opacity-50 dark:bg-white dark:text-black rounded-2xl px-3 py-1 shadow-sm focus:outline-none focus:border-gray-400;
42
  }
43
  </style>
frontend/src/lib/PaintFrame.svelte CHANGED
@@ -7,14 +7,19 @@
7
  import type { ZoomTransform } from 'd3-zoom';
8
  import { onMount } from 'svelte';
9
 
 
 
 
 
 
10
  export let transform: ZoomTransform;
11
- export let color = '';
 
12
 
13
  let position = {
14
  x: transform.invertX(768),
15
  y: transform.invertX(768)
16
  };
17
- export let prompt = '';
18
 
19
  let frameElement: HTMLDivElement;
20
  $: coord = {
@@ -22,9 +27,11 @@
22
  y: transform.applyY(position.y)
23
  };
24
 
 
 
25
  onMount(() => {
26
  function dragstarted(event: Event) {
27
- console.log(event);
28
  }
29
 
30
  function dragged(event: Event) {
@@ -37,7 +44,15 @@
37
  }
38
 
39
  function dragended(event: Event) {
40
- console.log(event);
 
 
 
 
 
 
 
 
41
  }
42
 
43
  const dragHandler = drag().on('start', dragstarted).on('drag', dragged).on('end', dragended);
@@ -54,6 +69,7 @@
54
  `}
55
  >
56
  <div class="small-frame z-0 flex relative" />
 
57
  <LoadingIcon />
58
  <h2 class="text-lg">Click to paint</h2>
59
 
 
7
  import type { ZoomTransform } from 'd3-zoom';
8
  import { onMount } from 'svelte';
9
 
10
+ import { loadingState } from '$lib/store';
11
+ import { useMyPresence } from '$lib/liveblocks';
12
+
13
+ const myPresence = useMyPresence();
14
+
15
  export let transform: ZoomTransform;
16
+ export let color = 'black';
17
+
18
 
19
  let position = {
20
  x: transform.invertX(768),
21
  y: transform.invertX(768)
22
  };
 
23
 
24
  let frameElement: HTMLDivElement;
25
  $: coord = {
 
27
  y: transform.applyY(position.y)
28
  };
29
 
30
+ $: prompt = $myPresence?.currentPrompt
31
+
32
  onMount(() => {
33
  function dragstarted(event: Event) {
34
+ // console.log(event);
35
  }
36
 
37
  function dragged(event: Event) {
 
44
  }
45
 
46
  function dragended(event: Event) {
47
+ const x = round(transform.invertX(event.x) - 512 / 2);
48
+ const y = round(transform.invertY(event.y) - 512 / 2);
49
+
50
+ myPresence.update({
51
+ frame: {
52
+ x,
53
+ y
54
+ }
55
+ });
56
  }
57
 
58
  const dragHandler = drag().on('start', dragstarted).on('drag', dragged).on('end', dragended);
 
69
  `}
70
  >
71
  <div class="small-frame z-0 flex relative" />
72
+ {$loadingState}
73
  <LoadingIcon />
74
  <h2 class="text-lg">Click to paint</h2>
75
 
frontend/src/lib/PromptModal.svelte CHANGED
@@ -33,7 +33,7 @@
33
 
34
  <form
35
  class="fixed w-screen top-0 left-0 bottom-0 right-0 max-h-screen z-50 flex items-center justify-center bg-black bg-opacity-80 px-3"
36
- on:submit|preventDefault={() => dispatch('prompt', { prompt })}
37
  on:click={() => dispatch('close')}
38
  >
39
  <input
 
33
 
34
  <form
35
  class="fixed w-screen top-0 left-0 bottom-0 right-0 max-h-screen z-50 flex items-center justify-center bg-black bg-opacity-80 px-3"
36
+ on:submit|preventDefault={() => dispatch('prompt')}
37
  on:click={() => dispatch('close')}
38
  >
39
  <input
frontend/src/lib/types.ts CHANGED
@@ -3,6 +3,10 @@ export type Presence = {
3
  x: number;
4
  y: number;
5
  } | null;
 
 
 
 
6
  isPrompting: boolean;
7
  isLoading: boolean;
8
  isMoving: boolean;
 
3
  x: number;
4
  y: number;
5
  } | null;
6
+ frame: {
7
+ x: number;
8
+ y: number;
9
+ } | null;
10
  isPrompting: boolean;
11
  isLoading: boolean;
12
  isMoving: boolean;