├── src
├── utils
│ ├── QuickPanelTemplateHelper.ts
│ ├── StringUtils.ts
│ ├── Token.ts
│ ├── ServerUtils.ts
│ ├── QuickPanelHelper.ts
│ └── BPUtils.ts
├── customcomponents
│ ├── useComponentVisible.tsx
│ ├── WorkflowPicker.tsx
│ ├── FloatIntWidget.tsx
│ ├── TextPrompt.tsx
│ ├── MTextArea.tsx
│ ├── SeedWidgetv2.tsx
│ ├── SeedWidget.tsx
│ ├── DropDownPicker.tsx
│ ├── DropdownStyleChooser.tsx
│ ├── DropdownPickerv2.tsx
│ └── NodeComponent.tsx
├── components
│ ├── Divider.tsx
│ ├── MenuDivider.tsx
│ ├── Label.tsx
│ ├── Detail.tsx
│ ├── Body.tsx
│ ├── index.ts
│ ├── Link.tsx
│ ├── Heading.tsx
│ ├── Progressbar.tsx
│ ├── ActionButton.tsx
│ ├── MenuItem.tsx
│ ├── RadioGroup.tsx
│ ├── Radio.tsx
│ ├── Button.tsx
│ ├── Icon.tsx
│ ├── Menu.tsx
│ ├── Checkbox.tsx
│ ├── Dropdown.tsx
│ ├── Textfield.tsx
│ ├── Textarea.tsx
│ └── Slider.tsx
├── index.tsx
├── controllers
│ ├── CommandController.ts
│ └── PanelController.ts
├── interfaces
│ └── types.ts
├── panels
│ ├── Panels.tsx
│ ├── CustomScriptPanel.tsx
│ ├── QuickPanel.tsx
│ └── MainPanelv2.tsx
└── index.css
├── templates
├── ComfyUI
├── customscript
│ ├── gb.js
│ ├── global.d.ts
│ ├── selectsubject.json
│ ├── jointext.js
│ ├── resizelayer.json
│ ├── splittext.json
│ ├── selectionmask.json
│ ├── selectiontool.json
│ ├── gaussianblur.json
│ ├── jointext.json
│ ├── redbox.json
│ ├── splittext.js
│ ├── resizelayer.js
│ ├── fixmask.json
│ └── redbox.js
├── config.json
├── workflows
│ ├── facerestore.json
│ └── inpainting.json
├── image_generator.json
└── styles
│ ├── sdxl_styles_fooocus.json
│ ├── sdxl_styles_sai.json
│ └── sdxl_styles_mre.json
├── .gitignore
├── extras
└── comfyshop-helper
│ ├── __init__.py
│ └── web
│ └── comfyshophelper.js
├── plugin
├── icons
│ ├── chevron.png
│ ├── default.png
│ ├── icon@1x.png
│ ├── icon@2x.png
│ ├── icon@2x.psd
│ ├── preview.png
│ ├── icon_qp@1x.png
│ ├── icon_qp@2x.png
│ ├── icon_workflow@1x.png
│ └── icon_workflow@2x.png
├── index.html
└── manifest.json
├── postcss.config.js
├── babel.config.js
├── .prettierrc.json
├── tailwind.config.js
├── tsconfig.json
├── .vscode
├── psdtools-revamped.code-workspace
└── tasks.json
├── README.md
├── package.json
└── webpack.config.js
/src/utils/QuickPanelTemplateHelper.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/templates/ComfyUI:
--------------------------------------------------------------------------------
1 | C:/ComfyPSD-backend
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | dist
2 | node_modules
3 | *-lock.*
--------------------------------------------------------------------------------
/extras/comfyshop-helper/__init__.py:
--------------------------------------------------------------------------------
1 | WEB_DIRECTORY = "./web"
2 | NODE_CLASS_MAPPINGS = {}
3 |
--------------------------------------------------------------------------------
/plugin/icons/chevron.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flipnism/comfyshop/HEAD/plugin/icons/chevron.png
--------------------------------------------------------------------------------
/plugin/icons/default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flipnism/comfyshop/HEAD/plugin/icons/default.png
--------------------------------------------------------------------------------
/plugin/icons/icon@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flipnism/comfyshop/HEAD/plugin/icons/icon@1x.png
--------------------------------------------------------------------------------
/plugin/icons/icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flipnism/comfyshop/HEAD/plugin/icons/icon@2x.png
--------------------------------------------------------------------------------
/plugin/icons/icon@2x.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flipnism/comfyshop/HEAD/plugin/icons/icon@2x.psd
--------------------------------------------------------------------------------
/plugin/icons/preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flipnism/comfyshop/HEAD/plugin/icons/preview.png
--------------------------------------------------------------------------------
/plugin/icons/icon_qp@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flipnism/comfyshop/HEAD/plugin/icons/icon_qp@1x.png
--------------------------------------------------------------------------------
/plugin/icons/icon_qp@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flipnism/comfyshop/HEAD/plugin/icons/icon_qp@2x.png
--------------------------------------------------------------------------------
/plugin/icons/icon_workflow@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flipnism/comfyshop/HEAD/plugin/icons/icon_workflow@1x.png
--------------------------------------------------------------------------------
/plugin/icons/icon_workflow@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/flipnism/comfyshop/HEAD/plugin/icons/icon_workflow@2x.png
--------------------------------------------------------------------------------
/templates/customscript/gb.js:
--------------------------------------------------------------------------------
1 | await batchPlay([{
2 | "_obj": "newPlacedLayer"
3 | }],{});
4 |
5 | await photoshop.core.performMenuCommand({"commandID":-395})
--------------------------------------------------------------------------------
/postcss.config.js:
--------------------------------------------------------------------------------
1 | const tailwindcss = require('tailwindcss');
2 | module.exports = {
3 | plugins: [
4 | 'postcss-preset-env',
5 | tailwindcss
6 | ],
7 | };
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: ['@babel/preset-env', '@babel/typescript', '@babel/preset-react'],
3 | env: {
4 | production: {
5 | only: ['src'],
6 | },
7 | },
8 | };
--------------------------------------------------------------------------------
/src/utils/StringUtils.ts:
--------------------------------------------------------------------------------
1 | export function calculateNested(array: string[]) {
2 | let count = 0;
3 | array.forEach((e) => {
4 | count = count + Math.round(e.length / 38);
5 | });
6 | return count;
7 | }
8 |
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "es5",
3 | "tabWidth": 2,
4 | "semi": true,
5 | "singleQuote": true,
6 | "printWidth": 180,
7 | "quoteProps": "preserve",
8 | "bracketSpacing": false,
9 | "jsxBracketSameLine": false
10 | }
11 |
--------------------------------------------------------------------------------
/plugin/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/templates/customscript/global.d.ts:
--------------------------------------------------------------------------------
1 | export {};
2 | declare global {
3 | function logme(msg: string): void;
4 | function showDialog(title: string, content: string): Promise;
5 | export namespace aio_server {
6 | const lastJsonMessage: object;
7 | function sendJsonMessage(message: any): void;
8 | }
9 | const bounds: {top: number; left: number; right: number; bottom};
10 | }
11 |
--------------------------------------------------------------------------------
/templates/customscript/selectsubject.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "selectsubject",
3 | "desc": "select subject using the power of photoshop",
4 | "icon_path": "M19 7.5v3m0 0v3m0-3h3m-3 0h-3m-2.25-4.125a3.375 3.375 0 11-6.75 0 3.375 3.375 0 016.75 0zM4 19.235v-.11a6.375 6.375 0 0112.75 0v.109A12.318 12.318 0 0110.374 21c-2.331 0-4.512-.645-6.374-1.766z",
5 | "executable": false,
6 | "func": [
7 | {
8 | "_obj": "autoCutout",
9 | "sampleAllLayers": false
10 | }
11 | ]
12 | }
13 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | export const content = ['./src/**/*.{js,jsx,ts,tsx}'];
3 | export const theme = {
4 | fontSize: {
5 | xxs: '0.5rem',
6 | xs: '0.55rem',
7 | sm: '0.6rem',
8 | title: '0.8rem',
9 | base: '1rem',
10 | lg: '1.25rem',
11 | },
12 | extend: {
13 | colors: {
14 | 'box-root': '#1e1e1e',
15 | 'box-child': '#141414',
16 | 'box-main-bg': '#323232',
17 | },
18 | },
19 | };
20 | export const plugins = [];
21 |
--------------------------------------------------------------------------------
/templates/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "qp_generator_model": "juggernaut_aftermath.safetensors",
3 | "qp_inpainting_model": "epicrealism_pureEvolutionV5-inpainting.safetensors",
4 | "imageloader_node": ["LoadImage", "BGImage", "Load Image", "Baseimage", "LoadResizeImageMask", "LoadResizeImageMask512", "Image"],
5 | "quickpanel_size": [
6 | [512, 512],
7 | [512, 768],
8 | [768, 768],
9 | [640, 832],
10 | [832, 640],
11 | [912, 512],
12 | [512, 912],
13 | [720, 720],
14 | [1280, 720]
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/templates/customscript/jointext.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | const selected_layers = doc.activeLayers;
4 | const reorder = selected_layers.sort((a, b) => a.bounds.top - b.bounds.top);
5 | let new_text = reorder.map(t => t.textItem.contents).join(" ");
6 |
7 | for await (const [index, value] of reorder.entries()) {
8 | if (index == 0) {
9 | value.textItem.contents = new_text;
10 | } else {
11 | await executeAsModal(async () => {
12 | await value.delete();
13 | }, { "commandName": "duh" })
14 | }
15 |
16 |
17 | }
--------------------------------------------------------------------------------
/templates/customscript/resizelayer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "resizelayer",
3 | "desc": "resize layer to fit with either this or that",
4 | "icon_path": "M13.2 2.24a.75.75 0 00.04 1.06l2.1 1.95H6.75a.75.75 0 000 1.5h8.59l-2.1 1.95a.75.75 0 101.02 1.1l3.5-3.25a.75.75 0 000-1.1l-3.5-3.25a.75.75 0 00-1.06.04zm-6.4 8a.75.75 0 00-1.06-.04l-3.5 3.25a.75.75 0 000 1.1l3.5 3.25a.75.75 0 101.02-1.1l-2.1-1.95h8.59a.75.75 0 000-1.5H4.66l2.1-1.95a.75.75 0 00.04-1.06z",
5 | "executable": true,
6 |
7 | "script": "resizelayer.js",
8 | "func": [{}]
9 | }
10 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "./dist/",
4 | "sourceMap": false,
5 | "noImplicitAny": false,
6 | "target": "es5",
7 | "module": "commonjs",
8 | "moduleResolution": "node",
9 | "esModuleInterop": true,
10 | "allowSyntheticDefaultImports": true,
11 | "jsx": "react",
12 | "typeRoots": [
13 | "node_modules/@types",
14 | ],
15 | "lib": [
16 | "esnext",
17 | "dom"
18 | ]
19 | },
20 | "exclude": [
21 | "node_modules"
22 | ],
23 | }
--------------------------------------------------------------------------------
/.vscode/psdtools-revamped.code-workspace:
--------------------------------------------------------------------------------
1 | {
2 | "folders": [
3 | {
4 | "path": ".."
5 | },
6 | {
7 | "path": "../../comfyui-photoshop"
8 | },
9 | {
10 | "path": "C:/ComfyPSD-backend/custom_nodes/julnodes"
11 | },
12 | {
13 | "path": "C:/ComfyPSD-backend/custom_nodes/ComfyUI-ricing"
14 | },
15 | {
16 | "path": "C:/ComfyPSD-backend/custom_nodes/comfyui-reactor-node"
17 | },
18 | {
19 | "path": "C:/ComfyPSD-backend/custom_nodes/comfyshop-helper"
20 | },
21 | {
22 | "path": "../../thumbnail-maker"
23 | }
24 | ],
25 | "settings": {
26 | "editor.inlineSuggest.showToolbar": "always"
27 | }
28 | }
--------------------------------------------------------------------------------
/templates/customscript/splittext.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "splittext",
3 | "desc": "split multiple line of texts",
4 | "icon_path": "M6 4.75A.75.75 0 016.75 4h10.5a.75.75 0 010 1.5H6.75A.75.75 0 016 4.75zM6 10a.75.75 0 01.75-.75h10.5a.75.75 0 010 1.5H6.75A.75.75 0 016 10zm0 5.25a.75.75 0 01.75-.75h10.5a.75.75 0 010 1.5H6.75a.75.75 0 01-.75-.75zM1.99 4.75a1 1 0 011-1H3a1 1 0 011 1v.01a1 1 0 01-1 1h-.01a1 1 0 01-1-1v-.01zM1.99 15.25a1 1 0 011-1H3a1 1 0 011 1v.01a1 1 0 01-1 1h-.01a1 1 0 01-1-1v-.01zM1.99 10a1 1 0 011-1H3a1 1 0 011 1v.01a1 1 0 01-1 1h-.01a1 1 0 01-1-1V10z",
5 | "executable": true,
6 | "script": "splittext.js",
7 | "func": [{}]
8 | }
9 |
--------------------------------------------------------------------------------
/src/customcomponents/useComponentVisible.tsx:
--------------------------------------------------------------------------------
1 | import {useState, useEffect, useRef} from 'react';
2 |
3 | export default function useComponentVisible(initialIsVisible) {
4 | const [isComponentVisible, setIsComponentVisible] = useState(initialIsVisible);
5 | const ref = useRef(null);
6 |
7 | const handleClickOutside = (event) => {
8 | if (ref.current && !ref.current.contains(event.target)) {
9 | setIsComponentVisible(false);
10 | }
11 | };
12 |
13 | useEffect(() => {
14 | document.addEventListener('click', handleClickOutside, true);
15 | return () => {
16 | document.removeEventListener('click', handleClickOutside, true);
17 | };
18 | }, []);
19 |
20 | return {ref, isComponentVisible, setIsComponentVisible};
21 | }
22 |
--------------------------------------------------------------------------------
/templates/customscript/selectionmask.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "selectionmask",
3 | "desc": "create mask from current selection",
4 | "icon_path": "M4.25 2A2.25 2.25 0 002 4.25v11.5A2.25 2.25 0 004.25 18h11.5A2.25 2.25 0 0018 15.75V4.25A2.25 2.25 0 0015.75 2H4.25zM6 13.25V3.5h8v9.75a.75.75 0 01-1.064.681L10 12.576l-2.936 1.355A.75.75 0 016 13.25z",
5 |
6 | "executable": false,
7 | "func": [
8 | {
9 | "_obj": "make",
10 | "new": {
11 | "_class": "channel"
12 | },
13 | "at": {
14 | "_ref": "channel",
15 | "_enum": "channel",
16 | "_value": "mask"
17 | },
18 | "using": {
19 | "_enum": "userMaskEnabled",
20 | "_value": "revealSelection"
21 | }
22 | }
23 | ]
24 | }
25 |
--------------------------------------------------------------------------------
/src/utils/Token.ts:
--------------------------------------------------------------------------------
1 | const fs = require('uxp').storage.localFileSystem;
2 | export function GetTokenFor(key: string) {
3 | const savedToken = localStorage.getItem(key);
4 | return new Promise(async (resolve, reject) => {
5 | if (!savedToken) {
6 | reject('Not Exist');
7 | return null;
8 | }
9 | const newToken = await fs.getEntryForPersistentToken(savedToken);
10 | newToken.isFolder ? resolve(newToken) : reject('cant do that');
11 | });
12 | }
13 | export async function PickFolderFor(key: string) {
14 | return new Promise(async (resolve, reject) => {
15 | const fo_result = await fs.getFolder();
16 | const _token = await fs.createPersistentToken(fo_result);
17 | localStorage.setItem(key, _token);
18 |
19 | resolve(fo_result);
20 | });
21 | }
22 |
--------------------------------------------------------------------------------
/templates/customscript/selectiontool.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "selectiontool",
3 | "desc": "selection tools",
4 |
5 | "icon_path": "M4.25 2A2.25 2.25 0 002 4.25v2a.75.75 0 001.5 0v-2a.75.75 0 01.75-.75h2a.75.75 0 000-1.5h-2zM13.75 2a.75.75 0 000 1.5h2a.75.75 0 01.75.75v2a.75.75 0 001.5 0v-2A2.25 2.25 0 0015.75 2h-2zM3.5 13.75a.75.75 0 00-1.5 0v2A2.25 2.25 0 004.25 18h2a.75.75 0 000-1.5h-2a.75.75 0 01-.75-.75v-2zM18 13.75a.75.75 0 00-1.5 0v2a.75.75 0 01-.75.75h-2a.75.75 0 000 1.5h2A2.25 2.25 0 0018 15.75v-2zM7 10a3 3 0 116 0 3 3 0 01-6 0z",
6 | "executable": false,
7 | "func": [
8 | {
9 | "_obj": "select",
10 | "_target": [
11 | {
12 | "_ref": "marqueeRectTool"
13 | }
14 | ],
15 | "dontRecord": true,
16 | "forceNotify": true
17 | }
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/Divider.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | namespace Spectrum {
4 | export type DividerSize = 'small' | 'medium' | 'large';
5 | }
6 |
7 | type Props = {
8 | className?: string;
9 | size?: Spectrum.DividerSize;
10 | };
11 |
12 | declare global {
13 | namespace JSX {
14 | interface IntrinsicElements {
15 | 'sp-divider': {
16 | children: undefined;
17 | class?: string;
18 | size?: Spectrum.DividerSize;
19 | };
20 | }
21 | }
22 | }
23 |
24 | /**
25 | * Renders a divider.
26 | *
27 | * @example
28 | * ```jsx
29 | *
30 | * ```
31 | */
32 | export default function Divider(props: Props) {
33 | return (
34 |
35 | {undefined}
36 |
37 | );
38 | }
39 |
--------------------------------------------------------------------------------
/templates/customscript/gaussianblur.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gaussianblur",
3 | "desc": "gaussian blur that shit",
4 | "icon_path": "M15.98 1.804a1 1 0 00-1.96 0l-.24 1.192a1 1 0 01-.784.785l-1.192.238a1 1 0 000 1.962l1.192.238a1 1 0 01.785.785l.238 1.192a1 1 0 001.962 0l.238-1.192a1 1 0 01.785-.785l1.192-.238a1 1 0 000-1.962l-1.192-.238a1 1 0 01-.785-.785l-.238-1.192zM6.949 5.684a1 1 0 00-1.898 0l-.683 2.051a1 1 0 01-.633.633l-2.051.683a1 1 0 000 1.898l2.051.684a1 1 0 01.633.632l.683 2.051a1 1 0 001.898 0l.683-2.051a1 1 0 01.633-.633l2.051-.683a1 1 0 000-1.898l-2.051-.683a1 1 0 01-.633-.633L6.95 5.684zM13.949 13.684a1 1 0 00-1.898 0l-.184.551a1 1 0 01-.632.633l-.551.183a1 1 0 000 1.898l.551.183a1 1 0 01.633.633l.183.551a1 1 0 001.898 0l.184-.551a1 1 0 01.632-.633l.551-.183a1 1 0 000-1.898l-.551-.184a1 1 0 01-.633-.632l-.183-.551z",
5 | "executable": true,
6 | "script": "gb.js",
7 | "func": [{}]
8 | }
9 |
--------------------------------------------------------------------------------
/src/components/MenuDivider.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | type Props = {
4 | className?: string;
5 | };
6 |
7 | declare global {
8 | namespace JSX {
9 | interface IntrinsicElements {
10 | 'sp-menu-divider': {
11 | children: undefined;
12 | class?: string;
13 | };
14 | }
15 | }
16 | }
17 |
18 | /**
19 | * A menu divider element that separates {@link Spectrum.MenuItem}s contained
20 | * in a {@link Spectrum.Dropdown}.
21 | *
22 | * @example
23 | *
24 | *
25 | * Group 1
26 | *
27 | * Group 2
28 | *
29 | *
30 | */
31 | export default function MenuDivider(props: Props) {
32 | return {undefined};
33 | }
34 |
--------------------------------------------------------------------------------
/templates/customscript/jointext.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jointext",
3 | "desc": "merge selected text layers into 1 layer",
4 | "icon_path": "M10 4.5c1.215 0 2.417.055 3.604.162a.68.68 0 01.615.597c.124 1.038.208 2.088.25 3.15l-1.689-1.69a.75.75 0 00-1.06 1.061l2.999 3a.75.75 0 001.06 0l3.001-3a.75.75 0 10-1.06-1.06l-1.748 1.747a41.31 41.31 0 00-.264-3.386 2.18 2.18 0 00-1.97-1.913 41.512 41.512 0 00-7.477 0 2.18 2.18 0 00-1.969 1.913 41.16 41.16 0 00-.16 1.61.75.75 0 101.495.12c.041-.52.093-1.038.154-1.552a.68.68 0 01.615-.597A40.012 40.012 0 0110 4.5zM5.281 9.22a.75.75 0 00-1.06 0l-3.001 3a.75.75 0 101.06 1.06l1.748-1.747c.042 1.141.13 2.27.264 3.386a2.18 2.18 0 001.97 1.913 41.533 41.533 0 007.477 0 2.18 2.18 0 001.969-1.913c.064-.534.117-1.071.16-1.61a.75.75 0 10-1.495-.12c-.041.52-.093 1.037-.154 1.552a.68.68 0 01-.615.597 40.013 40.013 0 01-7.208 0 .68.68 0 01-.615-.597 39.785 39.785 0 01-.25-3.15l1.689 1.69a.75.75 0 001.06-1.061l-2.999-3z",
5 | "executable": true,
6 | "script": "jointext.js",
7 | "func": [{}]
8 | }
9 |
--------------------------------------------------------------------------------
/templates/workflows/facerestore.json:
--------------------------------------------------------------------------------
1 | {
2 | "337": {
3 | "inputs": {
4 | "image": "BaseImage_u97o9.png",
5 | "choose file to upload": "image"
6 | },
7 | "show": true,
8 | "title": "BaseImage",
9 | "class_type": "LoadImage"
10 | },
11 | "340": {
12 | "inputs": {
13 | "filename_prefix": "ComfyUI",
14 | "images": [
15 | "341",
16 | 0
17 | ]
18 | },
19 | "show": false,
20 | "title": "Save Image",
21 | "class_type": "SaveImage"
22 | },
23 | "341": {
24 | "inputs": {
25 | "model_name": "GFPGANv1.4.pth",
26 | "facedetection": "retinaface_resnet50",
27 | "codeformer_fidelity": 1,
28 | "centerface_only": false,
29 | "upscale": false,
30 | "upscale_then_restore": false,
31 | "upscale_model_name": "4x-UltraSharp.pth",
32 | "image": [
33 | "337",
34 | 0
35 | ]
36 | },
37 | "show": true,
38 | "title": "FaceRestoreCFRevamped",
39 | "class_type": "FaceRestoreCFRevamped"
40 | }
41 | }
--------------------------------------------------------------------------------
/src/customcomponents/WorkflowPicker.tsx:
--------------------------------------------------------------------------------
1 | import React, {useEffect, useState} from 'react';
2 | import DropDrownPicker from './DropDownPicker';
3 | import {DDItems, DropdownPickerv2} from './DropdownPickerv2';
4 |
5 | type Props = {
6 | workflowFiles: any[];
7 | onSelectionChange: (e: any) => void;
8 | };
9 | export default function WorkflowPicker(props: Props) {
10 | const [items, setItems] = useState([]);
11 | function handleOnChange(e: DDItems) {
12 | const result = props?.workflowFiles.filter((ex) => ex.name.includes(e.value));
13 |
14 | props?.onSelectionChange(result[0]);
15 | }
16 | useEffect(() => {
17 | if (props?.workflowFiles) {
18 | setItems(props?.workflowFiles.map((e) => e.name.replace('.json', '')));
19 | }
20 | }, [props?.workflowFiles]);
21 |
22 | //return ;
23 | return ;
24 | }
25 |
--------------------------------------------------------------------------------
/src/components/Label.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | namespace Spectrum {
4 | export type LabelSlot = 'label';
5 | }
6 |
7 | type Props = {
8 | children?: React.ReactNode;
9 | className?: string;
10 | slot?: Spectrum.LabelSlot;
11 | isRequired?: boolean;
12 | };
13 |
14 | declare global {
15 | namespace JSX {
16 | interface IntrinsicElements {
17 | 'sp-label': {
18 | children?: React.ReactNode;
19 | class?: string;
20 | slot?: Spectrum.LabelSlot;
21 | isrequired?: boolean;
22 | };
23 | }
24 | }
25 | }
26 |
27 | /**
28 | * Renders a text label. Can also be used to add a label to many Spectrum UXP
29 | * UI elements when using the `slot="label"` attribute.
30 | *
31 | * @example
32 | * ```jsx
33 | *
34 | * Required label in a slot
35 | *
36 | * ```
37 | */
38 | export default function Label(props: Props) {
39 | return (
40 |
45 | {props?.children}
46 |
47 | );
48 | }
49 |
--------------------------------------------------------------------------------
/src/customcomponents/FloatIntWidget.tsx:
--------------------------------------------------------------------------------
1 | import React, {forwardRef, useEffect, useState} from 'react';
2 | import {Label, Textfield} from '../components';
3 |
4 | type Props = {
5 | title?: string;
6 | value?: number;
7 | min?: number;
8 | max?: number;
9 | onChange?: (e: number) => void;
10 | };
11 |
12 | export const FloatIntWidget = (props: Props) => {
13 | const [val, setVal] = useState(0);
14 |
15 | useEffect(() => {
16 | if (!val) return;
17 | setVal((e) => val);
18 | props?.onChange(Number(val));
19 | }, [val]);
20 |
21 | useEffect(() => {
22 | setVal((val) => props?.value);
23 | }, [props?.value]);
24 |
25 | return (
26 |
27 |
30 |
31 | {
37 | let return_value = Number(e.target.value);
38 | if (return_value > props?.max) return_value = props?.max;
39 | else if (return_value < props?.min) return_value = props?.min;
40 | setVal(return_value);
41 | }}
42 | />
43 |
44 | );
45 | };
46 |
--------------------------------------------------------------------------------
/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {entrypoints} from 'uxp';
3 | import {ControllerComponent, ControllerProps, MenuItems, PanelController, RT} from './controllers/PanelController';
4 | import {CommandMap, Panels} from './panels/Panels';
5 | import {CommandController} from './controllers/CommandController';
6 | import './index.css';
7 | interface Commands extends ControllerProps {
8 | component: ControllerComponent;
9 | }
10 | export const Commands: Readonly = [];
11 | const commands = Commands.reduce((acc, {component, ...command}) => {
12 | acc[command.id] = new CommandController(component, command);
13 | return acc;
14 | }, {} as CommandMap);
15 | const panels = Panels.reduce((acc, {component, menuItems, ...panel}) => {
16 | const _menuItems = menuItems.reduce((acc, menuItem) => {
17 | const oninvoke = (): RT => menuItem.oninvoke(commands);
18 | acc.push({...menuItem, oninvoke});
19 | return acc;
20 | }, []);
21 | acc[panel.id] = new PanelController(component, {...panel, menuItems: _menuItems});
22 | //
23 | return acc;
24 | }, {});
25 | export interface UxpPluginInfo {
26 | id: string;
27 | version: string;
28 | name: string;
29 | manifest: JSON;
30 | }
31 | entrypoints.setup({
32 | panels,
33 | });
34 |
--------------------------------------------------------------------------------
/templates/customscript/redbox.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "redbox",
3 | "desc": "redbox refly harun podcast template",
4 | "icon_path": "M1 4.75C1 3.784 1.784 3 2.75 3h14.5c.966 0 1.75.784 1.75 1.75v10.515a1.75 1.75 0 01-1.75 1.75h-1.5c-.078 0-.155-.005-.23-.015H4.48c-.075.01-.152.015-.23.015h-1.5A1.75 1.75 0 011 15.265V4.75zm16.5 7.385V11.01a.25.25 0 00-.25-.25h-1.5a.25.25 0 00-.25.25v1.125c0 .138.112.25.25.25h1.5a.25.25 0 00.25-.25zm0 2.005a.25.25 0 00-.25-.25h-1.5a.25.25 0 00-.25.25v1.125c0 .108.069.2.165.235h1.585a.25.25 0 00.25-.25v-1.11zm-15 1.11v-1.11a.25.25 0 01.25-.25h1.5a.25.25 0 01.25.25v1.125a.25.25 0 01-.164.235H2.75a.25.25 0 01-.25-.25zm2-4.24v1.125a.25.25 0 01-.25.25h-1.5a.25.25 0 01-.25-.25V11.01a.25.25 0 01.25-.25h1.5a.25.25 0 01.25.25zm13-2.005V7.88a.25.25 0 00-.25-.25h-1.5a.25.25 0 00-.25.25v1.125c0 .138.112.25.25.25h1.5a.25.25 0 00.25-.25zM4.25 7.63a.25.25 0 01.25.25v1.125a.25.25 0 01-.25.25h-1.5a.25.25 0 01-.25-.25V7.88a.25.25 0 01.25-.25h1.5zm0-3.13a.25.25 0 01.25.25v1.125a.25.25 0 01-.25.25h-1.5a.25.25 0 01-.25-.25V4.75a.25.25 0 01.25-.25h1.5zm11.5 1.625a.25.25 0 01-.25-.25V4.75a.25.25 0 01.25-.25h1.5a.25.25 0 01.25.25v1.125a.25.25 0 01-.25.25h-1.5zm-9 3.125a.75.75 0 000 1.5h6.5a.75.75 0 000-1.5h-6.5z",
5 | "executable": true,
6 | "script": "redbox.js",
7 | "func": [{}]
8 | }
9 |
--------------------------------------------------------------------------------
/src/components/Detail.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | namespace Spectrum {
4 | export type DetailScript = 'latin' | 'han' | 'arabic' | 'hebrew';
5 | export type DetailSize = 'S' | 'M' | 'L' | 'XL';
6 | export type DetailWeight = 'light' | 'default';
7 | }
8 |
9 | type Props = {
10 | children?: React.ReactNode;
11 | className?: string;
12 | script?: Spectrum.DetailScript;
13 | size?: Spectrum.DetailSize;
14 | weight?: Spectrum.DetailWeight;
15 | };
16 |
17 | declare global {
18 | namespace JSX {
19 | interface IntrinsicElements {
20 | 'sp-detail': {
21 | children?: React.ReactNode;
22 | class?: string;
23 | script?: Spectrum.DetailScript;
24 | size?: Spectrum.DetailSize;
25 | weight?: Spectrum.DetailWeight;
26 | };
27 | }
28 | }
29 | }
30 |
31 | /**
32 | * Renders detail text in a smaller font.
33 | *
34 | * @example
35 | * ```jsx
36 | * The fine details
37 | * ```
38 | */
39 | export default function Detail(props: Props) {
40 | return (
41 |
47 | {props?.children}
48 |
49 | );
50 | }
51 |
--------------------------------------------------------------------------------
/src/components/Body.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | namespace Spectrum {
4 | export type BodyClassification = 'serif' | 'sans serif';
5 | export type BodyScript = 'latin' | 'han' | 'arabic' | 'hebrew';
6 | export type BodySize = 'XS' | 'S' | 'M' | 'L' | 'XL' | 'XXL';
7 | }
8 |
9 | type Props = {
10 | children?: React.ReactNode;
11 | className?: string;
12 | classification?: Spectrum.BodyClassification;
13 | script?: Spectrum.BodyScript;
14 | size?: Spectrum.BodySize;
15 | };
16 |
17 | declare global {
18 | namespace JSX {
19 | interface IntrinsicElements {
20 | 'sp-body': {
21 | children?: React.ReactNode;
22 | class?: string;
23 | classification?: Spectrum.BodyClassification;
24 | script?: Spectrum.BodyScript;
25 | size?: Spectrum.BodySize;
26 | };
27 | }
28 | }
29 | }
30 |
31 | /**
32 | * Renders body text that is theme aware.
33 | *
34 | * @example
35 | * ```jsx
36 | * This is some body text
37 | * ```
38 | */
39 | export default function Body(props: Props) {
40 | return (
41 |
47 | {props?.children}
48 |
49 | );
50 | }
51 |
--------------------------------------------------------------------------------
/src/controllers/CommandController.ts:
--------------------------------------------------------------------------------
1 | import { createRoot } from 'react-dom/client';
2 | import { ControllerComponent, ControllerProps } from './PanelController';
3 |
4 | const _id = Symbol('_id');
5 | const _root = Symbol('_root');
6 | const _Component = Symbol('_Component');
7 | const _dialogOpts = Symbol('_dialogOpts');
8 |
9 | export class CommandController {
10 | constructor(Component: React.FC | ControllerComponent, { id, ...dialogOpts }: ControllerProps) {
11 | this[_id] = null;
12 | this[_root] = null;
13 | this[_Component] = null;
14 | this[_dialogOpts] = {};
15 |
16 | this[_Component] = Component;
17 | this[_id] = id;
18 | this[_dialogOpts] = Object.assign(
19 | {},
20 | {
21 | title: id,
22 | resize: 'none',
23 | size: {
24 | width: 480,
25 | height: 320,
26 | },
27 | },
28 | dialogOpts
29 | );
30 | ['run'].forEach((fn) => (this[fn] = this[fn].bind(this)));
31 | }
32 |
33 | async run() {
34 | if (!this[_root]) {
35 | this[_root] = document.createElement('dialog');
36 | const root = createRoot(this[_root]);
37 | root.render(this[_Component]({ dialog: this[_root] }));
38 | }
39 |
40 | document.body.appendChild(this[_root]);
41 |
42 | await this[_root].showModal(this[_dialogOpts]);
43 | this[_root].remove();
44 | }
45 | }
--------------------------------------------------------------------------------
/templates/customscript/splittext.js:
--------------------------------------------------------------------------------
1 | ///
2 |
3 |
4 |
5 |
6 |
7 | let i = 0;
8 | let selected_layer = await doc.activeLayers[0];
9 | let texitem = selected_layer.textItem;
10 |
11 | async function doStuff() {
12 |
13 | const all_texts = texitem.contents.split("\r");
14 |
15 | for await (const text of all_texts) {
16 | await executeAsModal(async () => {
17 | const newlayer = await selected_layer.duplicate();
18 | const newselectedlayer = newlayer.textItem;
19 | newselectedlayer.contents = text;
20 | newlayer.name = "dcsmstext_tamper";
21 | let height = newlayer.boundsNoEffects.bottom - newlayer.boundsNoEffects.top;
22 |
23 | console.log(height * i);
24 | await newlayer.translate(0, height * i);
25 | i++;
26 |
27 | }, { "commandName": "split text" })
28 | }
29 |
30 | }
31 | await doStuff();
32 | await executeAsModal(
33 | async (_executionContext, descriptor) => {
34 |
35 | let hostControl = _executionContext.hostControl;
36 | let documentID = app.activeDocument.id;
37 | let suspensionID = await hostControl.suspendHistory({
38 | documentID: documentID,
39 | name: 'split',
40 | });
41 | await selected_layer.delete();
42 |
43 | await hostControl.resumeHistory(suspensionID, true);
44 |
45 |
46 | },
47 | { commandName: 'split text()' }
48 | );
49 |
50 |
51 |
--------------------------------------------------------------------------------
/templates/customscript/resizelayer.js:
--------------------------------------------------------------------------------
1 | ///
2 | console.log("resilelayer");
3 | let layer = await doc.activeLayers[0];
4 | let b = layer.boundsNoEffects;
5 | let w = b.right - b.left;
6 | let h = b.bottom - b.top;
7 | const docWidth = app.activeDocument.width;
8 | const docHeight = app.activeDocument.height;
9 | let scale;
10 |
11 | if (w < docWidth) {
12 | scale = docWidth / w * 100;
13 | }
14 | else {
15 | scale = (docHeight / h) * 100;
16 | }
17 | logme("w docWidth");
18 |
19 | const aligncenter = [{
20 | "_obj": "align",
21 | "_target": [
22 | {
23 | "_ref": "layer",
24 | "_enum": "ordinal",
25 | "_value": "targetEnum"
26 | }
27 | ],
28 | "using": {
29 | "_enum": "alignDistributeSelector",
30 | "_value": "ADSCentersH"
31 | },
32 | "alignToCanvas": true,
33 | }, {
34 | "_obj": "align",
35 | "_target": [
36 | {
37 | "_ref": "layer",
38 | "_enum": "ordinal",
39 | "_value": "targetEnum"
40 | }
41 | ],
42 | "using": {
43 | "_enum": "alignDistributeSelector",
44 | "_value": "ADSCentersV"
45 | },
46 | "alignToCanvas": true,
47 | }]
48 | await executeAsModal(
49 | async () => {
50 | await batchPlay(aligncenter, {});
51 | await layer.scale(scale, scale);
52 | },
53 | { commandName: 'align center' }
54 | );
55 |
--------------------------------------------------------------------------------
/src/components/index.ts:
--------------------------------------------------------------------------------
1 | // Typography
2 | import Body from './Body';
3 | import Detail from './Detail';
4 | import Heading from './Heading';
5 | import Label from './Label';
6 | // UI
7 | import ActionButton from './ActionButton';
8 | import Button from './Button';
9 | import Checkbox from './Checkbox';
10 | import Divider from './Divider';
11 | import Dropdown from './Dropdown';
12 | import Icon from './Icon';
13 | import Link from './Link';
14 | import Menu from './Menu';
15 | import MenuDivider from './MenuDivider';
16 | import MenuItem from './MenuItem';
17 | import Progressbar from './Progressbar';
18 | import Radio from './Radio';
19 | import RadioGroup from './RadioGroup';
20 | import Slider from './Slider';
21 | import Textarea from './Textarea';
22 | import Textfield from './Textfield';
23 |
24 | const Spectrum = {
25 | Body,
26 | Detail,
27 | Heading,
28 | Label,
29 | ActionButton,
30 | Button,
31 | Checkbox,
32 | Divider,
33 | Dropdown,
34 | Icon,
35 | Link,
36 | Menu,
37 | MenuDivider,
38 | MenuItem,
39 | Progressbar,
40 | Radio,
41 | RadioGroup,
42 | Slider,
43 | Textarea,
44 | Textfield,
45 | };
46 |
47 | export {
48 | Body,
49 | Detail,
50 | Heading,
51 | Label,
52 | ActionButton,
53 | Button,
54 | Checkbox,
55 | Divider,
56 | Dropdown,
57 | Icon,
58 | Link,
59 | Progressbar,
60 | Radio,
61 | RadioGroup,
62 | Slider,
63 | Textarea,
64 | Textfield,
65 | };
66 |
67 | export default Spectrum;
68 |
--------------------------------------------------------------------------------
/src/interfaces/types.ts:
--------------------------------------------------------------------------------
1 | export type listItems = {
2 | id: number;
3 | sub_id: number;
4 | name: string;
5 | path?: string;
6 | item_index: number;
7 | };
8 | export enum STATE {
9 | enable,
10 | disable,
11 | interrupt,
12 | }
13 | export namespace NODETYPE {
14 | //FLOAT,INT,STRING,BOOLEAN
15 | export type BOOLEAN = {
16 | default?: boolean;
17 | label_off?: string;
18 | label_on?: string;
19 | };
20 | export type FLOAT = {
21 | default?: number;
22 | min?: number;
23 | max?: number;
24 | step?: number;
25 | };
26 | export type STRING = {
27 | default?: string;
28 | multiline?: boolean;
29 | };
30 | export type INT = {
31 | default?: number;
32 | min?: number;
33 | max?: number;
34 | step?: number;
35 | };
36 | }
37 | export interface BOUNDS {
38 | left: number;
39 | top: number;
40 | right: number;
41 | bottom: number;
42 | }
43 | export type InlineDialogContent = {
44 | isloading?: boolean;
45 | show: boolean;
46 | title: string;
47 | message: string;
48 | onOk?: (e: string) => void;
49 | onCancel?: (e: string) => void;
50 | };
51 |
52 | export type GLOBALCONFIG = {
53 | qp_generator_model: string;
54 | qp_inpainting_model: string;
55 | quickpanel_size: [];
56 | imageloader_node: string[];
57 | };
58 | export type CUSTOMSCRIPT = {
59 | name: string;
60 | desc?: string;
61 | icon_path: string;
62 | executable?: boolean;
63 | script?: string;
64 | func: any;
65 | };
66 |
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "options": {
4 | "env": {
5 | "NODE_OPTIONS": "--openssl-legacy-provider"
6 | }
7 | },
8 | "tasks": [
9 | {
10 | "type": "shell",
11 | "command": "pnpm run build",
12 | "label": "REVAMPED-DEBUG",
13 | "group": {
14 | "kind": "build",
15 | "isDefault": true
16 | },
17 | "presentation": {
18 | "reveal": "silent",
19 | "revealProblems": "onProblem",
20 | "close": true
21 | },
22 | "problemMatcher": []
23 | },
24 | {
25 | "label": "REVAMPED-RELEASE",
26 | "type": "shell",
27 | "command": "uxp plugin package --manifest .\\dist\\manifest.json --outputPath .\\build",
28 | "dependsOn": [
29 | "REVAMPED-PACKTORELEASE"
30 | ],
31 | "group": "build",
32 | "problemMatcher": []
33 | },
34 | {
35 | "type": "shell",
36 | "command": "pnpm run release",
37 | "label": "REVAMPED-PACKTORELEASE",
38 | "group": {
39 | "kind": "build",
40 | "isDefault": false
41 | },
42 | "presentation": {
43 | "reveal": "silent",
44 | "revealProblems": "onProblem",
45 | "close": true
46 | }
47 | }
48 | ],
49 | }
--------------------------------------------------------------------------------
/src/panels/Panels.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {RT, ControllerProps, MenuItems, ControllerComponent} from '../controllers/PanelController';
3 | import {CommandController} from '../controllers/CommandController';
4 |
5 | import {MainPanelv2} from './MainPanelv2';
6 | import {QuickPanel} from './QuickPanel';
7 | import {CustomScriptPanel} from './CustomScriptPanel';
8 | export const COMMAND_IDS = Object.freeze({
9 | SHOW_ABOUT: 'showAbout',
10 | });
11 | type CommandId = (typeof COMMAND_IDS)[keyof typeof COMMAND_IDS];
12 | export type CommandMap = Record;
13 | type InvokeFn = (controllers: CommandMap) => RT;
14 |
15 | export type Panels = Omit & {
16 | menuItems?: (Omit & {oninvoke: InvokeFn})[];
17 | } & {
18 | component: ControllerComponent;
19 | };
20 |
21 | export const Panels: Readonly = [
22 | {
23 | id: 'mainpanel',
24 | component: () => ,
25 | menuItems: [{id: 'reloadme', label: 'Reload Plugin', enabled: true, checked: false, oninvoke: () => location.reload()}],
26 | },
27 | {
28 | id: 'quickpanel',
29 | component: () => ,
30 | menuItems: [{id: 'reloadme2', label: 'Reload QuickPanel', enabled: true, checked: false, oninvoke: () => location.reload()}],
31 | },
32 | {
33 | id: 'customscriptpanel',
34 | component: () => ,
35 | menuItems: [{id: 'reloadme3', label: 'Reload CustomScript Panel', enabled: true, checked: false, oninvoke: () => location.reload()}],
36 | },
37 | ];
38 |
--------------------------------------------------------------------------------
/src/components/Link.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef } from 'react';
2 |
3 | namespace Spectrum {
4 | export type LinkVariant = 'overBackground';
5 | }
6 |
7 | type Props = {
8 | children?: React.ReactNode;
9 | onClick?: (e: MouseEvent) => void;
10 | className?: string;
11 | href?: string;
12 | quiet?: boolean;
13 | variant?: Spectrum.LinkVariant;
14 | };
15 |
16 | declare global {
17 | namespace JSX {
18 | interface IntrinsicElements {
19 | 'sp-link': {
20 | children?: React.ReactNode;
21 | ref?: React.RefObject;
22 | class?: string;
23 | href?: string;
24 | quiet?: boolean;
25 | variant?: Spectrum.LinkVariant;
26 | };
27 | }
28 | }
29 | }
30 |
31 | /**
32 | * Renders a link, that when clicked can launch a webpage in the user's default browser.
33 | *
34 | * @example
35 | * ```jsx
36 | * TJW
37 | * ```
38 | */
39 | export default function Link(props: Props) {
40 | const ref = useRef(null);
41 |
42 | useEffect(() => {
43 | const dispatchClick = (e: MouseEvent) => props.onClick?.(e);
44 |
45 | ref.current?.addEventListener('click', dispatchClick);
46 | return () => {
47 | ref.current?.removeEventListener('click', dispatchClick);
48 | };
49 | }, [props.onClick]);
50 |
51 | return (
52 |
59 | {props.children}
60 |
61 | );
62 | }
63 |
--------------------------------------------------------------------------------
/src/components/Heading.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | namespace Spectrum {
4 | export type HeadingClassification = 'serif' | 'sans serif';
5 | export type HeadingScript = 'latin' | 'han' | 'arabic' | 'hebrew';
6 | export type HeadingSize =
7 | | 'XXS'
8 | | 'XS'
9 | | 'S'
10 | | 'M'
11 | | 'L'
12 | | 'XL'
13 | | 'XXL'
14 | | 'XXXL';
15 | export type HeadingWeight = 'light' | 'default' | 'heavy';
16 | }
17 |
18 | type Props = {
19 | children?: React.ReactNode;
20 | className?: string;
21 | classification?: Spectrum.HeadingClassification;
22 | script?: Spectrum.HeadingScript;
23 | size?: Spectrum.HeadingSize;
24 | weight?: Spectrum.HeadingWeight;
25 | };
26 |
27 | declare global {
28 | namespace JSX {
29 | interface IntrinsicElements {
30 | 'sp-heading': {
31 | children?: React.ReactNode;
32 | class?: string;
33 | classification?: Spectrum.HeadingClassification;
34 | script?: Spectrum.HeadingScript;
35 | size?: Spectrum.HeadingSize;
36 | weight?: Spectrum.HeadingWeight;
37 | };
38 | }
39 | }
40 | }
41 |
42 | /**
43 | * Renders heading text that is theme aware.
44 | *
45 | * @example
46 | * ```jsx
47 | * Heading XL
48 | * ```
49 | */
50 | export default function Heading(props: Props) {
51 | return (
52 |
59 | {props?.children}
60 |
61 | );
62 | }
63 |
--------------------------------------------------------------------------------
/templates/customscript/fixmask.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fixmask",
3 | "desc": "trim masked for 1 pixel inset",
4 |
5 | "icon_path": "M12 4.467c0-.405.262-.75.559-1.027.276-.257.441-.584.441-.94 0-.828-.895-1.5-2-1.5s-2 .672-2 1.5c0 .362.171.694.456.953.29.265.544.6.544.994a.968.968 0 01-1.024.974 39.655 39.655 0 01-3.014-.306.75.75 0 00-.847.847c.14.993.242 1.999.306 3.014A.968.968 0 014.447 10c-.393 0-.729-.253-.994-.544C3.194 9.17 2.862 9 2.5 9 1.672 9 1 9.895 1 11s.672 2 1.5 2c.356 0 .683-.165.94-.441.276-.297.622-.559 1.027-.559a.997.997 0 011.004 1.03 39.747 39.747 0 01-.319 3.734.75.75 0 00.64.842c1.05.146 2.111.252 3.184.318A.97.97 0 0010 16.948c0-.394-.254-.73-.545-.995C9.171 15.693 9 15.362 9 15c0-.828.895-1.5 2-1.5s2 .672 2 1.5c0 .356-.165.683-.441.94-.297.276-.559.622-.559 1.027a.998.998 0 001.03 1.005c1.337-.05 2.659-.162 3.961-.337a.75.75 0 00.644-.644c.175-1.302.288-2.624.337-3.961A.998.998 0 0016.967 12c-.405 0-.75.262-1.027.559-.257.276-.584.441-.94.441-.828 0-1.5-.895-1.5-2s.672-2 1.5-2c.362 0 .694.17.953.455.265.291.601.545.995.545a.97.97 0 00.976-1.024 41.159 41.159 0 00-.318-3.184.75.75 0 00-.842-.64c-1.228.164-2.473.271-3.734.319A.997.997 0 0112 4.467z",
6 | "func": [
7 | {
8 | "_obj": "select",
9 | "_target": [
10 | {
11 | "_ref": "channel",
12 | "_enum": "channel",
13 | "_value": "mask"
14 | }
15 | ]
16 | },
17 | {
18 | "_obj": "minimum",
19 | "radius": {
20 | "_unit": "pixelsUnit",
21 | "_value": 1
22 | },
23 | "preserveShape": {
24 | "_enum": "preserveShape",
25 | "_value": "squareness"
26 | }
27 | }
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------
/src/components/Progressbar.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | namespace Spectrum {
4 | export type ProgressbarVariant = 'overBackground';
5 | export type ProgressbarSize = 'small';
6 | }
7 |
8 | type Props = {
9 | children?: React.ReactNode;
10 | className?: string;
11 | min?: number;
12 | max: number;
13 | size?: Spectrum.ProgressbarSize;
14 | showValue?: boolean;
15 | value: number;
16 | valueLabel?: string;
17 | variant?: Spectrum.ProgressbarVariant;
18 | };
19 |
20 | declare global {
21 | namespace JSX {
22 | interface IntrinsicElements {
23 | 'sp-progressbar': {
24 | children?: React.ReactNode;
25 | class?: string;
26 | min?: number;
27 | max: number;
28 | size?: Spectrum.ProgressbarSize;
29 | 'show-value'?: boolean;
30 | value: number;
31 | 'value-label'?: string;
32 | variant?: Spectrum.ProgressbarVariant;
33 | };
34 | }
35 | }
36 | }
37 |
38 | /**
39 | * Renders a progress bar.
40 | *
41 | * @example
42 | * ```jsx
43 | *
44 | * Uploading...
45 | *
46 | * ```
47 | */
48 | export default function Progressbar(props: Props) {
49 | return (
50 |
60 | {props.children}
61 |
62 | );
63 | }
64 |
--------------------------------------------------------------------------------
/src/customcomponents/TextPrompt.tsx:
--------------------------------------------------------------------------------
1 | import React, {useEffect, useState} from 'react';
2 | import {Textarea} from '../components';
3 |
4 | export type Props = {
5 | showTextPanel?: boolean;
6 | disabled?: boolean;
7 | title: string;
8 | content: string;
9 | onChange: (e: string) => void;
10 | };
11 |
12 | export const TextPrompt = (props: Props) => {
13 | const [visible, setVisible] = useState(props?.showTextPanel || false);
14 | const [currentValue, setCurrentValue] = useState(props?.content);
15 | useEffect(() => {
16 | if (currentValue) props?.onChange(currentValue);
17 | }, [currentValue]);
18 | return (
19 |
20 |
{
23 | if (!props?.disabled && !props?.showTextPanel) setVisible(!visible);
24 | }}
25 | >
26 | {!visible && (
27 | <>
28 |
{props?.title}
29 |
{currentValue || 'None'}
30 | >
31 | )}
32 |
33 |
46 |
47 |
48 | );
49 | };
50 |
--------------------------------------------------------------------------------
/templates/image_generator.json:
--------------------------------------------------------------------------------
1 | {
2 | "3": {
3 | "inputs": {
4 | "seed": "[seed]",
5 | "steps": 4,
6 | "cfg": 1.3,
7 | "sampler_name": "lcm",
8 | "scheduler": "sgm_uniform",
9 | "denoise": 1,
10 | "model": ["12", 0],
11 | "positive": ["6", 0],
12 | "negative": ["7", 0],
13 | "latent_image": ["5", 0]
14 | },
15 | "class_type": "KSampler"
16 | },
17 | "4": {
18 | "inputs": {
19 | "ckpt_name": "[model_name]"
20 | },
21 | "class_type": "CheckpointLoaderSimple"
22 | },
23 | "5": {
24 | "inputs": {
25 | "width": "[image_width]",
26 | "height": "[image_height]",
27 | "batch_size": 1
28 | },
29 | "class_type": "EmptyLatentImage"
30 | },
31 | "6": {
32 | "inputs": {
33 | "text": "[positive_prompt]",
34 | "clip": ["11", 1]
35 | },
36 | "class_type": "CLIPTextEncode"
37 | },
38 | "7": {
39 | "inputs": {
40 | "text": "[negative_prompt]",
41 | "clip": ["11", 1]
42 | },
43 | "class_type": "CLIPTextEncode"
44 | },
45 | "8": {
46 | "inputs": {
47 | "samples": ["3", 0],
48 | "vae": ["4", 2]
49 | },
50 | "class_type": "VAEDecode"
51 | },
52 | "9": {
53 | "inputs": {
54 | "filename_prefix": "ComfyUI",
55 | "images": ["8", 0]
56 | },
57 | "class_type": "SaveImage"
58 | },
59 | "11": {
60 | "inputs": {
61 | "lora_name": "lcm_sd15_pytorch_lora_weights.safetensors",
62 | "strength_model": 1,
63 | "strength_clip": 1,
64 | "model": ["4", 0],
65 | "clip": ["4", 1]
66 | },
67 | "class_type": "LoraLoader"
68 | },
69 | "12": {
70 | "inputs": {
71 | "sampling": "lcm",
72 | "zsnr": false,
73 | "model": ["11", 0]
74 | },
75 | "class_type": "ModelSamplingDiscrete"
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/src/customcomponents/MTextArea.tsx:
--------------------------------------------------------------------------------
1 | import React, {useEffect, useCallback, useState} from 'react';
2 |
3 | import {Textarea} from '../components';
4 | import {debounce} from 'lodash';
5 | import {calculateNested} from '../utils/StringUtils';
6 | type Props = {
7 | value?: string;
8 | type?: string;
9 | className?: string;
10 | placeholder?: string;
11 | onChange?: (e: string) => void;
12 | };
13 |
14 | export default function MTextArea(props: Props) {
15 | const [lineCount, setLineCount] = useState(5);
16 |
17 | const def = 16;
18 |
19 | const filterNegative = ['negative', 'Negative', 'neg', 'Neg', 'negative prompt'];
20 | return (
21 |
22 | {/*
23 |
29 | {props?.type || 'what is this?'}
30 |
31 |
*/}
32 |
51 | );
52 | }
53 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # **COMFYSHOP**
2 |
3 | photoshop plugin to work with ComfyUI Backend. first of all, u need to create a working directory somewhere and then store all files inside `./templates` folder into that folder. Note that, you must have to Symlink ComfyUI folder inside this folder and rename it `ComfyUI`
4 |
5 | once u open the Photoshop, it will ask you that folder (onetime only)
6 |
7 | there are 2 Panel you can work with
8 |
9 | Main Panel are when u can load any worklows that store inside `[workingdirectory]/workflows`
10 |
11 | u need to edit the workflow file and add some stuff in you want to show it on the GUI
12 |
13 | ```
14 | "337": {
15 | "inputs": {
16 | "image": "BaseImage_pk7aw.png",
17 | "choose file to upload": "image"
18 | },
19 | "show": true,
20 | "title": "BaseImage",
21 | "class_type": "LoadImage"
22 | },
23 | ```
24 |
25 | i add more value to every card
26 |
27 | > "show" => true if you want to show on the plugin GUI
28 |
29 | > "title" => for the GUI title
30 |
31 | u can install this simple addon inside `extras `folder to make it easier on ComfyUI frontend.
32 |
33 | copy `comfyshop-helper` folder into `custome_nodes `folder
34 |
35 | u can right click a node on comfyui frontend and choose show on comfyshop. it will add a ✅ emoji on it's title. once its done, u can press `Copy API to Clipboard` button on menu
36 |
37 |
38 | # **QUICKPANEL IMAGE GENERATOR**
39 |
40 | template file could be found in `./templates/image_generator.json` replace this file with your own template. you need to change some value to:
41 |
42 | ```
43 | "[model_name]"
44 | "[seed]"
45 | "[positive_prompt]"
46 | "[negative_prompt]"
47 | "[image_width]"
48 | "[image_height]"
49 | ```
50 |
51 | i.e
52 |
53 | ```
54 | "ckpt_name":"[model_name]"
55 | ```
56 |
57 | change the model in `./templates/config.json` to
58 |
59 | ```
60 | "qp_generator_model": "juggernaut_aftermath.safetensors",
61 | ```
62 |
--------------------------------------------------------------------------------
/src/components/ActionButton.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef } from 'react';
2 |
3 | namespace Spectrum {
4 | export interface ActionButtonEvent extends globalThis.Event {
5 | readonly target: (EventTarget & unknown) | null;
6 | }
7 | }
8 |
9 | type Props = {
10 | children?: React.ReactNode;
11 | onClick?: (e: Spectrum.ActionButtonEvent) => void;
12 | className?: string;
13 | disabled?: boolean;
14 | quiet?: boolean;
15 | size?:string;
16 | title?: string;
17 | };
18 |
19 | declare global {
20 | namespace JSX {
21 | interface IntrinsicElements {
22 | 'sp-action-button': {
23 | children?: React.ReactNode;
24 | ref?: React.RefObject;
25 | class?: string;
26 | disabled?: boolean;
27 | quiet?: boolean;
28 | title?: string;
29 | size?:string;
30 | };
31 | }
32 | }
33 | }
34 |
35 | /**
36 | * Renders an action button.
37 | *
38 | * @example
39 | * ```jsx
40 | *
41 | *
42 | * Zoom
43 | *
44 | * ```
45 | */
46 | export default function ActionButton(props: Props) {
47 | const ref = useRef(null);
48 |
49 | useEffect(() => {
50 | const dispatchClick = (e: Event) =>
51 | props.onClick?.(e as Spectrum.ActionButtonEvent);
52 |
53 | ref.current?.addEventListener('click', dispatchClick);
54 | return () => {
55 | ref.current?.removeEventListener('click', dispatchClick);
56 | };
57 | }, [props.onClick]);
58 |
59 | return (
60 |
68 | {props?.children}
69 |
70 | );
71 | }
72 |
--------------------------------------------------------------------------------
/src/components/MenuItem.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef } from 'react';
2 |
3 | namespace Spectrum {
4 | export interface MenuItemEvent extends globalThis.Event {
5 | readonly target: (EventTarget & { selected: boolean }) | null;
6 | }
7 | }
8 |
9 | type Props = {
10 | children?: React.ReactNode;
11 | onClick?: (e: Spectrum.MenuItemEvent) => void;
12 | className?: string;
13 | size?: string;
14 | disabled?: boolean;
15 | selected?: boolean;
16 | };
17 |
18 | declare global {
19 | namespace JSX {
20 | interface IntrinsicElements {
21 | 'sp-menu-item': {
22 | children?: React.ReactNode;
23 | ref?: React.RefObject;
24 | class?: string;
25 | size?: string;
26 | disabled?: boolean;
27 | selected?: boolean;
28 | };
29 | }
30 | }
31 | }
32 |
33 | /**
34 | * Renders a menu item, with an optional checkmark indicating selection.
35 | *
36 | * @example
37 | * ```jsx
38 | *
39 | * California
40 | * Phoenix
41 | * Portland
42 | *
43 | * ```
44 | */
45 | export default function MenuItem(props: Props) {
46 | const ref = useRef(null);
47 |
48 | useEffect(() => {
49 | const dispatchClick = (e: Event) => props.onClick?.(e as Spectrum.MenuItemEvent);
50 |
51 | ref.current?.addEventListener('click', dispatchClick);
52 | return () => {
53 | ref.current?.removeEventListener('click', dispatchClick);
54 | };
55 | }, [props.onClick]);
56 |
57 | return (
58 |
65 | {props.children}
66 |
67 | );
68 | }
69 |
--------------------------------------------------------------------------------
/src/components/RadioGroup.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef } from 'react';
2 |
3 | namespace Spectrum {
4 | export interface RadioGroupEvent extends globalThis.Event {
5 | readonly target: (EventTarget & { value: string }) | null;
6 | }
7 | }
8 |
9 | type Props = {
10 | children?: React.ReactNode;
11 | onChange?: (e: Spectrum.RadioGroupEvent) => void;
12 | className?: string;
13 | column?: boolean;
14 | value?: string;
15 | };
16 |
17 | declare global {
18 | namespace JSX {
19 | interface IntrinsicElements {
20 | 'sp-radio-group': {
21 | children?: React.ReactNode;
22 | ref?: React.RefObject;
23 | class?: string;
24 | column?: boolean;
25 | value?: string;
26 | };
27 | }
28 | }
29 | }
30 |
31 | /**
32 | * Renders a group of radio buttons horizontally or vertically (column layout), with an optional field {@link Spectrum.Label}.
33 | *
34 | * @example
35 | * ```jsx
36 | *
37 | * Select a product:
38 | * Adobe Photoshop
39 | * Adobe XD
40 | *
41 | * ```
42 | */
43 | export default function RadioGroup(props: Props) {
44 | const ref = useRef(null);
45 |
46 | useEffect(() => {
47 | const dispatchChange = (e: Event) =>
48 | props.onChange?.(e as Spectrum.RadioGroupEvent);
49 |
50 | ref.current?.addEventListener('change', dispatchChange);
51 | return () => {
52 | ref.current?.removeEventListener('change', dispatchChange);
53 | };
54 | }, [props.onChange]);
55 |
56 | return (
57 |
63 | {props?.children}
64 |
65 | );
66 | }
67 |
--------------------------------------------------------------------------------
/src/components/Radio.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef } from 'react';
2 |
3 | namespace Spectrum {
4 | export interface RadioEvent extends globalThis.Event {
5 | readonly target: (EventTarget & { value: string }) | null;
6 | }
7 | }
8 |
9 | type Props = {
10 | children?: React.ReactNode;
11 | onClick?: (e: Spectrum.RadioEvent) => void;
12 | className?: string;
13 | checked?: boolean;
14 | disabled?: boolean;
15 | emphasized?: boolean;
16 | invalid?: boolean;
17 | value?: string;
18 | };
19 |
20 | declare global {
21 | namespace JSX {
22 | interface IntrinsicElements {
23 | 'sp-radio': {
24 | children?: React.ReactNode;
25 | ref?: React.RefObject;
26 | class?: string;
27 | checked?: boolean;
28 | disabled?: boolean;
29 | emphasized?: boolean;
30 | invalid?: boolean;
31 | value?: string;
32 | };
33 | }
34 | }
35 | }
36 |
37 | /**
38 | * Renders a radio button with associated label.
39 | *
40 | * @example
41 | * ```jsx
42 | * Adobe Photoshop
43 | * ```
44 | */
45 | export default function Radio(props: Props) {
46 | const ref = useRef(null);
47 |
48 | useEffect(() => {
49 | const dispatchInput = (e: Event) =>
50 | props.onClick?.(e as Spectrum.RadioEvent);
51 |
52 | ref.current?.addEventListener('input', dispatchInput);
53 | return () => {
54 | ref.current?.removeEventListener('input', dispatchInput);
55 | };
56 | }, [props.onClick]);
57 |
58 | return (
59 |
68 | {props?.children}
69 |
70 | );
71 | }
72 |
--------------------------------------------------------------------------------
/src/components/Button.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef } from 'react';
2 |
3 | namespace Spectrum {
4 | export type ButtonVariant = 'cta' | 'primary' | 'secondary' | 'warning' | 'overBackground';
5 | export interface ButtonEvent extends globalThis.Event {
6 | readonly target: (EventTarget & unknown) | null;
7 | }
8 | }
9 |
10 | type Props = {
11 | children?: React.ReactNode;
12 | onClick?: (e: Spectrum.ButtonEvent) => void;
13 | className?: string;
14 | disabled?: boolean;
15 | quiet?: boolean;
16 | variant?: Spectrum.ButtonVariant;
17 | };
18 |
19 | declare global {
20 | namespace JSX {
21 | interface IntrinsicElements {
22 | 'sp-button': {
23 | children?: React.ReactNode;
24 | ref?: React.RefObject;
25 | class?: string;
26 | disabled?: boolean;
27 | quiet?: boolean;
28 | size: string;
29 | variant?: Spectrum.ButtonVariant;
30 | };
31 | }
32 | }
33 | }
34 |
35 | /**
36 | * Renders a button.
37 | *
38 | * @example
39 | * ```jsx
40 | * Click
41 | * ```
42 | */
43 | export default function Button(props: Props) {
44 | const ref = useRef(null);
45 |
46 | useEffect(() => {
47 | const dispatchClick = (e: Event) => props.onClick?.(e as Spectrum.ButtonEvent);
48 |
49 | ref.current?.addEventListener('click', dispatchClick);
50 | return () => {
51 | ref.current?.removeEventListener('click', dispatchClick);
52 | };
53 | }, [props.onClick]);
54 |
55 | const variant = props.variant || (props.quiet === true ? 'primary' : 'cta');
56 |
57 | return (
58 |
66 | {props.children}
67 |
68 | );
69 | }
70 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hello.dcsms.psdtools.revamped",
3 | "version": "0.0.1",
4 | "scripts": {
5 | "ts:watch": "nodemon -w src -e js,jsx,ts,tsx,json,css,html -w webpack.config.js -x pnpm run build",
6 | "build": "webpack --mode development",
7 | "release": "webpack && uxp plugin package --manifest ./dist/manifest.json --outputPath ./build"
8 | },
9 | "author": "jmkl",
10 | "license": "MIT",
11 | "devDependencies": {
12 | "@babel/core": "^7.22.17",
13 | "@babel/plugin-proposal-object-rest-spread": "^7.20.7",
14 | "@babel/plugin-syntax-class-properties": "^7.12.13",
15 | "@babel/plugin-transform-react-jsx": "^7.22.15",
16 | "@babel/preset-env": "^7.22.15",
17 | "@babel/preset-react": "^7.22.15",
18 | "@babel/preset-typescript": "^7.22.15",
19 | "@types/node": "^20.6.1",
20 | "@types/photoshop": "^25.0.0",
21 | "@types/react": "^18.2.21",
22 | "@types/react-dom": "^18.2.7",
23 | "@typescript-eslint/eslint-plugin": "^6.7.0",
24 | "@typescript-eslint/parser": "^6.7.0",
25 | "babel-loader": "^9.1.3",
26 | "babel-plugin-transform-react-remove-prop-types": "^0.4.24",
27 | "clean-webpack-plugin": "^4.0.0",
28 | "copy-webpack-plugin": "^11.0.0",
29 | "css-loader": "^6.8.1",
30 | "file-loader": "^6.2.0",
31 | "nodemon": "^3.0.1",
32 | "postcss": "^8.4.29",
33 | "postcss-loader": "^7.3.3",
34 | "postcss-preset-env": "^9.1.3",
35 | "style-loader": "^3.3.3",
36 | "tailwindcss": "^3.3.3",
37 | "typescript": "^5.2.2",
38 | "webpack": "^5.88.2",
39 | "webpack-cli": "^5.1.4"
40 | },
41 | "dependencies": {
42 | "@preact/signals-react": "^1.3.6",
43 | "lodash": "^4.17.21",
44 | "random": "^4.1.0",
45 | "react": "^18.2.0",
46 | "react-dom": "^18.2.0",
47 | "react-onclickoutside": "^6.13.0",
48 | "react-paginate": "^8.2.0",
49 | "react-use-websocket": "^4.4.0",
50 | "sval": "^0.4.8",
51 | "ts-md5": "^1.3.1",
52 | "uuid": "^9.0.1"
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/customcomponents/SeedWidgetv2.tsx:
--------------------------------------------------------------------------------
1 | import React, {forwardRef, useEffect, useImperativeHandle, useState} from 'react';
2 | import {ActionButton, Label, Textfield} from '../components';
3 | import DropDrownPicker from './DropDownPicker';
4 | type Props = {
5 | title?: string;
6 | value?: string;
7 | onChange?: (e: number) => void;
8 | };
9 | type MODE = 'random' | 'fixed' | 'increment';
10 | export const SeedWidgetv2 = (props: Props) => {
11 | function generateSeed() {
12 | const value = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
13 | setVal((val) => value.toString());
14 | }
15 |
16 | function updateSeed() {
17 | switch (mode) {
18 | case 'fixed':
19 | break;
20 | case 'increment':
21 | setVal((val) => (parseInt(val) + 1).toString());
22 | break;
23 | case 'random':
24 | generateSeed();
25 | break;
26 | }
27 |
28 | //
29 | }
30 | const [mode, setMode] = useState('random');
31 | const [val, setVal] = useState('0');
32 |
33 | useEffect(() => {
34 | if (val) {
35 | props?.onChange(Number(val));
36 | }
37 | }, [val]);
38 |
39 | useEffect(() => {
40 | setVal((val) => props?.value);
41 | }, [props?.value]);
42 |
43 | return (
44 |
45 |
48 |
49 |
50 |
{
55 | setMode(e.target.value as MODE);
56 | }}
57 | />
58 | {
63 | props?.onChange(Number(val));
64 | }}
65 | />
66 |
67 | {'>'}
68 |
69 |
70 |
71 | );
72 | };
73 |
--------------------------------------------------------------------------------
/src/components/Icon.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | namespace Spectrum {
4 | export type IconName =
5 | | 'ui:AlertMedium'
6 | | 'ui:AlertSmall'
7 | | 'ui:ArrowDownSmall'
8 | | 'ui:ArrowLeftMedium'
9 | | 'ui:ArrowUpSmall'
10 | | 'ui:Asterisk'
11 | | 'ui:CheckmarkMedium'
12 | | 'ui:CheckmarkSmall'
13 | | 'ui:ChevronDownMedium'
14 | | 'ui:ChevronDownSmall'
15 | | 'ui:ChevronLeftLarge'
16 | | 'ui:ChevronLeftMedium'
17 | | 'ui:ChevronRightLarge'
18 | | 'ui:ChevronRightMedium'
19 | | 'ui:ChevronRightSmall'
20 | | 'ui:ChevronUpSmall'
21 | | 'ui:CornerTriangle'
22 | | 'ui:CrossLarge'
23 | | 'ui:CrossMedium'
24 | | 'ui:CrossSmall'
25 | | 'ui:DashSmall'
26 | | 'ui:DoubleGripper'
27 | | 'ui:FolderBreadcrumb'
28 | | 'ui:HelpMedium'
29 | | 'ui:HelpSmall'
30 | | 'ui:InfoMedium'
31 | | 'ui:InfoSmall'
32 | | 'ui:Magnifier'
33 | | 'ui:More'
34 | | 'ui:SkipLeft'
35 | | 'ui:SkipRight'
36 | | 'ui:Star'
37 | | 'ui:StarOutline'
38 | | 'ui:SuccessMedium'
39 | | 'ui:SuccessSmall'
40 | | 'ui:TripleGripper';
41 |
42 | export type IconSize = 'xxs' | 'xs' | 's' | 'm' | 'l' | 'xl' | 'xxl';
43 |
44 | export type IconSlot = 'icon';
45 | }
46 |
47 | type Props = {
48 | className?: string;
49 | name?: Spectrum.IconName;
50 | size?: Spectrum.IconSize;
51 | slot?: Spectrum.IconSlot;
52 | };
53 |
54 | declare global {
55 | namespace JSX {
56 | interface IntrinsicElements {
57 | 'sp-icon': {
58 | children: undefined;
59 | class?: string;
60 | name?: Spectrum.IconName;
61 | size?: Spectrum.IconSize;
62 | slot?: Spectrum.IconSlot;
63 | };
64 | }
65 | }
66 | }
67 |
68 | /**
69 | * Display a named icon.
70 | *
71 | * @example
72 | * ```jsx
73 | *
74 | * ```
75 | */
76 | export default function Icon(props: Props) {
77 | return (
78 |
84 | {undefined}
85 |
86 | );
87 | }
88 |
--------------------------------------------------------------------------------
/src/customcomponents/SeedWidget.tsx:
--------------------------------------------------------------------------------
1 | import React, {forwardRef, useEffect, useImperativeHandle, useState} from 'react';
2 | import {ActionButton, Label, Textfield} from '../components';
3 | import DropDrownPicker from './DropDownPicker';
4 | type Props = {
5 | title?: string;
6 | value?: string;
7 | onChange?: (e: number) => void;
8 | };
9 | type MODE = 'random' | 'fixed' | 'increment';
10 | export const SeedWidget = forwardRef((props: Props, ref) => {
11 | function generateSeed() {
12 | const value = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
13 | setVal((val) => value.toString());
14 | }
15 |
16 | function updateSeed() {
17 | switch (mode) {
18 | case 'fixed':
19 | break;
20 | case 'increment':
21 | setVal((val) => (parseInt(val) + 1).toString());
22 | break;
23 | case 'random':
24 | generateSeed();
25 | break;
26 | }
27 |
28 | //
29 | }
30 | const [mode, setMode] = useState('random');
31 | const [val, setVal] = useState('0');
32 |
33 | useEffect(() => {
34 | if (val) {
35 | props?.onChange(Number(val));
36 |
37 | }
38 | }, [val]);
39 |
40 | useEffect(() => {
41 | setVal((val) => props?.value);
42 | }, [props?.value]);
43 |
44 | useImperativeHandle(ref, () => ({
45 | updateSeed,
46 | }));
47 | return (
48 |
49 |
52 |
53 |
54 |
{
59 | setMode(e.target.value as MODE);
60 | }}
61 | />
62 | {
67 | props?.onChange(Number(val));
68 | }}
69 | />
70 |
71 | {'>'}
72 |
73 |
74 |
75 | );
76 | });
77 |
--------------------------------------------------------------------------------
/src/components/Menu.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef } from 'react';
2 |
3 | namespace Spectrum {
4 | export type MenuSlot = 'options';
5 | export interface MenuEvent extends globalThis.Event {
6 | readonly target: (EventTarget & { selectedIndex: number }) | null;
7 | }
8 | }
9 |
10 | type Props = {
11 | children?: React.ReactNode;
12 | onChange?: (e: Spectrum.MenuEvent) => void;
13 | className?: string;
14 | slot?: Spectrum.MenuSlot;
15 | size?: string;
16 | selectedIndex?: number;
17 | };
18 |
19 | declare global {
20 | namespace JSX {
21 | interface IntrinsicElements {
22 | 'sp-menu': {
23 | size?: string;
24 | children?: React.ReactNode;
25 | ref?: React.RefObject;
26 | class?: string;
27 | slot?: Spectrum.MenuSlot;
28 | selectedIndex?: number;
29 | };
30 | }
31 | }
32 | }
33 |
34 | /**
35 | * Renders a menu with menu items. Inside the {@link Spectrum.Menu}, a series of
36 | * {@link Spectrum.MenuItem} or {@link Spectrum.MenuDivider} elements may exist.
37 | *
38 | * @remarks {@link Spectrum.MenuDivider} elements will only render inside an {@link Spectrum.Dropdown}.
39 | *
40 | * @example
41 | * ```jsx
42 | *
43 | * Deselect
44 | * Select inverse
45 | *
46 | * Make work path
47 | *
48 | * ```
49 | */
50 | export default function Menu(props: Props) {
51 | const ref = useRef(null);
52 |
53 | useEffect(() => {
54 | const dispatchChange = (e: Event) => props.onChange?.(e as Spectrum.MenuEvent);
55 |
56 | ref.current?.addEventListener('change', dispatchChange);
57 | return () => {
58 | ref.current?.removeEventListener('change', dispatchChange);
59 | };
60 | }, [props.onChange]);
61 |
62 | return (
63 |
70 | {props.children}
71 |
72 | );
73 | }
74 |
--------------------------------------------------------------------------------
/src/components/Checkbox.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useRef } from 'react';
2 |
3 | namespace Spectrum {
4 | export interface CheckboxEvent extends globalThis.Event {
5 | readonly target: (EventTarget & { checked: boolean; value: string }) | null;
6 | }
7 | }
8 |
9 | type Props = {
10 | children?: React.ReactNode;
11 | onChange?: (e: Spectrum.CheckboxEvent) => void;
12 | onInput?: (e: Spectrum.CheckboxEvent) => void;
13 | className?: string;
14 | checked?: boolean;
15 | disabled?: boolean;
16 | indeterminate?: boolean;
17 | invalid?: boolean;
18 | };
19 |
20 | declare global {
21 | namespace JSX {
22 | interface IntrinsicElements {
23 | 'sp-checkbox': {
24 | children?: React.ReactNode;
25 | ref?: React.RefObject;
26 | class?: string;
27 | size?: string;
28 | checked?: boolean;
29 | disabled?: boolean;
30 | indeterminate?: boolean;
31 | invalid?: boolean;
32 | };
33 | }
34 | }
35 | }
36 |
37 | /**
38 | * Renders a checkbox with associated label.
39 | *
40 | * @example
41 | * ```jsx
42 | * Checked
43 | * ```
44 | */
45 | export default function Checkbox(props: Props) {
46 | const ref = useRef(null);
47 |
48 | useEffect(() => {
49 | const dispatchChange = (e: Event) => props.onChange?.(e as Spectrum.CheckboxEvent);
50 |
51 | ref.current?.addEventListener('change', dispatchChange);
52 | return () => {
53 | ref.current?.removeEventListener('change', dispatchChange);
54 | };
55 | }, [props.onChange]);
56 |
57 | useEffect(() => {
58 | const dispatchInput = (e: Event) => props.onInput?.(e as Spectrum.CheckboxEvent);
59 |
60 | ref.current?.addEventListener('input', dispatchInput);
61 | return () => {
62 | ref.current?.removeEventListener('input', dispatchInput);
63 | };
64 | }, [props.onInput]);
65 |
66 | return (
67 |
76 | {props?.children}
77 |
78 | );
79 | }
80 |
--------------------------------------------------------------------------------
/src/controllers/PanelController.ts:
--------------------------------------------------------------------------------
1 | import {createRoot} from 'react-dom/client';
2 |
3 | const _id = Symbol('_id');
4 | const _root = Symbol('_root');
5 | const _attachment = Symbol('_attachment');
6 | const _Component = Symbol('_Component');
7 | const _menuItems = Symbol('_menuItems');
8 | export type ControllerComponent = (...args: any) => JSX.Element;
9 | export interface ControllerProps {
10 | id: string;
11 | title?: string;
12 | size?: {width: number; height: number};
13 | menuItems?: MenuItems[];
14 | }
15 | export interface MenuItems {
16 | id: string;
17 | label: string;
18 | enabled: boolean;
19 | checked: boolean;
20 | oninvoke(): RT;
21 | }
22 | export type RT = void | Promise;
23 | export class PanelController {
24 | constructor(Component: React.FC | ControllerComponent, {id, menuItems}: ControllerProps) {
25 | this[_id] = null;
26 | this[_root] = null;
27 | this[_attachment] = null;
28 | this[_Component] = null;
29 | this[_menuItems] = [];
30 |
31 | this[_Component] = Component;
32 | this[_id] = id;
33 | this[_menuItems] = menuItems || [];
34 | // @ts-expect-error
35 | this.menuItems = this[_menuItems].map((menuItem) => ({
36 | id: menuItem.id,
37 | label: menuItem.label,
38 | enabled: menuItem.enabled || true,
39 | checked: menuItem.checked || false,
40 | }));
41 |
42 | ['create', 'show', 'hide', 'destroy', 'invokeMenu'].forEach((fn) => (this[fn] = this[fn].bind(this)));
43 | }
44 |
45 | create() {
46 | this[_root] = document.createElement('div');
47 | this[_root].style.height = '100vh';
48 | this[_root].style.minHeight = '100%';
49 | // this[_root].style.overflowY = 'scroll';
50 | this[_root].style.padding = '8px';
51 |
52 | const root = createRoot(this[_root]);
53 | root.render(this[_Component]({panel: this}));
54 |
55 | return this[_root];
56 | }
57 |
58 | show(event) {
59 | if (!this[_root]) this.create();
60 | this[_attachment] = event;
61 | this[_attachment].appendChild(this[_root]);
62 | }
63 |
64 | hide() {
65 | if (this[_attachment] && this[_root]) {
66 | this[_attachment].removeChild(this[_root]);
67 | this[_attachment] = null;
68 | }
69 | }
70 |
71 | destroy() {}
72 |
73 | invokeMenu(id) {
74 | const menuItem = this[_menuItems].find((c) => c.id === id);
75 | if (menuItem) {
76 | const handler = menuItem.oninvoke;
77 | if (handler) {
78 | handler();
79 | }
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const CopyPlugin = require('copy-webpack-plugin');
3 | const version = '0.0.1';
4 | //const TerserPlugin = require("terser-webpack-plugin");
5 | module.exports = (_env, arg) => {
6 |
7 | return {
8 |
9 | // optimization: {
10 | // minimize: true,
11 | // minimizer: [new TerserPlugin({
12 | // extractComments: true,
13 | // parallel: true,
14 | // terserOptions: {
15 | // mangle: false,
16 | // compress: {
17 | // conditionals: false,
18 | // drop_console: true,
19 | // drop_debugger: true,
20 | // comparisons: false,
21 | // collapse_vars: false,
22 | // booleans: false,
23 | // inline: false,
24 | // keep_classnames: false
25 | // }
26 | // }
27 | // })]
28 | // },
29 | entry: './src/index.tsx',
30 | output: {
31 | path: path.resolve(__dirname, 'dist'),
32 | filename: 'index.js',
33 | //libraryTarget: "commonjs2"
34 | },
35 | devtool: 'eval',
36 | externals: {
37 | uxp: 'commonjs2 uxp',
38 | photoshop: 'commonjs2 photoshop',
39 | os: 'commonjs2 os',
40 | fs: 'commonjs2 fs',
41 | },
42 | resolve: {
43 | extensions: ['.ts', '.tsx', '.js', 'jsx', '.css'],
44 | },
45 | module: {
46 | rules: [
47 | {
48 | test: /\.(jsx?|tsx?)$/,
49 | resolve: {
50 | extensions: ['.js', 'jsx', '.ts', '.tsx'],
51 | },
52 |
53 | exclude: /(node_modules)/,
54 | use: [
55 | {
56 | loader: 'babel-loader',
57 | options: {
58 | plugins: ['@babel/transform-react-jsx', '@babel/proposal-object-rest-spread', '@babel/plugin-syntax-class-properties'],
59 | },
60 | },
61 | ],
62 | },
63 | {
64 | test: /\.(png|jpe?g|gif|jp2|webp)$/,
65 | type: 'asset/resource',
66 | },
67 | {
68 | test: /\.css$/,
69 | use: ['style-loader', 'css-loader', 'postcss-loader'],
70 | },
71 | ],
72 | },
73 |
74 |
75 | plugins: [
76 | new CopyPlugin({
77 | patterns: [
78 | { from: 'plugin' },
79 | {
80 | from: 'plugin/manifest.json',
81 | transform(content, absoluteFrom) {
82 | return content.toString().replace(/\"version\".+?\"[0-9].*\"/, `"version": "${version}"`);
83 | },
84 | },
85 | ],
86 | }),
87 | ],
88 | };
89 | };
90 |
--------------------------------------------------------------------------------
/src/utils/ServerUtils.ts:
--------------------------------------------------------------------------------
1 | export async function fetchObjectInfo(object_info: any) {
2 | const res = await fetch('http://127.0.0.1:8188/object_info/' + object_info);
3 | const result = await res.json();
4 | return result;
5 | }
6 | export function random_seed() {
7 | // Generate a random integer within a suitable range
8 | const min = 0;
9 | const max = 2147483647; // Maximum value for Torch seed
10 | const randomSeed = Math.floor(Math.random() * (max - min + 1)) + min;
11 |
12 | return randomSeed;
13 | }
14 |
15 | export function sendWorkflowDataToServer(workflow_data: any, uuid: string) {
16 | const data_to_send = JSON.stringify(
17 | {
18 | prompt: workflow_data,
19 | client_id: uuid,
20 | },
21 | null,
22 | 2
23 | );
24 |
25 | fetch('http://127.0.0.1:8188/prompt', {
26 | method: 'POST',
27 | body: data_to_send,
28 | headers: {
29 | 'Content-type': 'application/json; charset=UTF-8',
30 | },
31 | })
32 | .then((response) => {
33 | if (response.status != 200) {
34 | console.error('something is wrong', response.status);
35 | }
36 | })
37 | .catch((e) => console.log(e));
38 | }
39 |
40 | export function InterruptServer() {
41 | fetch('http://127.0.0.1:8188/interrupt', {
42 | method: 'POST',
43 | })
44 | .then((response) => {
45 | if (response.status != 200) {
46 | console.error('something is wrong', response.status);
47 | }
48 | })
49 | .catch((e) => console.log(e));
50 | }
51 |
52 | export const server_type = Object.freeze({
53 | status: 'status',
54 | execution_start: 'execution_start',
55 | execution_cached: 'execution_cached',
56 | executing: 'executing',
57 | progress: 'progress',
58 | executed: 'executed',
59 | });
60 | export async function _arrayBufferToBase64(buffer: ArrayBuffer) {
61 | var binary = '';
62 | var bytes = new Uint8Array(buffer);
63 | var len = bytes.byteLength;
64 | for (var i = 0; i < len; i++) {
65 | binary += String.fromCharCode(bytes[i]);
66 | }
67 | return window.btoa(binary);
68 | }
69 | export interface progress {
70 | type: 'progress';
71 | data: {value: number; max: number};
72 | }
73 | export interface status {
74 | type: 'status';
75 | data: {status: {exec_info: {queue_remaining: number}; sid: string}};
76 | }
77 | export interface execution_start {
78 | type: 'execution_start';
79 | data: {prompt_id: string};
80 | }
81 | export interface execution_cached {
82 | type: 'execution_cached';
83 | data: any;
84 | }
85 | export interface executing {
86 | type: 'executing';
87 | data: {node: string; prompt_id: string};
88 | }
89 | export interface progress {
90 | type: 'progress';
91 | data: {value: number; max: number};
92 | }
93 |
94 | export interface executed {
95 | type: 'executed';
96 | data: {node: string; output: {images: output_images[]}; prompt_id: string};
97 | }
98 | export interface output_images {
99 | filename: string;
100 | subfolder: string;
101 | type: string;
102 | }
103 |
--------------------------------------------------------------------------------
/src/components/Dropdown.tsx:
--------------------------------------------------------------------------------
1 | import React, {useEffect, useRef} from 'react';
2 |
3 | namespace Spectrum {
4 | export interface DropdownEvent extends globalThis.Event {
5 | readonly target: (EventTarget & {selectedIndex: number}) | null;
6 | }
7 | }
8 |
9 | type Props = {
10 | children?: React.ReactNode;
11 | onChange?: (e: Spectrum.DropdownEvent) => void;
12 | className?: string;
13 | disabled?: boolean;
14 | size?: string;
15 | invalid?: boolean;
16 | value?: string;
17 | quiet?: boolean;
18 | placeholder?: string;
19 | selectedIndex?: number;
20 | onSelectIndex?: (selectedIndex: number) => void;
21 | };
22 |
23 | declare global {
24 | namespace JSX {
25 | interface IntrinsicElements {
26 | 'sp-picker': {
27 | size?: string;
28 | children?: React.ReactNode;
29 | ref?: React.RefObject;
30 | class?: string;
31 | value?: string;
32 | disabled?: boolean;
33 | invalid?: boolean;
34 | quiet?: boolean;
35 | placeholder?: string;
36 | selectedIndex?: number;
37 | };
38 | }
39 | }
40 | }
41 |
42 | /**
43 | * Renders a dropdown with menu items. The dropdown must contain a {@link Spectrum.Menu}
44 | * with `slot="options"`, and inside the {@link Spectrum.Menu}, a series of {@link Spectrum.MenuItem}
45 | * or {@link Spectrum.MenuDivider} elements.
46 | *
47 | * @example
48 | * ```jsx
49 | *
50 | *
51 | * Deselect
52 | *
53 | * Make work path
54 | *
55 | *
56 | * ```
57 | */
58 | export default function Dropdown(props: Props) {
59 | const ref = useRef(null);
60 |
61 | useEffect(() => {
62 | const dispatchChange = (e: Event) => {
63 | const selectedIndex = (e.target as any).selectedIndex;
64 | props.onSelectIndex?.(selectedIndex);
65 | props.onChange?.(e as Spectrum.DropdownEvent);
66 | };
67 |
68 | ref.current?.addEventListener('change', dispatchChange);
69 |
70 | return () => {
71 | ref.current?.removeEventListener('change', dispatchChange);
72 | };
73 | }, [props.onChange, props.onSelectIndex]);
74 |
75 | useEffect(() => {
76 | if (props.selectedIndex !== undefined) {
77 | if (ref.current) {
78 | // Set the selected index by changing the "selectedIndex" property of your custom element
79 | (ref.current as any).selectedIndex = props.selectedIndex;
80 | }
81 | }
82 | }, [props.selectedIndex]);
83 | return (
84 |
95 | {props?.children}
96 |
97 | );
98 | }
99 |
--------------------------------------------------------------------------------
/src/customcomponents/DropDownPicker.tsx:
--------------------------------------------------------------------------------
1 | import React, {useEffect, useRef, useState} from 'react';
2 | import {Dropdown, Label} from '../components';
3 | import Menu from '../components/Menu';
4 | import MenuItem from '../components/MenuItem';
5 | import {HeroIcons} from '../interfaces/HeroIcons';
6 | export interface DropdownEvent extends globalThis.Event {
7 | readonly target: (EventTarget & {selectedIndex: number; value: string; className: string}) | null;
8 | }
9 | type Props = {
10 | onChange?: (e: DropdownEvent) => void;
11 | items?: string[];
12 | title?: string;
13 | disabled?: boolean;
14 | selectedIndex: number;
15 | DDWidth?: string;
16 | overrideClass?: string;
17 | which?: 'facerestoremodel' | 'upscalemodel' | 'method';
18 | horizontalmode?: boolean;
19 | subnode?: boolean;
20 | showSelector?: boolean;
21 | };
22 |
23 | export default function DropDrownPicker(props: Props) {
24 | const [hastitle, sethastitle] = useState(false);
25 | const [selIndex, setSelIndex] = useState(0);
26 | useEffect(() => {
27 | if (props.title != null) sethastitle(true);
28 | }, [props.title]);
29 | useEffect(() => {
30 | setSelIndex(props?.selectedIndex);
31 | }, [props?.selectedIndex]);
32 | function handleSelectIndex(isLeft: boolean) {
33 | const itemsLen = props?.items?.length;
34 | let newSelIndex = selIndex;
35 |
36 | if (isLeft) {
37 | newSelIndex = selIndex > 1 ? selIndex - 1 : 0;
38 | } else {
39 | newSelIndex = itemsLen - 1 > newSelIndex ? newSelIndex + 1 : itemsLen - 1;
40 | }
41 |
42 | // Update the selected index in the component's state
43 | setSelIndex((e) => newSelIndex);
44 |
45 | if (props.onChange) {
46 | props?.onChange;
47 | }
48 | }
49 | return (
50 |
51 | {hastitle && (
52 |
55 | )}
56 |
57 |
65 |
75 |
76 | {props?.showSelector && (
77 | <>
78 | handleSelectIndex(true)} />
79 | handleSelectIndex(false)} />
80 | >
81 | )}
82 |
83 |
84 | );
85 | }
86 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 |
3 | @tailwind components;
4 | @tailwind utilities;
5 |
6 | .content-card {
7 | max-height: 70vh;
8 | }
9 | .main {
10 | height: 80vh;
11 | }
12 | .content-cardv2 {
13 | height: 100%;
14 | overflow-y: auto;
15 | }
16 | .dropdown-content {
17 | display: flex;
18 | flex-direction: row;
19 | flex-wrap: wrap;
20 | width: 100%;
21 | max-height: 300px;
22 | overflow-y: auto;
23 | justify-content: space-between;
24 | }
25 | .imgtool-icon {
26 | width: 15px;
27 | height: 15px;
28 | }
29 | .chevron {
30 | background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA1klEQVR4nO2VsQ3CMBQFvQJFGnaizArsxAq07ESRghUORXFhRUTYP992Ir3rE/67E0oIQgghhBBCCLECeAHX0BlgAJ6WB2c+wL3KZXk3jMA0H2J5OKVpDaL19ADLS9Y0qUFi3XtA1Rr8sF5rgHsNNqzXHOBSgz/WWwww18ixvnfADXiTT1aNEuuReeRYPCD+2AV44FSj1DrL0MF0vGeNpta3sB7RzbpjjaLBoQWG/0Zf6xVrTM2sV6jRx7pDjf7Wd9Q4lvWCGse1nvHdOId1IYQQQohwPr4hXgtdC/n/GAAAAABJRU5ErkJggg==');
31 | }
32 | .reset-padding {
33 | margin-left: -8px;
34 | }
35 | .disabled {
36 | pointer-events: none;
37 | }
38 | .max-w-50 {
39 | max-width: 50%;
40 | text-overflow: ellipsis;
41 | white-space: nowrap;
42 | overflow: hidden;
43 | }
44 | .btn-text {
45 | cursor: pointer;
46 | font-family: 'Anton';
47 | font-weight: 400;
48 | font-size: 14px;
49 | color: #ffffff;
50 | background: #e62e00;
51 | }
52 | .enabled:hover {
53 | background: #ff8800;
54 | }
55 | .enabled:active {
56 | background: rgb(255, 208, 0);
57 | color: #323232;
58 | }
59 | .disabled {
60 | cursor: none;
61 | pointer-events: none;
62 | color: #313131;
63 | color: #727272;
64 | }
65 |
66 | .tab-main {
67 | font-family: 'Anton';
68 | font-weight: 400;
69 | font-size: 12px;
70 | }
71 | .acc-title-comfy {
72 | font-family: 'Montserrat';
73 | font-weight: 600;
74 | font-size: 6px;
75 | z-index: 2;
76 | }
77 | .acc-title {
78 | font-family: 'Montserrat';
79 | font-weight: 800;
80 | font-size: 10px;
81 | }
82 | .bg {
83 | color: #323232;
84 | background-color: #fff;
85 | }
86 | .bg-box-light {
87 | background-color: #272727;
88 | border-radius: 5px;
89 | }
90 | .box-bg {
91 | background-color: #1e1e1e;
92 | border-radius: 5px;
93 | border: solid 1px #444444;
94 | }
95 | .box-child {
96 | background-color: #141414;
97 | border-radius: 5px;
98 | }
99 | .height80vh {
100 | height: 80vh;
101 | }
102 | .h80vh {
103 | max-height: 80vh;
104 | }
105 |
106 | ul.react-paginate {
107 | display: flex;
108 | flex-direction: row;
109 | justify-content: space-between;
110 | list-style-type: none;
111 | width: 100%;
112 | padding: 4px;
113 | }
114 |
115 | ul.react-paginate li a {
116 | border-radius: 1px;
117 | padding: 2px;
118 | color: #fff;
119 | cursor: pointer;
120 | }
121 | ul.react-paginate li.previous a,
122 | ul.react-paginate li.next a,
123 | ul.react-paginate li.break a {
124 | border-color: transparent;
125 | }
126 | ul.react-paginate li.selected a {
127 | background-color: #0366d6;
128 | border-color: transparent;
129 | color: white;
130 | padding: 2px;
131 | }
132 | ul.react-paginate li.disabled a {
133 | color: grey;
134 | }
135 | ul.react-paginate li.disable,
136 | ul.react-paginate li.disabled a {
137 | cursor: default;
138 | }
139 |
--------------------------------------------------------------------------------
/src/customcomponents/DropdownStyleChooser.tsx:
--------------------------------------------------------------------------------
1 | import React, {useEffect, useRef, useState} from 'react';
2 | import {Textfield} from '../components';
3 |
4 | export type Props = {
5 | className?: string;
6 | styleFolder?: any;
7 | onStyleChoosen?: (e: STYLE) => void;
8 | };
9 |
10 | export type STYLE = {
11 | name: string;
12 | prompt: string;
13 | negative_prompt: string;
14 | };
15 |
16 | export const DropdownStyleChooser = (props: Props) => {
17 | const ref = useRef(null);
18 | const dummycontent = ['satu', 'dua', 'tiga', 'empat', 'lima', 'enam'];
19 | const defaultItem = JSON.parse(localStorage.getItem('DEFAULTSTYLE'));
20 | const [visible, setVisible] = useState(false);
21 | const [styleTemplate, setStyleTemplate] = useState([]);
22 | const [filterList, setFilterList] = useState(styleTemplate);
23 | const [currentTemplate, setCurrentTemplate] = useState