diff --git a/AutoCR/js/overview.js b/AutoCR/js/overview.js
index e7162ca..2bb7b51 100644
--- a/AutoCR/js/overview.js
+++ b/AutoCR/js/overview.js
@@ -1620,6 +1620,7 @@ function RichPresenceOverview()
+
>);
}
diff --git a/AutoCR/js/rp-preview.js b/AutoCR/js/rp-preview.js
new file mode 100644
index 0000000..3e7b0a4
--- /dev/null
+++ b/AutoCR/js/rp-preview.js
@@ -0,0 +1,114 @@
+function RichPresencePreview()
+{
+ // ── Placeholder labels for numeric / time macros ──────────────────────────
+
+ function macroPlaceholder(fmt)
+ {
+ if (!fmt) return '(CHAR)'; // null = ASCIIChar / UnicodeChar
+
+ if (fmt.category === 'time') return '(TIME)';
+
+ switch (fmt.type)
+ {
+ case 'VALUE': return '(INT)';
+ case 'UNSIGNED': return '(UINT)';
+ case 'SCORE':
+ case 'POINTS': return '(SCORE)';
+ case 'TENS': return '(INT0)';
+ case 'HUNDREDS': return '(INT00)';
+ case 'THOUSANDS': return '(INT000)';
+ case 'FIXED1': return '(DEC.1)';
+ case 'FIXED2': return '(DEC.2)';
+ case 'FIXED3': return '(DEC.3)';
+ default: return '(' + fmt.name + ')'; // Float1–Float6 etc.
+ }
+ }
+
+ // ── Resolve one display string ────────────────────────────────────────────
+
+ function resolveRPString(str)
+ {
+ return str.replace(/@([ _a-zA-Z][ _a-zA-Z0-9]*)\((.+?)\)/g, (match, rawName) =>
+ {
+ const name = rawName.trim();
+
+ // Lookup table?
+ if (current.rp.lookups.has(name))
+ {
+ const ranges = current.rp.lookups.get(name);
+ // Prefer non-fallback (*) entries so we show real label text.
+ const pool = ranges.filter(r => !r.isFallback());
+ const source = pool.length ? pool : ranges;
+ return (source[Math.floor(Math.random() * source.length)]?.value ?? '');
+ }
+
+ // Known macro (built-in or declared Format:)?
+ if (Object.prototype.hasOwnProperty.call(current.rp.macros, name))
+ return macroPlaceholder(current.rp.macros[name]);
+
+ // Completely unknown — leave raw so it's obvious.
+ return match;
+ });
+ }
+
+ // ── Partition display entries into dynamic vs. static ─────────────────────
+ // Static strings are fixed — they show exactly once and are never rerolled.
+
+ const MACRO_RE = /@[ _a-zA-Z][ _a-zA-Z0-9]*\(.+?\)/;
+
+ const display = current.rp ? current.rp.display : [];
+ const dynamicPool = display.filter(d => MACRO_RE.test(d.string));
+ const staticLines = display.filter(d => !MACRO_RE.test(d.string)).map(d => d.string);
+
+ // ── Generate 10 lines from the dynamic pool ───────────────────────────────
+
+ function generateLines()
+ {
+ if (!dynamicPool.length) return [];
+ return Array.from({ length: 10 }, () => {
+ const entry = dynamicPool[Math.floor(Math.random() * dynamicPool.length)];
+ return resolveRPString(entry.string);
+ });
+ }
+
+ // ── Component state ───────────────────────────────────────────────────────
+
+ const [lines, setLines] = React.useState(() => generateLines());
+
+ if (!current.rp || !display.length) return null;
+
+ function PreviewLine({ text })
+ {
+ return text
+ ? <>{text}>
+ : (empty string);
+ }
+
+ return (
+
+
Preview
+ {staticLines.length > 0 && (
+
+
+ Static Lines ({staticLines.length})
+
+
+ {staticLines.map((line, i) => (
+ -
+
+
+ ))}
+
+
+ )}
+ {dynamicPool.length > 0 && <>
+
+ {lines.map((line, i) => (
+
+ ))}
+
+
+ >}
+
+ );
+}