diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 4bd399d2a..f44d4ef9b 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -4,8 +4,12 @@ name: Master docker CI
on:
push:
branches: [master]
+ paths-ignore:
+ - '.github/**'
+ - '**.md'
tags:
- v*
+
workflow_dispatch:
env:
@@ -110,6 +114,6 @@ jobs:
with:
platforms: linux/amd64,linux/arm64,linux/arm/v7
context: .
- push: ${{ github.event_name != 'pull_request' }}
+ push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
diff --git a/.github/workflows/docker_dev.yml b/.github/workflows/docker_dev.yml
index cbf6b819f..529b1cab6 100644
--- a/.github/workflows/docker_dev.yml
+++ b/.github/workflows/docker_dev.yml
@@ -6,7 +6,13 @@ name: Development CI
on:
push:
branches: [dev]
+ paths-ignore:
+ - '.github/**'
+ - '**.md'
pull_request:
+ paths-ignore:
+ - '.github/**'
+ - '**.md'
workflow_dispatch:
inputs:
tags:
@@ -25,13 +31,17 @@ jobs:
yarn_install_and_build:
runs-on: ubuntu-latest
steps:
+
- name: Setup
uses: actions/setup-node@v3
+
- name: Checkout
uses: actions/checkout@v3
+
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
+
- name: Yarn cache
uses: actions/cache@v3
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 }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: ${{ runner.os }}-yarn-
+
- name: Nextjs cache
uses: actions/cache@v2
with:
@@ -50,8 +61,10 @@ jobs:
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.
restore-keys: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-
+
- run: yarn install --frozen-lockfile
- run: yarn build
+
- name: Cache build output
uses: actions/cache@v2
id: restore-build
@@ -72,8 +85,10 @@ jobs:
packages: write
contents: read
steps:
+
- name: Checkout
uses: actions/checkout@v2
+
- uses: actions/cache@v2
id: restore-build
with:
@@ -85,6 +100,7 @@ jobs:
./.next/standalone/
./packages.json
key: ${{ github.sha }}
+
- name: Docker meta
id: meta
uses: docker/metadata-action@v4
@@ -95,11 +111,15 @@ jobs:
tags: |
type=ref,event=pr
tpye=raw,value=dev,priority=1
+
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
+
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
+
- name: Login to GHCR
+ if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
registry: ghcr.io
@@ -111,6 +131,6 @@ jobs:
with:
platforms: linux/amd64,linux/arm64,linux/arm/v7
context: .
- push: true
+ push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
diff --git a/.gitignore b/.gitignore
index 684a71ab7..97405ae66 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,4 +35,5 @@ yarn-error.log*
*.tsbuildinfo
# storybook
-storybook-static
\ No newline at end of file
+storybook-static
+data/configs
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index 95fbce619..98fe3bdbe 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,19 +1,13 @@
FROM node:16-alpine
WORKDIR /app
ENV NODE_ENV production
-RUN addgroup --system --gid 1001 nodejs
-RUN adduser --system --uid 1001 nextjs
-
COPY /next.config.js ./
COPY /public ./public
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/static ./.next/static
-
EXPOSE 7575
ENV PORT 7575
VOLUME /app/data/configs
-CMD ["node", "server.js"]
\ No newline at end of file
+CMD ["node", "server.js"]
diff --git a/README.md b/README.md
index 5433cbded..f92a99571 100644
--- a/README.md
+++ b/README.md
@@ -9,9 +9,7 @@
-
-
A homepage for your server.
diff --git a/data/configs/config.json b/data/configs/config.json
index 060961492..b9c4ae388 100644
--- a/data/configs/config.json
+++ b/data/configs/config.json
@@ -1,12 +1,26 @@
{
"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": {
"searchBar": true,
- "searchUrl": "https://duckduckgo.com/?q=",
+ "searchUrl": "Custom",
"enabledModules": [
"Date",
- "Calendar"
+ "Calendar",
+ "Weather"
]
}
}
\ No newline at end of file
diff --git a/data/constants.ts b/data/constants.ts
index a03b01ee9..11dbe4c3b 100644
--- a/data/constants.ts
+++ b/data/constants.ts
@@ -1,2 +1,2 @@
export const REPO_URL = 'ajnart/homarr';
-export const CURRENT_VERSION = 'v0.3.0';
+export const CURRENT_VERSION = 'v0.3.1';
diff --git a/src/components/AppShelf/AddAppShelfItem.tsx b/src/components/AppShelf/AddAppShelfItem.tsx
index 9a413ba5d..cd6b7ff68 100644
--- a/src/components/AppShelf/AddAppShelfItem.tsx
+++ b/src/components/AppShelf/AddAppShelfItem.tsx
@@ -12,6 +12,7 @@ import {
LoadingOverlay,
ActionIcon,
Tooltip,
+ Title,
} from '@mantine/core';
import { useForm } from '@mantine/form';
import { motion } from 'framer-motion';
@@ -28,9 +29,9 @@ export function AddItemShelfButton(props: any) {
Add service}
opened={props.opened || opened}
onClose={() => setOpened(false)}
- title="Add a service"
>
diff --git a/src/components/AppShelf/AppShelf.tsx b/src/components/AppShelf/AppShelf.tsx
index d76413811..76ec5df39 100644
--- a/src/components/AppShelf/AppShelf.tsx
+++ b/src/components/AppShelf/AppShelf.tsx
@@ -1,30 +1,21 @@
import React, { useState } from 'react';
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 { serviceItem } from '../../tools/types';
import AppShelfMenu from './AppShelfMenu';
-const AppShelf = () => {
+const AppShelf = (props: any) => {
const { config } = useConfig();
return (
-
+
{config.services.map((service) => (
-
+
+
+
))}
-
+
);
};
@@ -42,16 +33,9 @@ export function AppShelfItem(props: any) {
setHovering(false);
}}
>
-
+
-
+
{service.name}
-
-
-
+
+
- {
- window.open(service.url);
- }}
- src={service.icon}
- />
-
-
-
+ >
+ {
+ window.open(service.url);
+ }}
+ />
+
+
+
+
);
diff --git a/src/components/ColorSchemeToggle/ColorSchemeSwitch.tsx b/src/components/ColorSchemeToggle/ColorSchemeSwitch.tsx
index b8fb15488..fda70c4d8 100644
--- a/src/components/ColorSchemeToggle/ColorSchemeSwitch.tsx
+++ b/src/components/ColorSchemeToggle/ColorSchemeSwitch.tsx
@@ -1,5 +1,5 @@
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';
const useStyles = createStyles((theme) => ({
@@ -40,6 +40,9 @@ export function ColorSchemeSwitch() {
toggleColorScheme()} size="md" />
Switch to {colorScheme === 'dark' ? 'light' : 'dark'} mode
+
+ Ctrl+J
+
);
}
diff --git a/src/components/SearchBar/SearchBar.tsx b/src/components/SearchBar/SearchBar.tsx
index 7a40ea4c0..e10c8b79b 100644
--- a/src/components/SearchBar/SearchBar.tsx
+++ b/src/components/SearchBar/SearchBar.tsx
@@ -1,18 +1,40 @@
-import { TextInput, Text, Popover, Box } from '@mantine/core';
-import { useForm } from '@mantine/hooks';
-import { useState } from 'react';
+import { TextInput, Kbd, createStyles, useMantineTheme, Text, Popover } from '@mantine/core';
+import { useForm, useHotkeys } from '@mantine/hooks';
+import { useRef, useState } from 'react';
import { Search, BrandYoutube, Download } from 'tabler-icons-react';
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) {
const { config, setConfig } = useConfig();
const [opened, setOpened] = useState(false);
const [icon, setIcon] = useState();
- 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 = (
+
+ Ctrl
+ +
+ K
+
+ );
const form = useForm({
initialValues: {
- querry: '',
+ query: '',
},
});
@@ -21,70 +43,66 @@ export default function SearchBar(props: any) {
}
return (
- {
+ // If query contains !yt or !t add "Searching on YouTube" or "Searching torrent"
+ const query = form.values.query.trim();
+ const isYoutube = query.startsWith('!yt');
+ const isTorrent = query.startsWith('!t');
+ if (isYoutube) {
+ setIcon();
+ } else if (isTorrent) {
+ setIcon();
+ } else {
+ setIcon();
+ }
}}
+ onSubmit={form.onSubmit((values) => {
+ // Find if query is prefixed by !yt or !t
+ const query = values.query.trim();
+ const isYoutube = query.startsWith('!yt');
+ const isTorrent = query.startsWith('!t');
+ if (isYoutube) {
+ window.open(`https://www.youtube.com/results?search_query=${query.substring(3)}`);
+ } else if (isTorrent) {
+ window.open(`https://bitsearch.to/search?q=${query.substring(3)}`);
+ } else {
+ window.open(`${queryUrl}${values.query}`);
+ }
+ })}
>
-
-
+
+ tip: Use the prefixes !yt and !t in front of your query to search on YouTube
+ or for a Torrent respectively.
+
+
+
);
}
diff --git a/src/components/Settings/SettingsMenu.tsx b/src/components/Settings/SettingsMenu.tsx
index 74b5c1b93..2f821a4ef 100644
--- a/src/components/Settings/SettingsMenu.tsx
+++ b/src/components/Settings/SettingsMenu.tsx
@@ -74,8 +74,8 @@ function SettingsMenu(props: any) {
/>
{searchUrl === 'Custom' && (
{
setCustomSearchUrl(event.currentTarget.value);
@@ -142,7 +142,8 @@ export function SettingsMenuButton(props: any) {
return (
<>
Settings}
opened={props.opened || opened}
onClose={() => setOpened(false)}
diff --git a/src/components/layout/Aside.tsx b/src/components/layout/Aside.tsx
index d51055c0b..912b231ac 100644
--- a/src/components/layout/Aside.tsx
+++ b/src/components/layout/Aside.tsx
@@ -1,8 +1,9 @@
import { Aside as MantineAside, Group } from '@mantine/core';
+import { DateModule } from '../modules';
import { CalendarModule } from '../modules/calendar/CalendarModule';
import ModuleWrapper from '../modules/moduleWrapper';
-export default function Aside() {
+export default function Aside(props: any) {
return (
-
+
+
);
diff --git a/src/components/layout/Footer.tsx b/src/components/layout/Footer.tsx
index b89fb2fcb..697f02123 100644
--- a/src/components/layout/Footer.tsx
+++ b/src/components/layout/Footer.tsx
@@ -54,24 +54,12 @@ export function Footer({ links }: FooterCenteredProps) {
));
- return (
-
-
- {items}
-
- component="a" href="https://github.com/ajnart/homarr" size="lg">
-
-
-
+ return (
+
+
+ component="a" href="https://github.com/ajnart/homarr" size="lg">
+
+
({
- root: {
- position: 'relative',
- 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')]: {
+ hide: {
+ [theme.fn.smallerThan('xs')]: {
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 {
- links: { link: string; label: string }[];
-}
-
-export function Header({ links }: HeaderResponsiveProps) {
- const [opened, toggleOpened] = useBooleanToggle(false);
+export function Header(props: any) {
const { classes, cx } = useStyles();
return (
-
-
-
+
+
+
-
-
+
+
+
-
- toggleOpened()}
- position="right"
- >
- {opened ?? (
-
-
-
- )}
-
);
}
diff --git a/src/components/layout/Layout.tsx b/src/components/layout/Layout.tsx
index ab646803d..8b82b4a74 100644
--- a/src/components/layout/Layout.tsx
+++ b/src/components/layout/Layout.tsx
@@ -1,36 +1,28 @@
-import { AppShell, Center, createStyles } from '@mantine/core';
+import { AppShell, createStyles } from '@mantine/core';
import { Header } from './Header';
import { Footer } from './Footer';
import Aside from './Aside';
-import Navbar from './Navbar';
const useStyles = createStyles((theme) => ({
- main: {
- [theme.fn.largerThan('md')]: {
- maxWidth: 1500,
- },
- },
+ main: {},
}));
export default function Layout({ children, style }: any) {
const { classes, cx } = useStyles();
return (
}
aside={}
- header={}
+ header={}
footer={}
>
-
-
- {children}
-
-
+
+ {children}
+
);
}
diff --git a/src/components/layout/Navbar.tsx b/src/components/layout/Navbar.tsx
index 7bb7a8410..fba3ecec1 100644
--- a/src/components/layout/Navbar.tsx
+++ b/src/components/layout/Navbar.tsx
@@ -14,7 +14,7 @@ export default function Navbar() {
base: 'auto',
}}
>
-
+
diff --git a/src/components/modules/moduleWrapper.tsx b/src/components/modules/moduleWrapper.tsx
index 52170c7be..55b421a5e 100644
--- a/src/components/modules/moduleWrapper.tsx
+++ b/src/components/modules/moduleWrapper.tsx
@@ -13,16 +13,7 @@ export default function ModuleWrapper(props: any) {
return null;
}
return (
-
+
);
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
index 8ec813ffb..272d367a2 100644
--- a/src/pages/index.tsx
+++ b/src/pages/index.tsx
@@ -1,4 +1,3 @@
-import { Group } from '@mantine/core';
import { getCookie, setCookies } from 'cookies-next';
import { GetServerSidePropsContext } from 'next';
import path from 'path';
@@ -6,7 +5,6 @@ import fs from 'fs';
import { useEffect } from 'react';
import AppShelf from '../components/AppShelf/AppShelf';
import LoadConfigComponent from '../components/Config/LoadConfig';
-import SearchBar from '../components/SearchBar/SearchBar';
import { Config } from '../tools/types';
import { useConfig } from '../tools/state';
@@ -54,10 +52,7 @@ export default function HomePage(props: any) {
}, [initialConfig]);
return (
<>
-
-
-
-
+
>
);