chore: update live chat and work server changes

This commit is contained in:
2026-04-26 16:37:06 +09:00
parent 63e5d263a7
commit 20a6333ed2
38 changed files with 2078 additions and 2281 deletions

View File

@@ -1,34 +1,32 @@
import { useEffect, useMemo, useState } from 'react';
import { Empty } from 'antd';
import { CodexDiffPreviewer } from '../previewer';
import type { CodexDiffPreviewerFile, CodexDiffPreviewerFileStatus } from '../previewer';
type RawTextModule = () => Promise<string | { default: string }>;
const repoTextModules = {
...import.meta.glob('/src/**/*.{ts,tsx,js,jsx,css,scss,html,json,md,mjs,yml,yaml}', {
eager: true,
query: '?raw',
import: 'default',
}),
...import.meta.glob('/docs/**/*.{ts,tsx,js,jsx,css,scss,html,json,md,mjs,yml,yaml}', {
eager: true,
query: '?raw',
import: 'default',
}),
...import.meta.glob('/scripts/**/*.{ts,tsx,js,jsx,css,scss,html,json,md,mjs,yml,yaml,sh}', {
eager: true,
query: '?raw',
import: 'default',
}),
...import.meta.glob('/etc/**/*.{ts,tsx,js,jsx,css,scss,html,json,md,mjs,yml,yaml,sh}', {
eager: true,
query: '?raw',
import: 'default',
}),
...import.meta.glob('/{README.md,package.json,package-lock.json,docker-compose.yml}', {
eager: true,
query: '?raw',
import: 'default',
}),
} as Record<string, string>;
} as Record<string, RawTextModule>;
const repoImageModules = {
...import.meta.glob('/src/**/*.{png,jpg,jpeg,gif,webp,svg,avif}', {
@@ -183,12 +181,20 @@ function parseRawDiffText(sourceSectionContent: string) {
return matches.map((match) => match[1].trimEnd()).filter(Boolean).join('\n\n');
}
function buildPreviewFiles(entries: ParsedChangedFile[]): CodexDiffPreviewerFile[] {
function normalizeRawTextModuleValue(value: string | { default: string }) {
return typeof value === 'string' ? value : value.default;
}
function buildPreviewFiles(
entries: ParsedChangedFile[],
loadedTextByPath: Record<string, string | undefined>,
): CodexDiffPreviewerFile[] {
return entries.map((entry) => {
const normalizedPath = `/${entry.path}`;
const isPublicFile = entry.path.startsWith('public/');
const publicAssetUrl = isPublicFile ? `/${entry.path.slice('public/'.length)}` : null;
const rawContent = repoTextModules[normalizedPath];
const canLoadRawContent = Boolean(repoTextModules[normalizedPath]);
const rawContent = loadedTextByPath[normalizedPath];
const imageUrl = repoImageModules[normalizedPath] ?? (IMAGE_FILE_PATTERN.test(entry.path) ? publicAssetUrl : null);
const isBinary = entry.status === 'binary' || BINARY_FILE_PATTERN.test(entry.path);
const isImage = IMAGE_FILE_PATTERN.test(entry.path);
@@ -211,11 +217,13 @@ function buildPreviewFiles(entries: ParsedChangedFile[]): CodexDiffPreviewerFile
: isPublicFile
? 'public 디렉터리 파일은 번들 import 없이 정적 URL로만 제공되어 전체 소스 미리보기를 표시하지 않습니다.'
: rawContent ??
(entry.status === 'deleted'
? '삭제된 파일이라 현재 저장소에서 전체 소스를 불러올 수 없습니다.'
: isImage
? '이미지 파일 URL을 현재 저장소에서 찾지 못했습니다.'
: '현재 저장소에서 파일 내용을 불러올 수 없습니다.'),
(canLoadRawContent
? '파일 내용을 불러오는 중입니다.'
: entry.status === 'deleted'
? '삭제된 파일이라 현재 저장소에서 전체 소스를 불러올 수 없습니다.'
: isImage
? '이미지 파일 URL을 현재 저장소에서 찾지 못했습니다.'
: '현재 저장소에서 파일 내용을 불러올 수 없습니다.'),
};
});
}
@@ -238,14 +246,61 @@ export function WorklogSourcePreview({
sourceSectionContent,
filesSectionContent,
}: WorklogSourcePreviewProps) {
const diffText = parseRawDiffText(sourceSectionContent);
const changedFiles = parseChangedFiles(filesSectionContent);
const sourcePaths = mergeSourceEntries(changedFiles, parseSourcePaths(sourceSectionContent));
const files = buildPreviewFiles(sourcePaths);
const diffText = useMemo(() => parseRawDiffText(sourceSectionContent), [sourceSectionContent]);
const sourcePaths = useMemo(() => {
const changedFiles = parseChangedFiles(filesSectionContent);
return mergeSourceEntries(changedFiles, parseSourcePaths(sourceSectionContent));
}, [filesSectionContent, sourceSectionContent]);
const [loadedTextByPath, setLoadedTextByPath] = useState<Record<string, string | undefined>>({});
const files = useMemo(() => buildPreviewFiles(sourcePaths, loadedTextByPath), [loadedTextByPath, sourcePaths]);
const description = diffText
? '변경 파일 기준 전체 소스와 raw diff를 Codex preview 스타일로 전환해 표시합니다.'
: '변경 파일 기준 전체 소스를 Codex preview 스타일로 표시합니다. raw diff는 작업일지 `## 소스` 섹션의 diff 코드블록을 그대로 사용합니다.';
useEffect(() => {
let isMounted = true;
const textModuleEntries = sourcePaths
.map((entry) => `/${entry.path}`)
.filter((path) => repoTextModules[path] && loadedTextByPath[path] === undefined);
if (!textModuleEntries.length) {
return () => {
isMounted = false;
};
}
Promise.all(
textModuleEntries.map(async (path) => {
const value = await repoTextModules[path]();
return [path, normalizeRawTextModuleValue(value)] as const;
}),
)
.then((loadedEntries) => {
if (!isMounted) {
return;
}
setLoadedTextByPath((current) => ({
...current,
...Object.fromEntries(loadedEntries),
}));
})
.catch(() => {
if (!isMounted) {
return;
}
setLoadedTextByPath((current) => ({
...current,
...Object.fromEntries(textModuleEntries.map((path) => [path, '현재 저장소에서 파일 내용을 불러올 수 없습니다.'])),
}));
});
return () => {
isMounted = false;
};
}, [loadedTextByPath, sourcePaths]);
if (!files.length && !diffText) {
return <Empty description="기록된 소스 미리보기가 없습니다." />;
}