Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 14 additions & 24 deletions src/components/Trackpad/ControlBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import React from "react";

interface ControlBarProps {
scrollMode: boolean;
showKeyboard: boolean;
modifier: ModifierState;
buffer: string;
onToggleScroll: () => void;
Expand All @@ -14,6 +15,7 @@ interface ControlBarProps {

export const ControlBar: React.FC<ControlBarProps> = ({
scrollMode,
showKeyboard,
modifier,
buffer,
onToggleScroll,
Expand All @@ -30,7 +32,7 @@ export const ControlBar: React.FC<ControlBarProps> = ({
const getModifierButtonClass = () => {
switch (modifier) {
case "Active":
if (buffer.length > 0) return "btn-success"
if (buffer.length > 0) return "btn-success";
else return "btn-warning";
case "Hold":
return "btn-warning";
Expand All @@ -43,7 +45,7 @@ export const ControlBar: React.FC<ControlBarProps> = ({
const getModifierLabel = () => {
switch (modifier) {
case "Active":
if (buffer.length > 0) return "Press"
if (buffer.length > 0) return "Press";
else return "Release";
case "Hold":
return "Release";
Expand All @@ -53,48 +55,36 @@ export const ControlBar: React.FC<ControlBarProps> = ({
};

return (
<div className="bg-base-200 p-2 grid grid-cols-5 gap-2 shrink-0">
<div className="bg-base-200 p-2 grid grid-cols-5 gap-2 shrink-0 h-full">
<button
className={`btn btn-sm ${scrollMode ? "btn-primary" : "btn-outline"}`}
className={`btn h-full ${scrollMode ? "btn-primary" : "btn-outline"}`}
onPointerDown={(e) => handleInteraction(e, onToggleScroll)}
>
{scrollMode ? "Scroll" : "Cursor"}
</button>
<button
className="btn btn-sm btn-outline"
>
Copy
</button>
<button
className="btn btn-sm btn-outline"
>
Paste
</button>
{/*
<button
className="btn btn-sm btn-outline"
className="btn btn-outline h-full"
onPointerDown={(e) => handleInteraction(e, onLeftClick)}
>
L-Click
</button>
*/}
<button
className="btn btn-sm btn-outline"
className="btn btn-outline h-full"
onPointerDown={(e) => handleInteraction(e, onRightClick)}
>
R-Click
</button>
<button
className={`btn btn-sm ${getModifierButtonClass()}`}
onPointerDown={(e) => handleInteraction(e, onModifierToggle)}
className={`btn h-full ${showKeyboard ? "btn-secondary" : "btn-outline"}`}
onPointerDown={(e) => handleInteraction(e, onKeyboardToggle)}
>
{getModifierLabel()}
Keyboard
</button>
<button
className="btn btn-sm btn-secondary"
onPointerDown={(e) => handleInteraction(e, onKeyboardToggle)}
className={`btn h-full ${getModifierButtonClass()}`}
onPointerDown={(e) => handleInteraction(e, onModifierToggle)}
>
Keyboard
{getModifierLabel()}
</button>
</div>
);
Expand Down
96 changes: 18 additions & 78 deletions src/components/Trackpad/ExtraKeys.tsx
Original file line number Diff line number Diff line change
@@ -1,90 +1,30 @@
import React, { useState } from "react";
import React from "react";
import { KeyGrid } from "./KeyGrid";

interface ExtraKeysProps {
sendKey: (key: string) => void;
onInputFocus?: () => void;
}

/** All extra keys in one row (must match KeyMap.ts). Play/Pause is a single toggle. */
const EXTRA_KEYS: { label: string; key: string }[] = [
const KEYS = [
{ label: "Esc", key: "esc" },
{ label: "Tab", key: "tab" },
{ label: "Ctrl", key: "ctrl" },
{ label: "Alt", key: "alt" },
{ label: "↑", key: "arrowup" },
{ label: "PrtSc", key: "printscreen" },
{ label: "Shift", key: "shift" },
{ label: "Meta", key: "meta" },
{ label: "Home", key: "home" },
{ label: "End", key: "end" },
{ label: "PgUp", key: "pgup" },
{ label: "PgDn", key: "pgdn" },
{ label: "Ins", key: "insert" },
{ label: "Del", key: "del" },
{ label: "↑", key: "arrowup" },
{ label: "↓", key: "arrowdown" },
{ label: "Del", key: "delete" },
{ label: "←", key: "arrowleft" },
{ label: "↓", key: "arrowdown" },
{ label: "→", key: "arrowright" },
{ label: "F1", key: "f1" },
{ label: "F2", key: "f2" },
{ label: "F3", key: "f3" },
{ label: "F4", key: "f4" },
{ label: "F5", key: "f5" },
{ label: "F6", key: "f6" },
{ label: "F7", key: "f7" },
{ label: "F8", key: "f8" },
{ label: "F9", key: "f9" },
{ label: "F10", key: "f10" },
{ label: "F11", key: "f11" },
{ label: "F12", key: "f12" },
{ label: "Mute", key: "audiomute" },
{ label: "Vol−", key: "audiovoldown" },
{ label: "Vol+", key: "audiovolup" },
{ label: "Prev", key: "audioprev" },
{ label: "Next", key: "audionext" },
];

export const ExtraKeys: React.FC<ExtraKeysProps> = ({ sendKey, onInputFocus: _onInputFocus }) => {
const [isPlaying, setIsPlaying] = useState(false);

const handleInteract = (e: React.PointerEvent, key: string) => {
e.preventDefault();
sendKey(key);
};

const handlePlayPause = (e: React.PointerEvent) => {
e.preventDefault();
if (isPlaying) {
sendKey("audiopause");
} else {
sendKey("audioplay");
}
setIsPlaying((prev) => !prev);
};
interface ExtraKeysProps {
sendKey: (key: string) => void;
}

return (
<div
className="bg-base-300 p-2 shrink-0 overflow-x-auto"
style={{ WebkitOverflowScrolling: "touch" }}
>
<div className="flex gap-2 flex-nowrap items-center min-w-max">
{EXTRA_KEYS.map(({ label, key }) => (
<button
key={key}
type="button"
className="btn btn-sm btn-neutral min-w-[2.5rem] shrink-0"
onPointerDown={(e) => handleInteract(e, key)}
>
{label}
</button>
))}
<button
type="button"
className="btn btn-sm btn-neutral min-w-[2.5rem] shrink-0"
onPointerDown={handlePlayPause}
title={isPlaying ? "Pause" : "Play"}
>
{isPlaying ? "Pause" : "Play"}
</button>
</div>
</div>
);
};
export const ExtraKeys: React.FC<ExtraKeysProps> = ({ sendKey }) => (
<KeyGrid
keys={KEYS}
sendKey={sendKey}
className="bg-base-300 grid-rows-2 h-full"
buttonClass="btn btn-neutral min-w-0 h-full"
/>
);
25 changes: 25 additions & 0 deletions src/components/Trackpad/FnKeys.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from "react";
import { KeyGrid } from "./KeyGrid";

const FN_KEYS = [
{ label: "F1", key: "f1" },
{ label: "F2", key: "f2" },
{ label: "F3", key: "f3" },
{ label: "F4", key: "f4" },
{ label: "F5", key: "f5" },
{ label: "F6", key: "f6" },
{ label: "F7", key: "f7" },
{ label: "F8", key: "f8" },
{ label: "F9", key: "f9" },
{ label: "F10", key: "f10" },
{ label: "F11", key: "f11" },
{ label: "F12", key: "f12" },
];

interface FnKeysProps {
sendKey: (key: string) => void;
}

export const FnKeys: React.FC<FnKeysProps> = ({ sendKey }) => (
<KeyGrid keys={FN_KEYS} sendKey={sendKey} className="grid-rows-2 h-full" />
);
35 changes: 35 additions & 0 deletions src/components/Trackpad/KeyGrid.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from "react";

interface KeyDef {
label: string;
key: string;
}

interface KeyGridProps {
keys: KeyDef[];
sendKey: (key: string) => void;
className?: string;
buttonClass?: string;
}

export const KeyGrid: React.FC<KeyGridProps> = ({ keys, sendKey, className = "", buttonClass = "btn min-w-0 h-full" }) => {
const handleInteract = (e: React.PointerEvent, key: string) => {
e.preventDefault();
sendKey(key);
};

return (
<div className={`grid grid-cols-6 gap-1 p-2 h-full ${className}`}>
{keys.map(({ label, key }) => (
<button
key={key}
type="button"
className={buttonClass}
onPointerDown={(e) => handleInteract(e, key)}
>
{label}
</button>
))}
</div>
);
};
55 changes: 55 additions & 0 deletions src/components/Trackpad/MediaKeys.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React, { useState } from "react";
import { VolumeX, Volume1, Volume2, SkipBack, Play, Pause, SkipForward } from "lucide-react";

interface MediaKeysProps {
sendKey: (key: string) => void;
}

const MEDIA_KEYS = [
{ label: "Mute", key: "audiomute", icon: VolumeX },
{ label: "Vol−", key: "audiovoldown", icon: Volume1 },
{ label: "Vol+", key: "audiovolup", icon: Volume2 },
{ label: "Prev", key: "audioprev", icon: SkipBack },
{ label: "Next", key: "audionext", icon: SkipForward },
];

export const MediaKeys: React.FC<MediaKeysProps> = ({ sendKey }) => {
const [isPlaying, setIsPlaying] = useState(false);

const handleInteract = (e: React.PointerEvent, key: string) => {
e.preventDefault();
sendKey(key);
};

const handlePlayPause = (e: React.PointerEvent) => {
e.preventDefault();
sendKey(isPlaying ? "audiopause" : "audioplay");
setIsPlaying(prev => !prev);
};

return (
<div className="grid grid-cols-6 gap-1 p-2 h-full">
{MEDIA_KEYS.map(({ label, key, icon: Icon }) => (
<button
key={key}
type="button"
className="btn btn-primary min-w-0 h-full"
onPointerDown={(e) => handleInteract(e, key)}
title={label}
aria-label={label}
>
<Icon size={18} />
</button>
))}
<button
type="button"
className="btn btn-primary min-w-0 h-full"
onPointerDown={handlePlayPause}
title={isPlaying ? "Pause" : "Play"}
aria-label={isPlaying ? "Pause" : "Play"}
>
{isPlaying ? <Pause size={18} /> : <Play size={18} />}
</button>
</div>
);
};
Loading