Add new logos (#463)
* Add new logos * fixup! Add new logos * fixup! fixup! Add new logos * Setup login screen * fixup! Setup login screen
14
README.md
|
|
@ -1,7 +1,14 @@
|
|||
# 📝 tududi
|
||||
<p align="center">
|
||||
<img src="public/wide-logo-light.png" alt="tududi" width="400">
|
||||
</p>
|
||||
|
||||
`tududi` is the self-hosted task management tool that puts you in control. Organize your life and projects with a clear, hierarchical structure,
|
||||
smart recurring tasks, and seamless Telegram integration. Get focused, stay productive, and keep your data private.
|
||||
<p align="center">
|
||||
<h2 align="center">Productivity made simple</p></h2>
|
||||
<p align="center">Organize your life and projects with a clear, hierarchical structure,<br>
|
||||
smart recurring tasks, and seamless Telegram integration.<br>
|
||||
Get focused, stay productive, and keep your data private.
|
||||
</p>
|
||||
</p>
|
||||
|
||||

|
||||
|
||||
|
|
@ -119,6 +126,7 @@ Contributions to tududi are welcome! Whether it's bug fixes, new features, docum
|
|||
6. Push to your fork and open a Pull Request
|
||||
|
||||
**Read our [Contributing Guide](.github/CONTRIBUTING.md) for:**
|
||||
|
||||
- Development setup and workflow
|
||||
- Code standards and best practices
|
||||
- Testing requirements
|
||||
|
|
|
|||
|
|
@ -253,7 +253,10 @@ const App: React.FC = () => {
|
|||
/>
|
||||
}
|
||||
/>
|
||||
<Route path="/about" element={<About />} />
|
||||
<Route
|
||||
path="/about"
|
||||
element={<About isDarkMode={isDarkMode} />}
|
||||
/>
|
||||
<Route
|
||||
path="/admin/users"
|
||||
element={
|
||||
|
|
|
|||
|
|
@ -2,7 +2,11 @@ import React, { useState, useEffect } from 'react';
|
|||
import { useTranslation } from 'react-i18next';
|
||||
import { HeartIcon } from '@heroicons/react/24/outline';
|
||||
|
||||
const About: React.FC = () => {
|
||||
interface AboutProps {
|
||||
isDarkMode?: boolean;
|
||||
}
|
||||
|
||||
const About: React.FC<AboutProps> = ({ isDarkMode = false }) => {
|
||||
const { t } = useTranslation();
|
||||
const [version, setVersion] = useState<string>('0.3');
|
||||
|
||||
|
|
@ -33,9 +37,17 @@ const About: React.FC = () => {
|
|||
<div className="max-w-2xl mx-auto">
|
||||
{/* Logo and Version */}
|
||||
<div className="text-center mb-8">
|
||||
<h2 className="text-4xl font-bold text-gray-900 dark:text-white mb-2">
|
||||
tududi
|
||||
</h2>
|
||||
<div className="flex justify-center mb-4">
|
||||
<img
|
||||
src={
|
||||
isDarkMode
|
||||
? '/wide-logo-light.png'
|
||||
: '/wide-logo-dark.png'
|
||||
}
|
||||
alt="tududi"
|
||||
className="h-16 w-auto"
|
||||
/>
|
||||
</div>
|
||||
<p className="text-lg text-gray-600 dark:text-gray-400">
|
||||
{t('about.version', 'Version')} {version}
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import i18n from 'i18next';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
|
@ -9,6 +9,16 @@ const Login: React.FC = () => {
|
|||
const [error, setError] = useState<string | null>(null);
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation();
|
||||
const [isDarkMode] = useState<boolean>(() => {
|
||||
const storedPreference = localStorage.getItem('isDarkMode');
|
||||
return storedPreference !== null
|
||||
? storedPreference === 'true'
|
||||
: window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
document.documentElement.classList.toggle('dark', isDarkMode);
|
||||
}, [isDarkMode]);
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
|
@ -45,17 +55,41 @@ const Login: React.FC = () => {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="bg-gray-100 flex flex-col items-center justify-center min-h-screen px-4">
|
||||
<h1 className="text-5xl font-bold text-gray-300 mb-6">tududi</h1>
|
||||
<div className="bg-white p-8 rounded-lg shadow-md w-full max-w-sm">
|
||||
<>
|
||||
{/* Navbar */}
|
||||
<nav className="fixed top-0 left-0 right-0 z-50 text-gray-900 dark:text-white">
|
||||
<div className="h-16 flex items-center px-4 sm:px-6 lg:px-8">
|
||||
<img
|
||||
src={
|
||||
isDarkMode
|
||||
? '/wide-logo-light.png'
|
||||
: '/wide-logo-dark.png'
|
||||
}
|
||||
alt="tududi"
|
||||
className="h-9 w-auto"
|
||||
/>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="bg-gray-100 dark:bg-gray-900 min-h-screen px-4 pt-16 flex items-center justify-center">
|
||||
<div className="w-full max-w-7xl flex flex-col lg:flex-row items-center justify-center gap-12 lg:gap-16">
|
||||
{/* Left side - Login Form */}
|
||||
<div className="w-full lg:w-auto flex flex-col items-center">
|
||||
<div className="bg-white dark:bg-gray-800 p-10 rounded-lg shadow-md w-full max-w-2xl">
|
||||
<h2 className="text-center text-2xl font-semibold text-gray-700 dark:text-gray-200 mb-12">
|
||||
{t('auth.login', 'Login')}
|
||||
</h2>
|
||||
{error && (
|
||||
<div className="mb-4 text-center text-red-500">{error}</div>
|
||||
<div className="mb-4 text-center text-red-500">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
<form onSubmit={handleSubmit}>
|
||||
<div className="mb-4">
|
||||
<label
|
||||
htmlFor="email"
|
||||
className="block text-gray-600 mb-1"
|
||||
className="block text-gray-600 dark:text-gray-300 mb-1"
|
||||
>
|
||||
{t('auth.email', 'Email')}
|
||||
</label>
|
||||
|
|
@ -64,15 +98,17 @@ const Login: React.FC = () => {
|
|||
id="email"
|
||||
name="email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
className="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
onChange={(e) =>
|
||||
setEmail(e.target.value)
|
||||
}
|
||||
className="w-full px-4 py-2 border dark:border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<label
|
||||
htmlFor="password"
|
||||
className="block text-gray-600 mb-1"
|
||||
className="block text-gray-600 dark:text-gray-300 mb-1"
|
||||
>
|
||||
{t('auth.password', 'Password')}
|
||||
</label>
|
||||
|
|
@ -81,8 +117,10 @@ const Login: React.FC = () => {
|
|||
id="password"
|
||||
name="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
className="w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||
onChange={(e) =>
|
||||
setPassword(e.target.value)
|
||||
}
|
||||
className="w-full px-4 py-2 border dark:border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -95,6 +133,18 @@ const Login: React.FC = () => {
|
|||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right side - Graphic */}
|
||||
<div className="hidden lg:flex items-center justify-center">
|
||||
<img
|
||||
src="/login-gfx.png"
|
||||
alt="Login illustration"
|
||||
className="max-w-md w-full h-auto"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ const Navbar: React.FC<NavbarProps> = ({
|
|||
isSidebarOpen,
|
||||
setIsSidebarOpen,
|
||||
openTaskModal,
|
||||
isDarkMode,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||
|
|
@ -164,12 +165,17 @@ const Navbar: React.FC<NavbarProps> = ({
|
|||
|
||||
<Link
|
||||
to="/"
|
||||
className={`flex items-center no-underline text-gray-900 dark:text-white ml-2 ${isSidebarOpen ? 'sm:ml-0' : 'sm:ml-2'}`}
|
||||
className={`flex items-center no-underline ml-2 ${isSidebarOpen ? 'sm:ml-0' : 'sm:ml-2'}`}
|
||||
>
|
||||
<span className="text-2xl font-bold">
|
||||
<span className="sm:hidden">t</span>
|
||||
<span className="hidden sm:inline">tududi</span>
|
||||
</span>
|
||||
<img
|
||||
src={
|
||||
isDarkMode
|
||||
? '/wide-logo-light.png'
|
||||
: '/wide-logo-dark.png'
|
||||
}
|
||||
alt="tududi"
|
||||
className="h-9 w-auto"
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,25 @@
|
|||
import React from 'react';
|
||||
|
||||
const SidebarHeader: React.FC = () => {
|
||||
interface SidebarHeaderProps {
|
||||
isDarkMode: boolean;
|
||||
}
|
||||
|
||||
const SidebarHeader: React.FC<SidebarHeaderProps> = ({ isDarkMode }) => {
|
||||
return (
|
||||
<div className="flex justify-center mb-6 mt-2">
|
||||
<a
|
||||
href="/"
|
||||
className="flex justify-center items-center mb-2 no-underline text-gray-900 dark:text-white"
|
||||
className="flex justify-center items-center mb-2 no-underline"
|
||||
>
|
||||
<span className="text-2xl font-bold mt-1">tududi</span>
|
||||
<img
|
||||
src={
|
||||
isDarkMode
|
||||
? '/wide-logo-light.png'
|
||||
: '/wide-logo-dark.png'
|
||||
}
|
||||
alt="tududi"
|
||||
className="h-12 w-auto"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
49
index.html
|
|
@ -150,34 +150,33 @@
|
|||
.logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 1.75rem;
|
||||
font-weight: 800;
|
||||
color: #1e293b;
|
||||
text-decoration: none;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .logo {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.logo:hover {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.logo-icon {
|
||||
margin-right: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.logo-image {
|
||||
height: 36px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.logo-icon svg {
|
||||
color: #1e293b;
|
||||
.logo-light {
|
||||
display: block;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .logo-icon svg {
|
||||
color: white;
|
||||
.logo-dark {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .logo-light {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .logo-dark {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
|
|
@ -1002,13 +1001,8 @@
|
|||
padding: 15px 0;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.logo-icon svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
.logo-image {
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.nav-links {
|
||||
|
|
@ -1292,13 +1286,8 @@
|
|||
<div class="container">
|
||||
<nav>
|
||||
<a href="/" class="logo">
|
||||
<div class="logo-icon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="28" height="28">
|
||||
<circle cx="16" cy="16" r="13" stroke="currentColor" stroke-width="3.5" fill="none"/>
|
||||
<path d="M10 16l4 4 8-8" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
|
||||
</svg>
|
||||
</div>
|
||||
<span>tududi</span>
|
||||
<img src="public/wide-logo-dark.png" alt="tududi" class="logo-image logo-light">
|
||||
<img src="public/wide-logo-light.png" alt="tududi" class="logo-image logo-dark">
|
||||
</a>
|
||||
<button class="mobile-menu-toggle" onclick="toggleMobileMenu()">
|
||||
<i class="fas fa-bars"></i>
|
||||
|
|
|
|||
83
package-lock.json
generated
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "tududi",
|
||||
"version": "v0.84.1",
|
||||
"version": "v0.85.1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "tududi",
|
||||
"version": "v0.84.1",
|
||||
"version": "v0.85.1",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
|
|
@ -74,6 +74,7 @@
|
|||
"autoprefixer": "^10.4.20",
|
||||
"babel-jest": "^29.0.0",
|
||||
"babel-loader": "^9.2.1",
|
||||
"copy-webpack-plugin": "^13.0.1",
|
||||
"cross-env": "~7.0.3",
|
||||
"css-loader": "^7.1.2",
|
||||
"eslint": "^8.0.0",
|
||||
|
|
@ -6436,6 +6437,30 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/copy-webpack-plugin": {
|
||||
"version": "13.0.1",
|
||||
"resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-13.0.1.tgz",
|
||||
"integrity": "sha512-J+YV3WfhY6W/Xf9h+J1znYuqTye2xkBUIGyTPWuBAT27qajBa5mR4f8WBmfDY3YjRftT2kqZZiLi1qf0H+UOFw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"glob-parent": "^6.0.1",
|
||||
"normalize-path": "^3.0.0",
|
||||
"schema-utils": "^4.2.0",
|
||||
"serialize-javascript": "^6.0.2",
|
||||
"tinyglobby": "^0.2.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18.12.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/webpack"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"webpack": "^5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/core-js-compat": {
|
||||
"version": "3.44.0",
|
||||
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.44.0.tgz",
|
||||
|
|
@ -7204,9 +7229,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
|
||||
"integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==",
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
|
||||
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
|
|
@ -18414,6 +18439,54 @@
|
|||
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tinyglobby": {
|
||||
"version": "0.2.15",
|
||||
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
|
||||
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fdir": "^6.5.0",
|
||||
"picomatch": "^4.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/SuperchupuDev"
|
||||
}
|
||||
},
|
||||
"node_modules/tinyglobby/node_modules/fdir": {
|
||||
"version": "6.5.0",
|
||||
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
|
||||
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"picomatch": "^3 || ^4"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"picomatch": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/tinyglobby/node_modules/picomatch": {
|
||||
"version": "4.0.3",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/jonschlinkert"
|
||||
}
|
||||
},
|
||||
"node_modules/tmpl": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@
|
|||
"autoprefixer": "^10.4.20",
|
||||
"babel-jest": "^29.0.0",
|
||||
"babel-loader": "^9.2.1",
|
||||
"copy-webpack-plugin": "^13.0.1",
|
||||
"cross-env": "~7.0.3",
|
||||
"css-loader": "^7.1.2",
|
||||
"eslint": "^8.0.0",
|
||||
|
|
|
|||
BIN
public/favicon-16.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
public/favicon-32.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
BIN
public/favicon-48.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 15 KiB |
BIN
public/favicon.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
|
|
@ -1,23 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" width="32" height="32">
|
||||
<style>
|
||||
.circle {
|
||||
stroke: #4a5568;
|
||||
fill: none;
|
||||
}
|
||||
.checkmark {
|
||||
stroke: #4a5568;
|
||||
fill: none;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.circle {
|
||||
stroke: #e2e8f0;
|
||||
}
|
||||
.checkmark {
|
||||
stroke: #e2e8f0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<rect width="32" height="32" fill="transparent"/>
|
||||
<circle class="circle" cx="16" cy="16" r="13" stroke-width="2"/>
|
||||
<path class="checkmark" d="M10 16l4 4 8-8" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 617 B |
BIN
public/icon-logo.png
Normal file
|
After Width: | Height: | Size: 65 KiB |
|
|
@ -9,17 +9,10 @@
|
|||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400;0,700;1,400;1,700&display=swap" rel="stylesheet">
|
||||
<!-- SVG favicon with built-in light/dark mode support -->
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
||||
|
||||
<!-- Light mode favicon for browsers that support it -->
|
||||
<link rel="icon" type="image/x-icon" href="/favicon-light.ico" media="(prefers-color-scheme: light)">
|
||||
|
||||
<!-- Dark mode favicon for browsers that support it -->
|
||||
<link rel="icon" type="image/x-icon" href="/favicon-dark.ico" media="(prefers-color-scheme: dark)">
|
||||
|
||||
<!-- Fallback favicon (medium gray - works reasonably in both modes) -->
|
||||
<link rel="shortcut icon" href="/favicon.ico">
|
||||
<!-- Favicon -->
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16.png">
|
||||
|
||||
<!-- Web app manifest for PWA support -->
|
||||
<link rel="manifest" href="/manifest.json">
|
||||
|
|
|
|||
BIN
public/login-gfx.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
|
|
@ -8,14 +8,29 @@
|
|||
"background_color": "#ffffff",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/favicon.svg",
|
||||
"sizes": "any",
|
||||
"type": "image/svg+xml",
|
||||
"src": "/icon-logo.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "any maskable"
|
||||
},
|
||||
{
|
||||
"src": "/favicon.ico",
|
||||
"src": "/favicon.png",
|
||||
"sizes": "32x32",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/favicon-32.png",
|
||||
"sizes": "32x32",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/favicon-16.png",
|
||||
"sizes": "16x16",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/favicon.ico",
|
||||
"sizes": "16x16 32x32 48x48",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
]
|
||||
|
|
|
|||
BIN
public/wide-logo-dark.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
public/wide-logo-light.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
|
|
@ -2,6 +2,7 @@ const path = require('path');
|
|||
const webpack = require('webpack');
|
||||
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
|
||||
const HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||
|
||||
const isDevelopment = process.env.NODE_ENV !== 'production';
|
||||
|
||||
|
|
@ -67,6 +68,17 @@ module.exports = {
|
|||
filename: 'index.html',
|
||||
template: 'public/index.html'
|
||||
}),
|
||||
new CopyWebpackPlugin({
|
||||
patterns: [
|
||||
{
|
||||
from: 'public',
|
||||
to: '',
|
||||
globOptions: {
|
||||
ignore: ['**/index.html'],
|
||||
},
|
||||
},
|
||||
],
|
||||
}),
|
||||
].filter(Boolean),
|
||||
module: {
|
||||
rules: [
|
||||
|
|
|
|||