candidates and board ui agent issue
Some checks failed
CI / check (push) Failing after 4m58s

This commit is contained in:
2026-09-05 10:46:06 +05:30
parent e02a0c23d4
commit 6249e00a3a
78 changed files with 15071 additions and 1998 deletions

View File

@@ -22,10 +22,42 @@ import { usePageAction } from './PageContext';
* snapshot, and settled blocks must not re-render with it.
*/
const INLINE = /(\*\*[^*]+\*\*|_[^_]+_)/g;
const INLINE = /(\*\*[^*]+\*\*|_[^_]+_|\[[^\]\n]+\]\([^)\s]*\))/g;
const LINK = /^\[([^\]\n]+)\]\(([^)\s]*)\)$/;
/** Inline `**bold**` and `_italic_`. Kept deliberately small — structure is
* carried by blocks, not by markup inside a paragraph. */
/**
* Where a link in an answer is allowed to point.
*
* An allow-list, and it is a security boundary rather than a tidiness rule: the
* text being parsed here was written by a model, and a model reading a document
* that says "link to javascript:…" is exactly the injection §I7 of CLAUDE.md
* calls untrusted input. Anything not on this list renders as the plain text it
* came from — visible, inert, and obvious.
*
* `#` alone is deliberately absent. A bare empty anchor is the shape the
* citation sanitiser removes; one that reaches here is a link to nowhere, and
* showing its label as text is better than an anchor that does nothing.
*/
const isSafeHref = (href) => (
/^\/(?!\/)/.test(href) // in-app route
|| /^#[^\s]+$/.test(href) // an anchor on this page, but not a bare '#'
|| /^https?:\/\//i.test(href) // the open web
|| /^mailto:[^\s]+$/i.test(href)
);
/**
* Inline `**bold**`, `_italic_` and `[label](href)`.
*
* Links were the gap: `markdownToBlocks` never touched them, and this renderer
* had no case for them, so `[staffing policy](#staffing)` reached the reader as
* its own source. Structure is still carried by blocks rather than by markup —
* this stays three constructs, not a markdown library.
*
* A citation-shaped link never arrives here at all. `[337042b3](#)` is removed
* upstream in `provider.js`, by the wrapper's shape, before a block is built —
* so the two concerns stay apart: the sanitiser decides what is an internal
* reference, and this decides how a real link looks.
*/
function Inline({ value }) {
const parts = React.useMemo(() => String(value).split(INLINE).filter(Boolean), [value]);
@@ -36,6 +68,33 @@ function Inline({ value }) {
if (part.startsWith('_') && part.endsWith('_')) {
return <em key={i} className="text-ink-3">{part.slice(1, -1)}</em>;
}
const link = LINK.exec(part);
if (link) {
const [, label, href] = link;
if (!isSafeHref(href)) return part;
/* An in-app route goes through the router, like every other internal link
in this file — a full page load would throw away the conversation the
reader is being pointed away from. Everything else is an anchor, and
anything leaving the app opens away from it. */
const external = /^https?:\/\//i.test(href);
const className = 'font-medium text-krow-blue underline decoration-krow-blue/30 underline-offset-2'
+ ' transition-colors hover:decoration-krow-blue focus-visible:outline-none'
+ ' focus-visible:ring-2 focus-visible:ring-krow-blue/50 rounded-sm';
if (href.startsWith('/')) return <Link key={i} to={href} className={className}>{label}</Link>;
return (
<a
key={i}
href={href}
className={className}
{...(external ? { target: '_blank', rel: 'noreferrer noopener' } : null)}
>
{label}
</a>
);
}
return part;
});
}