demo dashboard using new system, including optional accounts

dev
Anton Franzluebbers 2023-07-07 17:58:27 -04:00
parent 862a9d5e58
commit c6670adec0
62 changed files with 6569 additions and 9514 deletions

View File

@ -15,6 +15,6 @@ jobs:
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_rsa
ssh-keyscan -H ${{ secrets.SSH_HOST }} > ~/.ssh/known_hosts ssh-keyscan -H ${{ secrets.SSH_HOST }} > ~/.ssh/known_hosts
- name: connect and pull - name: connect and pull
run: ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "cd ${{ secrets.SSH_WORK_DIR }} && git pull && docker compose up -d --build && exit" run: ssh ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} "cd /home/ubuntu/VEL-Connect-PB/velconnect && git pull && docker compose up -d --build --remove-orphans && exit"
- name: cleanup - name: cleanup
run: rm -rf ~/.ssh run: rm -rf ~/.ssh

View File

@ -1,21 +0,0 @@
# build output
dist/
# generated types
.astro/
# dependencies
node_modules/
# logs
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# environment variables
.env
.env.production
# macOS-specific files
.DS_Store

View File

@ -1,4 +0,0 @@
{
"recommendations": ["astro-build.astro-vscode"],
"unwantedRecommendations": []
}

View File

@ -1,11 +0,0 @@
{
"version": "0.2.0",
"configurations": [
{
"command": "./node_modules/.bin/astro dev",
"name": "Development server",
"request": "launch",
"type": "node-terminal"
}
]
}

View File

@ -1,47 +0,0 @@
# Astro Starter Kit: Minimal
```
npm create astro@latest -- --template minimal
```
[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/minimal)
[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/minimal)
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/withastro/astro?devcontainer_path=.devcontainer/minimal/devcontainer.json)
> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun!
## 🚀 Project Structure
Inside of your Astro project, you'll see the following folders and files:
```
/
├── public/
├── src/
│ └── pages/
│ └── index.astro
└── package.json
```
Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name.
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components.
Any static assets, like images, can be placed in the `public/` directory.
## 🧞 Commands
All commands are run from the root of the project, from a terminal:
| Command | Action |
| :------------------------ | :----------------------------------------------- |
| `npm install` | Installs dependencies |
| `npm run dev` | Starts local dev server at `localhost:3000` |
| `npm run build` | Build your production site to `./dist/` |
| `npm run preview` | Preview your build locally, before deploying |
| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` |
| `npm run astro -- --help` | Get help using the Astro CLI |
## 👀 Want to learn more?
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).

View File

@ -1,4 +0,0 @@
import { defineConfig } from 'astro/config';
// https://astro.build/config
export default defineConfig({});

File diff suppressed because it is too large Load Diff

View File

@ -1,15 +0,0 @@
{
"name": "",
"type": "module",
"version": "0.0.1",
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"astro": "^2.8.0"
}
}

View File

@ -1,9 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 128 128">
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
<style>
path { fill: #000; }
@media (prefers-color-scheme: dark) {
path { fill: #FFF; }
}
</style>
</svg>

Before

Width:  |  Height:  |  Size: 749 B

View File

@ -1 +0,0 @@
/// <reference types="astro/client" />

View File

@ -1,15 +0,0 @@
---
---
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<meta name="viewport" content="width=device-width" />
<meta name="generator" content={Astro.generator} />
<title>Astro</title>
</head>
<body>
<h1>Astro</h1>
</body>
</html>

View File

@ -1,3 +0,0 @@
{
"extends": "astro/tsconfigs/strict"
}

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

View File

@ -0,0 +1,30 @@
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
example_dashboard/.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-*

2
example_dashboard/.npmrc Normal file
View File

@ -0,0 +1,2 @@
engine-strict=true
resolution-mode=highest

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

View File

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

10
example_dashboard/.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,10 @@
{
"configurations": [
{
"command": "npm run dev",
"name": "Run npm start",
"request": "launch",
"type": "node-terminal"
}
]
}

5289
example_dashboard/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,36 @@
{
"name": "example-dashboard",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --plugin-search-dir . --check . && eslint .",
"format": "prettier --plugin-search-dir . --write ."
},
"devDependencies": {
"@sveltejs/adapter-auto": "^2.0.0",
"@sveltejs/kit": "^1.20.4",
"@typescript-eslint/eslint-plugin": "^5.45.0",
"@typescript-eslint/parser": "^5.45.0",
"eslint": "^8.28.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-svelte": "^2.30.0",
"prettier": "^2.8.0",
"prettier-plugin-svelte": "^2.10.1",
"svelte": "^4.0.0",
"svelte-check": "^3.4.3",
"svelte-preprocess": "^5.0.4",
"tslib": "^2.4.1",
"typescript": "^5.0.0",
"vite": "^4.3.6",
"sass": "^1.63.6"
},
"type": "module",
"dependencies": {
"pocketbase": "^0.15.2"
}
}

12
example_dashboard/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 {};

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" />
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>

View File

@ -0,0 +1,16 @@
/* Write your global styles here, in SCSS syntax. Variables and mixins from the src/variables.scss file are available here without importing */
$color-blossom: #bc1f2d;
$color-fade: #be434d;
$color-bg: #1a1a1a;
$color-bg-alt: #333;
/* $color-text: #dedce5; */
$color-text: #c9c9c9;
$font-size-base: 2rem;
$font-family-base: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
'Noto Sans', sans-serif;
$font-family-heading: $font-family-base;
@import 'lib/css/main';

View File

@ -0,0 +1,44 @@
<script lang="ts">
import { currentUser, pb } from '../velconnect';
let email: string;
let password: string;
async function login() {
const user = await pb.collection('Users').authWithPassword(email, password);
console.log(user);
}
async function signUp() {
try {
const data = {
email,
password,
passwordConfirm: password
};
const createdUser = await pb.collection('Users').create(data);
await login();
} catch (err) {
console.error(err);
}
}
function signOut() {
pb.authStore.clear();
}
</script>
{#if $currentUser}
<p>
Signed in as {$currentUser.email}
<button on:click={signOut}>Sign Out</button>
</p>
{:else}
<form on:submit|preventDefault>
<input placeholder="Email" type="text" bind:value={email} />
<input placeholder="Password" type="password" bind:value={password} />
<button on:click={signUp}>Sign Up</button>
<button on:click={login}>Login</button>
</form>
{/if}

View File

@ -0,0 +1,37 @@
<script lang="ts">
import { currentDevice, currentUser, pb, type Device, pairedDevices } from '../velconnect';
let pairingCode: string;
let errorMessage: string | null;
async function pair() {
// find the device by pairing code
try {
let device = (await pb
.collection('Device')
.getFirstListItem(`pairing_code="${pairingCode}"`)) as Device;
// add it to the local data
currentDevice.set(device.id);
pairedDevices.set([...$pairedDevices, device.id]);
// add it to my account if logged in
if ($currentUser) {
$currentUser.devices.push(device.id);
await pb.collection('Users').update($currentUser.id, $currentUser);
}
errorMessage = null;
} catch (e) {
if (e == "ClientResponseError 404: The requested resource wasn't found.") {
errorMessage = 'Device not found with this pairing code.';
}
console.error('Not found: ' + e);
}
}
</script>
<input bind:value={pairingCode} placeholder="Paring Code" />
<button on:click={pair}>Pair</button>
{#if errorMessage}
<p>{errorMessage}</p>
{/if}

View File

@ -0,0 +1,257 @@
/* Sakura.css v1.4.1
* ================
* Minimal css theme.
* Project: https://github.com/oxalorg/sakura/
*/
/* Body */
html {
font-size: 62.5%; // So that root size becomes 10px
font-family: $font-family-base;
}
body {
// $font-size-base must be a rem value
font-size: $font-size-base;
line-height: 1.618;
max-width: 38em;
margin: auto;
color: $color-text;
background-color: $color-bg;
padding: 13px;
}
@media (max-width: 684px) {
body {
font-size: $font-size-base * 0.85;
}
}
@media (max-width: 382px) {
body {
font-size: $font-size-base * 0.75;
}
}
@mixin word-wrap() {
overflow-wrap: break-word;
word-wrap: break-word;
-ms-word-break: break-all;
word-break: break-word;
}
h1,
h2,
h3,
h4,
h5,
h6 {
line-height: 1.1;
font-family: $font-family-heading;
font-weight: 700;
margin-top: 3rem;
margin-bottom: 1.5rem;
@include word-wrap;
}
h1 {
font-size: 2.35em;
}
h2 {
font-size: 2em;
}
h3 {
font-size: 1.75em;
}
h4 {
font-size: 1.5em;
}
h5 {
font-size: 1.25em;
}
h6 {
font-size: 1em;
}
p {
margin-top: 0px;
margin-bottom: 2.5rem;
}
small,
sub,
sup {
font-size: 75%;
}
hr {
border-color: $color-blossom;
}
a {
text-decoration: none;
color: $color-blossom;
&:visited {
color: darken($color-blossom, 10%);
}
&:hover {
color: $color-fade;
border-bottom: 2px solid $color-text;
}
}
ul {
padding-left: 1.4em;
margin-top: 0px;
margin-bottom: 2.5rem;
}
li {
margin-bottom: 0.4em;
}
blockquote {
margin-left: 0px;
margin-right: 0px;
padding-left: 1em;
padding-top: 0.8em;
padding-bottom: 0.8em;
padding-right: 0.8em;
border-left: 5px solid $color-blossom;
margin-bottom: 2.5rem;
background-color: $color-bg-alt;
}
blockquote p {
margin-bottom: 0;
}
img,
video {
height: auto;
max-width: 100%;
margin-top: 0px;
margin-bottom: 2.5rem;
}
/* Pre and Code */
pre {
background-color: $color-bg-alt;
display: block;
padding: 1em;
overflow-x: auto;
margin-top: 0px;
margin-bottom: 2.5rem;
font-size: 0.9em;
}
code,
kbd,
samp {
font-size: 0.9em;
padding: 0 0.5em;
background-color: $color-bg-alt;
white-space: pre-wrap;
}
pre > code {
padding: 0;
background-color: transparent;
white-space: pre;
font-size: 1em;
}
/* Tables */
table {
text-align: justify;
width: 100%;
border-collapse: collapse;
}
td,
th {
padding: 0.5em;
border-bottom: 1px solid $color-bg-alt;
}
/* Buttons, forms and input */
input,
textarea {
border: 1px solid $color-text;
&:focus {
border: 1px solid $color-blossom;
}
}
textarea {
width: 100%;
}
.button,
button,
input[type='submit'],
input[type='reset'],
input[type='button'] {
display: inline-block;
padding: 5px 10px;
text-align: center;
text-decoration: none;
white-space: nowrap;
background-color: $color-blossom;
color: $color-bg;
border-radius: 1px;
border: 1px solid $color-blossom;
cursor: pointer;
box-sizing: border-box;
&[disabled] {
cursor: default;
opacity: 0.5;
}
&:focus:enabled,
&:hover:enabled {
background-color: $color-fade;
border-color: $color-fade;
color: $color-bg;
outline: 0;
}
}
textarea,
select,
input {
color: $color-text;
padding: 6px 10px; /* The 6px vertically centers text on FF, ignored by Webkit */
margin-bottom: 10px;
background-color: $color-bg-alt;
border: 1px solid $color-bg-alt;
border-radius: 4px;
box-shadow: none;
box-sizing: border-box;
&:focus {
border: 1px solid $color-blossom;
outline: 0;
}
}
input[type='checkbox']:focus {
outline: 1px dotted $color-blossom;
}
label,
legend,
fieldset {
display: block;
margin-bottom: 0.5rem;
font-weight: 600;
}

View File

@ -0,0 +1,26 @@
import PocketBase from 'pocketbase';
import { writable } from 'svelte/store';
import { type Record } from 'pocketbase';
export const pb = new PocketBase('http://127.0.0.1:8090');
export const currentUser = writable(pb.authStore.model);
pb.authStore.onChange((auth) => {
console.log('authStore changed', auth);
currentUser.set(pb.authStore.model);
});
let pairedDevicesInit: string[] = [];
export const pairedDevices = writable(pairedDevicesInit);
export const currentDevice = writable('');
interface HasData extends Record {
data: { [key: string]: string };
}
export interface Device extends Record {
current_room: string;
current_app: string;
data: { [key: string]: string };
}
export interface RoomData extends HasData {}

View File

@ -0,0 +1,5 @@
<script>
import '../app.scss';
</script>
<slot />

View File

@ -0,0 +1,235 @@
<script lang="ts">
import { onDestroy, onMount } from 'svelte';
import {
currentDevice,
type Device,
pairedDevices,
pb,
type RoomData,
currentUser
} from '../lib/velconnect';
import Login from '$lib/components/Login.svelte';
import Pair from '$lib/components/Pair.svelte';
if ($currentDevice == '' && $pairedDevices.length > 0) {
currentDevice.set($pairedDevices[0]);
}
let unsubscribeDeviceData: () => void;
let unsubscribeCurrentDevice: () => void;
let deviceData: Device | null;
let roomData: RoomData | null;
let sending = false;
$: deviceData,
() => {
console.log('Device data changed');
};
onMount(async () => {
if ($currentDevice != '') {
deviceData = await pb.collection('Device').getOne($currentDevice);
}
unsubscribeCurrentDevice = currentDevice.subscribe(async (val) => {
console.log('current device changed');
unsubscribeDeviceData?.();
if (val != '') {
deviceData = await pb.collection('Device').getOne($currentDevice);
unsubscribeDeviceData = await pb.collection('Device').subscribe(val, (data) => {
deviceData = data.record as Device;
});
} else {
deviceData = null;
roomData = null;
}
});
});
onDestroy(() => {
unsubscribeCurrentDevice?.();
unsubscribeDeviceData?.();
});
let abortController = new AbortController();
function delayedSend() {
console.log('fn: delayedSend()');
// abort the previous send
abortController.abort();
const newAbortController = new AbortController();
abortController = newAbortController;
setTimeout(() => {
if (!newAbortController.signal.aborted) {
send();
} else {
console.log('aborted');
}
}, 1000);
}
function send() {
console.log('sending...');
sending = true;
let promises = [];
if (deviceData) {
promises.push(pb.collection('Device').update(deviceData.id, deviceData));
}
if (roomData) {
promises.push(pb.collection('DataBlock').update(roomData.id, roomData));
}
Promise.all(promises).then(() => {
sending = false;
});
}
function removeDevice(d: string) {
pairedDevices.set($pairedDevices.filter((i) => i != d));
if ($currentDevice == d) {
console.log('Removed current device');
// if there are still devices left
if ($pairedDevices.length > 0) {
currentDevice.set($pairedDevices[0]);
} else {
currentDevice.set('');
}
}
if ($currentUser) {
$currentUser.devices.filter((i: string) => i != d);
pb.collection('Users').update($currentUser.id, $currentUser);
}
}
</script>
<h1>VEL-Connect</h1>
<img src="/img/velconnect_logo_1.png" alt="logo" width="70px" height="28px" />
<p>
This is a demo dashboard. Visit the <a href="https://github.com/velaboratory/VEL-Connect"
>GitHub repo</a
> to copy it and make your own.
</p>
<Login />
<Pair />
<div>
<h3>Devices:</h3>
{#each $pairedDevices as d}
<div>
<button
on:click={() => {
currentDevice.set(d);
}}>{d}</button
>
<button
on:click={() => {
removeDevice(d);
}}>x</button
>
</div>
{/each}
{#if $pairedDevices.length == 0}
<p>No devices paired. Enter a pairing code above.</p>
{/if}
</div>
{#if sending}
<div>
<progress />
</div>
{/if}
{#if deviceData != null && deviceData.data != null}
<div>
<h3>Device Info</h3>
<device-field>
<h6>Device ID:</h6>
<code>{deviceData.id}</code>
</device-field>
<device-field>
<h6>Pairing Code:</h6>
<code>{deviceData.pairing_code}</code>
</device-field>
<device-field>
<h6>First Seen</h6>
<p>{deviceData.created}</p>
</device-field>
<device-field>
<h6>Last Seen</h6>
<p>{deviceData.updated}</p>
</device-field>
</div>
<div>
<h3>Settings</h3>
<label>
User Name
<input
type="text"
placeholder="Enter username..."
bind:value={deviceData.friendly_name}
on:input={delayedSend}
/>
</label>
<label>
Avatar URL
<a href="https://demo.readyplayer.me" target="blank">
Create New Avatar
<svg style="width:1em;height:1em;margin-bottom:-.15em;" viewBox="0 0 24 24">
<path
fill="currentColor"
d="M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z"
/>
</svg>
</a>
<input
type="text"
placeholder="https://----.glb"
bind:value={deviceData.data.avatar_url}
on:input={delayedSend}
/>
</label>
<device-field>
<h6>Current Room</h6>
<a href="/join/{deviceData.current_app}/room_name" target="blank">
Shareable Link
<svg style="width:1em;height:1em;margin-bottom:-.15em;" viewBox="0 0 24 24">
<path
fill="currentColor"
d="M14,3V5H17.59L7.76,14.83L9.17,16.24L19,6.41V10H21V3M19,19H5V5H12V3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V12H19V19Z"
/>
</svg>
</a>
<input
type="text"
placeholder="room_1"
bind:value={deviceData.current_room}
on:input={delayedSend}
/>
</device-field>
</div>
<h6>Raw JSON:</h6>
<pre><code>{JSON.stringify(deviceData, null, 2)}</code></pre>
{/if}
<style lang="scss">
device-field {
display: flex;
flex-direction: column;
gap: 0em;
width: fit-content;
p {
margin: 0;
}
}
</style>

View File

@ -0,0 +1 @@
/* Variables and mixins declared here will be available in all other SCSS files */

View File

@ -0,0 +1,223 @@
/* Sakura.css v1.4.1
* ================
* Minimal css theme.
* Project: https://github.com/oxalorg/sakura/
*/
/* Body */
html {
font-size: 62.5%;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
}
body {
font-size: 1.8rem;
line-height: 1.618;
max-width: 38em;
margin: auto;
color: #4a4a4a;
background-color: #f9f9f9;
padding: 13px;
}
@media (max-width: 684px) {
body {
font-size: 1.53rem;
}
}
@media (max-width: 382px) {
body {
font-size: 1.35rem;
}
}
h1, h2, h3, h4, h5, h6 {
line-height: 1.1;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif;
font-weight: 700;
margin-top: 3rem;
margin-bottom: 1.5rem;
overflow-wrap: break-word;
word-wrap: break-word;
-ms-word-break: break-all;
word-break: break-word;
}
h1 {
font-size: 2.35em;
}
h2 {
font-size: 2em;
}
h3 {
font-size: 1.75em;
}
h4 {
font-size: 1.5em;
}
h5 {
font-size: 1.25em;
}
h6 {
font-size: 1em;
}
p {
margin-top: 0px;
margin-bottom: 2.5rem;
}
small, sub, sup {
font-size: 75%;
}
hr {
border-color: #1d7484;
}
a {
text-decoration: none;
color: #1d7484;
}
a:visited {
color: #144f5a;
}
a:hover {
color: #982c61;
border-bottom: 2px solid #4a4a4a;
}
ul {
padding-left: 1.4em;
margin-top: 0px;
margin-bottom: 2.5rem;
}
li {
margin-bottom: 0.4em;
}
blockquote {
margin-left: 0px;
margin-right: 0px;
padding-left: 1em;
padding-top: 0.8em;
padding-bottom: 0.8em;
padding-right: 0.8em;
border-left: 5px solid #1d7484;
margin-bottom: 2.5rem;
background-color: #f1f1f1;
}
blockquote p {
margin-bottom: 0;
}
img, video {
height: auto;
max-width: 100%;
margin-top: 0px;
margin-bottom: 2.5rem;
}
/* Pre and Code */
pre {
background-color: #f1f1f1;
display: block;
padding: 1em;
overflow-x: auto;
margin-top: 0px;
margin-bottom: 2.5rem;
font-size: 0.9em;
}
code, kbd, samp {
font-size: 0.9em;
padding: 0 0.5em;
background-color: #f1f1f1;
white-space: pre-wrap;
}
pre > code {
padding: 0;
background-color: transparent;
white-space: pre;
font-size: 1em;
}
/* Tables */
table {
text-align: justify;
width: 100%;
border-collapse: collapse;
}
td, th {
padding: 0.5em;
border-bottom: 1px solid #f1f1f1;
}
/* Buttons, forms and input */
input, textarea {
border: 1px solid #4a4a4a;
}
input:focus, textarea:focus {
border: 1px solid #1d7484;
}
textarea {
width: 100%;
}
.button, button, input[type=submit], input[type=reset], input[type=button] {
display: inline-block;
padding: 5px 10px;
text-align: center;
text-decoration: none;
white-space: nowrap;
background-color: #1d7484;
color: #f9f9f9;
border-radius: 1px;
border: 1px solid #1d7484;
cursor: pointer;
box-sizing: border-box;
}
.button[disabled], button[disabled], input[type=submit][disabled], input[type=reset][disabled], input[type=button][disabled] {
cursor: default;
opacity: 0.5;
}
.button:focus:enabled, .button:hover:enabled, button:focus:enabled, button:hover:enabled, input[type=submit]:focus:enabled, input[type=submit]:hover:enabled, input[type=reset]:focus:enabled, input[type=reset]:hover:enabled, input[type=button]:focus:enabled, input[type=button]:hover:enabled {
background-color: #982c61;
border-color: #982c61;
color: #f9f9f9;
outline: 0;
}
textarea, select, input {
color: #4a4a4a;
padding: 6px 10px; /* The 6px vertically centers text on FF, ignored by Webkit */
margin-bottom: 10px;
background-color: #f1f1f1;
border: 1px solid #f1f1f1;
border-radius: 4px;
box-shadow: none;
box-sizing: border-box;
}
textarea:focus, select:focus, input:focus {
border: 1px solid #1d7484;
outline: 0;
}
input[type=checkbox]:focus {
outline: 1px dotted #1d7484;
}
label, legend, fieldset {
display: block;
margin-bottom: 0.5rem;
font-weight: 600;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 815 B

After

Width:  |  Height:  |  Size: 815 B

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

Before

Width:  |  Height:  |  Size: 977 B

After

Width:  |  Height:  |  Size: 977 B

View File

Before

Width:  |  Height:  |  Size: 131 KiB

After

Width:  |  Height:  |  Size: 131 KiB

View File

Before

Width:  |  Height:  |  Size: 224 KiB

After

Width:  |  Height:  |  Size: 224 KiB

View File

Before

Width:  |  Height:  |  Size: 437 KiB

After

Width:  |  Height:  |  Size: 437 KiB

View File

Before

Width:  |  Height:  |  Size: 437 KiB

After

Width:  |  Height:  |  Size: 437 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@ -0,0 +1,18 @@
import { vitePreprocess } from '@sveltejs/kit/vite';
import adapter from '@sveltejs/adapter-auto';
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://kit.svelte.dev/docs/integrations#preprocessors
// for more information about preprocessors
preprocess: [vitePreprocess({})],
kit: {
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
adapter: adapter()
}
};
export default config;

View File

@ -0,0 +1,17 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true
}
// 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
}

View File

@ -0,0 +1,14 @@
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [sveltekit()],
css: {
preprocessorOptions: {
scss: {
additionalData: '@use "src/variables.scss" as *;'
}
}
}
});

View File

@ -68,6 +68,11 @@ func main() {
} }
} }
// double-check that data is not null
if (record.Get("data") == nil) || (record.Get("data") == "") {
record.Set("data", "{}")
}
// apply to the db // apply to the db
if err := dao.SaveRecord(record); err != nil { if err := dao.SaveRecord(record); err != nil {
return err return err
@ -134,6 +139,11 @@ func main() {
} }
} }
// double-check that data is not null
if (record.Get("data") == nil) || (record.Get("data") == "") {
record.Set("data", "{}")
}
// apply to the db // apply to the db
if err := dao.SaveRecord(record); err != nil { if err := dao.SaveRecord(record); err != nil {
return err return err

View File

@ -0,0 +1,54 @@
package migrations
import (
"encoding/json"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/daos"
m "github.com/pocketbase/pocketbase/migrations"
"github.com/pocketbase/pocketbase/models/schema"
)
func init() {
m.Register(func(db dbx.Builder) error {
dao := daos.New(db);
collection, err := dao.FindCollectionByNameOrId("fupstz47c55s69f")
if err != nil {
return err
}
// add
new_current_room_data := &schema.SchemaField{}
json.Unmarshal([]byte(`{
"system": false,
"id": "g5wezfiu",
"name": "current_room_data",
"type": "relation",
"required": false,
"unique": false,
"options": {
"collectionId": "3qwwkz4wb0lyi78",
"cascadeDelete": false,
"minSelect": null,
"maxSelect": 1,
"displayFields": []
}
}`), new_current_room_data)
collection.Schema.AddField(new_current_room_data)
return dao.SaveCollection(collection)
}, func(db dbx.Builder) error {
dao := daos.New(db);
collection, err := dao.FindCollectionByNameOrId("fupstz47c55s69f")
if err != nil {
return err
}
// remove
collection.Schema.RemoveField("g5wezfiu")
return dao.SaveCollection(collection)
})
}

View File

@ -0,0 +1,54 @@
package migrations
import (
"encoding/json"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/daos"
m "github.com/pocketbase/pocketbase/migrations"
"github.com/pocketbase/pocketbase/models/schema"
)
func init() {
m.Register(func(db dbx.Builder) error {
dao := daos.New(db);
collection, err := dao.FindCollectionByNameOrId("fupstz47c55s69f")
if err != nil {
return err
}
// remove
collection.Schema.RemoveField("g5wezfiu")
return dao.SaveCollection(collection)
}, func(db dbx.Builder) error {
dao := daos.New(db);
collection, err := dao.FindCollectionByNameOrId("fupstz47c55s69f")
if err != nil {
return err
}
// add
del_current_room_data := &schema.SchemaField{}
json.Unmarshal([]byte(`{
"system": false,
"id": "g5wezfiu",
"name": "current_room_data",
"type": "relation",
"required": false,
"unique": false,
"options": {
"collectionId": "3qwwkz4wb0lyi78",
"cascadeDelete": false,
"minSelect": null,
"maxSelect": 1,
"displayFields": []
}
}`), del_current_room_data)
collection.Schema.AddField(del_current_room_data)
return dao.SaveCollection(collection)
})
}

View File

@ -0,0 +1,33 @@
package migrations
import (
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/daos"
m "github.com/pocketbase/pocketbase/migrations"
)
func init() {
m.Register(func(db dbx.Builder) error {
dao := daos.New(db);
collection, err := dao.FindCollectionByNameOrId("_pb_users_auth_")
if err != nil {
return err
}
collection.Name = "Users"
return dao.SaveCollection(collection)
}, func(db dbx.Builder) error {
dao := daos.New(db);
collection, err := dao.FindCollectionByNameOrId("_pb_users_auth_")
if err != nil {
return err
}
collection.Name = "users"
return dao.SaveCollection(collection)
})
}

View File

@ -0,0 +1,54 @@
package migrations
import (
"encoding/json"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/daos"
m "github.com/pocketbase/pocketbase/migrations"
"github.com/pocketbase/pocketbase/models/schema"
)
func init() {
m.Register(func(db dbx.Builder) error {
dao := daos.New(db);
collection, err := dao.FindCollectionByNameOrId("_pb_users_auth_")
if err != nil {
return err
}
// add
new_devices := &schema.SchemaField{}
json.Unmarshal([]byte(`{
"system": false,
"id": "1hwaooub",
"name": "devices",
"type": "relation",
"required": false,
"unique": false,
"options": {
"collectionId": "fupstz47c55s69f",
"cascadeDelete": false,
"minSelect": null,
"maxSelect": null,
"displayFields": []
}
}`), new_devices)
collection.Schema.AddField(new_devices)
return dao.SaveCollection(collection)
}, func(db dbx.Builder) error {
dao := daos.New(db);
collection, err := dao.FindCollectionByNameOrId("_pb_users_auth_")
if err != nil {
return err
}
// remove
collection.Schema.RemoveField("1hwaooub")
return dao.SaveCollection(collection)
})
}

View File

@ -0,0 +1,34 @@
package migrations
import (
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/daos"
m "github.com/pocketbase/pocketbase/migrations"
"github.com/pocketbase/pocketbase/tools/types"
)
func init() {
m.Register(func(db dbx.Builder) error {
dao := daos.New(db);
collection, err := dao.FindCollectionByNameOrId("fupstz47c55s69f")
if err != nil {
return err
}
collection.ListRule = types.Pointer("")
return dao.SaveCollection(collection)
}, func(db dbx.Builder) error {
dao := daos.New(db);
collection, err := dao.FindCollectionByNameOrId("fupstz47c55s69f")
if err != nil {
return err
}
collection.ListRule = nil
return dao.SaveCollection(collection)
})
}