Skip to content

Commit

Permalink
Merge pull request #6 from Cyberhan123/easy-feature
Browse files Browse the repository at this point in the history
some easy feature
  • Loading branch information
Cyberhan123 authored Jan 7, 2024
2 parents a4d466c + 152d873 commit 37bd3a5
Show file tree
Hide file tree
Showing 12 changed files with 389 additions and 198 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<img src="./assets/img.png" width="256x">
</p>

Minimalist stable diffusion desktop application with only one executable file (No python required).
Minimalist stable-diffusion desktop application with only one executable file (No python required).

**It is currently in preview, so there may be many issues. Feedback is welcome.**

Expand Down
80 changes: 80 additions & 0 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@ import (
"fmt"
sd "github.com/seasonjs/stable-diffusion"
"github.com/wailsapp/wails/v2/pkg/runtime"
"image"
"image/png"
"io"
"os"
"path/filepath"
goruntime "runtime"
"strings"
)

// App struct
Expand Down Expand Up @@ -147,3 +151,79 @@ func (a *App) SetOptions(option sd.Options) {
a.options = &option
a.sd.SetOptions(option)
}

func (a *App) SaveImage(imageBase64 string) bool {
dialog, err := runtime.SaveFileDialog(a.ctx, runtime.SaveDialogOptions{})
if err != nil {
runtime.LogError(a.ctx, err.Error())
return false
}

if len(dialog) == 0 {
return false
}

parts := strings.Split(imageBase64, ";base64,")
if len(parts) != 2 {
_, _ = runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.InfoDialog,
Title: "Save Image Failed",
Message: err.Error(),
})
return false
}

decoded, err := base64.StdEncoding.DecodeString(parts[1])
if err != nil {
_, _ = runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.InfoDialog,
Title: "Save Image Failed",
Message: err.Error(),
})
runtime.LogError(a.ctx, err.Error())
return false
}

reader := strings.NewReader(string(decoded))
img, _, err := image.Decode(reader)
if err != nil {
_, _ = runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.InfoDialog,
Title: "Save Image Failed",
Message: err.Error(),
})
runtime.LogError(a.ctx, err.Error())
return false
}

file, err := os.Create(dialog)
if err != nil {
_, _ = runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.InfoDialog,
Title: "Save Image Failed",
Message: err.Error(),
})
runtime.LogError(a.ctx, err.Error())
return false
}
defer file.Close()

err = png.Encode(file, img)
if err != nil {
_, _ = runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.InfoDialog,
Title: "Save Image Failed",
Message: err.Error(),
})
runtime.LogError(a.ctx, err.Error())
return false
}

_, _ = runtime.MessageDialog(a.ctx, runtime.MessageDialogOptions{
Type: runtime.InfoDialog,
Title: "Save Image Success",
Message: fmt.Sprintf("Image saved to %s", dialog),
})

return true
}
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"preview": "vite preview"
},
"dependencies": {
"@ant-design/icons": "^5.2.6",
"ahooks": "^3.7.8",
"antd": "^5.12.5",
"lodash-es": "^4.17.21",
Expand Down
2 changes: 1 addition & 1 deletion frontend/package.json.md5
Original file line number Diff line number Diff line change
@@ -1 +1 @@
345c5f54959eff6a40e9fe7c677750f4
7bf71b5bfcacc78a6f1e620adc62bfb9
3 changes: 3 additions & 0 deletions frontend/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

204 changes: 10 additions & 194 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,206 +1,22 @@
import {useState} from 'react';
import ImageGallery from "./components/ImageGallery";
import {useLayoutEffect, useState} from 'react';
import {useMount} from "ahooks";
import {ConfigProvider, Layout, theme} from "antd";
import Predict from "./pages/Predict";

import './App.css';
import {useRequest} from "ahooks";
import {LoadFromFile, Predict} from "../wailsjs/go/main/App";
import {Button, Col, Flex, Form, Input, InputNumber, Layout, Row, Select, Slider, Switch} from "antd";
import Terminal from "./components/Terminal";
import {omit} from "lodash-es";

function App() {
const [hasLoadModel, setHasLoadModel] = useState(false)

const [form] = Form.useForm();
const {darkAlgorithm, defaultAlgorithm} = theme;

const {runAsync: loadModel, loading: loadModelLoading} = useRequest(async () => {
return await LoadFromFile()
}, {
manual: true
})
// NegativePrompt string
// ClipSkip int
// CfgScale float32
// Width int
// Height int
// SampleMethod SampleMethod
// SampleSteps int
// Strength float32
// Seed int64
// BatchCount int
// OutputsImageType OutputsImageType
const {runAsync: predict, loading: predictLoading, data: images} = useRequest(async (params) => {
const fullParams = omit(params, ["Prompt", "RandomSeed"])
fullParams.Width = Number(fullParams.Width)
fullParams.Height = Number(fullParams.Height)
fullParams.SampleMethod = Number(fullParams.SampleMethod)
fullParams.SampleSteps = Number(fullParams.SampleSteps)
fullParams.Seed = Number(fullParams.Seed)
fullParams.BatchCount = Number(fullParams.BatchCount)
fullParams.OutputsImageType = "PNG"
return await Predict(params.Prompt, fullParams)
}, {
manual: true
})
function App() {
const [isDark, setIsDark] = useState<boolean>(window?.matchMedia?.('(prefers-color-scheme: dark)')?.matches ?? false);

return <div>
return <ConfigProvider theme={{algorithm: isDark ? darkAlgorithm : defaultAlgorithm}}>
<Layout>
<Layout.Content style={{height: "100vh"}}>
<Row style={{margin: 10}}>
<Col span={10}>
<Form
labelCol={{span: 8}}
wrapperCol={{span: 16}}
// style={{maxWidth: 600}}
form={form}
initialValues={{
Prompt: "",
NegativePrompt: "",
CfgScale: 7.0,
Width: 512,
Height: 512,
SampleMethod: 0,
SampleSteps: 20,
Seed: 42,
BatchCount: 1
}}
layout="vertical"
>
<Form.Item label="Prompt" name="Prompt">
<Input.TextArea rows={2}/>
</Form.Item>
<Form.Item label="Negative Prompt" name="NegativePrompt">
<Input.TextArea rows={2}/>
</Form.Item>
<Form.Item label="Sampler Method" name="SampleMethod">
<Select
options={[
{
label: "Euler A",
value: 0
},
{
label: "Euler",
value: 1
},
{
label: "Heun",
value: 2
},
{
label: "DPM2",
value: 3
},
{
label: "DPM++ 2S A",
value: 4
},
{
label: "DPM++ 2M",
value: 5
},
{
label: "DPM++ 2M v2",
value: 6
},
{
label: "LCM",
value: 7
}]}
/>
</Form.Item>
<Form.Item>
<Row>
<Col span={12}>
<Form.Item label={"Steps"} name={["SampleSteps"]}>
<Slider
min={1}
max={50}
step={1}
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label={"CFG Scale"} name={["CfgScale"]}>
<Slider
min={1}
max={20}
step={0.5}
/>

</Form.Item>
</Col>
</Row>
</Form.Item>
<Form.Item>
<Row>
<Col span={12}>
<Form.Item label={"Custom Seed"} name={"Seed"}>
<Input type={"number"}/>
</Form.Item>
</Col>
{/*<Col span={12}>*/}
{/* <Form.Item label={"Random seed"} name={"RandomSeed"}>*/}
{/* <Switch defaultChecked/>*/}
{/* </Form.Item>*/}
{/*</Col>*/}
</Row>
</Form.Item>
<Form.Item>
<Row>
<Col span={12}>
<Form.Item label={"width"} name={"Width"}>
<InputNumber min={128} max={1024} step={128}/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label={"height"} name={"Height"}>
<InputNumber min={128} max={1024} step={128}/>
</Form.Item>
</Col>
</Row>
</Form.Item>
<Form.Item label={"Batch Count"} name={"BatchCount"}>
<Slider
min={1}
max={50}
step={1}
/>
</Form.Item>
</Form>
</Col>
<Col span={14}>
<div className="row-gen">
<Button
loading={loadModelLoading}
onClick={async () => {
const result = await loadModel()
if (result) {
setHasLoadModel(true)
}
}}>
Select Model
</Button>
<Button
style={{marginLeft: 20}}
type="primary"
disabled={!hasLoadModel}
onClick={async () => {
const params = form.getFieldsValue()
await predict(params)
}}>
{predictLoading ? "Cancel" : "Generate"}
</Button>
</div>
<Flex gap="middle" vertical>
<ImageGallery images={images} loading={predictLoading}/>
<Terminal/>
</Flex>
</Col>
</Row>
<Predict/>
</Layout.Content>
</Layout>
</div>;
</ConfigProvider>
}

export default App
29 changes: 29 additions & 0 deletions frontend/src/components/ImageGallery.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
.toolbar-wrapper {
position: fixed;
bottom: 32px;
left: 50%;
padding: 0px 24px;
color: #fff;
font-size: 20px;
background-color: rgba(0, 0, 0, 0.1);
border-radius: 100px;
transform: translateX(-50%);
}

.toolbar-wrapper .anticon {
padding: 12px;
cursor: pointer;
}

.toolbar-wrapper .anticon[disabled] {
cursor: not-allowed;
opacity: 0.3;
}

.toolbar-wrapper .anticon:hover {
opacity: 0.3;
}

.ant-image-preview-mask{
background-color: rgba(0, 0, 0, 0.8);
}
Loading

0 comments on commit 37bd3a5

Please sign in to comment.