feat(caddy): replace textarea with CodeMirror 6 editor in caddy-editor

Replaces the plain textarea on /caddy/edit with a CodeMirror 6 component
that provides live Caddyfile syntax highlighting (keywords, named matchers,
comments). The editor is dynamically imported (ssr: false) to prevent
hydration errors. The write/validate/save/reload state machine and content
flow remain unchanged.

Bundle impact: ~300 kB additional for the /caddy/edit route (CodeMirror 6
core + @uiw/react-codemirror).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Scrum4Me Agent 2026-05-13 23:39:29 +02:00
parent 97420b93cf
commit 8b72a00127
2 changed files with 38 additions and 7 deletions

View file

@ -0,0 +1,25 @@
'use client'
import CodeMirror from '@uiw/react-codemirror'
import { caddyfileLanguage } from '@/lib/codemirror/caddyfile-mode'
import { EditorView } from '@codemirror/view'
type Props = {
value: string
onChange: (next: string) => void
readOnly?: boolean
}
export default function CaddyCodeMirror({ value, onChange, readOnly }: Props) {
return (
<CodeMirror
value={value}
onChange={onChange}
readOnly={readOnly}
extensions={[caddyfileLanguage, EditorView.lineWrapping]}
theme="dark"
height="480px"
basicSetup={{ lineNumbers: true, foldGutter: false, highlightActiveLine: !readOnly }}
className="rounded-lg border border-border overflow-hidden text-xs"
/>
)
}

View file

@ -1,11 +1,21 @@
'use client'
import { useCallback, useEffect, useState } from 'react'
import dynamic from 'next/dynamic'
import Link from 'next/link'
import { useFlowRun } from '@/hooks/useFlowRun'
import ConfirmDialog from '@/components/ConfirmDialog'
import StreamingTerminal from '@/components/StreamingTerminal'
const CaddyCodeMirror = dynamic(() => import('./caddy-codemirror'), {
ssr: false,
loading: () => (
<div className="h-[480px] rounded-lg border border-border bg-zinc-950 p-4 text-xs text-zinc-500">
Loading editor
</div>
),
})
type Phase = 'edit' | 'writing' | 'validating' | 'validated' | 'saving' | 'saved'
type DialogPending = 'validate' | 'save' | null
@ -106,17 +116,13 @@ export default function CaddyEditor({ initialContent, initialError }: Props) {
</span>
)}
</div>
<textarea
<CaddyCodeMirror
value={content}
onChange={(e) => {
setContent(e.target.value)
// Reset validated state if user edits after validation
onChange={(next) => {
setContent(next)
if (phase === 'validated' || phase === 'saved') setPhase('edit')
}}
readOnly={isActive}
rows={24}
spellCheck={false}
className="w-full rounded-lg border border-border bg-zinc-950 p-4 font-mono text-xs text-zinc-100 focus:outline-none focus:ring-1 focus:ring-ring resize-y disabled:opacity-50"
/>
</div>