website content done

This commit is contained in:
liyunze 2024-03-15 22:21:35 +08:00
parent 3022207283
commit 304c08f132
28 changed files with 1231 additions and 1019 deletions

View file

@ -22,7 +22,7 @@ This guide is intended to help you get started with the coworking streams! Wheth
I am a coworking streamer myself and I also made several open source widgets for the coworking streams.
Please feel free to drop by my <a href="https://www.twitch.tv/RyanPython" target="_blank">stream</a>!
Please feel free to drop by my <a href="https://www.twitch.tv/RythonDev" target="_blank">stream</a>!
---

View file

@ -1,12 +1,13 @@
import { defineConfig } from "astro/config";
import tailwind from "@astrojs/tailwind";
import mdx from "@astrojs/mdx";
import react from "@astrojs/react";
// https://astro.build/config
export default defineConfig({
integrations: [tailwind(), mdx()],
server: {
port: 3000
}
integrations: [tailwind(), mdx(), react()],
server: {
port: 3000,
},
});

284
package-lock.json generated
View file

@ -9,13 +9,23 @@
"version": "2.0.0",
"dependencies": {
"@astrojs/mdx": "^2.1.1",
"@astrojs/react": "^3.1.0",
"@astrojs/tailwind": "^5.1.0",
"@astrojs/ts-plugin": "^1.5.3",
"@types/react": "^18.2.65",
"@types/react-dom": "^18.2.22",
"@vercel/analytics": "^1.0.1",
"astro": "^4.4.1",
"clsx": "^2.1.0",
"framer-motion": "^11.0.12",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.22.3",
"tailwind-merge": "^2.2.1",
"tailwindcss": "^3.4.1"
},
"devDependencies": {
"@types/react-router-dom": "^5.3.3",
"prettier": "^3.2.5",
"prettier-plugin-tailwindcss": "^0.5.11"
}
@ -113,6 +123,24 @@
"node": ">=18.14.1"
}
},
"node_modules/@astrojs/react": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@astrojs/react/-/react-3.1.0.tgz",
"integrity": "sha512-KZhZyV+sUDZEjlrmPWNiPGeYowG9uq6/aMbCgVaeKEBlWT4Kg32TNwBOhZk6AcE4LY1l3mIwt3orUGE2JV96/g==",
"dependencies": {
"@vitejs/plugin-react": "^4.2.0",
"ultrahtml": "^1.3.0"
},
"engines": {
"node": ">=18.14.1"
},
"peerDependencies": {
"@types/react": "^17.0.50 || ^18.0.21",
"@types/react-dom": "^17.0.17 || ^18.0.6",
"react": "^17.0.2 || ^18.0.0",
"react-dom": "^17.0.2 || ^18.0.0"
}
},
"node_modules/@astrojs/tailwind": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/@astrojs/tailwind/-/tailwind-5.1.0.tgz",
@ -459,6 +487,45 @@
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/plugin-transform-react-jsx-self": {
"version": "7.23.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.23.3.tgz",
"integrity": "sha512-qXRvbeKDSfwnlJnanVRp0SfuWE5DQhwQr5xtLBzp56Wabyo+4CMosF6Kfp+eOD/4FYpql64XVJ2W0pVLlJZxOQ==",
"dependencies": {
"@babel/helper-plugin-utils": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/plugin-transform-react-jsx-source": {
"version": "7.23.3",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.23.3.tgz",
"integrity": "sha512-91RS0MDnAWDNvGC6Wio5XYkyWI39FMFO+JK9+4AlgaTH+yWwVTsw7/sn6LK0lH7c5F+TFkpv/3LfCJ1Ydwof/g==",
"dependencies": {
"@babel/helper-plugin-utils": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/runtime": {
"version": "7.24.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.0.tgz",
"integrity": "sha512-Chk32uHMg6TnQdvw2e9IlqPpFX/6NLuK0Ys2PqLb7/gL5uFn9mXvK715FGLlOLQrcO4qIkNHkvPGktzzXexsFw==",
"dependencies": {
"regenerator-runtime": "^0.14.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/template": {
"version": "7.23.9",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.23.9.tgz",
@ -1015,6 +1082,14 @@
"node": ">=14"
}
},
"node_modules/@remix-run/router": {
"version": "1.15.3",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz",
"integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.12.0.tgz",
@ -1245,6 +1320,12 @@
"@types/unist": "*"
}
},
"node_modules/@types/history": {
"version": "4.7.11",
"resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz",
"integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==",
"dev": true
},
"node_modules/@types/mdast": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.3.tgz",
@ -1286,6 +1367,55 @@
"undici-types": "~5.26.4"
}
},
"node_modules/@types/prop-types": {
"version": "15.7.11",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz",
"integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng=="
},
"node_modules/@types/react": {
"version": "18.2.65",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.65.tgz",
"integrity": "sha512-98TsY0aW4jqx/3RqsUXwMDZSWR1Z4CUlJNue8ueS2/wcxZOsz4xmW1X8ieaWVRHcmmQM3R8xVA4XWB3dJnWwDQ==",
"dependencies": {
"@types/prop-types": "*",
"@types/scheduler": "*",
"csstype": "^3.0.2"
}
},
"node_modules/@types/react-dom": {
"version": "18.2.22",
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.22.tgz",
"integrity": "sha512-fHkBXPeNtfvri6gdsMYyW+dW7RXFo6Ad09nLFK0VQWR7yGLai/Cyvyj696gbwYvBnhGtevUG9cET0pmUbMtoPQ==",
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@types/react-router": {
"version": "5.1.20",
"resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz",
"integrity": "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==",
"dev": true,
"dependencies": {
"@types/history": "^4.7.11",
"@types/react": "*"
}
},
"node_modules/@types/react-router-dom": {
"version": "5.3.3",
"resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz",
"integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==",
"dev": true,
"dependencies": {
"@types/history": "^4.7.11",
"@types/react": "*",
"@types/react-router": "*"
}
},
"node_modules/@types/scheduler": {
"version": "0.16.8",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.8.tgz",
"integrity": "sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A=="
},
"node_modules/@types/unist": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.2.tgz",
@ -1301,6 +1431,24 @@
"resolved": "https://registry.npmjs.org/@vercel/analytics/-/analytics-1.0.1.tgz",
"integrity": "sha512-Ux0c9qUfkcPqng3vrR0GTrlQdqNJ2JREn/2ydrVuKwM3RtMfF2mWX31Ijqo1opSjNAq6rK76PwtANw6kl6TAow=="
},
"node_modules/@vitejs/plugin-react": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.2.1.tgz",
"integrity": "sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==",
"dependencies": {
"@babel/core": "^7.23.5",
"@babel/plugin-transform-react-jsx-self": "^7.23.3",
"@babel/plugin-transform-react-jsx-source": "^7.23.3",
"@types/babel__core": "^7.20.5",
"react-refresh": "^0.14.0"
},
"engines": {
"node": "^14.18.0 || >=16.0.0"
},
"peerDependencies": {
"vite": "^4.2.0 || ^5.0.0"
}
},
"node_modules/@volar/language-core": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.0.4.tgz",
@ -2118,6 +2266,11 @@
"node": ">=4"
}
},
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="
},
"node_modules/debug": {
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
@ -2568,6 +2721,30 @@
"url": "https://github.com/sponsors/rawify"
}
},
"node_modules/framer-motion": {
"version": "11.0.12",
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.0.12.tgz",
"integrity": "sha512-1VW4pk+4EH8RwWHdqntWTwF9peranyHn/BczvMzF9TGh/FwMKxoRZzkig8Nak9BQA8GymfIyCGo5adXwRuXDXA==",
"dependencies": {
"tslib": "^2.4.0"
},
"peerDependencies": {
"@emotion/is-prop-valid": "*",
"react": "^18.0.0",
"react-dom": "^18.0.0"
},
"peerDependenciesMeta": {
"@emotion/is-prop-valid": {
"optional": true
},
"react": {
"optional": true
},
"react-dom": {
"optional": true
}
}
},
"node_modules/fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
@ -3425,6 +3602,17 @@
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0"
},
"bin": {
"loose-envify": "cli.js"
}
},
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@ -5451,6 +5639,67 @@
"rc": "cli.js"
}
},
"node_modules/react": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz",
"integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==",
"dependencies": {
"loose-envify": "^1.1.0"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-dom": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz",
"integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==",
"dependencies": {
"loose-envify": "^1.1.0",
"scheduler": "^0.23.0"
},
"peerDependencies": {
"react": "^18.2.0"
}
},
"node_modules/react-refresh": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz",
"integrity": "sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/react-router": {
"version": "6.22.3",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz",
"integrity": "sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==",
"dependencies": {
"@remix-run/router": "1.15.3"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"react": ">=16.8"
}
},
"node_modules/react-router-dom": {
"version": "6.22.3",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz",
"integrity": "sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==",
"dependencies": {
"@remix-run/router": "1.15.3",
"react-router": "6.22.3"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"react": ">=16.8",
"react-dom": ">=16.8"
}
},
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@ -5491,6 +5740,11 @@
"node": ">=8.10.0"
}
},
"node_modules/regenerator-runtime": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
},
"node_modules/rehype": {
"version": "13.0.1",
"resolved": "https://registry.npmjs.org/rehype/-/rehype-13.0.1.tgz",
@ -6125,6 +6379,14 @@
}
]
},
"node_modules/scheduler": {
"version": "0.23.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz",
"integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==",
"dependencies": {
"loose-envify": "^1.1.0"
}
},
"node_modules/section-matter": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/section-matter/-/section-matter-1.0.0.tgz",
@ -6545,6 +6807,18 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/tailwind-merge": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.2.1.tgz",
"integrity": "sha512-o+2GTLkthfa5YUt4JxPfzMIpQzZ3adD1vLVkvKE1Twl9UAhGsEbIZhHHZVRttyW177S8PDJI3bTQNaebyofK3Q==",
"dependencies": {
"@babel/runtime": "^7.23.7"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/dcastil"
}
},
"node_modules/tailwindcss": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz",
@ -6705,6 +6979,11 @@
}
}
},
"node_modules/tslib": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
"integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
},
"node_modules/tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
@ -6728,6 +7007,11 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/ultrahtml": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.5.3.tgz",
"integrity": "sha512-GykOvZwgDWZlTQMtp5jrD4BVL+gNn2NVlVafjcFUJ7taY20tqYdwdoWBFy6GBJsNTZe1GkGPkSl5knQAjtgceg=="
},
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",

View file

@ -11,13 +11,23 @@
},
"dependencies": {
"@astrojs/mdx": "^2.1.1",
"@astrojs/react": "^3.1.0",
"@astrojs/tailwind": "^5.1.0",
"@astrojs/ts-plugin": "^1.5.3",
"@types/react": "^18.2.65",
"@types/react-dom": "^18.2.22",
"@vercel/analytics": "^1.0.1",
"astro": "^4.4.1",
"clsx": "^2.1.0",
"framer-motion": "^11.0.12",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.22.3",
"tailwind-merge": "^2.2.1",
"tailwindcss": "^3.4.1"
},
"devDependencies": {
"@types/react-router-dom": "^5.3.3",
"prettier": "^3.2.5",
"prettier-plugin-tailwindcss": "^0.5.11"
}

View file

@ -1,47 +0,0 @@
---
function timeAgo(date: Date) {
const dateObject = new Date(date);
const seconds = Math.floor(
(new Date().getTime() - dateObject.getTime()) / 1000,
);
let interval = Math.floor(seconds / 31536000);
if (interval >= 1) {
return interval + (interval === 1 ? " year ago" : " years ago");
}
interval = Math.floor(seconds / 2592000);
if (interval >= 1) {
return interval + (interval === 1 ? " month ago" : " months ago");
}
interval = Math.floor(seconds / 86400);
if (interval >= 1) {
return interval + (interval === 1 ? " day ago" : " days ago");
}
interval = Math.floor(seconds / 3600);
if (interval >= 1) {
return interval + (interval === 1 ? " hour ago" : " hours ago");
}
interval = Math.floor(seconds / 60);
if (interval > 1) {
return interval + (interval === 1 ? " minute ago" : " minutes ago");
}
return (
Math.floor(seconds) + (seconds === 1 ? " second ago" : " seconds ago")
);
}
const { title, tagline, pubDate, url } = Astro.props;
---
<a
href={url}
class="hover:no-underline hover:-translate-y-1 shadow-xl transition-all duration-300"
>
<div class="bg-[#303338] text-white
px-5 py-3
rounded-xl">
<h1 class="text-2xl font-bold">{title}</h1>
<p class="text-base">{tagline}</p>
<p class="text-sm text-gray-400">{timeAgo(new Date(pubDate))}</p>
</div>
</a>

View file

@ -0,0 +1,47 @@
---
function timeAgo(date: Date) {
const dateObject = new Date(date);
const seconds = Math.floor(
(new Date().getTime() - dateObject.getTime()) / 1000
);
let interval = Math.floor(seconds / 31536000);
if (interval >= 1) {
return interval + (interval === 1 ? " year ago" : " years ago");
}
interval = Math.floor(seconds / 2592000);
if (interval >= 1) {
return interval + (interval === 1 ? " month ago" : " months ago");
}
interval = Math.floor(seconds / 86400);
if (interval >= 1) {
return interval + (interval === 1 ? " day ago" : " days ago");
}
interval = Math.floor(seconds / 3600);
if (interval >= 1) {
return interval + (interval === 1 ? " hour ago" : " hours ago");
}
interval = Math.floor(seconds / 60);
if (interval > 1) {
return interval + (interval === 1 ? " minute ago" : " minutes ago");
}
return (
Math.floor(seconds) + (seconds === 1 ? " second ago" : " seconds ago")
);
}
const { title, tagline, pubDate, url } = Astro.props;
---
<a
href={url}
class="hover:no-underline hover:-translate-y-1 shadow-xl transition-all duration-300"
>
<div class="bg-[#303338] text-white
px-5 py-3
rounded-xl">
<h1 class="text-2xl font-bold">{title}</h1>
<p class="text-base">{tagline}</p>
<p class="text-sm text-gray-400">{timeAgo(new Date(pubDate))}</p>
</div>
</a>

View file

@ -0,0 +1,52 @@
---
import { HoverEffect } from "../ui/card-hover-effect";
import HeroTitle from "../ui/HeroTitle.astro";
const items = [
{
title: "Introduction",
description:
"What is Coworking? What are study streams? What's the purpose of it?",
link: "/get-started/introduction",
},
{
title: "Tips",
description:
"Learn some tips to enhance your stream and attract more viewers!",
link: "/guide/general-tips",
},
{
title: "Music and Audio",
description:
"Check out non-copyrighted playlists, non-music background audio and many more!",
link: "/guide/music-and-audio",
},
{
title: "Groups",
description:
"Check out some awesome communities related to coworking, studying and self-improvement!",
link: "/groups",
},
{
title: "Blog",
description:
"Take a look at some blog posts regarding the coworking community!",
link: "/blog",
},
{
title: "Credits",
description: "Support the people behind this website! Drop them",
link: "/credits",
},
];
---
<div class="relative w-fit mt-20 z-50 ">
<HeroTitle />
<div class="max-w-5xl mx-auto px-8 pt-10">
<HoverEffect items={items} client:load />
</div>
</div>
<style></style>

View file

@ -0,0 +1,154 @@
---
import VerticalNav from "./verticalNav.astro";
---
<div
transition:persist
transition:name="navbar"
class="w-full backdrop-blur fixed top-0 text-white bg-secondary/75 z-[60] text-lg py-3 border-b border-solid border-gray-800 h-[var(--header-height)]"
>
<div
class="mx-auto relative px-3 lg:px-8 max-w-7xl flex flex-row justify-between items-center gap-3"
>
<!-- Hamburger Icon -->
<div
class="lg:hidden flex items-center fixed z-30"
transition:name="hamburger"
>
<button
id="menu-toggle"
type="button"
class="text-white"
transition:persist
>
<svg
viewBox="0 0 100 80"
width="30"
height="30"
fill="currentColor"
>
<rect width="100" height="15" rx="6" ry="6"></rect>
<rect y="30" width="100" height="15" rx="6" ry="6"></rect>
<rect y="60" width="100" height="15" rx="6" ry="6"></rect>
</svg>
</button>
</div>
<!-- Logo -->
<div
class="flex-1 flex items-center text-xl select-none ml-10 lg:ml-0"
transition:name="cowo-logo"
>
<a
href="/"
class="no-target-blank no-underline text-white hover:no-underline"
>
Coworking <span class="font-bold">Guide</span>
</a>
</div>
<!-- Navigation Links -->
<div class="hidden lg:flex items-center gap-8" id="navbar-links">
<a
class="no-target-blank hover:text-black hover:bg-white rounded-md transition-colors duration-300 px-2 py-1 text-white hover:no-underline"
href="/get-started/introduction">Get started</a
>
<a
class="no-target-blank hover:text-black hover:bg-white rounded-md transition-colors duration-300 px-2 py-1 text-white hover:no-underline"
href="/guide/general-tips">Guide</a
>
<a
class="no-target-blank hover:text-black hover:bg-white rounded-md transition-colors duration-300 px-2 py-1 text-white hover:no-underline"
href="/groups">Groups</a
>
<a
class="no-target-blank hover:text-black hover:bg-white rounded-md transition-colors duration-300 px-2 py-1 text-white hover:no-underline"
href="/blog">Blog</a
>
<a
class="no-target-blank hover:text-black hover:bg-white rounded-md transition-colors duration-300 px-2 py-1 text-white hover:no-underline"
href="/credits">Credits</a
>
</div>
<!-- Theme Toggle (COMING SOON) -->
<div class="flex lg:flex-1 justify-end">
<!-- <button id="theme-toggle" type="button" class=""></button> -->
</div>
</div>
</div>
<aside
id="vertical-navbar"
class="fixed block overflow-y-auto -translate-x-[200%] transition-transform duration-300 z-30 lg:hidden bg-[#151515] py-8 px-8 h-[calc(100vh-var(--header-height))] top-[--header-height]"
transition:persist="vertical-nav-from-left"
>
<VerticalNav showHome={true} />
</aside>
<script>
// function updateDarkModeIcon() {
// const darkModeToggle = document.querySelector("#theme-toggle");
// const isDarkMode = localStorage.getItem("theme") === "dark";
// const moonSVG = `
// <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6">
// <path fill-rule="evenodd" d="M9.528 1.718a.75.75 0 0 1 .162.819A8.97 8.97 0 0 0 9 6a9 9 0 0 0 9 9 8.97 8.97 0 0 0 3.463-.69.75.75 0 0 1 .981.98 10.503 10.503 0 0 1-9.694 6.46c-5.799 0-10.5-4.7-10.5-10.5 0-4.368 2.667-8.112 6.46-9.694a.75.75 0 0 1 .818.162Z" clip-rule="evenodd" />
// </svg>
// `;
// const sunSVG = `
// <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6">
// <path d="M12 2.25a.75.75 0 0 1 .75.75v2.25a.75.75 0 0 1-1.5 0V3a.75.75 0 0 1 .75-.75ZM7.5 12a4.5 4.5 0 1 1 9 0 4.5 4.5 0 0 1-9 0ZM18.894 6.166a.75.75 0 0 0-1.06-1.06l-1.591 1.59a.75.75 0 1 0 1.06 1.061l1.591-1.59ZM21.75 12a.75.75 0 0 1-.75.75h-2.25a.75.75 0 0 1 0-1.5H21a.75.75 0 0 1 .75.75ZM17.834 18.894a.75.75 0 0 0 1.06-1.06l-1.59-1.591a.75.75 0 1 0-1.061 1.06l1.59 1.591ZM12 18a.75.75 0 0 1 .75.75V21a.75.75 0 0 1-1.5 0v-2.25A.75.75 0 0 1 12 18ZM7.758 17.303a.75.75 0 0 0-1.061-1.06l-1.591 1.59a.75.75 0 0 0 1.06 1.061l1.591-1.59ZM6 12a.75.75 0 0 1-.75.75H3a.75.75 0 0 1 0-1.5h2.25A.75.75 0 0 1 6 12ZM6.697 7.757a.75.75 0 0 0 1.06-1.06l-1.59-1.591a.75.75 0 0 0-1.061 1.06l1.59 1.591Z" />
// </svg>
// `;
// if (darkModeToggle) {
// if (isDarkMode) {
// darkModeToggle.innerHTML = moonSVG;
// } else {
// darkModeToggle.innerHTML = sunSVG;
// }
// }
// }
// const darkModeToggle = document.querySelector("#theme-toggle");
// if (!localStorage.getItem("theme")) {
// localStorage.setItem("theme", "dark");
// }
// if (darkModeToggle) {
// darkModeToggle.addEventListener("click", () => {
// const isDarkMode = localStorage.getItem("theme") === "dark";
// if (isDarkMode) {
// localStorage.setItem("theme", "light");
// document.documentElement.classList.remove("dark");
// } else {
// localStorage.setItem("theme", "dark");
// document.documentElement.classList.add("dark");
// }
// updateDarkModeIcon();
// });
// }
// JavaScript for toggling the menu
document.addEventListener("DOMContentLoaded", function () {
const menuToggle = document.querySelector("#menu-toggle");
const verticalNavBar = document.querySelector("#vertical-navbar");
if (menuToggle) {
menuToggle.addEventListener("click", function () {
if (verticalNavBar)
verticalNavBar.classList.toggle("-translate-x-[200%]");
});
}
// Close the menu when a link is clicked
const navbarLinks = document.querySelectorAll("#vertical-navbar a");
navbarLinks.forEach((link) => {
link.addEventListener("click", function () {
if (verticalNavBar)
verticalNavBar.classList.add("-translate-x-[200%]");
});
});
});
// window.onload = function () {
// updateDarkModeIcon();
// };
</script>

View file

@ -0,0 +1,154 @@
---
type Navigation = Record<
string,
{ title: string; url: string; order: number }[]
>;
const currentUrl = Astro.request.url;
const isCurrentUrl = (url: string) => currentUrl.includes(url);
export function formatTitle(title: string): string {
return title
.split("-")
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(" ");
}
export async function setup() {
// setup navigation
const getStartedFiles = await Astro.glob("../../pages/get-started/*.md");
const guideFiles = await Astro.glob("../../pages/guide/*.md");
const markdownFiles = [...getStartedFiles, ...guideFiles];
const navigation: Navigation = {};
for (const file of markdownFiles) {
const filePath = file.url;
const [_, category] = filePath!.split("/");
const formattedCategory = formatTitle(category);
const formattedTitle = file.frontmatter.title;
const order = file.frontmatter.order ?? 0;
if (!navigation[formattedCategory]) {
navigation[formattedCategory] = [];
}
navigation[formattedCategory].push({
title: formattedTitle,
url: filePath || "",
order,
});
}
// sort by "order"
const sortedNavigation: Navigation = {};
Object.keys(navigation)
.sort()
.forEach((key) => {
sortedNavigation[key] = navigation[key].sort(
(a, b) => a.order - b.order
);
});
return {
navigation: sortedNavigation,
};
}
interface Props {
showHome?: boolean | null;
}
const { navigation } = await setup();
const { showHome } = Astro.props;
---
<div class="relative">
<nav id="vertical-nav-component" class="flex flex-col">
{
showHome && (
<div class="mb-2">
<a
href="/"
class={`no-target-blank hover:no-underline hover:text-white text-lg mt-0
${isCurrentUrl("/") ? "text-white" : "text-gray-400"}`}
>
Home
</a>
</div>
)
}
{
Object.entries(navigation).map(([category, links]) => (
<div class="mb-2">
<div class="category no-target-blank text-lg font-bold mb-1 mt-0 text-white">
{category}
</div>
<ul class="list-disc list-outside font-normal">
{links.map((link) => (
<li class="mb-1 pl-0">
<a
href={link.url}
class={`no-target-blank transition-colors duration-150 hover:no-underline hover:text-white ${isCurrentUrl(link.url) ? "text-white" : "text-gray-400"}`}
>
{link.title}
</a>
</li>
))}
</ul>
</div>
))
}
<div class="mb-2">
<a
href="/groups"
class={`no-target-blank transition-colors duration-150 hover:no-underline hover:text-white text-lg mt-0
${isCurrentUrl("/groups") ? "text-white" : "text-gray-400"}`}
>
Groups
</a>
</div>
<div class="mb-2">
<a
href="/blog"
class={`no-target-blank transition-colors duration-150 hover:no-underline hover:text-white text-lg mt-0
${isCurrentUrl("/blog") ? "text-white" : "text-gray-400"}`}
>
Blog
</a>
</div>
<div class="mb-2">
<a
href="/credits"
class={`no-target-blank transition-colors duration-150 hover:no-underline hover:text-white text-lg mt-0
${isCurrentUrl("/credits") ? "text-white" : "text-gray-400"}`}
>
Credits
</a>
</div>
</nav>
</div>
<script>
document.addEventListener("astro:page-load", () => {
const currentUrl = window.location.pathname;
const isCurrentUrl = (url: string) => currentUrl === url;
const links = document.querySelectorAll(
"#vertical-nav-component a:not(.category), #vertical-nav-component a > a:not(.category)"
);
// add active class to current url
links.forEach((link) => {
if (isCurrentUrl(link.getAttribute("href")!)) {
link.classList.add("text-white");
link.classList.remove("text-gray-400");
} else {
link.classList.remove("text-white");
link.classList.add("text-gray-400");
}
});
});
</script>

View file

@ -1,230 +0,0 @@
<!-- <div
transition:persist
transition:name="navbar"
class="w-full fixed text-white bg-secondary z-50 text-lg py-3 font-['Inter'] border-b border-solid border-gray-800 h-[--header-height]"
>
<div
class="mx-auto relative px-3 lg:px-8 max-w-7xl flex flex-row justify-between items-center gap-3"
>
<div
class="lg:flex-1 flex items-center text-xl select-none"
transition:name="cowo-logo"
>
<a
href="/"
class="no-target-blank no-underline text-white hover:no-underline"
>
Coworking <span class="font-bold">Guide</span>
</a>
</div>
<div class="flex items-center gap-10">
<a
class="no-target-blank hover:underline text-white"
href="/get-started/introduction">Get started</a
>
<a
class="no-target-blank hover:underline text-white"
href="/guide/general-tips">Guide</a
>
<a class="no-target-blank hover:underline text-white" href="/groups"
>Groups</a
>
<a class="no-target-blank hover:underline text-white" href="/blog"
>Blog</a
>
<a
class="no-target-blank hover:underline text-white"
href="/credits">Credits</a
>
</div>
<div
class="flex lg:flex-1 justify-end"
transition:name="display-toggle"
>
<button id="theme-toggle" type="button" class="">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
fill="currentColor"
class="w-6 h-6"
>
<path
fill-rule="evenodd"
d="M9.528 1.718a.75.75 0 0 1 .162.819A8.97 8.97 0 0 0 9 6a9 9 0 0 0 9 9 8.97 8.97 0 0 0 3.463-.69.75.75 0 0 1 .981.98 10.503 10.503 0 0 1-9.694 6.46c-5.799 0-10.5-4.7-10.5-10.5 0-4.368 2.667-8.112 6.46-9.694a.75.75 0 0 1 .818.162Z"
clip-rule="evenodd"></path>
</svg>
</button>
</div>
</div>
</div>
<script defer>
function updateDarkModeIcon() {
const darkModeToggle = document.querySelector("#theme-toggle");
const isDarkMode = localStorage.getItem("theme") === "dark";
const moonSVG = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6">
<path fill-rule="evenodd" d="M9.528 1.718a.75.75 0 0 1 .162.819A8.97 8.97 0 0 0 9 6a9 9 0 0 0 9 9 8.97 8.97 0 0 0 3.463-.69.75.75 0 0 1 .981.98 10.503 10.503 0 0 1-9.694 6.46c-5.799 0-10.5-4.7-10.5-10.5 0-4.368 2.667-8.112 6.46-9.694a.75.75 0 0 1 .818.162Z" clip-rule="evenodd" />
</svg>
`;
const sunSVG = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6">
<path d="M12 2.25a.75.75 0 0 1 .75.75v2.25a.75.75 0 0 1-1.5 0V3a.75.75 0 0 1 .75-.75ZM7.5 12a4.5 4.5 0 1 1 9 0 4.5 4.5 0 0 1-9 0ZM18.894 6.166a.75.75 0 0 0-1.06-1.06l-1.591 1.59a.75.75 0 1 0 1.06 1.061l1.591-1.59ZM21.75 12a.75.75 0 0 1-.75.75h-2.25a.75.75 0 0 1 0-1.5H21a.75.75 0 0 1 .75.75ZM17.834 18.894a.75.75 0 0 0 1.06-1.06l-1.59-1.591a.75.75 0 1 0-1.061 1.06l1.59 1.591ZM12 18a.75.75 0 0 1 .75.75V21a.75.75 0 0 1-1.5 0v-2.25A.75.75 0 0 1 12 18ZM7.758 17.303a.75.75 0 0 0-1.061-1.06l-1.591 1.59a.75.75 0 0 0 1.06 1.061l1.591-1.59ZM6 12a.75.75 0 0 1-.75.75H3a.75.75 0 0 1 0-1.5h2.25A.75.75 0 0 1 6 12ZM6.697 7.757a.75.75 0 0 0 1.06-1.06l-1.59-1.591a.75.75 0 0 0-1.061 1.06l1.59 1.591Z" />
</svg>
`;
if (darkModeToggle) {
if (isDarkMode) {
darkModeToggle.innerHTML = moonSVG;
} else {
darkModeToggle.innerHTML = sunSVG;
}
}
}
const darkModeToggle = document.querySelector("#theme-toggle");
if (!localStorage.getItem("theme")) {
localStorage.setItem("theme", "dark");
}
if (darkModeToggle) {
darkModeToggle.addEventListener("click", () => {
const isDarkMode = localStorage.getItem("theme") === "dark";
if (isDarkMode) {
localStorage.setItem("theme", "light");
document.documentElement.classList.remove("dark");
} else {
localStorage.setItem("theme", "dark");
document.documentElement.classList.add("dark");
}
updateDarkModeIcon();
});
}
window.onload = function () {
updateDarkModeIcon();
};
</script> -->
<div
transition:persist
transition:name="navbar"
class="w-full fixed text-white bg-secondary z-50 text-lg py-3 font-['Inter'] border-b border-solid border-gray-800 h-[--header-height]"
>
<div
class="mx-auto relative px-3 lg:px-8 max-w-7xl flex flex-row justify-between items-center gap-3"
>
<!-- Hamburger Icon -->
<div class="lg:hidden flex items-center fixed">
<button id="menu-toggle" type="button" class="text-white">
<svg
viewBox="0 0 100 80"
width="30"
height="30"
fill="currentColor"
>
<rect width="100" height="15" rx="6" ry="6"></rect>
<rect y="30" width="100" height="15" rx="6" ry="6"></rect>
<rect y="60" width="100" height="15" rx="6" ry="6"></rect>
</svg>
</button>
</div>
<!-- Logo -->
<div
class="flex-1 flex items-center text-xl select-none ml-10 lg:ml-0"
transition:name="cowo-logo"
>
<a
href="/"
class="no-target-blank no-underline text-white hover:no-underline"
>
Coworking <span class="font-bold">Guide</span>
</a>
</div>
<!-- Navigation Links -->
<div class="hidden lg:flex items-center gap-10" id="navbar-links">
<a
class="no-target-blank hover:underline text-white"
href="/get-started/introduction">Get started</a
>
<a
class="no-target-blank hover:underline text-white"
href="/guide/general-tips">Guide</a
>
<a class="no-target-blank hover:underline text-white" href="/groups"
>Groups</a
>
<a class="no-target-blank hover:underline text-white" href="/blog"
>Blog</a
>
<a
class="no-target-blank hover:underline text-white"
href="/credits">Credits</a
>
</div>
<!-- Theme Toggle (COMING SOON) -->
<div class="flex lg:flex-1 justify-end">
<!-- <button id="theme-toggle" type="button" class=""></button> -->
</div>
</div>
</div>
<script defer>
// JavaScript for toggling the menu
document.addEventListener("DOMContentLoaded", function () {
const menuToggle = document.querySelector("#menu-toggle");
const navLinks = document.querySelector("#vertical-navbar");
menuToggle.addEventListener("click", function () {
navLinks.classList.toggle("hidden");
});
});
function updateDarkModeIcon() {
const darkModeToggle = document.querySelector("#theme-toggle");
const isDarkMode = localStorage.getItem("theme") === "dark";
const moonSVG = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6">
<path fill-rule="evenodd" d="M9.528 1.718a.75.75 0 0 1 .162.819A8.97 8.97 0 0 0 9 6a9 9 0 0 0 9 9 8.97 8.97 0 0 0 3.463-.69.75.75 0 0 1 .981.98 10.503 10.503 0 0 1-9.694 6.46c-5.799 0-10.5-4.7-10.5-10.5 0-4.368 2.667-8.112 6.46-9.694a.75.75 0 0 1 .818.162Z" clip-rule="evenodd" />
</svg>
`;
const sunSVG = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6">
<path d="M12 2.25a.75.75 0 0 1 .75.75v2.25a.75.75 0 0 1-1.5 0V3a.75.75 0 0 1 .75-.75ZM7.5 12a4.5 4.5 0 1 1 9 0 4.5 4.5 0 0 1-9 0ZM18.894 6.166a.75.75 0 0 0-1.06-1.06l-1.591 1.59a.75.75 0 1 0 1.06 1.061l1.591-1.59ZM21.75 12a.75.75 0 0 1-.75.75h-2.25a.75.75 0 0 1 0-1.5H21a.75.75 0 0 1 .75.75ZM17.834 18.894a.75.75 0 0 0 1.06-1.06l-1.59-1.591a.75.75 0 1 0-1.061 1.06l1.59 1.591ZM12 18a.75.75 0 0 1 .75.75V21a.75.75 0 0 1-1.5 0v-2.25A.75.75 0 0 1 12 18ZM7.758 17.303a.75.75 0 0 0-1.061-1.06l-1.591 1.59a.75.75 0 0 0 1.06 1.061l1.591-1.59ZM6 12a.75.75 0 0 1-.75.75H3a.75.75 0 0 1 0-1.5h2.25A.75.75 0 0 1 6 12ZM6.697 7.757a.75.75 0 0 0 1.06-1.06l-1.59-1.591a.75.75 0 0 0-1.061 1.06l1.59 1.591Z" />
</svg>
`;
if (darkModeToggle) {
if (isDarkMode) {
darkModeToggle.innerHTML = moonSVG;
} else {
darkModeToggle.innerHTML = sunSVG;
}
}
}
const darkModeToggle = document.querySelector("#theme-toggle");
if (!localStorage.getItem("theme")) {
localStorage.setItem("theme", "dark");
}
if (darkModeToggle) {
darkModeToggle.addEventListener("click", () => {
const isDarkMode = localStorage.getItem("theme") === "dark";
if (isDarkMode) {
localStorage.setItem("theme", "light");
document.documentElement.classList.remove("dark");
} else {
localStorage.setItem("theme", "dark");
document.documentElement.classList.add("dark");
}
updateDarkModeIcon();
});
}
window.onload = function () {
updateDarkModeIcon();
};
</script>

View file

@ -0,0 +1,26 @@
<div
class="bg-[white] h-[34vmax] aspect-square absolute rounded-full bg-[linear-gradient(to_right,#FF7782,_mediumpurple)] animate-[rotate_20s_infinite] opacity-80 origin-center left-1/2 top-1/2"
>
</div>
<div
class="h-full flex-grow w-full absolute backdrop-filter backdrop-blur-[12vmax]"
>
</div>
<style>
@keyframes rotate {
from {
rotate: 0deg;
translate: -50% -50%;
}
50% {
scale: 1 1.5;
}
to {
rotate: 360deg;
translate: -50% -50%;
}
}
</style>

View file

@ -0,0 +1,15 @@
<div class="flex flex-col">
<div class="text-center text-4xl lg:text-6xl font-bold">
<span class="text-slate-200">Getting started with</span>
<div
class="bg-clip-text text-transparent bg-gradient-pink-cyan animate-gradient py-1 lg:py-3 bg-[length:300%]"
>
Coworking streams
</div>
</div>
<div
class="mt-2 text-xl text-center flex flex-col justify-center items-center"
>
Brought to you by RythonDev x Coworking Crew
</div>
</div>

View file

@ -0,0 +1,115 @@
import { cn } from "../../../utils/cn";
import { AnimatePresence, motion } from "framer-motion";
import { useState } from "react";
export const HoverEffect = ({
items,
className,
}: {
items: {
title: string;
description: string;
link: string;
}[];
className?: string;
}) => {
let [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
return (
<div
className={cn(
"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3",
className,
)}
>
{items.map((item, idx) => (
<a
href={item?.link}
key={item?.link}
className="no-target-blank group relative block h-full w-full p-2 hover:no-underline"
onMouseEnter={() => setHoveredIndex(idx)}
onMouseLeave={() => setHoveredIndex(null)}
>
<AnimatePresence>
{hoveredIndex === idx && (
<motion.span
className="absolute inset-0 block h-full w-full rounded-3xl bg-neutral-200 dark:bg-cowo-pink"
layoutId="hoverBackground"
initial={{ opacity: 0 }}
animate={{
opacity: 1,
transition: { duration: 0.15 },
}}
exit={{
opacity: 0,
transition: { duration: 0.15, delay: 0.2 },
}}
/>
)}
</AnimatePresence>
<Card>
<CardTitle>{item.title}</CardTitle>
<CardDescription>{item.description}</CardDescription>
</Card>
</a>
))}
</div>
);
};
export const Card = ({
className,
children,
}: {
className?: string;
children: React.ReactNode;
}) => {
return (
<div
className={cn(
"relative z-20 h-full w-full overflow-hidden rounded-2xl border border-transparent bg-[#232323] p-4 group-hover:border-slate-700 dark:border-white/[0.2]",
className,
)}
>
<div className="relative z-50">
<div className="p-4">{children}</div>
</div>
</div>
);
};
export const CardTitle = ({
className,
children,
}: {
className?: string;
children: React.ReactNode;
}) => {
return (
<h4
className={cn(
"mt-4 font-bold tracking-wide text-zinc-100",
className,
)}
>
{children}
</h4>
);
};
export const CardDescription = ({
className,
children,
}: {
className?: string;
children: React.ReactNode;
}) => {
return (
<p
className={cn(
"mt-5 text-sm leading-relaxed tracking-wide text-zinc-400",
className,
)}
>
{children}
</p>
);
};

View file

@ -1,72 +0,0 @@
---
type Navigation = Record<
string,
{ title: string; url: string; order: number }[]
>;
const navigation = Astro.props.navigation as Navigation;
const currentUrl = Astro.request.url;
const isCurrentUrl = (url: string) => currentUrl.includes(url);
---
<aside
id="vertical-nav"
class="hidden -translate-x-full lg:translate-x-0 overflow-y-auto lg:block lg:sticky py-8 lg:px-4 lg:-mx-4 lg:max-h-[calc(100vh-var(--header-height))] lg:top-[--header-height]"
>
<div class="relative">
<nav class="flex flex-col">
{
Object.entries(navigation).map(([category, links]) => (
<div class="mb-2">
<a
class="no-target-blank text-lg font-bold mb-1 mt-0 text-white"
href={links[0].url}
>
{category}
</a>
<ul class="list-disc list-outside font-normal">
{links.map((link) => (
<li class="mb-1 pl-0">
<a
href={link.url}
class={`no-target-blank hover:underline ${isCurrentUrl(link.url) ? "text-blue-400" : "text-white"}`}
>
{link.title}
</a>
</li>
))}
</ul>
</div>
))
}
<div class="mb-2">
<a
href="/groups"
class={`no-target-blank hover:underline text-lg font-bold mt-0
${isCurrentUrl("/groups") ? "text-blue-400" : "text-white"}`}
>
Groups
</a>
</div>
<div class="mb-2">
<a
href="/blog"
class={`no-target-blank hover:underline text-lg font-bold mt-0
${isCurrentUrl("/blog") ? "text-blue-400" : "text-white"}`}
>
Blog
</a>
</div>
<div class="mb-2">
<a
href="/credits"
class={`no-target-blank hover:underline text-lg font-bold mt-0
${isCurrentUrl("/credits") ? "text-blue-400" : "text-white"}`}
>
Credits
</a>
</div>
</nav>
</div>
</aside>

View file

@ -104,6 +104,10 @@ Here are the listed pros and cons of using the "Coworking" category, which may o
The purpose of this post is not to persuade people to "pick a side", rather it is meant to inform and show the bigger picture of this "Coworking" category. Please do not pressure nor target any streamers regarding this topic, and don't forget to [VOTE for an official Coworking and Study category](https://twitch.uservoice.com/forums/310210-discover/suggestions/46004791-coworking).
## Edit:
As of 15th March 2024, the "Coworking" game category has been removed from IGDB and Twitch.
---
by [RythonDev](https://twitch.tv/RythonDev)

View file

@ -1,156 +1,35 @@
---
import Layout from "./Layout.astro";
import TableOfContent from "../components/TableOfContent.astro";
import VerticalNav from "../components/verticalNav.astro";
import TableOfContent from "../components/blocks/TableOfContent.astro";
import VerticalNav from "../components/blocks/verticalNav.astro";
type Navigation = Record<
string,
{ title: string; url: string; order: number }[]
>;
export function formatTitle(title: string): string {
return title
.split("-")
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(" ");
}
export async function setup() {
// setup navigation
const getStartedFiles = await Astro.glob("../pages/get-started/*.md");
const guideFiles = await Astro.glob("../pages/guide/*.md");
const markdownFiles = [...getStartedFiles, ...guideFiles];
const navigation: Navigation = {};
for (const file of markdownFiles) {
const filePath = file.url;
const [_, category] = filePath!.split("/");
const formattedCategory = formatTitle(category);
const formattedTitle = file.frontmatter.title;
const order = file.frontmatter.order ?? 0;
if (!navigation[formattedCategory]) {
navigation[formattedCategory] = [];
}
navigation[formattedCategory].push({
title: formattedTitle,
url: filePath || "",
order,
});
}
// sort by "order"
const sortedNavigation: Navigation = {};
Object.keys(navigation)
.sort()
.forEach((key) => {
sortedNavigation[key] = navigation[key].sort(
(a, b) => a.order - b.order,
);
});
return {
navigation: sortedNavigation,
};
}
const { frontmatter, headings } = Astro.props;
const { navigation } = await setup();
---
<Layout title={frontmatter.heading} description={frontmatter.subheading}>
<div class="pt-14 mx-auto px-4 sm:px-6 max-w-7xl">
<div class="flex flex-col lg:grid lg:grid-cols-10">
<!-- vertical navbar -->
<div class="lg:col-span-2" transition:name="vertical-navbar">
<VerticalNav navigation={navigation} />
</div>
<div class="lg:col-span-8">
<div class="flex flex-col lg:grid lg:grid-cols-10">
<div class="col-span-8 pr-10 pb-20 pt-8">
<slot />
</div>
<div
class="col-span-2 pt-8 lg:block lg:sticky lg:px-4 lg:-mx-4 lg:max-h-[calc(100vh-var(--header-height))] lg:top-[--header-height]"
>
<!-- TOC -->
<TableOfContent headings={headings} />
</div>
</div>
</div>
</div>
</div>
<style is:global>
h1 {
@apply text-4xl font-bold;
}
h2 {
@apply mt-5 scroll-mt-[calc(20px+var(--header-height))] text-3xl font-bold;
}
h3 {
@apply mt-5 scroll-mt-[calc(17px+var(--header-height))] text-2xl font-bold;
}
h4 {
@apply mt-5 scroll-mt-[calc(14px+var(--header-height))] text-xl font-bold;
}
p {
@apply mb-2 mt-2 font-[system-ui] leading-6;
}
hr {
@apply my-10 border-gray-500;
}
ul {
@apply list-outside list-disc leading-7;
}
blockquote {
@apply my-4 border-l-4 border-gray-500 pl-4;
}
a {
@apply inline-block text-blue-400 hover:underline;
}
ul ul {
@apply mb-2 ml-5 list-[circle];
}
ul li {
@apply mb-1 ml-5;
}
ol {
@apply list-outside list-decimal leading-7;
}
ol li {
@apply mb-1 ml-5;
}
span.code-highlight {
@apply rounded-md bg-[#323232] px-1 py-1 font-["Lucida_Console",monospace,courier-new] text-sm;
}
code {
@apply rounded-md bg-[#323232] px-3 py-3;
}
/* img {
@apply h-[100px] max-w-full;
} */
</style>
<script is:inline>
// make all <a> attributes have target="_blank" except for Table of Contents
document.querySelectorAll("a:not(.no-target-blank)").forEach((el) => {
el.setAttribute("target", "_blank");
});
</script>
<div class="mx-auto px-4 sm:px-6 max-w-7xl">
<div class="flex flex-col lg:grid lg:grid-cols-10">
<!-- vertical navbar -->
<div class="lg:col-span-2" transition:name="vertical-navbar">
<aside
class="hidden lg:translate-x-0 overflow-y-auto z-30 lg:block lg:sticky py-8 lg:px-4 lg:-mx-4 lg:max-h-[calc(100vh-var(--header-height))] lg:top-[--header-height]"
>
<VerticalNav />
</aside>
</div>
<div class="lg:col-span-8">
<div class="flex flex-col lg:grid lg:grid-cols-10">
<div class="col-span-8 pr-10 pb-20 pt-8">
<slot />
</div>
<div
class="col-span-2 pt-8 hidden lg:block lg:sticky lg:px-4 lg:-mx-4 lg:max-h-[calc(100vh-var(--header-height))] lg:top-[--header-height]"
>
<!-- TOC -->
<TableOfContent headings={headings} />
</div>
</div>
</div>
</div>
</div>
</Layout>

View file

@ -1,70 +1,21 @@
---
import Layout from "./Layout.astro";
import VerticalNav from "../components/verticalNav.astro";
type Navigation = Record<
string,
{ title: string; url: string; order: number }[]
>;
export function formatTitle(title: string): string {
return title
.split("-")
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(" ");
}
export async function setup() {
// setup navigation
const getStartedFiles = await Astro.glob("../pages/get-started/*.md");
const guideFiles = await Astro.glob("../pages/guide/*.md");
const markdownFiles = [...getStartedFiles, ...guideFiles];
const navigation: Navigation = {};
for (const file of markdownFiles) {
const filePath = file.url;
const [_, category] = filePath!.split("/");
const formattedCategory = formatTitle(category);
const formattedTitle = file.frontmatter.title;
const order = file.frontmatter.order ?? 0;
if (!navigation[formattedCategory]) {
navigation[formattedCategory] = [];
}
navigation[formattedCategory].push({
title: formattedTitle,
url: filePath || "",
order,
});
}
// sort by "order"
const sortedNavigation: Navigation = {};
Object.keys(navigation)
.sort()
.forEach((key) => {
sortedNavigation[key] = navigation[key].sort(
(a, b) => a.order - b.order,
);
});
return {
navigation: sortedNavigation,
};
}
const { navigation } = await setup();
import VerticalNav from "../components/blocks/verticalNav.astro";
---
<Layout title={Astro.props.title} description={Astro.props.description}>
<div class="pt-14 mx-auto px-4 sm:px-6 max-w-7xl">
<div class="flex flex-col lg:grid lg:grid-cols-10">
<!-- vertical navbar -->
<div class="lg:col-span-2" transition:name="vertical-navbar">
<VerticalNav navigation={navigation} />
</div>
<div class="lg:col-span-8 pt-8">
<!-- <div class="flex flex-col lg:grid lg:grid-cols-10">
<div class="mx-auto px-6 max-w-7xl">
<div class="flex flex-col lg:grid lg:grid-cols-10">
<!-- vertical navbar -->
<div class="lg:col-span-2" transition:name="vertical-navbar">
<aside
class="hidden lg:translate-x-0 overflow-y-auto z-30 lg:block lg:sticky py-8 lg:px-4 lg:-mx-4 lg:max-h-[calc(100vh-var(--header-height))] lg:top-[--header-height]"
>
<VerticalNav />
</aside>
</div>
<div class="lg:col-span-8 pt-8">
<!-- <div class="flex flex-col lg:grid lg:grid-cols-10">
<div class="col-span-8 pr-10 pb-10 pt-8">
<slot />
</div>
@ -74,62 +25,22 @@ const { navigation } = await setup();
<TableOfContent headings={headings} />
</div>
</div> -->
<slot />
</div>
</div>
</div>
<slot />
</div>
</div>
</div>
<style is:global>
h1 {
@apply text-4xl font-bold;
}
<style is:global>
span.code-highlight {
@apply rounded-md bg-[#323232] px-1 py-1 font-["Lucida_Console",monospace,courier-new] text-sm;
}
h2 {
@apply mt-5 scroll-mt-[calc(20px+var(--header-height))] text-3xl font-bold;
}
code {
@apply rounded-md bg-[#323232] px-3 py-3;
}
h3 {
@apply mt-5 scroll-mt-[calc(17px+var(--header-height))] text-2xl font-bold;
}
h4 {
@apply mt-5 scroll-mt-[calc(14px+var(--header-height))] text-xl font-bold;
}
p {
@apply mb-2 mt-2 font-[system-ui] leading-6;
}
hr {
@apply my-10 border-gray-500;
}
ul {
@apply list-outside list-disc leading-7;
}
blockquote {
@apply my-4 border-l-4 border-gray-500 pl-4;
}
ul ul {
@apply mb-2 ml-5 list-[circle];
}
ul li {
@apply mb-1 ml-5;
}
span.code-highlight {
@apply rounded-md bg-[#323232] px-1 py-1 font-["Lucida_Console",monospace,courier-new] text-sm;
}
code {
@apply rounded-md bg-[#323232] px-3 py-3;
}
/* img {
/* img {
@apply h-[100px] max-w-full;
} */
</style>
</style>
</Layout>

View file

@ -1,148 +1,100 @@
---
import Layout from "./Layout.astro";
import TableOfContent from "../components/TableOfContent.astro";
import VerticalNav from "../components/verticalNav.astro";
import TableOfContent from "../components/blocks/TableOfContent.astro";
import VerticalNav from "../components/blocks/verticalNav.astro";
type Navigation = Record<
string,
{ title: string; url: string; order: number }[]
>;
export function formatTitle(title: string): string {
return title
.split("-")
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(" ");
}
export async function setup() {
// setup navigation
const getStartedFiles = await Astro.glob("../pages/get-started/*.md");
const guideFiles = await Astro.glob("../pages/guide/*.md");
const markdownFiles = [...getStartedFiles, ...guideFiles];
const navigation: Navigation = {};
for (const file of markdownFiles) {
const filePath = file.url;
const [_, category] = filePath!.split("/");
const formattedCategory = formatTitle(category);
const formattedTitle = file.frontmatter.title;
const order = file.frontmatter.order ?? 0;
if (!navigation[formattedCategory]) {
navigation[formattedCategory] = [];
}
navigation[formattedCategory].push({
title: formattedTitle,
url: filePath || "",
order,
});
}
// sort by "order"
const sortedNavigation: Navigation = {};
Object.keys(navigation)
.sort()
.forEach((key) => {
sortedNavigation[key] = navigation[key].sort(
(a, b) => a.order - b.order,
);
});
return {
navigation: sortedNavigation,
};
}
const { frontmatter, headings } = Astro.props;
const { navigation } = await setup();
---
<Layout title={frontmatter.title} description={frontmatter.description}>
<div class="pt-14 mx-auto px-4 sm:px-6 max-w-7xl">
<div class="flex flex-col lg:grid lg:grid-cols-10">
<!-- vertical navbar -->
<div class="lg:col-span-2" transition:name="vertical-navbar">
<VerticalNav navigation={navigation} />
</div>
<div class="lg:col-span-8">
<div class="flex flex-col lg:grid lg:grid-cols-10">
<div class="col-span-8 pr-10 pb-10 pt-8">
<slot />
</div>
<div
class="hidden col-span-2 pt-8 lg:block lg:sticky lg:px-4 lg:-mx-4 lg:max-h-[calc(100vh-var(--header-height))] lg:top-[--header-height]"
>
<!-- TOC -->
<TableOfContent headings={headings} />
</div>
</div>
</div>
</div>
</div>
<div class="mx-auto px-6 max-w-7xl">
<div class="flex flex-col lg:grid lg:grid-cols-10">
<!-- vertical navbar -->
<div class="lg:col-span-2" transition:name="vertical-navbar">
<aside
class="hidden lg:translate-x-0 overflow-y-auto z-30 lg:block lg:sticky py-8 lg:px-4 lg:-mx-4 lg:max-h-[calc(100vh-var(--header-height))] lg:top-[--header-height]"
>
<VerticalNav />
</aside>
</div>
<div class="lg:col-span-8">
<div class="flex flex-col lg:grid lg:grid-cols-10">
<div class="col-span-8 pr-10 pb-10 pt-8">
<slot />
</div>
<div
class="hidden col-span-2 pt-8 lg:block lg:sticky lg:px-4 lg:-mx-4 lg:max-h-[calc(100vh-var(--header-height))] lg:top-[--header-height]"
>
<!-- TOC -->
<TableOfContent headings={headings} />
</div>
</div>
</div>
</div>
</div>
<style is:global>
h1 {
@apply text-4xl font-bold;
}
<style is:global>
h1 {
@apply mb-2 text-4xl font-bold;
}
h2 {
@apply mt-5 scroll-mt-[calc(20px+var(--header-height))] text-3xl font-bold;
}
h2 {
@apply mb-2 mt-5 scroll-mt-[calc(20px+var(--header-height))] text-3xl font-bold;
}
h3 {
@apply mt-5 scroll-mt-[calc(17px+var(--header-height))] text-2xl font-bold;
}
h3 {
@apply mb-2 mt-5 scroll-mt-[calc(17px+var(--header-height))] text-2xl font-bold;
}
h4 {
@apply mt-5 scroll-mt-[calc(14px+var(--header-height))] text-xl font-bold;
}
h4 {
@apply mb-2 mt-5 scroll-mt-[calc(14px+var(--header-height))] text-xl font-bold;
}
p {
@apply mb-2 mt-2 font-[system-ui] leading-6;
}
p {
@apply mb-2 mt-2 font-[system-ui] leading-6;
}
hr {
@apply my-10 border-gray-500;
}
hr {
@apply my-10 border-gray-500;
}
ul {
@apply list-outside list-disc leading-7;
}
ul {
@apply list-outside list-disc leading-7;
}
blockquote {
@apply my-4 border-l-4 border-gray-500 pl-4;
}
blockquote {
@apply my-4 border-l-4 border-gray-500 pl-4;
}
a {
@apply inline-block text-blue-400 hover:underline;
}
a {
@apply inline-block text-blue-400 hover:underline;
}
ul ul {
@apply mb-2 ml-5 list-[circle];
}
ul ul {
@apply mb-2 ml-5 list-[circle];
}
ul li {
@apply mb-1 ml-5;
}
ul li {
@apply mb-1 ml-5;
}
span.code-highlight {
@apply rounded-md bg-[#323232] px-1 py-1 font-["Lucida_Console",monospace,courier-new] text-sm;
}
span.code-highlight {
@apply rounded-md bg-[#323232] px-1 py-1 font-["Lucida_Console",monospace,courier-new] text-sm;
}
code {
@apply rounded-md bg-[#323232] px-3 py-3;
}
code {
@apply rounded-md bg-[#323232] px-3 py-3;
}
/* img {
/* img {
@apply h-[100px] max-w-full;
} */
</style>
</style>
<script is:inline>
// make all <a> attributes have target="_blank" except for Table of Contents
document.querySelectorAll("a:not(.no-target-blank)").forEach((el) => {
el.setAttribute("target", "_blank");
});
</script>
<script is:inline>
// make all <a> attributes have target="_blank" except for Table of Contents
document.querySelectorAll("a:not(.no-target-blank)").forEach((el) => {
el.setAttribute("target", "_blank");
});
</script>
</Layout>

View file

@ -1,10 +1,10 @@
---
import NavBar from "../components/navBar.astro";
import NavBar from "../components/blocks/navBar.astro";
import { ViewTransitions } from "astro:transitions";
export interface Props {
title: string;
description: string;
title: string;
description: string;
}
const { title, description } = Astro.props;
@ -12,201 +12,144 @@ const { title, description } = Astro.props;
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="description" content={description} />
<meta name="keywords" content="coworking, study, streams" />
<meta name="author" content="RyanPython" />
<meta name="theme-color" content="#2B2D31" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<script defer src="/_vercel/insights/script.js"></script>
<ViewTransitions />
<meta name="generator" content={Astro.generator} />
<title>{title}</title>
</head>
<body>
<NavBar />
<slot />
<head>
<meta charset="UTF-8" />
<meta name="description" content={description} />
<meta name="keywords" content="coworking, study, streams" />
<meta name="author" content="RythonDev" />
<meta name="theme-color" content="#2B2D31" />
<meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<script is:inline src="/_vercel/insights/script.js"></script>
<ViewTransitions />
<meta name="generator" content={Astro.generator} />
<title>{title}</title>
</head>
<body>
<div class="pt-[var(--header-height)]">
<NavBar />
<slot />
</div>
<style is:global>
@font-face {
font-family: "Fredoka";
src: url("/fonts/Fredoka-Bold.ttf") format("truetype");
font-weight: bold;
font-style: normal;
}
<style is:global>
:root {
--header-height: 3.5rem;
}
@font-face {
font-family: "Fredoka";
src: url("/fonts/Fredoka-Light.ttf") format("truetype");
font-weight: normal;
font-style: normal;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
@font-face {
font-family: "Fredoka";
src: url("/fonts/Fredoka-Medium.ttf") format("truetype");
font-weight: 500;
font-style: normal;
}
html {
font-family: system-ui, sans-serif;
background-color: #171717;
color: rgb(255 255 255);
}
@font-face {
font-family: "Fredoka";
src: url("/fonts/Fredoka-Regular.ttf") format("truetype");
font-weight: normal;
font-style: normal;
}
html,
body {
min-height: 100%;
/* smooth scroll */
scroll-behavior: smooth;
font-family: system-ui;
}
@font-face {
font-family: "Montserrat";
src: url("/fonts/Montserrat-Bold.ttf") format("truetype");
font-weight: bold;
font-style: normal;
}
body {
height: 100vh;
}
@font-face {
font-family: "Montserrat";
src: url("/fonts/Montserrat-Light.ttf") format("truetype");
font-weight: 300;
font-style: normal;
}
code {
font-family:
Menlo,
Monaco,
Lucida Console,
Liberation Mono,
DejaVu Sans Mono,
Bitstream Vera Sans Mono,
Courier New,
monospace;
}
@font-face {
font-family: "Montserrat";
src: url("/fonts/Montserrat-Medium.ttf") format("truetype");
font-weight: 500;
font-style: normal;
}
nav {
font-family: system-ui, sans-serif;
font-weight: 500;
}
@font-face {
font-family: "Montserrat";
src: url("/fonts/Montserrat-Regular.ttf") format("truetype");
font-weight: normal;
font-style: normal;
}
nav ul a {
text-decoration: none;
}
@font-face {
font-family: "Montserrat";
src: url("/fonts/Montserrat-SemiBold.ttf") format("truetype");
font-weight: 600;
font-style: normal;
}
a.preview {
text-decoration: none;
}
@font-face {
font-family: "Poppins";
src: url("/fonts/Poppins-Light.ttf") format("truetype");
font-weight: 300;
font-style: normal;
}
p code {
word-break: break-all; /* This will allow the URL to break at any character */
display: block; /* Ensures the entire URL is on its own line */
width: 100%; /* Make sure the code element takes up the full width of its container */
overflow-x: auto;
}
@font-face {
font-family: "Poppins";
src: url("/fonts/Poppins-Medium.ttf") format("truetype");
font-weight: 500;
font-style: normal;
}
blockquote {
@apply my-4 border-l-4 border-gray-500 pl-4;
}
@font-face {
font-family: "Poppins";
src: url("/fonts/Poppins-Regular.ttf") format("truetype");
font-weight: normal;
font-style: normal;
}
a {
@apply inline-block text-blue-400 hover:underline;
}
@font-face {
font-family: "Inter";
src: url("/fonts/Inter-Regular.ttf") format("truetype");
font-weight: normal;
font-style: normal;
}
ul,
ol {
@apply list-outside leading-7;
}
@font-face {
font-family: "Inter";
src: url("/fonts/Inter-Medium.ttf") format("truetype");
font-weight: 500;
font-style: normal;
}
ul {
@apply list-disc;
}
:root {
--header-height: 3.5rem;
}
ul ul,
ol ul {
@apply mb-2 ml-5 list-[circle];
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
ul li,
ol li {
@apply mb-1 ml-5;
}
html {
font-family: system-ui, sans-serif;
background-color: #171717;
color: rgb(255 255 255);
}
ol {
@apply ml-5 list-outside list-decimal leading-7;
}
</style>
<script is:inline>
const theme = (() => {
if (
typeof localStorage !== "undefined" &&
localStorage.getItem("theme")
) {
return localStorage.getItem("theme");
}
if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
return "dark";
}
return "light";
})();
html,
body {
min-height: 100%;
/* smooth scroll */
scroll-behavior: smooth;
}
if (theme === "light") {
document.documentElement.classList.remove("dark");
} else {
document.documentElement.classList.add("dark");
}
window.localStorage.setItem("theme", theme);
body {
height: 100vh;
}
code {
font-family:
Menlo,
Monaco,
Lucida Console,
Liberation Mono,
DejaVu Sans Mono,
Bitstream Vera Sans Mono,
Courier New,
monospace;
}
nav {
font-family: system-ui, sans-serif;
font-weight: 500;
}
nav ul a {
text-decoration: none;
}
a.preview {
text-decoration: none;
}
p code {
word-break: break-all; /* This will allow the URL to break at any character */
display: block; /* Ensures the entire URL is on its own line */
width: 100%; /* Make sure the code element takes up the full width of its container */
overflow-x: auto;
}
</style>
<script is:inline>
const theme = (() => {
if (
typeof localStorage !== "undefined" &&
localStorage.getItem("theme")
) {
return localStorage.getItem("theme");
}
if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
return "dark";
}
return "light";
})();
if (theme === "light") {
document.documentElement.classList.remove("dark");
} else {
document.documentElement.classList.add("dark");
}
window.localStorage.setItem("theme", theme);
</script>
</body>
// make all <a> attributes have target="_blank" except for Table of Contents
document
.querySelectorAll("a:not(.no-target-blank)")
.forEach((el) => {
el.setAttribute("target", "_blank");
});
</script>
</body>
</html>

View file

@ -1,26 +1,26 @@
---
import BlogsLayout from "../../layouts/BlogsLayout.astro";
import BlogPost from "../../components/BlogPostPreview.astro";
import BlogPost from "../../components/blocks/BlogPostPreview.astro";
import { getCollection } from "astro:content";
const allPosts = await getCollection("posts");
---
<BlogsLayout
title="Blog Posts"
description="A collection of blog posts, related to coworking, studying, streaming and many more!"
title="Blog Posts"
description="A collection of blog posts, related to coworking, studying, streaming and many more!"
>
<h1 class="mb-5">Blog Posts</h1>
<div>
{
allPosts.map((post) => (
<BlogPost
url={`blog/posts/${post.slug}/`}
title={post.data.heading}
tagline={post.data.subheading}
pubDate={post.data.pubDate}
/>
))
}
</div>
<h1 class="mb-5">Blog Posts</h1>
<div>
{
allPosts.map((post) => (
<BlogPost
url={`blog/posts/${post.slug}/`}
title={post.data.heading}
tagline={post.data.subheading}
pubDate={post.data.pubDate}
/>
))
}
</div>
</BlogsLayout>

View file

@ -16,6 +16,18 @@ Credits to the wonderful team of people behind this guide!
Main developer of the project
- [Twitch](https://twitch.tv/RythonDev)
- [YouTube](https://youtube.com/@RythonDev)
- [Website](https://rython.dev/)
<div class="flex flex-row gap-2">
[![Twitch Badge](https://img.shields.io/twitch/status/rythondev?color=9146FF&label=Twitch&logo=twitch&logoColor=white&style=for-the-badge)](https://twitch.tv/RythonDev)
[![YouTube Badge](https://img.shields.io/badge/YouTube-F00?logo=youtube&logoColor=fff&style=for-the-badge)](https://youtube.com/@RythonDev)
[![Website](https://img.shields.io/badge/Website-fff?logoColor=000&style=for-the-badge)](https://rython.dev/)
[![Ko-fi Badge](https://img.shields.io/badge/Ko--fi-FF5E5B?logo=kofi&logoColor=fff&style=for-the-badge)](https://ko-fi.com/rython)
</div>
<!-- REFERENCES FOR BADGES/ICONS -->
<!-- https://badges.pages.dev/ -->
<!-- https://shields.io/badges -->

View file

@ -17,16 +17,33 @@ Useful bots to add to your stream!
<div class="flex flex-row gap-2">
Available on:
![Twitch Badge](https://img.shields.io/badge/Twitch-9146FF?logo=twitch&logoColor=fff&style=for-the-badge)
</div>
Sery Bot is a moderation bot providing anti-hate raid and anti-follow bot protections, and other safety features and tools to streamers.
### [MohCitrus 🔗](https://www.twitch.tv/MohCitrus/about)
<div class="flex flex-row gap-2">
Available on:
![Twitch Badge](https://img.shields.io/badge/Twitch-9146FF?logo=twitch&logoColor=fff&style=for-the-badge)
</div>
MohCitrus is a translator bot made by [MohFocus (Twitch)](https://twitch.tv/MohFocus)! It can translate messages of viewers to any language via commands!
![MohCitrus](/images/bots/mohcitrus.webp)
### [Nightbot 🔗](https://nightbot.tv/)
<div class="flex flex-row gap-2">
Available on:
![Twitch Badge](https://img.shields.io/badge/Twitch-9146FF?logo=twitch&logoColor=fff&style=for-the-badge)
![YouTube Badge](https://img.shields.io/badge/YouTube-F00?logo=youtube&logoColor=fff&style=for-the-badge)
@ -34,16 +51,18 @@ Sery Bot is a moderation bot providing anti-hate raid and anti-follow bot protec
Nightbot is a chatbot that you can use to manage your chat. It has a lot of features and is easy to use. You can also use it to create custom commands for your stream.
### [MohCitrus 🔗](https://www.twitch.tv/MohCitrus/about)
### [BotRix 🔗](https://botrix.live/)
<div class="flex flex-row gap-2">
Available on:
![Twitch Badge](https://img.shields.io/badge/Twitch-9146FF?logo=twitch&logoColor=fff&style=for-the-badge)
![YouTube Badge](https://img.shields.io/badge/YouTube-F00?logo=youtube&logoColor=fff&style=for-the-badge)
</div>
MohCitrus is a translator bot made by [MohFocus (Twitch)](https://twitch.tv/MohFocus)! It can translate messages of viewers to any language via commands!
![MohCitrus](/images/bots/mohcitrus.webp)
BotRix is a chat bot that offers features and tools to enhance your community experience. You can use BotRix to moderate, send notifications, play minigames and more on Twitch, YouTube and Discord.
---
@ -53,27 +72,29 @@ MohCitrus is a translator bot made by [MohFocus (Twitch)](https://twitch.tv/MohF
<div class="flex flex-row gap-2">
Available on:
![Twitch Badge](https://img.shields.io/badge/Twitch-9146FF?logo=twitch&logoColor=fff&style=for-the-badge)
![YouTube Badge](https://img.shields.io/badge/YouTube-F00?logo=youtube&logoColor=fff&style=for-the-badge)
![Windows Badge](https://img.shields.io/badge/Windows-0078D4?logo=windows&logoColor=fff&style=for-the-badge)
</div>
- Windows only!
Streamer.bot is a local bot, meaning all connections are made directly from your local PC to any configured external services, such as Twitch, YouTube, etc.
This means you have full ownership and control of any data stored in Streamer.bot, and your bot does not depend on a central 3rd party service to continue operating.
Supercharge your livestream! Add advanced levels of interactivity to your streams with Streamer.bot.
### [Mix It Up bot 🔗](https://mixitupapp.com/)
<div class="flex flex-row gap-2">
Available on:
![Twitch Badge](https://img.shields.io/badge/Twitch-9146FF?logo=twitch&logoColor=fff&style=for-the-badge)
![YouTube Badge](https://img.shields.io/badge/YouTube-F00?logo=youtube&logoColor=fff&style=for-the-badge)
</div>
- Windows only!
![Windows Badge](https://img.shields.io/badge/Windows-0078D4?logo=windows&logoColor=fff&style=for-the-badge)
</div>
Mix It Up is a free, full featured, community driven, open-source stream bot developed by streamers for streamers.

View file

@ -7,7 +7,7 @@ order: 1
# Music and Audio
Every Coworking & Study stream has their vibe based on their stream audio!
The vibe of a coworking stream is determined by the background music or audio!
---
@ -116,12 +116,17 @@ There you can have your different outputs for different programmes. For example,
### Complicated ways
- #### [VoicemeeterBanana](https://vb-audio.com/Voicemeeter/banana.htm) (Windows only!)
- #### [VoicemeeterBanana](https://vb-audio.com/Voicemeeter/banana.htm)
![Windows Badge](https://img.shields.io/badge/Windows-0078D4?logo=windows&logoColor=fff&style=for-the-badge)
- Download Voicemeeter Banana (software to route audio sources) and set all the audio sources/inputs you don't want to include on stream into a Voicemeeter input/output source separate from the main routing.
- The outputs will show up separately in your Audio Mixer, which you can adjust/mute
- Tutorial: [Separate Game Audio, Discord, Music in OBS! (A Voicemeeter Tutorial for Beginners)](https://www.youtube.com/watch?v=XD9sWOjITYU&t=376s)
- #### [BlackHole audio](https://obsproject.com/forum/resources/mac-desktop-audio-using-blackhole.1191/) (Mac only!)
- #### [BlackHole audio](https://obsproject.com/forum/resources/mac-desktop-audio-using-blackhole.1191/)
![macOS Badge](https://img.shields.io/badge/macOS-fff?logo=macos&logoColor=000&style=for-the-badge)
- Free! Integrates with OBS
- #### [GroundControl Caster](https://gingeraudio.com/groundcontrol-caster/) (Mac only!)
- #### [GroundControl Caster](https://gingeraudio.com/groundcontrol-caster/)
![macOS Badge](https://img.shields.io/badge/macOS-fff?logo=macos&logoColor=000&style=for-the-badge)
- Free! Integrates with Twitch, OBS, StreamLabs, Zoom, FaceTime etc.

View file

@ -1,62 +1,17 @@
---
import Layout from "../layouts/Layout.astro";
import Hero from "../components/blocks/Hero.astro";
import BlurBackground from "../components/ui/BlurBackground.astro";
---
<Layout
title="Get started with Coworking streams"
description="A guide for new and existing coworking streamers!"
title="Get started with Coworking streams"
description="A guide for new and existing coworking streamers!"
>
<div class="relative flex justify-center items-center h-screen">
<div class="flex flex-col">
<div
class="text-center text-4xl font-bold font-['Inter'] leading-10"
>
<span class="mb-2">Getting started with</span>
<div
class="bg-clip-text text-transparent bg-gradient-pink-cyan py-2"
>
Coworking streams
</div>
</div>
<div class="flex mt-5 items-center gap-10">
<a
href="/get-started/introduction"
class="bg-cowo-pink text-white
text-lg
px-5 py-2
rounded-md font-medium
duration-150
hover:text-cowo-pink hover:bg-white"
>
Get started
</a>
<a
href="/guide/general-tips"
class="font-medium flex items-center text-lg
hover:underline
duration-150"
>
Check out our guide <svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="w-4 h-4"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="m8.25 4.5 7.5 7.5-7.5 7.5"></path>
</svg>
</a>
</div>
</div>
<!-- <div
class="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2"
>
</div> -->
</div>
<div class="relative overflow-hidden">
<BlurBackground />
<div class="flex justify-center items-start pb-10">
<Hero />
</div>
</div>
</Layout>
<style></style>

View file

@ -4,10 +4,8 @@ module.exports = {
theme: {
extend: {
backgroundImage: (theme) => ({
"gradient-blob":
"linear-gradient(to right, #FF7782, rgba(0,0,0,0))",
"gradient-pink-cyan":
"linear-gradient(to right, #FF7782, #8CC0DE)",
"linear-gradient(to right, #FF7782, aquamarine)",
}),
keyframes: {
rotate: {
@ -15,14 +13,26 @@ module.exports = {
"50%": { scale: "1 1.5" },
"100%": { transform: "rotate(360deg)" },
},
gradient: {
"0%": {
"background-position": "0% 50%",
},
"50%": {
"background-position": "100% 50%",
},
"100%": {
"background-position": "0% 50%",
},
},
},
animation: {
"spin-slow": "rotate 5s linear infinite",
"spin-slow": "rotate 15s linear infinite",
gradient: "gradient 15s ease infinite",
},
colors: {
"cowo-pink": "#FF7782",
"secondary": "#171717",
"primary": "#fafafa",
secondary: "#171717",
primary: "#fafafa",
},
},
},

View file

@ -1,3 +1,7 @@
{
"extends": "astro/tsconfigs/strict"
"extends": "astro/tsconfigs/strict",
"compilerOptions": {
"jsx": "react-jsx",
"jsxImportSource": "react"
}
}

7
utils/cn.ts Normal file
View file

@ -0,0 +1,7 @@
import type { ClassValue } from "clsx";
import { clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}