Browse Source

🎉 v1.4.0

tags/v1.4.4
Ivan Bravo Bravo 9 months ago
parent
commit
785ef1dbfa

+ 5
- 1
src/assets/css/components/_form.scss View File

@@ -36,7 +36,11 @@
}

&[disabled] {
@apply opacity-75 cursor-not-allowed;
@apply opacity-75 text-generic-900 cursor-not-allowed;
}

&.input--sm {
@apply text-sm px-2;
}
}


+ 2
- 3
src/assets/css/reset/_base.scss View File

@@ -20,9 +20,8 @@ html {
@apply bg-black text-generic-500 font-sans;
background-image: url('~assets/images/papyrus-dark.webp'); /* Background pattern from Toptal Subtle Patterns */
font-size: 16px;
-webkit-text-size-adjust: 100%;
-webkit-font-smoothing: antialiased;
box-sizing: border-box;
text-size-adjust: 100%;
font-smoothing: antialiased;
}

body,

+ 0
- 2
src/components/Layout/index.js View File

@@ -2,8 +2,6 @@ import Vue from 'vue'

import LayoutTopbar from './Topbar'
import LayoutNavbar from './Navbar'
import LayoutQueuebar from './Queuebar'

Vue.component('LayoutTopbar', LayoutTopbar)
Vue.component('LayoutNavbar', LayoutNavbar)
Vue.component('LayoutQueuebar', LayoutQueuebar)

+ 5
- 5
src/components/Nudity/PhotoRun.vue View File

@@ -57,9 +57,9 @@
</p>
</div>

<div v-show="run.finished && !run.failed" class="content__item">
<div v-show="run.finished && run.outputFile.exists" class="content__item">
<button v-tooltip="'Save photo'" class="button button--success button--sm" @click.prevent="save">
<font-awesome-icon icon="download" />
<font-awesome-icon icon="save" />
</button>
</div>

@@ -242,7 +242,7 @@ export default {
@apply relative border-2 border-dark-100;
background-image: url('~@/assets/images/curls.png'); /* Background pattern from Toptal Subtle Patterns */
min-height: 512px;
transition: all .15s linear;
transition: border-color .2s linear;

&.run--failed {
@apply border-danger-500;
@@ -268,7 +268,7 @@ export default {
.run__preview {
@apply absolute opacity-0 left-0 right-0 top-0 bottom-0 z-10;
@apply bg-cover bg-center;
transition: all .3s linear;
transition: opacity .3s linear;
}
}

@@ -277,7 +277,7 @@ export default {
@apply absolute z-20;
@apply flex opacity-0 bg-dark-500-80 w-full;
backdrop-filter: blur(6px);
transition: all .15s linear;
transition: opacity .1s linear;
}

.run__preferences {

+ 4
- 102
src/components/Nudity/Upload.vue View File

@@ -20,21 +20,6 @@
</div>
</div>

<!-- Dropzone -->
<div
id="uploader-dropzone"
class="uploader__dropzone"
:class="{'is-dragging': isDragging}"
@dragenter="onDragEnter"
@dragover="onDragOver"
@dragleave="onDragLeave"
@drop="openDrop">
<p class="dropzone-hint">
<font-awesome-icon icon="camera" />
Drop the dream here!
</p>
</div>

<div id="uploader-alternatives" class="uploader__alt">
<!-- File -->
<div class="box">
@@ -129,19 +114,17 @@

<script>
import {
isNil, isEmpty, startsWith,
map, isArray,
isEmpty, startsWith, map,
} from 'lodash'
import { Nudify } from '~/modules/nudify'
import { Consola } from '~/modules/consola'
import { tutorial } from '~/modules'

const consola = Consola.create('upload')
import { UploadMixin } from '~/mixins'

const { instagram } = $provider
const { dialog } = $provider.api

export default {
mixins: [UploadMixin],

props: {
model: {
type: String,
@@ -152,7 +135,6 @@ export default {
data: () => ({
webAddress: '',
instagramPhoto: '',
isDragging: false,
}),

mounted() {
@@ -160,28 +142,6 @@ export default {
},

methods: {
/**
* File selected, start a new transformation process
*/
addFile(file) {
if (isNil(file)) {
return
}

Nudify.addFile(file.path)
},

/**
*
*/
async addFiles(files) {
if (!isArray(files)) {
return
}

await Nudify.addFiles(files)
},

/**
*
*/
@@ -201,17 +161,6 @@ export default {
event.target.value = ''
},

/**
*
*/
openFolder() {
const paths = dialog.showOpenDialogSync({
properties: ['openDirectory'],
})

this.addFiles(paths)
},

/**
*
*/
@@ -253,53 +202,6 @@ export default {

this.instagramPhoto = ''
},

/**
*
*/
onDragEnter(event) {
event.dataTransfer.dropEffect = 'copy'
this.isDragging = true
},

/**
*
*/
onDragLeave() {
this.isDragging = false
},

/**
*
*/
onDragOver(event) {
event.preventDefault()
event.stopPropagation()
event.dataTransfer.dropEffect = 'copy'
this.isDragging = true
},

/**
*
*/
openDrop(event) {
event.preventDefault()
event.stopPropagation()

this.isDragging = false

const { files } = event.dataTransfer
const url = event.dataTransfer.getData('url')

if (url.length > 0) {
Nudify.addUrl(url)
consola.track('DROP_URL')
} else if (files.length > 0) {
const paths = map(files, 'path')
this.addFiles(paths)
consola.track('DROP_FILE')
}
},
},
}
</script>

src/components/Layout/Queuebar.vue → src/components/Queue/QueueBar.vue View File

@@ -25,14 +25,11 @@
</div>

<div class="jobs__list">
<figure
<QueuePhoto
v-for="(photo, index) of $nudify.waiting"
:key="index"
class="job"
:class="{ 'job--running': photo.running }"
@click.prevent="openJob(photo.id)">
<img :src="photo.file.path" data-private>
</figure>
:photo="photo"
data-private />
</div>
</section>

@@ -61,13 +58,11 @@
</div>

<div class="jobs__list">
<figure
<QueuePhoto
v-for="(photo, index) of $nudify.pending"
:key="index"
class="job"
@click.prevent="openJob(photo.id)">
<img :src="photo.file.path" data-private>
</figure>
:photo="photo"
data-private />
</div>
</section>

@@ -96,14 +91,11 @@
</div>

<div class="jobs__list">
<figure
<QueuePhoto
v-for="(photo, index) of $nudify.finished"
:key="index"
class="job"
:class="{ 'job--failed': photo.failed }"
@click.prevent="openJob(photo.id)">
<img :src="photo.file.path" data-private>
</figure>
:photo="photo"
data-private />
</div>
</section>
</div>
@@ -111,11 +103,7 @@

<script>
export default {
methods: {
openJob(photoId) {
this.$router.push(`/nudify/${photoId}`)
},
},

}
</script>

@@ -177,7 +165,8 @@ section {

.jobs__list {
@apply flex-1;
@apply px-4 py-2 overflow-y-auto max-h-full;
@apply py-2 overflow-y-auto max-h-full;
will-change: scroll-position;

.job {
@apply inline-block mb-2 cursor-pointer;

+ 130
- 0
src/components/Queue/QueuePhoto.vue View File

@@ -0,0 +1,130 @@
<template>
<div class="photo" :class="photoClass">
<div class="photo__preview" :style="previewStyle" />

<div class="photo__content">
<span v-show="photo.running || photo.finished">{{ photo.timer.duration }}s</span>

<button v-tooltip="'Open'" @click="open">
<font-awesome-icon icon="external-link-square-alt" />
</button>

<button v-show="!photo.running && !photo.waiting" v-tooltip="'Add to Queue'" @click="add">
<font-awesome-icon icon="play" />
</button>

<button v-show="photo.waiting" v-tooltip="'Remove from Queue'" @click="cancel">
<font-awesome-icon icon="sign-out-alt" />
</button>

<button v-show="photo.running" v-tooltip="'Stop'" @click="stop">
<font-awesome-icon icon="stop" />
</button>
</div>
</div>
</template>

<script>
export default {
props: {
photo: {
type: Object,
required: true,
},
},

computed: {
previewStyle() {
let photoPath = this.photo.file.path

if (this.photo.finished && this.photo.runs.length > 0) {
const [run] = this.photo.runs

if (run.outputFile.exists) {
photoPath = run.outputFile.path
}
}

return {
backgroundImage: `url("${photoPath}")`,
}
},

photoClass() {
return {
'photo--running': this.photo.running,
'photo--failed': this.photo.failed,
}
},
},

methods: {
open() {
this.$router.push(`/nudify/${this.photo.id}`)
},

add() {
this.photo.add()
},

cancel() {
this.photo.cancel()
},

stop() {
this.photo.cancel()
},
},
}
</script>

<style lang="scss" scoped>
.photo {
@apply mb-2 relative border-2 border-transparent;
height: 40px;
will-change: transform;

&.photo--running {
@apply border-primary-500;
}

&.photo--failed {
@apply border-danger-500;
}

&:hover {
.photo__content {
@apply opacity-100;
}
}

.photo__preview {
@apply absolute top-0 bottom-0 left-0 right-0 z-10;
@apply bg-cover bg-center;
}

.photo__content {
@apply absolute top-0 bottom-0 left-0 right-0 z-30;
@apply flex bg-dark-500-60 opacity-0;
backdrop-filter: blur(4px);
transition: opacity .1s linear;

span, button {
@apply flex-1;
}

span {
@apply flex items-center justify-center;
@apply text-white font-semibold;
}

button {
@apply outline-none;

&:hover {
@apply text-primary;
}
}
}
}
</style>

+ 15
- 0
src/components/Queue/index.js View File

@@ -0,0 +1,15 @@
// DreamTime.
// Copyright (C) DreamNet. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License 3.0 as published by
// the Free Software Foundation. See <https://www.gnu.org/licenses/gpl-3.0.html>
//
// Written by Ivan Bravo Bravo <ivan@dreamnet.tech>, 2020.

import Vue from 'vue'
import QueueBar from './QueueBar'
import QueuePhoto from './QueuePhoto'

Vue.component('QueueBar', QueueBar)
Vue.component('QueuePhoto', QueuePhoto)

+ 2
- 3
src/components/UI/AppPhoto.vue View File

@@ -1,5 +1,5 @@
<template>
<figure class="c-photo" :class="{'photo--hover': hover}">
<figure class="photo" :class="{'photo--hover': hover}">
<img :src="src">

<p v-if="$slots.default" class="photo__label">
@@ -25,11 +25,10 @@ export default {
</script>

<style lang="scss" scoped>
.c-photo {
.photo {
@apply relative flex flex-col z-50;
box-sizing: content-box;
transition: all 0.2s ease-in-out;
max-width: 200px;

&.photo--hover:hover {
transform: scale(1.5);

+ 48
- 24
src/components/UI/BoxItem.vue View File

@@ -1,5 +1,6 @@
<template>
<div v-if="isVisible" class="box__item" :class="cssClass" @click="click">
<!-- Icon -->
<slot name="icon">
<div v-if="icon" class="item__icon">
<img v-if="isImageIcon" :src="icon">
@@ -7,18 +8,18 @@
</div>
</slot>

<div class="item__content">
<div class="item__text">
<span class="item__label" v-html="label" />
<!-- Label & Description -->
<div v-if="label" class="item__content">
<span class="item__label" v-html="label" />

<slot name="description">
<span v-if="description" class="item__description" v-html="description" />
</slot>
</div>
<slot name="description">
<span v-if="description" class="item__description" v-html="description" />
</slot>
</div>

<div class="item__action">
<slot />
</div>
<!-- Actions -->
<div v-if="$slots.default" class="item__action" :class="{ 'item__action--full': !hasIcon && !label }">
<slot />
</div>
</div>
</template>
@@ -39,7 +40,7 @@ export default {

label: {
type: String,
required: true,
default: undefined,
},

description: {
@@ -68,6 +69,10 @@ export default {
return isNil(this.version) || this.version === dream.version
},

hasIcon() {
return !isNil(this.icon) || !isNil(this.$slots.icon)
},

isImageIcon() {
return startsWith(this.icon, 'http') || startsWith(this.icon, '/')
},
@@ -117,6 +122,17 @@ export default {

<style lang="scss">
.box.box--items {
&.box--items--horizontal {
.box__content {
@apply flex;
}

.box__item {
@apply flex-1;
@apply border-none;
}
}

.box__content {
@apply px-0;
}
@@ -124,7 +140,7 @@ export default {
.box__item {
@apply flex px-4 py-2;
min-height: 50px;
transition: all .1s ease-in-out;
transition: all .2s ease-in-out;

&.box__item--sub {
@apply pl-8;
@@ -132,7 +148,6 @@ export default {

&.box__item--link {
@apply cursor-pointer;
transition: all .1s ease-in-out;

&:hover {
@apply bg-dark-700 text-white;
@@ -146,25 +161,34 @@ export default {
.item__icon {
@apply mr-4 flex items-center justify-center text-2xl;
width: 42px;
min-width: 42px;
}

.item__content {
@apply flex-1 flex;
@apply flex-1 flex flex-col justify-center;

.item__text {
@apply flex-1 flex flex-col justify-center;
.item__label {
@apply block font-semibold text-generic-400;
}

.item__label {
@apply block font-semibold text-generic-400;
}
.item__description {
@apply block text-sm;
}
}

.item__description {
@apply block text-sm;
}
.item__action {
//@apply flex items-center justify-center;

.item__label {
@apply block font-semibold text-sm text-generic-400;
}

&:not(.item__action--full) {
@apply w-1/3 ml-4;
}

.item__action {
@apply w-1/3 ml-4 flex items-center justify-center;
&.item__action--full {
@apply flex-1;
}
}
}

+ 1
- 0
src/components/index.js View File

@@ -3,3 +3,4 @@ import './Layout'
import './Nudity'
import './Settings'
import './Form'
import './Queue'

+ 37
- 2
src/layouts/default.vue View File

@@ -1,5 +1,11 @@
<template>
<div class="layout">
<div
class="layout"
:class="{'layout--dragging': isDragging}"
@dragenter="onDragEnter"
@dragover="onDragOver"
@dragleave="onDragLeave"
@drop="onDrop">
<!-- Window Buttons -->
<LayoutTopbar />

@@ -7,18 +13,27 @@
<LayoutNavbar />

<!-- Queue -->
<LayoutQueuebar />
<QueueBar />

<!-- Content -->
<div id="layout-content" class="layout__content">
<nuxt />
</div>

<!-- Dragging -->
<div class="layout__dropzone">
<h2>Drop the dream here!</h2>
</div>
</div>
</template>

<script>
import { UploadMixin } from '~/mixins'

export default {
middleware: ['wizard'],

mixins: [UploadMixin],
}
</script>

@@ -31,6 +46,12 @@ export default {
grid-template-rows: 30px 50px 1fr;
grid-template-areas: "topbar topbar topbar" "navbar navbar navbar" "content content jobbar";

&.layout--dragging {
.layout__dropzone {
@apply flex opacity-100;
}
}

.layout__topbar {
grid-area: topbar;
}
@@ -48,5 +69,19 @@ export default {
grid-area: content;
height: calc(100vh - 80px);
}

.layout__dropzone {
@apply absolute left-0 right-0 top-0 bottom-0 z-50;
@apply hidden opacity-0 pointer-events-none;
@apply bg-dark-900-70 items-center justify-center;
@apply border-8 border-dotted;
backdrop-filter: blur(6px);
transition: opacity .2s ease-in-out;
will-change: opacity;

h2 {
@apply text-white font-bold text-3xl;
}
}
}
</style>

+ 129
- 0
src/mixins/UploadMixin.js View File

@@ -0,0 +1,129 @@
// DreamTime.
// Copyright (C) DreamNet. All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License 3.0 as published by
// the Free Software Foundation. See <https://www.gnu.org/licenses/gpl-3.0.html>
//
// Written by Ivan Bravo Bravo <ivan@dreamnet.tech>, 2020.

import {
isNil, map, isArray,
} from 'lodash'

import { Consola } from '~/modules/consola'
import { Nudify } from '~/modules/nudify'

const consola = Consola.create('upload')

const { dialog } = $provider.api

export const UploadMixin = {
data: () => ({
isDragging: false,
dragCounter: 0,
}),

mounted() {
this.$router.afterEach(() => {
this.dragCounter = 0
})
},

methods: {
/**
* File selected, start a new transformation process
*/
addFile(file) {
if (isNil(file)) {
return
}

Nudify.addFile(file.path)
},

/**
*
*/
async addFiles(files) {
if (!isArray(files)) {
return
}

await Nudify.addFiles(files)
},

/**
*
*/
openFolder() {
const paths = dialog.showOpenDialogSync({
properties: ['openDirectory'],
})

this.addFiles(paths)
},

/**
*
*/
onDragEnter(event) {
event.preventDefault()
event.dataTransfer.dropEffect = 'copy'

this.dragCounter += 1

this.isDragging = true
},

/**
*
*/
onDragLeave() {
this.dragCounter -= 1

if (this.dragCounter === 0) {
this.isDragging = false
}
},

/**
*
*/
onDragOver(event) {
event.preventDefault()
event.stopPropagation()

event.dataTransfer.dropEffect = 'copy'
this.isDragging = true
},

/**
*
*/
onDrop(event) {
event.preventDefault()
event.stopPropagation()

this.isDragging = false

const { files } = event.dataTransfer
const url = event.dataTransfer.getData('url')

consola.debug('onDrop', {
event,
files,
url,
})

if (url.length > 0) {
Nudify.addUrl(url)
consola.track('DROP_URL')
} else if (files.length > 0) {
const paths = map(files, 'path')
this.addFiles(paths)
consola.track('DROP_FILE')
}
},
},
}

+ 1
- 0
src/mixins/index.js View File

@@ -1,2 +1,3 @@
/* eslint-disable-next-line */
export { default as VModel } from './VModel'
export { UploadMixin } from './UploadMixin'

+ 1
- 1
src/modules/nudify/nudify.js View File

@@ -170,7 +170,7 @@ export const Nudify = {

Swal.showLoading()

await delay(1000)
await delay(500)

for (const path of paths) {
// eslint-disable-next-line no-await-in-loop

+ 4
- 6
src/modules/nudify/photo-run.js View File

@@ -8,7 +8,8 @@
// Written by Ivan Bravo Bravo <ivan@dreamnet.tech>, 2019.

import {
isNil, isEmpty, truncate, deburr, forIn, cloneDeep, random, toString,
isNil, isEmpty, forIn, cloneDeep, random, toString,
trim, kebabCase, truncate, deburr,
} from 'lodash'
import deferred from 'deferred'
import Swal from 'sweetalert2/dist/sweetalert2.js'
@@ -93,12 +94,9 @@ export class PhotoRun {
const now = Date.now()
const { file } = this.photo

const originalName = truncate(
deburr(file.name),
{ length: 30, omission: '' },
)
const originalName = trim(kebabCase(truncate(deburr(file.name), { length: 20, omission: '' })))

return `${originalName}-${this.id}${now}-dreamtime${file.extension}`
return `${originalName}-RUN${this.id}-${now}-dreamtime${file.extension}`
}

constructor(id, photo) {

+ 40
- 4
src/modules/nudify/photo.js View File

@@ -10,6 +10,7 @@
import {
cloneDeep, isNil, merge, isError,
} from 'lodash'
import path from 'path'
import { Queue } from '@dreamnet/queue'
import EventBus from 'js-event-bus'
import { settings } from '../system'
@@ -24,6 +25,8 @@ import { events } from '../events'
const { getCurrentWindow } = require('electron').remote

const { getModelsPath, getCropPath } = $provider.paths
const { fs } = $provider
const { shell, dialog } = $provider.api

export class Photo {
/**
@@ -144,6 +147,10 @@ export class Photo {
return this.running || this.finished
}

get executions() {
return this.preferences.body.executions
}

get canModify() {
return this.file.mimetype !== 'image/gif'
}
@@ -303,6 +310,37 @@ export class Photo {
}
}

/**
*
*/
saveAll() {
const dir = dialog.showOpenDialogSync({
// defaultPath: this.getFolderPath(),
properties: ['openDirectory'],
})

if (isNil(dir)) {
return
}

if (!fs.existsSync(dir[0])) {
return
}

this.consola.debug(`Saving all results in ${dir[0]}`)

this.runs.forEach((photoRun) => {
const savePath = path.join(dir[0], photoRun.outputName)

this.consola.debug(savePath)
photoRun.outputFile.copy(savePath)
})
}

openFolder() {
shell.openItem(this.getFolderPath())
}

/**
*
*/
@@ -432,9 +470,7 @@ export class Photo {
* This should only be called from the queue.
*/
async start() {
const { executions } = this.preferences.body

if (executions === 0) {
if (this.executions === 0) {
return
}

@@ -443,7 +479,7 @@ export class Photo {

// this.onStart()

for (let it = 1; it <= executions; it += 1) {
for (let it = 1; it <= this.executions; it += 1) {
const run = new PhotoRun(it, this)

this.runs.push(run)

+ 7
- 12
src/modules/tutorial.js View File

@@ -25,26 +25,26 @@ export const tutorial = {
overlayOpacity: 0.7,
steps: [
{
intro: 'Welcome to DreamTime! Let me give you some information so you don\'t get lost. Let\'s start with the Nudification page.',
intro: 'Welcome to DreamTime! Let me give you some information so you don\'t get lost. Let\'s start with the Nudification process.',
},

{
intro: 'From this page you can upload the photos you want to nudify, DreamTime has several ways to obtain the photo.',
intro: 'DreamTime has different ways in which you can upload the photo you want to nudify.',
},

{
element: '#uploader-dropzone',
intro: 'The easiest one is to drag and drop the photos to this area. Here you can drop entire folders or even photos from a web browser.',
element: '#uploader',
intro: 'The easiest one is to drag and drop your photos into the application, no matter what section you are in! Just drag and drop, it\'s that easy. You can drag entire folders, photos from the web browser or even web addresses.',
},

{
element: '#uploader-alternatives',
intro: 'Below are alternative methods, you can upload photos even from the Internet or Instagram.',
intro: 'On this page there are alternative ways, you can upload photos even from the Internet or Instagram.',
},

{
element: '#uploader-settings',
intro: 'What happens when uploading a photo is controlled by this option, initially it is established that the photos are placed in the Pending Queue. Speaking of the queue...',
intro: 'What happens when uploading a photo is controlled by this option, initially it is set that the photos are placed in the Pending Queue. Speaking of the queue...',
},

{
@@ -59,7 +59,7 @@ export const tutorial = {

{
element: '#queuebar-pending',
intro: 'This section shows the photos that are pending, they will not be nudified until you start the process manually. Just click on the photo and you can access the page with all the information you need.',
intro: 'This section shows the photos that are pending, they will not be nudified until you start the process manually.',
},

{
@@ -140,11 +140,6 @@ export const tutorial = {
intro: 'This option can dramatically increase or decrease the quality of the result. If you come from DeepNude, the Manual Crop option will be the one you feel most comfortable with, but we recommend you experiment with the other options and find the ideal one for your photo.',
},

{
element: '#preferences-advanced-color',
intro: 'Activating this option will apply an algorithm that could restore the original colors of the photo, it does not always work but it is worth trying.',
},

{
intro: 'That\'s all for now, as we said before we recommend you experiment with these preferences, with a little practice you will start creating amazing nudes!',
},

+ 3
- 3
src/package.json View File

@@ -4,7 +4,7 @@
"description": "An open-source and super-improved version of DeepNude.",
"author": "DreamNet",
"homepage": "https://time.dreamnet.tech",
"version": "1.3.4",
"version": "1.4.0",
"main": "electron/dist/index.js",
"license": "GPL-3.0-only",
"private": true,
@@ -53,7 +53,7 @@
"dependencies": {
"7zip-bin": "^5.0.3",
"@dreamnet/logplease": "^1.0.0",
"@dreamnet/queue": "^0.1.0",
"@dreamnet/queue": "^0.1.2",
"@fortawesome/fontawesome-svg-core": "^1.2.25",
"@fortawesome/free-brands-svg-icons": "^5.11.2",
"@fortawesome/free-regular-svg-icons": "^5.11.2",
@@ -155,4 +155,4 @@
"tailwindcss-alpha": "hacknug/tailwindcss-alpha#feature/tests",
"worker-loader": "^2.0.0"
}
}
}

+ 31
- 24
src/pages/nudify/_id.vue View File

@@ -2,11 +2,12 @@
<div v-if="photo" class="nudify content__body">
<div class="nudify__menu">
<div class="menu__container">
<!-- Original Preview -->
<div class="mb-6 flex justify-center">
<app-photo :src="photo.file.path" :hover="false" data-private />
</div>

<!-- Sections -->
<!-- Navigation -->
<div class="box box--items">
<div class="box__content">
<box-item
@@ -15,34 +16,43 @@
:href="`/nudify/${photo.id}/preferences`" />

<box-item
v-show="photo.canModify"
label="Results"
icon="heart"
:href="`/nudify/${photo.id}/results`" />
</div>
</div>

<!-- Tools -->
<div v-if="photo.canModify" class="box box--items">
<div class="box__content">
<box-item
label="Editor"
icon="paint-brush"
:href="`/nudify/${photo.id}/editor`" />

<box-item
v-show="photo.canModify && photo.preferences.advanced.scaleMode === 'cropjs'"
v-if="photo.preferences.advanced.scaleMode === 'cropjs'"
label="Crop"
icon="crop"
:href="`/nudify/${photo.id}/crop`" />

<box-item
v-show="photo.canModify && photo.preferences.advanced.scaleMode === 'overlay'"
v-if="photo.preferences.advanced.scaleMode === 'overlay'"
label="Overlay"
icon="magic"
:href="`/nudify/${photo.id}/overlay`" />

<box-item
label="Results"
icon="heart"
:href="`/nudify/${photo.id}/results`" />
</div>
</div>

<!-- Buttons -->
<button v-show="!photo.running && !photo.waiting" class="button button--success" @click.prevent="add">
<span class="icon"><font-awesome-icon icon="play" /></span>
<span>Add to queue</span>
<span>Add to Queue</span>
</button>

<button v-show="photo.finished && photo.executions > 1" class="button button--info" @click.prevent="saveAll">
<span class="icon"><font-awesome-icon icon="save" /></span>
<span>Save all</span>
</button>

<button v-show="photo.waiting" class="button button--danger" @click.prevent="cancel">
@@ -76,8 +86,6 @@
import { isNil } from 'lodash'
import { Nudify } from '~/modules/nudify'

const { shell } = $provider.api

export default {
middleware: ({ route, redirect }) => {
const { params, fullPath } = route
@@ -126,8 +134,12 @@ export default {
this.photo.cancel()
},

saveAll() {
this.photo.saveAll()
},

openFolder() {
shell.openItem(this.photo.getFolderPath())
this.photo.openFolder()
},

forget() {
@@ -140,25 +152,20 @@ export default {

<style lang="scss" scoped>
.nudify {
@apply relative flex;
min-height: 100%;
@apply relative flex pb-0;
height: 100%;
}

.nudify__menu {
@apply mr-4 ;
width: 200px;

.menu__container {
@apply sticky;
top: 1.5rem;
}
@apply mr-4 pr-6 h-full overflow-y-auto;
width: 250px;

.button {
.button {
@apply block w-full mb-6;
}
}

.nudify__content {
@apply flex-1 overflow-x-auto;
@apply flex-1 h-full overflow-auto;
}
</style>

+ 0
- 2
src/pages/nudify/_id/crop.vue View File

@@ -68,8 +68,6 @@
</template>

<script>
import { clone } from 'lodash'

export default {
computed: {
photo() {

+ 1
- 1
src/pages/nudify/_id/editor.vue View File

@@ -34,7 +34,7 @@ export default {
},
theme: blackTheme,
initMenu: 'draw',
menu: ['draw', 'shape', 'flip', 'rotate', 'text', 'mask'],
menu: ['draw', 'shape', 'flip', 'rotate', 'filter', 'mask'],
},
usageStatistics: false,
})

+ 62
- 9
src/pages/nudify/_id/results.vue View File

@@ -1,6 +1,51 @@
<template>
<!-- Results -->
<div v-if="photo.started" class="nudify-results">
<div v-if="photo.started" class="results">
<!-- Quick settings -->
<div class="box box--items box--items--horizontal">
<div class="box__content">
<box-item v-tooltip="{placement: 'bottom', content: 'Scale method'}">
<select
v-model="photo.preferences.advanced.scaleMode"
:disabled="photo.running"
class="input input--sm">
<option value="none">
None
</option>
<option value="auto-rescale">
Fixed
</option>
<option value="auto-resize">
Scale and pad
</option>
<option value="auto-resize-crop">
Scale and crop
</option>
<option value="overlay">
Overlay
</option>
<option value="cropjs">
Manual crop
</option>
</select>
</box-item>

<box-item v-tooltip="{placement: 'bottom', content: 'Color transfer'}">
<select
v-model="photo.preferences.advanced.useColorTransfer"
:disabled="photo.running"
class="input input--sm">
<option :value="true">
Enabled
</option>
<option :value="false">
Disabled
</option>
</select>
</box-item>
</div>
</div>

<!-- Runs -->
<div class="runs">
<nudify-photo-run v-for="(run, index) in photo.runs" :key="index" :run="run" />
@@ -8,8 +53,10 @@
</div>

<!-- Waiting -->
<div v-else-if="photo.waiting" class="nudify-results">
<div v-else-if="photo.waiting" class="results">
<div class="results__status">
<font-awesome-icon icon="cloud-sun-rain" class="icon" />

<h2>
Waiting for other dreams to end...
</h2>
@@ -17,10 +64,12 @@
</div>

<!-- Pending -->
<div v-else class="nudify-results">
<div v-else class="results">
<div class="results__status">
<font-awesome-icon icon="cloud-moon" class="icon" />

<h2>
What are you waiting for? Let's dream together
Add me to the queue to dream together.
</h2>
</div>
</div>
@@ -43,16 +92,20 @@ export default {
}
</script>

<style lang="scss">
.nudify-results {
<style lang="scss" scoped>
.results {
@apply h-full;
}

.results__status {
@apply flex justify-center mb-6;
@apply flex flex-col justify-center items-center h-full;

.icon {
@apply text-6xl text-white mb-4;
}

h2 {
@apply text-xl text-white;
@apply text-2xl;
}
}


+ 1
- 1
src/plugins/setup.js View File

@@ -23,7 +23,7 @@ Vue.mixin(BaseMixin)

// tippyjs
tippy.setDefaultProps({
delay: 100,
delay: [500, 0],
arrow: true,
})


+ 0
- 14
src/tailwind.config.js View File

@@ -68,20 +68,6 @@ module.exports = {
900: '#090a0c',
},

/*
dark: {
100: '#21262d',
200: '#1e2329',
300: '#1b2026',
400: '#191d22',
500: '#161a1f',
600: '#14171C',
700: '#0D1013',
800: '#0A0C0E',
900: '#070809',
},
*/

navbar: {
100: '#E6E6E7',
200: '#C1C1C3',

Loading…
Cancel
Save