feat: add URL-driven workspace state and streamed Codex research

This commit is contained in:
2026-09-28 20:08:28 +09:00
parent 85d23a328b
commit 8085ad2f90
207 changed files with 15287 additions and 16026 deletions
+25 -30
View File
@@ -1,49 +1,44 @@
import type { ReactNode } from 'react'
import type { ReactNode } from "react";
const URL = /(https?:\/\/[^\s]+)/g
const HTML_ENTITY = /&(?:#(\d+)|#x([\da-f]+)|amp|apos|gt|lt|quot);/gi
const URL = /(https?:\/\/[^\s]+)/g;
const HTML_ENTITY = /&(?:#(\d+)|#x([\da-f]+)|amp|apos|gt|lt|quot);/gi;
const NAMED_ENTITIES: Record<string, string> = {
'&amp;': '&',
'&apos;': "'",
'&gt;': '>',
'&lt;': '<',
'&quot;': '"',
}
"&amp;": "&",
"&apos;": "'",
"&gt;": ">",
"&lt;": "<",
"&quot;": '"',
};
function decodeHtmlEntities(text: string): string {
return text.replace(HTML_ENTITY, (entity, decimal, hexadecimal) => {
if (!decimal && !hexadecimal) {
return NAMED_ENTITIES[entity.toLowerCase()] ?? entity
return NAMED_ENTITIES[entity.toLowerCase()] ?? entity;
}
const codePoint = Number.parseInt(decimal ?? hexadecimal, decimal ? 10 : 16)
return codePoint <= 0x10ffff ? String.fromCodePoint(codePoint) : entity
})
const codePoint = Number.parseInt(decimal ?? hexadecimal, decimal ? 10 : 16);
return codePoint <= 0x10ffff ? String.fromCodePoint(codePoint) : entity;
});
}
function linkedText(text: string): ReactNode[] {
const decoded = decodeHtmlEntities(text)
const parts: ReactNode[] = []
let cursor = 0
const decoded = decodeHtmlEntities(text);
const parts: ReactNode[] = [];
let cursor = 0;
for (const match of decoded.matchAll(URL)) {
const href = match[0]
const start = match.index
if (start > cursor) parts.push(decoded.slice(cursor, start))
const href = match[0];
const start = match.index;
if (start > cursor) parts.push(decoded.slice(cursor, start));
parts.push(
<a
href={href}
key={`link-${start}`}
rel="noreferrer noopener"
target="_blank"
>
<a href={href} key={`link-${start}`} rel="noreferrer noopener" target="_blank">
{href}
</a>,
)
cursor = start + href.length
);
cursor = start + href.length;
}
if (cursor < decoded.length) parts.push(decoded.slice(cursor))
return parts
if (cursor < decoded.length) parts.push(decoded.slice(cursor));
return parts;
}
export function PostText({ text }: { text: string }) {
return <>{linkedText(text)}</>
return <>{linkedText(text)}</>;
}