Diffdown is a real-time collaborative Markdown editor/previewer built on the AT Protocol diffdown.com

Tweaks

Logos, meta tags, About copy

+67 -81
docs/assets/16.png

This is a binary file and will not be displayed.

docs/assets/32.png

This is a binary file and will not be displayed.

docs/assets/48.png

This is a binary file and will not be displayed.

docs/assets/64.png

This is a binary file and will not be displayed.

+20 -13
docs/assets/diffdown-logo-inkscape.svg
··· 1 1 <?xml version="1.0" encoding="UTF-8" standalone="no"?> 2 2 <svg 3 - width="420" 4 - height="420" 5 - viewBox="43 84 327.6 327.6" 3 + width="460" 4 + height="460" 5 + viewBox="43 84 358.8 358.8" 6 6 version="1.1" 7 7 id="svg2" 8 8 sodipodi:docname="diffdown-logo-inkscape.svg" 9 9 inkscape:version="1.3.2 (091e20e, 2023-11-25)" 10 - inkscape:export-filename="favicon.png" 10 + inkscape:export-filename="../../static/favicon.svg" 11 11 inkscape:export-xdpi="96" 12 12 inkscape:export-ydpi="96" 13 13 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" ··· 18 18 id="defs2" /> 19 19 <sodipodi:namedview 20 20 id="namedview2" 21 - pagecolor="#ffffff" 21 + pagecolor="#dddddd" 22 22 bordercolor="#000000" 23 23 borderopacity="0.25" 24 24 inkscape:showpageshadow="2" 25 25 inkscape:pageopacity="0.0" 26 26 inkscape:pagecheckerboard="0" 27 27 inkscape:deskcolor="#d1d1d1" 28 - inkscape:zoom="3.42" 29 - inkscape:cx="43.128655" 30 - inkscape:cy="49.853801" 31 - inkscape:window-width="1392" 28 + inkscape:zoom="1" 29 + inkscape:cx="57.5" 30 + inkscape:cy="161.5" 31 + inkscape:window-width="1560" 32 32 inkscape:window-height="1184" 33 - inkscape:window-x="3277" 33 + inkscape:window-x="2998" 34 34 inkscape:window-y="31" 35 35 inkscape:window-maximized="0" 36 - inkscape:current-layer="svg2" 37 - inkscape:export-bgcolor="#ffffffff" /> 36 + inkscape:current-layer="g2" 37 + inkscape:export-bgcolor="#ffffff89" /> 38 38 <g 39 39 id="g2" 40 - transform="matrix(5.7367355,0,0,5.7367355,-259.39616,-458.25129)"> 40 + transform="matrix(5.7367355,0,0,5.7367355,-243.79616,-442.65129)"> 41 + <circle 42 + style="fill:#ffffff;stroke-width:19.1338" 43 + id="path2" 44 + cx="222.39999" 45 + cy="263.39999" 46 + r="179.39999" 47 + transform="matrix(0.17431517,0,0,0.17431517,42.497368,77.160833)" /> 41 48 <text 42 49 style="font-style:normal;font-size:50.8px;line-height:1;font-family:'Inter 18pt';letter-spacing:0px;fill:#2563eb;fill-opacity:1" 43 50 x="52.5"
docs/assets/diffdown.ico

This is a binary file and will not be displayed.

docs/assets/favicon.png

This is a binary file and will not be displayed.

+1 -1
fly-staging.toml
··· 12 12 [http_service] 13 13 internal_port = 8080 14 14 force_https = true 15 - auto_stop_machines = "off" 15 + auto_stop_machines = "suspend" 16 16 auto_start_machines = true 17 17 min_machines_running = 1 18 18
+1 -1
internal/handler/handler.go
··· 152 152 func (h *Handler) Dashboard(w http.ResponseWriter, r *http.Request) { 153 153 user, userHandle := h.currentUser(r) 154 154 if user == nil { 155 - h.render(w, "landing.html", PageData{Title: "Diffdown"}) 155 + h.render(w, "landing.html", PageData{Title: "A Markdown Editor on AT Protocol"}) 156 156 return 157 157 } 158 158
+13 -12
static/css/style.css
··· 1 - @import url(https://fonts.bunny.net/css?family=barlow:100,200,300,400,500,600|lexend:100,200,300,400,500,600,700,800,900); 1 + @import url(https://fonts.bunny.net/css?family=barlow:100,200,300,400,500,600|lexend:100,200,300,400,500,600,700,800,900|albert-sans:100,200,300,400,450,500,600,700,800,900); 2 2 3 3 *, *::before, *::after { 4 4 box-sizing: border-box; ··· 19 19 --code-bg: #f3f4f6; 20 20 --alert-error-bg: #fef2f2; 21 21 --alert-error-border: #fecaca; 22 - --heading-font: "Lexend", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; 22 + --heading-font: "Lexend", -apple-system, Roboto, BlinkMacSystemFont, "Segoe UI", sans-serif; 23 23 --radius: 6px; 24 - --font: "Barlow", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; 24 + --font: "Albert Sans", -apple-system, Roboto, BlinkMacSystemFont, "Segoe UI", sans-serif; 25 25 --font-mono: "JetBrains Mono", "Fira Code", "Cascadia Code", monospace; 26 26 } 27 27 ··· 66 66 line-height: 1.5; 67 67 } 68 68 69 + strong { 70 + font-weight: 500; 71 + } 72 + 69 73 a { color: var(--primary); text-decoration: none; } 70 74 a:hover { text-decoration: underline; } 71 75 72 76 h1, h2, h3, h4, h5 { 73 77 font-family: var(--heading-font); 74 - font-weight: 500; 78 + font-weight: 450; 75 79 margin-top: 1.5rem; 76 80 margin-bottom: 0.5rem; 77 81 } ··· 234 238 } 235 239 236 240 .landing h1, .about-page h1 { font-size: 2.5rem; margin-bottom: 1.25rem; } 237 - .landing p, .about-page p { font-weight: 400; margin-bottom: 1.25rem; } 241 + .landing p, .about-page p { margin-bottom: 1.25rem; } 238 242 .landing-actions { display: flex; gap: 1rem; justify-content: center; } 239 243 240 244 .landing-header, .about-header { ··· 243 247 244 248 .landing-header p { 245 249 text-align: center; 246 - font-size: 1.1rem; 247 - font-weight: 400; 248 - margin-bottom: 1.25rem; 250 + font-size: 1.1rem; margin-bottom: 1.25rem; 249 251 } 250 252 251 253 .about-header { ··· 255 257 .about-header p { 256 258 text-align: left; 257 259 font-size: 1.1rem; 258 - font-weight: 400; 259 260 margin-bottom: 1.25rem; 260 261 } 261 262 ··· 332 333 .repo-list.list-view .repo-card:last-child { border-radius: 0 0 var(--radius) var(--radius); border-bottom: 1px solid var(--border); } 333 334 .repo-list.list-view .repo-card:only-child { border-radius: var(--radius); border-bottom: 1px solid var(--border); } 334 335 335 - .repo-list.list-view .repo-card h3 { margin-bottom: 0; font-size: 1rem; font-weight: 500; } 336 + .repo-list.list-view .repo-card h3 { margin-bottom: 0; font-size: 1rem; font-weight: 450; } 336 337 .repo-list.list-view .repo-card time { margin-top: 0; margin-left: auto; } 337 338 .repo-list.list-view .repo-card:hover { border-color: var(--border); background: color-mix(in srgb, var(--primary) 5%, var(--bg-card)); } 338 339 ··· 369 370 align-items: center; 370 371 padding: 0.75rem 1rem; 371 372 border-bottom: 1px solid var(--border); 372 - font-weight: 600; 373 + font-weight: 550; 373 374 } 374 375 375 376 .file-list { list-style: none; } ··· 418 419 border-bottom: 1px solid var(--border); 419 420 } 420 421 421 - .version-table th { background: var(--bg); font-weight: 600; } 422 + .version-table th { background: var(--bg); font-weight: 550; } 422 423 423 424 .diff-controls { margin-bottom: 1rem; } 424 425
static/favicon.ico

This is a binary file and will not be displayed.

static/favicon.png

This is a binary file and will not be displayed.

+11 -4
static/favicon.svg
··· 1 1 <?xml version="1.0" encoding="UTF-8" standalone="no"?> 2 2 <svg 3 - width="420" 4 - height="420" 5 - viewBox="43 84 327.6 327.6" 3 + width="459.99997" 4 + height="459.99997" 5 + viewBox="43 84 358.79998 358.79998" 6 6 version="1.1" 7 7 id="svg2" 8 8 xmlns="http://www.w3.org/2000/svg" ··· 11 11 id="defs2" /> 12 12 <g 13 13 id="g2" 14 - transform="matrix(5.7367355,0,0,5.7367355,-259.39616,-458.25129)"> 14 + transform="matrix(5.7367355,0,0,5.7367355,-243.79616,-442.65129)"> 15 + <circle 16 + style="fill:#ffffff;stroke-width:19.1338" 17 + id="path2" 18 + cx="222.39999" 19 + cy="263.39999" 20 + r="179.39999" 21 + transform="matrix(0.17431517,0,0,0.17431517,42.497368,77.160833)" /> 15 22 <text 16 23 style="font-style:normal;font-size:50.8px;line-height:1;font-family:'Inter 18pt';letter-spacing:0px;fill:#2563eb;fill-opacity:1" 17 24 x="52.5"
static/favicon/favicon.ico

This is a binary file and will not be displayed.

static/img/favicon.png

This is a binary file and will not be displayed.

-26
static/img/favicon.svg
··· 1 - <?xml version="1.0" encoding="UTF-8" standalone="no"?> 2 - <svg 3 - width="420" 4 - height="420" 5 - viewBox="43 84 327.6 327.6" 6 - version="1.1" 7 - id="svg2" 8 - xmlns="http://www.w3.org/2000/svg" 9 - xmlns:svg="http://www.w3.org/2000/svg"> 10 - <defs 11 - id="defs2" /> 12 - <g 13 - id="g2" 14 - transform="matrix(5.7367355,0,0,5.7367355,-259.39616,-458.25129)"> 15 - <text 16 - style="font-style:normal;font-size:50.8px;line-height:1;font-family:'Inter 18pt';letter-spacing:0px;fill:#2563eb;fill-opacity:1" 17 - x="52.5" 18 - y="141.88016" 19 - id="text1">D</text> 20 - <text 21 - style="font-style:normal;font-size:50.8px;line-height:1;font-family:'Inter 18pt';letter-spacing:0px;fill:#0000ff" 22 - x="78.900238" 23 - y="141.22972" 24 - id="text2">d</text> 25 - </g> 26 - </svg>
+18 -20
templates/about.html
··· 9 9 <h2>What is This?</h2> 10 10 <p>Diffdown is a real-time collaborative <a href="https://www.markdownguide.org/basic-syntax/">Markdown</a> editor/previewer built on <a href="https://atproto.brussels/about-the-atmosphere">AT Protocol</a> (the tech that powers <a href="https://bsky.app">Bluesky</a> and <a href="https://blueskydirectory.com">many other cool apps</a>). 11 11 <p>Diffdown is decentralized; it stores documents as <a href="https://atproto.wiki/en/wiki/reference/data/records">records</a> on the document creator's <a href="https://atproto.wiki/en/wiki/reference/core-architecture/pds">Personal Data Server (PDS)</a>, not on the Diffdown server or a cloud provider. Your data is yours, literally.</p> 12 - <h3>About Me</h3> 13 - <p>I'm a tech tinkerer, co-founder of <a href="https://limeleaf.coop">Limeleaf Worker Collective</a>, and an advisor to a few startups. Read about my journey building Diffdown <a href="https://leaflet.jluther.net">on my Leaflet</a>.</p> 14 - <h2>Contact</h2> 15 - <p>Feedback is welcome! Create an issue in the <a href="https://tangled.org/diffdown.com/diffdown-app/issues">Diffdown repository on Tangled</a>.</p> 16 - </div> 17 - <div class="about-col"> 18 - <h2>Status</h2> 19 - <p>This app is alpha quality. Use at your own risk. Expect bugs, breaking changes, and limited features. However, any documents you create will be stored on your AT Proto PDS, so even if Diffdown goes away, you will still have your documents and comments.</p> 20 - <p><strong class="warning">Important:</strong> Because AT Proto does not support private records (<a href="https://atproto.wiki/en/working-groups/private-data">yet</a>), any documents you create will be visible to the world (not on diffdown.com, but with a PDS record viewer, <a href="https://atproto.at/viewer?uri=did:plc:za4vlvbizdstoym7lpymc5q5/com.diffdown.document/3mhg62vlznz24">see this example</a>).</p> 21 - <p>The app wasn't designed for mobile, so it is likely to be a very bad UX on small screens.</p> 22 - <p>Also, it is running on a free <a href="https://fly.io">fly.io</a> instance, so it may be slow or unavailable at times.</p> 23 - <h3>Roadmap</h3> 24 - <ul> 25 - <li>Export to .md, HTML, PDF</li> 26 - <li>Publish to the ATmosphere (for example, <a href="https://leaflet.pub">Leaflet</a>)</li> 27 - <li>Document versioning</li> 28 - <li>Other ideas? <a href="https://tangled.org/diffdown.com/diffdown-app/issues">Create an issue</a>.</li> 29 - </ul> 30 12 <h3>Technology</h3> 13 + <p>I wrote this app to learn more about AT Protocol and see if I (not a software engineer) could build a useable product with <a href="https://www.ibm.com/think/topics/agentic-engineering">agentic engineering</a>. I used the following:</p> 31 14 <ul> 32 - <li><strong>Backend:</strong> Go, SQLite (only for session management; no user data is stored)</li> 33 - <li><strong>Frontend:</strong> Plain 'ole HTML, CSS, JavaScript</li> 15 + <li><strong>Backend:</strong> Go, SQLite (only for session management; Diffdown sotres <strong>no</strong> user data)</li> 16 + <li><strong>Frontend:</strong> Plain 'ole HTML, CSS, and JavaScript; eventually I will move it to <a href="https://htmx.org">HTMX</a> and <a href="https://tailwindcss.com/">Tailwind CSS</a></li> 34 17 <li><strong>Editor:</strong> <a href="https://prosemirror.net/">ProseMirror</a> with <a href="https://github.com/ProseMirror/prosemirror-collab">prosemirror-collab</a> for real-time collaboration; <a href="https://milkdown.dev/">Milkdown</a> for Markdown parsing and rendering</li> 35 18 <li><strong>Authentication:</strong> <a href="https://atproto.com/guides/auth">ATProto OAuth</a> (any PDS)</li> 36 19 <li><strong>Deployment and hosting:</strong> <a href="https://fly.io">Fly.io</a></li> 20 + <li><strong>LLM assistance:</strong> <a href="https://opencode.ai">OpenCode</a> with <a href="https://www.anthropic.com/news/claude-sonnet-4-6">Claude Sonnet 4.6</a> and <a href="https://www.minimax.io/">Minimax M2.7</a> models via <a href="https://openrouter.ai/">OpenRouter</a></li> 37 21 </ul> 22 + 23 + </div> 24 + <div class="about-col"> 25 + <h2>Status</h2> 26 + <p>As I said, I am not an engineer. 95% of Diffdown was written by LLMs. I am not qualified to assess the security or robustness of the code. Don't use it without a real engineer reviewing it first.</p> 27 + <p>Functionally, the app is alpha quality, at best. Expect bugs, flakiness, breaking changes, and limited features.</p> 28 + <p>The good news is that any documents you create will be stored on your AT Proto PDS, so even if Diffdown goes away, you will still have your documents and comments.</p> 29 + <p><strong class="warning">Important:</strong> Because AT Proto does not support private records (<a href="https://atproto.wiki/en/working-groups/private-data">yet</a>), any documents you create will be visible to the world (not on diffdown.com, but with a PDS record viewer, <a href="https://atproto.at/viewer?uri=did:plc:za4vlvbizdstoym7lpymc5q5/com.diffdown.document/3mhg62vlznz24">see this example</a>).</p> 30 + <p>The app wasn't designed for mobile, so it is likely to be a very bad UX on small screens.</p> 31 + <p>Also, it is running on a free <a href="https://fly.io">fly.io</a> instance, so it may be slow or unavailable at times.</p> 32 + <h2>Contact</h2> 33 + <p>Feedback is welcome! Create an issue, comment on the roadmap, or suggest an improvement in the <a href="https://tangled.org/diffdown.com/diffdown-app/issues">Diffdown repository on Tangled</a>.</p> 34 + 35 + 38 36 </div> 39 37 </section> 40 38 </div>
+3 -3
templates/base.html
··· 9 9 <link rel="apple-touch-icon" href="/favicon/apple-touch-icon.png"> 10 10 <link rel="manifest" href="/favicon/site.webmanifest"> 11 11 <title>{{.Title}} — Diffdown</title> 12 - <meta name="description" content="{{if .Description}}{{.Description}}{{else}}Diffdown — collaborative markdown editing.{{end}}"> 12 + <meta name="description" content="{{if .Description}}{{.Description}}{{else}}Diffdown — collaborative Markdown editing on AT Protocol.{{end}}"> 13 13 <!-- Open Graph --> 14 14 <meta property="og:type" content="website"> 15 15 <meta property="og:site_name" content="Diffdown"> 16 16 <meta property="og:title" content="{{.Title}} — Diffdown"> 17 - <meta property="og:description" content="{{if .Description}}{{.Description}}{{else}}Diffdown — collaborative markdown editing.{{end}}"> 17 + <meta property="og:description" content="{{if .Description}}{{.Description}}{{else}}Diffdown — collaborative Markdown editing on AT Protocol.{{end}}"> 18 18 <meta property="og:image" content="{{if .OGImage}}{{.OGImage}}{{else}}/favicon/android-chrome-512x512.png{{end}}"> 19 19 <!-- Twitter Card --> 20 20 <meta name="twitter:card" content="summary"> 21 21 <meta name="twitter:title" content="{{.Title}} — Diffdown"> 22 - <meta name="twitter:description" content="{{if .Description}}{{.Description}}{{else}}Diffdown — collaborative markdown editing.{{end}}"> 22 + <meta name="twitter:description" content="{{if .Description}}{{.Description}}{{else}}Diffdown — collaborative markdown editing on AT Protocol.{{end}}"> 23 23 <meta name="twitter:image" content="{{if .OGImage}}{{.OGImage}}{{else}}/favicon/android-chrome-512x512.png{{end}}"> 24 24 <link rel="stylesheet" href="/static/css/style.css"> 25 25 {{block "head" .}}{{end}}
-1
templates/landing.html
··· 13 13 <section> 14 14 <p><a href="/about">Diffdown is alpha-quality software</a>.</p> 15 15 </section> 16 - 17 16 </div> 18 17 {{end}}