Skip to content

Commit

Permalink
feat: support upscale and variation in midjourney, support image zoom (
Browse files Browse the repository at this point in the history
  • Loading branch information
lvqq authored May 24, 2023
1 parent 57334e4 commit 9c280ba
Show file tree
Hide file tree
Showing 11 changed files with 434 additions and 65 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
"markdown-it": "^13.0.1",
"markdown-it-highlightjs": "^4.0.1",
"markdown-it-kbd": "^2.2.2",
"midjourney-fetch": "0.1.4",
"medium-zoom": "^1.0.8",
"midjourney-fetch": "1.0.0",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"replicate-fetch": "^0.1.1",
Expand Down
22 changes: 18 additions & 4 deletions pnpm-lock.yaml

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

83 changes: 60 additions & 23 deletions src/components/MessageBox/index.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
import { FC, useCallback, useContext, useEffect } from 'react';
import { throttle } from 'lodash-es';
import type { MessageType } from 'midjourney-fetch';
import GlobalContext from '@contexts/global';
import { ConversationMode, Message } from '@interfaces';
import markdown from '@utils/markdown';
import { getRelativeTime } from '@utils/date';
import SystemAvatar from '@components/Avatar/system';
import useCopyCode from '@hooks/useCopyCode';
import MidjourneyOperations from '@components/MidjourneyOperations';
import { hasUpscaleOrVariation } from '@utils/midjourney';
import './index.css';

const MessageItem: FC<{
message: Message;
onOperationClick?: (
type: MessageType,
customId: string,
messageId: string,
prompt: string
) => void;
mode?: ConversationMode;
index?: number;
}> = ({ message, mode, index }) => {
}> = ({ message, onOperationClick, mode, index }) => {
const { i18n } = useContext(GlobalContext);
const isExpired = message.expiredAt && message.expiredAt <= Date.now();
const createdAt = getRelativeTime(message.createdAt, true);
Expand All @@ -25,27 +34,43 @@ const MessageItem: FC<{
{message.role === 'assistant' ? (
<SystemAvatar className="mt-[14px] mr-2" role={message.imageModel} />
) : null}
<div
dangerouslySetInnerHTML={{
__html: isExpired
? i18n.status_image_expired
: markdown.render(message.content),
}}
className={`prose message-box shadow-sm p-4 ${
message.role === 'user' ? 'bg-gradient text-white' : 'bg-[#ebeced]'
} ${
mode === 'image' ? 'img-no-margin' : ''
} break-words overflow-hidden rounded-[16px]`}
/>
{createdAt ? (
<div>
<div
className={`message-box-time hover:visible invisible text-[#a1a7a8] text-sm absolute top-[-20px] ${
message.role === 'user' ? 'right-0' : 'left-[calc(32px+0.5rem)]'
}`}
>
{createdAt}
</div>
) : null}
dangerouslySetInnerHTML={{
__html: isExpired
? i18n.status_image_expired
: markdown.render(message.content),
}}
className={`prose message-box shadow-sm p-4 ${
message.role === 'user' ? 'bg-gradient text-white' : 'bg-[#ebeced]'
} ${
mode === 'image' ? 'img-no-margin' : ''
} break-words overflow-hidden rounded-[16px]`}
/>
{createdAt ? (
<div
className={`message-box-time hover:visible invisible text-[#a1a7a8] text-sm absolute top-[-20px] ${
message.role === 'user' ? 'right-0' : 'left-[calc(32px+0.5rem)]'
}`}
>
{createdAt}
</div>
) : null}
{message.midjourneyMessage &&
hasUpscaleOrVariation(message.midjourneyMessage) ? (
<MidjourneyOperations
message={message.midjourneyMessage}
onClick={(type, customId) =>
onOperationClick(
type,
customId,
message.midjourneyMessage.id,
message.midjourneyMessage.prompt
)
}
/>
) : null}
</div>
</div>
);
};
Expand All @@ -55,7 +80,13 @@ const MessageBox: FC<{
messages: Message[];
mode: ConversationMode;
loading: boolean;
}> = ({ streamMessage, messages, mode, loading }) => {
onOperationClick?: (
type: MessageType,
customId: string,
messageId: string,
prompt: string
) => void;
}> = ({ streamMessage, messages, mode, loading, onOperationClick }) => {
const { i18n } = useContext(GlobalContext);

useCopyCode(i18n.success_copy);
Expand Down Expand Up @@ -100,7 +131,13 @@ const MessageBox: FC<{
/>
) : null}
{messages.map((message, index) => (
<MessageItem key={index} index={index} mode={mode} message={message} />
<MessageItem
key={index}
index={index}
mode={mode}
message={message}
onOperationClick={loading ? () => null : onOperationClick}
/>
))}
{streamMessage ? (
<MessageItem message={{ role: 'assistant', content: streamMessage }} />
Expand Down
60 changes: 60 additions & 0 deletions src/components/MidjourneyOperations/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { FC } from 'react';
import { Tag } from 'antd';
import type { MessageType } from 'midjourney-fetch';
import { MidjourneyMessage } from '@interfaces';
import {
filterUpscale,
filterVariation,
isComponentAvailable,
} from '@utils/midjourney';

const { CheckableTag } = Tag;

const MidjourneyOperations: FC<{
message: MidjourneyMessage;
onClick?: (type: MessageType, id: string) => void;
}> = ({ message, onClick }) => {
const upscale = filterUpscale(message.components[0]?.components ?? []);
const variation = filterVariation([
...(message.components[0]?.components ?? []),
...(message.components[1]?.components ?? []),
]);
return (
<div>
{upscale.map((option) => {
const isAvailable = isComponentAvailable(option);
return (
<CheckableTag
className="mt-3"
key={option.custom_id || option.label}
checked={!isAvailable}
style={isAvailable ? { background: 'rgba(0, 0, 0, 0.06)' } : {}}
onClick={() =>
isAvailable && onClick?.('upscale', option.custom_id)
}
>
{option.label || option.emoji?.name}
</CheckableTag>
);
})}
{variation.map((option) => {
const isAvailable = isComponentAvailable(option);
return (
<CheckableTag
className="mt-3"
key={option.custom_id}
checked={!isAvailable}
style={isAvailable ? { background: 'rgba(0, 0, 0, 0.06)' } : {}}
onClick={() =>
isAvailable && onClick?.('variation', option.custom_id)
}
>
{option.label || option.emoji?.name}
</CheckableTag>
);
})}
</div>
);
};

export default MidjourneyOperations;
9 changes: 9 additions & 0 deletions src/interfaces/index.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import type { MessageItem } from 'midjourney-fetch';
import {
LayoutConfig,
SupportedImageModels,
SupportedImgSize,
SupportedModel,
} from '@configs';

export type MidjourneyMessage = Pick<
MessageItem,
'id' | 'components' | 'attachments'
> & {
prompt: string;
};

export interface Message {
content: string;
role: 'assistant' | 'user';
imageModel?: SupportedImageModels; // distinguish avator
midjourneyMessage?: MidjourneyMessage;
createdAt?: number;
expiredAt?: number; // for image mode
}
Expand Down
7 changes: 7 additions & 0 deletions src/layouts/Layout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,13 @@ const { title } = Astro.props;
overflow: hidden;
text-overflow: ellipsis;
}
/** medium zoom */
.medium-zoom-overlay {
z-index: 20;
}
.medium-zoom-image {
z-index: 21;
}
/* mobile style */
@media screen and (max-width: 768px) {
html {
Expand Down
Loading

0 comments on commit 9c280ba

Please sign in to comment.