scripting added
All checks were successful
Build and Deploy MITM Webserver / build (push) Successful in 8s
All checks were successful
Build and Deploy MITM Webserver / build (push) Successful in 8s
This commit is contained in:
@@ -3,6 +3,7 @@ import { ApartmentOutlined, ApiOutlined, HomeOutlined, InfoCircleOutlined, Setti
|
||||
import { Layout, Menu } from 'antd';
|
||||
import React from 'react';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import TerminalIcon from '../icons/TerminalIcon';
|
||||
import { PATHS } from '../routes';
|
||||
import '../theme/layout.less';
|
||||
|
||||
@@ -12,6 +13,11 @@ const menuItems = [
|
||||
{ key: PATHS.HOME, icon: <HomeOutlined style={{ fontSize: '18px' }} />, label: 'Home' },
|
||||
{ key: PATHS.NETWORK, icon: <ApartmentOutlined style={{ fontSize: '18px' }} />, label: 'Network' },
|
||||
{ key: PATHS.SNIFFING, icon: <ApiOutlined style={{ fontSize: '18px' }} />, label: 'Sniffing' },
|
||||
{
|
||||
key: PATHS.SCRIPTING,
|
||||
icon: <TerminalIcon style={{ fontSize: '18px' }} width={18} height={18} />,
|
||||
label: 'Scripting',
|
||||
},
|
||||
{ key: '/about', icon: <InfoCircleOutlined style={{ fontSize: '18px' }} />, label: 'About' },
|
||||
{ key: '/settings', icon: <SettingOutlined style={{ fontSize: '18px' }} />, label: 'Settings' },
|
||||
];
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Navigate, Route, Routes } from 'react-router-dom';
|
||||
import App from './App'; // your layout component (has <Outlet />)
|
||||
import Home from './pages/Home';
|
||||
import Network from './pages/Network';
|
||||
import Scripting from './pages/Scripting';
|
||||
import Sniffing from './pages/Sniffing';
|
||||
import { PATHS } from './routes';
|
||||
|
||||
@@ -18,6 +19,7 @@ export default function AppRouter() {
|
||||
<Route path={PATHS.HOME.slice(1)} element={<Home />} />
|
||||
<Route path={PATHS.NETWORK.slice(1)} element={<Network />} />
|
||||
<Route path={PATHS.SNIFFING.slice(1)} element={<Sniffing />} />
|
||||
<Route path={PATHS.SCRIPTING.slice(1)} element={<Scripting />} />
|
||||
|
||||
{/* Fallback (renders inside layout too) */}
|
||||
<Route path="*" element={<NotFound />} />
|
||||
|
||||
54
frontend/src/components/PythonEditor.tsx
Normal file
54
frontend/src/components/PythonEditor.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import Prism from 'prismjs';
|
||||
import 'prismjs/components/prism-python';
|
||||
import 'prismjs/themes/prism.css';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import Editor from 'react-simple-code-editor';
|
||||
|
||||
// Props for the editor
|
||||
export type PythonEditorProps = {
|
||||
value: string;
|
||||
onChange: (code: string) => void;
|
||||
readOnly?: boolean;
|
||||
className?: string;
|
||||
height?: string | number; // e.g. '200px' or 200
|
||||
};
|
||||
|
||||
export default function PythonEditor({
|
||||
value,
|
||||
onChange,
|
||||
readOnly = false,
|
||||
height = 480,
|
||||
className = '',
|
||||
}: PythonEditorProps) {
|
||||
const highlight = useCallback((code: string) => {
|
||||
try {
|
||||
return Prism.highlight(code, Prism.languages.python, 'python');
|
||||
} catch (e) {
|
||||
return code;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const editorStyle = useMemo(
|
||||
() => ({
|
||||
fontFamily: '"JetBrains Mono", "Fira Code", monospace',
|
||||
fontSize: 14,
|
||||
height: typeof height === 'number' ? `${height}px` : height,
|
||||
}),
|
||||
[height],
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={`python-editor ${className}`}>
|
||||
<Editor
|
||||
value={value}
|
||||
onValueChange={(code) => onChange(code)}
|
||||
highlight={highlight}
|
||||
padding={12}
|
||||
preClassName="language-python"
|
||||
textareaId="python-editor"
|
||||
style={editorStyle}
|
||||
readOnly={readOnly}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
57
frontend/src/icons/TerminalIcon.tsx
Normal file
57
frontend/src/icons/TerminalIcon.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import React, { forwardRef } from 'react';
|
||||
|
||||
export type IconProps = React.SVGProps<SVGSVGElement> & {
|
||||
/**
|
||||
* Width/height of the icon. If a number is provided it will be used as px.
|
||||
* Default: 24
|
||||
*/
|
||||
size?: number | string;
|
||||
/**
|
||||
* Icon color — will be used as the fill for paths.
|
||||
* Default: 'currentColor' so color can be controlled via CSS.
|
||||
*/
|
||||
color?: string;
|
||||
/**
|
||||
* Accessible title. If provided, title will be rendered and aria-hidden will be false.
|
||||
*/
|
||||
title?: string;
|
||||
};
|
||||
|
||||
const TerminalIcon = forwardRef<SVGSVGElement, IconProps>(
|
||||
({ size = 24, color = 'currentColor', title, ...rest }, ref) => {
|
||||
// If user passed a numeric size, treat as px
|
||||
const sizeValue = typeof size === 'number' ? `${size}px` : size;
|
||||
|
||||
return (
|
||||
<svg
|
||||
ref={ref}
|
||||
width={sizeValue}
|
||||
height={sizeValue}
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-hidden={title ? undefined : true}
|
||||
role={title ? 'img' : 'presentation'}
|
||||
{...rest}
|
||||
>
|
||||
{title ? <title>{title}</title> : null}
|
||||
|
||||
<path
|
||||
d="M5.0333 14.8284L6.44751 16.2426L10.6902 12L6.44751 7.75733L5.0333 9.17155L7.86172 12L5.0333 14.8284Z"
|
||||
fill={color}
|
||||
/>
|
||||
<path d="M15 14H11V16H15V14Z" fill={color} />
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M2 2C0.895431 2 0 2.89543 0 4V20C0 21.1046 0.89543 22 2 22H22C23.1046 22 24 21.1046 24 20V4C24 2.89543 23.1046 2 22 2H2ZM22 4H2L2 20H22V4Z"
|
||||
fill={color}
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
TerminalIcon.displayName = 'TerminalIcon';
|
||||
|
||||
export default TerminalIcon;
|
||||
@@ -76,10 +76,6 @@ a:hover {
|
||||
/* === Code blocks / preformatted text === */
|
||||
pre,
|
||||
code {
|
||||
background-color: #f0f0f0;
|
||||
color: #262626;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
font-family: "JetBrains Mono", "Fira Code", monospace;
|
||||
}
|
||||
|
||||
|
||||
33
frontend/src/pages/Scripting.tsx
Normal file
33
frontend/src/pages/Scripting.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Card, Tabs } from 'antd';
|
||||
import { useState } from 'react';
|
||||
import PythonEditor from '../components/PythonEditor';
|
||||
|
||||
const TwoTabsPage: React.FC = () => {
|
||||
const [code, setCode] = useState<string>("print('hello world')\n");
|
||||
|
||||
const tabItems = [
|
||||
{
|
||||
key: '1',
|
||||
label: 'Saved Scripts',
|
||||
children: <></>,
|
||||
},
|
||||
{
|
||||
key: '2',
|
||||
label: 'Custom Script',
|
||||
children: (
|
||||
<div>
|
||||
<h3>Python Editor</h3>
|
||||
<PythonEditor value={code} onChange={setCode} />
|
||||
</div>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Card style={{ width: '100%' }}>
|
||||
<Tabs defaultActiveKey="1" items={tabItems} />
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default TwoTabsPage;
|
||||
@@ -3,4 +3,5 @@ export const PATHS = {
|
||||
HOME: '/home',
|
||||
NETWORK: '/network',
|
||||
SNIFFING: '/sniffing',
|
||||
SCRIPTING: '/scripting',
|
||||
};
|
||||
Reference in New Issue
Block a user