This commit is contained in:
Max Richter
2025-09-26 12:42:06 +02:00
parent deae5acac8
commit ae5cd8481a
17 changed files with 649 additions and 233 deletions

View File

@@ -1,18 +1,20 @@
<script lang="ts">
import type { ParseResult } from '../wasm';
import { json } from '@codemirror/lang-json';
import { markdown } from '@codemirror/lang-markdown';
import {
getTemplate,
listTemplates,
matchBlocks,
parseMarkdown,
parseMarkdownWithTemplate,
wasmReady
wasmReady,
type ParseResultSuccess
} from '../wasm';
import EditorPanel from './EditorPanel.svelte';
let templates = $state([] as string[]);
let templateValue = $state('');
let markdownValue = $state(`---
const DEFAULT_MARKDOWN_VALUE = `---
_type: Recipe
author.name: Max Richter
---
@@ -28,20 +30,45 @@ My favourite baguette recipe
## Steps
1. Mix Flour Water and Salt
2. Bake the bread`);
2. Bake the bread`;
const DEFAULT_TEMPLATE_VALUE = '';
let templateValue = $state(
typeof window !== 'undefined'
? localStorage.getItem('templateValue') || DEFAULT_TEMPLATE_VALUE
: DEFAULT_TEMPLATE_VALUE
);
let markdownValue = $state(
typeof window !== 'undefined'
? localStorage.getItem('markdownValue') || DEFAULT_MARKDOWN_VALUE
: DEFAULT_MARKDOWN_VALUE
);
let jsonOutput = $state('');
let detectedSchemaName = $derived.by(() => {
try {
const parsed = JSON.parse(jsonOutput);
return parsed['_schema'];
} catch (err) {
return JSON.parse(jsonOutput)['_schema'];
} catch {
return undefined;
}
});
let timings = $state<ParseResult['timings'] | null>(null);
let status = $state<'success' | 'error' | undefined>(undefined);
let timings = $state<ParseResultSuccess['timings'] | null>(null);
let templateStatus = $state<'success' | 'error' | 'indeterminate' | undefined>(undefined);
let dataStatus = $state<'success' | 'error' | 'indeterminate' | undefined>(undefined);
$effect(() => {
if (typeof window !== 'undefined') {
localStorage.setItem('templateValue', templateValue);
}
});
$effect(() => {
if (typeof window !== 'undefined') {
localStorage.setItem('markdownValue', markdownValue);
}
});
$effect(() => {
if ($wasmReady) {
@@ -54,20 +81,40 @@ My favourite baguette recipe
if (!$wasmReady) {
jsonOutput = 'Loading wasm...';
timings = null;
status = undefined;
templateStatus = undefined;
dataStatus = undefined;
return;
}
try {
const result = templateValue
? parseMarkdownWithTemplate(markdownValue, templateValue)
: parseMarkdown(markdownValue);
jsonOutput = JSON.stringify(result.data, null, 2);
timings = result.timings;
status = 'success';
if ('error' in result) {
jsonOutput = result.error;
if (result.error.startsWith('failed to compile template')) {
templateStatus = 'error';
dataStatus = 'indeterminate';
} else {
templateStatus = undefined;
dataStatus = 'error';
}
} else {
jsonOutput = JSON.stringify(result.data, null, 2);
timings = result.timings;
templateStatus = 'success';
dataStatus = 'success';
}
} catch (e: unknown) {
jsonOutput = (e as Error).message;
timings = null;
status = 'error';
if (jsonOutput.startsWith('failed to compile template')) {
templateStatus = 'error';
dataStatus = 'indeterminate';
} else {
templateStatus = undefined;
dataStatus = 'error';
}
}
});
@@ -79,47 +126,64 @@ My favourite baguette recipe
console.error(e);
}
}
function resetMarkdown() {
markdownValue = DEFAULT_MARKDOWN_VALUE;
}
</script>
<div class="grid min-h-0 flex-1 grid-cols-1 lg:grid-cols-3">
<EditorPanel
title="Template"
bind:value={templateValue}
placeholder="Enter your Marka template here..."
{status}
timing={timings?.template_compilation}
subtitle="Define your mapping schema"
>
{#snippet headerActions()}
<select
onchange={(e) => loadTemplate(e.currentTarget.value)}
class="rounded border border-gray-300 bg-white px-2 py-1 text-xs text-gray-700 shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500"
>
<option value="">Load a template</option>
{#each templates as template (template)}
<option value={template}>{template}</option>
{/each}
</select>
{/snippet}
</EditorPanel>
<div class="flex flex-1 overflow-hidden">
<div class="grid flex-1 grid-cols-1 overflow-hidden lg:grid-cols-3">
<EditorPanel
title="Template"
bind:value={templateValue}
placeholder="Enter your Marka template here..."
status={templateStatus}
timing={timings?.template_compilation}
subtitle="Define your mapping schema"
langExtension={markdown()}
>
{#snippet headerActions()}
<select
onchange={(e) => loadTemplate(e.currentTarget.value)}
class="rounded border border-gray-300 bg-white px-2 py-1 text-xs text-gray-700 shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500"
>
<option value="">Load a template</option>
{#each templates as template (template)}
<option value={template}>{template}</option>
{/each}
</select>
{/snippet}
</EditorPanel>
<EditorPanel
title="Markdown"
bind:value={markdownValue}
placeholder="Enter your markdown content here..."
{status}
timing={timings?.markdown_parsing}
subtitle="Your source content"
/>
<EditorPanel
title="Markdown"
bind:value={markdownValue}
placeholder="Enter your markdown content here..."
timing={timings?.markdown_parsing}
subtitle="Your source content"
langExtension={markdown()}
>
{#snippet headerActions()}
<button
onclick={resetMarkdown}
class="rounded border border-gray-300 bg-white px-2 py-1 text-xs text-gray-700 shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500"
>
Reset
</button>
{/snippet}
</EditorPanel>
<EditorPanel
title="Data"
value={jsonOutput}
readonly={true}
{status}
subtitle="Parsed JSON output"
pillText={!templateValue && detectedSchemaName
? `Detected Template: ${detectedSchemaName}`
: undefined}
/>
<EditorPanel
title="Data"
value={jsonOutput}
readonly={true}
status={dataStatus}
subtitle="Parsed JSON output"
pillText={!templateValue && detectedSchemaName
? `Detected Template: ${detectedSchemaName}`
: undefined}
langExtension={json()}
/>
</div>
</div>