docs: streamline release v2 documentation (#6760)
This commit is contained in:
1 parent
ac62e5bfd6
commit
b3aa6155cc
163 files changed
+2908
-4208
No files matched your search
@@ -0,0 +1,245 @@
|
||||
.diagram {
|
||||
--diagram-ink: var(--ifm-font-color-base);
|
||||
--diagram-muted: var(--ifm-color-emphasis-700);
|
||||
--diagram-panel: var(--ifm-background-surface-color);
|
||||
--diagram-line: var(--ifm-color-emphasis-300);
|
||||
margin: 0;
|
||||
padding: 1rem;
|
||||
border: 1px solid var(--diagram-line);
|
||||
border-radius: 0.75rem;
|
||||
color: var(--diagram-ink);
|
||||
background: var(--diagram-panel);
|
||||
}
|
||||
|
||||
.lane {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(8.5rem, 1.1fr) 2.75rem minmax(8.5rem, 1fr) 2.75rem minmax(8.5rem, 1fr) 2.75rem minmax(
|
||||
8.5rem,
|
||||
1fr
|
||||
);
|
||||
align-items: stretch;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
.stage {
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr;
|
||||
gap: 0.75rem;
|
||||
min-width: 0;
|
||||
padding: 0.7rem;
|
||||
border: 1px solid var(--diagram-line);
|
||||
border-radius: 0.55rem;
|
||||
margin: 0;
|
||||
background: var(--ifm-background-color);
|
||||
}
|
||||
|
||||
.stageHeading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
color: var(--diagram-ink);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a.stageHeading:hover {
|
||||
color: var(--ifm-color-primary);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.stageHeading > span:last-child {
|
||||
display: grid;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.stageHeading strong {
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
|
||||
.stageHeading small {
|
||||
color: var(--diagram-muted);
|
||||
font-size: 0.62rem;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.stageNumber {
|
||||
display: grid;
|
||||
flex: none;
|
||||
place-items: center;
|
||||
width: 1.45rem;
|
||||
height: 1.45rem;
|
||||
border: 1px solid var(--diagram-line);
|
||||
border-radius: 50%;
|
||||
color: var(--diagram-muted);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.64rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.serviceList,
|
||||
.widgetList {
|
||||
display: grid;
|
||||
align-content: center;
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
.service,
|
||||
.widget {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.45rem;
|
||||
min-width: 0;
|
||||
padding: 0.4rem;
|
||||
border: 1px solid var(--diagram-line);
|
||||
border-radius: 0.4rem;
|
||||
color: var(--diagram-ink);
|
||||
background: var(--diagram-panel);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.service:hover,
|
||||
.widget:hover {
|
||||
border-color: var(--ifm-color-primary);
|
||||
color: var(--diagram-ink);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.service img {
|
||||
flex: none;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
|
||||
.widget svg {
|
||||
flex: none;
|
||||
color: var(--diagram-muted);
|
||||
}
|
||||
|
||||
.service > span {
|
||||
display: grid;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.service strong,
|
||||
.widget span {
|
||||
overflow: hidden;
|
||||
font-size: 0.67rem;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.service code,
|
||||
.integration code {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
color: var(--diagram-muted);
|
||||
background: transparent;
|
||||
font-size: 0.56rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.integration {
|
||||
display: grid;
|
||||
align-content: center;
|
||||
justify-items: center;
|
||||
gap: 0.35rem;
|
||||
min-height: 5rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.integration > svg {
|
||||
color: var(--diagram-muted);
|
||||
}
|
||||
|
||||
.integration > span {
|
||||
color: var(--diagram-muted);
|
||||
font-size: 0.61rem;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.boardCalendar {
|
||||
display: grid;
|
||||
align-content: center;
|
||||
gap: 0.28rem;
|
||||
min-height: 5rem;
|
||||
padding: 0.42rem;
|
||||
border: 1px solid var(--diagram-line);
|
||||
border-radius: 0.4rem;
|
||||
background: var(--diagram-panel);
|
||||
}
|
||||
|
||||
.calendarHeader {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
gap: 0.3rem;
|
||||
}
|
||||
|
||||
.calendarHeader strong {
|
||||
font-size: 0.58rem;
|
||||
}
|
||||
|
||||
.calendarHeader span {
|
||||
color: var(--diagram-muted);
|
||||
font-size: 0.44rem;
|
||||
}
|
||||
|
||||
.calendarWeekdays,
|
||||
.calendarDays {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(7, minmax(0, 1fr));
|
||||
gap: 0.12rem;
|
||||
}
|
||||
|
||||
.calendarWeekdays span {
|
||||
color: var(--diagram-muted);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.38rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.calendarDays span {
|
||||
position: relative;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
min-width: 0;
|
||||
aspect-ratio: 1;
|
||||
border-radius: 0.18rem;
|
||||
color: var(--diagram-muted);
|
||||
background: color-mix(in srgb, var(--diagram-line) 28%, transparent);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.42rem;
|
||||
}
|
||||
|
||||
.calendarDays span[data-event] {
|
||||
color: var(--diagram-ink);
|
||||
background: color-mix(in srgb, var(--diagram-line) 62%, transparent);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.calendarDays span[data-event]::after {
|
||||
position: absolute;
|
||||
bottom: 0.08rem;
|
||||
width: 0.12rem;
|
||||
height: 0.12rem;
|
||||
border-radius: 50%;
|
||||
background: var(--diagram-muted);
|
||||
content: "";
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.lane {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.serviceList,
|
||||
.widgetList {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 430px) {
|
||||
.serviceList,
|
||||
.widgetList {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
import Link from "@docusaurus/Link";
|
||||
import { IconCalendar, IconDownload, IconPlugConnected } from "@tabler/icons-react";
|
||||
|
||||
import { qBittorentIntegration } from "@site/docs/integrations/q-bittorent";
|
||||
import { sonarrIntegration } from "@site/docs/integrations/sonarr";
|
||||
import { FlowConnector } from "@site/src/components/ui/flow-connector";
|
||||
|
||||
import styles from "./concept-flow.module.css";
|
||||
|
||||
const services = [
|
||||
{
|
||||
name: sonarrIntegration.name,
|
||||
iconUrl: getIconUrl(sonarrIntegration.iconUrl),
|
||||
endpoint: "sonarr:8989",
|
||||
href: "/docs/integrations/sonarr",
|
||||
},
|
||||
{
|
||||
name: qBittorentIntegration.name,
|
||||
iconUrl: getIconUrl(qBittorentIntegration.iconUrl),
|
||||
endpoint: "qbittorrent:8080",
|
||||
href: "/docs/integrations/q-bittorent",
|
||||
},
|
||||
];
|
||||
|
||||
const widgets = [
|
||||
{
|
||||
name: "Calendar",
|
||||
icon: IconCalendar,
|
||||
href: "/docs/widgets/calendar",
|
||||
},
|
||||
{
|
||||
name: "Downloads",
|
||||
icon: IconDownload,
|
||||
href: "/docs/widgets/downloads",
|
||||
},
|
||||
];
|
||||
|
||||
const calendarDays = Array.from({ length: 14 }, (_, index) => index + 1);
|
||||
const calendarEventDays = new Set([3, 8, 12]);
|
||||
|
||||
function getIconUrl(iconUrl: typeof sonarrIntegration.iconUrl) {
|
||||
if (typeof iconUrl === "string") return iconUrl;
|
||||
return iconUrl.light;
|
||||
}
|
||||
|
||||
export function ConceptFlow() {
|
||||
return (
|
||||
<figure className={styles.diagram} aria-label="How Homarr connects services to a board">
|
||||
<div className={styles.lane}>
|
||||
<Stage number="1" title="Service" detail="API endpoint">
|
||||
<div className={styles.serviceList}>
|
||||
{services.map((service) => (
|
||||
<Link className={styles.service} to={service.href} key={service.name}>
|
||||
<img src={service.iconUrl} alt="" />
|
||||
<span>
|
||||
<strong>{service.name}</strong>
|
||||
<code>{service.endpoint}</code>
|
||||
</span>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</Stage>
|
||||
|
||||
<FlowConnector />
|
||||
|
||||
<Stage number="2" title="Integration" detail="Server-side connection" href="/docs/management/integrations">
|
||||
<div className={styles.integration}>
|
||||
<IconPlugConnected size={26} aria-hidden="true" />
|
||||
<code>URL + credentials</code>
|
||||
<span>Fetch data and run actions</span>
|
||||
</div>
|
||||
</Stage>
|
||||
|
||||
<FlowConnector animationDelay={350} />
|
||||
|
||||
<Stage number="3" title="Widget" detail="Data and actions" href="/docs/category/widgets">
|
||||
<div className={styles.widgetList}>
|
||||
{widgets.map((widget) => {
|
||||
const Icon = widget.icon;
|
||||
return (
|
||||
<Link className={styles.widget} to={widget.href} key={widget.name}>
|
||||
<Icon size={17} aria-hidden="true" />
|
||||
<span>{widget.name}</span>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</Stage>
|
||||
|
||||
<FlowConnector animationDelay={700} />
|
||||
|
||||
<Stage number="4" title="Board" detail="Placed in the grid" href="/docs/management/boards">
|
||||
<div className={styles.boardCalendar} aria-hidden="true">
|
||||
<div className={styles.calendarHeader}>
|
||||
<strong>September</strong>
|
||||
<span>3 events</span>
|
||||
</div>
|
||||
<div className={styles.calendarWeekdays}>
|
||||
{["M", "T", "W", "T", "F", "S", "S"].map((day, index) => (
|
||||
<span key={`${day}-${index}`}>{day}</span>
|
||||
))}
|
||||
</div>
|
||||
<div className={styles.calendarDays}>
|
||||
{calendarDays.map((day) => (
|
||||
<span data-event={calendarEventDays.has(day) || undefined} key={day}>
|
||||
{day}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Stage>
|
||||
</div>
|
||||
</figure>
|
||||
);
|
||||
}
|
||||
|
||||
function Stage({
|
||||
number,
|
||||
title,
|
||||
detail,
|
||||
href,
|
||||
children,
|
||||
}: {
|
||||
number: string;
|
||||
title: string;
|
||||
detail: string;
|
||||
href?: string;
|
||||
children?: ReactNode;
|
||||
}) {
|
||||
const heading = (
|
||||
<>
|
||||
<span className={styles.stageNumber}>{number}</span>
|
||||
<span>
|
||||
<strong>{title}</strong>
|
||||
<small>{detail}</small>
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
|
||||
return (
|
||||
<section className={styles.stage}>
|
||||
{href ? (
|
||||
<Link className={styles.stageHeading} to={href}>
|
||||
{heading}
|
||||
</Link>
|
||||
) : (
|
||||
<div className={styles.stageHeading}>{heading}</div>
|
||||
)}
|
||||
{children}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
.overview {
|
||||
--start-ink: var(--ifm-font-color-base);
|
||||
--start-muted: var(--ifm-color-emphasis-700);
|
||||
--start-panel: var(--ifm-background-surface-color);
|
||||
--start-line: var(--ifm-color-emphasis-300);
|
||||
--start-coral: #fa5252;
|
||||
display: grid;
|
||||
gap: 1.5rem;
|
||||
margin: 1.5rem 0 3rem;
|
||||
}
|
||||
|
||||
.intro {
|
||||
padding-left: 1rem;
|
||||
border-left: 3px solid var(--ifm-color-primary);
|
||||
}
|
||||
|
||||
.intro h2 {
|
||||
margin: 0 0 0.4rem;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.intro p {
|
||||
max-width: 48rem;
|
||||
margin: 0;
|
||||
color: var(--start-muted);
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.editor {
|
||||
display: grid;
|
||||
gap: 0.9rem;
|
||||
padding-top: 0.25rem;
|
||||
}
|
||||
|
||||
.editorHeading {
|
||||
display: flex;
|
||||
align-items: end;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.editorHeading h3 {
|
||||
margin: 0 0 0.25rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.editorHeading p {
|
||||
margin: 0;
|
||||
color: var(--start-muted);
|
||||
font-size: 0.84rem;
|
||||
}
|
||||
|
||||
.editorHeading > a {
|
||||
display: inline-flex;
|
||||
flex: none;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
font-size: 0.78rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.editorSteps {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 0.65rem;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.editorStep {
|
||||
overflow: hidden;
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr;
|
||||
min-width: 0;
|
||||
border: 1px solid var(--start-line);
|
||||
border-radius: 0.65rem;
|
||||
background: var(--start-panel);
|
||||
}
|
||||
|
||||
.stepCopy {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.55rem;
|
||||
padding: 0.65rem;
|
||||
}
|
||||
|
||||
.stepCopy > span {
|
||||
display: grid;
|
||||
flex: none;
|
||||
place-items: center;
|
||||
width: 1.4rem;
|
||||
height: 1.4rem;
|
||||
border: 1px solid var(--start-line);
|
||||
border-radius: 50%;
|
||||
color: var(--start-muted);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.62rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.stepCopy > div {
|
||||
display: grid;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.stepCopy strong {
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
|
||||
.stepCopy small {
|
||||
color: var(--start-muted);
|
||||
font-size: 0.64rem;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.stepMedia {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
min-height: 9rem;
|
||||
padding: 0.4rem;
|
||||
border-top: 1px solid var(--start-line);
|
||||
background: #202020;
|
||||
}
|
||||
|
||||
.stepMedia img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-height: 12rem;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.stepMedia img[data-fit="cover"] {
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
object-position: top;
|
||||
}
|
||||
|
||||
.installPaths {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 0.8rem;
|
||||
margin: 1rem 0 1.5rem;
|
||||
}
|
||||
|
||||
.installPath {
|
||||
position: relative;
|
||||
display: grid;
|
||||
gap: 0.45rem;
|
||||
min-height: 10rem;
|
||||
padding: 1.1rem;
|
||||
border: 1px solid var(--ifm-color-emphasis-400);
|
||||
border-radius: 0.9rem;
|
||||
color: var(--start-ink);
|
||||
background: var(--start-panel);
|
||||
box-shadow: 0 3px 10px rgb(20 25 35 / 8%);
|
||||
text-decoration: none;
|
||||
transition:
|
||||
border-color 160ms ease,
|
||||
transform 160ms ease,
|
||||
box-shadow 160ms ease;
|
||||
}
|
||||
|
||||
.installPath:hover {
|
||||
border-color: rgb(250 82 82 / 55%);
|
||||
color: var(--start-ink);
|
||||
text-decoration: none;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 12px 28px rgb(20 25 35 / 8%);
|
||||
}
|
||||
|
||||
.installPath > span {
|
||||
color: #c92f2f;
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.62rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
:global([data-theme="dark"]) .installPath > span {
|
||||
color: var(--ifm-color-primary-light);
|
||||
}
|
||||
|
||||
.installPath strong {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.installPath small {
|
||||
color: var(--start-muted);
|
||||
line-height: 1.45;
|
||||
}
|
||||
|
||||
.installPath > svg {
|
||||
position: absolute;
|
||||
right: 0.9rem;
|
||||
bottom: 0.9rem;
|
||||
color: var(--start-coral);
|
||||
}
|
||||
|
||||
.recommendedPath {
|
||||
border-color: var(--ifm-color-primary);
|
||||
background: var(--start-panel);
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
.editorHeading {
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
.editorSteps {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.stepMedia img {
|
||||
max-height: 18rem;
|
||||
}
|
||||
|
||||
.installPaths {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.installPath {
|
||||
min-height: 8rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.installPath {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
import Link from "@docusaurus/Link";
|
||||
import { IconArrowRight } from "@tabler/icons-react";
|
||||
|
||||
import addItem from "@site/docs/getting-started/img/manage-board-header-choose-item.png";
|
||||
import moveItem from "@site/docs/getting-started/img/move-item.gif";
|
||||
import resizeItem from "@site/docs/getting-started/img/resize-item.gif";
|
||||
|
||||
import { ConceptFlow } from "./concept-flow";
|
||||
import styles from "./getting-started-overview.module.css";
|
||||
|
||||
const editingSteps = [
|
||||
{
|
||||
title: "Add",
|
||||
description: "Choose an app, widget, or container.",
|
||||
image: addItem,
|
||||
alt: "Add item menu in board edit mode",
|
||||
fit: "cover",
|
||||
},
|
||||
{
|
||||
title: "Move",
|
||||
description: "Drag the item to another grid position.",
|
||||
image: moveItem,
|
||||
alt: "Dragging an app tile on a board",
|
||||
fit: "contain",
|
||||
},
|
||||
{
|
||||
title: "Resize",
|
||||
description: "Drag the resize handle to change its grid area.",
|
||||
image: resizeItem,
|
||||
alt: "Resizing an app tile on a board",
|
||||
fit: "contain",
|
||||
},
|
||||
];
|
||||
|
||||
export function GettingStartedOverview() {
|
||||
return (
|
||||
<section className={styles.overview} aria-labelledby="getting-started-overview-title">
|
||||
<header className={styles.intro}>
|
||||
<h2 id="getting-started-overview-title">How Homarr works</h2>
|
||||
<p>Services expose data, integrations connect to them, widgets use the data, and boards arrange the widgets.</p>
|
||||
</header>
|
||||
|
||||
<ConceptFlow />
|
||||
|
||||
<section className={styles.editor} aria-labelledby="board-editor-title">
|
||||
<div className={styles.editorHeading}>
|
||||
<div>
|
||||
<h3 id="board-editor-title">Board edit mode</h3>
|
||||
<p>Add items, move them, and resize them on the grid. Each viewport can have its own layout.</p>
|
||||
</div>
|
||||
<Link to="/docs/management/boards">
|
||||
Board docs <IconArrowRight size={15} aria-hidden="true" />
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<ol className={styles.editorSteps}>
|
||||
{editingSteps.map((step, index) => (
|
||||
<li className={styles.editorStep} key={step.title}>
|
||||
<div className={styles.stepCopy}>
|
||||
<span>{index + 1}</span>
|
||||
<div>
|
||||
<strong>{step.title}</strong>
|
||||
<small>{step.description}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.stepMedia}>
|
||||
<img src={step.image} alt={step.alt} data-fit={step.fit} loading="lazy" />
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
</section>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
const installationPaths = [
|
||||
{
|
||||
eyebrow: "Recommended",
|
||||
title: "Docker Compose",
|
||||
description: "General-purpose deployment with a persistent appdata volume.",
|
||||
href: "/docs/getting-started/installation/docker",
|
||||
recommended: true,
|
||||
},
|
||||
{
|
||||
eyebrow: "Kubernetes",
|
||||
title: "Helm",
|
||||
description: "Deploy Homarr into an existing cluster.",
|
||||
href: "/docs/getting-started/installation/helm",
|
||||
recommended: false,
|
||||
},
|
||||
{
|
||||
eyebrow: "Platforms",
|
||||
title: "NAS and hosting guides",
|
||||
description: "Unraid, TrueNAS, Synology, Portainer, Proxmox, and more.",
|
||||
href: "/docs/category/installation-1",
|
||||
recommended: false,
|
||||
},
|
||||
];
|
||||
|
||||
export function InstallationPaths() {
|
||||
return (
|
||||
<div className={styles.installPaths}>
|
||||
{installationPaths.map((path) => {
|
||||
let className = styles.installPath;
|
||||
if (path.recommended) className = `${className} ${styles.recommendedPath}`;
|
||||
|
||||
return (
|
||||
<Link className={className} to={path.href} key={path.href}>
|
||||
<span>{path.eyebrow}</span>
|
||||
<strong>{path.title}</strong>
|
||||
<small>{path.description}</small>
|
||||
<IconArrowRight size={16} aria-hidden="true" />
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user