chore: update live chat and work server changes
This commit is contained in:
@@ -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="기록된 소스 미리보기가 없습니다." />;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user