From 446d87516a96ced930ff222f0f4ece1bcf0f332c Mon Sep 17 00:00:00 2001 From: Millun Atluri Date: Wed, 19 Jul 2023 14:34:03 +1000 Subject: [PATCH 01/13] * Updated contributiion guide * Updated nav to be in new order prioritizing more commonuly used tabs * Added set nav in mkdocs.yaml --- docs/contributing/CONTRIBUTING.md | 53 +++++------- .../contribution_guides/development.md | 80 +++++++++++++++++++ .../contributingToFrontend.md | 65 +++++++++++++++ .../development_guides/contributingToNodes.md | 1 + .../contribution_guides/documentation.md | 13 +++ .../contribution_guides/translation.md | 19 +++++ .../contribution_guides/tutorials.md | 11 +++ mkdocs.yml | 60 +++++++++++++- 8 files changed, 270 insertions(+), 32 deletions(-) create mode 100644 docs/contributing/contribution_guides/development.md create mode 100644 docs/contributing/contribution_guides/development_guides/contributingToFrontend.md create mode 100644 docs/contributing/contribution_guides/development_guides/contributingToNodes.md create mode 100644 docs/contributing/contribution_guides/documentation.md create mode 100644 docs/contributing/contribution_guides/translation.md create mode 100644 docs/contributing/contribution_guides/tutorials.md diff --git a/docs/contributing/CONTRIBUTING.md b/docs/contributing/CONTRIBUTING.md index 3360faed70..8f0b2d6134 100644 --- a/docs/contributing/CONTRIBUTING.md +++ b/docs/contributing/CONTRIBUTING.md @@ -1,53 +1,44 @@ +# How to Contribute + ## Welcome to Invoke AI - -We're thrilled to have you here and we're excited for you to contribute. - Invoke AI originated as a project built by the community, and that vision carries forward today as we aim to build the best pro-grade tools available. We work together to incorporate the latest in AI/ML research, making these tools available in over 20 languages to artists and creatives around the world as part of our fully permissive OSS project designed for individual users to self-host and use. -Here are some guidelines to help you get started: -### Technical Prerequisites +## Contributing to Invoke AI +Anyone who wishes to contribute to InvokeAI, whether features, bug fixes, code cleanup, testing, code reviews, documentation or translation is very much encouraged to do so. -Front-end: You'll need a working knowledge of React and TypeScript. +To join, just raise your hand on the InvokeAI Discord server (#dev-chat) or the GitHub discussion board. -Back-end: Depending on the scope of your contribution, you may need to know SQLite, FastAPI, Python, and Socketio. Also, a good majority of the backend logic involved in processing images is built in a modular way using a concept called "Nodes", which are isolated functions that carry out individual, discrete operations. This design allows for easy contributions of novel pipelines and capabilities. +### Areas of contribution: -### How to Submit Contributions +#### Development +If you’d like to help with development, please see our [development guide](docs/contributing/.contribution_guides/development.md). If you’re unfamiliar with contributing to open source projects, there is a tutorial contained within the development guide. -To start contributing, please follow these steps: +#### Documentation +If you’d like to help with documentation, please see our [documentation guide](docs/contributing/.contribution_guides/documenation.md). -1. Familiarize yourself with our roadmap and open projects to see where your skills and interests align. These documents can serve as a source of inspiration. -2. Open a Pull Request (PR) with a clear description of the feature you're adding or the problem you're solving. Make sure your contribution aligns with the project's vision. -3. Adhere to general best practices. This includes assuming interoperability with other nodes, keeping the scope of your functions as small as possible, and organizing your code according to our architecture documents. +#### Translation +If you'd like to help with translation, please see our [translation guide](docs/contributing/.contribution_guides/translation.md). -### Types of Contributions We're Looking For +#### Tutorials +Please reach out to @imic or @hipsterusername on [Discord](https://discord.gg/ZmtBAhwWhy) to help create tutorials for InvokeAI. -We welcome all contributions that improve the project. Right now, we're especially looking for: +We hope you enjoy using our software as much as we enjoy creating it, and we hope that some of those of you who are reading this will elect to become part of our contributor community. -1. Quality of life (QOL) enhancements on the front-end. -2. New backend capabilities added through nodes. -3. Incorporating additional optimizations from the broader open-source software community. -### Communication and Decision-making Process +### Contributors -Project maintainers and code owners review PRs to ensure they align with the project's goals. They may provide design or architectural guidance, suggestions on user experience, or provide more significant feedback on the contribution itself. Expect to receive feedback on your submissions, and don't hesitate to ask questions or propose changes. +This project is a combined effort of dedicated people from across the world. [Check out the list of all these amazing people](https://invoke-ai.github.io/InvokeAI/other/CONTRIBUTORS/). We thank them for their time, hard work and effort. -For more robust discussions, or if you're planning to add capabilities not currently listed on our roadmap, please reach out to us on our Discord server. That way, we can ensure your proposed contribution aligns with the project's direction before you start writing code. +### Code of Conduct -### Code of Conduct and Contribution Expectations +The InvokeAI community is a welcoming place, and we want your help in maintaining that. Please review our Code of Conduct **** to learn more. -We want everyone in our community to have a positive experience. To facilitate this, we've established a code of conduct and a statement of values that we expect all contributors to adhere to. Please take a moment to review these documents—they're essential to maintaining a respectful and inclusive environment. +### Support -By making a contribution to this project, you certify that: +For support, please use this repository's [GitHub Issues](https://github.com/invoke-ai/InvokeAI/issues), or join the [Discord](https://discord.gg/ZmtBAhwWhy). -1. The contribution was created in whole or in part by you and you have the right to submit it under the open-source license indicated in this project’s GitHub repository; or -2. The contribution is based upon previous work that, to the best of your knowledge, is covered under an appropriate open-source license and you have the right under that license to submit that work with modifications, whether created in whole or in part by you, under the same open-source license (unless you are permitted to submit under a different license); or -3. The contribution was provided directly to you by some other person who certified (1) or (2) and you have not modified it; or -4. You understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information you submit with it, including your sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open-source license(s) involved. - -This disclaimer is not a license and does not grant any rights or permissions. You must obtain necessary permissions and licenses, including from third parties, before contributing to this project. - -This disclaimer is provided "as is" without warranty of any kind, whether expressed or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, or non-infringement. In no event shall the authors or copyright holders be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the contribution or the use or other dealings in the contribution. +Original portions of the software are Copyright (c) 2023 by respective contributors. --- diff --git a/docs/contributing/contribution_guides/development.md b/docs/contributing/contribution_guides/development.md new file mode 100644 index 0000000000..33e565606f --- /dev/null +++ b/docs/contributing/contribution_guides/development.md @@ -0,0 +1,80 @@ +# Development + +## **What do I need to know to help?** + +If you are looking to help to with a code contribution, InvokeAI uses several different technologies under the hood: Python (Pydantic, FastAPI, diffusers) and Typescript (React, Redux Toolkit, ChakraUI, Mantine, Konva). Familiarity with StableDiffusion and image generation concepts is helpful, but not essential. + +For more information, please review our area specific documentation: + +* #### [InvokeAI Architecure](../ARCHITECTURE.md) +* #### [Frontend Documentation](development_guides/contributingToFrontend.md) +* #### [Node Documentation](../INVOCATIONS.md) + +If you don't feel ready to make a code contribution yet, no problem! You can also help out in other ways, such as [documentation](documentation.md) or [translation](translation.md). + +There are two paths to making a development contribution: + +1. Choosing an open issue to address. Open issues can be found in the [Issues](https://github.com/invoke-ai/InvokeAI/issues?q=is%3Aissue+is%3Aopen) section of the InvokeAI repository. These are tagged by the issue type (bug, enhancement, etc.) along with the “good first issues” tag denoting if they are suitable for first time contributors. + 1. Additional items can be found on our roadmap <******************************link to roadmap>******************************. The roadmap is organized in terms of priority, and contains features of varying size and complexity. If there is an inflight item you’d like to help with, reach out to the contributor assigned to the item to see how you can help. +2. Opening a new issue or feature to add. **Please make sure you have searched through existing issues before creating new ones.** + +*Regardless of what you choose, please post in the [#dev-chat](https://discord.com/channels/1020123559063990373/1049495067846524939) channel of the Discord before you start development in order to confirm that the issue or feature is aligned with the current direction of the project. We value our contributors time and effort and want to ensure that no one’s time is being misspent.* + +## **How do I make a contribution?** + +Never made an open source contribution before? Wondering how contributions work in our project? Here's a quick rundown! + +1. Find a [good first issue](https://github.com/invoke-ai/InvokeAI/contribute) that you are interested in addressing or a feature that you would like to add. Then, reach out to our team in the [#dev-chat](https://discord.com/channels/1020123559063990373/1049495067846524939) channel of the Discord to ensure you are setup for success. +2. Fork the [InvokeAI](https://github.com/invoke-ai/InvokeAI) repository to your GitHub profile. This means that you will have a copy of the repository under **your-GitHub-username/InvokeAI**. +3. Clone the repository to your local machine using: + +```bash +**git clone** https://github.com/your-GitHub-username/InvokeAI.git +``` + +1. Create a new branch for your fix using: + +```bash +**git checkout -b branch-name-here** +``` + +1. Make the appropriate changes for the issue you are trying to address or the feature that you want to add. +2. Add the file contents of the changed files to the "snapshot" git uses to manage the state of the project, also known as the index: + +```bash +**git add insert-paths-of-changed-files-here** +``` + +1. Store the contents of the index with a descriptive message. + +```bash +**git commit -m "Insert a short message of the changes made here"** +``` + +1. Push the changes to the remote repository using + +```markdown +**git push origin branch-name-here** +``` + +1. Submit a pull request to the **main** branch of the InvokeAI repository. +2. Title the pull request with a short description of the changes made and the issue or bug number associated with your change. For example, you can title an issue like so "Added more log outputting to resolve #1234". +3. In the description of the pull request, explain the changes that you made, any issues you think exist with the pull request you made, and any questions you have for the maintainer. It's OK if your pull request is not perfect (no pull request is), the reviewer will be able to help you fix any problems and improve it! +4. Wait for the pull request to be reviewed by other collaborators. +5. Make changes to the pull request if the reviewer(s) recommend them. +6. Celebrate your success after your pull request is merged! + +If you’d like to learn more about contributing to Open Source projects, here is a [Getting Started Guide](https://opensource.com/article/19/7/create-pull-request-github). + +## **Where can I go for help?** + +If you need help, you can ask questions in the [#dev-chat](https://discord.com/channels/1020123559063990373/1049495067846524939) channel of the Discord. + +For frontend related work, **@pyschedelicious** is the best person to reach out to. + +For backend related work, please reach out to **@pyschedelicious, @blessedcoolant** or **@lstein**. + +## **What does the Code of Conduct mean for me?** + +Our [Code of Conduct](../../CODE_OF_CONDUCT.md) means that you are responsible for treating everyone on the project with respect and courtesy regardless of their identity. If you are the victim of any inappropriate behavior or comments as described in our Code of Conduct, we are here for you and will do the best to ensure that the abuser is reprimanded appropriately, per our code. + diff --git a/docs/contributing/contribution_guides/development_guides/contributingToFrontend.md b/docs/contributing/contribution_guides/development_guides/contributingToFrontend.md new file mode 100644 index 0000000000..ee0a4ef1cb --- /dev/null +++ b/docs/contributing/contribution_guides/development_guides/contributingToFrontend.md @@ -0,0 +1,65 @@ +# Contributing to the Frontend + +# InvokeAI Web UI + +- [InvokeAI Web UI](https://github.com/invoke-ai/InvokeAI/tree/main/invokeai/frontend/web/docs#invokeai-web-ui) + - [Stack](https://github.com/invoke-ai/InvokeAI/tree/main/invokeai/frontend/web/docs#stack) + - [Contributing](https://github.com/invoke-ai/InvokeAI/tree/main/invokeai/frontend/web/docs#contributing) + - [Dev Environment](https://github.com/invoke-ai/InvokeAI/tree/main/invokeai/frontend/web/docs#dev-environment) + - [Production builds](https://github.com/invoke-ai/InvokeAI/tree/main/invokeai/frontend/web/docs#production-builds) + +The UI is a fairly straightforward Typescript React app, with the Unified Canvas being more complex. + +Code is located in `invokeai/frontend/web/` for review. + +## Stack + +State management is Redux via [Redux Toolkit](https://github.com/reduxjs/redux-toolkit). We lean heavily on RTK: + +- `createAsyncThunk` for HTTP requests +- `createEntityAdapter` for fetching images and models +- `createListenerMiddleware` for workflows + +The API client and associated types are generated from the OpenAPI schema. See API_CLIENT.md. + +Communication with server is a mix of HTTP and [socket.io](https://github.com/socketio/socket.io-client) (with a simple socket.io redux middleware to help). + +[Chakra-UI](https://github.com/chakra-ui/chakra-ui) & Mantine for components and styling. + +[Konva](https://github.com/konvajs/react-konva) for the canvas, but we are pushing the limits of what is feasible with it (and HTML canvas in general). We plan to rebuild it with [PixiJS](https://github.com/pixijs/pixijs) to take advantage of WebGL's improved raster handling. + +[Vite](https://vitejs.dev/) for bundling. + +Localisation is via [i18next](https://github.com/i18next/react-i18next), but translation happens on our [Weblate](https://hosted.weblate.org/engage/invokeai/) project. Only the English source strings should be changed on this repo. + +## Contributing + +Thanks for your interest in contributing to the InvokeAI Web UI! + +We encourage you to ping @psychedelicious and @blessedcoolant on [Discord](https://discord.gg/ZmtBAhwWhy) if you want to contribute, just to touch base and ensure your work doesn't conflict with anything else going on. The project is very active. + +### Dev Environment + +Install [node](https://nodejs.org/en/download/) and [yarn classic](https://classic.yarnpkg.com/lang/en/). + +From `invokeai/frontend/web/` run `yarn install` to get everything set up. + +Start everything in dev mode: + +1. Start the dev server: `yarn dev` +2. Start the InvokeAI Nodes backend: `python scripts/invokeai-web.py # run from the repo root` +3. Point your browser to the dev server address e.g. [http://localhost:5173/](http://localhost:5173/) + +### VSCode Remote Dev + +We've noticed an intermittent issue with the VSCode Remote Dev port forwarding. If you use this feature of VSCode, you may intermittently click the Invoke button and then get nothing until the request times out. Suggest disabling the IDE's port forwarding feature and doing it manually via SSH: + +`ssh -L 9090:localhost:9090 -L 5173:localhost:5173 user@host` + +### Production builds + +For a number of technical and logistical reasons, we need to commit UI build artefacts to the repo. + +If you submit a PR, there is a good chance we will ask you to include a separate commit with a build of the app. + +To build for production, run `yarn build`. \ No newline at end of file diff --git a/docs/contributing/contribution_guides/development_guides/contributingToNodes.md b/docs/contributing/contribution_guides/development_guides/contributingToNodes.md new file mode 100644 index 0000000000..f5f0962bd0 --- /dev/null +++ b/docs/contributing/contribution_guides/development_guides/contributingToNodes.md @@ -0,0 +1 @@ +# Contributing to Nodes \ No newline at end of file diff --git a/docs/contributing/contribution_guides/documentation.md b/docs/contributing/contribution_guides/documentation.md new file mode 100644 index 0000000000..1a0ec03cf7 --- /dev/null +++ b/docs/contributing/contribution_guides/documentation.md @@ -0,0 +1,13 @@ +# Documentation + +Documentation is an important part of any open source project. It provides a clear and concise way to communicate how the software works, how to use it, and how to troubleshoot issues. Without proper documentation, it can be difficult for users to understand the purpose and functionality of the project. + +## Contributing + +All documentation is maintained in the InvokeAI GitHub repository. If you come across documentation that is out of date or incorrect, please submit a pull request with the necessary changes. + +When updating or creating documentation, please keep in mind InvokeAI is a tool for everyone, not just those who have familiarity with generative art. + +## Help & Questions + +Please ping @imic1 or @hipsterusername in the [Discord](https://discord.com/channels/1020123559063990373/1049495067846524939) if you have any questions. \ No newline at end of file diff --git a/docs/contributing/contribution_guides/translation.md b/docs/contributing/contribution_guides/translation.md new file mode 100644 index 0000000000..669e403346 --- /dev/null +++ b/docs/contributing/contribution_guides/translation.md @@ -0,0 +1,19 @@ +# Translation + +InvokeAI uses [Weblate](https://weblate.org/) for translation. Weblate is a FOSS project providing a scalable translation service. Weblate automates the tedious parts of managing translation of a growing project, and the service is generously provided at no cost to FOSS projects like InvokeAI. + +## Contributing + +If you'd like to contribute by adding or updating a translation, please visit our [Weblate project](https://hosted.weblate.org/engage/invokeai/). You'll need to sign in with your GitHub account (a number of other accounts are supported, including Google). + +Once signed in, select a language and then the Web UI component. From here you can Browse and Translate strings from English to your chosen language. Zen mode offers a simpler translation experience. + +Your changes will be attributed to you in the automated PR process; you don't need to do anything else. + +## Help & Questions + +Please check Weblate's [documentation](https://docs.weblate.org/en/latest/index.html) or ping @Harvestor on [Discord](https://discord.com/channels/1020123559063990373/1049495067846524939) if you have any questions. + +## Thanks + +Thanks to the InvokeAI community for their efforts to translate the project! \ No newline at end of file diff --git a/docs/contributing/contribution_guides/tutorials.md b/docs/contributing/contribution_guides/tutorials.md new file mode 100644 index 0000000000..0d550e7023 --- /dev/null +++ b/docs/contributing/contribution_guides/tutorials.md @@ -0,0 +1,11 @@ +# Tutorials + +Tutorials help new & existing users expand their abilty to use InvokeAI to the full extent of our features and services. + +Currently, we have a set of tutorials available on our [YouTube channel](https://www.youtube.com/@invokeai), but as InvokeAI continues to evolve with new updates, we want to ensure that we are giving our users the resources they need to succeed. + +Tutorials can be in the form of videos or article walkthroughs on a subject of your choice. We recommend focusing tutorials on the key image generation methods, or on a specific component within one of the image generation methods. + +## Contributing + +Please reach out to @imic or @hipsterusername on [Discord](https://discord.gg/ZmtBAhwWhy) to help create tutorials for InvokeAI. \ No newline at end of file diff --git a/mkdocs.yml b/mkdocs.yml index ebd9ec0acf..cf1daaa779 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -12,7 +12,7 @@ repo_url: 'https://github.com/invoke-ai/InvokeAI' edit_uri: edit/main/docs/ # Copyright -copyright: Copyright © 2022 InvokeAI Team +copyright: Copyright © 2023 InvokeAI Team # Configuration theme: @@ -35,8 +35,11 @@ theme: features: - navigation.instant - navigation.tabs + - navigation.tabs.sticky - navigation.top - navigation.tracking + - navigation.indexes + - navigation.path - search.highlight - search.suggest - toc.integrate @@ -95,3 +98,58 @@ plugins: 'installation/INSTALL_DOCKER.md': 'installation/040_INSTALL_DOCKER.md' 'installation/INSTALLING_MODELS.md': 'installation/050_INSTALLING_MODELS.md' 'installation/INSTALL_PATCHMATCH.md': 'installation/060_INSTALL_PATCHMATCH.md' + +nav: + - Home: 'index.md' + - Installation: + - Overview: 'installation/index.md' + - Installing with the Automated Installer: 'installation/010_INSTALL_AUTOMATED.md' + - Installing manually: 'installation/010_INSTALL_AUTOMATED.md' + - NVIDIA Cuda / AMD ROCm: 'installation/030_INSTALL_CUDA_AND_ROCM.md' + - Installing with Docker: 'installation/040_INSTALL_DOCKER.md' + - Installing Models: 'installation/040_INSTALL_DOCKER.md' + - Installing PyPatchMatch: 'installation/060_INSTALL_PATCHMATCH.md' + - Installing xFormers: 'installation/070_INSTALL_XFORMERS.md' + - Developers Documentation: 'installation/Developers_documentation/BUILDING_BINARY_INSTALLERS.md' + - Deprecated Documentation: + - Binary Installer: 'installation/deprecated_documentation/INSTALL_BINARY.md' + - Runninng InvokeAI on Google Colab: 'installation/deprecated_documentation/INSTALL_JUPYTER.md' + - Manual Installation on Linux: 'installation/deprecated_documentation/INSTALL_LINUX.md' + - Manual Installation on macOS: 'installation/deprecated_documentation/INSTALL_macOS.md' + - Manual Installation on Windows: 'installation/deprecated_documentation/INSTALL_WINDOWS.md' + - Installing Invoke with pip: 'installation/deprecated_documentation/INSTALL_PCP.md' + - Source Installer: 'installation/deprecated_documentation/INSTALL_SOURCE.md' + - Features: + - Overview: 'features/index.md' + - Concepts: 'features/CONCEPTS.md' + - Configuration: 'features/CONFIGURATION.md' + - ControlNet: 'features/CONTROLNET.md' + - Image-to-Image: 'features/IMG2IMG.md' + - Controlling Logging: 'features/LOGGING.md' + - Model Mergeing: 'features/MODEL_MERGING.md' + - Nodes Editor (Experimental): 'features/NODES.md' + - NSFW Checker: 'features/NSFW.md' + - Postprocessing: 'features/POSTPROCESS.md' + - Prompting Features: 'features/PROMPTS.md' + - Training: 'features/TRAINING.md' + - Unified Canvas: 'features/UNIFIED_CANVAS.md' + - Variations: 'features/VARIATIONS.md' + - InvokeAI Web Server: 'features/WEB.md' + - WebUI Hotkeys: "features/WEBUIHOTKEYS.md" + - Other: 'features/OTHER.md' + - Contributing: + - How to Contribute: 'contributing/CONTRIBUTING.md' + - Development: 'contributing/contribution_guides/development.md' + - Documentation: 'contributing/contribution_guides/documentation.md' + - Translation: 'contributing/contribution_guides/translation.md' + - Tutorials: 'contributing/contribution_guides/tutorials.md' + - Changelog: 'CHANGELOG.md' + - Deprecated: + - Command Line Interface: 'deprecated/CLI.md' + - Embiggen: 'deprecated/EMBIGGEN.md' + - Inpainting: 'deprecated/INPAINTING.MD' + - Outpainting: 'deprecated/OUTPAINTING.MD' + - Help: + - Sampler Convergence: 'help/SAMPLER_CONVERGENCE.md' + + From ff74370eda53f920ed6f25af9a04f0296048a93d Mon Sep 17 00:00:00 2001 From: Millun Atluri Date: Wed, 19 Jul 2023 15:39:29 +1000 Subject: [PATCH 02/13] =?UTF-8?q?=E2=80=A2=20Updated=20best=20practices=20?= =?UTF-8?q?=E2=80=A2=20Updated=20index=20with=20new=20contribution=20guide?= =?UTF-8?q?=20link?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/contributing/CONTRIBUTING.md | 6 ++--- .../contribution_guides/development.md | 25 +++++++++++++------ docs/index.md | 10 +++----- mkdocs.yml | 1 + 4 files changed, 24 insertions(+), 18 deletions(-) diff --git a/docs/contributing/CONTRIBUTING.md b/docs/contributing/CONTRIBUTING.md index 8f0b2d6134..fe18345ec9 100644 --- a/docs/contributing/CONTRIBUTING.md +++ b/docs/contributing/CONTRIBUTING.md @@ -12,10 +12,10 @@ To join, just raise your hand on the InvokeAI Discord server (#dev-chat) or the ### Areas of contribution: #### Development -If you’d like to help with development, please see our [development guide](docs/contributing/.contribution_guides/development.md). If you’re unfamiliar with contributing to open source projects, there is a tutorial contained within the development guide. +If you’d like to help with development, please see our [development guide](contribution_guides/development.md). If you’re unfamiliar with contributing to open source projects, there is a tutorial contained within the development guide. #### Documentation -If you’d like to help with documentation, please see our [documentation guide](docs/contributing/.contribution_guides/documenation.md). +If you’d like to help with documentation, please see our [documentation guide](contribution_guides/documenation.md). #### Translation If you'd like to help with translation, please see our [translation guide](docs/contributing/.contribution_guides/translation.md). @@ -32,7 +32,7 @@ This project is a combined effort of dedicated people from across the world. [C ### Code of Conduct -The InvokeAI community is a welcoming place, and we want your help in maintaining that. Please review our Code of Conduct **** to learn more. +The InvokeAI community is a welcoming place, and we want your help in maintaining that. Please review our [Code of Conduct](../../CODE_OF_CONDUCT.md) to learn more. ### Support diff --git a/docs/contributing/contribution_guides/development.md b/docs/contributing/contribution_guides/development.md index 33e565606f..6c42efd051 100644 --- a/docs/contributing/contribution_guides/development.md +++ b/docs/contributing/contribution_guides/development.md @@ -9,6 +9,7 @@ For more information, please review our area specific documentation: * #### [InvokeAI Architecure](../ARCHITECTURE.md) * #### [Frontend Documentation](development_guides/contributingToFrontend.md) * #### [Node Documentation](../INVOCATIONS.md) +* #### [Local Development](../LOCAL_DEVELOPMENT.md) If you don't feel ready to make a code contribution yet, no problem! You can also help out in other ways, such as [documentation](documentation.md) or [translation](translation.md). @@ -20,44 +21,52 @@ There are two paths to making a development contribution: *Regardless of what you choose, please post in the [#dev-chat](https://discord.com/channels/1020123559063990373/1049495067846524939) channel of the Discord before you start development in order to confirm that the issue or feature is aligned with the current direction of the project. We value our contributors time and effort and want to ensure that no one’s time is being misspent.* +## Best Practices: +* Keep your pull requests small. Smaller pull requests are more likely to be accepted and merged +* Comments! Commenting your code helps reviwers easily understand your contribution +* Use Python and Typescript’s typing systems, and consider using an editor with [LSP](https://microsoft.github.io/language-server-protocol/) support to streamline development +* Make all communications public. This ensure knowledge is shared with the whole community + ## **How do I make a contribution?** Never made an open source contribution before? Wondering how contributions work in our project? Here's a quick rundown! -1. Find a [good first issue](https://github.com/invoke-ai/InvokeAI/contribute) that you are interested in addressing or a feature that you would like to add. Then, reach out to our team in the [#dev-chat](https://discord.com/channels/1020123559063990373/1049495067846524939) channel of the Discord to ensure you are setup for success. +Before starting these steps, ensure you have your local environment [configured for development](../LOCAL_DEVELOPMENT.md). + +1. Find a [good first issue](https://github.com/invoke-ai/InvokeAI/contribute) that you are interested in addressing or a feature that you would like to add. Then, reach out to our team in the [#dev-chat](https://discord.com/channels/1020123559063990373/1049495067846524939) channel of the Discord to ensure you are setup for success. 2. Fork the [InvokeAI](https://github.com/invoke-ai/InvokeAI) repository to your GitHub profile. This means that you will have a copy of the repository under **your-GitHub-username/InvokeAI**. 3. Clone the repository to your local machine using: ```bash -**git clone** https://github.com/your-GitHub-username/InvokeAI.git +git clone https://github.com/your-GitHub-username/InvokeAI.git ``` 1. Create a new branch for your fix using: ```bash -**git checkout -b branch-name-here** +git checkout -b branch-name-here ``` 1. Make the appropriate changes for the issue you are trying to address or the feature that you want to add. 2. Add the file contents of the changed files to the "snapshot" git uses to manage the state of the project, also known as the index: ```bash -**git add insert-paths-of-changed-files-here** +git add insert-paths-of-changed-files-here ``` 1. Store the contents of the index with a descriptive message. ```bash -**git commit -m "Insert a short message of the changes made here"** +git commit -m "Insert a short message of the changes made here" ``` 1. Push the changes to the remote repository using ```markdown -**git push origin branch-name-here** +git push origin branch-name-here ``` -1. Submit a pull request to the **main** branch of the InvokeAI repository. +1. Submit a pull request to the **development** branch of the InvokeAI repository. 2. Title the pull request with a short description of the changes made and the issue or bug number associated with your change. For example, you can title an issue like so "Added more log outputting to resolve #1234". 3. In the description of the pull request, explain the changes that you made, any issues you think exist with the pull request you made, and any questions you have for the maintainer. It's OK if your pull request is not perfect (no pull request is), the reviewer will be able to help you fix any problems and improve it! 4. Wait for the pull request to be reviewed by other collaborators. @@ -76,5 +85,5 @@ For backend related work, please reach out to **@pyschedelicious, @blessedcoolan ## **What does the Code of Conduct mean for me?** -Our [Code of Conduct](../../CODE_OF_CONDUCT.md) means that you are responsible for treating everyone on the project with respect and courtesy regardless of their identity. If you are the victim of any inappropriate behavior or comments as described in our Code of Conduct, we are here for you and will do the best to ensure that the abuser is reprimanded appropriately, per our code. +Our [Code of Conduct](CODE_OF_CONDUCT.md) means that you are responsible for treating everyone on the project with respect and courtesy regardless of their identity. If you are the victim of any inappropriate behavior or comments as described in our Code of Conduct, we are here for you and will do the best to ensure that the abuser is reprimanded appropriately, per our code. diff --git a/docs/index.md b/docs/index.md index 229cb6cf05..c2085c9f78 100644 --- a/docs/index.md +++ b/docs/index.md @@ -221,14 +221,10 @@ get solutions for common installation problems and other issues. Anyone who wishes to contribute to this project, whether documentation, features, bug fixes, code cleanup, testing, or code reviews, is very much -encouraged to do so. If you are unfamiliar with how to contribute to GitHub -projects, here is a -[Getting Started Guide](https://opensource.com/article/19/7/create-pull-request-github). +encouraged to do so. -A full set of contribution guidelines, along with templates, are in progress, -but for now the most important thing is to **make your pull request against the -"development" branch**, and not against "main". This will help keep public -breakage to a minimum and will allow you to propose more radical changes. +[Please take a look at our Contribution documentation to learn more about contributing to InvokeAI. +](contributing/CONTRIBUTING.md) ## :octicons-person-24: Contributors diff --git a/mkdocs.yml b/mkdocs.yml index cf1daaa779..42672fc0ab 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -143,6 +143,7 @@ nav: - Documentation: 'contributing/contribution_guides/documentation.md' - Translation: 'contributing/contribution_guides/translation.md' - Tutorials: 'contributing/contribution_guides/tutorials.md' + - Local Development: contributing/LOCAL_DEVELOPMENT.md - Changelog: 'CHANGELOG.md' - Deprecated: - Command Line Interface: 'deprecated/CLI.md' From 6ba48af0a924ef5729dd1e85f8cb3682ef0a0b15 Mon Sep 17 00:00:00 2001 From: Millun Atluri Date: Wed, 19 Jul 2023 22:04:17 +1000 Subject: [PATCH 03/13] Added community node documentation --- docs/nodes/communityNodes.md | 28 ++++++++++++++++++++++++ docs/nodes/overview.md | 41 ++++++++++++++++++++++++++++++++++++ mkdocs.yml | 23 ++++++++++++++------ 3 files changed, 85 insertions(+), 7 deletions(-) create mode 100644 docs/nodes/communityNodes.md create mode 100644 docs/nodes/overview.md diff --git a/docs/nodes/communityNodes.md b/docs/nodes/communityNodes.md new file mode 100644 index 0000000000..2c6941d3c8 --- /dev/null +++ b/docs/nodes/communityNodes.md @@ -0,0 +1,28 @@ +# Community Nodes + +These are nodes that have been developed by the community for the community. If you're not sure what a node is, you can learn more about nodes [here](overview.md). + +If you'd like to submit a node for the community, please refer to the [node creation overview](overview.md). + +To download a node, simply download the `.py` node file from the link and add it to the `/app/invocations/` folder in your Invoke AI install location. Along with the node, an example node graph should be provided to help you get started with the node. + +To use a community node graph, download the the `.json` node graph file and load it into Invoke AI via the **Load Nodes** button on the Node Editor. + +## List of Nodes + +-------------------------------- +### Super Cool Node Template + +**Description:** This node allows you to do super cool things with InvokeAI. + +**Node Link:** https://github.com/invoke-ai/InvokeAI/fake_node.py + +**Example Node Graph:** https://github.com/invoke-ai/InvokeAI/fake_node_graph.json + +**Output Examples** + +![Invoke AI](https://invoke-ai.github.io/InvokeAI/assets/invoke_ai_banner.png) + + +## Help +If you run into any issues with a node, please post in the [InvokeAI Discord](https://discord.gg/ZmtBAhwWhy). \ No newline at end of file diff --git a/docs/nodes/overview.md b/docs/nodes/overview.md new file mode 100644 index 0000000000..f7dc2384dd --- /dev/null +++ b/docs/nodes/overview.md @@ -0,0 +1,41 @@ +# Nodes +## What are Nodes? +An Node is simply a single operation that takes in some inputs and gives +out some outputs. We can then chain multiple nodes together to create more +complex functionality. All InvokeAI features are added through nodes. + +This means nodes can be used to easily extend the image generation capabilities of InvokeAI, and allow you build workflows to suit your needs. + +You can read more about nodes and the node editor [here](../features/NODES.md). + + +## Downloading Nodes +To download a new node, visit our list of [Community Nodes](communityNodes.md). These are codes that have been created by the community, for the community. + + +## Contributing Nodes + +To learn about creating a new node, please visit our [Node creation documenation](../contributing/INVOCATIONS.md). + +Once you’ve created a node and confirmed that it behaves as expected locally, follow these steps: +- Make sure the node is contained in a new Python (.py) file +- Submit a pull request with a link to your node in GitHub against the `nodes` branch to add the node to the [Community Nodes](Community Nodes) list + - Make sure you are following the template below and have provided all relevant details about the node and what it does. +- A maintainer will review the pull request and node. If the node is aligned with the direction of the project, you might be asked for permission to include it in the core project. + +### Community Node Template + +```markdown +-------------------------------- +### Super Cool Node Template + +**Description:** This node allows you to do super cool things with InvokeAI. + +**Node Link:** https://github.com/invoke-ai/InvokeAI/fake_node.py + +**Example Node Graph:** https://github.com/invoke-ai/InvokeAI/fake_node_graph.json + +**Output Examples** + +![InvokeAI](https://invoke-ai.github.io/InvokeAI/assets/invoke_ai_banner.png) +``` diff --git a/mkdocs.yml b/mkdocs.yml index 42672fc0ab..7d3e0e0b85 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -104,10 +104,10 @@ nav: - Installation: - Overview: 'installation/index.md' - Installing with the Automated Installer: 'installation/010_INSTALL_AUTOMATED.md' - - Installing manually: 'installation/010_INSTALL_AUTOMATED.md' + - Installing manually: 'installation/020_INSTALL_MANUAL.md' - NVIDIA Cuda / AMD ROCm: 'installation/030_INSTALL_CUDA_AND_ROCM.md' - Installing with Docker: 'installation/040_INSTALL_DOCKER.md' - - Installing Models: 'installation/040_INSTALL_DOCKER.md' + - Installing Models: 'installation/050_INSTALLING_MODELS.md' - Installing PyPatchMatch: 'installation/060_INSTALL_PATCHMATCH.md' - Installing xFormers: 'installation/070_INSTALL_XFORMERS.md' - Developers Documentation: 'installation/Developers_documentation/BUILDING_BINARY_INSTALLERS.md' @@ -115,10 +115,13 @@ nav: - Binary Installer: 'installation/deprecated_documentation/INSTALL_BINARY.md' - Runninng InvokeAI on Google Colab: 'installation/deprecated_documentation/INSTALL_JUPYTER.md' - Manual Installation on Linux: 'installation/deprecated_documentation/INSTALL_LINUX.md' - - Manual Installation on macOS: 'installation/deprecated_documentation/INSTALL_macOS.md' + - Manual Installation on macOS: 'installation/deprecated_documentation/INSTALL_MAC.md' - Manual Installation on Windows: 'installation/deprecated_documentation/INSTALL_WINDOWS.md' - Installing Invoke with pip: 'installation/deprecated_documentation/INSTALL_PCP.md' - Source Installer: 'installation/deprecated_documentation/INSTALL_SOURCE.md' + - Community Nodes: + - Community Nodes: 'nodes/communityNodes.md' + - Overview: 'nodes/overview.md' - Features: - Overview: 'features/index.md' - Concepts: 'features/CONCEPTS.md' @@ -139,18 +142,24 @@ nav: - Other: 'features/OTHER.md' - Contributing: - How to Contribute: 'contributing/CONTRIBUTING.md' - - Development: 'contributing/contribution_guides/development.md' + - Development: + - Overview: 'contributing/contribution_guides/development.md' + - InvokeAI Architecture: 'contributing/ARCHITECTURE.md' + - Frontend Documentation: 'contributing/contribution_guides/development_guides/contributingToFrontend.md' + - Local Development: 'contributing/LOCAL_DEVELOPMENT.md' - Documentation: 'contributing/contribution_guides/documentation.md' - Translation: 'contributing/contribution_guides/translation.md' - Tutorials: 'contributing/contribution_guides/tutorials.md' - - Local Development: contributing/LOCAL_DEVELOPMENT.md - Changelog: 'CHANGELOG.md' - Deprecated: - Command Line Interface: 'deprecated/CLI.md' - Embiggen: 'deprecated/EMBIGGEN.md' - - Inpainting: 'deprecated/INPAINTING.MD' - - Outpainting: 'deprecated/OUTPAINTING.MD' + - Inpainting: 'deprecated/INPAINTING.md' + - Outpainting: 'deprecated/OUTPAINTING.md' - Help: - Sampler Convergence: 'help/SAMPLER_CONVERGENCE.md' + - Other: + - Contributors: 'other/CONTRIBUTORS.md' + - CompViz-README: 'other/README-CompViz.md' From c291b82b94bdc82fa3cd1fac87f7d47f9be0abef Mon Sep 17 00:00:00 2001 From: Millun Atluri Date: Wed, 19 Jul 2023 23:56:38 +1000 Subject: [PATCH 04/13] Added contribution disclaimer --- docs/contributing/CONTRIBUTING.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/contributing/CONTRIBUTING.md b/docs/contributing/CONTRIBUTING.md index fe18345ec9..4b38e30633 100644 --- a/docs/contributing/CONTRIBUTING.md +++ b/docs/contributing/CONTRIBUTING.md @@ -32,7 +32,18 @@ This project is a combined effort of dedicated people from across the world. [C ### Code of Conduct -The InvokeAI community is a welcoming place, and we want your help in maintaining that. Please review our [Code of Conduct](../../CODE_OF_CONDUCT.md) to learn more. +The InvokeAI community is a welcoming place, and we want your help in maintaining that. Please review our [Code of Conduct](../../CODE_OF_CONDUCT.md) to learn more - it's essential to maintaining a respectful and inclusive environment. + +By making a contribution to this project, you certify that: + +1. The contribution was created in whole or in part by you and you have the right to submit it under the open-source license indicated in this project’s GitHub repository; or +2. The contribution is based upon previous work that, to the best of your knowledge, is covered under an appropriate open-source license and you have the right under that license to submit that work with modifications, whether created in whole or in part by you, under the same open-source license (unless you are permitted to submit under a different license); or +3. The contribution was provided directly to you by some other person who certified (1) or (2) and you have not modified it; or +4. You understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information you submit with it, including your sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open-source license(s) involved. + +This disclaimer is not a license and does not grant any rights or permissions. You must obtain necessary permissions and licenses, including from third parties, before contributing to this project. + +This disclaimer is provided "as is" without warranty of any kind, whether expressed or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, or non-infringement. In no event shall the authors or copyright holders be liable for any claim, damages, or other liability, whether in an action of contract, tort, or otherwise, arising from, out of, or in connection with the contribution or the use or other dealings in the contribution. ### Support From a683379ddac6ce7a4c51992378b4c39a120cc914 Mon Sep 17 00:00:00 2001 From: Millun Atluri Date: Thu, 20 Jul 2023 09:28:21 +1000 Subject: [PATCH 05/13] Updated docs to be more accurate based on Lincoln's feedback --- docs/contributing/contribution_guides/development.md | 2 +- .../development_guides/contributingToFrontend.md | 10 +++++----- .../development_guides/contributingToNodes.md | 1 - docs/nodes/communityNodes.md | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) delete mode 100644 docs/contributing/contribution_guides/development_guides/contributingToNodes.md diff --git a/docs/contributing/contribution_guides/development.md b/docs/contributing/contribution_guides/development.md index 6c42efd051..584fb5a4ed 100644 --- a/docs/contributing/contribution_guides/development.md +++ b/docs/contributing/contribution_guides/development.md @@ -66,7 +66,7 @@ git commit -m "Insert a short message of the changes made here" git push origin branch-name-here ``` -1. Submit a pull request to the **development** branch of the InvokeAI repository. +1. Submit a pull request to the **main** branch of the InvokeAI repository. 2. Title the pull request with a short description of the changes made and the issue or bug number associated with your change. For example, you can title an issue like so "Added more log outputting to resolve #1234". 3. In the description of the pull request, explain the changes that you made, any issues you think exist with the pull request you made, and any questions you have for the maintainer. It's OK if your pull request is not perfect (no pull request is), the reviewer will be able to help you fix any problems and improve it! 4. Wait for the pull request to be reviewed by other collaborators. diff --git a/docs/contributing/contribution_guides/development_guides/contributingToFrontend.md b/docs/contributing/contribution_guides/development_guides/contributingToFrontend.md index ee0a4ef1cb..08f7c69ce7 100644 --- a/docs/contributing/contribution_guides/development_guides/contributingToFrontend.md +++ b/docs/contributing/contribution_guides/development_guides/contributingToFrontend.md @@ -10,7 +10,7 @@ The UI is a fairly straightforward Typescript React app, with the Unified Canvas being more complex. -Code is located in `invokeai/frontend/web/` for review. +Code is located in `invokeai/frontend/web/src` for review. ## Stack @@ -45,10 +45,10 @@ Install [node](https://nodejs.org/en/download/) and [yarn classic](https://cl From `invokeai/frontend/web/` run `yarn install` to get everything set up. Start everything in dev mode: - -1. Start the dev server: `yarn dev` -2. Start the InvokeAI Nodes backend: `python scripts/invokeai-web.py # run from the repo root` -3. Point your browser to the dev server address e.g. [http://localhost:5173/](http://localhost:5173/) +1. Ensure your virtual environment is running +2. Start the dev server: `yarn dev` +3. Start the InvokeAI Nodes backend: `python scripts/invokeai-web.py # run from the repo root` +4. Point your browser to the dev server address e.g. [http://localhost:5173/](http://localhost:5173/) ### VSCode Remote Dev diff --git a/docs/contributing/contribution_guides/development_guides/contributingToNodes.md b/docs/contributing/contribution_guides/development_guides/contributingToNodes.md deleted file mode 100644 index f5f0962bd0..0000000000 --- a/docs/contributing/contribution_guides/development_guides/contributingToNodes.md +++ /dev/null @@ -1 +0,0 @@ -# Contributing to Nodes \ No newline at end of file diff --git a/docs/nodes/communityNodes.md b/docs/nodes/communityNodes.md index 2c6941d3c8..a17ac05f3b 100644 --- a/docs/nodes/communityNodes.md +++ b/docs/nodes/communityNodes.md @@ -4,7 +4,7 @@ These are nodes that have been developed by the community for the community. If If you'd like to submit a node for the community, please refer to the [node creation overview](overview.md). -To download a node, simply download the `.py` node file from the link and add it to the `/app/invocations/` folder in your Invoke AI install location. Along with the node, an example node graph should be provided to help you get started with the node. +To download a node, simply download the `.py` node file from the link and add it to the `invoke/app/invocations/` folder in your Invoke AI install location. Along with the node, an example node graph should be provided to help you get started with the node. To use a community node graph, download the the `.json` node graph file and load it into Invoke AI via the **Load Nodes** button on the Node Editor. From 53ed2521681ba0c605edab6bf56ad754a88dc1d8 Mon Sep 17 00:00:00 2001 From: Millun Atluri Date: Thu, 20 Jul 2023 09:34:16 +1000 Subject: [PATCH 06/13] Fixed typos in docs --- docs/nodes/communityNodes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/nodes/communityNodes.md b/docs/nodes/communityNodes.md index a17ac05f3b..8bd9a613a0 100644 --- a/docs/nodes/communityNodes.md +++ b/docs/nodes/communityNodes.md @@ -4,7 +4,7 @@ These are nodes that have been developed by the community for the community. If If you'd like to submit a node for the community, please refer to the [node creation overview](overview.md). -To download a node, simply download the `.py` node file from the link and add it to the `invoke/app/invocations/` folder in your Invoke AI install location. Along with the node, an example node graph should be provided to help you get started with the node. +To download a node, simply download the `.py` node file from the link and add it to the `invokeai/app/invocations/` folder in your Invoke AI install location. Along with the node, an example node graph should be provided to help you get started with the node. To use a community node graph, download the the `.json` node graph file and load it into Invoke AI via the **Load Nodes** button on the Node Editor. From a0b5930340db2b363174c2356ba8c06d92a7b0e8 Mon Sep 17 00:00:00 2001 From: Millun Atluri Date: Thu, 20 Jul 2023 09:35:09 +1000 Subject: [PATCH 07/13] Updated Code of Conduct URL --- docs/contributing/CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/contributing/CONTRIBUTING.md b/docs/contributing/CONTRIBUTING.md index 4b38e30633..415b214f48 100644 --- a/docs/contributing/CONTRIBUTING.md +++ b/docs/contributing/CONTRIBUTING.md @@ -32,7 +32,7 @@ This project is a combined effort of dedicated people from across the world. [C ### Code of Conduct -The InvokeAI community is a welcoming place, and we want your help in maintaining that. Please review our [Code of Conduct](../../CODE_OF_CONDUCT.md) to learn more - it's essential to maintaining a respectful and inclusive environment. +The InvokeAI community is a welcoming place, and we want your help in maintaining that. Please review our [Code of Conduct](https://github.com/invoke-ai/InvokeAI/blob/main/CODE_OF_CONDUCT.md) to learn more - it's essential to maintaining a respectful and inclusive environment. By making a contribution to this project, you certify that: From 1e5310793ce85939939690e4caacd26240bc4dbc Mon Sep 17 00:00:00 2001 From: Millun Atluri Date: Thu, 20 Jul 2023 09:46:05 +1000 Subject: [PATCH 08/13] Updated PR template --- pull_request_template.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pull_request_template.md b/pull_request_template.md index e365120f24..04d9a96a99 100644 --- a/pull_request_template.md +++ b/pull_request_template.md @@ -5,6 +5,7 @@ - [ ] Bug Fix - [ ] Optimization - [ ] Documentation Update +- [ ] Community Node ## Have you discussed this change with the InvokeAI team? @@ -12,6 +13,11 @@ - [ ] No, because: +## Have you updated relevant documentation? +- [ ] Yes +- [ ] No + + ## Description From f2515d9480b7569b32c94228d0a6296f5d652246 Mon Sep 17 00:00:00 2001 From: Lincoln Stein Date: Wed, 19 Jul 2023 22:13:56 -0400 Subject: [PATCH 09/13] fix v1-finetune.yaml is not in the subpath of "" (#3848) Co-authored-by: Lincoln Stein --- invokeai/app/services/config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/invokeai/app/services/config.py b/invokeai/app/services/config.py index 0a70c1dd5d..35334b1bef 100644 --- a/invokeai/app/services/config.py +++ b/invokeai/app/services/config.py @@ -277,7 +277,7 @@ class InvokeAISettings(BaseSettings): @classmethod def _excluded_from_yaml(self)->List[str]: # combination of deprecated parameters and internal ones that shouldn't be exposed as invokeai.yaml options - return ['type','initconf', 'gpu_mem_reserved', 'max_loaded_models', 'version', 'from_file', 'model', 'restore'] + return ['type','initconf', 'gpu_mem_reserved', 'max_loaded_models', 'version', 'from_file', 'model', 'restore', 'root'] class Config: env_file_encoding = 'utf-8' @@ -446,7 +446,7 @@ setting environment variables INVOKEAI_. Path to the runtime root directory ''' if self.root: - return Path(self.root).expanduser() + return Path(self.root).expanduser().absolute() else: return self.find_root() From f6d5e930201723079eb70dba9ed8a8145cadc390 Mon Sep 17 00:00:00 2001 From: blessedcoolant <54517381+blessedcoolant@users.noreply.github.com> Date: Thu, 20 Jul 2023 14:16:32 +1200 Subject: [PATCH 10/13] fix: Model List not scrolling through checkpoints (#3849) --- .../subpanels/ModelManagerPanel/ModelList.tsx | 81 ++++++++++--------- 1 file changed, 43 insertions(+), 38 deletions(-) diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelList.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelList.tsx index c9f8384b9a..722bd83b6e 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelList.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelList.tsx @@ -75,42 +75,49 @@ const ModelList = (props: ModelListProps) => { labelPos="side" /> - {['images', 'diffusers'].includes(modelFormatFilter) && - filteredDiffusersModels.length > 0 && ( - - - - Diffusers - - {filteredDiffusersModels.map((model) => ( - - ))} - - - )} - {['images', 'checkpoint'].includes(modelFormatFilter) && - filteredCheckpointModels.length > 0 && ( - - - - Checkpoint - - {filteredCheckpointModels.map((model) => ( - - ))} - - - )} + + {['images', 'diffusers'].includes(modelFormatFilter) && + filteredDiffusersModels.length > 0 && ( + + + + Diffusers + + {filteredDiffusersModels.map((model) => ( + + ))} + + + )} + {['images', 'checkpoint'].includes(modelFormatFilter) && + filteredCheckpointModels.length > 0 && ( + + + + Checkpoints + + {filteredCheckpointModels.map((model) => ( + + ))} + + + )} + ); @@ -146,8 +153,6 @@ const StyledModelContainer = (props: PropsWithChildren) => { return ( Date: Thu, 20 Jul 2023 14:17:51 +1200 Subject: [PATCH 11/13] feat: Add Setting Switch Component (#3847) --- invokeai/frontend/web/public/locales/en.json | 7 ++- .../web/src/common/components/IAISwitch.tsx | 4 +- .../SettingsModal/SettingSwitch.tsx | 57 +++++++++++++++++++ .../SettingsModal/SettingsModal.tsx | 48 +++++++++------- 4 files changed, 90 insertions(+), 26 deletions(-) create mode 100644 invokeai/frontend/web/src/features/system/components/SettingsModal/SettingSwitch.tsx diff --git a/invokeai/frontend/web/public/locales/en.json b/invokeai/frontend/web/public/locales/en.json index 32ff574925..f5922a6ff4 100644 --- a/invokeai/frontend/web/public/locales/en.json +++ b/invokeai/frontend/web/public/locales/en.json @@ -547,7 +547,8 @@ "saveSteps": "Save images every n steps", "confirmOnDelete": "Confirm On Delete", "displayHelpIcons": "Display Help Icons", - "useCanvasBeta": "Use Canvas Beta Layout", + "alternateCanvasLayout": "Alternate Canvas Layout", + "enableNodesEditor": "Enable Nodes Editor", "enableImageDebugging": "Enable Image Debugging", "useSlidersForAll": "Use Sliders For All Options", "showProgressInViewer": "Show Progress Images in Viewer", @@ -564,7 +565,9 @@ "ui": "User Interface", "favoriteSchedulers": "Favorite Schedulers", "favoriteSchedulersPlaceholder": "No schedulers favorited", - "showAdvancedOptions": "Show Advanced Options" + "showAdvancedOptions": "Show Advanced Options", + "experimental": "Experimental", + "beta": "Beta" }, "toast": { "serverError": "Server Error", diff --git a/invokeai/frontend/web/src/common/components/IAISwitch.tsx b/invokeai/frontend/web/src/common/components/IAISwitch.tsx index d25ab0d87e..9803626397 100644 --- a/invokeai/frontend/web/src/common/components/IAISwitch.tsx +++ b/invokeai/frontend/web/src/common/components/IAISwitch.tsx @@ -9,7 +9,7 @@ import { } from '@chakra-ui/react'; import { memo } from 'react'; -interface Props extends SwitchProps { +export interface IAISwitchProps extends SwitchProps { label?: string; width?: string | number; formControlProps?: FormControlProps; @@ -20,7 +20,7 @@ interface Props extends SwitchProps { /** * Customized Chakra FormControl + Switch multi-part component. */ -const IAISwitch = (props: Props) => { +const IAISwitch = (props: IAISwitchProps) => { const { label, isDisabled = false, diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingSwitch.tsx b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingSwitch.tsx new file mode 100644 index 0000000000..e035b90d3a --- /dev/null +++ b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingSwitch.tsx @@ -0,0 +1,57 @@ +import { Badge, BadgeProps, Flex, Text, TextProps } from '@chakra-ui/react'; +import IAISwitch, { IAISwitchProps } from 'common/components/IAISwitch'; +import { useTranslation } from 'react-i18next'; + +type SettingSwitchProps = IAISwitchProps & { + label: string; + useBadge?: boolean; + badgeLabel?: string; + textProps?: TextProps; + badgeProps?: BadgeProps; +}; + +export default function SettingSwitch(props: SettingSwitchProps) { + const { t } = useTranslation(); + + const { + label, + textProps, + useBadge = false, + badgeLabel = t('settings.experimental'), + badgeProps, + ...rest + } = props; + + return ( + + + + {label} + + {useBadge && ( + + {badgeLabel} + + )} + + + + ); +} diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx index ccc4a9aa24..5ec7a09b67 100644 --- a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx +++ b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx @@ -11,13 +11,12 @@ import { Text, useDisclosure, } from '@chakra-ui/react'; -import { createSelector, current } from '@reduxjs/toolkit'; +import { createSelector } from '@reduxjs/toolkit'; import { VALID_LOG_LEVELS } from 'app/logging/useLogger'; import { LOCALSTORAGE_KEYS, LOCALSTORAGE_PREFIX } from 'app/store/constants'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; import IAIButton from 'common/components/IAIButton'; import IAIMantineSelect from 'common/components/IAIMantineSelect'; -import IAISwitch from 'common/components/IAISwitch'; import { systemSelector } from 'features/system/store/systemSelectors'; import { SystemState, @@ -48,8 +47,9 @@ import { } from 'react'; import { useTranslation } from 'react-i18next'; import { LogLevelName } from 'roarr'; -import SettingsSchedulers from './SettingsSchedulers'; +import SettingSwitch from './SettingSwitch'; import SettingsClearIntermediates from './SettingsClearIntermediates'; +import SettingsSchedulers from './SettingsSchedulers'; const selector = createSelector( [systemSelector, uiSelector], @@ -206,7 +206,7 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => { {t('settings.general')} - ) => @@ -214,7 +214,7 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => { } /> {shouldShowAdvancedOptionsSettings && ( - ) => @@ -231,37 +231,29 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => { {t('settings.ui')} - ) => dispatch(setShouldDisplayGuides(e.target.checked)) } /> - {shouldShowBetaLayout && ( - ) => - dispatch(setShouldUseCanvasBetaLayout(e.target.checked)) - } - /> - )} - ) => dispatch(setShouldUseSliders(e.target.checked)) } /> - ) => dispatch(setShouldShowProgressInViewer(e.target.checked)) } /> - ) => @@ -270,9 +262,21 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => { ) } /> + {shouldShowBetaLayout && ( + ) => + dispatch(setShouldUseCanvasBetaLayout(e.target.checked)) + } + /> + )} {shouldShowNodesToggle && ( - @@ -282,7 +286,7 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => { {shouldShowDeveloperSettings && ( {t('settings.developer')} - { value={consoleLogLevel} data={VALID_LOG_LEVELS.concat()} /> - ) => From 039091c5d45781bdf8216d5ddb9e5568f8f2dc02 Mon Sep 17 00:00:00 2001 From: Millun Atluri Date: Thu, 20 Jul 2023 13:16:55 +1000 Subject: [PATCH 12/13] Updated frontend docs to be more accurate --- .../contribution_guides/development.md | 26 ++++++++++--------- .../contributingToFrontend.md | 16 +++++++++--- pull_request_template.md | 2 +- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/docs/contributing/contribution_guides/development.md b/docs/contributing/contribution_guides/development.md index 584fb5a4ed..59c2b05c0e 100644 --- a/docs/contributing/contribution_guides/development.md +++ b/docs/contributing/contribution_guides/development.md @@ -41,37 +41,39 @@ Before starting these steps, ensure you have your local environment [configured git clone https://github.com/your-GitHub-username/InvokeAI.git ``` -1. Create a new branch for your fix using: +If you're unfamiliar with using Git through the commandline, [GitHub Desktop](https://desktop.github.com) is a easy-to-use alternative with a UI. You can do all the same steps listed here, but through the interface. + +4. Create a new branch for your fix using: ```bash git checkout -b branch-name-here ``` -1. Make the appropriate changes for the issue you are trying to address or the feature that you want to add. -2. Add the file contents of the changed files to the "snapshot" git uses to manage the state of the project, also known as the index: +5. Make the appropriate changes for the issue you are trying to address or the feature that you want to add. +6. Add the file contents of the changed files to the "snapshot" git uses to manage the state of the project, also known as the index: ```bash git add insert-paths-of-changed-files-here ``` -1. Store the contents of the index with a descriptive message. +7. Store the contents of the index with a descriptive message. ```bash git commit -m "Insert a short message of the changes made here" ``` -1. Push the changes to the remote repository using +8. Push the changes to the remote repository using ```markdown git push origin branch-name-here ``` -1. Submit a pull request to the **main** branch of the InvokeAI repository. -2. Title the pull request with a short description of the changes made and the issue or bug number associated with your change. For example, you can title an issue like so "Added more log outputting to resolve #1234". -3. In the description of the pull request, explain the changes that you made, any issues you think exist with the pull request you made, and any questions you have for the maintainer. It's OK if your pull request is not perfect (no pull request is), the reviewer will be able to help you fix any problems and improve it! -4. Wait for the pull request to be reviewed by other collaborators. -5. Make changes to the pull request if the reviewer(s) recommend them. -6. Celebrate your success after your pull request is merged! +9. Submit a pull request to the **main** branch of the InvokeAI repository. +10. Title the pull request with a short description of the changes made and the issue or bug number associated with your change. For example, you can title an issue like so "Added more log outputting to resolve #1234". +11. In the description of the pull request, explain the changes that you made, any issues you think exist with the pull request you made, and any questions you have for the maintainer. It's OK if your pull request is not perfect (no pull request is), the reviewer will be able to help you fix any problems and improve it! +12. Wait for the pull request to be reviewed by other collaborators. +13. Make changes to the pull request if the reviewer(s) recommend them. +14. Celebrate your success after your pull request is merged! If you’d like to learn more about contributing to Open Source projects, here is a [Getting Started Guide](https://opensource.com/article/19/7/create-pull-request-github). @@ -81,7 +83,7 @@ If you need help, you can ask questions in the [#dev-chat](https://discord.com/c For frontend related work, **@pyschedelicious** is the best person to reach out to. -For backend related work, please reach out to **@pyschedelicious, @blessedcoolant** or **@lstein**. +For backend related work, please reach out to **@blessedcoolant**, **@lstein**, **@StAlKeR7779** or **@pyschedelicious**. ## **What does the Code of Conduct mean for me?** diff --git a/docs/contributing/contribution_guides/development_guides/contributingToFrontend.md b/docs/contributing/contribution_guides/development_guides/contributingToFrontend.md index 08f7c69ce7..d1f0fb7d38 100644 --- a/docs/contributing/contribution_guides/development_guides/contributingToFrontend.md +++ b/docs/contributing/contribution_guides/development_guides/contributingToFrontend.md @@ -10,7 +10,7 @@ The UI is a fairly straightforward Typescript React app, with the Unified Canvas being more complex. -Code is located in `invokeai/frontend/web/src` for review. +Code is located in `invokeai/frontend/web/` for review. ## Stack @@ -24,7 +24,7 @@ The API client and associated types are generated from the OpenAPI schema. See A Communication with server is a mix of HTTP and [socket.io](https://github.com/socketio/socket.io-client) (with a simple socket.io redux middleware to help). -[Chakra-UI](https://github.com/chakra-ui/chakra-ui) & Mantine for components and styling. +[Chakra-UI](https://github.com/chakra-ui/chakra-ui) & [Mantine](https://github.com/mantinedev/mantine) for components and styling. [Konva](https://github.com/konvajs/react-konva) for the canvas, but we are pushing the limits of what is feasible with it (and HTML canvas in general). We plan to rebuild it with [PixiJS](https://github.com/pixijs/pixijs) to take advantage of WebGL's improved raster handling. @@ -40,7 +40,17 @@ We encourage you to ping @psychedelicious and @blessedcoolant on [Discord](http ### Dev Environment -Install [node](https://nodejs.org/en/download/) and [yarn classic](https://classic.yarnpkg.com/lang/en/). +**Setup** + +1. Install [node](https://nodejs.org/en/download/). You can confirm node is installed with: +```bash +node --version +``` +2. Install [yarn classic](https://classic.yarnpkg.com/lang/en/) and confirm it is installed by running this: +```bash +npm install --global yarn +yarn --version +``` From `invokeai/frontend/web/` run `yarn install` to get everything set up. diff --git a/pull_request_template.md b/pull_request_template.md index 04d9a96a99..7a0203ef92 100644 --- a/pull_request_template.md +++ b/pull_request_template.md @@ -5,7 +5,7 @@ - [ ] Bug Fix - [ ] Optimization - [ ] Documentation Update -- [ ] Community Node +- [ ] Community Node Submission ## Have you discussed this change with the InvokeAI team? From 187cf906fa53330c53ca07b3ffd85abe83cdf014 Mon Sep 17 00:00:00 2001 From: psychedelicious <4822129+psychedelicious@users.noreply.github.com> Date: Thu, 20 Jul 2023 15:44:22 +1000 Subject: [PATCH 13/13] ui: enhance intermediates clear, enhance board auto-add (#3851) * feat(ui): enhance clear intermediates feature - retrieve the # of intermediates using a new query (just uses list images endpoint w/ limit of 0) - display the count in the UI - add types for clearIntermediates mutation - minor styling and verbiage changes * feat(ui): remove unused settings option for guides * feat(ui): use solid badge variant consistent with the rest of the usage of badges * feat(ui): update board ctx menu, add board auto-add - add context menu to system boards - only open is select board. did this so that you dont think its broken when you click it - add auto-add board. you can right click a user board to enable it for auto-add, or use the gallery settings popover to select it. the invoke button has a tooltip on a short delay to remind you that you have auto-add enabled - made useBoardName hook, provide it a board id and it gets your the board name - removed `boardIdToAdTo` state & logic, updated workflows to auto-switch and auto-add on image generation * fix(ui): clear controlnet when clearing intermediates * feat: Make Add Board icon a button * feat(db, api): clear intermediates now clears all of them * feat(ui): make reset webui text subtext style * feat(ui): board name change submits on blur --------- Co-authored-by: blessedcoolant <54517381+blessedcoolant@users.noreply.github.com> --- invokeai/app/api/routers/images.py | 22 ++-- invokeai/app/services/image_record_storage.py | 31 +++++ invokeai/app/services/images.py | 60 ++++----- .../socketio/socketInvocationComplete.ts | 33 +++-- .../components/Boards/BoardAutoAddSelect.tsx | 80 ++++++++++++ .../components/Boards/BoardContextMenu.tsx | 60 +++++++++ .../Boards/BoardsList/AddBoardButton.tsx | 12 +- .../Boards/BoardsList/AllAssetsBoard.tsx | 1 + .../Boards/BoardsList/AllImagesBoard.tsx | 1 + .../Boards/BoardsList/BatchBoard.tsx | 1 + .../Boards/BoardsList/GalleryBoard.tsx | 92 ++++++------- .../Boards/BoardsList/GenericBoard.tsx | 121 ++++++++++-------- .../Boards/BoardsList/NoBoardBoard.tsx | 1 + .../Boards/GalleryBoardContextMenuItems.tsx | 79 ++++++++++++ .../Boards/SystemBoardContextMenuItems.tsx | 12 ++ .../gallery/components/GalleryBoardName.tsx | 59 ++++----- .../components/GallerySettingsPopover.tsx | 18 +-- .../features/gallery/store/gallerySlice.ts | 25 +++- .../ProcessButtons/InvokeButton.tsx | 108 ++++++++++------ .../components/ProcessButtons/Loopback.tsx | 33 ----- .../ProcessButtons/ProcessButtons.tsx | 1 - .../SettingsClearIntermediates.tsx | 59 +++++---- .../SettingsModal/SettingsModal.tsx | 22 +--- .../src/features/system/store/systemSlice.ts | 9 -- .../ModelManagerPanel/ModelListItem.tsx | 11 +- .../web/src/services/api/endpoints/images.ts | 11 +- .../src/services/api/hooks/useBoardName.ts | 26 ++++ 27 files changed, 647 insertions(+), 341 deletions(-) create mode 100644 invokeai/frontend/web/src/features/gallery/components/Boards/BoardAutoAddSelect.tsx create mode 100644 invokeai/frontend/web/src/features/gallery/components/Boards/BoardContextMenu.tsx create mode 100644 invokeai/frontend/web/src/features/gallery/components/Boards/GalleryBoardContextMenuItems.tsx create mode 100644 invokeai/frontend/web/src/features/gallery/components/Boards/SystemBoardContextMenuItems.tsx delete mode 100644 invokeai/frontend/web/src/features/parameters/components/ProcessButtons/Loopback.tsx create mode 100644 invokeai/frontend/web/src/services/api/hooks/useBoardName.ts diff --git a/invokeai/app/api/routers/images.py b/invokeai/app/api/routers/images.py index 559afa3b37..01e7dd2c26 100644 --- a/invokeai/app/api/routers/images.py +++ b/invokeai/app/api/routers/images.py @@ -1,8 +1,7 @@ import io from typing import Optional -from fastapi import (Body, HTTPException, Path, Query, Request, Response, - UploadFile) +from fastapi import Body, HTTPException, Path, Query, Request, Response, UploadFile from fastapi.responses import FileResponse from fastapi.routing import APIRouter from PIL import Image @@ -11,9 +10,11 @@ from invokeai.app.invocations.metadata import ImageMetadata from invokeai.app.models.image import ImageCategory, ResourceOrigin from invokeai.app.services.image_record_storage import OffsetPaginatedResults from invokeai.app.services.item_storage import PaginatedResults -from invokeai.app.services.models.image_record import (ImageDTO, - ImageRecordChanges, - ImageUrlsDTO) +from invokeai.app.services.models.image_record import ( + ImageDTO, + ImageRecordChanges, + ImageUrlsDTO, +) from ..dependencies import ApiDependencies @@ -84,15 +85,16 @@ async def delete_image( # TODO: Does this need any exception handling at all? pass + @images_router.post("/clear-intermediates", operation_id="clear_intermediates") async def clear_intermediates() -> int: - """Clears first 100 intermediates""" + """Clears all intermediates""" try: - count_deleted = ApiDependencies.invoker.services.images.delete_many(is_intermediate=True) + count_deleted = ApiDependencies.invoker.services.images.delete_intermediates() return count_deleted except Exception as e: - # TODO: Does this need any exception handling at all? + raise HTTPException(status_code=500, detail="Failed to clear intermediates") pass @@ -130,6 +132,7 @@ async def get_image_dto( except Exception as e: raise HTTPException(status_code=404) + @images_router.get( "/{image_name}/metadata", operation_id="get_image_metadata", @@ -254,7 +257,8 @@ async def list_image_dtos( default=None, description="Whether to list intermediate images." ), board_id: Optional[str] = Query( - default=None, description="The board id to filter by. Use 'none' to find images without a board." + default=None, + description="The board id to filter by. Use 'none' to find images without a board.", ), offset: int = Query(default=0, description="The page offset"), limit: int = Query(default=10, description="The number of images per page"), diff --git a/invokeai/app/services/image_record_storage.py b/invokeai/app/services/image_record_storage.py index 09c3bdcc3e..eb69679a35 100644 --- a/invokeai/app/services/image_record_storage.py +++ b/invokeai/app/services/image_record_storage.py @@ -122,6 +122,11 @@ class ImageRecordStorageBase(ABC): """Deletes many image records.""" pass + @abstractmethod + def delete_intermediates(self) -> list[str]: + """Deletes all intermediate image records, returning a list of deleted image names.""" + pass + @abstractmethod def save( self, @@ -461,6 +466,32 @@ class SqliteImageRecordStorage(ImageRecordStorageBase): finally: self._lock.release() + + def delete_intermediates(self) -> list[str]: + try: + self._lock.acquire() + self._cursor.execute( + """--sql + SELECT image_name FROM images + WHERE is_intermediate = TRUE; + """ + ) + result = cast(list[sqlite3.Row], self._cursor.fetchall()) + image_names = list(map(lambda r: r[0], result)) + self._cursor.execute( + """--sql + DELETE FROM images + WHERE is_intermediate = TRUE; + """ + ) + self._conn.commit() + return image_names + except sqlite3.Error as e: + self._conn.rollback() + raise ImageRecordDeleteException from e + finally: + self._lock.release() + def save( self, image_name: str, diff --git a/invokeai/app/services/images.py b/invokeai/app/services/images.py index 13c6c04719..6fdb6237f8 100644 --- a/invokeai/app/services/images.py +++ b/invokeai/app/services/images.py @@ -6,21 +6,33 @@ from typing import TYPE_CHECKING, Optional from PIL.Image import Image as PILImageType from invokeai.app.invocations.metadata import ImageMetadata -from invokeai.app.models.image import (ImageCategory, - InvalidImageCategoryException, - InvalidOriginException, ResourceOrigin) -from invokeai.app.services.board_image_record_storage import \ - BoardImageRecordStorageBase +from invokeai.app.models.image import ( + ImageCategory, + InvalidImageCategoryException, + InvalidOriginException, + ResourceOrigin, +) +from invokeai.app.services.board_image_record_storage import BoardImageRecordStorageBase from invokeai.app.services.image_file_storage import ( - ImageFileDeleteException, ImageFileNotFoundException, - ImageFileSaveException, ImageFileStorageBase) + ImageFileDeleteException, + ImageFileNotFoundException, + ImageFileSaveException, + ImageFileStorageBase, +) from invokeai.app.services.image_record_storage import ( - ImageRecordDeleteException, ImageRecordNotFoundException, - ImageRecordSaveException, ImageRecordStorageBase, OffsetPaginatedResults) + ImageRecordDeleteException, + ImageRecordNotFoundException, + ImageRecordSaveException, + ImageRecordStorageBase, + OffsetPaginatedResults, +) from invokeai.app.services.item_storage import ItemStorageABC -from invokeai.app.services.models.image_record import (ImageDTO, ImageRecord, - ImageRecordChanges, - image_record_to_dto) +from invokeai.app.services.models.image_record import ( + ImageDTO, + ImageRecord, + ImageRecordChanges, + image_record_to_dto, +) from invokeai.app.services.resource_name import NameServiceBase from invokeai.app.services.urls import UrlServiceBase from invokeai.app.util.metadata import get_metadata_graph_from_raw_session @@ -109,12 +121,10 @@ class ImageServiceABC(ABC): pass @abstractmethod - def delete_many(self, is_intermediate: bool) -> int: - """Deletes many images.""" + def delete_intermediates(self) -> int: + """Deletes all intermediate images.""" pass - - @abstractmethod def delete_images_on_board(self, board_id: str): """Deletes all images on a board.""" @@ -401,21 +411,13 @@ class ImageService(ImageServiceABC): except Exception as e: self._services.logger.error("Problem deleting image records and files") raise e - - def delete_many(self, is_intermediate: bool): + + def delete_intermediates(self) -> int: try: - # only clears 100 at a time - images = self._services.image_records.get_many(offset=0, limit=100, is_intermediate=is_intermediate,) - count = len(images.items) - image_name_list = list( - map( - lambda r: r.image_name, - images.items, - ) - ) - for image_name in image_name_list: + image_names = self._services.image_records.delete_intermediates() + count = len(image_names) + for image_name in image_names: self._services.image_files.delete(image_name) - self._services.image_records.delete_many(image_name_list) return count except ImageRecordDeleteException: self._services.logger.error(f"Failed to delete image records") diff --git a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationComplete.ts b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationComplete.ts index c2c57e0913..97cccfa05c 100644 --- a/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationComplete.ts +++ b/invokeai/frontend/web/src/app/store/middleware/listenerMiddleware/listeners/socketio/socketInvocationComplete.ts @@ -6,11 +6,7 @@ import { imageSelected, } from 'features/gallery/store/gallerySlice'; import { progressImageSet } from 'features/system/store/systemSlice'; -import { - SYSTEM_BOARDS, - imagesAdapter, - imagesApi, -} from 'services/api/endpoints/images'; +import { imagesAdapter, imagesApi } from 'services/api/endpoints/images'; import { isImageOutput } from 'services/api/guards'; import { sessionCanceled } from 'services/api/thunks/session'; import { @@ -32,8 +28,7 @@ export const addInvocationCompleteEventListener = () => { ); const session_id = action.payload.data.graph_execution_state_id; - const { cancelType, isCancelScheduled, boardIdToAddTo } = - getState().system; + const { cancelType, isCancelScheduled } = getState().system; // Handle scheduled cancelation if (cancelType === 'scheduled' && isCancelScheduled) { @@ -88,26 +83,28 @@ export const addInvocationCompleteEventListener = () => { ) ); - // add image to the board if we had one selected - if (boardIdToAddTo && !SYSTEM_BOARDS.includes(boardIdToAddTo)) { + const { autoAddBoardId } = gallery; + + // add image to the board if auto-add is enabled + if (autoAddBoardId) { dispatch( imagesApi.endpoints.addImageToBoard.initiate({ - board_id: boardIdToAddTo, + board_id: autoAddBoardId, imageDTO, }) ); } - const { selectedBoardId } = gallery; - - if (boardIdToAddTo && boardIdToAddTo !== selectedBoardId) { - dispatch(boardIdSelected(boardIdToAddTo)); - } else if (!boardIdToAddTo) { - dispatch(boardIdSelected('all')); - } + const { selectedBoardId, shouldAutoSwitch } = gallery; // If auto-switch is enabled, select the new image - if (getState().gallery.shouldAutoSwitch) { + if (shouldAutoSwitch) { + // if auto-add is enabled, switch the board as the image comes in + if (autoAddBoardId && autoAddBoardId !== selectedBoardId) { + dispatch(boardIdSelected(autoAddBoardId)); + } else if (!autoAddBoardId) { + dispatch(boardIdSelected('images')); + } dispatch(imageSelected(imageDTO.image_name)); } } diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardAutoAddSelect.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardAutoAddSelect.tsx new file mode 100644 index 0000000000..827d49c88e --- /dev/null +++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardAutoAddSelect.tsx @@ -0,0 +1,80 @@ +import { SelectItem } from '@mantine/core'; +import { createSelector } from '@reduxjs/toolkit'; +import { stateSelector } from 'app/store/store'; +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; +import IAIMantineSearchableSelect from 'common/components/IAIMantineSearchableSelect'; +import IAIMantineSelectItemWithTooltip from 'common/components/IAIMantineSelectItemWithTooltip'; +import { autoAddBoardIdChanged } from 'features/gallery/store/gallerySlice'; +import { useCallback, useRef } from 'react'; +import { useListAllBoardsQuery } from 'services/api/endpoints/boards'; + +const selector = createSelector( + [stateSelector], + ({ gallery }) => { + const { autoAddBoardId } = gallery; + + return { + autoAddBoardId, + }; + }, + defaultSelectorOptions +); + +const BoardAutoAddSelect = () => { + const dispatch = useAppDispatch(); + const { autoAddBoardId } = useAppSelector(selector); + const inputRef = useRef(null); + const { boards, hasBoards } = useListAllBoardsQuery(undefined, { + selectFromResult: ({ data }) => { + const boards: SelectItem[] = [ + { + label: 'None', + value: 'none', + }, + ]; + data?.forEach(({ board_id, board_name }) => { + boards.push({ + label: board_name, + value: board_id, + }); + }); + return { + boards, + hasBoards: boards.length > 1, + }; + }, + }); + + const handleChange = useCallback( + (v: string | null) => { + if (!v) { + return; + } + + dispatch(autoAddBoardIdChanged(v === 'none' ? null : v)); + }, + [dispatch] + ); + + return ( + + item.label?.toLowerCase().includes(value.toLowerCase().trim()) || + item.value.toLowerCase().includes(value.toLowerCase().trim()) + } + onChange={handleChange} + /> + ); +}; + +export default BoardAutoAddSelect; diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardContextMenu.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardContextMenu.tsx new file mode 100644 index 0000000000..fa3a6b03be --- /dev/null +++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardContextMenu.tsx @@ -0,0 +1,60 @@ +import { Box, MenuItem, MenuList } from '@chakra-ui/react'; +import { useAppDispatch } from 'app/store/storeHooks'; +import { ContextMenu, ContextMenuProps } from 'chakra-ui-contextmenu'; +import { boardIdSelected } from 'features/gallery/store/gallerySlice'; +import { memo, useCallback } from 'react'; +import { FaFolder } from 'react-icons/fa'; +import { BoardDTO } from 'services/api/types'; +import { menuListMotionProps } from 'theme/components/menu'; +import GalleryBoardContextMenuItems from './GalleryBoardContextMenuItems'; +import SystemBoardContextMenuItems from './SystemBoardContextMenuItems'; + +type Props = { + board?: BoardDTO; + board_id: string; + children: ContextMenuProps['children']; + setBoardToDelete?: (board?: BoardDTO) => void; +}; + +const BoardContextMenu = memo( + ({ board, board_id, setBoardToDelete, children }: Props) => { + const dispatch = useAppDispatch(); + const handleSelectBoard = useCallback(() => { + dispatch(boardIdSelected(board?.board_id ?? board_id)); + }, [board?.board_id, board_id, dispatch]); + return ( + + + menuProps={{ size: 'sm', isLazy: true }} + menuButtonProps={{ + bg: 'transparent', + _hover: { bg: 'transparent' }, + }} + renderMenu={() => ( + + } onClickCapture={handleSelectBoard}> + Select Board + + {!board && } + {board && ( + + )} + + )} + > + {children} + + + ); + } +); + +BoardContextMenu.displayName = 'HoverableBoard'; + +export default BoardContextMenu; diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/AddBoardButton.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/AddBoardButton.tsx index a08fdec07f..7a07680878 100644 --- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/AddBoardButton.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/AddBoardButton.tsx @@ -1,5 +1,6 @@ -import IAIButton from 'common/components/IAIButton'; +import IAIIconButton from 'common/components/IAIIconButton'; import { useCallback } from 'react'; +import { FaPlus } from 'react-icons/fa'; import { useCreateBoardMutation } from 'services/api/endpoints/boards'; const DEFAULT_BOARD_NAME = 'My Board'; @@ -12,15 +13,14 @@ const AddBoardButton = () => { }, [createBoard]); return ( - } isLoading={isLoading} + tooltip="Add Board" aria-label="Add Board" onClick={handleCreateBoard} size="sm" - sx={{ px: 4 }} - > - Add Board - + /> ); }; diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/AllAssetsBoard.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/AllAssetsBoard.tsx index 5f4f1cbeb0..76f6238cd0 100644 --- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/AllAssetsBoard.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/AllAssetsBoard.tsx @@ -38,6 +38,7 @@ const AllAssetsBoard = ({ isSelected }: { isSelected: boolean }) => { return ( { return ( { return ( { const dispatch = useAppDispatch(); + const selector = useMemo( + () => + createSelector( + stateSelector, + ({ gallery }) => { + const isSelectedForAutoAdd = + board.board_id === gallery.autoAddBoardId; + + return { isSelectedForAutoAdd }; + }, + defaultSelectorOptions + ), + [board.board_id] + ); + + const { isSelectedForAutoAdd } = useAppSelector(selector); const { currentData: coverImage } = useGetImageDTOQuery( board.cover_image_name ?? skipToken @@ -53,10 +79,6 @@ const GalleryBoard = memo( updateBoard({ board_id, changes: { board_name: newBoardName } }); }; - const handleDeleteBoard = useCallback(() => { - setBoardToDelete(board); - }, [board, setBoardToDelete]); - const droppableData: MoveBoardDropData = useMemo( () => ({ id: board_id, @@ -68,37 +90,10 @@ const GalleryBoard = memo( return ( - - menuProps={{ size: 'sm', isLazy: true }} - menuButtonProps={{ - bg: 'transparent', - _hover: { bg: 'transparent' }, - }} - renderMenu={() => ( - - {board.image_count > 0 && ( - <> - {/* } - onClickCapture={handleAddBoardToBatch} - > - Add Board to Batch - */} - - )} - } - onClickCapture={handleDeleteBoard} - > - Delete Board - - - )} + {(ref) => ( - {board.image_count} + + {board.image_count} + { handleUpdateBoardName(nextValue); }} @@ -205,7 +209,7 @@ const GalleryBoard = memo( )} - + ); } diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GenericBoard.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GenericBoard.tsx index 5067dac33a..226100c490 100644 --- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GenericBoard.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/GenericBoard.tsx @@ -2,9 +2,12 @@ import { As, Badge, Flex } from '@chakra-ui/react'; import { TypesafeDroppableData } from 'app/components/ImageDnd/typesafeDnd'; import IAIDroppable from 'common/components/IAIDroppable'; import { IAINoContentFallback } from 'common/components/IAIImageFallback'; +import { BoardId } from 'features/gallery/store/gallerySlice'; import { ReactNode } from 'react'; +import BoardContextMenu from '../BoardContextMenu'; type GenericBoardProps = { + board_id: BoardId; droppableData?: TypesafeDroppableData; onClick: () => void; isSelected: boolean; @@ -22,6 +25,7 @@ const formatBadgeCount = (count: number) => const GenericBoard = (props: GenericBoardProps) => { const { + board_id, droppableData, onClick, isSelected, @@ -32,67 +36,72 @@ const GenericBoard = (props: GenericBoardProps) => { } = props; return ( - - - + + {(ref) => ( - {badgeCount !== undefined && ( - {formatBadgeCount(badgeCount)} - )} + + + + {badgeCount !== undefined && ( + {formatBadgeCount(badgeCount)} + )} + + + + + {label} + - - - - {label} - - + )} + ); }; diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx index a47d4cd49f..772f5af97d 100644 --- a/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/Boards/BoardsList/NoBoardBoard.tsx @@ -39,6 +39,7 @@ const NoBoardBoard = ({ isSelected }: { isSelected: boolean }) => { return ( Move} onClick={handleClick} diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/GalleryBoardContextMenuItems.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/GalleryBoardContextMenuItems.tsx new file mode 100644 index 0000000000..5d39eaaf28 --- /dev/null +++ b/invokeai/frontend/web/src/features/gallery/components/Boards/GalleryBoardContextMenuItems.tsx @@ -0,0 +1,79 @@ +import { MenuItem } from '@chakra-ui/react'; +import { createSelector } from '@reduxjs/toolkit'; +import { stateSelector } from 'app/store/store'; +import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; +import { autoAddBoardIdChanged } from 'features/gallery/store/gallerySlice'; +import { memo, useCallback, useMemo } from 'react'; +import { FaMinus, FaPlus, FaTrash } from 'react-icons/fa'; +import { BoardDTO } from 'services/api/types'; + +type Props = { + board: BoardDTO; + setBoardToDelete?: (board?: BoardDTO) => void; +}; + +const GalleryBoardContextMenuItems = ({ board, setBoardToDelete }: Props) => { + const dispatch = useAppDispatch(); + + const selector = useMemo( + () => + createSelector( + stateSelector, + ({ gallery }) => { + const isSelectedForAutoAdd = + board.board_id === gallery.autoAddBoardId; + + return { isSelectedForAutoAdd }; + }, + defaultSelectorOptions + ), + [board.board_id] + ); + + const { isSelectedForAutoAdd } = useAppSelector(selector); + + const handleDelete = useCallback(() => { + if (!setBoardToDelete) { + return; + } + setBoardToDelete(board); + }, [board, setBoardToDelete]); + + const handleToggleAutoAdd = useCallback(() => { + dispatch( + autoAddBoardIdChanged(isSelectedForAutoAdd ? null : board.board_id) + ); + }, [board.board_id, dispatch, isSelectedForAutoAdd]); + + return ( + <> + {board.image_count > 0 && ( + <> + {/* } + onClickCapture={handleAddBoardToBatch} + > + Add Board to Batch + */} + + )} + : } + onClickCapture={handleToggleAutoAdd} + > + {isSelectedForAutoAdd ? 'Disable Auto-Add' : 'Auto-Add to this Board'} + + } + onClickCapture={handleDelete} + > + Delete Board + + + ); +}; + +export default memo(GalleryBoardContextMenuItems); diff --git a/invokeai/frontend/web/src/features/gallery/components/Boards/SystemBoardContextMenuItems.tsx b/invokeai/frontend/web/src/features/gallery/components/Boards/SystemBoardContextMenuItems.tsx new file mode 100644 index 0000000000..58eb6d2c0c --- /dev/null +++ b/invokeai/frontend/web/src/features/gallery/components/Boards/SystemBoardContextMenuItems.tsx @@ -0,0 +1,12 @@ +import { BoardId } from 'features/gallery/store/gallerySlice'; +import { memo } from 'react'; + +type Props = { + board_id: BoardId; +}; + +const SystemBoardContextMenuItems = ({ board_id }: Props) => { + return <>; +}; + +export default memo(SystemBoardContextMenuItems); diff --git a/invokeai/frontend/web/src/features/gallery/components/GalleryBoardName.tsx b/invokeai/frontend/web/src/features/gallery/components/GalleryBoardName.tsx index 4aa65b234e..12454dd15b 100644 --- a/invokeai/frontend/web/src/features/gallery/components/GalleryBoardName.tsx +++ b/invokeai/frontend/web/src/features/gallery/components/GalleryBoardName.tsx @@ -1,20 +1,18 @@ import { ChevronUpIcon } from '@chakra-ui/icons'; -import { Button, Flex, Text } from '@chakra-ui/react'; +import { Box, Button, Flex, Spacer, Text } from '@chakra-ui/react'; import { createSelector } from '@reduxjs/toolkit'; import { stateSelector } from 'app/store/store'; import { useAppSelector } from 'app/store/storeHooks'; import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import { memo } from 'react'; -import { useListAllBoardsQuery } from 'services/api/endpoints/boards'; +import { useBoardName } from 'services/api/hooks/useBoardName'; const selector = createSelector( [stateSelector], (state) => { const { selectedBoardId } = state.gallery; - return { - selectedBoardId, - }; + return { selectedBoardId }; }, defaultSelectorOptions ); @@ -27,25 +25,7 @@ type Props = { const GalleryBoardName = (props: Props) => { const { isOpen, onToggle } = props; const { selectedBoardId } = useAppSelector(selector); - const { selectedBoardName } = useListAllBoardsQuery(undefined, { - selectFromResult: ({ data }) => { - let selectedBoardName = ''; - if (selectedBoardId === 'images') { - selectedBoardName = 'All Images'; - } else if (selectedBoardId === 'assets') { - selectedBoardName = 'All Assets'; - } else if (selectedBoardId === 'no_board') { - selectedBoardName = 'No Board'; - } else if (selectedBoardId === 'batch') { - selectedBoardName = 'Batch'; - } else { - const selectedBoard = data?.find((b) => b.board_id === selectedBoardId); - selectedBoardName = selectedBoard?.board_name || 'Unknown Board'; - } - - return { selectedBoardName }; - }, - }); + const boardName = useBoardName(selectedBoardId); return ( { size="sm" variant="ghost" sx={{ + position: 'relative', + gap: 2, w: 'full', justifyContent: 'center', alignItems: 'center', @@ -64,19 +46,22 @@ const GalleryBoardName = (props: Props) => { }, }} > - - {selectedBoardName} - + + + + {boardName} + + + { /> } > - + { dispatch(shouldAutoSwitchChanged(e.target.checked)) } /> + ); diff --git a/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts b/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts index 340559561f..314f933e9b 100644 --- a/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts +++ b/invokeai/frontend/web/src/features/gallery/store/gallerySlice.ts @@ -25,6 +25,7 @@ export type BoardId = type GalleryState = { selection: string[]; shouldAutoSwitch: boolean; + autoAddBoardId: string | null; galleryImageMinimumWidth: number; selectedBoardId: BoardId; batchImageNames: string[]; @@ -34,6 +35,7 @@ type GalleryState = { export const initialGalleryState: GalleryState = { selection: [], shouldAutoSwitch: true, + autoAddBoardId: null, galleryImageMinimumWidth: 96, selectedBoardId: 'images', batchImageNames: [], @@ -123,14 +125,34 @@ export const gallerySlice = createSlice({ state.batchImageNames = []; state.selection = []; }, + autoAddBoardIdChanged: (state, action: PayloadAction) => { + state.autoAddBoardId = action.payload; + }, }, extraReducers: (builder) => { builder.addMatcher( boardsApi.endpoints.deleteBoard.matchFulfilled, (state, action) => { - if (action.meta.arg.originalArgs === state.selectedBoardId) { + const deletedBoardId = action.meta.arg.originalArgs; + if (deletedBoardId === state.selectedBoardId) { state.selectedBoardId = 'images'; } + if (deletedBoardId === state.autoAddBoardId) { + state.autoAddBoardId = null; + } + } + ); + builder.addMatcher( + boardsApi.endpoints.listAllBoards.matchFulfilled, + (state, action) => { + const boards = action.payload; + if (!state.autoAddBoardId) { + return; + } + + if (!boards.map((b) => b.board_id).includes(state.autoAddBoardId)) { + state.autoAddBoardId = null; + } } ); }, @@ -147,6 +169,7 @@ export const { isBatchEnabledChanged, imagesAddedToBatch, imagesRemovedFromBatch, + autoAddBoardIdChanged, } = gallerySlice.actions; export default gallerySlice.reducer; diff --git a/invokeai/frontend/web/src/features/parameters/components/ProcessButtons/InvokeButton.tsx b/invokeai/frontend/web/src/features/parameters/components/ProcessButtons/InvokeButton.tsx index ab4949392d..3880f717b9 100644 --- a/invokeai/frontend/web/src/features/parameters/components/ProcessButtons/InvokeButton.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/ProcessButtons/InvokeButton.tsx @@ -1,6 +1,9 @@ -import { Box, ChakraProps } from '@chakra-ui/react'; +import { Box, ChakraProps, Tooltip } from '@chakra-ui/react'; +import { createSelector } from '@reduxjs/toolkit'; import { userInvoked } from 'app/store/actions'; +import { stateSelector } from 'app/store/store'; import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; +import { defaultSelectorOptions } from 'app/store/util/defaultMemoizeOptions'; import IAIButton, { IAIButtonProps } from 'common/components/IAIButton'; import IAIIconButton, { IAIIconButtonProps, @@ -8,11 +11,13 @@ import IAIIconButton, { import { useIsReadyToInvoke } from 'common/hooks/useIsReadyToInvoke'; import { clampSymmetrySteps } from 'features/parameters/store/generationSlice'; import ProgressBar from 'features/system/components/ProgressBar'; +import { selectIsBusy } from 'features/system/store/systemSelectors'; import { activeTabNameSelector } from 'features/ui/store/uiSelectors'; import { useCallback } from 'react'; import { useHotkeys } from 'react-hotkeys-hook'; import { useTranslation } from 'react-i18next'; import { FaPlay } from 'react-icons/fa'; +import { useBoardName } from 'services/api/hooks/useBoardName'; const IN_PROGRESS_STYLES: ChakraProps['sx'] = { _disabled: { @@ -26,6 +31,20 @@ const IN_PROGRESS_STYLES: ChakraProps['sx'] = { }, }; +const selector = createSelector( + [stateSelector, activeTabNameSelector, selectIsBusy], + ({ gallery }, activeTabName, isBusy) => { + const { autoAddBoardId } = gallery; + + return { + isBusy, + autoAddBoardId, + activeTabName, + }; + }, + defaultSelectorOptions +); + interface InvokeButton extends Omit { iconButton?: boolean; @@ -35,8 +54,8 @@ export default function InvokeButton(props: InvokeButton) { const { iconButton = false, ...rest } = props; const dispatch = useAppDispatch(); const isReady = useIsReadyToInvoke(); - const activeTabName = useAppSelector(activeTabNameSelector); - const isProcessing = useAppSelector((state) => state.system.isProcessing); + const { isBusy, autoAddBoardId, activeTabName } = useAppSelector(selector); + const autoAddBoardName = useBoardName(autoAddBoardId); const handleInvoke = useCallback(() => { dispatch(clampSymmetrySteps()); @@ -75,43 +94,52 @@ export default function InvokeButton(props: InvokeButton) { )} - {iconButton ? ( - } - isDisabled={!isReady || isProcessing} - onClick={handleInvoke} - tooltip={t('parameters.invoke')} - tooltipProps={{ placement: 'top' }} - colorScheme="accent" - id="invoke-button" - {...rest} - sx={{ - w: 'full', - flexGrow: 1, - ...(isProcessing ? IN_PROGRESS_STYLES : {}), - }} - /> - ) : ( - - Invoke - - )} + + {iconButton ? ( + } + isDisabled={!isReady || isBusy} + onClick={handleInvoke} + tooltip={t('parameters.invoke')} + tooltipProps={{ placement: 'top' }} + colorScheme="accent" + id="invoke-button" + {...rest} + sx={{ + w: 'full', + flexGrow: 1, + ...(isBusy ? IN_PROGRESS_STYLES : {}), + }} + /> + ) : ( + + Invoke + + )} + ); diff --git a/invokeai/frontend/web/src/features/parameters/components/ProcessButtons/Loopback.tsx b/invokeai/frontend/web/src/features/parameters/components/ProcessButtons/Loopback.tsx deleted file mode 100644 index 3bd405d1ce..0000000000 --- a/invokeai/frontend/web/src/features/parameters/components/ProcessButtons/Loopback.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { createSelector } from '@reduxjs/toolkit'; -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import IAIIconButton from 'common/components/IAIIconButton'; -import { postprocessingSelector } from 'features/parameters/store/postprocessingSelectors'; -import { setShouldLoopback } from 'features/parameters/store/postprocessingSlice'; -import { useTranslation } from 'react-i18next'; -import { FaRecycle } from 'react-icons/fa'; - -const loopbackSelector = createSelector( - postprocessingSelector, - ({ shouldLoopback }) => shouldLoopback -); - -const LoopbackButton = () => { - const dispatch = useAppDispatch(); - const shouldLoopback = useAppSelector(loopbackSelector); - - const { t } = useTranslation(); - - return ( - } - onClick={() => { - dispatch(setShouldLoopback(!shouldLoopback)); - }} - /> - ); -}; - -export default LoopbackButton; diff --git a/invokeai/frontend/web/src/features/parameters/components/ProcessButtons/ProcessButtons.tsx b/invokeai/frontend/web/src/features/parameters/components/ProcessButtons/ProcessButtons.tsx index 4449866ef2..f132092012 100644 --- a/invokeai/frontend/web/src/features/parameters/components/ProcessButtons/ProcessButtons.tsx +++ b/invokeai/frontend/web/src/features/parameters/components/ProcessButtons/ProcessButtons.tsx @@ -9,7 +9,6 @@ const ProcessButtons = () => { return ( - {/* {activeTabName === 'img2img' && } */} ); diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsClearIntermediates.tsx b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsClearIntermediates.tsx index d75eb4d4c2..9d095f3511 100644 --- a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsClearIntermediates.tsx +++ b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsClearIntermediates.tsx @@ -1,60 +1,71 @@ -import { useAppDispatch, useAppSelector } from 'app/store/storeHooks'; -import { useCallback, useEffect, useState } from 'react'; -import { StyledFlex } from './SettingsModal'; import { Heading, Text } from '@chakra-ui/react'; +import { useAppDispatch } from 'app/store/storeHooks'; +import { useCallback, useEffect } from 'react'; import IAIButton from '../../../../common/components/IAIButton'; -import { useClearIntermediatesMutation } from '../../../../services/api/endpoints/images'; -import { addToast } from '../../store/systemSlice'; +import { + useClearIntermediatesMutation, + useGetIntermediatesCountQuery, +} from '../../../../services/api/endpoints/images'; import { resetCanvas } from '../../../canvas/store/canvasSlice'; +import { addToast } from '../../store/systemSlice'; +import { StyledFlex } from './SettingsModal'; +import { controlNetReset } from 'features/controlNet/store/controlNetSlice'; export default function SettingsClearIntermediates() { const dispatch = useAppDispatch(); - const [isDisabled, setIsDisabled] = useState(false); + + const { data: intermediatesCount, refetch: updateIntermediatesCount } = + useGetIntermediatesCountQuery(); const [clearIntermediates, { isLoading: isLoadingClearIntermediates }] = useClearIntermediatesMutation(); const handleClickClearIntermediates = useCallback(() => { - clearIntermediates({}) + clearIntermediates() .unwrap() .then((response) => { + dispatch(controlNetReset()); dispatch(resetCanvas()); dispatch( addToast({ - title: - response === 0 - ? `No intermediates to clear` - : `Successfully cleared ${response} intermediates`, + title: `Cleared ${response} intermediates`, status: 'info', }) ); - if (response < 100) { - setIsDisabled(true); - } }); }, [clearIntermediates, dispatch]); + useEffect(() => { + // update the count on mount + updateIntermediatesCount(); + }, [updateIntermediatesCount]); + + const buttonText = intermediatesCount + ? `Clear ${intermediatesCount} Intermediate${ + intermediatesCount > 1 ? 's' : '' + }` + : 'No Intermediates to Clear'; + return ( Clear Intermediates - {isDisabled ? 'Intermediates Cleared' : 'Clear 100 Intermediates'} + {buttonText} - - Will permanently delete first 100 intermediates found on disk and in - database + + Clearing intermediates will reset your Canvas and ControlNet state. - This will also clear your canvas state. - + Intermediate images are byproducts of generation, different from the - result images in the gallery. Purging intermediates will free disk - space. Your gallery images will not be deleted. + result images in the gallery. Clearing intermediates will free disk + space. + Your gallery images will not be deleted. ); } diff --git a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx index 5ec7a09b67..31dd6162ec 100644 --- a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx +++ b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx @@ -24,7 +24,6 @@ import { setEnableImageDebugging, setIsNodesEnabled, setShouldConfirmOnDelete, - setShouldDisplayGuides, shouldAntialiasProgressImageChanged, shouldLogToConsoleChanged, } from 'features/system/store/systemSlice'; @@ -56,7 +55,6 @@ const selector = createSelector( (system: SystemState, ui: UIState) => { const { shouldConfirmOnDelete, - shouldDisplayGuides, enableImageDebugging, consoleLogLevel, shouldLogToConsole, @@ -73,7 +71,6 @@ const selector = createSelector( return { shouldConfirmOnDelete, - shouldDisplayGuides, enableImageDebugging, shouldUseCanvasBetaLayout, shouldUseSliders, @@ -139,7 +136,6 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => { const { shouldConfirmOnDelete, - shouldDisplayGuides, enableImageDebugging, shouldUseCanvasBetaLayout, shouldUseSliders, @@ -195,7 +191,7 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => { @@ -231,14 +227,6 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => { {t('settings.ui')} - ) => - dispatch(setShouldDisplayGuides(e.target.checked)) - } - /> - { {shouldShowResetWebUiText && ( <> - {t('settings.resetWebUIDesc1')} - {t('settings.resetWebUIDesc2')} + + {t('settings.resetWebUIDesc1')} + + + {t('settings.resetWebUIDesc2')} + )} diff --git a/invokeai/frontend/web/src/features/system/store/systemSlice.ts b/invokeai/frontend/web/src/features/system/store/systemSlice.ts index 5bbaa2265f..1f2b452151 100644 --- a/invokeai/frontend/web/src/features/system/store/systemSlice.ts +++ b/invokeai/frontend/web/src/features/system/store/systemSlice.ts @@ -38,7 +38,6 @@ export interface SystemState { currentIteration: number; totalIterations: number; currentStatusHasSteps: boolean; - shouldDisplayGuides: boolean; isCancelable: boolean; enableImageDebugging: boolean; toastQueue: UseToastOptions[]; @@ -84,14 +83,12 @@ export interface SystemState { shouldAntialiasProgressImage: boolean; language: keyof typeof LANGUAGES; isUploading: boolean; - boardIdToAddTo?: string; isNodesEnabled: boolean; } export const initialSystemState: SystemState = { isConnected: false, isProcessing: false, - shouldDisplayGuides: true, isGFPGANAvailable: true, isESRGANAvailable: true, shouldConfirmOnDelete: true, @@ -134,9 +131,6 @@ export const systemSlice = createSlice({ setShouldConfirmOnDelete: (state, action: PayloadAction) => { state.shouldConfirmOnDelete = action.payload; }, - setShouldDisplayGuides: (state, action: PayloadAction) => { - state.shouldDisplayGuides = action.payload; - }, setIsCancelable: (state, action: PayloadAction) => { state.isCancelable = action.payload; }, @@ -204,7 +198,6 @@ export const systemSlice = createSlice({ */ builder.addCase(appSocketSubscribed, (state, action) => { state.sessionId = action.payload.sessionId; - state.boardIdToAddTo = action.payload.boardId; state.canceledSession = ''; }); @@ -213,7 +206,6 @@ export const systemSlice = createSlice({ */ builder.addCase(appSocketUnsubscribed, (state) => { state.sessionId = null; - state.boardIdToAddTo = undefined; }); /** @@ -390,7 +382,6 @@ export const { setIsProcessing, setShouldConfirmOnDelete, setCurrentStatus, - setShouldDisplayGuides, setIsCancelable, setEnableImageDebugging, addToast, diff --git a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelListItem.tsx b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelListItem.tsx index 224b0ac003..4de5131f65 100644 --- a/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelListItem.tsx +++ b/invokeai/frontend/web/src/features/ui/components/tabs/ModelManager/subpanels/ModelManagerPanel/ModelListItem.tsx @@ -98,16 +98,7 @@ export default function ModelListItem(props: ModelListItemProps) { onClick={handleSelectModel} > - + { modelBaseTypeMap[ model.base_model as keyof typeof modelBaseTypeMap diff --git a/invokeai/frontend/web/src/services/api/endpoints/images.ts b/invokeai/frontend/web/src/services/api/endpoints/images.ts index a37edd48aa..52f410e315 100644 --- a/invokeai/frontend/web/src/services/api/endpoints/images.ts +++ b/invokeai/frontend/web/src/services/api/endpoints/images.ts @@ -127,6 +127,13 @@ export const imagesApi = api.injectEndpoints({ // 24 hours - reducing this to a few minutes would reduce memory usage. keepUnusedDataFor: 86400, }), + getIntermediatesCount: build.query({ + query: () => ({ url: getListImagesUrl({ is_intermediate: true }) }), + providesTags: ['IntermediatesCount'], + transformResponse: (response: OffsetPaginatedResults_ImageDTO_) => { + return response.total; + }, + }), getImageDTO: build.query({ query: (image_name) => ({ url: `images/${image_name}` }), providesTags: (result, error, arg) => { @@ -148,8 +155,9 @@ export const imagesApi = api.injectEndpoints({ }, keepUnusedDataFor: 86400, // 24 hours }), - clearIntermediates: build.mutation({ + clearIntermediates: build.mutation({ query: () => ({ url: `images/clear-intermediates`, method: 'POST' }), + invalidatesTags: ['IntermediatesCount'], }), deleteImage: build.mutation({ query: ({ image_name }) => ({ @@ -617,6 +625,7 @@ export const imagesApi = api.injectEndpoints({ }); export const { + useGetIntermediatesCountQuery, useListImagesQuery, useLazyListImagesQuery, useGetImageDTOQuery, diff --git a/invokeai/frontend/web/src/services/api/hooks/useBoardName.ts b/invokeai/frontend/web/src/services/api/hooks/useBoardName.ts new file mode 100644 index 0000000000..d63b6e0425 --- /dev/null +++ b/invokeai/frontend/web/src/services/api/hooks/useBoardName.ts @@ -0,0 +1,26 @@ +import { BoardId } from 'features/gallery/store/gallerySlice'; +import { useListAllBoardsQuery } from '../endpoints/boards'; + +export const useBoardName = (board_id: BoardId | null | undefined) => { + const { boardName } = useListAllBoardsQuery(undefined, { + selectFromResult: ({ data }) => { + let boardName = ''; + if (board_id === 'images') { + boardName = 'All Images'; + } else if (board_id === 'assets') { + boardName = 'All Assets'; + } else if (board_id === 'no_board') { + boardName = 'No Board'; + } else if (board_id === 'batch') { + boardName = 'Batch'; + } else { + const selectedBoard = data?.find((b) => b.board_id === board_id); + boardName = selectedBoard?.board_name || 'Unknown Board'; + } + + return { boardName }; + }, + }); + + return boardName; +};