Compare commits

...

3 Commits

Author SHA1 Message Date
claude[bot]
77c2e28efa chore(frontend): regenerate pnpm-lock.yaml for jszip dependency
Co-authored-by: Nicholas Tindle <ntindle@users.noreply.github.com>
2026-01-09 22:00:38 +00:00
Claude
c27759cf54 fix(frontend): move share page logo to top right and link to login
- Move AutoGPT logo from top left to top right in the header
- Change logo link to /login page (redirects to library if signed in,
  signup if new user)
2026-01-09 20:36:38 +00:00
Claude
897ab3699e feat(frontend): add AutoGPT logo to share page and zip download for outputs
- Add AutoGPT logo header to the share page layout with dark/light mode support
- Modify download functionality to bundle all outputs into a single zip file
- Add jszip dependency for zip file generation
- Handle both text outputs and file attachments in the zip
2026-01-09 20:09:52 +00:00
4 changed files with 106 additions and 14 deletions

View File

@@ -79,6 +79,7 @@
"geist": "1.5.1",
"highlight.js": "11.11.1",
"jaro-winkler": "0.2.8",
"jszip": "3.10.1",
"katex": "0.16.25",
"launchdarkly-react-client-sdk": "3.9.0",
"lodash": "4.17.21",

View File

@@ -161,6 +161,9 @@ importers:
jaro-winkler:
specifier: 0.2.8
version: 0.2.8
jszip:
specifier: 3.10.1
version: 3.10.1
katex:
specifier: 0.16.25
version: 0.16.25
@@ -4963,6 +4966,9 @@ packages:
engines: {node: '>=16.x'}
hasBin: true
immediate@3.0.6:
resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
immer@10.2.0:
resolution: {integrity: sha512-d/+XTN3zfODyjr89gM3mPq1WNX2B8pYsu7eORitdwyA2sBubnTl3laYlBk4sXY5FUa5qTZGBDPJICVbvqzjlbw==}
@@ -5266,6 +5272,9 @@ packages:
resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==}
engines: {node: '>=4.0'}
jszip@3.10.1:
resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==}
junit-report-builder@5.1.1:
resolution: {integrity: sha512-ZNOIIGMzqCGcHQEA2Q4rIQQ3Df6gSIfne+X9Rly9Bc2y55KxAZu8iGv+n2pP0bLf0XAOctJZgeloC54hWzCahQ==}
engines: {node: '>=16'}
@@ -5304,6 +5313,9 @@ packages:
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
engines: {node: '>= 0.8.0'}
lie@3.3.0:
resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==}
lilconfig@3.1.3:
resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
engines: {node: '>=14'}
@@ -12838,6 +12850,8 @@ snapshots:
image-size@2.0.2: {}
immediate@3.0.6: {}
immer@10.2.0: {}
immer@11.1.3: {}
@@ -13126,6 +13140,13 @@ snapshots:
object.assign: 4.1.7
object.values: 1.2.1
jszip@3.10.1:
dependencies:
lie: 3.3.0
pako: 1.0.11
readable-stream: 2.3.8
setimmediate: 1.0.5
junit-report-builder@5.1.1:
dependencies:
lodash: 4.17.21
@@ -13172,6 +13193,10 @@ snapshots:
prelude-ls: 1.2.1
type-check: 0.4.0
lie@3.3.0:
dependencies:
immediate: 3.0.6
lilconfig@3.1.3: {}
lines-and-columns@1.2.4: {}

View File

@@ -1,4 +1,6 @@
import type { Metadata } from "next";
import Image from "next/image";
import Link from "next/link";
export const metadata: Metadata = {
title: "Shared Agent Run - AutoGPT",
@@ -13,6 +15,28 @@ export default function ShareLayout({
}) {
return (
<div className="min-h-screen bg-background">
<header className="border-b border-border bg-background">
<div className="container mx-auto flex justify-end px-4 py-4">
<Link href="/login" className="inline-block">
<Image
src="/autogpt-logo-dark-bg.png"
alt="AutoGPT"
width={120}
height={32}
className="hidden h-8 w-auto dark:block"
priority
/>
<Image
src="/autogpt-logo-light-bg.png"
alt="AutoGPT"
width={120}
height={32}
className="block h-8 w-auto dark:hidden"
priority
/>
</Link>
</div>
</header>
<div className="container mx-auto px-4 py-8">{children}</div>
</div>
);

View File

@@ -1,3 +1,4 @@
import JSZip from "jszip";
import { OutputRenderer, OutputMetadata } from "../types";
export interface DownloadItem {
@@ -6,9 +7,46 @@ export interface DownloadItem {
renderer: OutputRenderer;
}
async function fetchFileAsBlob(url: string): Promise<Blob | null> {
try {
const response = await fetch(url);
if (!response.ok) {
console.error(`Failed to fetch ${url}: ${response.status}`);
return null;
}
return await response.blob();
} catch (error) {
console.error(`Error fetching ${url}:`, error);
return null;
}
}
function getUniqueFilename(filename: string, usedNames: Set<string>): string {
if (!usedNames.has(filename)) {
usedNames.add(filename);
return filename;
}
const dotIndex = filename.lastIndexOf(".");
const baseName = dotIndex > 0 ? filename.slice(0, dotIndex) : filename;
const extension = dotIndex > 0 ? filename.slice(dotIndex) : "";
let counter = 1;
let newName = `${baseName}_${counter}${extension}`;
while (usedNames.has(newName)) {
counter++;
newName = `${baseName}_${counter}${extension}`;
}
usedNames.add(newName);
return newName;
}
export async function downloadOutputs(items: DownloadItem[]) {
const zip = new JSZip();
const usedFilenames = new Set<string>();
let hasFiles = false;
const concatenableTexts: string[] = [];
const nonConcatenableDownloads: Array<{ blob: Blob; filename: string }> = [];
for (const item of items) {
if (item.renderer.isConcatenable(item.value, item.metadata)) {
@@ -17,7 +55,6 @@ export async function downloadOutputs(items: DownloadItem[]) {
item.metadata,
);
if (copyContent) {
// Extract text from CopyContent
let text: string;
if (typeof copyContent.data === "string") {
text = copyContent.data;
@@ -34,18 +71,21 @@ export async function downloadOutputs(items: DownloadItem[]) {
item.metadata,
);
if (downloadContent) {
let blob: Blob | null = null;
const filename = downloadContent.filename;
if (typeof downloadContent.data === "string") {
if (downloadContent.data.startsWith("http")) {
const link = document.createElement("a");
link.href = downloadContent.data;
link.download = downloadContent.filename;
link.click();
blob = await fetchFileAsBlob(downloadContent.data);
}
} else {
nonConcatenableDownloads.push({
blob: downloadContent.data as Blob,
filename: downloadContent.filename,
});
blob = downloadContent.data as Blob;
}
if (blob) {
const uniqueFilename = getUniqueFilename(filename, usedFilenames);
zip.file(uniqueFilename, blob);
hasFiles = true;
}
}
}
@@ -53,12 +93,14 @@ export async function downloadOutputs(items: DownloadItem[]) {
if (concatenableTexts.length > 0) {
const combinedText = concatenableTexts.join("\n\n---\n\n");
const blob = new Blob([combinedText], { type: "text/plain" });
downloadBlob(blob, "combined_output.txt");
const filename = getUniqueFilename("combined_output.txt", usedFilenames);
zip.file(filename, combinedText);
hasFiles = true;
}
for (const download of nonConcatenableDownloads) {
downloadBlob(download.blob, download.filename);
if (hasFiles) {
const zipBlob = await zip.generateAsync({ type: "blob" });
downloadBlob(zipBlob, "outputs.zip");
}
}