initial commit

This commit is contained in:
Nikurasu 2024-08-22 14:55:43 +02:00
commit c902bc9236
Signed by: Nikurasu
GPG key ID: 9E7F14C03EF1F271
30 changed files with 4091 additions and 0 deletions

28
.gitignore vendored Executable file
View file

@ -0,0 +1,28 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
# Config files
.webextrc
.webextrc.*

33
package.json Executable file
View file

@ -0,0 +1,33 @@
{
"name": "ext-test",
"private": true,
"version": "1.0.0",
"type": "module",
"scripts": {
"dev": "TARGET=firefox vite",
"build-firefox": "tsc && TARGET=firefox vite build",
"build": "tsc && vite build"
},
"dependencies": {
"@emotion/react": "^11.13.0",
"@emotion/styled": "^11.13.0",
"@fontsource/roboto": "^5.0.13",
"@mui/icons-material": "^5.16.4",
"@mui/material": "^5.16.4",
"@mui/styled-engine-sc": "6.0.0-alpha.18",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"styled-components": "^6.1.12",
"wouter": "^3.3.1"
},
"devDependencies": {
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.9",
"@types/webextension-polyfill": "^0.10.0",
"@vitejs/plugin-react": "^4.2.1",
"typescript": "^5.3.2",
"vite": "^5.0.0",
"vite-plugin-web-extension": "^4.0.0",
"webextension-polyfill": "^0.10.0"
}
}

3523
pnpm-lock.yaml Executable file

File diff suppressed because it is too large Load diff

29
public/icon-with-shadow.svg Executable file
View file

@ -0,0 +1,29 @@
<svg width="585" height="585" viewBox="0 0 585 585" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="mask0_2_13" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="0" y="0" width="585" height="585">
<path d="M585 292.5C585 454.043 454.043 585 292.5 585C130.957 585 0 454.043 0 292.5C0 130.957 130.957 0 292.5 0C454.043 0 585 130.957 585 292.5Z" fill="url(#paint0_radial_2_13)"/>
</mask>
<g mask="url(#mask0_2_13)">
<path d="M585 292.5C585 454.043 454.043 585 292.5 585C130.957 585 0 454.043 0 292.5C0 130.957 130.957 0 292.5 0C454.043 0 585 130.957 585 292.5Z" fill="url(#paint1_linear_2_13)"/>
</g>
<path fill-rule="evenodd" clip-rule="evenodd" d="M281 182C308.614 182 331 159.614 331 132L401 132C417.569 132 431 145.431 431 162V232.158C456.744 234.195 477 255.732 477 282C477 308.268 456.744 329.805 431 331.842V402C431 418.569 417.569 432 401 432H331C331 459.614 308.614 482 281 482C253.386 482 231 459.614 231 432H161C144.431 432 131 418.569 131 402L131 332C158.614 332 181 309.614 181 282C181 254.386 158.614 232 131 232L131 162C131 145.431 144.431 132 161 132L231 132C231 159.614 253.386 182 281 182Z" fill="url(#paint2_linear_2_13)"/>
<path d="M364.115 102.193L234.791 127.497C232.666 127.913 231.092 129.712 230.964 131.871L223.009 266.034C222.821 269.194 225.728 271.647 228.816 270.936L264.822 262.638C268.191 261.862 271.235 264.825 270.543 268.208L259.845 320.515C259.125 324.035 262.435 327.046 265.878 326.001L288.117 319.254C291.565 318.209 294.877 321.228 294.148 324.751L277.148 406.914C276.084 412.053 282.93 414.856 285.785 410.449L287.692 407.507L393.073 197.505C394.838 193.989 391.795 189.98 387.927 190.725L350.865 197.867C347.382 198.538 344.419 195.299 345.402 191.897L369.592 108.161C370.576 104.753 367.602 101.511 364.115 102.193Z" fill="url(#paint3_linear_2_13)"/>
<defs>
<radialGradient id="paint0_radial_2_13" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(292.958 292.042) rotate(90) scale(292.958 292.958)">
<stop/>
<stop offset="1" stop-opacity="0"/>
</radialGradient>
<linearGradient id="paint1_linear_2_13" x1="216.056" y1="140.528" x2="477.969" y2="374.671" gradientUnits="userSpaceOnUse">
<stop stop-color="#41D1FF"/>
<stop offset="1" stop-color="#BD34FE"/>
</linearGradient>
<linearGradient id="paint2_linear_2_13" x1="116.5" y1="223" x2="414.5" y2="482" gradientUnits="userSpaceOnUse">
<stop stop-color="#41D1FF"/>
<stop offset="1" stop-color="#BD34FE"/>
</linearGradient>
<linearGradient id="paint3_linear_2_13" x1="270.74" y1="109.063" x2="309.973" y2="378.586" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFEA83"/>
<stop offset="0.0833333" stop-color="#FFDD35"/>
<stop offset="1" stop-color="#FFA800"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
public/icon/128.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
public/icon/16.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 698 B

BIN
public/icon/32.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
public/icon/48.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
public/icon/96.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

7
src/background.ts Executable file
View file

@ -0,0 +1,7 @@
import browser from "webextension-polyfill";
console.log("Hello from the background!");
browser.runtime.onInstalled.addListener((details) => {
console.log("Extension installed:", details);
});

31
src/components/MenuBar.tsx Executable file
View file

@ -0,0 +1,31 @@
import { IconButton, Toolbar, Typography } from '@mui/material';
import AppBar from '@mui/material/AppBar';
import Box from '@mui/material/Box';
import MenuIcon from '@mui/icons-material/Menu'
import { useContext } from 'react';
import { DrawerOpenContext } from '../contexts/DrawerOpenContext';
export default function MenuBar() {
const {drawerOpen, setDrawerOpen} = useContext(DrawerOpenContext)
return (
<Box sx={{flexGrow: 1}}>
<AppBar position="static">
<Toolbar>
<IconButton
size='large'
edge='start'
color='inherit'
aria-label='menu'
sx={{ mr: 2 }}
onClick={() => setDrawerOpen(!drawerOpen)}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" component="div" sx={{ flexGrow: 1 }}>
Tamakis Homepage
</Typography>
</Toolbar>
</AppBar>
</Box>
)
}

18
src/components/linkcard.tsx Executable file
View file

@ -0,0 +1,18 @@
import { Card, CardActionArea, CardContent, Typography } from "@mui/material";
export default function LinkCard({title = "None", subtitle = "None", hyperlink = "https://example.com"}) {
return (
<Card sx={{ maxWidth: 345, minWidth: 256 }}>
<CardActionArea href={hyperlink}>
<CardContent>
<Typography gutterBottom variant="h5" component="div">
{title}
</Typography>
<Typography variant="body2" color="text.secondary">
{subtitle}
</Typography>
</CardContent>
</CardActionArea>
</Card>
)
}

View file

@ -0,0 +1,28 @@
import { useContext } from "react";
import { DrawerOpenContext } from "../contexts/DrawerOpenContext";
import { Drawer, List, Box, ListItem, ListItemButton, ListItemText, Typography } from "@mui/material";
import { CurrentPageContext } from "../contexts/CurrentPageContext";
export default function MenuDrawer() {
const {drawerOpen, setDrawerOpen} = useContext(DrawerOpenContext)
const {currentPage, setCurrentPage} = useContext(CurrentPageContext)
return (
<Drawer open={drawerOpen} onClose={() => setDrawerOpen(!drawerOpen)}>
<Box role='presentation' sx={{width: 250}} onClick={() => setDrawerOpen(!drawerOpen )}>
<List>
<ListItem key='home' disablePadding>
<ListItemButton onClick={() => setCurrentPage('home')}>
<ListItemText primary='Home' />
</ListItemButton>
</ListItem>
<ListItem key='edit' disablePadding>
<ListItemButton onClick={() => setCurrentPage('edit')}>
<ListItemText primary='Edit' />
</ListItemButton>
</ListItem>
</List>
</Box>
</Drawer>
)
}

View file

@ -0,0 +1,25 @@
import { Grid, styled, TextField, Typography } from "@mui/material";
import { useState } from "react";
const FormGrid = styled(Grid)(() => ({
display: 'flex',
flexDirection: 'column'
}))
export default function SettingsPage() {
const [data, setData] = useState([{title: '', subtitle: '', hyperlink: ''}])
return (
<Grid container spacing={3} sx={{mt: 4, mb: 4}}>
<FormGrid item xs={12} md={6}>
<TextField
required
id='text'
label='text'
name='text'
onChange={e => console.log(e.target.name)}
/>
</FormGrid>
</Grid>
)
}

View file

@ -0,0 +1,24 @@
import { ContainerProps } from "@mui/material"
import { createContext, Dispatch, SetStateAction, useState } from "react"
type CurrentPageContextType = {
currentPage: string,
setCurrentPage: Dispatch<SetStateAction<string>>
}
const CurrentPageContext = createContext<CurrentPageContextType>({
currentPage: 'home',
setCurrentPage: () => {}
})
const CurrentPageContextProvider = (props: ContainerProps) => {
const [currentPage, setCurrentPage] = useState('home')
return (
<CurrentPageContext.Provider value={{currentPage, setCurrentPage}}>
{props.children}
</CurrentPageContext.Provider>
)
}
export {CurrentPageContext, CurrentPageContextProvider}

View file

@ -0,0 +1,25 @@
import { ContainerProps } from "@mui/material";
import { createContext, Dispatch, SetStateAction, useState } from "react";
type DrawerOpenContextType = {
drawerOpen: boolean,
setDrawerOpen: Dispatch<SetStateAction<boolean>>
}
const DrawerOpenContext = createContext<DrawerOpenContextType>({
drawerOpen: false,
setDrawerOpen: () => {},
})
const DrawerOpenContextProvider = (props: ContainerProps) => {
const [drawerOpen, setDrawerOpen] = useState(false)
return (
<DrawerOpenContext.Provider value={{drawerOpen, setDrawerOpen}}>
{props.children}
</DrawerOpenContext.Provider>
)
}
export {DrawerOpenContext, DrawerOpenContextProvider}

24
src/data/sites.json Executable file
View file

@ -0,0 +1,24 @@
{
"sites": [
{
"title": "Sonarr",
"subtitle": "A tracker for all your series",
"hyperlink": "https://sonarr.local.thanalan.cat-enby.club"
},
{
"title": "Radarr",
"subtitle": "A tracker for all your movies",
"hyperlink": "https://radarr.local.thanalan.cat-enby.club"
},
{
"title": "qbittorrent",
"subtitle": "Torrent Software powered by qt",
"hyperlink": "https://qb.local.thanalan.cat-enby.club"
},
{
"title": "sabnzb",
"subtitle": "Usenet client",
"hyperlink": "https://sab.local.thanalan.cat-enby.club"
}
]
}

13
src/index.html Executable file
View file

@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="./index.tsx"></script>
</body>
</html>

26
src/index.tsx Executable file
View file

@ -0,0 +1,26 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './site/App'
import MenuBar from './components/MenuBar';
import '@fontsource/roboto/300.css';
import '@fontsource/roboto/400.css';
import '@fontsource/roboto/500.css';
import '@fontsource/roboto/700.css';
import { ThemeProvider } from '@mui/material/styles';
import { CssBaseline } from '@mui/material';
import theme from './theme';
import { DrawerOpenContextProvider } from './contexts/DrawerOpenContext';
import { CurrentPageContextProvider } from './contexts/CurrentPageContext';
ReactDOM.createRoot(document.getElementById('root')!).render(
<ThemeProvider theme={theme}>
<CssBaseline>
<DrawerOpenContextProvider>
<CurrentPageContextProvider>
<App />
</CurrentPageContextProvider>
</DrawerOpenContextProvider>
</CssBaseline>
</ThemeProvider>
)

28
src/manifest.json Executable file
View file

@ -0,0 +1,28 @@
{
"{{chrome}}.manifest_version": 3,
"{{firefox}}.manifest_version": 2,
"icons": {
"16": "icon/16.png",
"32": "icon/32.png",
"48": "icon/48.png",
"96": "icon/96.png",
"128": "icon/128.png"
},
"{{chrome}}.action": {
"default_popup": "src/popup.html"
},
"{{firefox}}.settings_overrides": {
"homepage": "src/index.html"
},
"{{firefox}}.browser_action": {
"default_popup":"src/popup.html"
},
"chrome_url_overrides": {
"newtab": "src/index.html"
},
"background": {
"{{chrome}}.service_worker": "src/background.ts",
"{{firefox}}.scripts": ["src/background.ts"]
},
"permissions": ["storage"]
}

13
src/popup.html Normal file
View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Popup</title>
</head>
<body></body>
<script type="module" src="./popup.tsx"></script>
</html>

9
src/popup.tsx Executable file
View file

@ -0,0 +1,9 @@
import React from "react";
import ReactDOM from "react-dom/client";
import Popup from "./popup/Popup";
ReactDOM.createRoot(document.body).render(
<React.StrictMode>
<Popup />
</React.StrictMode>
);

37
src/popup/Popup.css Executable file
View file

@ -0,0 +1,37 @@
div {
height: 100%;
display: flex;
flex-direction: column;
gap: 16px;
align-items: center;
justify-content: center;
}
img {
width: 200px;
height: 200px;
}
h1 {
font-size: 18px;
color: white;
font-weight: bold;
margin: 0;
}
p {
color: white;
opacity: 0.7;
margin: 0;
}
code {
font-size: 12px;
padding: 2px 4px;
background-color: #ffffff24;
border-radius: 2px;
}
.test {
color: white
}

33
src/popup/Popup.tsx Executable file
View file

@ -0,0 +1,33 @@
import { useEffect } from 'react';
import "./Popup.css";
import Sites from "@Data/sites.json"
import React from 'react';
export default function() {
const [test, setTest] = React.useState(
JSON.parse(localStorage.getItem('test') as string) || false
)
const handleToggle = () => {
localStorage.setItem('test', JSON.stringify(!test))
setTest(!test)
}
useEffect(() => {
console.log("Hello from the popup!");
}, []);
return (
<div>
<img src="/icon-with-shadow.svg" />
<h1>vite-plugin-web-extension</h1>
<ul>
{Sites.sites.map(site => (
<li><a href={site.hyperlink}>{site.title}</a></li>
))}
</ul>
<button onClick={handleToggle}>Test</button>
{test &&<div className='test'>Test</div>}
</div>
)
}

52
src/site/App.tsx Executable file
View file

@ -0,0 +1,52 @@
import Sites from '@Data/sites.json'
import { Box, Button, debounce, FormLabel, Grid, OutlinedInput, styled, TextField, Typography } from '@mui/material'
import React, { useContext, useState } from 'react'
import MenuBar from '../components/MenuBar'
import LinkCard from '../components/linkcard'
import { Link, Route } from 'wouter'
import MenuDrawer from '../components/menudrawer'
import { CurrentPageContext } from '../contexts/CurrentPageContext'
import SettingsPage from '../components/settingsPage'
const useLocalStorage = (storageKey: string, fallbackState: any) => {
const [value, setValue] = React.useState(
JSON.parse(localStorage.getItem(storageKey) as string) ?? fallbackState
)
React.useEffect(() => {
localStorage.setItem(storageKey, JSON.stringify(value))
}, [value, setValue])
return [value, setValue]
}
function App() {
const {currentPage, setCurrentPage} = useContext(CurrentPageContext)
const [drawerOpen, setDrawerOpen] = useState(false)
return (
<>
<MenuBar />
<MenuDrawer />
<Box sx={{m: 4}} maxWidth="lg">
{currentPage === 'home' ? (
<Grid container spacing={2}>
{Sites.sites.map(site => (
<Grid item key={site.title}>
<LinkCard
title={site.title}
subtitle={site.subtitle}
hyperlink={site.hyperlink}
/>
</Grid>
))}
</Grid>
): null}
{currentPage === 'edit' ? (
<SettingsPage />
): null}
</Box>
</>
)
}
export default App

18
src/theme.ts Executable file
View file

@ -0,0 +1,18 @@
import { createTheme } from "@mui/material";
import { red } from "@mui/material/colors";
const theme = createTheme({
palette: {
primary: {
main: '#556cd6',
},
secondary: {
main: '#19857b',
},
error: {
main: red.A400,
},
},
})
export default theme

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

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

26
tsconfig.json Executable file
View file

@ -0,0 +1,26 @@
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"paths": {
"@Data/*": [
"./src/data/*"
],
}
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}

9
tsconfig.node.json Executable file
View file

@ -0,0 +1,9 @@
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}

31
vite.config.ts Executable file
View file

@ -0,0 +1,31 @@
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import webExtension, { readJsonFile } from "vite-plugin-web-extension";
import path from 'path'
function generateManifest() {
const manifest = readJsonFile("src/manifest.json");
const pkg = readJsonFile("package.json");
return {
name: pkg.name,
description: pkg.description,
version: pkg.version,
...manifest,
};
}
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
react(),
webExtension({
manifest: generateManifest,
browser: process.env.TARGET || "chrome",
}),
],
resolve: {
alias: {
"@Data": path.resolve(__dirname, './src/data')
}
}
});