refactor: migrate remaining components to Design System v4

111 files across 14 directories: common, tree-editor, kb-accelerator,
copilot, assistant, analytics, library, procedural, procedural-editor,
public, script-editor, ui, admin, step-library.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Michael Chihlas
2026-03-22 02:18:15 -04:00
parent 858f890ed3
commit d1a56f0529
111 changed files with 1330 additions and 1330 deletions

View File

@@ -55,29 +55,29 @@ export function ParameterCard({
}
return (
<div className="border border-border rounded-xl overflow-hidden">
<div className="border border-[#1e2130] rounded-xl overflow-hidden">
{/* Header */}
<button
type="button"
onClick={() => setExpanded(v => !v)}
className="w-full flex items-center gap-2 px-3 py-2.5 bg-white/[0.02] hover:bg-white/[0.04] transition-colors"
>
<GripVertical size={14} className="text-muted-foreground/50 shrink-0" />
{expanded ? <ChevronDown size={14} className="text-muted-foreground" /> : <ChevronRight size={14} className="text-muted-foreground" />}
<span className="text-sm font-medium text-foreground flex-1 text-left">
<GripVertical size={14} className="text-[#848b9b]/50 shrink-0" />
{expanded ? <ChevronDown size={14} className="text-[#848b9b]" /> : <ChevronRight size={14} className="text-[#848b9b]" />}
<span className="text-sm font-medium text-[#e2e5eb] flex-1 text-left">
{param.label || param.key || `Parameter ${index + 1}`}
</span>
<span className="font-label text-[0.625rem] text-muted-foreground uppercase">{param.type}</span>
<span className="font-sans text-xs text-[0.625rem] text-[#848b9b] uppercase">{param.type}</span>
{param.required && <span className="text-red-400 text-xs">*</span>}
</button>
{/* Body */}
{expanded && (
<div className="px-3 py-3 space-y-3 border-t border-border">
<div className="px-3 py-3 space-y-3 border-t border-[#1e2130]">
{/* Row 1: key + label */}
<div className="grid grid-cols-2 gap-3">
<div>
<label className="text-xs text-muted-foreground mb-1 block">Key (used in &#123;&#123;key&#125;&#125;)</label>
<label className="text-xs text-[#848b9b] mb-1 block">Key (used in &#123;&#123;key&#125;&#125;)</label>
<Input
value={param.key}
onChange={e => update({ key: e.target.value.replace(/[^a-zA-Z0-9_]/g, '') })}
@@ -86,7 +86,7 @@ export function ParameterCard({
/>
</div>
<div>
<label className="text-xs text-muted-foreground mb-1 block">Label</label>
<label className="text-xs text-[#848b9b] mb-1 block">Label</label>
<Input
value={param.label}
onChange={e => update({ label: e.target.value })}
@@ -99,12 +99,12 @@ export function ParameterCard({
{/* Row 2: type + group */}
<div className="grid grid-cols-2 gap-3">
<div>
<label className="text-xs text-muted-foreground mb-1 block">Type</label>
<label className="text-xs text-[#848b9b] mb-1 block">Type</label>
<select
value={param.type}
onChange={e => update({ type: e.target.value as ScriptParameter['type'] })}
disabled={disabled}
className="w-full rounded-[10px] border border-border bg-card text-foreground px-3 py-2 text-sm focus:outline-none focus:border-[rgba(6,182,212,0.3)] disabled:cursor-not-allowed disabled:opacity-50"
className="w-full rounded-lg border border-[#1e2130] bg-[#14161d] text-[#e2e5eb] px-3 py-2 text-sm focus:outline-none focus:border-[rgba(6,182,212,0.3)] disabled:cursor-not-allowed disabled:opacity-50"
>
{PARAM_TYPES.map(t => (
<option key={t.value} value={t.value}>{t.label}</option>
@@ -112,7 +112,7 @@ export function ParameterCard({
</select>
</div>
<div>
<label className="text-xs text-muted-foreground mb-1 block">Group (optional)</label>
<label className="text-xs text-[#848b9b] mb-1 block">Group (optional)</label>
<Input
value={param.group ?? ''}
onChange={e => update({ group: e.target.value || null })}
@@ -125,7 +125,7 @@ export function ParameterCard({
{/* Row 3: placeholder + help text */}
<div className="grid grid-cols-2 gap-3">
<div>
<label className="text-xs text-muted-foreground mb-1 block">Placeholder</label>
<label className="text-xs text-[#848b9b] mb-1 block">Placeholder</label>
<Input
value={param.placeholder ?? ''}
onChange={e => update({ placeholder: e.target.value || null })}
@@ -134,7 +134,7 @@ export function ParameterCard({
/>
</div>
<div>
<label className="text-xs text-muted-foreground mb-1 block">Help text</label>
<label className="text-xs text-[#848b9b] mb-1 block">Help text</label>
<Input
value={param.help_text ?? ''}
onChange={e => update({ help_text: e.target.value || null })}
@@ -146,23 +146,23 @@ export function ParameterCard({
{/* Row 4: toggles */}
<div className="flex items-center gap-4">
<label className="flex items-center gap-2 text-sm text-foreground">
<label className="flex items-center gap-2 text-sm text-[#e2e5eb]">
<input
type="checkbox"
checked={param.required}
onChange={e => update({ required: e.target.checked })}
disabled={disabled}
className="rounded border-border"
className="rounded border-[#1e2130]"
/>
Required
</label>
<label className="flex items-center gap-2 text-sm text-foreground">
<label className="flex items-center gap-2 text-sm text-[#e2e5eb]">
<input
type="checkbox"
checked={param.sensitive}
onChange={e => update({ sensitive: e.target.checked })}
disabled={disabled}
className="rounded border-border"
className="rounded border-[#1e2130]"
/>
Sensitive (redacted in logs)
</label>
@@ -170,7 +170,7 @@ export function ParameterCard({
{/* Default value */}
<div>
<label className="text-xs text-muted-foreground mb-1 block">Default value</label>
<label className="text-xs text-[#848b9b] mb-1 block">Default value</label>
<Input
value={param.default !== null && param.default !== undefined ? String(param.default) : ''}
onChange={e => update({ default: e.target.value || null })}
@@ -182,7 +182,7 @@ export function ParameterCard({
{/* Select options (only for select type) */}
{param.type === 'select' && (
<div>
<label className="text-xs text-muted-foreground mb-1 block">Options</label>
<label className="text-xs text-[#848b9b] mb-1 block">Options</label>
<div className="space-y-1.5">
{(param.options ?? []).map((opt, i) => (
<div key={i} className="flex items-center gap-2">
@@ -202,7 +202,7 @@ export function ParameterCard({
type="button"
onClick={() => removeOption(i)}
disabled={disabled}
className="p-1 text-muted-foreground hover:text-rose-500 transition-colors"
className="p-1 text-[#848b9b] hover:text-rose-500 transition-colors"
>
<X size={14} />
</button>
@@ -212,7 +212,7 @@ export function ParameterCard({
type="button"
onClick={addOption}
disabled={disabled}
className="flex items-center gap-1 text-xs text-primary hover:underline"
className="flex items-center gap-1 text-xs text-[#22d3ee] hover:underline"
>
<Plus size={12} /> Add option
</button>
@@ -223,12 +223,12 @@ export function ParameterCard({
{/* Validation (for text/number types) */}
{(param.type === 'text' || param.type === 'number' || param.type === 'textarea') && (
<div>
<label className="text-xs text-muted-foreground mb-1 block">Validation (optional)</label>
<label className="text-xs text-[#848b9b] mb-1 block">Validation (optional)</label>
<div className="grid grid-cols-3 gap-2">
{param.type === 'number' ? (
<>
<div>
<label className="text-[0.625rem] text-muted-foreground">Min value</label>
<label className="text-[0.625rem] text-[#848b9b]">Min value</label>
<Input
type="number"
value={param.validation?.min_value ?? ''}
@@ -237,7 +237,7 @@ export function ParameterCard({
/>
</div>
<div>
<label className="text-[0.625rem] text-muted-foreground">Max value</label>
<label className="text-[0.625rem] text-[#848b9b]">Max value</label>
<Input
type="number"
value={param.validation?.max_value ?? ''}
@@ -249,7 +249,7 @@ export function ParameterCard({
) : (
<>
<div>
<label className="text-[0.625rem] text-muted-foreground">Min length</label>
<label className="text-[0.625rem] text-[#848b9b]">Min length</label>
<Input
type="number"
value={param.validation?.min_length ?? ''}
@@ -258,7 +258,7 @@ export function ParameterCard({
/>
</div>
<div>
<label className="text-[0.625rem] text-muted-foreground">Max length</label>
<label className="text-[0.625rem] text-[#848b9b]">Max length</label>
<Input
type="number"
value={param.validation?.max_length ?? ''}
@@ -269,7 +269,7 @@ export function ParameterCard({
</>
)}
<div>
<label className="text-[0.625rem] text-muted-foreground">Pattern (regex)</label>
<label className="text-[0.625rem] text-[#848b9b]">Pattern (regex)</label>
<Input
value={param.validation?.pattern ?? ''}
onChange={e => updateValidation({ pattern: e.target.value || undefined })}
@@ -282,13 +282,13 @@ export function ParameterCard({
)}
{/* Actions row */}
<div className="flex items-center justify-between pt-1 border-t border-border">
<div className="flex items-center justify-between pt-1 border-t border-[#1e2130]">
<div className="flex items-center gap-1">
<button
type="button"
onClick={() => onMoveUp(index)}
disabled={isFirst || disabled}
className="text-xs text-muted-foreground hover:text-foreground disabled:opacity-30 px-1.5 py-0.5"
className="text-xs text-[#848b9b] hover:text-[#e2e5eb] disabled:opacity-30 px-1.5 py-0.5"
>
Up
</button>
@@ -296,7 +296,7 @@ export function ParameterCard({
type="button"
onClick={() => onMoveDown(index)}
disabled={isLast || disabled}
className="text-xs text-muted-foreground hover:text-foreground disabled:opacity-30 px-1.5 py-0.5"
className="text-xs text-[#848b9b] hover:text-[#e2e5eb] disabled:opacity-30 px-1.5 py-0.5"
>
Down
</button>

View File

@@ -107,7 +107,7 @@ export function ParameterDetectorStepper({
<div className="border border-primary/20 rounded-xl bg-primary/[0.03] p-4 space-y-3">
{/* Progress */}
<div className="flex items-center justify-between">
<p className="text-xs font-medium text-foreground">
<p className="text-xs font-medium text-[#e2e5eb]">
Candidate {currentIndex + 1} of {candidates.length}
</p>
<div className="flex items-center gap-1">
@@ -127,10 +127,10 @@ export function ParameterDetectorStepper({
{/* Matched line */}
<div className="rounded-lg bg-black/20 px-3 py-2">
<p className="font-label text-xs text-amber-400 break-all">
<p className="font-sans text-xs text-xs text-amber-400 break-all">
{current.matchedLine}
</p>
<p className="font-label text-[0.5rem] text-muted-foreground mt-1">
<p className="font-sans text-xs text-[0.5rem] text-[#848b9b] mt-1">
Line {current.lineNumber} · {current.source === 'param_block' ? 'param() block' : 'variable assignment'}
</p>
</div>
@@ -138,7 +138,7 @@ export function ParameterDetectorStepper({
{/* Editable fields */}
<div className="grid grid-cols-2 gap-3">
<div>
<label className="text-xs text-muted-foreground mb-1 block">Key</label>
<label className="text-xs text-[#848b9b] mb-1 block">Key</label>
<Input
value={key}
onChange={e => setKey(e.target.value.replace(/[^a-zA-Z0-9_]/g, ''))}
@@ -149,7 +149,7 @@ export function ParameterDetectorStepper({
)}
</div>
<div>
<label className="text-xs text-muted-foreground mb-1 block">Label</label>
<label className="text-xs text-[#848b9b] mb-1 block">Label</label>
<Input
value={label}
onChange={e => setLabel(e.target.value)}
@@ -160,12 +160,12 @@ export function ParameterDetectorStepper({
<div className="grid grid-cols-2 gap-3">
<div>
<label className="text-xs text-muted-foreground mb-1 flex items-center gap-1.5">
<label className="text-xs text-[#848b9b] mb-1 flex items-center gap-1.5">
Type
<button
type="button"
onClick={() => setShowInferenceInfo(!showInferenceInfo)}
className="text-muted-foreground hover:text-primary transition-colors"
className="text-[#848b9b] hover:text-[#22d3ee] transition-colors"
title={current.inferenceReason}
>
<Info size={11} />
@@ -174,20 +174,20 @@ export function ParameterDetectorStepper({
<select
value={type}
onChange={e => setType(e.target.value as ScriptParameter['type'])}
className="w-full rounded-[10px] border border-border bg-card text-foreground px-3 py-2 text-sm focus:outline-none focus:border-[rgba(6,182,212,0.3)]"
className="w-full rounded-lg border border-[#1e2130] bg-[#14161d] text-[#e2e5eb] px-3 py-2 text-sm focus:outline-none focus:border-[rgba(6,182,212,0.3)]"
>
{PARAM_TYPES.map(t => (
<option key={t.value} value={t.value}>{t.label}</option>
))}
</select>
{showInferenceInfo && (
<p className="text-[0.625rem] text-primary/80 mt-1 italic">
<p className="text-[0.625rem] text-[#22d3ee]/80 mt-1 italic">
{current.inferenceReason}
</p>
)}
</div>
<div>
<label className="text-xs text-muted-foreground mb-1 block">Default value</label>
<label className="text-xs text-[#848b9b] mb-1 block">Default value</label>
<Input
value={defaultValue}
onChange={e => setDefaultValue(e.target.value)}
@@ -197,32 +197,32 @@ export function ParameterDetectorStepper({
</div>
<div className="flex items-center gap-4">
<label className="flex items-center gap-2 text-sm text-foreground">
<label className="flex items-center gap-2 text-sm text-[#e2e5eb]">
<input
type="checkbox"
checked={required}
onChange={e => setRequired(e.target.checked)}
className="rounded border-border"
className="rounded border-[#1e2130]"
/>
Required
</label>
<label className="flex items-center gap-2 text-sm text-foreground">
<label className="flex items-center gap-2 text-sm text-[#e2e5eb]">
<input
type="checkbox"
checked={sensitive}
onChange={e => setSensitive(e.target.checked)}
className="rounded border-border"
className="rounded border-[#1e2130]"
/>
Sensitive
</label>
</div>
{/* Actions */}
<div className="flex items-center justify-end gap-2 pt-1 border-t border-border">
<div className="flex items-center justify-end gap-2 pt-1 border-t border-[#1e2130]">
<button
type="button"
onClick={handleSkip}
className="flex items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground transition-colors px-3 py-1.5"
className="flex items-center gap-1.5 text-sm text-[#848b9b] hover:text-[#e2e5eb] transition-colors px-3 py-1.5"
>
<SkipForward size={13} />
{isLast ? 'Skip & Finish' : 'Skip'}
@@ -231,7 +231,7 @@ export function ParameterDetectorStepper({
type="button"
onClick={handleAccept}
disabled={!key.trim() || !label.trim()}
className="flex items-center gap-1.5 bg-gradient-brand text-[#101114] font-semibold text-sm px-4 py-1.5 rounded-[10px] hover:opacity-90 active:scale-[0.97] transition-all shadow-lg shadow-primary/20 disabled:opacity-50 disabled:cursor-not-allowed"
className="flex items-center gap-1.5 bg-[#22d3ee] text-white font-semibold text-sm px-4 py-1.5 rounded-lg hover:brightness-110 active:scale-[0.98] transition-all disabled:opacity-50 disabled:cursor-not-allowed"
>
{isLast ? (
<><Check size={13} /> Accept &amp; Finish</>

View File

@@ -97,10 +97,10 @@ export function ParameterSchemaBuilder({ schema, onChange, disabled }: Props) {
type="button"
onClick={() => mode === 'json' ? switchToVisual() : undefined}
className={cn(
'flex items-center gap-1.5 font-label text-xs px-3 py-1.5 rounded-full border transition-all',
'flex items-center gap-1.5 font-sans text-xs text-xs px-3 py-1.5 rounded-full border transition-all',
mode === 'visual'
? 'bg-primary/10 border-primary/30 text-foreground'
: 'border-border text-muted-foreground hover:text-foreground'
? 'bg-[rgba(34,211,238,0.10)] border-primary/30 text-[#e2e5eb]'
: 'border-[#1e2130] text-[#848b9b] hover:text-[#e2e5eb]'
)}
>
<List size={12} /> Visual
@@ -109,10 +109,10 @@ export function ParameterSchemaBuilder({ schema, onChange, disabled }: Props) {
type="button"
onClick={() => mode === 'visual' ? switchToJson() : undefined}
className={cn(
'flex items-center gap-1.5 font-label text-xs px-3 py-1.5 rounded-full border transition-all',
'flex items-center gap-1.5 font-sans text-xs text-xs px-3 py-1.5 rounded-full border transition-all',
mode === 'json'
? 'bg-primary/10 border-primary/30 text-foreground'
: 'border-border text-muted-foreground hover:text-foreground'
? 'bg-[rgba(34,211,238,0.10)] border-primary/30 text-[#e2e5eb]'
: 'border-[#1e2130] text-[#848b9b] hover:text-[#e2e5eb]'
)}
>
<Code size={12} /> JSON
@@ -122,7 +122,7 @@ export function ParameterSchemaBuilder({ schema, onChange, disabled }: Props) {
{mode === 'visual' ? (
<>
{parameters.length === 0 ? (
<p className="text-sm text-muted-foreground py-4 text-center">
<p className="text-sm text-[#848b9b] py-4 text-center">
No parameters defined. Add one to create dynamic form fields.
</p>
) : (
@@ -147,7 +147,7 @@ export function ParameterSchemaBuilder({ schema, onChange, disabled }: Props) {
type="button"
onClick={handleAdd}
disabled={disabled}
className="flex items-center gap-1.5 text-sm text-primary hover:underline self-start"
className="flex items-center gap-1.5 text-sm text-[#22d3ee] hover:underline self-start"
>
<Plus size={14} /> Add Parameter
</button>
@@ -159,7 +159,7 @@ export function ParameterSchemaBuilder({ schema, onChange, disabled }: Props) {
onChange={e => { setJsonText(e.target.value); setJsonError(null) }}
disabled={disabled}
spellCheck={false}
className="w-full min-h-[300px] resize-y font-label text-sm bg-card border border-border rounded-xl p-4 text-foreground focus:outline-none focus:border-[rgba(6,182,212,0.3)] disabled:cursor-not-allowed disabled:opacity-50"
className="w-full min-h-[300px] resize-y font-sans text-xs text-sm bg-[#14161d] border border-[#1e2130] rounded-xl p-4 text-[#e2e5eb] focus:outline-none focus:border-[rgba(6,182,212,0.3)] disabled:cursor-not-allowed disabled:opacity-50"
placeholder='{ "parameters": [...] }'
/>
{jsonError && (

View File

@@ -16,7 +16,7 @@ export function ScriptBodyEditor({ value, onChange, disabled }: Props) {
}, [])
return (
<div className="rounded-xl border border-border overflow-hidden">
<div className="rounded-xl border border-[#1e2130] overflow-hidden">
<Editor
height="300px"
language="powershell"
@@ -25,7 +25,7 @@ export function ScriptBodyEditor({ value, onChange, disabled }: Props) {
onChange={v => onChange(v ?? '')}
beforeMount={handleBeforeMount}
loading={
<div className="flex h-[300px] items-center justify-center bg-card">
<div className="flex h-[300px] items-center justify-center bg-[#14161d]">
<Spinner size="sm" className="h-6 w-6 border-t-foreground" />
</div>
}

View File

@@ -291,7 +291,7 @@ export function ScriptTemplateEditor({ templateId, onBack, onSaved }: Props) {
if (isLoading) {
return (
<div className="flex items-center justify-center py-20">
<Loader2 size={28} className="text-primary animate-spin" />
<Loader2 size={28} className="text-[#22d3ee] animate-spin" />
</div>
)
}
@@ -302,22 +302,22 @@ export function ScriptTemplateEditor({ templateId, onBack, onSaved }: Props) {
<button
type="button"
onClick={handleBack}
className="flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground transition-colors w-fit"
className="flex items-center gap-1.5 text-xs text-[#848b9b] hover:text-[#e2e5eb] transition-colors w-fit"
>
<ArrowLeft size={12} />
Back to templates
</button>
<h1 className="text-2xl font-heading font-bold text-foreground">
<h1 className="text-2xl font-heading font-bold text-[#e2e5eb]">
{templateId ? 'Edit Template' : 'New Template'}
</h1>
{/* ── Metadata ──────────────────────────────────────────────── */}
<section className="glass-card-static p-5 space-y-4">
<p className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">Metadata</p>
<section className="card-flat p-5 space-y-4">
<p className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">Metadata</p>
<div>
<label className="text-sm font-medium text-foreground mb-1 block">
<label className="text-sm font-medium text-[#e2e5eb] mb-1 block">
Name <span className="text-red-400">*</span>
</label>
<Input
@@ -329,7 +329,7 @@ export function ScriptTemplateEditor({ templateId, onBack, onSaved }: Props) {
<div className="grid grid-cols-2 gap-4">
<div>
<label className="text-sm font-medium text-foreground mb-1 block">Description</label>
<label className="text-sm font-medium text-[#e2e5eb] mb-1 block">Description</label>
<Textarea
value={form.description}
onChange={e => updateField('description', e.target.value)}
@@ -338,7 +338,7 @@ export function ScriptTemplateEditor({ templateId, onBack, onSaved }: Props) {
/>
</div>
<div>
<label className="text-sm font-medium text-foreground mb-1 block">Use Case</label>
<label className="text-sm font-medium text-[#e2e5eb] mb-1 block">Use Case</label>
<Textarea
value={form.use_case}
onChange={e => updateField('use_case', e.target.value)}
@@ -350,13 +350,13 @@ export function ScriptTemplateEditor({ templateId, onBack, onSaved }: Props) {
<div className="grid grid-cols-3 gap-4">
<div>
<label className="text-sm font-medium text-foreground mb-1 block">
<label className="text-sm font-medium text-[#e2e5eb] mb-1 block">
Category <span className="text-red-400">*</span>
</label>
<select
value={form.category_id}
onChange={e => updateField('category_id', e.target.value)}
className="w-full rounded-[10px] border border-border bg-card text-foreground px-3 py-2 text-sm focus:outline-none focus:border-[rgba(6,182,212,0.3)]"
className="w-full rounded-lg border border-[#1e2130] bg-[#14161d] text-[#e2e5eb] px-3 py-2 text-sm focus:outline-none focus:border-[rgba(6,182,212,0.3)]"
>
<option value="">Select category</option>
{categories.map(c => (
@@ -365,11 +365,11 @@ export function ScriptTemplateEditor({ templateId, onBack, onSaved }: Props) {
</select>
</div>
<div>
<label className="text-sm font-medium text-foreground mb-1 block">Complexity</label>
<label className="text-sm font-medium text-[#e2e5eb] mb-1 block">Complexity</label>
<select
value={form.complexity}
onChange={e => updateField('complexity', e.target.value as FormState['complexity'])}
className="w-full rounded-[10px] border border-border bg-card text-foreground px-3 py-2 text-sm focus:outline-none focus:border-[rgba(6,182,212,0.3)]"
className="w-full rounded-lg border border-[#1e2130] bg-[#14161d] text-[#e2e5eb] px-3 py-2 text-sm focus:outline-none focus:border-[rgba(6,182,212,0.3)]"
>
<option value="beginner">Beginner</option>
<option value="intermediate">Intermediate</option>
@@ -377,7 +377,7 @@ export function ScriptTemplateEditor({ templateId, onBack, onSaved }: Props) {
</select>
</div>
<div>
<label className="text-sm font-medium text-foreground mb-1 block">Estimated Runtime</label>
<label className="text-sm font-medium text-[#e2e5eb] mb-1 block">Estimated Runtime</label>
<Input
value={form.estimated_runtime}
onChange={e => updateField('estimated_runtime', e.target.value)}
@@ -388,7 +388,7 @@ export function ScriptTemplateEditor({ templateId, onBack, onSaved }: Props) {
<div className="grid grid-cols-2 gap-4">
<div>
<label className="text-sm font-medium text-foreground mb-1 block">Tags (comma-separated)</label>
<label className="text-sm font-medium text-[#e2e5eb] mb-1 block">Tags (comma-separated)</label>
<Input
value={form.tags}
onChange={e => updateField('tags', e.target.value)}
@@ -396,7 +396,7 @@ export function ScriptTemplateEditor({ templateId, onBack, onSaved }: Props) {
/>
</div>
<div>
<label className="text-sm font-medium text-foreground mb-1 block">Required Modules (comma-separated)</label>
<label className="text-sm font-medium text-[#e2e5eb] mb-1 block">Required Modules (comma-separated)</label>
<Input
value={form.requires_modules}
onChange={e => updateField('requires_modules', e.target.value)}
@@ -406,41 +406,41 @@ export function ScriptTemplateEditor({ templateId, onBack, onSaved }: Props) {
</div>
<div className="flex items-center gap-6">
<label className="flex items-center gap-2 text-sm text-foreground">
<label className="flex items-center gap-2 text-sm text-[#e2e5eb]">
<input
type="checkbox"
checked={form.requires_elevation}
onChange={e => updateField('requires_elevation', e.target.checked)}
className="rounded border-border"
className="rounded border-[#1e2130]"
/>
Requires elevation (Run as Administrator)
</label>
{/* Share toggle — only for owners/admins editing an existing template */}
{templateId && template && canShareScriptTemplate && (
<label className="flex items-center gap-2 text-sm text-foreground">
<label className="flex items-center gap-2 text-sm text-[#e2e5eb]">
<input
type="checkbox"
checked={template.team_id !== null}
onChange={e => handleShare(e.target.checked)}
className="rounded border-border"
className="rounded border-[#1e2130]"
/>
Share with team
<span className="text-xs text-muted-foreground">(visible to all team members)</span>
<span className="text-xs text-[#848b9b]">(visible to all team members)</span>
</label>
)}
</div>
</section>
{/* ── Script Body ───────────────────────────────────────────── */}
<section className="glass-card-static p-5 space-y-3">
<p className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
<section className="card-flat p-5 space-y-3">
<p className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">
Script Body <span className="text-red-400">*</span>
</p>
<p className="text-xs text-muted-foreground">
Use <code className="font-label text-amber-400">{'{{param_key}}'}</code> for parameter placeholders.
Supports <code className="font-label text-amber-400">{'{% if param %} ... {% endif %}'}</code> conditionals
and filters like <code className="font-label text-amber-400">{'{{ param | as_secure_string }}'}</code>.
<p className="text-xs text-[#848b9b]">
Use <code className="font-sans text-xs text-amber-400">{'{{param_key}}'}</code> for parameter placeholders.
Supports <code className="font-sans text-xs text-amber-400">{'{% if param %} ... {% endif %}'}</code> conditionals
and filters like <code className="font-sans text-xs text-amber-400">{'{{ param | as_secure_string }}'}</code>.
</p>
<ScriptBodyEditor
value={form.script_body}
@@ -452,7 +452,7 @@ export function ScriptTemplateEditor({ templateId, onBack, onSaved }: Props) {
<button
type="button"
onClick={handleDetectParameters}
className="flex items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] hover:border-[rgba(255,255,255,0.12)] px-3 py-1.5 rounded-[10px] transition-all"
className="flex items-center gap-1.5 text-sm text-[#848b9b] hover:text-[#e2e5eb] bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] hover:border-[rgba(255,255,255,0.12)] px-3 py-1.5 rounded-lg transition-all"
>
<Scan size={14} />
Detect Parameters
@@ -460,7 +460,7 @@ export function ScriptTemplateEditor({ templateId, onBack, onSaved }: Props) {
)}
{detectionSummary && (
<p className="text-xs text-muted-foreground italic">{detectionSummary}</p>
<p className="text-xs text-[#848b9b] italic">{detectionSummary}</p>
)}
{showStepper && detectedCandidates.length > 0 && (
@@ -475,10 +475,10 @@ export function ScriptTemplateEditor({ templateId, onBack, onSaved }: Props) {
</section>
{/* ── Parameters Schema ─────────────────────────────────────── */}
<section className="glass-card-static p-5 space-y-3">
<p className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">Parameters</p>
<p className="text-xs text-muted-foreground">
Define form fields that users fill in when generating a script. Each parameter maps to a <code className="font-label text-amber-400">{'{{key}}'}</code> placeholder in the script body.
<section className="card-flat p-5 space-y-3">
<p className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">Parameters</p>
<p className="text-xs text-[#848b9b]">
Define form fields that users fill in when generating a script. Each parameter maps to a <code className="font-sans text-xs text-amber-400">{'{{key}}'}</code> placeholder in the script body.
</p>
<ParameterSchemaBuilder
schema={form.parameters_schema}
@@ -487,14 +487,14 @@ export function ScriptTemplateEditor({ templateId, onBack, onSaved }: Props) {
</section>
{/* ── Fixed Action Bar ──────────────────────────────────────── */}
<div className="fixed bottom-0 left-0 right-0 z-20 border-t border-border bg-background/80 backdrop-blur-sm px-6 py-3">
<div className="fixed bottom-0 left-0 right-0 z-20 border-t border-[#1e2130] bg-[#0c0d10]/80 px-6 py-3">
<div className="max-w-5xl mx-auto flex items-center justify-between">
<div className="flex items-center gap-3">
<button
type="button"
onClick={handleSave}
disabled={isSaving}
className="flex items-center gap-1.5 bg-gradient-brand text-[#101114] font-semibold text-sm px-5 py-2 rounded-[10px] hover:opacity-90 active:scale-[0.97] transition-all shadow-lg shadow-primary/20 disabled:opacity-50 disabled:cursor-not-allowed"
className="flex items-center gap-1.5 bg-[#22d3ee] text-white font-semibold text-sm px-5 py-2 rounded-lg hover:brightness-110 active:scale-[0.98] transition-all disabled:opacity-50 disabled:cursor-not-allowed"
>
{isSaving ? <Loader2 size={14} className="animate-spin" /> : <Save size={14} />}
{templateId ? 'Save Changes' : 'Create Template'}
@@ -502,7 +502,7 @@ export function ScriptTemplateEditor({ templateId, onBack, onSaved }: Props) {
<button
type="button"
onClick={handleBack}
className="text-sm text-muted-foreground hover:text-foreground transition-colors px-4 py-2"
className="text-sm text-[#848b9b] hover:text-[#e2e5eb] transition-colors px-4 py-2"
>
Cancel
</button>
@@ -515,14 +515,14 @@ export function ScriptTemplateEditor({ templateId, onBack, onSaved }: Props) {
<button
type="button"
onClick={handleDelete}
className="text-xs font-label text-rose-500 hover:text-rose-400 px-2 py-1"
className="text-xs font-sans text-xs text-rose-500 hover:text-rose-400 px-2 py-1"
>
Confirm
</button>
<button
type="button"
onClick={() => setDeleteConfirm(false)}
className="text-xs font-label text-muted-foreground hover:text-foreground px-2 py-1"
className="text-xs font-sans text-xs text-[#848b9b] hover:text-[#e2e5eb] px-2 py-1"
>
Cancel
</button>

View File

@@ -71,7 +71,7 @@ export function ScriptTemplateListView({ onEdit, onCreate }: Props) {
{/* Back link */}
<Link
to="/scripts"
className="flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground transition-colors w-fit"
className="flex items-center gap-1.5 text-xs text-[#848b9b] hover:text-[#e2e5eb] transition-colors w-fit"
>
<ArrowLeft size={12} />
Back to Script Library
@@ -80,8 +80,8 @@ export function ScriptTemplateListView({ onEdit, onCreate }: Props) {
{/* Header row */}
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-heading font-bold text-foreground">Manage Templates</h1>
<p className="text-sm text-muted-foreground mt-1">
<h1 className="text-2xl font-heading font-bold text-[#e2e5eb]">Manage Templates</h1>
<p className="text-sm text-[#848b9b] mt-1">
Create and edit PowerShell script templates.
</p>
</div>
@@ -89,7 +89,7 @@ export function ScriptTemplateListView({ onEdit, onCreate }: Props) {
<button
type="button"
onClick={onCreate}
className="flex items-center gap-1.5 bg-gradient-brand text-[#101114] font-semibold text-sm px-4 py-2 rounded-[10px] hover:opacity-90 active:scale-[0.97] transition-all shadow-lg shadow-primary/20"
className="flex items-center gap-1.5 bg-[#22d3ee] text-white font-semibold text-sm px-4 py-2 rounded-lg hover:brightness-110 active:scale-[0.98] transition-all"
>
<Plus size={16} />
New Template
@@ -99,70 +99,70 @@ export function ScriptTemplateListView({ onEdit, onCreate }: Props) {
{/* Search */}
<div className="relative w-64">
<Search size={14} className="absolute left-2.5 top-1/2 -translate-y-1/2 text-muted-foreground pointer-events-none" />
<Search size={14} className="absolute left-2.5 top-1/2 -translate-y-1/2 text-[#848b9b] pointer-events-none" />
<input
type="text"
value={searchQuery}
onChange={e => setSearchQuery(e.target.value)}
placeholder="Search templates…"
className="w-full pl-8 pr-3 py-1.5 text-sm rounded-md border border-border bg-card text-foreground placeholder:text-muted-foreground focus:outline-none focus:border-[rgba(6,182,212,0.3)] focus:ring-1 focus:ring-[rgba(6,182,212,0.2)]"
className="w-full pl-8 pr-3 py-1.5 text-sm rounded-md border border-[#1e2130] bg-[#14161d] text-[#e2e5eb] placeholder:text-[#848b9b] focus:outline-none focus:border-[rgba(6,182,212,0.3)] focus:ring-1 focus:ring-[rgba(6,182,212,0.2)]"
/>
</div>
{/* Template list */}
{isLoading ? (
<div className="flex items-center justify-center py-12">
<Loader2 size={28} className="text-primary animate-spin" />
<Loader2 size={28} className="text-[#22d3ee] animate-spin" />
</div>
) : templates.length === 0 ? (
<div className="glass-card-static flex flex-col items-center justify-center gap-3 py-12 text-center">
<FileCode size={32} className="text-muted-foreground/40" />
<p className="text-sm text-muted-foreground">
<div className="card-flat flex flex-col items-center justify-center gap-3 py-12 text-center">
<FileCode size={32} className="text-[#848b9b]/40" />
<p className="text-sm text-[#848b9b]">
{searchQuery ? 'No templates match your search' : 'No templates yet. Create your first one!'}
</p>
</div>
) : (
<div className="glass-card-static overflow-hidden">
<div className="card-flat overflow-hidden">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-border">
<th className="text-left font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground px-4 py-3">Name</th>
<th className="text-left font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground px-4 py-3">Category</th>
<th className="text-left font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground px-4 py-3">Complexity</th>
<th className="text-left font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground px-4 py-3">Scope</th>
<th className="text-right font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground px-4 py-3">Uses</th>
<th className="text-right font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground px-4 py-3">Actions</th>
<tr className="border-b border-[#1e2130]">
<th className="text-left font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b] px-4 py-3">Name</th>
<th className="text-left font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b] px-4 py-3">Category</th>
<th className="text-left font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b] px-4 py-3">Complexity</th>
<th className="text-left font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b] px-4 py-3">Scope</th>
<th className="text-right font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b] px-4 py-3">Uses</th>
<th className="text-right font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b] px-4 py-3">Actions</th>
</tr>
</thead>
<tbody>
{templates.map(t => (
<tr
key={t.id}
className="border-b border-border last:border-b-0 hover:bg-white/[0.02] transition-colors"
className="border-b border-[#1e2130] last:border-b-0 hover:bg-white/[0.02] transition-colors"
>
<td className="px-4 py-3">
<span className="text-foreground font-medium">{t.name}</span>
<span className="text-[#e2e5eb] font-medium">{t.name}</span>
{t.description && (
<p className="text-xs text-muted-foreground line-clamp-1 mt-0.5">{t.description}</p>
<p className="text-xs text-[#848b9b] line-clamp-1 mt-0.5">{t.description}</p>
)}
</td>
<td className="px-4 py-3 text-muted-foreground">{getCategoryName(t.category_id)}</td>
<td className="px-4 py-3 text-[#848b9b]">{getCategoryName(t.category_id)}</td>
<td className="px-4 py-3">
<span className={cn('font-label text-[0.625rem] uppercase tracking-wide px-1.5 py-0.5 rounded', COMPLEXITY_CLASSES[t.complexity])}>
<span className={cn('font-sans text-xs text-[0.625rem] uppercase tracking-wide px-1.5 py-0.5 rounded', COMPLEXITY_CLASSES[t.complexity])}>
{t.complexity}
</span>
</td>
<td className="px-4 py-3">
<span className={cn(
'inline-flex items-center gap-1 font-label text-[0.625rem] uppercase tracking-wide px-1.5 py-0.5 rounded border',
'inline-flex items-center gap-1 font-sans text-xs text-[0.625rem] uppercase tracking-wide px-1.5 py-0.5 rounded border',
t.team_id
? 'text-primary bg-primary/10 border-primary/20'
: 'text-muted-foreground bg-white/5 border-border'
? 'text-[#22d3ee] bg-[rgba(34,211,238,0.10)] border-primary/20'
: 'text-[#848b9b] bg-white/5 border-[#1e2130]'
)}>
{t.team_id ? <><Users size={10} /> Team</> : <><UserIcon size={10} /> Personal</>}
</span>
</td>
<td className="px-4 py-3 text-right text-muted-foreground">{t.usage_count}</td>
<td className="px-4 py-3 text-right text-[#848b9b]">{t.usage_count}</td>
<td className="px-4 py-3 text-right">
<div className="flex items-center justify-end gap-1">
{canManageScriptTemplate(t) && (
@@ -170,7 +170,7 @@ export function ScriptTemplateListView({ onEdit, onCreate }: Props) {
<button
type="button"
onClick={() => onEdit(t.id)}
className="p-1.5 rounded-md text-muted-foreground hover:text-foreground hover:bg-white/5 transition-colors"
className="p-1.5 rounded-md text-[#848b9b] hover:text-[#e2e5eb] hover:bg-white/5 transition-colors"
title="Edit template"
>
<Pencil size={14} />
@@ -180,14 +180,14 @@ export function ScriptTemplateListView({ onEdit, onCreate }: Props) {
<button
type="button"
onClick={() => handleDelete(t.id)}
className="text-[0.625rem] font-label text-rose-500 hover:text-rose-400 px-1.5 py-0.5"
className="text-[0.625rem] font-sans text-xs text-rose-500 hover:text-rose-400 px-1.5 py-0.5"
>
Confirm
</button>
<button
type="button"
onClick={() => setDeleteConfirm(null)}
className="text-[0.625rem] font-label text-muted-foreground hover:text-foreground px-1.5 py-0.5"
className="text-[0.625rem] font-sans text-xs text-[#848b9b] hover:text-[#e2e5eb] px-1.5 py-0.5"
>
Cancel
</button>
@@ -196,7 +196,7 @@ export function ScriptTemplateListView({ onEdit, onCreate }: Props) {
<button
type="button"
onClick={() => setDeleteConfirm(t.id)}
className="p-1.5 rounded-md text-muted-foreground hover:text-rose-500 hover:bg-white/5 transition-colors"
className="p-1.5 rounded-md text-[#848b9b] hover:text-rose-500 hover:bg-white/5 transition-colors"
title="Delete template"
>
<Trash2 size={14} />