feat(drawer): artifacts section — files, code blocks, deploy URL
This commit is contained in:
@@ -1 +1 @@
|
||||
1775812935
|
||||
1775818515
|
||||
|
||||
@@ -380,6 +380,67 @@ const EventPayload = styled.pre`
|
||||
opacity: 0.8;
|
||||
`;
|
||||
|
||||
const FileList = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
`;
|
||||
|
||||
const FileRow = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 10px 14px;
|
||||
background: var(--bg-input);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 12px;
|
||||
`;
|
||||
|
||||
const FileBadge = styled.span<{ $kind: string }>`
|
||||
padding: 2px 8px;
|
||||
font-size: 9px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
border-radius: 10px;
|
||||
color: #fff;
|
||||
background: ${({ $kind }) => {
|
||||
switch ($kind) {
|
||||
case 'html':
|
||||
case 'htm':
|
||||
return '#f97316';
|
||||
case 'js':
|
||||
case 'javascript':
|
||||
case 'jsx':
|
||||
return '#eab308';
|
||||
case 'ts':
|
||||
case 'typescript':
|
||||
case 'tsx':
|
||||
return '#3b82f6';
|
||||
case 'css':
|
||||
case 'scss':
|
||||
return '#ec4899';
|
||||
case 'json':
|
||||
case 'yaml':
|
||||
return '#8b5cf6';
|
||||
case 'md':
|
||||
case 'markdown':
|
||||
return '#6b7280';
|
||||
case 'url':
|
||||
return '#22c55e';
|
||||
default:
|
||||
return '#525252';
|
||||
}
|
||||
}};
|
||||
`;
|
||||
|
||||
const FilePath = styled.code`
|
||||
flex: 1;
|
||||
word-break: break-all;
|
||||
color: var(--text-primary);
|
||||
`;
|
||||
|
||||
const Loading = styled.div`
|
||||
padding: 60px 20px;
|
||||
text-align: center;
|
||||
@@ -460,6 +521,27 @@ export default function SubTaskDetailDrawer({ subTaskId, onClose }: Props) {
|
||||
[detail],
|
||||
);
|
||||
|
||||
const artifacts = useMemo(() => {
|
||||
if (!detail) return { mainFile: '', codeFiles: [] as Array<{ path: string; lang: string }>, deployUrl: '' };
|
||||
let mainFile = '';
|
||||
let codeFiles: Array<{ path: string; lang: string }> = [];
|
||||
let deployUrl = '';
|
||||
for (const ev of detail.events) {
|
||||
if (ev.eventType === 'completed' && ev.payload && typeof ev.payload === 'object') {
|
||||
const p = ev.payload as Record<string, unknown>;
|
||||
if (typeof p.file === 'string' && p.file) mainFile = p.file;
|
||||
if (Array.isArray(p.extractedFiles)) {
|
||||
codeFiles = (p.extractedFiles as Array<{ path?: string; lang?: string }>)
|
||||
.filter((f): f is { path: string; lang: string } => !!f?.path)
|
||||
.map((f) => ({ path: f.path, lang: f.lang ?? '' }));
|
||||
}
|
||||
if (typeof p.deployUrl === 'string') deployUrl = p.deployUrl;
|
||||
if (typeof p.repoUrl === 'string' && !deployUrl) deployUrl = p.repoUrl;
|
||||
}
|
||||
}
|
||||
return { mainFile, codeFiles, deployUrl };
|
||||
}, [detail]);
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
fetch(`${API_URL}/api/rails/sub-tasks/${subTaskId}`, {
|
||||
@@ -565,6 +647,36 @@ export default function SubTaskDetailDrawer({ subTaskId, onClose }: Props) {
|
||||
</Section>
|
||||
)}
|
||||
|
||||
{(artifacts.mainFile || artifacts.codeFiles.length > 0 || artifacts.deployUrl) && (
|
||||
<Section>
|
||||
<SectionLabel>산출물</SectionLabel>
|
||||
<FileList>
|
||||
{artifacts.mainFile && (
|
||||
<FileRow>
|
||||
<FileBadge $kind="md">log</FileBadge>
|
||||
<FilePath>{artifacts.mainFile}</FilePath>
|
||||
</FileRow>
|
||||
)}
|
||||
{artifacts.codeFiles.map((f) => (
|
||||
<FileRow key={f.path}>
|
||||
<FileBadge $kind={f.lang || 'code'}>{f.lang || 'code'}</FileBadge>
|
||||
<FilePath>files/{f.path}</FilePath>
|
||||
</FileRow>
|
||||
))}
|
||||
{artifacts.deployUrl && (
|
||||
<FileRow>
|
||||
<FileBadge $kind="url">url</FileBadge>
|
||||
<FilePath>
|
||||
<a href={artifacts.deployUrl} target="_blank" rel="noopener noreferrer" style={{ color: '#5fafff' }}>
|
||||
{artifacts.deployUrl}
|
||||
</a>
|
||||
</FilePath>
|
||||
</FileRow>
|
||||
)}
|
||||
</FileList>
|
||||
</Section>
|
||||
)}
|
||||
|
||||
{llmResult && llmResult.text && (
|
||||
<Section>
|
||||
<SectionLabel>LLM 응답</SectionLabel>
|
||||
|
||||
Reference in New Issue
Block a user