Skip to content

Commit

Permalink
Add UI migration states
Browse files Browse the repository at this point in the history
  • Loading branch information
vovakulikov committed Oct 22, 2024
1 parent 850363d commit 7c95dce
Show file tree
Hide file tree
Showing 7 changed files with 323 additions and 2 deletions.
24 changes: 24 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1380,6 +1380,7 @@
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-form": "^0.1.0",
"@radix-ui/react-popover": "^1.0.7",
"@radix-ui/react-progress": "^1.1.0",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-tabs": "^1.1.0",
"@radix-ui/react-tooltip": "^1.0.7",
Expand Down
4 changes: 2 additions & 2 deletions vscode/src/chat/chat-view/prompts-migration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export async function startPromptsMigration(): Promise<void> {
const commandKey = command.key ?? command.slashCommand

try {
const prompts = await graphqlClient.queryPrompts(commandKey.replace(/\s+/g,'-'))
const prompts = await graphqlClient.queryPrompts(commandKey.replace(/\s+/g, '-'))

// If there is no prompts associated with the command include this
// command to migration
Expand All @@ -105,7 +105,7 @@ export async function startPromptsMigration(): Promise<void> {

for (let index = 0; index < commands.length; index++) {
const command = commands[index]
const commandKey = (command.key ?? command.slashCommand).replace(/\s+/g,'-')
const commandKey = (command.key ?? command.slashCommand).replace(/\s+/g, '-')

const newPrompt = await graphqlClient.createPrompt({
ownerId: currentUserId,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@

.root {
display: flex;
flex-direction: column;
padding: 0.75rem;
border-radius: 3px;
border: 1px solid var(--vscode-dropdown-border);
background-color: var(--vscode-dropdown-background);
}

.icons-header {
display: flex;
gap: 0.5rem;
margin-bottom: 0.5rem;
}

.heading {
font-size: 1rem;
font-weight: 500;
margin-bottom: 0.25rem;
}

.description-text {
margin-bottom: 0.75rem;
line-height: 1rem;
}

.actions {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 0.5rem;
}

.action {
flex-shrink: 0;
}

.footer {
margin-top: 0.75rem;
line-height: 1rem;
font-size: 12px;
}

.inner-container {
margin-top: 0.5rem;
}

.loader {
width: 100%;
height: 0.5rem;
position: relative;
overflow: hidden;
margin-top: 0.5rem;
margin-bottom: 0.5rem;
border-radius: 3px;
background-color: var(--vscode-editor-background);

/* Fix overflow clipping in Safari */
/* https://gist.github.com/domske/b66047671c780a238b51c51ffde8d3a0 */
transform: translateZ(0);

&-indicator {
width: 100%;
height: 100%;
background-color: var(--vscode-button-background);
transition: transform 660ms cubic-bezier(0.65, 0, 0.35, 1);
}
}

.error {
color: #de3400;
background-color: #f7bcbc;
line-height: 150%;
padding: 0.5rem;
word-break: break-word;
border-left: 0.5rem solid #d8000c;
font-weight: bold;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import type { Meta } from '@storybook/react'

import { VSCodeStandaloneComponent, VSCodeWebview } from '../../storybook/VSCodeStoryDecorator'

import type { StoryObj } from '@storybook/react'
import { PromptsMigration } from './PromptsMigration'

const meta: Meta<typeof PromptsMigration> = {
title: 'cody/PromptsMigration',
component: PromptsMigration,
decorators: [VSCodeWebview, VSCodeStandaloneComponent],
}

export default meta

type Story = StoryObj<typeof PromptsMigration>

export const DefaultInitialState: Story = {
args: {
status: 'initial',
isMigrationAvailable: false,
},
}

export const InitialStateWithAvailableMigration: Story = {
args: {
status: 'initial',
isMigrationAvailable: true,
},
}

export const LoadingStateScanning: Story = {
args: {
status: 'loading',
migratedPrompts: 0,
promptsToMigrate: undefined,
},
}

export const LoadingStateMigrating: Story = {
args: {
status: 'loading',
migratedPrompts: 1,
promptsToMigrate: 10,
},
}

export const ErroredStateMigrating: Story = {
args: {
status: 'error',
errorMessage: 'some migration error happened',
},
}

export const SuccessfulStateMigrating: Story = {
args: {
status: 'finished',
},
}
157 changes: 157 additions & 0 deletions vscode/webviews/components/promptsMigration/PromptsMigration.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import * as Progress from '@radix-ui/react-progress'
import { clsx } from 'clsx'
import { ArrowRight, BookText, LucideExternalLink, PencilRuler, SquareChevronRight } from 'lucide-react'
import type { FC } from 'react'

import { LoadingDots } from '../../chat/components/LoadingDots'
import { Button } from '../../components/shadcn/ui/button'

import styles from './PromptsMigration.module.css'

type PromptsMigrationProps =
| { status: 'initial'; isMigrationAvailable: boolean }
| { status: 'loading'; migratedPrompts: number; promptsToMigrate: number | undefined }
| { status: 'error'; errorMessage: string }
| { status: 'finished' }

export const PromptsMigration: FC<PromptsMigrationProps> = props => {
const { status } = props

return (
<div className={styles.root}>
<header className={clsx('tw-text-muted-foreground', styles.iconsHeader)}>
<PencilRuler size={20} />
<ArrowRight size={20} />
<BookText size={20} />
</header>

{status === 'initial' && (
<PromptsMigrationInitial isMigrationAvailable={props.isMigrationAvailable} />
)}

{status === 'loading' && (
<PromptsMigrationLoading
migratedPrompts={props.migratedPrompts}
promptsToMigrate={props.promptsToMigrate}
/>
)}

{status === 'error' && <PromptMigrationFailed errorMessage={props.errorMessage} />}
{status === 'finished' && <PromptMigrationSuccess />}
</div>
)
}

interface PromptsMigrationInitial {
isMigrationAvailable: boolean
}

const PromptsMigrationInitial: FC<PromptsMigrationInitial> = props => {
const { isMigrationAvailable } = props

return (
<>
<h3 className={styles.heading}>Commands are now Prompts</h3>

<span className={styles.descriptionText}>
Prompts are assuming the features of commands, including custom commands.
</span>

<div className={styles.actions}>
{isMigrationAvailable && (
<Button variant="default" className={styles.action}>
<SquareChevronRight size={16} />
Migrate commands
</Button>
)}

<Button variant="outline" className={styles.action}>
Explore docs
<LucideExternalLink size={16} />
</Button>
</div>

{isMigrationAvailable && (
<span className={clsx(styles.footer, 'tw-text-muted-foreground')}>
Migrate your local custom commands into prompts to keep them.{' '}
<span className="tw-underline">Commands will be deprecated.</span>
</span>
)}
</>
)
}

interface PromptsMigrationLoadingProps {
migratedPrompts: number
promptsToMigrate: number | undefined
}

const PromptsMigrationLoading: FC<PromptsMigrationLoadingProps> = props => {
const { migratedPrompts, promptsToMigrate } = props
const isScanningPromptLibrary = promptsToMigrate === undefined

return (
<div className={styles.innerContainer}>
{isScanningPromptLibrary && (
<>
<span className={styles.descriptionText}>
Scanning prompts library and custom commands{' '}
</span>
<LoadingDots />
</>
)}

{!isScanningPromptLibrary && (
<>
<span className={styles.descriptionText}>
Migrating, {migratedPrompts} out of {promptsToMigrate} commands.
</span>

<Progress.Root
className={styles.loader}
value={(migratedPrompts / promptsToMigrate) * 100}
>
<Progress.Indicator
className={styles.loaderIndicator}
style={{
transform: `translateX(-${
100 - (migratedPrompts / promptsToMigrate) * 100
}%)`,
}}
/>
</Progress.Root>
</>
)}
</div>
)
}

interface PromptMigrationFailedProps {
errorMessage: string
}

const PromptMigrationFailed: FC<PromptMigrationFailedProps> = props => {
const { errorMessage } = props

return (
<div className={styles.innerContainer}>
<div className={styles.error}>{errorMessage}</div>

<Button variant="outline" className="tw-mt-5">
<SquareChevronRight size={16} />
Try again
</Button>
</div>
)
}

const PromptMigrationSuccess: FC = () => {
return (
<>
<h3 className={styles.heading}>Migration completed</h3>
<span className={styles.descriptionText}>
All custom commands were migrated and now available in prompts library.
</span>
</>
)
}
1 change: 1 addition & 0 deletions vscode/webviews/storybook/VSCodeStoryDecorator.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ body {
}

.container--webview {
padding: 0.5rem;
max-height: max(500px, calc(100vh - 6rem));
overflow: auto;
}
Expand Down

0 comments on commit 7c95dce

Please sign in to comment.