Compare commits

...

3 commits

Author SHA1 Message Date
b8bee291b3 Add pocketbase js SDK and typings 2023-12-08 21:34:50 +01:00
0d330c4f60 Add pocketbase migrations 2023-12-08 21:34:16 +01:00
c75003f5f0 Create svelte ionic app 2023-12-08 20:52:41 +01:00
41 changed files with 6136 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
/pocketbase
/pb_data/

View file

@ -1,2 +1,8 @@
# getstuffdone
## update types
```
npx pocketbase-typegen --db ./pb_data/data.db --out app/src/lib/pocketbase-types.ts
```

13
app/.eslintignore Normal file
View file

@ -0,0 +1,13 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

31
app/.eslintrc.cjs Normal file
View file

@ -0,0 +1,31 @@
/** @type { import("eslint").Linter.FlatConfig } */
module.exports = {
root: true,
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:svelte/recommended',
'prettier'
],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
parserOptions: {
sourceType: 'module',
ecmaVersion: 2020,
extraFileExtensions: ['.svelte']
},
env: {
browser: true,
es2017: true,
node: true
},
overrides: [
{
files: ['*.svelte'],
parser: 'svelte-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser'
}
}
]
};

10
app/.gitignore vendored Normal file
View file

@ -0,0 +1,10 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
vite.config.js.timestamp-*
vite.config.ts.timestamp-*

1
app/.npmrc Normal file
View file

@ -0,0 +1 @@
engine-strict=true

13
app/.prettierignore Normal file
View file

@ -0,0 +1,13 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

8
app/.prettierrc Normal file
View file

@ -0,0 +1,8 @@
{
"useTabs": true,
"singleQuote": true,
"trailingComma": "none",
"printWidth": 100,
"plugins": ["prettier-plugin-svelte"],
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
}

38
app/README.md Normal file
View file

@ -0,0 +1,38 @@
# create-svelte
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/master/packages/create-svelte).
## Creating a project
If you're seeing this, you've probably already done this step. Congrats!
```bash
# create a new project in the current directory
npm create svelte@latest
# create a new project in my-app
npm create svelte@latest my-app
```
## Developing
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
```bash
npm run dev
# or start the server and open the app in a new browser tab
npm run dev -- --open
```
## Building
To create a production version of your app:
```bash
npm run build
```
You can preview the production build with `npm run preview`.
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.

22
app/capacitor.config.ts Normal file
View file

@ -0,0 +1,22 @@
import { CapacitorConfig } from '@capacitor/cli';
const appId = 'app.ionic.io';
const appName = 'app';
const server = process.argv.includes('-hmr') ? {
'url': 'http://192.168.178.25:5173', // always have http:// in url
'cleartext': true
} : {};
const webDir = 'build';
const config: CapacitorConfig = {
appId,
appName,
webDir,
server
};
if (process.argv.includes('-hmr')) console.log('WARNING: running capacitor with livereload config', config);
export default config;

4653
app/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

45
app/package.json Normal file
View file

@ -0,0 +1,45 @@
{
"name": "app",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "vite dev --host",
"build": "vite build",
"preview": "vite preview",
"test": "npm run test:integration && npm run test:unit",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --check . && eslint .",
"format": "prettier --write .",
"test:integration": "playwright test",
"test:unit": "vitest"
},
"devDependencies": {
"@capacitor/cli": "^5.5.1",
"@playwright/test": "^1.28.1",
"@sveltejs/adapter-static": "^2.0.3",
"@sveltejs/kit": "^1.27.4",
"@typescript-eslint/eslint-plugin": "^6.0.0",
"@typescript-eslint/parser": "^6.0.0",
"eslint": "^8.28.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-svelte": "^2.30.0",
"prettier": "^3.0.0",
"prettier-plugin-svelte": "^3.0.0",
"svelte": "^4.2.7",
"svelte-check": "^3.6.0",
"svelte-preprocess": "^5.1.1",
"tslib": "^2.4.1",
"typescript": "^5.0.0",
"vite": "^4.5.1",
"vitest": "^0.32.2"
},
"type": "module",
"dependencies": {
"@capacitor/core": "^5.5.1",
"@ionic/core": "^7.2.1",
"ionic-svelte": "^0.5.82",
"ionicons": "^7.2.1",
"pocketbase": "^0.19.0"
}
}

12
app/playwright.config.ts Normal file
View file

@ -0,0 +1,12 @@
import type { PlaywrightTestConfig } from '@playwright/test';
const config: PlaywrightTestConfig = {
webServer: {
command: 'npm run build && npm run preview',
port: 4173
},
testDir: 'tests',
testMatch: /(.+\.)?(test|spec)\.[jt]s/
};
export default config;

12
app/src/app.d.ts vendored Normal file
View file

@ -0,0 +1,12 @@
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface Platform {}
}
}
export {};

12
app/src/app.html Normal file
View file

@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>

7
app/src/index.test.ts Normal file
View file

@ -0,0 +1,7 @@
import { describe, it, expect } from 'vitest';
describe('sum test', () => {
it('adds 1 + 2 to equal 3', () => {
expect(1 + 2).toBe(3);
});
});

4
app/src/lib/api.ts Normal file
View file

@ -0,0 +1,4 @@
import PocketBase from "pocketbase";
import type { TypedPocketBase } from "./pocketbase-types"
export const pb = new PocketBase('http://127.0.0.1:8090') as TypedPocketBase

1
app/src/lib/index.ts Normal file
View file

@ -0,0 +1 @@
// place files you want to import through the `$lib` alias in this folder.

View file

@ -0,0 +1,92 @@
/**
* This file was @generated using pocketbase-typegen
*/
import type PocketBase from 'pocketbase'
import type { RecordService } from 'pocketbase'
export enum Collections {
Completions = "completions",
Lists = "lists",
Tasks = "tasks",
Users = "users",
}
// Alias types for improved usability
export type IsoDateString = string
export type RecordIdString = string
export type HTMLString = string
// System fields
export type BaseSystemFields<T = never> = {
id: RecordIdString
created: IsoDateString
updated: IsoDateString
collectionId: string
collectionName: Collections
expand?: T
}
export type AuthSystemFields<T = never> = {
email: string
emailVisibility: boolean
username: string
verified: boolean
} & BaseSystemFields<T>
// Record types for each collection
export type CompletionsRecord = {
task?: RecordIdString
user?: RecordIdString
}
export type ListsRecord = {
name?: string
parent?: RecordIdString
}
export type TasksRecord = {
cooldown?: number
description?: HTMLString
list: RecordIdString
name?: string
schedule?: IsoDateString
}
export type UsersRecord = {
avatar?: string
name?: string
}
// Response types include system fields and match responses from the PocketBase API
export type CompletionsResponse<Texpand = unknown> = Required<CompletionsRecord> & BaseSystemFields<Texpand>
export type ListsResponse<Texpand = unknown> = Required<ListsRecord> & BaseSystemFields<Texpand>
export type TasksResponse<Texpand = unknown> = Required<TasksRecord> & BaseSystemFields<Texpand>
export type UsersResponse<Texpand = unknown> = Required<UsersRecord> & AuthSystemFields<Texpand>
// Types containing all Records and Responses, useful for creating typing helper functions
export type CollectionRecords = {
completions: CompletionsRecord
lists: ListsRecord
tasks: TasksRecord
users: UsersRecord
}
export type CollectionResponses = {
completions: CompletionsResponse
lists: ListsResponse
tasks: TasksResponse
users: UsersResponse
}
// Type for usage with type asserted PocketBase instance
// https://github.com/pocketbase/js-sdk#specify-typescript-definitions
export type TypedPocketBase = PocketBase & {
collection(idOrName: 'completions'): RecordService<CompletionsResponse>
collection(idOrName: 'lists'): RecordService<ListsResponse>
collection(idOrName: 'tasks'): RecordService<TasksResponse>
collection(idOrName: 'users'): RecordService<UsersResponse>
}

View file

@ -0,0 +1,48 @@
<script lang="ts">
import { setupIonicBase } from 'ionic-svelte';
/* Call Ionic's setup routine */
setupIonicBase();
/* Import all components - or do partial loading - see below */
import 'ionic-svelte/components/all';
/* Theme variables */
import '../theme/variables.css';
/*
This part - import 'ionic-svelte/components/all'; - loads all components at once.
This adds at least >800kb (uncompressed) to your bundle - 80 components (so do your math!!)
You can also choose to import each component you want to use separately and leave out others.
It is recommended to do this in this file, as you only need to do such once. But you are free
to do this elsewhere if you like to code-split differently.
Example: if you replace the line import 'ionic-svelte/components/all'; with the imports below, you will see the resulting bundle being much smaller
import 'ionic-svelte/components/ion-app';
import 'ionic-svelte/components/ion-card';
import 'ionic-svelte/components/ion-card-title';
import 'ionic-svelte/components/ion-card-subtitle';
import 'ionic-svelte/components/ion-card-header';
import 'ionic-svelte/components/ion-card-content';
import 'ionic-svelte/components/ion-chip';
import 'ionic-svelte/components/ion-button';
Click the ionic-svelte-components-all-import above to go to the full list of possible imports.
Please don't forget to import ion-app in this file when you decide to code-split:
import 'ionic-svelte/components/ion-app';
You can report issues here - https://github.com/Tommertom/svelte-ionic-npm/issues
Want to know what is happening more - follow me on Twitter - https://twitter.com/Tommertomm
Discord channel on Ionic server - https://discordapp.com/channels/520266681499779082/1049388501629681675
*/
</script>
<ion-app>
<slot />
</ion-app>

View file

@ -0,0 +1 @@
export const ssr = false;

View file

@ -0,0 +1,43 @@
<ion-card>
<ion-card-header>
<ion-card-subtitle>Great success!!</ion-card-subtitle>
<ion-card-title>Welcome to your app!</ion-card-title>
</ion-card-header>
<ion-card-content>
Thank you for using this starter. Click buttons below to open these guides (will open in new
window). Don't forget to open DevTools to see this app in mobile mode. Happy coding!!!
</ion-card-content>
<ion-item>
<ion-label>Visit Ionic Showcase app with sourceviewer</ion-label>
<ion-button href="https://ionicsvelte.firebaseapp.com/" target="_new" fill="outline" slot="end"
>View</ion-button
>
</ion-item>
<ion-item>
<ion-label>Visit Ionic component docs</ion-label>
<ion-button
href="https://ionicframework.com/docs/components"
target="_new"
fill="outline"
slot="end">View</ion-button
>
</ion-item>
<ion-item>
<ion-label>Visit Svelte Kit docs</ion-label>
<ion-button
href="https://kit.svelte.dev/docs/introduction"
target="_new"
fill="outline"
slot="end">View</ion-button
>
</ion-item>
<ion-item>
<ion-label>Visit Svelte docs</ion-label>
<ion-button href="https://svelte.dev/docs" target="_new" fill="outline" slot="end"
>View</ion-button
>
</ion-item>
</ion-card>

View file

@ -0,0 +1,72 @@
<script lang="ts">
import { pb } from "$lib/api";
let email = '';
let password = '';
const handleLogin = async (event: Event) => {
// Prevent the form from submitting normally
event.preventDefault();
email = email.trim();
password = password.trim();
// Replace with your own login logic
if (email && password) {
console.log('Login submitted:', email, password);
const resp = await pb.collection("users").authWithPassword(email, password);
console.log('Logged in:', resp);
}
};
// Event handlers for ionic-input
const handleEmailInput = (event: { target: HTMLIonInputElement }) => {
email = event.target.value as string;
};
const handlePasswordInput = (event: { target: HTMLIonInputElement }) => {
password = event.target.value as string;
};
</script>
<ion-app>
<ion-header>
<ion-toolbar>
<ion-title>Login</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<form on:submit={handleLogin}>
<ion-item>
<ion-label position="stacked">Email</ion-label>
<ion-input
type="email"
placeholder="Enter your email"
value={email}
on:ionInput={handleEmailInput}
required
>
</ion-input>
</ion-item>
<ion-item>
<ion-label position="stacked">Password</ion-label>
<ion-input
type="password"
placeholder="Enter your password"
value={password}
on:ionInput={handlePasswordInput}
required
>
</ion-input>
</ion-item>
<ion-button type="submit" expand="block" class="ion-margin-top"> Login </ion-button>
</form>
</ion-content>
</ion-app>
<style>
/* Additional styles if necessary */
</style>

388
app/src/theme/variables.css Normal file
View file

@ -0,0 +1,388 @@
/* Ionic Variables and Theming. For more info, please see:
http://ionicframework.com/docs/theming/ */
/** Ionic CSS Variables **/
:root {
/** primary **/
--ion-color-primary: #3880ff;
--ion-color-primary-rgb: 56, 128, 255;
--ion-color-primary-contrast: #ffffff;
--ion-color-primary-contrast-rgb: 255, 255, 255;
--ion-color-primary-shade: #3171e0;
--ion-color-primary-tint: #4c8dff;
/** secondary **/
--ion-color-secondary: #3dc2ff;
--ion-color-secondary-rgb: 61, 194, 255;
--ion-color-secondary-contrast: #ffffff;
--ion-color-secondary-contrast-rgb: 255, 255, 255;
--ion-color-secondary-shade: #36abe0;
--ion-color-secondary-tint: #50c8ff;
/** tertiary **/
--ion-color-tertiary: #5260ff;
--ion-color-tertiary-rgb: 82, 96, 255;
--ion-color-tertiary-contrast: #ffffff;
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
--ion-color-tertiary-shade: #4854e0;
--ion-color-tertiary-tint: #6370ff;
/** success **/
--ion-color-success: #2dd36f;
--ion-color-success-rgb: 45, 211, 111;
--ion-color-success-contrast: #ffffff;
--ion-color-success-contrast-rgb: 255, 255, 255;
--ion-color-success-shade: #28ba62;
--ion-color-success-tint: #42d77d;
/** warning **/
--ion-color-warning: #ffc409;
--ion-color-warning-rgb: 255, 196, 9;
--ion-color-warning-contrast: #000000;
--ion-color-warning-contrast-rgb: 0, 0, 0;
--ion-color-warning-shade: #e0ac08;
--ion-color-warning-tint: #ffca22;
/** danger **/
--ion-color-danger: #eb445a;
--ion-color-danger-rgb: 235, 68, 90;
--ion-color-danger-contrast: #ffffff;
--ion-color-danger-contrast-rgb: 255, 255, 255;
--ion-color-danger-shade: #cf3c4f;
--ion-color-danger-tint: #ed576b;
/** dark **/
--ion-color-dark: #222428;
--ion-color-dark-rgb: 34, 36, 40;
--ion-color-dark-contrast: #ffffff;
--ion-color-dark-contrast-rgb: 255, 255, 255;
--ion-color-dark-shade: #1e2023;
--ion-color-dark-tint: #383a3e;
/** medium **/
--ion-color-medium: #92949c;
--ion-color-medium-rgb: 146, 148, 156;
--ion-color-medium-contrast: #ffffff;
--ion-color-medium-contrast-rgb: 255, 255, 255;
--ion-color-medium-shade: #808289;
--ion-color-medium-tint: #9d9fa6;
/** light **/
--ion-color-light: #f4f5f8;
--ion-color-light-rgb: 244, 245, 248;
--ion-color-light-contrast: #000000;
--ion-color-light-contrast-rgb: 0, 0, 0;
--ion-color-light-shade: #d7d8da;
--ion-color-light-tint: #f5f6f9;
}
@media (prefers-color-scheme: dark) {
/*
* Dark Colors
* -------------------------------------------
*/
body {
--ion-color-primary: #428cff;
--ion-color-primary-rgb: 66, 140, 255;
--ion-color-primary-contrast: #ffffff;
--ion-color-primary-contrast-rgb: 255, 255, 255;
--ion-color-primary-shade: #3a7be0;
--ion-color-primary-tint: #5598ff;
--ion-color-secondary: #50c8ff;
--ion-color-secondary-rgb: 80, 200, 255;
--ion-color-secondary-contrast: #ffffff;
--ion-color-secondary-contrast-rgb: 255, 255, 255;
--ion-color-secondary-shade: #46b0e0;
--ion-color-secondary-tint: #62ceff;
--ion-color-tertiary: #6a64ff;
--ion-color-tertiary-rgb: 106, 100, 255;
--ion-color-tertiary-contrast: #ffffff;
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
--ion-color-tertiary-shade: #5d58e0;
--ion-color-tertiary-tint: #7974ff;
--ion-color-success: #2fdf75;
--ion-color-success-rgb: 47, 223, 117;
--ion-color-success-contrast: #000000;
--ion-color-success-contrast-rgb: 0, 0, 0;
--ion-color-success-shade: #29c467;
--ion-color-success-tint: #44e283;
--ion-color-warning: #ffd534;
--ion-color-warning-rgb: 255, 213, 52;
--ion-color-warning-contrast: #000000;
--ion-color-warning-contrast-rgb: 0, 0, 0;
--ion-color-warning-shade: #e0bb2e;
--ion-color-warning-tint: #ffd948;
--ion-color-danger: #ff4961;
--ion-color-danger-rgb: 255, 73, 97;
--ion-color-danger-contrast: #ffffff;
--ion-color-danger-contrast-rgb: 255, 255, 255;
--ion-color-danger-shade: #e04055;
--ion-color-danger-tint: #ff5b71;
--ion-color-dark: #f4f5f8;
--ion-color-dark-rgb: 244, 245, 248;
--ion-color-dark-contrast: #000000;
--ion-color-dark-contrast-rgb: 0, 0, 0;
--ion-color-dark-shade: #d7d8da;
--ion-color-dark-tint: #f5f6f9;
--ion-color-medium: #989aa2;
--ion-color-medium-rgb: 152, 154, 162;
--ion-color-medium-contrast: #000000;
--ion-color-medium-contrast-rgb: 0, 0, 0;
--ion-color-medium-shade: #86888f;
--ion-color-medium-tint: #a2a4ab;
--ion-color-light: #222428;
--ion-color-light-rgb: 34, 36, 40;
--ion-color-light-contrast: #ffffff;
--ion-color-light-contrast-rgb: 255, 255, 255;
--ion-color-light-shade: #1e2023;
--ion-color-light-tint: #383a3e;
}
/*
* iOS Dark Theme
* -------------------------------------------
*/
.ios body {
--ion-background-color: #000000;
--ion-background-color-rgb: 0, 0, 0;
--ion-text-color: #ffffff;
--ion-text-color-rgb: 255, 255, 255;
--ion-color-step-50: #0d0d0d;
--ion-color-step-100: #1a1a1a;
--ion-color-step-150: #262626;
--ion-color-step-200: #333333;
--ion-color-step-250: #404040;
--ion-color-step-300: #4d4d4d;
--ion-color-step-350: #595959;
--ion-color-step-400: #666666;
--ion-color-step-450: #737373;
--ion-color-step-500: #808080;
--ion-color-step-550: #8c8c8c;
--ion-color-step-600: #999999;
--ion-color-step-650: #a6a6a6;
--ion-color-step-700: #b3b3b3;
--ion-color-step-750: #bfbfbf;
--ion-color-step-800: #cccccc;
--ion-color-step-850: #d9d9d9;
--ion-color-step-900: #e6e6e6;
--ion-color-step-950: #f2f2f2;
--ion-item-background: #000000;
--ion-card-background: #1c1c1d;
}
.ios ion-modal {
--ion-background-color: var(--ion-color-step-100);
--ion-toolbar-background: var(--ion-color-step-150);
--ion-toolbar-border-color: var(--ion-color-step-250);
}
/*
* Material Design Dark Theme
* -------------------------------------------
*/
.md body {
--ion-background-color: #121212;
--ion-background-color-rgb: 18, 18, 18;
--ion-text-color: #ffffff;
--ion-text-color-rgb: 255, 255, 255;
--ion-border-color: #222222;
--ion-color-step-50: #1e1e1e;
--ion-color-step-100: #2a2a2a;
--ion-color-step-150: #363636;
--ion-color-step-200: #414141;
--ion-color-step-250: #4d4d4d;
--ion-color-step-300: #595959;
--ion-color-step-350: #656565;
--ion-color-step-400: #717171;
--ion-color-step-450: #7d7d7d;
--ion-color-step-500: #898989;
--ion-color-step-550: #949494;
--ion-color-step-600: #a0a0a0;
--ion-color-step-650: #acacac;
--ion-color-step-700: #b8b8b8;
--ion-color-step-750: #c4c4c4;
--ion-color-step-800: #d0d0d0;
--ion-color-step-850: #dbdbdb;
--ion-color-step-900: #e7e7e7;
--ion-color-step-950: #f3f3f3;
--ion-item-background: #1e1e1e;
--ion-toolbar-background: #1f1f1f;
--ion-tab-bar-background: #1f1f1f;
--ion-card-background: #1e1e1e;
}
}
/*
* Classic class selector way - assign dark md or dark ios to force dark mode
* See https://ionicframework.com/docs/theming/dark-mode
*/
body.dark {
/* Dark mode variables go here */
/*
* Dark Colors
* -------------------------------------------
*/
--ion-color-primary: #428cff;
--ion-color-primary-rgb: 66, 140, 255;
--ion-color-primary-contrast: #ffffff;
--ion-color-primary-contrast-rgb: 255, 255, 255;
--ion-color-primary-shade: #3a7be0;
--ion-color-primary-tint: #5598ff;
--ion-color-secondary: #50c8ff;
--ion-color-secondary-rgb: 80, 200, 255;
--ion-color-secondary-contrast: #ffffff;
--ion-color-secondary-contrast-rgb: 255, 255, 255;
--ion-color-secondary-shade: #46b0e0;
--ion-color-secondary-tint: #62ceff;
--ion-color-tertiary: #6a64ff;
--ion-color-tertiary-rgb: 106, 100, 255;
--ion-color-tertiary-contrast: #ffffff;
--ion-color-tertiary-contrast-rgb: 255, 255, 255;
--ion-color-tertiary-shade: #5d58e0;
--ion-color-tertiary-tint: #7974ff;
--ion-color-success: #2fdf75;
--ion-color-success-rgb: 47, 223, 117;
--ion-color-success-contrast: #000000;
--ion-color-success-contrast-rgb: 0, 0, 0;
--ion-color-success-shade: #29c467;
--ion-color-success-tint: #44e283;
--ion-color-warning: #ffd534;
--ion-color-warning-rgb: 255, 213, 52;
--ion-color-warning-contrast: #000000;
--ion-color-warning-contrast-rgb: 0, 0, 0;
--ion-color-warning-shade: #e0bb2e;
--ion-color-warning-tint: #ffd948;
--ion-color-danger: #ff4961;
--ion-color-danger-rgb: 255, 73, 97;
--ion-color-danger-contrast: #ffffff;
--ion-color-danger-contrast-rgb: 255, 255, 255;
--ion-color-danger-shade: #e04055;
--ion-color-danger-tint: #ff5b71;
--ion-color-dark: #f4f5f8;
--ion-color-dark-rgb: 244, 245, 248;
--ion-color-dark-contrast: #000000;
--ion-color-dark-contrast-rgb: 0, 0, 0;
--ion-color-dark-shade: #d7d8da;
--ion-color-dark-tint: #f5f6f9;
--ion-color-medium: #989aa2;
--ion-color-medium-rgb: 152, 154, 162;
--ion-color-medium-contrast: #000000;
--ion-color-medium-contrast-rgb: 0, 0, 0;
--ion-color-medium-shade: #86888f;
--ion-color-medium-tint: #a2a4ab;
--ion-color-light: #222428;
--ion-color-light-rgb: 34, 36, 40;
--ion-color-light-contrast: #ffffff;
--ion-color-light-contrast-rgb: 255, 255, 255;
--ion-color-light-shade: #1e2023;
--ion-color-light-tint: #383a3e;
}
body.dark.ios {
--ion-background-color: #000000;
--ion-background-color-rgb: 0, 0, 0;
--ion-text-color: #ffffff;
--ion-text-color-rgb: 255, 255, 255;
--ion-color-step-50: #0d0d0d;
--ion-color-step-100: #1a1a1a;
--ion-color-step-150: #262626;
--ion-color-step-200: #333333;
--ion-color-step-250: #404040;
--ion-color-step-300: #4d4d4d;
--ion-color-step-350: #595959;
--ion-color-step-400: #666666;
--ion-color-step-450: #737373;
--ion-color-step-500: #808080;
--ion-color-step-550: #8c8c8c;
--ion-color-step-600: #999999;
--ion-color-step-650: #a6a6a6;
--ion-color-step-700: #b3b3b3;
--ion-color-step-750: #bfbfbf;
--ion-color-step-800: #cccccc;
--ion-color-step-850: #d9d9d9;
--ion-color-step-900: #e6e6e6;
--ion-color-step-950: #f2f2f2;
--ion-item-background: #000000;
--ion-card-background: #1c1c1d;
}
body.dark.ios ion-modal {
--ion-background-color: var(--ion-color-step-100);
--ion-toolbar-background: var(--ion-color-step-150);
--ion-toolbar-border-color: var(--ion-color-step-250);
}
body.dark.md {
--ion-background-color: #121212;
--ion-background-color-rgb: 18, 18, 18;
--ion-text-color: #ffffff;
--ion-text-color-rgb: 255, 255, 255;
--ion-border-color: #222222;
--ion-color-step-50: #1e1e1e;
--ion-color-step-100: #2a2a2a;
--ion-color-step-150: #363636;
--ion-color-step-200: #414141;
--ion-color-step-250: #4d4d4d;
--ion-color-step-300: #595959;
--ion-color-step-350: #656565;
--ion-color-step-400: #717171;
--ion-color-step-450: #7d7d7d;
--ion-color-step-500: #898989;
--ion-color-step-550: #949494;
--ion-color-step-600: #a0a0a0;
--ion-color-step-650: #acacac;
--ion-color-step-700: #b8b8b8;
--ion-color-step-750: #c4c4c4;
--ion-color-step-800: #d0d0d0;
--ion-color-step-850: #dbdbdb;
--ion-color-step-900: #e7e7e7;
--ion-color-step-950: #f3f3f3;
--ion-item-background: #1e1e1e;
--ion-toolbar-background: #1f1f1f;
--ion-tab-bar-background: #1f1f1f;
--ion-card-background: #1e1e1e;
}

BIN
app/static/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

18
app/svelte.config.js Normal file
View file

@ -0,0 +1,18 @@
import adapter from '@sveltejs/adapter-static';
import preprocess from 'svelte-preprocess';
/** @type {import('@sveltejs/kit').Config} */
const config = {
preprocess: preprocess(),
kit: {
adapter: adapter({
pages: 'build',
assets: 'build',
fallback: 'index.html',
precompress: false
})
}
};
export default config;

6
app/tests/test.ts Normal file
View file

@ -0,0 +1,6 @@
import { expect, test } from '@playwright/test';
test('index page has expected h1', async ({ page }) => {
await page.goto('/');
await expect(page.getByRole('heading', { name: 'Welcome to SvelteKit' })).toBeVisible();
});

24
app/tsconfig.json Normal file
View file

@ -0,0 +1,24 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"typeRoots": [
"./node_modules/ionic-svelte"
],
"types": [
"ionic-svelte"
],
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"moduleResolution": "bundler"
}
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
//
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
// from the referenced tsconfig.json - TypeScript does not merge them in
}

9
app/vite.config.ts Normal file
View file

@ -0,0 +1,9 @@
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vitest/config';
export default defineConfig({
plugins: [sveltekit()],
test: {
include: ['src/**/*.{test,spec}.{js,ts}']
}
});

View file

@ -0,0 +1,82 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((db) => {
const collection = new Collection({
"id": "pf13z4hs1iubw41",
"created": "2023-12-08 18:40:59.946Z",
"updated": "2023-12-08 18:40:59.946Z",
"name": "tasks",
"type": "base",
"system": false,
"schema": [
{
"system": false,
"id": "hthpvzja",
"name": "name",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "hrzb5spy",
"name": "description",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "qjz9b0tm",
"name": "schedule",
"type": "date",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": "",
"max": ""
}
},
{
"system": false,
"id": "ryukqaol",
"name": "repeatInterval",
"type": "number",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"noDecimal": false
}
}
],
"indexes": [],
"listRule": null,
"viewRule": null,
"createRule": null,
"updateRule": null,
"deleteRule": null,
"options": {}
});
return Dao(db).saveCollection(collection);
}, (db) => {
const dao = new Dao(db);
const collection = dao.findCollectionByNameOrId("pf13z4hs1iubw41");
return dao.deleteCollection(collection);
})

View file

@ -0,0 +1,41 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((db) => {
const collection = new Collection({
"id": "i42xt9r6ykqdq3j",
"created": "2023-12-08 18:41:27.908Z",
"updated": "2023-12-08 18:41:27.908Z",
"name": "tasklists",
"type": "base",
"system": false,
"schema": [
{
"system": false,
"id": "gjhxaxm4",
"name": "name",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
}
],
"indexes": [],
"listRule": null,
"viewRule": null,
"createRule": null,
"updateRule": null,
"deleteRule": null,
"options": {}
});
return Dao(db).saveCollection(collection);
}, (db) => {
const dao = new Dao(db);
const collection = dao.findCollectionByNameOrId("i42xt9r6ykqdq3j");
return dao.deleteCollection(collection);
})

View file

@ -0,0 +1,48 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("pf13z4hs1iubw41")
// remove
collection.schema.removeField("hrzb5spy")
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "bkjgp8xt",
"name": "description",
"type": "editor",
"required": false,
"presentable": false,
"unique": false,
"options": {
"convertUrls": false
}
}))
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("pf13z4hs1iubw41")
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "hrzb5spy",
"name": "description",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
}))
// remove
collection.schema.removeField("bkjgp8xt")
return dao.saveCollection(collection)
})

View file

@ -0,0 +1,33 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("pf13z4hs1iubw41")
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "6gfllcfj",
"name": "list",
"type": "relation",
"required": false,
"presentable": false,
"unique": false,
"options": {
"collectionId": "i42xt9r6ykqdq3j",
"cascadeDelete": false,
"minSelect": null,
"maxSelect": 1,
"displayFields": null
}
}))
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("pf13z4hs1iubw41")
// remove
collection.schema.removeField("6gfllcfj")
return dao.saveCollection(collection)
})

View file

@ -0,0 +1,48 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("pf13z4hs1iubw41")
// update
collection.schema.addField(new SchemaField({
"system": false,
"id": "6gfllcfj",
"name": "list",
"type": "relation",
"required": false,
"presentable": false,
"unique": false,
"options": {
"collectionId": "i42xt9r6ykqdq3j",
"cascadeDelete": true,
"minSelect": null,
"maxSelect": 1,
"displayFields": null
}
}))
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("pf13z4hs1iubw41")
// update
collection.schema.addField(new SchemaField({
"system": false,
"id": "6gfllcfj",
"name": "list",
"type": "relation",
"required": false,
"presentable": false,
"unique": false,
"options": {
"collectionId": "i42xt9r6ykqdq3j",
"cascadeDelete": false,
"minSelect": null,
"maxSelect": 1,
"displayFields": null
}
}))
return dao.saveCollection(collection)
})

View file

@ -0,0 +1,33 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("i42xt9r6ykqdq3j")
// add
collection.schema.addField(new SchemaField({
"system": false,
"id": "vm4xdjfy",
"name": "parentList",
"type": "relation",
"required": false,
"presentable": false,
"unique": false,
"options": {
"collectionId": "i42xt9r6ykqdq3j",
"cascadeDelete": true,
"minSelect": null,
"maxSelect": 1,
"displayFields": null
}
}))
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("i42xt9r6ykqdq3j")
// remove
collection.schema.removeField("vm4xdjfy")
return dao.saveCollection(collection)
})

View file

@ -0,0 +1,48 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("pf13z4hs1iubw41")
// update
collection.schema.addField(new SchemaField({
"system": false,
"id": "6gfllcfj",
"name": "list",
"type": "relation",
"required": true,
"presentable": false,
"unique": false,
"options": {
"collectionId": "i42xt9r6ykqdq3j",
"cascadeDelete": true,
"minSelect": null,
"maxSelect": 1,
"displayFields": null
}
}))
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("pf13z4hs1iubw41")
// update
collection.schema.addField(new SchemaField({
"system": false,
"id": "6gfllcfj",
"name": "list",
"type": "relation",
"required": false,
"presentable": false,
"unique": false,
"options": {
"collectionId": "i42xt9r6ykqdq3j",
"cascadeDelete": true,
"minSelect": null,
"maxSelect": 1,
"displayFields": null
}
}))
return dao.saveCollection(collection)
})

View file

@ -0,0 +1,44 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("pf13z4hs1iubw41")
// update
collection.schema.addField(new SchemaField({
"system": false,
"id": "ryukqaol",
"name": "cooldown",
"type": "number",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"noDecimal": false
}
}))
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("pf13z4hs1iubw41")
// update
collection.schema.addField(new SchemaField({
"system": false,
"id": "ryukqaol",
"name": "repeatInterval",
"type": "number",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"noDecimal": false
}
}))
return dao.saveCollection(collection)
})

View file

@ -0,0 +1,44 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("pf13z4hs1iubw41")
// update
collection.schema.addField(new SchemaField({
"system": false,
"id": "ryukqaol",
"name": "cooldown",
"type": "number",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": 1,
"max": null,
"noDecimal": true
}
}))
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("pf13z4hs1iubw41")
// update
collection.schema.addField(new SchemaField({
"system": false,
"id": "ryukqaol",
"name": "cooldown",
"type": "number",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"noDecimal": false
}
}))
return dao.saveCollection(collection)
})

View file

@ -0,0 +1,59 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((db) => {
const collection = new Collection({
"id": "jebj4y3fqo5gtte",
"created": "2023-12-08 19:02:25.763Z",
"updated": "2023-12-08 19:02:25.763Z",
"name": "completions",
"type": "base",
"system": false,
"schema": [
{
"system": false,
"id": "dcsjlzex",
"name": "task",
"type": "relation",
"required": false,
"presentable": false,
"unique": false,
"options": {
"collectionId": "pf13z4hs1iubw41",
"cascadeDelete": false,
"minSelect": null,
"maxSelect": 1,
"displayFields": null
}
},
{
"system": false,
"id": "ewnc7huz",
"name": "user",
"type": "relation",
"required": false,
"presentable": false,
"unique": false,
"options": {
"collectionId": "_pb_users_auth_",
"cascadeDelete": false,
"minSelect": null,
"maxSelect": 1,
"displayFields": null
}
}
],
"indexes": [],
"listRule": null,
"viewRule": null,
"createRule": null,
"updateRule": null,
"deleteRule": null,
"options": {}
});
return Dao(db).saveCollection(collection);
}, (db) => {
const dao = new Dao(db);
const collection = dao.findCollectionByNameOrId("jebj4y3fqo5gtte");
return dao.deleteCollection(collection);
})

View file

@ -0,0 +1,16 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("i42xt9r6ykqdq3j")
collection.name = "lists"
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("i42xt9r6ykqdq3j")
collection.name = "tasklists"
return dao.saveCollection(collection)
})

View file

@ -0,0 +1,48 @@
/// <reference path="../pb_data/types.d.ts" />
migrate((db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("i42xt9r6ykqdq3j")
// update
collection.schema.addField(new SchemaField({
"system": false,
"id": "vm4xdjfy",
"name": "parent",
"type": "relation",
"required": false,
"presentable": false,
"unique": false,
"options": {
"collectionId": "i42xt9r6ykqdq3j",
"cascadeDelete": true,
"minSelect": null,
"maxSelect": 1,
"displayFields": null
}
}))
return dao.saveCollection(collection)
}, (db) => {
const dao = new Dao(db)
const collection = dao.findCollectionByNameOrId("i42xt9r6ykqdq3j")
// update
collection.schema.addField(new SchemaField({
"system": false,
"id": "vm4xdjfy",
"name": "parentList",
"type": "relation",
"required": false,
"presentable": false,
"unique": false,
"options": {
"collectionId": "i42xt9r6ykqdq3j",
"cascadeDelete": true,
"minSelect": null,
"maxSelect": 1,
"displayFields": null
}
}))
return dao.saveCollection(collection)
})