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?
*.d.ts
!types-assets.d.ts
!types.d.ts
*.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": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
"preview": "pnpm build && vite preview"
},
"dependencies": {
"preact": "^10.26.9",
@ -17,6 +17,7 @@
"eslint": "^9.36.0",
"eslint-config-preact": "^2.0.0",
"typescript": "^5.9.2",
"user-agent-data-types": "0.4.2",
"vite": "^7.0.4"
},
"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) {
const [ recommendedDownload, setRecommendedDownload ] = useState<RecommendedDownload | null>();
useEffect(() => setRecommendedDownload(getRecommendedDownload()), []);
useEffect(() => {
getRecommendedDownload()?.then(setRecommendedDownload);
}, []);
return (recommendedDownload &&
<Button
className={`download-button desktop-only ${big ? "big" : ""}`}
href={recommendedDownload.url}
iconSvg={downloadIcon}
text={<>
Download now{" "}
{big
? <span class="platform">v{packageJson.version} for {recommendedDownload.name}</span>
: <span class="platform">for {recommendedDownload.name}</span>
}
</>}
/>
<>
{recommendedDownload.platform !== "linux"
? (
<Button
className={`download-button desktop-only ${big ? "big" : ""}`}
href={recommendedDownload.url}
iconSvg={downloadIcon}
text={<>
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;
justify-content: space-between;
align-items: center;
flex-grow: 1;
align-self: stretch;
width: 100%;
}
.menu-toggle {

View File

@ -19,6 +19,7 @@ export interface DownloadMatrixEntry {
description: Record<Architecture, string> | string;
downloads: Record<string, DownloadInfo>;
helpUrl?: string;
quickStartTitle?: string;
quickStartCode?: string;
}
@ -43,6 +44,7 @@ export const downloadMatrix: DownloadMatrix = {
x64: "Compatible with Intel or AMD devices running Windows 10 and 11.",
arm64: "Compatible with ARM devices (e.g. with Qualcomm Snapdragon).",
},
quickStartTitle: "To install via Winget:",
quickStartCode: "winget install TriliumNext.Notes",
downloads: {
exe: {
@ -55,10 +57,6 @@ export const downloadMatrix: DownloadMatrix = {
scoop: {
name: "Scoop",
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.",
arm64: "For ARM-based Linux distributions, compatible with aarch64 architecture.",
},
quickStartTitle: "Select an appropriate package format, depending on your distribution:",
downloads: {
deb: {
recommended: true,
name: "Download .deb"
name: ".deb"
},
rpm: {
recommended: true,
name: "Download .rpm"
name: ".rpm"
},
flatpak: {
name: ".flatpak"
@ -105,6 +104,7 @@ export const downloadMatrix: DownloadMatrix = {
x64: "For Intel-based Macs running macOS Big Sur or later.",
arm64: "For Apple Silicon Macs such as those with M1 and M2 chips.",
},
quickStartTitle: "To install via Homebrew:",
quickStartCode: "brew install --cask trilium-notes",
downloads: {
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 (navigator.userAgentData) {
const { architecture } = await navigator.userAgentData.getHighEntropyValues(["architecture"]);
return architecture?.startsWith("arm") ? "arm64" : "x64";
}
const userAgent = navigator.userAgent.toLowerCase();
if (userAgent.includes('arm64') || userAgent.includes('aarch64')) {
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;
const architecture = getArchitecture();
const architecture = await getArchitecture();
const platform = getPlatform();
if (!platform || !architecture) return null;

View File

@ -6,7 +6,7 @@
grid-column: 1 / 4;
}
.download-card {
.download-card {
h3 {
color: var(--accent-color);
font-size: 1.5em;
@ -32,10 +32,17 @@
color: var(--accent-color);
}
.quick-start-title {
margin-bottom: 0;
font-size: 0.9em;
color: var(--muted-color);
}
.quick-start {
background-color: #ececec;
padding: 0.75em;
border-radius: 6px;
margin-top: 0.5em;
@media (prefers-color-scheme: dark) {
background-color: black;
@ -49,7 +56,7 @@
}
.download-options {
justify-content: flex-end;
justify-content: center;
align-items: stretch;
flex-grow: 1;
align-items: center;
@ -80,9 +87,19 @@
}
.download-desktop {
.download-card:first-of-type { --accent-color: var(--brand-1); }
.download-card:nth-of-type(2) { --accent-color: var(--brand-2); }
.download-card:last-of-type { --accent-color: var(--brand-3); }
.download-card {
@media (min-width: 720px) {
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 {
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 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 Button, { Link } from "../../components/Button.js";
import Icon from "../../components/Icon.js";
@ -10,8 +10,15 @@ import "./get-started.css";
import packageJson from "../../../../../package.json" with { type: "json" };
export default function DownloadPage() {
const [ currentArch, setCurrentArch ] = useState(getArchitecture() ?? "x64");
usePageTitle("Download");
const [ currentArch, setCurrentArch ] = useState<Architecture>("x64");
const [ userPlatform, setUserPlatform ] = useState<Platform>();
useLayoutEffect(() => {
getArchitecture().then((arch) => setCurrentArch(arch ?? "x64"));
setUserPlatform(getPlatform() ?? "windows");
}, []);
usePageTitle("Get started");
return (
<>
@ -31,7 +38,10 @@ export default function DownloadPage() {
</div>
<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 class="download-footer">
@ -41,14 +51,21 @@ export default function DownloadPage() {
<Section title="Set up a server for access on multiple devices">
<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>
</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>) {
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);
return (
<Card title={<>
{unwrapText(entry.title)}
{entry.helpUrl && (
<Link
className="more-info"
href={entry.helpUrl}
openExternally
>
<Icon svg={helpIcon} />
</Link>
)}
</>} className="download-card">
<Card
title={
<>
{unwrapText(entry.title)}
{entry.helpUrl && (
<Link
className="more-info"
href={entry.helpUrl}
openExternally
>
<Icon svg={helpIcon} />
</Link>
)}
</>
}
className={`download-card ${platform} ${isRecommended ? "recommended" : ""}`}
>
{unwrapText(entry.description)}
{entry.quickStartTitle && <p class="quick-start-title">{entry.quickStartTitle}</p>}
{entry.quickStartCode && (
<pre className="quick-start">
<code>{entry.quickStartCode}</code>
@ -104,3 +127,13 @@ export function DownloadCard({ app, arch, entry: [ platform, entry ] }: { app: A
</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 {
flex-basis: 40%;
flex-basis: 30%;
flex-shrink: 0;
color: var(--muted-color);
h1 {
@ -57,6 +58,10 @@ section.hero-section {
}
}
.screenshot-container {
flex-grow: 1;
}
.screenshot {
position: relative;
width: 100%;

View File

@ -60,6 +60,7 @@ function HeroSection() {
setScreenshotUrl(`/screenshot_desktop_mac_${colorScheme}.webp`);
break;
case "linux":
setScreenshotUrl(`/screenshot_desktop_linux_${colorScheme}.webp`);
break;
case "windows":
default:
@ -76,7 +77,6 @@ function HeroSection() {
<div className="download-wrapper">
<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" />
<div className="additional-options">
<Button iconSvg={gitHubIcon} outline text="GitHub" href="https://github.com/TriliumNext/Trilium/" openExternally />
@ -86,7 +86,9 @@ function HeroSection() {
</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>
)
}

View File

@ -15,7 +15,7 @@
:root {
--foreground-color: #fff;
--background-color: #0a0e14;
--muted-color: #cacaca;
--muted-color: #c5c5c5;
--header-background-color: rgba(0, 0, 0, 0.75);
--card-background-color: #ffffff12;
--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:
specifier: ^5.9.2
version: 5.9.2
user-agent-data-types:
specifier: 0.4.2
version: 0.4.2
vite:
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)
@ -13299,6 +13302,9 @@ packages:
peerDependencies:
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:
resolution: {integrity: sha512-5cnLm4gseXjAclKowC4IjByaGsjtAoV6PrOQOljplNB54ReUYJP8HdAFq2muHinSDAh09PPX/uXDPfdxRHvuSA==}
engines: {node: '>= 0.8.0'}
@ -14680,6 +14686,8 @@ snapshots:
'@ckeditor/ckeditor5-typing': 46.1.1
'@ckeditor/ckeditor5-utils': 46.1.1
ckeditor5: 46.1.1(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-autosave@46.1.1':
dependencies:
@ -14710,6 +14718,8 @@ snapshots:
'@ckeditor/ckeditor5-ui': 46.1.1
'@ckeditor/ckeditor5-utils': 46.1.1
ckeditor5: 46.1.1(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-bookmark@46.1.1':
dependencies:
@ -15026,8 +15036,6 @@ snapshots:
'@ckeditor/ckeditor5-utils': 46.1.1
ckeditor5: 46.1.1(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
es-toolkit: 1.39.5
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-editor-multi-root@46.1.1':
dependencies:
@ -15221,6 +15229,8 @@ snapshots:
'@ckeditor/ckeditor5-widget': 46.1.1
ckeditor5: 46.1.1(patch_hash=8331a09d41443b39ea1c784daaccfeb0da4f9065ed556e7de92e9c77edd9eb41)
es-toolkit: 1.39.5
transitivePeerDependencies:
- supports-color
'@ckeditor/ckeditor5-icons@46.1.1': {}
@ -29683,6 +29693,8 @@ snapshots:
dependencies:
react: 16.14.0
user-agent-data-types@0.4.2: {}
userhome@1.0.1: {}
username@5.1.0: