Skip to content

Commit

Permalink
Add image save & set wallpaper
Browse files Browse the repository at this point in the history
  • Loading branch information
DustinBrett committed Oct 27, 2024
1 parent 81a2bfe commit 7550bd9
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 19 deletions.
67 changes: 61 additions & 6 deletions components/system/Taskbar/AI/AIChat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import {
} from "components/system/Taskbar/AI/functions";
import {
AIIcon,
BackgroundIcon,
ChatIcon,
CopyIcon,
EditIcon,
PersonIcon,
SaveIcon,
SendFilledIcon,
SendIcon,
SpeakIcon,
Expand All @@ -28,7 +30,7 @@ import StyledAIChat from "components/system/Taskbar/AI/StyledAIChat";
import { CloseIcon } from "components/system/Window/Titlebar/WindowActionIcons";
import Button from "styles/common/Button";
import { clsx, getExtension, label, viewWidth } from "utils/functions";
import { PREVENT_SCROLL } from "utils/constants";
import { DESKTOP_PATH, PREVENT_SCROLL, SAVE_PATH } from "utils/constants";
import {
type MessageTypes,
type ConvoStyles,
Expand Down Expand Up @@ -144,7 +146,7 @@ const AIChat: FC<AIChatProps> = ({ toggleAI }) => {
);
const [containerElement, setContainerElement] =
useState<HTMLElement | null>();
const { removeFromStack } = useSession();
const { removeFromStack, setWallpaper } = useSession();
const { zIndex, ...focusableProps } = useFocusable(
WINDOW_ID,
undefined,
Expand All @@ -159,7 +161,7 @@ const AIChat: FC<AIChatProps> = ({ toggleAI }) => {
textArea.style.height = "auto";
textArea.style.height = `${textArea.scrollHeight}px`;
}, []);
const { exists, readFile, stat } = useFileSystem();
const { createPath, exists, readFile, stat, updateFolder } = useFileSystem();
const canvasRefs = useRef<Record<number, HTMLCanvasElement>>({});
const sendMessage = useCallback(async () => {
const { text } = conversation[conversation.length - 1];
Expand Down Expand Up @@ -221,6 +223,32 @@ const AIChat: FC<AIChatProps> = ({ toggleAI }) => {
readFile,
stat,
]);
const saveCanvasImage = useCallback(
async (
index: number,
saveName: string,
savePath: string
): Promise<string> => {
const canvas = canvasRefs.current[index];
let newFileName = `${saveName}.png`;

if (canvas) {
newFileName = await createPath(
newFileName,
savePath,
Buffer.from(
canvas.toDataURL("image/png").replace("data:image/png;base64,", ""),
"base64"
)
);

updateFolder(savePath);
}

return newFileName;
},
[createPath, updateFolder]
);

useEffect(() => {
textAreaRef.current?.focus(PREVENT_SCROLL);
Expand Down Expand Up @@ -407,7 +435,7 @@ const AIChat: FC<AIChatProps> = ({ toggleAI }) => {
})}
>
<button
className="copy"
className="control"
onClick={() => {
navigator.clipboard?.writeText(text);
setCopiedIndex(index);
Expand All @@ -420,7 +448,7 @@ const AIChat: FC<AIChatProps> = ({ toggleAI }) => {
</button>
{type === "user" && (
<button
className="edit"
className="control"
onClick={() => {
if (textAreaRef.current) {
textAreaRef.current.value = text;
Expand All @@ -436,14 +464,41 @@ const AIChat: FC<AIChatProps> = ({ toggleAI }) => {
)}
{"speechSynthesis" in window && type === "ai" && (
<button
className="speak"
className="control"
onClick={() => speakMessage(text)}
type="button"
{...label("Read aloud")}
>
<SpeakIcon />
</button>
)}
{type === "ai" && withCanvas && (
<>
<button
className="control"
onClick={() =>
saveCanvasImage(index, text, DESKTOP_PATH)
}
type="button"
{...label("Save")}
>
<SaveIcon />
</button>
<button
className="control"
onClick={() =>
saveCanvasImage(index, text, SAVE_PATH).then(
(newFileName) =>
setWallpaper(`${SAVE_PATH}/${newFileName}`)
)
}
type="button"
{...label("Set as background")}
>
<BackgroundIcon />
</button>
</>
)}
</div>
{withCanvas && (
<div
Expand Down
22 changes: 13 additions & 9 deletions components/system/Taskbar/AI/StyledAIChat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -390,9 +390,7 @@ const StyledAIChat = styled(motion.section)<StyledAIChatProps>`
pointer-events: none;
}
.copy,
.edit,
.speak {
.control {
background-color: transparent;
border-radius: 5px;
height: 32px;
Expand All @@ -408,20 +406,26 @@ const StyledAIChat = styled(motion.section)<StyledAIChatProps>`
background-color: rgb(42, 42, 42);
}
.copy-icon,
.edit-icon,
.speak-icon {
.control-icon {
fill: #fff;
height: 20px;
width: 20px;
}
.save-icon {
position: relative;
top: 1px;
}
.background-icon {
position: relative;
top: -1px;
}
}
&:hover,
&.last {
.copy,
.edit,
.speak {
.control {
visibility: visible;
}
}
Expand Down
4 changes: 3 additions & 1 deletion components/system/Taskbar/AI/ai.worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,9 @@ globalThis.addEventListener(
globalThis.tvmjsGlobalEnv.logger("", "");

if (data.hasWindowAI) {
rebuildSession("I'll try to create that.");
rebuildSession(
"I'll try to create that using Stable Diffusion 1.5."
);
}
} else {
while (retry++ < 3 && !response) {
Expand Down
26 changes: 23 additions & 3 deletions components/system/Taskbar/AI/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,37 @@ export const PersonIcon = memo(() => (

export const CopyIcon = memo(() => (
<svg
className="copy-icon"
className="control-icon"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M5.503 4.627 5.5 6.75v10.504a3.25 3.25 0 0 0 3.25 3.25h8.616a2.251 2.251 0 0 1-2.122 1.5H8.75A4.75 4.75 0 0 1 4 17.254V6.75c0-.98.627-1.815 1.503-2.123ZM17.75 2A2.25 2.25 0 0 1 20 4.25v13a2.25 2.25 0 0 1-2.25 2.25h-9a2.25 2.25 0 0 1-2.25-2.25v-13A2.25 2.25 0 0 1 8.75 2h9Zm0 1.5h-9a.75.75 0 0 0-.75.75v13c0 .414.336.75.75.75h9a.75.75 0 0 0 .75-.75v-13a.75.75 0 0 0-.75-.75Z" />
</svg>
));

export const BackgroundIcon = memo(() => (
<svg
className="control-icon background-icon"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M13.5 7.5h9V24H0V7.5h9V9H1.5v13.5H21V9h-7.5Zm-3-4.629L7.277 6.094 6.223 5.027 11.25 0l5.027 5.027-1.054 1.067L12 2.87V13.5h-1.5ZM9 12H4.5v7.5H18V12h-4.5v-1.5h6V21H3V10.5h6Zm0 0" />
</svg>
));

export const SaveIcon = memo(() => (
<svg
className="control-icon save-icon"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M21 1.5c.21 0 .406.04.586.117.18.078.336.188.477.32.132.141.242.297.32.477.078.18.117.375.117.586v19.5H4.184L1.5 19.805V3c0-.21.04-.406.117-.586.078-.18.188-.336.32-.477.141-.132.297-.242.477-.32.18-.078.375-.117.586-.117Zm-15 9h12V3H6Zm9 6H7.5V21H9v-3h1.5v3H15ZM21 3h-1.5v9h-15V3H3v16.184L4.805 21H6v-6h10.5v6H21Zm0 0" />
</svg>
));

export const EditIcon = memo(() => (
<svg
className="edit-icon"
className="control-icon"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
Expand Down Expand Up @@ -114,7 +134,7 @@ export const WarningIcon = memo(() => (

export const SpeakIcon = memo(() => (
<svg
className="speak-icon"
className="control-icon"
viewBox="0 0 20 19"
xmlns="http://www.w3.org/2000/svg"
>
Expand Down

0 comments on commit 7550bd9

Please sign in to comment.