├── src ├── public │ ├── robots.txt │ └── favicon.ico ├── components │ ├── sidebar │ │ ├── SidebarNewPost.vue │ │ ├── SidebarTools.vue │ │ ├── SidebarUserProfile.vue │ │ └── SidebarMenu.vue │ ├── Loading.vue │ ├── post │ │ ├── PostReason.vue │ │ ├── PostText.vue │ │ ├── PostReply.vue │ │ ├── PostHeader.vue │ │ ├── Post.vue │ │ └── PostActions.vue │ ├── Header.vue │ ├── Login.vue │ ├── Timeline.vue │ ├── Notifications.vue │ └── Notification.vue ├── pages │ ├── notifications.vue │ └── index.vue ├── plugins │ └── bsky-agent.ts ├── composables │ └── useRichText.ts ├── stores │ └── session.ts └── app.vue ├── .yarnrc.yml ├── tsconfig.json ├── .gitignore ├── nuxt.config.ts ├── package.json ├── README.md └── .github └── workflows └── deploy.yml /src/public/robots.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/components/sidebar/SidebarNewPost.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/Loading.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | 3 | yarnPath: .yarn/releases/yarn-4.5.1.cjs 4 | -------------------------------------------------------------------------------- /src/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JuanjoSalvador/sunset-bsky/HEAD/src/public/favicon.ico -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // https://nuxt.com/docs/guide/concepts/typescript 3 | "extends": "./.nuxt/tsconfig.json" 4 | } 5 | -------------------------------------------------------------------------------- /src/pages/notifications.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Nuxt dev/build outputs 2 | .output 3 | .data 4 | .nuxt 5 | .nitro 6 | .cache 7 | .yarn 8 | dist 9 | 10 | # Node dependencies 11 | node_modules 12 | 13 | # Logs 14 | logs 15 | *.log 16 | 17 | # Misc 18 | .DS_Store 19 | .fleet 20 | .idea 21 | 22 | # Local env files 23 | .env 24 | .env.* 25 | !.env.example 26 | -------------------------------------------------------------------------------- /src/components/sidebar/SidebarTools.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 16 | 17 | -------------------------------------------------------------------------------- /nuxt.config.ts: -------------------------------------------------------------------------------- 1 | // https://nuxt.com/docs/api/configuration/nuxt-config 2 | export default defineNuxtConfig({ 3 | devtools: { enabled: true }, 4 | css: ['bulma/css/bulma.min.css'], 5 | modules: [ 6 | '@pinia/nuxt', 7 | '@vesp/nuxt-fontawesome' 8 | ], 9 | plugins: [ 10 | { src: '~/plugins/bsky-agent.ts', mode: 'client' }, 11 | ], 12 | fontawesome: { 13 | icons: { 14 | solid: ['repeat', 'heart', 'bell', 'right-from-bracket', 'house', 'reply', 'book-open'], 15 | regular: ['comment', 'heart'] 16 | } 17 | }, 18 | srcDir: 'src', 19 | }) -------------------------------------------------------------------------------- /src/plugins/bsky-agent.ts: -------------------------------------------------------------------------------- 1 | import { BskyAgent } from '@atproto/api' 2 | 3 | import { useSessionStore } from '~/stores/session' 4 | 5 | export default defineNuxtPlugin({ 6 | name: 'bsky-agent', 7 | async setup() { 8 | const sessionStore = useSessionStore() 9 | 10 | const agent = new BskyAgent({ 11 | service: 'https://bsky.social', 12 | persistSession: (_evt, sess) => { 13 | sessionStore.setSession(sess) 14 | }, 15 | }) 16 | 17 | return { 18 | provide: { 19 | bskyAgent: agent, 20 | }, 21 | } 22 | }, 23 | }) -------------------------------------------------------------------------------- /src/pages/index.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 27 | -------------------------------------------------------------------------------- /src/components/post/PostReason.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 15 | 16 | -------------------------------------------------------------------------------- /src/components/post/PostText.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 23 | 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuxt-app", 3 | "private": true, 4 | "type": "module", 5 | "scripts": { 6 | "build": "nuxt build", 7 | "dev": "nuxt dev", 8 | "generate": "nuxt generate", 9 | "preview": "nuxt preview", 10 | "postinstall": "nuxt prepare" 11 | }, 12 | "dependencies": { 13 | "@atproto/api": "^0.13.12", 14 | "@nuxtjs/bulma": "^1.3.0", 15 | "@pinia/nuxt": "^0.5.5", 16 | "@vesp/nuxt-fontawesome": "^1.1.0", 17 | "add": "^2.0.6", 18 | "markdown-it": "^14.1.0", 19 | "module": "^1.2.5", 20 | "nuxi": "^3.15.0", 21 | "nuxt": "^3.13.2", 22 | "vue": "latest", 23 | "vue-router": "latest" 24 | }, 25 | "devDependencies": { 26 | "@fortawesome/free-brands-svg-icons": "^6.6.0", 27 | "@fortawesome/free-regular-svg-icons": "^6.6.0", 28 | "@fortawesome/free-solid-svg-icons": "^6.6.0", 29 | "@types/add": "^2", 30 | "@types/markdown-it": "^14" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/components/post/PostReply.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 26 | 27 | -------------------------------------------------------------------------------- /src/composables/useRichText.ts: -------------------------------------------------------------------------------- 1 | import { RichText } from '@atproto/api' 2 | import MarkdownIt from 'markdown-it' 3 | 4 | export function useRichText(text: string, agent?: any) { 5 | const htmlText = ref('') 6 | 7 | onMounted(async () => { 8 | const rt = new RichText({ text }) 9 | await rt.detectFacets(agent) 10 | 11 | let markdown = '' 12 | for (const segment of rt.segments()) { 13 | if (segment.isMention()) { 14 | // Check if the mention has a valid did (or whatever condition you want to use for validity) 15 | if (segment.mention?.did) { 16 | markdown += `[${segment.text}](https://bsky.app/profile/${segment.mention?.did})` 17 | } 18 | else { 19 | // If not valid, just append the mention text without a link 20 | markdown += segment.text 21 | } 22 | } 23 | else { 24 | markdown += segment.text 25 | } 26 | } 27 | 28 | const md = new MarkdownIt() 29 | htmlText.value = md.render(markdown) 30 | 31 | console.log(htmlText.value) 32 | }) 33 | 34 | return { 35 | htmlText, 36 | } 37 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nuxt Minimal Starter 2 | 3 | Look at the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduction) to learn more. 4 | 5 | ## Setup 6 | 7 | Make sure to install dependencies: 8 | 9 | ```bash 10 | # npm 11 | npm install 12 | 13 | # pnpm 14 | pnpm install 15 | 16 | # yarn 17 | yarn install 18 | 19 | # bun 20 | bun install 21 | ``` 22 | 23 | ## Development Server 24 | 25 | Start the development server on `http://localhost:3000`: 26 | 27 | ```bash 28 | # npm 29 | npm run dev 30 | 31 | # pnpm 32 | pnpm dev 33 | 34 | # yarn 35 | yarn dev 36 | 37 | # bun 38 | bun run dev 39 | ``` 40 | 41 | ## Production 42 | 43 | Build the application for production: 44 | 45 | ```bash 46 | # npm 47 | npm run build 48 | 49 | # pnpm 50 | pnpm build 51 | 52 | # yarn 53 | yarn build 54 | 55 | # bun 56 | bun run build 57 | ``` 58 | 59 | Locally preview production build: 60 | 61 | ```bash 62 | # npm 63 | npm run preview 64 | 65 | # pnpm 66 | pnpm preview 67 | 68 | # yarn 69 | yarn preview 70 | 71 | # bun 72 | bun run preview 73 | ``` 74 | 75 | Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information. 76 | -------------------------------------------------------------------------------- /src/components/post/PostHeader.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 35 | 36 | -------------------------------------------------------------------------------- /src/components/sidebar/SidebarUserProfile.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 34 | 35 | -------------------------------------------------------------------------------- /src/stores/session.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | 3 | function getSessionFromLocalStorage() { 4 | if (typeof window !== 'undefined') 5 | return JSON.parse(localStorage.getItem('session') || 'null') 6 | 7 | return null 8 | } 9 | 10 | function isSessionLoadedFromLocalStorage() { 11 | if (typeof window !== 'undefined') 12 | return !!localStorage.getItem('session') 13 | 14 | return false 15 | } 16 | 17 | export const useSessionStore = defineStore({ 18 | id: 'session', 19 | 20 | // Store state 21 | state: () => ({ 22 | session: getSessionFromLocalStorage(), 23 | isSessionLoaded: isSessionLoadedFromLocalStorage(), 24 | }), 25 | 26 | // Store actions and getters 27 | actions: { 28 | setSession(sessionData: any) { 29 | if (typeof window !== 'undefined') 30 | localStorage.setItem('session', JSON.stringify(sessionData)) 31 | 32 | this.session = sessionData 33 | this.isSessionLoaded = true 34 | }, 35 | 36 | getSession() { 37 | return this.session 38 | }, 39 | 40 | clearSession() { 41 | if (typeof window !== 'undefined') 42 | localStorage.removeItem('session') 43 | 44 | this.session = null 45 | this.isSessionLoaded = false 46 | }, 47 | }, 48 | }) -------------------------------------------------------------------------------- /src/app.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 48 | -------------------------------------------------------------------------------- /src/components/Header.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 46 | 47 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | # https://github.com/actions/deploy-pages#usage 2 | name: Deploy to GitHub Pages 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - main 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | - run: corepack enable 14 | - uses: actions/setup-node@v4 15 | with: 16 | node-version: "18" 17 | # Pick your own package manager and build script 18 | - run: yarn install 19 | - run: npx nuxt build --preset github_pages 20 | - name: Upload artifact 21 | uses: actions/upload-pages-artifact@v3 22 | with: 23 | path: ./.output/public 24 | # Deployment job 25 | deploy: 26 | # Add a dependency to the build job 27 | needs: build 28 | # Grant GITHUB_TOKEN the permissions required to make a Pages deployment 29 | permissions: 30 | pages: write # to deploy to Pages 31 | id-token: write # to verify the deployment originates from an appropriate source 32 | # Deploy to the github_pages environment 33 | environment: 34 | name: github_pages 35 | url: ${{ steps.deployment.outputs.page_url }} 36 | # Specify runner + deployment step 37 | runs-on: ubuntu-latest 38 | steps: 39 | - name: Deploy to GitHub Pages 40 | id: deployment 41 | uses: actions/deploy-pages@v4 42 | -------------------------------------------------------------------------------- /src/components/sidebar/SidebarMenu.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 49 | 50 | -------------------------------------------------------------------------------- /src/components/Login.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 58 | 59 | -------------------------------------------------------------------------------- /src/components/post/Post.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 60 | 61 | -------------------------------------------------------------------------------- /src/components/Timeline.vue: -------------------------------------------------------------------------------- 1 | 75 | 76 | 82 | 83 | -------------------------------------------------------------------------------- /src/components/Notifications.vue: -------------------------------------------------------------------------------- 1 | 79 | 80 | -------------------------------------------------------------------------------- /src/components/post/PostActions.vue: -------------------------------------------------------------------------------- 1 | 2 | 91 | 92 | 114 | 115 | -------------------------------------------------------------------------------- /src/components/Notification.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | --------------------------------------------------------------------------------