feat: parse :::writing block metadata and use the subject as the block title (#28280)

Newer OpenAI chat models put metadata on the opening line of a colon fence block, like :::writing{variant="email" id="48173" subject="Short question" recipient="mail@example.com"}. The tokenizer matched that line and discarded it, so every block rendered under the same generic "Writing" heading no matter what it contained.

The opening line is now parsed into an attributes map on the token and the header uses it: the subject becomes the title, the recipient follows it and the full string is reachable on hover when the row is too narrow for it. Blocks without metadata render exactly as before, and the other fence types get the parsed attributes for free.

Attributes are read only from inside the {...} braces, not from the whole opening line. Scanning the whole line turned ordinary prose containing key="value" into metadata, and it backtracked quadratically: a 40k character opening line took 586ms to parse, and that runs again on every re-lex while the message streams. Anchored to the braces it is 0.0ms.

Nothing here turns the recipient into a link or a send action. That metadata is model output and can be steered by whatever is in the context, so a prefilled mail action is a separate decision rather than a side effect of parsing.
This commit is contained in:
Classic298
2026-08-10 19:44:06 -06:00
committed by GitHub
parent 385d08bea5
commit eff5c4a2d9
3 changed files with 95 additions and 10 deletions
@@ -18,9 +18,12 @@
export let onTaskClick: Function = () => {};
export let onSourceClick: Function = () => {};
const fenceType: string = token.fenceType ?? 'default';
$: fenceType = token.fenceType ?? 'default';
$: attributes = token.attributes ?? {};
const label = fenceType.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
$: label =
attributes.subject || fenceType.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
$: header = attributes.recipient ? `${label} · ${attributes.recipient}` : label;
let copied = false;
@@ -34,13 +37,16 @@
</script>
<div class="relative group my-2 rounded-2xl border border-gray-100 dark:border-gray-800 px-4 py-3">
<!-- Header row: type badge + copy button -->
<div class="flex items-center justify-between mb-2">
<span class="text-xs font-normal text-gray-500 dark:text-gray-400">
{label}
<div class="flex items-center justify-between gap-2 mb-2">
<span
class="text-xs font-normal text-gray-500 dark:text-gray-400 truncate"
dir="auto"
title={header}
>
{header}
</span>
<div class="invisible group-hover:visible flex gap-0.5">
<div class="invisible group-hover:visible flex gap-0.5 shrink-0">
<Tooltip content={copied ? $i18n.t('Copied') : $i18n.t('Copy')}>
<button
class="p-1 rounded-lg bg-transparent hover:bg-black/5 dark:hover:bg-white/5 transition"