├── .prettierrc ├── public ├── postap.png ├── cyberpunk.png ├── fairytale.png ├── mountains.jpg ├── images │ └── weather │ │ ├── 01d@2x.png │ │ ├── 01n@2x.png │ │ ├── 02d@2x.png │ │ ├── 02n@2x.png │ │ ├── 03d@2x.png │ │ ├── 03n@2x.png │ │ ├── 04d@2x.png │ │ ├── 04n@2x.png │ │ ├── 09d@2x.png │ │ ├── 09n@2x.png │ │ ├── 10d@2x.png │ │ ├── 10n@2x.png │ │ ├── 11d@2x.png │ │ ├── 11n@2x.png │ │ ├── 13d@2x.png │ │ ├── 13n@2x.png │ │ ├── 50d@2x.png │ │ ├── 50n@2x.png │ │ └── weather-icons-license.txt └── vercel.svg ├── src ├── app │ ├── favicon.ico │ ├── snake │ │ └── page.tsx │ ├── notepad │ │ └── page.tsx │ ├── layout.tsx │ ├── page.tsx │ └── api │ │ ├── facts │ │ └── route.ts │ │ ├── quotes │ │ └── route.ts │ │ └── weather │ │ └── route.ts ├── assets │ ├── images │ │ ├── gh.png │ │ ├── snake.png │ │ ├── refresh.png │ │ ├── spinner.gif │ │ ├── changeCity.png │ │ └── weather │ │ │ ├── 01d@2x.png │ │ │ ├── 01n@2x.png │ │ │ ├── 02d@2x.png │ │ │ ├── 02n@2x.png │ │ │ ├── 03d@2x.png │ │ │ ├── 03n@2x.png │ │ │ ├── 04d@2x.png │ │ │ ├── 04n@2x.png │ │ │ ├── 09d@2x.png │ │ │ ├── 09n@2x.png │ │ │ ├── 10d@2x.png │ │ │ ├── 10n@2x.png │ │ │ ├── 11d@2x.png │ │ │ ├── 11n@2x.png │ │ │ ├── 13d@2x.png │ │ │ ├── 13n@2x.png │ │ │ ├── 50d@2x.png │ │ │ └── 50n@2x.png │ └── icons │ │ ├── CheckIcon.tsx │ │ ├── ArrowLeftIcon.tsx │ │ ├── CloseIcon.tsx │ │ ├── EditIcon.tsx │ │ ├── PictureIcon.tsx │ │ ├── HumidityIcon.tsx │ │ ├── HomeIcon.tsx │ │ ├── RefreshIcon.tsx │ │ ├── PlannerMobileIcon.tsx │ │ ├── NotepadEditIcon.tsx │ │ ├── WeatherMobileIcon.tsx │ │ ├── GithubIcon.tsx │ │ ├── DeleteIcon.tsx │ │ ├── DragHandleIcon.tsx │ │ ├── SettingsIcon.tsx │ │ ├── PressureIcon.tsx │ │ ├── WindIcon.tsx │ │ ├── HandIcon.tsx │ │ └── SnakeIcon.tsx ├── services │ ├── fetchFact.ts │ ├── fetchQuote.ts │ ├── providers.tsx │ └── fetchWeatherData.ts ├── hooks │ ├── useIsMounted.ts │ ├── useClickOutside.ts │ ├── useCurrentDate.ts │ ├── useEditUserData.ts │ ├── useSettings.ts │ ├── useIntro.ts │ ├── useNotepad.ts │ ├── useWeatherData.ts │ ├── useWelcome.ts │ ├── usePlanner.ts │ ├── useHomepage.ts │ └── useSnake.ts ├── store │ ├── mobileViewStore.ts │ ├── snakeStore.ts │ ├── weatherStore.ts │ ├── plannerStore.ts │ ├── notepadStore.ts │ ├── userStore.ts │ └── settingsStore.ts ├── components │ ├── settings │ │ ├── SettingsTitle.tsx │ │ ├── SettingsSection.tsx │ │ ├── SettingsModals.tsx │ │ ├── SettingsSlider.tsx │ │ ├── SettingsGithub.tsx │ │ ├── Settings.tsx │ │ ├── SettingsSectionRow.tsx │ │ └── SettingsContent.tsx │ ├── auth │ │ └── ProtectedRoute.tsx │ ├── views │ │ ├── homepage │ │ │ ├── weather │ │ │ │ ├── WeatherParameter.tsx │ │ │ │ └── WeatherHourBox.tsx │ │ │ ├── Homepage.tsx │ │ │ ├── planner │ │ │ │ ├── PlannerHeader.tsx │ │ │ │ ├── Planner.tsx │ │ │ │ └── PlannerItem.tsx │ │ │ └── welcome │ │ │ │ └── Welcome.tsx │ │ ├── notepad │ │ │ ├── NotepadTextArea.tsx │ │ │ └── Notepad.tsx │ │ ├── intro │ │ │ └── Intro.tsx │ │ └── snake │ │ │ └── Snake.tsx │ ├── layout │ │ ├── SnakeButton.tsx │ │ ├── MobileView.tsx │ │ ├── SideButtons.tsx │ │ ├── MobileNavigation.tsx │ │ └── Layout.tsx │ ├── common │ │ ├── PageWrapper.tsx │ │ └── Loader.tsx │ └── modals │ │ ├── NotepadAlert.tsx │ │ ├── EditUserDataModal.tsx │ │ └── ClearAllDataModal.tsx ├── utils │ ├── clearAllData.ts │ ├── localStorageUtils.ts │ └── countryMapping.ts └── theme │ ├── components │ ├── contentBox.tsx │ ├── button.ts │ └── text.ts │ ├── generateTheme.ts │ ├── colors.ts │ └── extendedColors.ts ├── .eslintrc.json ├── next-env.d.ts ├── next.config.js ├── .gitignore ├── CHANGELOG.md ├── tsconfig.json ├── CONTRIBUTING.md ├── package.json └── README.md /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "bracketSameLine": true 4 | } 5 | -------------------------------------------------------------------------------- /public/postap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/public/postap.png -------------------------------------------------------------------------------- /public/cyberpunk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/public/cyberpunk.png -------------------------------------------------------------------------------- /public/fairytale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/public/fairytale.png -------------------------------------------------------------------------------- /public/mountains.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/public/mountains.jpg -------------------------------------------------------------------------------- /src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/src/app/favicon.ico -------------------------------------------------------------------------------- /src/assets/images/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/src/assets/images/gh.png -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next", "prettier"], 3 | "ignorePatterns": ["next.config.js"] 4 | } 5 | -------------------------------------------------------------------------------- /src/assets/images/snake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/src/assets/images/snake.png -------------------------------------------------------------------------------- /src/assets/images/refresh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/src/assets/images/refresh.png -------------------------------------------------------------------------------- /src/assets/images/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/src/assets/images/spinner.gif -------------------------------------------------------------------------------- /public/images/weather/01d@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/public/images/weather/01d@2x.png -------------------------------------------------------------------------------- /public/images/weather/01n@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/public/images/weather/01n@2x.png -------------------------------------------------------------------------------- /public/images/weather/02d@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/public/images/weather/02d@2x.png -------------------------------------------------------------------------------- /public/images/weather/02n@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/public/images/weather/02n@2x.png -------------------------------------------------------------------------------- /public/images/weather/03d@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/public/images/weather/03d@2x.png -------------------------------------------------------------------------------- /public/images/weather/03n@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/public/images/weather/03n@2x.png -------------------------------------------------------------------------------- /public/images/weather/04d@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/public/images/weather/04d@2x.png -------------------------------------------------------------------------------- /public/images/weather/04n@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/public/images/weather/04n@2x.png -------------------------------------------------------------------------------- /public/images/weather/09d@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/public/images/weather/09d@2x.png -------------------------------------------------------------------------------- /public/images/weather/09n@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/public/images/weather/09n@2x.png -------------------------------------------------------------------------------- /public/images/weather/10d@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/public/images/weather/10d@2x.png -------------------------------------------------------------------------------- /public/images/weather/10n@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/public/images/weather/10n@2x.png -------------------------------------------------------------------------------- /public/images/weather/11d@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/public/images/weather/11d@2x.png -------------------------------------------------------------------------------- /public/images/weather/11n@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/public/images/weather/11n@2x.png -------------------------------------------------------------------------------- /public/images/weather/13d@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/public/images/weather/13d@2x.png -------------------------------------------------------------------------------- /public/images/weather/13n@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/public/images/weather/13n@2x.png -------------------------------------------------------------------------------- /public/images/weather/50d@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/public/images/weather/50d@2x.png -------------------------------------------------------------------------------- /public/images/weather/50n@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/public/images/weather/50n@2x.png -------------------------------------------------------------------------------- /src/assets/images/changeCity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/src/assets/images/changeCity.png -------------------------------------------------------------------------------- /src/assets/images/weather/01d@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/src/assets/images/weather/01d@2x.png -------------------------------------------------------------------------------- /src/assets/images/weather/01n@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/src/assets/images/weather/01n@2x.png -------------------------------------------------------------------------------- /src/assets/images/weather/02d@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/src/assets/images/weather/02d@2x.png -------------------------------------------------------------------------------- /src/assets/images/weather/02n@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/src/assets/images/weather/02n@2x.png -------------------------------------------------------------------------------- /src/assets/images/weather/03d@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/src/assets/images/weather/03d@2x.png -------------------------------------------------------------------------------- /src/assets/images/weather/03n@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/src/assets/images/weather/03n@2x.png -------------------------------------------------------------------------------- /src/assets/images/weather/04d@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/src/assets/images/weather/04d@2x.png -------------------------------------------------------------------------------- /src/assets/images/weather/04n@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/src/assets/images/weather/04n@2x.png -------------------------------------------------------------------------------- /src/assets/images/weather/09d@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/src/assets/images/weather/09d@2x.png -------------------------------------------------------------------------------- /src/assets/images/weather/09n@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/src/assets/images/weather/09n@2x.png -------------------------------------------------------------------------------- /src/assets/images/weather/10d@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/src/assets/images/weather/10d@2x.png -------------------------------------------------------------------------------- /src/assets/images/weather/10n@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/src/assets/images/weather/10n@2x.png -------------------------------------------------------------------------------- /src/assets/images/weather/11d@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/src/assets/images/weather/11d@2x.png -------------------------------------------------------------------------------- /src/assets/images/weather/11n@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/src/assets/images/weather/11n@2x.png -------------------------------------------------------------------------------- /src/assets/images/weather/13d@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/src/assets/images/weather/13d@2x.png -------------------------------------------------------------------------------- /src/assets/images/weather/13n@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/src/assets/images/weather/13n@2x.png -------------------------------------------------------------------------------- /src/assets/images/weather/50d@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/src/assets/images/weather/50d@2x.png -------------------------------------------------------------------------------- /src/assets/images/weather/50n@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matt765/mistyloop/HEAD/src/assets/images/weather/50n@2x.png -------------------------------------------------------------------------------- /next-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | 4 | // NOTE: This file should not be edited 5 | // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. 6 | -------------------------------------------------------------------------------- /src/services/fetchFact.ts: -------------------------------------------------------------------------------- 1 | export const fetchFact = async () => { 2 | let data; 3 | do { 4 | const response = await fetch('/api/facts'); 5 | data = await response.json(); 6 | } while (data.text.length > 130 || data.text.length < 35); 7 | return data.text; 8 | }; 9 | -------------------------------------------------------------------------------- /src/hooks/useIsMounted.ts: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { useState, useEffect } from 'react'; 4 | 5 | export const useIsMounted = (): boolean => { 6 | const [isMounted, setIsMounted] = useState(false); 7 | 8 | useEffect(() => { 9 | setIsMounted(true); 10 | }, []); 11 | 12 | return isMounted; 13 | }; 14 | -------------------------------------------------------------------------------- /src/app/snake/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { SnakeGame } from '@/components/views/snake/Snake'; 4 | import { ProtectedRoute } from '@/components/auth/ProtectedRoute'; 5 | 6 | export default function SnakePage() { 7 | return ( 8 | 9 | 10 | 11 | ); 12 | } 13 | -------------------------------------------------------------------------------- /src/assets/icons/CheckIcon.tsx: -------------------------------------------------------------------------------- 1 | export const CheckIcon = () => ( 2 | 7 | 8 | 9 | ); 10 | -------------------------------------------------------------------------------- /src/services/fetchQuote.ts: -------------------------------------------------------------------------------- 1 | export const fetchQuote = async () => { 2 | let data; 3 | const category = 'happiness'; 4 | do { 5 | const response = await fetch(`/api/quotes?category=${category}`); 6 | data = await response.json(); 7 | } while (data[0].quote.length > 125 || data[0].quote.length < 35); 8 | return { quote: data[0].quote, author: data[0].author }; 9 | }; 10 | -------------------------------------------------------------------------------- /src/app/notepad/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { Notepad } from '@/components/views/notepad/Notepad'; 4 | import { useIsMounted } from '@/hooks/useIsMounted'; 5 | import { ProtectedRoute } from '@/components/auth/ProtectedRoute'; 6 | 7 | export default function NotepadPage() { 8 | return ( 9 | 10 | 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /src/assets/icons/ArrowLeftIcon.tsx: -------------------------------------------------------------------------------- 1 | export const ArrowLeftIcon = () => ( 2 | 3 | 4 | 5 | ); 6 | -------------------------------------------------------------------------------- /src/assets/icons/CloseIcon.tsx: -------------------------------------------------------------------------------- 1 | export const CloseIcon = () => ( 2 | 7 | 8 | 9 | ); 10 | -------------------------------------------------------------------------------- /src/assets/icons/EditIcon.tsx: -------------------------------------------------------------------------------- 1 | export const EditIcon = () => ( 2 | 3 | 4 | 5 | ); 6 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: false, 4 | env: { 5 | MY_SECRET_API_KEY: process.env.MY_SECRET_API_KEY, 6 | ANOTHER_SECRET: process.env.ANOTHER_SECRET, 7 | }, 8 | images: { 9 | remotePatterns: [ 10 | { 11 | protocol: 'https', 12 | hostname: 'cdn.weatherapi.com', 13 | pathname: '**', 14 | }, 15 | ], 16 | }, 17 | }; 18 | 19 | module.exports = nextConfig; 20 | -------------------------------------------------------------------------------- /src/store/mobileViewStore.ts: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | import { ViewType } from '@/hooks/useHomepage'; 3 | 4 | interface MobileViewState { 5 | mobileView: ViewType; 6 | setMobileView: (view: ViewType) => void; 7 | } 8 | 9 | export const useMobileViewStore = create((set) => ({ 10 | mobileView: 'mobileHome', 11 | setMobileView: (view) => { 12 | localStorage.setItem('currentMobileView', view); 13 | set({ mobileView: view }); 14 | }, 15 | })); 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | .pnpm-debug.log* 27 | 28 | # local env files 29 | .env*.local 30 | .env 31 | 32 | # vercel 33 | .vercel 34 | -------------------------------------------------------------------------------- /public/images/weather/weather-icons-license.txt: -------------------------------------------------------------------------------- 1 | Weather Icons License 2 | ===================== 3 | 4 | Weather icons provided by OpenWeather. 5 | 6 | Source: https://openweathermap.org/ 7 | Copyright (c) OpenWeather Ltd. 8 | Licensed under Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) 9 | License: https://creativecommons.org/licenses/by-sa/4.0/ 10 | 11 | No modifications have been made to the original icons. 12 | 13 | This project is not affiliated with or endorsed by OpenWeather Ltd. -------------------------------------------------------------------------------- /src/store/snakeStore.ts: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | 3 | import { 4 | loadFromLocalStorage, 5 | saveToLocalStorage, 6 | } from '@/utils/localStorageUtils'; 7 | 8 | type SnakeStoreState = { 9 | record: number; 10 | setRecord: (record: number) => void; 11 | }; 12 | 13 | export const useSnakeStore = create((set) => ({ 14 | record: loadFromLocalStorage('snakeStoreRecord', 0), 15 | setRecord: (record) => { 16 | saveToLocalStorage('snakeStoreRecord', record); 17 | set({ record }); 18 | }, 19 | })); 20 | 21 | -------------------------------------------------------------------------------- /src/assets/icons/PictureIcon.tsx: -------------------------------------------------------------------------------- 1 | export const PictureIcon = () => ( 2 | 7 | 8 | 9 | ); 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 1.0.0 (13-02-2025) 4 | 5 | - add changelog 6 | - add first tag & Github release 7 | - update package.json 8 | - add contributing guidelines 9 | 10 | 11 | ## 1.0.1 (15-02-2025) 12 | 13 | - migrate to new weather api 14 | - redesign homepage buttons 15 | - fix laptop resolution 16 | - refactor local storage utils 17 | - adjust quotes api url 18 | 19 | ## 1.0.2 (20-05-2025) 20 | 21 | - migrate to app router 22 | - create separate layout folder 23 | - extract views into subpages 24 | - fix console errors 25 | - adjust mobile resolution 26 | - migrate planner to @hello-pangea/dnd 27 | -------------------------------------------------------------------------------- /src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import { Providers } from '@/services/providers'; 2 | import { Layout } from '../components/layout/Layout'; 3 | 4 | export const metadata = { 5 | title: 'MistyLoop', 6 | description: 7 | 'Application designed to be an alternative to default starting page in the browser', 8 | }; 9 | 10 | export default function RootLayout({ 11 | children, 12 | }: { 13 | children: React.ReactNode; 14 | }) { 15 | return ( 16 | 17 | 18 | 19 | {children} 20 | 21 | 22 | 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /src/components/settings/SettingsTitle.tsx: -------------------------------------------------------------------------------- 1 | import { Box } from '@chakra-ui/react'; 2 | 3 | interface SettingsTitleProps { 4 | title: string; 5 | } 6 | 7 | export const SettingsTitle = ({ title }: SettingsTitleProps) => { 8 | return ( 9 | 20 | {title} 21 | 22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /src/store/weatherStore.ts: -------------------------------------------------------------------------------- 1 | import { create, SetState } from 'zustand'; 2 | import { WeatherData } from '@/hooks/useWeatherData'; 3 | 4 | interface WeatherStore { 5 | weatherData: WeatherData | null; 6 | isLoading: boolean; 7 | isError: boolean; 8 | setWeatherData: (data: WeatherData | null, hasError?: boolean) => void; 9 | } 10 | 11 | export const useWeatherStore = create((set: SetState) => ({ 12 | weatherData: null, 13 | isLoading: false, 14 | isError: false, 15 | setWeatherData: (data: WeatherData | null, hasError: boolean = false) => 16 | set((state) => ({ ...state, weatherData: data, isError: hasError })), 17 | })); 18 | -------------------------------------------------------------------------------- /src/assets/icons/HumidityIcon.tsx: -------------------------------------------------------------------------------- 1 | export const HumidityIcon = () => 2 | 17 | 18 | -------------------------------------------------------------------------------- /src/app/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { useMediaQuery } from '@chakra-ui/react'; 4 | import { Homepage } from '@/components/views/homepage/Homepage'; 5 | import { Intro } from '@/components/views/intro/Intro'; 6 | import { MobileView } from '@/components/layout/MobileView'; 7 | import { useUserStoreWrapper } from '@/store/userStore'; 8 | 9 | export default function Home() { 10 | const { name, city } = useUserStoreWrapper(); 11 | const [isDesktop] = useMediaQuery('(min-width: 1280px)'); 12 | const [isMobile] = useMediaQuery('(max-width: 1279px)'); 13 | 14 | if (!name || !city) { 15 | return ; 16 | } 17 | 18 | return ( 19 | <> 20 | {isDesktop && } 21 | {isMobile && } 22 | 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /src/assets/icons/HomeIcon.tsx: -------------------------------------------------------------------------------- 1 | export const HomeIcon = () => { 2 | return ( 3 | 8 | Home 9 | 10 | 11 | ); 12 | }; 13 | -------------------------------------------------------------------------------- /src/assets/icons/RefreshIcon.tsx: -------------------------------------------------------------------------------- 1 | export const RefreshIcon = () => ( 2 | 3 | 4 | 5 | ); 6 | -------------------------------------------------------------------------------- /src/assets/icons/PlannerMobileIcon.tsx: -------------------------------------------------------------------------------- 1 | export const PlannerMobileIcon = () => ( 2 | 3 | 4 | 5 | ); 6 | -------------------------------------------------------------------------------- /src/app/api/facts/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from 'next/server'; 2 | 3 | const baseFactsApiUrl = process.env.FACTS_API_URL || ''; 4 | 5 | export async function GET() { 6 | try { 7 | const factsApiUrl = `${baseFactsApiUrl}?language=en`; 8 | const response = await fetch(factsApiUrl); 9 | 10 | if (!response.ok) { 11 | const errorText = await response.text(); 12 | return NextResponse.json( 13 | { error: `Facts API response error: ${errorText}` }, 14 | { status: response.status } 15 | ); 16 | } 17 | 18 | const data = await response.json(); 19 | return NextResponse.json(data); 20 | } catch (error) { 21 | console.error('Error fetching facts:', error); 22 | return NextResponse.json( 23 | { error: 'Failed to fetch facts' }, 24 | { status: 500 } 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/assets/icons/NotepadEditIcon.tsx: -------------------------------------------------------------------------------- 1 | export const NotepadEditIcon = () => ( 2 | 3 | 4 | 5 | ); 6 | -------------------------------------------------------------------------------- /src/hooks/useClickOutside.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, RefObject } from 'react'; 2 | 3 | export const useClickOutside = ( 4 | ref: RefObject, 5 | handler: (event: MouseEvent | TouchEvent) => void 6 | ): void => { 7 | useEffect(() => { 8 | const listener = (event: MouseEvent | TouchEvent) => { 9 | // Do nothing if clicking ref's element or descendent elements 10 | if (!ref.current || ref.current.contains(event.target as Node)) { 11 | return; 12 | } 13 | handler(event); 14 | }; 15 | document.addEventListener('mousedown', listener); 16 | document.addEventListener('touchstart', listener); 17 | return () => { 18 | document.removeEventListener('mousedown', listener); 19 | document.removeEventListener('touchstart', listener); 20 | }; 21 | }, [ref, handler]); 22 | }; 23 | -------------------------------------------------------------------------------- /src/components/settings/SettingsSection.tsx: -------------------------------------------------------------------------------- 1 | import { BoxProps, Flex } from '@chakra-ui/react'; 2 | 3 | interface Props extends BoxProps { 4 | paddingBottom?: string; 5 | children: React.ReactNode; 6 | } 7 | 8 | export const SettingsSection = ({ 9 | paddingBottom, 10 | children, 11 | ...props 12 | }: Props) => { 13 | return ( 14 | 30 | {children} 31 | 32 | ); 33 | }; 34 | -------------------------------------------------------------------------------- /src/assets/icons/WeatherMobileIcon.tsx: -------------------------------------------------------------------------------- 1 | export const WeatherMobileIcon = () => ( 2 | 3 | 4 | 5 | ); 6 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "strict": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "noEmit": true, 14 | "esModuleInterop": true, 15 | "module": "esnext", 16 | "moduleResolution": "bundler", 17 | "resolveJsonModule": true, 18 | "isolatedModules": true, 19 | "jsx": "preserve", 20 | "incremental": true, 21 | "baseUrl": ".", 22 | "paths": { 23 | "@/*": [ 24 | "./src/*" 25 | ] 26 | }, 27 | "plugins": [ 28 | { 29 | "name": "next" 30 | } 31 | ] 32 | }, 33 | "include": [ 34 | "**/*.ts", 35 | "**/*.tsx", 36 | "next-env.d.ts", 37 | ".next/types/**/*.ts" 38 | ], 39 | "exclude": [ 40 | "node_modules" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Thank you for your interest in contributing to MistyLoop project. I appreciate your feedback, which is invaluable in helping me improve and grow this application. If you encounter any issues, have suggestions for improvements, or would like to share your thoughts, please don't hesitate to open an issue on the GitHub Issues page or to contact me directly. 2 | 3 | If you would like to support the ongoing development and maintenance of this project, you can do so through the GitHub Sponsors program. Your sponsorship helps me dedicate more time and resources to this project. To become a sponsor, you can click the "Sponsor" button on my profile. 4 | 5 | For some time, I will hold off on accepting external merge requests. While I greatly appreciate your willingness to contribute code, I am currently focused on managing the project's development internally. This may change in the future. 6 | 7 | Thank you once again for your interest and support! 8 | 9 | ~matt765 -------------------------------------------------------------------------------- /src/assets/icons/GithubIcon.tsx: -------------------------------------------------------------------------------- 1 | export const GithubIcon = () => { 2 | return ( 3 | 4 | 5 | 6 | ); 7 | }; 8 | -------------------------------------------------------------------------------- /src/assets/icons/DeleteIcon.tsx: -------------------------------------------------------------------------------- 1 | export const DeleteIcon = () => 2 | 19 | 20 | -------------------------------------------------------------------------------- /src/assets/icons/DragHandleIcon.tsx: -------------------------------------------------------------------------------- 1 | export const DragHandleIcon = () => 2 | 19 | 20 | -------------------------------------------------------------------------------- /src/assets/icons/SettingsIcon.tsx: -------------------------------------------------------------------------------- 1 | export const SettingsIcon = () => ( 2 | 11 | 12 | 13 | 14 | 15 | ); 16 | -------------------------------------------------------------------------------- /src/components/auth/ProtectedRoute.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { ReactNode, useEffect } from 'react'; 4 | import { useRouter } from 'next/navigation'; 5 | import { useUserStoreWrapper } from '@/store/userStore'; 6 | import { Flex } from '@chakra-ui/react'; 7 | import { Loader } from '@/components/common/Loader'; 8 | 9 | interface ProtectedRouteProps { 10 | children: ReactNode; 11 | } 12 | 13 | export const ProtectedRoute = ({ children }: ProtectedRouteProps) => { 14 | const { name, city, isMounted } = useUserStoreWrapper(); 15 | const router = useRouter(); 16 | 17 | useEffect(() => { 18 | if (isMounted && (!name || !city)) { 19 | router.replace('/'); 20 | } 21 | }, [name, city, isMounted, router]); 22 | 23 | if (!isMounted) { 24 | return ( 25 | 26 | 27 | 28 | ); 29 | } 30 | 31 | if (!name || !city) { 32 | return null; 33 | } 34 | 35 | return <>{children}; 36 | }; 37 | -------------------------------------------------------------------------------- /src/components/views/homepage/weather/WeatherParameter.tsx: -------------------------------------------------------------------------------- 1 | import { Flex, Icon, Text } from '@chakra-ui/react'; 2 | 3 | interface WeatherParameterProps { 4 | icon: React.FC>; 5 | title: string; 6 | value: string | number | undefined; 7 | } 8 | 9 | export const WeatherParameter = ({ 10 | icon, 11 | title, 12 | value, 13 | }: WeatherParameterProps) => ( 14 | 22 | 23 | 24 | 25 | 26 | 29 | {title} 30 | 31 | 34 | {value} 35 | 36 | 37 | 38 | ); 39 | -------------------------------------------------------------------------------- /src/utils/clearAllData.ts: -------------------------------------------------------------------------------- 1 | import { useNotepadStore } from '@/store/notepadStore'; 2 | import { plannerItemsDefault, usePlannerStore } from '@/store/plannerStore'; 3 | import { useSettingsStore } from '@/store/settingsStore'; 4 | import { useSnakeStore } from '@/store/snakeStore'; 5 | import { useUserStore } from '@/store/userStore'; 6 | 7 | export const clearAllData = () => { 8 | const notepadStore = useNotepadStore.getState(); 9 | const plannerStore = usePlannerStore.getState(); 10 | const settingsStore = useSettingsStore.getState(); 11 | const snakeStore = useSnakeStore.getState(); 12 | const userStore = useUserStore.getState(); 13 | 14 | notepadStore.setStoreNote(''); 15 | notepadStore.setIsNotepadModalConfirmed(false); 16 | plannerStore.setPlannerItems([]); 17 | settingsStore.resetSettings(); 18 | snakeStore.setRecord(0); 19 | userStore.setName(''); 20 | userStore.setCity(''); 21 | 22 | localStorage.clear(); 23 | 24 | const { setPlannerItems } = usePlannerStore.getState(); 25 | setPlannerItems(plannerItemsDefault); 26 | }; 27 | -------------------------------------------------------------------------------- /src/app/api/quotes/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from 'next/server'; 2 | 3 | const apiKey = process.env.QUOTES_API_KEY; 4 | const apiUrl = process.env.QUOTES_API_URL || ''; 5 | 6 | export async function GET() { 7 | if (!apiKey) { 8 | return NextResponse.json({ error: 'API key is missing' }, { status: 500 }); 9 | } 10 | 11 | try { 12 | const response = await fetch(apiUrl, { 13 | method: 'GET', 14 | headers: { 15 | 'X-Api-Key': apiKey, 16 | } as HeadersInit, 17 | }); 18 | 19 | if (!response.ok) { 20 | const errorText = await response.text(); 21 | return NextResponse.json( 22 | { error: `API response error: ${errorText}` }, 23 | { status: response.status } 24 | ); 25 | } 26 | 27 | const data = await response.json(); 28 | return NextResponse.json(data); 29 | } catch (error) { 30 | console.error('Error fetching quotes:', error); 31 | return NextResponse.json( 32 | { error: 'Failed to fetch quotes' }, 33 | { status: 500 } 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/theme/components/contentBox.tsx: -------------------------------------------------------------------------------- 1 | import useSettingsStore from '@/store/settingsStore'; 2 | import { Flex, BoxProps, useColorMode } from '@chakra-ui/react'; 3 | import { Ref } from 'react'; 4 | 5 | interface ContentBoxProps extends BoxProps { 6 | children: React.ReactNode; 7 | ref?: Ref; 8 | } 9 | 10 | export const ContentBox = ({ children, ref, ...props }: ContentBoxProps) => { 11 | const { colorMode } = useColorMode(); 12 | const theme = useSettingsStore((state) => state.theme); 13 | 14 | const boxShadowStyle = 15 | colorMode === 'light' && theme === 'basicTheme' 16 | ? { boxShadow: { base: "", lg: '0 3px 8px rgba(0,0,0,.74)' } } 17 | : {}; 18 | 19 | return ( 20 | 30 | {children} 31 | 32 | ); 33 | }; 34 | -------------------------------------------------------------------------------- /public/vercel.svg: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /src/assets/icons/PressureIcon.tsx: -------------------------------------------------------------------------------- 1 | export const PressureIcon = () => 2 | 17 | 18 | -------------------------------------------------------------------------------- /src/store/plannerStore.ts: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | 3 | import { 4 | loadFromLocalStorage, 5 | saveToLocalStorage, 6 | } from '@/utils/localStorageUtils'; 7 | 8 | export const plannerItemsDefault = [ 9 | { 10 | text: 'Finish front-end project', 11 | isCrossed: false, 12 | }, 13 | { 14 | text: 'Fix a printer', 15 | isCrossed: false, 16 | }, 17 | { 18 | text: 'Buy new music album', 19 | isCrossed: false, 20 | }, 21 | { 22 | text: 'Sort old photos', 23 | isCrossed: true, 24 | }, 25 | { 26 | text: 'Feed the cat', 27 | isCrossed: false, 28 | }, 29 | ]; 30 | 31 | interface PlannerItem { 32 | text: string; 33 | isCrossed: boolean; 34 | } 35 | 36 | type PlannerStore = { 37 | plannerItems: PlannerItem[]; 38 | setPlannerItems: (items: PlannerItem[]) => void; 39 | }; 40 | 41 | const LOCAL_STORAGE_KEY = 'plannerItems'; 42 | 43 | export const usePlannerStore = create((set) => ({ 44 | plannerItems: loadFromLocalStorage(LOCAL_STORAGE_KEY, plannerItemsDefault), 45 | setPlannerItems: (items) => { 46 | saveToLocalStorage(LOCAL_STORAGE_KEY, items); 47 | set({ plannerItems: items }); 48 | }, 49 | })); 50 | -------------------------------------------------------------------------------- /src/utils/localStorageUtils.ts: -------------------------------------------------------------------------------- 1 | export const saveToLocalStorage = (key: string, value: T) => { 2 | if (typeof window !== 'undefined') { 3 | try { 4 | const serializedValue = JSON.stringify(value); 5 | localStorage.setItem(key, serializedValue); 6 | } catch (error) { 7 | console.warn(`Error saving to localStorage for key "${key}":`, error); 8 | } 9 | } 10 | }; 11 | 12 | export const loadFromLocalStorage = (key: string, initialValue: T): T => { 13 | if (typeof window === 'undefined') { 14 | return initialValue; 15 | } 16 | 17 | try { 18 | const storedData = localStorage.getItem(key); 19 | if (!storedData) { 20 | return initialValue; 21 | } 22 | 23 | const parsedData = JSON.parse(storedData) as T; 24 | 25 | // Additional type validation 26 | if (parsedData === null || parsedData === undefined) { 27 | return initialValue; 28 | } 29 | 30 | return parsedData; 31 | } catch (error) { 32 | // If there's any error parsing the data, clear the corrupted data 33 | localStorage.removeItem(key); 34 | console.warn(`Error loading data from localStorage for key "${key}":`, error); 35 | return initialValue; 36 | } 37 | }; -------------------------------------------------------------------------------- /src/assets/icons/WindIcon.tsx: -------------------------------------------------------------------------------- 1 | export const WindIcon = () => 2 | 17 | 18 | -------------------------------------------------------------------------------- /src/components/settings/SettingsModals.tsx: -------------------------------------------------------------------------------- 1 | import { useDisclosure } from '@chakra-ui/react'; 2 | import { EditUserData } from '@/components/modals/EditUserDataModal'; 3 | import { ClearAllData } from '@/components/modals/ClearAllDataModal'; 4 | 5 | interface SettingsModalsProps { 6 | onSettingsPanelClose: () => void; 7 | } 8 | 9 | export const SettingsModals: React.FC = ({ 10 | onSettingsPanelClose, 11 | }) => { 12 | const { 13 | isOpen: isEditUserDataOpen, 14 | onOpen: onEditUserDataOpen, 15 | onClose: onEditUserDataClose, 16 | } = useDisclosure(); 17 | const { 18 | isOpen: isClearAllDataOpen, 19 | onOpen: onClearAllDataOpen, 20 | onClose: onClearAllDataClose, 21 | } = useDisclosure(); 22 | 23 | return ( 24 | <> 25 | {isEditUserDataOpen && ( 26 | { 28 | onEditUserDataClose(); 29 | onSettingsPanelClose(); 30 | }} 31 | /> 32 | )} 33 | {isClearAllDataOpen && ( 34 | { 36 | onClearAllDataClose(); 37 | onSettingsPanelClose(); 38 | }} 39 | /> 40 | )} 41 | 42 | ); 43 | }; 44 | -------------------------------------------------------------------------------- /src/hooks/useCurrentDate.ts: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | export const useCurrentDate = () => { 4 | const [currentDate, setCurrentDate] = useState(new Date()); 5 | 6 | useEffect(() => { 7 | const timer = setInterval(() => { 8 | setCurrentDate(new Date()); 9 | }, 60 * 1000); // Update every minute 10 | 11 | return () => { 12 | clearInterval(timer); 13 | }; 14 | }, []); 15 | 16 | const daysOfWeek = [ 17 | 'Sunday', 18 | 'Monday', 19 | 'Tuesday', 20 | 'Wednesday', 21 | 'Thursday', 22 | 'Friday', 23 | 'Saturday', 24 | ]; 25 | 26 | const dayOfWeek = daysOfWeek[currentDate.getDay()]; 27 | 28 | const monthNames = [ 29 | 'January', 30 | 'February', 31 | 'March', 32 | 'April', 33 | 'May', 34 | 'June', 35 | 'July', 36 | 'August', 37 | 'September', 38 | 'October', 39 | 'November', 40 | 'December', 41 | ]; 42 | 43 | const monthName = monthNames[currentDate.getMonth()]; 44 | const dayOfMonth = currentDate.getDate(); 45 | const year = currentDate.getFullYear(); 46 | 47 | return { 48 | dayOfWeek, 49 | dayOfMonth, 50 | monthName, 51 | year, 52 | }; 53 | }; 54 | 55 | export default useCurrentDate; 56 | -------------------------------------------------------------------------------- /src/components/layout/SnakeButton.tsx: -------------------------------------------------------------------------------- 1 | import { SnakeIcon } from '@/assets/icons/SnakeIcon'; 2 | import { ViewType } from '@/hooks/useHomepage'; 3 | import { Button, Flex, Icon } from '@chakra-ui/react'; 4 | 5 | interface SnakeButtonProps { 6 | handleToggleView: (view: ViewType, deviceType: 'mobile' | 'desktop') => void; 7 | desktopView: ViewType; 8 | } 9 | 10 | export const SnakeButton = ({ 11 | handleToggleView, 12 | desktopView, 13 | }: SnakeButtonProps) => { 14 | const toggleView = () => { 15 | handleToggleView( 16 | desktopView === 'snake' ? 'dashboard' : 'snake', 17 | 'desktop' 18 | ); 19 | }; 20 | return ( 21 | 29 | 43 | 44 | ); 45 | }; 46 | -------------------------------------------------------------------------------- /src/store/notepadStore.ts: -------------------------------------------------------------------------------- 1 | import { create } from 'zustand'; 2 | 3 | import { 4 | loadFromLocalStorage, 5 | saveToLocalStorage, 6 | } from '@/utils/localStorageUtils'; 7 | 8 | interface NotepadStore { 9 | storeNote: string; 10 | setStoreNote: (newText: string) => void; 11 | isNotepadModalConfirmed: boolean; 12 | setIsNotepadModalConfirmed: (value: boolean) => void; 13 | isModalVisible: boolean; 14 | setIsModalVisible: (value: boolean) => void; 15 | } 16 | 17 | const LOCAL_STORAGE_KEY = 'notepadStoreNote'; 18 | const LOCAL_STORAGE_CONFIRMED_KEY = 'isNotepadModalConfirmed'; 19 | 20 | export const useNotepadStore = create((set) => ({ 21 | storeNote: loadFromLocalStorage(LOCAL_STORAGE_KEY, ''), 22 | setStoreNote: (newText: string) => { 23 | saveToLocalStorage(LOCAL_STORAGE_KEY, newText); 24 | set({ storeNote: newText }); 25 | }, 26 | isNotepadModalConfirmed: loadFromLocalStorage( 27 | LOCAL_STORAGE_CONFIRMED_KEY, 28 | false 29 | ), 30 | setIsNotepadModalConfirmed: (value: boolean) => { 31 | saveToLocalStorage(LOCAL_STORAGE_CONFIRMED_KEY, value); 32 | set({ isNotepadModalConfirmed: value }); 33 | }, 34 | isModalVisible: false, 35 | setIsModalVisible: (value: boolean) => set({ isModalVisible: value }), 36 | })); 37 | -------------------------------------------------------------------------------- /src/components/views/homepage/weather/WeatherHourBox.tsx: -------------------------------------------------------------------------------- 1 | import { Flex, Text } from '@chakra-ui/react'; 2 | import Image from 'next/image'; 3 | import { useWeatherUtils } from '@/hooks/useWeatherUtils'; 4 | 5 | export interface WeatherHourBoxProps { 6 | date?: string; 7 | hour?: string; 8 | icon?: string; 9 | weather?: { icon: string }[]; 10 | dt?: string; 11 | temp?: string; 12 | } 13 | 14 | export const WeatherHourBox = ({ 15 | date, 16 | hour, 17 | icon, 18 | temp, 19 | }: WeatherHourBoxProps) => { 20 | const { getWeatherImage } = useWeatherUtils(); 21 | const iconUrl = icon ? getWeatherImage(icon) : '/images/weather/01d@2x.png'; 22 | 23 | return ( 24 | 34 | {date} 35 | {hour}:00 36 | Weather Icon 37 | {temp}° 38 | 39 | ); 40 | }; 41 | -------------------------------------------------------------------------------- /src/assets/icons/HandIcon.tsx: -------------------------------------------------------------------------------- 1 | export const HandIcon = () => 2 | 17 | 18 | -------------------------------------------------------------------------------- /src/services/providers.tsx: -------------------------------------------------------------------------------- 1 | 'use client'; 2 | 3 | import { ChakraProvider } from '@chakra-ui/react'; 4 | import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; 5 | import { Analytics } from '@vercel/analytics/react'; 6 | import { useState, useEffect } from 'react'; 7 | 8 | import '@fontsource/roboto/100.css'; 9 | import '@fontsource/quicksand'; 10 | import '@fontsource/heebo/600.css'; 11 | import '@fontsource/heebo/500.css'; 12 | import '@fontsource/heebo/400.css'; 13 | import '@fontsource/inter/600.css'; 14 | import '@fontsource/inter/500.css'; 15 | import '@fontsource/inter/400.css'; 16 | 17 | import { generateTheme } from '@/theme/generateTheme'; 18 | import { extendedColors } from '@/theme/extendedColors'; 19 | import { colors } from '@/theme/colors'; 20 | import useSettingsStore from '@/store/settingsStore'; 21 | 22 | export function Providers({ children }: { children: React.ReactNode }) { 23 | const [queryClient] = useState(() => new QueryClient()); 24 | const theme = useSettingsStore((state) => state.theme); 25 | const basicTheme = generateTheme(colors); 26 | const extendedTheme = generateTheme(extendedColors); 27 | 28 | return ( 29 | 30 | 32 | {children} 33 | 34 | 35 | 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /src/components/settings/SettingsSlider.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Slider, 3 | SliderFilledTrack, 4 | SliderThumb, 5 | SliderTrack, 6 | Tooltip, 7 | } from '@chakra-ui/react'; 8 | import { useState } from 'react'; 9 | 10 | import useSettingsStore from '@/store/settingsStore'; 11 | 12 | export const SettingsSlider = () => { 13 | const [showTooltip, setShowTooltip] = useState(false); 14 | const sliderValue = useSettingsStore((state) => state.sliderValue); 15 | const setSliderValue = useSettingsStore((state) => state.setSliderValue); 16 | 17 | return ( 18 | setSliderValue(v)} 25 | onMouseEnter={() => setShowTooltip(true)} 26 | onMouseLeave={() => setShowTooltip(false)} 27 | mb="1.5rem" 28 | mt="1.2rem" 29 | position="relative" 30 | sx={{ 31 | '& .chakra-slider__filled-track': { 32 | backgroundColor: 'settingsMainColor !important', 33 | }, 34 | }}> 35 | 36 | 37 | 38 | 45 | 46 | 47 | 48 | ); 49 | }; 50 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mistyloop", 3 | "version": "1.0.2", 4 | "private": true, 5 | "license": "CC-BY-NC-ND-4.0", 6 | "description": "Application designed to be an alternative to default starting page in the browser", 7 | "author": { 8 | "name": "matt765", 9 | "email": "mateusz.wyrebek@gmail.com", 10 | "url": "https://matt765-portfolio.vercel.app/" 11 | }, 12 | "scripts": { 13 | "dev": "next dev", 14 | "build": "next build", 15 | "start": "next start", 16 | "lint": "next lint" 17 | }, 18 | "dependencies": { 19 | "@chakra-ui/react": "^2.6.1", 20 | "@emotion/react": "^11.10.8", 21 | "@emotion/styled": "^11.10.8", 22 | "@fontsource/heebo": "^4.5.15", 23 | "@fontsource/inter": "^4.5.15", 24 | "@fontsource/quicksand": "^4.5.12", 25 | "@fontsource/roboto": "^4.5.8", 26 | "@hello-pangea/dnd": "^18.0.1", 27 | "@tanstack/react-query": "^5.76.1", 28 | "@types/node": "^18.16.3", 29 | "@types/react": "^18.2.4", 30 | "@types/react-dom": "^18.2.3", 31 | "@vercel/analytics": "^1.0.1", 32 | "axios": "^1.4.0", 33 | "next": "^15.3.2", 34 | "query-string": "^8.1.0", 35 | "react": "^19.1.0", 36 | "react-dom": "^19.1.0", 37 | "typescript": "^5.0.4" 38 | }, 39 | "devDependencies": { 40 | "eslint": "^8.39.0", 41 | "eslint-config-next": "^15.3.2", 42 | "eslint-config-prettier": "^8.8.0", 43 | "prettier": "^2.8.8", 44 | "zustand": "^4.3.8" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/components/views/notepad/NotepadTextArea.tsx: -------------------------------------------------------------------------------- 1 | import { Textarea } from '@chakra-ui/react'; 2 | 3 | interface NotepadTextAreaProps { 4 | textAreaRef: React.RefObject; 5 | handleBoxClick: () => void; 6 | handleTextChange: () => void; 7 | onMouseEnter: () => void; 8 | onMouseLeave: () => void; 9 | initialValue?: string; 10 | } 11 | 12 | export const NotepadTextArea = ({ 13 | textAreaRef, 14 | handleBoxClick, 15 | handleTextChange, 16 | onMouseEnter, 17 | onMouseLeave, 18 | initialValue = '', 19 | }: NotepadTextAreaProps) => { 20 | return ( 21 |