Merge branch 'main' into feature/manifest-v3-update

This commit is contained in:
Octech2722 2025-09-30 15:31:22 -05:00 committed by GitHub
commit 8616a65015
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 155 additions and 56 deletions

View File

@ -24,5 +24,5 @@ dist-ssr
*.sw? *.sw?
*.d.ts *.d.ts
!types-assets.d.ts !types.d.ts
*.map *.map

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

View File

@ -5,7 +5,7 @@
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vite build", "build": "vite build",
"preview": "vite preview" "preview": "pnpm build && vite preview"
}, },
"dependencies": { "dependencies": {
"preact": "^10.26.9", "preact": "^10.26.9",
@ -17,6 +17,7 @@
"eslint": "^9.36.0", "eslint": "^9.36.0",
"eslint-config-preact": "^2.0.0", "eslint-config-preact": "^2.0.0",
"typescript": "^5.9.2", "typescript": "^5.9.2",
"user-agent-data-types": "0.4.2",
"vite": "^7.0.4" "vite": "^7.0.4"
}, },
"eslintConfig": { "eslintConfig": {

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

View File

@ -11,20 +11,44 @@ interface DownloadButtonProps {
export default function DownloadButton({ big }: DownloadButtonProps) { export default function DownloadButton({ big }: DownloadButtonProps) {
const [ recommendedDownload, setRecommendedDownload ] = useState<RecommendedDownload | null>(); const [ recommendedDownload, setRecommendedDownload ] = useState<RecommendedDownload | null>();
useEffect(() => setRecommendedDownload(getRecommendedDownload()), []); useEffect(() => {
getRecommendedDownload()?.then(setRecommendedDownload);
}, []);
return (recommendedDownload && return (recommendedDownload &&
<Button <>
className={`download-button desktop-only ${big ? "big" : ""}`} {recommendedDownload.platform !== "linux"
href={recommendedDownload.url} ? (
iconSvg={downloadIcon} <Button
text={<> className={`download-button desktop-only ${big ? "big" : ""}`}
Download now{" "} href={recommendedDownload.url}
{big iconSvg={downloadIcon}
? <span class="platform">v{packageJson.version} for {recommendedDownload.name}</span> text={<>
: <span class="platform">for {recommendedDownload.name}</span> Download now{" "}
} {big
</>} ? <span class="platform">v{packageJson.version} for {recommendedDownload.name}</span>
/> : <span class="platform">for {recommendedDownload.name}</span>
}
</>}
/>
) : (
<Button
className={`download-button desktop-only ${big ? "big" : ""}`}
href="/get-started/"
iconSvg={downloadIcon}
text={<>
Download now{" "}
{big
? <span class="platform">v{packageJson.version} for Linux</span>
: <span class="platform">for Linux</span>
}
</>}
/>
)}
{big && (
<a class="more-download-options desktop-only" href="./get-started/">More platforms & server setup</a>
)}
</>
) )
} }

View File

@ -75,8 +75,7 @@ header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
flex-grow: 1; width: 100%;
align-self: stretch;
} }
.menu-toggle { .menu-toggle {

View File

@ -19,6 +19,7 @@ export interface DownloadMatrixEntry {
description: Record<Architecture, string> | string; description: Record<Architecture, string> | string;
downloads: Record<string, DownloadInfo>; downloads: Record<string, DownloadInfo>;
helpUrl?: string; helpUrl?: string;
quickStartTitle?: string;
quickStartCode?: string; quickStartCode?: string;
} }
@ -43,6 +44,7 @@ export const downloadMatrix: DownloadMatrix = {
x64: "Compatible with Intel or AMD devices running Windows 10 and 11.", x64: "Compatible with Intel or AMD devices running Windows 10 and 11.",
arm64: "Compatible with ARM devices (e.g. with Qualcomm Snapdragon).", arm64: "Compatible with ARM devices (e.g. with Qualcomm Snapdragon).",
}, },
quickStartTitle: "To install via Winget:",
quickStartCode: "winget install TriliumNext.Notes", quickStartCode: "winget install TriliumNext.Notes",
downloads: { downloads: {
exe: { exe: {
@ -55,10 +57,6 @@ export const downloadMatrix: DownloadMatrix = {
scoop: { scoop: {
name: "Scoop", name: "Scoop",
url: "https://scoop.sh/#/apps?q=trilium&id=7c08bc3c105b9ee5c00dd4245efdea0f091b8a5c" url: "https://scoop.sh/#/apps?q=trilium&id=7c08bc3c105b9ee5c00dd4245efdea0f091b8a5c"
},
winget: {
name: "Winget",
url: "https://github.com/microsoft/winget-pkgs/tree/master/manifests/t/TriliumNext/Notes/"
} }
} }
}, },
@ -71,14 +69,15 @@ export const downloadMatrix: DownloadMatrix = {
x64: "For most Linux distributions, compatible with x86_64 architecture.", x64: "For most Linux distributions, compatible with x86_64 architecture.",
arm64: "For ARM-based Linux distributions, compatible with aarch64 architecture.", arm64: "For ARM-based Linux distributions, compatible with aarch64 architecture.",
}, },
quickStartTitle: "Select an appropriate package format, depending on your distribution:",
downloads: { downloads: {
deb: { deb: {
recommended: true, recommended: true,
name: "Download .deb" name: ".deb"
}, },
rpm: { rpm: {
recommended: true, recommended: true,
name: "Download .rpm" name: ".rpm"
}, },
flatpak: { flatpak: {
name: ".flatpak" name: ".flatpak"
@ -105,6 +104,7 @@ export const downloadMatrix: DownloadMatrix = {
x64: "For Intel-based Macs running macOS Big Sur or later.", x64: "For Intel-based Macs running macOS Big Sur or later.",
arm64: "For Apple Silicon Macs such as those with M1 and M2 chips.", arm64: "For Apple Silicon Macs such as those with M1 and M2 chips.",
}, },
quickStartTitle: "To install via Homebrew:",
quickStartCode: "brew install --cask trilium-notes", quickStartCode: "brew install --cask trilium-notes",
downloads: { downloads: {
dmg: { dmg: {
@ -188,9 +188,14 @@ export function buildDownloadUrl(app: App, platform: Platform, format: string, a
} }
} }
export function getArchitecture(): Architecture | null { export async function getArchitecture(): Promise<Architecture | null> {
if (typeof window === "undefined") return null; if (typeof window === "undefined") return null;
if (navigator.userAgentData) {
const { architecture } = await navigator.userAgentData.getHighEntropyValues(["architecture"]);
return architecture?.startsWith("arm") ? "arm64" : "x64";
}
const userAgent = navigator.userAgent.toLowerCase(); const userAgent = navigator.userAgent.toLowerCase();
if (userAgent.includes('arm64') || userAgent.includes('aarch64')) { if (userAgent.includes('arm64') || userAgent.includes('aarch64')) {
return 'arm64'; return 'arm64';
@ -212,10 +217,10 @@ export function getPlatform(): Platform | null {
} }
} }
export function getRecommendedDownload(): RecommendedDownload | null { export async function getRecommendedDownload(): Promise<RecommendedDownload | null> {
if (typeof window === "undefined") return null; if (typeof window === "undefined") return null;
const architecture = getArchitecture(); const architecture = await getArchitecture();
const platform = getPlatform(); const platform = getPlatform();
if (!platform || !architecture) return null; if (!platform || !architecture) return null;

View File

@ -6,7 +6,7 @@
grid-column: 1 / 4; grid-column: 1 / 4;
} }
.download-card { .download-card {
h3 { h3 {
color: var(--accent-color); color: var(--accent-color);
font-size: 1.5em; font-size: 1.5em;
@ -32,10 +32,17 @@
color: var(--accent-color); color: var(--accent-color);
} }
.quick-start-title {
margin-bottom: 0;
font-size: 0.9em;
color: var(--muted-color);
}
.quick-start { .quick-start {
background-color: #ececec; background-color: #ececec;
padding: 0.75em; padding: 0.75em;
border-radius: 6px; border-radius: 6px;
margin-top: 0.5em;
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
background-color: black; background-color: black;
@ -49,7 +56,7 @@
} }
.download-options { .download-options {
justify-content: flex-end; justify-content: center;
align-items: stretch; align-items: stretch;
flex-grow: 1; flex-grow: 1;
align-items: center; align-items: center;
@ -80,9 +87,19 @@
} }
.download-desktop { .download-desktop {
.download-card:first-of-type { --accent-color: var(--brand-1); } .download-card {
.download-card:nth-of-type(2) { --accent-color: var(--brand-2); } @media (min-width: 720px) {
.download-card:last-of-type { --accent-color: var(--brand-3); } transform: scale(0.9);
&.recommended {
transform: scale(1);
}
}
&.windows { --accent-color: var(--brand-1); }
&.linux { --accent-color: var(--brand-2); }
&.macos { --accent-color: var(--brand-3); }
}
.download-footer { .download-footer {
text-align: center; text-align: center;

View File

@ -1,7 +1,7 @@
import { useState } from "preact/hooks"; import { useLayoutEffect, useState } from "preact/hooks";
import Card from "../../components/Card.js"; import Card from "../../components/Card.js";
import Section from "../../components/Section.js"; import Section from "../../components/Section.js";
import { App, Architecture, buildDownloadUrl, downloadMatrix, DownloadMatrixEntry, getArchitecture, Platform } from "../../download-helper.js"; import { App, Architecture, buildDownloadUrl, downloadMatrix, DownloadMatrixEntry, getArchitecture, getPlatform, Platform } from "../../download-helper.js";
import { usePageTitle } from "../../hooks.js"; import { usePageTitle } from "../../hooks.js";
import Button, { Link } from "../../components/Button.js"; import Button, { Link } from "../../components/Button.js";
import Icon from "../../components/Icon.js"; import Icon from "../../components/Icon.js";
@ -10,8 +10,15 @@ import "./get-started.css";
import packageJson from "../../../../../package.json" with { type: "json" }; import packageJson from "../../../../../package.json" with { type: "json" };
export default function DownloadPage() { export default function DownloadPage() {
const [ currentArch, setCurrentArch ] = useState(getArchitecture() ?? "x64"); const [ currentArch, setCurrentArch ] = useState<Architecture>("x64");
usePageTitle("Download"); const [ userPlatform, setUserPlatform ] = useState<Platform>();
useLayoutEffect(() => {
getArchitecture().then((arch) => setCurrentArch(arch ?? "x64"));
setUserPlatform(getPlatform() ?? "windows");
}, []);
usePageTitle("Get started");
return ( return (
<> <>
@ -31,7 +38,10 @@ export default function DownloadPage() {
</div> </div>
<div className="grid-3-cols download-desktop"> <div className="grid-3-cols download-desktop">
{Object.entries(downloadMatrix.desktop).map(entry => <DownloadCard app="desktop" arch={currentArch} entry={entry} />)} {reorderPlatforms(Object.entries(downloadMatrix.desktop), userPlatform ?? "")
.map(entry => (
<DownloadCard app="desktop" arch={currentArch} entry={entry} isRecommended={userPlatform === entry[0]} />
))}
</div> </div>
<div class="download-footer"> <div class="download-footer">
@ -41,14 +51,21 @@ export default function DownloadPage() {
<Section title="Set up a server for access on multiple devices"> <Section title="Set up a server for access on multiple devices">
<div className="grid-2-cols download-server"> <div className="grid-2-cols download-server">
{Object.entries(downloadMatrix.server).map(entry => <DownloadCard app="server" arch={currentArch} entry={entry} />)} {Object.entries(downloadMatrix.server).map(entry => (
<DownloadCard app="server" arch={currentArch} entry={entry} />
))}
</div> </div>
</Section> </Section>
</> </>
) )
} }
export function DownloadCard({ app, arch, entry: [ platform, entry ] }: { app: App, arch: Architecture, entry: [string, DownloadMatrixEntry] }) { export function DownloadCard({ app, arch, entry: [ platform, entry ], isRecommended }: {
app: App,
arch: Architecture,
entry: [string, DownloadMatrixEntry],
isRecommended?: boolean;
}) {
function unwrapText(text: string | Record<Architecture, string>) { function unwrapText(text: string | Record<Architecture, string>) {
return (typeof text === "string" ? text : text[arch]); return (typeof text === "string" ? text : text[arch]);
} }
@ -58,20 +75,26 @@ export function DownloadCard({ app, arch, entry: [ platform, entry ] }: { app: A
const restDownloads = allDownloads.filter(download => !download[1].recommended); const restDownloads = allDownloads.filter(download => !download[1].recommended);
return ( return (
<Card title={<> <Card
{unwrapText(entry.title)} title={
{entry.helpUrl && ( <>
<Link {unwrapText(entry.title)}
className="more-info" {entry.helpUrl && (
href={entry.helpUrl} <Link
openExternally className="more-info"
> href={entry.helpUrl}
<Icon svg={helpIcon} /> openExternally
</Link> >
)} <Icon svg={helpIcon} />
</>} className="download-card"> </Link>
)}
</>
}
className={`download-card ${platform} ${isRecommended ? "recommended" : ""}`}
>
{unwrapText(entry.description)} {unwrapText(entry.description)}
{entry.quickStartTitle && <p class="quick-start-title">{entry.quickStartTitle}</p>}
{entry.quickStartCode && ( {entry.quickStartCode && (
<pre className="quick-start"> <pre className="quick-start">
<code>{entry.quickStartCode}</code> <code>{entry.quickStartCode}</code>
@ -104,3 +127,13 @@ export function DownloadCard({ app, arch, entry: [ platform, entry ] }: { app: A
</Card> </Card>
) )
} }
function reorderPlatforms(entries: [string, DownloadMatrixEntry][], platformToCenter: Platform | "") {
const entryToCenter = entries.find(x => x[0] === platformToCenter);
if (!entryToCenter) return entries;
const others = entries.filter(x => x !== entryToCenter);
const mid = Math.floor(others.length / 2);
others.splice(mid, 0, entryToCenter);
return others;
}

View File

@ -47,7 +47,8 @@ section.hero-section {
} }
.title-section { .title-section {
flex-basis: 40%; flex-basis: 30%;
flex-shrink: 0;
color: var(--muted-color); color: var(--muted-color);
h1 { h1 {
@ -57,6 +58,10 @@ section.hero-section {
} }
} }
.screenshot-container {
flex-grow: 1;
}
.screenshot { .screenshot {
position: relative; position: relative;
width: 100%; width: 100%;

View File

@ -60,6 +60,7 @@ function HeroSection() {
setScreenshotUrl(`/screenshot_desktop_mac_${colorScheme}.webp`); setScreenshotUrl(`/screenshot_desktop_mac_${colorScheme}.webp`);
break; break;
case "linux": case "linux":
setScreenshotUrl(`/screenshot_desktop_linux_${colorScheme}.webp`);
break; break;
case "windows": case "windows":
default: default:
@ -76,7 +77,6 @@ function HeroSection() {
<div className="download-wrapper"> <div className="download-wrapper">
<DownloadButton big /> <DownloadButton big />
<a class="more-download-options desktop-only" href="./get-started/">More platforms & server setup</a>
<Button href="./get-started/" className="mobile-only" text="Get started" /> <Button href="./get-started/" className="mobile-only" text="Get started" />
<div className="additional-options"> <div className="additional-options">
<Button iconSvg={gitHubIcon} outline text="GitHub" href="https://github.com/TriliumNext/Trilium/" openExternally /> <Button iconSvg={gitHubIcon} outline text="GitHub" href="https://github.com/TriliumNext/Trilium/" openExternally />
@ -86,7 +86,9 @@ function HeroSection() {
</div> </div>
{screenshotUrl && <img class="screenshot" src={screenshotUrl} alt="Screenshot of the Trilium Notes desktop application" />} <div className="screenshot-container">
{screenshotUrl && <img class="screenshot" src={screenshotUrl} alt="Screenshot of the Trilium Notes desktop application" />}
</div>
</Section> </Section>
) )
} }

View File

@ -15,7 +15,7 @@
:root { :root {
--foreground-color: #fff; --foreground-color: #fff;
--background-color: #0a0e14; --background-color: #0a0e14;
--muted-color: #cacaca; --muted-color: #c5c5c5;
--header-background-color: rgba(0, 0, 0, 0.75); --header-background-color: rgba(0, 0, 0, 0.75);
--card-background-color: #ffffff12; --card-background-color: #ffffff12;
--card-box-shadow: 0 0 12px rgba(0, 0, 0, 0.15); --card-box-shadow: 0 0 12px rgba(0, 0, 0, 0.15);

1
apps/website/src/types.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="user-agent-data-types" />

16
pnpm-lock.yaml generated
View File

@ -787,6 +787,9 @@ importers:
typescript: typescript:
specifier: ^5.9.2 specifier: ^5.9.2
version: 5.9.2 version: 5.9.2
user-agent-data-types:
specifier: 0.4.2
version: 0.4.2
vite: vite:
specifier: ^7.0.4 specifier: ^7.0.4
version: 7.1.7(@types/node@24.6.0)(jiti@2.6.0)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) version: 7.1.7(@types/node@24.6.0)(jiti@2.6.0)(less@4.1.3)(lightningcss@1.30.1)(sass-embedded@1.91.0)(sass@1.91.0)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)
@ -13299,6 +13302,9 @@ packages:
peerDependencies: peerDependencies:
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
user-agent-data-types@0.4.2:
resolution: {integrity: sha512-jXep3kO/dGNmDOkbDa8ccp4QArgxR4I76m3QVcJ1aOF0B9toc+YtSXtX5gLdDTZXyWlpQYQrABr6L1L2GZOghw==}
userhome@1.0.1: userhome@1.0.1:
resolution: {integrity: sha512-5cnLm4gseXjAclKowC4IjByaGsjtAoV6PrOQOljplNB54ReUYJP8HdAFq2muHinSDAh09PPX/uXDPfdxRHvuSA==} resolution: {integrity: sha512-5cnLm4gseXjAclKowC4IjByaGsjtAoV6PrOQOljplNB54ReUYJP8HdAFq2muHinSDAh09PPX/uXDPfdxRHvuSA==}
engines: {node: '>= 0.8.0'} engines: {node: '>= 0.8.0'}
@ -14680,6 +14686,8 @@ snapshots:
'@ckeditor/ckeditor5-typing': 46.1.1 '@ckeditor/ckeditor5-typing': 46.1.1
'@ckeditor/ckeditor5-utils': 46.1.1 '@ckeditor/ckeditor5-utils': 46.1.1
ckeditor5: 46.1.1(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41) ckeditor5: 46.1.1(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-autosave@46.1.1': '@ckeditor/ckeditor5-autosave@46.1.1':
dependencies: dependencies:
@ -14710,6 +14718,8 @@ snapshots:
'@ckeditor/ckeditor5-ui': 46.1.1 '@ckeditor/ckeditor5-ui': 46.1.1
'@ckeditor/ckeditor5-utils': 46.1.1 '@ckeditor/ckeditor5-utils': 46.1.1
ckeditor5: 46.1.1(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41) ckeditor5: 46.1.1(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-bookmark@46.1.1': '@ckeditor/ckeditor5-bookmark@46.1.1':
dependencies: dependencies:
@ -15026,8 +15036,6 @@ snapshots:
'@ckeditor/ckeditor5-utils': 46.1.1 '@ckeditor/ckeditor5-utils': 46.1.1
ckeditor5: 46.1.1(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41) ckeditor5: 46.1.1(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
es-toolkit: 1.39.5 es-toolkit: 1.39.5
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-editor-multi-root@46.1.1': '@ckeditor/ckeditor5-editor-multi-root@46.1.1':
dependencies: dependencies:
@ -15221,6 +15229,8 @@ snapshots:
'@ckeditor/ckeditor5-widget': 46.1.1 '@ckeditor/ckeditor5-widget': 46.1.1
ckeditor5: 46.1.1(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41) ckeditor5: 46.1.1(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
es-toolkit: 1.39.5 es-toolkit: 1.39.5
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-icons@46.1.1': {} '@ckeditor/ckeditor5-icons@46.1.1': {}
@ -29683,6 +29693,8 @@ snapshots:
dependencies: dependencies:
react: 16.14.0 react: 16.14.0
user-agent-data-types@0.4.2: {}
userhome@1.0.1: {} userhome@1.0.1: {}
username@5.1.0: username@5.1.0: