radames HF staff commited on
Commit
836ca1d
β€’
1 Parent(s): daf00a0
frontend/src/lib/ColorPalette.svelte CHANGED
@@ -11,17 +11,36 @@
11
  }
12
  return d3.hcl(color.h, color.c, 100).formatHex();
13
  }
 
 
 
 
 
 
 
 
 
 
 
 
14
  </script>
15
 
16
  <div class="flex flex-col items-center">
17
  <div class="flex bg-black">
18
- {#each colors as color}
19
- <div class="aspect-square relative">
 
 
 
20
  <svg class="max-w-full" width="100" viewBox="0 0 50 50">
21
  <rect x="0" y="0" width="50" height="50" fill={color.formatHex()} />
22
  </svg>
23
  <span
24
- class="absolute bottom-0 text-center text-xs pl-1 font-bold uppercase"
 
 
 
 
25
  style="color:{getLabelColor(color)}">{color.formatHex()}</span
26
  >
27
  </div>
 
11
  }
12
  return d3.hcl(color.h, color.c, 100).formatHex();
13
  }
14
+ let isCopying = -1;
15
+
16
+ async function copyStringToClipboard(text: string, n: number) {
17
+ // usingo Clipboard API
18
+ if (isCopying > -1) return;
19
+ isCopying = n;
20
+ await navigator.permissions.query({ name: 'clipboard-write' });
21
+ await navigator.clipboard.writeText(text);
22
+ setTimeout(() => {
23
+ isCopying = -1;
24
+ }, 500);
25
+ }
26
  </script>
27
 
28
  <div class="flex flex-col items-center">
29
  <div class="flex bg-black">
30
+ {#each colors as color, i}
31
+ <div
32
+ class="cursor-pointer aspect-square relative"
33
+ on:click={() => copyStringToClipboard(color.formatHex(), i)}
34
+ >
35
  <svg class="max-w-full" width="100" viewBox="0 0 50 50">
36
  <rect x="0" y="0" width="50" height="50" fill={color.formatHex()} />
37
  </svg>
38
  <span
39
+ title="Copy single color"
40
+ class="cursor-pointer absolute bottom-0 text-center text-xs pl-1 font-bold uppercase {isCopying ===
41
+ i
42
+ ? 'animate-ping'
43
+ : ''}"
44
  style="color:{getLabelColor(color)}">{color.formatHex()}</span
45
  >
46
  </div>
frontend/src/lib/Palette.svelte CHANGED
@@ -14,6 +14,7 @@
14
  $: imageSrc = promptData.images[seletecdImage].imgURL;
15
 
16
  let isCopying = false;
 
17
  async function copyStringToClipboard(text: string) {
18
  // usingo Clipboard API
19
  if (isCopying) return;
@@ -36,7 +37,7 @@
36
  <div class="row-start-3 md:row-start-2 col-span-6 md:col-span-4 flex items-center justify-center">
37
  <ColorPalette {colors} />
38
  </div>
39
- <div class="row-start-2 col-span-6 md:col-span-2 flex justify-center pb-3">
40
  <div class="relative">
41
  <img class="relative max-w-[100px] w-full" src={imageSrc} alt={prompt} />
42
  <div class="absolute flex justify-around w-full">
@@ -52,15 +53,24 @@
52
  </div>
53
  </div>
54
  </div>
55
- <div class="row-start-4 col-span-6 md:col-span-2 md:col-start-5 flex justify-center">
 
 
56
  <div class="flex justify-center items-center">
57
- <button class="button" on:click={() => dispatch('remix', { prompt })}> Remix </button>
58
  <button
59
  class="button"
 
 
 
 
 
 
 
 
60
  disabled={isCopying}
61
  on:click={() => copyStringToClipboard(colors.map((color) => color.formatHex()).join(', '))}
62
  >
63
- {isCopying? 'Copied' : 'Copy'}
64
  </button>
65
  </div>
66
  </div>
 
14
  $: imageSrc = promptData.images[seletecdImage].imgURL;
15
 
16
  let isCopying = false;
17
+
18
  async function copyStringToClipboard(text: string) {
19
  // usingo Clipboard API
20
  if (isCopying) return;
 
37
  <div class="row-start-3 md:row-start-2 col-span-6 md:col-span-4 flex items-center justify-center">
38
  <ColorPalette {colors} />
39
  </div>
40
+ <div class="row-start-2 col-span-6 md:col-span-2 flex justify-center md:justify-end pb-3">
41
  <div class="relative">
42
  <img class="relative max-w-[100px] w-full" src={imageSrc} alt={prompt} />
43
  <div class="absolute flex justify-around w-full">
 
53
  </div>
54
  </div>
55
  </div>
56
+ <div
57
+ class="row-start-4 col-span-6 md:col-span-2 md:col-start-5 flex justify-center md:justify-end"
58
+ >
59
  <div class="flex justify-center items-center">
 
60
  <button
61
  class="button"
62
+ title="Send this prompt to input so you can remix it"
63
+ on:click={() => dispatch('remix', { prompt })}
64
+ >
65
+ Remix
66
+ </button>
67
+ <button
68
+ class="button"
69
+ title="Copy all colors to clipboard"
70
  disabled={isCopying}
71
  on:click={() => copyStringToClipboard(colors.map((color) => color.formatHex()).join(', '))}
72
  >
73
+ {isCopying ? 'Copied' : 'Copy'}
74
  </button>
75
  </div>
76
  </div>
frontend/src/lib/utils.ts CHANGED
@@ -12,7 +12,6 @@ function sortColors(colors: Color[]): Color[] {
12
  return colors
13
  .map((color) => d3.hcl(color))
14
  .sort((a, b) => {
15
- console.log(a);
16
  const aa = a.h;
17
  const bb = b.h;
18
 
@@ -109,4 +108,4 @@ function slugify(text: string) {
109
  .replace(/\-\-+/g, '-')
110
  .replace(/^-+/, '')
111
  .replace(/-+$/, '');
112
- }
 
12
  return colors
13
  .map((color) => d3.hcl(color))
14
  .sort((a, b) => {
 
15
  const aa = a.h;
16
  const bb = b.h;
17
 
 
108
  .replace(/\-\-+/g, '-')
109
  .replace(/^-+/, '')
110
  .replace(/-+$/, '');
111
+ }
frontend/src/routes/+page.svelte CHANGED
@@ -41,7 +41,6 @@
41
  };
42
  const websocket = new WebSocket(PUBLIC_WS_ENDPOINT);
43
  websocket.onopen = async function (event) {
44
- console.log(event);
45
  websocket.send(JSON.stringify({ hash: hash }));
46
  };
47
  websocket.onclose = (evt) => {
@@ -53,7 +52,6 @@
53
  websocket.onmessage = async function (event) {
54
  try {
55
  const data = JSON.parse(event.data);
56
- console.log(data);
57
  $loadingState = '';
58
  switch (data.msg) {
59
  case 'send_data':
@@ -74,7 +72,6 @@
74
  break;
75
  case 'process_completed':
76
  $loadingState = data.success ? 'Complete' : 'Error';
77
- console.log(data.output);
78
  const images = await extractColorsImages(data.output.data[0], _prompt);
79
  promptsData = [
80
  {
@@ -82,6 +79,10 @@
82
  images
83
  }
84
  ].concat(promptsData);
 
 
 
 
85
  websocket.close();
86
  $isLoading = false;
87
  return;
@@ -90,7 +91,7 @@
90
  break;
91
  }
92
  } catch (e) {
93
- console.log(e);
94
  $isLoading = false;
95
  $loadingState = 'Error';
96
  }
@@ -115,20 +116,37 @@
115
  </script>
116
 
117
  <div class="max-w-screen-md mx-auto px-3 py-8 relative z-0">
118
- <div class="relative">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
  <form class="grid grid-cols-6" on:submit|preventDefault={() => generatePalette(prompt)}>
120
  <input
121
- class="text-sm disabled:opacity-50 col-span-4 md:col-span-5 italic dark:placeholder:text-black placeholder:text-white text-white dark:text-black placeholder:text-opacity-40 dark:bg-white bg-slate-900 border-2 border-black rounded-2xl px-2 shadow-sm focus:outline-none focus:border-gray-400 focus:ring-1 "
122
  placeholder="A photo of a beautiful sunset in San Francisco"
 
123
  type="text"
124
  name="prompt"
125
  bind:value={prompt}
126
  disabled={$isLoading}
127
  />
128
  <button
129
- class="disabled:opacity-50 col-span-2 md:col-span-1 dark:bg-white dark:text-black border-2 border-black rounded-2xl ml-2 px-2 py-2 text-xs shadow-sm font-bold focus:outline-none focus:border-gray-400"
130
  on:click|preventDefault={() => generatePalette(prompt)}
131
  disabled={$isLoading}
 
 
132
  >
133
  Create Palette
134
  </button>
@@ -138,8 +156,13 @@
138
  {/if}
139
  </div>
140
 
 
 
 
 
 
141
  {#if promptsData}
142
- <div class="pt-5">
143
  {#each promptsData as promptData}
144
  <Pallette {promptData} on:remix={remix} />
145
  <div class="border-b border-gray-200 py-2" />
@@ -147,3 +170,15 @@
147
  </div>
148
  {/if}
149
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  };
42
  const websocket = new WebSocket(PUBLIC_WS_ENDPOINT);
43
  websocket.onopen = async function (event) {
 
44
  websocket.send(JSON.stringify({ hash: hash }));
45
  };
46
  websocket.onclose = (evt) => {
 
52
  websocket.onmessage = async function (event) {
53
  try {
54
  const data = JSON.parse(event.data);
 
55
  $loadingState = '';
56
  switch (data.msg) {
57
  case 'send_data':
 
72
  break;
73
  case 'process_completed':
74
  $loadingState = data.success ? 'Complete' : 'Error';
 
75
  const images = await extractColorsImages(data.output.data[0], _prompt);
76
  promptsData = [
77
  {
 
79
  images
80
  }
81
  ].concat(promptsData);
82
+ window.scrollTo({
83
+ top: 0,
84
+ behavior: 'smooth'
85
+ });
86
  websocket.close();
87
  $isLoading = false;
88
  return;
 
91
  break;
92
  }
93
  } catch (e) {
94
+ console.error(e);
95
  $isLoading = false;
96
  $loadingState = 'Error';
97
  }
 
116
  </script>
117
 
118
  <div class="max-w-screen-md mx-auto px-3 py-8 relative z-0">
119
+ <h1 class="text-3xl font-bold leading-normal">Palette generation with Stable Diffussion</h1>
120
+ <p class="text-sm">
121
+ Original ideas:
122
+
123
+ <a
124
+ class="link"
125
+ target="_blank"
126
+ rel="nofollow noopener"
127
+ href="https://twitter.com/mattdesl/status/1569457653298139136"
128
+ >
129
+ Matt DesLauriers
130
+ </a>,
131
+ <a class="link" href="https://drib.net/homage"> dribnet </a>
132
+ </p>
133
+ <div class="relative sticky top-0 z-50 bg-white dark:bg-black p-3">
134
  <form class="grid grid-cols-6" on:submit|preventDefault={() => generatePalette(prompt)}>
135
  <input
136
+ class="input"
137
  placeholder="A photo of a beautiful sunset in San Francisco"
138
+ title="Input prompt to generate image and obtain palette"
139
  type="text"
140
  name="prompt"
141
  bind:value={prompt}
142
  disabled={$isLoading}
143
  />
144
  <button
145
+ class="button"
146
  on:click|preventDefault={() => generatePalette(prompt)}
147
  disabled={$isLoading}
148
+ title="Generate Palette"
149
+
150
  >
151
  Create Palette
152
  </button>
 
156
  {/if}
157
  </div>
158
 
159
+ <div class="flex items-center gap-4 my-10">
160
+ <div class="font-bold text-sm">156 submitted palettes</div>
161
+ <div class="grow border-b border-gray-200" />
162
+ </div>
163
+
164
  {#if promptsData}
165
+ <div>
166
  {#each promptsData as promptData}
167
  <Pallette {promptData} on:remix={remix} />
168
  <div class="border-b border-gray-200 py-2" />
 
170
  </div>
171
  {/if}
172
  </div>
173
+
174
+ <style lang="postcss" scoped>
175
+ .link {
176
+ @apply text-xs underline font-bold hover:no-underline hover:text-gray-500 visited:text-gray-500;
177
+ }
178
+ .input {
179
+ @apply text-sm disabled:opacity-50 col-span-4 md:col-span-5 italic dark:placeholder:text-black placeholder:text-white text-white dark:text-black placeholder:text-opacity-40 dark:bg-white bg-slate-900 border-2 border-black rounded-2xl px-2 shadow-sm focus:outline-none focus:border-gray-400 focus:ring-1;
180
+ }
181
+ .button {
182
+ @apply disabled:opacity-50 col-span-2 md:col-span-1 dark:bg-white dark:text-black border-2 border-black rounded-2xl ml-2 px-2 py-2 text-xs shadow-sm font-bold focus:outline-none focus:border-gray-400;
183
+ }
184
+ </style>
frontend/tailwind.config.cjs CHANGED
@@ -2,7 +2,11 @@
2
  module.exports = {
3
  content: ['./src/**/*.{html,js,svelte,ts}'],
4
  theme: {
5
- extend: {}
 
 
 
 
6
  },
7
  plugins: [require('@tailwindcss/forms'), require('@tailwindcss/line-clamp')]
8
  };
 
2
  module.exports = {
3
  content: ['./src/**/*.{html,js,svelte,ts}'],
4
  theme: {
5
+ extend: {
6
+ animation: {
7
+ ping: 'ping 0.5s cubic-bezier(0, 0, 0.2, 1)'
8
+ }
9
+ }
10
  },
11
  plugins: [require('@tailwindcss/forms'), require('@tailwindcss/line-clamp')]
12
  };