add side panel of txt2img and img2img to load local generated images
parent
549b49397a
commit
eedae87ea9
|
|
@ -1,4 +1,4 @@
|
||||||
console.log('[3D Model Loader] loading...');
|
console.log('[Canvas Editor] loading...');
|
||||||
|
|
||||||
async function _import() {
|
async function _import() {
|
||||||
if (!globalThis.canvasEditor || !globalThis.canvasEditor.import) {
|
if (!globalThis.canvasEditor || !globalThis.canvasEditor.import) {
|
||||||
|
|
@ -10,6 +10,8 @@ async function _import() {
|
||||||
|
|
||||||
await _import();
|
await _import();
|
||||||
|
|
||||||
|
let _r = 0;
|
||||||
|
|
||||||
(async function () {
|
(async function () {
|
||||||
const container = gradioApp().querySelector('#canvas-editor-container');
|
const container = gradioApp().querySelector('#canvas-editor-container');
|
||||||
|
|
||||||
|
|
@ -19,6 +21,21 @@ await _import();
|
||||||
const apiKey = gradioApp().querySelector('#canvas-editor-polotno-api-key');
|
const apiKey = gradioApp().querySelector('#canvas-editor-polotno-api-key');
|
||||||
const apiKeyValue = apiKey.value;
|
const apiKeyValue = apiKey.value;
|
||||||
|
|
||||||
|
setLoadMoreFunc(py2js);
|
||||||
|
|
||||||
|
const txt2ImgFilePaths = await py2js('getImgFilePaths', '{"type":"txt2img", "num":1, "size":15}');
|
||||||
|
const img2ImgFilePaths = await py2js('getImgFilePaths', '{"type":"img2img", "num":1, "size":15}');
|
||||||
|
|
||||||
|
const txt2ImgFilePathsJsonData = JSON.parse(txt2ImgFilePaths);
|
||||||
|
const img2ImgFilePathsJsonData = JSON.parse(img2ImgFilePaths);
|
||||||
|
|
||||||
|
function to_gradio(v) {
|
||||||
|
return [v, _r++];
|
||||||
|
}
|
||||||
|
|
||||||
|
setTxt2imgInfoJSON(txt2ImgFilePathsJsonData);
|
||||||
|
setImg2imgInfoJSON(img2ImgFilePathsJsonData);
|
||||||
|
|
||||||
createPolotnoApp({
|
createPolotnoApp({
|
||||||
key: apiKeyValue,
|
key: apiKeyValue,
|
||||||
container: container
|
container: container
|
||||||
|
|
@ -28,16 +45,16 @@ await _import();
|
||||||
|
|
||||||
function dataURLtoFile(dataurl, filename) {
|
function dataURLtoFile(dataurl, filename) {
|
||||||
var arr = dataurl.split(','),
|
var arr = dataurl.split(','),
|
||||||
mime = arr[0].match(/:(.*?);/)[1],
|
mime = arr[0].match(/:(.*?);/)[1],
|
||||||
bstr = atob(arr[1]),
|
bstr = atob(arr[1]),
|
||||||
n = bstr.length,
|
n = bstr.length,
|
||||||
u8arr = new Uint8Array(n);
|
u8arr = new Uint8Array(n);
|
||||||
|
|
||||||
while(n--){
|
while (n--) {
|
||||||
u8arr[n] = bstr.charCodeAt(n);
|
u8arr[n] = bstr.charCodeAt(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new File([u8arr], filename, {type:mime});
|
return new File([u8arr], filename, {type: mime});
|
||||||
}
|
}
|
||||||
|
|
||||||
window.sendImageCanvasEditor = async function (type) {
|
window.sendImageCanvasEditor = async function (type) {
|
||||||
|
|
@ -63,17 +80,17 @@ await _import();
|
||||||
updateGradioImage(imageElems[0], dt);
|
updateGradioImage(imageElems[0], dt);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCanvasEditorTabIndex(){
|
function getCanvasEditorTabIndex() {
|
||||||
const tabCanvasEditorDiv = document.getElementById('tab_canvas_editor');
|
const tabCanvasEditorDiv = document.getElementById('tab_canvas_editor');
|
||||||
const parent = tabCanvasEditorDiv.parentNode;
|
const parent = tabCanvasEditorDiv.parentNode;
|
||||||
const siblings = parent.childNodes;
|
const siblings = parent.childNodes;
|
||||||
|
|
||||||
let index = -1;
|
let index = -1;
|
||||||
for (let i = 0; i < siblings.length; i++) {
|
for (let i = 0; i < siblings.length; i++) {
|
||||||
if (siblings[i] === tabCanvasEditorDiv) {
|
if (siblings[i] === tabCanvasEditorDiv) {
|
||||||
index = i;
|
index = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return index / 3;
|
return index / 3;
|
||||||
|
|
@ -88,7 +105,7 @@ await _import();
|
||||||
}
|
}
|
||||||
|
|
||||||
window.sendImageToCanvasEditor = function () {
|
window.sendImageToCanvasEditor = function () {
|
||||||
const gallerySelector = isTxt2Img()? '#txt2img_gallery': '#img2img_gallery';
|
const gallerySelector = isTxt2Img() ? '#txt2img_gallery' : '#img2img_gallery';
|
||||||
|
|
||||||
const txt2imgGallery = gradioApp().querySelector(gallerySelector);
|
const txt2imgGallery = gradioApp().querySelector(gallerySelector);
|
||||||
|
|
||||||
|
|
@ -115,8 +132,7 @@ await _import();
|
||||||
removable: true,
|
removable: true,
|
||||||
resizable: true,
|
resizable: true,
|
||||||
});
|
});
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
alert("No image selected");
|
alert("No image selected");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -198,7 +214,7 @@ await _import();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function updateGradioImage (element, dt) {
|
function updateGradioImage(element, dt) {
|
||||||
let clearButton = element.querySelector("button[aria-label='Clear']");
|
let clearButton = element.querySelector("button[aria-label='Clear']");
|
||||||
|
|
||||||
if (clearButton) {
|
if (clearButton) {
|
||||||
|
|
@ -215,4 +231,51 @@ await _import();
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function py2js(pyname, ...args) {
|
||||||
|
// call python's function
|
||||||
|
// (1) Set args to gradio's field
|
||||||
|
// (2) Click gradio's button
|
||||||
|
// (3) JS callback will be kicked with return value from gradio
|
||||||
|
|
||||||
|
// (1)
|
||||||
|
return (args.length === 0 ? Promise.resolve() : js2py(pyname + '_args', JSON.stringify(args)))
|
||||||
|
.then(() => {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const callback_name = `canvas-editor-${pyname}`;
|
||||||
|
// (3)
|
||||||
|
globalThis[callback_name] = value => {
|
||||||
|
delete globalThis[callback_name];
|
||||||
|
resolve(value);
|
||||||
|
}
|
||||||
|
// (2)
|
||||||
|
gradioApp().querySelector(`#${callback_name}_get`).click();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function js2py(gradio_field, value) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const callback_name = `canvas-editor-${gradio_field}`;
|
||||||
|
|
||||||
|
// (2)
|
||||||
|
globalThis[callback_name] = () => {
|
||||||
|
|
||||||
|
delete globalThis[callback_name];
|
||||||
|
|
||||||
|
// (3)
|
||||||
|
const callback_after = callback_name + '_after';
|
||||||
|
globalThis[callback_after] = () => {
|
||||||
|
delete globalThis[callback_after];
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
return to_gradio(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
// (1)
|
||||||
|
gradioApp().querySelector(`#${callback_name}_set`).click();
|
||||||
|
});
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
|
|
@ -0,0 +1,134 @@
|
||||||
|
"use strict";
|
||||||
|
var __importDefault = this && this.__importDefault || function (e) {
|
||||||
|
return e && e.__esModule ? e : {default: e}
|
||||||
|
};
|
||||||
|
Object.defineProperty(exports, "__esModule", {value: !0}), exports.ImagesGrid = void 0;
|
||||||
|
const react_1 = __importDefault(require("react")), styled_1 = __importDefault(require("../utils/styled")),
|
||||||
|
core_1 = require("@blueprintjs/core"), l10n_1 = require("../utils/l10n"), page_1 = require("../canvas/page"),
|
||||||
|
ImagesListContainer = (0, styled_1.default)("div", react_1.default.forwardRef)`
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
`, ImagesRow = (0, styled_1.default)("div")`
|
||||||
|
width: 33%;
|
||||||
|
float: left;
|
||||||
|
`, ImgWrapDiv = (0, styled_1.default)("div")`
|
||||||
|
padding: 5px;
|
||||||
|
width: 100%;
|
||||||
|
&:hover .credit {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 500px) {
|
||||||
|
.credit {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`, ImgContainerDiv = (0, styled_1.default)("div")`
|
||||||
|
border-radius: 5px;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: ${e => e["data-shadowenabled"] ? "0 0 5px rgba(16, 22, 26, 0.3)" : ""};
|
||||||
|
`, Img = (0, styled_1.default)("img")`
|
||||||
|
width: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
display: block;
|
||||||
|
`, CreditWrap = (0, styled_1.default)("div")`
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0px;
|
||||||
|
left: 0px;
|
||||||
|
font-size: 10px;
|
||||||
|
padding: 3px;
|
||||||
|
padding-top: 10px;
|
||||||
|
text-align: center;
|
||||||
|
background: linear-gradient(
|
||||||
|
to bottom,
|
||||||
|
rgba(0, 0, 0, 0),
|
||||||
|
rgba(0, 0, 0, 0.4),
|
||||||
|
rgba(0, 0, 0, 0.6)
|
||||||
|
);
|
||||||
|
width: 100%;
|
||||||
|
opacity: 0;
|
||||||
|
color: white;
|
||||||
|
`, NoResults = (0, styled_1.default)("p")`
|
||||||
|
text-align: center;
|
||||||
|
padding: 30px;
|
||||||
|
`, Image = ({
|
||||||
|
url: e,
|
||||||
|
credit: t,
|
||||||
|
onSelect: r,
|
||||||
|
crossOrigin: a,
|
||||||
|
shadowEnabled: l,
|
||||||
|
itemHeight: o,
|
||||||
|
className: i,
|
||||||
|
onLoad: n
|
||||||
|
}) => {
|
||||||
|
const d = null == l || l;
|
||||||
|
return react_1.default.createElement(ImgWrapDiv, {
|
||||||
|
onClick: () => {
|
||||||
|
r()
|
||||||
|
}, className: "polotno-close-panel"
|
||||||
|
}, react_1.default.createElement(ImgContainerDiv, {"data-shadowenabled": d}, react_1.default.createElement(Img, {
|
||||||
|
className: i,
|
||||||
|
style: {height: null != o ? o : "auto"},
|
||||||
|
src: e,
|
||||||
|
draggable: !0,
|
||||||
|
crossOrigin: a,
|
||||||
|
onDragStart: () => {
|
||||||
|
(0, page_1.registerNextDomDrop)((({x: e, y: t}, a) => {
|
||||||
|
r({x: e, y: t}, a)
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
onDragEnd: () => {
|
||||||
|
(0, page_1.registerNextDomDrop)(null)
|
||||||
|
},
|
||||||
|
onLoad: n
|
||||||
|
}), t && react_1.default.createElement(CreditWrap, {className: "credit"}, t)))
|
||||||
|
}, ImagesGrid = ({
|
||||||
|
images: e,
|
||||||
|
onSelect: t,
|
||||||
|
isLoading: r,
|
||||||
|
getPreview: a,
|
||||||
|
loadMore: l,
|
||||||
|
getCredit: o,
|
||||||
|
getImageClassName: i,
|
||||||
|
rowsNumber: n,
|
||||||
|
crossOrigin: d = "anonymous",
|
||||||
|
shadowEnabled: s,
|
||||||
|
itemHeight: c,
|
||||||
|
error: u
|
||||||
|
}) => {
|
||||||
|
const g = n || 2, m = react_1.default.useRef(null), p = [];
|
||||||
|
for (var f = 0; f < g; f++) p.push((e || []).filter(((e, t) => t % g === f)));
|
||||||
|
const _ = react_1.default.useRef(null), h = () => {
|
||||||
|
var t, a, o;
|
||||||
|
const i = (null === (t = m.current) || void 0 === t ? void 0 : t.scrollHeight) > (null === (a = m.current) || void 0 === a ? void 0 : a.offsetHeight) + 5,
|
||||||
|
n = e && e.length,
|
||||||
|
d = Array.from(null === (o = m.current) || void 0 === o ? void 0 : o.querySelectorAll("img")).every((e => e.complete));
|
||||||
|
!i && l && !r && n && d && (_.current || (_.current = window.setTimeout((() => {
|
||||||
|
_.current = null, l && l()
|
||||||
|
}), 100)))
|
||||||
|
}, v = () => {
|
||||||
|
h()
|
||||||
|
};
|
||||||
|
return react_1.default.useEffect((() => (h(), () => {
|
||||||
|
window.clearTimeout(_.current), _.current = null
|
||||||
|
})), [e && e.length, r]), react_1.default.createElement(ImagesListContainer, {
|
||||||
|
onScroll: e => {
|
||||||
|
const t = e.target.scrollHeight - e.target.scrollTop - e.target.offsetHeight;
|
||||||
|
l && !r && t < 200 && l()
|
||||||
|
}, ref: m
|
||||||
|
}, p.map(((e, l) => react_1.default.createElement(ImagesRow, {
|
||||||
|
key: l,
|
||||||
|
style: {width: 100 / g + "%"}
|
||||||
|
}, e.map((e => react_1.default.createElement(Image, {
|
||||||
|
url: a(e),
|
||||||
|
onSelect: (r, a) => t(e, r, a),
|
||||||
|
key: a(e),
|
||||||
|
credit: o && o(e),
|
||||||
|
crossOrigin: d,
|
||||||
|
shadowEnabled: s,
|
||||||
|
itemHeight: c,
|
||||||
|
className: i && i(e),
|
||||||
|
onLoad: v
|
||||||
|
}))), r && react_1.default.createElement("div", {style: {padding: "30px"}}, react_1.default.createElement(core_1.Spinner, null))))), !r && (!e || !e.length) && !u && react_1.default.createElement(NoResults, null, (0, l10n_1.t)("sidePanel.noResults")), u && react_1.default.createElement(NoResults, null, (0, l10n_1.t)("sidePanel.error")))
|
||||||
|
};
|
||||||
|
exports.ImagesGrid = ImagesGrid;
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -11,6 +11,8 @@
|
||||||
"webpack-cli": "^5.0.1"
|
"webpack-cli": "^5.0.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@meronex/icons": "^4.0.0",
|
||||||
|
"mobx-react-lite": "^3.4.3",
|
||||||
"polotno": "^1.8.6",
|
"polotno": "^1.8.6",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0"
|
"react-dom": "^18.2.0"
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import json
|
||||||
|
|
||||||
import gradio as gr
|
import gradio as gr
|
||||||
|
|
||||||
import modules.scripts as scripts
|
import modules.scripts as scripts
|
||||||
|
|
@ -8,6 +10,59 @@ from typing import Callable
|
||||||
from modules.shared import opts
|
from modules.shared import opts
|
||||||
from modules import shared
|
from modules import shared
|
||||||
|
|
||||||
|
def get_file_paths(folder):
|
||||||
|
file_paths = []
|
||||||
|
|
||||||
|
for root, directories, files in os.walk(folder):
|
||||||
|
for filename in files:
|
||||||
|
file_path = os.path.join(root, filename)
|
||||||
|
file_url = 'file=' + file_path.replace('\\', '/')
|
||||||
|
file_paths.append({'url': file_url})
|
||||||
|
|
||||||
|
file_paths.reverse()
|
||||||
|
|
||||||
|
return file_paths
|
||||||
|
|
||||||
|
def get_img_file_paths(args):
|
||||||
|
args = args[2: len(args) - 2]
|
||||||
|
args = args.replace("\\", "")
|
||||||
|
|
||||||
|
page_info = json.loads(args)
|
||||||
|
|
||||||
|
page_type = page_info['type']
|
||||||
|
page_num = int(page_info['num'])
|
||||||
|
page_size = int(page_info['size'])
|
||||||
|
|
||||||
|
path = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
|
||||||
|
path = os.path.dirname(path)
|
||||||
|
|
||||||
|
path = os.path.dirname(path)
|
||||||
|
|
||||||
|
path = os.path.dirname(path)
|
||||||
|
|
||||||
|
if page_type == 'txt2img':
|
||||||
|
path = os.path.join(path, "outputs", "txt2img-images")
|
||||||
|
elif page_type == 'img2img':
|
||||||
|
path = os.path.join(path, "outputs", "img2img-images")
|
||||||
|
|
||||||
|
img_file_paths = get_file_paths(path)
|
||||||
|
|
||||||
|
total_len = len(img_file_paths)
|
||||||
|
|
||||||
|
start = (page_num - 1) * page_size
|
||||||
|
end = start + page_size
|
||||||
|
|
||||||
|
if start > total_len:
|
||||||
|
return json.dumps("[false]")
|
||||||
|
elif end > total_len:
|
||||||
|
end = total_len
|
||||||
|
|
||||||
|
img_file_paths = img_file_paths[start: end]
|
||||||
|
|
||||||
|
return json.dumps(img_file_paths)
|
||||||
|
# 要遍历的文件夹路径
|
||||||
|
|
||||||
class Script(scripts.Script):
|
class Script(scripts.Script):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
@ -109,6 +164,10 @@ def on_ui_tabs():
|
||||||
|
|
||||||
gr.HTML(value='\n'.join(js_), elem_id=import_id, visible=False)
|
gr.HTML(value='\n'.join(js_), elem_id=import_id, visible=False)
|
||||||
|
|
||||||
|
with gr.Group(visible=False):
|
||||||
|
sink = gr.HTML(value='', visible=False) # to suppress error in javascript
|
||||||
|
jscall('getImgFilePaths', get_img_file_paths, id, js, sink)
|
||||||
|
|
||||||
with gr.Row():
|
with gr.Row():
|
||||||
gr.HTML('<div id="canvas-editor-container"></div>')
|
gr.HTML('<div id="canvas-editor-container"></div>')
|
||||||
with gr.Row():
|
with gr.Row():
|
||||||
|
|
|
||||||
221
src/index.js
221
src/index.js
|
|
@ -1,31 +1,205 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from 'react-dom/client';
|
||||||
import { PolotnoContainer, SidePanelWrap, WorkspaceWrap } from 'polotno';
|
import {PolotnoContainer, SidePanelWrap, WorkspaceWrap} from 'polotno';
|
||||||
import { Toolbar } from 'polotno/toolbar/toolbar';
|
import {Toolbar} from 'polotno/toolbar/toolbar';
|
||||||
import { ZoomButtons } from 'polotno/toolbar/zoom-buttons';
|
import {ZoomButtons} from 'polotno/toolbar/zoom-buttons';
|
||||||
import { SidePanel } from 'polotno/side-panel';
|
import {SidePanel} from 'polotno/side-panel';
|
||||||
import { Workspace } from 'polotno/canvas/workspace';
|
import {Workspace} from 'polotno/canvas/workspace';
|
||||||
|
import {getImageSize} from 'polotno/utils/image';
|
||||||
|
import {createStore} from 'polotno/model/store';
|
||||||
|
import {ImagesGrid} from 'polotno/side-panel/images-grid';
|
||||||
|
import {observer} from 'mobx-react-lite';
|
||||||
|
import {SectionTab} from 'polotno/side-panel';
|
||||||
|
import {
|
||||||
|
TemplatesSection,
|
||||||
|
TextSection,
|
||||||
|
PhotosSection,
|
||||||
|
ElementsSection,
|
||||||
|
UploadSection,
|
||||||
|
BackgroundSection,
|
||||||
|
LayersSection,
|
||||||
|
SizeSection,
|
||||||
|
} from 'polotno/side-panel';
|
||||||
|
import {DEFAULT_SECTIONS} from "polotno/side-panel/side-panel";
|
||||||
|
|
||||||
import { createStore } from 'polotno/model/store';
|
let _txt2imgInfoJSON;
|
||||||
|
let _img2imgInfoJSON;
|
||||||
|
|
||||||
export const App = ({ store }) => {
|
export const setTxt2imgInfoJSON = (txt2imgInfoJSON) => {
|
||||||
return (
|
_txt2imgInfoJSON = txt2imgInfoJSON;
|
||||||
<PolotnoContainer style={{ width: '95vw', height: '100vh' }}>
|
}
|
||||||
<SidePanelWrap>
|
|
||||||
<SidePanel store={store} />
|
export const setImg2imgInfoJSON = (img2imgInfoJSON) => {
|
||||||
</SidePanelWrap>
|
_img2imgInfoJSON = img2imgInfoJSON;
|
||||||
<WorkspaceWrap>
|
}
|
||||||
<Toolbar store={store} />
|
|
||||||
<Workspace store={store} />
|
let txt2ImgPageNum = 2;
|
||||||
<ZoomButtons store={store} />
|
let img2ImgPageNum = 2;
|
||||||
</WorkspaceWrap>
|
|
||||||
</PolotnoContainer>
|
export const Txt2ImgPhotosPanel = observer(({store}) => {
|
||||||
);
|
const [images, setImages] = React.useState([]);
|
||||||
|
|
||||||
|
async function loadImages() {
|
||||||
|
setImages(_txt2imgInfoJSON);
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
||||||
|
}
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
loadImages();
|
||||||
|
},
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{height: '100%', display: 'flex', flexDirection: 'column'}}>
|
||||||
|
<p>txt2img Library</p>
|
||||||
|
<ImagesGrid
|
||||||
|
images={images}
|
||||||
|
getPreview={(image) => image.url}
|
||||||
|
onSelect={async (image, pos) => {
|
||||||
|
const {width, height} = await getImageSize(image.url);
|
||||||
|
store.activePage.addElement({
|
||||||
|
type: 'image',
|
||||||
|
src: image.url,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
x: pos ? pos.x : store.width / 2 - width / 2,
|
||||||
|
y: pos ? pos.y : store.height / 2 - height / 2,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
rowsNumber={2}
|
||||||
|
isLoading={!images.length}
|
||||||
|
loadMore={async () => {
|
||||||
|
let pageInfo = '{"type": "txt2img", "num": pageNum, "size": 15}';
|
||||||
|
|
||||||
|
pageInfo = pageInfo.replace("pageNum", txt2ImgPageNum.toString());
|
||||||
|
|
||||||
|
const txt2ImgFilePaths = await _loadMoreFunc('getImgFilePaths', pageInfo)
|
||||||
|
|
||||||
|
const txt2ImgFilePathsJsonData = JSON.parse(txt2ImgFilePaths);
|
||||||
|
|
||||||
|
_txt2imgInfoJSON = _txt2imgInfoJSON.concat(txt2ImgFilePathsJsonData);
|
||||||
|
|
||||||
|
await loadImages();
|
||||||
|
|
||||||
|
txt2ImgPageNum = txt2ImgPageNum + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
let _loadMoreFunc;
|
||||||
|
|
||||||
|
export const setLoadMoreFunc = (loadMoreFunc) => {
|
||||||
|
_loadMoreFunc = loadMoreFunc;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Img2TxtPhotosPanel = observer(({store}) => {
|
||||||
|
const [images, setImages] = React.useState([]);
|
||||||
|
|
||||||
|
async function loadImages() {
|
||||||
|
// here we should implement your own API requests
|
||||||
|
setImages(_img2imgInfoJSON);
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 3000));
|
||||||
|
}
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
loadImages();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{height: '100%', display: 'flex', flexDirection: 'column'}}>
|
||||||
|
<p>img2img Library</p>
|
||||||
|
<ImagesGrid
|
||||||
|
images={images}
|
||||||
|
getPreview={(image) => image.url}
|
||||||
|
onSelect={async (image, pos) => {
|
||||||
|
const {width, height} = await getImageSize(image.url);
|
||||||
|
store.activePage.addElement({
|
||||||
|
type: 'image',
|
||||||
|
src: image.url,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
x: pos ? pos.x : store.width / 2 - width / 2,
|
||||||
|
y: pos ? pos.y : store.height / 2 - height / 2,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
rowsNumber={2}
|
||||||
|
isLoading={!images.length}
|
||||||
|
loadMore={async () => {
|
||||||
|
let pageInfo = '{"type": "img2img", "num": pageNum, "size": 15}';
|
||||||
|
|
||||||
|
pageInfo = pageInfo.replace("pageNum", img2ImgPageNum.toString());
|
||||||
|
|
||||||
|
const img2ImgFilePaths = await _loadMoreFunc('getImgFilePaths', pageInfo)
|
||||||
|
|
||||||
|
const img2ImgFilePathsJsonData = JSON.parse(img2ImgFilePaths);
|
||||||
|
|
||||||
|
_img2imgInfoJSON = _img2imgInfoJSON.concat(img2ImgFilePathsJsonData);
|
||||||
|
|
||||||
|
await loadImages();
|
||||||
|
|
||||||
|
img2ImgPageNum = img2ImgPageNum + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const Txt2ImgPhotos = {
|
||||||
|
name: 'sd-txt2img',
|
||||||
|
Tab: (props) => (
|
||||||
|
<SectionTab name="Txt2img" {...props}>
|
||||||
|
</SectionTab>
|
||||||
|
),
|
||||||
|
Panel: Txt2ImgPhotosPanel,
|
||||||
|
};
|
||||||
|
|
||||||
|
const Img2ImgPhotos = {
|
||||||
|
name: 'sd-img2img',
|
||||||
|
Tab: (props) => (
|
||||||
|
<SectionTab name="Img2img" {...props}>
|
||||||
|
</SectionTab>
|
||||||
|
),
|
||||||
|
// we need observer to update component automatically on any store changes
|
||||||
|
Panel: Img2TxtPhotosPanel,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const sections = [
|
||||||
|
TemplatesSection,
|
||||||
|
TextSection,
|
||||||
|
PhotosSection,
|
||||||
|
ElementsSection,
|
||||||
|
UploadSection,
|
||||||
|
BackgroundSection,
|
||||||
|
LayersSection,
|
||||||
|
SizeSection,
|
||||||
|
Txt2ImgPhotos,
|
||||||
|
Img2ImgPhotos,
|
||||||
|
];
|
||||||
|
|
||||||
|
export const App = ({store}) => {
|
||||||
|
return (
|
||||||
|
<PolotnoContainer style={{width: '95vw', height: '90vh'}}>
|
||||||
|
<SidePanelWrap>
|
||||||
|
<SidePanel store={store} sections={sections}/>
|
||||||
|
</SidePanelWrap>
|
||||||
|
<WorkspaceWrap>
|
||||||
|
<Toolbar store={store}/>
|
||||||
|
<Workspace store={store}/>
|
||||||
|
<ZoomButtons store={store}/>
|
||||||
|
</WorkspaceWrap>
|
||||||
|
</PolotnoContainer>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
let _store;
|
let _store;
|
||||||
|
|
||||||
export const createPolotnoApp = ({ key, container }) => {
|
export const createPolotnoApp = ({key, container}) => {
|
||||||
const store = createStore({
|
const store = createStore({
|
||||||
key: key,
|
key: key,
|
||||||
showCredit: true,
|
showCredit: true,
|
||||||
|
|
@ -35,7 +209,7 @@ export const createPolotnoApp = ({ key, container }) => {
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(container);
|
const root = ReactDOM.createRoot(container);
|
||||||
|
|
||||||
root.render(<App store={store} />);
|
root.render(<App store={store}/>);
|
||||||
|
|
||||||
_store = store;
|
_store = store;
|
||||||
};
|
};
|
||||||
|
|
@ -45,4 +219,7 @@ export const getPolotnoStore = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
window.createPolotnoApp = createPolotnoApp;
|
window.createPolotnoApp = createPolotnoApp;
|
||||||
window.getPolotnoStore = getPolotnoStore;
|
window.getPolotnoStore = getPolotnoStore;
|
||||||
|
window.setTxt2imgInfoJSON = setTxt2imgInfoJSON;
|
||||||
|
window.setImg2imgInfoJSON = setImg2imgInfoJSON;
|
||||||
|
window.setLoadMoreFunc = setLoadMoreFunc;
|
||||||
Loading…
Reference in New Issue