Skip to content

Commit

Permalink
Add "Manual/Copy" install mode to dependency manager (#2462)
Browse files Browse the repository at this point in the history
* Add "Manual/Copy" install mode to dependency manager

* add notice about manually restarting

* Update src/renderer/contexts/DependencyContext.tsx

Co-authored-by: Michael Schmidt <[email protected]>

* Update src/renderer/contexts/DependencyContext.tsx

Co-authored-by: Michael Schmidt <[email protected]>

* Update src/renderer/contexts/DependencyContext.tsx

Co-authored-by: Michael Schmidt <[email protected]>

* remove unneeded flags

* update tooltip text

* Replace toast with popover

---------

Co-authored-by: Michael Schmidt <[email protected]>
  • Loading branch information
joeyballentine and RunDevelopment authored Jan 21, 2024
1 parent 213f734 commit 35ae6bc
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 35 deletions.
2 changes: 1 addition & 1 deletion src/common/pip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export const runPip = async (
});
};

const getFindLinks = (dependencies: readonly PyPiPackage[]): string[] => {
export const getFindLinks = (dependencies: readonly PyPiPackage[]): string[] => {
const links = new Set<string>();
for (const p of dependencies) {
if (p.findLink) {
Expand Down
157 changes: 123 additions & 34 deletions src/renderer/contexts/DependencyContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,25 @@ import {
ModalFooter,
ModalHeader,
ModalOverlay,
Popover,
PopoverArrow,
PopoverBody,
PopoverCloseButton,
PopoverContent,
PopoverHeader,
PopoverTrigger,
Progress,
Select,
Spacer,
Spinner,
Switch,
Tag,
Text,
Textarea,
Tooltip,
VStack,
useDisclosure,
} from '@chakra-ui/react';
import { clipboard } from 'electron';
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { BsQuestionCircle, BsTerminalFill } from 'react-icons/bs';
import { HiOutlineRefresh } from 'react-icons/hi';
Expand All @@ -46,7 +54,7 @@ import {
Version,
} from '../../common/common-types';
import { log } from '../../common/log';
import { OnStdio, runPipInstall, runPipUninstall } from '../../common/pip';
import { OnStdio, getFindLinks, runPipInstall, runPipUninstall } from '../../common/pip';
import { noop } from '../../common/util';
import { versionGt } from '../../common/version';
import { Markdown } from '../components/Markdown';
Expand All @@ -56,6 +64,12 @@ import { BackendContext } from './BackendContext';
import { GlobalContext } from './GlobalNodeState';
import { SettingsContext } from './SettingsContext';

const installModes = {
NORMAL: 'Normal',
DIRECT_PIP: 'Direct Pip',
MANUAL_COPY: 'Manual/Copy',
};

export interface DependencyContextValue {
openDependencyManager: () => void;
availableUpdates: number;
Expand Down Expand Up @@ -382,7 +396,7 @@ export const DependencyProvider = memo(({ children }: React.PropsWithChildren<un
const [isSystemPython] = useIsSystemPython;

const [isConsoleOpen, setIsConsoleOpen] = useState(false);
const [usePipDirectly, setUsePipDirectly] = useState(false);
const [installMode, setInstallMode] = useState(installModes.NORMAL);

const { data: installedPyPi, refetch: refetchInstalledPyPi } = useQuery({
queryKey: 'dependencies',
Expand Down Expand Up @@ -444,25 +458,64 @@ export const DependencyProvider = memo(({ children }: React.PropsWithChildren<un
});
};

const installPackage = (p: Package) => {
setInstallingPackage(p);
const {
isOpen: isPopoverOpen,
onToggle: onPopoverToggle,
onClose: onPopoverClose,
} = useDisclosure();
const copyCommandToClipboard = (command: string) => {
clipboard.writeText(command);
onPopoverToggle();
setTimeout(() => {
onPopoverClose();
}, 5000);
};

const installPackage = (pkg: Package) => {
if (installMode === installModes.MANUAL_COPY) {
const deps = pkg.dependencies.map((p) => `${p.pypiName}==${p.version}`);
const findLinks = getFindLinks(pkg.dependencies).flatMap((l) => [
'--extra-index-url',
l,
]);
const args = [
pythonInfo.python,
'-m',
'pip',
'install',
'--upgrade',
...deps,
...findLinks,
];
const cmd = args.join(' ');
copyCommandToClipboard(cmd);
return;
}
setInstallingPackage(pkg);
changePackages(() =>
runPipInstall(
pythonInfo,
p.dependencies,
usePipDirectly ? undefined : setProgress,
pkg.dependencies,
installMode === installModes.NORMAL ? setProgress : undefined,
onStdio
)
);
};

const uninstallPackage = (p: Package) => {
setUninstallingPackage(p);
const uninstallPackage = (pkg: Package) => {
if (installMode === installModes.MANUAL_COPY) {
const deps = pkg.dependencies.map((p) => p.pypiName);
const args = [pythonInfo.python, '-m', 'pip', 'uninstall', ...deps];
const cmd = args.join(' ');
copyCommandToClipboard(cmd);
return;
}
setUninstallingPackage(pkg);
changePackages(() =>
runPipUninstall(
pythonInfo,
p.dependencies,
usePipDirectly ? undefined : setProgress,
pkg.dependencies,
installMode === installModes.NORMAL ? setProgress : undefined,
onStdio
)
);
Expand Down Expand Up @@ -531,29 +584,65 @@ export const DependencyProvider = memo(({ children }: React.PropsWithChildren<un
Python ({pythonInfo.version}) [
{isSystemPython ? 'System' : 'Integrated'}]
</Text>
<HStack>
<HStack gap={2}>
<HStack>
<Switch
isChecked={usePipDirectly}
isDisabled={isRunningShell}
onChange={() => {
setUsePipDirectly(!usePipDirectly);
}}
/>
<Text>Use Pip Directly</Text>
<Tooltip
hasArrow
borderRadius={8}
label="Disable progress bars and use pip to directly download and install the packages. Use this setting if you are having issues installing normally."
maxW="auto"
openDelay={500}
px={2}
py={0}
>
<Center>
<Icon as={BsQuestionCircle} />
</Center>
</Tooltip>
<HStack>
<Popover
closeOnBlur={false}
colorScheme="green"
isOpen={isPopoverOpen}
placement="top"
returnFocusOnClose={false}
onClose={onPopoverClose}
>
<PopoverTrigger>
<Select
isDisabled={isRunningShell}
size="md"
value={installMode}
onChange={(e) => {
setInstallMode(e.target.value);
}}
>
<option>{installModes.NORMAL}</option>
<option>{installModes.DIRECT_PIP}</option>
<option>{installModes.MANUAL_COPY}</option>
</Select>
</PopoverTrigger>
<PopoverContent>
<PopoverHeader fontWeight="semibold">
Command copied to clipboard.
</PopoverHeader>
<PopoverArrow />
<PopoverCloseButton />
<PopoverBody>
Open up an external terminal, paste the
command, and run it. When it is done
running, manually restart chaiNNer.
</PopoverBody>
</PopoverContent>
</Popover>

<Tooltip
hasArrow
borderRadius={8}
label={
<Markdown nonInteractive>
{'The dependency install mode. ChaiNNer supports 3 ways of installing packages:\n\n' +
'- Normal: This is the default installation mode. This mode manually downloads packages in order to display download progress.\n' +
'- Direct Pip: This will invoke pip more directly, like installing python packages normally. Use this setting when having issues with Normal. Note: this makes it impossible to show installation progress.\n' +
'- Manual/Copy: Copy the pip install command to your clipboard for you to run in your own terminal. You will have to manually restart chaiNner afterwards.'}
</Markdown>
}
openDelay={500}
px={2}
py={0}
>
<Center>
<Icon as={BsQuestionCircle} />
</Center>
</Tooltip>
</HStack>
</HStack>
<Button
aria-label={isConsoleOpen ? 'Hide Console' : 'View Console'}
Expand Down Expand Up @@ -615,7 +704,7 @@ export const DependencyProvider = memo(({ children }: React.PropsWithChildren<un
key={p.id}
p={p}
progress={
!usePipDirectly &&
installMode === installModes.NORMAL &&
isRunningShell &&
(installingPackage || uninstallingPackage)
?.id === p.id
Expand Down

0 comments on commit 35ae6bc

Please sign in to comment.