aico-image-editor
Version:
Combine multiple image into and create single combined image
406 lines (405 loc) • 36.4 kB
HTML
<div class="modalParent" x-data="aiModal">
<div class="modal fade pe-none" id="aiModal" aria-hidden="true" x-init="() => {
$store.elements.aiModalEL = $el;
$store.modal.createModal($el, $dispatch)
}">
<div class="modal-dialog modal-dialog-centered pe-auto modal-xl">
<div class="modal-content">
<div class="block block-rounded mb-0" @keydown.window.shift="updateShiftKeyState(true)" @keyup.window.shift="updateShiftKeyState(false)">
<div class="block-header block-header-default">
<h3 class="block-title" x-text="getTranslatedHTML('aiTabTitle')"></h3>
<div class="block-options">
<button type="button" class="btn-block-option" id="bttncancel" @click="() => {
$store.modal.closeModal($store.elements.aiModalEL)
}" data-bs-dismiss="modal" aria-label="Close"> <i class="fa fa-times"></i></button>
</div>
</div>
<div class="block-content block-content-full">
<div class="mb-2">
<span class="text-primary" x-text="getTranslatedHTML('aiWelcomeText')"></span>
</div>
<div class="row">
<div class="col-12">
<div class="alert alert-danger alert-dismissible mb-2" x-show="showAllErrors">
<button type="button" class="btn-close" @click="hideErrorMesssages()" aria-label="Close"></button>
<p class="mb-0" x-show="showPromptError" x-text="getTranslatedHTML('promptError')"></p>
<p class="mb-0" x-show="showEmptyCanvasError" x-text="getTranslatedHTML('aiCanvasImageError')"></p>
<p class="mb-0" x-show="showFileSizeValidationError" x-text="getTranslatedHTML('aiFileSizeValidationError', {
maxFileSize: maxFileSize
})"></p>
<p class="mb-0" x-show="showServerError" x-text="getTranslatedHTML('aiServerError')"></p>
</div>
<p class="mb-4" x-text="getTranslatedHTML('aiImageInformativeText')"></p>
<!-- <img :src="$store.canvas.aiImageSrc" class="img-fluid" alt="" /> -->
</div>
</div>
<div class="row">
<div class="col-6">
<div class="mb-3">
<div class="d-flex align-items-center mb-2">
<h6 class="mb-2 text-uppercase">
dall-e-2, <span x-text="getTranslatedHTML('generateNewImage')"></span>/<span x-text="getTranslatedHTML('editImage')"></span>/<span x-text="getTranslatedHTML('generateVariantImage')"></span>
</h6>
<span class="linedesign ms-3 me-0"></span>
</div>
<div class="row gx-2 mb-3">
<div class="col">
<div>
<input type="text" id="prompt" x-model="prompt" class="form-control border-primary" :placeholder="getTranslatedHTML('promptLabel', {
promptType: 'imgText'
})"
:disabled="activeOperationID === 'aiOperation-2'" />
</div>
</div>
<div class="col-auto">
<dropdown-menu datatemplateurl="./dropdownMenu.html" styles="global" shadow="true" x-data="dropdownMenu"
class="btn-group btn-group-lg" dropdown-trigger-class="dropdown-toggle-split">
<button type="button" class="btn btn-primary btn-lg" @click="generateAIImage()" slot="dropdown-append">
<span x-text="activeOperationLabel"></span>
</button>
<span slot="dropdown-trigger" class="pe-none">
<span class="visually-hidden">Toggle Dropdown</span>
</span>
<ul slot="dropdown-content" class="list-unstyled">
<template x-for="aiOperation in aiOperations">
<li>
<a class="dropdown-item position-relative pe-4" href="javascript:void(0)" @click="updateActiveAIOperation(aiOperation.id)"
:class="activeOperationID === aiOperation.id ? 'active':''"
:style="aiOperation.id === 'aiOperation-0' && {borderTopLeftRadius: '10px', borderBottomLeftRadius: '10px'}">
<span x-text="aiOperation.label" class="fs-sm"></span>
<span class="position-absolute end-0 me-2 top-50 translate-middle-y" x-show="activeOperationID === aiOperation.id">
<i class="fa fa-check"></i>
</span>
</a>
</li>
</template>
</ul>
</dropdown-menu>
</div>
</div>
<div class="row gx-2 gy-3 mb-3">
<div class="col-auto">
<div class="d-flex align-items-center mb-2">
<h6 class="mb-0" x-text="getTranslatedHTML('editFunctions')"></h6>
<span class="linedesign ms-3 me-0"></span>
</div>
<div class="btn-group me-1 mb-3 flex-nowrap">
<button type="button" class="btn btn-lg btn-secondary" @click="setDrawMode('erase')" x-data="tooltip"
:title="getTranslatedHTML('eraseTitle')" :class="activeDrawMode === 'erase' ? 'active':''""
>
<span></span><i class="fa-solid fa-eraser"></i>
</button>
<button type="button" class="btn btn-lg btn-secondary" @click="setDrawMode('draw')" x-data="tooltip"
:title="getTranslatedHTML('bringBackTitle')" :class="activeDrawMode === 'draw' ? 'active':''"
>
<span><i class="fa-solid fa-rotate-left"></i></span>
</button>
<button type="button" class="btn btn-lg btn-secondary" @click="setDrawMode('create')" x-data="tooltip"
:title="getTranslatedHTML('selectTitle')" :class="activeDrawMode === 'create' ? 'active':''" >
<span class="me-1"><i class="fa-solid fa-plus"></i></span>
</button>
<button type="button" class="btn btn-lg btn-secondary">
<info-popover datatemplateurl="./infoPopover.html" styles="global" shadow="true" class="d-inline-block align-middle"
data-bs-trigger="hover focus" data-bs-placement="bottom" x-data="infoPopover">
<span slot="info-popover-trigger">
<i class="fa-solid fa-circle-info"></i>
</span>
<div slot="info-popover-content" x-data="localization">
<p class="mb-1" x-text="getTranslatedHTML('eraserHint')"></p>
<p class="mb-1" x-text="getTranslatedHTML('bringBackEraserHint')"></p>
<p class="mb-0" x-text="getTranslatedHTML('marqueeSelectionHint')"></p>
</div>
</info-popover>
</button>
</div>
</div>
<div class="col">
<div class="row align-items-center">
<div class="col-12">
<div class="d-flex align-items-center">
<h6 class="mb-0" x-text="getTranslatedHTML('eraserSizeText')"></h6>
<span class="linedesign ms-3 me-0"></span>
</div>
</div>
<div class="col">
<div class="custom-range-slider">
<input type="range" class="form-range custom-range-slider__control" x-model="freeDrawingBrushWidth" min="10" max="100" @change="setupFreeDrawingBrush(window.__aiCanvas)" />
<span class="custom-range-slider__bubble" x-text="freeDrawingBrushWidth" :style="{left: setBubblePosition(freeDrawingBrushWidth, 10, 100)}"></span>
</div>
</div>
<div class="col-4">
<input type="number" min="10" max="100" class="form-control border-primary" x-model="freeDrawingBrushWidth" />
</div>
</div>
</div>
</div>
</div>
<div class="mb-3">
<div class="d-flex align-items-center mb-2">
<h6 class="mb-0 text-uppercase">
<span x-text="getTranslatedHTML('backgroundRemovalReplace')"></span>
</h6>
<info-popover datatemplateurl="./infoPopover.html" styles="global" shadow="true" class="d-inline-block align-middle ms-2"
data-bs-trigger="hover focus" data-bs-placement="bottom" x-data="infoPopover">
<span slot="info-popover-trigger" class="cursor-pointer">
<i class="fa-solid fa-circle-info fa-lg text-warning lh-1"></i>
</span>
<div slot="info-popover-content" x-data="localization">
<small x-html="getTranslatedHTML('replaceBackgroundApiNotice')"></small>
</div>
</info-popover>
<span class="linedesign ms-3 me-0"></span>
</div>
<div class="mb-3">
<small class="text-warning" x-text="getTranslatedHTML('watermarkNotice')"></small>
</div>
<div class="row gx-3 gy-3 mb-3 align-items-center" :class="photoroomAPIMode === 'edit' ? 'd-none':''">
<div class="col-4">
<div class="d-flex align-items-center">
<label class="form-label" for="subject-shadow" x-text="getTranslatedHTML('backgroundRemovalColorPlaceholder')"></label>
<span class="linedesign ms-3 me-0"></span>
</div>
<select x-model="backgroundRemovalFormat" class="form-select border-primary form-control-sm">
<option value="png">png</option>
<option value="jpg">jpeg</option>
<option value="webp">webp</option>
</select>
</div>
<div class="col-4">
<div class="d-flex align-items-center">
<label class="form-label" for="subject-shadow" x-text="getTranslatedHTML('backgroundRemovalSizePlaceholder')"></label>
<span class="linedesign ms-3 me-0"></span>
</div>
<select x-model="backgroundRemovalSize" class="form-select border-primary form-control-sm">
<option value="preview">preview (0.25 MP)</option>
<option value="medium">medium (1.5 MP)</option>
<option value="hd">hd (4 MP)</option>
<option value="full">full (36 MP)</option>
</select>
</div>
<div class="col-4">
<div class="form-check form-switch form-check-inline">
<input class="form-check-input" type="checkbox" value="" id="canvas-bg-color" name="canvas-bg-color" x-model="shouldBackgroundRemovalCrop" />
<label class="form-check-label" for="canvas-bg-color" x-text="getTranslatedHTML('crop')"></label>
</div>
</div>
</div>
<div class="row gx-3 gy-3 mb-3 align-items-center" :class="photoroomAPIMode === 'remove' ? 'd-none':''">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" value="" id="remove-background" name="remove-background" x-model="prRemoveBackground"/>
<label class="form-check-label" for="remove-background" x-text="getTranslatedHTML('removeBackground')"></label>
</div>
<div class="col-12" x-show="prRemoveBackground">
<input type="text" class="form-control border-primary" x-model="backgroundPrompt" :placeholder="getTranslatedHTML('promptLabel', {
promptType: 'background'
})"/>
</div>
<div class="col-12">
<div class="row gx-3">
<!-- <div class="col-auto" x-show="prRemoveBackground">
<label class="form-label" for="background-scaling" x-text="getTranslatedHTML('photoroomBackgroundScaling')"></label>
<select x-model="backgroundScaling" class="form-select border-primary form-control-sm" id="background-scaling">
<option value="fill" x-text="getTranslatedHTML('fill')"></option>
<option value="fit" x-text="getTranslatedHTML('fit')"></option>
</select>
</div> -->
<div class="col-8">
<div class="d-flex align-items-center">
<label class="form-label" for="subject-scaling" x-text="getTranslatedHTML('photoroomSubjectScaling')"></label>
<span class="linedesign ms-3 me-0"></span>
</div>
<select x-model="subjectScaling" class="form-select border-primary form-control-sm w-50 mt-1" for="subject-scaling">
<option value="fill" x-text="getTranslatedHTML('fill')"></option>
<option value="fit" x-text="getTranslatedHTML('fit')"></option>
</select>
</div>
<div class="col-4">
<div class="d-flex align-items-center">
<label class="form-label" for="subject-shadow" x-text="getTranslatedHTML('shadowText')"></label>
<span class="linedesign ms-3 me-0"></span>
</div>
<select x-model="subjectShadow" class="form-select border-primary form-control-sm mt-1" for="subject-shadow">
<option value="" x-text="getTranslatedHTML('noShadow')"></option>
<option value="ai.soft" x-text="getTranslatedHTML('soft')"></option>
<option value="ai.hard" x-text="getTranslatedHTML('hard')"></option>
<option value="ai.floating" x-text="getTranslatedHTML('floating')"></option>
</select>
</div>
</div>
</div>
<div class="col-12">
<div>
<div class="d-flex align-items-center">
<label class="form-label" for="subject-scaling" x-text="getTranslatedHTML('size')"></label>
<span class="linedesign ms-3 me-0"></span>
</div>
<div class="space-x-2 mt-1">
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" id="output-size-custom" name="output-size-custom" value="custom"
x-model="outputSize"/>
<label class="form-check-label" for="output-size-custom" x-text="getTranslatedHTML('custom')"></label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" id="output-size-original" name="output-size-original" value="originalImage"
x-model="outputSize"/>
<label class="form-check-label" for="output-size-original" x-text="getTranslatedHTML('originalImage')"></label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" id="output-size-cropped" name="output-size-cropped" value="croppedSubject"
x-model="outputSize"/>
<label class="form-check-label" for="output-size-cropped" x-text="getTranslatedHTML('cropped')"></label>
</div>
</div>
</div>
<div class="mt-2">
<div class="row" x-show="outputSize === 'custom'">
<div class="col-6">
<!-- <label class="form-check-label" for="output-width" x-text="getTranslatedHTML('width')"></label> -->
<input type="number" class="form-control border" id="output-width" x-model="outputWidth"
:placeholder="getTranslatedHTML('width')"/>
</div>
<div class="col-6">
<!-- <label class="form-check-label" for="output-height" x-text="getTranslatedHTML('height')"></label> -->
<input type="number" class="form-control border" id="output-height" x-model="outputHeight"
:placeholder="getTranslatedHTML('height')"/>
</div>
</div>
<div class="row" x-show="outputSize === 'originalImage' || outputSize === 'croppedSubject'">
<div class="col-6">
<!-- <label class="form-check-label" for="output-max-width" x-text="getTranslatedHTML('maxWidth')"></label> -->
<input type="number" class="form-control border" id="output-max-width" x-model="outputMaxWidth"
:placeholder="getTranslatedHTML('maxWidth')"/>
</div>
<div class="col-6">
<!-- <label class="form-check-label" for="output-max-height" x-text="getTranslatedHTML('maxHeight')"></label> -->
<input type="number" class="form-control border" id="output-max-height" x-model="outputMaxHeight"
:placeholder="getTranslatedHTML('maxHeight')"/>
</div>
</div>
</div>
<div class="mt-4">
<!-- <div class="form-check form-switch" x-show="!prRemoveBackground">
<input class="form-check-input" type="checkbox" id="expand-mode" name="expand-mode" x-model="expandMode" />
<label class="form-check-label" for="expand-mode" x-text="getTranslatedHTML('uncrop')"></label>
</div> -->
<div>
<div class="d-flex align-items-center">
<label class="form-label" for="subject-scaling" x-text="getTranslatedHTML('referenceBoxLabel')"></label>
<span class="linedesign ms-3 me-0"></span>
</div>
<div class="space-x-2 mt-1">
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" id="subject-box" name="subject-box" value="subjectBox" x-model="referenceBox">
<label class="form-check-label" for="subject-box" x-text="getTranslatedHTML('subjectBox')"></label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" id="original-image" name="original-image" value="originalImage" x-model="referenceBox">
<label class="form-check-label" for="original-image" x-text="getTranslatedHTML('originalImage')"></label>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-12">
<dropdown-menu datatemplateurl="./dropdownMenu.html" styles="global" shadow="true" x-data="dropdownMenu"
class="btn-group btn-group-lg" dropdown-trigger-class="dropdown-toggle-split">
<button type="button" class="btn btn-lg btn-primary" @click="processBackground()" slot="dropdown-append">
<span x-text="getTranslatedHTML('removeBackgroundBtnText')" x-show="photoroomAPIMode === 'remove'"></span>
<span x-text="getTranslatedHTML('editBackgroundText')" x-show="photoroomAPIMode !== 'remove'"></span>
</button>
<span slot="dropdown-trigger" class="pe-none">
<span class="visually-hidden">Toggle Dropdown</span>
</span>
<ul slot="dropdown-content" class="list-unstyled mb-0">
<li>
<a href="javascript:void(0)" class="dropdown-item position-relative pe-5" @click="setPhotoroomAPIMode('remove')"
:class="photoroomAPIMode === 'remove' ? 'active':''">
<span x-text="getTranslatedHTML('removeBackgroundBtnText')"></span>
<span class="position-absolute end-0 me-2 top-50 translate-middle-y" x-show="photoroomAPIMode === 'remove'">
<i class="fa fa-check"></i>
</span>
</a>
</li>
<li>
<a href="javascript:void(0)" class="dropdown-item position-relative pe-5" @click="setPhotoroomAPIMode('edit')"
:class="photoroomAPIMode === 'edit' ? 'active':''">
<span x-text="getTranslatedHTML('editBackgroundText')"></span>
<span class="position-absolute end-0 me-2 top-50 translate-middle-y" x-show="photoroomAPIMode === 'edit'">
<i class="fa fa-check"></i>
</span>
</a>
</li>
</ul>
</dropdown-menu>
</div>
</div>
</div>
<div class="col-6">
<div class="row gx-2 gy-2">
<div class="col-12">
<div id="ai-canvas-wrapper" class="ratio ratio-1x1" x-bind="aiTriggerEL"
:class="{'pe-none': activeOperationID !== 'aiOperation-1'}">
<div x-init="$store.elements.aiCanvasResizeEL = $el">
<canvas id="aiCanvas" x-intersect.once="initAICanvas($el)"></canvas>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="server-loader position-absolute w-100 h-100 start-0 top-0 pe-none bg-white-75 justify-content-center align-items-center z-index-2" :class="isAiModalLoaderVisible ? 'd-flex':'d-none'">
<i class="fas fa-spinner fa-pulse"></i>
</div>
</div>
<div class="block-content block-content-full text-end border-top">
<dropdown-menu datatemplateurl="./dropdownMenu.html" styles="global" shadow="true" x-data="dropdownMenu"
class="btn-group btn-group-lg " dropdown-trigger-class="dropdown-toggle-split">
<button slot="dropdown-append" type="button" class="btn btn-lg btn-primary" x-text="getTranslatedHTML('aiTabUploadButtonLabel', {
addToType: addToType
})"
@click="upoloadAndUse()"></button>
<span slot="dropdown-trigger" class="pe-none">
<span class="visually-hidden">Toggle Dropdown</span>
</span>
<ul slot="dropdown-content" class="list-unstyled">
<li>
<a href="javascript:void(0)" class="dropdown-item position-relative pe-5" @click="setAIImageUploadMode('mainPicture')"
:class="aIImageUploadMode === 'mainPicture' ? 'active':''">
<span x-text="getTranslatedHTML('aiImageUploadToPicture')"></span>
<span class="position-absolute end-0 me-2 top-50 translate-middle-y" x-show="aIImageUploadMode === 'mainPicture'">
<i class="fa fa-check"></i>
</span>
</a>
</li>
<li>
<a href="javascript:void(0)" class="dropdown-item position-relative pe-5" @click="setAIImageUploadMode('motive')"
:class="aIImageUploadMode === 'motive' ? 'active':''">
<span x-text="getTranslatedHTML('aiImageUploadToMotive')"></span>
<span class="position-absolute end-0 me-2 top-50 translate-middle-y" x-show="aIImageUploadMode === 'motive'">
<i class="fa fa-check"></i>
</span>
</a>
</li>
<li>
<a href="javascript:void(0)" class="dropdown-item position-relative pe-5" @click="setAIImageUploadMode('background')"
:class="aIImageUploadMode === 'background' ? 'active':''">
<span x-text="getTranslatedHTML('aiImageUploadToBackground')"></span>
<span class="position-absolute end-0 me-2 top-50 translate-middle-y" x-show="aIImageUploadMode === 'background'">
<i class="fa fa-check"></i>
</span>
</a>
</li>
</ul>
</dropdown-menu>
</div>
</div>
</div>
</div>
</div>
<div class="modal-backdrop fade show d-none" @aimodal-shown.window="($event) => {
$el.classList.remove('d-none');
}" @aimodal-hidden.window="($event) => {
$el.classList.add('d-none');
}" @click="$store.modal.closeModal($store.elements.aiModalEL)"></div>
</div>