feat: preview
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { useEditorStore } from "@/stores/editorStore";
|
||||
import { renderMicron } from "./micronRenderer";
|
||||
import { cn } from "@/lib/utils";
|
||||
@@ -62,9 +61,9 @@ export default function PreviewPane() {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<ScrollArea className="flex-1 bg-background">
|
||||
<div className="flex-1 bg-background overflow-auto">
|
||||
{previewMode === "ascii" ? (
|
||||
<pre className="p-4 font-mono text-sm whitespace-pre leading-tight text-green-100/90">
|
||||
<pre className="p-2 font-mono text-[11px] whitespace-pre leading-tight text-green-100/90">
|
||||
{compiledAscii || (
|
||||
<span className="text-muted-foreground">
|
||||
ASCII preview will appear here…
|
||||
@@ -74,7 +73,7 @@ export default function PreviewPane() {
|
||||
) : previewMode === "micron" ? (
|
||||
compiledMicron ? (
|
||||
<div
|
||||
className="p-4 font-mono text-sm whitespace-pre leading-tight"
|
||||
className="p-2 font-mono text-[11px] whitespace-pre leading-tight"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: renderMicron(compiledMicron),
|
||||
}}
|
||||
@@ -91,11 +90,11 @@ export default function PreviewPane() {
|
||||
{compiledScript || "No dynamic script generated."}
|
||||
</pre>
|
||||
) : (
|
||||
<pre className="p-4 font-mono text-sm whitespace-pre-wrap break-words text-muted-foreground">
|
||||
<pre className="p-4 font-mono text-xs whitespace-pre-wrap break-words text-muted-foreground">
|
||||
{compiledMicron || "Raw Micron output will appear here…"}
|
||||
</pre>
|
||||
)}
|
||||
</ScrollArea>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -15,59 +15,82 @@ function escapeHtml(text: string): string {
|
||||
function renderInline(raw: string): string {
|
||||
let out = "";
|
||||
let i = 0;
|
||||
const openTags: string[] = [];
|
||||
|
||||
const closeAll = () => {
|
||||
while (openTags.length) out += openTags.pop()!;
|
||||
};
|
||||
// Track open/close state for toggle-style tags
|
||||
let boldOpen = false;
|
||||
let italicOpen = false;
|
||||
let underOpen = false;
|
||||
let fgOpen = false;
|
||||
let bgOpen = false;
|
||||
let alignOpen = false;
|
||||
|
||||
while (i < raw.length) {
|
||||
// Backtick formatting codes
|
||||
if (raw[i] === "`") {
|
||||
const code = raw[i + 1];
|
||||
if (code === "!") {
|
||||
out += "<strong>"; openTags.push("</strong>"); i += 2; continue;
|
||||
if (boldOpen) { out += "</strong>"; boldOpen = false; }
|
||||
else { out += "<strong>"; boldOpen = true; }
|
||||
i += 2; continue;
|
||||
} else if (code === "*") {
|
||||
out += "<em>"; openTags.push("</em>"); i += 2; continue;
|
||||
if (italicOpen) { out += "</em>"; italicOpen = false; }
|
||||
else { out += "<em>"; italicOpen = true; }
|
||||
i += 2; continue;
|
||||
} else if (code === "_") {
|
||||
out += "<u>"; openTags.push("</u>"); i += 2; continue;
|
||||
if (underOpen) { out += "</u>"; underOpen = false; }
|
||||
else { out += "<u>"; underOpen = true; }
|
||||
i += 2; continue;
|
||||
} else if (code === "`") {
|
||||
closeAll(); i += 2; continue;
|
||||
} else if (code === "f" || code === "b") {
|
||||
out += "</span>"; i += 2; continue;
|
||||
// Reset all
|
||||
if (boldOpen) { out += "</strong>"; boldOpen = false; }
|
||||
if (italicOpen) { out += "</em>"; italicOpen = false; }
|
||||
if (underOpen) { out += "</u>"; underOpen = false; }
|
||||
if (fgOpen) { out += "</span>"; fgOpen = false; }
|
||||
if (bgOpen) { out += "</span>"; bgOpen = false; }
|
||||
if (alignOpen) { out += "</span>"; alignOpen = false; }
|
||||
i += 2; continue;
|
||||
} else if (code === "f") {
|
||||
if (fgOpen) { out += "</span>"; fgOpen = false; }
|
||||
i += 2; continue;
|
||||
} else if (code === "b") {
|
||||
if (bgOpen) { out += "</span>"; bgOpen = false; }
|
||||
i += 2; continue;
|
||||
} else if (code === "a") {
|
||||
out += "</span>"; i += 2; continue;
|
||||
if (alignOpen) { out += "</span>"; alignOpen = false; }
|
||||
i += 2; continue;
|
||||
} else if (code === "F") {
|
||||
// Foreground color — 3-digit hex only per spec
|
||||
const hexMatch = raw.slice(i + 2).match(/^([0-9a-fA-F]{3})(?![0-9a-fA-F])/);
|
||||
const hexMatch = raw.slice(i + 2).match(/^([0-9a-fA-F]{3})/);
|
||||
if (hexMatch) {
|
||||
if (fgOpen) { out += "</span>"; }
|
||||
const [r, g, b] = hexMatch[1].split("");
|
||||
const hex = r + r + g + g + b + b;
|
||||
out += `<span style="color:#${hex}">`;
|
||||
openTags.push("</span>");
|
||||
fgOpen = true;
|
||||
i += 2 + hexMatch[1].length;
|
||||
continue;
|
||||
}
|
||||
} else if (code === "B") {
|
||||
// Background color — 3-digit hex only per spec
|
||||
const hexMatch = raw.slice(i + 2).match(/^([0-9a-fA-F]{3})(?![0-9a-fA-F])/);
|
||||
const hexMatch = raw.slice(i + 2).match(/^([0-9a-fA-F]{3})/);
|
||||
if (hexMatch) {
|
||||
if (bgOpen) { out += "</span>"; }
|
||||
const [r, g, b] = hexMatch[1].split("");
|
||||
const hex = r + r + g + g + b + b;
|
||||
out += `<span style="background:#${hex}">`;
|
||||
openTags.push("</span>");
|
||||
bgOpen = true;
|
||||
i += 2 + hexMatch[1].length;
|
||||
continue;
|
||||
}
|
||||
} else if (code === "c") {
|
||||
out += `<span style="display:block;text-align:center">`;
|
||||
openTags.push("</span>"); i += 2; continue;
|
||||
alignOpen = true; i += 2; continue;
|
||||
} else if (code === "r") {
|
||||
out += `<span style="display:block;text-align:right">`;
|
||||
openTags.push("</span>"); i += 2; continue;
|
||||
alignOpen = true; i += 2; continue;
|
||||
} else if (code === "l") {
|
||||
out += `<span style="display:block;text-align:left">`;
|
||||
openTags.push("</span>"); i += 2; continue;
|
||||
alignOpen = true; i += 2; continue;
|
||||
} else if (code === "<") {
|
||||
// Form element: `<...> or `<!...> or `<?...> or `<^...>
|
||||
const closeAngle = raw.indexOf(">", i + 2);
|
||||
@@ -116,7 +139,13 @@ function renderInline(raw: string): string {
|
||||
i++;
|
||||
}
|
||||
|
||||
closeAll();
|
||||
// Close any remaining open tags
|
||||
if (boldOpen) out += "</strong>";
|
||||
if (italicOpen) out += "</em>";
|
||||
if (underOpen) out += "</u>";
|
||||
if (fgOpen) out += "</span>";
|
||||
if (bgOpen) out += "</span>";
|
||||
if (alignOpen) out += "</span>";
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
@@ -127,4 +127,34 @@
|
||||
html {
|
||||
@apply font-sans;
|
||||
}
|
||||
|
||||
/* Dark-mode scrollbars */
|
||||
* {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: oklch(0.35 0 0) transparent;
|
||||
}
|
||||
*::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
*::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
*::-webkit-scrollbar-thumb {
|
||||
background: oklch(0.35 0 0);
|
||||
border-radius: 3px;
|
||||
}
|
||||
*::-webkit-scrollbar-thumb:hover {
|
||||
background: oklch(0.45 0 0);
|
||||
}
|
||||
*::-webkit-scrollbar-corner {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.dark *::-webkit-scrollbar-thumb {
|
||||
background: oklch(0.3 0 0);
|
||||
}
|
||||
.dark *::-webkit-scrollbar-thumb:hover {
|
||||
background: oklch(0.4 0 0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user