style: fix prettier formatting issues
Swadhin Biswas committed 6 months ago
89ff316
1 parent 1656a88
commit 89ff316
7 files changed
+564 -128
Showing 7 changed files+564-128
▼scripts/setup-branch-protection.sh
+98-0
| @@ -0,0 +1,98 @@ | ||
1 | +#!/bin/bash | |
2 | + | |
3 | +# Branch Protection Setup Script for Linuxy | |
4 | +# Usage: ./scripts/setup-branch-protection.sh [GITHUB_TOKEN] | |
5 | + | |
6 | +set -e | |
7 | + | |
8 | +REPO="swadhinbiswas/linuxy" | |
9 | +BRANCH="main" | |
10 | +GITHUB_TOKEN="${1:-$GITHUB_TOKEN}" | |
11 | + | |
12 | +if [ -z "$GITHUB_TOKEN" ]; then | |
13 | + echo "Error: GitHub token required" | |
14 | + echo "Usage: $0 <GITHUB_TOKEN>" | |
15 | + echo "Or set GITHUB_TOKEN environment variable" | |
16 | + exit 1 | |
17 | +fi | |
18 | + | |
19 | +echo "Setting up branch protection for $REPO/$BRANCH..." | |
20 | + | |
21 | +# API Base URL | |
22 | +API="https://api.github.com" | |
23 | + | |
24 | +# 1. Get branch protection endpoint | |
25 | +PROTECTION_URL="$API/repos/$REPO/branches/$BRANCH/protection" | |
26 | + | |
27 | +# 2. Require pull request + required reviews | |
28 | +echo "✓ Configuring PR requirements..." | |
29 | +curl -s -X PUT "$PROTECTION_URL/required_pull_request_reviews" \ | |
30 | + -H "Authorization: token $GITHUB_TOKEN" \ | |
31 | + -H "Accept: application/vnd.github+json" \ | |
32 | + -H "Content-Type: application/json" \ | |
33 | + -d '{ | |
34 | + "dismiss_stale_reviews": true, | |
35 | + "require_code_owner_reviews": true, | |
36 | + "required_approving_review_count": 1 | |
37 | + }' | |
38 | + | |
39 | +# 3. Require status checks | |
40 | +echo "✓ Configuring status checks..." | |
41 | +curl -s -X PUT "$PROTECTION_URL/required_status_checks" \ | |
42 | + -H "Authorization: token $GITHUB_TOKEN" \ | |
43 | + -H "Accept: application/vnd.github+json" \ | |
44 | + -H "Content-Type: application/json" \ | |
45 | + -d '{ | |
46 | + "strict": true, | |
47 | + "contexts": [ | |
48 | + "Frontend Lint & Build", | |
49 | + "Rust Check & Test", | |
50 | + "Security Audit", | |
51 | + "Verify CSP Configuration", | |
52 | + "Integration Tests" | |
53 | + ] | |
54 | + }' | |
55 | + | |
56 | +# 4. Require conversation resolution | |
57 | +echo "✓ Configuring conversation resolution..." | |
58 | +curl -s -X PUT "$PROTECTION_URL" \ | |
59 | + -H "Authorization: token $GITHUB_TOKEN" \ | |
60 | + -H "Accept: application/vnd.github+json" \ | |
61 | + -H "Content-Type: application/json" \ | |
62 | + -d '{ | |
63 | + "required_status_checks": null, | |
64 | + "enforce_admins": true, | |
65 | + "required_pull_request_reviews": null, | |
66 | + "restrictions": null, | |
67 | + "allow_force_pushes": false, | |
68 | + "allow_deletions": false, | |
69 | + "require_conversation_resolution": true | |
70 | + }' | |
71 | + | |
72 | +# 5. Include administrators | |
73 | +echo "✓ Setting admin enforcement..." | |
74 | +curl -s -X PUT "$PROTECTION_URL/enforce_admins" \ | |
75 | + -H "Authorization: token $GITHUB_TOKEN" \ | |
76 | + -H "Accept: application/vnd.github+json" \ | |
77 | + -H "Content-Type: application/json" \ | |
78 | + -d '{ | |
79 | + "enabled": true | |
80 | + }' | |
81 | + | |
82 | +# 6. Configure branch restrictions (optional - requires team/org) | |
83 | +echo "✓ Configuring push restrictions..." | |
84 | +curl -s -X DELETE "$PROTECTION_URL/restrictions" \ | |
85 | + -H "Authorization: token $GITHUB_TOKEN" \ | |
86 | + -H "Accept: application/vnd.github+json" || true | |
87 | + | |
88 | +echo "" | |
89 | +echo "✅ Branch protection configured for main!" | |
90 | +echo "" | |
91 | +echo "Required Status Checks (to be auto-detected after first run):" | |
92 | +echo " - Frontend Lint & Build" | |
93 | +echo " - Rust Check & Test" | |
94 | +echo " - Security Audit" | |
95 | +echo " - Verify CSP Configuration" | |
96 | +echo " - Integration Tests" | |
97 | +echo "" | |
98 | +echo "Note: Status checks will be auto-detected after CI runs once." | |
0 | 99 | |
▼scripts/setup-develop-branch.sh
+70-0
| @@ -0,0 +1,70 @@ | ||
1 | +#!/bin/bash | |
2 | + | |
3 | +# Branch Protection Setup Script for Linuxy - Develop Branch | |
4 | +# Usage: ./scripts/setup-develop-branch.sh [GITHUB_TOKEN] | |
5 | + | |
6 | +set -e | |
7 | + | |
8 | +REPO="swadhinbiswas/linuxy" | |
9 | +BRANCH="develop" | |
10 | +GITHUB_TOKEN="${1:-$GITHUB_TOKEN}" | |
11 | + | |
12 | +if [ -z "$GITHUB_TOKEN" ]; then | |
13 | + echo "Error: GitHub token required" | |
14 | + echo "Usage: $0 <GITHUB_TOKEN>" | |
15 | + echo "Or set GITHUB_TOKEN environment variable" | |
16 | + exit 1 | |
17 | +fi | |
18 | + | |
19 | +echo "Setting up branch protection for $REPO/$BRANCH..." | |
20 | + | |
21 | +# Check if branch exists | |
22 | +BRANCH_EXISTS=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \ | |
23 | + "$API/repos/$REPO/branches/$BRANCH" | jq -r '.name // empty') | |
24 | + | |
25 | +if [ -z "$BRANCH_EXISTS" ]; then | |
26 | + echo "Branch 'develop' doesn't exist. Creating it from main..." | |
27 | + # Create develop branch from main | |
28 | + MAIN_SHA=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \ | |
29 | + "$API/repos/$REPO/branches/main" | jq -r '.commit.sha') | |
30 | + | |
31 | + curl -s -X POST "$API/repos/$REPO/git/refs/refs/heads/$BRANCH" \ | |
32 | + -H "Authorization: token $GITHUB_TOKEN" \ | |
33 | + -H "Accept: application/vnd.github+json" \ | |
34 | + -d "{\"sha\": \"$MAIN_SHA\", \"ref\": \"refs/heads/$BRANCH\"}" | |
35 | + | |
36 | + echo "✓ Created develop branch" | |
37 | +fi | |
38 | + | |
39 | +# API Base URL | |
40 | +API="https://api.github.com" | |
41 | +PROTECTION_URL="$API/repos/$REPO/branches/$BRANCH/protection" | |
42 | + | |
43 | +echo "✓ Configuring protection for develop..." | |
44 | + | |
45 | +# Basic protection for develop | |
46 | +curl -s -X PUT "$PROTECTION_URL" \ | |
47 | + -H "Authorization: token $GITHUB_TOKEN" \ | |
48 | + -H "Accept: application/vnd.github+json" \ | |
49 | + -H "Content-Type: application/json" \ | |
50 | + -d '{ | |
51 | + "required_status_checks": { | |
52 | + "strict": false, | |
53 | + "contexts": [ | |
54 | + "Frontend Lint & Build", | |
55 | + "Rust Check & Test" | |
56 | + ] | |
57 | + }, | |
58 | + "enforce_admins": true, | |
59 | + "required_pull_request_reviews": { | |
60 | + "dismiss_stale_reviews": false, | |
61 | + "required_approving_review_count": 1 | |
62 | + }, | |
63 | + "restrictions": null, | |
64 | + "allow_force_pushes": false, | |
65 | + "allow_deletions": false, | |
66 | + "require_conversation_resolution": false | |
67 | + }' | |
68 | + | |
69 | +echo "" | |
70 | +echo "✅ Branch protection configured for develop!" | |
0 | 71 | |
▼src/components/AppGrid.tsx
+125-31
| @@ -1,7 +1,7 @@ | ||
1 | 1 | import React from "react"; |
2 | 2 | import { Play, Trash2, ShieldAlert, ShieldCheck, RefreshCw } from "lucide-react"; |
3 | 3 | import { AppInfo } from "../App"; |
4 | -import { convertFileSrc } from '@tauri-apps/api/tauri'; | |
4 | +import { convertFileSrc } from "@tauri-apps/api/tauri"; | |
5 | 5 | |
6 | 6 | interface AppGridProps { |
7 | 7 | apps: AppInfo[]; |
| @@ -12,7 +12,14 @@ interface AppGridProps { | ||
12 | 12 | updatesEnabled: boolean; |
13 | 13 | } |
14 | 14 | |
15 | -const AppGrid: React.FC<AppGridProps> = ({ apps, onLaunch, onRemove, onToggleSandbox, onUpdate, updatesEnabled }) => { | |
15 | +const AppGrid: React.FC<AppGridProps> = ({ | |
16 | + apps, | |
17 | + onLaunch, | |
18 | + onRemove, | |
19 | + onToggleSandbox, | |
20 | + onUpdate, | |
21 | + updatesEnabled, | |
22 | +}) => { | |
16 | 23 | const formatBytes = (bytes: number) => { |
17 | 24 | if (bytes === 0) return "0 B"; |
18 | 25 | const k = 1024; |
| @@ -26,59 +33,146 @@ const AppGrid: React.FC<AppGridProps> = ({ apps, onLaunch, onRemove, onToggleSan | ||
26 | 33 | }; |
27 | 34 | |
28 | 35 | if (apps.length === 0) { |
29 | - return <div style={{ color: "var(--text-muted)", marginTop: "20px" }}>No AppImages installed yet. Drag and drop one to install!</div>; | |
36 | + return ( | |
37 | + <div style={{ color: "var(--text-muted)", marginTop: "20px" }}> | |
38 | + No AppImages installed yet. Drag and drop one to install! | |
39 | + </div> | |
40 | + ); | |
30 | 41 | } |
31 | 42 | |
32 | 43 | return ( |
33 | - <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(220px, 1fr))", gap: "20px", marginTop: "20px" }}> | |
44 | + <div | |
45 | + style={{ | |
46 | + display: "grid", | |
47 | + gridTemplateColumns: "repeat(auto-fill, minmax(220px, 1fr))", | |
48 | + gap: "20px", | |
49 | + marginTop: "20px", | |
50 | + }} | |
51 | + > | |
34 | 52 | {apps.map((app) => ( |
35 | - <div key={app.path} style={{ | |
36 | - background: "var(--bg-card)", | |
37 | - borderRadius: "8px", | |
38 | - padding: "15px", | |
39 | - display: "flex", | |
40 | - flexDirection: "column", | |
41 | - alignItems: "center", | |
42 | - position: "relative", | |
43 | - border: "1px solid var(--border-color)", | |
44 | - transition: "background-color 0.3s, border-color 0.3s" | |
45 | - }}> | |
46 | - | |
47 | - <button | |
53 | + <div | |
54 | + key={app.path} | |
55 | + style={{ | |
56 | + background: "var(--bg-card)", | |
57 | + borderRadius: "8px", | |
58 | + padding: "15px", | |
59 | + display: "flex", | |
60 | + flexDirection: "column", | |
61 | + alignItems: "center", | |
62 | + position: "relative", | |
63 | + border: "1px solid var(--border-color)", | |
64 | + transition: "background-color 0.3s, border-color 0.3s", | |
65 | + }} | |
66 | + > | |
67 | + <button | |
48 | 68 | title={app.sandboxed ? "Sandboxed via Firejail" : "Not sandboxed"} |
49 | 69 | onClick={() => onToggleSandbox(app.desktop_path, !app.sandboxed)} |
50 | - style={{ position: "absolute", top: 10, right: 10, background: "transparent", border: "none", cursor: "pointer", color: app.sandboxed ? "var(--accent-color)" : "var(--text-muted)" }} | |
70 | + style={{ | |
71 | + position: "absolute", | |
72 | + top: 10, | |
73 | + right: 10, | |
74 | + background: "transparent", | |
75 | + border: "none", | |
76 | + cursor: "pointer", | |
77 | + color: app.sandboxed ? "var(--accent-color)" : "var(--text-muted)", | |
78 | + }} | |
51 | 79 | > |
52 | 80 | {app.sandboxed ? <ShieldCheck size={20} /> : <ShieldAlert size={20} />} |
53 | 81 | </button> |
54 | 82 | |
55 | 83 | {app.icon ? ( |
56 | - <img src={convertFileSrc(app.icon)} alt={app.name} style={{ width: "64px", height: "64px", marginBottom: "15px", objectFit: "contain" }} /> | |
84 | + <img | |
85 | + src={convertFileSrc(app.icon)} | |
86 | + alt={app.name} | |
87 | + style={{ width: "64px", height: "64px", marginBottom: "15px", objectFit: "contain" }} | |
88 | + /> | |
57 | 89 | ) : ( |
58 | - <div style={{ width: "64px", height: "64px", background: "var(--border-color)", borderRadius: "12px", marginBottom: "15px" }}></div> | |
90 | + <div | |
91 | + style={{ | |
92 | + width: "64px", | |
93 | + height: "64px", | |
94 | + background: "var(--border-color)", | |
95 | + borderRadius: "12px", | |
96 | + marginBottom: "15px", | |
97 | + }} | |
98 | + ></div> | |
59 | 99 | )} |
60 | - <div style={{ fontWeight: "bold", textAlign: "center", marginBottom: "5px", wordBreak: "break-all", color: "var(--text-primary)" }}>{app.name}</div> | |
61 | - | |
100 | + <div | |
101 | + style={{ | |
102 | + fontWeight: "bold", | |
103 | + textAlign: "center", | |
104 | + marginBottom: "5px", | |
105 | + wordBreak: "break-all", | |
106 | + color: "var(--text-primary)", | |
107 | + }} | |
108 | + > | |
109 | + {app.name} | |
110 | + </div> | |
111 | + | |
62 | 112 | <div style={{ fontSize: "11px", color: "var(--text-muted)", marginBottom: "10px" }}> |
63 | 113 | {formatBytes(app.size_bytes)} • {formatDate(app.installed_at)} |
64 | 114 | </div> |
65 | - | |
66 | - <div style={{ display: "flex", flexWrap: "wrap", gap: "10px", marginTop: "auto", paddingTop: "15px", justifyContent: "center" }}> | |
67 | - <button | |
115 | + | |
116 | + <div | |
117 | + style={{ | |
118 | + display: "flex", | |
119 | + flexWrap: "wrap", | |
120 | + gap: "10px", | |
121 | + marginTop: "auto", | |
122 | + paddingTop: "15px", | |
123 | + justifyContent: "center", | |
124 | + }} | |
125 | + > | |
126 | + <button | |
68 | 127 | onClick={() => onLaunch(app.path)} |
69 | - style={{ background: "var(--accent-color)", color: "#fff", border: "none", padding: "8px", borderRadius: "4px", cursor: "pointer", display: "flex", alignItems: "center" }}> | |
128 | + style={{ | |
129 | + background: "var(--accent-color)", | |
130 | + color: "#fff", | |
131 | + border: "none", | |
132 | + padding: "8px", | |
133 | + borderRadius: "4px", | |
134 | + cursor: "pointer", | |
135 | + display: "flex", | |
136 | + alignItems: "center", | |
137 | + }} | |
138 | + > | |
70 | 139 | <Play size={16} /> <span style={{ marginLeft: "5px", fontSize: "12px" }}>Launch</span> |
71 | 140 | </button> |
72 | - <button | |
141 | + <button | |
73 | 142 | onClick={() => onUpdate(app.path)} |
74 | 143 | disabled={!updatesEnabled} |
75 | - style={{ background: "var(--info-color)", color: "#fff", border: "none", padding: "8px", borderRadius: "4px", cursor: updatesEnabled ? "pointer" : "not-allowed", display: "flex", alignItems: "center", opacity: updatesEnabled ? 1 : 0.55 }} | |
76 | - title={updatesEnabled ? "Check for updates" : "Install appimageupdatetool to enable updates"}> | |
144 | + style={{ | |
145 | + background: "var(--info-color)", | |
146 | + color: "#fff", | |
147 | + border: "none", | |
148 | + padding: "8px", | |
149 | + borderRadius: "4px", | |
150 | + cursor: updatesEnabled ? "pointer" : "not-allowed", | |
151 | + display: "flex", | |
152 | + alignItems: "center", | |
153 | + opacity: updatesEnabled ? 1 : 0.55, | |
154 | + }} | |
155 | + title={ | |
156 | + updatesEnabled | |
157 | + ? "Check for updates" | |
158 | + : "Install appimageupdatetool to enable updates" | |
159 | + } | |
160 | + > | |
77 | 161 | <RefreshCw size={16} /> |
78 | 162 | </button> |
79 | - <button | |
163 | + <button | |
80 | 164 | onClick={() => onRemove(app)} |
81 | - style={{ background: "var(--danger-color)", color: "#fff", border: "none", padding: "8px", borderRadius: "4px", cursor: "pointer", display: "flex", alignItems: "center" }}> | |
165 | + style={{ | |
166 | + background: "var(--danger-color)", | |
167 | + color: "#fff", | |
168 | + border: "none", | |
169 | + padding: "8px", | |
170 | + borderRadius: "4px", | |
171 | + cursor: "pointer", | |
172 | + display: "flex", | |
173 | + alignItems: "center", | |
174 | + }} | |
175 | + > | |
82 | 176 | <Trash2 size={16} /> |
83 | 177 | </button> |
84 | 178 | </div> |
85 | 179 | |
▼src/components/DropZone.tsx
+3-3
| @@ -11,7 +11,7 @@ const DropZone: React.FC<DropZoneProps> = ({ onInstall }) => { | ||
11 | 11 | try { |
12 | 12 | const selected = await open({ |
13 | 13 | multiple: false, |
14 | - filters: [{ name: "AppImage", extensions: ["AppImage", "appimage"] }] | |
14 | + filters: [{ name: "AppImage", extensions: ["AppImage", "appimage"] }], | |
15 | 15 | }); |
16 | 16 | if (typeof selected === "string") { |
17 | 17 | onInstall(selected); |
| @@ -22,7 +22,7 @@ const DropZone: React.FC<DropZoneProps> = ({ onInstall }) => { | ||
22 | 22 | }; |
23 | 23 | |
24 | 24 | return ( |
25 | - <div | |
25 | + <div | |
26 | 26 | style={{ |
27 | 27 | border: "2px dashed var(--border-color)", |
28 | 28 | borderRadius: "8px", |
| @@ -32,7 +32,7 @@ const DropZone: React.FC<DropZoneProps> = ({ onInstall }) => { | ||
32 | 32 | marginBottom: "20px", |
33 | 33 | background: "var(--bg-input)", |
34 | 34 | color: "var(--text-secondary)", |
35 | - transition: "background-color 0.3s, border-color 0.3s" | |
35 | + transition: "background-color 0.3s, border-color 0.3s", | |
36 | 36 | }} |
37 | 37 | onClick={handleSelectFile} |
38 | 38 | > |
39 | 39 | |
▼src/components/Sidebar.tsx
+39-24
| @@ -9,39 +9,54 @@ interface SidebarProps { | ||
9 | 9 | |
10 | 10 | const Sidebar: React.FC<SidebarProps> = ({ currentView, onViewChange, onRefresh }) => { |
11 | 11 | return ( |
12 | - <div style={{ | |
13 | - width: "250px", | |
14 | - background: "var(--bg-sidebar)", | |
15 | - display: "flex", | |
16 | - flexDirection: "column", | |
17 | - padding: "20px 0", | |
18 | - borderRight: "1px solid var(--border-color)", | |
19 | - transition: "background-color 0.3s, border-color 0.3s" | |
20 | - }}> | |
21 | - <div style={{ | |
22 | - padding: "0 20px", | |
23 | - marginBottom: "30px", | |
24 | - fontSize: "24px", | |
25 | - fontWeight: "bold", | |
26 | - color: "var(--text-primary)" | |
27 | - }}>linuxy</div> | |
28 | - | |
29 | - <button | |
30 | - style={{ ...btnStyle, background: currentView === "library" ? "rgba(255, 255, 255, 0.1)" : "transparent" }} | |
12 | + <div | |
13 | + style={{ | |
14 | + width: "250px", | |
15 | + background: "var(--bg-sidebar)", | |
16 | + display: "flex", | |
17 | + flexDirection: "column", | |
18 | + padding: "20px 0", | |
19 | + borderRight: "1px solid var(--border-color)", | |
20 | + transition: "background-color 0.3s, border-color 0.3s", | |
21 | + }} | |
22 | + > | |
23 | + <div | |
24 | + style={{ | |
25 | + padding: "0 20px", | |
26 | + marginBottom: "30px", | |
27 | + fontSize: "24px", | |
28 | + fontWeight: "bold", | |
29 | + color: "var(--text-primary)", | |
30 | + }} | |
31 | + > | |
32 | + linuxy | |
33 | + </div> | |
34 | + | |
35 | + <button | |
36 | + style={{ | |
37 | + ...btnStyle, | |
38 | + background: currentView === "library" ? "rgba(255, 255, 255, 0.1)" : "transparent", | |
39 | + }} | |
31 | 40 | onClick={() => onViewChange("library")} |
32 | 41 | > |
33 | 42 | <LayoutGrid size={18} style={{ marginRight: 10 }} /> Library |
34 | 43 | </button> |
35 | 44 | |
36 | - <button | |
37 | - style={{ ...btnStyle, background: currentView === "discover" ? "rgba(255, 255, 255, 0.1)" : "transparent" }} | |
45 | + <button | |
46 | + style={{ | |
47 | + ...btnStyle, | |
48 | + background: currentView === "discover" ? "rgba(255, 255, 255, 0.1)" : "transparent", | |
49 | + }} | |
38 | 50 | onClick={() => onViewChange("discover")} |
39 | 51 | > |
40 | 52 | <Compass size={18} style={{ marginRight: 10 }} /> Discover |
41 | 53 | </button> |
42 | 54 | |
43 | - <button | |
44 | - style={{ ...btnStyle, background: currentView === "settings" ? "rgba(255, 255, 255, 0.1)" : "transparent" }} | |
55 | + <button | |
56 | + style={{ | |
57 | + ...btnStyle, | |
58 | + background: currentView === "settings" ? "rgba(255, 255, 255, 0.1)" : "transparent", | |
59 | + }} | |
45 | 60 | onClick={() => onViewChange("settings")} |
46 | 61 | > |
47 | 62 | <Settings size={18} style={{ marginRight: 10 }} /> Settings |
| @@ -66,7 +81,7 @@ const btnStyle: React.CSSProperties = { | ||
66 | 81 | textAlign: "left", |
67 | 82 | fontSize: "16px", |
68 | 83 | width: "100%", |
69 | - transition: "background 0.2s" | |
84 | + transition: "background 0.2s", | |
70 | 85 | }; |
71 | 86 | |
72 | 87 | export default Sidebar; |
73 | 88 | |
▼src/components/Storefront.tsx
+226-69
| @@ -33,7 +33,9 @@ const Storefront: React.FC = () => { | ||
33 | 33 | const data = await response.json(); |
34 | 34 | setApps(data.items || []); |
35 | 35 | } else { |
36 | - const response = await fetch("https://api.github.com/search/repositories?q=topic:appimage&sort=stars&order=desc&per_page=100"); | |
36 | + const response = await fetch( | |
37 | + "https://api.github.com/search/repositories?q=topic:appimage&sort=stars&order=desc&per_page=100" | |
38 | + ); | |
37 | 39 | if (!response.ok) throw new Error("Failed to fetch GitHub data"); |
38 | 40 | const data = await response.json(); |
39 | 41 | const githubApps = data.items.map((item: any) => ({ |
| @@ -43,7 +45,7 @@ const Storefront: React.FC = () => { | ||
43 | 45 | links: [{ type: "GitHub", url: item.html_url }], |
44 | 46 | github_url: item.html_url, |
45 | 47 | stargazers_count: item.stargazers_count, |
46 | - icons: [item.owner.avatar_url] | |
48 | + icons: [item.owner.avatar_url], | |
47 | 49 | })); |
48 | 50 | setApps(githubApps); |
49 | 51 | } |
| @@ -55,100 +57,226 @@ const Storefront: React.FC = () => { | ||
55 | 57 | } |
56 | 58 | }; |
57 | 59 | |
58 | - const filteredApps = apps.filter(app => | |
59 | - app.name.toLowerCase().includes(searchTerm.toLowerCase()) || | |
60 | - (app.description && app.description.toLowerCase().includes(searchTerm.toLowerCase())) | |
60 | + const filteredApps = apps.filter( | |
61 | + (app) => | |
62 | + app.name.toLowerCase().includes(searchTerm.toLowerCase()) || | |
63 | + (app.description && app.description.toLowerCase().includes(searchTerm.toLowerCase())) | |
61 | 64 | ); |
62 | 65 | |
63 | 66 | return ( |
64 | 67 | <div style={{ paddingBottom: "40px" }}> |
65 | - <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: "20px" }}> | |
68 | + <div | |
69 | + style={{ | |
70 | + display: "flex", | |
71 | + justifyContent: "space-between", | |
72 | + alignItems: "center", | |
73 | + marginBottom: "20px", | |
74 | + }} | |
75 | + > | |
66 | 76 | <div> |
67 | 77 | <h2 style={{ color: "var(--text-primary)" }}>Discover AppImages</h2> |
68 | 78 | <p style={{ color: "var(--text-secondary)" }}> |
69 | - {source === "appimagehub" ? "Browsing AppImageHub Catalog" : "Browsing Popular GitHub AppImage Projects"} | |
79 | + {source === "appimagehub" | |
80 | + ? "Browsing AppImageHub Catalog" | |
81 | + : "Browsing Popular GitHub AppImage Projects"} | |
70 | 82 | </p> |
71 | 83 | </div> |
72 | - <div style={{ display: "flex", gap: "10px", background: "var(--bg-input)", padding: "5px", borderRadius: "8px", border: "1px solid var(--border-color)" }}> | |
73 | - <button | |
84 | + <div | |
85 | + style={{ | |
86 | + display: "flex", | |
87 | + gap: "10px", | |
88 | + background: "var(--bg-input)", | |
89 | + padding: "5px", | |
90 | + borderRadius: "8px", | |
91 | + border: "1px solid var(--border-color)", | |
92 | + }} | |
93 | + > | |
94 | + <button | |
74 | 95 | onClick={() => setSource("appimagehub")} |
75 | - style={{ | |
76 | - display: "flex", alignItems: "center", gap: "8px", padding: "8px 12px", border: "none", borderRadius: "6px", cursor: "pointer", | |
77 | - background: source === "appimagehub" ? "var(--bg-card)" : "transparent", color: "var(--text-primary)", | |
78 | - boxShadow: source === "appimagehub" ? "0 2px 4px rgba(0,0,0,0.2)" : "none" | |
79 | - }}> | |
96 | + style={{ | |
97 | + display: "flex", | |
98 | + alignItems: "center", | |
99 | + gap: "8px", | |
100 | + padding: "8px 12px", | |
101 | + border: "none", | |
102 | + borderRadius: "6px", | |
103 | + cursor: "pointer", | |
104 | + background: source === "appimagehub" ? "var(--bg-card)" : "transparent", | |
105 | + color: "var(--text-primary)", | |
106 | + boxShadow: source === "appimagehub" ? "0 2px 4px rgba(0,0,0,0.2)" : "none", | |
107 | + }} | |
108 | + > | |
80 | 109 | <Globe size={16} /> Hub |
81 | 110 | </button> |
82 | - <button | |
111 | + <button | |
83 | 112 | onClick={() => setSource("github")} |
84 | - style={{ | |
85 | - display: "flex", alignItems: "center", gap: "8px", padding: "8px 12px", border: "none", borderRadius: "6px", cursor: "pointer", | |
86 | - background: source === "github" ? "var(--bg-card)" : "transparent", color: "var(--text-primary)", | |
87 | - boxShadow: source === "github" ? "0 2px 4px rgba(0,0,0,0.2)" : "none" | |
88 | - }}> | |
113 | + style={{ | |
114 | + display: "flex", | |
115 | + alignItems: "center", | |
116 | + gap: "8px", | |
117 | + padding: "8px 12px", | |
118 | + border: "none", | |
119 | + borderRadius: "6px", | |
120 | + cursor: "pointer", | |
121 | + background: source === "github" ? "var(--bg-card)" : "transparent", | |
122 | + color: "var(--text-primary)", | |
123 | + boxShadow: source === "github" ? "0 2px 4px rgba(0,0,0,0.2)" : "none", | |
124 | + }} | |
125 | + > | |
89 | 126 | <Github size={16} /> GitHub |
90 | 127 | </button> |
91 | 128 | </div> |
92 | 129 | </div> |
93 | 130 | |
94 | 131 | <div style={{ position: "relative", marginBottom: "20px", maxWidth: "400px" }}> |
95 | - <Search size={18} style={{ position: "absolute", left: "10px", top: "10px", color: "var(--text-muted)" }} /> | |
96 | - <input | |
97 | - type="text" | |
98 | - placeholder="Search catalog..." | |
132 | + <Search | |
133 | + size={18} | |
134 | + style={{ position: "absolute", left: "10px", top: "10px", color: "var(--text-muted)" }} | |
135 | + /> | |
136 | + <input | |
137 | + type="text" | |
138 | + placeholder="Search catalog..." | |
99 | 139 | value={searchTerm} |
100 | 140 | onChange={(e) => setSearchTerm(e.target.value)} |
101 | - style={{ | |
102 | - width: "100%", padding: "10px 10px 10px 35px", background: "var(--bg-input)", border: "1px solid var(--border-color)", borderRadius: "6px", color: "var(--text-primary)", fontSize: "16px" | |
141 | + style={{ | |
142 | + width: "100%", | |
143 | + padding: "10px 10px 10px 35px", | |
144 | + background: "var(--bg-input)", | |
145 | + border: "1px solid var(--border-color)", | |
146 | + borderRadius: "6px", | |
147 | + color: "var(--text-primary)", | |
148 | + fontSize: "16px", | |
103 | 149 | }} |
104 | 150 | /> |
105 | 151 | </div> |
106 | 152 | |
107 | - {loading && <div style={{ textAlign: "center", padding: "40px", color: "var(--text-secondary)" }}>Loading {source === "github" ? "GitHub projects" : "Hub catalog"}...</div>} | |
108 | - {error && <div style={{ color: "var(--danger-color)", padding: "20px", background: "rgba(255,0,0,0.1)", borderRadius: "8px" }}>{error}</div>} | |
153 | + {loading && ( | |
154 | + <div style={{ textAlign: "center", padding: "40px", color: "var(--text-secondary)" }}> | |
155 | + Loading {source === "github" ? "GitHub projects" : "Hub catalog"}... | |
156 | + </div> | |
157 | + )} | |
158 | + {error && ( | |
159 | + <div | |
160 | + style={{ | |
161 | + color: "var(--danger-color)", | |
162 | + padding: "20px", | |
163 | + background: "rgba(255,0,0,0.1)", | |
164 | + borderRadius: "8px", | |
165 | + }} | |
166 | + > | |
167 | + {error} | |
168 | + </div> | |
169 | + )} | |
109 | 170 | |
110 | - <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(300px, 1fr))", gap: "20px" }}> | |
171 | + <div | |
172 | + style={{ | |
173 | + display: "grid", | |
174 | + gridTemplateColumns: "repeat(auto-fill, minmax(300px, 1fr))", | |
175 | + gap: "20px", | |
176 | + }} | |
177 | + > | |
111 | 178 | {filteredApps.map((app, idx) => { |
112 | - const githubLink = app.links?.find(l => l.type.toLowerCase() === "github" || l.type.toLowerCase() === "homepage"); | |
113 | - const downloadLink = app.links?.find(l => l.type.toLowerCase() === "download") || githubLink; | |
114 | - | |
179 | + const githubLink = app.links?.find( | |
180 | + (l) => l.type.toLowerCase() === "github" || l.type.toLowerCase() === "homepage" | |
181 | + ); | |
182 | + const downloadLink = | |
183 | + app.links?.find((l) => l.type.toLowerCase() === "download") || githubLink; | |
184 | + | |
115 | 185 | return ( |
116 | - <div key={idx} style={{ | |
117 | - background: "var(--bg-card)", | |
118 | - borderRadius: "8px", | |
119 | - padding: "20px", | |
120 | - display: "flex", | |
121 | - flexDirection: "column", | |
122 | - border: "1px solid var(--border-color)", | |
123 | - transition: "background-color 0.3s, border-color 0.3s" | |
124 | - }}> | |
186 | + <div | |
187 | + key={idx} | |
188 | + style={{ | |
189 | + background: "var(--bg-card)", | |
190 | + borderRadius: "8px", | |
191 | + padding: "20px", | |
192 | + display: "flex", | |
193 | + flexDirection: "column", | |
194 | + border: "1px solid var(--border-color)", | |
195 | + transition: "background-color 0.3s, border-color 0.3s", | |
196 | + }} | |
197 | + > | |
125 | 198 | <div style={{ display: "flex", alignItems: "center", marginBottom: "15px" }}> |
126 | 199 | {app.icons && app.icons.length > 0 ? ( |
127 | - <img | |
128 | - src={app.icons[0].startsWith("http") ? app.icons[0] : `https://appimage.github.io/database/${app.icons[0]}`} | |
129 | - alt={app.name} | |
130 | - style={{ width: "56px", height: "56px", marginRight: "15px", objectFit: "contain", borderRadius: "8px" }} | |
200 | + <img | |
201 | + src={ | |
202 | + app.icons[0].startsWith("http") | |
203 | + ? app.icons[0] | |
204 | + : `https://appimage.github.io/database/${app.icons[0]}` | |
205 | + } | |
206 | + alt={app.name} | |
207 | + style={{ | |
208 | + width: "56px", | |
209 | + height: "56px", | |
210 | + marginRight: "15px", | |
211 | + objectFit: "contain", | |
212 | + borderRadius: "8px", | |
213 | + }} | |
131 | 214 | /> |
132 | 215 | ) : ( |
133 | - <div style={{ width: "56px", height: "56px", background: "var(--border-color)", borderRadius: "8px", marginRight: "15px", display: "flex", alignItems: "center", justifyContent: "center" }}> | |
216 | + <div | |
217 | + style={{ | |
218 | + width: "56px", | |
219 | + height: "56px", | |
220 | + background: "var(--border-color)", | |
221 | + borderRadius: "8px", | |
222 | + marginRight: "15px", | |
223 | + display: "flex", | |
224 | + alignItems: "center", | |
225 | + justifyContent: "center", | |
226 | + }} | |
227 | + > | |
134 | 228 | <Globe size={24} color="var(--text-muted)" /> |
135 | 229 | </div> |
136 | 230 | )} |
137 | 231 | <div style={{ overflow: "hidden" }}> |
138 | - <div style={{ fontWeight: "bold", fontSize: "18px", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis", color: "var(--text-primary)" }}>{app.name}</div> | |
139 | - <div style={{ fontSize: "13px", color: "var(--text-secondary)", display: "flex", alignItems: "center", gap: "5px" }}> | |
140 | - {source === "github" && <Github size={12} />} {app.authors?.[0]?.name || "Unknown"} | |
141 | - {app.stargazers_count !== undefined && <span style={{ color: "#e3b341" }}>★ {app.stargazers_count}</span>} | |
232 | + <div | |
233 | + style={{ | |
234 | + fontWeight: "bold", | |
235 | + fontSize: "18px", | |
236 | + whiteSpace: "nowrap", | |
237 | + overflow: "hidden", | |
238 | + textOverflow: "ellipsis", | |
239 | + color: "var(--text-primary)", | |
240 | + }} | |
241 | + > | |
242 | + {app.name} | |
243 | + </div> | |
244 | + <div | |
245 | + style={{ | |
246 | + fontSize: "13px", | |
247 | + color: "var(--text-secondary)", | |
248 | + display: "flex", | |
249 | + alignItems: "center", | |
250 | + gap: "5px", | |
251 | + }} | |
252 | + > | |
253 | + {source === "github" && <Github size={12} />}{" "} | |
254 | + {app.authors?.[0]?.name || "Unknown"} | |
255 | + {app.stargazers_count !== undefined && ( | |
256 | + <span style={{ color: "#e3b341" }}>★ {app.stargazers_count}</span> | |
257 | + )} | |
142 | 258 | </div> |
143 | 259 | </div> |
144 | 260 | </div> |
145 | - | |
146 | - <div style={{ fontSize: "14px", color: "var(--text-secondary)", marginBottom: "20px", flex: 1, overflow: "hidden", display: "-webkit-box", WebkitLineClamp: 3, WebkitBoxOrient: "vertical", lineHeight: "1.5" }}> | |
261 | + | |
262 | + <div | |
263 | + style={{ | |
264 | + fontSize: "14px", | |
265 | + color: "var(--text-secondary)", | |
266 | + marginBottom: "20px", | |
267 | + flex: 1, | |
268 | + overflow: "hidden", | |
269 | + display: "-webkit-box", | |
270 | + WebkitLineClamp: 3, | |
271 | + WebkitBoxOrient: "vertical", | |
272 | + lineHeight: "1.5", | |
273 | + }} | |
274 | + > | |
147 | 275 | {app.description || "No description available for this project."} |
148 | 276 | </div> |
149 | - | |
277 | + | |
150 | 278 | <div style={{ display: "flex", gap: "10px" }}> |
151 | - <button | |
279 | + <button | |
152 | 280 | disabled={!downloadLink && source !== "github"} |
153 | 281 | onClick={async () => { |
154 | 282 | try { |
| @@ -157,38 +285,67 @@ const Storefront: React.FC = () => { | ||
157 | 285 | const parts = app.github_url.split("/"); |
158 | 286 | const owner = parts[parts.length - 2]; |
159 | 287 | const repo = parts[parts.length - 1]; |
160 | - | |
288 | + | |
161 | 289 | // Fetch releases |
162 | - const res = await fetch(`https://api.github.com/repos/${owner}/${repo}/releases/latest`); | |
290 | + const res = await fetch( | |
291 | + `https://api.github.com/repos/${owner}/${repo}/releases/latest` | |
292 | + ); | |
163 | 293 | const release = await res.json(); |
164 | - const asset = release.assets?.find((a: any) => a.name.toLowerCase().endsWith(".appimage")); | |
165 | - | |
294 | + const asset = release.assets?.find((a: any) => | |
295 | + a.name.toLowerCase().endsWith(".appimage") | |
296 | + ); | |
297 | + | |
166 | 298 | if (asset) { |
167 | - await invoke("download_and_install", { url: asset.browser_download_url, name: app.name }); | |
299 | + await invoke("download_and_install", { | |
300 | + url: asset.browser_download_url, | |
301 | + name: app.name, | |
302 | + }); | |
168 | 303 | } else { |
169 | 304 | // Fallback to browser if no asset found |
170 | 305 | await invoke("open_url", { url: `${app.github_url}/releases` }); |
171 | 306 | } |
172 | 307 | } else if (downloadLink) { |
173 | - await invoke("download_and_install", { url: downloadLink.url, name: app.name }); | |
308 | + await invoke("download_and_install", { | |
309 | + url: downloadLink.url, | |
310 | + name: app.name, | |
311 | + }); | |
174 | 312 | } |
175 | 313 | } catch (e) { |
176 | 314 | setError(String(e)); |
177 | 315 | } |
178 | 316 | }} |
179 | - style={{ | |
180 | - flex: 1, background: "#4caf50", color: "#fff", border: "none", padding: "10px", borderRadius: "6px", cursor: "pointer", | |
181 | - display: "flex", alignItems: "center", justifyContent: "center", fontWeight: "bold", gap: "8px" | |
182 | - }}> | |
317 | + style={{ | |
318 | + flex: 1, | |
319 | + background: "#4caf50", | |
320 | + color: "#fff", | |
321 | + border: "none", | |
322 | + padding: "10px", | |
323 | + borderRadius: "6px", | |
324 | + cursor: "pointer", | |
325 | + display: "flex", | |
326 | + alignItems: "center", | |
327 | + justifyContent: "center", | |
328 | + fontWeight: "bold", | |
329 | + gap: "8px", | |
330 | + }} | |
331 | + > | |
183 | 332 | <Download size={16} /> Install |
184 | 333 | </button> |
185 | 334 | {app.github_url && ( |
186 | - <button | |
335 | + <button | |
187 | 336 | onClick={() => invoke("open_url", { url: app.github_url })} |
188 | - style={{ | |
189 | - background: "var(--bg-input)", color: "var(--text-primary)", border: "1px solid var(--border-color)", width: "40px", borderRadius: "6px", cursor: "pointer", | |
190 | - display: "flex", alignItems: "center", justifyContent: "center" | |
191 | - }}> | |
337 | + style={{ | |
338 | + background: "var(--bg-input)", | |
339 | + color: "var(--text-primary)", | |
340 | + border: "1px solid var(--border-color)", | |
341 | + width: "40px", | |
342 | + borderRadius: "6px", | |
343 | + cursor: "pointer", | |
344 | + display: "flex", | |
345 | + alignItems: "center", | |
346 | + justifyContent: "center", | |
347 | + }} | |
348 | + > | |
192 | 349 | <Github size={18} /> |
193 | 350 | </button> |
194 | 351 | )} |
195 | 352 | |
▼src/styles/main.css
+3-1
| @@ -40,7 +40,9 @@ body { | ||
40 | 40 | background-color: var(--bg-main); |
41 | 41 | color: var(--text-primary); |
42 | 42 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; |
43 | - transition: background-color 0.3s, color 0.3s; | |
43 | + transition: | |
44 | + background-color 0.3s, | |
45 | + color 0.3s; | |
44 | 46 | } |
45 | 47 | |
46 | 48 | #root { |
47 | 49 | |