From be72765d02e51ddab6caaa8cce02db74be121543 Mon Sep 17 00:00:00 2001
From: psychedelicious <4822129+psychedelicious@users.noreply.github.com>
Date: Sat, 20 Jan 2024 20:03:12 +1100
Subject: [PATCH] fix(ui): bump @invoke-ai/ui, fix TS issues

---
 invokeai/frontend/web/package.json            |   6 +-
 invokeai/frontend/web/pnpm-lock.yaml          | 102 +++++++++---------
 .../InformationalPopover.tsx}                 |  75 +++++++------
 .../constants.ts                              |   0
 .../InvAutosizeTextarea.tsx                   |  40 -------
 .../InvTextarea.stories.tsx                   |  21 ----
 .../components/InvAutosizeTextarea/types.ts   |   7 --
 .../ParamControlAdapterControlMode.tsx        |   7 +-
 .../ParamControlAdapterResizeMode.tsx         |   7 +-
 .../parameters/ParamControlAdapterWeight.tsx  |   7 +-
 .../components/DynamicPromptsPreviewModal.tsx |   2 +-
 .../ParamDynamicPromptsMaxPrompts.tsx         |  11 +-
 .../components/ParamDynamicPromptsPreview.tsx |  50 +++++----
 .../ParamDynamicPromptsSeedBehaviour.tsx      |  13 ++-
 .../AddModelsPanel/AdvancedAddCheckpoint.tsx  |  17 +--
 .../AddModelsPanel/AdvancedAddDiffusers.tsx   |  27 +++--
 .../subpanels/MergeModelsPanel.tsx            |   6 +-
 .../ModelManagerPanel/CheckpointModelEdit.tsx |  19 ++--
 .../ModelManagerPanel/DiffusersModelEdit.tsx  |  17 +--
 .../ModelManagerPanel/LoRAModelEdit.tsx       |  17 +--
 .../TopRightPanel/WorkflowEditorSettings.tsx  |  20 +++-
 .../Advanced/ParamCFGRescaleMultiplier.tsx    |   7 +-
 .../components/Advanced/ParamClipSkip.tsx     |   7 +-
 .../ParamCanvasCoherenceMode.tsx              |   7 +-
 .../ParamCanvasCoherenceSteps.tsx             |   7 +-
 .../ParamCanvasCoherenceStrength.tsx          |   7 +-
 .../MaskAdjustment/ParamMaskBlur.tsx          |   7 +-
 .../MaskAdjustment/ParamMaskBlurMethod.tsx    |   7 +-
 .../InfillAndScaling/ParamInfillMethod.tsx    |   7 +-
 .../ParamScaleBeforeProcessing.tsx            |   7 +-
 .../components/Core/ParamCFGScale.tsx         |   7 +-
 .../components/Core/ParamScheduler.tsx        |   7 +-
 .../parameters/components/Core/ParamSteps.tsx |   7 +-
 .../ImageToImage/ImageToImageStrength.tsx     |   7 +-
 .../components/Seed/ParamSeedNumberInput.tsx  |   7 +-
 .../VAEModel/ParamVAEModelSelect.tsx          |  11 +-
 .../components/VAEModel/ParamVAEPrecision.tsx |   7 +-
 .../components/QueueIterationsNumberInput.tsx |   6 +-
 .../SettingsModal/SettingsModal.tsx           |  10 +-
 .../components/WorkflowLibraryPagination.tsx  |   2 +-
 40 files changed, 313 insertions(+), 292 deletions(-)
 rename invokeai/frontend/web/src/common/components/{IAIInformationalPopover/IAIInformationalPopover.tsx => InformationalPopover/InformationalPopover.tsx} (70%)
 rename invokeai/frontend/web/src/common/components/{IAIInformationalPopover => InformationalPopover}/constants.ts (100%)
 delete mode 100644 invokeai/frontend/web/src/common/components/InvAutosizeTextarea/InvAutosizeTextarea.tsx
 delete mode 100644 invokeai/frontend/web/src/common/components/InvAutosizeTextarea/InvTextarea.stories.tsx
 delete mode 100644 invokeai/frontend/web/src/common/components/InvAutosizeTextarea/types.ts

diff --git a/invokeai/frontend/web/package.json b/invokeai/frontend/web/package.json
index 0def97ea68..1d90180ef5 100644
--- a/invokeai/frontend/web/package.json
+++ b/invokeai/frontend/web/package.json
@@ -32,8 +32,8 @@
     "fix": "eslint --fix . && prettier --log-level warn --write .",
     "preinstall": "npx only-allow pnpm",
     "postinstall": "pnpm run theme",
-    "theme": "chakra-cli tokens --strict-component-types --strict-token-types node_modules/@invoke-ai/ui",
-    "theme:watch": "chakra-cli tokens --strict-component-types --strict-token-types node_modules/@invoke-ai/ui --watch",
+    "theme": "chakra-cli tokens node_modules/@invoke-ai/ui",
+    "theme:watch": "chakra-cli tokens node_modules/@invoke-ai/ui --watch",
     "storybook": "storybook dev -p 6006",
     "build-storybook": "storybook build",
     "unimported": "npx unimported"
@@ -66,7 +66,7 @@
     "@emotion/react": "^11.11.3",
     "@emotion/styled": "^11.11.0",
     "@fontsource-variable/inter": "^5.0.16",
-    "@invoke-ai/ui": "file:/home/bat/Documents/Code/ui",
+    "@invoke-ai/ui": "^0.0.8",
     "@mantine/form": "6.0.21",
     "@nanostores/react": "^0.7.1",
     "@reduxjs/toolkit": "2.0.1",
diff --git a/invokeai/frontend/web/pnpm-lock.yaml b/invokeai/frontend/web/pnpm-lock.yaml
index bb18789305..6e84da6b86 100644
--- a/invokeai/frontend/web/pnpm-lock.yaml
+++ b/invokeai/frontend/web/pnpm-lock.yaml
@@ -53,8 +53,8 @@ dependencies:
     specifier: ^5.0.16
     version: 5.0.16
   '@invoke-ai/ui':
-    specifier: file:/home/bat/Documents/Code/ui
-    version: file:../../../../ui(@chakra-ui/anatomy@2.2.2)(@chakra-ui/icons@2.1.1)(@chakra-ui/layout@2.3.1)(@chakra-ui/portal@2.1.0)(@chakra-ui/react@2.8.2)(@chakra-ui/styled-system@2.9.2)(@chakra-ui/theme-tools@2.1.2)(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(@fontsource-variable/inter@5.0.16)(@nanostores/react@0.7.1)(chakra-react-select@4.7.6)(framer-motion@10.18.0)(lodash-es@4.17.21)(nanostores@0.9.5)(overlayscrollbars-react@0.5.3)(overlayscrollbars@2.4.6)(react-dom@18.2.0)(react-i18next@14.0.0)(react-select@5.8.0)(react@18.2.0)
+    specifier: ^0.0.8
+    version: 0.0.8(@chakra-ui/anatomy@2.2.2)(@chakra-ui/icons@2.1.1)(@chakra-ui/layout@2.3.1)(@chakra-ui/portal@2.1.0)(@chakra-ui/react@2.8.2)(@chakra-ui/styled-system@2.9.2)(@chakra-ui/theme-tools@2.1.2)(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(@fontsource-variable/inter@5.0.16)(@nanostores/react@0.7.1)(chakra-react-select@4.7.6)(framer-motion@10.18.0)(lodash-es@4.17.21)(nanostores@0.9.5)(overlayscrollbars-react@0.5.3)(overlayscrollbars@2.4.6)(react-dom@18.2.0)(react-i18next@14.0.0)(react-select@5.8.0)(react@18.2.0)
   '@mantine/form':
     specifier: 6.0.21
     version: 6.0.21(react@18.2.0)
@@ -3688,6 +3688,54 @@ packages:
     resolution: {integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==}
     dev: true
 
+  /@invoke-ai/ui@0.0.8(@chakra-ui/anatomy@2.2.2)(@chakra-ui/icons@2.1.1)(@chakra-ui/layout@2.3.1)(@chakra-ui/portal@2.1.0)(@chakra-ui/react@2.8.2)(@chakra-ui/styled-system@2.9.2)(@chakra-ui/theme-tools@2.1.2)(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(@fontsource-variable/inter@5.0.16)(@nanostores/react@0.7.1)(chakra-react-select@4.7.6)(framer-motion@10.18.0)(lodash-es@4.17.21)(nanostores@0.9.5)(overlayscrollbars-react@0.5.3)(overlayscrollbars@2.4.6)(react-dom@18.2.0)(react-i18next@14.0.0)(react-select@5.8.0)(react@18.2.0):
+    resolution: {integrity: sha512-FjW9rV2Bh1h+EDU5eMQH+oR4vpA77GBrAOdbuIezeDSVcmACU5tuJXIPxw121ZAsH2ioyhgGPMvfAsF1HkSigA==}
+    peerDependencies:
+      '@chakra-ui/anatomy': ^2.2.2
+      '@chakra-ui/icons': ^2.1.1
+      '@chakra-ui/layout': ^2.3.1
+      '@chakra-ui/portal': ^2.1.0
+      '@chakra-ui/react': ^2.8.2
+      '@chakra-ui/styled-system': ^2.9.2
+      '@chakra-ui/theme-tools': ^2.1.2
+      '@emotion/react': ^11.11.3
+      '@emotion/styled': ^11.11.0
+      '@fontsource-variable/inter': ^5.0.16
+      '@nanostores/react': ^0.7.1
+      chakra-react-select: ^4.7.6
+      framer-motion: ^10.18.0
+      lodash-es: ^4.17.21
+      nanostores: ^0.9.5
+      overlayscrollbars: ^2.4.6
+      overlayscrollbars-react: ^0.5.3
+      react: ^18.2.0
+      react-dom: ^18.2.0
+      react-i18next: ^14.0.0
+      react-select: ^5.8.0
+    dependencies:
+      '@chakra-ui/anatomy': 2.2.2
+      '@chakra-ui/icons': 2.1.1(@chakra-ui/system@2.6.2)(react@18.2.0)
+      '@chakra-ui/layout': 2.3.1(@chakra-ui/system@2.6.2)(react@18.2.0)
+      '@chakra-ui/portal': 2.1.0(react-dom@18.2.0)(react@18.2.0)
+      '@chakra-ui/react': 2.8.2(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(@types/react@18.2.48)(framer-motion@10.18.0)(react-dom@18.2.0)(react@18.2.0)
+      '@chakra-ui/styled-system': 2.9.2
+      '@chakra-ui/theme-tools': 2.1.2(@chakra-ui/styled-system@2.9.2)
+      '@emotion/react': 11.11.3(@types/react@18.2.48)(react@18.2.0)
+      '@emotion/styled': 11.11.0(@emotion/react@11.11.3)(@types/react@18.2.48)(react@18.2.0)
+      '@fontsource-variable/inter': 5.0.16
+      '@nanostores/react': 0.7.1(nanostores@0.9.5)(react@18.2.0)
+      chakra-react-select: 4.7.6(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/layout@2.3.1)(@chakra-ui/media-query@3.3.0)(@chakra-ui/menu@2.2.1)(@chakra-ui/spinner@2.1.0)(@chakra-ui/system@2.6.2)(@emotion/react@11.11.3)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+      framer-motion: 10.18.0(react-dom@18.2.0)(react@18.2.0)
+      lodash-es: 4.17.21
+      nanostores: 0.9.5
+      overlayscrollbars: 2.4.6
+      overlayscrollbars-react: 0.5.3(overlayscrollbars@2.4.6)(react@18.2.0)
+      react: 18.2.0
+      react-dom: 18.2.0(react@18.2.0)
+      react-i18next: 14.0.0(i18next@23.7.16)(react-dom@18.2.0)(react@18.2.0)
+      react-select: 5.8.0(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+    dev: false
+
   /@isaacs/cliui@8.0.2:
     resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
     engines: {node: '>=12'}
@@ -13745,53 +13793,3 @@ packages:
       react: 18.2.0
       use-sync-external-store: 1.2.0(react@18.2.0)
     dev: false
-
-  file:../../../../ui(@chakra-ui/anatomy@2.2.2)(@chakra-ui/icons@2.1.1)(@chakra-ui/layout@2.3.1)(@chakra-ui/portal@2.1.0)(@chakra-ui/react@2.8.2)(@chakra-ui/styled-system@2.9.2)(@chakra-ui/theme-tools@2.1.2)(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(@fontsource-variable/inter@5.0.16)(@nanostores/react@0.7.1)(chakra-react-select@4.7.6)(framer-motion@10.18.0)(lodash-es@4.17.21)(nanostores@0.9.5)(overlayscrollbars-react@0.5.3)(overlayscrollbars@2.4.6)(react-dom@18.2.0)(react-i18next@14.0.0)(react-select@5.8.0)(react@18.2.0):
-    resolution: {directory: ../../../../ui, type: directory}
-    id: file:../../../../ui
-    name: '@invoke-ai/ui'
-    peerDependencies:
-      '@chakra-ui/anatomy': ^2.2.2
-      '@chakra-ui/icons': ^2.1.1
-      '@chakra-ui/layout': ^2.3.1
-      '@chakra-ui/portal': ^2.1.0
-      '@chakra-ui/react': ^2.8.2
-      '@chakra-ui/styled-system': ^2.9.2
-      '@chakra-ui/theme-tools': ^2.1.2
-      '@emotion/react': ^11.11.3
-      '@emotion/styled': ^11.11.0
-      '@fontsource-variable/inter': ^5.0.16
-      '@nanostores/react': ^0.7.1
-      chakra-react-select: ^4.7.6
-      framer-motion: ^10.18.0
-      lodash-es: ^4.17.21
-      nanostores: ^0.9.5
-      overlayscrollbars: ^2.4.6
-      overlayscrollbars-react: ^0.5.3
-      react: ^18.2.0
-      react-dom: ^18.2.0
-      react-i18next: ^14.0.0
-      react-select: ^5.8.0
-    dependencies:
-      '@chakra-ui/anatomy': 2.2.2
-      '@chakra-ui/icons': 2.1.1(@chakra-ui/system@2.6.2)(react@18.2.0)
-      '@chakra-ui/layout': 2.3.1(@chakra-ui/system@2.6.2)(react@18.2.0)
-      '@chakra-ui/portal': 2.1.0(react-dom@18.2.0)(react@18.2.0)
-      '@chakra-ui/react': 2.8.2(@emotion/react@11.11.3)(@emotion/styled@11.11.0)(@types/react@18.2.48)(framer-motion@10.18.0)(react-dom@18.2.0)(react@18.2.0)
-      '@chakra-ui/styled-system': 2.9.2
-      '@chakra-ui/theme-tools': 2.1.2(@chakra-ui/styled-system@2.9.2)
-      '@emotion/react': 11.11.3(@types/react@18.2.48)(react@18.2.0)
-      '@emotion/styled': 11.11.0(@emotion/react@11.11.3)(@types/react@18.2.48)(react@18.2.0)
-      '@fontsource-variable/inter': 5.0.16
-      '@nanostores/react': 0.7.1(nanostores@0.9.5)(react@18.2.0)
-      chakra-react-select: 4.7.6(@chakra-ui/form-control@2.2.0)(@chakra-ui/icon@3.2.0)(@chakra-ui/layout@2.3.1)(@chakra-ui/media-query@3.3.0)(@chakra-ui/menu@2.2.1)(@chakra-ui/spinner@2.1.0)(@chakra-ui/system@2.6.2)(@emotion/react@11.11.3)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
-      framer-motion: 10.18.0(react-dom@18.2.0)(react@18.2.0)
-      lodash-es: 4.17.21
-      nanostores: 0.9.5
-      overlayscrollbars: 2.4.6
-      overlayscrollbars-react: 0.5.3(overlayscrollbars@2.4.6)(react@18.2.0)
-      react: 18.2.0
-      react-dom: 18.2.0(react@18.2.0)
-      react-i18next: 14.0.0(i18next@23.7.16)(react-dom@18.2.0)(react@18.2.0)
-      react-select: 5.8.0(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
-    dev: false
diff --git a/invokeai/frontend/web/src/common/components/IAIInformationalPopover/IAIInformationalPopover.tsx b/invokeai/frontend/web/src/common/components/InformationalPopover/InformationalPopover.tsx
similarity index 70%
rename from invokeai/frontend/web/src/common/components/IAIInformationalPopover/IAIInformationalPopover.tsx
rename to invokeai/frontend/web/src/common/components/InformationalPopover/InformationalPopover.tsx
index 8248324a26..cd2b320155 100644
--- a/invokeai/frontend/web/src/common/components/IAIInformationalPopover/IAIInformationalPopover.tsx
+++ b/invokeai/frontend/web/src/common/components/InformationalPopover/InformationalPopover.tsx
@@ -28,51 +28,48 @@ type Props = {
   children: ReactElement;
 };
 
-const IAIInformationalPopover = ({
-  feature,
-  children,
-  inPortal = true,
-  ...rest
-}: Props) => {
-  const shouldEnableInformationalPopovers = useAppSelector(
-    (s) => s.system.shouldEnableInformationalPopovers
-  );
+export const InformationalPopover = memo(
+  ({ feature, children, inPortal = true, ...rest }: Props) => {
+    const shouldEnableInformationalPopovers = useAppSelector(
+      (s) => s.system.shouldEnableInformationalPopovers
+    );
 
-  const data = useMemo(() => POPOVER_DATA[feature], [feature]);
+    const data = useMemo(() => POPOVER_DATA[feature], [feature]);
 
-  const popoverProps = useMemo(
-    () => merge(omit(data, ['image', 'href', 'buttonLabel']), rest),
-    [data, rest]
-  );
+    const popoverProps = useMemo(
+      () => merge(omit(data, ['image', 'href', 'buttonLabel']), rest),
+      [data, rest]
+    );
 
-  if (!shouldEnableInformationalPopovers) {
-    return children;
-  }
+    if (!shouldEnableInformationalPopovers) {
+      return children;
+    }
 
-  return (
-    <Popover
-      isLazy
-      closeOnBlur={false}
-      trigger="hover"
-      variant="informational"
-      openDelay={OPEN_DELAY}
-      modifiers={POPPER_MODIFIERS}
-      placement="top"
-      {...popoverProps}
-    >
-      <PopoverTrigger>{children}</PopoverTrigger>
-      {inPortal ? (
-        <Portal>
+    return (
+      <Popover
+        isLazy
+        closeOnBlur={false}
+        trigger="hover"
+        variant="informational"
+        openDelay={OPEN_DELAY}
+        modifiers={POPPER_MODIFIERS}
+        placement="top"
+        {...popoverProps}
+      >
+        <PopoverTrigger>{children}</PopoverTrigger>
+        {inPortal ? (
+          <Portal>
+            <Content data={data} feature={feature} />
+          </Portal>
+        ) : (
           <Content data={data} feature={feature} />
-        </Portal>
-      ) : (
-        <Content data={data} feature={feature} />
-      )}
-    </Popover>
-  );
-};
+        )}
+      </Popover>
+    );
+  }
+);
 
-export default memo(IAIInformationalPopover);
+InformationalPopover.displayName = 'InformationalPopover';
 
 type ContentProps = {
   data?: PopoverData;
diff --git a/invokeai/frontend/web/src/common/components/IAIInformationalPopover/constants.ts b/invokeai/frontend/web/src/common/components/InformationalPopover/constants.ts
similarity index 100%
rename from invokeai/frontend/web/src/common/components/IAIInformationalPopover/constants.ts
rename to invokeai/frontend/web/src/common/components/InformationalPopover/constants.ts
diff --git a/invokeai/frontend/web/src/common/components/InvAutosizeTextarea/InvAutosizeTextarea.tsx b/invokeai/frontend/web/src/common/components/InvAutosizeTextarea/InvAutosizeTextarea.tsx
deleted file mode 100644
index b4c7e1ef5a..0000000000
--- a/invokeai/frontend/web/src/common/components/InvAutosizeTextarea/InvAutosizeTextarea.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import { Box, forwardRef, Textarea as ChakraTextarea } from '@invoke-ai/ui';
-import { useGlobalModifiersSetters } from 'common/hooks/useGlobalModifiers';
-import { stopPastePropagation } from 'common/util/stopPastePropagation';
-import type { KeyboardEvent } from 'react';
-import { memo, useCallback } from 'react';
-import ResizeTextarea from 'react-textarea-autosize';
-
-import type { InvAutosizeTextareaProps } from './types';
-
-export const InvAutosizeTextarea = memo(
-  forwardRef<InvAutosizeTextareaProps, typeof ResizeTextarea>(
-    (props: InvAutosizeTextareaProps, ref) => {
-      const { setShift } = useGlobalModifiersSetters();
-      const onKeyUpDown = useCallback(
-        (e: KeyboardEvent<HTMLTextAreaElement>) => {
-          setShift(e.shiftKey);
-        },
-        [setShift]
-      );
-      return (
-        <Box pos="relative">
-          <ChakraTextarea
-            as={ResizeTextarea}
-            ref={ref}
-            overflow="scroll"
-            w="100%"
-            minRows={3}
-            minH={20}
-            onPaste={stopPastePropagation}
-            onKeyUp={onKeyUpDown}
-            onKeyDown={onKeyUpDown}
-            {...props}
-          />
-        </Box>
-      );
-    }
-  )
-);
-
-InvAutosizeTextarea.displayName = 'InvAutosizeTextarea';
diff --git a/invokeai/frontend/web/src/common/components/InvAutosizeTextarea/InvTextarea.stories.tsx b/invokeai/frontend/web/src/common/components/InvAutosizeTextarea/InvTextarea.stories.tsx
deleted file mode 100644
index 3a7bd15d8e..0000000000
--- a/invokeai/frontend/web/src/common/components/InvAutosizeTextarea/InvTextarea.stories.tsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import type { Meta, StoryObj } from '@storybook/react';
-
-import { InvAutosizeTextarea } from './InvAutosizeTextarea';
-import type { InvAutosizeTextareaProps } from './types';
-
-const meta: Meta<typeof InvAutosizeTextarea> = {
-  title: 'Primitives/InvAutosizeTextarea',
-  tags: ['autodocs'],
-  component: InvAutosizeTextarea,
-};
-
-export default meta;
-type Story = StoryObj<typeof InvAutosizeTextarea>;
-
-const Component = (props: InvAutosizeTextareaProps) => {
-  return <InvAutosizeTextarea {...props} />;
-};
-
-export const Default: Story = {
-  render: Component,
-};
diff --git a/invokeai/frontend/web/src/common/components/InvAutosizeTextarea/types.ts b/invokeai/frontend/web/src/common/components/InvAutosizeTextarea/types.ts
deleted file mode 100644
index 350428eaf7..0000000000
--- a/invokeai/frontend/web/src/common/components/InvAutosizeTextarea/types.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import type { TextareaProps as ChakraTextareaProps } from '@invoke-ai/ui';
-import type { TextareaAutosizeProps } from 'react-textarea-autosize';
-
-export type InvAutosizeTextareaProps = Omit<
-  ChakraTextareaProps & TextareaAutosizeProps,
-  'resize'
->;
diff --git a/invokeai/frontend/web/src/features/controlAdapters/components/parameters/ParamControlAdapterControlMode.tsx b/invokeai/frontend/web/src/features/controlAdapters/components/parameters/ParamControlAdapterControlMode.tsx
index 9819697990..1cee707bb6 100644
--- a/invokeai/frontend/web/src/features/controlAdapters/components/parameters/ParamControlAdapterControlMode.tsx
+++ b/invokeai/frontend/web/src/features/controlAdapters/components/parameters/ParamControlAdapterControlMode.tsx
@@ -1,6 +1,7 @@
 import type { ComboboxOnChange } from '@invoke-ai/ui';
 import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui';
 import { useAppDispatch } from 'app/store/storeHooks';
+import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
 import { useControlAdapterControlMode } from 'features/controlAdapters/hooks/useControlAdapterControlMode';
 import { useControlAdapterIsEnabled } from 'features/controlAdapters/hooks/useControlAdapterIsEnabled';
 import { controlAdapterControlModeChanged } from 'features/controlAdapters/store/controlAdaptersSlice';
@@ -53,8 +54,10 @@ const ParamControlAdapterControlMode = ({ id }: Props) => {
   }
 
   return (
-    <FormControl isDisabled={!isEnabled} feature="controlNetControlMode">
-      <FormLabel>{t('controlnet.controlMode')}</FormLabel>
+    <FormControl isDisabled={!isEnabled}>
+      <InformationalPopover feature="controlNetControlMode">
+        <FormLabel>{t('controlnet.controlMode')}</FormLabel>
+      </InformationalPopover>
       <Combobox
         value={value}
         options={CONTROL_MODE_DATA}
diff --git a/invokeai/frontend/web/src/features/controlAdapters/components/parameters/ParamControlAdapterResizeMode.tsx b/invokeai/frontend/web/src/features/controlAdapters/components/parameters/ParamControlAdapterResizeMode.tsx
index 4a652604a8..1ee1fdc2ca 100644
--- a/invokeai/frontend/web/src/features/controlAdapters/components/parameters/ParamControlAdapterResizeMode.tsx
+++ b/invokeai/frontend/web/src/features/controlAdapters/components/parameters/ParamControlAdapterResizeMode.tsx
@@ -1,6 +1,7 @@
 import type { ComboboxOnChange } from '@invoke-ai/ui';
 import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui';
 import { useAppDispatch } from 'app/store/storeHooks';
+import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
 import { useControlAdapterIsEnabled } from 'features/controlAdapters/hooks/useControlAdapterIsEnabled';
 import { useControlAdapterResizeMode } from 'features/controlAdapters/hooks/useControlAdapterResizeMode';
 import { controlAdapterResizeModeChanged } from 'features/controlAdapters/store/controlAdaptersSlice';
@@ -54,8 +55,10 @@ const ParamControlAdapterResizeMode = ({ id }: Props) => {
   }
 
   return (
-    <FormControl feature="controlNetResizeMode">
-      <FormLabel>{t('controlnet.resizeMode')}</FormLabel>
+    <FormControl>
+      <InformationalPopover feature="controlNetResizeMode">
+        <FormLabel>{t('controlnet.resizeMode')}</FormLabel>
+      </InformationalPopover>
       <Combobox
         value={value}
         options={options}
diff --git a/invokeai/frontend/web/src/features/controlAdapters/components/parameters/ParamControlAdapterWeight.tsx b/invokeai/frontend/web/src/features/controlAdapters/components/parameters/ParamControlAdapterWeight.tsx
index 95e3a99df5..27a4115ae7 100644
--- a/invokeai/frontend/web/src/features/controlAdapters/components/parameters/ParamControlAdapterWeight.tsx
+++ b/invokeai/frontend/web/src/features/controlAdapters/components/parameters/ParamControlAdapterWeight.tsx
@@ -5,6 +5,7 @@ import {
   FormLabel,
 } from '@invoke-ai/ui';
 import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
 import { useControlAdapterIsEnabled } from 'features/controlAdapters/hooks/useControlAdapterIsEnabled';
 import { useControlAdapterWeight } from 'features/controlAdapters/hooks/useControlAdapterWeight';
 import { controlAdapterWeightChanged } from 'features/controlAdapters/store/controlAdaptersSlice';
@@ -48,8 +49,10 @@ const ParamControlAdapterWeight = ({ id }: ParamControlAdapterWeightProps) => {
   }
 
   return (
-    <FormControl isDisabled={!isEnabled} feature="controlNetWeight">
-      <FormLabel>{t('controlnet.weight')}</FormLabel>
+    <FormControl isDisabled={!isEnabled}>
+      <InformationalPopover feature="controlNetWeight">
+        <FormLabel>{t('controlnet.weight')}</FormLabel>
+      </InformationalPopover>
       <CompositeSlider
         value={weight}
         onChange={onChange}
diff --git a/invokeai/frontend/web/src/features/dynamicPrompts/components/DynamicPromptsPreviewModal.tsx b/invokeai/frontend/web/src/features/dynamicPrompts/components/DynamicPromptsPreviewModal.tsx
index 8a67e36874..1783ccf9d1 100644
--- a/invokeai/frontend/web/src/features/dynamicPrompts/components/DynamicPromptsPreviewModal.tsx
+++ b/invokeai/frontend/web/src/features/dynamicPrompts/components/DynamicPromptsPreviewModal.tsx
@@ -25,7 +25,7 @@ export const DynamicPromptsModal = memo(() => {
       <ModalContent w="80vw" h="80vh" maxW="unset" maxH="unset">
         <ModalHeader>{t('dynamicPrompts.dynamicPrompts')}</ModalHeader>
         <ModalCloseButton />
-        <ModalBody as={Flex} flexDir="column" gap={2} w="full" h="full" pb={4}>
+        <ModalBody as={Flex} flexDir="column" gap={4} w="full" h="full" pb={4}>
           <Flex gap={4}>
             <ParamDynamicPromptsSeedBehaviour />
             <ParamDynamicPromptsMaxPrompts />
diff --git a/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsMaxPrompts.tsx b/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsMaxPrompts.tsx
index e6f5616f13..cd0daaf22a 100644
--- a/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsMaxPrompts.tsx
+++ b/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsMaxPrompts.tsx
@@ -5,6 +5,7 @@ import {
   FormLabel,
 } from '@invoke-ai/ui';
 import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
 import { maxPromptsChanged } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
 import { memo, useCallback } from 'react';
 import { useTranslation } from 'react-i18next';
@@ -38,12 +39,10 @@ const ParamDynamicPromptsMaxPrompts = () => {
   );
 
   return (
-    <FormControl
-      isDisabled={isDisabled}
-      feature="dynamicPromptsMaxPrompts"
-      renderInfoPopoverInPortal={false}
-    >
-      <FormLabel>{t('dynamicPrompts.maxPrompts')}</FormLabel>
+    <FormControl isDisabled={isDisabled}>
+      <InformationalPopover feature="dynamicPromptsMaxPrompts" inPortal={false}>
+        <FormLabel>{t('dynamicPrompts.maxPrompts')}</FormLabel>
+      </InformationalPopover>
       <CompositeSlider
         min={sliderMin}
         max={sliderMax}
diff --git a/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsPreview.tsx b/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsPreview.tsx
index c6c424d479..4b5de0cb85 100644
--- a/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsPreview.tsx
+++ b/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsPreview.tsx
@@ -1,9 +1,17 @@
 import type { ChakraProps } from '@invoke-ai/ui';
-import { Flex, ListItem, OrderedList, Spinner, Text } from '@invoke-ai/ui';
+import {
+  Flex,
+  FormControl,
+  FormLabel,
+  ListItem,
+  OrderedList,
+  Spinner,
+  Text,
+} from '@invoke-ai/ui';
 import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
 import { useAppSelector } from 'app/store/storeHooks';
 import { IAINoContentFallback } from 'common/components/IAIImageFallback';
-import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
+import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
 import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
 import { selectDynamicPromptsSlice } from 'features/dynamicPrompts/store/dynamicPromptsSlice';
 import { memo, useMemo } from 'react';
@@ -36,29 +44,27 @@ const ParamDynamicPromptsPreview = () => {
 
   if (isError) {
     return (
-      <IAIInformationalPopover feature="dynamicPrompts">
-        <Flex
-          w="full"
-          h="full"
-          layerStyle="second"
-          alignItems="center"
-          justifyContent="center"
-          p={8}
-        >
-          <IAINoContentFallback
-            icon={PiWarningCircleBold}
-            label="Problem generating prompts"
-          />
-        </Flex>
-      </IAIInformationalPopover>
+      <Flex
+        w="full"
+        h="full"
+        layerStyle="second"
+        alignItems="center"
+        justifyContent="center"
+        p={8}
+      >
+        <IAINoContentFallback
+          icon={PiWarningCircleBold}
+          label="Problem generating prompts"
+        />
+      </Flex>
     );
   }
 
   return (
-    <>
-      <Text fontSize="sm" fontWeight="bold">
-        {label}
-      </Text>
+    <FormControl orientation="vertical" w="full" h="full">
+      <InformationalPopover feature="dynamicPrompts" inPortal={false}>
+        <FormLabel>{label}</FormLabel>
+      </InformationalPopover>
       <Flex
         w="full"
         h="full"
@@ -96,7 +102,7 @@ const ParamDynamicPromptsPreview = () => {
           </Flex>
         )}
       </Flex>
-    </>
+    </FormControl>
   );
 };
 
diff --git a/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsSeedBehaviour.tsx b/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsSeedBehaviour.tsx
index 069f41ff3c..87a860ac89 100644
--- a/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsSeedBehaviour.tsx
+++ b/invokeai/frontend/web/src/features/dynamicPrompts/components/ParamDynamicPromptsSeedBehaviour.tsx
@@ -1,6 +1,7 @@
 import type { ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui';
 import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui';
 import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
 import {
   isSeedBehaviour,
   seedBehaviourChanged,
@@ -44,11 +45,13 @@ const ParamDynamicPromptsSeedBehaviour = () => {
   );
 
   return (
-    <FormControl
-      feature="dynamicPromptsSeedBehaviour"
-      renderInfoPopoverInPortal={false}
-    >
-      <FormLabel>{t('dynamicPrompts.seedBehaviour.label')}</FormLabel>
+    <FormControl>
+      <InformationalPopover
+        feature="dynamicPromptsSeedBehaviour"
+        inPortal={false}
+      >
+        <FormLabel>{t('dynamicPrompts.seedBehaviour.label')}</FormLabel>
+      </InformationalPopover>
       <Combobox value={value} options={options} onChange={handleChange} />
     </FormControl>
   );
diff --git a/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AdvancedAddCheckpoint.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AdvancedAddCheckpoint.tsx
index 1278f31667..0960c51417 100644
--- a/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AdvancedAddCheckpoint.tsx
+++ b/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AdvancedAddCheckpoint.tsx
@@ -3,6 +3,7 @@ import {
   Checkbox,
   Flex,
   FormControl,
+  FormErrorMessage,
   FormLabel,
   Input,
 } from '@invoke-ai/ui';
@@ -120,10 +121,7 @@ const AdvancedAddCheckpoint = (props: AdvancedAddCheckpointProps) => {
   return (
     <form onSubmit={handleSubmit(onSubmit)} style={formStyles}>
       <Flex flexDirection="column" gap={2}>
-        <FormControl
-          isInvalid={Boolean(errors.model_name)}
-          error={errors.model_name?.message}
-        >
+        <FormControl isInvalid={Boolean(errors.model_name)}>
           <FormLabel>{t('modelManager.model')}</FormLabel>
           <Input
             {...register('model_name', {
@@ -131,15 +129,15 @@ const AdvancedAddCheckpoint = (props: AdvancedAddCheckpointProps) => {
                 value.trim().length > 3 || 'Must be at least 3 characters',
             })}
           />
+          {errors.model_name?.message && (
+            <FormErrorMessage>{errors.model_name?.message}</FormErrorMessage>
+          )}
         </FormControl>
         <BaseModelSelect<CheckpointModelConfig>
           control={control}
           name="base_model"
         />
-        <FormControl
-          isInvalid={Boolean(errors.path)}
-          error={errors.path?.message}
-        >
+        <FormControl isInvalid={Boolean(errors.path)}>
           <FormLabel>{t('modelManager.modelLocation')}</FormLabel>
           <Input
             {...register('path', {
@@ -148,6 +146,9 @@ const AdvancedAddCheckpoint = (props: AdvancedAddCheckpointProps) => {
               onBlur,
             })}
           />
+          {errors.path?.message && (
+            <FormErrorMessage>{errors.path?.message}</FormErrorMessage>
+          )}
         </FormControl>
         <FormControl>
           <FormLabel>{t('modelManager.description')}</FormLabel>
diff --git a/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AdvancedAddDiffusers.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AdvancedAddDiffusers.tsx
index 2938662150..18e55ff686 100644
--- a/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AdvancedAddDiffusers.tsx
+++ b/invokeai/frontend/web/src/features/modelManager/subpanels/AddModelsPanel/AdvancedAddDiffusers.tsx
@@ -1,4 +1,11 @@
-import { Button, Flex, FormControl, FormLabel, Input } from '@invoke-ai/ui';
+import {
+  Button,
+  Flex,
+  FormControl,
+  FormErrorMessage,
+  FormLabel,
+  Input,
+} from '@invoke-ai/ui';
 import { useAppDispatch } from 'app/store/storeHooks';
 import { setAdvancedAddScanModel } from 'features/modelManager/store/modelManagerSlice';
 import BaseModelSelect from 'features/modelManager/subpanels/shared/BaseModelSelect';
@@ -103,10 +110,7 @@ const AdvancedAddDiffusers = (props: AdvancedAddDiffusersProps) => {
   return (
     <form onSubmit={handleSubmit(onSubmit)} style={formStyles}>
       <Flex flexDirection="column" gap={2}>
-        <FormControl
-          isInvalid={Boolean(errors.model_name)}
-          error={errors.model_name?.message}
-        >
+        <FormControl isInvalid={Boolean(errors.model_name)}>
           <FormLabel>{t('modelManager.name')}</FormLabel>
           <Input
             {...register('model_name', {
@@ -114,6 +118,9 @@ const AdvancedAddDiffusers = (props: AdvancedAddDiffusersProps) => {
                 value.trim().length > 3 || 'Must be at least 3 characters',
             })}
           />
+          {errors.model_name?.message && (
+            <FormErrorMessage>{errors.model_name?.message}</FormErrorMessage>
+          )}
         </FormControl>
         <FormControl>
           <FormLabel>{t('modelManager.baseModel')}</FormLabel>
@@ -122,10 +129,7 @@ const AdvancedAddDiffusers = (props: AdvancedAddDiffusersProps) => {
             name="base_model"
           />
         </FormControl>
-        <FormControl
-          isInvalid={Boolean(errors.path)}
-          error={errors.path?.message}
-        >
+        <FormControl isInvalid={Boolean(errors.path)}>
           <FormLabel>{t('modelManager.modelLocation')}</FormLabel>
           <Input
             {...register('path', {
@@ -133,7 +137,10 @@ const AdvancedAddDiffusers = (props: AdvancedAddDiffusersProps) => {
                 value.trim().length > 0 || 'Must provide a path',
               onBlur,
             })}
-          />
+          />{' '}
+          {errors.path?.message && (
+            <FormErrorMessage>{errors.path?.message}</FormErrorMessage>
+          )}
         </FormControl>
         <FormControl>
           <FormLabel>{t('modelManager.description')}</FormLabel>
diff --git a/invokeai/frontend/web/src/features/modelManager/subpanels/MergeModelsPanel.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/MergeModelsPanel.tsx
index 94d6574c0e..8c113f5da5 100644
--- a/invokeai/frontend/web/src/features/modelManager/subpanels/MergeModelsPanel.tsx
+++ b/invokeai/frontend/web/src/features/modelManager/subpanels/MergeModelsPanel.tsx
@@ -7,6 +7,7 @@ import {
   CompositeSlider,
   Flex,
   FormControl,
+  FormHelperText,
   FormLabel,
   Input,
   Radio,
@@ -318,7 +319,7 @@ const MergeModelsPanel = () => {
         gap={4}
         bg="base.800"
       >
-        <FormControl helperText={t('modelManager.modelMergeAlphaHelp')}>
+        <FormControl>
           <FormLabel>{t('modelManager.alpha')}</FormLabel>
           <CompositeSlider
             min={0.01}
@@ -337,6 +338,9 @@ const MergeModelsPanel = () => {
             onChange={handleChangeModelMergeAlpha}
             onReset={handleResetModelMergeAlpha}
           />
+          <FormHelperText>
+            {t('modelManager.modelMergeAlphaHelp')}
+          </FormHelperText>
         </FormControl>
       </Flex>
 
diff --git a/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/CheckpointModelEdit.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/CheckpointModelEdit.tsx
index 7f76140555..549c725b34 100644
--- a/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/CheckpointModelEdit.tsx
+++ b/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/CheckpointModelEdit.tsx
@@ -5,6 +5,7 @@ import {
   Divider,
   Flex,
   FormControl,
+  FormErrorMessage,
   FormLabel,
   Input,
   Text,
@@ -139,10 +140,7 @@ const CheckpointModelEdit = (props: CheckpointModelEditProps) => {
       >
         <form onSubmit={handleSubmit(onSubmit)}>
           <Flex flexDirection="column" overflowY="scroll" gap={4}>
-            <FormControl
-              isInvalid={Boolean(errors.model_name)}
-              error={errors.model_name?.message}
-            >
+            <FormControl isInvalid={Boolean(errors.model_name)}>
               <FormLabel>{t('modelManager.name')}</FormLabel>
               <Input
                 {...register('model_name', {
@@ -150,6 +148,11 @@ const CheckpointModelEdit = (props: CheckpointModelEditProps) => {
                     value.trim().length > 3 || 'Must be at least 3 characters',
                 })}
               />
+              {errors.model_name?.message && (
+                <FormErrorMessage>
+                  {errors.model_name?.message}
+                </FormErrorMessage>
+              )}
             </FormControl>
             <FormControl>
               <FormLabel>{t('modelManager.description')}</FormLabel>
@@ -163,10 +166,7 @@ const CheckpointModelEdit = (props: CheckpointModelEditProps) => {
               control={control}
               name="variant"
             />
-            <FormControl
-              isInvalid={Boolean(errors.path)}
-              error={errors.path?.message}
-            >
+            <FormControl isInvalid={Boolean(errors.path)}>
               <FormLabel>{t('modelManager.modelLocation')}</FormLabel>
               <Input
                 {...register('path', {
@@ -174,6 +174,9 @@ const CheckpointModelEdit = (props: CheckpointModelEditProps) => {
                     value.trim().length > 0 || 'Must provide a path',
                 })}
               />
+              {errors.path?.message && (
+                <FormErrorMessage>{errors.path?.message}</FormErrorMessage>
+              )}
             </FormControl>
             <FormControl>
               <FormLabel>{t('modelManager.vaeLocation')}</FormLabel>
diff --git a/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/DiffusersModelEdit.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/DiffusersModelEdit.tsx
index 03f7c7ffaf..a873d06e61 100644
--- a/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/DiffusersModelEdit.tsx
+++ b/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/DiffusersModelEdit.tsx
@@ -3,6 +3,7 @@ import {
   Divider,
   Flex,
   FormControl,
+  FormErrorMessage,
   FormLabel,
   Input,
   Text,
@@ -103,10 +104,7 @@ const DiffusersModelEdit = (props: DiffusersModelEditProps) => {
 
       <form onSubmit={handleSubmit(onSubmit)}>
         <Flex flexDirection="column" overflowY="scroll" gap={4}>
-          <FormControl
-            isInvalid={Boolean(errors.model_name)}
-            error={errors.model_name?.message}
-          >
+          <FormControl isInvalid={Boolean(errors.model_name)}>
             <FormLabel>{t('modelManager.name')}</FormLabel>
             <Input
               {...register('model_name', {
@@ -114,6 +112,9 @@ const DiffusersModelEdit = (props: DiffusersModelEditProps) => {
                   value.trim().length > 3 || 'Must be at least 3 characters',
               })}
             />
+            {errors.model_name?.message && (
+              <FormErrorMessage>{errors.model_name?.message}</FormErrorMessage>
+            )}
           </FormControl>
           <FormControl>
             <FormLabel>{t('modelManager.description')}</FormLabel>
@@ -127,10 +128,7 @@ const DiffusersModelEdit = (props: DiffusersModelEditProps) => {
             control={control}
             name="variant"
           />
-          <FormControl
-            isInvalid={Boolean(errors.path)}
-            error={errors.path?.message}
-          >
+          <FormControl isInvalid={Boolean(errors.path)}>
             <FormLabel>{t('modelManager.modelLocation')}</FormLabel>
             <Input
               {...register('path', {
@@ -138,6 +136,9 @@ const DiffusersModelEdit = (props: DiffusersModelEditProps) => {
                   value.trim().length > 0 || 'Must provide a path',
               })}
             />
+            {errors.path?.message && (
+              <FormErrorMessage>{errors.path?.message}</FormErrorMessage>
+            )}
           </FormControl>
           <FormControl>
             <FormLabel>{t('modelManager.vaeLocation')}</FormLabel>
diff --git a/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/LoRAModelEdit.tsx b/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/LoRAModelEdit.tsx
index 3e0b4c91ee..67a6f5c1e5 100644
--- a/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/LoRAModelEdit.tsx
+++ b/invokeai/frontend/web/src/features/modelManager/subpanels/ModelManagerPanel/LoRAModelEdit.tsx
@@ -3,6 +3,7 @@ import {
   Divider,
   Flex,
   FormControl,
+  FormErrorMessage,
   FormLabel,
   Input,
   Text,
@@ -104,10 +105,7 @@ const LoRAModelEdit = (props: LoRAModelEditProps) => {
 
       <form onSubmit={handleSubmit(onSubmit)}>
         <Flex flexDirection="column" overflowY="scroll" gap={4}>
-          <FormControl
-            isInvalid={Boolean(errors.model_name)}
-            error={errors.model_name?.message}
-          >
+          <FormControl isInvalid={Boolean(errors.model_name)}>
             <FormLabel>{t('modelManager.name')}</FormLabel>
             <Input
               {...register('model_name', {
@@ -115,6 +113,9 @@ const LoRAModelEdit = (props: LoRAModelEditProps) => {
                   value.trim().length > 3 || 'Must be at least 3 characters',
               })}
             />
+            {errors.model_name?.message && (
+              <FormErrorMessage>{errors.model_name?.message}</FormErrorMessage>
+            )}
           </FormControl>
           <FormControl>
             <FormLabel>{t('modelManager.description')}</FormLabel>
@@ -125,10 +126,7 @@ const LoRAModelEdit = (props: LoRAModelEditProps) => {
             name="base_model"
           />
 
-          <FormControl
-            isInvalid={Boolean(errors.path)}
-            error={errors.path?.message}
-          >
+          <FormControl isInvalid={Boolean(errors.path)}>
             <FormLabel>{t('modelManager.modelLocation')}</FormLabel>
             <Input
               {...register('path', {
@@ -136,6 +134,9 @@ const LoRAModelEdit = (props: LoRAModelEditProps) => {
                   value.trim().length > 0 || 'Must provide a path',
               })}
             />
+            {errors.path?.message && (
+              <FormErrorMessage>{errors.path?.message}</FormErrorMessage>
+            )}
           </FormControl>
           <Button type="submit" isLoading={isLoading}>
             {t('modelManager.updateModel')}
diff --git a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopRightPanel/WorkflowEditorSettings.tsx b/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopRightPanel/WorkflowEditorSettings.tsx
index ebfa660141..99c4f38ba0 100644
--- a/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopRightPanel/WorkflowEditorSettings.tsx
+++ b/invokeai/frontend/web/src/features/nodes/components/flow/panels/TopRightPanel/WorkflowEditorSettings.tsx
@@ -2,6 +2,7 @@ import {
   Divider,
   Flex,
   FormControl,
+  FormHelperText,
   FormLabel,
   Heading,
   Modal,
@@ -110,46 +111,55 @@ const WorkflowEditorSettings = ({ children }: Props) => {
           <ModalBody>
             <Flex flexDirection="column" gap={4} py={4}>
               <Heading size="sm">{t('parameters.general')}</Heading>
-              <FormControl helperText={t('nodes.animatedEdgesHelp')}>
+              <FormControl>
                 <FormLabel>{t('nodes.animatedEdges')}</FormLabel>
                 <Switch
                   onChange={handleChangeShouldAnimate}
                   isChecked={shouldAnimateEdges}
                 />
+                <FormHelperText>{t('nodes.animatedEdgesHelp')}</FormHelperText>
               </FormControl>
               <Divider />
-              <FormControl helperText={t('nodes.snapToGridHelp')}>
+              <FormControl>
                 <FormLabel>{t('nodes.snapToGrid')}</FormLabel>
                 <Switch
                   isChecked={shouldSnapToGrid}
                   onChange={handleChangeShouldSnap}
                 />
+                <FormHelperText>{t('nodes.snapToGridHelp')}</FormHelperText>
               </FormControl>
               <Divider />
-              <FormControl helperText={t('nodes.colorCodeEdgesHelp')}>
+              <FormControl>
                 <FormLabel>{t('nodes.colorCodeEdges')}</FormLabel>
                 <Switch
                   isChecked={shouldColorEdges}
                   onChange={handleChangeShouldColor}
                 />
+                <FormHelperText>{t('nodes.colorCodeEdgesHelp')}</FormHelperText>
               </FormControl>
               <Divider />
-              <FormControl helperText={t('nodes.fullyContainNodesHelp')}>
+              <FormControl>
                 <FormLabel>{t('nodes.fullyContainNodes')}</FormLabel>
                 <Switch
                   isChecked={selectionModeIsChecked}
                   onChange={handleChangeSelectionMode}
                 />
+                <FormHelperText>
+                  {t('nodes.fullyContainNodesHelp')}
+                </FormHelperText>
               </FormControl>
               <Heading size="sm" pt={4}>
                 {t('common.advanced')}
               </Heading>
-              <FormControl helperText={t('nodes.validateConnectionsHelp')}>
+              <FormControl>
                 <FormLabel>{t('nodes.validateConnections')}</FormLabel>
                 <Switch
                   isChecked={shouldValidateGraph}
                   onChange={handleChangeShouldValidate}
                 />
+                <FormHelperText>
+                  {t('nodes.validateConnectionsHelp')}
+                </FormHelperText>
               </FormControl>
               <ReloadNodeTemplatesButton />
             </Flex>
diff --git a/invokeai/frontend/web/src/features/parameters/components/Advanced/ParamCFGRescaleMultiplier.tsx b/invokeai/frontend/web/src/features/parameters/components/Advanced/ParamCFGRescaleMultiplier.tsx
index 19f9d6f7da..b1646b9b87 100644
--- a/invokeai/frontend/web/src/features/parameters/components/Advanced/ParamCFGRescaleMultiplier.tsx
+++ b/invokeai/frontend/web/src/features/parameters/components/Advanced/ParamCFGRescaleMultiplier.tsx
@@ -5,6 +5,7 @@ import {
   FormLabel,
 } from '@invoke-ai/ui';
 import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
 import { setCfgRescaleMultiplier } from 'features/parameters/store/generationSlice';
 import { memo, useCallback } from 'react';
 import { useTranslation } from 'react-i18next';
@@ -44,8 +45,10 @@ const ParamCFGRescaleMultiplier = () => {
   );
 
   return (
-    <FormControl feature="paramCFGRescaleMultiplier">
-      <FormLabel>{t('parameters.cfgRescaleMultiplier')}</FormLabel>
+    <FormControl>
+      <InformationalPopover feature="paramCFGRescaleMultiplier">
+        <FormLabel>{t('parameters.cfgRescaleMultiplier')}</FormLabel>
+      </InformationalPopover>
       <CompositeSlider
         value={cfgRescaleMultiplier}
         defaultValue={initial}
diff --git a/invokeai/frontend/web/src/features/parameters/components/Advanced/ParamClipSkip.tsx b/invokeai/frontend/web/src/features/parameters/components/Advanced/ParamClipSkip.tsx
index 939bab747a..ffb2e76829 100644
--- a/invokeai/frontend/web/src/features/parameters/components/Advanced/ParamClipSkip.tsx
+++ b/invokeai/frontend/web/src/features/parameters/components/Advanced/ParamClipSkip.tsx
@@ -5,6 +5,7 @@ import {
   FormLabel,
 } from '@invoke-ai/ui';
 import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
 import { setClipSkip } from 'features/parameters/store/generationSlice';
 import { CLIP_SKIP_MAP } from 'features/parameters/types/constants';
 import { memo, useCallback, useMemo } from 'react';
@@ -50,8 +51,10 @@ const ParamClipSkip = () => {
   }
 
   return (
-    <FormControl feature="clipSkip">
-      <FormLabel>{t('parameters.clipSkip')}</FormLabel>
+    <FormControl>
+      <InformationalPopover feature="clipSkip">
+        <FormLabel>{t('parameters.clipSkip')}</FormLabel>
+      </InformationalPopover>
       <CompositeSlider
         value={clipSkip}
         defaultValue={initial}
diff --git a/invokeai/frontend/web/src/features/parameters/components/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceMode.tsx b/invokeai/frontend/web/src/features/parameters/components/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceMode.tsx
index 1c6b538c50..fcdc1eee6f 100644
--- a/invokeai/frontend/web/src/features/parameters/components/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceMode.tsx
+++ b/invokeai/frontend/web/src/features/parameters/components/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceMode.tsx
@@ -1,6 +1,7 @@
 import type { ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui';
 import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui';
 import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
 import { setCanvasCoherenceMode } from 'features/parameters/store/generationSlice';
 import { isParameterCanvasCoherenceMode } from 'features/parameters/types/parameterSchemas';
 import { memo, useCallback, useMemo } from 'react';
@@ -39,8 +40,10 @@ const ParamCanvasCoherenceMode = () => {
   );
 
   return (
-    <FormControl feature="compositingCoherenceMode">
-      <FormLabel>{t('parameters.coherenceMode')}</FormLabel>
+    <FormControl>
+      <InformationalPopover feature="compositingCoherenceMode">
+        <FormLabel>{t('parameters.coherenceMode')}</FormLabel>
+      </InformationalPopover>
       <Combobox options={options} value={value} onChange={onChange} />
     </FormControl>
   );
diff --git a/invokeai/frontend/web/src/features/parameters/components/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceSteps.tsx b/invokeai/frontend/web/src/features/parameters/components/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceSteps.tsx
index 79fe2d171b..9f6ca54f7a 100644
--- a/invokeai/frontend/web/src/features/parameters/components/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceSteps.tsx
+++ b/invokeai/frontend/web/src/features/parameters/components/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceSteps.tsx
@@ -5,6 +5,7 @@ import {
   FormLabel,
 } from '@invoke-ai/ui';
 import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
 import { setCanvasCoherenceSteps } from 'features/parameters/store/generationSlice';
 import { memo, useCallback } from 'react';
 import { useTranslation } from 'react-i18next';
@@ -46,8 +47,10 @@ const ParamCanvasCoherenceSteps = () => {
   );
 
   return (
-    <FormControl feature="compositingCoherenceSteps">
-      <FormLabel>{t('parameters.coherenceSteps')}</FormLabel>
+    <FormControl>
+      <InformationalPopover feature="compositingCoherenceSteps">
+        <FormLabel>{t('parameters.coherenceSteps')}</FormLabel>
+      </InformationalPopover>
       <CompositeSlider
         min={sliderMin}
         max={sliderMax}
diff --git a/invokeai/frontend/web/src/features/parameters/components/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceStrength.tsx b/invokeai/frontend/web/src/features/parameters/components/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceStrength.tsx
index 369bdb1f33..ecac32251e 100644
--- a/invokeai/frontend/web/src/features/parameters/components/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceStrength.tsx
+++ b/invokeai/frontend/web/src/features/parameters/components/Canvas/Compositing/CoherencePass/ParamCanvasCoherenceStrength.tsx
@@ -5,6 +5,7 @@ import {
   FormLabel,
 } from '@invoke-ai/ui';
 import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
 import { setCanvasCoherenceStrength } from 'features/parameters/store/generationSlice';
 import { memo, useCallback } from 'react';
 import { useTranslation } from 'react-i18next';
@@ -24,8 +25,10 @@ const ParamCanvasCoherenceStrength = () => {
   );
 
   return (
-    <FormControl feature="compositingStrength">
-      <FormLabel>{t('parameters.coherenceStrength')}</FormLabel>
+    <FormControl>
+      <InformationalPopover feature="compositingStrength">
+        <FormLabel>{t('parameters.coherenceStrength')}</FormLabel>
+      </InformationalPopover>
       <CompositeSlider
         min={0}
         max={1}
diff --git a/invokeai/frontend/web/src/features/parameters/components/Canvas/Compositing/MaskAdjustment/ParamMaskBlur.tsx b/invokeai/frontend/web/src/features/parameters/components/Canvas/Compositing/MaskAdjustment/ParamMaskBlur.tsx
index aa491d39c4..dd250fb6b0 100644
--- a/invokeai/frontend/web/src/features/parameters/components/Canvas/Compositing/MaskAdjustment/ParamMaskBlur.tsx
+++ b/invokeai/frontend/web/src/features/parameters/components/Canvas/Compositing/MaskAdjustment/ParamMaskBlur.tsx
@@ -5,6 +5,7 @@ import {
   FormLabel,
 } from '@invoke-ai/ui';
 import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
 import { setMaskBlur } from 'features/parameters/store/generationSlice';
 import { memo, useCallback } from 'react';
 import { useTranslation } from 'react-i18next';
@@ -33,8 +34,10 @@ const ParamMaskBlur = () => {
   );
 
   return (
-    <FormControl feature="compositingBlur">
-      <FormLabel>{t('parameters.maskBlur')}</FormLabel>
+    <FormControl>
+      <InformationalPopover feature="compositingBlur">
+        <FormLabel>{t('parameters.maskBlur')}</FormLabel>
+      </InformationalPopover>
       <CompositeSlider
         min={sliderMin}
         max={sliderMax}
diff --git a/invokeai/frontend/web/src/features/parameters/components/Canvas/Compositing/MaskAdjustment/ParamMaskBlurMethod.tsx b/invokeai/frontend/web/src/features/parameters/components/Canvas/Compositing/MaskAdjustment/ParamMaskBlurMethod.tsx
index d00ffa955a..b81da48b45 100644
--- a/invokeai/frontend/web/src/features/parameters/components/Canvas/Compositing/MaskAdjustment/ParamMaskBlurMethod.tsx
+++ b/invokeai/frontend/web/src/features/parameters/components/Canvas/Compositing/MaskAdjustment/ParamMaskBlurMethod.tsx
@@ -1,6 +1,7 @@
 import type { ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui';
 import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui';
 import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
 import { setMaskBlurMethod } from 'features/parameters/store/generationSlice';
 import { isParameterMaskBlurMethod } from 'features/parameters/types/parameterSchemas';
 import { memo, useCallback, useMemo } from 'react';
@@ -32,8 +33,10 @@ const ParamMaskBlurMethod = () => {
   );
 
   return (
-    <FormControl feature="compositingBlurMethod">
-      <FormLabel>{t('parameters.maskBlurMethod')}</FormLabel>
+    <FormControl>
+      <InformationalPopover feature="compositingBlurMethod">
+        <FormLabel>{t('parameters.maskBlurMethod')}</FormLabel>
+      </InformationalPopover>
       <Combobox value={value} onChange={onChange} options={options} />
     </FormControl>
   );
diff --git a/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillMethod.tsx b/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillMethod.tsx
index 51f4bcff9c..62a51fed2c 100644
--- a/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillMethod.tsx
+++ b/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamInfillMethod.tsx
@@ -1,6 +1,7 @@
 import type { ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui';
 import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui';
 import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
 import { setInfillMethod } from 'features/parameters/store/generationSlice';
 import { memo, useCallback, useMemo } from 'react';
 import { useTranslation } from 'react-i18next';
@@ -38,8 +39,10 @@ const ParamInfillMethod = () => {
   );
 
   return (
-    <FormControl isDisabled={options.length === 0} feature="infillMethod">
-      <FormLabel>{t('parameters.infillMethod')}</FormLabel>
+    <FormControl isDisabled={options.length === 0}>
+      <InformationalPopover feature="infillMethod">
+        <FormLabel>{t('parameters.infillMethod')}</FormLabel>
+      </InformationalPopover>
       <Combobox value={value} options={options} onChange={onChange} />
     </FormControl>
   );
diff --git a/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamScaleBeforeProcessing.tsx b/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamScaleBeforeProcessing.tsx
index 340ec8932c..bf4b9be2ba 100644
--- a/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamScaleBeforeProcessing.tsx
+++ b/invokeai/frontend/web/src/features/parameters/components/Canvas/InfillAndScaling/ParamScaleBeforeProcessing.tsx
@@ -1,6 +1,7 @@
 import type { ComboboxOnChange, ComboboxOption } from '@invoke-ai/ui';
 import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui';
 import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
 import { setBoundingBoxScaleMethod } from 'features/canvas/store/canvasSlice';
 import { isBoundingBoxScaleMethod } from 'features/canvas/store/canvasTypes';
 import { selectOptimalDimension } from 'features/parameters/store/generationSlice';
@@ -37,8 +38,10 @@ const ParamScaleBeforeProcessing = () => {
   );
 
   return (
-    <FormControl feature="scaleBeforeProcessing">
-      <FormLabel>{t('parameters.scaleBeforeProcessing')}</FormLabel>
+    <FormControl>
+      <InformationalPopover feature="scaleBeforeProcessing">
+        <FormLabel>{t('parameters.scaleBeforeProcessing')}</FormLabel>
+      </InformationalPopover>
       <Combobox value={value} options={OPTIONS} onChange={onChange} />
     </FormControl>
   );
diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamCFGScale.tsx b/invokeai/frontend/web/src/features/parameters/components/Core/ParamCFGScale.tsx
index 1b39bf4c88..985d5763a5 100644
--- a/invokeai/frontend/web/src/features/parameters/components/Core/ParamCFGScale.tsx
+++ b/invokeai/frontend/web/src/features/parameters/components/Core/ParamCFGScale.tsx
@@ -5,6 +5,7 @@ import {
   FormLabel,
 } from '@invoke-ai/ui';
 import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
 import { setCfgScale } from 'features/parameters/store/generationSlice';
 import { memo, useCallback, useMemo } from 'react';
 import { useTranslation } from 'react-i18next';
@@ -34,8 +35,10 @@ const ParamCFGScale = () => {
   );
 
   return (
-    <FormControl feature="paramCFGScale">
-      <FormLabel>{t('parameters.cfgScale')}</FormLabel>
+    <FormControl>
+      <InformationalPopover feature="paramCFGScale">
+        <FormLabel>{t('parameters.cfgScale')}</FormLabel>
+      </InformationalPopover>
       <CompositeSlider
         value={cfgScale}
         defaultValue={initial}
diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamScheduler.tsx b/invokeai/frontend/web/src/features/parameters/components/Core/ParamScheduler.tsx
index 32a6d5fc38..08d0ae635f 100644
--- a/invokeai/frontend/web/src/features/parameters/components/Core/ParamScheduler.tsx
+++ b/invokeai/frontend/web/src/features/parameters/components/Core/ParamScheduler.tsx
@@ -1,6 +1,7 @@
 import type { ComboboxOnChange } from '@invoke-ai/ui';
 import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui';
 import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
 import { setScheduler } from 'features/parameters/store/generationSlice';
 import { SCHEDULER_OPTIONS } from 'features/parameters/types/constants';
 import { isParameterScheduler } from 'features/parameters/types/parameterSchemas';
@@ -28,8 +29,10 @@ const ParamScheduler = () => {
   );
 
   return (
-    <FormControl feature="paramScheduler">
-      <FormLabel>{t('parameters.scheduler')}</FormLabel>
+    <FormControl>
+      <InformationalPopover feature="paramScheduler">
+        <FormLabel>{t('parameters.scheduler')}</FormLabel>
+      </InformationalPopover>
       <Combobox value={value} options={SCHEDULER_OPTIONS} onChange={onChange} />
     </FormControl>
   );
diff --git a/invokeai/frontend/web/src/features/parameters/components/Core/ParamSteps.tsx b/invokeai/frontend/web/src/features/parameters/components/Core/ParamSteps.tsx
index 689fcd42c1..3401f503ea 100644
--- a/invokeai/frontend/web/src/features/parameters/components/Core/ParamSteps.tsx
+++ b/invokeai/frontend/web/src/features/parameters/components/Core/ParamSteps.tsx
@@ -5,6 +5,7 @@ import {
   FormLabel,
 } from '@invoke-ai/ui';
 import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
 import { setSteps } from 'features/parameters/store/generationSlice';
 import { memo, useCallback, useMemo } from 'react';
 import { useTranslation } from 'react-i18next';
@@ -36,8 +37,10 @@ const ParamSteps = () => {
   );
 
   return (
-    <FormControl feature="paramSteps">
-      <FormLabel>{t('parameters.steps')}</FormLabel>
+    <FormControl>
+      <InformationalPopover feature="paramSteps">
+        <FormLabel>{t('parameters.steps')}</FormLabel>
+      </InformationalPopover>
       <CompositeSlider
         value={steps}
         defaultValue={initial}
diff --git a/invokeai/frontend/web/src/features/parameters/components/ImageToImage/ImageToImageStrength.tsx b/invokeai/frontend/web/src/features/parameters/components/ImageToImage/ImageToImageStrength.tsx
index 4637ea21ea..7f063aa841 100644
--- a/invokeai/frontend/web/src/features/parameters/components/ImageToImage/ImageToImageStrength.tsx
+++ b/invokeai/frontend/web/src/features/parameters/components/ImageToImage/ImageToImageStrength.tsx
@@ -5,6 +5,7 @@ import {
   FormLabel,
 } from '@invoke-ai/ui';
 import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
 import { setImg2imgStrength } from 'features/parameters/store/generationSlice';
 import { memo, useCallback } from 'react';
 import { useTranslation } from 'react-i18next';
@@ -39,8 +40,10 @@ const ImageToImageStrength = () => {
   );
 
   return (
-    <FormControl feature="paramDenoisingStrength">
-      <FormLabel>{`${t('parameters.denoisingStrength')}`}</FormLabel>
+    <FormControl>
+      <InformationalPopover feature="paramDenoisingStrength">
+        <FormLabel>{`${t('parameters.denoisingStrength')}`}</FormLabel>
+      </InformationalPopover>
       <CompositeSlider
         step={coarseStep}
         fineStep={fineStep}
diff --git a/invokeai/frontend/web/src/features/parameters/components/Seed/ParamSeedNumberInput.tsx b/invokeai/frontend/web/src/features/parameters/components/Seed/ParamSeedNumberInput.tsx
index ab33b3ac6e..be6a980927 100644
--- a/invokeai/frontend/web/src/features/parameters/components/Seed/ParamSeedNumberInput.tsx
+++ b/invokeai/frontend/web/src/features/parameters/components/Seed/ParamSeedNumberInput.tsx
@@ -1,6 +1,7 @@
 import { CompositeNumberInput, FormControl, FormLabel } from '@invoke-ai/ui';
 import { NUMPY_RAND_MAX, NUMPY_RAND_MIN } from 'app/constants';
 import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
 import { setSeed } from 'features/parameters/store/generationSlice';
 import { memo, useCallback } from 'react';
 import { useTranslation } from 'react-i18next';
@@ -21,8 +22,10 @@ export const ParamSeedNumberInput = memo(() => {
   );
 
   return (
-    <FormControl flexGrow={1} feature="paramSeed">
-      <FormLabel>{t('parameters.seed')}</FormLabel>
+    <FormControl flexGrow={1}>
+      <InformationalPopover feature="paramSeed">
+        <FormLabel>{t('parameters.seed')}</FormLabel>
+      </InformationalPopover>
       <CompositeNumberInput
         step={1}
         min={NUMPY_RAND_MIN}
diff --git a/invokeai/frontend/web/src/features/parameters/components/VAEModel/ParamVAEModelSelect.tsx b/invokeai/frontend/web/src/features/parameters/components/VAEModel/ParamVAEModelSelect.tsx
index 3188c1ae21..d0072442e7 100644
--- a/invokeai/frontend/web/src/features/parameters/components/VAEModel/ParamVAEModelSelect.tsx
+++ b/invokeai/frontend/web/src/features/parameters/components/VAEModel/ParamVAEModelSelect.tsx
@@ -1,6 +1,7 @@
 import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui';
 import { createMemoizedSelector } from 'app/store/createMemoizedSelector';
 import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
 import { useGroupedModelCombobox } from 'common/hooks/useGroupedModelCombobox';
 import {
   selectGenerationSlice,
@@ -46,12 +47,10 @@ const ParamVAEModelSelect = () => {
     });
 
   return (
-    <FormControl
-      isDisabled={!options.length}
-      isInvalid={!options.length}
-      feature="paramVAE"
-    >
-      <FormLabel>{t('modelManager.vae')}</FormLabel>
+    <FormControl isDisabled={!options.length} isInvalid={!options.length}>
+      <InformationalPopover feature="paramVAE">
+        <FormLabel>{t('modelManager.vae')}</FormLabel>
+      </InformationalPopover>
       <Combobox
         isClearable
         value={value}
diff --git a/invokeai/frontend/web/src/features/parameters/components/VAEModel/ParamVAEPrecision.tsx b/invokeai/frontend/web/src/features/parameters/components/VAEModel/ParamVAEPrecision.tsx
index 24cbdb0925..14b4700dea 100644
--- a/invokeai/frontend/web/src/features/parameters/components/VAEModel/ParamVAEPrecision.tsx
+++ b/invokeai/frontend/web/src/features/parameters/components/VAEModel/ParamVAEPrecision.tsx
@@ -1,6 +1,7 @@
 import type { ComboboxOnChange } from '@invoke-ai/ui';
 import { Combobox, FormControl, FormLabel } from '@invoke-ai/ui';
 import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
 import { vaePrecisionChanged } from 'features/parameters/store/generationSlice';
 import { isParameterPrecision } from 'features/parameters/types/parameterSchemas';
 import { memo, useCallback, useMemo } from 'react';
@@ -33,8 +34,10 @@ const ParamVAEModelSelect = () => {
   );
 
   return (
-    <FormControl feature="paramVAEPrecision" w="14rem" flexShrink={0}>
-      <FormLabel>{t('modelManager.vaePrecision')}</FormLabel>
+    <FormControl w="14rem" flexShrink={0}>
+      <InformationalPopover feature="paramVAEPrecision">
+        <FormLabel>{t('modelManager.vaePrecision')}</FormLabel>
+      </InformationalPopover>
       <Combobox value={value} options={options} onChange={onChange} />
     </FormControl>
   );
diff --git a/invokeai/frontend/web/src/features/queue/components/QueueIterationsNumberInput.tsx b/invokeai/frontend/web/src/features/queue/components/QueueIterationsNumberInput.tsx
index dc50c39b11..10707da730 100644
--- a/invokeai/frontend/web/src/features/queue/components/QueueIterationsNumberInput.tsx
+++ b/invokeai/frontend/web/src/features/queue/components/QueueIterationsNumberInput.tsx
@@ -1,6 +1,6 @@
 import { CompositeNumberInput } from '@invoke-ai/ui';
 import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
-import IAIInformationalPopover from 'common/components/IAIInformationalPopover/IAIInformationalPopover';
+import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
 import { setIterations } from 'features/parameters/store/generationSlice';
 import { memo, useCallback } from 'react';
 
@@ -17,7 +17,7 @@ export const QueueIterationsNumberInput = memo(() => {
   );
 
   return (
-    <IAIInformationalPopover feature="paramIterations">
+    <InformationalPopover feature="paramIterations">
       <CompositeNumberInput
         step={coarseStep}
         fineStep={fineStep}
@@ -34,7 +34,7 @@ export const QueueIterationsNumberInput = memo(() => {
         flexShrink={0}
         variant="iterations"
       />
-    </IAIInformationalPopover>
+    </InformationalPopover>
   );
 });
 
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 59dd6677e0..ed81e9de51 100644
--- a/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx
+++ b/invokeai/frontend/web/src/features/system/components/SettingsModal/SettingsModal.tsx
@@ -15,6 +15,7 @@ import {
   useDisclosure,
 } from '@invoke-ai/ui';
 import { useAppDispatch, useAppSelector } from 'app/store/storeHooks';
+import { InformationalPopover } from 'common/components/InformationalPopover/InformationalPopover';
 import ScrollableContent from 'common/components/OverlayScrollbars/ScrollableContent';
 import { useClearStorage } from 'common/hooks/useClearStorage';
 import { shouldUseCpuNoiseChanged } from 'features/parameters/store/generationSlice';
@@ -261,8 +262,13 @@ const SettingsModal = ({ children, config }: SettingsModalProps) => {
                       onChange={handleChangeShouldAntialiasProgressImage}
                     />
                   </FormControl>
-                  <FormControl feature="noiseUseCPU">
-                    <FormLabel>{t('parameters.useCpuNoise')}</FormLabel>
+                  <FormControl>
+                    <InformationalPopover
+                      feature="noiseUseCPU"
+                      inPortal={false}
+                    >
+                      <FormLabel>{t('parameters.useCpuNoise')}</FormLabel>
+                    </InformationalPopover>
                     <Switch
                       isChecked={shouldUseCpuNoise}
                       onChange={handleChangeShouldUseCpuNoise}
diff --git a/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryPagination.tsx b/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryPagination.tsx
index 997e170942..d000d5863a 100644
--- a/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryPagination.tsx
+++ b/invokeai/frontend/web/src/features/workflowLibrary/components/WorkflowLibraryPagination.tsx
@@ -65,7 +65,7 @@ const WorkflowLibraryPagination = ({ page, setPage, data }: Props) => {
           w={10}
           isDisabled={data.pages === 1}
           onClick={p.page === page ? undefined : p.onClick}
-          variant={p.page === page ? 'invokeAI' : 'ghost'}
+          variant={p.page === page ? 'solid' : 'ghost'}
           key={p.page}
           transitionDuration="0s" // the delay in animation looks jank
         >