demo dashboard using new system, including optional accounts
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
{
|
|
||||||
"recommendations": ["astro-build.astro-vscode"],
|
|
||||||
"unwantedRecommendations": []
|
|
||||||
}
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
{
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"command": "./node_modules/.bin/astro dev",
|
|
||||||
"name": "Development server",
|
|
||||||
"request": "launch",
|
|
||||||
"type": "node-terminal"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
# Astro Starter Kit: Minimal
|
|
||||||
|
|
||||||
```
|
|
||||||
npm create astro@latest -- --template minimal
|
|
||||||
```
|
|
||||||
|
|
||||||
[](https://stackblitz.com/github/withastro/astro/tree/latest/examples/minimal)
|
|
||||||
[](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/minimal)
|
|
||||||
[](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).
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
||||||
import { defineConfig } from 'astro/config';
|
|
||||||
|
|
||||||
// https://astro.build/config
|
|
||||||
export default defineConfig({});
|
|
||||||
|
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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 |
|
|
@ -1 +0,0 @@
|
||||||
/// <reference types="astro/client" />
|
|
||||||
|
|
@ -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>
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"extends": "astro/tsconfigs/strict"
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
/build
|
||||||
|
/.svelte-kit
|
||||||
|
/package
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
vite.config.js.timestamp-*
|
||||||
|
vite.config.ts.timestamp-*
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
engine-strict=true
|
||||||
|
resolution-mode=highest
|
||||||
|
|
@ -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
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"useTabs": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "none",
|
||||||
|
"printWidth": 100,
|
||||||
|
"plugins": ["prettier-plugin-svelte"],
|
||||||
|
"pluginSearchDirs": ["."],
|
||||||
|
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"command": "npm run dev",
|
||||||
|
"name": "Run npm start",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "node-terminal"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 {};
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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';
|
||||||
|
|
@ -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}
|
||||||
|
|
@ -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}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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 {}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<script>
|
||||||
|
import '../app.scss';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<slot />
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
/* Variables and mixins declared here will be available in all other SCSS files */
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 815 B After Width: | Height: | Size: 815 B |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 977 B After Width: | Height: | Size: 977 B |
|
Before Width: | Height: | Size: 131 KiB After Width: | Height: | Size: 131 KiB |
|
Before Width: | Height: | Size: 224 KiB After Width: | Height: | Size: 224 KiB |
|
Before Width: | Height: | Size: 437 KiB After Width: | Height: | Size: 437 KiB |
|
Before Width: | Height: | Size: 437 KiB After Width: | Height: | Size: 437 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |
|
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 7.1 KiB |
|
|
@ -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;
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -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 *;'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
})
|
||||||
|
}
|
||||||