From cc47d5859e3a08ba396a822c0ddebbf437e91b55 Mon Sep 17 00:00:00 2001 From: Alex TYRODE Date: Fri, 25 Apr 2025 06:25:21 +0000 Subject: [PATCH 01/10] chore: update package dependencies - Bumped version of @atyrode/excalidraw from 0.18.0-1 to 0.18.0-2. - Updated @tanstack/query-core and @tanstack/react-query to versions 5.74.4 and 5.74.6 respectively. - Updated @types/node to version 22.15.1 and posthog-js to version 1.236.6 in yarn.lock. --- src/frontend/package.json | 2 +- src/frontend/yarn.lock | 52 +++++++++++++++++++-------------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/frontend/package.json b/src/frontend/package.json index 75ebc8f..48e8371 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "private": true, "dependencies": { - "@atyrode/excalidraw": "^0.18.0-1", + "@atyrode/excalidraw": "^0.18.0-2", "@monaco-editor/react": "^4.7.0", "@tanstack/react-query": "^5.74.3", "@tanstack/react-query-devtools": "^5.74.3", diff --git a/src/frontend/yarn.lock b/src/frontend/yarn.lock index dd45438..b6b25d0 100644 --- a/src/frontend/yarn.lock +++ b/src/frontend/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@atyrode/excalidraw@^0.18.0-1": - version "0.18.0-1" - resolved "https://registry.yarnpkg.com/@atyrode/excalidraw/-/excalidraw-0.18.0-1.tgz#1ba7c5e6a2d5c2b994069eb45fe41c7cfc198364" - integrity sha512-0Hqnj1wqfPuc6RaI/l2dCXAvw9wM6TZ9wfbZwi1ySx3dVJ+Hs79+2p1Gtx/dcCbkG8L3XiqtC1bFh3Cmls3LmQ== +"@atyrode/excalidraw@^0.18.0-2": + version "0.18.0-2" + resolved "https://registry.yarnpkg.com/@atyrode/excalidraw/-/excalidraw-0.18.0-2.tgz#66be05d5b8a2458dd65fb58baa104fd803a41c5d" + integrity sha512-z3scC5BzVnu8ddS1TJTNxX+wPa3bj72j9RVleSV7iDZsClLDqgcrW+Z/cVHEphWpvBKlMzRo31vDdg+dqE6dDg== dependencies: "@braintree/sanitize-url" "6.0.2" "@excalidraw/laser-pointer" "1.3.1" @@ -537,29 +537,29 @@ resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.0.tgz#fd92d31a2931483c25677b9c6698106490cbbc76" integrity sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ== -"@tanstack/query-core@5.74.3": - version "5.74.3" - resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.74.3.tgz#1fc97bd9a47f2acdf9f49737b1e6969e7bbcb7d7" - integrity sha512-Mqk+5o3qTuAiZML248XpNH8r2cOzl15+LTbUsZQEwvSvn1GU4VQhvqzAbil36p+MBxpr/58oBSnRzhrBevDhfg== +"@tanstack/query-core@5.74.4": + version "5.74.4" + resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.74.4.tgz#08c4f88f336738d822d9242c5e7d2be50f5c25b3" + integrity sha512-YuG0A0+3i9b2Gfo9fkmNnkUWh5+5cFhWBN0pJAHkHilTx6A0nv8kepkk4T4GRt4e5ahbtFj2eTtkiPcVU1xO4A== -"@tanstack/query-devtools@5.73.3": - version "5.73.3" - resolved "https://registry.yarnpkg.com/@tanstack/query-devtools/-/query-devtools-5.73.3.tgz#8fc9872ed4408964b2c560d811caee491cfbdc3c" - integrity sha512-hBQyYwsOuO7QOprK75NzfrWs/EQYjgFA0yykmcvsV62q0t6Ua97CU3sYgjHx0ZvxkXSOMkY24VRJ5uv9f5Ik4w== +"@tanstack/query-devtools@5.74.6": + version "5.74.6" + resolved "https://registry.yarnpkg.com/@tanstack/query-devtools/-/query-devtools-5.74.6.tgz#88a29fac2ba3db6c9bda8f40808f808d7a88df0b" + integrity sha512-djaFT11mVCOW3e0Ezfyiq7T6OoHy2LRI1fUFQvj+G6+/4A1FkuRMNUhQkdP1GXlx8id0f1/zd5fgDpIy5SU/Iw== "@tanstack/react-query-devtools@^5.74.3": - version "5.74.3" - resolved "https://registry.yarnpkg.com/@tanstack/react-query-devtools/-/react-query-devtools-5.74.3.tgz#3b079cd938b4f00d779e233e7fbb96c90264bf0f" - integrity sha512-H7TsOBB1fRCuuawrBzKMoIszqqILr2IN5oGLYMl7QG7ERJpMdc4hH8OwzBhVxJnmKeGwgtTQgcdKepfoJCWvFg== + version "5.74.6" + resolved "https://registry.yarnpkg.com/@tanstack/react-query-devtools/-/react-query-devtools-5.74.6.tgz#169a3e2beab0c87ce3ebbda909bbd2c6590239f8" + integrity sha512-vlsDwz4/FsblK0h7VAlXUdJ+9OV+i1n8OLb8CLLAZqu0M9GCnbajytZwsRmns33PXBZ6wQBJ859kg6aajx+e9Q== dependencies: - "@tanstack/query-devtools" "5.73.3" + "@tanstack/query-devtools" "5.74.6" "@tanstack/react-query@^5.74.3": - version "5.74.3" - resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.74.3.tgz#f7acd825abaea091f009d1c3f115212e45c4ee74" - integrity sha512-QrycUn0wxjVPzITvQvOxFRdhlAwIoOQSuav7qWD4SWCoKCdLbyRZ2vji2GuBq/glaxbF4wBx3fqcYRDOt8KDTA== + version "5.74.4" + resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.74.4.tgz#d73ee1899c08a227519cbf53b9a0e0b1e67cd3fe" + integrity sha512-mAbxw60d4ffQ4qmRYfkO1xzRBPUEf/72Dgo3qqea0J66nIKuDTLEqQt0ku++SDFlMGMnB6uKDnEG1xD/TDse4Q== dependencies: - "@tanstack/query-core" "5.74.3" + "@tanstack/query-core" "5.74.4" "@types/d3-scale-chromatic@^3.0.0": version "3.1.0" @@ -603,9 +603,9 @@ integrity sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA== "@types/node@^22.14.0": - version "22.14.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.14.1.tgz#53b54585cec81c21eee3697521e31312d6ca1e6f" - integrity sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw== + version "22.15.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.15.1.tgz#4cd2c8717a61ae2979c6a0624b4d1b67415bf2c0" + integrity sha512-gSZyd0Qmv7qvbd2fJ9HGdYmv1yhNdelIA4YOtN6vkcmSwFhthxSEsBgU/JYZcXjWT6DFzoATcHrc52Ckh8SeRA== dependencies: undici-types "~6.21.0" @@ -1646,9 +1646,9 @@ postcss@^8.4.36: source-map-js "^1.2.1" posthog-js@^1.236.0: - version "1.236.0" - resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.236.0.tgz#26a37491b102e0bed53b7cc924ae9bc121394fba" - integrity sha512-tTKBu/ZHVfBHAvpHhLQX+8WfNWr1q4r2fZmySBYJPR+pK+HQtSwvjHCibaH6ukK38HphLCqc48YTbQGUbZn7Nw== + version "1.236.6" + resolved "https://registry.yarnpkg.com/posthog-js/-/posthog-js-1.236.6.tgz#f4d73d25773f4e4bafd7632717c97b27bd9f1b7c" + integrity sha512-IX4fkn3HCK+ObdHr/AuWd+Ks7bgMpRpOQB93b5rDJAWkG4if4xFVUn5pgEjyCNeOO2GM1ECnp08q9tYNYEfwbA== dependencies: core-js "^3.38.1" fflate "^0.4.8" From 6be130534402d6b9578146e6a887f58b6b04600c Mon Sep 17 00:00:00 2001 From: Alex TYRODE Date: Fri, 25 Apr 2025 06:29:10 +0000 Subject: [PATCH 02/10] feat: integrate authentication modal into ExcalidrawWrapper - Added authentication handling to ExcalidrawWrapper, displaying AuthModal when the user is not authenticated. - Removed AuthModal state management from AuthGate, simplifying its structure. - Updated AuthModal to use the Dialog component for improved UI consistency. --- src/frontend/src/App.tsx | 2 ++ src/frontend/src/AuthGate.tsx | 36 ++------------------- src/frontend/src/ExcalidrawWrapper.tsx | 27 ++++++++++++++-- src/frontend/src/ui/AuthModal.tsx | 44 ++++++++++++++++---------- 4 files changed, 56 insertions(+), 53 deletions(-) diff --git a/src/frontend/src/App.tsx b/src/frontend/src/App.tsx index 6fa94fb..216b873 100644 --- a/src/frontend/src/App.tsx +++ b/src/frontend/src/App.tsx @@ -125,6 +125,8 @@ export default function App({ setExcalidrawAPI={setExcalidrawAPI} onChange={debouncedLogChange} MainMenu={MainMenu} + isAuthenticated={isAuthenticated} + isAuthLoading={isAuthLoading} > {children} diff --git a/src/frontend/src/AuthGate.tsx b/src/frontend/src/AuthGate.tsx index 2c22846..e3958e8 100644 --- a/src/frontend/src/AuthGate.tsx +++ b/src/frontend/src/AuthGate.tsx @@ -1,6 +1,5 @@ import React, { useEffect, useRef, useState } from "react"; import { useAuthCheck } from "./api/hooks"; -import AuthModal from "./ui/AuthModal"; /** * If unauthenticated, it shows the AuthModal as an overlay, but still renders the app behind it. @@ -50,37 +49,6 @@ export default function AuthGate({ children }: { children: React.ReactNode }) { // eslint-disable-next-line react-hooks/exhaustive-deps }, [isAuthenticated, coderAuthDone]); - // State to control modal visibility and exit animation - const [showAuthModal, setShowAuthModal] = useState(false); - const [isExiting, setIsExiting] = useState(false); - - // Update showAuthModal when authentication status changes - useEffect(() => { - if (isAuthenticated === false) { - setShowAuthModal(true); - setIsExiting(false); - } else if (isAuthenticated === true && showAuthModal) { - // Start exit animation when user becomes authenticated - setIsExiting(true); - // Modal will be removed after animation completes via onExitComplete - } - }, [isAuthenticated, showAuthModal]); - - // Handle exit animation completion - const handleExitComplete = () => { - setShowAuthModal(false); - }; - - // Always render children; overlay AuthModal if not authenticated - return ( - <> - {children} - {showAuthModal && ( - - )} - - ); + // Just render children - AuthModal is now handled by ExcalidrawWrapper + return <>{children}; } diff --git a/src/frontend/src/ExcalidrawWrapper.tsx b/src/frontend/src/ExcalidrawWrapper.tsx index 68bb1c5..fb3d0d1 100644 --- a/src/frontend/src/ExcalidrawWrapper.tsx +++ b/src/frontend/src/ExcalidrawWrapper.tsx @@ -1,4 +1,4 @@ -import React, { Children, cloneElement } from 'react'; +import React, { Children, cloneElement, useState, useEffect } from 'react'; import DiscordButton from './ui/DiscordButton'; import FeedbackButton from './ui/FeedbackButton'; import type { ExcalidrawImperativeAPI } from '@atyrode/excalidraw/types'; @@ -6,6 +6,7 @@ import type { NonDeletedExcalidrawElement } from '@atyrode/excalidraw/element/ty import type { AppState } from '@atyrode/excalidraw/types'; import { MainMenuConfig } from './ui/MainMenu'; import { renderCustomEmbeddable } from './CustomEmbeddableRenderer'; +import AuthModal from './ui/AuthModal'; const defaultInitialData = { elements: [], @@ -25,6 +26,8 @@ interface ExcalidrawWrapperProps { onChange: (elements: NonDeletedExcalidrawElement[], state: AppState) => void; MainMenu: any; renderTopRightUI?: () => React.ReactNode; + isAuthenticated?: boolean | null; + isAuthLoading?: boolean; } export const ExcalidrawWrapper: React.FC = ({ @@ -35,7 +38,19 @@ export const ExcalidrawWrapper: React.FC = ({ onChange, MainMenu, renderTopRightUI, + isAuthenticated = null, + isAuthLoading = false, }) => { + // Add state for modal animation + const [isExiting, setIsExiting] = useState(false); + + // Handle auth state changes + useEffect(() => { + if (isAuthenticated === true) { + setIsExiting(true); + } + }, [isAuthenticated]); + const renderExcalidraw = (children: React.ReactNode) => { const Excalidraw = Children.toArray(children).find( (child: any) => @@ -65,7 +80,15 @@ export const ExcalidrawWrapper: React.FC = ({ )), }, - + <> + + {!isAuthLoading && isAuthenticated === false && ( + {}} + /> + )} + ); }; diff --git a/src/frontend/src/ui/AuthModal.tsx b/src/frontend/src/ui/AuthModal.tsx index 0d76291..4b0263b 100644 --- a/src/frontend/src/ui/AuthModal.tsx +++ b/src/frontend/src/ui/AuthModal.tsx @@ -2,21 +2,21 @@ import React, { useState, useEffect } from "react"; import { capture } from "../utils/posthog"; import { Mail } from "lucide-react"; import { queryClient } from "../api/queryClient"; -import Modal from "./Modal"; + import "../styles/AuthModal.scss"; +import { Dialog } from "@atyrode/excalidraw"; interface AuthModalProps { description?: React.ReactNode; warningText?: string; - onExitComplete?: () => void; + onCloseRequest: () => void; isExiting?: boolean; } const AuthModal: React.FC = ({ description = <>Welcome to your whiteboard IDE. Open terminals and start coding right away in your own Ubuntu VM!, warningText = "🚧 This is a beta. Make backups! 🚧", - onExitComplete, - isExiting = false, + onCloseRequest, }) => { const [isMounted, setIsMounted] = useState(false); @@ -44,18 +44,9 @@ const AuthModal: React.FC = ({ if (!isMounted) return null; - return ( - -
- + // Prepare the content for the Dialog + const dialogContent = ( +

{description}

@@ -172,7 +163,26 @@ const AuthModal: React.FC = ({ {warningText}
- + ); + + return ( + + pad.ws logo +

pad.ws

+
+ } + closeOnClickOutside={false} + children={dialogContent} + /> ); }; From 2036daea1bdb35fb99d84b00decc135e9d6e3fc2 Mon Sep 17 00:00:00 2001 From: Alex TYRODE Date: Fri, 25 Apr 2025 06:39:18 +0000 Subject: [PATCH 03/10] refactor: replace AuthModal with AuthDialog and update MainMenu - Replaced AuthModal with a new AuthDialog component for improved functionality and clarity. - Updated ExcalidrawWrapper to use AuthDialog, simplifying the authentication flow. - Removed the old AuthModal file. --- src/frontend/src/ExcalidrawWrapper.tsx | 7 +- src/frontend/src/ui/AuthDialog.tsx | 200 +++++++++++++++++++++++++ src/frontend/src/ui/AuthModal.tsx | 189 ----------------------- 3 files changed, 203 insertions(+), 193 deletions(-) create mode 100644 src/frontend/src/ui/AuthDialog.tsx delete mode 100644 src/frontend/src/ui/AuthModal.tsx diff --git a/src/frontend/src/ExcalidrawWrapper.tsx b/src/frontend/src/ExcalidrawWrapper.tsx index fb3d0d1..5bc7692 100644 --- a/src/frontend/src/ExcalidrawWrapper.tsx +++ b/src/frontend/src/ExcalidrawWrapper.tsx @@ -6,7 +6,7 @@ import type { NonDeletedExcalidrawElement } from '@atyrode/excalidraw/element/ty import type { AppState } from '@atyrode/excalidraw/types'; import { MainMenuConfig } from './ui/MainMenu'; import { renderCustomEmbeddable } from './CustomEmbeddableRenderer'; -import AuthModal from './ui/AuthModal'; +import AuthDialog from './ui/AuthDialog'; const defaultInitialData = { elements: [], @@ -83,9 +83,8 @@ export const ExcalidrawWrapper: React.FC = ({ <> {!isAuthLoading && isAuthenticated === false && ( - {}} + {}} /> )} diff --git a/src/frontend/src/ui/AuthDialog.tsx b/src/frontend/src/ui/AuthDialog.tsx new file mode 100644 index 0000000..51162f4 --- /dev/null +++ b/src/frontend/src/ui/AuthDialog.tsx @@ -0,0 +1,200 @@ +import React, { useState, useEffect } from "react"; +import { capture } from "../utils/posthog"; +import { Mail } from "lucide-react"; +import { queryClient } from "../api/queryClient"; + +import "../styles/AuthModal.scss"; +import { Dialog } from "@atyrode/excalidraw"; + +interface AuthDialogProps { + description?: React.ReactNode; + warningText?: string; + onClose?: () => void; + children?: React.ReactNode; +} + +export const AuthDialog = ({ + description = <>Welcome to your whiteboard IDE. Open terminals and start coding right away in your own Ubuntu VM!, + warningText = "🚧 This is a beta. Make backups! 🚧", + onClose, + children, +}: AuthDialogProps) => { + const [modalIsShown, setModalIsShown] = useState(true); + + useEffect(() => { + capture("auth_modal_shown"); + }, []); + + useEffect(() => { + const checkLocalStorage = () => { + const authCompleted = localStorage.getItem('auth_completed'); + if (authCompleted) { + localStorage.removeItem('auth_completed'); + queryClient.invalidateQueries({ queryKey: ['auth'] }); + clearInterval(intervalId); + handleClose(); + } + }; + + const intervalId = setInterval(checkLocalStorage, 500); + + return () => { + clearInterval(intervalId); + }; + }, []); + + const handleClose = React.useCallback(() => { + setModalIsShown(false); + + if (onClose) { + onClose(); + } + }, [onClose]); + + // Prepare the content for the Dialog + const dialogContent = ( +
+
+ +

{description}

+ + {/* Sign-in buttons */} +
+ + + +
+ + {/* Footer */} + + + {/* Warning message */} +
+ {warningText} +
+
+ ); + + return ( + <> + {modalIsShown && ( + + pad.ws logo +

pad.ws

+
+ } + closeOnClickOutside={false} + children={children || dialogContent} + /> + )} + + ); +}; + +export default AuthDialog; diff --git a/src/frontend/src/ui/AuthModal.tsx b/src/frontend/src/ui/AuthModal.tsx deleted file mode 100644 index 4b0263b..0000000 --- a/src/frontend/src/ui/AuthModal.tsx +++ /dev/null @@ -1,189 +0,0 @@ -import React, { useState, useEffect } from "react"; -import { capture } from "../utils/posthog"; -import { Mail } from "lucide-react"; -import { queryClient } from "../api/queryClient"; - -import "../styles/AuthModal.scss"; -import { Dialog } from "@atyrode/excalidraw"; - -interface AuthModalProps { - description?: React.ReactNode; - warningText?: string; - onCloseRequest: () => void; - isExiting?: boolean; -} - -const AuthModal: React.FC = ({ - description = <>Welcome to your whiteboard IDE. Open terminals and start coding right away in your own Ubuntu VM!, - warningText = "🚧 This is a beta. Make backups! 🚧", - onCloseRequest, -}) => { - const [isMounted, setIsMounted] = useState(false); - - useEffect(() => { - setIsMounted(true); - capture("auth_modal_shown"); - }, []); - - useEffect(() => { - const checkLocalStorage = () => { - const authCompleted = localStorage.getItem('auth_completed'); - if (authCompleted) { - localStorage.removeItem('auth_completed'); - queryClient.invalidateQueries({ queryKey: ['auth'] }); - clearInterval(intervalId); - } - }; - - const intervalId = setInterval(checkLocalStorage, 500); - - return () => { - clearInterval(intervalId); - }; - }, []); - - if (!isMounted) return null; - - // Prepare the content for the Dialog - const dialogContent = ( -
-
- -

{description}

- - {/* Sign-in buttons */} -
- - - -
- - {/* Footer */} - - - {/* Warning message */} -
- {warningText} -
-
- ); - - return ( - - pad.ws logo -

pad.ws

-
- } - closeOnClickOutside={false} - children={dialogContent} - /> - ); -}; - -export default AuthModal; From c25c7b752d42276da030aa06ed8c93532b8e82cd Mon Sep 17 00:00:00 2001 From: Alex TYRODE Date: Fri, 25 Apr 2025 07:21:35 +0000 Subject: [PATCH 04/10] feat: add new icon components for Discord, GitHub, and Google - Introduced DiscordIcon, GithubIcon, and GoogleIcon components. - Each icon component supports customizable properties such as className, width, height, and fill/stroke colors. - Updated index.ts to export the new icon components for easy access. --- src/frontend/src/icons/DiscordIcon.tsx | 33 ++++++++++++++++++++ src/frontend/src/icons/GithubIcon.tsx | 37 ++++++++++++++++++++++ src/frontend/src/icons/GoogleIcon.tsx | 43 ++++++++++++++++++++++++++ src/frontend/src/icons/index.ts | 3 ++ 4 files changed, 116 insertions(+) create mode 100644 src/frontend/src/icons/DiscordIcon.tsx create mode 100644 src/frontend/src/icons/GithubIcon.tsx create mode 100644 src/frontend/src/icons/GoogleIcon.tsx create mode 100644 src/frontend/src/icons/index.ts diff --git a/src/frontend/src/icons/DiscordIcon.tsx b/src/frontend/src/icons/DiscordIcon.tsx new file mode 100644 index 0000000..344b90d --- /dev/null +++ b/src/frontend/src/icons/DiscordIcon.tsx @@ -0,0 +1,33 @@ +import React from "react"; + +interface DiscordIconProps { + className?: string; + width?: number; + height?: number; + fill?: string; +} + +export const DiscordIcon: React.FC = ({ + className = "", + width = 20, + height = 20, + fill = "currentColor", +}) => { + return ( + + + + ); +}; + +export default DiscordIcon; diff --git a/src/frontend/src/icons/GithubIcon.tsx b/src/frontend/src/icons/GithubIcon.tsx new file mode 100644 index 0000000..dd94550 --- /dev/null +++ b/src/frontend/src/icons/GithubIcon.tsx @@ -0,0 +1,37 @@ +import React from "react"; + +interface GithubIconProps { + className?: string; + width?: number; + height?: number; + stroke?: string; + strokeWidth?: number; +} + +export const GithubIcon: React.FC = ({ + className = "", + width = 20, + height = 20, + stroke = "currentColor", + strokeWidth = 2, +}) => { + return ( + + + + + ); +}; + +export default GithubIcon; diff --git a/src/frontend/src/icons/GoogleIcon.tsx b/src/frontend/src/icons/GoogleIcon.tsx new file mode 100644 index 0000000..bac7d73 --- /dev/null +++ b/src/frontend/src/icons/GoogleIcon.tsx @@ -0,0 +1,43 @@ +import React from "react"; + +interface GoogleIconProps { + className?: string; + width?: number; + height?: number; +} + +export const GoogleIcon: React.FC = ({ + className = "", + width = 20, + height = 20, +}) => { + return ( + + + + + + + + ); +}; + +export default GoogleIcon; diff --git a/src/frontend/src/icons/index.ts b/src/frontend/src/icons/index.ts new file mode 100644 index 0000000..05d4ba1 --- /dev/null +++ b/src/frontend/src/icons/index.ts @@ -0,0 +1,3 @@ +export { default as GoogleIcon } from './GoogleIcon'; +export { default as GithubIcon } from './GithubIcon'; +export { default as DiscordIcon } from './DiscordIcon'; From d447e27b1ce96a9364f2216364d03b627d5779ce Mon Sep 17 00:00:00 2001 From: Alex TYRODE Date: Fri, 25 Apr 2025 07:38:38 +0000 Subject: [PATCH 05/10] feat: add AuthDialog styles and update component references - Introduced AuthDialog.scss for styling the authentication modal, enhancing the UI with a structured layout and responsive design. - Updated AuthDialog component to use new styles and replaced references from AuthModal to AuthDialog. - Integrated new icon components (GoogleIcon, GithubIcon, DiscordIcon) for improved visual consistency in the authentication buttons. --- .../{AuthModal.scss => AuthDialog.scss} | 82 +++++++----------- src/frontend/src/styles/index.scss | 2 +- src/frontend/src/ui/AuthDialog.tsx | 84 +++---------------- 3 files changed, 39 insertions(+), 129 deletions(-) rename src/frontend/src/styles/{AuthModal.scss => AuthDialog.scss} (50%) diff --git a/src/frontend/src/styles/AuthModal.scss b/src/frontend/src/styles/AuthDialog.scss similarity index 50% rename from src/frontend/src/styles/AuthModal.scss rename to src/frontend/src/styles/AuthDialog.scss index f6a6427..b82e078 100644 --- a/src/frontend/src/styles/AuthModal.scss +++ b/src/frontend/src/styles/AuthDialog.scss @@ -2,28 +2,10 @@ @import './Modal.scss'; .auth-modal { - &__content { - position: relative; - z-index: 1; - padding: 20px; - margin: 12px; - padding-top: 0px; - padding-bottom: 0px; - } - &__title-container { display: flex; align-items: center; - gap: 0.5rem; - } - &__separator { - width: 100%; - height: 1.5px; - background: rgba(120, 120, 120, 0.25); - margin: 0.75rem auto 1.5rem auto; - border: none; - border-radius: 1px; } &__title { @@ -33,7 +15,6 @@ color: white; text-align: center; display: inline-block; - margin-bottom: 10px; &-dot { color: #fa8933; @@ -41,14 +22,14 @@ } &__description { - margin-top: 35px; - margin-bottom: 35px; color: #a0a0a9; - font-size: 16px; + font-size: 18px; line-height: 1.5; text-align: center; white-space: pre-line; - max-width: 270px; + padding: 0 90px; + margin-bottom: 40px; + margin-top: 40px; .highlight { color: #cfcfcf; @@ -61,60 +42,53 @@ align-items: center; flex-direction: column; gap: 13px; - margin-top: 30px; margin-bottom: 40px; - } - &__button { - display: flex; - align-items: center; - justify-content: center; - gap: 8px; - width: 85%; - height: 44px; - border-radius: 6px; - font-size: 15px; - font-weight: 500; - transition: all 0.2s ease; - cursor: pointer; - - &--outline { - border: 1px solid #3a3a43; - background-color: #2a2a33; + button { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + width: 85%; + height: 44px; + border-radius: 6px; + border: 1px solid #727279; + font-size: 15px; + font-weight: 500; + transition: all 0.2s ease; + cursor: pointer; + background-color: #464652; color: white; - } - - &--primary { - border: none; - background-color: white; - color: #232329; + + &:hover { + border: 1px solid #cc6d24; + } } } &__footer { + display: flex; justify-content: center; align-items: center; - gap: 12px; - font-size: 14px; - color: #6b7280; + gap: 16px; + color: #a0a0a0; + margin-bottom: 10px; &-link { display: flex; align-items: center; - color: #6b7280; - text-decoration: none; + color: #b8723c !important; font-size: 14px; transition: color 0.15s; &:hover { - color: #a0a0a0; + color: #a0a0a0 !important; } } } &__warning { - margin-top: 15px; text-align: center; color: #606060bd; font-size: 12px; diff --git a/src/frontend/src/styles/index.scss b/src/frontend/src/styles/index.scss index 3d51eef..913c1c3 100644 --- a/src/frontend/src/styles/index.scss +++ b/src/frontend/src/styles/index.scss @@ -1,7 +1,7 @@ @import './fonts.scss'; @import './HtmlEditor.scss'; @import './Editor.scss'; -@import './AuthModal.scss'; +@import './AuthDialog.scss'; @import './FeedbackButton.scss'; @import './DiscordButton.scss'; @import './MainMenuLabel.scss'; diff --git a/src/frontend/src/ui/AuthDialog.tsx b/src/frontend/src/ui/AuthDialog.tsx index 51162f4..0daee0a 100644 --- a/src/frontend/src/ui/AuthDialog.tsx +++ b/src/frontend/src/ui/AuthDialog.tsx @@ -2,8 +2,8 @@ import React, { useState, useEffect } from "react"; import { capture } from "../utils/posthog"; import { Mail } from "lucide-react"; import { queryClient } from "../api/queryClient"; +import { GoogleIcon, GithubIcon, DiscordIcon } from "../icons"; -import "../styles/AuthModal.scss"; import { Dialog } from "@atyrode/excalidraw"; interface AuthDialogProps { @@ -14,8 +14,8 @@ interface AuthDialogProps { } export const AuthDialog = ({ - description = <>Welcome to your whiteboard IDE. Open terminals and start coding right away in your own Ubuntu VM!, - warningText = "🚧 This is a beta. Make backups! 🚧", + description = <>Welcome to your whiteboard IDE.

Open terminals and start coding right away in your own Ubuntu VM!, + warningText = "This is a beta. Backup your work!", onClose, children, }: AuthDialogProps) => { @@ -54,14 +54,12 @@ export const AuthDialog = ({ // Prepare the content for the Dialog const dialogContent = (
-

{description}

{/* Sign-in buttons */}
@@ -129,35 +89,11 @@ export const AuthDialog = ({ {/* Footer */} } From 8b1fb2f65730f266df68fb7365e53a63ed5b6f0f Mon Sep 17 00:00:00 2001 From: Alex TYRODE Date: Fri, 25 Apr 2025 08:15:39 +0000 Subject: [PATCH 06/10] refactor: remove Modal component and update styles - Deleted the Modal component and its associated styles to streamline the codebase. - Updated AuthDialog.scss to include necessary styles for the authentication modal. - Enhanced index.scss with new styles for fullscreen dialogs and modal backgrounds. --- src/frontend/src/styles/AuthDialog.scss | 2 +- src/frontend/src/styles/Modal.scss | 141 ------------------------ src/frontend/src/styles/index.scss | 20 ++++ src/frontend/src/ui/Modal.tsx | 97 ---------------- 4 files changed, 21 insertions(+), 239 deletions(-) delete mode 100644 src/frontend/src/styles/Modal.scss delete mode 100644 src/frontend/src/ui/Modal.tsx diff --git a/src/frontend/src/styles/AuthDialog.scss b/src/frontend/src/styles/AuthDialog.scss index b82e078..e1e6360 100644 --- a/src/frontend/src/styles/AuthDialog.scss +++ b/src/frontend/src/styles/AuthDialog.scss @@ -1,7 +1,7 @@ /* Auth Modal Styles */ -@import './Modal.scss'; .auth-modal { + &__title-container { display: flex; align-items: center; diff --git a/src/frontend/src/styles/Modal.scss b/src/frontend/src/styles/Modal.scss deleted file mode 100644 index 3f466c8..0000000 --- a/src/frontend/src/styles/Modal.scss +++ /dev/null @@ -1,141 +0,0 @@ -/* Generic Modal Styles */ -.modal { - &__overlay { - position: absolute; - inset: 0; - z-index: 50; - display: flex; - align-items: center; - justify-content: center; - /* Animation is now applied via inline style */ - } - - &__wrapper { - position: relative; - display: flex; - align-items: center; - justify-content: center; - } - - &__backdrop { - position: absolute; - inset: 0; - background-color: rgba(0, 0, 0, 0.2); - backdrop-filter: blur(0.6px); - z-index: -1; - } - - &__container { - padding: 18px; - position: relative; - z-index: 10; - width: 100%; - border-radius: 12px; - background-color: #232329; - box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.3), 0 8px 10px -6px rgba(0, 0, 0, 0.2); - /* Animation is now applied via inline style */ - font-family: 'Roboto', sans-serif; - } - - &__favicon { - position: absolute; - top: -11.5%; - left: 8%; - width: 65px; - height: 65px; - z-index: 0; /* Behind modal */ - pointer-events: none; - user-select: none; - opacity: 0; /* Initial state, animation will control visibility */ - /* Animation is now applied via inline style */ - } -} - -/* Animation for favicon to slide up and fade in */ -@keyframes fadeInSlideUp { - from { - opacity: 0; - transform: translateY(5px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -/* Animation for favicon to slide down and fade out */ -@keyframes fadeOutSlideDown { - from { - opacity: 1; - transform: translateY(0); - } - to { - opacity: 0; - transform: translateY(5px); - } -} - -/* Animation keyframes */ -@keyframes modalFadeIn { - from { - opacity: 0; - } - to { - opacity: 1; - } -} - -@keyframes modalFadeOut { - from { - opacity: 1; - } - to { - opacity: 0; - } -} - -@keyframes modalZoomIn { - from { - transform: scale(0.95); - opacity: 0; - } - to { - transform: scale(1); - opacity: 1; - } -} - -@keyframes modalZoomOut { - from { - transform: scale(1); - opacity: 1; - } - to { - transform: scale(0.95); - opacity: 0; - } -} - -/* Media query for screens under 700x700 */ -@media (max-width: 734px), (max-height: 700px) { - .modal { - &__wrapper { - width: 100vw; - height: 100vh; - } - - &__container { - width: 100vw; - height: 100vh; - max-width: 100vw !important; - max-height: 100vh; - border-radius: 0; - margin: 0; - padding: 0; - box-shadow: none; - display: flex; - align-items: center; - justify-content: center; - } - } -} diff --git a/src/frontend/src/styles/index.scss b/src/frontend/src/styles/index.scss index 913c1c3..4732ea4 100644 --- a/src/frontend/src/styles/index.scss +++ b/src/frontend/src/styles/index.scss @@ -54,3 +54,23 @@ body { margin-left: 11px !important; font-size: 15px !important; } + +.excalidraw .Dialog--fullscreen { + .Dialog__close { + display: none; + } +} + +.excalidraw .Modal__background { + background-color: rgba(0, 0, 0, 0.2); + backdrop-filter: blur(1px); + animation: Modal__background__fade-in 0.3s ease-out forwards !important; + + &.animations-disabled { + animation: none !important; + } +} + +.excalidraw .Modal__content { + animation: Modal__content_fade-in 0.3s ease-out forwards !important; +} \ No newline at end of file diff --git a/src/frontend/src/ui/Modal.tsx b/src/frontend/src/ui/Modal.tsx deleted file mode 100644 index 57a9f04..0000000 --- a/src/frontend/src/ui/Modal.tsx +++ /dev/null @@ -1,97 +0,0 @@ -import React, { useEffect } from "react"; -import ReactDOM from "react-dom"; -import "../styles/Modal.scss"; - -interface ModalProps { - children: React.ReactNode; - showLogo?: boolean; - logoSrc?: string; - logoAlt?: string; - maxWidth?: string | number; - className?: string; - isExiting?: boolean; - onExitComplete?: () => void; -} - -const Modal: React.FC = ({ - children, - showLogo = true, - logoSrc = "/assets/images/favicon.png", - logoAlt = "Logo", - maxWidth = "500px", - className = "", - isExiting = false, - onExitComplete, -}) => { - // For entrance: Modal appears first, then logo with delay - // For exit: Logo disappears first, then modal with delay - const overlayAnimation = isExiting ? "modalFadeOut" : "modalFadeIn"; - const containerAnimation = isExiting ? "modalZoomOut" : "modalZoomIn"; - const faviconAnimation = isExiting ? "fadeOutSlideDown" : "fadeInSlideUp"; - - // Animation delays - const overlayDelay = isExiting ? "0.3s" : "0s"; // Delay modal exit until logo animation completes - const containerDelay = isExiting ? "0.3s" : "0s"; // Delay container exit until logo animation completes - const faviconDelay = isExiting ? "0s" : "0.3s"; // Logo appears with delay on entrance, but exits immediately - - // Handle exit animation completion - useEffect(() => { - if (isExiting && onExitComplete) { - // Wait for all animations to complete before calling onExitComplete - // Logo animation (0.3s) + delay (0.3s) + modal animation (0.3s) - const timer = setTimeout(() => { - onExitComplete(); - }, 600); // Total animation duration with delay - - return () => clearTimeout(timer); - } - }, [isExiting, onExitComplete]); - - const modalContent = ( -
- {/* Backdrop with blur effect */} - - ); - - // Use createPortal to render the modal at the end of the document body - return ReactDOM.createPortal(modalContent, document.body); -}; - -export default Modal; From 2bf7cfc2c06576be78a99a82204aa1889825a887 Mon Sep 17 00:00:00 2001 From: Alex TYRODE Date: Fri, 25 Apr 2025 17:14:58 +0000 Subject: [PATCH 07/10] feat: enhance AuthDialog with new styles and speech bubble - Updated AuthDialog.scss to improve layout and responsiveness, including new animations for the logo and speech bubble. - Modified AuthDialog component to display a random greeting message from a predefined list. - Adjusted styles in index.scss for modal backgrounds to enhance visual consistency. - Improved button styles and layout within the authentication modal for better user experience. --- src/frontend/src/styles/AuthDialog.scss | 146 +++++++++++++++++++++--- src/frontend/src/styles/index.scss | 4 +- src/frontend/src/ui/AuthDialog.tsx | 109 ++++++++++++------ 3 files changed, 209 insertions(+), 50 deletions(-) diff --git a/src/frontend/src/styles/AuthDialog.scss b/src/frontend/src/styles/AuthDialog.scss index e1e6360..1aab2b1 100644 --- a/src/frontend/src/styles/AuthDialog.scss +++ b/src/frontend/src/styles/AuthDialog.scss @@ -1,36 +1,149 @@ /* Auth Modal Styles */ +.excalidraw .Dialog--fullscreen { + .auth-modal { + &__logo-container { + display: none; + } + &__content { + height: 100%; + margin-bottom: 0; + display: flex; + flex-direction: column; + justify-content: space-between; + flex: 1; + } + &__buttons { + margin-bottom: auto; + } + } + + .Island { + height: 100%; + display: flex; + flex-direction: column; + } +} + .auth-modal { + .Island { + padding-top: 15px !important; + padding-bottom: 20px !important; + } + + &__wrapper { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 5; + background-color: rgba(0, 0, 0, 0.2); + backdrop-filter: blur(1px); + } + + &__logo-container { + position: relative; + width: fit-content; + width: 100%; + height: 100%; + top: 50%; + left: 50%; + opacity: 0; + animation: logo-fade-in 2s cubic-bezier(0.00, 1.26, 0.64, 0.95) forwards; + animation-delay: 0.5s; + } + + @keyframes logo-fade-in { + from { + transform: translate(-270px, -318px); + opacity: 0; + } + to { + transform: translate(-230px, -318px); + opacity: 1; + } + } + + &__logo { + width: 60px; + height: 60px; + object-fit: contain; + } + + &__logo-speech-bubble { + position: absolute; + background-color: rgb(232, 232, 232); + color: #333; + padding: 8px 12px; + border-radius: 12px; + font-size: 14px; + max-width: 180px; + left: 73px; + top: 0px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); + animation: bubble-appear 0.3s ease-out forwards; + animation-delay: 1.5s; + opacity: 0; + transform: translateY(5px); + + &::before { + content: ''; + position: absolute; + left: -10px; + top: 10px; + border-width: 5px 10px 5px 0; + border-style: solid; + border-color: transparent rgb(232, 232, 232) transparent transparent; + } + } + + @keyframes bubble-appear { + from { + opacity: 0; + transform: translateY(5px); + } + to { + opacity: 1; + transform: translateY(0); + } + } + &__title-container { display: flex; align-items: center; - } &__title { margin: 0 auto; - font-size: 72px; + font-size: 65px; font-weight: 700; color: white; text-align: center; - display: inline-block; &-dot { color: #fa8933; } } + &__content { + display: flex; + flex-direction: column; + align-items: center; + } + &__description { color: #a0a0a9; font-size: 18px; line-height: 1.5; text-align: center; white-space: pre-line; - padding: 0 90px; margin-bottom: 40px; - margin-top: 40px; - + margin-top: 10px; + min-width: 300px; + max-width: 65%; + .highlight { color: #cfcfcf; font-weight: bold; @@ -42,17 +155,19 @@ align-items: center; flex-direction: column; gap: 13px; - margin-bottom: 40px; + width: 100%; + padding-bottom: 50px; button { display: flex; align-items: center; justify-content: center; + width: 65%; + min-width: 250px; gap: 8px; - width: 85%; height: 44px; border-radius: 6px; - border: 1px solid #727279; + border: 2px solid #727279; font-size: 15px; font-weight: 500; transition: all 0.2s ease; @@ -61,7 +176,7 @@ color: white; &:hover { - border: 1px solid #cc6d24; + border: 2px solid #cc6d24; } } } @@ -74,7 +189,10 @@ gap: 16px; color: #a0a0a0; margin-bottom: 10px; - + padding-top: 10px; + border-top: 1px solid var(--dialog-border-color); + width: 100%; + &-link { display: flex; align-items: center; @@ -89,9 +207,9 @@ } &__warning { - text-align: center; - color: #606060bd; - font-size: 12px; + text-align: right; + color: #828282bd; + font-size: 14px; font-weight: 400; } } diff --git a/src/frontend/src/styles/index.scss b/src/frontend/src/styles/index.scss index 4732ea4..8fa96ed 100644 --- a/src/frontend/src/styles/index.scss +++ b/src/frontend/src/styles/index.scss @@ -62,8 +62,8 @@ body { } .excalidraw .Modal__background { - background-color: rgba(0, 0, 0, 0.2); - backdrop-filter: blur(1px); + background-color: rgba(0, 0, 0, 0); + backdrop-filter: blur(0px); animation: Modal__background__fade-in 0.3s ease-out forwards !important; &.animations-disabled { diff --git a/src/frontend/src/ui/AuthDialog.tsx b/src/frontend/src/ui/AuthDialog.tsx index 0daee0a..130d5ca 100644 --- a/src/frontend/src/ui/AuthDialog.tsx +++ b/src/frontend/src/ui/AuthDialog.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useMemo } from "react"; import { capture } from "../utils/posthog"; import { Mail } from "lucide-react"; import { queryClient } from "../api/queryClient"; @@ -14,15 +14,48 @@ interface AuthDialogProps { } export const AuthDialog = ({ - description = <>Welcome to your whiteboard IDE.

Open terminals and start coding right away in your own Ubuntu VM!, - warningText = "This is a beta. Backup your work!", + description = <>Welcome to your whiteboard IDE.

Open terminals, VSCode or Cursor in your pad, and start coding right away., + warningText = <>This is a open-source project in beta.
Backup your work!, onClose, children, }: AuthDialogProps) => { const [modalIsShown, setModalIsShown] = useState(true); + // Array of random messages that the logo can "say" + const logoMessages = [ + "Hello there!", + "Welcome to pad.ws!", + "Ready to code?", + "Let's build something cool!", + "Code, collaborate, create!", + "Happy coding!", + "Ideas become reality here!", + "Let's get productive!", + "Let's turn ideas into code!" + ]; + + // Select a random message when component mounts + const randomMessage = useMemo(() => { + const randomIndex = Math.floor(Math.random() * logoMessages.length); + return logoMessages[randomIndex]; + }, []); + useEffect(() => { capture("auth_modal_shown"); + + // Load GitHub buttons script + const script = document.createElement('script'); + script.src = 'https://buttons.github.io/buttons.js'; + script.async = true; + script.defer = true; + document.body.appendChild(script); + + return () => { + // Clean up script when component unmounts + if (document.body.contains(script)) { + document.body.removeChild(script); + } + }; }, []); useEffect(() => { @@ -54,7 +87,6 @@ export const AuthDialog = ({ // Prepare the content for the Dialog const dialogContent = (
); return ( <> {modalIsShown && ( - - {/* pad.ws logo */} -

pad.ws

+
+
+ pad.ws logo +
+ {randomMessage}
- } - closeOnClickOutside={false} - children={children || dialogContent} - /> +
+ +

pad.ws

+
+ } + closeOnClickOutside={false} + children={children || dialogContent} + /> +
)} ); From c797e1dd7c95e7324091d15e3177260ed9f88ffe Mon Sep 17 00:00:00 2001 From: Alex TYRODE Date: Fri, 25 Apr 2025 17:17:04 +0000 Subject: [PATCH 08/10] fix: correct punctuation in AuthDialog description --- src/frontend/src/ui/AuthDialog.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/src/ui/AuthDialog.tsx b/src/frontend/src/ui/AuthDialog.tsx index 130d5ca..97673c4 100644 --- a/src/frontend/src/ui/AuthDialog.tsx +++ b/src/frontend/src/ui/AuthDialog.tsx @@ -14,7 +14,7 @@ interface AuthDialogProps { } export const AuthDialog = ({ - description = <>Welcome to your whiteboard IDE.

Open terminals, VSCode or Cursor in your pad, and start coding right away., + description = <>Welcome to your whiteboard IDE.

Open terminals, VSCode, or Cursor in your pad, and start coding right away., warningText = <>This is a open-source project in beta.
Backup your work!, onClose, children, From aaac754a36eab82c3371936a01bc2b80d8404aae Mon Sep 17 00:00:00 2001 From: Alex TYRODE Date: Fri, 25 Apr 2025 17:30:54 +0000 Subject: [PATCH 09/10] fix: correct spelling in warning text of AuthDialog component --- src/frontend/src/ui/AuthDialog.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/src/ui/AuthDialog.tsx b/src/frontend/src/ui/AuthDialog.tsx index 97673c4..b7e0dfe 100644 --- a/src/frontend/src/ui/AuthDialog.tsx +++ b/src/frontend/src/ui/AuthDialog.tsx @@ -15,7 +15,7 @@ interface AuthDialogProps { export const AuthDialog = ({ description = <>Welcome to your whiteboard IDE.

Open terminals, VSCode, or Cursor in your pad, and start coding right away., - warningText = <>This is a open-source project in beta.
Backup your work!, + warningText = <>This is an open-source project in beta.
Back up your work!, onClose, children, }: AuthDialogProps) => { From 90f9624d677acb50594bbafcff9d938bca4186ee Mon Sep 17 00:00:00 2001 From: Alex TYRODE Date: Fri, 25 Apr 2025 17:36:59 +0000 Subject: [PATCH 10/10] refactor: switch DiscordButton to new icon --- src/frontend/src/styles/DiscordButton.scss | 3 +++ src/frontend/src/ui/DiscordButton.tsx | 25 ++++++---------------- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/src/frontend/src/styles/DiscordButton.scss b/src/frontend/src/styles/DiscordButton.scss index aa5e57a..9bdba12 100644 --- a/src/frontend/src/styles/DiscordButton.scss +++ b/src/frontend/src/styles/DiscordButton.scss @@ -1,4 +1,7 @@ .discord-button { + display: flex; + align-items: center; + justify-content: center; height: 2.75rem; min-width: 2.75rem; margin-left: 0.275rem; diff --git a/src/frontend/src/ui/DiscordButton.tsx b/src/frontend/src/ui/DiscordButton.tsx index 0cee6d0..e9cf00e 100644 --- a/src/frontend/src/ui/DiscordButton.tsx +++ b/src/frontend/src/ui/DiscordButton.tsx @@ -1,4 +1,5 @@ import React from "react"; +import DiscordIcon from "../icons/DiscordIcon"; const DISCORD_URL = "https://discord.gg/NnXSESxWpA"; @@ -15,24 +16,12 @@ const DiscordButton: React.FC = () => { type="button" style={{ padding: 0, width: "2.5rem" }} > - + ); };