Add real-time Jinja2 syntax validation with debounced API check
All checks were successful
Validate / Hassfest (push) Successful in 3s
All checks were successful
Validate / Hassfest (push) Successful in 3s
Validates template syntax as user types (800ms debounce). Calls preview-raw API and shows red error text below the editor if Jinja2 parsing fails. Clears error when template is valid. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,25 @@
|
||||
let error = $state('');
|
||||
let confirmDelete = $state<any>(null);
|
||||
let slotPreview = $state<Record<string, string>>({});
|
||||
let slotErrors = $state<Record<string, string>>({});
|
||||
let validateTimers: Record<string, ReturnType<typeof setTimeout>> = {};
|
||||
|
||||
function validateSlot(slotKey: string, template: string) {
|
||||
// Clear previous timer
|
||||
if (validateTimers[slotKey]) clearTimeout(validateTimers[slotKey]);
|
||||
if (!template) { slotErrors = { ...slotErrors, [slotKey]: '' }; return; }
|
||||
|
||||
// Debounce 800ms
|
||||
validateTimers[slotKey] = setTimeout(async () => {
|
||||
try {
|
||||
const res = await api('/template-configs/preview-raw', { method: 'POST', body: JSON.stringify({ template }) });
|
||||
slotErrors = { ...slotErrors, [slotKey]: res.error || '' };
|
||||
} catch {
|
||||
// Network error, don't show as template error
|
||||
slotErrors = { ...slotErrors, [slotKey]: '' };
|
||||
}
|
||||
}, 800);
|
||||
}
|
||||
|
||||
const defaultForm = () => ({
|
||||
name: '', description: '', icon: '',
|
||||
@@ -161,7 +180,10 @@
|
||||
</div>
|
||||
</div>
|
||||
{#if (slot.rows || 2) > 2}
|
||||
<JinjaEditor value={(form as any)[slot.key] || ''} onchange={(v) => (form as any)[slot.key] = v} rows={slot.rows || 6} />
|
||||
<JinjaEditor value={(form as any)[slot.key] || ''} onchange={(v) => { (form as any)[slot.key] = v; validateSlot(slot.key, v); }} rows={slot.rows || 6} />
|
||||
{#if slotErrors[slot.key]}
|
||||
<p class="mt-1 text-xs" style="color: var(--color-error-fg);">Syntax error: {slotErrors[slot.key]}</p>
|
||||
{/if}
|
||||
{#if slotPreview[slot.key]}
|
||||
<div class="mt-1 p-2 bg-[var(--color-muted)] rounded text-sm">
|
||||
<pre class="whitespace-pre-wrap">{slotPreview[slot.key]}</pre>
|
||||
@@ -197,15 +219,11 @@
|
||||
{#if config.icon}<MdiIcon name={config.icon} />{/if}
|
||||
<p class="font-medium">{config.name}</p>
|
||||
</div>
|
||||
<pre class="text-xs text-[var(--color-muted-foreground)] mt-1 whitespace-pre-wrap font-mono bg-[var(--color-muted)] rounded p-2">{config.message_assets_added?.slice(0, 120)}...</pre>
|
||||
{#if slotPreview['message_assets_added_' + config.id]}
|
||||
<div class="mt-2 p-2 bg-[var(--color-success-bg)] rounded text-sm">
|
||||
<pre class="whitespace-pre-wrap">{slotPreview['message_assets_added_' + config.id]}</pre>
|
||||
</div>
|
||||
{#if config.description}
|
||||
<p class="text-sm text-[var(--color-muted-foreground)] mt-1">{config.description}</p>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="flex items-center gap-1 ml-4">
|
||||
<IconButton icon="mdiEye" title={t('templateConfig.preview')} onclick={() => preview(config.id, 'message_assets_added')} />
|
||||
<IconButton icon="mdiPencil" title={t('common.edit')} onclick={() => edit(config)} />
|
||||
<IconButton icon="mdiDelete" title={t('common.delete')} onclick={() => remove(config.id)} variant="danger" />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user