diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx | 9 | ||||
-rw-r--r-- | src/client/views/nodes/DocumentView.scss | 2 | ||||
-rw-r--r-- | src/client/views/nodes/ImageBox.scss | 8 | ||||
-rw-r--r-- | src/client/views/nodes/ImageBox.tsx | 49 | ||||
-rw-r--r-- | src/client/views/smartdraw/DrawingFillHandler.tsx | 19 | ||||
-rw-r--r-- | src/server/ApiManagers/FireflyManager.ts | 41 |
6 files changed, 89 insertions, 39 deletions
diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 112bfd178..e1144f21a 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -2266,7 +2266,6 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection <div className="collectionfreeformview-aiView-options"> <input className="collectionfreeformview-aiView-prompt" placeholder="Prompt (Optional)" type="text" value={this._drawingFillInput} onChange={action(e => this._canInteract && (this._drawingFillInput = e.target.value))} /> <div className="collectionfreeformview-aiView-strength"> - Reference Strength <Slider className="collectionfreeformview-aiView-slider" sx={{ @@ -2282,6 +2281,7 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection onChange={action((e, val) => this._canInteract && (this._fireflyRefStrength = val as number))} valueLabelDisplay="auto" /> + Reference Strength </div> <Button text="Send" @@ -2295,9 +2295,10 @@ export class CollectionFreeFormView extends CollectionSubView<Partial<collection this.props.Document, this._fireflyRefStrength, this._drawingFillInput !== '' ? this._drawingFillInput : StrCast(this.props.Document.title) !== 'grouping' ? StrCast(this.props.Document.title) : '' - ); - this._drawingFillInput = ''; - this._drawingFillLoading = false; + ).then(() => { + this._drawingFillInput = ''; + this._drawingFillLoading = false; + }); }), 'create image' )} diff --git a/src/client/views/nodes/DocumentView.scss b/src/client/views/nodes/DocumentView.scss index a3d47290a..e71f391c6 100644 --- a/src/client/views/nodes/DocumentView.scss +++ b/src/client/views/nodes/DocumentView.scss @@ -276,11 +276,13 @@ right: 0; top: 0; overflow-y: scroll; + scrollbar-width: thin; } .documentView-editorView { width: 100%; overflow-y: scroll; + scrollbar-width: thin; justify-items: center; background-color: rgb(223, 223, 223); diff --git a/src/client/views/nodes/ImageBox.scss b/src/client/views/nodes/ImageBox.scss index 1b6b3c85a..759f2584b 100644 --- a/src/client/views/nodes/ImageBox.scss +++ b/src/client/views/nodes/ImageBox.scss @@ -144,16 +144,20 @@ display: flex; flex-direction: column; align-items: center; - padding: 5 0 0 5; - gap: 5px; + text-align: center; .imageBox-aiView-img { width: 100%; + padding: 5px; &:hover { filter: brightness(0.8); } } + + .imageBox-aiView-caption { + font-size: 7px; + } } .imageBox-aiView { diff --git a/src/client/views/nodes/ImageBox.tsx b/src/client/views/nodes/ImageBox.tsx index 9838af4d0..8a7fb99b1 100644 --- a/src/client/views/nodes/ImageBox.tsx +++ b/src/client/views/nodes/ImageBox.tsx @@ -536,23 +536,35 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { } componentAIViewHistory = () => { + const imgs: FireflyImageData[] = this.dataDoc.ai_firefly_history ? JSON.parse(StrCast(this.dataDoc.ai_firefly_history)) : []; return ( - <div className="imageBox-aiView-history"> - <Button text="Clear History" type={Type.SEC} size={Size.XSMALL} /> - {this._prevImgs.map(img => ( - <div key={img.pathname}> - <img - className="imageBox-aiView-img" - src={img.href} - onClick={() => { - this.dataDoc[this.fieldKey] = new ImageField(img.pathname); - this.dataDoc.ai_firefly_prompt = img.prompt; - this.dataDoc.ai_firefly_seed = img.seed; - }} - /> - <text>{img.prompt}</text> - </div> - ))} + <div className="imageBox-aiView-history" ref={this.createDashEventsTarget}> + <Button + text="Clear History" + type={Type.SEC} + style={{ marginTop: '5px' }} + size={Size.XSMALL} + onClick={action(() => { + this._prevImgs = []; + this.dataDoc.ai_firefly_history = undefined; + })} + /> + {imgs.length >= 2 && + imgs.map(img => ( + <div> + <img + key={img.pathname} + className="imageBox-aiView-img" + src={img.href} + onClick={() => { + this.dataDoc[this.fieldKey] = new ImageField(img.pathname); + this.dataDoc.ai_firefly_prompt = img.prompt; + this.dataDoc.ai_firefly_seed = img.seed; + }} + /> + <text className="imageBox-aiView-caption">{img.prompt.replace(/ ~~~/g, ',')}</text> + </div> + ))} </div> ); }; @@ -562,6 +574,7 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { const showRegenerate = this.Document[DocData].ai; return ( <div className="imageBox-aiView"> + Edit Image with AI {showRegenerate && ( <div className="imageBox-aiView-regenerate-container"> <div className="imageBox-aiView-regenerate"> @@ -587,9 +600,9 @@ export class ImageBox extends ViewBoxAnnotatableComponent<FieldViewProps>() { const imgField = new ImageField(url); this._prevImgs.length === 0 && this._prevImgs.push({ prompt: StrCast(this.dataDoc.ai_firefly_prompt), seed: this.dataDoc.ai_firefly_seed as number, href: this.paths.lastElement(), pathname: field.url.pathname }); - this._prevImgs.unshift({ prompt: newImgs[0].prompt, seed: newImgs[0].seed, href: this.paths.lastElement(), pathname: url }); - this.dataDoc.ai_firefly_history = `${this._prevImgs}`; this.dataDoc[this.fieldKey] = imgField; + this._prevImgs.unshift({ prompt: newImgs[0].prompt, seed: newImgs[0].seed, href: this.paths.lastElement(), pathname: url }); + this.dataDoc.ai_firefly_history = JSON.stringify(this._prevImgs); this._regenerateLoading = false; this._regenInput = ''; } diff --git a/src/client/views/smartdraw/DrawingFillHandler.tsx b/src/client/views/smartdraw/DrawingFillHandler.tsx index 423e3b2ac..57327631a 100644 --- a/src/client/views/smartdraw/DrawingFillHandler.tsx +++ b/src/client/views/smartdraw/DrawingFillHandler.tsx @@ -1,7 +1,7 @@ import { imageUrlToBase64 } from '../../../ClientUtils'; import { Doc } from '../../../fields/Doc'; import { DocData } from '../../../fields/DocSymbols'; -import { ImageCast } from '../../../fields/Types'; +import { DocCast, ImageCast } from '../../../fields/Types'; import { Upload } from '../../../server/SharedMediaTypes'; import { gptDescribeImage } from '../../apis/gpt/GPT'; import { Docs } from '../../documents/Documents'; @@ -11,10 +11,23 @@ import { OpenWhere } from '../nodes/OpenWhere'; import { AspectRatioLimits, FireflyDimensionsMap, FireflyImageDimensions, FireflyStylePresets } from './FireflyConstants'; export class DrawingFillHandler { - static drawingToImage = (drawing: Doc, strength: number, user_prompt: string) => { + static drawingToImage = async (drawing: Doc, strength: number, user_prompt: string) => { const docData = drawing[DocData]; const tags: string[] = ((docData?.tags as unknown as string[]) ?? []).map(tag => tag.slice(1)) ?? []; const styles = tags.filter(tag => FireflyStylePresets.has(tag)); + const styleDocs = Doc.Links(drawing) + .map(link => Doc.getOppositeAnchor(link, drawing)) + .map(anchor => anchor && DocCast(anchor.embedContainer)); + const styleRef = styleDocs.filter(doc => doc !== undefined && doc.type === 'image').lastElement(); + let styleUrl: string | undefined; + if (styleRef) { + const styleImg = await DocumentView.GetDocImage(styleRef); + if (styleImg) { + const { href } = ImageCast(styleImg).url; + const hrefParts = href.split('.'); + styleUrl = `${hrefParts.slice(0, -1).join('.')}_o.${hrefParts.lastElement()}`; + } + } DocumentView.GetDocImage(drawing)?.then(imageField => { if (imageField) { const aspectRatio = (drawing.width as number) / (drawing.height as number); @@ -35,7 +48,7 @@ export class DrawingFillHandler { .then((hrefBase64: string) => gptDescribeImage(hrefBase64)) .then(prompt => { Networking.PostToServer('/queryFireflyImageFromStructure', - { prompt: `${user_prompt}, ${prompt}`, width: dims.width, height: dims.height, structureUrl, strength, styles }) + { prompt: `${user_prompt}, ${prompt}`, width: dims.width, height: dims.height, structure: structureUrl, strength: strength, presets: styles, styleRef: styleUrl}) .then((info: Upload.ImageInformation) => DocumentViewInternal.addDocTabFunc(Docs.Create.ImageDocument(info.accessPaths.agnostic.client, { ai: 'firefly', ai_firefly_prompt: user_prompt || prompt, _width: 500, data_nativeWidth: info.nativeWidth, data_nativeHeight: info.nativeHeight }), OpenWhere.addRight) diff --git a/src/server/ApiManagers/FireflyManager.ts b/src/server/ApiManagers/FireflyManager.ts index a1f8fab8d..185752404 100644 --- a/src/server/ApiManagers/FireflyManager.ts +++ b/src/server/ApiManagers/FireflyManager.ts @@ -20,7 +20,7 @@ export default class FireflyManager extends ApiManager { return undefined; }); - generateImageFromStructure = (prompt: string = 'a realistic illustration of a cat coding', width: number = 2048, height: number = 2048, structureUrl: string, strength: number = 50, styles: string[]) => + generateImageFromStructure = (prompt: string = 'a realistic illustration of a cat coding', width: number = 2048, height: number = 2048, structureUrl: string, strength: number = 50, styles: string[], styleUrl: string) => this.getBearerToken().then(response => response?.json().then((data: { access_token: string }) => //prettier-ignore @@ -44,7 +44,12 @@ export default class FireflyManager extends ApiManager { }, }, //prettier-ignore - style: { presets: styles } + style: { + imageReference: { + source: { uploadId: styleUrl } + }, + presets: styles + } }), }) .then(response2 => response2.json().then(json => JSON.stringify((json.outputs?.[0] as { image: { url: string } })?.image))) @@ -235,16 +240,28 @@ export default class FireflyManager extends ApiManager { method: Method.POST, subscription: '/queryFireflyImageFromStructure', secureHandler: async ({ req, res }) => - this.uploadImageToDropbox(req.body.structureUrl).then(uploadUrl => - uploadUrl instanceof Error - ? _invalid(res, uploadUrl.message) - : this.generateImageFromStructure(req.body.prompt, req.body.width, req.body.height, uploadUrl, req.body.strength, req.body.styles).then(fire => - DashUploadUtils.UploadImage(JSON.parse(fire ?? '').url).then(info => { - if (info instanceof Error) _invalid(res, info.message); - else _success(res, info); - }) - ) - ), + this.uploadImageToDropbox(req.body.styleRef) + .then(styleUrl => { + if (styleUrl instanceof Error) { + _invalid(res, styleUrl.message); + throw new Error('Error uploading images to dropbox'); + } + return this.uploadImageToDropbox(req.body.structure).then(structureUrl => { + if (structureUrl instanceof Error) { + _invalid(res, structureUrl.message); + throw new Error('Error uploading images to dropbox'); + } + return { styleUrl, structureUrl }; + }); + }) + .then(uploads => + this.generateImageFromStructure(req.body.prompt, req.body.width, req.body.height, uploads.structureUrl, req.body.strength, req.body.presets, uploads.styleUrl).then(fire => + DashUploadUtils.UploadImage(JSON.parse(fire ?? '').url).then(info => { + if (info instanceof Error) _invalid(res, info.message); + else _success(res, info); + }) + ) + ), }); register({ method: Method.POST, |