Merge branch 'gethomepage:dev' into integration
This commit is contained in:
commit
f568f4e91f
8
.github/PULL_REQUEST_TEMPLATE.md
vendored
8
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -6,6 +6,7 @@
|
|||||||
==================== STOP ====
|
==================== STOP ====
|
||||||
|
|
||||||
⚠️ Before opening this pull request please review the guidelines in the checklist below.
|
⚠️ Before opening this pull request please review the guidelines in the checklist below.
|
||||||
|
|
||||||
If this PR does not meet those guidelines it will not be accepted, and everyone will be sad.
|
If this PR does not meet those guidelines it will not be accepted, and everyone will be sad.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -14,9 +15,6 @@ If this PR does not meet those guidelines it will not be accepted, and everyone
|
|||||||
<!--
|
<!--
|
||||||
Please include a summary of the change. Screenshots and/or videos can also be helpful if appropriate.
|
Please include a summary of the change. Screenshots and/or videos can also be helpful if appropriate.
|
||||||
|
|
||||||
*** Please see the development guidelines for new widgets: https://gethomepage.dev/more/development/#service-widget-guidelines
|
|
||||||
*** If you do not follow these guidelines your PR will likely be closed without review.
|
|
||||||
|
|
||||||
New service widgets should include example(s) of relevant API output as well as updates to the docs for the new widget.
|
New service widgets should include example(s) of relevant API output as well as updates to the docs for the new widget.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -30,13 +28,13 @@ What type of change does your PR introduce to Homepage?
|
|||||||
|
|
||||||
- [ ] New service widget
|
- [ ] New service widget
|
||||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||||
- [ ] New feature (non-breaking change which adds functionality)
|
- [ ] New feature or enhancement (non-breaking change which adds functionality)
|
||||||
- [ ] Documentation only
|
- [ ] Documentation only
|
||||||
- [ ] Other (please explain)
|
- [ ] Other (please explain)
|
||||||
|
|
||||||
## Checklist:
|
## Checklist:
|
||||||
|
|
||||||
- [ ] If applicable, I have added corresponding documentation changes.
|
- [ ] If applicable, I have added corresponding documentation changes.
|
||||||
- [ ] If applicable, I have reviewed the [feature](https://gethomepage.dev/more/development/#new-feature-guidelines) and / or [service widget guidelines](https://gethomepage.dev/more/development/#service-widget-guidelines).
|
- [ ] If applicable, I have reviewed the [feature / enhancement](https://gethomepage.dev/more/development/#new-feature-guidelines) and / or [service widget guidelines](https://gethomepage.dev/more/development/#service-widget-guidelines).
|
||||||
- [ ] I have checked that all code style checks pass using [pre-commit hooks](https://gethomepage.dev/more/development/#code-formatting-with-pre-commit-hooks) and [linting checks](https://gethomepage.dev/more/development/#code-linting).
|
- [ ] I have checked that all code style checks pass using [pre-commit hooks](https://gethomepage.dev/more/development/#code-formatting-with-pre-commit-hooks) and [linting checks](https://gethomepage.dev/more/development/#code-linting).
|
||||||
- [ ] If applicable, I have tested my code for new features & regressions on both mobile & desktop devices, using the latest version of major browsers.
|
- [ ] If applicable, I have tested my code for new features & regressions on both mobile & desktop devices, using the latest version of major browsers.
|
||||||
|
|||||||
@ -63,7 +63,7 @@ The homepage team appreciates all effort and interest from the community in fili
|
|||||||
- Issues, pull requests and discussions that are closed will be locked after 30 days of inactivity.
|
- Issues, pull requests and discussions that are closed will be locked after 30 days of inactivity.
|
||||||
- Discussions with a marked answer will be automatically closed.
|
- Discussions with a marked answer will be automatically closed.
|
||||||
- Discussions in the 'General' or 'Support' categories will be closed after 180 days of inactivity.
|
- Discussions in the 'General' or 'Support' categories will be closed after 180 days of inactivity.
|
||||||
- Feature requests that do not meet the following thresholds will be closed: 5 "up-votes" after 180 days of inactivity or 10 "up-votes" after 365 days.
|
- Feature requests that do not meet the following thresholds will be closed: 10 "up-votes" after 180 days of inactivity or 20 "up-votes" after 365 days.
|
||||||
|
|
||||||
In all cases, threads can be re-opened by project maintainers and, of course, users can always create a new discussion for related concerns.
|
In all cases, threads can be re-opened by project maintainers and, of course, users can always create a new discussion for related concerns.
|
||||||
Finally, remember that all information remains searchable and 'closed' feature requests can still serve as inspiration for new features.
|
Finally, remember that all information remains searchable and 'closed' feature requests can still serve as inspiration for new features.
|
||||||
|
|||||||
@ -134,7 +134,7 @@ Services may have descriptions,
|
|||||||
|
|
||||||
## Icons
|
## Icons
|
||||||
|
|
||||||
Services may have an icon attached to them, you can use icons from [Dashboard Icons](https://github.com/walkxcode/dashboard-icons) automatically, by passing the name of the icon, with, or without `.png` or with `.svg` to use the svg version.
|
Services may have an icon attached to them, you can use icons from [Dashboard Icons](https://github.com/homarr-labs/dashboard-icons) automatically, by passing the name of the icon, with, or without `.png`, `.webp` or `.svg` to specify the desired version.
|
||||||
|
|
||||||
You can also specify prefixed icons from:
|
You can also specify prefixed icons from:
|
||||||
|
|
||||||
|
|||||||
@ -23,3 +23,5 @@ Finally, run the server:
|
|||||||
```bash
|
```bash
|
||||||
pnpm start
|
pnpm start
|
||||||
```
|
```
|
||||||
|
|
||||||
|
When updating homepage versions you will need to re-build the static files i.e. repeat the process above.
|
||||||
|
|||||||
@ -46,9 +46,9 @@ See the [pre-commit documentation](https://pre-commit.com/#install) to get start
|
|||||||
In general, homepage is meant to be a dashboard for 'self-hosted' services and we believe it is a small way we can help showcase this kind of software. While exceptions are made, mostly when there is no viable
|
In general, homepage is meant to be a dashboard for 'self-hosted' services and we believe it is a small way we can help showcase this kind of software. While exceptions are made, mostly when there is no viable
|
||||||
self-hosted / open-source alternative, we ask that any widgets, etc. are developed primarily for a self-hosted tool.
|
self-hosted / open-source alternative, we ask that any widgets, etc. are developed primarily for a self-hosted tool.
|
||||||
|
|
||||||
## New Feature Guidelines
|
## New Feature or Enhancement Guidelines {#new-feature-guidelines}
|
||||||
|
|
||||||
- New features should be linked to an existing feature request. The purpose of this requirement is to avoid the addition (and maintenance) of features that might only benefit a small number of users.
|
- New features or enhancements, **no matter how small**, must be linked to an existing feature request. The purpose of this requirement is to avoid the addition (and maintenance) of features that might only benefit a small number of users.
|
||||||
- If you have ideas for a larger feature you may want to open a discussion first.
|
- If you have ideas for a larger feature you may want to open a discussion first.
|
||||||
|
|
||||||
## Service Widget Guidelines
|
## Service Widget Guidelines
|
||||||
|
|||||||
@ -62,10 +62,12 @@ widget:
|
|||||||
format: size
|
format: size
|
||||||
```
|
```
|
||||||
|
|
||||||
Supported formats for the values are `text`, `number`, `float`, `percent`, `bytes`, `bitrate`, `size`, `date` and `relativeDate`.
|
Supported formats for the values are `text`, `number`, `float`, `percent`, `duration`, `bytes`, `bitrate`, `size`, `date` and `relativeDate`.
|
||||||
|
|
||||||
The `dateStyle` and `timeStyle` options of the `date` format are passed directly to [Intl.DateTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat) and the `style` and `numeric` options of `relativeDate` are passed to [Intl.RelativeTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat/RelativeTimeFormat).
|
The `dateStyle` and `timeStyle` options of the `date` format are passed directly to [Intl.DateTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat) and the `style` and `numeric` options of `relativeDate` are passed to [Intl.RelativeTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat/RelativeTimeFormat).
|
||||||
|
|
||||||
|
The `duration` format expects the duration to be specified in seconds. The `scale` transformation tool can be used if a conversion is required.
|
||||||
|
|
||||||
The `size` format will return the length of the array or string, or the number of keys in an object. This is then formatted as `number`.
|
The `size` format will return the length of the array or string, or the number of keys in an object. This is then formatted as `number`.
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
|||||||
@ -11,7 +11,7 @@ Learn more about [Gluetun](https://github.com/qdm12/gluetun).
|
|||||||
|
|
||||||
Allowed fields: `["public_ip", "region", "country"]`.
|
Allowed fields: `["public_ip", "region", "country"]`.
|
||||||
|
|
||||||
To setup authentication, follow [the official Gluetun documentation](https://github.com/qdm12/gluetun-wiki/blob/main/setup/advanced/control-server.md#authentication).
|
To setup authentication, follow [the official Gluetun documentation](https://github.com/qdm12/gluetun-wiki/blob/main/setup/advanced/control-server.md#authentication). Note that to use the api key method, you must add the route `GET /v1/publicip/ip` to the `routes` array in your Gluetun config.toml.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
widget:
|
widget:
|
||||||
|
|||||||
@ -14,8 +14,10 @@ export default class ErrorBoundary extends React.Component {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// You can also log error messages to an error reporting service here
|
// You can also log error messages to an error reporting service here
|
||||||
// eslint-disable-next-line no-console
|
if (error || errorInfo) {
|
||||||
console.error(error, errorInfo);
|
// eslint-disable-next-line no-console
|
||||||
|
console.error("component error: %s, info: %s", error, errorInfo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|||||||
@ -101,7 +101,26 @@ export default function ResolvedIcon({ icon, width = 32, height = 32, alt = "log
|
|||||||
const iconName = icon.replace(".svg", "");
|
const iconName = icon.replace(".svg", "");
|
||||||
return (
|
return (
|
||||||
<Image
|
<Image
|
||||||
src={`https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/svg/${iconName}.svg`}
|
src={`https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/${iconName}.svg`}
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
style={{
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
objectFit: "contain",
|
||||||
|
maxHeight: "100%",
|
||||||
|
maxWidth: "100%",
|
||||||
|
}}
|
||||||
|
alt={alt}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (icon.endsWith(".webp")) {
|
||||||
|
const iconName = icon.replace(".webp", "");
|
||||||
|
return (
|
||||||
|
<Image
|
||||||
|
src={`https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/webp/${iconName}.webp`}
|
||||||
width={width}
|
width={width}
|
||||||
height={height}
|
height={height}
|
||||||
style={{
|
style={{
|
||||||
@ -119,7 +138,7 @@ export default function ResolvedIcon({ icon, width = 32, height = 32, alt = "log
|
|||||||
const iconName = icon.replace(".png", "");
|
const iconName = icon.replace(".png", "");
|
||||||
return (
|
return (
|
||||||
<Image
|
<Image
|
||||||
src={`https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/${iconName}.png`}
|
src={`https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/${iconName}.png`}
|
||||||
width={width}
|
width={width}
|
||||||
height={height}
|
height={height}
|
||||||
style={{
|
style={{
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
import { useState, useEffect, Fragment } from "react";
|
import { useState, useEffect, Fragment } from "react";
|
||||||
import { useTranslation } from "next-i18next";
|
import { useTranslation } from "next-i18next";
|
||||||
import { FiSearch } from "react-icons/fi";
|
import { FiSearch } from "react-icons/fi";
|
||||||
import { SiDuckduckgo, SiMicrosoftbing, SiGoogle, SiBaidu, SiBrave } from "react-icons/si";
|
import { SiDuckduckgo, SiGoogle, SiBaidu, SiBrave } from "react-icons/si";
|
||||||
|
import { BiLogoBing } from "react-icons/bi";
|
||||||
import { Listbox, Transition, Combobox } from "@headlessui/react";
|
import { Listbox, Transition, Combobox } from "@headlessui/react";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
|
||||||
@ -25,7 +26,7 @@ export const searchProviders = {
|
|||||||
name: "Bing",
|
name: "Bing",
|
||||||
url: "https://www.bing.com/search?q=",
|
url: "https://www.bing.com/search?q=",
|
||||||
suggestionUrl: "https://api.bing.com/osjson.aspx?query=",
|
suggestionUrl: "https://api.bing.com/osjson.aspx?query=",
|
||||||
icon: SiMicrosoftbing,
|
icon: BiLogoBing,
|
||||||
},
|
},
|
||||||
baidu: {
|
baidu: {
|
||||||
name: "Baidu",
|
name: "Baidu",
|
||||||
|
|||||||
@ -166,6 +166,18 @@ const headerStyles = {
|
|||||||
boxedWidgets: "m-5 mb-0 sm:m-9 sm:mb-0 sm:mt-1",
|
boxedWidgets: "m-5 mb-0 sm:m-9 sm:mb-0 sm:mt-1",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function getAllServices(services) {
|
||||||
|
function getServices(group) {
|
||||||
|
let nestedServices = [...group.services];
|
||||||
|
if (group.groups.length > 0) {
|
||||||
|
nestedServices = [...nestedServices, ...group.groups.map(getServices).flat()];
|
||||||
|
}
|
||||||
|
return nestedServices;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [...services.map(getServices).flat()];
|
||||||
|
}
|
||||||
|
|
||||||
function Home({ initialSettings }) {
|
function Home({ initialSettings }) {
|
||||||
const { i18n } = useTranslation();
|
const { i18n } = useTranslation();
|
||||||
const { theme, setTheme } = useContext(ThemeContext);
|
const { theme, setTheme } = useContext(ThemeContext);
|
||||||
@ -182,10 +194,9 @@ function Home({ initialSettings }) {
|
|||||||
const { data: bookmarks } = useSWR("/api/bookmarks");
|
const { data: bookmarks } = useSWR("/api/bookmarks");
|
||||||
const { data: widgets } = useSWR("/api/widgets");
|
const { data: widgets } = useSWR("/api/widgets");
|
||||||
|
|
||||||
const servicesAndBookmarks = [
|
const servicesAndBookmarks = [...bookmarks.map((bg) => bg.bookmarks).flat(), ...getAllServices(services)].filter(
|
||||||
...services.map((sg) => sg.services).flat(),
|
(i) => i?.href,
|
||||||
...bookmarks.map((bg) => bg.bookmarks).flat(),
|
);
|
||||||
].filter((i) => i?.href);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (settings.language) {
|
if (settings.language) {
|
||||||
@ -459,7 +470,7 @@ function Home({ initialSettings }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function Wrapper({ initialSettings, fallback }) {
|
export default function Wrapper({ initialSettings, fallback }) {
|
||||||
const { theme } = useContext(ThemeContext);
|
const { themeContext } = useContext(ThemeContext);
|
||||||
const wrappedStyle = {};
|
const wrappedStyle = {};
|
||||||
let backgroundBlur = false;
|
let backgroundBlur = false;
|
||||||
let backgroundSaturate = false;
|
let backgroundSaturate = false;
|
||||||
@ -490,9 +501,9 @@ export default function Wrapper({ initialSettings, fallback }) {
|
|||||||
id="page_wrapper"
|
id="page_wrapper"
|
||||||
className={classNames(
|
className={classNames(
|
||||||
"relative",
|
"relative",
|
||||||
theme && theme,
|
initialSettings.theme && initialSettings.theme,
|
||||||
initialSettings.color && `theme-${initialSettings.color}`,
|
initialSettings.color && `theme-${initialSettings.color}`,
|
||||||
theme === "dark" ? "scheme-dark" : "scheme-light",
|
themeContext === "dark" ? "scheme-dark" : "scheme-light",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
|||||||
@ -78,6 +78,9 @@ function formatValue(t, mapping, rawValue) {
|
|||||||
case "percent":
|
case "percent":
|
||||||
value = t("common.percent", { value });
|
value = t("common.percent", { value });
|
||||||
break;
|
break;
|
||||||
|
case "duration":
|
||||||
|
value = t("common.duration", { value });
|
||||||
|
break;
|
||||||
case "bytes":
|
case "bytes":
|
||||||
value = t("common.bytes", { value });
|
value = t("common.bytes", { value });
|
||||||
break;
|
break;
|
||||||
|
|||||||
@ -46,7 +46,7 @@ export default function Component({ service }) {
|
|||||||
<div className="absolute -top-2 -left-2 -right-2 -bottom-2">
|
<div className="absolute -top-2 -left-2 -right-2 -bottom-2">
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
height: `${Math.max(20, fsData.size / fsData.free)}%`,
|
height: `${Math.max(20, (140 * (fsData.size - fsData.free)) / fsData.size)}px`,
|
||||||
}}
|
}}
|
||||||
className="absolute bottom-0 border-t border-t-theme-500 bg-gradient-to-b from-theme-500/40 to-theme-500/10 w-full"
|
className="absolute bottom-0 border-t border-t-theme-500 bg-gradient-to-b from-theme-500/40 to-theme-500/10 w-full"
|
||||||
/>
|
/>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user