Skip to content

Commit dab3d24

Browse files
Update frontend to AGI theme with draggable slider and build check workflow
* Update frontend to AGI theme and fix image loading - Fix .gitignore to track image-list.json (was causing 404 on deploy) - Update color scheme from purple to AGI brand colors (teal, orange) - Add AGI color palette to tailwind.config.js - Convert to light theme with warm off-white background - Replace Brain icon with AGI logo in header - Update all components (page, ThumbnailRibbon, AnnotationViewer) - Improve accessibility with proper contrast ratios * Fix progress indicator to scale for 1000+ images - Calculate group size dynamically based on total image count - Each dot now represents ~100 images (for 1000 images) - Progress indicator correctly highlights for any image index * Add draggable slider for quick image navigation - Replace dot indicators with a draggable progress slider - Click anywhere on the slider to jump to that position - Drag the handle to scrub through images quickly - Shows filled track with teal-to-orange gradient - Segment markers for visual reference - Smooth transition animations * Add PR preview workflow for GitHub Pages - Deploy PR previews to /pr-preview/pr-{number}/ subdirectory - Automatic cleanup when PR is closed - Uses rossjrw/pr-preview-action for deployment - Builds with correct base path for preview URL * Replace PR preview with build check workflow GitHub Pages with workflow-based deployment only supports single deployments, making PR previews unfeasible. Changed to build check that verifies PRs compile successfully before merge.
1 parent 26e2c43 commit dab3d24

File tree

7 files changed

+1322
-117
lines changed

7 files changed

+1322
-117
lines changed

.github/workflows/pr-check.yml

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
name: PR Build Check
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, reopened]
6+
7+
permissions:
8+
contents: read
9+
pull-requests: write
10+
11+
concurrency:
12+
group: "pr-check-${{ github.event.number }}"
13+
cancel-in-progress: true
14+
15+
jobs:
16+
build-check:
17+
runs-on: ubuntu-latest
18+
steps:
19+
- name: Checkout repository
20+
uses: actions/checkout@v4
21+
with:
22+
lfs: true
23+
24+
- name: Setup Node.js
25+
uses: actions/setup-node@v4
26+
with:
27+
node-version: '20'
28+
cache: 'npm'
29+
cache-dependency-path: frontend/package-lock.json
30+
31+
- name: Install dependencies
32+
working-directory: frontend
33+
run: npm ci
34+
35+
- name: Copy static assets
36+
working-directory: frontend
37+
run: |
38+
mkdir -p public/thumbnails public/downsampled public/annotations/nsd
39+
cp -r ../data/thumbnails/* public/thumbnails/ 2>/dev/null || echo "No thumbnails to copy"
40+
cp -r ../images/downsampled/* public/downsampled/ 2>/dev/null || echo "No images to copy"
41+
cp -r ../annotations/nsd/*.json public/annotations/nsd/ 2>/dev/null || echo "No annotations to copy"
42+
43+
- name: Build Next.js site
44+
working-directory: frontend
45+
run: npm run build
46+
47+
- name: Verify build output
48+
run: |
49+
echo "Build completed successfully!"
50+
echo "Output files:"
51+
ls -la frontend/out/
52+
echo ""
53+
echo "Static assets:"
54+
ls -la frontend/out/thumbnails/ 2>/dev/null | head -5 || echo "No thumbnails"
55+
ls -la frontend/out/downsampled/ 2>/dev/null | head -5 || echo "No downsampled images"
56+
ls -la frontend/out/annotations/nsd/ 2>/dev/null | head -5 || echo "No annotations"
57+
58+
- name: Comment PR with build status
59+
uses: actions/github-script@v7
60+
with:
61+
script: |
62+
const body = `## Build Check Passed\n\nThe frontend build completed successfully. Once merged, changes will deploy to: https://annotation-garden.github.io/image-annotation/`;
63+
64+
// Find existing comment
65+
const { data: comments } = await github.rest.issues.listComments({
66+
owner: context.repo.owner,
67+
repo: context.repo.repo,
68+
issue_number: context.issue.number,
69+
});
70+
71+
const botComment = comments.find(comment =>
72+
comment.user.type === 'Bot' && comment.body.includes('Build Check')
73+
);
74+
75+
if (botComment) {
76+
await github.rest.issues.updateComment({
77+
owner: context.repo.owner,
78+
repo: context.repo.repo,
79+
comment_id: botComment.id,
80+
body: body
81+
});
82+
} else {
83+
await github.rest.issues.createComment({
84+
owner: context.repo.owner,
85+
repo: context.repo.repo,
86+
issue_number: context.issue.number,
87+
body: body
88+
});
89+
}

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,11 +112,12 @@ ehthumbs.db
112112
test_outputs/
113113
test_results/
114114

115-
# Frontend public assets (copied for deployment)
115+
# Frontend public assets (copied for deployment, except essentials)
116116
frontend/public/*
117117
!frontend/public/AGI-square.svg
118118
!frontend/public/AGI-square.png
119119
!frontend/public/favicon.ico
120+
!frontend/public/image-list.json
120121

121122
# Image files (but keep README, and downsampled)
122123
images/original/*.png

frontend/app/components/AnnotationViewer.tsx

Lines changed: 38 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ export default function AnnotationViewer({ annotation }: AnnotationViewerProps)
1313
const [viewMode, setViewMode] = useState<'text' | 'json'>('text')
1414

1515
const handleCopy = () => {
16-
const textToCopy = viewMode === 'json'
16+
const textToCopy = viewMode === 'json'
1717
? JSON.stringify(annotation.response_data || annotation.response, null, 2)
1818
: annotation.response
19-
19+
2020
navigator.clipboard.writeText(textToCopy)
2121
setCopied(true)
2222
setTimeout(() => setCopied(false), 2000)
@@ -34,8 +34,8 @@ export default function AnnotationViewer({ annotation }: AnnotationViewerProps)
3434
return (
3535
<div className="space-y-3">
3636
{data.map((item, index) => (
37-
<div key={index} className="bg-gray-800/30 rounded-lg p-3 border border-purple-500/10">
38-
<div className="text-xs text-purple-400 mb-2">Item {index + 1}</div>
37+
<div key={index} className="bg-agi-teal/5 rounded-lg p-3 border border-agi-teal/10">
38+
<div className="text-xs text-agi-orange mb-2">Item {index + 1}</div>
3939
{renderJsonData(item)}
4040
</div>
4141
))}
@@ -49,15 +49,15 @@ export default function AnnotationViewer({ annotation }: AnnotationViewerProps)
4949
<div className="space-y-2">
5050
{Object.entries(data).map(([key, value]) => (
5151
<div key={key} className="flex flex-col gap-1">
52-
<span className="text-xs text-purple-400 font-medium">
52+
<span className="text-xs text-agi-teal font-medium">
5353
{key.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase())}:
5454
</span>
5555
{typeof value === 'object' ? (
56-
<div className="ml-3 border-l-2 border-purple-500/20 pl-3">
56+
<div className="ml-3 border-l-2 border-agi-teal/20 pl-3">
5757
{renderJsonData(value)}
5858
</div>
5959
) : (
60-
<span className="text-sm text-gray-300 ml-3">
60+
<span className="text-sm text-agi-teal-800 ml-3">
6161
{String(value)}
6262
</span>
6363
)}
@@ -68,15 +68,15 @@ export default function AnnotationViewer({ annotation }: AnnotationViewerProps)
6868
}
6969

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

7474
return (
7575
<div className="space-y-4">
7676
{/* Prompt Text */}
77-
<div className="bg-gray-800/30 rounded-lg p-3 border border-purple-500/10">
78-
<div className="text-xs text-purple-400 mb-1">Prompt</div>
79-
<div className="text-sm text-gray-300 leading-relaxed">
77+
<div className="bg-agi-teal/5 rounded-lg p-3 border border-agi-teal/10">
78+
<div className="text-xs text-agi-orange mb-1">Prompt</div>
79+
<div className="text-sm text-agi-teal-800 leading-relaxed">
8080
{annotation.prompt_text}
8181
</div>
8282
</div>
@@ -88,8 +88,8 @@ export default function AnnotationViewer({ annotation }: AnnotationViewerProps)
8888
onClick={() => setViewMode('text')}
8989
className={`px-3 py-1.5 rounded-lg text-sm flex items-center gap-1.5 transition-all ${
9090
viewMode === 'text'
91-
? 'bg-purple-500/20 text-purple-300 border border-purple-500/30'
92-
: 'bg-gray-800/30 text-gray-400 hover:bg-gray-800/50 border border-gray-700/30'
91+
? 'bg-agi-teal/10 text-agi-teal border border-agi-teal/30'
92+
: 'bg-stone-100 text-agi-teal-600 hover:bg-stone-200 border border-stone-200'
9393
}`}
9494
>
9595
<FileText className="w-3.5 h-3.5" />
@@ -99,8 +99,8 @@ export default function AnnotationViewer({ annotation }: AnnotationViewerProps)
9999
onClick={() => setViewMode('json')}
100100
className={`px-3 py-1.5 rounded-lg text-sm flex items-center gap-1.5 transition-all ${
101101
viewMode === 'json'
102-
? 'bg-purple-500/20 text-purple-300 border border-purple-500/30'
103-
: 'bg-gray-800/30 text-gray-400 hover:bg-gray-800/50 border border-gray-700/30'
102+
? 'bg-agi-teal/10 text-agi-teal border border-agi-teal/30'
103+
: 'bg-stone-100 text-agi-teal-600 hover:bg-stone-200 border border-stone-200'
104104
}`}
105105
>
106106
<FileJson className="w-3.5 h-3.5" />
@@ -113,27 +113,27 @@ export default function AnnotationViewer({ annotation }: AnnotationViewerProps)
113113
<div className="relative">
114114
<button
115115
onClick={handleCopy}
116-
className="absolute top-2 right-2 p-2 rounded-lg bg-gray-800/50 hover:bg-gray-800/70 transition-all z-10"
116+
className="absolute top-2 right-2 p-2 rounded-lg bg-stone-100 hover:bg-agi-teal/10 transition-all z-10"
117117
aria-label="Copy to clipboard"
118118
>
119119
{copied ? (
120-
<Check className="w-4 h-4 text-green-400" />
120+
<Check className="w-4 h-4 text-green-600" />
121121
) : (
122-
<Copy className="w-4 h-4 text-gray-400" />
122+
<Copy className="w-4 h-4 text-agi-teal-600" />
123123
)}
124124
</button>
125125

126-
<div className="bg-gray-800/30 rounded-lg p-4 pr-12 max-h-[400px] overflow-y-auto border border-purple-500/10">
126+
<div className="bg-agi-teal/5 rounded-lg p-4 pr-12 max-h-[400px] overflow-y-auto border border-agi-teal/10">
127127
{viewMode === 'text' ? (
128-
<div className="whitespace-pre-wrap text-sm text-gray-300 leading-relaxed">
128+
<div className="whitespace-pre-wrap text-sm text-agi-teal-800 leading-relaxed">
129129
{annotation.response}
130130
</div>
131131
) : hasJsonData ? (
132132
<div className="text-sm">
133133
{renderJsonData(annotation.response_data)}
134134
</div>
135135
) : (
136-
<pre className="text-xs overflow-x-auto text-gray-300">
136+
<pre className="text-xs overflow-x-auto text-agi-teal-800">
137137
<code>{JSON.stringify(annotation.response, null, 2)}</code>
138138
</pre>
139139
)}
@@ -144,44 +144,44 @@ export default function AnnotationViewer({ annotation }: AnnotationViewerProps)
144144
{(annotation.token_metrics || annotation.performance_metrics) && (
145145
<div className="grid grid-cols-2 gap-3">
146146
{annotation.token_metrics && (
147-
<div className="bg-gray-800/30 rounded-lg p-3 border border-purple-500/10">
147+
<div className="bg-agi-teal/5 rounded-lg p-3 border border-agi-teal/10">
148148
<div className="flex items-center gap-2 mb-2">
149-
<Activity className="w-4 h-4 text-purple-400" />
150-
<span className="text-xs font-medium text-gray-400">Token Usage</span>
149+
<Activity className="w-4 h-4 text-agi-orange" />
150+
<span className="text-xs font-medium text-agi-teal-600">Token Usage</span>
151151
</div>
152152
<div className="space-y-1">
153153
<div className="flex justify-between text-xs">
154-
<span className="text-gray-500">Input:</span>
155-
<span className="text-gray-300">{annotation.token_metrics.input_tokens}</span>
154+
<span className="text-agi-teal-500">Input:</span>
155+
<span className="text-agi-teal-800">{annotation.token_metrics.input_tokens}</span>
156156
</div>
157157
<div className="flex justify-between text-xs">
158-
<span className="text-gray-500">Output:</span>
159-
<span className="text-gray-300">{annotation.token_metrics.output_tokens}</span>
158+
<span className="text-agi-teal-500">Output:</span>
159+
<span className="text-agi-teal-800">{annotation.token_metrics.output_tokens}</span>
160160
</div>
161161
<div className="flex justify-between text-xs font-medium">
162-
<span className="text-gray-500">Total:</span>
163-
<span className="text-purple-300">{annotation.token_metrics.total_tokens}</span>
162+
<span className="text-agi-teal-500">Total:</span>
163+
<span className="text-agi-teal">{annotation.token_metrics.total_tokens}</span>
164164
</div>
165165
</div>
166166
</div>
167167
)}
168168

169169
{annotation.performance_metrics && (
170-
<div className="bg-gray-800/30 rounded-lg p-3 border border-purple-500/10">
170+
<div className="bg-agi-teal/5 rounded-lg p-3 border border-agi-teal/10">
171171
<div className="flex items-center gap-2 mb-2">
172-
<Zap className="w-4 h-4 text-purple-400" />
173-
<span className="text-xs font-medium text-gray-400">Performance</span>
172+
<Zap className="w-4 h-4 text-agi-orange" />
173+
<span className="text-xs font-medium text-agi-teal-600">Performance</span>
174174
</div>
175175
<div className="space-y-1">
176176
<div className="flex justify-between text-xs">
177-
<span className="text-gray-500">Speed:</span>
178-
<span className="text-gray-300">
177+
<span className="text-agi-teal-500">Speed:</span>
178+
<span className="text-agi-teal-800">
179179
{annotation.performance_metrics.tokens_per_second.toFixed(1)} t/s
180180
</span>
181181
</div>
182182
<div className="flex justify-between text-xs">
183-
<span className="text-gray-500">Time:</span>
184-
<span className="text-gray-300">
183+
<span className="text-agi-teal-500">Time:</span>
184+
<span className="text-agi-teal-800">
185185
{(annotation.performance_metrics.total_duration_ms / 1000).toFixed(2)}s
186186
</span>
187187
</div>
@@ -192,4 +192,4 @@ export default function AnnotationViewer({ annotation }: AnnotationViewerProps)
192192
)}
193193
</div>
194194
)
195-
}
195+
}

0 commit comments

Comments
 (0)