diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx
index 6e84174..92b70fc 100644
--- a/frontend/src/components/Header.tsx
+++ b/frontend/src/components/Header.tsx
@@ -2,6 +2,7 @@ import { useState } from 'react';
import { Select } from './Select';
import { Button } from './Button';
import { capitalize, SUPPORTED_LANGUAGES } from 'coderjam-shared';
+import { Tooltip } from './Tooltip';
export type HeaderProps = {
language: string;
@@ -47,26 +48,27 @@ export function Header({ language, onLanguageChange, isRunning, onRunCode }: Hea
-
-
-
+
+
+
@@ -582,7 +596,8 @@ export function PadPage() {
className={`w-2 h-2 rounded-full flex items-center justify-center bg-current ${getUserColorClassname(user.name)}`}
>
- {user.name} {user.id === pad?.ownerId ? '👑' : ''}
+ {user.name}{' '}
+ {user.id === pad?.ownerId ? '(code executor)' : ''}
))}
diff --git a/frontend/src/components/Tooltip.tsx b/frontend/src/components/Tooltip.tsx
new file mode 100644
index 0000000..164f6d7
--- /dev/null
+++ b/frontend/src/components/Tooltip.tsx
@@ -0,0 +1,70 @@
+import React, { useState, useRef, useEffect } from 'react';
+
+interface TooltipProps extends Omit, 'type'> {
+ // Tooltip text, if not provided the tooltip will not be displayed
+ text?: string;
+ // Tooltip delay in milliseconds
+ delay?: number;
+ direction?: 'top' | 'bottom';
+ children: React.ReactNode;
+}
+
+export const Tooltip: React.FC = ({
+ text,
+ delay = 100,
+ children,
+ className = '',
+ direction = 'top',
+ ...props
+}) => {
+ const [shown, setShown] = useState(false);
+ const timeoutRef = useRef(null);
+
+ const handleMouseEnter = React.useCallback(() => {
+ if (text) {
+ timeoutRef.current = window.setTimeout(() => {
+ setShown(true);
+ }, delay);
+ }
+ }, [text, delay]);
+
+ const handleMouseLeave = React.useCallback(() => {
+ if (timeoutRef.current) {
+ clearTimeout(timeoutRef.current);
+ timeoutRef.current = null;
+ }
+ setShown(false);
+ }, []);
+
+ useEffect(() => {
+ return () => {
+ if (timeoutRef.current) {
+ clearTimeout(timeoutRef.current);
+ }
+ };
+ }, []);
+
+ return text ? (
+
+ {children}
+
+ {text}
+
+
+ ) : (
+ children
+ );
+};
diff --git a/frontend/src/runners/go-runner.ts b/frontend/src/runners/go-runner.ts
index cb09b80..9bce6cf 100644
--- a/frontend/src/runners/go-runner.ts
+++ b/frontend/src/runners/go-runner.ts
@@ -70,7 +70,7 @@ let singletonGo: Go | undefined = undefined;
let cmd: WebAssembly.WebAssemblyInstantiatedSource;
function postprocessOutput(output: OutputEntry[]): OutputEntry[] {
- return output.filter(entry => entry.text !== '# command-line-arguments');
+ return output.filter((entry) => entry.text !== '# command-line-arguments');
}
function isReady(): boolean {
diff --git a/frontend/src/runners/python-runner.ts b/frontend/src/runners/python-runner.ts
index 9d52371..6aa38c0 100644
--- a/frontend/src/runners/python-runner.ts
+++ b/frontend/src/runners/python-runner.ts
@@ -53,10 +53,10 @@ async function runCode(code: string): Promise {
} catch (errRaw: unknown) {
const err = errRaw as Error;
console.error('Pyodide error:', err);
- outputEntries.push({
- text: err.message,
- type: 'error',
- });
+ outputEntries.push({
+ text: err.message,
+ type: 'error',
+ });
}
pyodide.setStdout({});
pyodide.setStderr({});