├── .node-version ├── .npmrc ├── src ├── routes │ ├── (site) │ │ ├── +page.svelte │ │ ├── auth │ │ │ ├── login │ │ │ │ └── +page.svelte │ │ │ ├── signup │ │ │ │ └── +page.svelte │ │ │ ├── confirm-verification │ │ │ │ └── +page.svelte │ │ │ ├── confirm-email-change │ │ │ │ └── +page.svelte │ │ │ ├── forgot-password │ │ │ │ └── +page.svelte │ │ │ └── confirm-password-reset │ │ │ │ └── [token] │ │ │ │ └── +page.svelte │ │ ├── +page.ts │ │ └── +layout.svelte │ ├── (app) │ │ ├── +layout.ts │ │ ├── profile │ │ │ └── +page.svelte │ │ ├── moves │ │ │ └── +page.svelte │ │ ├── +layout.svelte │ │ ├── tools │ │ │ ├── battle-mode │ │ │ │ └── +page.svelte │ │ │ ├── +page.svelte │ │ │ ├── auto-set-builder │ │ │ │ └── +page.svelte │ │ │ ├── 30s │ │ │ │ └── +page.svelte │ │ │ └── set-builder │ │ │ │ └── +page.svelte │ │ └── time │ │ │ └── +page.svelte │ └── +layout.svelte ├── lib │ ├── const.ts │ ├── auth │ │ ├── NewEmail.svelte │ │ ├── Verify.svelte │ │ ├── auth_form.svelte.ts │ │ ├── Signup.svelte │ │ ├── Login.svelte │ │ └── ForgotPassword.svelte │ ├── state │ │ ├── page.ts │ │ ├── session.ts │ │ ├── state.ts │ │ ├── battle_moves.svelte.ts │ │ └── moves.svelte.ts │ ├── local_db.ts │ ├── MoveType.svelte │ ├── YouSureAboutThat.svelte │ ├── BattleMove.svelte │ ├── Moves.svelte │ ├── MoveForm.svelte │ ├── Nav.svelte │ └── style.css ├── settings.ts ├── pocketbase.ts ├── utils │ ├── auth_guard.ts │ └── app_guard.ts ├── app.d.ts ├── app.html ├── pocket-types.ts └── service-worker.js ├── .vscode └── settings.json ├── static ├── airhorn.mp3 ├── favicon.ico ├── favicon.png ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon-64x64.png ├── favicon-96x96.png ├── mstile-150x150.png ├── apple-touch-icon.png ├── android-chrome-192x192.png ├── android-chrome-384x384.png ├── browserconfig.xml ├── manifest.json └── safari-pinned-tab.svg ├── .gitignore ├── .prettierrc ├── vite.config.js ├── .eslintignore ├── .prettierignore ├── tsconfig.json ├── svelte.config.js ├── .eslintrc.cjs ├── README.md ├── package.json └── .github └── copilot-instructions.md /.node-version: -------------------------------------------------------------------------------- 1 | 17.0.1 -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /src/routes/(site)/+page.svelte: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "cSpell.words": ["pocketbase"] 3 | } 4 | -------------------------------------------------------------------------------- /static/airhorn.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stolinski/the-break/HEAD/static/airhorn.mp3 -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stolinski/the-break/HEAD/static/favicon.ico -------------------------------------------------------------------------------- /static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stolinski/the-break/HEAD/static/favicon.png -------------------------------------------------------------------------------- /static/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stolinski/the-break/HEAD/static/favicon-16x16.png -------------------------------------------------------------------------------- /static/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stolinski/the-break/HEAD/static/favicon-32x32.png -------------------------------------------------------------------------------- /static/favicon-64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stolinski/the-break/HEAD/static/favicon-64x64.png -------------------------------------------------------------------------------- /static/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stolinski/the-break/HEAD/static/favicon-96x96.png -------------------------------------------------------------------------------- /src/lib/const.ts: -------------------------------------------------------------------------------- 1 | export const TYPES = ['go-down', 'toprock', 'footwork', 'freeze', 'power'] as const 2 | -------------------------------------------------------------------------------- /src/settings.ts: -------------------------------------------------------------------------------- 1 | export const settings = { 2 | app_name: 'The Break', 3 | app_route: '/moves' 4 | } 5 | -------------------------------------------------------------------------------- /static/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stolinski/the-break/HEAD/static/mstile-150x150.png -------------------------------------------------------------------------------- /static/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stolinski/the-break/HEAD/static/apple-touch-icon.png -------------------------------------------------------------------------------- /src/lib/auth/NewEmail.svelte: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | -------------------------------------------------------------------------------- /static/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stolinski/the-break/HEAD/static/android-chrome-192x192.png -------------------------------------------------------------------------------- /static/android-chrome-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stolinski/the-break/HEAD/static/android-chrome-384x384.png -------------------------------------------------------------------------------- /src/lib/state/page.ts: -------------------------------------------------------------------------------- 1 | import { writable } from 'svelte/store' 2 | 3 | export const page = writable({ 4 | title: 'The Break' 5 | }) 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "semi": false, 4 | "singleQuote": true, 5 | "trailingComma": "none", 6 | "useTabs": true 7 | } 8 | -------------------------------------------------------------------------------- /src/routes/(site)/auth/login/+page.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/routes/(site)/+page.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit' 2 | 3 | export function load() { 4 | throw redirect(307, '/auth/login') 5 | } 6 | -------------------------------------------------------------------------------- /src/routes/(site)/auth/signup/+page.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/routes/(site)/auth/confirm-verification/+page.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/routes/(site)/auth/confirm-email-change/+page.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/lib/state/session.ts: -------------------------------------------------------------------------------- 1 | import { page } from '$app/stores' 2 | import { derived } from 'svelte/store' 3 | 4 | export const user = derived(page, ($page) => $page?.data?.session?.user) 5 | -------------------------------------------------------------------------------- /src/routes/(site)/auth/forgot-password/+page.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/routes/(site)/+layout.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | {@render children()} -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | // vite.config.js 2 | import { sveltekit } from '@sveltejs/kit/vite' 3 | 4 | /** @type {import('vite').UserConfig} */ 5 | const config = { 6 | plugins: [sveltekit()] 7 | } 8 | 9 | export default config 10 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /src/pocketbase.ts: -------------------------------------------------------------------------------- 1 | import PocketBase from 'pocketbase' 2 | 3 | import type { TypedPocketBase } from './pocket-types' 4 | 5 | export const pb = new PocketBase('https://api.thebreak.app') as TypedPocketBase 6 | 7 | export const users = pb.collection('users') 8 | -------------------------------------------------------------------------------- /static/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #000000 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.svelte-kit/tsconfig.json", 3 | "compilerOptions": { 4 | "allowJs": true, 5 | "checkJs": true, 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "resolveJsonModule": true, 9 | "skipLibCheck": true, 10 | "sourceMap": true, 11 | "strict": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/routes/(app)/+layout.ts: -------------------------------------------------------------------------------- 1 | import type { UsersResponse } from '../../pocket-types' 2 | import { pb } from '../../pocketbase' 3 | 4 | export const ssr = false 5 | 6 | export const load = async function ({ depends }) { 7 | depends('app:user') 8 | return { 9 | user: pb.authStore.isValid ? (pb.authStore.model as UsersResponse) : undefined 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/routes/+layout.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 |
9 | {@render children()} 10 |
11 | 12 | -------------------------------------------------------------------------------- /src/lib/local_db.ts: -------------------------------------------------------------------------------- 1 | import Dexie from 'dexie' 2 | import type { Move } from '$state/moves.svelte' 3 | 4 | class LocalDB extends Dexie { 5 | moves: Dexie.Table 6 | 7 | constructor() { 8 | super('MovesDB') 9 | this.version(1).stores({ 10 | moves: 'id, name, props, type, user, value, needsSync' 11 | }) 12 | this.moves = this.table('moves') 13 | } 14 | } 15 | export const localDB = new LocalDB() 16 | -------------------------------------------------------------------------------- /src/utils/auth_guard.ts: -------------------------------------------------------------------------------- 1 | import { pb } from '$/pocketbase' 2 | import type { UsersResponse } from '$/pocket-types' 3 | import { goto } from '$app/navigation' 4 | 5 | export function auth_guard() { 6 | let user: UsersResponse | undefined 7 | if (pb.authStore.isValid) { 8 | user = pb.authStore.model as UsersResponse 9 | } else { 10 | user = undefined 11 | } 12 | 13 | if (!user) { 14 | goto('/auth/login') 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/app.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import type { TypedPocketBase, UsersResponse } from './pocket-types' 3 | 4 | // See https://kit.svelte.dev/docs/types#app 5 | // for information about these interfaces 6 | // and what to do when importing types 7 | declare global { 8 | namespace App { 9 | interface Locals { 10 | pb: TypedPocketBase 11 | user: UsersResponse | undefined 12 | } 13 | // interface Platform {} 14 | // interface Session {} 15 | // interface Stuff {} 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /static/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "background_color": "#0d1114", 3 | "display": "standalone", 4 | "icons": [ 5 | { 6 | "sizes": "32x32", 7 | "src": "favicon-32x32.png", 8 | "type": "image/png" 9 | }, 10 | { 11 | "sizes": "96x96", 12 | "src": "favicon-96x96.png", 13 | "type": "image/png" 14 | }, 15 | { 16 | "sizes": "192x192", 17 | "src": "android-chrome-192x192.png", 18 | "type": "image/png" 19 | } 20 | ], 21 | "lang": "en-US", 22 | "name": "The Break", 23 | "short_name": "The Break" 24 | } 25 | -------------------------------------------------------------------------------- /svelte.config.js: -------------------------------------------------------------------------------- 1 | import adapter from '@sveltejs/adapter-cloudflare' 2 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte' 3 | 4 | /** @type {import('@sveltejs/kit').Config} */ 5 | const config = { 6 | // Consult https://github.com/sveltejs/svelte-preprocess 7 | // for more information about preprocessors 8 | preprocess: vitePreprocess(), 9 | 10 | kit: { 11 | adapter: adapter(), 12 | 13 | alias: { 14 | $state: './src/lib/state', 15 | $: './src', 16 | $utils: './src/utils' 17 | } 18 | } 19 | } 20 | 21 | export default config 22 | -------------------------------------------------------------------------------- /src/utils/app_guard.ts: -------------------------------------------------------------------------------- 1 | import { pb } from '$/pocketbase' 2 | import { settings } from '$/settings' 3 | import type { UsersResponse } from '$/pocket-types' 4 | import { goto } from '$app/navigation' 5 | 6 | // Sends users to the app if they try to access login or landing pages after logging in. 7 | export function app_guard() { 8 | let user: UsersResponse | undefined 9 | if (pb.authStore.isValid) { 10 | user = pb.authStore.model as UsersResponse 11 | } else { 12 | user = undefined 13 | } 14 | 15 | if (user) { 16 | goto(settings.app_route) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parser: '@typescript-eslint/parser', 4 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'], 5 | plugins: ['svelte3', '@typescript-eslint'], 6 | ignorePatterns: ['*.cjs'], 7 | overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }], 8 | settings: { 9 | 'svelte3/typescript': () => require('typescript') 10 | }, 11 | parserOptions: { 12 | sourceType: 'module', 13 | ecmaVersion: 2020 14 | }, 15 | env: { 16 | browser: true, 17 | es2017: true, 18 | node: true 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/lib/state/state.ts: -------------------------------------------------------------------------------- 1 | import { writable } from 'svelte/store' 2 | 3 | interface State { 4 | new_move: { 5 | status: 'OPEN' | 'CLOSED' 6 | } 7 | } 8 | 9 | const default_state: State = { 10 | new_move: { 11 | status: 'CLOSED' 12 | } 13 | } 14 | 15 | const new_state = () => { 16 | const { subscribe, update, set } = writable(default_state) 17 | 18 | function toggle_new_move() { 19 | update((state) => { 20 | state.new_move.status = state.new_move.status === 'OPEN' ? 'CLOSED' : 'OPEN' 21 | return state 22 | }) 23 | } 24 | 25 | return { subscribe, update, set, toggle_new_move } 26 | } 27 | 28 | export const state = new_state() 29 | -------------------------------------------------------------------------------- /src/routes/(app)/profile/+page.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 |

{pb.authStore.model.name}

15 | 16 |

17 | Email: {pb.authStore.model.email} 18 |

19 | 20 |

More coming soon

21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/lib/state/battle_moves.svelte.ts: -------------------------------------------------------------------------------- 1 | const createBattleMoves = () => { 2 | const initial_moves = localStorage?.getItem('battle_moves') 3 | let battle_moves: string[] = $state(initial_moves ? JSON.parse(initial_moves) : []) 4 | 5 | function use(move_id: string) { 6 | // If there are no moves, create an empty array 7 | if (!battle_moves.includes(move_id)) { 8 | battle_moves.push(move_id) 9 | sync_with_local_storage() 10 | } 11 | } 12 | 13 | function reset() { 14 | battle_moves = [] 15 | sync_with_local_storage() 16 | } 17 | 18 | function sync_with_local_storage() { 19 | localStorage.setItem('battle_moves', JSON.stringify(battle_moves)) 20 | } 21 | 22 | return { 23 | reset, 24 | use, 25 | get moves() { 26 | return battle_moves 27 | } 28 | } 29 | } 30 | 31 | export const battle_moves = createBattleMoves() 32 | -------------------------------------------------------------------------------- /src/lib/auth/Verify.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 |
15 | {#if !verified_sent} 16 |

17 | Your email is not verified. 18 | 19 |

20 | {:else} 21 |

Verification email sent to {user.email}. Please check your email.

22 | {/if} 23 |
24 | 25 | 37 | -------------------------------------------------------------------------------- /src/lib/auth/auth_form.svelte.ts: -------------------------------------------------------------------------------- 1 | import { settings } from '$/settings' 2 | import { goto } from '$app/navigation' 3 | 4 | export function auth_form_state() { 5 | let status: 'LOADING' | 'SUCCESS' | 'ERROR' | 'INITIAL' = $state('INITIAL') 6 | let error_message: string | undefined = $state() 7 | 8 | function loading() { 9 | status = 'LOADING' 10 | } 11 | function error(e_message: string) { 12 | // toast.error(e_message) 13 | status = 'ERROR' 14 | error_message = e_message 15 | } 16 | 17 | function success(route: string | boolean = settings.app_route) { 18 | error_message = undefined 19 | status = 'SUCCESS' 20 | if (route && typeof route === 'string') goto(route) 21 | } 22 | 23 | return { 24 | get status() { 25 | return status 26 | }, 27 | get error_message() { 28 | return error_message 29 | }, 30 | loading, 31 | error, 32 | success 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/lib/MoveType.svelte: -------------------------------------------------------------------------------- 1 | 4 | 5 |

6 | {type_name.charAt(0)} 7 | {type_name} 8 |

9 | 10 | 43 | -------------------------------------------------------------------------------- /src/lib/YouSureAboutThat.svelte: -------------------------------------------------------------------------------- 1 | 30 | 31 | 32 |

33 |   34 | {#if attempt_count !== 0} 35 | 36 | Are you sure? Press {action_text} 37 | {remaining} more time{one_more_left ? '' : 's'}. 38 | 39 | {/if} 40 |

41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Break 2 | 3 | https://thebreak.app 4 | 5 | An app for the bboys and bgirls. 6 | 7 | It allows for you to have a move book with classifications/names/values. This way you can organize your moves and combos. There are also several tools built into the site based off of practice games I've picked up. Right now there are 3 tools. 8 | 9 | 30/30 - This is essentially a stop watch that plays an airhorn every 30 seconds. It's useful for developing endurance. Practice hard for 30 seconds, then rest for 30 seconds. 10 | 11 | Battle Mode - This shows all of your moves in their different categories. Swipe a move and select it as 'used'. It's then hidden. If you select all of the moves you did in a given round, the ones remaining are the moves you have left. By the time you get to the finals of a jam, you have a list of the moves you have left. 12 | 13 | ## About The Codebase 14 | 15 | - Svelte 5 16 | - Svelte Kit 2 17 | - Pocketbase hosted on Coolify 18 | - Hosted on Cloudflare Pages 19 | 20 | syntax.fm 21 | -------------------------------------------------------------------------------- /src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %sveltekit.head% 8 | 9 | 10 | 14 | 15 | 16 | 17 | 18 | 23 | 24 | 25 |
%sveltekit.body%
26 | 27 | 28 | -------------------------------------------------------------------------------- /src/routes/(app)/moves/+page.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/routes/(app)/+layout.svelte: -------------------------------------------------------------------------------- 1 | 25 | 26 | {#if data.user && !data.user.verified} 27 | 28 | {/if} 29 | 30 | {#if $skpage.url.pathname !== '/time'} 31 |
32 |

{$page.title}

33 |
34 | {/if} 35 | 36 |