feat: Script Generator Phase 1 — backend models, engine, API, and AD templates #105

Merged
chihlasm merged 78 commits from feat/script-generator into main 2026-03-15 00:19:00 +00:00
Showing only changes of commit 9397e46eec - Show all commits

View File

@@ -0,0 +1,48 @@
import { useRef, useCallback } from 'react'
import { PowerShellHighlighter } from '@/components/scripts/PowerShellHighlighter'
interface Props {
value: string
onChange: (value: string) => void
disabled?: boolean
}
export function ScriptBodyEditor({ value, onChange, disabled }: Props) {
const textareaRef = useRef<HTMLTextAreaElement>(null)
const handleTab = useCallback((e: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (e.key === 'Tab') {
e.preventDefault()
const ta = e.currentTarget
const start = ta.selectionStart
const end = ta.selectionEnd
const newValue = value.substring(0, start) + ' ' + value.substring(end)
onChange(newValue)
// Restore cursor position after React re-render
requestAnimationFrame(() => {
ta.selectionStart = ta.selectionEnd = start + 4
})
}
}, [value, onChange])
return (
<div className="relative rounded-xl border border-border overflow-hidden">
{/* Highlighted overlay (read-only visual layer) */}
<div className="absolute inset-0 pointer-events-none overflow-auto p-4">
<PowerShellHighlighter script={value || ' '} />
</div>
{/* Editable textarea (transparent text, visible caret) */}
<textarea
ref={textareaRef}
value={value}
onChange={e => onChange(e.target.value)}
onKeyDown={handleTab}
disabled={disabled}
spellCheck={false}
className="relative z-10 w-full min-h-[300px] resize-y font-label text-sm bg-transparent text-transparent caret-foreground p-4 focus:outline-none focus:ring-1 focus:ring-[rgba(6,182,212,0.2)] disabled:cursor-not-allowed disabled:opacity-50"
placeholder="# Enter your PowerShell script here…&#10;# Use {{ param_name }} for parameter placeholders"
/>
</div>
)
}