Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions .github/workflows/pr-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
name: PR Build Check

on:
pull_request:
types: [opened, synchronize, reopened]

permissions:
contents: read
pull-requests: write

concurrency:
group: "pr-check-${{ github.event.number }}"
cancel-in-progress: true

jobs:
build-check:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
lfs: true

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json

- name: Install dependencies
working-directory: frontend
run: npm ci

- name: Copy static assets
working-directory: frontend
run: |
mkdir -p public/thumbnails public/downsampled public/annotations/nsd
cp -r ../data/thumbnails/* public/thumbnails/ 2>/dev/null || echo "No thumbnails to copy"
cp -r ../images/downsampled/* public/downsampled/ 2>/dev/null || echo "No images to copy"
cp -r ../annotations/nsd/*.json public/annotations/nsd/ 2>/dev/null || echo "No annotations to copy"

- name: Build Next.js site
working-directory: frontend
run: npm run build

- name: Verify build output
run: |
echo "Build completed successfully!"
echo "Output files:"
ls -la frontend/out/
echo ""
echo "Static assets:"
ls -la frontend/out/thumbnails/ 2>/dev/null | head -5 || echo "No thumbnails"
ls -la frontend/out/downsampled/ 2>/dev/null | head -5 || echo "No downsampled images"
ls -la frontend/out/annotations/nsd/ 2>/dev/null | head -5 || echo "No annotations"

- name: Comment PR with build status
uses: actions/github-script@v7
with:
script: |
const body = `## Build Check Passed\n\nThe frontend build completed successfully. Once merged, changes will deploy to: https://annotation-garden.github.io/image-annotation/`;

// Find existing comment
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});

const botComment = comments.find(comment =>
comment.user.type === 'Bot' && comment.body.includes('Build Check')
);

if (botComment) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: body
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: body
});
}
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,12 @@ ehthumbs.db
test_outputs/
test_results/

# Frontend public assets (copied for deployment)
# Frontend public assets (copied for deployment, except essentials)
frontend/public/*
!frontend/public/AGI-square.svg
!frontend/public/AGI-square.png
!frontend/public/favicon.ico
!frontend/public/image-list.json

# Image files (but keep README, and downsampled)
images/original/*.png
Expand Down
76 changes: 38 additions & 38 deletions frontend/app/components/AnnotationViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ export default function AnnotationViewer({ annotation }: AnnotationViewerProps)
const [viewMode, setViewMode] = useState<'text' | 'json'>('text')

const handleCopy = () => {
const textToCopy = viewMode === 'json'
const textToCopy = viewMode === 'json'
? JSON.stringify(annotation.response_data || annotation.response, null, 2)
: annotation.response

navigator.clipboard.writeText(textToCopy)
setCopied(true)
setTimeout(() => setCopied(false), 2000)
Expand All @@ -34,8 +34,8 @@ export default function AnnotationViewer({ annotation }: AnnotationViewerProps)
return (
<div className="space-y-3">
{data.map((item, index) => (
<div key={index} className="bg-gray-800/30 rounded-lg p-3 border border-purple-500/10">
<div className="text-xs text-purple-400 mb-2">Item {index + 1}</div>
<div key={index} className="bg-agi-teal/5 rounded-lg p-3 border border-agi-teal/10">
<div className="text-xs text-agi-orange mb-2">Item {index + 1}</div>
{renderJsonData(item)}
</div>
))}
Expand All @@ -49,15 +49,15 @@ export default function AnnotationViewer({ annotation }: AnnotationViewerProps)
<div className="space-y-2">
{Object.entries(data).map(([key, value]) => (
<div key={key} className="flex flex-col gap-1">
<span className="text-xs text-purple-400 font-medium">
<span className="text-xs text-agi-teal font-medium">
{key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}:
</span>
{typeof value === 'object' ? (
<div className="ml-3 border-l-2 border-purple-500/20 pl-3">
<div className="ml-3 border-l-2 border-agi-teal/20 pl-3">
{renderJsonData(value)}
</div>
) : (
<span className="text-sm text-gray-300 ml-3">
<span className="text-sm text-agi-teal-800 ml-3">
{String(value)}
</span>
)}
Expand All @@ -68,15 +68,15 @@ export default function AnnotationViewer({ annotation }: AnnotationViewerProps)
}

// Primitive value
return <span className="text-sm text-gray-300">{String(data)}</span>
return <span className="text-sm text-agi-teal-800">{String(data)}</span>
}

return (
<div className="space-y-4">
{/* Prompt Text */}
<div className="bg-gray-800/30 rounded-lg p-3 border border-purple-500/10">
<div className="text-xs text-purple-400 mb-1">Prompt</div>
<div className="text-sm text-gray-300 leading-relaxed">
<div className="bg-agi-teal/5 rounded-lg p-3 border border-agi-teal/10">
<div className="text-xs text-agi-orange mb-1">Prompt</div>
<div className="text-sm text-agi-teal-800 leading-relaxed">
{annotation.prompt_text}
</div>
</div>
Expand All @@ -88,8 +88,8 @@ export default function AnnotationViewer({ annotation }: AnnotationViewerProps)
onClick={() => setViewMode('text')}
className={`px-3 py-1.5 rounded-lg text-sm flex items-center gap-1.5 transition-all ${
viewMode === 'text'
? 'bg-purple-500/20 text-purple-300 border border-purple-500/30'
: 'bg-gray-800/30 text-gray-400 hover:bg-gray-800/50 border border-gray-700/30'
? 'bg-agi-teal/10 text-agi-teal border border-agi-teal/30'
: 'bg-stone-100 text-agi-teal-600 hover:bg-stone-200 border border-stone-200'
}`}
>
<FileText className="w-3.5 h-3.5" />
Expand All @@ -99,8 +99,8 @@ export default function AnnotationViewer({ annotation }: AnnotationViewerProps)
onClick={() => setViewMode('json')}
className={`px-3 py-1.5 rounded-lg text-sm flex items-center gap-1.5 transition-all ${
viewMode === 'json'
? 'bg-purple-500/20 text-purple-300 border border-purple-500/30'
: 'bg-gray-800/30 text-gray-400 hover:bg-gray-800/50 border border-gray-700/30'
? 'bg-agi-teal/10 text-agi-teal border border-agi-teal/30'
: 'bg-stone-100 text-agi-teal-600 hover:bg-stone-200 border border-stone-200'
}`}
>
<FileJson className="w-3.5 h-3.5" />
Expand All @@ -113,27 +113,27 @@ export default function AnnotationViewer({ annotation }: AnnotationViewerProps)
<div className="relative">
<button
onClick={handleCopy}
className="absolute top-2 right-2 p-2 rounded-lg bg-gray-800/50 hover:bg-gray-800/70 transition-all z-10"
className="absolute top-2 right-2 p-2 rounded-lg bg-stone-100 hover:bg-agi-teal/10 transition-all z-10"
aria-label="Copy to clipboard"
>
{copied ? (
<Check className="w-4 h-4 text-green-400" />
<Check className="w-4 h-4 text-green-600" />
) : (
<Copy className="w-4 h-4 text-gray-400" />
<Copy className="w-4 h-4 text-agi-teal-600" />
)}
</button>

<div className="bg-gray-800/30 rounded-lg p-4 pr-12 max-h-[400px] overflow-y-auto border border-purple-500/10">
<div className="bg-agi-teal/5 rounded-lg p-4 pr-12 max-h-[400px] overflow-y-auto border border-agi-teal/10">
{viewMode === 'text' ? (
<div className="whitespace-pre-wrap text-sm text-gray-300 leading-relaxed">
<div className="whitespace-pre-wrap text-sm text-agi-teal-800 leading-relaxed">
{annotation.response}
</div>
) : hasJsonData ? (
<div className="text-sm">
{renderJsonData(annotation.response_data)}
</div>
) : (
<pre className="text-xs overflow-x-auto text-gray-300">
<pre className="text-xs overflow-x-auto text-agi-teal-800">
<code>{JSON.stringify(annotation.response, null, 2)}</code>
</pre>
)}
Expand All @@ -144,44 +144,44 @@ export default function AnnotationViewer({ annotation }: AnnotationViewerProps)
{(annotation.token_metrics || annotation.performance_metrics) && (
<div className="grid grid-cols-2 gap-3">
{annotation.token_metrics && (
<div className="bg-gray-800/30 rounded-lg p-3 border border-purple-500/10">
<div className="bg-agi-teal/5 rounded-lg p-3 border border-agi-teal/10">
<div className="flex items-center gap-2 mb-2">
<Activity className="w-4 h-4 text-purple-400" />
<span className="text-xs font-medium text-gray-400">Token Usage</span>
<Activity className="w-4 h-4 text-agi-orange" />
<span className="text-xs font-medium text-agi-teal-600">Token Usage</span>
</div>
<div className="space-y-1">
<div className="flex justify-between text-xs">
<span className="text-gray-500">Input:</span>
<span className="text-gray-300">{annotation.token_metrics.input_tokens}</span>
<span className="text-agi-teal-500">Input:</span>
<span className="text-agi-teal-800">{annotation.token_metrics.input_tokens}</span>
</div>
<div className="flex justify-between text-xs">
<span className="text-gray-500">Output:</span>
<span className="text-gray-300">{annotation.token_metrics.output_tokens}</span>
<span className="text-agi-teal-500">Output:</span>
<span className="text-agi-teal-800">{annotation.token_metrics.output_tokens}</span>
</div>
<div className="flex justify-between text-xs font-medium">
<span className="text-gray-500">Total:</span>
<span className="text-purple-300">{annotation.token_metrics.total_tokens}</span>
<span className="text-agi-teal-500">Total:</span>
<span className="text-agi-teal">{annotation.token_metrics.total_tokens}</span>
</div>
</div>
</div>
)}

{annotation.performance_metrics && (
<div className="bg-gray-800/30 rounded-lg p-3 border border-purple-500/10">
<div className="bg-agi-teal/5 rounded-lg p-3 border border-agi-teal/10">
<div className="flex items-center gap-2 mb-2">
<Zap className="w-4 h-4 text-purple-400" />
<span className="text-xs font-medium text-gray-400">Performance</span>
<Zap className="w-4 h-4 text-agi-orange" />
<span className="text-xs font-medium text-agi-teal-600">Performance</span>
</div>
<div className="space-y-1">
<div className="flex justify-between text-xs">
<span className="text-gray-500">Speed:</span>
<span className="text-gray-300">
<span className="text-agi-teal-500">Speed:</span>
<span className="text-agi-teal-800">
{annotation.performance_metrics.tokens_per_second.toFixed(1)} t/s
</span>
</div>
<div className="flex justify-between text-xs">
<span className="text-gray-500">Time:</span>
<span className="text-gray-300">
<span className="text-agi-teal-500">Time:</span>
<span className="text-agi-teal-800">
{(annotation.performance_metrics.total_duration_ms / 1000).toFixed(2)}s
</span>
</div>
Expand All @@ -192,4 +192,4 @@ export default function AnnotationViewer({ annotation }: AnnotationViewerProps)
)}
</div>
)
}
}
Loading