api fix and script fe improv
All checks were successful
Build and Deploy MITM Webserver / build (push) Successful in 9s

This commit is contained in:
2026-03-01 11:57:54 +01:00
parent 60634a77eb
commit 7c08aed33a
3 changed files with 661 additions and 509 deletions

View File

@@ -493,11 +493,7 @@ async def upload_script(
except ValueError as e:
logger.warning("Invalid name provided: %s", name)
raise HTTPException(status_code=400, detail=str(e))
if not script.filename.endswith(".py"):
logger.warning("Upload rejected: script not .py (name=%s original=%s)", name, script.filename)
raise HTTPException(status_code=400, detail="only .py scripts allowed")
spath = script_path_for(name)
if os.path.exists(spath):
logger.warning("Upload rejected: script with name already exists: %s", name)

View File

@@ -1,76 +1,111 @@
// src/components/PythonEditor.tsx
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';
import React, { useEffect, useMemo, useRef } from 'react';
// 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
wrap?: boolean; // if true, long lines will wrap; default false (no wrap)
height?: string | number;
wrap?: boolean;
fontSize?: number;
tabSize?: number;
};
const DEFAULT_FONT =
'"JetBrains Mono", "Fira Code", "Source Code Pro", ui-monospace, SFMono-Regular, Menlo, Monaco, monospace';
export default function PythonEditor({
value,
onChange,
readOnly = false,
height = 480,
className = '',
wrap = false,
fontSize = 14,
tabSize = 4,
}: PythonEditorProps) {
const highlight = useCallback((code: string) => {
try {
return Prism.highlight(code, Prism.languages.python, 'python');
} catch (e) {
return code;
const textareaRef = useRef<HTMLTextAreaElement | null>(null);
const preRef = useRef<HTMLPreElement | null>(null);
const h = typeof height === 'number' ? `${height}px` : height;
const lineHeight = Math.round(fontSize * 1.6);
// Highlight whenever value changes
useEffect(() => {
if (preRef.current) {
preRef.current.innerHTML = Prism.highlight(value, Prism.languages.python, 'python');
}
}, []);
}, [value]);
// container style: fixed height + scrolling
const containerStyle = useMemo(() => {
const h = typeof height === 'number' ? `${height}px` : height;
return {
height: h,
maxHeight: h,
overflow: 'auto' as const, // enable scrolling
position: 'relative' as const,
borderRadius: 6,
border: '1px solid rgba(0,0,0,0.06)',
background: 'var(--code-bg, white)',
};
}, [height]);
// Scroll sync
const handleScroll = () => {
if (!textareaRef.current || !preRef.current) return;
preRef.current.scrollTop = textareaRef.current.scrollTop;
preRef.current.scrollLeft = textareaRef.current.scrollLeft;
};
// editor style: fill container and use monospace font
const editorStyle = useMemo(
const sharedStyle: React.CSSProperties = useMemo(
() => ({
fontFamily: '"JetBrains Mono", "Fira Code", monospace',
fontSize: 14,
// make editor fill the container (pre/textarea inside will take up height)
minHeight: '100%',
boxSizing: 'border-box' as const,
fontFamily: DEFAULT_FONT,
fontSize: `${fontSize}px`,
lineHeight: `${lineHeight}px`,
tabSize,
whiteSpace: wrap ? 'pre-wrap' : 'pre',
wordBreak: 'break-word',
padding: 12,
outline: 'none',
whiteSpace: wrap ? 'pre-wrap' : 'pre', // pre-wrap if wrap requested
wordBreak: 'break-word' as const,
boxSizing: 'border-box',
}),
[wrap],
[fontSize, lineHeight, tabSize, wrap],
);
return (
<div className={`python-editor ${className}`} style={containerStyle}>
<Editor
<div
style={{
position: 'relative',
height: h,
borderRadius: 6,
border: '1px solid rgba(0,0,0,0.06)',
overflow: 'hidden',
}}
>
{/* Highlight layer */}
<pre
ref={preRef}
aria-hidden="true"
className="language-python"
style={{
...sharedStyle,
margin: 0,
position: 'absolute',
inset: 0,
overflow: 'auto',
pointerEvents: 'none',
}}
/>
{/* Editable layer */}
<textarea
ref={textareaRef}
value={value}
onValueChange={(code) => onChange(code)}
highlight={highlight}
padding={0} // padding handled by editorStyle
preClassName="language-python"
textareaId="python-editor"
style={editorStyle}
readOnly={readOnly}
onChange={(e) => onChange(e.target.value)}
onScroll={handleScroll}
spellCheck={false}
style={{
...sharedStyle,
position: 'absolute',
inset: 0,
resize: 'none',
border: 'none',
outline: 'none',
background: 'transparent',
color: 'transparent', // hide textarea text
caretColor: 'black', // show caret
overflow: 'auto',
}}
/>
</div>
);

File diff suppressed because it is too large Load Diff