mirror of
				https://github.com/ajnart/homarr.git
				synced 2025-10-31 02:25:57 +01:00 
			
		
		
		
	🚀 Patch v0.3.1
Patch v0.3.1
This commit is contained in:
		
							
								
								
									
										6
									
								
								.github/workflows/docker.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.github/workflows/docker.yml
									
									
									
									
										vendored
									
									
								
							| @@ -4,8 +4,12 @@ name: Master docker CI | |||||||
| on: | on: | ||||||
|   push: |   push: | ||||||
|     branches: [master] |     branches: [master] | ||||||
|  |     paths-ignore: | ||||||
|  |       - '.github/**' | ||||||
|  |       - '**.md' | ||||||
|     tags: |     tags: | ||||||
|       - v* |       - v* | ||||||
|  |      | ||||||
|   workflow_dispatch: |   workflow_dispatch: | ||||||
|  |  | ||||||
| env: | env: | ||||||
| @@ -110,6 +114,6 @@ jobs: | |||||||
|         with: |         with: | ||||||
|           platforms: linux/amd64,linux/arm64,linux/arm/v7 |           platforms: linux/amd64,linux/arm64,linux/arm/v7 | ||||||
|           context: . |           context: . | ||||||
|           push: ${{ github.event_name != 'pull_request' }} |           push: true | ||||||
|           tags: ${{ steps.meta.outputs.tags }} |           tags: ${{ steps.meta.outputs.tags }} | ||||||
|           labels: ${{ steps.meta.outputs.labels }} |           labels: ${{ steps.meta.outputs.labels }} | ||||||
|   | |||||||
							
								
								
									
										22
									
								
								.github/workflows/docker_dev.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										22
									
								
								.github/workflows/docker_dev.yml
									
									
									
									
										vendored
									
									
								
							| @@ -6,7 +6,13 @@ name: Development CI | |||||||
| on: | on: | ||||||
|   push: |   push: | ||||||
|     branches: [dev] |     branches: [dev] | ||||||
|  |     paths-ignore: | ||||||
|  |       - '.github/**' | ||||||
|  |       - '**.md' | ||||||
|   pull_request: |   pull_request: | ||||||
|  |     paths-ignore: | ||||||
|  |       - '.github/**' | ||||||
|  |       - '**.md' | ||||||
|   workflow_dispatch: |   workflow_dispatch: | ||||||
|     inputs: |     inputs: | ||||||
|       tags: |       tags: | ||||||
| @@ -25,13 +31,17 @@ jobs: | |||||||
|   yarn_install_and_build: |   yarn_install_and_build: | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|  |  | ||||||
|       - name: Setup |       - name: Setup | ||||||
|         uses: actions/setup-node@v3 |         uses: actions/setup-node@v3 | ||||||
|  |  | ||||||
|       - name: Checkout |       - name: Checkout | ||||||
|         uses: actions/checkout@v3 |         uses: actions/checkout@v3 | ||||||
|  |  | ||||||
|       - name: Get yarn cache directory path |       - name: Get yarn cache directory path | ||||||
|         id: yarn-cache-dir-path |         id: yarn-cache-dir-path | ||||||
|         run: echo "::set-output name=dir::$(yarn cache dir)" |         run: echo "::set-output name=dir::$(yarn cache dir)" | ||||||
|  |  | ||||||
|       - name: Yarn cache |       - name: Yarn cache | ||||||
|         uses: actions/cache@v3 |         uses: actions/cache@v3 | ||||||
|         id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) |         id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) | ||||||
| @@ -39,6 +49,7 @@ jobs: | |||||||
|           path: ${{ steps.yarn-cache-dir-path.outputs.dir }} |           path: ${{ steps.yarn-cache-dir-path.outputs.dir }} | ||||||
|           key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} |           key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} | ||||||
|           restore-keys: ${{ runner.os }}-yarn- |           restore-keys: ${{ runner.os }}-yarn- | ||||||
|  |  | ||||||
|       - name: Nextjs cache |       - name: Nextjs cache | ||||||
|         uses: actions/cache@v2 |         uses: actions/cache@v2 | ||||||
|         with: |         with: | ||||||
| @@ -50,8 +61,10 @@ jobs: | |||||||
|           key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }} |           key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }} | ||||||
|           # If source files changed but packages didn't, rebuild from a prior cache. |           # If source files changed but packages didn't, rebuild from a prior cache. | ||||||
|           restore-keys: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}- |           restore-keys: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}- | ||||||
|  |  | ||||||
|       - run: yarn install --frozen-lockfile |       - run: yarn install --frozen-lockfile | ||||||
|       - run: yarn build |       - run: yarn build | ||||||
|  |  | ||||||
|       - name: Cache build output |       - name: Cache build output | ||||||
|         uses: actions/cache@v2 |         uses: actions/cache@v2 | ||||||
|         id: restore-build |         id: restore-build | ||||||
| @@ -72,8 +85,10 @@ jobs: | |||||||
|       packages: write |       packages: write | ||||||
|       contents: read |       contents: read | ||||||
|     steps: |     steps: | ||||||
|  |  | ||||||
|       - name: Checkout |       - name: Checkout | ||||||
|         uses: actions/checkout@v2 |         uses: actions/checkout@v2 | ||||||
|  |  | ||||||
|       - uses: actions/cache@v2 |       - uses: actions/cache@v2 | ||||||
|         id: restore-build |         id: restore-build | ||||||
|         with: |         with: | ||||||
| @@ -85,6 +100,7 @@ jobs: | |||||||
|              ./.next/standalone/ |              ./.next/standalone/ | ||||||
|              ./packages.json |              ./packages.json | ||||||
|           key: ${{ github.sha }} |           key: ${{ github.sha }} | ||||||
|  |  | ||||||
|       - name: Docker meta |       - name: Docker meta | ||||||
|         id: meta |         id: meta | ||||||
|         uses: docker/metadata-action@v4 |         uses: docker/metadata-action@v4 | ||||||
| @@ -95,11 +111,15 @@ jobs: | |||||||
|           tags: | |           tags: | | ||||||
|             type=ref,event=pr |             type=ref,event=pr | ||||||
|             tpye=raw,value=dev,priority=1 |             tpye=raw,value=dev,priority=1 | ||||||
|  |  | ||||||
|       - name: Set up QEMU |       - name: Set up QEMU | ||||||
|         uses: docker/setup-qemu-action@v2 |         uses: docker/setup-qemu-action@v2 | ||||||
|  |  | ||||||
|       - name: Set up Docker Buildx |       - name: Set up Docker Buildx | ||||||
|         uses: docker/setup-buildx-action@v2 |         uses: docker/setup-buildx-action@v2 | ||||||
|  |  | ||||||
|       - name: Login to GHCR |       - name: Login to GHCR | ||||||
|  |         if: github.event_name != 'pull_request' | ||||||
|         uses: docker/login-action@v2 |         uses: docker/login-action@v2 | ||||||
|         with: |         with: | ||||||
|           registry: ghcr.io |           registry: ghcr.io | ||||||
| @@ -111,6 +131,6 @@ jobs: | |||||||
|         with: |         with: | ||||||
|           platforms: linux/amd64,linux/arm64,linux/arm/v7 |           platforms: linux/amd64,linux/arm64,linux/arm/v7 | ||||||
|           context: . |           context: . | ||||||
|           push: true |           push: ${{ github.event_name != 'pull_request' }} | ||||||
|           tags: ${{ steps.meta.outputs.tags }} |           tags: ${{ steps.meta.outputs.tags }} | ||||||
|           labels: ${{ steps.meta.outputs.labels }} |           labels: ${{ steps.meta.outputs.labels }} | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -36,3 +36,4 @@ yarn-error.log* | |||||||
|  |  | ||||||
| # storybook | # storybook | ||||||
| storybook-static | storybook-static | ||||||
|  | data/configs | ||||||
| @@ -1,18 +1,12 @@ | |||||||
| FROM node:16-alpine | FROM node:16-alpine | ||||||
| WORKDIR /app | WORKDIR /app | ||||||
| ENV NODE_ENV production | ENV NODE_ENV production | ||||||
| RUN addgroup --system --gid 1001 nodejs |  | ||||||
| RUN adduser --system --uid 1001 nextjs |  | ||||||
|  |  | ||||||
| COPY /next.config.js ./ | COPY /next.config.js ./ | ||||||
| COPY  /public ./public | COPY  /public ./public | ||||||
| COPY /package.json ./package.json | COPY /package.json ./package.json | ||||||
|  | # Automatically leverage output traces to reduce image size. https://nextjs.org/docs/advanced-features/output-file-tracing | ||||||
| # Automatically leverage output traces to reduce image size |  | ||||||
| # https://nextjs.org/docs/advanced-features/output-file-tracing |  | ||||||
| COPY /.next/standalone ./ | COPY /.next/standalone ./ | ||||||
| COPY /.next/static ./.next/static | COPY /.next/static ./.next/static | ||||||
|  |  | ||||||
| EXPOSE 7575 | EXPOSE 7575 | ||||||
| ENV PORT 7575 | ENV PORT 7575 | ||||||
| VOLUME /app/data/configs | VOLUME /app/data/configs | ||||||
|   | |||||||
| @@ -9,9 +9,7 @@ | |||||||
|     <img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/ajnart/homarr?label=Downloads%20"></a> |     <img alt="Docker Pulls" src="https://img.shields.io/docker/pulls/ajnart/homarr?label=Downloads%20"></a> | ||||||
|   </p> |   </p> | ||||||
|   <p align="center"> |   <p align="center"> | ||||||
|   <a href=""> |  | ||||||
| <img align="end" width=600 src="https://user-images.githubusercontent.com/49837342/168315259-b778c816-10fe-44db-bd25-3eea6f31b233.png" /> | <img align="end" width=600 src="https://user-images.githubusercontent.com/49837342/168315259-b778c816-10fe-44db-bd25-3eea6f31b233.png" /> | ||||||
|   <a/> |  | ||||||
|   </p> |   </p> | ||||||
|   <p align = "center"> |   <p align = "center"> | ||||||
|     A homepage for <i>your</i> server. |     A homepage for <i>your</i> server. | ||||||
|   | |||||||
| @@ -1,12 +1,26 @@ | |||||||
| { | { | ||||||
|   "name": "config", |   "name": "config", | ||||||
|   "services": [], |   "services": [ | ||||||
|  |     { | ||||||
|  |       "type": "Other", | ||||||
|  |       "name": "YouTube", | ||||||
|  |       "icon": "https://cdn.jsdelivr.net/gh/walkxhub/dashboard-icons/png/youtube.png", | ||||||
|  |       "url": "https://youtube.com/" | ||||||
|  |     }, | ||||||
|  |     { | ||||||
|  |       "type": "Other", | ||||||
|  |       "name": "YouTube ", | ||||||
|  |       "icon": "https://cdn.jsdelivr.net/gh/walkxhub/dashboard-icons/png/youtube.png", | ||||||
|  |       "url": "https://youtube.com/" | ||||||
|  |     } | ||||||
|  |   ], | ||||||
|   "settings": { |   "settings": { | ||||||
|     "searchBar": true, |     "searchBar": true, | ||||||
|     "searchUrl": "https://duckduckgo.com/?q=", |     "searchUrl": "Custom", | ||||||
|     "enabledModules": [ |     "enabledModules": [ | ||||||
|       "Date", |       "Date", | ||||||
|       "Calendar" |       "Calendar", | ||||||
|  |       "Weather" | ||||||
|     ] |     ] | ||||||
|   } |   } | ||||||
| } | } | ||||||
| @@ -1,2 +1,2 @@ | |||||||
| export const REPO_URL = 'ajnart/homarr'; | export const REPO_URL = 'ajnart/homarr'; | ||||||
| export const CURRENT_VERSION = 'v0.3.0'; | export const CURRENT_VERSION = 'v0.3.1'; | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ import { | |||||||
|   LoadingOverlay, |   LoadingOverlay, | ||||||
|   ActionIcon, |   ActionIcon, | ||||||
|   Tooltip, |   Tooltip, | ||||||
|  |   Title, | ||||||
| } from '@mantine/core'; | } from '@mantine/core'; | ||||||
| import { useForm } from '@mantine/form'; | import { useForm } from '@mantine/form'; | ||||||
| import { motion } from 'framer-motion'; | import { motion } from 'framer-motion'; | ||||||
| @@ -28,9 +29,9 @@ export function AddItemShelfButton(props: any) { | |||||||
|       <Modal |       <Modal | ||||||
|         size="xl" |         size="xl" | ||||||
|         radius="md" |         radius="md" | ||||||
|  |         title={<Title order={3}>Add service</Title>} | ||||||
|         opened={props.opened || opened} |         opened={props.opened || opened} | ||||||
|         onClose={() => setOpened(false)} |         onClose={() => setOpened(false)} | ||||||
|         title="Add a service" |  | ||||||
|       > |       > | ||||||
|         <AddAppShelfItemForm setOpened={setOpened} /> |         <AddAppShelfItemForm setOpened={setOpened} /> | ||||||
|       </Modal> |       </Modal> | ||||||
|   | |||||||
| @@ -1,30 +1,21 @@ | |||||||
| import React, { useState } from 'react'; | import React, { useState } from 'react'; | ||||||
| import { motion } from 'framer-motion'; | import { motion } from 'framer-motion'; | ||||||
| import { Text, AspectRatio, SimpleGrid, Card, Image, useMantineTheme } from '@mantine/core'; | import { Text, AspectRatio, Card, Image, useMantineTheme, Center, Grid } from '@mantine/core'; | ||||||
| import { useConfig } from '../../tools/state'; | import { useConfig } from '../../tools/state'; | ||||||
| import { serviceItem } from '../../tools/types'; | import { serviceItem } from '../../tools/types'; | ||||||
| import AppShelfMenu from './AppShelfMenu'; | import AppShelfMenu from './AppShelfMenu'; | ||||||
|  |  | ||||||
| const AppShelf = () => { | const AppShelf = (props: any) => { | ||||||
|   const { config } = useConfig(); |   const { config } = useConfig(); | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <SimpleGrid |     <Grid gutter="xl" align="center"> | ||||||
|       cols={7} |  | ||||||
|       spacing="xl" |  | ||||||
|       breakpoints={[ |  | ||||||
|         { maxWidth: 2400, cols: 6, spacing: 'xl' }, |  | ||||||
|         { maxWidth: 1800, cols: 5, spacing: 'xl' }, |  | ||||||
|         { maxWidth: 1500, cols: 4, spacing: 'lg' }, |  | ||||||
|         { maxWidth: 800, cols: 3, spacing: 'md' }, |  | ||||||
|         { maxWidth: 400, cols: 3, spacing: 'sm' }, |  | ||||||
|         { maxWidth: 400, cols: 2, spacing: 'sm' }, |  | ||||||
|       ]} |  | ||||||
|     > |  | ||||||
|       {config.services.map((service) => ( |       {config.services.map((service) => ( | ||||||
|  |         <Grid.Col span={6} xl={2} xs={4} sm={3} md={3}> | ||||||
|           <AppShelfItem key={service.name} service={service} /> |           <AppShelfItem key={service.name} service={service} /> | ||||||
|  |         </Grid.Col> | ||||||
|       ))} |       ))} | ||||||
|     </SimpleGrid> |     </Grid> | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @@ -42,16 +33,9 @@ export function AppShelfItem(props: any) { | |||||||
|         setHovering(false); |         setHovering(false); | ||||||
|       }} |       }} | ||||||
|     > |     > | ||||||
|       <Card |       <Card withBorder radius="lg" shadow="md"> | ||||||
|         style={{ |  | ||||||
|           boxShadow: hovering ? '0px 0px 3px rgba(0, 0, 0, 0.5)' : '0px 0px 1px rgba(0, 0, 0, 0.5)', |  | ||||||
|           backgroundColor: |  | ||||||
|             theme.colorScheme === 'dark' ? theme.colors.dark[6] : theme.colors.gray[1], |  | ||||||
|         }} |  | ||||||
|         radius="md" |  | ||||||
|       > |  | ||||||
|         <Card.Section> |         <Card.Section> | ||||||
|           <Text mt="sm" align="center" lineClamp={1} weight={500}> |           <Text mt="sm" align="center" lineClamp={1} weight={550}> | ||||||
|             {service.name} |             {service.name} | ||||||
|           </Text> |           </Text> | ||||||
|           <motion.div |           <motion.div | ||||||
| @@ -68,6 +52,7 @@ export function AppShelfItem(props: any) { | |||||||
|             <AppShelfMenu service={service} /> |             <AppShelfMenu service={service} /> | ||||||
|           </motion.div> |           </motion.div> | ||||||
|         </Card.Section> |         </Card.Section> | ||||||
|  |         <Center> | ||||||
|           <Card.Section> |           <Card.Section> | ||||||
|             <AspectRatio |             <AspectRatio | ||||||
|               ratio={3 / 5} |               ratio={3 / 5} | ||||||
| @@ -84,18 +69,18 @@ export function AppShelfItem(props: any) { | |||||||
|                 }} |                 }} | ||||||
|               > |               > | ||||||
|                 <Image |                 <Image | ||||||
|                 style={{ |                   width={80} | ||||||
|                   maxWidth: 80, |                   height={80} | ||||||
|                 }} |                   src={service.icon} | ||||||
|                   fit="contain" |                   fit="contain" | ||||||
|                   onClick={() => { |                   onClick={() => { | ||||||
|                     window.open(service.url); |                     window.open(service.url); | ||||||
|                   }} |                   }} | ||||||
|                 src={service.icon} |  | ||||||
|                 /> |                 /> | ||||||
|               </motion.i> |               </motion.i> | ||||||
|             </AspectRatio> |             </AspectRatio> | ||||||
|           </Card.Section> |           </Card.Section> | ||||||
|  |         </Center> | ||||||
|       </Card> |       </Card> | ||||||
|     </motion.div> |     </motion.div> | ||||||
|   ); |   ); | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| import React from 'react'; | import React from 'react'; | ||||||
| import { createStyles, Switch, Group, useMantineColorScheme } from '@mantine/core'; | import { createStyles, Switch, Group, useMantineColorScheme, Kbd } from '@mantine/core'; | ||||||
| import { Sun, MoonStars } from 'tabler-icons-react'; | import { Sun, MoonStars } from 'tabler-icons-react'; | ||||||
|  |  | ||||||
| const useStyles = createStyles((theme) => ({ | const useStyles = createStyles((theme) => ({ | ||||||
| @@ -40,6 +40,9 @@ export function ColorSchemeSwitch() { | |||||||
|         <Switch checked={colorScheme === 'dark'} onChange={() => toggleColorScheme()} size="md" /> |         <Switch checked={colorScheme === 'dark'} onChange={() => toggleColorScheme()} size="md" /> | ||||||
|       </div> |       </div> | ||||||
|       Switch to {colorScheme === 'dark' ? 'light' : 'dark'} mode |       Switch to {colorScheme === 'dark' ? 'light' : 'dark'} mode | ||||||
|  |       <Group spacing={2}> | ||||||
|  |         <Kbd>Ctrl</Kbd>+<Kbd>J</Kbd> | ||||||
|  |       </Group> | ||||||
|     </Group> |     </Group> | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,18 +1,40 @@ | |||||||
| import { TextInput, Text, Popover, Box } from '@mantine/core'; | import { TextInput, Kbd, createStyles, useMantineTheme, Text, Popover } from '@mantine/core'; | ||||||
| import { useForm } from '@mantine/hooks'; | import { useForm, useHotkeys } from '@mantine/hooks'; | ||||||
| import { useState } from 'react'; | import { useRef, useState } from 'react'; | ||||||
| import { Search, BrandYoutube, Download } from 'tabler-icons-react'; | import { Search, BrandYoutube, Download } from 'tabler-icons-react'; | ||||||
| import { useConfig } from '../../tools/state'; | import { useConfig } from '../../tools/state'; | ||||||
|  |  | ||||||
|  | const useStyles = createStyles((theme) => ({ | ||||||
|  |   hide: { | ||||||
|  |     [theme.fn.smallerThan('sm')]: { | ||||||
|  |       display: 'none', | ||||||
|  |     }, | ||||||
|  |     display: 'flex', | ||||||
|  |     alignItems: 'center', | ||||||
|  |   }, | ||||||
|  | })); | ||||||
|  |  | ||||||
| export default function SearchBar(props: any) { | export default function SearchBar(props: any) { | ||||||
|   const { config, setConfig } = useConfig(); |   const { config, setConfig } = useConfig(); | ||||||
|   const [opened, setOpened] = useState(false); |   const [opened, setOpened] = useState(false); | ||||||
|   const [icon, setIcon] = useState(<Search />); |   const [icon, setIcon] = useState(<Search />); | ||||||
|   const querryUrl = config.settings.searchUrl || 'https://www.google.com/search?q='; |   const queryUrl = config.settings.searchUrl || 'https://www.google.com/search?q='; | ||||||
|  |   const textInput: any = useRef(null); | ||||||
|  |   useHotkeys([['ctrl+K', () => textInput.current.focus()]]); | ||||||
|  |  | ||||||
|  |   const { classes, cx } = useStyles(); | ||||||
|  |   const theme = useMantineTheme(); | ||||||
|  |   const rightSection = ( | ||||||
|  |     <div className={classes.hide}> | ||||||
|  |       <Kbd>Ctrl</Kbd> | ||||||
|  |       <span style={{ margin: '0 5px' }}>+</span> | ||||||
|  |       <Kbd>K</Kbd> | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  |  | ||||||
|   const form = useForm({ |   const form = useForm({ | ||||||
|     initialValues: { |     initialValues: { | ||||||
|       querry: '', |       query: '', | ||||||
|     }, |     }, | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
| @@ -21,18 +43,12 @@ export default function SearchBar(props: any) { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <Box |  | ||||||
|       mb={"xl"} |  | ||||||
|       style={{ |  | ||||||
|         width: '100%', |  | ||||||
|       }} |  | ||||||
|     > |  | ||||||
|     <form |     <form | ||||||
|       onChange={() => { |       onChange={() => { | ||||||
|           // If querry contains !yt or !t add "Searching on YouTube" or "Searching torrent" |         // If query contains !yt or !t add "Searching on YouTube" or "Searching torrent" | ||||||
|           const querry = form.values.querry.trim(); |         const query = form.values.query.trim(); | ||||||
|           const isYoutube = querry.startsWith('!yt'); |         const isYoutube = query.startsWith('!yt'); | ||||||
|           const isTorrent = querry.startsWith('!t'); |         const isTorrent = query.startsWith('!t'); | ||||||
|         if (isYoutube) { |         if (isYoutube) { | ||||||
|           setIcon(<BrandYoutube size={22} />); |           setIcon(<BrandYoutube size={22} />); | ||||||
|         } else if (isTorrent) { |         } else if (isTorrent) { | ||||||
| @@ -42,49 +58,51 @@ export default function SearchBar(props: any) { | |||||||
|         } |         } | ||||||
|       }} |       }} | ||||||
|       onSubmit={form.onSubmit((values) => { |       onSubmit={form.onSubmit((values) => { | ||||||
|           // Find if querry is prefixed by !yt or !t |         // Find if query is prefixed by !yt or !t | ||||||
|           const querry = values.querry.trim(); |         const query = values.query.trim(); | ||||||
|           const isYoutube = querry.startsWith('!yt'); |         const isYoutube = query.startsWith('!yt'); | ||||||
|           const isTorrent = querry.startsWith('!t'); |         const isTorrent = query.startsWith('!t'); | ||||||
|         if (isYoutube) { |         if (isYoutube) { | ||||||
|             window.open(`https://www.youtube.com/results?search_query=${querry.substring(3)}`); |           window.open(`https://www.youtube.com/results?search_query=${query.substring(3)}`); | ||||||
|         } else if (isTorrent) { |         } else if (isTorrent) { | ||||||
|             window.open(`https://bitsearch.to/search?q=${querry.substring(3)}`); |           window.open(`https://bitsearch.to/search?q=${query.substring(3)}`); | ||||||
|         } else { |         } else { | ||||||
|             window.open(`${querryUrl}${values.querry}`); |           window.open(`${queryUrl}${values.query}`); | ||||||
|         } |         } | ||||||
|       })} |       })} | ||||||
|     > |     > | ||||||
|       <Popover |       <Popover | ||||||
|         opened={opened} |         opened={opened} | ||||||
|           style={{ |  | ||||||
|             width: '100%', |  | ||||||
|           }} |  | ||||||
|         position="bottom" |         position="bottom" | ||||||
|         placement="start" |         placement="start" | ||||||
|  |         width={260} | ||||||
|         withArrow |         withArrow | ||||||
|  |         radius="md" | ||||||
|         trapFocus={false} |         trapFocus={false} | ||||||
|           transition="pop-top-left" |         transition="pop-bottom-right" | ||||||
|         onFocusCapture={() => setOpened(true)} |         onFocusCapture={() => setOpened(true)} | ||||||
|         onBlurCapture={() => setOpened(false)} |         onBlurCapture={() => setOpened(false)} | ||||||
|         target={ |         target={ | ||||||
|           <TextInput |           <TextInput | ||||||
|             variant="filled" |             variant="filled" | ||||||
|               color="blue" |  | ||||||
|             icon={icon} |             icon={icon} | ||||||
|  |             ref={textInput} | ||||||
|  |             rightSectionWidth={90} | ||||||
|  |             rightSection={rightSection} | ||||||
|             radius="md" |             radius="md" | ||||||
|             size="md" |             size="md" | ||||||
|               placeholder="Search the web" |             styles={{ rightSection: { pointerEvents: 'none' } }} | ||||||
|  |             placeholder="Search the web..." | ||||||
|             {...props} |             {...props} | ||||||
|               {...form.getInputProps('querry')} |             {...form.getInputProps('query')} | ||||||
|           /> |           /> | ||||||
|         } |         } | ||||||
|       > |       > | ||||||
|         <Text> |         <Text> | ||||||
|             tip: Use the prefixes <b>!yt</b> and <b>!t</b> in front of your query to search on YouTube or for a Torrent respectively. |           tip: Use the prefixes <b>!yt</b> and <b>!t</b> in front of your query to search on YouTube | ||||||
|  |           or for a Torrent respectively. | ||||||
|         </Text> |         </Text> | ||||||
|       </Popover> |       </Popover> | ||||||
|     </form> |     </form> | ||||||
|     </Box> |  | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -74,8 +74,8 @@ function SettingsMenu(props: any) { | |||||||
|         /> |         /> | ||||||
|         {searchUrl === 'Custom' && ( |         {searchUrl === 'Custom' && ( | ||||||
|           <TextInput |           <TextInput | ||||||
|             label="Querry URL" |             label="Query URL" | ||||||
|             placeholder="Custom querry url" |             placeholder="Custom query url" | ||||||
|             value={customSearchUrl} |             value={customSearchUrl} | ||||||
|             onChange={(event) => { |             onChange={(event) => { | ||||||
|               setCustomSearchUrl(event.currentTarget.value); |               setCustomSearchUrl(event.currentTarget.value); | ||||||
| @@ -142,7 +142,8 @@ export function SettingsMenuButton(props: any) { | |||||||
|   return ( |   return ( | ||||||
|     <> |     <> | ||||||
|       <Modal |       <Modal | ||||||
|         size="md" |         size="xl" | ||||||
|  |         radius="md" | ||||||
|         title={<Title order={3}>Settings</Title>} |         title={<Title order={3}>Settings</Title>} | ||||||
|         opened={props.opened || opened} |         opened={props.opened || opened} | ||||||
|         onClose={() => setOpened(false)} |         onClose={() => setOpened(false)} | ||||||
|   | |||||||
| @@ -1,8 +1,9 @@ | |||||||
| import { Aside as MantineAside, Group } from '@mantine/core'; | import { Aside as MantineAside, Group } from '@mantine/core'; | ||||||
|  | import { DateModule } from '../modules'; | ||||||
| import { CalendarModule } from '../modules/calendar/CalendarModule'; | import { CalendarModule } from '../modules/calendar/CalendarModule'; | ||||||
| import ModuleWrapper from '../modules/moduleWrapper'; | import ModuleWrapper from '../modules/moduleWrapper'; | ||||||
|  |  | ||||||
| export default function Aside() { | export default function Aside(props: any) { | ||||||
|   return ( |   return ( | ||||||
|     <MantineAside |     <MantineAside | ||||||
|       hiddenBreakpoint="md" |       hiddenBreakpoint="md" | ||||||
| @@ -14,8 +15,9 @@ export default function Aside() { | |||||||
|         base: 'auto', |         base: 'auto', | ||||||
|       }} |       }} | ||||||
|     > |     > | ||||||
|       <Group mt="sm" direction="column"> |       <Group mt="sm" grow direction="column"> | ||||||
|         <ModuleWrapper module={CalendarModule} /> |         <ModuleWrapper module={CalendarModule} /> | ||||||
|  |         <ModuleWrapper module={DateModule} /> | ||||||
|       </Group> |       </Group> | ||||||
|     </MantineAside> |     </MantineAside> | ||||||
|   ); |   ); | ||||||
|   | |||||||
| @@ -55,23 +55,11 @@ export function Footer({ links }: FooterCenteredProps) { | |||||||
|   )); |   )); | ||||||
|  |  | ||||||
|   return (  |   return (  | ||||||
|     <FooterComponent height="auto" style={{ border: 'none' }}> |     <FooterComponent p={5} height="auto" style={{ border: 'none', position: 'fixed', bottom: 0, right: 0 }}> | ||||||
|       <Group |       <Group position="right" mr="xs" mb="xs"> | ||||||
|         sx={{ |  | ||||||
|           position: 'fixed', |  | ||||||
|           bottom: 0, |  | ||||||
|           right: 15, |  | ||||||
|         }} |  | ||||||
|         direction="row" |  | ||||||
|         align="center" |  | ||||||
|         mb={15} |  | ||||||
|       > |  | ||||||
|         <Group className={classes.links}>{items}</Group> |  | ||||||
|         <Group spacing="xs" position="right" noWrap> |  | ||||||
|         <ActionIcon<'a'> component="a" href="https://github.com/ajnart/homarr" size="lg"> |         <ActionIcon<'a'> component="a" href="https://github.com/ajnart/homarr" size="lg"> | ||||||
|           <BrandGithub size={18} /> |           <BrandGithub size={18} /> | ||||||
|         </ActionIcon> |         </ActionIcon> | ||||||
|         </Group> |  | ||||||
|         <Text |         <Text | ||||||
|           style={{ |           style={{ | ||||||
|             fontSize: '0.90rem', |             fontSize: '0.90rem', | ||||||
|   | |||||||
| @@ -1,117 +1,35 @@ | |||||||
| import React from 'react'; | import React from 'react'; | ||||||
| import { createStyles, Header as Head, Group, Drawer, Center } from '@mantine/core'; | import { createStyles, Header as Head, Group, Box } from '@mantine/core'; | ||||||
| import { useBooleanToggle } from '@mantine/hooks'; |  | ||||||
| import { NextLink } from '@mantine/next'; |  | ||||||
| import { Logo } from './Logo'; | import { Logo } from './Logo'; | ||||||
| import CalendarComponent from '../modules/calendar/CalendarModule'; | import SearchBar from '../SearchBar/SearchBar'; | ||||||
| import { SettingsMenuButton } from '../Settings/SettingsMenu'; |  | ||||||
| import { AddItemShelfButton } from '../AppShelf/AddAppShelfItem'; | import { AddItemShelfButton } from '../AppShelf/AddAppShelfItem'; | ||||||
|  | import { SettingsMenuButton } from '../Settings/SettingsMenu'; | ||||||
|  |  | ||||||
| const HEADER_HEIGHT = 60; | const HEADER_HEIGHT = 60; | ||||||
|  |  | ||||||
| const useStyles = createStyles((theme) => ({ | const useStyles = createStyles((theme) => ({ | ||||||
|   root: { |   hide: { | ||||||
|     position: 'relative', |     [theme.fn.smallerThan('xs')]: { | ||||||
|     zIndex: 1, |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   dropdown: { |  | ||||||
|     position: 'absolute', |  | ||||||
|     top: HEADER_HEIGHT, |  | ||||||
|     left: 0, |  | ||||||
|     right: 0, |  | ||||||
|     zIndex: 0, |  | ||||||
|     borderTopRightRadius: 0, |  | ||||||
|     borderTopLeftRadius: 0, |  | ||||||
|     borderTopWidth: 0, |  | ||||||
|     overflow: 'hidden', |  | ||||||
|  |  | ||||||
|     [theme.fn.largerThan('md')]: { |  | ||||||
|       display: 'none', |       display: 'none', | ||||||
|     }, |     }, | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   header: { |  | ||||||
|     display: 'flex', |  | ||||||
|     height: '100%', |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   links: { |  | ||||||
|     [theme.fn.smallerThan('md')]: { |  | ||||||
|       display: 'none', |  | ||||||
|     }, |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   burger: { |  | ||||||
|     [theme.fn.largerThan('md')]: { |  | ||||||
|       display: 'none', |  | ||||||
|     }, |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   link: { |  | ||||||
|     display: 'block', |  | ||||||
|     lineHeight: 1, |  | ||||||
|     padding: '8px 12px', |  | ||||||
|     borderRadius: theme.radius.sm, |  | ||||||
|     textDecoration: 'none', |  | ||||||
|     color: theme.colorScheme === 'dark' ? theme.colors.dark[0] : theme.colors.gray[7], |  | ||||||
|     fontSize: theme.fontSizes.sm, |  | ||||||
|     fontWeight: 500, |  | ||||||
|  |  | ||||||
|     '&:hover': { |  | ||||||
|       backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[6] : theme.colors.gray[0], |  | ||||||
|     }, |  | ||||||
|  |  | ||||||
|     [theme.fn.smallerThan('sm')]: { |  | ||||||
|       borderRadius: 0, |  | ||||||
|       padding: theme.spacing.md, |  | ||||||
|     }, |  | ||||||
|   }, |  | ||||||
|  |  | ||||||
|   linkActive: { |  | ||||||
|     '&, &:hover': { |  | ||||||
|       backgroundColor: |  | ||||||
|         theme.colorScheme === 'dark' |  | ||||||
|           ? theme.fn.rgba(theme.colors[theme.primaryColor][9], 0.25) |  | ||||||
|           : theme.colors[theme.primaryColor][0], |  | ||||||
|       color: theme.colors[theme.primaryColor][theme.colorScheme === 'dark' ? 3 : 7], |  | ||||||
|     }, |  | ||||||
|   }, |  | ||||||
| })); | })); | ||||||
|  |  | ||||||
| interface HeaderResponsiveProps { | export function Header(props: any) { | ||||||
|   links: { link: string; label: string }[]; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| export function Header({ links }: HeaderResponsiveProps) { |  | ||||||
|   const [opened, toggleOpened] = useBooleanToggle(false); |  | ||||||
|   const { classes, cx } = useStyles(); |   const { classes, cx } = useStyles(); | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <Head height={HEADER_HEIGHT}> |     <Head height="auto"> | ||||||
|       <Group direction="row" align="center" position="apart" className={classes.header} mx="xl"> |       <Group m="xs" position="apart"> | ||||||
|         <NextLink style={{ textDecoration: 'none' }} href="/"> |         <Box className={classes.hide}> | ||||||
|           <Logo style={{ fontSize: 22 }} /> |           <Logo style={{ fontSize: 22 }} /> | ||||||
|         </NextLink> |         </Box> | ||||||
|         <Group> |         <Group noWrap> | ||||||
|  |           <SearchBar /> | ||||||
|           <SettingsMenuButton /> |           <SettingsMenuButton /> | ||||||
|           <AddItemShelfButton /> |           <AddItemShelfButton /> | ||||||
|         </Group> |         </Group> | ||||||
|       </Group> |       </Group> | ||||||
|  |  | ||||||
|       <Drawer |  | ||||||
|         opened={opened} |  | ||||||
|         overlayOpacity={0.55} |  | ||||||
|         overlayBlur={3} |  | ||||||
|         onClose={() => toggleOpened()} |  | ||||||
|         position="right" |  | ||||||
|       > |  | ||||||
|         {opened ?? ( |  | ||||||
|           <Center> |  | ||||||
|             <CalendarComponent /> |  | ||||||
|           </Center> |  | ||||||
|         )} |  | ||||||
|       </Drawer> |  | ||||||
|     </Head> |     </Head> | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,27 +1,20 @@ | |||||||
| import { AppShell, Center, createStyles } from '@mantine/core'; | import { AppShell, createStyles } from '@mantine/core'; | ||||||
| import { Header } from './Header'; | import { Header } from './Header'; | ||||||
| import { Footer } from './Footer'; | import { Footer } from './Footer'; | ||||||
| import Aside from './Aside'; | import Aside from './Aside'; | ||||||
| import Navbar from './Navbar'; |  | ||||||
|  |  | ||||||
| const useStyles = createStyles((theme) => ({ | const useStyles = createStyles((theme) => ({ | ||||||
|   main: { |   main: {}, | ||||||
|     [theme.fn.largerThan('md')]: { |  | ||||||
|       maxWidth: 1500, |  | ||||||
|     }, |  | ||||||
|   }, |  | ||||||
| })); | })); | ||||||
|  |  | ||||||
| export default function Layout({ children, style }: any) { | export default function Layout({ children, style }: any) { | ||||||
|   const { classes, cx } = useStyles(); |   const { classes, cx } = useStyles(); | ||||||
|   return ( |   return ( | ||||||
|     <AppShell |     <AppShell | ||||||
|       navbar={<Navbar />} |  | ||||||
|       aside={<Aside />} |       aside={<Aside />} | ||||||
|       header={<Header links={[]} />} |       header={<Header />} | ||||||
|       footer={<Footer links={[]} />} |       footer={<Footer links={[]} />} | ||||||
|     > |     > | ||||||
|       <Center> |  | ||||||
|       <main |       <main | ||||||
|         className={cx(classes.main)} |         className={cx(classes.main)} | ||||||
|         style={{ |         style={{ | ||||||
| @@ -30,7 +23,6 @@ export default function Layout({ children, style }: any) { | |||||||
|       > |       > | ||||||
|         {children} |         {children} | ||||||
|       </main> |       </main> | ||||||
|       </Center> |  | ||||||
|     </AppShell> |     </AppShell> | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ export default function Navbar() { | |||||||
|         base: 'auto', |         base: 'auto', | ||||||
|       }} |       }} | ||||||
|     > |     > | ||||||
|       <Group mt="sm" direction="column"> |       <Group mt="sm" direction="column" align="center"> | ||||||
|         <ModuleWrapper module={DateModule} /> |         <ModuleWrapper module={DateModule} /> | ||||||
|       </Group> |       </Group> | ||||||
|     </MantineNavbar> |     </MantineNavbar> | ||||||
|   | |||||||
| @@ -13,16 +13,7 @@ export default function ModuleWrapper(props: any) { | |||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
|   return ( |   return ( | ||||||
|     <Card |     <Card hidden={!isShown} mx="sm" withBorder radius="lg" shadow="sm"> | ||||||
|       hidden={!isShown} |  | ||||||
|       mx="sm" |  | ||||||
|       radius="lg" |  | ||||||
|       shadow="sm" |  | ||||||
|       style={{ |  | ||||||
|         // Make background color of the card depend on the theme |  | ||||||
|         backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[5] : 'white', |  | ||||||
|       }} |  | ||||||
|     > |  | ||||||
|       <module.component /> |       <module.component /> | ||||||
|     </Card> |     </Card> | ||||||
|   ); |   ); | ||||||
|   | |||||||
| @@ -1,4 +1,3 @@ | |||||||
| import { Group } from '@mantine/core'; |  | ||||||
| import { getCookie, setCookies } from 'cookies-next'; | import { getCookie, setCookies } from 'cookies-next'; | ||||||
| import { GetServerSidePropsContext } from 'next'; | import { GetServerSidePropsContext } from 'next'; | ||||||
| import path from 'path'; | import path from 'path'; | ||||||
| @@ -6,7 +5,6 @@ import fs from 'fs'; | |||||||
| import { useEffect } from 'react'; | import { useEffect } from 'react'; | ||||||
| import AppShelf from '../components/AppShelf/AppShelf'; | import AppShelf from '../components/AppShelf/AppShelf'; | ||||||
| import LoadConfigComponent from '../components/Config/LoadConfig'; | import LoadConfigComponent from '../components/Config/LoadConfig'; | ||||||
| import SearchBar from '../components/SearchBar/SearchBar'; |  | ||||||
| import { Config } from '../tools/types'; | import { Config } from '../tools/types'; | ||||||
| import { useConfig } from '../tools/state'; | import { useConfig } from '../tools/state'; | ||||||
|  |  | ||||||
| @@ -54,10 +52,7 @@ export default function HomePage(props: any) { | |||||||
|   }, [initialConfig]); |   }, [initialConfig]); | ||||||
|   return ( |   return ( | ||||||
|     <> |     <> | ||||||
|       <SearchBar /> |  | ||||||
|       <Group align="start" position="apart" noWrap> |  | ||||||
|       <AppShelf /> |       <AppShelf /> | ||||||
|       </Group> |  | ||||||
|       <LoadConfigComponent /> |       <LoadConfigComponent /> | ||||||
|     </> |     </> | ||||||
|   ); |   ); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user