use existing components and CTA

This commit is contained in:
Andrew Davis 2025-04-02 23:29:41 +10:00
parent c65d222ee0
commit 669e4244a6
No known key found for this signature in database
GPG Key ID: 30AB5B89A109D044
1 changed files with 450 additions and 484 deletions

View File

@ -1,14 +1,19 @@
import { useState, useEffect, useRef, useCallback } from "react"; import { useState, useEffect, useRef, useCallback } from "react";
import { LuPlus, LuTrash, LuX, LuPenLine, LuLoader, LuGripVertical, LuInfo } from "react-icons/lu"; import { LuPlus, LuTrash, LuX, LuPenLine, LuLoader, LuGripVertical, LuInfo, LuCopy, LuArrowUp, LuArrowDown } from "react-icons/lu";
import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions } from "@headlessui/react"; import { Combobox, ComboboxInput, ComboboxOption, ComboboxOptions } from "@headlessui/react";
import { KeySequence, useMacrosStore } from "../hooks/stores"; import { KeySequence, useMacrosStore } from "../hooks/stores";
import { SettingsPageHeader } from "../components/SettingsPageheader"; import { SettingsPageHeader } from "@/components/SettingsPageheader";
import { Button } from "../components/Button"; import { Button } from "@/components/Button";
import Checkbox from "@/components/Checkbox";
import { keys, modifiers } from "../keyboardMappings"; import { keys, modifiers } from "../keyboardMappings";
import { useJsonRpc } from "../hooks/useJsonRpc"; import { useJsonRpc } from "../hooks/useJsonRpc";
import notifications from "../notifications"; import notifications from "../notifications";
import { SettingsItem } from "../routes/devices.$id.settings"; import { SettingsItem } from "../routes/devices.$id.settings";
import { InputFieldWithLabel, FieldError } from "@/components/InputField";
import Fieldset from "@/components/Fieldset";
import { SelectMenuBasic } from "@/components/SelectMenuBasic";
import EmptyCard from "@/components/EmptyCard";
const DEFAULT_DELAY = 50; const DEFAULT_DELAY = 50;
@ -109,15 +114,15 @@ function KeyCombobox({
} }
const PRESET_DELAYS = [ const PRESET_DELAYS = [
{ value: 50, label: "50ms" }, { value: "50", label: "50ms" },
{ value: 100, label: "100ms" }, { value: "100", label: "100ms" },
{ value: 200, label: "200ms" }, { value: "200", label: "200ms" },
{ value: 300, label: "300ms" }, { value: "300", label: "300ms" },
{ value: 500, label: "500ms" }, { value: "500", label: "500ms" },
{ value: 750, label: "750ms" }, { value: "750", label: "750ms" },
{ value: 1000, label: "1000ms" }, { value: "1000", label: "1000ms" },
{ value: 1500, label: "1500ms" }, { value: "1500", label: "1500ms" },
{ value: 2000, label: "2000ms" }, { value: "2000", label: "2000ms" },
]; ];
const MAX_STEPS_PER_MACRO = 10; const MAX_STEPS_PER_MACRO = 10;
@ -171,26 +176,20 @@ function MacroStepCard({
<div className="mb-2 flex items-center justify-between"> <div className="mb-2 flex items-center justify-between">
<div className="flex items-center gap-1.5"> <div className="flex items-center gap-1.5">
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<button <Button
type="button" size="XS"
className="p-1 text-slate-400 hover:text-slate-600 dark:text-slate-500 dark:hover:text-slate-400 disabled:opacity-50" theme="light"
onClick={onMoveUp} onClick={onMoveUp}
disabled={stepIndex === 0} disabled={stepIndex === 0}
> LeadingIcon={LuArrowUp}
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> />
<path d="m18 15-6-6-6 6"/> <Button
</svg> size="XS"
</button> theme="light"
<button
type="button"
className="p-1 text-slate-400 hover:text-slate-600 dark:text-slate-500 dark:hover:text-slate-400 disabled:opacity-50"
onClick={onMoveDown} onClick={onMoveDown}
disabled={isLastStep} disabled={isLastStep}
> LeadingIcon={LuArrowDown}
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> />
<path d="m6 9 6 6 6-6"/>
</svg>
</button>
</div> </div>
<span className="macro-step-number flex h-5 w-5 items-center justify-center rounded-full bg-blue-100 text-xs font-semibold text-blue-700 dark:bg-blue-900/40 dark:text-blue-200"> <span className="macro-step-number flex h-5 w-5 items-center justify-center rounded-full bg-blue-100 text-xs font-semibold text-blue-700 dark:bg-blue-900/40 dark:text-blue-200">
{stepIndex + 1} {stepIndex + 1}
@ -199,18 +198,13 @@ function MacroStepCard({
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
{onDelete && ( {onDelete && (
<button <Button
type="button" size="XS"
className="flex items-center text-xs text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300" theme="danger"
text="Delete"
onClick={onDelete} onClick={onDelete}
> LeadingIcon={LuTrash}
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> />
<path d="M3 6h18"></path>
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"></path>
<path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"></path>
</svg>
<span className="ml-1">Delete</span>
</button>
)} )}
</div> </div>
</div> </div>
@ -236,9 +230,9 @@ function MacroStepCard({
: 'bg-slate-100 border-slate-200 text-slate-600 hover:bg-slate-200 dark:bg-slate-800 dark:border-slate-700 dark:text-slate-300 dark:hover:bg-slate-700' : 'bg-slate-100 border-slate-200 text-slate-600 hover:bg-slate-200 dark:bg-slate-800 dark:border-slate-700 dark:text-slate-300 dark:hover:bg-slate-700'
}`} }`}
> >
<input <Checkbox
type="checkbox"
className="sr-only" className="sr-only"
size="SM"
checked={ensureArray(step.modifiers).includes(option.value)} checked={ensureArray(step.modifiers).includes(option.value)}
onChange={e => { onChange={e => {
const modifiersArray = ensureArray(step.modifiers); const modifiersArray = ensureArray(step.modifiers);
@ -266,19 +260,20 @@ function MacroStepCard({
{ensureArray(step.keys).map((key, keyIndex) => ( {ensureArray(step.keys).map((key, keyIndex) => (
<span <span
key={keyIndex} key={keyIndex}
className="macro-key-badge inline-flex items-center rounded-md bg-blue-100 px-2 py-1 text-xs font-medium text-blue-700 dark:bg-blue-900/40 dark:text-blue-200" className="inline-flex items-center rounded-md bg-blue-100 px-2 py-1 text-xs font-medium text-blue-700 dark:bg-blue-900/40 dark:text-blue-200"
> >
<span className="px-1">
{key} {key}
<button </span>
type="button" <Button
className="ml-1 text-xs text-blue-500 hover:text-blue-700 dark:text-blue-400 dark:hover:text-blue-300" size="XS"
theme="blank"
onClick={() => { onClick={() => {
const newKeys = ensureArray(step.keys).filter((_, i) => i !== keyIndex); const newKeys = ensureArray(step.keys).filter((_, i) => i !== keyIndex);
onKeySelect({ value: null, keys: newKeys }); onKeySelect({ value: null, keys: newKeys });
}} }}
> LeadingIcon={LuX}
× />
</button>
</span> </span>
))} ))}
</div> </div>
@ -313,17 +308,13 @@ function MacroStepCard({
</div> </div>
</div> </div>
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<select <SelectMenuBasic
className="w-full rounded-md border border-slate-300 bg-slate-50 p-2 text-sm shadow-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500 dark:border-slate-600 dark:bg-slate-800 dark:text-white" size="SM"
value={step.delay} fullWidth
value={step.delay.toString()}
onChange={(e) => onDelayChange(parseInt(e.target.value, 10))} onChange={(e) => onDelayChange(parseInt(e.target.value, 10))}
> options={PRESET_DELAYS}
{PRESET_DELAYS.map((option) => ( />
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
</div> </div>
</div> </div>
</div> </div>
@ -928,9 +919,7 @@ export default function SettingsMacrosRoute() {
const ErrorMessage = ({ error }: { error?: string }) => { const ErrorMessage = ({ error }: { error?: string }) => {
if (!error) return null; if (!error) return null;
return ( return (
<p className="mt-1 text-xs text-red-500 dark:text-red-400"> <FieldError error={error} />
{error}
</p>
); );
}; };
@ -940,22 +929,7 @@ export default function SettingsMacrosRoute() {
title="Keyboard Macros" title="Keyboard Macros"
description="Create and manage keyboard macros for quick actions" description="Create and manage keyboard macros for quick actions"
/> />
{macros.length > 0 && (
{errorMessage && (
<div className="macro-error">
<div className="flex">
<div className="flex-shrink-0">
<svg className="macro-error-icon" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fillRule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.414 1.414L10 11.414l1.72 1.72a.75.75 0 101.414-1.414L11.414 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.586 8.28 7.22z" clipRule="evenodd" />
</svg>
</div>
<div className="ml-3">
<h3 className="macro-error-text">{errorMessage}</h3>
</div>
</div>
</div>
)}
<div className="flex items-center justify-between mb-4"> <div className="flex items-center justify-between mb-4">
<SettingsItem <SettingsItem
title="Macros" title="Macros"
@ -974,6 +948,9 @@ export default function SettingsMacrosRoute() {
</div> </div>
</SettingsItem> </SettingsItem>
</div> </div>
)}
{errorMessage && (<FieldError error={errorMessage} />)}
{loading && ( {loading && (
<div className="flex items-center justify-center p-8"> <div className="flex items-center justify-center p-8">
@ -986,12 +963,14 @@ export default function SettingsMacrosRoute() {
<div className="flex items-center justify-between mb-2"> <div className="flex items-center justify-between mb-2">
<h3 className="text-sm font-semibold text-black dark:text-white">Add New Macro</h3> <h3 className="text-sm font-semibold text-black dark:text-white">Add New Macro</h3>
</div> </div>
<Fieldset>
<div className="grid grid-cols-1 md:grid-cols-2 gap-2"> <div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<div className="flex flex-col"> <InputFieldWithLabel
<input
type="text" type="text"
className={`macro-input ${errors.name ? 'border-red-500 dark:border-red-500' : ''}`} label="Macro Name"
placeholder="Macro Name"
value={newMacro.name} value={newMacro.name}
error={errors.name}
onChange={e => { onChange={e => {
setNewMacro(prev => ({ ...prev, name: e.target.value })); setNewMacro(prev => ({ ...prev, name: e.target.value }));
if (errors.name) { if (errors.name) {
@ -1000,15 +979,13 @@ export default function SettingsMacrosRoute() {
setErrors(newErrors); setErrors(newErrors);
} }
}} }}
placeholder="Macro Name"
/> />
<ErrorMessage error={errors.name} /> <InputFieldWithLabel
</div>
<div className="flex flex-col">
<input
type="text" type="text"
className={`macro-input ${errors.description ? 'border-red-500 dark:border-red-500' : ''}`} label="Description"
placeholder="Description (optional)"
value={newMacro.description} value={newMacro.description}
error={errors.description}
onChange={e => { onChange={e => {
setNewMacro(prev => ({ ...prev, description: e.target.value })); setNewMacro(prev => ({ ...prev, description: e.target.value }));
if (errors.description) { if (errors.description) {
@ -1017,11 +994,9 @@ export default function SettingsMacrosRoute() {
setErrors(newErrors); setErrors(newErrors);
} }
}} }}
placeholder="Description (optional)"
/> />
<ErrorMessage error={errors.description} />
</div>
</div> </div>
</Fieldset>
<div className="mt-4"> <div className="mt-4">
<div className="macro-section-header"> <div className="macro-section-header">
@ -1040,6 +1015,7 @@ export default function SettingsMacrosRoute() {
<div className="mt-2 text-xs text-slate-500 dark:text-slate-400"> <div className="mt-2 text-xs text-slate-500 dark:text-slate-400">
You can add up to {MAX_STEPS_PER_MACRO} steps per macro You can add up to {MAX_STEPS_PER_MACRO} steps per macro
</div> </div>
<Fieldset>
<div className="mt-2 space-y-4"> <div className="mt-2 space-y-4">
{(newMacro.steps || []).map((step, stepIndex) => ( {(newMacro.steps || []).map((step, stepIndex) => (
<MacroStepCard <MacroStepCard
@ -1069,15 +1045,16 @@ export default function SettingsMacrosRoute() {
isLastStep={stepIndex === (newMacro.steps?.length || 0) - 1} isLastStep={stepIndex === (newMacro.steps?.length || 0) - 1}
/> />
))} ))}
</div>
</Fieldset>
<div className="mt-4 border-t border-slate-200 pt-4 dark:border-slate-700"> <div className="mt-4 border-t border-slate-200 pt-4 dark:border-slate-700">
<button <Button
type="button" size="MD"
className={`w-full flex items-center justify-center gap-1 rounded-md px-3 py-2 text-sm font-medium transition-colors ${ theme="light"
isMaxStepsReachedForNewMacro fullWidth
? 'bg-slate-100 text-slate-400 cursor-not-allowed dark:bg-slate-800 dark:text-slate-500' LeadingIcon={LuPlus}
: 'bg-slate-100 text-slate-700 hover:bg-slate-200 dark:bg-slate-800 dark:text-slate-200 dark:hover:bg-slate-700' text={`Add Step ${isMaxStepsReachedForNewMacro ? `(${MAX_STEPS_PER_MACRO} max)` : ''}`}
}`}
onClick={() => { onClick={() => {
if (isMaxStepsReachedForNewMacro) { if (isMaxStepsReachedForNewMacro) {
showTemporaryError(`You can only add a maximum of ${MAX_STEPS_PER_MACRO} steps per macro.`); showTemporaryError(`You can only add a maximum of ${MAX_STEPS_PER_MACRO} steps per macro.`);
@ -1094,10 +1071,7 @@ export default function SettingsMacrosRoute() {
clearErrors(); clearErrors();
}} }}
disabled={isMaxStepsReachedForNewMacro} disabled={isMaxStepsReachedForNewMacro}
> />
<LuPlus className="h-4 w-4" />
<span>Add Step {isMaxStepsReachedForNewMacro && `(${MAX_STEPS_PER_MACRO} max)`}</span>
</button>
</div> </div>
<div className="mt-6 flex items-center justify-between border-t border-slate-200 pt-4 dark:border-slate-700"> <div className="mt-6 flex items-center justify-between border-t border-slate-200 pt-4 dark:border-slate-700">
@ -1136,7 +1110,6 @@ export default function SettingsMacrosRoute() {
size="SM" size="SM"
theme="light" theme="light"
text="Cancel" text="Cancel"
LeadingIcon={LuX}
onClick={() => { onClick={() => {
if (newMacro.name || newMacro.description || newMacro.steps?.some(s => s.keys?.length || s.modifiers?.length)) { if (newMacro.name || newMacro.description || newMacro.steps?.some(s => s.keys?.length || s.modifiers?.length)) {
setShowClearConfirm(true); setShowClearConfirm(true);
@ -1151,26 +1124,34 @@ export default function SettingsMacrosRoute() {
</div> </div>
</div> </div>
</div> </div>
</div> )}
{macros.length === 0 && !showAddMacro && (
<EmptyCard
headline="No macros created yet"
BtnElm={
<Button
size="SM"
theme="primary"
text="Add New Macro"
onClick={() => setShowAddMacro(true)}
disabled={isMaxMacrosReached}
/>
}
/>
)} )}
{macros.length > 0 && ( {macros.length > 0 && (
<div className="space-y-2">
{macros.length === 0 ? (
<p className="text-center text-sm text-slate-500 dark:text-slate-400 py-4">
No macros created yet. Add your first macro above.
</p>
) : (
<div className="space-y-1"> <div className="space-y-1">
{macros.map((macro, index) => {macros.map((macro, index) =>
editingMacro && editingMacro.id === macro.id ? ( editingMacro && editingMacro.id === macro.id ? (
<div key={macro.id} className="rounded-md border border-blue-300 bg-blue-50 p-3 dark:border-blue-700 dark:bg-blue-900/20"> <div key={macro.id} className="rounded-md border border-blue-300 bg-blue-50 p-3 dark:border-blue-700 dark:bg-blue-900/20">
<Fieldset>
<div className="mb-2 grid grid-cols-1 md:grid-cols-2 gap-2"> <div className="mb-2 grid grid-cols-1 md:grid-cols-2 gap-2">
<div className="flex flex-col"> <InputFieldWithLabel
<input
type="text" type="text"
className={`macro-input ${errors.name ? 'border-red-500 dark:border-red-500' : ''}`} label="Macro Name"
placeholder="Macro Name"
value={editingMacro.name} value={editingMacro.name}
error={errors.name}
onChange={e => { onChange={e => {
setEditingMacro({ ...editingMacro, name: e.target.value }); setEditingMacro({ ...editingMacro, name: e.target.value });
if (errors.name) { if (errors.name) {
@ -1179,15 +1160,13 @@ export default function SettingsMacrosRoute() {
setErrors(newErrors); setErrors(newErrors);
} }
}} }}
placeholder="Macro Name"
/> />
<ErrorMessage error={errors.name} /> <InputFieldWithLabel
</div>
<div className="flex flex-col">
<input
type="text" type="text"
className={`macro-input ${errors.description ? 'border-red-500 dark:border-red-500' : ''}`} label="Description"
placeholder="Description (optional)"
value={editingMacro.description} value={editingMacro.description}
error={errors.description}
onChange={e => { onChange={e => {
setEditingMacro({ ...editingMacro, description: e.target.value }); setEditingMacro({ ...editingMacro, description: e.target.value });
if (errors.description) { if (errors.description) {
@ -1196,11 +1175,9 @@ export default function SettingsMacrosRoute() {
setErrors(newErrors); setErrors(newErrors);
} }
}} }}
placeholder="Description (optional)"
/> />
<ErrorMessage error={errors.description} />
</div>
</div> </div>
</Fieldset>
<div className="mt-4"> <div className="mt-4">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
@ -1219,6 +1196,7 @@ export default function SettingsMacrosRoute() {
<div className="mt-2 text-xs text-slate-500 dark:text-slate-400"> <div className="mt-2 text-xs text-slate-500 dark:text-slate-400">
You can add up to {MAX_STEPS_PER_MACRO} steps per macro You can add up to {MAX_STEPS_PER_MACRO} steps per macro
</div> </div>
<Fieldset>
<div className="mt-2 space-y-4"> <div className="mt-2 space-y-4">
{editingMacro.steps.map((step, stepIndex) => ( {editingMacro.steps.map((step, stepIndex) => (
<MacroStepCard <MacroStepCard
@ -1248,15 +1226,16 @@ export default function SettingsMacrosRoute() {
isLastStep={stepIndex === editingMacro.steps.length - 1} isLastStep={stepIndex === editingMacro.steps.length - 1}
/> />
))} ))}
</div>
</Fieldset>
<div className="mt-4 border-t border-slate-200 pt-4 dark:border-slate-700"> <div className="mt-4 border-t border-slate-200 pt-4 dark:border-slate-700">
<button <Button
type="button" size="MD"
className={`w-full flex items-center justify-center gap-1 rounded-md px-3 py-2 text-sm font-medium transition-colors ${ theme="light"
editingMacro.steps.length >= MAX_STEPS_PER_MACRO fullWidth
? 'bg-slate-100 text-slate-400 cursor-not-allowed dark:bg-slate-800 dark:text-slate-500' LeadingIcon={LuPlus}
: 'bg-slate-100 text-slate-700 hover:bg-slate-200 dark:bg-slate-800 dark:text-slate-200 dark:hover:bg-slate-700' text={`Add Step ${editingMacro.steps.length >= MAX_STEPS_PER_MACRO ? `(${MAX_STEPS_PER_MACRO} max)` : ''}`}
}`}
onClick={() => { onClick={() => {
if (editingMacro.steps.length >= MAX_STEPS_PER_MACRO) { if (editingMacro.steps.length >= MAX_STEPS_PER_MACRO) {
showTemporaryError(`You can only add a maximum of ${MAX_STEPS_PER_MACRO} steps per macro.`); showTemporaryError(`You can only add a maximum of ${MAX_STEPS_PER_MACRO} steps per macro.`);
@ -1273,11 +1252,7 @@ export default function SettingsMacrosRoute() {
clearErrors(); clearErrors();
}} }}
disabled={editingMacro.steps.length >= MAX_STEPS_PER_MACRO} disabled={editingMacro.steps.length >= MAX_STEPS_PER_MACRO}
> />
<LuPlus className="h-4 w-4" />
<span>Add Step {editingMacro.steps.length >= MAX_STEPS_PER_MACRO && `(${MAX_STEPS_PER_MACRO} max)`}</span>
</button>
</div>
</div> </div>
<div className="mt-4 flex items-center justify-between border-t border-slate-200 pt-4 dark:border-slate-700"> <div className="mt-4 flex items-center justify-between border-t border-slate-200 pt-4 dark:border-slate-700">
@ -1293,7 +1268,6 @@ export default function SettingsMacrosRoute() {
size="SM" size="SM"
theme="light" theme="light"
text="Cancel" text="Cancel"
LeadingIcon={LuX}
onClick={() => { onClick={() => {
setEditingMacro(null); setEditingMacro(null);
setErrors({}); setErrors({});
@ -1378,14 +1352,15 @@ export default function SettingsMacrosRoute() {
<span className="text-sm text-slate-600 dark:text-slate-400"> <span className="text-sm text-slate-600 dark:text-slate-400">
Delete macro? Delete macro?
</span> </span>
<div className="flex items-center gap-x-2">
<Button <Button
size="XS" size="XS"
theme="danger" theme="danger"
text={isDeleting ? "Deleting..." : "Yes"} text="Yes"
disabled={isDeleting}
onClick={() => { onClick={() => {
handleDeleteMacro(macro.id); handleDeleteMacro(macro.id);
}} }}
disabled={isDeleting}
/> />
<Button <Button
size="XS" size="XS"
@ -1394,35 +1369,28 @@ export default function SettingsMacrosRoute() {
onClick={() => setMacroToDelete(null)} onClick={() => setMacroToDelete(null)}
/> />
</div> </div>
</div>
) : ( ) : (
<> <>
<button <Button
type="button" size="XS"
className="rounded-md p-1 text-slate-500 hover:bg-slate-100 hover:text-green-500 dark:text-slate-400 dark:hover:bg-slate-700 dark:hover:text-green-400" theme="light"
LeadingIcon={LuPenLine}
onClick={() => handleEditMacro(macro)} onClick={() => handleEditMacro(macro)}
title="Edit" />
> <Button
<LuPenLine className="h-4 w-4" /> size="XS"
</button> theme="light"
<button LeadingIcon={LuCopy}
type="button"
className="rounded-md p-1 text-slate-500 hover:bg-slate-100 hover:text-red-500 dark:text-slate-400 dark:hover:bg-slate-700 dark:hover:text-red-400"
onClick={() => handleDuplicateMacro(macro)} onClick={() => handleDuplicateMacro(macro)}
title="Duplicate" />
> <Button
<svg className="h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> size="XS"
<rect x="8" y="8" width="12" height="12" rx="2" /> theme="light"
<path d="M16 8V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h2" /> LeadingIcon={LuTrash}
</svg>
</button>
<button
type="button"
className="rounded-md p-1 text-slate-500 hover:bg-slate-100 hover:text-red-500 dark:text-slate-400 dark:hover:bg-slate-700 dark:hover:text-red-400"
onClick={() => setMacroToDelete(macro.id)} onClick={() => setMacroToDelete(macro.id)}
title="Delete" className="text-red-500 dark:text-red-400"
> />
<LuTrash className="h-4 w-4" />
</button>
</> </>
)} )}
</div> </div>
@ -1432,8 +1400,6 @@ export default function SettingsMacrosRoute() {
</div> </div>
)} )}
</div> </div>
)}
</div>
</div> </div>
); );
} }