Skip to content

Commit e608f46

Browse files
authored
Merge pull request #52 from AutoMaker-Org/new-project-from-template
feat: Add new project from template feature and UI enhancements
2 parents 9afcc5f + 8de4056 commit e608f46

28 files changed

Lines changed: 3343 additions & 713 deletions

.github/scripts/upload-to-r2.js

Lines changed: 60 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1-
const { S3Client, PutObjectCommand, GetObjectCommand } = require('@aws-sdk/client-s3');
2-
const fs = require('fs');
3-
const path = require('path');
1+
const {
2+
S3Client,
3+
PutObjectCommand,
4+
GetObjectCommand,
5+
} = require("@aws-sdk/client-s3");
6+
const fs = require("fs");
7+
const path = require("path");
48

59
const s3Client = new S3Client({
6-
region: 'auto',
7-
endpoint: `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`,
10+
region: "auto",
11+
endpoint: process.env.R2_ENDPOINT,
812
credentials: {
913
accessKeyId: process.env.R2_ACCESS_KEY_ID,
1014
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY,
@@ -18,15 +22,17 @@ const GITHUB_REPO = process.env.GITHUB_REPOSITORY;
1822

1923
async function fetchExistingReleases() {
2024
try {
21-
const response = await s3Client.send(new GetObjectCommand({
22-
Bucket: BUCKET,
23-
Key: 'releases.json',
24-
}));
25+
const response = await s3Client.send(
26+
new GetObjectCommand({
27+
Bucket: BUCKET,
28+
Key: "releases.json",
29+
})
30+
);
2531
const body = await response.Body.transformToString();
2632
return JSON.parse(body);
2733
} catch (error) {
28-
if (error.name === 'NoSuchKey' || error.$metadata?.httpStatusCode === 404) {
29-
console.log('No existing releases.json found, creating new one');
34+
if (error.name === "NoSuchKey" || error.$metadata?.httpStatusCode === 404) {
35+
console.log("No existing releases.json found, creating new one");
3036
return { latestVersion: null, releases: [] };
3137
}
3238
throw error;
@@ -37,12 +43,14 @@ async function uploadFile(localPath, r2Key, contentType) {
3743
const fileBuffer = fs.readFileSync(localPath);
3844
const stats = fs.statSync(localPath);
3945

40-
await s3Client.send(new PutObjectCommand({
41-
Bucket: BUCKET,
42-
Key: r2Key,
43-
Body: fileBuffer,
44-
ContentType: contentType,
45-
}));
46+
await s3Client.send(
47+
new PutObjectCommand({
48+
Bucket: BUCKET,
49+
Key: r2Key,
50+
Body: fileBuffer,
51+
ContentType: contentType,
52+
})
53+
);
4654

4755
console.log(`Uploaded: ${r2Key} (${stats.size} bytes)`);
4856
return stats.size;
@@ -51,44 +59,44 @@ async function uploadFile(localPath, r2Key, contentType) {
5159
function findArtifacts(dir, pattern) {
5260
if (!fs.existsSync(dir)) return [];
5361
const files = fs.readdirSync(dir);
54-
return files.filter(f => pattern.test(f)).map(f => path.join(dir, f));
62+
return files.filter((f) => pattern.test(f)).map((f) => path.join(dir, f));
5563
}
5664

5765
async function main() {
58-
const artifactsDir = 'artifacts';
66+
const artifactsDir = "artifacts";
5967

6068
// Find all artifacts
6169
const artifacts = {
62-
windows: findArtifacts(
63-
path.join(artifactsDir, 'windows-builds'),
64-
/\.exe$/
65-
),
66-
macos: findArtifacts(
67-
path.join(artifactsDir, 'macos-builds'),
68-
/-x64\.dmg$/
69-
),
70+
windows: findArtifacts(path.join(artifactsDir, "windows-builds"), /\.exe$/),
71+
macos: findArtifacts(path.join(artifactsDir, "macos-builds"), /-x64\.dmg$/),
7072
macosArm: findArtifacts(
71-
path.join(artifactsDir, 'macos-builds'),
73+
path.join(artifactsDir, "macos-builds"),
7274
/-arm64\.dmg$/
7375
),
7476
linux: findArtifacts(
75-
path.join(artifactsDir, 'linux-builds'),
77+
path.join(artifactsDir, "linux-builds"),
7678
/\.AppImage$/
7779
),
7880
};
7981

80-
console.log('Found artifacts:');
82+
console.log("Found artifacts:");
8183
for (const [platform, files] of Object.entries(artifacts)) {
82-
console.log(` ${platform}: ${files.length > 0 ? files.map(f => path.basename(f)).join(', ') : 'none'}`);
84+
console.log(
85+
` ${platform}: ${
86+
files.length > 0
87+
? files.map((f) => path.basename(f)).join(", ")
88+
: "none"
89+
}`
90+
);
8391
}
8492

8593
// Upload each artifact to R2
8694
const assets = {};
8795
const contentTypes = {
88-
windows: 'application/x-msdownload',
89-
macos: 'application/x-apple-diskimage',
90-
macosArm: 'application/x-apple-diskimage',
91-
linux: 'application/x-executable',
96+
windows: "application/x-msdownload",
97+
macos: "application/x-apple-diskimage",
98+
macosArm: "application/x-apple-diskimage",
99+
linux: "application/x-executable",
92100
};
93101

94102
for (const [platform, files] of Object.entries(artifacts)) {
@@ -107,7 +115,7 @@ async function main() {
107115
url: `${PUBLIC_URL}/releases/${VERSION}/${filename}`,
108116
filename,
109117
size,
110-
arch: platform === 'macosArm' ? 'arm64' : 'x64',
118+
arch: platform === "macosArm" ? "arm64" : "x64",
111119
};
112120
}
113121

@@ -122,27 +130,31 @@ async function main() {
122130
};
123131

124132
// Remove existing entry for this version if re-running
125-
releasesData.releases = releasesData.releases.filter(r => r.version !== VERSION);
133+
releasesData.releases = releasesData.releases.filter(
134+
(r) => r.version !== VERSION
135+
);
126136

127137
// Prepend new release
128138
releasesData.releases.unshift(newRelease);
129139
releasesData.latestVersion = VERSION;
130140

131141
// Upload updated releases.json
132-
await s3Client.send(new PutObjectCommand({
133-
Bucket: BUCKET,
134-
Key: 'releases.json',
135-
Body: JSON.stringify(releasesData, null, 2),
136-
ContentType: 'application/json',
137-
CacheControl: 'public, max-age=60',
138-
}));
139-
140-
console.log('Successfully updated releases.json');
142+
await s3Client.send(
143+
new PutObjectCommand({
144+
Bucket: BUCKET,
145+
Key: "releases.json",
146+
Body: JSON.stringify(releasesData, null, 2),
147+
ContentType: "application/json",
148+
CacheControl: "public, max-age=60",
149+
})
150+
);
151+
152+
console.log("Successfully updated releases.json");
141153
console.log(`Latest version: ${VERSION}`);
142154
console.log(`Total releases: ${releasesData.releases.length}`);
143155
}
144156

145-
main().catch(err => {
146-
console.error('Failed to upload to R2:', err);
157+
main().catch((err) => {
158+
console.error("Failed to upload to R2:", err);
147159
process.exit(1);
148160
});

.github/workflows/release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ jobs:
129129

130130
- name: Upload to R2 and update releases.json
131131
env:
132-
R2_ACCOUNT_ID: ${{ secrets.R2_ACCOUNT_ID }}
132+
R2_ENDPOINT: ${{ secrets.R2_ENDPOINT }}
133133
R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }}
134134
R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }}
135135
R2_BUCKET_NAME: ${{ secrets.R2_BUCKET_NAME }}

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ node_modules/
77
# Build outputs
88
dist/
99
.next/
10-
node_modules
1110
.automaker/images/
1211
.automaker/
1312
/.automaker/*

apps/app/src/app/globals.css

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
@custom-variant catppuccin (&:is(.catppuccin *));
1313
@custom-variant onedark (&:is(.onedark *));
1414
@custom-variant synthwave (&:is(.synthwave *));
15+
@custom-variant red (&:is(.red *));
1516

1617
@theme inline {
1718
--color-background: var(--background);
@@ -1072,6 +1073,75 @@
10721073
--running-indicator-text: oklch(0.75 0.26 350);
10731074
}
10741075

1076+
/* Red Theme - Bold crimson/red aesthetic */
1077+
.red {
1078+
--background: oklch(0.12 0.03 15); /* Deep dark red-tinted black */
1079+
--background-50: oklch(0.12 0.03 15 / 0.5);
1080+
--background-80: oklch(0.12 0.03 15 / 0.8);
1081+
1082+
--foreground: oklch(0.95 0.01 15); /* Off-white with warm tint */
1083+
--foreground-secondary: oklch(0.7 0.02 15);
1084+
--foreground-muted: oklch(0.5 0.03 15);
1085+
1086+
--card: oklch(0.18 0.04 15); /* Slightly lighter dark red */
1087+
--card-foreground: oklch(0.95 0.01 15);
1088+
--popover: oklch(0.15 0.035 15);
1089+
--popover-foreground: oklch(0.95 0.01 15);
1090+
1091+
--primary: oklch(0.55 0.25 25); /* Vibrant crimson red */
1092+
--primary-foreground: oklch(0.98 0 0);
1093+
1094+
--brand-400: oklch(0.6 0.23 25);
1095+
--brand-500: oklch(0.55 0.25 25); /* Crimson */
1096+
--brand-600: oklch(0.5 0.27 25);
1097+
1098+
--secondary: oklch(0.22 0.05 15);
1099+
--secondary-foreground: oklch(0.95 0.01 15);
1100+
1101+
--muted: oklch(0.22 0.05 15);
1102+
--muted-foreground: oklch(0.5 0.03 15);
1103+
1104+
--accent: oklch(0.28 0.06 15);
1105+
--accent-foreground: oklch(0.95 0.01 15);
1106+
1107+
--destructive: oklch(0.6 0.28 30); /* Bright orange-red for destructive */
1108+
1109+
--border: oklch(0.35 0.08 15);
1110+
--border-glass: oklch(0.55 0.25 25 / 0.3);
1111+
1112+
--input: oklch(0.18 0.04 15);
1113+
--ring: oklch(0.55 0.25 25);
1114+
1115+
--chart-1: oklch(0.55 0.25 25); /* Crimson */
1116+
--chart-2: oklch(0.7 0.2 50); /* Orange */
1117+
--chart-3: oklch(0.8 0.18 80); /* Gold */
1118+
--chart-4: oklch(0.6 0.22 0); /* Pure red */
1119+
--chart-5: oklch(0.65 0.2 350); /* Pink-red */
1120+
1121+
--sidebar: oklch(0.1 0.025 15);
1122+
--sidebar-foreground: oklch(0.95 0.01 15);
1123+
--sidebar-primary: oklch(0.55 0.25 25);
1124+
--sidebar-primary-foreground: oklch(0.98 0 0);
1125+
--sidebar-accent: oklch(0.22 0.05 15);
1126+
--sidebar-accent-foreground: oklch(0.95 0.01 15);
1127+
--sidebar-border: oklch(0.35 0.08 15);
1128+
--sidebar-ring: oklch(0.55 0.25 25);
1129+
1130+
/* Action button colors - Red theme */
1131+
--action-view: oklch(0.55 0.25 25); /* Crimson */
1132+
--action-view-hover: oklch(0.5 0.27 25);
1133+
--action-followup: oklch(0.7 0.2 50); /* Orange */
1134+
--action-followup-hover: oklch(0.65 0.22 50);
1135+
--action-commit: oklch(0.6 0.2 140); /* Green for positive actions */
1136+
--action-commit-hover: oklch(0.55 0.22 140);
1137+
--action-verify: oklch(0.6 0.2 140); /* Green */
1138+
--action-verify-hover: oklch(0.55 0.22 140);
1139+
1140+
/* Running indicator - Crimson */
1141+
--running-indicator: oklch(0.55 0.25 25);
1142+
--running-indicator-text: oklch(0.6 0.23 25);
1143+
}
1144+
10751145
@layer base {
10761146
* {
10771147
@apply border-border outline-ring/50;
@@ -1327,6 +1397,39 @@
13271397
.text-running-indicator {
13281398
color: var(--running-indicator-text);
13291399
}
1400+
1401+
/* Animated border for in-progress cards */
1402+
@keyframes border-rotate {
1403+
0% {
1404+
background-position: 0% 50%;
1405+
}
1406+
50% {
1407+
background-position: 100% 50%;
1408+
}
1409+
100% {
1410+
background-position: 0% 50%;
1411+
}
1412+
}
1413+
1414+
.animated-border-wrapper {
1415+
position: relative;
1416+
border-radius: 0.75rem;
1417+
padding: 2px;
1418+
background: linear-gradient(
1419+
90deg,
1420+
var(--running-indicator),
1421+
color-mix(in oklch, var(--running-indicator), transparent 50%),
1422+
var(--running-indicator),
1423+
color-mix(in oklch, var(--running-indicator), transparent 50%),
1424+
var(--running-indicator)
1425+
);
1426+
background-size: 200% 100%;
1427+
animation: border-rotate 3s ease infinite;
1428+
}
1429+
1430+
.animated-border-wrapper > * {
1431+
border-radius: calc(0.75rem - 2px);
1432+
}
13301433
}
13311434

13321435
/* Retro Overrides for Utilities */

apps/app/src/app/page.tsx

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ import { RunningAgentsView } from "@/components/views/running-agents-view";
1515
import { useAppStore } from "@/store/app-store";
1616
import { useSetupStore } from "@/store/setup-store";
1717
import { getElectronAPI, isElectron } from "@/lib/electron";
18-
import { FileBrowserProvider, useFileBrowser, setGlobalFileBrowser } from "@/contexts/file-browser-context";
18+
import {
19+
FileBrowserProvider,
20+
useFileBrowser,
21+
setGlobalFileBrowser,
22+
} from "@/contexts/file-browser-context";
1923

2024
function HomeContent() {
2125
const {
@@ -24,6 +28,8 @@ function HomeContent() {
2428
setIpcConnected,
2529
theme,
2630
currentProject,
31+
previewTheme,
32+
getEffectiveTheme,
2733
} = useAppStore();
2834
const { isFirstRun, setupComplete } = useSetupStore();
2935
const [isMounted, setIsMounted] = useState(false);
@@ -72,9 +78,9 @@ function HomeContent() {
7278
};
7379
}, [handleStreamerPanelShortcut]);
7480

75-
// Compute the effective theme: project theme takes priority over global theme
76-
// This is reactive because it depends on currentProject and theme from the store
77-
const effectiveTheme = currentProject?.theme || theme;
81+
// Compute the effective theme: previewTheme takes priority, then project theme, then global theme
82+
// This is reactive because it depends on previewTheme, currentProject, and theme from the store
83+
const effectiveTheme = getEffectiveTheme();
7884

7985
// Prevent hydration issues
8086
useEffect(() => {
@@ -122,7 +128,7 @@ function HomeContent() {
122128
testConnection();
123129
}, [setIpcConnected]);
124130

125-
// Apply theme class to document (uses effective theme - project-specific or global)
131+
// Apply theme class to document (uses effective theme - preview, project-specific, or global)
126132
useEffect(() => {
127133
const root = document.documentElement;
128134
root.classList.remove(
@@ -137,7 +143,8 @@ function HomeContent() {
137143
"gruvbox",
138144
"catppuccin",
139145
"onedark",
140-
"synthwave"
146+
"synthwave",
147+
"red"
141148
);
142149

143150
if (effectiveTheme === "dark") {
@@ -162,6 +169,8 @@ function HomeContent() {
162169
root.classList.add("onedark");
163170
} else if (effectiveTheme === "synthwave") {
164171
root.classList.add("synthwave");
172+
} else if (effectiveTheme === "red") {
173+
root.classList.add("red");
165174
} else if (effectiveTheme === "light") {
166175
root.classList.add("light");
167176
} else if (effectiveTheme === "system") {
@@ -173,7 +182,7 @@ function HomeContent() {
173182
root.classList.add("light");
174183
}
175184
}
176-
}, [effectiveTheme]);
185+
}, [effectiveTheme, previewTheme, currentProject, theme]);
177186

178187
const renderView = () => {
179188
switch (currentView) {

0 commit comments

Comments
 (0)