Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
"husky": "^9.1.7",
"jsdom": "^26.1.0",
"lint-staged": "^16.1.4",
"prettier-plugin-astro": "^0.14.1",
"prettier-plugin-tailwindcss": "^0.8.0",
"tailwindcss": "^4.1.11",
"turbo": "^2.5.6",
Expand Down
Binary file added packages/docs/.gitbook/assets/album.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/docs/.gitbook/assets/app-layout-overview.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/docs/.gitbook/assets/dashboard-main.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/docs/.gitbook/assets/installed-plugins.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/docs/.gitbook/assets/log-viewer.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/docs/.gitbook/assets/my-themes.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/docs/.gitbook/assets/playlist-detail-view.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/docs/.gitbook/assets/playlists.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/docs/.gitbook/assets/plugin-store.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/docs/.gitbook/assets/preferences.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/docs/.gitbook/assets/search-albums.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/docs/.gitbook/assets/search-artists.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added packages/docs/.gitbook/assets/search-tracks.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/docs/.gitbook/assets/sources-view.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added packages/docs/.gitbook/assets/theme store.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified packages/docs/.gitbook/assets/whats-new.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 4 additions & 1 deletion packages/website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@
"dependencies": {
"@astrojs/react": "^5.0.3",
"@astrojs/sitemap": "^3.7.0",
"@fontsource-variable/bricolage-grotesque": "^5.2.10",
"@fontsource-variable/dm-sans": "^5.2.8",
"@iconify-json/simple-icons": "^1.2.67",
"@lucide/astro": "^0.563.0",
"astro": "^6.0.0",
"astro-icon": "^1.1.5",
"clsx": "^2.1.1",
"lucide-react": "^0.542.0",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added packages/website/public/images/nuki-hello.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added packages/website/public/images/nuki-studying.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion packages/website/src/components/Badge.astro
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const sizeClasses = {

<h2
class:list={[
'absolute left-1/2 -translate-x-1/2 themed-border border-border bg-[var(--color-badge)] rounded-md font-heading font-black uppercase tracking-widest whitespace-nowrap',
'themed-border border-border bg-background font-heading absolute left-1/2 -translate-x-1/2 rounded-md font-black tracking-widest whitespace-nowrap uppercase',
sizeClasses[size],
className,
]}
Expand Down
14 changes: 9 additions & 5 deletions packages/website/src/components/Box.astro
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
---
type Props = {
import type { HTMLAttributes } from 'astro/types';

type Props = HTMLAttributes<'div'> & {
as?: string;
class?: string;
[key: string]: unknown;
};

const { as: Element = 'div', class: className, ...rest } = Astro.props;
---

<Element
class:list={[
'themed-border border-border bg-primary rounded-md shadow-shadow relative text-center',
'themed-border border-border bg-background shadow-shadow relative overflow-visible rounded-md text-center',
className,
]}
{...rest}
>
<slot name="badge" />
<slot />
<div
class="overflow-hidden rounded-[calc(var(--radius,0.375rem)-var(--border-width,2px))]"
>
<slot />
</div>
</Element>
2 changes: 1 addition & 1 deletion packages/website/src/components/Button.astro
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const baseClasses =

const variantClasses = {
default: 'text-foreground bg-primary',
secondary: 'text-foreground bg-background',
secondary: 'text-foreground bg-background-secondary',
text: 'text-foreground bg-transparent border-transparent shadow-none hover:translate-x-0 hover:translate-y-0',
discord:
'text-foreground bg-(--color-discord) prefers-dark:text-foreground prefers-dark:bg-background prefers-dark:border-(--color-discord)',
Expand Down
44 changes: 44 additions & 0 deletions packages/website/src/components/CopyButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import clsx from 'clsx';
import { Check, Copy } from 'lucide-react';
import { ReactNode, useState, type FC } from 'react';

type CopyButtonProps = {
text: string;
className?: string;
children?: ReactNode;
};

const RESET_DELAY = 2000;

export const CopyButton: FC<CopyButtonProps> = ({
text,
className,
children,
}) => {
const [copied, setCopied] = useState(false);

const handleClick = async () => {
await navigator.clipboard.writeText(text);
setCopied(true);
setTimeout(() => setCopied(false), RESET_DELAY);
};

return (
<button
type="button"
onClick={handleClick}
className={clsx(
'inline-flex cursor-pointer flex-row items-center justify-center gap-2 rounded p-1 transition-colors',
className,
)}
aria-label="Copy to clipboard"
>
<span className="text-sm">{children}</span>
{copied ? (
<Check className="h-3.5 w-3.5 text-green-500" />
) : (
<Copy className="h-3.5 w-3.5" />
)}
</button>
);
};
82 changes: 28 additions & 54 deletions packages/website/src/components/Downloads.astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@
import { Icon } from 'astro-icon/components';

import { stars } from '../data/github';
import { version, releaseUrl } from '../data/version';
import { releaseUrl, version } from '../data/version';
import Badge from './Badge.astro';
import Box from './Box.astro';
import { CopyButton } from './CopyButton';

const platforms = [
{
Expand All @@ -25,109 +26,82 @@ const platforms = [
];
---

<Box class="md:hidden px-6 py-5 z-10">
<Box class="z-10 px-6 py-5 md:hidden">
<Badge slot="badge" size="sm">Desktop only</Badge>
<div
class="flex flex-col items-center gap-3 mt-1 pointer-events-none select-none min-[400px]:flex-row min-[400px]:justify-center"
class="pointer-events-none mt-1 flex items-center justify-center gap-2 select-none min-[400px]:gap-3"
>
{
platforms.map((platform) => (
<span class="inline-flex items-center justify-center gap-2 themed-border border-border/40 bg-background-secondary/40 text-foreground/40 rounded-md w-32 py-2 text-sm font-bold">
<Icon name={platform.icon} class="w-4 h-4" />
<span class="themed-border border-border/40 bg-background-secondary/40 text-foreground/40 inline-flex items-center justify-center gap-1.5 rounded-md px-3 py-2 text-xs font-bold min-[400px]:gap-2 min-[400px]:px-4 min-[400px]:text-sm">
<Icon name={platform.icon} class="h-4 w-4" />
{platform.name}
</span>
))
}
</div>
<p class="text-xs mt-3">Downloads are only available on a desktop device.</p>
<p class="mt-3 text-xs">Downloads are only available on a desktop device.</p>
</Box>

<Box as="section" id="downloads" class="hidden md:block px-8 py-6 z-10">
<Box
as="section"
id="downloads"
class="bg-primary z-10 hidden px-8 py-6 md:block"
>
<Badge slot="badge">Downloads</Badge>
<div class="flex justify-center gap-4 mt-2">
<div class="mt-2 flex justify-center gap-3">
{
platforms.map((platform) => (
<a
href={platform.url}
class="themed-border btn-interactive inline-flex items-center justify-center gap-2.5 border-border bg-background-secondary rounded-md w-36 py-3 font-bold shadow-shadow transition-all hover:translate-x-shadow-x hover:translate-y-shadow-y hover:shadow-none"
class="themed-border btn-interactive border-border bg-background-secondary shadow-shadow hover:translate-x-shadow-x hover:translate-y-shadow-y inline-flex items-center justify-center gap-2 rounded-md px-4 py-2.5 text-sm font-bold transition-all hover:shadow-none"
>
<Icon name={platform.icon} class="w-5 h-5" />
<Icon name={platform.icon} class="h-4 w-4" />
{platform.name}
</a>
))
}
</div>
<a
href="https://github.com/nukeop/nuclear/releases"
class="block text-center text-sm font-medium mt-4 underline underline-offset-2 hover:no-underline"
class="mt-4 block text-center text-sm font-medium underline underline-offset-2 hover:no-underline"
>
All downloads
</a>
<div
class="text-xs text-foreground/60 mt-4 max-w-md mx-auto text-center leading-relaxed"
class="text-foreground/60 mx-auto mt-4 max-w-md text-center text-xs leading-relaxed"
>
<p>
<strong class="text-foreground/80">macOS users:</strong> macOS may block Nuclear
from opening. Try right-clicking the app and selecting Open. If that doesn't
work, run this in Terminal:
</p>
<div class="relative mt-1.5 mb-0.5 group">
<div class="relative mt-1.5 mb-0.5">
<code
id="xattr-cmd"
class="block px-2 py-1 pr-8 bg-background-secondary rounded text-[11px] break-all"
class="bg-background-secondary block rounded px-2 py-1 pr-8 text-[11px] break-all"
>sudo xattr -r -d com.apple.quarantine /Applications/Nuclear.app</code
>
<button
id="copy-xattr"
type="button"
class="absolute right-1 top-1/2 -translate-y-1/2 p-1 rounded text-foreground/40 hover:text-foreground/80 transition-colors"
aria-label="Copy command"
>
<svg
class="w-3.5 h-3.5"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"
></path>
</svg>
</button>
<CopyButton
client:load
text="sudo xattr -r -d com.apple.quarantine /Applications/Nuclear.app"
className="absolute top-1/2 right-1 -translate-y-1/2"
/>
</div>
</div>
<script>
document
.getElementById('copy-xattr')
?.addEventListener('click', async () => {
const cmd = document.getElementById('xattr-cmd')?.textContent ?? '';
await navigator.clipboard.writeText(cmd);
const btn = document.getElementById('copy-xattr');
if (btn) {
btn.innerHTML = `<svg class="w-3.5 h-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg>`;
setTimeout(() => {
btn.innerHTML = `<svg class="w-3.5 h-3.5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>`;
}, 2000);
}
});
</script>
</Box>

<div class="relative mt-6 z-10">
<div class="relative z-10 mt-6">
<span
class="absolute -top-3 -right-3 themed-border border-border rounded-md px-2 py-0.5 text-xs font-black rotate-6 pointer-events-none whitespace-nowrap z-20"
class="themed-border border-border pointer-events-none absolute -top-3 -right-3 z-20 rotate-6 rounded-md px-2 py-0.5 text-xs font-black whitespace-nowrap"
style={`color: var(--color-star); background: var(--color-star-bg)`}
>
{stars ?? '17k'} &#9733;
</span>
<a
href="https://github.com/nukeop/nuclear"
class="themed-border btn-interactive inline-flex items-center justify-center gap-2.5 border-border bg-background-secondary rounded-md px-6 py-3 font-bold shadow-shadow transition-all hover:translate-x-shadow-x hover:translate-y-shadow-y hover:shadow-none"
class="themed-border btn-interactive border-border bg-background-secondary shadow-shadow hover:translate-x-shadow-x hover:translate-y-shadow-y inline-flex items-center justify-center gap-2.5 rounded-md px-6 py-3 font-bold transition-all hover:shadow-none"
>
<Icon name="simple-icons:github" class="w-5 h-5" />
<Icon name="simple-icons:github" class="h-5 w-5" />
Learn more
</a>
</div>
87 changes: 87 additions & 0 deletions packages/website/src/components/FAQ.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
---
type FaqItem = {
question: string;
answer: string;
};

const faqItems: FaqItem[] = [
{
question: 'Is Nuclear music player free?',
answer:
'Yes. Nuclear is completely free and open-source, released under the AGPL-3.0 license. There are no ads, no trackers, no premium tier, and no in-app purchases. Anyone can download it, use it, and audit the source code.',
},
{
question: 'What is Nuclear music player?',
answer:
'Nuclear is a free, open-source music player without ads or tracking. Search for any song or artist, build playlists, and start listening. Runs on Windows, macOS, and Linux.',
},
{
question: 'Is Nuclear music player safe?',
answer:
'Yes. Nuclear is open source, so the entire code is publicly auditable on GitHub. Releases are built by GitHub Actions from the public repository. There is no telemetry, no analytics, and no data collection.',
},
{
question: 'Is Nuclear music player available for Android?',
answer:
'No. Nuclear is a desktop application, available for Windows, macOS (Intel and Apple Silicon), and Linux (Flatpak, AUR, AppImage, and others). There is no official Android or iOS version.',
},
{
question: 'How do I install Nuclear?',
answer:
'Download the latest release for your platform from the GitHub releases page. Linux users can also install Nuclear from Flathub, the AUR, or as an AppImage, while Windows users can use winget, and Mac users can use homebrew. Full installation instructions are available in the documentation.',
},
];

const faqJsonLd = {
'@context': 'https://schema.org',
'@type': 'FAQPage',
mainEntity: faqItems.map((item) => ({
'@type': 'Question',
name: item.question,
acceptedAnswer: {
'@type': 'Answer',
text: item.answer,
},
})),
};
---

<section id="faq" class="mx-auto w-full max-w-3xl px-4 py-16">
<h2
class="font-heading mb-8 text-center text-3xl font-black tracking-widest uppercase md:text-4xl"
>
FAQ
</h2>

<div class="relative flex flex-col gap-3">
<img
src="/images/nuki-crawling-out.png"
alt="Nuki peeking out with a magnifying glass"
class="pointer-events-none absolute right-4 bottom-full z-10 -mb-1.5 w-32 select-none md:w-40"
/>
{
faqItems.map((item) => (
<details class="themed-border border-border bg-background-secondary shadow-shadow group rounded-md">
<summary class="font-heading cursor-pointer list-none px-5 py-4 text-base font-bold tracking-wide uppercase select-none [&::-webkit-details-marker]:hidden">
<span class="flex items-center justify-between gap-4">
<span>{item.question}</span>
<span
class="text-primary text-2xl leading-none transition-transform group-open:rotate-45"
aria-hidden="true"
>
+
</span>
</span>
</summary>
<div class="border-border border-t-2 px-5 py-4">
<p class="text-foreground/80 text-sm leading-relaxed">
{item.answer}
</p>
</div>
</details>
))
}
</div>
</section>

<script type="application/ld+json" set:html={JSON.stringify(faqJsonLd)} />
Loading
Loading