├── .gitattributes ├── .gitignore ├── .vscode ├── extensions.json └── launch.json ├── LICENCE ├── README.md ├── astro.config.mjs ├── package-lock.json ├── package.json ├── public ├── android.svg ├── aniwave.svg ├── chatgpt.svg ├── coolors.svg ├── deepl.svg ├── deepseek.svg ├── discord.svg ├── drive.svg ├── favicon.svg ├── figma.svg ├── github.svg ├── gmail.svg ├── gphotos.svg ├── hackernews.svg ├── instagram.svg ├── linkedin.svg ├── mangadex.svg ├── mastodon.svg ├── myanimelist.svg ├── pinterest.svg ├── reddit.svg ├── render.gif ├── telegram.svg ├── twitch.svg ├── verge.svg ├── whatsapp.svg ├── x.svg ├── youtube.svg └── zlibrary.svg ├── src ├── components │ ├── card.astro │ └── commandLine.astro ├── content │ └── bookmarks.md ├── env.d.ts ├── layouts │ └── Layout.astro └── pages │ ├── api │ └── aiSearch.ts │ └── index.astro ├── tsconfig.json └── whatsapp.svg /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | 4 | # generated types 5 | .astro/ 6 | 7 | # dependencies 8 | node_modules/ 9 | 10 | # logs 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # environment variables 17 | .env 18 | .env.production 19 | 20 | # macOS-specific files 21 | .DS_Store 22 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["astro-build.astro-vscode"], 3 | "unwantedRecommendations": [] 4 | } 5 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "command": "./node_modules/.bin/astro dev", 6 | "name": "Development server", 7 | "request": "launch", 8 | "type": "node-terminal" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Stefan Yas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Terminal-like Web Interface 2 | 3 | A beatiful and aesthetic start page for your browser that i made for myself based on this [aesthetic-startpage](https://github.com/stefan-yas/aesthetic-startpage). 4 | 5 | https://github.com/user-attachments/assets/5b2821f3-ddb9-4e37-92c9-7e6d2abc6ab1 6 | 7 | ## Features 8 | 9 | ### Interactive Terminal Input 10 | - **Placeholder Typing Effect**: The input field displays a typing effect with example queries. 11 | - **Keyboard Shortcuts**: 12 | - `Ctrl + C`: Clears the input field. 13 | - `Arrow Up/Down`: Navigates through command history. 14 | - `Enter`: Executes the command or query. 15 | 16 | - **Special Commands**: 17 | - `r:`: Searches Reddit using Google. 18 | - `m:`: Searches MyAnimeList using Google. 19 | - `a:`: Performs an AI-based search (requires backend API). 20 | - Direct URL input: Navigates directly to the entered URL. 21 | 22 | ### AI Search Integration 23 | - You can search with `a:` and ai will find the most suitable website based on your query and directly go to that website. 24 | - This uses *gemini-2.0-flash* and Google Programmable Search Engine Id and Api. 25 | 26 | ### Accessibility and Usability 27 | - **Autofocus**: Automatically focuses the input field when the page loads. 28 | - **Keydown Event**: Focuses the input field when any key is pressed. 29 | 30 | ## Deployment 31 | Just Fork this repository and deploy it via a site like vercel. 32 | 33 | **DO NOT FORGET TO ADD ENVIROMENT VARIABLES GEMINI_API_KEY, GOOGLE_SEARCH_API_KEY, GOOGLE_SEARCH_ENGINE_ID** 34 | -------------------------------------------------------------------------------- /astro.config.mjs: -------------------------------------------------------------------------------- 1 | import vercel from '@astrojs/vercel/serverless'; 2 | import {defineConfig} from 'astro/config'; 3 | 4 | export default defineConfig({ 5 | output: 'server', // Enable SSR 6 | adapter: vercel(), // Use Vercel adapter 7 | }); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@aeshetic-startpage", 3 | "type": "module", 4 | "version": "1.0.0", 5 | "private": true, 6 | "scripts": { 7 | "dev": "astro dev", 8 | "start": "astro dev", 9 | "build": "astro build", 10 | "preview": "astro preview", 11 | "astro": "astro" 12 | }, 13 | "dependencies": { 14 | "@astrojs/vercel": "^8.1.1", 15 | "@google/generative-ai": "^0.23.0", 16 | "astro": "^5.4.2" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /public/android.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/aniwave.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/chatgpt.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/coolors.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/deepl.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/deepseek.svg: -------------------------------------------------------------------------------- 1 | DeepSeek -------------------------------------------------------------------------------- /public/discord.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/drive.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/favicon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/figma.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/gmail.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/gphotos.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/hackernews.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/instagram.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/linkedin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /public/mangadex.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/mastodon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/myanimelist.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/pinterest.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /public/reddit.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/render.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahmetdem/start-page/996fb5e3ca25d4052fbd2276d6963c59b04203cc/public/render.gif -------------------------------------------------------------------------------- /public/telegram.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /public/twitch.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | twitch [#182] 6 | Created with Sketch. 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /public/verge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /public/whatsapp.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /public/x.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/youtube.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/zlibrary.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/components/card.astro: -------------------------------------------------------------------------------- 1 | --- 2 | const { links } = (await Astro.glob("../content/bookmarks.md"))[0].frontmatter; 3 | 4 | links.sort((a, b) => a.title.localeCompare(b.title)); 5 | 6 | --- 7 |
8 | 9 |
10 | 20 |
21 | 22 | 23 |
24 | 34 |
35 | 36 | 37 |
38 | 48 |
49 | 50 | 51 |
52 | 62 |
63 |
64 | 65 | -------------------------------------------------------------------------------- /src/components/commandLine.astro: -------------------------------------------------------------------------------- 1 | --- 2 | const name = "ahmetdem"; 3 | --- 4 | 5 |
6 |

{name}@ |

7 | 8 |
9 | 10 |

11 | 12 | 68 | 69 |

70 | 71 | 324 | -------------------------------------------------------------------------------- /src/content/bookmarks.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: General 3 | links: 4 | - href: https://mail.google.com/ 5 | title: Gmail 6 | src: gmail.svg 7 | alt: gmail-icon 8 | - href: https://photos.google.com 9 | title: Photos 10 | src: ./gphotos.svg 11 | alt: google-photos-icon 12 | - href: https://youtube.com 13 | title: YouTube 14 | src: youtube.svg 15 | alt: youtube-icon 16 | - href: https://drive.google.com/ 17 | title: Drive 18 | src: drive.svg 19 | alt: drive-icon 20 | - href: https://myanimelist.net/ 21 | title: Myanimelist 22 | src: myanimelist.svg 23 | alt: myanimelist-icon 24 | - href: https://discord.com/app 25 | title: Discord 26 | src: discord.svg 27 | alt: discord-icon 28 | - href: https://www.twitch.tv/ 29 | title: Twitch 30 | src: twitch.svg 31 | alt: twitch-icon 32 | - href: https://web.whatsapp.com/ 33 | title: WhatsApp 34 | src: whatsapp.svg 35 | alt: whatsapp-icon 36 | - href: https://www.reddit.com/ 37 | title: Reddit 38 | src: reddit.svg 39 | alt: reddit-icon 40 | - href: https://x.com/home 41 | title: X 42 | src: x.svg 43 | alt: x-icon 44 | - href: https://chat.deepseek.com/ 45 | title: DeepSeek 46 | src: deepseek.svg 47 | alt: deepseek-icon 48 | - href: https://github.com/ahmetdem 49 | title: Github 50 | src: github.svg 51 | alt: github-icon 52 | - href: https://mangadex.org/ 53 | title: MangaDex 54 | src: mangadex.svg 55 | alt: mangadex-icon 56 | - href: https://aybuzem.aybu.edu.tr/my/ 57 | title: Aybuzem 58 | src: aniwave.svg 59 | alt: aniwave-icon 60 | - href: https://www.deepl.com/en/translator 61 | title: DeepL 62 | src: deepl.svg 63 | alt: deepl-icon 64 | - href: https://chatgpt.com/ 65 | title: ChatGpt 66 | src: chatgpt.svg 67 | alt: chatgpt-icon 68 | - href: https://z-library.sk/ 69 | title: ZLibrary 70 | src: zlibrary.svg 71 | alt: zlibrary-icon 72 | - href: https://www.linkedin.com/in/ahmetdem/ 73 | title: Linkedin 74 | src: linkedin.svg 75 | alt: linkedin-icon 76 | - href: https://www.instagram.com/ 77 | title: Instagram 78 | src: instagram.svg 79 | alt: instagram-icon 80 | - href: https://tr.pinterest.com/ 81 | title: Pinterest 82 | src: pinterest.svg 83 | alt: pinterest-icon 84 | --- 85 | -------------------------------------------------------------------------------- /src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | -------------------------------------------------------------------------------- /src/layouts/Layout.astro: -------------------------------------------------------------------------------- 1 | --- 2 | export interface Props { 3 | title: string; 4 | } 5 | 6 | const { title } = Astro.props; 7 | --- 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 22 | 23 | 24 | 28 | {title} 29 | 30 | 31 | 32 | 33 | 34 | 69 | -------------------------------------------------------------------------------- /src/pages/api/aiSearch.ts: -------------------------------------------------------------------------------- 1 | export const prerender = false 2 | 3 | import type { APIRoute } from "astro"; 4 | import { GoogleGenerativeAI } from "@google/generative-ai"; 5 | 6 | const apiKey = import.meta.env.GEMINI_API_KEY; 7 | const genAI = new GoogleGenerativeAI(apiKey); 8 | const model = genAI.getGenerativeModel({ model: "gemini-2.0-flash" }); 9 | 10 | // Google Custom Search JSON API credentials 11 | const googleSearchApiKey = import.meta.env.GOOGLE_SEARCH_API_KEY; 12 | const googleSearchEngineId = import.meta.env.GOOGLE_SEARCH_ENGINE_ID; 13 | 14 | export const GET: APIRoute = async ({ request }) => { 15 | const { searchParams } = new URL(request.url); 16 | const query = searchParams.get("q"); 17 | const getResults = searchParams.get("results") === "true"; 18 | 19 | if (!query) { 20 | return new Response(JSON.stringify({ error: "Missing query parameter" }), { 21 | status: 400, 22 | headers: { "Content-Type": "application/json" }, 23 | }); 24 | } 25 | 26 | try { 27 | // If we need to get search results first 28 | if (getResults) { 29 | // Fetch top 5 search results from Google CSE 30 | const searchResults = await fetchGoogleSearchResults(query); 31 | 32 | if (searchResults.length === 0) { 33 | // Fallback to just using AI if search results failed 34 | const response = await model.generateContent(query); 35 | const aiResponse = response.response.text().trim(); 36 | return new Response(JSON.stringify({ result: aiResponse }), { 37 | headers: { "Content-Type": "application/json" }, 38 | }); 39 | } 40 | 41 | // Ask AI to choose the best result 42 | const promptWithResults = `Based on the following search results for "${query}", which one is the most relevant and useful? Return ONLY the URL of the best result without any additional text. 43 | 44 | Results: 45 | ${searchResults.map((r, i) => `${i+1}. Title: ${r.title} 46 | URL: ${r.link} 47 | Snippet: ${r.snippet || 'N/A'} 48 | `).join('\n')}`; 49 | 50 | const response = await model.generateContent(promptWithResults); 51 | const bestUrl = response.response.text().trim(); 52 | 53 | return new Response(JSON.stringify({ 54 | result: bestUrl, 55 | allResults: searchResults 56 | }), { 57 | headers: { "Content-Type": "application/json" }, 58 | }); 59 | } else { 60 | // Original behavior - just get AI suggestion 61 | const response = await model.generateContent(query); 62 | const aiResponse = response.response.text().trim(); 63 | return new Response(JSON.stringify({ result: aiResponse }), { 64 | headers: { "Content-Type": "application/json" }, 65 | }); 66 | } 67 | } catch (err) { 68 | return new Response(JSON.stringify({ error: err.message }), { 69 | status: 500, 70 | headers: { "Content-Type": "application/json" }, 71 | }); 72 | } 73 | }; 74 | 75 | // Function to fetch search results using Google Custom Search JSON API 76 | async function fetchGoogleSearchResults(query) { 77 | try { 78 | const url = new URL('https://www.googleapis.com/customsearch/v1'); 79 | url.searchParams.append('key', googleSearchApiKey); 80 | url.searchParams.append('cx', googleSearchEngineId); 81 | url.searchParams.append('q', query); 82 | url.searchParams.append('num', '5'); // Get top 5 results 83 | 84 | const response = await fetch(url.toString()); 85 | 86 | if (!response.ok) { 87 | throw new Error(`Google Search API error: ${response.status}`); 88 | } 89 | 90 | const data = await response.json(); 91 | 92 | if (!data.items || data.items.length === 0) { 93 | return []; 94 | } 95 | 96 | // Transform the results to a simpler format 97 | return data.items.map(item => ({ 98 | title: item.title, 99 | link: item.link, 100 | snippet: item.snippet || '' 101 | })); 102 | } catch (error) { 103 | console.error("Error fetching Google search results:", error); 104 | return []; 105 | } 106 | } -------------------------------------------------------------------------------- /src/pages/index.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from "../layouts/Layout.astro"; 3 | import Card from "../components/card.astro"; 4 | import CommandLine from "../components/commandLine.astro"; 5 | --- 6 | 7 | 8 |
9 | 10 | 11 |
12 |
13 | 14 | 64 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "astro/tsconfigs/base" 3 | } -------------------------------------------------------------------------------- /whatsapp.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | --------------------------------------------------------------------------------