mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
feat(ui): ux improvements & redesign
This is a squash merge of a bajillion messy small commits created while iterating on the UI component library and redesign.
This commit is contained in:
parent
a47d91f0e7
commit
f0b102d830
@ -28,12 +28,14 @@ module.exports = {
|
||||
'i18next',
|
||||
'path',
|
||||
'unused-imports',
|
||||
'simple-import-sort',
|
||||
'eslint-plugin-import',
|
||||
],
|
||||
root: true,
|
||||
rules: {
|
||||
'path/no-relative-imports': ['error', { maxDepth: 0 }],
|
||||
curly: 'error',
|
||||
'i18next/no-literal-string': 2,
|
||||
'i18next/no-literal-string': 'warn',
|
||||
'react/jsx-no-bind': ['error', { allowBind: true }],
|
||||
'react/jsx-curly-brace-presence': [
|
||||
'error',
|
||||
@ -43,6 +45,7 @@ module.exports = {
|
||||
'no-var': 'error',
|
||||
'brace-style': 'error',
|
||||
'prefer-template': 'error',
|
||||
'import/no-duplicates': 'error',
|
||||
radix: 'error',
|
||||
'space-before-blocks': 'error',
|
||||
'import/prefer-default-export': 'off',
|
||||
@ -65,7 +68,26 @@ module.exports = {
|
||||
allowSingleExtends: true,
|
||||
},
|
||||
],
|
||||
'@typescript-eslint/consistent-type-imports': [
|
||||
'error',
|
||||
{
|
||||
prefer: 'type-imports',
|
||||
fixStyle: 'separate-type-imports',
|
||||
disallowTypeAnnotations: true,
|
||||
},
|
||||
],
|
||||
'@typescript-eslint/no-import-type-side-effects': 'error',
|
||||
'simple-import-sort/imports': 'error',
|
||||
'simple-import-sort/exports': 'error',
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.stories.tsx'],
|
||||
rules: {
|
||||
'i18next/no-literal-string': 'off',
|
||||
},
|
||||
},
|
||||
],
|
||||
settings: {
|
||||
react: {
|
||||
version: 'detect',
|
||||
|
@ -12,4 +12,5 @@ index.html
|
||||
src/services/api/schema.d.ts
|
||||
static/
|
||||
src/theme/css/overlayscrollbars.css
|
||||
src/theme_/css/overlayscrollbars.css
|
||||
pnpm-lock.yaml
|
||||
|
23
invokeai/frontend/web/.storybook/ReduxInit.tsx
Normal file
23
invokeai/frontend/web/.storybook/ReduxInit.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
import { PropsWithChildren, useEffect } from 'react';
|
||||
import { modelChanged } from '../src/features/parameters/store/generationSlice';
|
||||
import { useAppDispatch } from '../src/app/store/storeHooks';
|
||||
import { useGlobalModifiersInit } from '../src/common/hooks/useGlobalModifiers';
|
||||
/**
|
||||
* Initializes some state for storybook. Must be in a different component
|
||||
* so that it is run inside the redux context.
|
||||
*/
|
||||
export const ReduxInit = (props: PropsWithChildren) => {
|
||||
const dispatch = useAppDispatch();
|
||||
useGlobalModifiersInit();
|
||||
useEffect(() => {
|
||||
dispatch(
|
||||
modelChanged({
|
||||
model_name: 'test_model',
|
||||
base_model: 'sd-1',
|
||||
model_type: 'main',
|
||||
})
|
||||
);
|
||||
}, []);
|
||||
|
||||
return props.children;
|
||||
};
|
@ -6,6 +6,7 @@ const config: StorybookConfig = {
|
||||
'@storybook/addon-links',
|
||||
'@storybook/addon-essentials',
|
||||
'@storybook/addon-interactions',
|
||||
'@storybook/addon-storysource',
|
||||
],
|
||||
framework: {
|
||||
name: '@storybook/react-vite',
|
||||
|
@ -1,16 +1,17 @@
|
||||
import { Preview } from '@storybook/react';
|
||||
import { themes } from '@storybook/theming';
|
||||
import i18n from 'i18next';
|
||||
import React from 'react';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
import { Provider } from 'react-redux';
|
||||
import GlobalHotkeys from '../src/app/components/GlobalHotkeys';
|
||||
import ThemeLocaleProvider from '../src/app/components/ThemeLocaleProvider';
|
||||
import { $baseUrl } from '../src/app/store/nanostores/baseUrl';
|
||||
import { createStore } from '../src/app/store/store';
|
||||
import { Container } from '@chakra-ui/react';
|
||||
// TODO: Disabled for IDE performance issues with our translation JSON
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
import translationEN from '../public/locales/en.json';
|
||||
import { ReduxInit } from './ReduxInit';
|
||||
|
||||
i18n.use(initReactI18next).init({
|
||||
lng: 'en',
|
||||
@ -25,17 +26,21 @@ i18n.use(initReactI18next).init({
|
||||
});
|
||||
|
||||
const store = createStore(undefined, false);
|
||||
$baseUrl.set('http://localhost:9090');
|
||||
|
||||
const preview: Preview = {
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<Provider store={store}>
|
||||
<ThemeLocaleProvider>
|
||||
<GlobalHotkeys />
|
||||
<Story />
|
||||
</ThemeLocaleProvider>
|
||||
</Provider>
|
||||
),
|
||||
(Story) => {
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<ThemeLocaleProvider>
|
||||
<ReduxInit>
|
||||
<Story />
|
||||
</ReduxInit>
|
||||
</ThemeLocaleProvider>
|
||||
</Provider>
|
||||
);
|
||||
},
|
||||
],
|
||||
parameters: {
|
||||
docs: {
|
||||
|
15
invokeai/frontend/web/.unimportedrc.json
Normal file
15
invokeai/frontend/web/.unimportedrc.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"entry": ["src/main.tsx"],
|
||||
"extensions": [".ts", ".tsx"],
|
||||
"ignorePatterns": [
|
||||
"**/node_modules/**",
|
||||
"dist/**",
|
||||
"public/**",
|
||||
"**/*.stories.tsx",
|
||||
"config/**"
|
||||
],
|
||||
"ignoreUnresolved": [],
|
||||
"ignoreUnimported": ["src/i18.d.ts", "vite.config.ts", "src/vite-env.d.ts"],
|
||||
"respectGitignore": true,
|
||||
"ignoreUnused": []
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import react from '@vitejs/plugin-react-swc';
|
||||
import { visualizer } from 'rollup-plugin-visualizer';
|
||||
import { PluginOption, UserConfig } from 'vite';
|
||||
import type { PluginOption, UserConfig } from 'vite';
|
||||
import eslint from 'vite-plugin-eslint';
|
||||
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { UserConfig } from 'vite';
|
||||
import type { UserConfig } from 'vite';
|
||||
|
||||
import { commonPlugins } from './common';
|
||||
|
||||
export const appConfig: UserConfig = {
|
||||
|
@ -1,7 +1,8 @@
|
||||
import path from 'path';
|
||||
import { UserConfig } from 'vite';
|
||||
import dts from 'vite-plugin-dts';
|
||||
import type { UserConfig } from 'vite';
|
||||
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js';
|
||||
import dts from 'vite-plugin-dts';
|
||||
|
||||
import { commonPlugins } from './common';
|
||||
|
||||
export const packageConfig: UserConfig = {
|
||||
|
@ -31,13 +31,17 @@
|
||||
"lint": "concurrently -g -n eslint,prettier,tsc,madge -c cyan,green,magenta,yellow \"pnpm run lint:eslint\" \"pnpm run lint:prettier\" \"pnpm run lint:tsc\" \"pnpm run lint:madge\"",
|
||||
"fix": "eslint --fix . && prettier --log-level warn --write .",
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
"postinstall": "patch-package && pnpm run theme",
|
||||
"postinstall": "pnpm run theme",
|
||||
"theme": "chakra-cli tokens src/theme/theme.ts",
|
||||
"theme:watch": "chakra-cli tokens src/theme/theme.ts --watch",
|
||||
"storybook": "storybook dev -p 6006",
|
||||
"build-storybook": "storybook build"
|
||||
"build-storybook": "storybook build",
|
||||
"unimported": "npx unimported"
|
||||
},
|
||||
"madge": {
|
||||
"excludeRegExp": [
|
||||
"^index.ts$"
|
||||
],
|
||||
"detectiveOptions": {
|
||||
"ts": {
|
||||
"skipTypeImports": true
|
||||
@ -53,6 +57,7 @@
|
||||
"@chakra-ui/layout": "^2.3.1",
|
||||
"@chakra-ui/portal": "^2.1.0",
|
||||
"@chakra-ui/react": "^2.8.2",
|
||||
"@chakra-ui/react-use-size": "^2.1.0",
|
||||
"@chakra-ui/styled-system": "^2.9.2",
|
||||
"@chakra-ui/theme-tools": "^2.1.2",
|
||||
"@dagrejs/graphlib": "^2.1.13",
|
||||
@ -61,14 +66,11 @@
|
||||
"@emotion/react": "^11.11.1",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@fontsource-variable/inter": "^5.0.16",
|
||||
"@mantine/core": "^6.0.19",
|
||||
"@mantine/form": "^6.0.19",
|
||||
"@mantine/hooks": "^6.0.19",
|
||||
"@nanostores/react": "^0.7.1",
|
||||
"@reduxjs/toolkit": "^2.0.1",
|
||||
"@roarr/browser-log-writer": "^1.3.0",
|
||||
"@storybook/manager-api": "^7.6.4",
|
||||
"@storybook/theming": "^7.6.4",
|
||||
"chakra-react-select": "^4.7.6",
|
||||
"compare-versions": "^6.1.0",
|
||||
"dateformat": "^5.0.3",
|
||||
"framer-motion": "^10.16.15",
|
||||
@ -81,7 +83,6 @@
|
||||
"new-github-issue-url": "^1.0.0",
|
||||
"overlayscrollbars": "^2.4.5",
|
||||
"overlayscrollbars-react": "^0.5.3",
|
||||
"patch-package": "^8.0.0",
|
||||
"query-string": "^8.1.0",
|
||||
"react": "^18.2.0",
|
||||
"react-colorful": "^5.6.1",
|
||||
@ -94,6 +95,8 @@
|
||||
"react-konva": "^18.2.10",
|
||||
"react-redux": "^9.0.2",
|
||||
"react-resizable-panels": "^0.0.55",
|
||||
"react-select": "5.7.7",
|
||||
"react-textarea-autosize": "^8.5.3",
|
||||
"react-use": "^17.4.2",
|
||||
"react-virtuoso": "^4.6.2",
|
||||
"reactflow": "^11.10.1",
|
||||
@ -118,13 +121,17 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@chakra-ui/cli": "^2.4.1",
|
||||
"@storybook/addon-docs": "^7.6.4",
|
||||
"@storybook/addon-essentials": "^7.6.4",
|
||||
"@storybook/addon-interactions": "^7.6.4",
|
||||
"@storybook/addon-links": "^7.6.4",
|
||||
"@storybook/addon-storysource": "^7.6.4",
|
||||
"@storybook/blocks": "^7.6.4",
|
||||
"@storybook/manager-api": "^7.6.4",
|
||||
"@storybook/react": "^7.6.4",
|
||||
"@storybook/react-vite": "^7.6.4",
|
||||
"@storybook/test": "^7.6.4",
|
||||
"@storybook/theming": "^7.6.4",
|
||||
"@types/dateformat": "^5.0.2",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/node": "^20.9.0",
|
||||
@ -138,9 +145,11 @@
|
||||
"eslint": "^8.55.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-i18next": "^6.0.3",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-path": "^1.2.2",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-simple-import-sort": "^10.0.0",
|
||||
"eslint-plugin-storybook": "^0.6.15",
|
||||
"eslint-plugin-unused-imports": "^3.0.0",
|
||||
"madge": "^6.1.0",
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -50,9 +50,33 @@
|
||||
"uncategorized": "Uncategorized",
|
||||
"downloadBoard": "Download Board"
|
||||
},
|
||||
"accordions": {
|
||||
"generation": {
|
||||
"title": "Generation",
|
||||
"modelTab": "Model",
|
||||
"conceptsTab": "Concepts"
|
||||
},
|
||||
"image": {
|
||||
"title": "Image"
|
||||
},
|
||||
"advanced": {
|
||||
"title": "Advanced"
|
||||
},
|
||||
"control": {
|
||||
"title": "Control",
|
||||
"controlAdaptersTab": "Control Adapters",
|
||||
"ipTab": "Image Prompts"
|
||||
},
|
||||
"compositing": {
|
||||
"title": "Compositing",
|
||||
"coherenceTab": "Coherence Pass",
|
||||
"infillMaskTab": "Infill & Mask"
|
||||
}
|
||||
},
|
||||
"common": {
|
||||
"accept": "Accept",
|
||||
"advanced": "Advanced",
|
||||
"advancedOptions": "Advanced Options",
|
||||
"ai": "ai",
|
||||
"areYouSure": "Are you sure?",
|
||||
"auto": "Auto",
|
||||
@ -79,6 +103,7 @@
|
||||
"file": "File",
|
||||
"folder": "Folder",
|
||||
"format": "format",
|
||||
"free": "Free",
|
||||
"generate": "Generate",
|
||||
"githubLabel": "Github",
|
||||
"hotkeysLabel": "Hotkeys",
|
||||
@ -221,7 +246,6 @@
|
||||
"colorMapTileSize": "Tile Size",
|
||||
"importImageFromCanvas": "Import Image From Canvas",
|
||||
"importMaskFromCanvas": "Import Mask From Canvas",
|
||||
"incompatibleBaseModel": "Incompatible base model:",
|
||||
"lineart": "Lineart",
|
||||
"lineartAnime": "Lineart Anime",
|
||||
"lineartAnimeDescription": "Anime-style lineart processing",
|
||||
@ -246,6 +270,7 @@
|
||||
"prompt": "Prompt",
|
||||
"resetControlImage": "Reset Control Image",
|
||||
"resize": "Resize",
|
||||
"resizeSimple": "Resize (Simple)",
|
||||
"resizeMode": "Resize Mode",
|
||||
"safe": "Safe",
|
||||
"saveControlImage": "Save Control Image",
|
||||
@ -284,7 +309,7 @@
|
||||
"queue": "Queue",
|
||||
"queueFront": "Add to Front of Queue",
|
||||
"queueBack": "Add to Queue",
|
||||
"queueCountPrediction": "Add {{predicted}} to Queue",
|
||||
"queueCountPrediction": "{{promptsCount}} prompts × {{iterations}} iterations -> {{count}} generations",
|
||||
"queueMaxExceeded": "Max of {{max_queue_size}} exceeded, would skip {{skip}}",
|
||||
"queuedCount": "{{pending}} Pending",
|
||||
"queueTotal": "{{total}} Total",
|
||||
@ -788,17 +813,23 @@
|
||||
},
|
||||
"models": {
|
||||
"addLora": "Add LoRA",
|
||||
"allLoRAsAdded": "All LoRAs added",
|
||||
"loraAlreadyAdded": "LoRA already added",
|
||||
"esrganModel": "ESRGAN Model",
|
||||
"loading": "loading",
|
||||
"incompatibleBaseModel": "Incompatible base model",
|
||||
"noMainModelSelected": "No main model selected",
|
||||
"noLoRAsAvailable": "No LoRAs available",
|
||||
"noLoRAsLoaded": "No LoRAs Loaded",
|
||||
"noMatchingLoRAs": "No matching LoRAs",
|
||||
"noMatchingModels": "No matching Models",
|
||||
"noModelsAvailable": "No models available",
|
||||
"lora": "LoRA",
|
||||
"selectLoRA": "Select a LoRA",
|
||||
"selectModel": "Select a Model",
|
||||
"noLoRAsInstalled": "No LoRAs installed",
|
||||
"noRefinerModelsInstalled": "No SDXL Refiner models installed"
|
||||
"noRefinerModelsInstalled": "No SDXL Refiner models installed",
|
||||
"defaultVAE": "Default VAE"
|
||||
},
|
||||
"nodes": {
|
||||
"addNode": "Add Node",
|
||||
@ -1037,6 +1068,7 @@
|
||||
"prototypeDesc": "This invocation is a prototype. It may have breaking changes during app updates and may be removed at any time."
|
||||
},
|
||||
"parameters": {
|
||||
"aspect": "Aspect",
|
||||
"aspectRatio": "Aspect Ratio",
|
||||
"aspectRatioFree": "Free",
|
||||
"boundingBoxHeader": "Bounding Box",
|
||||
@ -1077,6 +1109,7 @@
|
||||
"imageFit": "Fit Initial Image To Output Size",
|
||||
"images": "Images",
|
||||
"imageToImage": "Image to Image",
|
||||
"imageSize": "Image Size",
|
||||
"img2imgStrength": "Image To Image Strength",
|
||||
"infillMethod": "Infill Method",
|
||||
"infillScalingHeader": "Infill and Scaling",
|
||||
@ -1127,8 +1160,8 @@
|
||||
"seamCorrectionHeader": "Seam Correction",
|
||||
"seamHighThreshold": "High",
|
||||
"seamlessTiling": "Seamless Tiling",
|
||||
"seamlessXAxis": "X Axis",
|
||||
"seamlessYAxis": "Y Axis",
|
||||
"seamlessXAxis": "Seamless Tiling X Axis",
|
||||
"seamlessYAxis": "Seamless Tiling Y Axis",
|
||||
"seamlessX": "Seamless X",
|
||||
"seamlessY": "Seamless Y",
|
||||
"seamlessX&Y": "Seamless X & Y",
|
||||
@ -1171,6 +1204,7 @@
|
||||
},
|
||||
"dynamicPrompts": {
|
||||
"combinatorial": "Combinatorial Generation",
|
||||
"showDynamicPrompts": "Show Dynamic Prompts",
|
||||
"dynamicPrompts": "Dynamic Prompts",
|
||||
"enableDynamicPrompts": "Enable Dynamic Prompts",
|
||||
"maxPrompts": "Max Prompts",
|
||||
@ -1187,7 +1221,8 @@
|
||||
},
|
||||
"sdxl": {
|
||||
"cfgScale": "CFG Scale",
|
||||
"concatPromptStyle": "Concatenate Prompt & Style",
|
||||
"concatPromptStyle": "Concatenating Prompt & Style",
|
||||
"freePromptStyle": "Manual Style Prompting",
|
||||
"denoisingStrength": "Denoising Strength",
|
||||
"loading": "Loading...",
|
||||
"negAestheticScore": "Negative Aesthetic Score",
|
||||
|
@ -1,8 +1,9 @@
|
||||
import fs from 'node:fs';
|
||||
|
||||
import openapiTS from 'openapi-typescript';
|
||||
|
||||
const OPENAPI_URL = 'http://127.0.0.1:9090/openapi.json';
|
||||
const OUTPUT_FILE = 'src/services/api/schema.d.ts';
|
||||
const OUTPUT_FILE = 'src/services/api/schema.ts';
|
||||
|
||||
async function main() {
|
||||
process.stdout.write(
|
||||
|
@ -1,13 +1,17 @@
|
||||
import { Flex, Grid } from '@chakra-ui/react';
|
||||
import { useStore } from '@nanostores/react';
|
||||
import { useSocketIO } from 'app/hooks/useSocketIO';
|
||||
import { useLogger } from 'app/logging/useLogger';
|
||||
import { appStarted } from 'app/store/middleware/listenerMiddleware/listeners/appStarted';
|
||||
import { $headerComponent } from 'app/store/nanostores/headerComponent';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { PartialAppConfig } from 'app/types/invokeai';
|
||||
import type { PartialAppConfig } from 'app/types/invokeai';
|
||||
import ImageUploader from 'common/components/ImageUploader';
|
||||
import { useClearStorage } from 'common/hooks/useClearStorage';
|
||||
import { useGlobalModifiersInit } from 'common/hooks/useGlobalModifiers';
|
||||
import ChangeBoardModal from 'features/changeBoardModal/components/ChangeBoardModal';
|
||||
import DeleteImageModal from 'features/deleteImageModal/components/DeleteImageModal';
|
||||
import { DynamicPromptsModal } from 'features/dynamicPrompts/components/DynamicPromptsPreviewModal';
|
||||
import SiteHeader from 'features/system/components/SiteHeader';
|
||||
import { configChanged } from 'features/system/store/configSlice';
|
||||
import { languageSelector } from 'features/system/store/systemSelectors';
|
||||
@ -16,12 +20,10 @@ import i18n from 'i18n';
|
||||
import { size } from 'lodash-es';
|
||||
import { memo, useCallback, useEffect } from 'react';
|
||||
import { ErrorBoundary } from 'react-error-boundary';
|
||||
|
||||
import AppErrorBoundaryFallback from './AppErrorBoundaryFallback';
|
||||
import GlobalHotkeys from './GlobalHotkeys';
|
||||
import PreselectedImage from './PreselectedImage';
|
||||
import Toaster from './Toaster';
|
||||
import { useSocketIO } from 'app/hooks/useSocketIO';
|
||||
import { useClearStorage } from 'common/hooks/useClearStorage';
|
||||
|
||||
const DEFAULT_CONFIG = {};
|
||||
|
||||
@ -41,6 +43,7 @@ const App = ({ config = DEFAULT_CONFIG, selectedImage }: Props) => {
|
||||
|
||||
// singleton!
|
||||
useSocketIO();
|
||||
useGlobalModifiersInit();
|
||||
|
||||
const handleReset = useCallback(() => {
|
||||
clearStorage();
|
||||
@ -96,8 +99,8 @@ const App = ({ config = DEFAULT_CONFIG, selectedImage }: Props) => {
|
||||
</Grid>
|
||||
<DeleteImageModal />
|
||||
<ChangeBoardModal />
|
||||
<DynamicPromptsModal />
|
||||
<Toaster />
|
||||
<GlobalHotkeys />
|
||||
<PreselectedImage selectedImage={selectedImage} />
|
||||
</ErrorBoundary>
|
||||
);
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Flex, Heading, Link, Text, useToast } from '@chakra-ui/react';
|
||||
import IAIButton from 'common/components/IAIButton';
|
||||
import { Flex, Heading, Link, useToast } from '@chakra-ui/react';
|
||||
import { InvButton } from 'common/components/InvButton/InvButton';
|
||||
import { InvText } from 'common/components/InvText/wrapper';
|
||||
import newGithubIssueUrl from 'new-github-issue-url';
|
||||
import { memo, useCallback, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -67,30 +68,24 @@ const AppErrorBoundaryFallback = ({ error, resetErrorBoundary }: Props) => {
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
sx={{
|
||||
fontWeight: 600,
|
||||
color: 'error.500',
|
||||
_dark: { color: 'error.400' },
|
||||
}}
|
||||
>
|
||||
<InvText fontWeight="semibold" color="error.400">
|
||||
{error.name}: {error.message}
|
||||
</Text>
|
||||
</InvText>
|
||||
</Flex>
|
||||
<Flex sx={{ gap: 4 }}>
|
||||
<IAIButton
|
||||
<InvButton
|
||||
leftIcon={<FaArrowRotateLeft />}
|
||||
onClick={resetErrorBoundary}
|
||||
>
|
||||
{t('accessibility.resetUI')}
|
||||
</IAIButton>
|
||||
<IAIButton leftIcon={<FaCopy />} onClick={handleCopy}>
|
||||
</InvButton>
|
||||
<InvButton leftIcon={<FaCopy />} onClick={handleCopy}>
|
||||
{t('common.copyError')}
|
||||
</IAIButton>
|
||||
</InvButton>
|
||||
<Link href={url} isExternal>
|
||||
<IAIButton leftIcon={<FaExternalLinkAlt />}>
|
||||
<InvButton leftIcon={<FaExternalLinkAlt />}>
|
||||
{t('accessibility.createIssue')}
|
||||
</IAIButton>
|
||||
</InvButton>
|
||||
</Link>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
@ -1,112 +0,0 @@
|
||||
import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
|
||||
import { stateSelector } from 'app/store/store';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { useQueueBack } from 'features/queue/hooks/useQueueBack';
|
||||
import { useQueueFront } from 'features/queue/hooks/useQueueFront';
|
||||
import {
|
||||
ctrlKeyPressed,
|
||||
metaKeyPressed,
|
||||
shiftKeyPressed,
|
||||
} from 'features/ui/store/hotkeysSlice';
|
||||
import { setActiveTab } from 'features/ui/store/uiSlice';
|
||||
import React, { memo } from 'react';
|
||||
import { isHotkeyPressed, useHotkeys } from 'react-hotkeys-hook';
|
||||
|
||||
const globalHotkeysSelector = createMemoizedSelector(
|
||||
[stateSelector],
|
||||
({ hotkeys }) => {
|
||||
const { shift, ctrl, meta } = hotkeys;
|
||||
return { shift, ctrl, meta };
|
||||
}
|
||||
);
|
||||
|
||||
// TODO: Does not catch keypresses while focused in an input. Maybe there is a way?
|
||||
|
||||
/**
|
||||
* Logical component. Handles app-level global hotkeys.
|
||||
* @returns null
|
||||
*/
|
||||
const GlobalHotkeys: React.FC = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { shift, ctrl, meta } = useAppSelector(globalHotkeysSelector);
|
||||
const {
|
||||
queueBack,
|
||||
isDisabled: isDisabledQueueBack,
|
||||
isLoading: isLoadingQueueBack,
|
||||
} = useQueueBack();
|
||||
|
||||
useHotkeys(
|
||||
['ctrl+enter', 'meta+enter'],
|
||||
queueBack,
|
||||
{
|
||||
enabled: () => !isDisabledQueueBack && !isLoadingQueueBack,
|
||||
preventDefault: true,
|
||||
enableOnFormTags: ['input', 'textarea', 'select'],
|
||||
},
|
||||
[queueBack, isDisabledQueueBack, isLoadingQueueBack]
|
||||
);
|
||||
|
||||
const {
|
||||
queueFront,
|
||||
isDisabled: isDisabledQueueFront,
|
||||
isLoading: isLoadingQueueFront,
|
||||
} = useQueueFront();
|
||||
|
||||
useHotkeys(
|
||||
['ctrl+shift+enter', 'meta+shift+enter'],
|
||||
queueFront,
|
||||
{
|
||||
enabled: () => !isDisabledQueueFront && !isLoadingQueueFront,
|
||||
preventDefault: true,
|
||||
enableOnFormTags: ['input', 'textarea', 'select'],
|
||||
},
|
||||
[queueFront, isDisabledQueueFront, isLoadingQueueFront]
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
'*',
|
||||
() => {
|
||||
if (isHotkeyPressed('shift')) {
|
||||
!shift && dispatch(shiftKeyPressed(true));
|
||||
} else {
|
||||
shift && dispatch(shiftKeyPressed(false));
|
||||
}
|
||||
if (isHotkeyPressed('ctrl')) {
|
||||
!ctrl && dispatch(ctrlKeyPressed(true));
|
||||
} else {
|
||||
ctrl && dispatch(ctrlKeyPressed(false));
|
||||
}
|
||||
if (isHotkeyPressed('meta')) {
|
||||
!meta && dispatch(metaKeyPressed(true));
|
||||
} else {
|
||||
meta && dispatch(metaKeyPressed(false));
|
||||
}
|
||||
},
|
||||
{ keyup: true, keydown: true },
|
||||
[shift, ctrl, meta]
|
||||
);
|
||||
|
||||
useHotkeys('1', () => {
|
||||
dispatch(setActiveTab('txt2img'));
|
||||
});
|
||||
|
||||
useHotkeys('2', () => {
|
||||
dispatch(setActiveTab('img2img'));
|
||||
});
|
||||
|
||||
useHotkeys('3', () => {
|
||||
dispatch(setActiveTab('unifiedCanvas'));
|
||||
});
|
||||
|
||||
useHotkeys('4', () => {
|
||||
dispatch(setActiveTab('nodes'));
|
||||
});
|
||||
|
||||
useHotkeys('5', () => {
|
||||
dispatch(setActiveTab('modelManager'));
|
||||
});
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default memo(GlobalHotkeys);
|
@ -1,29 +1,25 @@
|
||||
import { Middleware } from '@reduxjs/toolkit';
|
||||
import 'i18n';
|
||||
|
||||
import type { Middleware } from '@reduxjs/toolkit';
|
||||
import { $socketOptions } from 'app/hooks/useSocketIO';
|
||||
import { $authToken } from 'app/store/nanostores/authToken';
|
||||
import { $baseUrl } from 'app/store/nanostores/baseUrl';
|
||||
import { $customStarUI, CustomStarUi } from 'app/store/nanostores/customStarUI';
|
||||
import type { CustomStarUi } from 'app/store/nanostores/customStarUI';
|
||||
import { $customStarUI } from 'app/store/nanostores/customStarUI';
|
||||
import { $headerComponent } from 'app/store/nanostores/headerComponent';
|
||||
import { $isDebugging } from 'app/store/nanostores/isDebugging';
|
||||
import { $projectId } from 'app/store/nanostores/projectId';
|
||||
import { $queueId, DEFAULT_QUEUE_ID } from 'app/store/nanostores/queueId';
|
||||
import { $store } from 'app/store/nanostores/store';
|
||||
import { createStore } from 'app/store/store';
|
||||
import { PartialAppConfig } from 'app/types/invokeai';
|
||||
import type { PartialAppConfig } from 'app/types/invokeai';
|
||||
import Loading from 'common/components/Loading/Loading';
|
||||
import AppDndContext from 'features/dnd/components/AppDndContext';
|
||||
import 'i18n';
|
||||
import React, {
|
||||
PropsWithChildren,
|
||||
ReactNode,
|
||||
lazy,
|
||||
memo,
|
||||
useEffect,
|
||||
useMemo,
|
||||
} from 'react';
|
||||
import type { PropsWithChildren, ReactNode } from 'react';
|
||||
import React, { lazy, memo, useEffect, useMemo } from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { addMiddleware, resetMiddlewares } from 'redux-dynamic-middlewares';
|
||||
import { ManagerOptions, SocketOptions } from 'socket.io-client';
|
||||
import type { ManagerOptions, SocketOptions } from 'socket.io-client';
|
||||
|
||||
const App = lazy(() => import('./App'));
|
||||
const ThemeLocaleProvider = lazy(() => import('./ThemeLocaleProvider'));
|
||||
|
@ -1,24 +1,17 @@
|
||||
import {
|
||||
ChakraProvider,
|
||||
createLocalStorageManager,
|
||||
extendTheme,
|
||||
} from '@chakra-ui/react';
|
||||
import { ReactNode, memo, useEffect, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { TOAST_OPTIONS, theme as invokeAITheme } from 'theme/theme';
|
||||
|
||||
import '@fontsource-variable/inter';
|
||||
import { MantineProvider } from '@mantine/core';
|
||||
import { useMantineTheme } from 'mantine-theme/theme';
|
||||
import 'overlayscrollbars/overlayscrollbars.css';
|
||||
import 'theme/css/overlayscrollbars.css';
|
||||
import 'common/components/OverlayScrollbars/overlayscrollbars.css';
|
||||
|
||||
import { ChakraProvider, extendTheme } from '@chakra-ui/react';
|
||||
import type { ReactNode } from 'react';
|
||||
import { memo, useEffect, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { theme as invokeAITheme, TOAST_OPTIONS } from 'theme/theme';
|
||||
|
||||
type ThemeLocaleProviderProps = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
const manager = createLocalStorageManager('@@invokeai-color-mode');
|
||||
|
||||
function ThemeLocaleProvider({ children }: ThemeLocaleProviderProps) {
|
||||
const { i18n } = useTranslation();
|
||||
|
||||
@ -35,18 +28,10 @@ function ThemeLocaleProvider({ children }: ThemeLocaleProviderProps) {
|
||||
document.body.dir = direction;
|
||||
}, [direction]);
|
||||
|
||||
const mantineTheme = useMantineTheme();
|
||||
|
||||
return (
|
||||
<MantineProvider theme={mantineTheme}>
|
||||
<ChakraProvider
|
||||
theme={theme}
|
||||
colorModeManager={manager}
|
||||
toastOptions={TOAST_OPTIONS}
|
||||
>
|
||||
{children}
|
||||
</ChakraProvider>
|
||||
</MantineProvider>
|
||||
<ChakraProvider theme={theme} toastOptions={TOAST_OPTIONS}>
|
||||
{children}
|
||||
</ChakraProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { useToast } from '@chakra-ui/react';
|
||||
import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
|
||||
import { addToast, clearToastQueue } from 'features/system/store/systemSlice';
|
||||
import { MakeToastArg, makeToast } from 'features/system/util/makeToast';
|
||||
import type { MakeToastArg } from 'features/system/util/makeToast';
|
||||
import { makeToast } from 'features/system/util/makeToast';
|
||||
import { memo, useCallback, useEffect } from 'react';
|
||||
|
||||
/**
|
||||
|
@ -1,94 +0,0 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
type FeatureHelpInfo = {
|
||||
text: string;
|
||||
href: string;
|
||||
guideImage: string;
|
||||
};
|
||||
|
||||
export enum Feature {
|
||||
PROMPT,
|
||||
GALLERY,
|
||||
OTHER,
|
||||
SEED,
|
||||
VARIATIONS,
|
||||
UPSCALE,
|
||||
FACE_CORRECTION,
|
||||
IMAGE_TO_IMAGE,
|
||||
BOUNDING_BOX,
|
||||
SEAM_CORRECTION,
|
||||
INFILL_AND_SCALING,
|
||||
}
|
||||
/** For each tooltip in the UI, the below feature definitions & props will pull relevant information into the tooltip.
|
||||
*
|
||||
* To-do: href & GuideImages are placeholders, and are not currently utilized, but will be updated (along with the tooltip UI) as feature and UI develop and we get a better idea on where things "forever homes" will be .
|
||||
*/
|
||||
const useFeatures = (): Record<Feature, FeatureHelpInfo> => {
|
||||
const { t } = useTranslation();
|
||||
return useMemo(
|
||||
() => ({
|
||||
[Feature.PROMPT]: {
|
||||
text: t('tooltip.feature.prompt'),
|
||||
href: 'link/to/docs/feature3.html',
|
||||
guideImage: 'asset/path.gif',
|
||||
},
|
||||
[Feature.GALLERY]: {
|
||||
text: t('tooltip.feature.gallery'),
|
||||
href: 'link/to/docs/feature3.html',
|
||||
guideImage: 'asset/path.gif',
|
||||
},
|
||||
[Feature.OTHER]: {
|
||||
text: t('tooltip.feature.other'),
|
||||
href: 'link/to/docs/feature3.html',
|
||||
guideImage: 'asset/path.gif',
|
||||
},
|
||||
[Feature.SEED]: {
|
||||
text: t('tooltip.feature.seed'),
|
||||
href: 'link/to/docs/feature3.html',
|
||||
guideImage: 'asset/path.gif',
|
||||
},
|
||||
[Feature.VARIATIONS]: {
|
||||
text: t('tooltip.feature.variations'),
|
||||
href: 'link/to/docs/feature3.html',
|
||||
guideImage: 'asset/path.gif',
|
||||
},
|
||||
[Feature.UPSCALE]: {
|
||||
text: t('tooltip.feature.upscale'),
|
||||
href: 'link/to/docs/feature1.html',
|
||||
guideImage: 'asset/path.gif',
|
||||
},
|
||||
[Feature.FACE_CORRECTION]: {
|
||||
text: t('tooltip.feature.faceCorrection'),
|
||||
href: 'link/to/docs/feature3.html',
|
||||
guideImage: 'asset/path.gif',
|
||||
},
|
||||
[Feature.IMAGE_TO_IMAGE]: {
|
||||
text: t('tooltip.feature.imageToImage'),
|
||||
href: 'link/to/docs/feature3.html',
|
||||
guideImage: 'asset/path.gif',
|
||||
},
|
||||
[Feature.BOUNDING_BOX]: {
|
||||
text: t('tooltip.feature.boundingBox'),
|
||||
href: 'link/to/docs/feature3.html',
|
||||
guideImage: 'asset/path.gif',
|
||||
},
|
||||
[Feature.SEAM_CORRECTION]: {
|
||||
text: t('tooltip.feature.seamCorrection'),
|
||||
href: 'link/to/docs/feature3.html',
|
||||
guideImage: 'asset/path.gif',
|
||||
},
|
||||
[Feature.INFILL_AND_SCALING]: {
|
||||
text: t('tooltip.feature.infillAndScaling'),
|
||||
href: 'link/to/docs/feature3.html',
|
||||
guideImage: 'asset/path.gif',
|
||||
},
|
||||
}),
|
||||
[t]
|
||||
);
|
||||
};
|
||||
|
||||
export const useFeatureHelpInfo = (feature: Feature): FeatureHelpInfo => {
|
||||
const features = useFeatures();
|
||||
return features[feature];
|
||||
};
|
@ -3,14 +3,16 @@ import { $authToken } from 'app/store/nanostores/authToken';
|
||||
import { $baseUrl } from 'app/store/nanostores/baseUrl';
|
||||
import { $isDebugging } from 'app/store/nanostores/isDebugging';
|
||||
import { useAppDispatch } from 'app/store/storeHooks';
|
||||
import { MapStore, atom, map } from 'nanostores';
|
||||
import type { MapStore } from 'nanostores';
|
||||
import { atom, map } from 'nanostores';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import {
|
||||
import type {
|
||||
ClientToServerEvents,
|
||||
ServerToClientEvents,
|
||||
} from 'services/events/types';
|
||||
import { setEventListeners } from 'services/events/util/setEventListeners';
|
||||
import { ManagerOptions, Socket, SocketOptions, io } from 'socket.io-client';
|
||||
import type { ManagerOptions, Socket, SocketOptions } from 'socket.io-client';
|
||||
import { io } from 'socket.io-client';
|
||||
|
||||
// Inject socket options and url into window for debugging
|
||||
declare global {
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { createLogWriter } from '@roarr/browser-log-writer';
|
||||
import { atom } from 'nanostores';
|
||||
import { Logger, ROARR, Roarr } from 'roarr';
|
||||
import type { Logger } from 'roarr';
|
||||
import { ROARR, Roarr } from 'roarr';
|
||||
import { z } from 'zod';
|
||||
|
||||
ROARR.write = createLogWriter();
|
||||
|
||||
@ -26,19 +28,20 @@ export type LoggerNamespace =
|
||||
export const logger = (namespace: LoggerNamespace) =>
|
||||
$logger.get().child({ namespace });
|
||||
|
||||
export const VALID_LOG_LEVELS = [
|
||||
export const zLogLevel = z.enum([
|
||||
'trace',
|
||||
'debug',
|
||||
'info',
|
||||
'warn',
|
||||
'error',
|
||||
'fatal',
|
||||
] as const;
|
||||
|
||||
export type InvokeLogLevel = (typeof VALID_LOG_LEVELS)[number];
|
||||
]);
|
||||
export type LogLevel = z.infer<typeof zLogLevel>;
|
||||
export const isLogLevel = (v: unknown): v is LogLevel =>
|
||||
zLogLevel.safeParse(v).success;
|
||||
|
||||
// Translate human-readable log levels to numbers, used for log filtering
|
||||
export const LOG_LEVEL_MAP: Record<InvokeLogLevel, number> = {
|
||||
export const LOG_LEVEL_MAP: Record<LogLevel, number> = {
|
||||
trace: 10,
|
||||
debug: 20,
|
||||
info: 30,
|
||||
|
@ -4,13 +4,9 @@ import { stateSelector } from 'app/store/store';
|
||||
import { useAppSelector } from 'app/store/storeHooks';
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { ROARR, Roarr } from 'roarr';
|
||||
import {
|
||||
$logger,
|
||||
BASE_CONTEXT,
|
||||
LOG_LEVEL_MAP,
|
||||
LoggerNamespace,
|
||||
logger,
|
||||
} from './logger';
|
||||
|
||||
import type { LoggerNamespace } from './logger';
|
||||
import { $logger, BASE_CONTEXT, LOG_LEVEL_MAP, logger } from './logger';
|
||||
|
||||
const selector = createMemoizedSelector(stateSelector, ({ system }) => {
|
||||
const { consoleLogLevel, shouldLogToConsole } = system;
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { createAction } from '@reduxjs/toolkit';
|
||||
import { InvokeTabName } from 'features/ui/store/tabMap';
|
||||
import { BatchConfig } from 'services/api/types';
|
||||
import type { InvokeTabName } from 'features/ui/store/tabMap';
|
||||
import type { BatchConfig } from 'services/api/types';
|
||||
|
||||
export const enqueueRequested = createAction<{
|
||||
tabName: InvokeTabName;
|
||||
|
@ -8,7 +8,7 @@ import { postprocessingPersistDenylist } from 'features/parameters/store/postpro
|
||||
import { systemPersistDenylist } from 'features/system/store/systemPersistDenylist';
|
||||
import { uiPersistDenylist } from 'features/ui/store/uiPersistDenylist';
|
||||
import { omit } from 'lodash-es';
|
||||
import { SerializeFunction } from 'redux-remember';
|
||||
import type { SerializeFunction } from 'redux-remember';
|
||||
|
||||
const serializationDenylist: {
|
||||
[key: string]: string[];
|
||||
|
@ -11,7 +11,7 @@ import { initialSystemState } from 'features/system/store/systemSlice';
|
||||
import { initialHotkeysState } from 'features/ui/store/hotkeysSlice';
|
||||
import { initialUIState } from 'features/ui/store/uiSlice';
|
||||
import { defaultsDeep } from 'lodash-es';
|
||||
import { UnserializeFunction } from 'redux-remember';
|
||||
import type { UnserializeFunction } from 'redux-remember';
|
||||
|
||||
const initialStates: {
|
||||
[key: string]: object; // TODO: type this properly
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { UnknownAction } from '@reduxjs/toolkit';
|
||||
import type { UnknownAction } from '@reduxjs/toolkit';
|
||||
import { isAnyGraphBuilt } from 'features/nodes/store/actions';
|
||||
import { nodeTemplatesBuilt } from 'features/nodes/store/nodesSlice';
|
||||
import { receivedOpenAPISchema } from 'services/api/thunks/schema';
|
||||
import { Graph } from 'services/api/types';
|
||||
import type { Graph } from 'services/api/types';
|
||||
|
||||
export const actionSanitizer = <A extends UnknownAction>(action: A): A => {
|
||||
if (isAnyGraphBuilt(action)) {
|
||||
|
@ -1,11 +1,12 @@
|
||||
import type { TypedAddListener, TypedStartListening } from '@reduxjs/toolkit';
|
||||
import {
|
||||
UnknownAction,
|
||||
import type {
|
||||
ListenerEffect,
|
||||
addListener,
|
||||
createListenerMiddleware,
|
||||
TypedAddListener,
|
||||
TypedStartListening,
|
||||
UnknownAction,
|
||||
} from '@reduxjs/toolkit';
|
||||
import { addListener, createListenerMiddleware } from '@reduxjs/toolkit';
|
||||
import type { AppDispatch, RootState } from 'app/store/store';
|
||||
|
||||
import { addCommitStagingAreaImageListener } from './listeners/addCommitStagingAreaImageListener';
|
||||
import { addFirstListImagesListener } from './listeners/addFirstListImagesListener.ts';
|
||||
import { addAnyEnqueuedListener } from './listeners/anyEnqueued';
|
||||
@ -42,13 +43,13 @@ import {
|
||||
addImageRemovedFromBoardFulfilledListener,
|
||||
addImageRemovedFromBoardRejectedListener,
|
||||
} from './listeners/imageRemovedFromBoard';
|
||||
import { addImagesStarredListener } from './listeners/imagesStarred';
|
||||
import { addImagesUnstarredListener } from './listeners/imagesUnstarred';
|
||||
import { addImageToDeleteSelectedListener } from './listeners/imageToDeleteSelected';
|
||||
import {
|
||||
addImageUploadedFulfilledListener,
|
||||
addImageUploadedRejectedListener,
|
||||
} from './listeners/imageUploaded';
|
||||
import { addImagesStarredListener } from './listeners/imagesStarred';
|
||||
import { addImagesUnstarredListener } from './listeners/imagesUnstarred';
|
||||
import { addInitialImageSelectedListener } from './listeners/initialImageSelected';
|
||||
import { addModelSelectedListener } from './listeners/modelSelected';
|
||||
import { addModelsLoadedListener } from './listeners/modelsLoaded';
|
||||
@ -69,9 +70,9 @@ import { addSocketSubscribedEventListener as addSocketSubscribedListener } from
|
||||
import { addSocketUnsubscribedEventListener as addSocketUnsubscribedListener } from './listeners/socketio/socketUnsubscribed';
|
||||
import { addStagingAreaImageSavedListener } from './listeners/stagingAreaImageSaved';
|
||||
import { addTabChangedListener } from './listeners/tabChanged';
|
||||
import { addUpdateAllNodesRequestedListener } from './listeners/updateAllNodesRequested';
|
||||
import { addUpscaleRequestedListener } from './listeners/upscaleRequested';
|
||||
import { addWorkflowLoadRequestedListener } from './listeners/workflowLoadRequested';
|
||||
import { addUpdateAllNodesRequestedListener } from './listeners/updateAllNodesRequested';
|
||||
|
||||
export const listenerMiddleware = createListenerMiddleware();
|
||||
|
||||
|
@ -8,6 +8,7 @@ import {
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { t } from 'i18next';
|
||||
import { queueApi } from 'services/api/endpoints/queue';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
const matcher = isAnyOf(commitStagingAreaImage, discardStagedImages);
|
||||
|
@ -2,9 +2,10 @@ import { createAction } from '@reduxjs/toolkit';
|
||||
import { imageSelected } from 'features/gallery/store/gallerySlice';
|
||||
import { IMAGE_CATEGORIES } from 'features/gallery/store/types';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
import { startAppListening } from '..';
|
||||
import type { ImageCache } from 'services/api/types';
|
||||
import { getListImagesUrl, imagesAdapter } from 'services/api/util';
|
||||
import { ImageCache } from 'services/api/types';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
export const appStarted = createAction('app/appStarted');
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { queueApi } from 'services/api/endpoints/queue';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
export const addAnyEnqueuedListener = () => {
|
||||
|
@ -4,6 +4,7 @@ import {
|
||||
shouldUseWatermarkerChanged,
|
||||
} from 'features/system/store/systemSlice';
|
||||
import { appInfoApi } from 'services/api/endpoints/appInfo';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
export const addAppConfigReceivedListener = () => {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { createAction } from '@reduxjs/toolkit';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
export const appStarted = createAction('app/appStarted');
|
||||
|
@ -5,7 +5,8 @@ import { zPydanticValidationError } from 'features/system/store/zodSchemas';
|
||||
import { t } from 'i18next';
|
||||
import { truncate, upperFirst } from 'lodash-es';
|
||||
import { queueApi } from 'services/api/endpoints/queue';
|
||||
import { TOAST_OPTIONS, theme } from 'theme/theme';
|
||||
import { theme, TOAST_OPTIONS } from 'theme/theme';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
const { toast } = createStandaloneToast({
|
||||
|
@ -4,6 +4,7 @@ import { getImageUsage } from 'features/deleteImageModal/store/selectors';
|
||||
import { nodeEditorReset } from 'features/nodes/store/nodesSlice';
|
||||
import { clearInitialImage } from 'features/parameters/store/generationSlice';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
export const addDeleteBoardAndImagesFulfilledListener = () => {
|
||||
|
@ -9,9 +9,10 @@ import {
|
||||
IMAGE_CATEGORIES,
|
||||
} from 'features/gallery/store/types';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
import { startAppListening } from '..';
|
||||
import { imagesSelectors } from 'services/api/util';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
export const addBoardIdSelectedListener = () => {
|
||||
startAppListening({
|
||||
matcher: isAnyOf(boardIdSelected, galleryViewChanged),
|
||||
|
@ -1,11 +1,12 @@
|
||||
import { canvasCopiedToClipboard } from 'features/canvas/store/actions';
|
||||
import { startAppListening } from '..';
|
||||
import { $logger } from 'app/logging/logger';
|
||||
import { canvasCopiedToClipboard } from 'features/canvas/store/actions';
|
||||
import { getBaseLayerBlob } from 'features/canvas/util/getBaseLayerBlob';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { copyBlobToClipboard } from 'features/system/util/copyBlobToClipboard';
|
||||
import { t } from 'i18next';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
export const addCanvasCopiedToClipboardListener = () => {
|
||||
startAppListening({
|
||||
actionCreator: canvasCopiedToClipboard,
|
||||
|
@ -1,11 +1,12 @@
|
||||
import { canvasDownloadedAsImage } from 'features/canvas/store/actions';
|
||||
import { startAppListening } from '..';
|
||||
import { $logger } from 'app/logging/logger';
|
||||
import { canvasDownloadedAsImage } from 'features/canvas/store/actions';
|
||||
import { downloadBlob } from 'features/canvas/util/downloadBlob';
|
||||
import { getBaseLayerBlob } from 'features/canvas/util/getBaseLayerBlob';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { t } from 'i18next';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
export const addCanvasDownloadedAsImageListener = () => {
|
||||
startAppListening({
|
||||
actionCreator: canvasDownloadedAsImage,
|
||||
|
@ -1,11 +1,12 @@
|
||||
import { logger } from 'app/logging/logger';
|
||||
import { canvasImageToControlAdapter } from 'features/canvas/store/actions';
|
||||
import { getBaseLayerBlob } from 'features/canvas/util/getBaseLayerBlob';
|
||||
import { controlAdapterImageChanged } from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { t } from 'i18next';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
import { canvasImageToControlAdapter } from 'features/canvas/store/actions';
|
||||
|
||||
export const addCanvasImageToControlNetListener = () => {
|
||||
startAppListening({
|
||||
|
@ -2,9 +2,10 @@ import { logger } from 'app/logging/logger';
|
||||
import { canvasMaskSavedToGallery } from 'features/canvas/store/actions';
|
||||
import { getCanvasData } from 'features/canvas/util/getCanvasData';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
import { startAppListening } from '..';
|
||||
import { t } from 'i18next';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
export const addCanvasMaskSavedToGalleryListener = () => {
|
||||
startAppListening({
|
||||
|
@ -5,6 +5,7 @@ import { controlAdapterImageChanged } from 'features/controlAdapters/store/contr
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { t } from 'i18next';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
export const addCanvasMaskToControlNetListener = () => {
|
||||
|
@ -4,9 +4,10 @@ import { setMergedCanvas } from 'features/canvas/store/canvasSlice';
|
||||
import { getFullBaseLayerBlob } from 'features/canvas/util/getFullBaseLayerBlob';
|
||||
import { getCanvasBaseLayer } from 'features/canvas/util/konvaInstanceProvider';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
import { startAppListening } from '..';
|
||||
import { t } from 'i18next';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
export const addCanvasMergedListener = () => {
|
||||
startAppListening({
|
||||
|
@ -2,9 +2,10 @@ import { logger } from 'app/logging/logger';
|
||||
import { canvasSavedToGallery } from 'features/canvas/store/actions';
|
||||
import { getBaseLayerBlob } from 'features/canvas/util/getBaseLayerBlob';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
import { startAppListening } from '..';
|
||||
import { t } from 'i18next';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
export const addCanvasSavedToGalleryListener = () => {
|
||||
startAppListening({
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { AnyListenerPredicate } from '@reduxjs/toolkit';
|
||||
import type { AnyListenerPredicate } from '@reduxjs/toolkit';
|
||||
import { logger } from 'app/logging/logger';
|
||||
import { RootState } from 'app/store/store';
|
||||
import type { RootState } from 'app/store/store';
|
||||
import { controlAdapterImageProcessed } from 'features/controlAdapters/store/actions';
|
||||
import {
|
||||
controlAdapterAutoConfigToggled,
|
||||
@ -10,9 +10,10 @@ import {
|
||||
controlAdapterProcessortTypeChanged,
|
||||
selectControlAdapterById,
|
||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import { startAppListening } from '..';
|
||||
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
type AnyControlAdapterParamChangeAction =
|
||||
| ReturnType<typeof controlAdapterProcessorParamsChanged>
|
||||
| ReturnType<typeof controlAdapterModelChanged>
|
||||
|
@ -8,14 +8,15 @@ import {
|
||||
selectControlAdapterById,
|
||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import { isControlNetOrT2IAdapter } from 'features/controlAdapters/store/types';
|
||||
import { isImageOutput } from 'features/nodes/types/common';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { t } from 'i18next';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
import { queueApi } from 'services/api/endpoints/queue';
|
||||
import { BatchConfig, ImageDTO } from 'services/api/types';
|
||||
import type { BatchConfig, ImageDTO } from 'services/api/types';
|
||||
import { socketInvocationComplete } from 'services/events/actions';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
import { isImageOutput } from 'features/nodes/types/common';
|
||||
|
||||
export const addControlNetImageProcessedListener = () => {
|
||||
startAppListening({
|
||||
|
@ -14,7 +14,8 @@ import { buildCanvasGraph } from 'features/nodes/util/graph/buildCanvasGraph';
|
||||
import { prepareLinearUIBatch } from 'features/nodes/util/graph/buildLinearBatchConfig';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
import { queueApi } from 'services/api/endpoints/queue';
|
||||
import { ImageDTO } from 'services/api/types';
|
||||
import type { ImageDTO } from 'services/api/types';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
/**
|
||||
|
@ -5,6 +5,7 @@ import { buildLinearSDXLImageToImageGraph } from 'features/nodes/util/graph/buil
|
||||
import { buildLinearSDXLTextToImageGraph } from 'features/nodes/util/graph/buildLinearSDXLTextToImageGraph';
|
||||
import { buildLinearTextToImageGraph } from 'features/nodes/util/graph/buildLinearTextToImageGraph';
|
||||
import { queueApi } from 'services/api/endpoints/queue';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
export const addEnqueueRequestedLinear = () => {
|
||||
|
@ -2,7 +2,8 @@ import { enqueueRequested } from 'app/store/actions';
|
||||
import { buildNodesGraph } from 'features/nodes/util/graph/buildNodesGraph';
|
||||
import { buildWorkflow } from 'features/nodes/util/workflow/buildWorkflow';
|
||||
import { queueApi } from 'services/api/endpoints/queue';
|
||||
import { BatchConfig } from 'services/api/types';
|
||||
import type { BatchConfig } from 'services/api/types';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
export const addEnqueueRequestedNodes = () => {
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { logger } from 'app/logging/logger';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
export const addImageAddedToBoardFulfilledListener = () => {
|
||||
|
@ -18,6 +18,7 @@ import { clamp, forEach } from 'lodash-es';
|
||||
import { api } from 'services/api';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
import { imagesAdapter } from 'services/api/util';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
export const addRequestedSingleImageDeletionListener = () => {
|
||||
|
@ -6,16 +6,17 @@ import {
|
||||
controlAdapterImageChanged,
|
||||
controlAdapterIsEnabledChanged,
|
||||
} from 'features/controlAdapters/store/controlAdaptersSlice';
|
||||
import {
|
||||
import type {
|
||||
TypesafeDraggableData,
|
||||
TypesafeDroppableData,
|
||||
} from 'features/dnd/types';
|
||||
import { imageSelected } from 'features/gallery/store/gallerySlice';
|
||||
import { fieldImageValueChanged } from 'features/nodes/store/nodesSlice';
|
||||
import { workflowExposedFieldAdded } from 'features/nodes/store/workflowSlice';
|
||||
import { initialImageChanged } from 'features/parameters/store/generationSlice';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
|
||||
import { startAppListening } from '../';
|
||||
import { workflowExposedFieldAdded } from 'features/nodes/store/workflowSlice';
|
||||
|
||||
export const dndDropped = createAction<{
|
||||
overData: TypesafeDroppableData;
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { logger } from 'app/logging/logger';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
export const addImageRemovedFromBoardFulfilledListener = () => {
|
||||
|
@ -4,6 +4,7 @@ import {
|
||||
imagesToDeleteSelected,
|
||||
isModalOpenChanged,
|
||||
} from 'features/deleteImageModal/store/slice';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
export const addImageToDeleteSelectedListener = () => {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { UseToastOptions } from '@chakra-ui/react';
|
||||
import type { UseToastOptions } from '@chakra-ui/react';
|
||||
import { logger } from 'app/logging/logger';
|
||||
import { setInitialCanvasImage } from 'features/canvas/store/canvasSlice';
|
||||
import {
|
||||
@ -11,9 +11,10 @@ import { addToast } from 'features/system/store/systemSlice';
|
||||
import { t } from 'i18next';
|
||||
import { omit } from 'lodash-es';
|
||||
import { boardsApi } from 'services/api/endpoints/boards';
|
||||
import { startAppListening } from '..';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
export const addImageUploadedFulfilledListener = () => {
|
||||
startAppListening({
|
||||
matcher: imagesApi.endpoints.uploadImage.matchFulfilled,
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
import { startAppListening } from '..';
|
||||
import { selectionChanged } from 'features/gallery/store/gallerySlice';
|
||||
import { ImageDTO } from 'services/api/types';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
import type { ImageDTO } from 'services/api/types';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
export const addImagesStarredListener = () => {
|
||||
startAppListening({
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
import { startAppListening } from '..';
|
||||
import { selectionChanged } from 'features/gallery/store/gallerySlice';
|
||||
import { ImageDTO } from 'services/api/types';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
import type { ImageDTO } from 'services/api/types';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
export const addImagesUnstarredListener = () => {
|
||||
startAppListening({
|
||||
|
@ -3,6 +3,7 @@ import { initialImageChanged } from 'features/parameters/store/generationSlice';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { makeToast } from 'features/system/util/makeToast';
|
||||
import { t } from 'i18next';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
export const addInitialImageSelectedListener = () => {
|
||||
|
@ -7,17 +7,18 @@ import {
|
||||
import { loraRemoved } from 'features/lora/store/loraSlice';
|
||||
import { modelSelected } from 'features/parameters/store/actions';
|
||||
import {
|
||||
heightChanged,
|
||||
modelChanged,
|
||||
setHeight,
|
||||
setWidth,
|
||||
vaeSelected,
|
||||
widthChanged,
|
||||
} from 'features/parameters/store/generationSlice';
|
||||
import { zParameterModel } from 'features/parameters/types/parameterSchemas';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { makeToast } from 'features/system/util/makeToast';
|
||||
import { t } from 'i18next';
|
||||
import { forEach } from 'lodash-es';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
import { zParameterModel } from 'features/parameters/types/parameterSchemas';
|
||||
|
||||
export const addModelSelectedListener = () => {
|
||||
startAppListening({
|
||||
@ -89,12 +90,12 @@ export const addModelSelectedListener = () => {
|
||||
state.ui.shouldAutoChangeDimensions
|
||||
) {
|
||||
if (['sdxl', 'sdxl-refiner'].includes(newModel.base_model)) {
|
||||
dispatch(setWidth(1024));
|
||||
dispatch(setHeight(1024));
|
||||
dispatch(widthChanged(1024));
|
||||
dispatch(heightChanged(1024));
|
||||
dispatch(setBoundingBoxDimensions({ width: 1024, height: 1024 }));
|
||||
} else {
|
||||
dispatch(setWidth(512));
|
||||
dispatch(setHeight(512));
|
||||
dispatch(widthChanged(512));
|
||||
dispatch(heightChanged(512));
|
||||
dispatch(setBoundingBoxDimensions({ width: 512, height: 512 }));
|
||||
}
|
||||
}
|
||||
|
@ -12,20 +12,17 @@ import {
|
||||
} from 'features/parameters/store/generationSlice';
|
||||
import {
|
||||
zParameterModel,
|
||||
zParameterSDXLRefinerModel,
|
||||
zParameterVAEModel,
|
||||
} from 'features/parameters/types/parameterSchemas';
|
||||
import {
|
||||
refinerModelChanged,
|
||||
setShouldUseSDXLRefiner,
|
||||
} from 'features/sdxl/store/sdxlSlice';
|
||||
import { refinerModelChanged } from 'features/sdxl/store/sdxlSlice';
|
||||
import { forEach, some } from 'lodash-es';
|
||||
import {
|
||||
mainModelsAdapter,
|
||||
modelsApi,
|
||||
vaeModelsAdapter,
|
||||
} from 'services/api/endpoints/models';
|
||||
import { TypeGuardFor } from 'services/api/types';
|
||||
import type { TypeGuardFor } from 'services/api/types';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
export const addModelsLoadedListener = () => {
|
||||
@ -102,7 +99,6 @@ export const addModelsLoadedListener = () => {
|
||||
if (models.length === 0) {
|
||||
// No models loaded at all
|
||||
dispatch(refinerModelChanged(null));
|
||||
dispatch(setShouldUseSDXLRefiner(false));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -115,21 +111,10 @@ export const addModelsLoadedListener = () => {
|
||||
)
|
||||
: false;
|
||||
|
||||
if (isCurrentModelAvailable) {
|
||||
if (!isCurrentModelAvailable) {
|
||||
dispatch(refinerModelChanged(null));
|
||||
return;
|
||||
}
|
||||
|
||||
const result = zParameterSDXLRefinerModel.safeParse(models[0]);
|
||||
|
||||
if (!result.success) {
|
||||
log.error(
|
||||
{ error: result.error.format() },
|
||||
'Failed to parse SDXL Refiner Model'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(refinerModelChanged(result.data));
|
||||
},
|
||||
});
|
||||
startAppListening({
|
||||
|
@ -11,6 +11,7 @@ import {
|
||||
import { setPositivePrompt } from 'features/parameters/store/generationSlice';
|
||||
import { utilitiesApi } from 'services/api/endpoints/utilities';
|
||||
import { appSocketConnected } from 'services/events/actions';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
const matcher = isAnyOf(
|
||||
|
@ -4,6 +4,7 @@ import { nodeTemplatesBuilt } from 'features/nodes/store/nodesSlice';
|
||||
import { parseSchema } from 'features/nodes/util/schema/parseSchema';
|
||||
import { size } from 'lodash-es';
|
||||
import { receivedOpenAPISchema } from 'services/api/thunks/schema';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
export const addReceivedOpenAPISchemaListener = () => {
|
||||
|
@ -1,10 +1,11 @@
|
||||
import { logger } from 'app/logging/logger';
|
||||
import { isInitializedChanged } from 'features/system/store/systemSlice';
|
||||
import { size } from 'lodash-es';
|
||||
import { api } from 'services/api';
|
||||
import { receivedOpenAPISchema } from 'services/api/thunks/schema';
|
||||
import { appSocketConnected, socketConnected } from 'services/events/actions';
|
||||
|
||||
import { startAppListening } from '../..';
|
||||
import { isInitializedChanged } from 'features/system/store/systemSlice';
|
||||
|
||||
export const addSocketConnectedEventListener = () => {
|
||||
startAppListening({
|
||||
|
@ -3,6 +3,7 @@ import {
|
||||
appSocketDisconnected,
|
||||
socketDisconnected,
|
||||
} from 'services/events/actions';
|
||||
|
||||
import { startAppListening } from '../..';
|
||||
|
||||
export const addSocketDisconnectedEventListener = () => {
|
||||
|
@ -3,6 +3,7 @@ import {
|
||||
appSocketGeneratorProgress,
|
||||
socketGeneratorProgress,
|
||||
} from 'services/events/actions';
|
||||
|
||||
import { startAppListening } from '../..';
|
||||
|
||||
export const addGeneratorProgressEventListener = () => {
|
||||
|
@ -3,6 +3,7 @@ import {
|
||||
appSocketGraphExecutionStateComplete,
|
||||
socketGraphExecutionStateComplete,
|
||||
} from 'services/events/actions';
|
||||
|
||||
import { startAppListening } from '../..';
|
||||
|
||||
export const addGraphExecutionStateCompleteEventListener = () => {
|
||||
|
@ -7,6 +7,7 @@ import {
|
||||
imageSelected,
|
||||
} from 'features/gallery/store/gallerySlice';
|
||||
import { IMAGE_CATEGORIES } from 'features/gallery/store/types';
|
||||
import { isImageOutput } from 'features/nodes/types/common';
|
||||
import {
|
||||
LINEAR_UI_OUTPUT,
|
||||
nodeIDDenyList,
|
||||
@ -18,8 +19,8 @@ import {
|
||||
appSocketInvocationComplete,
|
||||
socketInvocationComplete,
|
||||
} from 'services/events/actions';
|
||||
|
||||
import { startAppListening } from '../..';
|
||||
import { isImageOutput } from 'features/nodes/types/common';
|
||||
|
||||
// These nodes output an image, but do not actually *save* an image, so we don't want to handle the gallery logic on them
|
||||
const nodeTypeDenylist = ['load_image', 'image'];
|
||||
|
@ -3,6 +3,7 @@ import {
|
||||
appSocketInvocationError,
|
||||
socketInvocationError,
|
||||
} from 'services/events/actions';
|
||||
|
||||
import { startAppListening } from '../..';
|
||||
|
||||
export const addInvocationErrorEventListener = () => {
|
||||
|
@ -3,6 +3,7 @@ import {
|
||||
appSocketInvocationRetrievalError,
|
||||
socketInvocationRetrievalError,
|
||||
} from 'services/events/actions';
|
||||
|
||||
import { startAppListening } from '../..';
|
||||
|
||||
export const addInvocationRetrievalErrorEventListener = () => {
|
||||
|
@ -3,6 +3,7 @@ import {
|
||||
appSocketInvocationStarted,
|
||||
socketInvocationStarted,
|
||||
} from 'services/events/actions';
|
||||
|
||||
import { startAppListening } from '../..';
|
||||
|
||||
export const addInvocationStartedEventListener = () => {
|
||||
|
@ -5,6 +5,7 @@ import {
|
||||
socketModelLoadCompleted,
|
||||
socketModelLoadStarted,
|
||||
} from 'services/events/actions';
|
||||
|
||||
import { startAppListening } from '../..';
|
||||
|
||||
export const addModelLoadEventListener = () => {
|
||||
|
@ -4,6 +4,7 @@ import {
|
||||
appSocketQueueItemStatusChanged,
|
||||
socketQueueItemStatusChanged,
|
||||
} from 'services/events/actions';
|
||||
|
||||
import { startAppListening } from '../..';
|
||||
|
||||
export const addSocketQueueItemStatusChangedEventListener = () => {
|
||||
|
@ -3,6 +3,7 @@ import {
|
||||
appSocketSessionRetrievalError,
|
||||
socketSessionRetrievalError,
|
||||
} from 'services/events/actions';
|
||||
|
||||
import { startAppListening } from '../..';
|
||||
|
||||
export const addSessionRetrievalErrorEventListener = () => {
|
||||
|
@ -3,6 +3,7 @@ import {
|
||||
appSocketSubscribedSession,
|
||||
socketSubscribedSession,
|
||||
} from 'services/events/actions';
|
||||
|
||||
import { startAppListening } from '../..';
|
||||
|
||||
export const addSocketSubscribedEventListener = () => {
|
||||
|
@ -3,6 +3,7 @@ import {
|
||||
appSocketUnsubscribedSession,
|
||||
socketUnsubscribedSession,
|
||||
} from 'services/events/actions';
|
||||
|
||||
import { startAppListening } from '../..';
|
||||
|
||||
export const addSocketUnsubscribedEventListener = () => {
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { stagingAreaImageSaved } from 'features/canvas/store/actions';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
import { startAppListening } from '..';
|
||||
import { t } from 'i18next';
|
||||
import { imagesApi } from 'services/api/endpoints/images';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
export const addStagingAreaImageSavedListener = () => {
|
||||
startAppListening({
|
||||
|
@ -2,6 +2,7 @@ import { modelChanged } from 'features/parameters/store/generationSlice';
|
||||
import { setActiveTab } from 'features/ui/store/uiSlice';
|
||||
import { NON_REFINER_BASE_MODELS } from 'services/api/constants';
|
||||
import { mainModelsAdapter, modelsApi } from 'services/api/endpoints/models';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
export const addTabChangedListener = () => {
|
||||
|
@ -1,15 +1,16 @@
|
||||
import { logger } from 'app/logging/logger';
|
||||
import { updateAllNodesRequested } from 'features/nodes/store/actions';
|
||||
import { nodeReplaced } from 'features/nodes/store/nodesSlice';
|
||||
import { NodeUpdateError } from 'features/nodes/types/error';
|
||||
import { isInvocationNode } from 'features/nodes/types/invocation';
|
||||
import {
|
||||
getNeedsUpdate,
|
||||
updateNode,
|
||||
} from 'features/nodes/util/node/nodeUpdate';
|
||||
import { NodeUpdateError } from 'features/nodes/types/error';
|
||||
import { isInvocationNode } from 'features/nodes/types/invocation';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { makeToast } from 'features/system/util/makeToast';
|
||||
import { t } from 'i18next';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
export const addUpdateAllNodesRequestedListener = () => {
|
||||
|
@ -2,12 +2,13 @@ import { createAction } from '@reduxjs/toolkit';
|
||||
import { logger } from 'app/logging/logger';
|
||||
import { parseify } from 'common/util/serialize';
|
||||
import { buildAdHocUpscaleGraph } from 'features/nodes/util/graph/buildAdHocUpscaleGraph';
|
||||
import { createIsAllowedToUpscaleSelector } from 'features/parameters/hooks/useIsAllowedToUpscale';
|
||||
import { addToast } from 'features/system/store/systemSlice';
|
||||
import { t } from 'i18next';
|
||||
import { queueApi } from 'services/api/endpoints/queue';
|
||||
import type { BatchConfig, ImageDTO } from 'services/api/types';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
import { BatchConfig, ImageDTO } from 'services/api/types';
|
||||
import { createIsAllowedToUpscaleSelector } from 'features/parameters/hooks/useIsAllowedToUpscale';
|
||||
|
||||
export const upscaleRequested = createAction<{ imageDTO: ImageDTO }>(
|
||||
`upscale/upscaleRequested`
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { logger } from 'app/logging/logger';
|
||||
import { parseify } from 'common/util/serialize';
|
||||
import { workflowLoadRequested } from 'features/nodes/store/actions';
|
||||
import { workflowLoaded } from 'features/nodes/store/actions';
|
||||
import {
|
||||
workflowLoaded,
|
||||
workflowLoadRequested,
|
||||
} from 'features/nodes/store/actions';
|
||||
import { $flow } from 'features/nodes/store/reactFlowInstance';
|
||||
import {
|
||||
WorkflowMigrationError,
|
||||
@ -14,6 +16,7 @@ import { setActiveTab } from 'features/ui/store/uiSlice';
|
||||
import { t } from 'i18next';
|
||||
import { z } from 'zod';
|
||||
import { fromZodError } from 'zod-validation-error';
|
||||
|
||||
import { startAppListening } from '..';
|
||||
|
||||
export const addWorkflowLoadRequestedListener = () => {
|
||||
|
3
invokeai/frontend/web/src/app/store/nanostores/README.md
Normal file
3
invokeai/frontend/web/src/app/store/nanostores/README.md
Normal file
@ -0,0 +1,3 @@
|
||||
# nanostores
|
||||
|
||||
For non-serializable data that needs to be available throughout the app, or when redux is not appropriate, use nanostores.
|
@ -1,4 +1,4 @@
|
||||
import { MenuItemProps } from '@chakra-ui/react';
|
||||
import type { MenuItemProps } from '@chakra-ui/react';
|
||||
import { atom } from 'nanostores';
|
||||
|
||||
export type CustomStarUi = {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { atom } from 'nanostores';
|
||||
import { ReactNode } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
export const $headerComponent = atom<ReactNode | undefined>(undefined);
|
||||
|
@ -1,3 +0,0 @@
|
||||
/**
|
||||
* For non-serializable data that needs to be available throughout the app, or when redux is not appropriate, use nanostores.
|
||||
*/
|
@ -1,4 +1,4 @@
|
||||
import { createStore } from 'app/store/store';
|
||||
import type { createStore } from 'app/store/store';
|
||||
import { atom } from 'nanostores';
|
||||
|
||||
export const $store = atom<
|
||||
|
@ -1,6 +1,5 @@
|
||||
import type { ThunkDispatch, UnknownAction } from '@reduxjs/toolkit';
|
||||
import {
|
||||
ThunkDispatch,
|
||||
UnknownAction,
|
||||
autoBatchEnhancer,
|
||||
combineReducers,
|
||||
configureStore,
|
||||
@ -11,6 +10,7 @@ import controlAdaptersReducer from 'features/controlAdapters/store/controlAdapte
|
||||
import deleteImageModalReducer from 'features/deleteImageModal/store/slice';
|
||||
import dynamicPromptsReducer from 'features/dynamicPrompts/store/dynamicPromptsSlice';
|
||||
import galleryReducer from 'features/gallery/store/gallerySlice';
|
||||
import hrfReducer from 'features/hrf/store/hrfSlice';
|
||||
import loraReducer from 'features/lora/store/loraSlice';
|
||||
import modelmanagerReducer from 'features/modelManager/store/modelManagerSlice';
|
||||
import nodesReducer from 'features/nodes/store/nodesSlice';
|
||||
@ -21,12 +21,14 @@ import queueReducer from 'features/queue/store/queueSlice';
|
||||
import sdxlReducer from 'features/sdxl/store/sdxlSlice';
|
||||
import configReducer from 'features/system/store/configSlice';
|
||||
import systemReducer from 'features/system/store/systemSlice';
|
||||
import hotkeysReducer from 'features/ui/store/hotkeysSlice';
|
||||
import uiReducer from 'features/ui/store/uiSlice';
|
||||
import { createStore as createIDBKeyValStore, get, set } from 'idb-keyval';
|
||||
import dynamicMiddlewares from 'redux-dynamic-middlewares';
|
||||
import { Driver, rememberEnhancer, rememberReducer } from 'redux-remember';
|
||||
import type { Driver } from 'redux-remember';
|
||||
import { rememberEnhancer, rememberReducer } from 'redux-remember';
|
||||
import { api } from 'services/api';
|
||||
import { authToastMiddleware } from 'services/api/authToastMiddleware';
|
||||
|
||||
import { STORAGE_PREFIX } from './constants';
|
||||
import { serialize } from './enhancers/reduxRemember/serialize';
|
||||
import { unserialize } from './enhancers/reduxRemember/unserialize';
|
||||
@ -34,7 +36,6 @@ import { actionSanitizer } from './middleware/devtools/actionSanitizer';
|
||||
import { actionsDenylist } from './middleware/devtools/actionsDenylist';
|
||||
import { stateSanitizer } from './middleware/devtools/stateSanitizer';
|
||||
import { listenerMiddleware } from './middleware/listenerMiddleware';
|
||||
import { authToastMiddleware } from 'services/api/authToastMiddleware';
|
||||
|
||||
const allReducers = {
|
||||
canvas: canvasReducer,
|
||||
@ -45,7 +46,6 @@ const allReducers = {
|
||||
system: systemReducer,
|
||||
config: configReducer,
|
||||
ui: uiReducer,
|
||||
hotkeys: hotkeysReducer,
|
||||
controlAdapters: controlAdaptersReducer,
|
||||
dynamicPrompts: dynamicPromptsReducer,
|
||||
deleteImageModal: deleteImageModalReducer,
|
||||
@ -55,6 +55,7 @@ const allReducers = {
|
||||
sdxl: sdxlReducer,
|
||||
queue: queueReducer,
|
||||
workflow: workflowReducer,
|
||||
hrf: hrfReducer,
|
||||
[api.reducerPath]: api.reducer,
|
||||
};
|
||||
|
||||
@ -76,6 +77,7 @@ const rememberedKeys: (keyof typeof allReducers)[] = [
|
||||
'dynamicPrompts',
|
||||
'lora',
|
||||
'modelmanager',
|
||||
'hrf',
|
||||
];
|
||||
|
||||
// Create a custom idb-keyval store (just needed to customize the name)
|
||||
@ -147,4 +149,6 @@ export type RootState = ReturnType<ReturnType<typeof createStore>['getState']>;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type AppThunkDispatch = ThunkDispatch<RootState, any, UnknownAction>;
|
||||
export type AppDispatch = ReturnType<typeof createStore>['dispatch'];
|
||||
export const stateSelector = (state: RootState) => state;
|
||||
export function stateSelector(state: RootState) {
|
||||
return state;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
|
||||
import { AppThunkDispatch, RootState } from 'app/store/store';
|
||||
import type { AppThunkDispatch, RootState } from 'app/store/store';
|
||||
import type { TypedUseSelectorHook } from 'react-redux';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
// Use throughout your app instead of plain `useDispatch` and `useSelector`
|
||||
export const useAppDispatch = () => useDispatch<AppThunkDispatch>();
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { CONTROLNET_PROCESSORS } from 'features/controlAdapters/store/constants';
|
||||
import { InvokeTabName } from 'features/ui/store/tabMap';
|
||||
import { O } from 'ts-toolbelt';
|
||||
import type { CONTROLNET_PROCESSORS } from 'features/controlAdapters/store/constants';
|
||||
import type { InvokeTabName } from 'features/ui/store/tabMap';
|
||||
import type { O } from 'ts-toolbelt';
|
||||
|
||||
/**
|
||||
* A disable-able application feature
|
||||
|
@ -0,0 +1,64 @@
|
||||
import { Flex } from '@chakra-ui/layout';
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { InvControl } from 'common/components/InvControl/InvControl';
|
||||
import { InvSlider } from 'common/components/InvSlider/InvSlider';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { AspectRatioPreview } from './AspectRatioPreview';
|
||||
|
||||
const meta: Meta<typeof AspectRatioPreview> = {
|
||||
title: 'Components/AspectRatioPreview',
|
||||
tags: ['autodocs'],
|
||||
component: AspectRatioPreview,
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof InvControl>;
|
||||
|
||||
const MIN = 64;
|
||||
const MAX = 1024;
|
||||
const STEP = 64;
|
||||
const FINE_STEP = 8;
|
||||
const INITIAL = 512;
|
||||
const MARKS = Array.from(
|
||||
{ length: Math.floor(MAX / STEP) },
|
||||
(_, i) => MIN + i * STEP
|
||||
);
|
||||
|
||||
const Component = () => {
|
||||
const [width, setWidth] = useState(INITIAL);
|
||||
const [height, setHeight] = useState(INITIAL);
|
||||
return (
|
||||
<Flex w="full" flexDir="column">
|
||||
<InvControl label="Width">
|
||||
<InvSlider
|
||||
value={width}
|
||||
min={MIN}
|
||||
max={MAX}
|
||||
step={STEP}
|
||||
fineStep={FINE_STEP}
|
||||
onChange={setWidth}
|
||||
marks={MARKS}
|
||||
/>
|
||||
</InvControl>
|
||||
<InvControl label="Height">
|
||||
<InvSlider
|
||||
value={height}
|
||||
min={MIN}
|
||||
max={MAX}
|
||||
step={STEP}
|
||||
fineStep={FINE_STEP}
|
||||
onChange={setHeight}
|
||||
marks={MARKS}
|
||||
/>
|
||||
</InvControl>
|
||||
<Flex h={96} w={96} p={4}>
|
||||
<AspectRatioPreview width={width} height={height} />
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export const AspectRatioWithSliderInvControls: Story = {
|
||||
render: Component,
|
||||
};
|
@ -0,0 +1,60 @@
|
||||
import { Flex, Icon } from '@chakra-ui/react';
|
||||
import { useSize } from '@chakra-ui/react-use-size';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { useRef } from 'react';
|
||||
import { FaImage } from 'react-icons/fa';
|
||||
|
||||
import {
|
||||
BOX_SIZE_CSS_CALC,
|
||||
ICON_CONTAINER_STYLES,
|
||||
MOTION_ICON_ANIMATE,
|
||||
MOTION_ICON_EXIT,
|
||||
MOTION_ICON_INITIAL,
|
||||
} from './constants';
|
||||
import { useAspectRatioPreviewState } from './hooks';
|
||||
import type { AspectRatioPreviewProps } from './types';
|
||||
|
||||
export const AspectRatioPreview = (props: AspectRatioPreviewProps) => {
|
||||
const { width: _width, height: _height, icon = FaImage } = props;
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const containerSize = useSize(containerRef);
|
||||
|
||||
const { width, height, shouldShowIcon } = useAspectRatioPreviewState({
|
||||
width: _width,
|
||||
height: _height,
|
||||
containerSize,
|
||||
});
|
||||
|
||||
return (
|
||||
<Flex
|
||||
w="full"
|
||||
h="full"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
ref={containerRef}
|
||||
>
|
||||
<Flex
|
||||
bg="blackAlpha.400"
|
||||
borderRadius="base"
|
||||
width={`${width}px`}
|
||||
height={`${height}px`}
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<AnimatePresence>
|
||||
{shouldShowIcon && (
|
||||
<Flex
|
||||
as={motion.div}
|
||||
initial={MOTION_ICON_INITIAL}
|
||||
animate={MOTION_ICON_ANIMATE}
|
||||
exit={MOTION_ICON_EXIT}
|
||||
style={ICON_CONTAINER_STYLES}
|
||||
>
|
||||
<Icon as={icon} color="base.700" boxSize={BOX_SIZE_CSS_CALC} />
|
||||
</Flex>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</Flex>
|
||||
</Flex>
|
||||
);
|
||||
};
|
@ -0,0 +1,23 @@
|
||||
// When the aspect ratio is between these two values, we show the icon (experimentally determined)
|
||||
export const ICON_LOW_CUTOFF = 0.23;
|
||||
export const ICON_HIGH_CUTOFF = 1 / ICON_LOW_CUTOFF;
|
||||
export const ICON_SIZE_PX = 48;
|
||||
export const ICON_PADDING_PX = 16;
|
||||
export const BOX_SIZE_CSS_CALC = `min(${ICON_SIZE_PX}px, calc(100% - ${ICON_PADDING_PX}px))`;
|
||||
export const MOTION_ICON_INITIAL = {
|
||||
opacity: 0,
|
||||
};
|
||||
export const MOTION_ICON_ANIMATE = {
|
||||
opacity: 1,
|
||||
transition: { duration: 0.1 },
|
||||
};
|
||||
export const MOTION_ICON_EXIT = {
|
||||
opacity: 0,
|
||||
transition: { duration: 0.1 },
|
||||
};
|
||||
export const ICON_CONTAINER_STYLES = {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
};
|
@ -0,0 +1,48 @@
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { ICON_HIGH_CUTOFF, ICON_LOW_CUTOFF } from './constants';
|
||||
|
||||
type Dimensions = {
|
||||
width: number;
|
||||
height: number;
|
||||
};
|
||||
|
||||
type UseAspectRatioPreviewStateArg = {
|
||||
width: number;
|
||||
height: number;
|
||||
containerSize?: Dimensions;
|
||||
};
|
||||
type UseAspectRatioPreviewState = (
|
||||
arg: UseAspectRatioPreviewStateArg
|
||||
) => Dimensions & { shouldShowIcon: boolean };
|
||||
|
||||
export const useAspectRatioPreviewState: UseAspectRatioPreviewState = ({
|
||||
width: _width,
|
||||
height: _height,
|
||||
containerSize,
|
||||
}) => {
|
||||
const dimensions = useMemo(() => {
|
||||
if (!containerSize) {
|
||||
return { width: 0, height: 0, shouldShowIcon: false };
|
||||
}
|
||||
|
||||
const aspectRatio = _width / _height;
|
||||
let width = _width;
|
||||
let height = _height;
|
||||
|
||||
if (_width > _height) {
|
||||
width = containerSize.width;
|
||||
height = width / aspectRatio;
|
||||
} else {
|
||||
height = containerSize.height;
|
||||
width = height * aspectRatio;
|
||||
}
|
||||
|
||||
const shouldShowIcon =
|
||||
aspectRatio < ICON_HIGH_CUTOFF && aspectRatio > ICON_LOW_CUTOFF;
|
||||
|
||||
return { width, height, shouldShowIcon };
|
||||
}, [_height, _width, containerSize]);
|
||||
|
||||
return dimensions;
|
||||
};
|
@ -0,0 +1,7 @@
|
||||
import type { IconType } from 'react-icons';
|
||||
|
||||
export type AspectRatioPreviewProps = {
|
||||
width: number;
|
||||
height: number;
|
||||
icon?: IconType;
|
||||
};
|
@ -1,22 +0,0 @@
|
||||
import { Box, Image } from '@chakra-ui/react';
|
||||
import InvokeAILogoImage from 'assets/images/logo.png';
|
||||
import { memo } from 'react';
|
||||
|
||||
const GreyscaleInvokeAIIcon = () => (
|
||||
<Box pos="relative" w={4} h={4}>
|
||||
<Image
|
||||
src={InvokeAILogoImage}
|
||||
alt="invoke-ai-logo"
|
||||
pos="absolute"
|
||||
top={-0.5}
|
||||
insetInlineStart={-0.5}
|
||||
w={5}
|
||||
h={5}
|
||||
minW={5}
|
||||
minH={5}
|
||||
filter="saturate(0)"
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
|
||||
export default memo(GreyscaleInvokeAIIcon);
|
@ -1,93 +0,0 @@
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogBody,
|
||||
AlertDialogContent,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogOverlay,
|
||||
forwardRef,
|
||||
useDisclosure,
|
||||
} from '@chakra-ui/react';
|
||||
import {
|
||||
cloneElement,
|
||||
memo,
|
||||
ReactElement,
|
||||
ReactNode,
|
||||
useCallback,
|
||||
useRef,
|
||||
} from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import IAIButton from './IAIButton';
|
||||
|
||||
type Props = {
|
||||
acceptButtonText?: string;
|
||||
acceptCallback: () => void;
|
||||
cancelButtonText?: string;
|
||||
cancelCallback?: () => void;
|
||||
children: ReactNode;
|
||||
title: string;
|
||||
triggerComponent: ReactElement;
|
||||
};
|
||||
|
||||
const IAIAlertDialog = forwardRef((props: Props, ref) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const {
|
||||
acceptButtonText = t('common.accept'),
|
||||
acceptCallback,
|
||||
cancelButtonText = t('common.cancel'),
|
||||
cancelCallback,
|
||||
children,
|
||||
title,
|
||||
triggerComponent,
|
||||
} = props;
|
||||
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const cancelRef = useRef<HTMLButtonElement | null>(null);
|
||||
|
||||
const handleAccept = useCallback(() => {
|
||||
acceptCallback();
|
||||
onClose();
|
||||
}, [acceptCallback, onClose]);
|
||||
|
||||
const handleCancel = useCallback(() => {
|
||||
cancelCallback && cancelCallback();
|
||||
onClose();
|
||||
}, [cancelCallback, onClose]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{cloneElement(triggerComponent, {
|
||||
onClick: onOpen,
|
||||
ref: ref,
|
||||
})}
|
||||
|
||||
<AlertDialog
|
||||
isOpen={isOpen}
|
||||
leastDestructiveRef={cancelRef}
|
||||
onClose={onClose}
|
||||
isCentered
|
||||
>
|
||||
<AlertDialogOverlay>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader fontSize="lg" fontWeight="bold">
|
||||
{title}
|
||||
</AlertDialogHeader>
|
||||
|
||||
<AlertDialogBody>{children}</AlertDialogBody>
|
||||
|
||||
<AlertDialogFooter>
|
||||
<IAIButton ref={cancelRef} onClick={handleCancel}>
|
||||
{cancelButtonText}
|
||||
</IAIButton>
|
||||
<IAIButton colorScheme="error" onClick={handleAccept} ml={3}>
|
||||
{acceptButtonText}
|
||||
</IAIButton>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialogOverlay>
|
||||
</AlertDialog>
|
||||
</>
|
||||
);
|
||||
});
|
||||
export default memo(IAIAlertDialog);
|
@ -1,43 +0,0 @@
|
||||
import {
|
||||
Button,
|
||||
ButtonProps,
|
||||
forwardRef,
|
||||
Tooltip,
|
||||
TooltipProps,
|
||||
} from '@chakra-ui/react';
|
||||
import { memo, ReactNode } from 'react';
|
||||
|
||||
export interface IAIButtonProps extends ButtonProps {
|
||||
tooltip?: TooltipProps['label'];
|
||||
tooltipProps?: Omit<TooltipProps, 'children' | 'label'>;
|
||||
isChecked?: boolean;
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
const IAIButton = forwardRef((props: IAIButtonProps, forwardedRef) => {
|
||||
const {
|
||||
children,
|
||||
tooltip = '',
|
||||
tooltipProps: { placement = 'top', hasArrow = true, ...tooltipProps } = {},
|
||||
isChecked,
|
||||
...rest
|
||||
} = props;
|
||||
return (
|
||||
<Tooltip
|
||||
label={tooltip}
|
||||
placement={placement}
|
||||
hasArrow={hasArrow}
|
||||
{...tooltipProps}
|
||||
>
|
||||
<Button
|
||||
ref={forwardedRef}
|
||||
colorScheme={isChecked ? 'accent' : 'base'}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
);
|
||||
});
|
||||
|
||||
export default memo(IAIButton);
|
@ -1,106 +0,0 @@
|
||||
import { ChevronUpIcon } from '@chakra-ui/icons';
|
||||
import {
|
||||
Box,
|
||||
Collapse,
|
||||
Flex,
|
||||
Spacer,
|
||||
Text,
|
||||
useColorMode,
|
||||
useDisclosure,
|
||||
} from '@chakra-ui/react';
|
||||
import { AnimatePresence, motion } from 'framer-motion';
|
||||
import { PropsWithChildren, memo } from 'react';
|
||||
import { mode } from 'theme/util/mode';
|
||||
|
||||
export type IAIToggleCollapseProps = PropsWithChildren & {
|
||||
label: string;
|
||||
activeLabel?: string;
|
||||
defaultIsOpen?: boolean;
|
||||
};
|
||||
|
||||
const IAICollapse = (props: IAIToggleCollapseProps) => {
|
||||
const { label, activeLabel, children, defaultIsOpen = false } = props;
|
||||
const { isOpen, onToggle } = useDisclosure({ defaultIsOpen });
|
||||
const { colorMode } = useColorMode();
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Flex
|
||||
onClick={onToggle}
|
||||
sx={{
|
||||
alignItems: 'center',
|
||||
p: 2,
|
||||
px: 4,
|
||||
gap: 2,
|
||||
borderTopRadius: 'base',
|
||||
borderBottomRadius: isOpen ? 0 : 'base',
|
||||
bg: mode('base.250', 'base.750')(colorMode),
|
||||
color: mode('base.900', 'base.100')(colorMode),
|
||||
_hover: {
|
||||
bg: mode('base.300', 'base.700')(colorMode),
|
||||
},
|
||||
fontSize: 'sm',
|
||||
fontWeight: 600,
|
||||
cursor: 'pointer',
|
||||
transitionProperty: 'common',
|
||||
transitionDuration: 'normal',
|
||||
userSelect: 'none',
|
||||
}}
|
||||
data-testid={`${label} collapsible`}
|
||||
>
|
||||
{label}
|
||||
<AnimatePresence>
|
||||
{activeLabel && (
|
||||
<motion.div
|
||||
key="statusText"
|
||||
initial={{
|
||||
opacity: 0,
|
||||
}}
|
||||
animate={{
|
||||
opacity: 1,
|
||||
transition: { duration: 0.1 },
|
||||
}}
|
||||
exit={{
|
||||
opacity: 0,
|
||||
transition: { duration: 0.1 },
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
sx={{ color: 'accent.500', _dark: { color: 'accent.300' } }}
|
||||
>
|
||||
{activeLabel}
|
||||
</Text>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
<Spacer />
|
||||
<ChevronUpIcon
|
||||
sx={{
|
||||
w: '1rem',
|
||||
h: '1rem',
|
||||
transform: isOpen ? 'rotate(0deg)' : 'rotate(180deg)',
|
||||
transitionProperty: 'common',
|
||||
transitionDuration: 'normal',
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
<Collapse in={isOpen} animateOpacity style={{ overflow: 'unset' }}>
|
||||
<Box
|
||||
sx={{
|
||||
p: 4,
|
||||
pb: 4,
|
||||
borderBottomRadius: 'base',
|
||||
bg: 'base.150',
|
||||
_dark: {
|
||||
bg: 'base.800',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
</Collapse>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(IAICollapse);
|
@ -1,8 +1,14 @@
|
||||
import { ChakraProps, Flex } from '@chakra-ui/react';
|
||||
import type { ChakraProps } from '@chakra-ui/react';
|
||||
import { Flex } from '@chakra-ui/react';
|
||||
import { memo, useCallback } from 'react';
|
||||
import { RgbaColorPicker } from 'react-colorful';
|
||||
import { ColorPickerBaseProps, RgbaColor } from 'react-colorful/dist/types';
|
||||
import IAINumberInput from './IAINumberInput';
|
||||
import type {
|
||||
ColorPickerBaseProps,
|
||||
RgbaColor,
|
||||
} from 'react-colorful/dist/types';
|
||||
|
||||
import { InvControl } from './InvControl/InvControl';
|
||||
import { InvNumberInput } from './InvNumberInput/InvNumberInput';
|
||||
|
||||
type IAIColorPickerProps = ColorPickerBaseProps<RgbaColor> & {
|
||||
withNumberInput?: boolean;
|
||||
@ -52,43 +58,46 @@ const IAIColorPicker = (props: IAIColorPickerProps) => {
|
||||
/>
|
||||
{withNumberInput && (
|
||||
<Flex>
|
||||
<IAINumberInput
|
||||
value={color.r}
|
||||
onChange={handleChangeR}
|
||||
min={0}
|
||||
max={255}
|
||||
step={1}
|
||||
label="Red"
|
||||
w={numberInputWidth}
|
||||
/>
|
||||
<IAINumberInput
|
||||
value={color.g}
|
||||
onChange={handleChangeG}
|
||||
min={0}
|
||||
max={255}
|
||||
step={1}
|
||||
label="Green"
|
||||
w={numberInputWidth}
|
||||
/>
|
||||
<IAINumberInput
|
||||
value={color.b}
|
||||
onChange={handleChangeB}
|
||||
min={0}
|
||||
max={255}
|
||||
step={1}
|
||||
label="Blue"
|
||||
w={numberInputWidth}
|
||||
/>
|
||||
<IAINumberInput
|
||||
value={color.a}
|
||||
onChange={handleChangeA}
|
||||
step={0.1}
|
||||
min={0}
|
||||
max={1}
|
||||
label="Alpha"
|
||||
w={numberInputWidth}
|
||||
isInteger={false}
|
||||
/>
|
||||
<InvControl label="Red">
|
||||
<InvNumberInput
|
||||
value={color.r}
|
||||
onChange={handleChangeR}
|
||||
min={0}
|
||||
max={255}
|
||||
step={1}
|
||||
w={numberInputWidth}
|
||||
/>
|
||||
</InvControl>
|
||||
<InvControl label="Green">
|
||||
<InvNumberInput
|
||||
value={color.g}
|
||||
onChange={handleChangeG}
|
||||
min={0}
|
||||
max={255}
|
||||
step={1}
|
||||
w={numberInputWidth}
|
||||
/>
|
||||
</InvControl>
|
||||
<InvControl label="Blue">
|
||||
<InvNumberInput
|
||||
value={color.b}
|
||||
onChange={handleChangeB}
|
||||
min={0}
|
||||
max={255}
|
||||
step={1}
|
||||
w={numberInputWidth}
|
||||
/>
|
||||
</InvControl>
|
||||
<InvControl label="Alpha">
|
||||
<InvNumberInput
|
||||
value={color.a}
|
||||
onChange={handleChangeA}
|
||||
step={0.1}
|
||||
min={0}
|
||||
max={1}
|
||||
w={numberInputWidth}
|
||||
/>
|
||||
</InvControl>
|
||||
</Flex>
|
||||
)}
|
||||
</Flex>
|
||||
|
@ -1,37 +1,29 @@
|
||||
import {
|
||||
ChakraProps,
|
||||
Flex,
|
||||
FlexProps,
|
||||
Icon,
|
||||
Image,
|
||||
useColorMode,
|
||||
} from '@chakra-ui/react';
|
||||
import type { ChakraProps, FlexProps } from '@chakra-ui/react';
|
||||
import { Flex, Icon, Image } from '@chakra-ui/react';
|
||||
import {
|
||||
IAILoadingImageFallback,
|
||||
IAINoContentFallback,
|
||||
} from 'common/components/IAIImageFallback';
|
||||
import ImageMetadataOverlay from 'common/components/ImageMetadataOverlay';
|
||||
import { useImageUploadButton } from 'common/hooks/useImageUploadButton';
|
||||
import type {
|
||||
TypesafeDraggableData,
|
||||
TypesafeDroppableData,
|
||||
} from 'features/dnd/types';
|
||||
import ImageContextMenu from 'features/gallery/components/ImageContextMenu/ImageContextMenu';
|
||||
import {
|
||||
import type {
|
||||
MouseEvent,
|
||||
ReactElement,
|
||||
ReactNode,
|
||||
SyntheticEvent,
|
||||
memo,
|
||||
useCallback,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { memo, useCallback, useState } from 'react';
|
||||
import { FaImage, FaUpload } from 'react-icons/fa';
|
||||
import { ImageDTO, PostUploadAction } from 'services/api/types';
|
||||
import { mode } from 'theme/util/mode';
|
||||
import type { ImageDTO, PostUploadAction } from 'services/api/types';
|
||||
|
||||
import IAIDraggable from './IAIDraggable';
|
||||
import IAIDroppable from './IAIDroppable';
|
||||
import SelectionOverlay from './SelectionOverlay';
|
||||
import {
|
||||
TypesafeDraggableData,
|
||||
TypesafeDroppableData,
|
||||
} from 'features/dnd/types';
|
||||
|
||||
const defaultUploadElement = (
|
||||
<Icon
|
||||
@ -98,7 +90,6 @@ const IAIDndImage = (props: IAIDndImageProps) => {
|
||||
dataTestId,
|
||||
} = props;
|
||||
|
||||
const { colorMode } = useColorMode();
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const handleMouseOver = useCallback(
|
||||
(e: MouseEvent<HTMLDivElement>) => {
|
||||
@ -128,10 +119,10 @@ const IAIDndImage = (props: IAIDndImageProps) => {
|
||||
? {}
|
||||
: {
|
||||
cursor: 'pointer',
|
||||
bg: mode('base.200', 'base.700')(colorMode),
|
||||
bg: 'base.700',
|
||||
_hover: {
|
||||
bg: mode('base.300', 'base.650')(colorMode),
|
||||
color: mode('base.500', 'base.300')(colorMode),
|
||||
bg: 'base.650',
|
||||
color: 'base.300',
|
||||
},
|
||||
};
|
||||
|
||||
@ -208,7 +199,7 @@ const IAIDndImage = (props: IAIDndImageProps) => {
|
||||
borderRadius: 'base',
|
||||
transitionProperty: 'common',
|
||||
transitionDuration: '0.1s',
|
||||
color: mode('base.500', 'base.500')(colorMode),
|
||||
color: 'base.500',
|
||||
...uploadButtonStyles,
|
||||
}}
|
||||
{...getUploadButtonProps()}
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { SystemStyleObject, useColorModeValue } from '@chakra-ui/react';
|
||||
import { MouseEvent, ReactElement, memo } from 'react';
|
||||
import IAIIconButton from './IAIIconButton';
|
||||
import type { SystemStyleObject } from '@chakra-ui/react';
|
||||
import type { MouseEvent, ReactElement } from 'react';
|
||||
import { memo } from 'react';
|
||||
|
||||
import { InvIconButton } from './InvIconButton/InvIconButton';
|
||||
|
||||
type Props = {
|
||||
onClick: (event: MouseEvent<HTMLButtonElement>) => void;
|
||||
@ -12,12 +14,8 @@ type Props = {
|
||||
const IAIDndImageIcon = (props: Props) => {
|
||||
const { onClick, tooltip, icon, styleOverrides } = props;
|
||||
|
||||
const resetIconShadow = useColorModeValue(
|
||||
`drop-shadow(0px 0px 0.1rem var(--invokeai-colors-base-600))`,
|
||||
`drop-shadow(0px 0px 0.1rem var(--invokeai-colors-base-800))`
|
||||
);
|
||||
return (
|
||||
<IAIIconButton
|
||||
<InvIconButton
|
||||
onClick={onClick}
|
||||
aria-label={tooltip}
|
||||
tooltip={tooltip}
|
||||
@ -35,7 +33,7 @@ const IAIDndImageIcon = (props: Props) => {
|
||||
transitionDuration: 'normal',
|
||||
fill: 'base.100',
|
||||
_hover: { fill: 'base.50' },
|
||||
filter: resetIconShadow,
|
||||
filter: 'drop-shadow(0px 0px 0.1rem var(--invokeai-colors-base-800))',
|
||||
},
|
||||
...styleOverrides,
|
||||
}}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { Box, BoxProps } from '@chakra-ui/react';
|
||||
import type { BoxProps } from '@chakra-ui/react';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { useDraggableTypesafe } from 'features/dnd/hooks/typesafeHooks';
|
||||
import { TypesafeDraggableData } from 'features/dnd/types';
|
||||
import type { TypesafeDraggableData } from 'features/dnd/types';
|
||||
import { memo, useRef } from 'react';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user