feat: initialize new tab page with Preact and Vite setup

- Added index.html as the main entry point for the application.
- Created package.json with dependencies for Preact, Halfmoon, and TypeScript.
- Included favicon.svg for the application icon.
- Added robots.txt for search engine directives.
- Created app.css for styling the application.
- Implemented app.tsx as the main application component with Navbar and Container.
- Developed container component to display a greeting and search form.
- Added weather description JSON data for dynamic weather updates.
- Built navbar component to fetch and display current weather information.
- Set up index.css for global styles.
- Configured main.tsx to render the application.
- Added TypeScript environment definitions.
- Established TypeScript configuration files for app and node.
- Configured Vite with Preact preset and PostCSS for production optimizations.
This commit is contained in:
wont-stream 2025-04-09 21:56:32 -04:00
parent 3ebaf0a468
commit 2f60206e25
18 changed files with 965 additions and 0 deletions

0
src/components/app.css Normal file
View file

13
src/components/app.tsx Normal file
View file

@ -0,0 +1,13 @@
import Navbar from './navbar';
import Container from './container';
import './app.css';
export default () => {
return (
<>
<Navbar />
<Container />
</>
)
}

View file

@ -0,0 +1,14 @@
export default () => {
return (
<>
<div class="container bg-body-tertiary shadow text-center position-absolute top-50 start-50 translate-middle mx-auto py-4">
<h6 class="display-4">Hello, Seth.</h6>
<br />
<br />
<form role="search" action={"https://www.google.com/search"} method="get">
<input class="form-control me-2" autoFocus type="search" placeholder="Search with Google" aria-label="Search with Google" name="q" />
</form>
</div>
</>
)
}

View file

@ -0,0 +1,282 @@
{
"0":{
"day":{
"description":"Sunny",
"image":"http://openweathermap.org/img/wn/01d@2x.png"
},
"night":{
"description":"Clear",
"image":"http://openweathermap.org/img/wn/01n@2x.png"
}
},
"1":{
"day":{
"description":"Mainly Sunny",
"image":"http://openweathermap.org/img/wn/01d@2x.png"
},
"night":{
"description":"Mainly Clear",
"image":"http://openweathermap.org/img/wn/01n@2x.png"
}
},
"2":{
"day":{
"description":"Partly Cloudy",
"image":"http://openweathermap.org/img/wn/02d@2x.png"
},
"night":{
"description":"Partly Cloudy",
"image":"http://openweathermap.org/img/wn/02n@2x.png"
}
},
"3":{
"day":{
"description":"Cloudy",
"image":"http://openweathermap.org/img/wn/03d@2x.png"
},
"night":{
"description":"Cloudy",
"image":"http://openweathermap.org/img/wn/03n@2x.png"
}
},
"45":{
"day":{
"description":"Foggy",
"image":"http://openweathermap.org/img/wn/50d@2x.png"
},
"night":{
"description":"Foggy",
"image":"http://openweathermap.org/img/wn/50n@2x.png"
}
},
"48":{
"day":{
"description":"Rime Fog",
"image":"http://openweathermap.org/img/wn/50d@2x.png"
},
"night":{
"description":"Rime Fog",
"image":"http://openweathermap.org/img/wn/50n@2x.png"
}
},
"51":{
"day":{
"description":"Light Drizzle",
"image":"http://openweathermap.org/img/wn/09d@2x.png"
},
"night":{
"description":"Light Drizzle",
"image":"http://openweathermap.org/img/wn/09n@2x.png"
}
},
"53":{
"day":{
"description":"Drizzle",
"image":"http://openweathermap.org/img/wn/09d@2x.png"
},
"night":{
"description":"Drizzle",
"image":"http://openweathermap.org/img/wn/09n@2x.png"
}
},
"55":{
"day":{
"description":"Heavy Drizzle",
"image":"http://openweathermap.org/img/wn/09d@2x.png"
},
"night":{
"description":"Heavy Drizzle",
"image":"http://openweathermap.org/img/wn/09n@2x.png"
}
},
"56":{
"day":{
"description":"Light Freezing Drizzle",
"image":"http://openweathermap.org/img/wn/09d@2x.png"
},
"night":{
"description":"Light Freezing Drizzle",
"image":"http://openweathermap.org/img/wn/09n@2x.png"
}
},
"57":{
"day":{
"description":"Freezing Drizzle",
"image":"http://openweathermap.org/img/wn/09d@2x.png"
},
"night":{
"description":"Freezing Drizzle",
"image":"http://openweathermap.org/img/wn/09n@2x.png"
}
},
"61":{
"day":{
"description":"Light Rain",
"image":"http://openweathermap.org/img/wn/10d@2x.png"
},
"night":{
"description":"Light Rain",
"image":"http://openweathermap.org/img/wn/10n@2x.png"
}
},
"63":{
"day":{
"description":"Rain",
"image":"http://openweathermap.org/img/wn/10d@2x.png"
},
"night":{
"description":"Rain",
"image":"http://openweathermap.org/img/wn/10n@2x.png"
}
},
"65":{
"day":{
"description":"Heavy Rain",
"image":"http://openweathermap.org/img/wn/10d@2x.png"
},
"night":{
"description":"Heavy Rain",
"image":"http://openweathermap.org/img/wn/10n@2x.png"
}
},
"66":{
"day":{
"description":"Light Freezing Rain",
"image":"http://openweathermap.org/img/wn/10d@2x.png"
},
"night":{
"description":"Light Freezing Rain",
"image":"http://openweathermap.org/img/wn/10n@2x.png"
}
},
"67":{
"day":{
"description":"Freezing Rain",
"image":"http://openweathermap.org/img/wn/10d@2x.png"
},
"night":{
"description":"Freezing Rain",
"image":"http://openweathermap.org/img/wn/10n@2x.png"
}
},
"71":{
"day":{
"description":"Light Snow",
"image":"http://openweathermap.org/img/wn/13d@2x.png"
},
"night":{
"description":"Light Snow",
"image":"http://openweathermap.org/img/wn/13n@2x.png"
}
},
"73":{
"day":{
"description":"Snow",
"image":"http://openweathermap.org/img/wn/13d@2x.png"
},
"night":{
"description":"Snow",
"image":"http://openweathermap.org/img/wn/13n@2x.png"
}
},
"75":{
"day":{
"description":"Heavy Snow",
"image":"http://openweathermap.org/img/wn/13d@2x.png"
},
"night":{
"description":"Heavy Snow",
"image":"http://openweathermap.org/img/wn/13n@2x.png"
}
},
"77":{
"day":{
"description":"Snow Grains",
"image":"http://openweathermap.org/img/wn/13d@2x.png"
},
"night":{
"description":"Snow Grains",
"image":"http://openweathermap.org/img/wn/13n@2x.png"
}
},
"80":{
"day":{
"description":"Light Showers",
"image":"http://openweathermap.org/img/wn/09d@2x.png"
},
"night":{
"description":"Light Showers",
"image":"http://openweathermap.org/img/wn/09n@2x.png"
}
},
"81":{
"day":{
"description":"Showers",
"image":"http://openweathermap.org/img/wn/09d@2x.png"
},
"night":{
"description":"Showers",
"image":"http://openweathermap.org/img/wn/09n@2x.png"
}
},
"82":{
"day":{
"description":"Heavy Showers",
"image":"http://openweathermap.org/img/wn/09d@2x.png"
},
"night":{
"description":"Heavy Showers",
"image":"http://openweathermap.org/img/wn/09n@2x.png"
}
},
"85":{
"day":{
"description":"Light Snow Showers",
"image":"http://openweathermap.org/img/wn/13d@2x.png"
},
"night":{
"description":"Light Snow Showers",
"image":"http://openweathermap.org/img/wn/13n@2x.png"
}
},
"86":{
"day":{
"description":"Snow Showers",
"image":"http://openweathermap.org/img/wn/13d@2x.png"
},
"night":{
"description":"Snow Showers",
"image":"http://openweathermap.org/img/wn/13n@2x.png"
}
},
"95":{
"day":{
"description":"Thunderstorm",
"image":"http://openweathermap.org/img/wn/11d@2x.png"
},
"night":{
"description":"Thunderstorm",
"image":"http://openweathermap.org/img/wn/11n@2x.png"
}
},
"96":{
"day":{
"description":"Light Thunderstorms With Hail",
"image":"http://openweathermap.org/img/wn/11d@2x.png"
},
"night":{
"description":"Light Thunderstorms With Hail",
"image":"http://openweathermap.org/img/wn/11n@2x.png"
}
},
"99":{
"day":{
"description":"Thunderstorm With Hail",
"image":"http://openweathermap.org/img/wn/11d@2x.png"
},
"night":{
"description":"Thunderstorm With Hail",
"image":"http://openweathermap.org/img/wn/11n@2x.png"
}
}
}

View file

@ -0,0 +1,62 @@
import { useState } from 'preact/hooks';
import { ChevronLeft, ChevronRight } from 'lucide-preact';
import desc from "./desc.json";
const fetchWeather = async (lat: number, long: number) => {
const req = await fetch(`https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${long}&current=temperature_2m,is_day,weather_code&timezone=${Intl.DateTimeFormat().resolvedOptions().timeZone}`)
const res = await req.json();
return res as {
current: {
temperature_2m: number,
is_day: number;
weather_code: number;
}
}
}
export default () => {
const [weather, setWeather] = useState<string>("Loading...");
const [imgSrc, setSrc] = useState<string>("favicon.svg");
const [wttrDesc, setWttrDesc] = useState<string>("Loading...");
const getWeather = async (coords: GeolocationCoordinates) => {
const weather = await fetchWeather(coords.latitude, coords.longitude);
const weatherCode = weather.current.weather_code;
const isDay = weather.current.is_day === 1;
const dayOrNight = isDay ? "Day" : "Night";
const weatherDesc = desc[weatherCode.toString() as keyof typeof desc][dayOrNight.toLowerCase() as "day" | "night"];
setWeather(`${weather.current.temperature_2m}°C`);
setSrc(weatherDesc.image);
setWttrDesc(`${weatherDesc.description} & ${dayOrNight}`);
}
(async () => {
const req = await fetch("https://cf.ipv4-army.workers.dev/")
const res = await req.json();
return await getWeather(res);
})()
return (
<>
<nav class="navbar shadow fixed-top" style="background-color: var(--bs-content-bg); border-bottom: var(--bs-border-width) solid var(--bs-content-border-color);">
<div class="container-fluid">
<div class="navbar-brand">
<img src={imgSrc} alt="Logo" width="24" height="24" class="d-inline-block align-text-top" />
{wttrDesc}
</div>
<span class="navbar-text">
{weather}
</span>
<div class="d-flex hstack gap-2" role="search">
<button type="button" class="btn btn-outline-light btn-sm" onClick={history.back}><ChevronLeft size={20} /></button>
<button type="button" class="btn btn-outline-light btn-sm" onClick={history.forward}><ChevronRight size={20} /></button>
</div>
</div>
</nav>
</>
)
}

0
src/index.css Normal file
View file

6
src/main.tsx Normal file
View file

@ -0,0 +1,6 @@
import { render } from 'preact'
import './index.css'
import 'halfmoon/css/halfmoon.min.css';
import App from './components/app.tsx'
render(<App />, document.getElementById('app')!)

1
src/vite-env.d.ts vendored Normal file
View file

@ -0,0 +1 @@
/// <reference types="vite/client" />