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:
parent
97420b93cf
commit
8b72a00127
2 changed files with 38 additions and 7 deletions
25
app/caddy/_components/caddy-codemirror.tsx
Normal file
25
app/caddy/_components/caddy-codemirror.tsx
Normal 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"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,21 @@
|
||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useCallback, useEffect, useState } from 'react'
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
|
import dynamic from 'next/dynamic'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { useFlowRun } from '@/hooks/useFlowRun'
|
import { useFlowRun } from '@/hooks/useFlowRun'
|
||||||
import ConfirmDialog from '@/components/ConfirmDialog'
|
import ConfirmDialog from '@/components/ConfirmDialog'
|
||||||
import StreamingTerminal from '@/components/StreamingTerminal'
|
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 Phase = 'edit' | 'writing' | 'validating' | 'validated' | 'saving' | 'saved'
|
||||||
|
|
||||||
type DialogPending = 'validate' | 'save' | null
|
type DialogPending = 'validate' | 'save' | null
|
||||||
|
|
@ -106,17 +116,13 @@ export default function CaddyEditor({ initialContent, initialError }: Props) {
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<textarea
|
<CaddyCodeMirror
|
||||||
value={content}
|
value={content}
|
||||||
onChange={(e) => {
|
onChange={(next) => {
|
||||||
setContent(e.target.value)
|
setContent(next)
|
||||||
// Reset validated state if user edits after validation
|
|
||||||
if (phase === 'validated' || phase === 'saved') setPhase('edit')
|
if (phase === 'validated' || phase === 'saved') setPhase('edit')
|
||||||
}}
|
}}
|
||||||
readOnly={isActive}
|
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>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue