From 8ab93e3b67d307f416fe806268cd6592e8cfce8f Mon Sep 17 00:00:00 2001 From: Alex TYRODE Date: Fri, 25 Apr 2025 19:41:36 +0000 Subject: [PATCH 1/9] refactor: move every stylesheet as sibling of their counterpart --- src/frontend/{src/styles => }/index.scss | 16 +- src/frontend/index.tsx | 2 +- .../CustomEmbeddableRenderer.scss | 0 src/frontend/src/CustomEmbeddableRenderer.tsx | 1 + .../pad/{styles => buttons}/ActionButton.scss | 0 src/frontend/src/pad/buttons/ActionButton.tsx | 3 +- .../{styles => buttons}/ActionButtonGrid.scss | 0 .../src/pad/buttons/ActionButtonGrid.tsx | 2 +- .../pad/{styles => containers}/Dashboard.scss | 0 src/frontend/src/pad/containers/Dashboard.tsx | 3 +- .../{styles => controls}/ControlButton.scss | 0 .../src/pad/controls/ControlButton.tsx | 2 +- .../{styles => controls}/StateIndicator.scss | 0 .../src/pad/controls/StateIndicator.tsx | 2 +- .../src/{styles => pad/editors}/Editor.scss | 0 src/frontend/src/pad/editors/Editor.tsx | 2 +- .../{styles => pad/editors}/HtmlEditor.scss | 0 src/frontend/src/pad/editors/HtmlEditor.tsx | 2 +- .../src/pad/styles/CanvasBackups.scss | 205 ------------------ src/frontend/src/pad/styles/index.scss | 6 - src/frontend/src/styles/MainMenuLabel.scss | 23 -- src/frontend/src/styles/fonts.scss | 9 - .../src/{styles => ui}/AuthDialog.scss | 0 src/frontend/src/ui/AuthDialog.tsx | 4 +- .../src/{styles => ui}/BackupsDialog.scss | 0 src/frontend/src/ui/BackupsDialog.tsx | 2 +- .../src/{styles => ui}/DiscordButton.scss | 0 src/frontend/src/ui/DiscordButton.tsx | 2 +- .../src/{styles => ui}/FeedbackButton.scss | 0 src/frontend/src/ui/FeedbackButton.tsx | 1 + src/frontend/src/ui/MainMenu.scss | 24 ++ src/frontend/src/ui/MainMenu.tsx | 2 +- 32 files changed, 47 insertions(+), 266 deletions(-) rename src/frontend/{src/styles => }/index.scss (83%) rename src/frontend/src/{styles => }/CustomEmbeddableRenderer.scss (100%) rename src/frontend/src/pad/{styles => buttons}/ActionButton.scss (100%) rename src/frontend/src/pad/{styles => buttons}/ActionButtonGrid.scss (100%) rename src/frontend/src/pad/{styles => containers}/Dashboard.scss (100%) rename src/frontend/src/pad/{styles => controls}/ControlButton.scss (100%) rename src/frontend/src/pad/{styles => controls}/StateIndicator.scss (100%) rename src/frontend/src/{styles => pad/editors}/Editor.scss (100%) rename src/frontend/src/{styles => pad/editors}/HtmlEditor.scss (100%) delete mode 100644 src/frontend/src/pad/styles/CanvasBackups.scss delete mode 100644 src/frontend/src/pad/styles/index.scss delete mode 100644 src/frontend/src/styles/MainMenuLabel.scss delete mode 100644 src/frontend/src/styles/fonts.scss rename src/frontend/src/{styles => ui}/AuthDialog.scss (100%) rename src/frontend/src/{styles => ui}/BackupsDialog.scss (100%) rename src/frontend/src/{styles => ui}/DiscordButton.scss (100%) rename src/frontend/src/{styles => ui}/FeedbackButton.scss (100%) create mode 100644 src/frontend/src/ui/MainMenu.scss diff --git a/src/frontend/src/styles/index.scss b/src/frontend/index.scss similarity index 83% rename from src/frontend/src/styles/index.scss rename to src/frontend/index.scss index 0ba50a2..c302f5a 100644 --- a/src/frontend/src/styles/index.scss +++ b/src/frontend/index.scss @@ -1,11 +1,11 @@ -@import './fonts.scss'; -@import './HtmlEditor.scss'; -@import './Editor.scss'; -@import './AuthDialog.scss'; -@import './FeedbackButton.scss'; -@import './DiscordButton.scss'; -@import './MainMenuLabel.scss'; -@import './CustomEmbeddableRenderer.scss'; +@font-face { + font-family: 'Roboto'; + src: url('/assets/fonts/Roboto-VariableFont_wdth,wght.ttf') format('truetype-variations'); + font-weight: 100 900; + font-stretch: 75% 100%; + font-style: normal; + font-display: swap; +} /* Override Excalidraw styles */ diff --git a/src/frontend/index.tsx b/src/frontend/index.tsx index 7c9fee1..35c5414 100644 --- a/src/frontend/index.tsx +++ b/src/frontend/index.tsx @@ -9,7 +9,7 @@ import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; import { queryClient } from './src/api/queryClient'; import "@atyrode/excalidraw/index.css"; -import "./src/styles/index.scss"; +import "./index.scss"; import type * as TExcalidraw from "@atyrode/excalidraw"; diff --git a/src/frontend/src/styles/CustomEmbeddableRenderer.scss b/src/frontend/src/CustomEmbeddableRenderer.scss similarity index 100% rename from src/frontend/src/styles/CustomEmbeddableRenderer.scss rename to src/frontend/src/CustomEmbeddableRenderer.scss diff --git a/src/frontend/src/CustomEmbeddableRenderer.tsx b/src/frontend/src/CustomEmbeddableRenderer.tsx index 4bffe2f..4e6f7a8 100644 --- a/src/frontend/src/CustomEmbeddableRenderer.tsx +++ b/src/frontend/src/CustomEmbeddableRenderer.tsx @@ -9,6 +9,7 @@ import { Editor, } from './pad'; import { ActionButton } from './pad/buttons'; +import "./CustomEmbeddableRenderer.scss"; export const renderCustomEmbeddable = ( element: NonDeleted, diff --git a/src/frontend/src/pad/styles/ActionButton.scss b/src/frontend/src/pad/buttons/ActionButton.scss similarity index 100% rename from src/frontend/src/pad/styles/ActionButton.scss rename to src/frontend/src/pad/buttons/ActionButton.scss diff --git a/src/frontend/src/pad/buttons/ActionButton.tsx b/src/frontend/src/pad/buttons/ActionButton.tsx index 6ec3e67..9d9a6ef 100644 --- a/src/frontend/src/pad/buttons/ActionButton.tsx +++ b/src/frontend/src/pad/buttons/ActionButton.tsx @@ -1,10 +1,9 @@ import React, { useState, useEffect, useRef } from 'react'; -import '../styles/index.scss'; import { useWorkspaceState } from '../../api/hooks'; // Import SVGs as modules - using relative paths from the action button location import { Terminal, Braces, Settings, Plus, ExternalLink, Monitor } from 'lucide-react'; import { ActionType, TargetType, CodeVariant, ActionButtonProps } from './types'; -import '../styles/ActionButton.scss'; +import './ActionButton.scss'; import { capture } from '../../utils/posthog'; import { ExcalidrawElementFactory, PlacementMode } from '../../lib/ExcalidrawElementFactory'; diff --git a/src/frontend/src/pad/styles/ActionButtonGrid.scss b/src/frontend/src/pad/buttons/ActionButtonGrid.scss similarity index 100% rename from src/frontend/src/pad/styles/ActionButtonGrid.scss rename to src/frontend/src/pad/buttons/ActionButtonGrid.scss diff --git a/src/frontend/src/pad/buttons/ActionButtonGrid.tsx b/src/frontend/src/pad/buttons/ActionButtonGrid.tsx index cb9b853..7f7cd74 100644 --- a/src/frontend/src/pad/buttons/ActionButtonGrid.tsx +++ b/src/frontend/src/pad/buttons/ActionButtonGrid.tsx @@ -1,7 +1,7 @@ import React from 'react'; import ActionButton from './ActionButton'; import { TargetType, CodeVariant, ActionType } from './types'; -import '../styles/ActionButtonGrid.scss'; +import './ActionButtonGrid.scss'; // Function to get button background color based on target and variant const getButtonColor = (target: TargetType, codeVariant?: CodeVariant): string => { diff --git a/src/frontend/src/pad/styles/Dashboard.scss b/src/frontend/src/pad/containers/Dashboard.scss similarity index 100% rename from src/frontend/src/pad/styles/Dashboard.scss rename to src/frontend/src/pad/containers/Dashboard.scss diff --git a/src/frontend/src/pad/containers/Dashboard.tsx b/src/frontend/src/pad/containers/Dashboard.tsx index 45db6fc..19e7ce1 100644 --- a/src/frontend/src/pad/containers/Dashboard.tsx +++ b/src/frontend/src/pad/containers/Dashboard.tsx @@ -5,8 +5,7 @@ import StateIndicator from '../controls/StateIndicator'; import ControlButton from '../controls/ControlButton'; import { ActionButtonGrid } from '../buttons'; import { useWorkspaceState } from '../../api/hooks'; -import '../styles/index.scss'; -import '../styles/Dashboard.scss'; +import './Dashboard.scss'; // Direct import from types type TargetType = 'terminal' | 'code'; diff --git a/src/frontend/src/pad/styles/ControlButton.scss b/src/frontend/src/pad/controls/ControlButton.scss similarity index 100% rename from src/frontend/src/pad/styles/ControlButton.scss rename to src/frontend/src/pad/controls/ControlButton.scss diff --git a/src/frontend/src/pad/controls/ControlButton.tsx b/src/frontend/src/pad/controls/ControlButton.tsx index 18e3412..f765663 100644 --- a/src/frontend/src/pad/controls/ControlButton.tsx +++ b/src/frontend/src/pad/controls/ControlButton.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useWorkspaceState, useStartWorkspace, useStopWorkspace } from '../../api/hooks'; -import '../styles/index.scss'; +import './ControlButton.scss'; import { Play, Square, LoaderCircle } from 'lucide-react'; export const ControlButton: React.FC = () => { diff --git a/src/frontend/src/pad/styles/StateIndicator.scss b/src/frontend/src/pad/controls/StateIndicator.scss similarity index 100% rename from src/frontend/src/pad/styles/StateIndicator.scss rename to src/frontend/src/pad/controls/StateIndicator.scss diff --git a/src/frontend/src/pad/controls/StateIndicator.tsx b/src/frontend/src/pad/controls/StateIndicator.tsx index b840ed2..53f47ac 100644 --- a/src/frontend/src/pad/controls/StateIndicator.tsx +++ b/src/frontend/src/pad/controls/StateIndicator.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { useWorkspaceState, useAuthCheck } from '../../api/hooks'; -import '../styles/index.scss'; +import './StateIndicator.scss'; export const StateIndicator: React.FC = () => { const { data: isAuthenticated, isLoading: isAuthLoading } = useAuthCheck(); diff --git a/src/frontend/src/styles/Editor.scss b/src/frontend/src/pad/editors/Editor.scss similarity index 100% rename from src/frontend/src/styles/Editor.scss rename to src/frontend/src/pad/editors/Editor.scss diff --git a/src/frontend/src/pad/editors/Editor.tsx b/src/frontend/src/pad/editors/Editor.tsx index fad0763..9124f13 100644 --- a/src/frontend/src/pad/editors/Editor.tsx +++ b/src/frontend/src/pad/editors/Editor.tsx @@ -1,7 +1,7 @@ import React, { useRef, useState, useEffect, useCallback } from 'react'; import MonacoEditor from '@monaco-editor/react'; import LanguageSelector from './LanguageSelector'; -import '../../styles/Editor.scss'; +import './Editor.scss'; interface EditorProps { defaultValue?: string; diff --git a/src/frontend/src/styles/HtmlEditor.scss b/src/frontend/src/pad/editors/HtmlEditor.scss similarity index 100% rename from src/frontend/src/styles/HtmlEditor.scss rename to src/frontend/src/pad/editors/HtmlEditor.scss diff --git a/src/frontend/src/pad/editors/HtmlEditor.tsx b/src/frontend/src/pad/editors/HtmlEditor.tsx index 84715ac..6a7f4ef 100644 --- a/src/frontend/src/pad/editors/HtmlEditor.tsx +++ b/src/frontend/src/pad/editors/HtmlEditor.tsx @@ -3,7 +3,7 @@ import type { NonDeleted, ExcalidrawEmbeddableElement } from '@atyrode/excalidra import type { AppState } from '@atyrode/excalidraw/types'; import Editor from './Editor'; import { ExcalidrawElementFactory } from '../../lib/ExcalidrawElementFactory'; -import '../../styles/HtmlEditor.scss'; +import './HtmlEditor.scss'; interface HtmlEditorProps { element: NonDeleted; diff --git a/src/frontend/src/pad/styles/CanvasBackups.scss b/src/frontend/src/pad/styles/CanvasBackups.scss deleted file mode 100644 index 7dea2d8..0000000 --- a/src/frontend/src/pad/styles/CanvasBackups.scss +++ /dev/null @@ -1,205 +0,0 @@ -.canvas-backups { - display: flex; - flex-direction: column; - height: 100%; - width: 100%; - padding: 12px; - overflow-y: auto; - background-color: transparent; - border-radius: 7px; - font-family: Arial, sans-serif; - box-sizing: border-box; - transition: all 0.3s ease-in-out; - - &--loading, - &--error, - &--empty { - display: flex; - align-items: center; - justify-content: center; - height: 100%; - color: #ffffff; - font-style: italic; - opacity: 0.9; - animation: fadeIn 0.5s ease-in-out; - } - - &--error { - color: #f44336; - } - - &__title { - margin: 0 0 1rem; - font-size: 1.2rem; - font-weight: 500; - color: #ffffff; - text-align: center; - opacity: 0.9; - } - - &__list { - list-style: none; - padding: 0; - margin: 0; - flex-grow: 1; - overflow-y: auto; - } - - &__item { - display: flex; - align-items: center; - justify-content: space-between; - padding: 12px 15px; - margin-bottom: 8px; - background-color: #32373c; - border-radius: 10px; - cursor: pointer; - transition: all 0.3s ease; - position: relative; - overflow: hidden; - - &::after { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(255, 255, 255, 0); - transition: background-color 0.3s ease; - pointer-events: none; - border-radius: 10px; - } - - &:hover::after { - background-color: rgba(255, 255, 255, 0.1); - } - - &:active::after { - background-color: rgba(255, 255, 255, 0.05); - } - - &:last-child { - margin-bottom: 0; - } - } - - &__item-content { - display: flex; - align-items: center; - gap: 10px; - } - - &__number { - font-size: 0.9rem; - font-weight: 600; - color: #cc6d24; - background-color: rgba(106, 122, 255, 0.1); - padding: 4px 8px; - border-radius: 4px; - min-width: 28px; - text-align: center; - } - - &__timestamp { - font-size: 0.9rem; - color: #ffffff; - opacity: 0.9; - } - - &__restore-button { - background-color: transparent; - border: none; - color: #cc6d24; - font-size: 0.9rem; - cursor: pointer; - padding: 0.25rem 0.5rem; - border-radius: 4px; - transition: all 0.2s ease; - - &:hover { - background-color: rgba(106, 122, 255, 0.1); - } - } - - &__confirmation { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - padding: 20px; - background-color: #32373c; - border-radius: 10px; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); - text-align: center; - color: #ffffff; - animation: fadeIn 0.4s ease-in-out; - } - - &__warning { - color: #f44336; - font-weight: 500; - margin: 0.5rem 0 1rem; - } - - &__actions { - display: flex; - gap: 1rem; - } - - &__button { - padding: 10px 16px; - border: none; - border-radius: 7px; - font-weight: 500; - cursor: pointer; - transition: all 0.3s ease; - position: relative; - overflow: hidden; - - &::after { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(255, 255, 255, 0); - transition: background-color 0.3s ease; - pointer-events: none; - border-radius: 7px; - } - - &:hover::after { - background-color: rgba(255, 255, 255, 0.1); - } - - &:active::after { - background-color: rgba(255, 255, 255, 0.05); - } - - &--restore { - background-color: #cc6d24; - border: 1px solid #cecece00; - color: white; - - &:hover { - border: 1px solid #cecece; - } - } - - &--cancel { - background-color: #4a4a54; - color: #ffffff; - - &:hover { - background-color: #3a3a44; - } - } - } -} - -@keyframes fadeIn { - from { opacity: 0; transform: translateY(10px); } - to { opacity: 1; transform: translateY(0); } -} diff --git a/src/frontend/src/pad/styles/index.scss b/src/frontend/src/pad/styles/index.scss deleted file mode 100644 index 3a2e46b..0000000 --- a/src/frontend/src/pad/styles/index.scss +++ /dev/null @@ -1,6 +0,0 @@ -@import './ActionButton.scss'; -@import './ActionButtonGrid.scss'; -@import './ControlButton.scss'; -@import './StateIndicator.scss'; -@import './Dashboard.scss'; -@import './CanvasBackups.scss'; diff --git a/src/frontend/src/styles/MainMenuLabel.scss b/src/frontend/src/styles/MainMenuLabel.scss deleted file mode 100644 index 6468bc9..0000000 --- a/src/frontend/src/styles/MainMenuLabel.scss +++ /dev/null @@ -1,23 +0,0 @@ -.main-menu-label { - width: 100%; - display: flex; - align-items: center; - gap: 0.5rem; - margin-top: 0.25rem; - margin-bottom: 0.25rem; - font-weight: 500; - font-size: 1rem; - cursor: default; -} - -.main-menu-top-row { - display: flex; - align-items: center; - justify-content: space-between; - padding-left: 0.5rem; -} - -.main-menu-label span:last-child { - color: #fa8933; - font-weight: 1000; -} diff --git a/src/frontend/src/styles/fonts.scss b/src/frontend/src/styles/fonts.scss deleted file mode 100644 index 1506b7a..0000000 --- a/src/frontend/src/styles/fonts.scss +++ /dev/null @@ -1,9 +0,0 @@ -/* Font declarations */ -@font-face { - font-family: 'Roboto'; - src: url('/assets/fonts/Roboto-VariableFont_wdth,wght.ttf') format('truetype-variations'); - font-weight: 100 900; - font-stretch: 75% 100%; - font-style: normal; - font-display: swap; -} diff --git a/src/frontend/src/styles/AuthDialog.scss b/src/frontend/src/ui/AuthDialog.scss similarity index 100% rename from src/frontend/src/styles/AuthDialog.scss rename to src/frontend/src/ui/AuthDialog.scss diff --git a/src/frontend/src/ui/AuthDialog.tsx b/src/frontend/src/ui/AuthDialog.tsx index b7e0dfe..1386dba 100644 --- a/src/frontend/src/ui/AuthDialog.tsx +++ b/src/frontend/src/ui/AuthDialog.tsx @@ -1,8 +1,8 @@ import React, { useState, useEffect, useMemo } from "react"; import { capture } from "../utils/posthog"; -import { Mail } from "lucide-react"; import { queryClient } from "../api/queryClient"; -import { GoogleIcon, GithubIcon, DiscordIcon } from "../icons"; +import { GoogleIcon, GithubIcon } from "../icons"; +import "./AuthDialog.scss"; import { Dialog } from "@atyrode/excalidraw"; diff --git a/src/frontend/src/styles/BackupsDialog.scss b/src/frontend/src/ui/BackupsDialog.scss similarity index 100% rename from src/frontend/src/styles/BackupsDialog.scss rename to src/frontend/src/ui/BackupsDialog.scss diff --git a/src/frontend/src/ui/BackupsDialog.tsx b/src/frontend/src/ui/BackupsDialog.tsx index 0018714..5eef5a0 100644 --- a/src/frontend/src/ui/BackupsDialog.tsx +++ b/src/frontend/src/ui/BackupsDialog.tsx @@ -2,7 +2,7 @@ import React, { useState, useCallback } from "react"; import { Dialog } from "@atyrode/excalidraw"; import { useCanvasBackups, CanvasBackup } from "../api/hooks"; import { normalizeCanvasData } from "../utils/canvasUtils"; -import "../styles/BackupsDialog.scss"; +import "./BackupsDialog.scss"; interface BackupsModalProps { excalidrawAPI?: any; diff --git a/src/frontend/src/styles/DiscordButton.scss b/src/frontend/src/ui/DiscordButton.scss similarity index 100% rename from src/frontend/src/styles/DiscordButton.scss rename to src/frontend/src/ui/DiscordButton.scss diff --git a/src/frontend/src/ui/DiscordButton.tsx b/src/frontend/src/ui/DiscordButton.tsx index e9cf00e..361d3a0 100644 --- a/src/frontend/src/ui/DiscordButton.tsx +++ b/src/frontend/src/ui/DiscordButton.tsx @@ -1,6 +1,6 @@ import React from "react"; import DiscordIcon from "../icons/DiscordIcon"; - +import "./DiscordButton.scss"; const DISCORD_URL = "https://discord.gg/NnXSESxWpA"; const DiscordButton: React.FC = () => { diff --git a/src/frontend/src/styles/FeedbackButton.scss b/src/frontend/src/ui/FeedbackButton.scss similarity index 100% rename from src/frontend/src/styles/FeedbackButton.scss rename to src/frontend/src/ui/FeedbackButton.scss diff --git a/src/frontend/src/ui/FeedbackButton.tsx b/src/frontend/src/ui/FeedbackButton.tsx index 4cc6802..c4209ec 100644 --- a/src/frontend/src/ui/FeedbackButton.tsx +++ b/src/frontend/src/ui/FeedbackButton.tsx @@ -1,5 +1,6 @@ import React from "react"; import { capture } from "../utils/posthog"; +import "./FeedbackButton.scss"; const FeedbackButton: React.FC = () => { const handleClick = () => { diff --git a/src/frontend/src/ui/MainMenu.scss b/src/frontend/src/ui/MainMenu.scss new file mode 100644 index 0000000..1bcece1 --- /dev/null +++ b/src/frontend/src/ui/MainMenu.scss @@ -0,0 +1,24 @@ +.main-menu-label { + width: 100%; + display: flex; + align-items: center; + gap: 0.5rem; + margin-top: 0.25rem; + margin-bottom: 0.25rem; + font-weight: 500; + font-size: 1rem; + cursor: default; + } + + .main-menu-top-row { + display: flex; + align-items: center; + justify-content: space-between; + padding-left: 0.5rem; + } + + .main-menu-label span:last-child { + color: #fa8933; + font-weight: 1000; + } + \ No newline at end of file diff --git a/src/frontend/src/ui/MainMenu.tsx b/src/frontend/src/ui/MainMenu.tsx index a33dde1..988e74f 100644 --- a/src/frontend/src/ui/MainMenu.tsx +++ b/src/frontend/src/ui/MainMenu.tsx @@ -8,7 +8,7 @@ import { capture } from '../utils/posthog'; import { ExcalidrawElementFactory, PlacementMode } from '../lib/ExcalidrawElementFactory'; import { useUserProfile } from "../api/hooks"; import { queryClient } from "../api/queryClient"; - +import "./MainMenu.scss"; interface MainMenuConfigProps { MainMenu: typeof MainMenuType; excalidrawAPI: ExcalidrawImperativeAPI | null; From febca223a641aea516c5e74ebd7524bb59a2f891 Mon Sep 17 00:00:00 2001 From: Alex TYRODE Date: Fri, 25 Apr 2025 21:21:09 +0000 Subject: [PATCH 2/9] refactor: switch ActionButton to BEM --- .../src/pad/buttons/ActionButton.scss | 670 +++++++----------- src/frontend/src/pad/buttons/ActionButton.tsx | 74 +- 2 files changed, 298 insertions(+), 446 deletions(-) diff --git a/src/frontend/src/pad/buttons/ActionButton.scss b/src/frontend/src/pad/buttons/ActionButton.scss index 474c385..a321a37 100644 --- a/src/frontend/src/pad/buttons/ActionButton.scss +++ b/src/frontend/src/pad/buttons/ActionButton.scss @@ -1,309 +1,247 @@ -.action-wrapper { - width: 100%; - height: 100%; - - display: flex; - flex-direction: column; - - background-color: #00000000; - - font-family: Arial, sans-serif; - box-sizing: border-box; - transition: all 0.3s ease-in-out; - - /* Add responsive classes for different button widths */ - &.compact-mode-1 { - .button-settings-icon { - display: none; - margin-left: 8px; - } - } - - &.compact-mode-2 { - .button-icon { - display: none; - } - - .button-settings-icon { - display: none; - margin-left: 8px; - } +.action-button { + &__wrapper { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + background-color: #00000000; + font-family: Arial, sans-serif; + box-sizing: border-box; + transition: all 0.3s ease-in-out; - } - - &.compact-mode-3 { - .button-icon { - display: none; + /* Default state (non-compact) - show all elements */ + &--compact-0 .action-button { + &__icon, + &__text, + &__action-text, + &__settings-icon { + display: flex; + } } - .button-settings-icon { - display: none; + /* Hide settings icon */ + &--compact-1 .action-button { + &__icon, + &__text, + &__action-text { + display: flex; + } } - .button-action-text { - display: none; + /* Hide action icon */ + &--compact-2 .action-button { + &__text, + &__action-text { + display: flex; + } } - .button-action-icon { - display: flex; - margin-right: 0; - } - } - - &.compact-mode-4 { - .button-icon { - display: none; + /* Switch from action-text to action-icon */ + &--compact-3 .action-button { + &__text, + &__action-icon { + display: flex; + } } - .button-settings-icon { - display: none; + /* Show action-text again, hide action-icon and change layout to vertical */ + &--compact-4 .action-button { + &__text, + &__action-text { + display: flex; + } + + &__content { + flex-direction: column; + } } - .button-action-text { - display: none; + /* Only action-icon is shown */ + &--compact-5 .action-button { + &__action-icon { + display: flex; + } + + &__action-icon-svg { + /* Make the plus icon (embed action) slightly larger to compensate for optical illusion */ + &[data-action-type="embed"] { + transform: scale(1.25); + } + } } - - .button-action-icon { - display: none; + + /* Unified rule for single-element modes (4 and 5) */ + &--compact-4 .action-button, + &--compact-5 .action-button { + &__content { + justify-content: center; + } } - .button-text { - text-align: center; - width: 100%; + /* Target type modifiers */ + &--vscode { + .action-tabs__item--selected { + &::before { + background-color: #6a7aff !important; /* Brighter blue for VSCode indicator */ + } + } } - .button-left { - width: 100%; - justify-content: center; + &--cursor { + .action-tabs__item--selected { + &::before { + background-color: #dc143c !important; /* Crimson for Cursor indicator */ + } + } } - .button-right { - display: none; + &--terminal { + .action-tabs__item--selected { + &::before { + background-color: #4caf50 !important; /* Green for Terminal indicator */ + } + } } } - &.compact-mode-5 { - .button-icon, - .button-text, - .button-action-text, - .button-settings-icon { - display: none; - } + &__main { + width: 100%; + height: 100%; + border-radius: 10px; + } + + &__container { + width: 100%; + height: 100%; + padding: 12px 15px; + background-color: transparent; + color: white; + border: none; + border-radius: 10px; + position: relative; + overflow: hidden; + font-size: 16px; + font-weight: 500; + cursor: pointer; + transition: all 0.3s ease; - button { - padding: 8px; /* Reduce padding to give more space to the icon */ + &::after { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(255, 255, 255, 0); + transition: background-color 0.3s ease; + pointer-events: none; + border-radius: 10px; } - .button-action-icon { - display: flex; - margin: 0; - width: 80%; /* Take full width */ - justify-content: center; - align-items: center; - - img.action-icon-svg { - width: 60%; /* Scale to take most of the space */ - height: 60%; /* Scale to take most of the space */ - filter: invert(70%); /* Match the gray filter from normal mode */ - max-width: 48px; /* Set a reasonable maximum size */ - max-height: 48px; /* Set a reasonable maximum size */ - - /* Make the plus icon (embed action) slightly larger to compensate for optical illusion */ - &[data-action-type="embed"] { - transform: scale(1.15); - } - - /* No hover effect on the icon itself as we're using the button hover effect */ - } + &:hover::after { + background-color: rgba(255, 255, 255, 0.1); } - .button-content { - justify-content: center; /* Center the content */ - height: 100%; /* Take full height */ + &:active::after { + background-color: rgba(255, 255, 255, 0.05); } - .button-left { - display: none; /* Hide left section completely */ + &--disabled { + cursor: not-allowed; + opacity: 0.5; } + } + + &__content { + width: 100%; + display: flex; + justify-content: space-between; + align-items: center; + } + + &__left { + display: flex; + align-items: center; + justify-content: flex-start; + } + + &__right { + display: flex; + align-items: center; + justify-content: flex-end; + } + + &__icon { + margin-right: 8px; + display: none; /* Hidden by default */ + align-items: center; + flex-shrink: 1; - .button-right { - justify-content: center; - align-items: center; + img.action-button__icon-svg { width: 100%; height: 100%; - margin: 0; /* Remove any margin */ + filter: invert(100%); } } - /* Unified rule for single-element modes (4 and 5) */ - &.compact-mode-4, - &.compact-mode-5 { - /* Common styles for button content */ - .button-content { - justify-content: center; /* Center the content */ - height: 100%; /* Take full height */ - } - - /* Reduce padding for more space */ - button { - padding: 8px; - } - - /* Hide settings icon in both modes */ - .button-settings-icon { - display: none; - } + &__text { + font-weight: 500; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + flex-shrink: 0; /* Prevent text from shrinking */ + display: none; /* Hidden by default */ } - &__main-button { - width: 100%; - height: 100%; - min-width: 0; /* Allow shrinking to prevent overflow */ - border-radius: 10px; - - button { + &__action-text { + color: rgba(255, 255, 255, 0.6); + font-size: 14px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + flex-shrink: 0; /* Prevent text from shrinking */ + display: none; /* Hidden by default */ + } + + &__action-icon { + display: none; /* Hidden by default, shown in compact modes 3 and 4 */ + align-items: center; + margin-right: 0px; + + img.action-button__action-icon-svg { width: 100%; height: 100%; - - padding: 12px 15px; - - background-color: transparent; - color: white; + filter: invert(70%); + transition: all 0.2s ease; - border: none; - border-radius: 10px; - position: relative; - overflow: hidden; - - font-size: 16px; - font-weight: 500; - - cursor: pointer; - - transition: all 0.3s ease; - - &::after { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(255, 255, 255, 0); - transition: background-color 0.3s ease; - pointer-events: none; - border-radius: 10px; - } - - &:hover::after { - background-color: rgba(255, 255, 255, 0.1); - } - - &:active::after { - background-color: rgba(255, 255, 255, 0.05); - } - - .button-content { - width: 100%; - - display: flex; - justify-content: space-between; - align-items: center; - min-width: 0; /* Allows flex items to shrink below content size */ - } - - .button-left { - display: flex; - align-items: center; - justify-content: flex-start; - min-width: 0; /* Allows flex items to shrink below content size */ - } - - .button-right { - display: flex; - align-items: center; - justify-content: flex-end; - min-width: 0; /* Allows flex items to shrink below content size */ - } - - .button-icon { - margin-right: 8px; - display: flex; - align-items: center; - flex-shrink: 1; /* Allow icon to shrink */ - - img.button-icon-svg { - width: 100%; - height: 100%; - filter: invert(100%); - } - } - - .button-text { - font-weight: 500; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - flex-shrink: 0; /* Prevent text from shrinking */ - } - - .button-action-text { - color: rgba(255, 255, 255, 0.6); - font-size: 14px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - flex-shrink: 0; /* Prevent text from shrinking */ - } - - .button-action-icon { - display: none; /* Hidden by default, shown in compact modes 3 and 4 */ - align-items: center; - margin-right: 8px; - - img.action-icon-svg { - width: 100%; - height: 100%; - filter: invert(70%); - transition: all 0.2s ease; - - /* Make the plus icon (embed action) slightly larger to compensate for optical illusion */ - &[data-action-type="embed"] { - transform: scale(1.15); - } - - /* No hover effect on the icon itself as we're using the button hover effect */ - } - } - - .button-settings-icon { - display: flex; - align-items: center; - cursor: pointer; - padding: 2px; - border-radius: 4px; - transition: all 0.2s ease; - flex-shrink: 1; /* Allow settings icon to shrink */ - margin-left: 8px; - - - img.settings-icon-svg { - width: 18px; - height: 18px; - filter: invert(70%); - transition: all 0.2s ease; - - } - } - - &--disabled { - cursor: not-allowed; - opacity: 0.5; + /* Make the plus icon (embed action) slightly larger to compensate for optical illusion */ + &[data-action-type="embed"] { + transform: scale(1.15); } } } - &__tabs { + &__settings-icon { + display: none; /* Hidden by default */ + align-items: center; + cursor: pointer; + padding: 2px; + border-radius: 4px; + transition: all 0.2s ease; + flex-shrink: 1; /* Allow settings icon to shrink */ + margin-left: 8px; + + img.action-button__settings-icon-svg { + width: 18px; + height: 18px; + filter: invert(70%); + transition: all 0.2s ease; + } + } +} + +.action-tabs { + &__container { background-color: #32373c; padding: 22px; display: flex; @@ -311,164 +249,80 @@ gap: 10px; transition: background-color 0.3s ease; box-sizing: border-box; + } + + &__row { + display: flex; + width: 100%; + border-radius: 6px; + overflow: hidden; - .tabs-row { - display: flex; - width: 100%; - border-radius: 6px; - overflow: hidden; + &--target .action-tabs__item { + background-color: #4a4a54; - .tab { - flex: 1; - padding: 10px 12px; - text-align: center; - background-color: #3a3a44; - color: white; - cursor: pointer; - transition: all 0.2s ease; - user-select: none; - position: relative; - overflow: hidden; - - &::after { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(255, 255, 255, 0); - transition: background-color 0.3s ease; - pointer-events: none; - } - - &:hover::after { - background-color: rgba(255, 255, 255, 0.1); - } - - &.selected { - background-color: #4a4a54; - position: relative; - - &::before { - content: ''; - position: absolute; - bottom: 0; - left: 0; - width: 100%; - height: 3px; - background-color: #6a6aff; - } - } - } - - &.target-tabs .tab { - background-color: #4a4a54; - - &.selected { - background-color: #202225; - } + &--selected { + background-color: #202225; } + } + + &--editor .action-tabs__item { + background-color: #4a4a54; - &.editor-tabs .tab { - background-color: #4a4a54; - - &.selected { - background-color: #202225; - } + &--selected { + background-color: #202225; } + } + + &--action .action-tabs__item { + background-color: #4a4a54; - &.action-tabs .tab { - background-color: #4a4a54; - - &.selected { - background-color: #202225; - } + &--selected { + background-color: #202225; } } } -} - - -/* VSCode tab indicator */ -.action-wrapper.vscode-selected { - .action-wrapper__tabs { - .tabs-row { - .tab.selected { - &::before { - background-color: #6a7aff !important; /* Brighter blue for VSCode indicator */ - } - } + + &__item { + flex: 1; + padding: 10px 12px; + text-align: center; + background-color: #3a3a44; + color: white; + cursor: pointer; + transition: all 0.2s ease; + user-select: none; + position: relative; + overflow: hidden; + + &::after { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(255, 255, 255, 0); + transition: background-color 0.3s ease; + pointer-events: none; } - } -} - -/* Cursor tab indicator */ -.action-wrapper.cursor-selected { - .action-wrapper__tabs { - .tabs-row { - .tab.selected { - &::before { - background-color: #dc143c !important; /* Crimson for Cursor indicator */ - } - } + + &:hover::after { + background-color: rgba(255, 255, 255, 0.1); } - } -} - -/* Terminal tab indicator */ -.action-wrapper.terminal-selected { - .action-wrapper__tabs { - .tabs-row { - .tab.selected { - &::before { - background-color: #4caf50 !important; /* Green for Terminal indicator */ - } + + &--selected { + background-color: #4a4a54; + position: relative; + + &::before { + content: ''; + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: 3px; + background-color: #6a6aff; } } } } - -.action-button { - width: 100%; - height: 100%; -} - -/* Styles for expanded state */ -.action-button-grid__item.expanded .action-wrapper { - transform: scale(1); - opacity: 1; - transition: all 0.4s ease-in-out; -} - -/* Animation for the tabs when expanded */ -.action-button-grid__item.expanded .action-wrapper__tabs { - animation: fadeIn 0.4s ease-in-out 0.2s; /* Delay the animation to start after the button expands */ - animation-fill-mode: both; /* Keep the final state */ -} - -/* Improve button transition when expanded */ -.action-button-grid__item.expanded .action-wrapper__main-button button { - transition: all 0.4s ease-in-out; -} - -@keyframes fadeIn { - from { - opacity: 0; - transform: translateY(-10px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -/* Add a nice scale effect when expanding */ -.action-button-grid__item { - transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); /* Use a nice easing for the expansion */ - - &.expanded { - transform: scale(1.02); /* Slightly larger when expanded */ - transition-delay: 0.1s; /* Small delay before expanding */ - } -} diff --git a/src/frontend/src/pad/buttons/ActionButton.tsx b/src/frontend/src/pad/buttons/ActionButton.tsx index 9d9a6ef..bea21e9 100644 --- a/src/frontend/src/pad/buttons/ActionButton.tsx +++ b/src/frontend/src/pad/buttons/ActionButton.tsx @@ -211,8 +211,6 @@ const ActionButton: React.FC = ({ const [showOptions, setShowOptions] = useState(initialShowOptions); const [compactMode, setCompactMode] = useState(0); // 0: normal, 1: hide settings, 2: hide icon, 3: replace action text with icon, 4: ultra compact const wrapperRef = useRef(null); - const buttonClassName = 'action-button'; - useEffect(() => { if (!wrapperRef.current) return; @@ -441,58 +439,58 @@ const ActionButton: React.FC = ({ } }; - // Get the appropriate class name based on the selected target and code variant + // Apply custom background color if provided + const buttonStyle = backgroundColor ? { backgroundColor } : {}; + + // Get the appropriate BEM modifier class based on the selected target and code variant const getTypeClassName = () => { if (selectedTarget === 'terminal') { - return 'terminal-selected'; + return 'action-button__wrapper--terminal'; } else if (selectedTarget === 'code') { if (selectedCodeVariant === 'cursor') { - return 'cursor-selected'; + return 'action-button__wrapper--cursor'; } else { - return 'vscode-selected'; + return 'action-button__wrapper--vscode'; } } return ''; }; - // Apply custom background color if provided - const buttonStyle = backgroundColor ? { backgroundColor } : {}; - return (
-
+
From 5b6c5d9d5f63a5545f410f346452bfa96ca2558d Mon Sep 17 00:00:00 2001 From: Alex TYRODE Date: Sat, 26 Apr 2025 00:44:19 +0000 Subject: [PATCH 9/9] refactor: switch MainMenu to BEM --- src/frontend/src/ui/MainMenu.scss | 29 +++++++++++++++-------------- src/frontend/src/ui/MainMenu.tsx | 6 +++--- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/frontend/src/ui/MainMenu.scss b/src/frontend/src/ui/MainMenu.scss index 1bcece1..8e20a7b 100644 --- a/src/frontend/src/ui/MainMenu.scss +++ b/src/frontend/src/ui/MainMenu.scss @@ -1,4 +1,12 @@ -.main-menu-label { +.main-menu { + &__top-row { + display: flex; + align-items: center; + justify-content: space-between; + padding-left: 0.5rem; + } + + &__label { width: 100%; display: flex; align-items: center; @@ -8,17 +16,10 @@ font-weight: 500; font-size: 1rem; cursor: default; + + &-username { + color: #fa8933; + font-weight: 1000; + } } - - .main-menu-top-row { - display: flex; - align-items: center; - justify-content: space-between; - padding-left: 0.5rem; - } - - .main-menu-label span:last-child { - color: #fa8933; - font-weight: 1000; - } - \ No newline at end of file +} diff --git a/src/frontend/src/ui/MainMenu.tsx b/src/frontend/src/ui/MainMenu.tsx index 988e74f..b69c5a7 100644 --- a/src/frontend/src/ui/MainMenu.tsx +++ b/src/frontend/src/ui/MainMenu.tsx @@ -131,10 +131,10 @@ export const MainMenuConfig: React.FC = ({ return ( -
- +
+ - {username} + {username}