jbilcke-hf HF staff commited on
Commit
f309083
1 Parent(s): 8e2433b

better captions

Browse files
package-lock.json CHANGED
@@ -49,7 +49,9 @@
49
  "postcss": "8.4.26",
50
  "react": "18.2.0",
51
  "react-circular-progressbar": "^2.1.0",
 
52
  "react-dom": "18.2.0",
 
53
  "react-icons": "^4.11.0",
54
  "react-konva": "^18.2.10",
55
  "react-virtualized-auto-sizer": "^1.0.20",
@@ -5947,6 +5949,18 @@
5947
  "react": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
5948
  }
5949
  },
 
 
 
 
 
 
 
 
 
 
 
 
5950
  "node_modules/react-dom": {
5951
  "version": "18.2.0",
5952
  "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
@@ -5967,6 +5981,27 @@
5967
  "loose-envify": "^1.1.0"
5968
  }
5969
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5970
  "node_modules/react-icons": {
5971
  "version": "4.11.0",
5972
  "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.11.0.tgz",
 
49
  "postcss": "8.4.26",
50
  "react": "18.2.0",
51
  "react-circular-progressbar": "^2.1.0",
52
+ "react-contenteditable": "^3.3.7",
53
  "react-dom": "18.2.0",
54
+ "react-draggable": "^4.4.6",
55
  "react-icons": "^4.11.0",
56
  "react-konva": "^18.2.10",
57
  "react-virtualized-auto-sizer": "^1.0.20",
 
5949
  "react": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0"
5950
  }
5951
  },
5952
+ "node_modules/react-contenteditable": {
5953
+ "version": "3.3.7",
5954
+ "resolved": "https://registry.npmjs.org/react-contenteditable/-/react-contenteditable-3.3.7.tgz",
5955
+ "integrity": "sha512-GA9NbC0DkDdpN3iGvib/OMHWTJzDX2cfkgy5Tt98JJAbA3kLnyrNbBIpsSpPpq7T8d3scD39DHP+j8mAM7BIfQ==",
5956
+ "dependencies": {
5957
+ "fast-deep-equal": "^3.1.3",
5958
+ "prop-types": "^15.7.1"
5959
+ },
5960
+ "peerDependencies": {
5961
+ "react": ">=16.3"
5962
+ }
5963
+ },
5964
  "node_modules/react-dom": {
5965
  "version": "18.2.0",
5966
  "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
 
5981
  "loose-envify": "^1.1.0"
5982
  }
5983
  },
5984
+ "node_modules/react-draggable": {
5985
+ "version": "4.4.6",
5986
+ "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.6.tgz",
5987
+ "integrity": "sha512-LtY5Xw1zTPqHkVmtM3X8MUOxNDOUhv/khTgBgrUvwaS064bwVvxT+q5El0uUFNx5IEPKXuRejr7UqLwBIg5pdw==",
5988
+ "dependencies": {
5989
+ "clsx": "^1.1.1",
5990
+ "prop-types": "^15.8.1"
5991
+ },
5992
+ "peerDependencies": {
5993
+ "react": ">= 16.3.0",
5994
+ "react-dom": ">= 16.3.0"
5995
+ }
5996
+ },
5997
+ "node_modules/react-draggable/node_modules/clsx": {
5998
+ "version": "1.2.1",
5999
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
6000
+ "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
6001
+ "engines": {
6002
+ "node": ">=6"
6003
+ }
6004
+ },
6005
  "node_modules/react-icons": {
6006
  "version": "4.11.0",
6007
  "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.11.0.tgz",
package.json CHANGED
@@ -50,7 +50,9 @@
50
  "postcss": "8.4.26",
51
  "react": "18.2.0",
52
  "react-circular-progressbar": "^2.1.0",
 
53
  "react-dom": "18.2.0",
 
54
  "react-icons": "^4.11.0",
55
  "react-konva": "^18.2.10",
56
  "react-virtualized-auto-sizer": "^1.0.20",
 
50
  "postcss": "8.4.26",
51
  "react": "18.2.0",
52
  "react-circular-progressbar": "^2.1.0",
53
+ "react-contenteditable": "^3.3.7",
54
  "react-dom": "18.2.0",
55
+ "react-draggable": "^4.4.6",
56
  "react-icons": "^4.11.0",
57
  "react-konva": "^18.2.10",
58
  "react-virtualized-auto-sizer": "^1.0.20",
src/app/interface/panel/bubble/index.tsx ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use client"
2
+
3
+ import { ReactNode, useRef } from "react"
4
+
5
+ // import Draggable from "react-draggable"
6
+ import ContentEditable, { ContentEditableEvent } from "react-contenteditable"
7
+
8
+ import { useStore } from "@/app/store"
9
+ import { cn } from "@/lib/utils"
10
+
11
+ export function Bubble({ children, onChange }: {
12
+ children: ReactNode;
13
+ onChange: (newCaption: string) => void
14
+ }) {
15
+
16
+ const ref = useRef<HTMLDivElement>(null)
17
+ const zoomLevel = useStore(state => state.zoomLevel)
18
+ const showCaptions = useStore(state => state.showCaptions)
19
+
20
+ const text = useRef(`${children || ''}`)
21
+
22
+ const handleChange = (evt: ContentEditableEvent) => {
23
+ // hmm no, this returns us some rich HTML - but it's too early for that
24
+ // text.current = evt.target.value
25
+
26
+ text.current = `${ref.current?.innerText || ''}`
27
+ };
28
+
29
+ const handleBlur = () => {
30
+ onChange(text.current)
31
+ };
32
+
33
+ // we can wrap this bubble in a <Draggable>
34
+ // but the zoom will break it, so we will need to figure out something
35
+ return (
36
+ <div className={cn(
37
+ `bottom-0`,
38
+ // `cursor-grab`,
39
+ `absolute flex w-full items-center justify-center`,
40
+ zoomLevel > 200 ? `p-4 md:p-8` :
41
+ zoomLevel > 180 ? `p-[14px] md:p-8` :
42
+ zoomLevel > 160 ? `p-[12px] md:p-[28px]` :
43
+ zoomLevel > 140 ? `p-[10px] md:p-[26px]` :
44
+ zoomLevel > 120 ? `p-2 md:p-6` :
45
+ zoomLevel > 100 ? `p-1.5 md:p-[20px]` :
46
+ zoomLevel > 90 ? `p-1.5 md:p-4` :
47
+ zoomLevel > 40 ? `p-1 md:p-2` :
48
+ `p-0.5 md:p-2`,
49
+ )}>
50
+ <div
51
+ ref={ref}
52
+ className={cn(
53
+ `bg-stone-50`,
54
+ `border-stone-800`,
55
+ `transition-all duration-200 ease-in-out`,
56
+ zoomLevel > 140 ? `border-[1.5px] md:border-[2px]` :
57
+ zoomLevel > 120 ? `border-[1px] md:border-[1.5px]` :
58
+ zoomLevel > 90 ? `border-[0.5px] md:border-[1px]` :
59
+ zoomLevel > 40 ? `border-transparent md:border-[0.5px]` :
60
+ `border-transparent md:border-transparent`,
61
+ `print:border-[1px]`,
62
+
63
+ zoomLevel > 200 ? `p-4 md:p-8` :
64
+ zoomLevel > 180 ? `p-[14px] md:p-6` :
65
+ zoomLevel > 160 ? `p-3 md:p-5` :
66
+ zoomLevel > 140 ? `p-[10px] md:p-4` :
67
+ zoomLevel > 120 ? `p-2 md:p-3` :
68
+ zoomLevel > 100 ? `p-1.5 md:p-2` :
69
+ zoomLevel > 90 ? `p-1 md:p-1` :
70
+ zoomLevel > 40 ? `p-0.5 md:p-0.5` :
71
+ `p-0.5 md:p-0.5]`,
72
+
73
+ zoomLevel > 220 ? `text-base md:text-lg` :
74
+ zoomLevel > 200 ? `text-sm md:text-md` :
75
+ zoomLevel > 180 ? `text-xs md:text-base` :
76
+ zoomLevel > 140 ? `text-2xs md:text-sm` :
77
+ zoomLevel > 120 ? `text-3xs md:text-xs` :
78
+ zoomLevel > 100 ? `text-4xs md:text-2xs` :
79
+ zoomLevel > 90 ? `text-5xs md:text-3xs` :
80
+ zoomLevel > 40 ? `text-6xs md:text-4xs`
81
+ : `text-7xs md:text-5xs`,
82
+
83
+ zoomLevel > 140 ? `rounded-lg md:rounded-xl` :
84
+ zoomLevel > 120 ? `rounded-md md:rounded-lg` :
85
+ zoomLevel > 90 ? `rounded-sm md:rounded-md` :
86
+ zoomLevel > 40 ? `rounded-xs md:rounded-sm` :
87
+ `rounded-none md:rounded-none`,
88
+
89
+ showCaptions ? (
90
+ zoomLevel > 90 ? `block` : `hidden md:block`
91
+ ) : `hidden`,
92
+
93
+ `text-center`
94
+ )}>
95
+ <ContentEditable
96
+ html={text.current}
97
+ className="line-clamp-3"
98
+ onBlur={handleBlur}
99
+ onChange={handleChange}
100
+ />
101
+ </div>
102
+ </div>
103
+ )
104
+ }
src/app/interface/panel/{bubble.tsx → bubble/legacy.tsx} RENAMED
@@ -2,7 +2,7 @@ import { ReactNode } from "react"
2
 
3
  import { cn } from "@/lib/utils"
4
 
5
- export function Bubble({
6
  children,
7
  className
8
  }: {
 
2
 
3
  import { cn } from "@/lib/utils"
4
 
5
+ export function BubbleLegacy({
6
  children,
7
  className
8
  }: {
src/app/interface/panel/index.tsx CHANGED
@@ -12,6 +12,7 @@ import { cn } from "@/lib/utils"
12
  import { getInitialRenderedScene } from "@/lib/getInitialRenderedScene"
13
  import { Progress } from "@/app/interface/progress"
14
  import { EditModal } from "../edit-modal"
 
15
 
16
  export function Panel({
17
  page,
@@ -59,9 +60,9 @@ export function Panel({
59
 
60
  const captions = useStore(state => state.captions)
61
  const caption = captions[panelIndex] || ""
62
-
 
63
  const zoomLevel = useStore(state => state.zoomLevel)
64
- const showCaptions = useStore(state => state.showCaptions)
65
 
66
  const addToUpscaleQueue = useStore(state => state.addToUpscaleQueue)
67
 
@@ -251,6 +252,7 @@ export function Panel({
251
 
252
  const frameClassName = cn(
253
  //`flex`,
 
254
  `w-full h-full`,
255
  `border-stone-800`,
256
  `transition-all duration-200 ease-in-out`,
@@ -275,6 +277,10 @@ export function Panel({
275
  setPanelPrompt(newPrompt, panelIndex)
276
  }
277
 
 
 
 
 
278
  if (prompt && !rendered.assetUrl) {
279
  return (
280
  <div className={cn(
@@ -296,61 +302,16 @@ export function Panel({
296
  onMouseEnter={() => setMouseOver(true)}
297
  onMouseLeave={() => setMouseOver(false)}
298
  >
299
- <div className={cn(
300
- `bg-stone-50`,
301
- `border-stone-800`,
302
- `transition-all duration-200 ease-in-out`,
303
- zoomLevel > 140 ? `border-b-[2px] md:border-b-[4px]` :
304
- zoomLevel > 120 ? `border-b-[1.5px] md:border-b-[3px]` :
305
- zoomLevel > 90 ? `border-b-[1px] md:border-b-[2px]` :
306
- zoomLevel > 40 ? `border-b-[0.5px] md:border-b-[1px]` :
307
- `border-transparent md:border-b-[0.5px]`,
308
- `print:border-b-[1.5px]`,
309
- `truncate`,
310
-
311
- zoomLevel > 200 ? `p-4 md:p-8` :
312
- zoomLevel > 180 ? `p-[14px] md:p-8` :
313
- zoomLevel > 160 ? `p-[12px] md:p-[28px]` :
314
- zoomLevel > 140 ? `p-[10px] md:p-[26px]` :
315
- zoomLevel > 120 ? `p-2 md:p-6` :
316
- zoomLevel > 100 ? `p-1.5 md:p-[20px]` :
317
- zoomLevel > 90 ? `p-1.5 md:p-4` :
318
- zoomLevel > 40 ? `p-1 md:p-2` :
319
- `p-0.5 md:p-2`,
320
-
321
- zoomLevel > 220 ? `text-xl md:text-4xl` :
322
- zoomLevel > 200 ? `text-lg md:text-3xl` :
323
- zoomLevel > 180 ? `text-md md:text-2xl` :
324
- zoomLevel > 140 ? `text-2xs md:text-2xl` :
325
- zoomLevel > 120 ? `text-3xs md:text-xl` :
326
- zoomLevel > 100 ? `text-4xs md:text-lg` :
327
- zoomLevel > 90 ? `text-5xs md:text-sm` :
328
- zoomLevel > 40 ? `md:text-xs` : `md:text-2xs`,
329
-
330
- showCaptions ? (
331
- zoomLevel > 90 ? `block` : `hidden md:block`
332
- ) : `hidden`,
333
- )}
334
- >{caption || ""}
335
- </div>
336
- {rendered.assetUrl &&
337
- <img
338
- ref={ref}
339
- src={rendered.assetUrl}
340
- width={width}
341
- height={height}
342
- alt={rendered.alt}
343
  className={cn(
344
- `comic-panel w-full h-full object-cover max-w-max`,
345
- // showCaptions ? `-mt-11` : ''
346
- )}
347
- />}
348
- {
349
- // there is an issue, this env check doesn't work..
350
- // process.env.NEXT_PUBLIC_CAN_REDRAW === "true" ?
351
- <div
352
- className={cn(`relative -mt-8 ml-2 md:-mt-12md:ml-3 lg:-mt-14 lg:ml-4`,)}>
353
- <div className="flex flex-row space-x-2">
354
  <div
355
  onClick={rendered.status === "completed" ? handleReload : undefined}
356
  className={cn(
@@ -364,7 +325,13 @@ export function Panel({
364
  <RxReload
365
  className="w-3 h-3 md:w-4 md:h-4 lg:w-5 lg:h-5"
366
  />
367
- <span className="text-2xs md:text-xs lg:text-sm">Redraw</span>
 
 
 
 
 
 
368
  </div>
369
  <EditModal
370
  isEnabled={rendered.status === "completed"}
@@ -383,14 +350,30 @@ export function Panel({
383
  <RxPencil2
384
  className="w-3 h-3 md:w-4 md:h-4 lg:w-5 lg:h-5"
385
  />
386
- <span className="text-2xs md:text-xs lg:text-sm">Edit</span>
 
 
 
 
 
 
387
  </div>
388
 
389
  </EditModal>
390
- </div>
391
- </div>
392
- //: null
393
- }
 
 
 
 
 
 
 
 
 
 
394
  </div>
395
  )
396
  }
 
12
  import { getInitialRenderedScene } from "@/lib/getInitialRenderedScene"
13
  import { Progress } from "@/app/interface/progress"
14
  import { EditModal } from "../edit-modal"
15
+ import { Bubble } from "./bubble"
16
 
17
  export function Panel({
18
  page,
 
60
 
61
  const captions = useStore(state => state.captions)
62
  const caption = captions[panelIndex] || ""
63
+ const setPanelCaption = useStore(state => state.setPanelCaption)
64
+
65
  const zoomLevel = useStore(state => state.zoomLevel)
 
66
 
67
  const addToUpscaleQueue = useStore(state => state.addToUpscaleQueue)
68
 
 
252
 
253
  const frameClassName = cn(
254
  //`flex`,
255
+ `relative`,
256
  `w-full h-full`,
257
  `border-stone-800`,
258
  `transition-all duration-200 ease-in-out`,
 
277
  setPanelPrompt(newPrompt, panelIndex)
278
  }
279
 
280
+ const handleSaveCaption = (newCaption: string) => {
281
+ console.log(`Asked to save a new caption: ${newCaption}`)
282
+ setPanelCaption(newCaption, panelIndex)
283
+ }
284
  if (prompt && !rendered.assetUrl) {
285
  return (
286
  <div className={cn(
 
302
  onMouseEnter={() => setMouseOver(true)}
303
  onMouseLeave={() => setMouseOver(false)}
304
  >
305
+ {(prompt && rendered.assetUrl && caption)
306
+ ? <Bubble onChange={handleSaveCaption}>{caption}</Bubble>
307
+ : null}
308
+ <div
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
309
  className={cn(
310
+ `absolute`,
311
+ `top-0 w-full`,
312
+ `flex justify-between`,
313
+ `p-2 space-x-2`
314
+ )}>
 
 
 
 
 
315
  <div
316
  onClick={rendered.status === "completed" ? handleReload : undefined}
317
  className={cn(
 
325
  <RxReload
326
  className="w-3 h-3 md:w-4 md:h-4 lg:w-5 lg:h-5"
327
  />
328
+ <span className={cn(
329
+ zoomLevel > 80
330
+ ? `text-xs md:text-sm lg:text-base` :
331
+ zoomLevel > 40
332
+ ? `text-2xs md:text-xs lg:text-sm` :
333
+ `text-3xs md:text-2xs lg:text-xs`
334
+ )}>Redraw</span>
335
  </div>
336
  <EditModal
337
  isEnabled={rendered.status === "completed"}
 
350
  <RxPencil2
351
  className="w-3 h-3 md:w-4 md:h-4 lg:w-5 lg:h-5"
352
  />
353
+ <span className={cn(
354
+ zoomLevel > 80
355
+ ? `text-xs md:text-sm lg:text-base` :
356
+ zoomLevel > 40
357
+ ? `text-2xs md:text-xs lg:text-sm` :
358
+ `text-3xs md:text-2xs lg:text-xs`
359
+ )}>Edit</span>
360
  </div>
361
 
362
  </EditModal>
363
+ </div>
364
+
365
+ {rendered.assetUrl &&
366
+ <img
367
+ ref={ref}
368
+ src={rendered.assetUrl}
369
+ width={width}
370
+ height={height}
371
+ alt={rendered.alt}
372
+ className={cn(
373
+ `comic-panel w-full h-full object-cover max-w-max`,
374
+ // showCaptions ? `-mt-11` : ''
375
+ )}
376
+ />}
377
  </div>
378
  )
379
  }
src/app/queries/getStory.ts CHANGED
@@ -27,10 +27,10 @@ export const getStory = async ({
27
  role: "system",
28
  content: [
29
  `You are a writer specialized in ${preset.llmPrompt}`,
30
- `Please write detailed drawing instructions and a one-sentence short caption for the ${nbTotalPanels} panels of a new silent story. Please make sure each of the ${nbTotalPanels} panels include info about character gender, age, origin, clothes, colors, location, lights, etc.`,
31
  `Give your response as a VALID JSON array like this: \`Array<{ panel: number; instructions: string; caption: string}>\`.`,
32
  // `Give your response as Markdown bullet points.`,
33
- `Be brief in your ${nbTotalPanels} instructions and captions, don't add your own comments. Be straight to the point, and never reply things like "Sure, I can.." etc. Reply using valid JSON.`
34
  ].filter(item => item).join("\n")
35
  },
36
  {
 
27
  role: "system",
28
  content: [
29
  `You are a writer specialized in ${preset.llmPrompt}`,
30
+ `Please write detailed drawing instructions and a short (1 or 2 sentences long) speech caption for the ${nbTotalPanels} panels of a new story. Please make sure each of the ${nbTotalPanels} panels include info about character gender, age, origin, clothes, colors, location, lights, etc.`,
31
  `Give your response as a VALID JSON array like this: \`Array<{ panel: number; instructions: string; caption: string}>\`.`,
32
  // `Give your response as Markdown bullet points.`,
33
+ `Be brief in your ${nbTotalPanels} instructions and narrative captions, don't add your own comments. Be straight to the point, and never reply things like "Sure, I can.." etc. Reply using valid JSON.`
34
  ].filter(item => item).join("\n")
35
  },
36
  {
src/app/store/index.ts CHANGED
@@ -40,6 +40,7 @@ export const useStore = create<{
40
  setLayout: (layout: LayoutName) => void
41
  setLayouts: (layouts: LayoutName[]) => void
42
  setCaptions: (captions: string[]) => void
 
43
  setZoomLevel: (zoomLevel: number) => void
44
  setPage: (page: HTMLDivElement) => void
45
  setGeneratingStory: (isGeneratingStory: boolean) => void
@@ -135,6 +136,14 @@ export const useStore = create<{
135
  showCaptions,
136
  })
137
  },
 
 
 
 
 
 
 
 
138
  setLayout: (layoutName: LayoutName) => {
139
 
140
  const { nbPages } = get()
 
40
  setLayout: (layout: LayoutName) => void
41
  setLayouts: (layouts: LayoutName[]) => void
42
  setCaptions: (captions: string[]) => void
43
+ setPanelCaption: (newCaption: string, index: number) => void
44
  setZoomLevel: (zoomLevel: number) => void
45
  setPage: (page: HTMLDivElement) => void
46
  setGeneratingStory: (isGeneratingStory: boolean) => void
 
136
  showCaptions,
137
  })
138
  },
139
+ setPanelCaption: (newCaption, index) => {
140
+ const { captions } = get()
141
+ set({
142
+ captions: captions.map((c, i) => (
143
+ index === i ? newCaption : c
144
+ ))
145
+ })
146
+ },
147
  setLayout: (layoutName: LayoutName) => {
148
 
149
  const { nbPages } = get()