dotstate 0.3.3

A modern, secure, and user-friendly dotfile manager built with Rust
Documentation
---
import '../styles/global.css';

interface Props {
    title?: string;
    description?: string;
    pageClass?: string;
}

const {
    title = 'dotstate — a calmer home for the files that configure everything',
    description = 'dotstate is a dotfile manager for people who\'d rather stop thinking about dotfiles. Keeps your shell, editor, and terminal configs synced across every machine — through profiles, inheritance, and one command to sync.',
    pageClass = '',
} = Astro.props;
---

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">

    <title>{title}</title>
    <meta name="title" content={title}>
    <meta name="description" content={description}>
    <meta name="keywords" content="dotfile manager, dotfiles, rust, dotfile sync, github dotfiles, configuration management, dotfile backup, symlink manager, terminal ui, tui, rust cli, dotfile tool, config sync, profile management, dotfile organizer">
    <meta name="author" content="Serkan Yersen">
    <meta name="robots" content="index, follow">
    <meta name="language" content="English">
    <link rel="canonical" href="https://dotstate.serkan.dev">

    <meta property="og:type" content="website">
    <meta property="og:url" content="https://dotstate.serkan.dev">
    <meta property="og:title" content={title}>
    <meta property="og:description" content={description}>
    <meta property="og:image" content="https://dotstate.serkan.dev/og-image.png">
    <meta property="og:site_name" content="dotstate">

    <meta property="twitter:card" content="summary_large_image">
    <meta property="twitter:url" content="https://dotstate.serkan.dev">
    <meta property="twitter:title" content={title}>
    <meta property="twitter:description" content={description}>
    <meta property="twitter:image" content="https://dotstate.serkan.dev/og-image.png">
    <meta property="twitter:creator" content="@serkanyersen">

    <meta name="theme-color" content="#3e5b3c">

    <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><circle cx='50' cy='50' r='40' fill='%233e5b3c'/></svg>">

    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,300;9..144,400;9..144,500&family=Inter+Tight:wght@300;400;500;600&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">

    <script type="application/ld+json">
    {
        "@context": "https://schema.org",
        "@type": "SoftwareApplication",
        "name": "dotstate",
        "applicationCategory": "DeveloperApplication",
        "operatingSystem": "Linux, macOS, Windows",
        "offers": { "@type": "Offer", "price": "0", "priceCurrency": "USD" },
        "description": "A modern, secure dotfile manager built with Rust.",
        "url": "https://dotstate.serkan.dev",
        "downloadUrl": "https://crates.io/crates/dotstate",
        "programmingLanguage": "Rust",
        "license": "https://opensource.org/licenses/MIT",
        "author": { "@type": "Person", "name": "Serkan Yersen" }
    }
    </script>
</head>
<body class={pageClass}>
    <slot />

    <script>
        // Shared scroll-reveal. Fail-safe: content is visible by default; JS adds .pre to hide,
        // then .on to animate in. If anything breaks, content stays visible.
        (function () {
            function init() {
                const els = Array.from(document.querySelectorAll<HTMLElement>('.reveal'));
                if (!els.length) return;
                const reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
                if (reduce) return;
                els.forEach((el) => el.classList.add('pre'));
                if (!('IntersectionObserver' in window)) {
                    els.forEach((el) => el.classList.add('on'));
                    return;
                }
                requestAnimationFrame(() => { requestAnimationFrame(() => {
                    const io = new IntersectionObserver((entries) => {
                        entries.forEach((entry) => {
                            if (entry.isIntersecting) {
                                entry.target.classList.add('on');
                                io.unobserve(entry.target);
                            }
                        });
                    }, { threshold: 0.05, rootMargin: '0px 0px -20px 0px' });
                    els.forEach((el) => io.observe(el));
                }); });
                setTimeout(() => els.forEach((el) => el.classList.add('on')), 2000);
            }
            if (document.readyState === 'loading') {
                document.addEventListener('DOMContentLoaded', init);
            } else {
                init();
            }
        })();
    </script>

    <script>
        import { inject } from '@vercel/analytics';
        import { injectSpeedInsights } from '@vercel/speed-insights';
        inject();
        injectSpeedInsights();
    </script>
</body>
</html>