├── 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 | 94 | ) : ( 95 | { 100 | setValue(e.target.value); 101 | }} 102 | /> 103 | )} 104 |
105 | )} 106 | {props?.type === 'boolean' && ( 107 | { 110 | setValue(e.target.checked); 111 | }} 112 | /> 113 | )} 114 |
115 |
116 | ); 117 | }; 118 | 119 | function generateSeed() { 120 | const curvalue = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); 121 | setValue(curvalue); 122 | } 123 | function updateSeed() { 124 | 125 | switch (selectedItem) { 126 | case 'fixed': 127 | break; 128 | case 'increment': 129 | setValue((value as number) + 1); 130 | break; 131 | case 'random': 132 | generateSeed(); 133 | break; 134 | } 135 | 136 | // 137 | } 138 | const isSeed = () => { 139 | return ( 140 |
141 |
{}}> 142 | {!visible && ( 143 | <> 144 |

{ 146 | if (!props?.disabled) setVisible(!visible); 147 | }} 148 | className="leading-6 text-gray-600 overflow-ellipsis whitespace-nowrap max-w-50" 149 | > 150 | {props?.title + ' [' + selectedItem + ']'} 151 |

152 |

{ 154 | updateSeed(); 155 | }} 156 | className="leading-6 overflow-ellipsis whitespace-nowrap max-w-50" 157 | > 158 | {value || 'None'} 159 |

160 | 161 | )} 162 | 163 |
164 | {props?.items.map((value, index) => { 165 | return ( 166 |
{ 170 | const selectedIndex = props?.items.findIndex((x) => x == value); 171 | setSelectedItem(props?.items[selectedIndex]); 172 | setVisible(false); 173 | // props?.onItemChoosed({selectedIndex: selectedIndex, value: props?.items[selectedIndex]}); 174 | }} 175 | > 176 | {value} 177 |
178 | ); 179 | })} 180 |
181 |
182 |
183 | ); 184 | }; 185 | const isString = () => { 186 | return ( 187 |
188 |
{ 191 | if (!props?.disabled) setVisible(!visible); 192 | }} 193 | > 194 | {!visible && ( 195 | <> 196 |

{props?.title}

197 |

{value || 'None'}

198 | 199 | )} 200 | 201 |
202 | {props?.multiline ? ( 203 |