working nix builds
can compile both the react application statically so it can be served by nginx and then builds a docker container with nginx for easy deployment to something like fly.io I mean since it's static I could probably host it for free on a website like netlify or something, but I still wanted a docker container for now. Notably I did have to move the generated javascript parser into the mast-react-vite folder so that `buildNpmPackage` was scoped to the mast-react-vite folder. I'm not sure how to get it working in a truly monorepo style build working for it yet, but I'm not exactly inclined to make it super shiny right now either.
This commit is contained in:
parent
f24e56159a
commit
0ca396148b
114
flake.nix
114
flake.nix
|
@ -9,20 +9,108 @@
|
|||
flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
in
|
||||
{
|
||||
devShells.default = pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
go
|
||||
pigeon
|
||||
nodejs
|
||||
sqlite
|
||||
];
|
||||
username = "admin";
|
||||
passwordHash =
|
||||
"$apr1$bhZ6EAAr$E8cv5p/2RUBrxLZD.9Jpi."; # Replace with your hash
|
||||
|
||||
shellHook = ''
|
||||
echo "System: ${system}"
|
||||
nginxConfig = pkgs.writeText "nginx.conf" ''
|
||||
worker_processes auto;
|
||||
daemon off;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
include ${pkgs.nginx}/conf/mime.types;
|
||||
|
||||
server {
|
||||
listen 8080;
|
||||
|
||||
location / {
|
||||
auth_basic "Restricted Content";
|
||||
auth_basic_user_file /etc/nginx/htpasswd;
|
||||
|
||||
root /app;
|
||||
index index.html;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
}
|
||||
'';
|
||||
|
||||
htpasswd = pkgs.writeText "htpasswd" ''
|
||||
${username}:${passwordHash}
|
||||
'';
|
||||
in {
|
||||
packages = rec {
|
||||
react = pkgs.buildNpmPackage {
|
||||
pname = "mast-react";
|
||||
version = "0.0.1";
|
||||
src = ./mast-react-vite;
|
||||
|
||||
npmDepsHash = "sha256-oqq55MEzXGEiF9UW7rZFyQrkyTPrUEkh2enZmbbZ7Ks=";
|
||||
buildInputs = with pkgs; [ nodejs typescript ];
|
||||
nativeBuildInputs = with pkgs; [ nodejs ];
|
||||
|
||||
# npmWorkspace = ./mast-react-vite;
|
||||
npmBuildScript = "build";
|
||||
installPhase = ''
|
||||
mkdir -p $out
|
||||
cp -r dist $out/
|
||||
'';
|
||||
};
|
||||
}
|
||||
);
|
||||
react-server = pkgs.dockerTools.buildImage {
|
||||
name = "react-server";
|
||||
tag = "latest";
|
||||
|
||||
copyToRoot = pkgs.buildEnv {
|
||||
name = "image-root";
|
||||
paths = [
|
||||
pkgs.nginx
|
||||
react
|
||||
(pkgs.writeScriptBin "start-server" ''
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Create necessary directories
|
||||
mkdir -p /etc/nginx
|
||||
cp ${htpasswd} /etc/nginx/htpasswd
|
||||
|
||||
# Create app directory and copy built files
|
||||
mkdir -p /app
|
||||
cp -r ${react}/* /app/
|
||||
|
||||
# Start nginx
|
||||
${pkgs.nginx}/bin/nginx -c ${nginxConfig}
|
||||
'')
|
||||
];
|
||||
pathsToLink = [ "/bin" ];
|
||||
};
|
||||
|
||||
config = {
|
||||
Cmd = [ "/bin/start-server" ];
|
||||
ExposedPorts = { "8080/tcp" = { }; };
|
||||
};
|
||||
};
|
||||
};
|
||||
devShells.default = pkgs.mkShell {
|
||||
buildInputs = with pkgs; [ go pigeon nodejs sqlite git-bug openssl ];
|
||||
|
||||
shellHook = ''
|
||||
alias start-dev='npm run dev'
|
||||
alias build-dev='npm run build'
|
||||
alias clean-dev='rm -rf dist node_modules'
|
||||
alias ws-server='go run ./server/main.go'
|
||||
alias gen-pass='openssl passwd -apr1 '
|
||||
|
||||
echo "System: ${system}"
|
||||
echo "Available commands:"
|
||||
echo " start-dev - Start Vite dev server"
|
||||
echo " build-dev - Build the application"
|
||||
echo " clean-dev - Clean build artifacts"
|
||||
echo " ws-server - Run the websocket server"
|
||||
echo " gen-pass - Generate a basic_auth password"
|
||||
'';
|
||||
};
|
||||
});
|
||||
}
|
||||
|
|
223
mast-react-vite/package-lock.json
generated
223
mast-react-vite/package-lock.json
generated
|
@ -8,10 +8,12 @@
|
|||
"name": "mast-react",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-checkbox": "^1.1.2",
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@tanstack/react-table": "^8.20.5",
|
||||
"@vlcn.io/react": "^3.1.0",
|
||||
"@vlcn.io/rx-tbl": "^0.15.0",
|
||||
"@vlcn.io/ws-browserdb": "^0.2.0",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^0.454.0",
|
||||
|
@ -692,6 +694,68 @@
|
|||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/primitive": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz",
|
||||
"integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA=="
|
||||
},
|
||||
"node_modules/@radix-ui/react-checkbox": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.1.2.tgz",
|
||||
"integrity": "sha512-/i0fl686zaJbDQLNKrkCbMyDm6FQMt4jg323k7HuqitoANm9sE23Ql8yOK3Wusk34HSLKDChhMux05FnP6KUkw==",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.0",
|
||||
"@radix-ui/react-compose-refs": "1.1.0",
|
||||
"@radix-ui/react-context": "1.1.1",
|
||||
"@radix-ui/react-presence": "1.1.1",
|
||||
"@radix-ui/react-primitive": "2.0.0",
|
||||
"@radix-ui/react-use-controllable-state": "1.1.0",
|
||||
"@radix-ui/react-use-previous": "1.1.0",
|
||||
"@radix-ui/react-use-size": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-compose-refs": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz",
|
||||
"integrity": "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-context": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz",
|
||||
"integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-icons": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.0.tgz",
|
||||
|
@ -700,6 +764,144 @@
|
|||
"react": "^16.x || ^17.x || ^18.x"
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-presence": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.1.tgz",
|
||||
"integrity": "sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.0",
|
||||
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-primitive": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz",
|
||||
"integrity": "sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
|
||||
"integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-callback-ref": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz",
|
||||
"integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-controllable-state": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz",
|
||||
"integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-callback-ref": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-layout-effect": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz",
|
||||
"integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-previous": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.0.tgz",
|
||||
"integrity": "sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-use-size": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz",
|
||||
"integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-use-layout-effect": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.24.2.tgz",
|
||||
|
@ -1203,13 +1405,13 @@
|
|||
"version": "15.7.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz",
|
||||
"integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==",
|
||||
"dev": true
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "18.3.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz",
|
||||
"integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"@types/prop-types": "*",
|
||||
"csstype": "^3.0.2"
|
||||
|
@ -1219,7 +1421,7 @@
|
|||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz",
|
||||
"integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
|
@ -1503,6 +1705,19 @@
|
|||
"resolved": "https://registry.npmjs.org/@vlcn.io/wa-sqlite/-/wa-sqlite-0.22.0.tgz",
|
||||
"integrity": "sha512-OujKro0mAqP7/efUeCGB6zBiyMoSCFVe7jQKPF0n47U9ZhOaIW3kQUVCwF+CmzvzQfN1Vl4PrFQRNNxlSwTCNQ=="
|
||||
},
|
||||
"node_modules/@vlcn.io/ws-browserdb": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@vlcn.io/ws-browserdb/-/ws-browserdb-0.2.0.tgz",
|
||||
"integrity": "sha512-TKPygQomMPurQ8mwAwEefN5BROw30FagL21S3FzvUunyCybOByysAEoV/OrJ7xNCvYXN9yjg0qyf7iZZfeOsYg==",
|
||||
"dependencies": {
|
||||
"@types/throttle-debounce": "^5.0.0",
|
||||
"@vlcn.io/crsqlite-wasm": "0.16.0",
|
||||
"@vlcn.io/rx-tbl": "0.15.0",
|
||||
"@vlcn.io/ws-client": "0.2.0",
|
||||
"@vlcn.io/ws-common": "0.2.0",
|
||||
"@vlcn.io/xplat-api": "0.15.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@vlcn.io/ws-client": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@vlcn.io/ws-client/-/ws-client-0.2.0.tgz",
|
||||
|
@ -1909,7 +2124,7 @@
|
|||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||
"dev": true
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.7",
|
||||
|
|
|
@ -5,15 +5,17 @@
|
|||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"build": "vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@radix-ui/react-checkbox": "^1.1.2",
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
"@tanstack/react-table": "^8.20.5",
|
||||
"@vlcn.io/react": "^3.1.0",
|
||||
"@vlcn.io/rx-tbl": "^0.15.0",
|
||||
"@vlcn.io/ws-browserdb": "^0.2.0",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^0.454.0",
|
||||
|
|
|
@ -1,18 +1,20 @@
|
|||
import { useState } from 'react'
|
||||
import { useCachedState, useQuery } from "@vlcn.io/react";
|
||||
import { useQuery } from "@vlcn.io/react";
|
||||
import {
|
||||
ColumnDef,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
useReactTable,
|
||||
} from "@tanstack/react-table"
|
||||
import { DataTable } from "@/components/ui/data-table"
|
||||
import * as addParser from '../../parser/add_parser.js';
|
||||
import * as commandParser from '../../parser/command_parser.js';
|
||||
import * as commandParser from '@/lib/command_parser.js';
|
||||
import { Checkbox } from "@/components/ui/checkbox"
|
||||
import { Input } from "@/components/ui/input"
|
||||
|
||||
function Marshall(parsed) {
|
||||
interface MarshalledCommand {
|
||||
description: string;
|
||||
tags: string;
|
||||
project: string;
|
||||
}
|
||||
|
||||
function Marshall(parsed): MarshalledCommand {
|
||||
let project = "";
|
||||
const tags = [];
|
||||
for (const attr of parsed.attributes) {
|
||||
|
@ -171,6 +173,10 @@ function App({ ctx }) {
|
|||
}
|
||||
}
|
||||
// React to key presses for selection
|
||||
// TODO:
|
||||
// We should use the react way
|
||||
// Where we update based on changes to newText
|
||||
// Rather than changes to the input value
|
||||
else if (e.target.value.trim() !== "") {
|
||||
try {
|
||||
const parsed = commandParser.parse(e.target.value);
|
||||
|
|
78
mast-react-vite/src/Root.tsx
Normal file
78
mast-react-vite/src/Root.tsx
Normal file
|
@ -0,0 +1,78 @@
|
|||
import App from "./App.tsx";
|
||||
import schemaContent from "./schemas/main2.sql?raw";
|
||||
import { DBProvider } from "@vlcn.io/react";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
/**
|
||||
* Generates a random room name to sync with or pulls one from local storage.
|
||||
*/
|
||||
function getRoom(hash: HashBag): string {
|
||||
return hash.room || localStorage.getItem("room") || newRoom();
|
||||
}
|
||||
|
||||
function hashChanged() {
|
||||
const hash = parseHash();
|
||||
const room = getRoom(hash);
|
||||
if (room != hash.room) {
|
||||
hash.room = room;
|
||||
window.location.hash = writeHash(hash);
|
||||
}
|
||||
localStorage.setItem("room", room);
|
||||
return room;
|
||||
}
|
||||
const room = hashChanged();
|
||||
|
||||
export default function Root() {
|
||||
const [theRoom, setTheRoom] = useState(room);
|
||||
useEffect(() => {
|
||||
const cb = () => {
|
||||
const room = hashChanged();
|
||||
if (room != theRoom) {
|
||||
setTheRoom(room);
|
||||
}
|
||||
};
|
||||
addEventListener("hashchange", cb);
|
||||
return () => {
|
||||
removeEventListener("hashchange", cb);
|
||||
};
|
||||
}, []); // ignore -- theRoom is managed by the effect
|
||||
|
||||
return (
|
||||
<DBProvider
|
||||
dbname={theRoom}
|
||||
schema={{
|
||||
name: "main2.sql",
|
||||
content: schemaContent,
|
||||
}}
|
||||
Render={() => <App dbname={theRoom} />}
|
||||
></DBProvider>
|
||||
);
|
||||
}
|
||||
|
||||
type HashBag = { [key: string]: string };
|
||||
function parseHash(): HashBag {
|
||||
const hash = window.location.hash;
|
||||
const ret: { [key: string]: string } = {};
|
||||
if (hash.length > 1) {
|
||||
const substr = hash.substring(1);
|
||||
const parts = substr.split(",");
|
||||
for (const part of parts) {
|
||||
const [key, value] = part.split("=");
|
||||
ret[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function writeHash(hash: HashBag) {
|
||||
const parts = [];
|
||||
for (const key in hash) {
|
||||
parts.push(`${key}=${hash[key]}`);
|
||||
}
|
||||
return parts.join(",");
|
||||
}
|
||||
|
||||
function newRoom() {
|
||||
return crypto.randomUUID().replaceAll("-", "");
|
||||
}
|
1304
mast-react-vite/src/lib/command_parser.js
Normal file
1304
mast-react-vite/src/lib/command_parser.js
Normal file
File diff suppressed because it is too large
Load diff
|
@ -2,15 +2,18 @@ import { StrictMode } from 'react'
|
|||
import { createRoot } from 'react-dom/client'
|
||||
import './index.css'
|
||||
import App from "./App.tsx";
|
||||
import Root from "./Root.tsx";
|
||||
import { start } from "@vlcn.io/ws-client/worker.js";
|
||||
import { createDbProvider } from "@vlcn.io/ws-browserdb";
|
||||
|
||||
import initWasm from '@vlcn.io/crsqlite-wasm';
|
||||
import wasmUrl from '@vlcn.io/crsqlite-wasm/crsqlite.wasm?url';
|
||||
import tblrx from '@vlcn.io/rx-tbl';
|
||||
|
||||
const sqlite = await initWasm(() => wasmUrl);
|
||||
|
||||
const db = await sqlite.open("todo.db");
|
||||
await db.exec(`
|
||||
async function initializeDatabase() {
|
||||
const sqlite = await initWasm(() => wasmUrl);
|
||||
const db = await sqlite.open("todo.db");
|
||||
await db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS todos (
|
||||
id BLOB PRIMARY KEY NOT NULL,
|
||||
description TEXT,
|
||||
|
@ -23,10 +26,65 @@ await db.exec(`
|
|||
completed INTEGER NOT NULL DEFAULT 0
|
||||
);
|
||||
SELECT crsql_as_crr('todos');
|
||||
`);
|
||||
`);
|
||||
db.onUpdate(async () => {
|
||||
const changes = await db.execA(`SELECT * FROM crsql_changes WHERE db_version > 0 AND site_id = crsql_site_id()`);
|
||||
|
||||
const rx = tblrx(db);
|
||||
const ctx = { db, rx };
|
||||
sendChangesToServer(changes);
|
||||
});
|
||||
return db;
|
||||
}
|
||||
|
||||
const config = {
|
||||
dbProvider: createDbProvider(),
|
||||
transportProvider: (dbName) => new WebSocket(`ws://localhost:8080/sync/${dbName}`),
|
||||
};
|
||||
|
||||
start(config);
|
||||
const ws = new WebSocket('ws://localhost:8080/sync');
|
||||
|
||||
ws.onmessage = async function(event) {
|
||||
const { type, data } = JSON.parse(event.data);
|
||||
if (type === 'changes') {
|
||||
await applyChanges(data);
|
||||
}
|
||||
};
|
||||
|
||||
function sendChangesToServer(changes) {
|
||||
const formattedChanges = changes.map(change => ({
|
||||
TableName: change[0],
|
||||
PK: Array.isArray(change[1]) ? btoa(String.fromCharCode.apply(null, change[1])) : btoa(change[1]),
|
||||
ColumnName: change[2],
|
||||
Value: Array.isArray(change[3]) ? btoa(String.fromCharCode.apply(null, change[3])) : change[3],
|
||||
ColVersion: Number(change[4]),
|
||||
DBVersion: Number(change[5]),
|
||||
SiteID: Array.isArray(change[6]) ? btoa(new TextDecoder('utf8').decode(change[6])) : btoa(change[6]),
|
||||
CL: Number(change[7]),
|
||||
Seq: Number(change[8])
|
||||
}));
|
||||
console.log(formattedChanges)
|
||||
|
||||
const message = {
|
||||
type: "changes",
|
||||
data: formattedChanges
|
||||
};
|
||||
|
||||
ws.send(JSON.stringify(message));
|
||||
}
|
||||
|
||||
async function applyChanges(changes) {
|
||||
const db = initializeDatabase();
|
||||
const rx = tblrx(db);
|
||||
const ctx = { db, rx };
|
||||
await db.tx(async (tx) => {
|
||||
for (const change of changes) {
|
||||
await tx.exec(
|
||||
`INSERT INTO crsql_changes VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
change
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
document.getElementById("root")!.classList.add("dark");
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
}
|
||||
],
|
||||
"compilerOptions": {
|
||||
"noEmitOnError": false,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
|
|
Loading…
Reference in a new issue