A CLI for scaffolding ATProto web applications

feat: add vanilla javascript template #3

closed opened by besaid.zone targeting main from feat/add-vanilla-javascript
Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:qttsv4e7pu2jl3ilanfgc3zn/sh.tangled.repo.pull/3mf5ln3xtoo22
+289 -10
Diff #0
+5
.changeset/spotty-buckets-jump.md
··· 1 + --- 2 + "@nulfrost/create-atproto-app": minor 3 + --- 4 + 5 + add vanilla javascript template
-3
README.md
··· 1 1 # create-atproto-app 2 2 3 - > [!WARNING] 4 - > We are still working out the proper setups for all of the templates, there could be breaking changes! 5 - 6 3 This is a thin wrapper on top of `create-vite` that adds specific code for working with the AT Protocol. 7 4 8 5 ## Getting Started
+1
lefthook.yml
··· 2 2 jobs: 3 3 - name: format 4 4 run: pnpm fmt {staged_files} 5 + stage_fixed: true 5 6 exclude: 6 7 - "**/node_modules" 7 8 - "templates/**"
+2
src/commands/init.ts
··· 231 231 return ["README.md", "eslint.config.js", "public", "src", "package.json"]; 232 232 case "svelte-ts": 233 233 return ["README.md", "src", "package.json", "svelte.config.js"]; 234 + case "vanilla": 235 + return ["src"]; 234 236 default: 235 237 return []; 236 238 }
+3 -6
src/constants.ts
··· 24 24 ], 25 25 }, 26 26 { 27 - value: "vanilla", 27 + value: "Vanilla", 28 28 label: "Vanilla", 29 - disabled: true, 30 - hint: "coming soon", 31 29 variants: [ 32 30 { 33 - value: "javascript", 34 - label: "JavaScript (With OAuth)", 35 - docs: "https://github.com/bluesky-social/cookbook/tree/main/vanillajs-oauth-web-app", 31 + value: "vanilla", 32 + label: "JavaScript", 36 33 }, 37 34 ], 38 35 },
+35
templates/vanilla/README.md
··· 1 + # HTML + JavaScript + Vite + AT Protocol 2 + 3 + This is a minimal project with just enough to get you going with developing an AT Protocol web application using the public APIs. 4 + 5 + ## Resources 6 + 7 + - [Introduction to the AT Protocol](https://atproto.com/articles/atproto-ethos) 8 + - [Bluesky Developer Documentation](https://docs.bsky.app) 9 + - [@atproto/lex documentation](https://github.com/bluesky-social/atproto/tree/HEAD/packages/lex/lex#quick-start) 10 + 11 + ## Getting Started 12 + 13 + You'll need to install some lexicons before starting development. In your terminal, run: 14 + 15 + ```bash 16 + npx @atproto/lex install app.bsky.actor.getProfile 17 + ``` 18 + 19 + Then generate the TypeScript files. You can change the destination to where these files are generated but remember to update your ``.gitignore`` file so you don't commit them. 20 + 21 + ```bash 22 + npx @atproto/lex build --out ./src/__generated__ 23 + ``` 24 + 25 + Next, install the package dependencies: 26 + 27 + ```bash 28 + npm install 29 + ``` 30 + 31 + Finally, run the development server: 32 + 33 + ```bash 34 + npm run dev 35 + ```
+20
templates/vanilla/package.json
··· 1 + { 2 + "name": "vanilla", 3 + "private": true, 4 + "version": "0.0.0", 5 + "type": "module", 6 + "scripts": { 7 + "dev": "vite", 8 + "build": "vite build", 9 + "preview": "vite preview", 10 + "update-lexicons": "lex install --update --save", 11 + "postinstall": "lex install --ci", 12 + "prebuild": "lex build --out ./src/__generated__" 13 + }, 14 + "dependencies": { 15 + "@atproto/lex": "^0.0.16" 16 + }, 17 + "devDependencies": { 18 + "vite": "^7.3.1" 19 + } 20 + }
+93
templates/vanilla/src/main.js
··· 1 + import "./style.css"; 2 + import { Client } from "@atproto/lex"; 3 + 4 + import * as app from "./__generated__/app.js"; 5 + 6 + const client = new Client("https://public.api.bsky.app"); 7 + 8 + function escapeHtml(str) { 9 + if (!str) return ""; 10 + return str 11 + .replaceAll("&", "&amp;") 12 + .replaceAll("<", "&lt;") 13 + .replaceAll(">", "&gt;") 14 + .replaceAll('"', "&quot;") 15 + .replaceAll("'", "&#039;"); 16 + } 17 + 18 + function formatStat(stat) { 19 + if (!stat || typeof stat !== "number") { 20 + return; 21 + } 22 + 23 + return new Intl.NumberFormat(window.navigator.language).format(stat); 24 + } 25 + 26 + document.addEventListener("DOMContentLoaded", async () => { 27 + const root = document.querySelector("#app"); 28 + 29 + root.innerHTML = `<p>Loading profile...</p>`; 30 + 31 + // https://atproto.com/specs/did 32 + const people = [ 33 + "did:plc:ragtjsm2j2vknwkz3zp4oxrd", 34 + "did:plc:w4xbfzo7kqfes5zb7r6qv3rw", 35 + "did:plc:vc7f4oafdgxsihk4cry2xpze", 36 + "did:plc:6i6n57nrkq6xavqbdo6bvkqr", 37 + "did:plc:fip3nyk6tjo3senpq4ei2cxw", 38 + "did:plc:yfvwmnlztr4dwkb7hwz55r2g", 39 + ]; 40 + 41 + const randomPerson = Math.floor(Math.random() * people.length); 42 + 43 + const { 44 + avatar, 45 + displayName, 46 + handle, 47 + followersCount, 48 + followsCount, 49 + description, 50 + postsCount, 51 + } = await client.call(app.bsky.actor.getProfile, { 52 + actor: people[randomPerson], 53 + }); 54 + 55 + root.innerHTML = ` 56 + <article class="profile"> 57 + <header class="header"> 58 + <img 59 + src="${escapeHtml(avatar)}" 60 + alt="" 61 + height="100" 62 + width="100" 63 + class="avatar" 64 + /> 65 + <div> 66 + <h2>${escapeHtml(displayName)}</h2> 67 + <p>${escapeHtml(handle)}</p> 68 + </div> 69 + </header> 70 + <section class="stats"> 71 + <p> 72 + <span class="number"> 73 + ${formatStat(followersCount)} 74 + </span> 75 + followers 76 + </p> 77 + <p> 78 + <span class="number"> 79 + ${formatStat(followsCount)} 80 + </span> 81 + following 82 + </p> 83 + <p> 84 + <span class="number"> 85 + ${formatStat(postsCount)} 86 + </span> 87 + posts 88 + </p> 89 + </section> 90 + <footer>${escapeHtml(description)}</footer> 91 + </article> 92 + `; 93 + });
+128
templates/vanilla/src/style.css
··· 1 + @import "https://unpkg.com/open-props/colors.min.css"; 2 + @import "https://unpkg.com/open-props/shadows.min.css"; 3 + 4 + @layer reset { 5 + *, 6 + *::before, 7 + *::after { 8 + box-sizing: border-box; 9 + } 10 + 11 + * { 12 + margin: 0; 13 + padding: 0; 14 + } 15 + 16 + html { 17 + -webkit-font-smoothing: antialiased; 18 + text-rendering: optimizespeed; 19 + text-size-adjust: none; 20 + tab-size: 2; 21 + scrollbar-gutter: stable; 22 + interpolate-size: allow-keywords; 23 + line-height: 1.5; 24 + height: 100%; 25 + background-color: #f8f9fa; 26 + } 27 + 28 + body { 29 + margin: 0; 30 + /* https://systemfontstack.com */ 31 + font-family: 32 + Menlo, 33 + Consolas, 34 + Monaco, 35 + Adwaita Mono, 36 + Liberation Mono, 37 + Lucida Console, 38 + monospace; 39 + font-synthesis: none; 40 + 41 + height: 100%; 42 + display: flex; 43 + justify-content: center; 44 + align-items: center; 45 + } 46 + 47 + ul[role="list"], 48 + ol[role="list"] { 49 + list-style: none; 50 + padding: 0; 51 + } 52 + 53 + ::marker { 54 + line-height: 0; 55 + } 56 + 57 + :focus-visible { 58 + outline-offset: 2px; 59 + } 60 + 61 + @media (prefers-reduced-motion: no-preference) { 62 + html:focus-within { 63 + scroll-behavior: smooth; 64 + } 65 + } 66 + 67 + a { 68 + color: inherit; 69 + text-underline-offset: 0.2ex; 70 + } 71 + 72 + h1, 73 + h2, 74 + h3, 75 + h4 { 76 + text-wrap: balance; 77 + } 78 + 79 + a[href] { 80 + -webkit-tap-highlight-color: transparent; 81 + } 82 + 83 + p, 84 + h1, 85 + h2, 86 + h3, 87 + h4, 88 + h5, 89 + h6 { 90 + overflow-wrap: break-word; 91 + } 92 + 93 + p { 94 + text-wrap: pretty; 95 + } 96 + } 97 + 98 + .avatar { 99 + border-radius: 100%; 100 + border: 1px solid grey; 101 + } 102 + 103 + .header { 104 + display: flex; 105 + align-items: center; 106 + gap: 1rem; 107 + } 108 + 109 + .number { 110 + font-weight: bold; 111 + } 112 + 113 + .stats { 114 + display: flex; 115 + gap: 0.5rem; 116 + } 117 + 118 + .profile { 119 + width: 80ch; 120 + display: flex; 121 + flex-direction: column; 122 + gap: 1rem; 123 + background-color: #ffffff; 124 + border-radius: 0.5rem; 125 + padding: 2rem; 126 + box-shadow: var(--shadow-1); 127 + border: 1px solid var(--stone-0); 128 + }
+2 -1
tsconfig.json
··· 8 8 "skipLibCheck": true, 9 9 "noEmit": true 10 10 }, 11 - "include": ["src", "__tests__"] 11 + "include": ["src", "__tests__"], 12 + "exclude": ["templates/**"] 12 13 }

History

1 round 0 comments
sign up or login to add to the discussion
besaid.zone submitted #0
4 commits
expand
feat: add vanilla javascript template
fix: remove space from vanilla template source folder
chore(templates): try a different escapeHtml implementation
chore: changeset
2/2 success
expand
expand 0 comments
closed without merging