+
{name}
+
+ {comb.map((v, i) => (
+ {v}
+ ))}
+
+
+ );
+}
diff --git a/dashboard/src/interfaces/components/Modal.ts b/dashboard/src/interfaces/components/Modal.ts
new file mode 100644
index 000000000..1a1ced70b
--- /dev/null
+++ b/dashboard/src/interfaces/components/Modal.ts
@@ -0,0 +1,11 @@
+export interface IModalProps {
+ children?: React.ReactNode;
+ fullHeight?: boolean;
+ open?: boolean;
+}
+
+export interface IModalShortcutProps {
+ open?: IModalProps['open'];
+ onClose?: React.ComponentProps<'svg'>['onClick'];
+ modalContainerProps?: IModalProps;
+}
diff --git a/dashboard/src/interfaces/components/Shortcuts.ts b/dashboard/src/interfaces/components/Shortcuts.ts
new file mode 100644
index 000000000..fcff82d7e
--- /dev/null
+++ b/dashboard/src/interfaces/components/Shortcuts.ts
@@ -0,0 +1,8 @@
+export interface IKeyProps {
+ children?: React.ReactNode;
+}
+
+export interface IShortcutEntryProps {
+ name?: React.ReactNode;
+ comb?: IKeyProps['children'][];
+}
diff --git a/dashboard/src/interfaces/kbdsrct.ts b/dashboard/src/interfaces/kbdsrct.ts
new file mode 100644
index 000000000..a0ff44675
--- /dev/null
+++ b/dashboard/src/interfaces/kbdsrct.ts
@@ -0,0 +1,4 @@
+export interface IKbdsrct {
+ comb: string[];
+ cb: () => void;
+}
diff --git a/dashboard/src/layouts/AppLayout.tsx b/dashboard/src/layouts/AppLayout.tsx
index 6ba50cfa5..1406df0fc 100644
--- a/dashboard/src/layouts/AppLayout.tsx
+++ b/dashboard/src/layouts/AppLayout.tsx
@@ -1,5 +1,12 @@
+import ModalShortcut from '@/components/ModalShortcut';
import { PageLayout } from '@/interfaces/layouts';
+import {
+ execKbdsrct,
+ registerKbdsrct,
+ unregisterKbdsrct,
+} from '@/libs/kbdsrct';
import { createTheme, NextUIProvider } from '@nextui-org/react';
+import { useEffect, useRef, useState } from 'react';
const AppLayout: PageLayout = ({
children,
@@ -13,6 +20,81 @@ const AppLayout: PageLayout = ({
overflow: 'hidden',
},
}) => {
+ const [modalShortcutOpen, setModalShortcutOpen] = useState(false);
+
+ const pressesRef = useRef