add a delete button to the variables field. when the user clicks on it all variables should be deleted. The button should be an overlay the positions itself in the right corner of the variables field. Also make sure the background of the butoon does not overlay the borders of the variables field and make them less visible. Lets think step by step. Python 3.12 code: import math
import re
from typing import Any, Union, Callable
from PySide6.QtWidgets import (
    QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
    QLineEdit, QTextEdit, QScrollBar, QPushButton, QLabel, QFrame,
    QGridLayout
)
from PySide6.QtGui import QFont
from PySide6.QtCore import Qt

def convert_angle(func: Callable[[float], float], x: float, use_degrees: bool) -> float:
    return func(math.radians(x)) if use_degrees else func(x)

def kilograms_to_pounds(kg: float) -> float:
    return kg / 0.45359237

def pounds_to_kilograms(lb: float) -> float:
    return lb * 0.45359237

def celsius_to_kelvin(c: float) -> float:
    return c + 273.15

def kelvin_to_celsius(k: float) -> float:
    return k - 273.15

def celsius_to_fahrenheit(c: float) -> float:
    return c * 9 / 5 + 32

def fahrenheit_to_celsius(f: float) -> float:
    return (f - 32) * 5 / 9

def capitalize_sentence(sentence: str) -> str:
    return ' '.join(word.capitalize() for word in sentence.split())

def remove_duplicates(text: str) -> str:
    words = ' '.join(line.strip() for line in text.splitlines() if line.strip()).split()
    return ' '.join(dict.fromkeys(words))

def get_safe_globals(use_degrees: bool, variables: dict) -> dict:
    trig_funcs = {'sin', 'cos', 'tan', 'asin', 'acos', 'atan'}
    safe_globals = {
        "sqrt": math.sqrt,
        "log": math.log10,
        "ln": math.log,
        "pi": math.pi,
        "e": math.e,
        "lb": kilograms_to_pounds,
        "kg": pounds_to_kilograms,
        "K": celsius_to_kelvin,
        "C": kelvin_to_celsius,
        "F": celsius_to_fahrenheit,
        "CF": fahrenheit_to_celsius,
        "capitalize": capitalize_sentence,
        "duplicates": remove_duplicates,
    }
    for func in trig_funcs:
        safe_globals[func] = lambda x, f=func: convert_angle(getattr(math, f), x, use_degrees)
    safe_globals.update(variables)
    return safe_globals

def preprocess_text_functions(expression: str) -> str:
    """Preprocess text functions with argument quotes."""
    def add_quotes(match):
        func, args = match.groups()
        args = args.strip()
        if not (args.startswith('"') and args.endswith('"')) and not (args.startswith("'") and args.endswith("'")):
            args = f'"{args}"'
        return f'{func}({args})'
    return re.sub(r'\b(capitalize|duplicates)\s*\(\s*([^)]+)\s*\)', add_quotes, expression)

def validate_inverse_trig(expression: str, safe_globals: dict) -> Union[str, None]:
    """Check inverse trig function domain validity."""
    for func, arg in re.findall(r'\b(asin|acos)\s*\(\s*([^)]+)\s*\)', expression):
        try:
            value = eval(arg, {"builtins": None}, safe_globals)
            if abs(value) > 1:
                return f"Error: {func}({arg}) is undefined for |{arg}| > 1"
        except Exception as e:
            return f"Error: {e}"
    return None

def safe_evaluate(expression: str, safe_globals: dict) -> Union[str, Any]:
    try:
        result = eval(expression, {"builtins": None}, safe_globals)
        if isinstance(result, (int, float)):
            if isinstance(result, float):
                abs_result = abs(result)
                if (abs_result >= 1e9) or (abs_result != 0 and abs_result < 1e-6):
                    result = f"{result:.3e}"
                else:
                    result = f"{result:.3f}"
            else:
                if len(str(abs(result))) > 9:
                    result = f"{result:.3e}"
                else:
                    result = str(result)
            return result
        else:
            return result
    except Exception as e:
        return f"Error: {e}"

def evaluate_expression(expression: str, variables: dict, use_degrees: bool = False) -> Union[float, str]:
    assignment_match = re.match(r'^\s*([a-zA-Z_]\w*)\s*=\s*(.+)$', expression)
    expression = preprocess_text_functions(expression)
    safe_globals = get_safe_globals(use_degrees, variables)

    if assignment_match:
        var_name, var_expr = assignment_match.groups()
        var_value = safe_evaluate(var_expr, safe_globals)
        try:
            if isinstance(var_value, str) and not var_value.startswith("Error"):
                if '.' in var_value:
                    var_value_converted = float(var_value)
                else:
                    var_value_converted = int(var_value)
                variables[var_name] = var_value_converted
                return f"{var_name} = {var_value_converted}"
            else:
                return var_value
        except ValueError:
            return f"Error: Cannot assign non-numeric value to variable '{var_name}'"
    else:
        error = validate_inverse_trig(expression, safe_globals)
        if error:
            return error
        return safe_evaluate(expression, safe_globals)

class CalculatorGUI(QMainWindow):
    def __init__(self):
        super().__init__()
        self.use_degrees = False
        self.variables = {}
        self.history_entries = []
        self.text_functions = ['capitalize', 'duplicates']
        self.setup_window()
        self.create_widgets()
        self.layout_widgets()
        self.connect_signals()

    def setup_window(self) -> None:
        self.setWindowTitle("Calculator")
        self.setGeometry(100, 100, 1280, 720)

    def create_widgets(self) -> None:
        font = QFont()
        font.setPointSize(12)
        font.setFamily('Literata')

        self.entry = QLineEdit()
        self.entry.setFont(font)
        self.entry.setPlaceholderText('Enter expression')
        self.entry.setClearButtonEnabled(True)
        self.entry.returnPressed.connect(self.calculate)

        self.history_text = QTextEdit()
        self.history_text.setFont(font)
        self.history_text.setReadOnly(True)
        self.history_text.setStyleSheet("position: relative;")

        self.scrollbar = QScrollBar(Qt.Vertical)
        self.history_text.setVerticalScrollBar(self.scrollbar)

        # Search Button
        self.search_button = QPushButton("🔍", self.history_text)
        self.search_button.setFixedSize(24, 24)
        self.search_button.setToolTip("Search History")
        self.search_button.setStyleSheet("""
            QPushButton {
                border: none;
                background-color: rgba(255, 255, 255, 200);
                font-size: 14px;
                border-radius: 12px;
            }
            QPushButton:hover {
                background-color: rgba(0, 120, 215, 200);
                color: white;
            }
        """)
        self.search_button.clicked.connect(self.toggle_search)
        self.search_button.raise_()

        # Variables Button
        self.vars_button = QPushButton("🧮", self.history_text)  # Using an abacus emoji as an example
        self.vars_button.setFixedSize(24, 24)
        self.vars_button.setToolTip("Show Variables")
        self.vars_button.setStyleSheet("""
            QPushButton {
                border: none;
                background-color: rgba(255, 255, 255, 200);
                font-size: 14px;
                border-radius: 12px;
            }
            QPushButton:hover {
                background-color: rgba(0, 150, 136, 200);
                color: white;
            }
        """)
        self.vars_button.clicked.connect(self.toggle_vars_overlay)
        self.vars_button.raise_()

        # Delete History Button
        self.delete_button = QPushButton("🗑️", self.history_text)
        self.delete_button.setFixedSize(24, 24)
        self.delete_button.setToolTip("Clear History")
        self.delete_button.setStyleSheet("""
            QPushButton {
                border: none;
                background-color: rgba(255, 255, 255, 200);
                font-size: 14px;
                border-radius: 12px;
            }
            QPushButton:hover {
                background-color: rgba(255, 0, 0, 200);
                color: white;
            }
        """)
        self.delete_button.clicked.connect(self.clear_history)
        self.delete_button.raise_()

        # Variables Overlay
        self.vars_overlay = QLineEdit(self.history_text)
        self.vars_overlay.setReadOnly(True)
        self.vars_overlay.setPlaceholderText("a = 1, b = 85, ...")
        self.vars_overlay.setFixedHeight(24)
        self.vars_overlay.setStyleSheet("""
            QLineEdit {
                border: 1px solid gray;
                border-radius: 4px;
                padding: 2px 4px;
                background-color: white;
            }
        """)
        self.vars_overlay.setVisible(False)

        # Delete Variables Button within Variables Overlay
        self.vars_delete_button = QPushButton("🗑️", self.history_text)
        self.vars_delete_button.setFixedSize(24, 24)
        self.vars_delete_button.setToolTip("Clear Variables")
        self.vars_delete_button.setStyleSheet("""
            QPushButton {
                border: none;
                background-color: rgba(255, 255, 255, 200);
                font-size: 14px;
                border-radius: 12px;
            }
            QPushButton:hover {
                background-color: rgba(255, 0, 0, 200);
                color: white;
            }
        """)
        self.vars_delete_button.clicked.connect(self.clear_variables)
        self.vars_delete_button.setVisible(False)

        # Search Bar
        self.search_bar = QLineEdit(self.history_text)
        self.search_bar.setPlaceholderText("Search...")
        self.search_bar.setFixedHeight(24)
        self.search_bar.setVisible(False)
        self.search_bar.setStyleSheet("""
            QLineEdit {
                border: 1px solid gray;
                border-radius: 4px;
                padding: 2px 4px;
                background-color: white;
            }
        """)
        self.search_bar.textChanged.connect(self.filter_history)
        self.search_bar.setClearButtonEnabled(True)

        # Function Buttons
        self.function_buttons = {
            "Functions": ["sqrt", "sin", "cos", "tan", "asin", "acos", "atan", "log", "ln"],
            "Conversions": ["lb", "kg", "K", "C", "F", "CF"],
            "Text": ["capitalize", "duplicates"],
            "Constants": ["pi", "e"],
            "Settings": []
        }
        self.function_frame = QFrame()
        self.create_function_buttons(font)

    def create_function_buttons(self, font: QFont) -> None:
        layout = QGridLayout()
        categories = [("Functions", 3), ("Conversions", 2), ("Text", 2), ("Constants", 2), ("Settings", 1)]
        starting_col = 0

        for category, col_span in categories:
            label = QLabel(category)
            label.setFont(font)
            layout.addWidget(label, 0, starting_col, 1, col_span, Qt.AlignCenter)

            buttons = self.function_buttons.get(category, [])
            if category == "Functions":
                for i, func in enumerate(buttons):
                    button = QPushButton(func)
                    button.setFont(font)
                    button.clicked.connect(lambda _, f=func: self.insert_function(f, is_text_function=(f in self.text_functions)))
                    layout.addWidget(button, (i // 3) + 1, starting_col + (i % 3))

            elif category == "Settings":
                self.mode_button = QPushButton("Radians")
                self.mode_button.setFont(font)
                self.mode_button.clicked.connect(self.toggle_mode)
                layout.addWidget(self.mode_button, 1, starting_col, 1, 1)

            elif category == "Conversions":
                for idx, func in enumerate(buttons):
                    display_text = {
                        "lb": "lb(kg)", "kg": "kg(lb)", "K": "K(°C)",
                        "C": "°C(K)", "F": "F(°C)", "CF": "°C(F)"
                    }.get(func, func)
                    button = QPushButton(display_text)
                    button.setFont(font)
                    button.clicked.connect(lambda _, f=func: self.insert_function(f, is_text_function=False))
                    row, col = 1 + idx // 2, starting_col + idx % 2
                    layout.addWidget(button, row, col)

            elif category == "Text":
                for idx, func in enumerate(buttons):
                    button = QPushButton(func)
                    button.setFont(font)
                    button.clicked.connect(lambda _, f=func: self.insert_function(f, is_text_function=True))
                    row, col = 1 + idx // col_span, starting_col + idx % col_span
                    layout.addWidget(button, row, col)

            elif category == "Constants":
                for idx, func in enumerate(buttons):
                    button = QPushButton(func)
                    button.setFont(font)
                    button.clicked.connect(lambda _, f=func: self.insert_function(f, is_constant=True))
                    row, col = 1 + idx // col_span, starting_col + idx % col_span
                    layout.addWidget(button, row, col)

            # Fill empty spaces if needed
            total_rows = math.ceil(len(buttons) / col_span)
            for extra in range(col_span * total_rows - len(buttons)):
                layout.addWidget(QLabel(""), 1 + extra // col_span, starting_col + extra % col_span)

            starting_col += col_span

        self.function_frame.setLayout(layout)

    def layout_widgets(self) -> None:
        central_widget = QWidget()
        main_layout = QVBoxLayout()

        main_layout.addWidget(self.entry)

        history_layout = QHBoxLayout()
        history_layout.addWidget(self.history_text)
        history_layout.addWidget(self.scrollbar)
        main_layout.addLayout(history_layout)

        main_layout.addWidget(self.function_frame)

        central_widget.setLayout(main_layout)
        self.setCentralWidget(central_widget)

    def connect_signals(self) -> None:
        QApplication.instance().focusChanged.connect(self.on_focus_changed)

    def toggle_mode(self) -> None:
        self.use_degrees = not self.use_degrees
        self.mode_button.setText("Degrees" if self.use_degrees else "Radians")

    def insert_function(self, function: str, is_constant: bool = False, is_text_function: bool = False) -> None:
        current_text = self.entry.text()
        cursor_position = self.entry.cursorPosition()
        if is_constant:
            new_text = f"{current_text[:cursor_position]}{function} "
            new_cursor_position = cursor_position + len(function) + 1
        elif is_text_function and function in self.text_functions:
            new_text = f'{current_text[:cursor_position]}{function}() {current_text[cursor_position:]}'
            new_cursor_position = cursor_position + len(function) + 1
        else:
            new_text = f"{current_text[:cursor_position]}{function}() {current_text[cursor_position:]}"
            new_cursor_position = cursor_position + len(function) + 1
        self.entry.setText(new_text)
        self.entry.setCursorPosition(new_cursor_position)
        self.entry.setFocus()

    def calculate(self) -> None:
        expression = self.entry.text().strip()
        if expression:
            result = evaluate_expression(
                expression, self.variables, use_degrees=self.use_degrees
            )
            self.display_result(expression, result)
            self.entry.clear()

    def display_result(self, expression: str, result: Union[float, str]) -> None:
        mode = "D" if self.use_degrees else "R"
        if re.match(r'^\s*[a-zA-Z_]\w*\s*=\s*.+$', expression):
            history_entry = f"[{mode}] {result}"
        else:
            history_entry = f"[{mode}] {expression} = {result}"
        self.history_entries.append(history_entry)
        self.refresh_history()
        self.history_text.verticalScrollBar().setValue(
            self.history_text.verticalScrollBar().maximum()
        )

    def toggle_search(self) -> None:
        self.search_bar.setVisible(not self.search_bar.isVisible())
        self.update_overlay_buttons_position()

    def filter_history(self, text: str) -> None:
        self.refresh_history(text)

    def refresh_history(self, filter_text: str = "") -> None:
        self.history_text.clear()
        for entry in self.history_entries:
            if filter_text.lower() in entry.lower():
                self.history_text.append(entry)

    def resizeEvent(self, event) -> None:
        super().resizeEvent(event)
        self.update_overlay_buttons_position()

    def update_overlay_buttons_position(self) -> None:
        size = self.history_text.viewport().size()

        margin_right = 5
        margin_top = 5
        spacing = 5

        # Determine available width for bars
        available_width = size.width()

        # Calculate and position the search button
        search_visible_width = self.search_bar.width() if self.search_bar.isVisible() else 0
        self.search_button.move(
            available_width - search_visible_width - self.search_button.width() - margin_right,
            margin_top
        )

        # Calculate and position the variables button
        vars_visible_width = self.vars_overlay.width() if self.vars_overlay.isVisible() else 0
        self.vars_button.move(
            available_width - vars_visible_width - self.vars_button.width() - margin_right,
            self.search_button.y() + self.search_button.height() + spacing
        )

        # Position the delete button
        self.delete_button.move(
            available_width - self.delete_button.width() - margin_right,
            self.vars_button.y() + self.vars_button.height() + spacing
        )

        # Variables Overlay Position
        if self.vars_overlay.isVisible():
            self.vars_overlay.move(
                available_width - vars_visible_width - margin_right,
                self.vars_button.y()
            )

        # Search Bar Position
        if self.search_bar.isVisible():
            self.search_bar.move(
                available_width - search_visible_width - margin_right,
                margin_top
            )

    def on_focus_changed(self, old: QWidget, new: QWidget) -> None:
        if self.search_bar.isVisible() and not self.search_bar.text().strip() and new != self.search_bar:
            self.toggle_search()

    def showEvent(self, event) -> None:
        super().showEvent(event)
        self.update_overlay_buttons_position()

    def clear_history(self) -> None:
        self.history_entries.clear()
        self.refresh_history()

    def toggle_vars_overlay(self) -> None:
        if self.vars_overlay.isVisible():
            self.vars_overlay.setVisible(False)
        else:
            self.update_vars_overlay()
            self.vars_overlay.setVisible(True)
        self.update_overlay_buttons_position()

    def update_vars_overlay(self) -> None:
        if not self.variables:
            self.vars_overlay.setText("No variables assigned.")
        else:
            vars_text = ", ".join(f"{var} = {value}" for var, value in self.variables.items())
            self.vars_overlay.setText(vars_text)

    def clear_variables(self) -> None:
        self.variables.clear()
        self.update_vars_overlay()
        self.vars_overlay.setVisible(False)
        self.vars_delete_button.setVisible(False)

def main() -> None:
    app = QApplication([])
    calculator = CalculatorGUI()
    calculator.show()
    app.exec()

if __name__ == "__main__":
    main()