Compare commits
23 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 516feae195 | |||
| 82475127ed | |||
| 35366651ea | |||
| e36914714f | |||
| 0db5ab19ed | |||
| e44f71cd92 | |||
| 9cb8a75f74 | |||
| b34bb19fb8 | |||
| 026b090426 | |||
| 394e1394e0 | |||
| e176843da9 | |||
| 9a5bff18bf | |||
| 5cadecd692 | |||
| cb20a55e9b | |||
| e1430713df | |||
| 8067b5ae94 | |||
| e4386eb009 | |||
| 260d55d3de | |||
| 1fd4b6794a | |||
| 2c52b9c266 | |||
| e71f49e20d | |||
| ec2a8b15f4 | |||
| 1a53ceb860 |
24 changed files with 5840 additions and 499 deletions
5
LICENSE
Normal file
5
LICENSE
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
Copyright (C) 2025 by Anders Englöf Ytterström <yttan@fastmail.se>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
18
README
Normal file
18
README
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
Strand
|
||||
======
|
||||
|
||||
Q: What is this?
|
||||
A: A digital countdown app for a health project, including
|
||||
going to the gym, doing cardio and being on a diet.
|
||||
|
||||
Q: What does the name mean?
|
||||
A: It is swedish for "beach", referring to typical "beach <year>"
|
||||
goals. Since this is yet another beach goal, and I am a swede,
|
||||
it was the first that popped into my head.
|
||||
|
||||
Stack
|
||||
-----
|
||||
|
||||
Vanilla Svelte, with a plugin for persistent store using
|
||||
localStorage, as well as some PWA addons for better offline
|
||||
use on mobile devices.
|
||||
47
README.md
47
README.md
|
|
@ -1,47 +0,0 @@
|
|||
# Svelte + TS + Vite
|
||||
|
||||
This template should help get you started developing with Svelte and TypeScript in Vite.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
[VS Code](https://code.visualstudio.com/) + [Svelte](https://marketplace.visualstudio.com/items?itemName=svelte.svelte-vscode).
|
||||
|
||||
## Need an official Svelte framework?
|
||||
|
||||
Check out [SvelteKit](https://github.com/sveltejs/kit#readme), which is also powered by Vite. Deploy anywhere with its serverless-first approach and adapt to various platforms, with out of the box support for TypeScript, SCSS, and Less, and easily-added support for mdsvex, GraphQL, PostCSS, Tailwind CSS, and more.
|
||||
|
||||
## Technical considerations
|
||||
|
||||
**Why use this over SvelteKit?**
|
||||
|
||||
- It brings its own routing solution which might not be preferable for some users.
|
||||
- It is first and foremost a framework that just happens to use Vite under the hood, not a Vite app.
|
||||
|
||||
This template contains as little as possible to get started with Vite + TypeScript + Svelte, while taking into account the developer experience with regards to HMR and intellisense. It demonstrates capabilities on par with the other `create-vite` templates and is a good starting point for beginners dipping their toes into a Vite + Svelte project.
|
||||
|
||||
Should you later need the extended capabilities and extensibility provided by SvelteKit, the template has been structured similarly to SvelteKit so that it is easy to migrate.
|
||||
|
||||
**Why `global.d.ts` instead of `compilerOptions.types` inside `jsconfig.json` or `tsconfig.json`?**
|
||||
|
||||
Setting `compilerOptions.types` shuts out all other types not explicitly listed in the configuration. Using triple-slash references keeps the default TypeScript setting of accepting type information from the entire workspace, while also adding `svelte` and `vite/client` type information.
|
||||
|
||||
**Why include `.vscode/extensions.json`?**
|
||||
|
||||
Other templates indirectly recommend extensions via the README, but this file allows VS Code to prompt the user to install the recommended extension upon opening the project.
|
||||
|
||||
**Why enable `allowJs` in the TS template?**
|
||||
|
||||
While `allowJs: false` would indeed prevent the use of `.js` files in the project, it does not prevent the use of JavaScript syntax in `.svelte` files. In addition, it would force `checkJs: false`, bringing the worst of both worlds: not being able to guarantee the entire codebase is TypeScript, and also having worse typechecking for the existing JavaScript. In addition, there are valid use cases in which a mixed codebase may be relevant.
|
||||
|
||||
**Why is HMR not preserving my local component state?**
|
||||
|
||||
HMR state preservation comes with a number of gotchas! It has been disabled by default in both `svelte-hmr` and `@sveltejs/vite-plugin-svelte` due to its often surprising behavior. You can read the details [here](https://github.com/rixo/svelte-hmr#svelte-hmr).
|
||||
|
||||
If you have state that's important to retain within a component, consider creating an external store which would not be replaced by HMR.
|
||||
|
||||
```ts
|
||||
// store.ts
|
||||
// An extremely simple external store
|
||||
import { writable } from 'svelte/store'
|
||||
export default writable(0)
|
||||
```
|
||||
|
|
@ -183,324 +183,337 @@ const gym = [
|
|||
},
|
||||
{
|
||||
name: "A 1:1",
|
||||
planned_at: "2025-02-02",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "A 1:2",
|
||||
planned_at: "2025-02-04",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "A 1:3",
|
||||
planned_at: "2025-02-06",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "A 2:1",
|
||||
planned_at: "2025-02-09",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "A 2:2",
|
||||
name: "A 1:2",
|
||||
planned_at: "2025-02-11",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "A 2:3",
|
||||
name: "A 1:3",
|
||||
planned_at: "2025-02-13",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "A 3:1",
|
||||
name: "A 2:1",
|
||||
planned_at: "2025-02-16",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "A 3:2",
|
||||
name: "A 2:2",
|
||||
planned_at: "2025-02-18",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "A 3:3",
|
||||
name: "A 2:3",
|
||||
planned_at: "2025-02-20",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "A 4:1",
|
||||
name: "A 3:1",
|
||||
planned_at: "2025-02-23",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "A 4:2",
|
||||
name: "A 3:2",
|
||||
planned_at: "2025-02-25",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "A 4:3",
|
||||
name: "A 3:3",
|
||||
planned_at: "2025-02-27",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "B 1:1",
|
||||
name: "A 4:1",
|
||||
planned_at: "2025-03-02",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "B 1:2",
|
||||
name: "A 4:2",
|
||||
planned_at: "2025-03-04",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "B 1:3",
|
||||
name: "A 4:3",
|
||||
planned_at: "2025-03-06",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "B 2:1",
|
||||
name: "B 1:1",
|
||||
planned_at: "2025-03-09",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "B 2:2",
|
||||
name: "B 1:2",
|
||||
planned_at: "2025-03-11",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "B 2:3",
|
||||
name: "B 1:3",
|
||||
planned_at: "2025-03-13",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "B 3:1",
|
||||
name: "B 2:1",
|
||||
planned_at: "2025-03-16",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "B 3:2",
|
||||
name: "B 2:2",
|
||||
planned_at: "2025-03-18",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "B 3:3",
|
||||
name: "B 2:3",
|
||||
planned_at: "2025-03-20",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "B 4:1",
|
||||
name: "B 3:1",
|
||||
planned_at: "2025-03-23",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "B 4:2",
|
||||
name: "B 3:2",
|
||||
planned_at: "2025-03-25",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "B 4:3",
|
||||
name: "B 3:3",
|
||||
planned_at: "2025-03-27",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "C 1:1",
|
||||
name: "B 4:1",
|
||||
planned_at: "2025-03-30",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "C 1:2",
|
||||
name: "B 4:2",
|
||||
planned_at: "2025-04-01",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "C 1:3",
|
||||
name: "B 4:3",
|
||||
planned_at: "2025-04-03",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "C 2:1",
|
||||
name: "C 1:1",
|
||||
planned_at: "2025-04-06",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "C 2:2",
|
||||
name: "C 1:2",
|
||||
planned_at: "2025-04-08",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "C 2:3",
|
||||
name: "C 1:3",
|
||||
planned_at: "2025-04-10",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "C 3:1",
|
||||
name: "C 2:1",
|
||||
planned_at: "2025-04-13",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "C 3:2",
|
||||
name: "C 2:2",
|
||||
planned_at: "2025-04-15",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "C 3:3",
|
||||
name: "C 2:3",
|
||||
planned_at: "2025-04-17",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "C 3:1",
|
||||
planned_at: "2025-04-20",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "C 3:2",
|
||||
planned_at: "2025-04-22",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
{
|
||||
name: "C 3:3",
|
||||
planned_at: "2025-04-24",
|
||||
completed: false,
|
||||
completed_at: null,
|
||||
},
|
||||
];
|
||||
|
||||
const diet = {
|
||||
days: [
|
||||
{ date: "2025-02-10", completed: false },
|
||||
{ date: "2025-02-11", completed: false },
|
||||
{ date: "2025-02-12", completed: false },
|
||||
{ date: "2025-02-13", completed: false },
|
||||
{ date: "2025-02-14", completed: false },
|
||||
{ date: "2025-02-15", completed: false },
|
||||
{ date: "2025-02-16", completed: false },
|
||||
{ date: "2025-02-17", completed: false },
|
||||
{ date: "2025-02-18", completed: false },
|
||||
{ date: "2025-02-19", completed: false },
|
||||
{ date: "2025-02-10", completed: false },
|
||||
{ date: "2025-02-21", completed: false },
|
||||
{ date: "2025-02-22", completed: false },
|
||||
{ date: "2025-02-23", completed: false },
|
||||
{ date: "2025-02-24", completed: false },
|
||||
{ date: "2025-02-25", completed: false },
|
||||
{ date: "2025-02-26", completed: false },
|
||||
{ date: "2025-02-27", completed: false },
|
||||
{ date: "2025-02-28", completed: false },
|
||||
{ date: "2025-03-01", completed: false },
|
||||
{ date: "2025-03-02", completed: false },
|
||||
{ date: "2025-03-03", completed: false },
|
||||
{ date: "2025-03-05", completed: false },
|
||||
{ date: "2025-03-06", completed: false },
|
||||
{ date: "2025-03-07", completed: false },
|
||||
{ date: "2025-03-08", completed: false },
|
||||
{ date: "2025-03-09", completed: false },
|
||||
{ date: "2025-03-10", completed: false },
|
||||
{ date: "2025-03-11", completed: false },
|
||||
{ date: "2025-03-12", completed: false },
|
||||
{ date: "2025-03-13", completed: false },
|
||||
{ date: "2025-03-14", completed: false },
|
||||
{ date: "2025-03-15", completed: false },
|
||||
{ date: "2025-03-16", completed: false },
|
||||
{ date: "2025-03-17", completed: false },
|
||||
{ date: "2025-03-18", completed: false },
|
||||
{ date: "2025-03-19", completed: false },
|
||||
{ date: "2025-03-10", completed: false },
|
||||
{ date: "2025-03-21", completed: false },
|
||||
{ date: "2025-03-22", completed: false },
|
||||
{ date: "2025-03-23", completed: false },
|
||||
{ date: "2025-03-24", completed: false },
|
||||
{ date: "2025-03-25", completed: false },
|
||||
{ date: "2025-03-26", completed: false },
|
||||
{ date: "2025-03-27", completed: false },
|
||||
{ date: "2025-03-28", completed: false },
|
||||
{ date: "2025-03-30", completed: false },
|
||||
{ date: "2025-03-31", completed: false },
|
||||
{ date: "2025-04-01", completed: false },
|
||||
{ date: "2025-04-02", completed: false },
|
||||
{ date: "2025-04-03", completed: false },
|
||||
{ date: "2025-04-04", completed: false },
|
||||
{ date: "2025-04-05", completed: false },
|
||||
{ date: "2025-04-06", completed: false },
|
||||
{ date: "2025-04-07", completed: false },
|
||||
{ date: "2025-04-08", completed: false },
|
||||
{ date: "2025-04-09", completed: false },
|
||||
{ date: "2025-04-10", completed: false },
|
||||
{ date: "2025-04-11", completed: false },
|
||||
{ date: "2025-04-12", completed: false },
|
||||
{ date: "2025-04-13", completed: false },
|
||||
{ date: "2025-04-14", completed: false },
|
||||
{ date: "2025-04-15", completed: false },
|
||||
{ date: "2025-04-16", completed: false },
|
||||
{ date: "2025-04-18", completed: false },
|
||||
{ date: "2025-04-19", completed: false },
|
||||
{ date: "2025-04-10", completed: false },
|
||||
{ date: "2025-04-21", completed: false },
|
||||
{ date: "2025-04-22", completed: false },
|
||||
{ date: "2025-04-23", completed: false },
|
||||
{ date: "2025-04-24", completed: false },
|
||||
{ date: "2025-04-25", completed: false },
|
||||
{ date: "2025-04-26", completed: false },
|
||||
{ date: "2025-04-27", completed: false },
|
||||
{ date: "2025-04-28", completed: false },
|
||||
{ date: "2025-04-29", completed: false },
|
||||
{ date: "2025-04-30", completed: false },
|
||||
{ date: "2025-05-01", completed: false },
|
||||
{ date: "2025-05-02", completed: false },
|
||||
{ date: "2025-05-03", completed: false },
|
||||
{ date: "2025-05-04", completed: false },
|
||||
{ date: "2025-05-05", completed: false },
|
||||
{ date: "2025-05-06", completed: false },
|
||||
{ date: "2025-05-07", completed: false },
|
||||
{ date: "2025-05-08", completed: false },
|
||||
{ date: "2025-05-09", completed: false },
|
||||
{ date: "2025-05-10", completed: false },
|
||||
{ date: "2025-05-11", completed: false },
|
||||
{ date: "2025-05-12", completed: false },
|
||||
{ date: "2025-05-13", completed: false },
|
||||
{ date: "2025-05-14", completed: false },
|
||||
{ date: "2025-05-15", completed: false },
|
||||
{ date: "2025-05-16", completed: false },
|
||||
{ date: "2025-05-17", completed: false },
|
||||
{ date: "2025-05-18", completed: false },
|
||||
{ date: "2025-05-19", completed: false },
|
||||
{ date: "2025-05-10", completed: false },
|
||||
{ date: "2025-05-21", completed: false },
|
||||
{ date: "2025-05-22", completed: false },
|
||||
{ date: "2025-05-23", completed: false },
|
||||
{ date: "2025-05-24", completed: false },
|
||||
{ date: "2025-05-25", completed: false },
|
||||
{ date: "2025-05-26", completed: false },
|
||||
{ date: "2025-05-27", completed: false },
|
||||
{ date: "2025-05-28", completed: false },
|
||||
{ date: "2025-05-29", completed: false },
|
||||
{ date: "2025-05-30", completed: false },
|
||||
{ date: "2025-05-31", completed: false },
|
||||
],
|
||||
reds: {
|
||||
"2025-03-04": "Semmeldagen",
|
||||
"2025-03-29": "Födelsedag",
|
||||
"2025-04-17": "Påsk",
|
||||
},
|
||||
};
|
||||
const fast = [
|
||||
{ name: "1:1", planned_at: "2025-04-30", completed: false },
|
||||
{ name: "1:2", planned_at: "2025-05-02", completed: false },
|
||||
{ name: "2:1", planned_at: "2025-05-05", completed: false },
|
||||
{ name: "2:2", planned_at: "2025-05-07", completed: false },
|
||||
{ name: "2:3", planned_at: "2025-05-09", completed: false },
|
||||
{ name: "3:1", planned_at: "2025-05-12", completed: false },
|
||||
{ name: "3:2", planned_at: "2025-05-14", completed: false },
|
||||
{ name: "3:3", planned_at: "2025-05-16", completed: false },
|
||||
{ name: "4:1", planned_at: "2025-05-19", completed: false },
|
||||
{ name: "4:2", planned_at: "2025-05-21", completed: false },
|
||||
{ name: "4:3", planned_at: "2025-05-23", completed: false },
|
||||
{ name: "5:1", planned_at: "2025-05-26", completed: false },
|
||||
{ name: "5:2", planned_at: "2025-05-28", completed: false },
|
||||
{ name: "5:3", planned_at: "2025-05-30", completed: false },
|
||||
];
|
||||
|
||||
const diet = [
|
||||
{ date: "2025-02-10", completed: false },
|
||||
{ date: "2025-02-11", completed: false },
|
||||
{ date: "2025-02-12", completed: false },
|
||||
{ date: "2025-02-13", completed: false },
|
||||
{ date: "2025-02-14", completed: false },
|
||||
{ date: "2025-02-15", completed: false },
|
||||
{ date: "2025-02-16", completed: false },
|
||||
{ date: "2025-02-17", completed: false },
|
||||
{ date: "2025-02-18", completed: false },
|
||||
{ date: "2025-02-19", completed: false },
|
||||
{ date: "2025-02-10", completed: false },
|
||||
{ date: "2025-02-21", completed: false },
|
||||
{ date: "2025-02-22", completed: false },
|
||||
{ date: "2025-02-23", completed: false },
|
||||
{ date: "2025-02-24", completed: false },
|
||||
{ date: "2025-02-25", completed: false },
|
||||
{ date: "2025-02-26", completed: false },
|
||||
{ date: "2025-02-27", completed: false },
|
||||
{ date: "2025-02-28", completed: false },
|
||||
{ date: "2025-03-01", completed: false },
|
||||
{ date: "2025-03-02", completed: false },
|
||||
{ date: "2025-03-03", completed: false },
|
||||
{ date: "2025-03-04", excluded: true, because: "Semmeldagen" },
|
||||
{ date: "2025-03-05", completed: false },
|
||||
{ date: "2025-03-06", completed: false },
|
||||
{ date: "2025-03-07", completed: false },
|
||||
{ date: "2025-03-08", completed: false },
|
||||
{ date: "2025-03-09", completed: false },
|
||||
{ date: "2025-03-10", completed: false },
|
||||
{ date: "2025-03-11", completed: false },
|
||||
{ date: "2025-03-12", completed: false },
|
||||
{ date: "2025-03-13", completed: false },
|
||||
{ date: "2025-03-14", completed: false },
|
||||
{ date: "2025-03-15", completed: false },
|
||||
{ date: "2025-03-16", completed: false },
|
||||
{ date: "2025-03-17", completed: false },
|
||||
{ date: "2025-03-18", completed: false },
|
||||
{ date: "2025-03-19", completed: false },
|
||||
{ date: "2025-03-20", completed: false },
|
||||
{ date: "2025-03-21", completed: false },
|
||||
{ date: "2025-03-22", completed: false },
|
||||
{ date: "2025-03-23", completed: false },
|
||||
{ date: "2025-03-24", completed: false },
|
||||
{ date: "2025-03-25", completed: false },
|
||||
{ date: "2025-03-26", completed: false },
|
||||
{ date: "2025-03-27", completed: false },
|
||||
{ date: "2025-03-28", completed: false },
|
||||
{ date: "2025-03-29", excluded: true, because: "Födelsedag" },
|
||||
{ date: "2025-03-30", completed: false },
|
||||
{ date: "2025-03-31", completed: false },
|
||||
{ date: "2025-04-01", completed: false },
|
||||
{ date: "2025-04-02", completed: false },
|
||||
{ date: "2025-04-03", completed: false },
|
||||
{ date: "2025-04-04", completed: false },
|
||||
{ date: "2025-04-05", completed: false },
|
||||
{ date: "2025-04-06", completed: false },
|
||||
{ date: "2025-04-07", completed: false },
|
||||
{ date: "2025-04-08", completed: false },
|
||||
{ date: "2025-04-09", completed: false },
|
||||
{ date: "2025-04-10", completed: false },
|
||||
{ date: "2025-04-11", completed: false },
|
||||
{ date: "2025-04-12", completed: false },
|
||||
{ date: "2025-04-13", completed: false },
|
||||
{ date: "2025-04-14", completed: false },
|
||||
{ date: "2025-04-15", completed: false },
|
||||
{ date: "2025-04-16", completed: false },
|
||||
{ date: "2025-04-17", excluded: true, because: "Påsk" },
|
||||
{ date: "2025-04-18", completed: false },
|
||||
{ date: "2025-04-19", completed: false },
|
||||
{ date: "2025-04-10", completed: false },
|
||||
{ date: "2025-04-21", completed: false },
|
||||
{ date: "2025-04-22", completed: false },
|
||||
{ date: "2025-04-23", completed: false },
|
||||
{ date: "2025-04-24", completed: false },
|
||||
{ date: "2025-04-25", completed: false },
|
||||
{ date: "2025-04-26", completed: false },
|
||||
{ date: "2025-04-27", completed: false },
|
||||
{ date: "2025-04-28", completed: false },
|
||||
{ date: "2025-04-29", completed: false },
|
||||
{ date: "2025-04-30", completed: false },
|
||||
{ date: "2025-05-01", completed: false },
|
||||
{ date: "2025-05-02", completed: false },
|
||||
{ date: "2025-05-03", completed: false },
|
||||
{ date: "2025-05-04", completed: false },
|
||||
{ date: "2025-05-05", completed: false },
|
||||
{ date: "2025-05-06", completed: false },
|
||||
{ date: "2025-05-07", completed: false },
|
||||
{ date: "2025-05-08", completed: false },
|
||||
{ date: "2025-05-09", completed: false },
|
||||
{ date: "2025-05-10", completed: false },
|
||||
{ date: "2025-05-11", completed: false },
|
||||
{ date: "2025-05-12", completed: false },
|
||||
{ date: "2025-05-13", completed: false },
|
||||
{ date: "2025-05-14", completed: false },
|
||||
{ date: "2025-05-15", completed: false },
|
||||
{ date: "2025-05-16", completed: false },
|
||||
{ date: "2025-05-17", completed: false },
|
||||
{ date: "2025-05-18", completed: false },
|
||||
{ date: "2025-05-19", completed: false },
|
||||
{ date: "2025-05-10", completed: false },
|
||||
{ date: "2025-05-21", completed: false },
|
||||
{ date: "2025-05-22", completed: false },
|
||||
{ date: "2025-05-23", completed: false },
|
||||
{ date: "2025-05-24", completed: false },
|
||||
{ date: "2025-05-25", completed: false },
|
||||
{ date: "2025-05-26", completed: false },
|
||||
{ date: "2025-05-27", completed: false },
|
||||
{ date: "2025-05-28", completed: false },
|
||||
{ date: "2025-05-29", completed: false },
|
||||
{ date: "2025-05-30", completed: false },
|
||||
{ date: "2025-05-31", completed: false },
|
||||
];
|
||||
export default {
|
||||
cardio,
|
||||
gym,
|
||||
diet,
|
||||
fast,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,9 +2,8 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + Svelte + TS</title>
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<title>Strand</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
|
|
|||
4802
package-lock.json
generated
4802
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -7,15 +7,19 @@
|
|||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"check": "svelte-check --tsconfig ./tsconfig.app.json && tsc -p tsconfig.node.json"
|
||||
"check": "svelte-check --tsconfig ./tsconfig.app.json && tsc -p tsconfig.node.json",
|
||||
"format": "prettier ./src --write"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/vite-plugin-svelte": "^5.0.3",
|
||||
"@tsconfig/svelte": "^5.0.4",
|
||||
"prettier": "^3.5.3",
|
||||
"prettier-plugin-svelte": "^3.3.3",
|
||||
"svelte": "^5.15.0",
|
||||
"svelte-check": "^4.1.1",
|
||||
"typescript": "~5.6.2",
|
||||
"vite": "^6.0.5"
|
||||
"vite": "^6.0.5",
|
||||
"vite-plugin-pwa": "^0.21.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"svelte-persisted-store": "^0.12.0"
|
||||
|
|
|
|||
BIN
public/icon512_maskable.png
Normal file
BIN
public/icon512_maskable.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
BIN
public/icon512_rounded.png
Normal file
BIN
public/icon512_rounded.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
|
|
@ -1 +0,0 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
|
|
@ -1,41 +1,35 @@
|
|||
<script lang="ts">
|
||||
// import svelteLogo from "./assets/svelte.svg";
|
||||
// import viteLogo from "/vite.svg";
|
||||
import { currentView } from "./lib/store";
|
||||
import Summary from "./lib/Summary.svelte";
|
||||
import GymProgress from "./lib/GymProgress.svelte";
|
||||
import CardioProgress from "./lib/CardioProgress.svelte";
|
||||
import DietProgress from "./lib/DietProgress.svelte";
|
||||
// import svelteLogo from "./assets/svelte.svg";
|
||||
// import viteLogo from "/vite.svg";
|
||||
import { currentView } from "./lib/store";
|
||||
import Summary from "./lib/Summary.svelte";
|
||||
import GymProgress from "./lib/GymProgress.svelte";
|
||||
import CardioProgress from "./lib/CardioProgress.svelte";
|
||||
import DietProgress from "./lib/DietProgress.svelte";
|
||||
import FastProgress from "./lib/FastProgress.svelte";
|
||||
import Info from "./lib/Info.svelte";
|
||||
</script>
|
||||
|
||||
<div class="chrome">
|
||||
<main>
|
||||
{#if $currentView === 0}
|
||||
<Summary />
|
||||
{/if}
|
||||
{#if $currentView === 1}
|
||||
<GymProgress />
|
||||
{/if}
|
||||
{#if $currentView === 2}
|
||||
<CardioProgress />
|
||||
{/if}
|
||||
{#if $currentView === 3}
|
||||
<DietProgress />
|
||||
{/if}
|
||||
</main>
|
||||
{#if $currentView === 0}
|
||||
<Summary />
|
||||
{/if}
|
||||
{#if $currentView === 1}
|
||||
<GymProgress />
|
||||
{/if}
|
||||
{#if $currentView === 2}
|
||||
<CardioProgress />
|
||||
{/if}
|
||||
{#if $currentView === 3}
|
||||
<DietProgress />
|
||||
{/if}
|
||||
{#if $currentView === 4}
|
||||
<Info />
|
||||
{/if}
|
||||
{#if $currentView === 5}
|
||||
<FastProgress />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.chrome {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
|
||||
main {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
text-align: center;
|
||||
padding: 0.5em;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
169
src/app.css
169
src/app.css
|
|
@ -1,68 +1,135 @@
|
|||
:root {
|
||||
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
font-family: "Open Sans", Inter, system-ui, Avenir, Helvetica, Arial,
|
||||
sans-serif;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
|
||||
color-scheme: light dark;
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
background-color: #242424;
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
color: #646cff;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
a:hover {
|
||||
color: #535bf2;
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.top-button {
|
||||
position: absolute;
|
||||
top: 0.25em;
|
||||
right: 0.25em;
|
||||
button {
|
||||
padding: 1em;
|
||||
background-color: #ff0;
|
||||
color: #000;
|
||||
|
||||
&:hover {
|
||||
filter: brightness(95%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #e0e0e0;
|
||||
box-sizing: border-box;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3.2em;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: 2em;
|
||||
font-size: 3em;
|
||||
line-height: 1.1;
|
||||
margin: 0.2em 0;
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 8px;
|
||||
border: 1px solid transparent;
|
||||
padding: 0.6em 1.2em;
|
||||
font-size: 1em;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
background-color: #1a1a1a;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.25s;
|
||||
}
|
||||
button:hover {
|
||||
border-color: #646cff;
|
||||
}
|
||||
button:focus,
|
||||
button:focus-visible {
|
||||
outline: 4px auto -webkit-focus-ring-color;
|
||||
appearance: none;
|
||||
border-radius: 0;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
font-size: 1em;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
color: #213547;
|
||||
background-color: #ffffff;
|
||||
article {
|
||||
opacity: 0.8;
|
||||
label {
|
||||
display: block;
|
||||
padding: 1em 0.66em;
|
||||
|
||||
input {
|
||||
grid-area: input;
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
a:hover {
|
||||
color: #747bff;
|
||||
strong {
|
||||
display: block;
|
||||
font-size: 1.25em;
|
||||
}
|
||||
button {
|
||||
background-color: #f9f9f9;
|
||||
small {
|
||||
grid-area: date;
|
||||
}
|
||||
}
|
||||
}
|
||||
article:has(:checked) {
|
||||
opacity: 0.25;
|
||||
|
||||
+ article:not(:has(:checked)) {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.clickable {
|
||||
box-shadow: 0 3px 0 rgba(0, 0, 0, 0.75);
|
||||
border: 2px solid #222;
|
||||
border-radius: 8px;
|
||||
background-color: #f1f1f1;
|
||||
display: block;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: #e1e1e1;
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translateY(1px);
|
||||
}
|
||||
}
|
||||
|
||||
.back {
|
||||
background-color: #e0e0e0;
|
||||
color: #000;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: #d0d0d0;
|
||||
}
|
||||
}
|
||||
|
||||
.calendar {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
gap: 2em 0.5em;
|
||||
}
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
padding: 1em;
|
||||
gap: 0.5em;
|
||||
align-items: center;
|
||||
|
||||
button {
|
||||
padding: 0.5em 1em;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.25em;
|
||||
flex: 1;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
192
src/lib/Arc.svelte
Normal file
192
src/lib/Arc.svelte
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
<script lang="ts">
|
||||
import DietProgress from "./DietProgress.svelte";
|
||||
import P from "./svg-path";
|
||||
import { gym, cardio, diet, fast } from "./store";
|
||||
|
||||
export const DEG_TO_RAD = Math.PI / 180;
|
||||
export const RAD_TO_DEG = 180 / Math.PI;
|
||||
export const FULL_CIRCLE_IN_RADIANS = 2 * Math.PI;
|
||||
|
||||
const size = 400;
|
||||
const segmentHeight = 16;
|
||||
const span = 0.8 * FULL_CIRCLE_IN_RADIANS;
|
||||
const startAngle = 0.25 * FULL_CIRCLE_IN_RADIANS + span / 2;
|
||||
|
||||
const perimiterWidth = size * Math.PI * (span / FULL_CIRCLE_IN_RADIANS);
|
||||
const pixelToRadians = span / perimiterWidth;
|
||||
const x = 0;
|
||||
const y = 0;
|
||||
|
||||
const points = (
|
||||
radius: number,
|
||||
radLength: number,
|
||||
thickness: number,
|
||||
offset?: number,
|
||||
) => {
|
||||
const borderRadius = thickness / 2;
|
||||
const outerRadius = radius;
|
||||
const innerRadius = outerRadius - thickness;
|
||||
const radEndAngle = startAngle - radLength;
|
||||
const borderRadiusAngle =
|
||||
(borderRadius / (outerRadius * FULL_CIRCLE_IN_RADIANS)) *
|
||||
FULL_CIRCLE_IN_RADIANS;
|
||||
const isLongTrack = radLength - 2 * borderRadiusAngle > Math.PI;
|
||||
const start = startAngle + (offset ?? 0);
|
||||
|
||||
return P()
|
||||
.moveTo(
|
||||
-Math.sin(start) * (outerRadius - borderRadius),
|
||||
Math.cos(start) * (outerRadius - borderRadius),
|
||||
)
|
||||
.arcTo(
|
||||
borderRadius,
|
||||
borderRadius,
|
||||
false,
|
||||
true,
|
||||
-Math.sin(start - borderRadiusAngle) * outerRadius,
|
||||
Math.cos(start - borderRadiusAngle) * outerRadius,
|
||||
)
|
||||
.arcTo(
|
||||
outerRadius,
|
||||
outerRadius,
|
||||
isLongTrack,
|
||||
true,
|
||||
-Math.sin(radEndAngle + borderRadiusAngle) * outerRadius,
|
||||
Math.cos(radEndAngle + borderRadiusAngle) * outerRadius,
|
||||
)
|
||||
.arcTo(
|
||||
borderRadius,
|
||||
borderRadius,
|
||||
false,
|
||||
true,
|
||||
-Math.sin(radEndAngle) * (outerRadius - borderRadius),
|
||||
Math.cos(radEndAngle) * (outerRadius - borderRadius),
|
||||
)
|
||||
.lineTo(
|
||||
-Math.sin(radEndAngle) * (innerRadius + borderRadius),
|
||||
Math.cos(radEndAngle) * (innerRadius + borderRadius),
|
||||
)
|
||||
.arcTo(
|
||||
borderRadius,
|
||||
borderRadius,
|
||||
false,
|
||||
true,
|
||||
-Math.sin(radEndAngle + borderRadiusAngle) * innerRadius,
|
||||
Math.cos(radEndAngle + borderRadiusAngle) * innerRadius,
|
||||
)
|
||||
.arcTo(
|
||||
innerRadius,
|
||||
innerRadius,
|
||||
isLongTrack,
|
||||
false,
|
||||
-Math.sin(start - borderRadiusAngle) * innerRadius,
|
||||
Math.cos(start - borderRadiusAngle) * innerRadius,
|
||||
)
|
||||
.arcTo(
|
||||
borderRadius,
|
||||
borderRadius,
|
||||
false,
|
||||
true,
|
||||
-Math.sin(start) * (innerRadius + borderRadius),
|
||||
Math.cos(start) * (innerRadius + borderRadius),
|
||||
)
|
||||
.close()
|
||||
.stringify();
|
||||
};
|
||||
|
||||
let items = [
|
||||
{
|
||||
c: "gym",
|
||||
progress: $gym.filter((c) => c.completed).length / $gym.length,
|
||||
level: 0,
|
||||
},
|
||||
{
|
||||
c: "cardio",
|
||||
progress: $cardio.filter((c) => c.completed).length / $cardio.length,
|
||||
level: 1,
|
||||
},
|
||||
{
|
||||
c: "diet",
|
||||
progress:
|
||||
$diet.filter((c) => c.completed).length /
|
||||
$diet.filter((c) => !c.excluded).length,
|
||||
level: 2,
|
||||
},
|
||||
{
|
||||
c: "fast",
|
||||
progress: $fast.filter((c) => c.completed).length / $fast.length,
|
||||
level: 3,
|
||||
},
|
||||
];
|
||||
</script>
|
||||
|
||||
<figure>
|
||||
<svg viewBox="0 0 {size} {size}" role="presentation">
|
||||
<g transform="${`translate(${x},${y})`}" fill="none">
|
||||
<g transform={`translate(${size / 2},${size / 2})`}>
|
||||
{#each items as { c, progress, level }}
|
||||
<path
|
||||
fill="#fff"
|
||||
opacity="0.066"
|
||||
d={points(
|
||||
size / 2 - level * (segmentHeight + 4),
|
||||
span,
|
||||
segmentHeight,
|
||||
)}
|
||||
/>
|
||||
<path
|
||||
class={c}
|
||||
fill="currentColor"
|
||||
d={points(
|
||||
size / 2 - level * (segmentHeight + 4),
|
||||
progress * span,
|
||||
segmentHeight,
|
||||
)}
|
||||
/>
|
||||
<path
|
||||
fill="#fff"
|
||||
opacity="0.4"
|
||||
d={points(
|
||||
size / 2 - level * (segmentHeight + 4) - 4,
|
||||
progress * span * 0.98,
|
||||
4,
|
||||
)}
|
||||
/>
|
||||
{/each}
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</figure>
|
||||
|
||||
<style>
|
||||
svg {
|
||||
display: block;
|
||||
max-width: 80%;
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
|
||||
figure {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
place-content: center;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
margin: 2rem 0 1.5rem;
|
||||
}
|
||||
|
||||
.cardio {
|
||||
color: #5dc5f8;
|
||||
}
|
||||
|
||||
.gym {
|
||||
color: #f62b5a;
|
||||
}
|
||||
|
||||
.diet {
|
||||
color: #35d450;
|
||||
}
|
||||
|
||||
.fast {
|
||||
color: #ff00ff;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,23 +1,22 @@
|
|||
<script lang="ts">
|
||||
import { cardio, currentView } from "./store";
|
||||
import { cardio } from "./store";
|
||||
import {dm, back } from "./common";
|
||||
const remaining = $cardio.length;
|
||||
const days = ["ons", "fre", "mån"];
|
||||
let done = $derived($cardio.filter((c) => c.completed).length);
|
||||
|
||||
const back = () => {
|
||||
currentView.update((_) => 0);
|
||||
};
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<button onclick={() => back()}><-</button>
|
||||
<header>
|
||||
<button class="clickable back" onclick={() => back()}>←</button>
|
||||
<h1>
|
||||
Cardio {done}/{remaining}
|
||||
Konditionsträning: {done} av {remaining}
|
||||
</h1>
|
||||
<progress max={remaining} value={done}></progress>
|
||||
</header>
|
||||
<main>
|
||||
<progress hidden max={remaining} value={done}></progress>
|
||||
<div class="calendar">
|
||||
{#each $cardio as col, i}
|
||||
<article>
|
||||
<article class="clickable">
|
||||
<label>
|
||||
<input type="checkbox" bind:checked={col.completed} />
|
||||
<strong
|
||||
|
|
@ -25,47 +24,19 @@
|
|||
.map((m) => `${m} min`)
|
||||
.join(" + ")}, {col.pause_for} vila</strong
|
||||
>
|
||||
<small>{days[i % 3]} {col.planned_at}</small>
|
||||
<small>{days[i % 3]} {dm(col.planned_at)}</small>
|
||||
</label>
|
||||
</article>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
.calendar {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
gap: 0.5em 0.25em;
|
||||
main {
|
||||
background-color: #5dc5f8;
|
||||
border-top: 4px solid rgba(0, 0, 0, 0.2)
|
||||
}
|
||||
|
||||
article {
|
||||
border: 2px solid #aaa;
|
||||
border-radius: 7px;
|
||||
|
||||
label {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
grid-template-areas: "input name" "input date";
|
||||
padding: 1.5em;
|
||||
|
||||
input {
|
||||
grid-area: input;
|
||||
}
|
||||
strong {
|
||||
grid-area: name;
|
||||
}
|
||||
small {
|
||||
grid-area: date;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
article:has(:checked) {
|
||||
border-color: #080;
|
||||
background-color: #afa;
|
||||
}
|
||||
|
||||
|
||||
article:nth-child(1) {
|
||||
grid-column: 2;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,79 +1,71 @@
|
|||
<script lang="ts">
|
||||
import { diet, currentView } from "./store";
|
||||
const dayscount = $diet.days.length;
|
||||
let done = $derived($diet.days.filter((c) => c.completed).length);
|
||||
|
||||
const back = () => {
|
||||
currentView.update((_) => 0);
|
||||
};
|
||||
import { diet } from "./store";
|
||||
import { dm, back } from "./common";
|
||||
const dayscount = $derived($diet.filter((c) => !c.excluded).length);
|
||||
let done = $derived($diet.filter((c) => c.completed).length);
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<button onclick={() => back()}><-</button>
|
||||
<header>
|
||||
<button class="clickable back" onclick={() => back()}>←</button>
|
||||
<h1>
|
||||
Diet {done}/{dayscount}
|
||||
Diet: {done} av {dayscount}
|
||||
</h1>
|
||||
<progress max={dayscount} value={done}></progress>
|
||||
</header>
|
||||
<main>
|
||||
<progress max={dayscount} hidden value={done}></progress>
|
||||
|
||||
<div class="calendar">
|
||||
{#each $diet.days as day, i}
|
||||
<article>
|
||||
{#if day.date in $diet.reds}
|
||||
{#each $diet as day, i}
|
||||
<article class="clickable">
|
||||
{#if day.excluded}
|
||||
<div class="red">
|
||||
{$diet.reds[day.date]}
|
||||
{day.because}
|
||||
</div>
|
||||
{:else}
|
||||
<label>
|
||||
<input type="checkbox" bind:checked={day.completed} />
|
||||
{day.date}
|
||||
{dm(day.date)}
|
||||
</label>
|
||||
{/if}
|
||||
</article>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
main {
|
||||
background-color: #35d450;
|
||||
border-top: 4px solid rgba(0, 0, 0, 0.2)
|
||||
}
|
||||
|
||||
.calendar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5em 0.25em;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
}
|
||||
|
||||
.red {
|
||||
background: #ddd;
|
||||
color: #888;
|
||||
padding: 1.5em;
|
||||
padding: 0.75em;
|
||||
overflow: hidden;
|
||||
font-size: 0.66em;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
article:has(.red) {
|
||||
pointer-events: none;
|
||||
opacity: 0.33 !important;
|
||||
}
|
||||
|
||||
article {
|
||||
border: 2px solid #aaa;
|
||||
border-radius: 7px;
|
||||
> * {
|
||||
padding: 0.75em 0.5em;
|
||||
font-size: 0.9em;
|
||||
text-align: center;
|
||||
|
||||
label {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
grid-template-areas: "input name" "input date";
|
||||
padding: 1.5em;
|
||||
&:first-word {
|
||||
font-size: 2em;
|
||||
display: block;
|
||||
}
|
||||
|
||||
input {
|
||||
grid-area: input;
|
||||
}
|
||||
strong {
|
||||
grid-area: name;
|
||||
}
|
||||
small {
|
||||
grid-area: date;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
article:has(:checked) {
|
||||
border-color: #080;
|
||||
background-color: #afa;
|
||||
}
|
||||
|
||||
article:nth-child(1) {
|
||||
grid-column: 3;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
39
src/lib/FastProgress.svelte
Normal file
39
src/lib/FastProgress.svelte
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<script lang="ts">
|
||||
import { fast } from "./store";
|
||||
import { dm, back } from "./common";
|
||||
const remaining = $fast.length;
|
||||
const days = ["ons", "fre", "mån"];
|
||||
let done = $derived($fast.filter((c) => c.completed).length);
|
||||
</script>
|
||||
|
||||
<header>
|
||||
<button class="clickable back" onclick={() => back()}>←</button>
|
||||
<h1>
|
||||
Fasta: {done} av {remaining}
|
||||
</h1>
|
||||
</header>
|
||||
<main>
|
||||
<progress hidden max={remaining} value={done}></progress>
|
||||
<div class="calendar">
|
||||
{#each $fast as col, i}
|
||||
<article class="clickable">
|
||||
<label>
|
||||
<input type="checkbox" bind:checked={col.completed} />
|
||||
<strong>{col.name}</strong>
|
||||
<small>{days[i % 3]} {dm(col.planned_at)}</small>
|
||||
</label>
|
||||
</article>
|
||||
{/each}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
main {
|
||||
background-color: #5dc5f8;
|
||||
border-top: 4px solid rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
article:nth-child(1) {
|
||||
grid-column: 2;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,64 +1,36 @@
|
|||
<script lang="ts">
|
||||
import { gym, currentView } from "./store";
|
||||
import { gym } from "./store";
|
||||
import {dm, back } from "./common";
|
||||
const remaining = $gym.length;
|
||||
const days = ["sön", "tis", "tor"];
|
||||
let done = $derived($gym.filter((c) => c.completed).length);
|
||||
|
||||
const back = () => {
|
||||
currentView.update((_) => 0);
|
||||
};
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<button onclick={() => back()}><-</button>
|
||||
<header>
|
||||
<button class="clickable back" onclick={() => back()}>←</button>
|
||||
<h1>
|
||||
Gym {done}/{remaining}
|
||||
Styrketräning: {done} av {remaining}
|
||||
</h1>
|
||||
<progress max={remaining} value={done}></progress>
|
||||
</header>
|
||||
<main>
|
||||
<progress hidden max={remaining} value={done}></progress>
|
||||
<div class="calendar">
|
||||
{#each $gym as col}
|
||||
<article>
|
||||
{#each $gym as col, i}
|
||||
<article class="clickable">
|
||||
<label>
|
||||
<input type="checkbox" bind:checked={col.completed} />
|
||||
<strong>{col.name}</strong>
|
||||
<small>{col.planned_at}</small>
|
||||
<small>{days[i % 3]} {dm(col.planned_at)}</small>
|
||||
</label>
|
||||
</article>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
.calendar {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
gap: 0.5em 0.25em;
|
||||
}
|
||||
|
||||
article {
|
||||
border: 2px solid #aaa;
|
||||
border-radius: 7px;
|
||||
|
||||
label {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
grid-template-areas: "input name" "input date";
|
||||
padding: 1.5em;
|
||||
|
||||
input {
|
||||
grid-area: input;
|
||||
}
|
||||
strong {
|
||||
grid-area: name;
|
||||
}
|
||||
small {
|
||||
grid-area: date;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
article:has(:checked) {
|
||||
border-color: #080;
|
||||
background-color: #afa;
|
||||
main {
|
||||
background-color: #f62b5a;
|
||||
border-top: 4px solid rgba(0, 0, 0, 0.2)
|
||||
}
|
||||
|
||||
article:nth-child(1) {
|
||||
|
|
|
|||
92
src/lib/Info.svelte
Normal file
92
src/lib/Info.svelte
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
<script lang="ts">
|
||||
import { currentView } from "./store";
|
||||
const back = () => {
|
||||
currentView.update((_) => 0);
|
||||
};
|
||||
</script>
|
||||
|
||||
<header>
|
||||
<button class="clickable back" onclick={() => back()}>←</button>
|
||||
<h1>Va?</h1>
|
||||
</header>
|
||||
<main>
|
||||
<div class="infobox">
|
||||
<h2>Hej, Anders har en 40-årskris xD</h2>
|
||||
<p><em>Dessutom hägrar en vecka på Sweden rock festival.</em>
|
||||
Ryggen och benen behöver vara i form.</p>
|
||||
<p>
|
||||
För att göra träningen litet mer belönande skapades denna app för att ge honom
|
||||
möjlighet att bocka i allt han gör.
|
||||
</p>
|
||||
<h2>Börja löpträna</h2>
|
||||
<p>Anders har tidigare löptränat, men inte gjort det kontinuerligt sedan 2017.</p>
|
||||
<p><b>Metod:</b> löpträning 3 gånger per vecka: 2 pass med 2 intervaller, ett långpass. Linjär
|
||||
progression med 60–120 sekunders ökning per pass.</p>
|
||||
<strong>Mål: 5km löpning efter 7 veckor.</strong>
|
||||
<h2>Återuppta styrkelyft</h2>
|
||||
<p>Anders föredrar baslyft med frivikter. Hans PB slogs senast januari 2023.</p>
|
||||
<p>
|
||||
<b>Metod</b>: styrketräning 3 gånger per vecka. 4 veckor grundträning (A-blocket),
|
||||
4 veckor specialiserad träning (B-blocket), 3 veckor toppning (C-blocket).
|
||||
</p>
|
||||
<strong>Mål: 11 veckors träning, med avslutande toppning och
|
||||
styrkelyftstotal.</strong>
|
||||
<h2>Inleda diet</h2>
|
||||
<p>
|
||||
Anders har ett uppskattat BMI på 28-32 vid start av denna utmaning.
|
||||
</p>
|
||||
<p>
|
||||
<b>Metod</b>: Följa
|
||||
<a href="https://www.youtube.com/watch?v=fB_ESE2XwOU">Alan Thralls tips</a>, som listas nedan.
|
||||
Anders cheat meals: Semla på semmeldagen, fika på födelsedagen, liten godispåse på lördagar.
|
||||
</p>
|
||||
<blockquote>
|
||||
<ul>
|
||||
<li title="Nothing except meals.">Stop snacking.</li>
|
||||
<li
|
||||
title="Fat source you will get anyway. Vegetables not included, eat free."
|
||||
>
|
||||
1 protein, 1 carb per meal.
|
||||
</li>
|
||||
<li title="no 'ands'. Do not add anything to the meal.">
|
||||
Avoid addons.
|
||||
</li>
|
||||
<li title="No soda, no chips, no västerbottensticks.">
|
||||
Fast food? just get 1 item
|
||||
</li>
|
||||
<li title="no beer, no soda, no milk - just drink water">
|
||||
No liquid calories.
|
||||
</li>
|
||||
</ul>
|
||||
</blockquote>
|
||||
<p><strong>Mål: Följa ovan kostråd 16 veckor, med fördefinierade cheat meals.</strong></p>
|
||||
<h2>Vem?</h2>
|
||||
<p>
|
||||
Anders skapade denna PWA. Han är en webbutvecklare som är pappa, älskar hårdrock och föredrar att
|
||||
betrakta sina datorer som byggsatser.
|
||||
</p>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
h2 {
|
||||
margin-bottom: 0;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0.66rem;
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
background-color: #ff0;
|
||||
border-top: 4px solid rgba(0, 0, 0, 0.2)
|
||||
}
|
||||
|
||||
.infobox {
|
||||
margin: 0.1em;
|
||||
background-color: #fff;
|
||||
border: 3px solid #aaa;
|
||||
border-radius: 1rem;
|
||||
padding: 0.5em 1em;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,62 +1,205 @@
|
|||
<script lang="ts">
|
||||
import { gym, cardio, diet, currentView } from "./store";
|
||||
let gymProgress = $derived($gym.filter((c) => c.completed).length);
|
||||
let cardioProgress = $derived($cardio.filter((c) => c.completed).length);
|
||||
let dietProgress = $derived($diet.days.filter((c) => c.completed).length);
|
||||
import { gym, fast, cardio, diet, currentView } from "./store";
|
||||
import Arc from "./Arc.svelte";
|
||||
let gymProgress = $derived($gym.filter((c) => c.completed).length);
|
||||
let cardioProgress = $derived($cardio.filter((c) => c.completed).length);
|
||||
let fastProgress = $derived($fast.filter((c) => c.completed).length);
|
||||
let dietProgress = $derived($diet.filter((c) => c.completed).length);
|
||||
let dietTotal = $derived($diet.filter((c) => !c.excluded).length);
|
||||
|
||||
const navigate = (v: 0 | 1 | 2 | 3) => {
|
||||
currentView.update((_) => v);
|
||||
};
|
||||
const navigate = (v: 0 | 1 | 2 | 3 | 4 | 5) => {
|
||||
currentView.update((_) => v);
|
||||
};
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<h1>Summary</h1>
|
||||
<ul class="cards">
|
||||
<li>
|
||||
<button onclick={() => navigate(1)}>
|
||||
<i>{gymProgress}</i> / {$gym.length} <span>gympass gjorda</span>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button onclick={() => navigate(2)}>
|
||||
<i>{cardioProgress}</i> / {$cardio.length}
|
||||
<span>löppass lufsade</span>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button onclick={() => navigate(3)}>
|
||||
<i>{dietProgress}</i> / {$diet.days.length}
|
||||
<span>dagar på diet</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<main>
|
||||
<h1>Dags att komma i form!</h1>
|
||||
<Arc />
|
||||
<div class="progress">
|
||||
<h2>Styrketräning, 3d/v under 11 veckor</h2>
|
||||
<div class="progress-row">
|
||||
<progress class="gym" value={gymProgress} max={$gym.length}></progress>
|
||||
<i>{gymProgress} / {$gym.length}</i>
|
||||
<button class="clickable gym" onclick={() => navigate(1)}>+</button>
|
||||
</div>
|
||||
<h2>Konditionsträning, 3d/v under 7 veckor</h2>
|
||||
<div class="progress-row">
|
||||
<progress class="cardio" value={cardioProgress} max={$cardio.length}
|
||||
></progress>
|
||||
<i>{cardioProgress} / {$cardio.length}</i>
|
||||
<button class="clickable cardio" onclick={() => navigate(2)}>+</button>
|
||||
</div>
|
||||
<h2>Diet, feb–maj</h2>
|
||||
<div class="progress-row">
|
||||
<progress class="diet" value={dietProgress} max={dietTotal}></progress>
|
||||
<i>{dietProgress} / {dietTotal}</i>
|
||||
<button class="clickable diet" onclick={() => navigate(3)}>+</button>
|
||||
</div>
|
||||
<h2>Fasta, 3d/v under maj</h2>
|
||||
<div class="progress-row">
|
||||
<progress class="fast" value={fastProgress} max={$fast.length}></progress>
|
||||
<i>{fastProgress} / {$fast.length}</i>
|
||||
<button class="clickable fast" onclick={() => navigate(5)}>+</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="top-button">
|
||||
<button class="clickable info" onclick={() => navigate(4)}> Va? </button>
|
||||
</div>
|
||||
<p class="madeby">Skapad av <a href="https://madr.se">Anders</a></p>
|
||||
</main>
|
||||
|
||||
<style>
|
||||
.cards {
|
||||
list-style: none;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 1em;
|
||||
.madeby {
|
||||
text-align: center;
|
||||
font-size: 0.75em;
|
||||
}
|
||||
|
||||
> li {
|
||||
flex: 1;
|
||||
border: 2px solid #aaa;
|
||||
border-radius: 5px;
|
||||
padding: 0;
|
||||
main {
|
||||
background: #222;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
i {
|
||||
font-size: 2em;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
span {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
progress {
|
||||
&.cardio {
|
||||
--pcolor: #5dc5f8;
|
||||
}
|
||||
|
||||
&.diet {
|
||||
--pcolor: #35d450;
|
||||
}
|
||||
|
||||
&.gym {
|
||||
--pcolor: #f62b5a;
|
||||
}
|
||||
|
||||
&.fast {
|
||||
--pcolor: #ff00ff;
|
||||
}
|
||||
}
|
||||
|
||||
progress,
|
||||
::-webkit-progress-bar {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
height: var(--h);
|
||||
display: block;
|
||||
width: calc(100% - 2rem);
|
||||
border: none;
|
||||
padding: 0;
|
||||
background-color: rgba(128, 128, 128, 0.25);
|
||||
margin: 0 1rem;
|
||||
border-radius: var(--br);
|
||||
}
|
||||
|
||||
::-webkit-progress-bar {
|
||||
margin-right: 0;
|
||||
margin-left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/*
|
||||
Nope, cannot comma separate selector like this:
|
||||
|
||||
::-moz-progress-bar,
|
||||
::-webkit-progress-value { ... }
|
||||
|
||||
or even like this:
|
||||
|
||||
progress::-webkit-progress-value { ... }
|
||||
|
||||
Bug, closed with wontfix in 2015:
|
||||
https://issues.chromium.org/issues/40564916
|
||||
|
||||
Sincerely, FUCK YOU, Chrome dev team.
|
||||
*/
|
||||
::-moz-progress-bar {
|
||||
background-color: var(--pcolor);
|
||||
background-image: linear-gradient(
|
||||
rgba(255, 255, 255, 0.25) 0,
|
||||
rgba(255, 255, 255, 0.25) 4px,
|
||||
var(--pcolor) 4px,
|
||||
var(--pcolor)
|
||||
);
|
||||
border: 0.25rem solid var(--pcolor);
|
||||
border-width: 0.25rem 0.5rem;
|
||||
border-radius: var(--br);
|
||||
}
|
||||
|
||||
::-webkit-progress-value {
|
||||
background-color: var(--pcolor);
|
||||
background-image: linear-gradient(
|
||||
rgba(255, 255, 255, 0.25) 0,
|
||||
rgba(255, 255, 255, 0.25) 4px,
|
||||
var(--pcolor) 4px,
|
||||
var(--pcolor)
|
||||
);
|
||||
border: 0.25rem solid var(--pcolor);
|
||||
border-width: 0.25rem 0.5rem;
|
||||
border-radius: var(--br);
|
||||
}
|
||||
|
||||
.progress {
|
||||
--pcolor: #f00;
|
||||
--h: 1.66rem;
|
||||
--br: calc(var(--h) / 2);
|
||||
--mg: 0.75rem;
|
||||
--border: 3px solid #444;
|
||||
margin: 2rem 0;
|
||||
border: var(--border);
|
||||
border-radius: 1rem;
|
||||
padding-bottom: var(--mg);
|
||||
|
||||
> h2 {
|
||||
padding: 0 1rem;
|
||||
margin: var(--mg) 0;
|
||||
font-size: 1em;
|
||||
line-height: 1.25;
|
||||
|
||||
* + & {
|
||||
border-top: var(--border);
|
||||
padding-top: var(--mg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.progress-row {
|
||||
position: relative;
|
||||
|
||||
> i {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-style: normal;
|
||||
font-size: 0.9em;
|
||||
font-weight: bold;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
> button {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 0.5rem;
|
||||
transform: translateY(-50%);
|
||||
color: #fff;
|
||||
background-color: var(--btn-bg);
|
||||
padding: 0.25em 0.66em;
|
||||
|
||||
&.cardio {
|
||||
--btn-bg: #5dc5f8;
|
||||
}
|
||||
|
||||
&.diet {
|
||||
--btn-bg: #35d450;
|
||||
}
|
||||
|
||||
&.gym {
|
||||
--btn-bg: #f62b5a;
|
||||
}
|
||||
|
||||
&.fast {
|
||||
--btn-bg: #ff00ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
25
src/lib/common.ts
Normal file
25
src/lib/common.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import { currentView } from "./store";
|
||||
|
||||
const M = [
|
||||
"jan",
|
||||
"feb",
|
||||
"mar",
|
||||
"apr",
|
||||
"maj",
|
||||
"jun",
|
||||
"jul",
|
||||
"aug",
|
||||
"sep",
|
||||
"okt",
|
||||
"nov",
|
||||
"dec",
|
||||
];
|
||||
|
||||
export const back = () => {
|
||||
currentView.update((_) => 0);
|
||||
};
|
||||
|
||||
export const dm = (date) => {
|
||||
const d = new Date(date);
|
||||
return `${d.getDate()} ${M[d.getMonth()]}`;
|
||||
};
|
||||
|
|
@ -7,6 +7,7 @@ export const currentView = writable(0);
|
|||
export const cardio = persisted("cardio", defaultState.cardio);
|
||||
export const gym = persisted("gym", defaultState.gym);
|
||||
export const diet = persisted("diet", defaultState.diet);
|
||||
export const fast = persisted("fast", defaultState.fast);
|
||||
|
||||
export type Exercise = {
|
||||
completed: boolean;
|
||||
|
|
|
|||
31
src/lib/svg-path.ts
Normal file
31
src/lib/svg-path.ts
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
export default function () {
|
||||
const commands: string[] = [];
|
||||
return {
|
||||
stringify() {
|
||||
return `${commands.join("\n")}`;
|
||||
},
|
||||
arcTo(
|
||||
ry: number,
|
||||
rx: number,
|
||||
long: boolean,
|
||||
cw: boolean,
|
||||
y: number,
|
||||
x: number,
|
||||
) {
|
||||
commands.push(`A ${rx} ${ry} 0 ${long ? 1 : 0} ${cw ? 1 : 0} ${x} ${y}`);
|
||||
return this;
|
||||
},
|
||||
moveTo(y: number, x: number) {
|
||||
commands.push(`M ${x} ${y}`);
|
||||
return this;
|
||||
},
|
||||
lineTo(y: number, x: number) {
|
||||
commands.push(`L ${x} ${y}`);
|
||||
return this;
|
||||
},
|
||||
close() {
|
||||
commands.push("z");
|
||||
return this;
|
||||
},
|
||||
};
|
||||
}
|
||||
12
src/main.ts
12
src/main.ts
|
|
@ -1,9 +1,9 @@
|
|||
import { mount } from 'svelte'
|
||||
import './app.css'
|
||||
import App from './App.svelte'
|
||||
import { mount } from "svelte";
|
||||
import "./app.css";
|
||||
import App from "./App.svelte";
|
||||
|
||||
const app = mount(App, {
|
||||
target: document.getElementById('app')!,
|
||||
})
|
||||
target: document.getElementById("app")!,
|
||||
});
|
||||
|
||||
export default app
|
||||
export default app;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,38 @@
|
|||
import { defineConfig } from 'vite'
|
||||
import { svelte } from '@sveltejs/vite-plugin-svelte'
|
||||
import { defineConfig } from "vite";
|
||||
import { VitePWA } from "vite-plugin-pwa";
|
||||
import { svelte } from "@sveltejs/vite-plugin-svelte";
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [svelte()],
|
||||
})
|
||||
plugins: [
|
||||
svelte(),
|
||||
VitePWA({
|
||||
injectRegister: "auto",
|
||||
includeAssets: ["icon512_rounded.png", "icon512_maskable.png"],
|
||||
manifest: {
|
||||
theme_color: "#8936FF",
|
||||
background_color: "#222",
|
||||
icons: [
|
||||
{
|
||||
purpose: "maskable",
|
||||
sizes: "512x512",
|
||||
src: "icon512_maskable.png",
|
||||
type: "image/png",
|
||||
},
|
||||
{
|
||||
purpose: "any",
|
||||
sizes: "512x512",
|
||||
src: "icon512_rounded.png",
|
||||
type: "image/png",
|
||||
},
|
||||
],
|
||||
orientation: "portrait",
|
||||
display: "standalone",
|
||||
dir: "ltr",
|
||||
lang: "sv-SE",
|
||||
name: "Strand",
|
||||
short_name: "strand",
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue