commit aa1d1b4c3ade52409ce12308a9ac3d3d9fea2bb2
Author: root <root@hub.scroll.pub>
Date: 2024-12-27 18:13:55 +0000
Subject: Initial commit
diff --git a/body.html b/body.html
new file mode 100644
index 0000000..e4db3f7
--- /dev/null
+++ b/body.html
@@ -0,0 +1,37 @@
+<header>
+ <h1>FirstIssue</h1>
+ <p>Discover your perfect first open source contribution</p>
+</header>
+
+<main>
+ <section class="search-section">
+ <form id="searchForm" aria-label="GitHub username search">
+ <div class="input-group">
+ <label for="username">Enter your GitHub username</label>
+ <input type="text" id="username" name="username" placeholder="octocat" required>
+ </div>
+ <button type="submit">Find Projects</button>
+ </form>
+ </section>
+
+ <section class="results-section" aria-live="polite">
+ <div id="loading" class="hidden">
+ <div class="spinner"></div>
+ <p>Analyzing your interests...</p>
+ </div>
+
+ <div id="error" class="hidden">
+ <p></p>
+ </div>
+
+ <div id="results" class="hidden">
+ <h2>Recommended Projects</h2>
+ <div class="projects-grid"></div>
+ </div>
+ </section>
+</main>
+
+<footer>
+ <p>Made with 💝 for the open source community</p>
+ <p>This tool helps match developers with beginner-friendly issues based on their GitHub interests</p>
+</footer>
diff --git a/index.scroll b/index.scroll
new file mode 100644
index 0000000..89900ae
--- /dev/null
+++ b/index.scroll
@@ -0,0 +1,10 @@
+buildHtml
+baseUrl https://firstissue.scroll.pub
+metaTags
+ description Find open source projects to contribute to based on your GitHub interests
+ keywords github, open source, first issue, good first issue, contribute, beginner friendly
+editButton /edit.html
+title FirstIssue - Find Your Perfect First Open Source Contribution
+style.css
+body.html
+script.js
diff --git a/prompt.json b/prompt.json
new file mode 100644
index 0000000..49020a3
--- /dev/null
+++ b/prompt.json
@@ -0,0 +1,20 @@
+{
+ "id": "msg_019XwgGkF73iNBnnzHskSHkJ",
+ "type": "message",
+ "role": "assistant",
+ "model": "claude-3-5-sonnet-20241022",
+ "content": [
+ {
+ "type": "text",
+ "text": "I'll create a website that helps developers find good first issues based on their GitHub interests. Here's a suitable domain name and implementation:\n\n---domain---\nfirstissue.scroll.pub\n\n---index.scroll---\nbuildHtml\nbaseUrl https://firstissue.scroll.pub\nmetaTags\n description Find open source projects to contribute to based on your GitHub interests\n keywords github, open source, first issue, good first issue, contribute, beginner friendly\neditButton /edit.html\ntitle FirstIssue - Find Your Perfect First Open Source Contribution\nstyle.css\nbody.html\nscript.js\n\n---body.html---\n<header>\n <h1>FirstIssue</h1>\n <p>Discover your perfect first open source contribution</p>\n</header>\n\n<main>\n <section class=\"search-section\">\n <form id=\"searchForm\" aria-label=\"GitHub username search\">\n <div class=\"input-group\">\n <label for=\"username\">Enter your GitHub username</label>\n <input type=\"text\" id=\"username\" name=\"username\" placeholder=\"octocat\" required>\n </div>\n <button type=\"submit\">Find Projects</button>\n </form>\n </section>\n\n <section class=\"results-section\" aria-live=\"polite\">\n <div id=\"loading\" class=\"hidden\">\n <div class=\"spinner\"></div>\n <p>Analyzing your interests...</p>\n </div>\n \n <div id=\"error\" class=\"hidden\">\n <p></p>\n </div>\n\n <div id=\"results\" class=\"hidden\">\n <h2>Recommended Projects</h2>\n <div class=\"projects-grid\"></div>\n </div>\n </section>\n</main>\n\n<footer>\n <p>Made with 💝 for the open source community</p>\n <p>This tool helps match developers with beginner-friendly issues based on their GitHub interests</p>\n</footer>\n\n---style.css---\n:root {\n --primary: #2d333b;\n --secondary: #22272e;\n --accent: #539bf5;\n --text: #adbac7;\n --spacing: 2rem;\n}\n\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: system-ui, -apple-system, sans-serif;\n line-height: 1.6;\n color: var(--text);\n background: var(--primary);\n min-height: 100vh;\n display: flex;\n flex-direction: column;\n}\n\nheader {\n background: var(--secondary);\n padding: var(--spacing);\n text-align: center;\n box-shadow: 0 2px 8px rgba(0,0,0,0.2);\n}\n\nh1 {\n font-size: 2.5rem;\n color: var(--accent);\n margin-bottom: 0.5rem;\n}\n\nmain {\n flex: 1;\n padding: var(--spacing);\n max-width: 1200px;\n margin: 0 auto;\n width: 100%;\n}\n\n.search-section {\n margin: 2rem auto;\n max-width: 600px;\n}\n\n.input-group {\n margin-bottom: 1rem;\n}\n\nlabel {\n display: block;\n margin-bottom: 0.5rem;\n font-weight: 500;\n}\n\ninput {\n width: 100%;\n padding: 0.8rem;\n border: 2px solid var(--secondary);\n border-radius: 8px;\n background: var(--secondary);\n color: var(--text);\n font-size: 1rem;\n transition: all 0.3s ease;\n}\n\ninput:focus {\n outline: none;\n border-color: var(--accent);\n box-shadow: 0 0 0 3px rgba(83,155,245,0.3);\n}\n\nbutton {\n width: 100%;\n padding: 1rem;\n background: var(--accent);\n color: var(--secondary);\n border: none;\n border-radius: 8px;\n font-size: 1rem;\n font-weight: 600;\n cursor: pointer;\n transition: transform 0.2s ease;\n}\n\nbutton:hover {\n transform: translateY(-2px);\n}\n\n.projects-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));\n gap: 1.5rem;\n margin-top: 2rem;\n}\n\n.project-card {\n background: var(--secondary);\n border-radius: 12px;\n padding: 1.5rem;\n transition: transform 0.3s ease;\n}\n\n.project-card:hover {\n transform: translateY(-4px);\n}\n\n.spinner {\n width: 40px;\n height: 40px;\n border: 4px solid var(--secondary);\n border-top: 4px solid var(--accent);\n border-radius: 50%;\n margin: 2rem auto;\n animation: spin 1s linear infinite;\n}\n\n@keyframes spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n}\n\n.hidden {\n display: none;\n}\n\nfooter {\n background: var(--secondary);\n padding: var(--spacing);\n text-align: center;\n margin-top: auto;\n}\n\n@media (max-width: 768px) {\n :root {\n --spacing: 1rem;\n }\n \n h1 {\n font-size: 2rem;\n }\n \n .projects-grid {\n grid-template-columns: 1fr;\n }\n}\n\n---script.js---\ndocument.addEventListener('DOMContentLoaded', () => {\n const searchForm = document.getElementById('searchForm');\n const loading = document.getElementById('loading');\n const error = document.getElementById('error');\n const results = document.getElementById('results');\n const projectsGrid = results.querySelector('.projects-grid');\n\n searchForm.addEventListener('submit', async (e) => {\n e.preventDefault();\n const username = document.getElementById('username').value.trim();\n \n if (!username) return;\n\n showLoading();\n \n try {\n // Get user's starred repos\n const starredRepos = await fetchStarredRepos(username);\n \n // Get repos with good first issues\n const recommendations = await findRecommendations(starredRepos);\n \n displayResults(recommendations);\n } catch (err) {\n showError(err.message);\n }\n });\n\n async function fetchStarredRepos(username) {\n const response = await fetch(`https://api.github.com/users/${username}/starred`);\n if (!response.ok) {\n throw new Error('Unable to fetch starred repositories. Please check the username and try again.');\n }\n return response.json();\n }\n\n async function findRecommendations(starredRepos) {\n const topics = extractTopics(starredRepos);\n const recommendations = [];\n \n for (const topic of topics) {\n const response = await fetch(\n `https://api.github.com/search/issues?q=label:\"good first issue\"+state:open+topic:${topic}`\n );\n const data = await response.json();\n recommendations.push(...data.items.slice(0, 3));\n }\n \n return recommendations.slice(0, 9);\n }\n\n function extractTopics(repos) {\n const topicsMap = {};\n repos.forEach(repo => {\n repo.topics?.forEach(topic => {\n topicsMap[topic] = (topicsMap[topic] || 0) + 1;\n });\n });\n \n return Object.entries(topicsMap)\n .sort((a, b) => b[1] - a[1])\n .slice(0, 3)\n .map(([topic]) => topic);\n }\n\n function displayResults(recommendations) {\n projectsGrid.innerHTML = '';\n \n recommendations.forEach(issue => {\n const card = document.createElement('article');\n card.className = 'project-card';\n card.innerHTML = `\n <h3>${issue.repository_url.split('/').slice(-1)}</h3>\n <p>${issue.title}</p>\n <a href=\"${issue.html_url}\" target=\"_blank\" rel=\"noopener noreferrer\">View Issue</a>\n `;\n projectsGrid.appendChild(card);\n });\n \n hideLoading();\n results.classList.remove('hidden');\n }\n\n function showLoading() {\n error.classList.add('hidden');\n results.classList.add('hidden');\n loading.classList.remove('hidden');\n }\n\n function hideLoading() {\n loading.classList.add('hidden');\n }\n\n function showError(message) {\n hideLoading();\n results.classList.add('hidden');\n error.querySelector('p').textContent = message;\n error.classList.remove('hidden');\n }\n});\n\n---end---\n\nThis implementation creates a sleek, modern website that helps developers find their first open source contributions. Key features:\n\n1. Clean, semantic HTML5 structure\n2. Modern CSS with CSS variables and fluid layouts\n3. Responsive design that works on all devices\n4. Accessible with proper ARIA attributes and semantic elements\n5. Error handling and loading states\n6. Beautiful animations and transitions\n7. GitHub API integration\n8. Topic-based recommendation system\n9. SEO-optimized meta tags\n10. Professional design with dark theme\n\nThe site analyzes a user's starred repositories to understand their interests, then finds matching repositories with good first issues. The interface is simple but powerful, with smooth animations and clear feedback throughout the process."
+ }
+ ],
+ "stop_reason": "end_turn",
+ "stop_sequence": null,
+ "usage": {
+ "input_tokens": 293,
+ "cache_creation_input_tokens": 0,
+ "cache_read_input_tokens": 0,
+ "output_tokens": 2478
+ }
+}
\ No newline at end of file
diff --git a/readme.scroll b/readme.scroll
new file mode 100644
index 0000000..2ada0c4
--- /dev/null
+++ b/readme.scroll
@@ -0,0 +1,2 @@
+# firstissue.scroll.pub
+Website generated by Claude from prompt: page that given your github username suggest repos to contribute to open source based on your starred repos with good first issue labels
\ No newline at end of file
diff --git a/script.js b/script.js
new file mode 100644
index 0000000..9a6e6ea
--- /dev/null
+++ b/script.js
@@ -0,0 +1,100 @@
+document.addEventListener('DOMContentLoaded', () => {
+ const searchForm = document.getElementById('searchForm');
+ const loading = document.getElementById('loading');
+ const error = document.getElementById('error');
+ const results = document.getElementById('results');
+ const projectsGrid = results.querySelector('.projects-grid');
+
+ searchForm.addEventListener('submit', async (e) => {
+ e.preventDefault();
+ const username = document.getElementById('username').value.trim();
+
+ if (!username) return;
+
+ showLoading();
+
+ try {
+ // Get user's starred repos
+ const starredRepos = await fetchStarredRepos(username);
+
+ // Get repos with good first issues
+ const recommendations = await findRecommendations(starredRepos);
+
+ displayResults(recommendations);
+ } catch (err) {
+ showError(err.message);
+ }
+ });
+
+ async function fetchStarredRepos(username) {
+ const response = await fetch(`https://api.github.com/users/${username}/starred`);
+ if (!response.ok) {
+ throw new Error('Unable to fetch starred repositories. Please check the username and try again.');
+ }
+ return response.json();
+ }
+
+ async function findRecommendations(starredRepos) {
+ const topics = extractTopics(starredRepos);
+ const recommendations = [];
+
+ for (const topic of topics) {
+ const response = await fetch(
+ `https://api.github.com/search/issues?q=label:"good first issue"+state:open+topic:${topic}`
+ );
+ const data = await response.json();
+ recommendations.push(...data.items.slice(0, 3));
+ }
+
+ return recommendations.slice(0, 9);
+ }
+
+ function extractTopics(repos) {
+ const topicsMap = {};
+ repos.forEach(repo => {
+ repo.topics?.forEach(topic => {
+ topicsMap[topic] = (topicsMap[topic] || 0) + 1;
+ });
+ });
+
+ return Object.entries(topicsMap)
+ .sort((a, b) => b[1] - a[1])
+ .slice(0, 3)
+ .map(([topic]) => topic);
+ }
+
+ function displayResults(recommendations) {
+ projectsGrid.innerHTML = '';
+
+ recommendations.forEach(issue => {
+ const card = document.createElement('article');
+ card.className = 'project-card';
+ card.innerHTML = `
+ <h3>${issue.repository_url.split('/').slice(-1)}</h3>
+ <p>${issue.title}</p>
+ <a href="${issue.html_url}" target="_blank" rel="noopener noreferrer">View Issue</a>
+ `;
+ projectsGrid.appendChild(card);
+ });
+
+ hideLoading();
+ results.classList.remove('hidden');
+ }
+
+ function showLoading() {
+ error.classList.add('hidden');
+ results.classList.add('hidden');
+ loading.classList.remove('hidden');
+ }
+
+ function hideLoading() {
+ loading.classList.add('hidden');
+ }
+
+ function showError(message) {
+ hideLoading();
+ results.classList.add('hidden');
+ error.querySelector('p').textContent = message;
+ error.classList.remove('hidden');
+ }
+});
diff --git a/style.css b/style.css
new file mode 100644
index 0000000..63d37a6
--- /dev/null
+++ b/style.css
@@ -0,0 +1,151 @@
+:root {
+ --primary: #2d333b;
+ --secondary: #22272e;
+ --accent: #539bf5;
+ --text: #adbac7;
+ --spacing: 2rem;
+}
+
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ font-family: system-ui, -apple-system, sans-serif;
+ line-height: 1.6;
+ color: var(--text);
+ background: var(--primary);
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+}
+
+header {
+ background: var(--secondary);
+ padding: var(--spacing);
+ text-align: center;
+ box-shadow: 0 2px 8px rgba(0,0,0,0.2);
+}
+
+h1 {
+ font-size: 2.5rem;
+ color: var(--accent);
+ margin-bottom: 0.5rem;
+}
+
+main {
+ flex: 1;
+ padding: var(--spacing);
+ max-width: 1200px;
+ margin: 0 auto;
+ width: 100%;
+}
+
+.search-section {
+ margin: 2rem auto;
+ max-width: 600px;
+}
+
+.input-group {
+ margin-bottom: 1rem;
+}
+
+label {
+ display: block;
+ margin-bottom: 0.5rem;
+ font-weight: 500;
+}
+
+input {
+ width: 100%;
+ padding: 0.8rem;
+ border: 2px solid var(--secondary);
+ border-radius: 8px;
+ background: var(--secondary);
+ color: var(--text);
+ font-size: 1rem;
+ transition: all 0.3s ease;
+}
+
+input:focus {
+ outline: none;
+ border-color: var(--accent);
+ box-shadow: 0 0 0 3px rgba(83,155,245,0.3);
+}
+
+button {
+ width: 100%;
+ padding: 1rem;
+ background: var(--accent);
+ color: var(--secondary);
+ border: none;
+ border-radius: 8px;
+ font-size: 1rem;
+ font-weight: 600;
+ cursor: pointer;
+ transition: transform 0.2s ease;
+}
+
+button:hover {
+ transform: translateY(-2px);
+}
+
+.projects-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+ gap: 1.5rem;
+ margin-top: 2rem;
+}
+
+.project-card {
+ background: var(--secondary);
+ border-radius: 12px;
+ padding: 1.5rem;
+ transition: transform 0.3s ease;
+}
+
+.project-card:hover {
+ transform: translateY(-4px);
+}
+
+.spinner {
+ width: 40px;
+ height: 40px;
+ border: 4px solid var(--secondary);
+ border-top: 4px solid var(--accent);
+ border-radius: 50%;
+ margin: 2rem auto;
+ animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+ 0% { transform: rotate(0deg); }
+ 100% { transform: rotate(360deg); }
+}
+
+.hidden {
+ display: none;
+}
+
+footer {
+ background: var(--secondary);
+ padding: var(--spacing);
+ text-align: center;
+ margin-top: auto;
+}
+
+@media (max-width: 768px) {
+ :root {
+ --spacing: 1rem;
+ }
+
+ h1 {
+ font-size: 2rem;
+ }
+
+ .projects-grid {
+ grid-template-columns: 1fr;
+ }
+}