chore: exclude local resource artifacts from main sync
This commit is contained in:
@@ -2,12 +2,16 @@ import type { ChatMessagePart } from './types';
|
||||
|
||||
const LINK_CARD_LINE_PATTERN = /^\s*\[\[link-card:(.+?)\]\]\s*$/i;
|
||||
const PROMPT_LINE_PATTERN = /^\s*\[\[prompt:(.+?)\]\]\s*$/i;
|
||||
const STANDALONE_MARKDOWN_LINK_LINE_PATTERN = /^\s*(?:[-*+]\s+|\d+\.\s+)?\[([^\]]+)\]\(([^)]+)\)\s*$/;
|
||||
const STANDALONE_URL_LINE_PATTERN = /^\s*(?:[-*+]\s+|\d+\.\s+)?((?:https?:\/\/|\/)[^\s<>)\]]+)\s*$/i;
|
||||
const PROMPT_BLOCK_START_PATTERN = /^\s*\[\[prompt:\s*$/i;
|
||||
const PROMPT_BLOCK_END_PATTERN = /^\s*\]\]\s*$/;
|
||||
const PROMPT_CODE_BLOCK_START_PATTERN = /^\s*```(?:json|prompt)(?:\s+prompt)?\s*$/i;
|
||||
const CODE_BLOCK_END_PATTERN = /^\s*```\s*$/;
|
||||
const RESOURCE_PATH_PREFIXES = ['/api/chat/resources/', '/public/.codex_chat/', '/.codex_chat/'] as const;
|
||||
const CHAT_API_RESOURCE_MARKER = '/api/chat/resources/';
|
||||
const CHAT_DOT_CODEX_MARKER = '/.codex_chat/';
|
||||
const CHAT_PUBLIC_DOT_CODEX_MARKER = '/public/.codex_chat/';
|
||||
const RESOURCE_MANAGER_PREVIEW_MARKER = '/api/resource-manager/preview/';
|
||||
const RESOURCE_MANAGER_ROOT_MARKER = 'resource/';
|
||||
type PromptPart = Extract<ChatMessagePart, { type: 'prompt' }>;
|
||||
type PromptOption = PromptPart['options'][number];
|
||||
type PromptPreview = NonNullable<PromptOption['preview']>;
|
||||
@@ -17,6 +21,65 @@ function normalizeText(value: unknown) {
|
||||
return String(value ?? '').trim();
|
||||
}
|
||||
|
||||
function normalizeResourceManagerPathSegment(segment: string) {
|
||||
const normalized = normalizeText(segment);
|
||||
|
||||
if (!normalized) {
|
||||
return '';
|
||||
}
|
||||
|
||||
try {
|
||||
return encodeURIComponent(decodeURIComponent(normalized));
|
||||
} catch {
|
||||
return encodeURIComponent(normalized);
|
||||
}
|
||||
}
|
||||
|
||||
function buildResourceManagerPreviewUrl(value: string) {
|
||||
const normalized = normalizeText(value).replace(/\\/g, '/');
|
||||
const matchedResourcePath = normalized.match(/(?:^|\/)(resource\/.+)$/i)?.[1];
|
||||
const resourcePath = normalizeText(matchedResourcePath).replace(/^\/+/, '');
|
||||
|
||||
if (!resourcePath) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const relativePath = resourcePath.slice(RESOURCE_MANAGER_ROOT_MARKER.length).replace(/^\/+/, '');
|
||||
|
||||
if (!relativePath) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const encodedPath = relativePath
|
||||
.split('/')
|
||||
.filter(Boolean)
|
||||
.map((segment) => normalizeResourceManagerPathSegment(segment))
|
||||
.join('/');
|
||||
|
||||
return encodedPath ? `${RESOURCE_MANAGER_PREVIEW_MARKER}${encodedPath}` : '';
|
||||
}
|
||||
|
||||
function extractKnownPreviewPath(value: string) {
|
||||
const normalized = normalizeText(value);
|
||||
|
||||
if (!normalized) {
|
||||
return '';
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = new URL(normalized);
|
||||
const pathname = `${parsed.pathname || ''}${parsed.search || ''}${parsed.hash || ''}`;
|
||||
|
||||
if (pathname.startsWith(CHAT_API_RESOURCE_MARKER) || pathname.startsWith(RESOURCE_MANAGER_PREVIEW_MARKER)) {
|
||||
return pathname;
|
||||
}
|
||||
|
||||
return normalized;
|
||||
} catch {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeUrl(value: string) {
|
||||
const normalized = normalizeText(value);
|
||||
|
||||
@@ -24,6 +87,12 @@ function normalizeUrl(value: string) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const knownPreviewPath = extractKnownPreviewPath(normalized);
|
||||
|
||||
if (knownPreviewPath) {
|
||||
return knownPreviewPath;
|
||||
}
|
||||
|
||||
const malformedResourceMatch = normalized.match(/^https?:\/(api\/chat\/resources\/.+)$/i);
|
||||
if (malformedResourceMatch?.[1]) {
|
||||
return `/${malformedResourceMatch[1]}`;
|
||||
@@ -48,6 +117,14 @@ function normalizeUrl(value: string) {
|
||||
return `${CHAT_API_RESOURCE_MARKER}${normalized.slice(dotCodexIndex + 1)}`;
|
||||
}
|
||||
|
||||
if (normalized === 'resource' || normalized === '/resource' || normalized.startsWith('resource/') || normalized.includes('/resource/')) {
|
||||
const resourceManagerPreviewUrl = buildResourceManagerPreviewUrl(normalized);
|
||||
|
||||
if (resourceManagerPreviewUrl) {
|
||||
return resourceManagerPreviewUrl;
|
||||
}
|
||||
}
|
||||
|
||||
if (/^(?:https?:\/\/|\/)/i.test(normalized)) {
|
||||
return normalized;
|
||||
}
|
||||
@@ -193,59 +270,10 @@ function resolveLinkCardUrlAndActionLabel(rawUrl: string, rawActionLabel?: strin
|
||||
};
|
||||
}
|
||||
|
||||
function hasKnownFileExtension(url: string) {
|
||||
const pathname = url.split('?')[0] ?? '';
|
||||
return /\.[a-z0-9]{1,8}$/i.test(pathname);
|
||||
}
|
||||
|
||||
function isInternalResourceUrl(url: string) {
|
||||
return RESOURCE_PATH_PREFIXES.some((prefix) => url.startsWith(prefix));
|
||||
}
|
||||
|
||||
function isStructuredLinkCardCandidate(url: string) {
|
||||
const normalized = normalizeUrl(url);
|
||||
|
||||
if (!normalized) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isInternalResourceUrl(normalized)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return /^https?:\/\//i.test(normalized) && !hasKnownFileExtension(normalized);
|
||||
}
|
||||
|
||||
function buildFallbackLinkTitle(url: string) {
|
||||
try {
|
||||
const parsed = new URL(url, typeof window !== 'undefined' ? window.location.origin : 'https://local.invalid');
|
||||
const lastSegment = parsed.pathname.split('/').filter(Boolean).at(-1)?.trim();
|
||||
return lastSegment || parsed.hostname || normalizeText(url);
|
||||
} catch {
|
||||
return normalizeText(url);
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeStandaloneTitle(value: string) {
|
||||
return value
|
||||
.replace(/^\s*(?:[-*+]\s+|\d+\.\s+)?/, '')
|
||||
.replace(/[`'"]+/g, '')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim();
|
||||
}
|
||||
|
||||
function resolveStandaloneLinkTitle(keptLines: string[], url: string) {
|
||||
for (let index = keptLines.length - 1; index >= 0; index -= 1) {
|
||||
const candidate = normalizeStandaloneTitle(keptLines[index] ?? '');
|
||||
|
||||
if (candidate) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
|
||||
return buildFallbackLinkTitle(url);
|
||||
}
|
||||
|
||||
function buildLinkCardPart(rawBody: string): ChatMessagePart | null {
|
||||
const segments = rawBody
|
||||
.split('|')
|
||||
@@ -330,6 +358,17 @@ function buildPromptPart(rawBody: string): ChatMessagePart | null {
|
||||
};
|
||||
}
|
||||
|
||||
function buildPromptPartFromBlock(rawBody: string) {
|
||||
const trimmed = rawBody.trim();
|
||||
|
||||
if (!trimmed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const promptWrapperMatched = trimmed.match(/^\[\[prompt:\s*([\s\S]*?)\s*\]\]$/i);
|
||||
return buildPromptPart(promptWrapperMatched?.[1] ?? trimmed);
|
||||
}
|
||||
|
||||
export function extractChatMessageParts(text: string) {
|
||||
const lines = String(text ?? '').split('\n');
|
||||
const keptLines: string[] = [];
|
||||
@@ -382,7 +421,8 @@ export function extractChatMessageParts(text: string) {
|
||||
return true;
|
||||
};
|
||||
|
||||
for (const line of lines) {
|
||||
for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
|
||||
const line = lines[lineIndex] ?? '';
|
||||
const promptMatched = line.match(PROMPT_LINE_PATTERN);
|
||||
|
||||
if (promptMatched) {
|
||||
@@ -392,29 +432,65 @@ export function extractChatMessageParts(text: string) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (PROMPT_BLOCK_START_PATTERN.test(line)) {
|
||||
const wrappedLines = [line];
|
||||
const promptBodyLines: string[] = [];
|
||||
let cursor = lineIndex + 1;
|
||||
let foundBlockEnd = false;
|
||||
|
||||
for (; cursor < lines.length; cursor += 1) {
|
||||
const nextLine = lines[cursor] ?? '';
|
||||
wrappedLines.push(nextLine);
|
||||
|
||||
if (PROMPT_BLOCK_END_PATTERN.test(nextLine)) {
|
||||
foundBlockEnd = true;
|
||||
break;
|
||||
}
|
||||
|
||||
promptBodyLines.push(nextLine);
|
||||
}
|
||||
|
||||
if (foundBlockEnd && pushPart(buildPromptPartFromBlock(promptBodyLines.join('\n')))) {
|
||||
lineIndex = cursor;
|
||||
continue;
|
||||
}
|
||||
|
||||
keptLines.push(...wrappedLines);
|
||||
lineIndex = foundBlockEnd ? cursor : lines.length;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (PROMPT_CODE_BLOCK_START_PATTERN.test(line)) {
|
||||
const fencedLines = [line];
|
||||
const jsonBodyLines: string[] = [];
|
||||
let cursor = lineIndex + 1;
|
||||
let foundFenceEnd = false;
|
||||
|
||||
for (; cursor < lines.length; cursor += 1) {
|
||||
const nextLine = lines[cursor] ?? '';
|
||||
fencedLines.push(nextLine);
|
||||
|
||||
if (CODE_BLOCK_END_PATTERN.test(nextLine)) {
|
||||
foundFenceEnd = true;
|
||||
break;
|
||||
}
|
||||
|
||||
jsonBodyLines.push(nextLine);
|
||||
}
|
||||
|
||||
if (foundFenceEnd && pushPart(buildPromptPartFromBlock(jsonBodyLines.join('\n')))) {
|
||||
lineIndex = cursor;
|
||||
continue;
|
||||
}
|
||||
|
||||
keptLines.push(...fencedLines);
|
||||
lineIndex = foundFenceEnd ? cursor : lines.length;
|
||||
continue;
|
||||
}
|
||||
|
||||
const matched = line.match(LINK_CARD_LINE_PATTERN);
|
||||
|
||||
if (!matched) {
|
||||
const markdownLinkMatch = line.match(STANDALONE_MARKDOWN_LINK_LINE_PATTERN);
|
||||
if (markdownLinkMatch) {
|
||||
const [, rawTitle, rawUrl] = markdownLinkMatch;
|
||||
if (isStructuredLinkCardCandidate(rawUrl ?? '')) {
|
||||
if (pushPart(buildLinkCardPart(`${rawTitle}|${rawUrl}`))) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const standaloneUrlMatch = line.match(STANDALONE_URL_LINE_PATTERN);
|
||||
if (standaloneUrlMatch) {
|
||||
const rawUrl = standaloneUrlMatch[1] ?? '';
|
||||
if (isStructuredLinkCardCandidate(rawUrl)) {
|
||||
if (pushPart(buildLinkCardPart(`${resolveStandaloneLinkTitle(keptLines, rawUrl)}|${rawUrl}`))) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keptLines.push(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user