Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Draft] Homepage refresh #82

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
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
2 changes: 1 addition & 1 deletion docs/concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ This documentation is mostly focused on the javascript implementation of automer

## Core concepts

Using automerge means storing your data in automerge [documents](#Documents). Documents have [URL](#Document-URLs)s which you can use to share or request documents with/from other peers using a [repository](#Repositories). Repositories give you [`DocHandle`](#dochandles)s which you use to make changes to the document and listen for changes from other peers.
Using automerge means storing your data in automerge [documents](#documents). Documents have [URL](#Document-URLs)s which you can use to share or request documents with/from other peers using a [repository](#Repositories). Repositories give you [`DocHandle`](#dochandles)s which you use to make changes to the document and listen for changes from other peers.

Automerge as used in javascript applications is actually a composition of two libraries. [`automerge-repo`](https://www.npmjs.com/package/@automerge/automerge-repo) which provides the networking and storage plumbing, and [`automerge`](https://www.npmjs.com/package/@automerge/automerge) which provides the CRDT implementation, a transport agnostic [sync protocol](#sync-protocol), and a compressed [storage format](#storage-format) which `automerge-repo` uses to implement various networking and storage plugins.

Expand Down
8 changes: 4 additions & 4 deletions docusaurus.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ const darkCodeTheme = require("prism-react-renderer/themes/dracula");

/** @type {import('@docusaurus/types').Config} */
const config = {
title: "Automerge CRDT",
title: "Welcome to Automerge",
tagline:
"Automerge is a library of data structures for building collaborative applications.",
"A cross-platform sync engine for building local-first collaborative apps.",
url: "https://automerge.github.io",
baseUrl: "/",
trailingSlash: true,
Expand All @@ -23,7 +23,7 @@ const config = {
markdown: {
mermaid: true,
},
themes: ["@docusaurus/theme-mermaid"],
themes: ["@docusaurus/theme-mermaid", "@docusaurus/theme-live-codeblock"],

presets: [
[
Expand Down Expand Up @@ -151,7 +151,7 @@ const config = {
{
name: "description",
content:
"Automerge is a library for building collaborative, local-first applications.",
"Automerge is a toolkit for building collaborative, local-first applications.",
},
],
prism: {
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
"write-heading-ids": "docusaurus write-heading-ids"
},
"dependencies": {
"@automerge/automerge-repo-react-hooks": "^1.2.0",
"@docusaurus/core": "3.2.1",
"@docusaurus/preset-classic": "3.2.1",
"@docusaurus/theme-live-codeblock": "3.2.1",
"@docusaurus/theme-mermaid": "3.2.1",
"@fontsource/merriweather": "^5.0.12",
"@fontsource/overpass": "^5.0.17",
Expand All @@ -29,6 +31,7 @@
"clsx": "^1.1.1",
"hast-util-is-element": "1.1.0",
"prism-react-renderer": "^1.2.1",
"raw-loader": "^4.0.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"rehype-katex": "5",
Expand Down
24 changes: 12 additions & 12 deletions src/components/HomepageFeatures.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,35 @@
import React from 'react'
import clsx from 'clsx'
import styles from './HomepageFeatures.module.css'
import Link from '@docusaurus/Link'

const FeatureList = [
{
title: 'Automatic merging',
title: 'Collaboration is effortless',
Svg: require('../../static/img/merge.svg').default,
description: (
<>
Automerge is a Conflict-Free Replicated Data Type (CRDT), which allows concurrent changes on different devices
to be merged automatically without requiring any central server.
The core of Automerge is a <a href="http://crdt.tech" target="_blank">Conflict-free Replicated Data Type (CRDT)</a>, which allows concurrent changes on different devices
to be merged automatically without a central server.
</>
),
},
{
title: 'Network-agnostic',
title: 'Clouds are optional',
Svg: require('../../static/img/network.svg').default,
description: (
<>
Use any connection-oriented network protocol: client-server, peer-to-peer, or local. Or use unidirectional
messaging: send an Automerge file as an email attachment or store it on a file server.
Automerge's sync engine supports any <Link to="docs/repositories/networking">network protocol</Link>: client-server, peer-to-peer, or local. You can even send an <Link to="docs/concepts/#documents">Automerge document</Link> as an email attachment or store it on a file server.
</>
),
},
{
title: 'Portable',
title: 'Code is cross-platform',
Svg: require('../../static/img/portable.svg').default,
description: (
<>
Implemented in <a href="https://github.com/automerge/automerge">JavaScript</a> and{' '}
<a href="https://github.com/automerge/automerge-rs">Rust</a>, with FFI bindings across platforms including iOS,
Electron, Chrome, Safari, Edge, Firefox, and more.
Implemented in <a href="https://github.com/automerge/automerge" target="_blank">JavaScript</a> and{' '}
<a href="https://github.com/automerge/automerge-rs" target="_blank">Rust</a> for maximum portability, Automerge lets you build collaborative multiplayer experiences across web, mobile, and desktop.
</>
),
},
Expand All @@ -50,10 +49,11 @@ function Feature({ Svg, title, description }) {
)
}

export default function HomepageFeatures() {
export default function HomepageFeatures(props) {
return (
<section className={styles.features}>
<section className={clsx(styles.features, props.className || '')}>
<div className="container">
<h2 className="text--center">The Automerge Advantage</h2>
<div className="row">
{FeatureList.map((props, idx) => (
<Feature key={idx} {...props} />
Expand Down
2 changes: 1 addition & 1 deletion src/components/HomepageFeatures.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@
.featureSvg {
height: 200px;
width: 200px;
}
}
35 changes: 35 additions & 0 deletions src/components/Logos.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from 'react'
import GoodnotesBlack from '../../static/img/logos/GoodNotes-Black.svg';
import GoodnotesWhite from '../../static/img/logos/GoodNotes-White.svg';
import DxosBlack from "../../static/img/logos/dxos-logotype-black.svg";
import DxosWhite from "../../static/img/logos/dxos-logotype-white.svg";
import styles from "./Logos.module.css"

export function GoodnotesLogo() {
return <Logo name="Goodnotes" url="https://goodnotes.com" LightSVG={GoodnotesWhite} DarkSVG={GoodnotesBlack} />
}

export function DxosLogo() {
return <Logo
name="DXOS"
url="https://dxos.org"
LightSVG={DxosWhite}
DarkSVG={DxosBlack} />
}

export function Logo({ name, url, LightSVG, DarkSVG }) {
const title = name + " logo";
return (<a href={url} aria-label={name} target="_blank">
<DarkSVG height={"5em"} className={styles.logoDark} alt={title} />
<LightSVG height={"5em"} className={styles.logoLight} alt={title} />
</a>)
}

export default function Logos() {
return (
<div className="container" id="logos" >
<GoodnotesLogo />
<DxosLogo />
</div>
)
}
16 changes: 16 additions & 0 deletions src/components/Logos.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#logos {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}


html[data-theme="light"] .logoLight {
display: none;
}


html[data-theme="dark"] .logoDark {
display: none;
}
59 changes: 59 additions & 0 deletions src/components/RecentPosts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React from 'react'
import Link from '@docusaurus/Link'
import styles from './RecentPosts.module.css'

// Quick & dirty hack to get list of blog posts
// (the "proper" way would be a custom plugin)
import BlogPosts from "../../.docusaurus/docusaurus-plugin-content-blog/default/blog-post-list-prop-default.json"

function getPostDate(permalink) {
const matches = permalink.match(/\d{4}\/\d{2}\/\d{2}/g);
if (matches.length === 1) {
const matchDate = new Date(matches[0]);
return matchDate.toLocaleString('en-us',
{ month: 'short', year: 'numeric' })
} else {
return null
}
}

function BlogDate({ date }) {
return (<span className={styles.date}>
{date}
</span>)
}

function BlogList() {
const posts = BlogPosts.items;
return (<div className={styles.bloglist}>
{posts
.filter(({ unlisted }) => !unlisted)
.slice(0, 3)
.map(({ title, permalink }) => {
const date = getPostDate(permalink);
return (<li key={permalink} className={styles.post}>
{date && <BlogDate date={date} />}
<Link to={permalink} style={{ textDecoration: "none" }}>
{title}
</Link>
</li>)
}
)}

</div>)
}



export default function RecentPosts() {
return (<div>
<h3>Read the latest blog posts</h3>
<BlogList />
<Link to="blog">
More posts
</Link>
</div>)
}



26 changes: 26 additions & 0 deletions src/components/RecentPosts.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.bloglist {
display: flex;
flex-direction: column;
justify-content: left;
gap: .3em;
width: 100%;
padding-bottom: .5em;
}


.date {
opacity: 70%;
text-align: right;
}

.post {
max-width: 60ch;
padding-bottom: .3em;
display: grid;
grid-template-columns: 10ch 1fr;
justify-content: left;
column-gap: 1ch;
width: 100%;
list-style-type: none;
text-align: left;
}
73 changes: 69 additions & 4 deletions src/pages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import Link from '@docusaurus/Link'
import useDocusaurusContext from '@docusaurus/useDocusaurusContext'
import styles from './index.module.css'
import HomepageFeatures from '../components/HomepageFeatures'
import Logos from '../components/Logos'
import RecentPosts from '../components/RecentPosts'
import '@fontsource/merriweather'
import '@fontsource/overpass'

Expand All @@ -13,7 +15,7 @@ function HomepageHeader() {
return (
<header className={clsx('hero hero--secondary', styles.heroBanner)}>
<div className="container">
<h1 className="hero__title">Build local-first software</h1>
<h1 className="hero__title">{siteConfig.title}</h1>
<p className="hero__subtitle">{siteConfig.tagline}</p>
<div className={styles.buttons}>
<Link className="button button--primary button--lg" to="docs/hello">
Expand All @@ -25,16 +27,79 @@ function HomepageHeader() {
)
}


function ResearchProduction() {
return (<section className={styles.homesection}>

<div className="container text--center" style={{ alignSelf: "flex-start" }}>
<h2>Rigorously researched</h2>
<p>Developed at <a href="https://inkandswitch.com">Ink & Switch</a> by the team who started the <a href="https://inkandswitch.com/local-first">local-first software</a> movement, Automerge features industry-leading technology based on years of research.</p>
<Link className="button button--primary button--lg" to="https://inkandswitch.com/local-first">
Read the white paper
</Link>
</div>
<div className="container text--center" style={{ alignSelf: "flex-start" }}>
<h2>Proven in production</h2>
<p>Automerge powers well-known collaboration apps, as well as other developer tools for building local-first apps.</p>
<Logos />

</div>
</section >)

}

function IntroVideo() {
return (<section className={styles.homesection}>
<div className="container text--center">
<h2>See Automerge in Action</h2>
<iframe width="560" height="315" src="https://www.youtube.com/embed/L9fdyDlhByM?si=skxe0RBRA_OXmXgD" title="YouTube video player" allow="accelerometer; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerPolicy="strict-origin-when-cross-origin" allowFullScreen></iframe>
</div>
</section>)
}


function ExternalLinkIcon() {
return (<span style={{ paddingLeft: "1ch" }} ><svg width="13.5" height="13.5" aria-hidden="true" viewBox="0 0 24 24" className="iconExternalLink_nPIU"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"></path></svg></span>)
}


function Community() {
return (<section className={styles.homesection}>
<div className="container text--center">
<h2>Merge with the community</h2>
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", textWrap: "wrap" }}>

<RecentPosts />

<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', textWrap: "nowrap" }}>
<h3>Connect with us</h3>
<p>Join the conversation on <Link to="https://discord.gg/zKGe4DCfgR">Discord<ExternalLinkIcon /></Link></p>
<p>Follow&nbsp;
<Link to="https://github.com/automerge">@automerge on Github<ExternalLinkIcon /></Link>
</p>

<p>Follow&nbsp;
<Link to="https://twitter.com/inkandswitch">@inkandswitch on Twitter<ExternalLinkIcon /></Link>
</p>
</div>
</div>
</div>
</section>)
}

export default function Home() {
const { siteConfig } = useDocusaurusContext()
return (
<Layout
title={`Automerge CRDT`}
description="Automerge is a library of data structures for building collaborative applications."
title={siteConfig.title}
description={siteConfig.description}
>
<HomepageHeader />
<main>
<HomepageFeatures />
<HomepageFeatures className={styles.homesection} />
<ResearchProduction />
<IntroVideo />
<Community />
</main>
</Layout>
)
Expand Down
Loading
Loading