Compare commits
316 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| add1ece149 | |||
| 421cd97fe9 | |||
| 58959fe347 | |||
| d9cd0692b5 | |||
| 3155cc930f | |||
| 8c2b7f9c7d | |||
| 960c64984e | |||
| d79f4efa98 | |||
| 8bfe7bb174 | |||
| 03217f3e65 | |||
| fd81ba3be7 | |||
| c58ce483dd | |||
| 13ecd5a626 | |||
| 82807dcfce | |||
| 739775ea94 | |||
| e2216c17e8 | |||
| 33019c116b | |||
| bd833e236a | |||
| 0916829ba2 | |||
| 122c0d2ab0 | |||
| 1e174e81d3 | |||
| a772372b2b | |||
| 74a2d179b9 | |||
| 5891dfd6b7 | |||
| a7aa385d98 | |||
| c9dc61b918 | |||
| 2fd87b2d14 | |||
| 7769910027 | |||
| 1152ab0d0c | |||
| 1ccdab2780 | |||
| e714c3d3df | |||
| 54e5017c38 | |||
| 8a78e45440 | |||
| 1c50dd5c84 | |||
| 31dd8a89da | |||
| 1098f03c7d | |||
| 0e053bf69c | |||
| 0b81605bdf | |||
| 7849bf4eb2 | |||
| e329b95f2f | |||
| 25963d5289 | |||
| aa0810be02 | |||
| 15c6617151 | |||
| adc80c54c4 | |||
| 0e318e7cc7 | |||
| 5091ca0bab | |||
| 3002181160 | |||
| f8ae71a962 | |||
| c66da87401 | |||
| 3cf3d996dc | |||
| 5889a1ed79 | |||
| be311e6dc8 | |||
| aa41895675 | |||
| 970f6fc78a | |||
| c1503d959e | |||
| 6ea6eaaac7 | |||
| 2915717a3a | |||
| 6643379133 | |||
| 1ab3deae92 | |||
| 016b1fe6e2 | |||
| ebad30ce4d | |||
| 0712abe729 | |||
| 2c3fba31d3 | |||
| c815db4594 | |||
| 43100ad650 | |||
| af52d5992c | |||
| 5fbf2b87c1 | |||
| 204ef670f9 | |||
| 26b48b26c8 | |||
| 02c08b35f2 | |||
| bda0735c7f | |||
| c7887d7d8f | |||
| 3f02fc7ea5 | |||
| 100c5e612c | |||
| d59c7b6e6e | |||
| 0ad61bdde9 | |||
| 4a9d7acc41 | |||
| 6ad04bb21a | |||
| 293c74d7c1 | |||
| 6d2049e1f6 | |||
| cf0f359921 | |||
| 58668443c4 | |||
| 9d5234b629 | |||
| 5ce0c9ce4f | |||
| 8c04b40d03 | |||
| cdf59db5b6 | |||
| f2b9e78e45 | |||
| 4e6c3a63d0 | |||
| e69d008bfa | |||
| 5d3cad0def | |||
| 4f39030ed4 | |||
| 2397ddcd4c | |||
| b9152678b8 | |||
| a9c8d3dbad | |||
| 60758ed84d | |||
| 53ca47d63e | |||
| afdf765a8f | |||
| 71da2dd6a2 | |||
| 17206abca7 | |||
| 263879d215 | |||
| 4cb50e5c6a | |||
| ba2d9a7886 | |||
| 83279fa5b0 | |||
| b443e1854b | |||
| c00fc55462 | |||
| b198605643 | |||
| 2486107041 | |||
| a07b53f2a7 | |||
| 1d6c82a16a | |||
| 1de6270f79 | |||
| 4fdcc9694d | |||
| 4c300dc73b | |||
| e0d85a6a3d | |||
| d08e86e92f | |||
| 4e1436b0cc | |||
| cf278fed3a | |||
| ae73f3e74b | |||
| 540658e3d0 | |||
| 1d94143b21 | |||
| 648876c06b | |||
| bb9c3ec396 | |||
| dec0041c21 | |||
| a334f13e6f | |||
| da38df7df4 | |||
| 2d1a3ba5d6 | |||
| 3d3f73c259 | |||
| d6941fd1bf | |||
| 4a95028fe8 | |||
| ffe8f60f38 | |||
| 9e7833b149 | |||
| e405dc1598 | |||
| cb2675bf27 | |||
| 724cd79ab3 | |||
| f07ea11817 | |||
| 7315e3397a | |||
| 7806760153 | |||
| 7561a06b69 | |||
| 06333e9123 | |||
| d8d91dd688 | |||
| fa8865d013 | |||
| fbafd747d3 | |||
| 63d1e627ad | |||
| 1a3e9b879e | |||
| d0b08b72e2 | |||
| f26aa01211 | |||
| 6ec28efe78 | |||
| cdaff97614 | |||
| e85adc1386 | |||
| 2872026ac9 | |||
| 4c26dca6c5 | |||
| 15fdb938ee | |||
| bbe54dc3e3 | |||
| 01874b9e9e | |||
| 3ade3d4d6a | |||
| 24ae4aad41 | |||
| ae3a94b8d4 | |||
| 735bd4bb2f | |||
| 41aeceed69 | |||
| 5a1c86a0dd | |||
| 7ccca76119 | |||
| 2debefd556 | |||
| c86ed5e9a0 | |||
| ce9316d20a | |||
| 1e36ca20d9 | |||
| 0acde00ecc | |||
| d8cdb019fb | |||
| bf7d419df2 | |||
| fd774c0be2 | |||
| 08569de94d | |||
| d864c2e562 | |||
| a016aa749b | |||
| b788b64df5 | |||
| dc3c3c56a1 | |||
| 1dd192cd4d | |||
| d82df88296 | |||
| eb4281ac30 | |||
| e27a77d674 | |||
| 2f29b0a2de | |||
| 791481e564 | |||
| 0e4d815225 | |||
| f479f6043f | |||
| 4db349581b | |||
| d761a59d6d | |||
| 36e4b04957 | |||
| 5c22c15076 | |||
| 48c0535c8f | |||
| f87190af86 | |||
| d17fc0897d | |||
| cfe174d564 | |||
| fbc39f2103 | |||
| c81dd8c24a | |||
| e30bca5090 | |||
| f9127df48a | |||
| 81c61fc6f8 | |||
| 4b06cb2679 | |||
| b2c5c85d7e | |||
| ea5b8789c0 | |||
| 2387fe53ef | |||
| 469d4a5116 | |||
| 8bb77873cc | |||
| b8521be13e | |||
| f7dd6840e7 | |||
| a1248a2e34 | |||
| d7e812d253 | |||
| 8cf12bc91c | |||
| 888e858578 | |||
| b59ad398db | |||
| 9708cedf30 | |||
| 6e5afa2dac | |||
| b459f75d24 | |||
| e2d251e8b7 | |||
| 9c76c7537d | |||
| f148805e80 | |||
| 4f820a4f09 | |||
| 12bc90d44f | |||
| 1116f1ae3f | |||
| c21fd15815 | |||
| c6e52138b3 | |||
| 159e687c19 | |||
| cabc38ce09 | |||
| 727977d5c6 | |||
| 2d3855621e | |||
| 9a9b865843 | |||
| 1d003c77e6 | |||
| c4d201c5ba | |||
| c1ba8f5e10 | |||
| 5eaf5f37e3 | |||
| c87472b722 | |||
| 2c03ad1123 | |||
| 36fa46cccb | |||
| 73dae9c1cd | |||
| d212281ee8 | |||
| a2cfbb17f5 | |||
| 352491b743 | |||
| 3439962d6a | |||
| 772b84250b | |||
| 9d91e7bdb8 | |||
| 8dfdae0c51 | |||
| 7933b6542c | |||
| f69bdac7b9 | |||
| 2d61cd1563 | |||
| 85bfcdbf6a | |||
| 405d71ddbe | |||
| 62012c7fe8 | |||
| f07ef4b62e | |||
| 16b690e400 | |||
| d9658c538d | |||
| 659b71ec53 | |||
| 7c6fa4eadc | |||
| 54ef9df770 | |||
| ebb5cede1b | |||
| 4df012d8ba | |||
| 4ce6f57998 | |||
| f9e6207f09 | |||
| 85463eee5c | |||
| c0fcd00536 | |||
| 1450f7dc08 | |||
| f01bf50a96 | |||
| d053cb08ae | |||
| d73b200d35 | |||
| 73a850613d | |||
| 1413a0bda9 | |||
| 618c0013ee | |||
| 74603cba65 | |||
| e41f8194d5 | |||
| 2ef5379a3b | |||
| 27167f4e47 | |||
| cdc47f8ac9 | |||
| a1adbf5bd9 | |||
| dcd5a8835c | |||
| 4c45ac5ef0 | |||
| 74b0ae464b | |||
| 410a88099e | |||
| 7d5ef80b5b | |||
| 25a63ca182 | |||
| 72bdbf3ffa | |||
| 237b127686 | |||
| a2fc7ec460 | |||
| 57a5d03518 | |||
| 83a36d412e | |||
| 39485dc6e1 | |||
| bd1d868749 | |||
| 3eab01027b | |||
| 8d5d20b703 | |||
| 606a51c767 | |||
| fd93444ac4 | |||
| cee114f784 | |||
| c42772ce47 | |||
| f51b5d9e7c | |||
| 8460470eb7 | |||
| 8039962dbf | |||
| 80ef1b595f | |||
| 1ae2dd3609 | |||
| d7d8b414e5 | |||
| bb93e502c9 | |||
| 71128abb5f | |||
| acafe25deb | |||
| 900ed963c0 | |||
| bd63083ee3 | |||
| af1fdad045 | |||
| d46b32b0d7 | |||
| eda8826704 | |||
| ef394bab64 | |||
| 338df0f14d | |||
| 561603da1d | |||
| 700330767f | |||
| 980024dfec | |||
| 1804ec0c9e | |||
| 8a2556b773 | |||
| 1c64569d9a | |||
| ed26d36941 | |||
| 97e58928ed | |||
| 6630e52ba4 | |||
| 4f71fcefbd | |||
| 3580cf2559 | |||
| 5150a017bf |
75
.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
name: Build and Deploy
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ main ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: linux_amd64
|
||||||
|
timeout-minutes: 15
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
timeout-minutes: 5
|
||||||
|
|
||||||
|
- name: Build Front End
|
||||||
|
run: npm run build
|
||||||
|
timeout-minutes: 10
|
||||||
|
env:
|
||||||
|
NODE_OPTIONS: '--max-old-space-size=4096'
|
||||||
|
VITE_AUTH0_CLIENTID: ${{ secrets.VITE_AUTH0_CLIENTID }}
|
||||||
|
VITE_AUTH0_DOMAIN: ${{ secrets.VITE_AUTH0_DOMAIN }}
|
||||||
|
|
||||||
|
- name: Stop Service
|
||||||
|
run: |
|
||||||
|
sudo rc-service immersive stop || true
|
||||||
|
|
||||||
|
- name: Deploy to /opt/immersive
|
||||||
|
run: |
|
||||||
|
# Ensure group write so we can delete old files
|
||||||
|
sudo chmod -R g+w /opt/immersive || true
|
||||||
|
|
||||||
|
# Remove old files except data directory and env file
|
||||||
|
find /opt/immersive -mindepth 1 -maxdepth 1 ! -name 'data' ! -name '.env.production' -exec rm -rf {} +
|
||||||
|
|
||||||
|
# Copy built files to target
|
||||||
|
cp -r . /opt/immersive/
|
||||||
|
|
||||||
|
# Remove unnecessary directories
|
||||||
|
rm -rf /opt/immersive/.git /opt/immersive/.github
|
||||||
|
|
||||||
|
# Set permissions on start.sh and ensure group write for future deploys
|
||||||
|
chmod +x /opt/immersive/start.sh
|
||||||
|
sudo chmod -R g+w /opt/immersive
|
||||||
|
|
||||||
|
# Set ownership to immersive user
|
||||||
|
sudo chown -R immersive:immersive /opt/immersive
|
||||||
|
|
||||||
|
- name: Create Environment File
|
||||||
|
env:
|
||||||
|
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
|
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||||
|
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
|
NEW_RELIC_LICENSE_KEY: ${{ secrets.NEW_RELIC_LICENSE_KEY }}
|
||||||
|
run: |
|
||||||
|
# Create .env.production with secrets (only accessible by immersive user)
|
||||||
|
echo "# Auto-generated by CI/CD - Do not edit manually" > /opt/immersive/.env.production
|
||||||
|
echo "ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}" >> /opt/immersive/.env.production
|
||||||
|
echo "CLOUDFLARE_ACCOUNT_ID=${CLOUDFLARE_ACCOUNT_ID}" >> /opt/immersive/.env.production
|
||||||
|
echo "CLOUDFLARE_API_TOKEN=${CLOUDFLARE_API_TOKEN}" >> /opt/immersive/.env.production
|
||||||
|
echo "NEW_RELIC_LICENSE_KEY=${NEW_RELIC_LICENSE_KEY}" >> /opt/immersive/.env.production
|
||||||
|
|
||||||
|
# Secure the environment file
|
||||||
|
sudo chown immersive:immersive /opt/immersive/.env.production
|
||||||
|
sudo chmod 600 /opt/immersive/.env.production
|
||||||
|
|
||||||
|
- name: Start Service
|
||||||
|
run: |
|
||||||
|
sudo rc-service immersive start
|
||||||
19
.github/workflows/main.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
name: Node.js CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "deepdiagram" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "deepdiagram" ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: self-hosted
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Use Node.js 20.x
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: 20.x
|
||||||
|
- run: cp -r ./dist/* /var/www/deepdiagram
|
||||||
50
.github/workflows/node.js.yml
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
||||||
|
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
|
||||||
|
|
||||||
|
name: Node.js Github Side
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ "deepdiagram" ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ "deepdiagram" ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: build app
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Use Node.js 20.x
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: 20.x
|
||||||
|
cache-dependency-path: package-lock.json
|
||||||
|
- run: echo "test"
|
||||||
|
- run: npm ci
|
||||||
|
- run: npm run build
|
||||||
|
env:
|
||||||
|
VITE_SYNCDB_ENDPOINT: ${{ secrets.VITE_SYNCDB_ENDPOINT }}
|
||||||
|
VITE_USER_ENDPOINT: ${{ secrets.VITE_USER_ENDPOINT }}
|
||||||
|
VITE_CREATE_ENDPOINT: ${{ secrets.VITE_CREATE_ENDPOINT }}
|
||||||
|
NODE_OPTIONS: --max-old-space-size=4096
|
||||||
|
- name: Archive production artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: upload-dist
|
||||||
|
path: ./dist
|
||||||
|
overwrite: true
|
||||||
|
deploy:
|
||||||
|
name: deploy on linode
|
||||||
|
needs: build
|
||||||
|
runs-on: self-hosted
|
||||||
|
steps:
|
||||||
|
- name: get artifact
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: upload-dist
|
||||||
|
path: dist
|
||||||
|
- run: pwd
|
||||||
|
- run: cp -r ./dist/* /var/www/deepdiagram/
|
||||||
|
|
||||||
2
.gitignore
vendored
@ -25,3 +25,5 @@ dist-ssr
|
|||||||
|
|
||||||
# Local Netlify folder
|
# Local Netlify folder
|
||||||
.netlify
|
.netlify
|
||||||
|
/data/
|
||||||
|
/.env.production
|
||||||
|
|||||||
280
ALPINE_SERVICE.md
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
# Alpine Linux Service Setup
|
||||||
|
|
||||||
|
This guide covers installing and running Immersive as a service on Alpine Linux using OpenRC.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Update packages
|
||||||
|
apk update
|
||||||
|
|
||||||
|
# Install Node.js 18+ and npm
|
||||||
|
apk add nodejs npm
|
||||||
|
|
||||||
|
# Install build dependencies (required for native modules like leveldown)
|
||||||
|
apk add python3 make g++ git
|
||||||
|
|
||||||
|
# Verify Node version (must be >= 18)
|
||||||
|
node --version
|
||||||
|
```
|
||||||
|
|
||||||
|
## Create Service User
|
||||||
|
|
||||||
|
Create a dedicated user to run the service (security best practice):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create immersive group and user (no login shell, no home directory)
|
||||||
|
addgroup -S immersive
|
||||||
|
adduser -S -G immersive -H -s /sbin/nologin immersive
|
||||||
|
|
||||||
|
# Create directories with proper ownership
|
||||||
|
mkdir -p /opt/immersive
|
||||||
|
mkdir -p /var/log/immersive
|
||||||
|
mkdir -p /var/run/immersive
|
||||||
|
|
||||||
|
chown -R immersive:immersive /opt/immersive
|
||||||
|
chown -R immersive:immersive /var/log/immersive
|
||||||
|
chown -R immersive:immersive /var/run/immersive
|
||||||
|
```
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create application directory
|
||||||
|
mkdir -p /opt/immersive
|
||||||
|
cd /opt/immersive
|
||||||
|
|
||||||
|
# Clone or copy the application
|
||||||
|
git clone <your-repo-url> .
|
||||||
|
# OR copy files manually
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
npm ci --production=false
|
||||||
|
|
||||||
|
# Build the application
|
||||||
|
NODE_OPTIONS='--max-old-space-size=4096' npm run build
|
||||||
|
|
||||||
|
# Copy Havok physics WASM (if not already done by build)
|
||||||
|
npm run havok
|
||||||
|
|
||||||
|
# Create data directory for PouchDB
|
||||||
|
mkdir -p /opt/immersive/data
|
||||||
|
|
||||||
|
# Set ownership to immersive user
|
||||||
|
chown -R immersive:immersive /opt/immersive
|
||||||
|
```
|
||||||
|
|
||||||
|
## Start Script
|
||||||
|
|
||||||
|
The `start.sh` script is included in the repository. After deployment, ensure it's executable:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod +x /opt/immersive/start.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
The script sets up the environment and starts the Node.js server, logging output to `/var/log/immersive/`.
|
||||||
|
|
||||||
|
## OpenRC Service
|
||||||
|
|
||||||
|
Create `/etc/init.d/immersive`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/sbin/openrc-run
|
||||||
|
|
||||||
|
name="immersive"
|
||||||
|
description="Immersive WebXR Diagramming Application"
|
||||||
|
|
||||||
|
command="/opt/immersive/start.sh"
|
||||||
|
command_user="immersive:immersive"
|
||||||
|
command_background="yes"
|
||||||
|
pidfile="/var/run/immersive/immersive.pid"
|
||||||
|
|
||||||
|
directory="/opt/immersive"
|
||||||
|
output_log="/var/log/immersive/app.log"
|
||||||
|
error_log="/var/log/immersive/error.log"
|
||||||
|
|
||||||
|
depend() {
|
||||||
|
need net
|
||||||
|
after firewall
|
||||||
|
}
|
||||||
|
|
||||||
|
start_pre() {
|
||||||
|
checkpath --directory --owner immersive:immersive --mode 0755 /var/log/immersive
|
||||||
|
checkpath --directory --owner immersive:immersive --mode 0755 /var/run/immersive
|
||||||
|
checkpath --file --owner immersive:immersive --mode 0644 /var/log/immersive/app.log
|
||||||
|
checkpath --file --owner immersive:immersive --mode 0644 /var/log/immersive/error.log
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Make it executable and enable:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod +x /etc/init.d/immersive
|
||||||
|
rc-update add immersive default
|
||||||
|
```
|
||||||
|
|
||||||
|
## Service Management
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start the service
|
||||||
|
rc-service immersive start
|
||||||
|
|
||||||
|
# Stop the service
|
||||||
|
rc-service immersive stop
|
||||||
|
|
||||||
|
# Restart the service
|
||||||
|
rc-service immersive restart
|
||||||
|
|
||||||
|
# Check status
|
||||||
|
rc-service immersive status
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
tail -f /var/log/immersive/app.log
|
||||||
|
tail -f /var/log/immersive/error.log
|
||||||
|
```
|
||||||
|
|
||||||
|
## Log Rotation
|
||||||
|
|
||||||
|
Create `/etc/logrotate.d/immersive`:
|
||||||
|
|
||||||
|
```
|
||||||
|
/var/log/immersive/*.log {
|
||||||
|
daily
|
||||||
|
rotate 7
|
||||||
|
compress
|
||||||
|
delaycompress
|
||||||
|
missingok
|
||||||
|
notifempty
|
||||||
|
create 0644 immersive immersive
|
||||||
|
postrotate
|
||||||
|
rc-service immersive restart > /dev/null 2>&1 || true
|
||||||
|
endscript
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Install logrotate if not present:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
apk add logrotate
|
||||||
|
```
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
Create `/opt/immersive/.env.production` for production settings:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Server
|
||||||
|
NODE_ENV=production
|
||||||
|
PORT=3001
|
||||||
|
|
||||||
|
# Auth0 (if using authentication)
|
||||||
|
# VITE_AUTH0_DOMAIN=your-domain.auth0.com
|
||||||
|
# VITE_AUTH0_CLIENT_ID=your-client-id
|
||||||
|
|
||||||
|
# Database sync endpoint (optional)
|
||||||
|
# VITE_SYNCDB_ENDPOINT=https://your-couchdb-server.com
|
||||||
|
```
|
||||||
|
|
||||||
|
## Firewall (if using iptables)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Allow port 3001
|
||||||
|
iptables -A INPUT -p tcp --dport 3001 -j ACCEPT
|
||||||
|
|
||||||
|
# Save rules
|
||||||
|
rc-service iptables save
|
||||||
|
```
|
||||||
|
|
||||||
|
## Reverse Proxy (Optional)
|
||||||
|
|
||||||
|
If using nginx as a reverse proxy:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
apk add nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
Create `/etc/nginx/http.d/immersive.conf`:
|
||||||
|
|
||||||
|
```nginx
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name your-domain.com;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:3001;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection 'upgrade';
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_cache_bypass $http_upgrade;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Enable and start nginx:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rc-update add nginx default
|
||||||
|
rc-service nginx start
|
||||||
|
```
|
||||||
|
|
||||||
|
## Gitea CI/CD Runner (Optional)
|
||||||
|
|
||||||
|
If using a Gitea Actions runner to deploy, grant the runner user write access to `/opt/immersive`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Add gitea-runner to immersive group
|
||||||
|
adduser gitea-runner immersive
|
||||||
|
|
||||||
|
# Set group write permissions on /opt/immersive
|
||||||
|
chmod -R g+w /opt/immersive
|
||||||
|
|
||||||
|
# Ensure new files inherit group ownership
|
||||||
|
chmod g+s /opt/immersive
|
||||||
|
|
||||||
|
# Allow runner to manage the service
|
||||||
|
# Add to /etc/sudoers.d/gitea-runner:
|
||||||
|
echo 'gitea-runner ALL=(ALL) NOPASSWD: /sbin/rc-service immersive *' > /etc/sudoers.d/gitea-runner
|
||||||
|
echo 'gitea-runner ALL=(ALL) NOPASSWD: /bin/chown -R immersive\:immersive /opt/immersive' >> /etc/sudoers.d/gitea-runner
|
||||||
|
chmod 440 /etc/sudoers.d/gitea-runner
|
||||||
|
```
|
||||||
|
|
||||||
|
The GitHub Actions workflow in `.github/workflows/build.yml` will handle deployment automatically on push to main.
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
**Service fails to start:**
|
||||||
|
```bash
|
||||||
|
# Check logs
|
||||||
|
cat /var/log/immersive/error.log
|
||||||
|
|
||||||
|
# Run manually as immersive user to see errors
|
||||||
|
su -s /bin/sh immersive -c "cd /opt/immersive && NODE_ENV=production node server.js"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Native module errors (leveldown):**
|
||||||
|
```bash
|
||||||
|
# Rebuild native modules
|
||||||
|
cd /opt/immersive
|
||||||
|
npm rebuild leveldown
|
||||||
|
```
|
||||||
|
|
||||||
|
**Permission issues:**
|
||||||
|
```bash
|
||||||
|
# Ensure proper ownership (must be immersive user)
|
||||||
|
chown -R immersive:immersive /opt/immersive
|
||||||
|
chown -R immersive:immersive /var/log/immersive
|
||||||
|
chown -R immersive:immersive /var/run/immersive
|
||||||
|
chmod -R 755 /opt/immersive
|
||||||
|
```
|
||||||
|
|
||||||
|
**Port already in use:**
|
||||||
|
```bash
|
||||||
|
# Find process using port 3001
|
||||||
|
lsof -i :3001
|
||||||
|
# Or
|
||||||
|
netstat -tlnp | grep 3001
|
||||||
|
```
|
||||||
137
CLAUDE.md
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
This is "immersive" - a WebXR/VR diagramming application built with BabylonJS and React. It allows users to create and interact with 3D diagrams in both standard web browsers and VR environments, with real-time collaboration via PouchDB sync.
|
||||||
|
|
||||||
|
## Build and Development Commands
|
||||||
|
|
||||||
|
### Development
|
||||||
|
- `npm run dev` - Start Vite dev server on port 3001 (DO NOT USE per user instructions)
|
||||||
|
- `npm run build` - Build production bundle (includes version bump)
|
||||||
|
- `npm run preview` - Preview production build on port 3001
|
||||||
|
- `npm test` - Run tests with Vitest
|
||||||
|
- `npm run socket` - Start WebSocket server for collaboration (port 8080)
|
||||||
|
- `npm run serverBuild` - Compile TypeScript server code
|
||||||
|
- `npm run havok` - Copy Havok physics WASM files to Vite deps
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
- Run all tests: `npm test`
|
||||||
|
- No single test command is configured; tests use Vitest
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### Core Technologies
|
||||||
|
- **BabylonJS 8.x**: 3D engine with WebXR support and Havok physics
|
||||||
|
- **React + Mantine**: UI framework for 2D interface and settings
|
||||||
|
- **PouchDB**: Client-side database with CouchDB sync for collaboration
|
||||||
|
- **Auth0**: Authentication provider
|
||||||
|
- **Vite**: Build tool and dev server
|
||||||
|
|
||||||
|
### Key Architecture Patterns
|
||||||
|
|
||||||
|
#### Singleton Scene Management
|
||||||
|
The application uses a singleton pattern for the BabylonJS Scene via `DefaultScene` (src/defaultScene.ts). Always access the scene through `DefaultScene.Scene` rather than creating new instances.
|
||||||
|
|
||||||
|
#### Observable-Based Event System
|
||||||
|
The application heavily uses BabylonJS Observables for event handling:
|
||||||
|
- **DiagramManager.onDiagramEventObservable**: Central hub for diagram entity changes
|
||||||
|
- **DiagramManager.onUserEventObservable**: User position/state updates for multiplayer
|
||||||
|
- **AppConfig.onConfigChangedObservable**: Application settings changes
|
||||||
|
- **controllerObservable**: VR controller input events
|
||||||
|
|
||||||
|
Event observers use a mask system (`DiagramEventObserverMask`) to distinguish:
|
||||||
|
- `FROM_DB`: Events coming from database sync (shouldn't trigger database writes)
|
||||||
|
- `TO_DB`: Events that should be persisted to database
|
||||||
|
|
||||||
|
#### Diagram Entity System
|
||||||
|
All 3D objects in the scene are represented by `DiagramEntity` types (src/diagram/types/diagramEntity.ts):
|
||||||
|
- Entities have a template reference (e.g., `#image-template`)
|
||||||
|
- Managed by `DiagramManager` which maintains a Map of `DiagramObject` instances
|
||||||
|
- Changes propagate through the Observable system to database and other clients
|
||||||
|
|
||||||
|
#### VR Controller Architecture
|
||||||
|
Controllers inherit from `AbstractController` with specialized implementations:
|
||||||
|
- `LeftController`: Menu interactions, navigation
|
||||||
|
- `RightController`: Object manipulation, selection
|
||||||
|
- Controllers communicate via `controllerObservable` with `ControllerEvent` messages
|
||||||
|
- `Rigplatform` manages the player rig and handles locomotion
|
||||||
|
|
||||||
|
#### Database & Sync
|
||||||
|
- `PouchdbPersistenceManager` (src/integration/database/pouchdbPersistenceManager.ts) handles all persistence
|
||||||
|
- Supports optional encryption via `Encryption` class
|
||||||
|
- Syncs to remote CouchDB via proxy (configured in vite.config.ts)
|
||||||
|
- URL pattern `/db/public/:db` or `/db/private/:db` determines database name
|
||||||
|
- Uses `presence.ts` for broadcasting user positions over WebSocket
|
||||||
|
|
||||||
|
### Project Structure
|
||||||
|
- `src/vrcore/`: Engine initialization and core VR setup
|
||||||
|
- `src/controllers/`: VR controller implementations and input handling
|
||||||
|
- `src/diagram/`: 3D diagram entities, management, and scene interaction
|
||||||
|
- `src/integration/`: Database sync, encryption, and presence system
|
||||||
|
- `src/menus/`: In-VR 3D menus (not React components)
|
||||||
|
- `src/objects/`: Reusable 3D objects (buttons, handles, avatars)
|
||||||
|
- `src/react/`: React UI components for 2D interface
|
||||||
|
- `src/util/`: Shared utilities and configuration
|
||||||
|
- `server/`: WebSocket server for real-time presence
|
||||||
|
|
||||||
|
### Configuration System
|
||||||
|
Two configuration systems exist (being migrated):
|
||||||
|
1. **AppConfig class** (src/util/appConfig.ts): Observable-based config with typed properties
|
||||||
|
2. **ConfigType** (bottom of appConfig.ts): Legacy localStorage-based config
|
||||||
|
|
||||||
|
Settings include snapping values, physics toggles, fly mode, and turn snap angles.
|
||||||
|
|
||||||
|
## Important Development Notes
|
||||||
|
|
||||||
|
### Proxy Configuration
|
||||||
|
The dev and preview servers proxy certain routes to production:
|
||||||
|
- `/sync/*` - Database sync endpoint
|
||||||
|
- `/create-db` - Database creation
|
||||||
|
- `/api/images` - Image uploads
|
||||||
|
|
||||||
|
### Physics System
|
||||||
|
- Uses Havok physics engine (requires WASM file via `npm run havok`)
|
||||||
|
- Physics can be enabled/disabled via AppConfig
|
||||||
|
- `customPhysics.ts` provides helper functions
|
||||||
|
|
||||||
|
### WebGPU Support
|
||||||
|
The engine initializer supports both WebGL and WebGPU backends via the `useWebGpu` parameter.
|
||||||
|
|
||||||
|
### Encryption
|
||||||
|
Databases can be optionally encrypted. The `Encryption` class handles AES encryption with password-derived keys. Salt is stored in metadata document.
|
||||||
|
|
||||||
|
### Environment Variables
|
||||||
|
- `VITE_USER_ENDPOINT`: User authentication endpoint
|
||||||
|
- `VITE_SYNCDB_ENDPOINT`: Remote database sync endpoint
|
||||||
|
|
||||||
|
Check `.env.local` for local configuration.
|
||||||
|
|
||||||
|
## Naming Conventions
|
||||||
|
|
||||||
|
### Tool and Material Naming
|
||||||
|
|
||||||
|
**Material Names:** Materials follow the pattern `material-{color}` where `{color}` is the hex color string (e.g., `material-#ff0000` for red).
|
||||||
|
|
||||||
|
**Tool Mesh Names:** Tools use the pattern `tool-{toolType}-{color}`:
|
||||||
|
- Example: `tool-BOX-#ff0000` (red box tool)
|
||||||
|
- ToolTypes: `BOX`, `SPHERE`, `CYLINDER`, `CONE`, `PLANE`, `PERSON`
|
||||||
|
|
||||||
|
**Tool Instance Names:** `tool-instance-{toolType}-{color}` (e.g., `tool-instance-BOX-#ff0000`)
|
||||||
|
|
||||||
|
**Implementation details:**
|
||||||
|
- 16 predefined toolbox colors (see docs/NAMING_CONVENTIONS.md)
|
||||||
|
- Materials created in `src/toolbox/functions/buildColor.ts`
|
||||||
|
- Tool meshes created in `src/toolbox/functions/buildTool.ts`
|
||||||
|
- When extracting colors from materials, use: `emissiveColor || diffuseColor` (priority order)
|
||||||
|
|
||||||
|
### Rendering Modes
|
||||||
|
|
||||||
|
Three rendering modes affect material properties:
|
||||||
|
1. **Lightmap with Lighting**: Uses `diffuseColor` + `lightmapTexture` (expensive)
|
||||||
|
2. **Unlit with Emissive Texture** (default): Uses `emissiveColor` + `emissiveTexture` (lightmap)
|
||||||
|
3. **Flat Emissive**: Uses only `emissiveColor` (fastest)
|
||||||
|
|
||||||
|
See `src/util/renderingMode.ts` and `src/util/lightmapGenerator.ts` for implementation.
|
||||||
403
EXPRESS_API_PLAN.md
Normal file
@ -0,0 +1,403 @@
|
|||||||
|
# Express.js API Server Plan
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
Add an Express.js backend server to handle API routes (starting with Claude API), with support for either combined or split deployment.
|
||||||
|
|
||||||
|
## Advantages Over Next.js Migration
|
||||||
|
- **Minimal frontend changes** - only API URL configuration
|
||||||
|
- **No routing changes** - keep react-router-dom as-is
|
||||||
|
- **Flexible deployment** - combined or split frontend/backend
|
||||||
|
- **Already partially exists** - `server.js` in root has Express + vite-express scaffolding
|
||||||
|
|
||||||
|
## Deployment Options
|
||||||
|
|
||||||
|
### Option A: Combined (Single Server)
|
||||||
|
```
|
||||||
|
Express Server (vite-express)
|
||||||
|
├── Serves static files from dist/
|
||||||
|
└── Handles /api/* routes
|
||||||
|
```
|
||||||
|
- Simpler setup, one deployment
|
||||||
|
- Good for: VPS, Railway, Fly.io, DigitalOcean App Platform
|
||||||
|
|
||||||
|
### Option B: Split (Separate Hosts)
|
||||||
|
```
|
||||||
|
Static Host (CDN) API Server (Node.js)
|
||||||
|
├── Cloudflare Pages ├── Railway
|
||||||
|
├── Netlify ├── Fly.io
|
||||||
|
├── Vercel ├── AWS Lambda
|
||||||
|
└── S3 + CloudFront └── Any VPS
|
||||||
|
|
||||||
|
Serves dist/ Handles /api/*
|
||||||
|
```
|
||||||
|
- Better scalability, cheaper static hosting
|
||||||
|
- Good for: High traffic, global CDN distribution
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Current State
|
||||||
|
|
||||||
|
### Existing `server.js` (incomplete)
|
||||||
|
```javascript
|
||||||
|
import express from "express";
|
||||||
|
import ViteExpress from "vite-express";
|
||||||
|
import dotenv from "dotenv";
|
||||||
|
import expressProxy from "express-http-proxy";
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
const app = express();
|
||||||
|
app.use("/api", expressProxy("local.immersiveidea.com"));
|
||||||
|
ViteExpress.listen(app, process.env.PORT || 3001, () => console.log("Server is listening..."));
|
||||||
|
```
|
||||||
|
|
||||||
|
### Missing Dependencies
|
||||||
|
The following packages are imported but not in package.json:
|
||||||
|
- `express`
|
||||||
|
- `vite-express`
|
||||||
|
- `express-http-proxy`
|
||||||
|
- `dotenv`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementation Plan
|
||||||
|
|
||||||
|
### Phase 1: Install Dependencies
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install express vite-express dotenv cors
|
||||||
|
```
|
||||||
|
|
||||||
|
- `express` - Web framework
|
||||||
|
- `vite-express` - Vite integration for combined deployment
|
||||||
|
- `dotenv` - Environment variable loading
|
||||||
|
- `cors` - Cross-origin support for split deployment
|
||||||
|
|
||||||
|
### Phase 2: Create API Routes Structure
|
||||||
|
|
||||||
|
Create a modular API structure:
|
||||||
|
|
||||||
|
```
|
||||||
|
server/
|
||||||
|
├── server.js # Existing WebSocket server (keep as-is)
|
||||||
|
├── api/
|
||||||
|
│ ├── index.js # Main API router
|
||||||
|
│ └── claude.js # Claude API proxy route
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 3: Update Root `server.js`
|
||||||
|
|
||||||
|
Replace the current incomplete server.js with:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import express from "express";
|
||||||
|
import ViteExpress from "vite-express";
|
||||||
|
import cors from "cors";
|
||||||
|
import dotenv from "dotenv";
|
||||||
|
import apiRoutes from "./server/api/index.js";
|
||||||
|
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
// CORS configuration for split deployment
|
||||||
|
// In combined mode, same-origin requests don't need CORS
|
||||||
|
const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(",") || [];
|
||||||
|
if (allowedOrigins.length > 0) {
|
||||||
|
app.use(cors({
|
||||||
|
origin: allowedOrigins,
|
||||||
|
credentials: true,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
app.use(express.json());
|
||||||
|
|
||||||
|
// API routes
|
||||||
|
app.use("/api", apiRoutes);
|
||||||
|
|
||||||
|
// Check if running in API-only mode (split deployment)
|
||||||
|
const apiOnly = process.env.API_ONLY === "true";
|
||||||
|
|
||||||
|
if (apiOnly) {
|
||||||
|
// API-only mode: no static file serving
|
||||||
|
app.listen(process.env.PORT || 3000, () => {
|
||||||
|
console.log(`API server running on port ${process.env.PORT || 3000}`);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Combined mode: Vite handles static files + SPA
|
||||||
|
ViteExpress.listen(app, process.env.PORT || 3001, () => {
|
||||||
|
console.log(`Server running on port ${process.env.PORT || 3001}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 4: Create API Router
|
||||||
|
|
||||||
|
**`server/api/index.js`**:
|
||||||
|
```javascript
|
||||||
|
import { Router } from "express";
|
||||||
|
import claudeRouter from "./claude.js";
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
// Claude API proxy
|
||||||
|
router.use("/claude", claudeRouter);
|
||||||
|
|
||||||
|
// Health check
|
||||||
|
router.get("/health", (req, res) => {
|
||||||
|
res.json({ status: "ok" });
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
|
```
|
||||||
|
|
||||||
|
**`server/api/claude.js`**:
|
||||||
|
```javascript
|
||||||
|
import { Router } from "express";
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
const ANTHROPIC_API_URL = "https://api.anthropic.com";
|
||||||
|
|
||||||
|
router.post("/*", async (req, res) => {
|
||||||
|
const apiKey = process.env.ANTHROPIC_API_KEY;
|
||||||
|
|
||||||
|
if (!apiKey) {
|
||||||
|
return res.status(500).json({ error: "API key not configured" });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the path after /api/claude (e.g., /v1/messages)
|
||||||
|
const path = req.params[0] || req.path;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${ANTHROPIC_API_URL}${path}`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"x-api-key": apiKey,
|
||||||
|
"anthropic-version": "2023-06-01",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(req.body),
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
res.status(response.status).json(data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Claude API error:", error);
|
||||||
|
res.status(500).json({ error: "Failed to proxy request to Claude API" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 5: Update Vite Config
|
||||||
|
|
||||||
|
Remove the Claude proxy from `vite.config.ts` since Express handles it now.
|
||||||
|
|
||||||
|
**Before** (lines 41-56):
|
||||||
|
```javascript
|
||||||
|
'^/api/claude': {
|
||||||
|
target: 'https://api.anthropic.com',
|
||||||
|
changeOrigin: true,
|
||||||
|
rewrite: (path) => path.replace(/^\/api\/claude/, ''),
|
||||||
|
configure: (proxy) => {
|
||||||
|
proxy.on('proxyReq', (proxyReq) => {
|
||||||
|
const apiKey = env.ANTHROPIC_API_KEY;
|
||||||
|
// ...
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**After**: Remove this block entirely. The Express server handles `/api/claude/*`.
|
||||||
|
|
||||||
|
Keep the other proxies (`/sync/*`, `/create-db`, `/api/images`) - they still proxy to deepdiagram.com in dev mode.
|
||||||
|
|
||||||
|
### Phase 6: Add API URL Configuration (for Split Deployment)
|
||||||
|
|
||||||
|
Create a utility to get the API base URL:
|
||||||
|
|
||||||
|
**`src/util/apiConfig.ts`**:
|
||||||
|
```typescript
|
||||||
|
// API base URL - empty string for same-origin (combined deployment)
|
||||||
|
// Set VITE_API_URL for split deployment (e.g., "https://api.yourdomain.com")
|
||||||
|
export const API_BASE_URL = import.meta.env.VITE_API_URL || '';
|
||||||
|
|
||||||
|
export function apiUrl(path: string): string {
|
||||||
|
return `${API_BASE_URL}${path}`;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Update `src/react/services/diagramAI.ts`**:
|
||||||
|
```typescript
|
||||||
|
import { apiUrl } from '../../util/apiConfig';
|
||||||
|
|
||||||
|
// Change from:
|
||||||
|
const response = await fetch('/api/claude/v1/messages', { ... });
|
||||||
|
|
||||||
|
// To:
|
||||||
|
const response = await fetch(apiUrl('/api/claude/v1/messages'), { ... });
|
||||||
|
```
|
||||||
|
|
||||||
|
This change is backward-compatible:
|
||||||
|
- **Combined deployment**: `VITE_API_URL` is empty, calls go to same origin
|
||||||
|
- **Split deployment**: `VITE_API_URL=https://api.example.com`, calls go to API server
|
||||||
|
|
||||||
|
### Phase 7: Update package.json Scripts
|
||||||
|
|
||||||
|
```json
|
||||||
|
"scripts": {
|
||||||
|
"dev": "node server.js",
|
||||||
|
"build": "node versionBump.js && vite build",
|
||||||
|
"start": "NODE_ENV=production node server.js",
|
||||||
|
"start:api": "API_ONLY=true node server.js",
|
||||||
|
"test": "vitest",
|
||||||
|
"socket": "node server/server.js",
|
||||||
|
"serverBuild": "cd server && tsc"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Changes:**
|
||||||
|
- `dev`: Runs Express + vite-express (serves Vite in dev mode)
|
||||||
|
- `start`: Combined mode - serves dist/ + API
|
||||||
|
- `start:api`: API-only mode for split deployment
|
||||||
|
- Removed `preview` (use `start` instead)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## File Changes Summary
|
||||||
|
|
||||||
|
| Action | File | Description |
|
||||||
|
|--------|------|-------------|
|
||||||
|
| Modify | `package.json` | Add dependencies, update scripts |
|
||||||
|
| Modify | `server.js` | Full Express server with CORS + API routes |
|
||||||
|
| Create | `server/api/index.js` | Main API router |
|
||||||
|
| Create | `server/api/claude.js` | Claude API proxy endpoint |
|
||||||
|
| Create | `src/util/apiConfig.ts` | API URL configuration utility |
|
||||||
|
| Modify | `src/react/services/diagramAI.ts` | Use apiUrl() for API calls |
|
||||||
|
| Modify | `vite.config.ts` | Remove `/api/claude` proxy block |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## How vite-express Works
|
||||||
|
|
||||||
|
`vite-express` is a simple integration that:
|
||||||
|
|
||||||
|
1. **Development**: Runs Vite's dev server as middleware, providing HMR
|
||||||
|
2. **Production**: Serves the built `dist/` folder as static files
|
||||||
|
|
||||||
|
This means:
|
||||||
|
- One server handles both API and frontend
|
||||||
|
- No CORS issues (same origin)
|
||||||
|
- HMR works in development
|
||||||
|
- Production-ready with `vite build`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Production Deployment
|
||||||
|
|
||||||
|
### Option A: Combined Deployment
|
||||||
|
|
||||||
|
Single server handles both frontend and API:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build frontend
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# Start combined server (serves dist/ + API)
|
||||||
|
npm run start
|
||||||
|
```
|
||||||
|
|
||||||
|
**Environment variables (.env)**:
|
||||||
|
```bash
|
||||||
|
PORT=3001
|
||||||
|
ANTHROPIC_API_KEY=sk-ant-...
|
||||||
|
```
|
||||||
|
|
||||||
|
The Express server will:
|
||||||
|
1. Handle `/api/*` routes directly
|
||||||
|
2. Serve static files from `dist/`
|
||||||
|
3. Fall back to `dist/index.html` for SPA routing
|
||||||
|
|
||||||
|
### Option B: Split Deployment
|
||||||
|
|
||||||
|
Separate hosting for frontend (CDN) and API (Node server):
|
||||||
|
|
||||||
|
**API Server:**
|
||||||
|
```bash
|
||||||
|
# Start API-only server
|
||||||
|
npm run start:api
|
||||||
|
```
|
||||||
|
|
||||||
|
**Environment variables (.env for API server)**:
|
||||||
|
```bash
|
||||||
|
PORT=3000
|
||||||
|
API_ONLY=true
|
||||||
|
ANTHROPIC_API_KEY=sk-ant-...
|
||||||
|
ALLOWED_ORIGINS=https://your-frontend.com,https://www.your-frontend.com
|
||||||
|
```
|
||||||
|
|
||||||
|
**Frontend (Static Host):**
|
||||||
|
```bash
|
||||||
|
# Build with API URL configured
|
||||||
|
VITE_API_URL=https://api.yourdomain.com npm run build
|
||||||
|
|
||||||
|
# Deploy dist/ to your static host (Cloudflare Pages, Netlify, etc.)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Environment variables (.env.production for frontend build)**:
|
||||||
|
```bash
|
||||||
|
VITE_API_URL=https://api.yourdomain.com
|
||||||
|
```
|
||||||
|
|
||||||
|
### Deployment Examples
|
||||||
|
|
||||||
|
| Deployment | Frontend | API Server | Cost |
|
||||||
|
|------------|----------|------------|------|
|
||||||
|
| Combined | Railway | (same) | ~$5/mo |
|
||||||
|
| Combined | Fly.io | (same) | Free tier |
|
||||||
|
| Split | Cloudflare Pages (free) | Railway ($5/mo) | ~$5/mo |
|
||||||
|
| Split | Netlify (free) | Fly.io (free) | Free |
|
||||||
|
| Split | Vercel (free) | AWS Lambda | Pay-per-use |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Future API Routes
|
||||||
|
|
||||||
|
To add more API routes, create new files in `server/api/`:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// server/api/index.js
|
||||||
|
import claudeRouter from "./claude.js";
|
||||||
|
import imagesRouter from "./images.js"; // future
|
||||||
|
import authRouter from "./auth.js"; // future
|
||||||
|
|
||||||
|
router.use("/claude", claudeRouter);
|
||||||
|
router.use("/images", imagesRouter);
|
||||||
|
router.use("/auth", authRouter);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Migration Order
|
||||||
|
|
||||||
|
1. `npm install express vite-express dotenv cors`
|
||||||
|
2. Create `server/api/index.js`
|
||||||
|
3. Create `server/api/claude.js`
|
||||||
|
4. Create `src/util/apiConfig.ts`
|
||||||
|
5. Update `src/react/services/diagramAI.ts` to use `apiUrl()`
|
||||||
|
6. Update `server.js` (root) with full Express + CORS setup
|
||||||
|
7. Remove `/api/claude` proxy from `vite.config.ts`
|
||||||
|
8. Update `package.json` scripts
|
||||||
|
9. Test combined: `npm run dev` and verify Claude API works
|
||||||
|
10. (Optional) Test split: Set `VITE_API_URL` and `API_ONLY=true`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- **WebSocket server unchanged**: `server/server.js` (port 8080) runs separately
|
||||||
|
- **Minimal frontend changes**: Only `diagramAI.ts` updated to use `apiUrl()`
|
||||||
|
- **Environment variables**: `ANTHROPIC_API_KEY` already in `.env.local`
|
||||||
|
- **Node version**: Requires Node 18+ for native `fetch`
|
||||||
|
- **CORS**: Only enabled when `ALLOWED_ORIGINS` is set (split deployment)
|
||||||
|
- **Backward compatible**: Works as combined deployment by default
|
||||||
30
LICENSE.txt
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
|
||||||
|
Permissions Conditions Limitations
|
||||||
|
Commercial use
|
||||||
|
Distribution
|
||||||
|
Modification
|
||||||
|
Private use
|
||||||
|
License and copyright notice
|
||||||
|
Liability
|
||||||
|
Warranty
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) [2024] [Michael Mainguy]
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
167
NEXT_MIGRATION_PLAN.md
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
# Vite to Next.js Migration Plan
|
||||||
|
|
||||||
|
## Goal
|
||||||
|
Migrate from Vite to Next.js App Router to get proper API route support, with minimal changes to existing code.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
- **Router**: App Router with `'use client'` on all pages
|
||||||
|
- **Rendering**: CSR only (no SSR) - simplifies migration since BabylonJS can't SSR
|
||||||
|
- **API Routes**: Claude API now, structured for future expansion
|
||||||
|
- **External Proxies**: Keep sync/create-db/images as Next.js rewrites to deepdiagram.com
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 1: Setup (No Breaking Changes)
|
||||||
|
|
||||||
|
### 1.1 Install Next.js
|
||||||
|
```bash
|
||||||
|
npm install next
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.2 Create `next.config.js`
|
||||||
|
```javascript
|
||||||
|
/** @type {import('next').NextConfig} */
|
||||||
|
const nextConfig = {
|
||||||
|
async rewrites() {
|
||||||
|
return [
|
||||||
|
{ source: '/sync/:path*', destination: 'https://www.deepdiagram.com/sync/:path*' },
|
||||||
|
{ source: '/create-db', destination: 'https://www.deepdiagram.com/create-db' },
|
||||||
|
{ source: '/api/images', destination: 'https://www.deepdiagram.com/api/images' },
|
||||||
|
];
|
||||||
|
},
|
||||||
|
webpack: (config, { isServer }) => {
|
||||||
|
config.experiments = { ...config.experiments, asyncWebAssembly: true };
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
module.exports = nextConfig;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 1.3 Update `tsconfig.json`
|
||||||
|
Add path alias:
|
||||||
|
```json
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": { "@/*": ["./*"] }
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 2: Create New Files
|
||||||
|
|
||||||
|
### 2.1 `src/react/providers.tsx` (extract from webApp.tsx)
|
||||||
|
- Move Auth0Provider and FeatureProvider wrapping here
|
||||||
|
- Add `'use client'` directive
|
||||||
|
- Handle window/document checks for SSR safety
|
||||||
|
|
||||||
|
### 2.2 `app/layout.tsx`
|
||||||
|
- Root layout with html/body tags
|
||||||
|
- Metadata (title, favicon from current index.html)
|
||||||
|
- Import global CSS
|
||||||
|
|
||||||
|
### 2.3 `app/globals.css`
|
||||||
|
```css
|
||||||
|
@import '../src/react/styles.css';
|
||||||
|
@import '@mantine/core/styles.css';
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.4 `app/api/claude/[...path]/route.ts`
|
||||||
|
- POST handler that proxies to api.anthropic.com
|
||||||
|
- Injects `ANTHROPIC_API_KEY` from env
|
||||||
|
- Adds `x-api-key` and `anthropic-version` headers
|
||||||
|
|
||||||
|
### 2.5 Page files (all with `'use client'`)
|
||||||
|
| Route | File | Component |
|
||||||
|
|-------|------|-----------|
|
||||||
|
| `/` | `app/page.tsx` | About |
|
||||||
|
| `/documentation` | `app/documentation/page.tsx` | Documentation |
|
||||||
|
| `/examples` | `app/examples/page.tsx` | Examples |
|
||||||
|
| `/pricing` | `app/pricing/page.tsx` | Pricing |
|
||||||
|
| `/db/[visibility]/[db]` | `app/db/[visibility]/[db]/page.tsx` | VrExperience |
|
||||||
|
| 404 | `app/not-found.tsx` | NotFound |
|
||||||
|
|
||||||
|
### 2.6 `src/react/components/ProtectedPage.tsx`
|
||||||
|
- Next.js version of route protection
|
||||||
|
- Uses `useRouter` from `next/navigation` for redirects
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 3: Modify Existing Files
|
||||||
|
|
||||||
|
### 3.1 `src/react/pages/vrExperience.tsx`
|
||||||
|
**Changes:**
|
||||||
|
- Remove `useParams()` from react-router-dom
|
||||||
|
- Accept `visibility` and `db` as props instead
|
||||||
|
- Replace `useNavigate()` with `useRouter()` from `next/navigation`
|
||||||
|
|
||||||
|
### 3.2 `src/react/pageHeader.tsx`
|
||||||
|
**Changes:**
|
||||||
|
- Replace `import {Link} from "react-router-dom"` with `import Link from "next/link"`
|
||||||
|
- Change `to={item.href}` to `href={item.href}` on Link components
|
||||||
|
|
||||||
|
### 3.3 `src/react/marketing/about.tsx`
|
||||||
|
**Changes:**
|
||||||
|
- Replace `useNavigate()` with `useRouter()` from `next/navigation`
|
||||||
|
- Change `navigate('/path')` to `router.push('/path')`
|
||||||
|
|
||||||
|
### 3.4 `package.json`
|
||||||
|
```json
|
||||||
|
"scripts": {
|
||||||
|
"dev": "next dev -p 3001",
|
||||||
|
"build": "node versionBump.js && next build",
|
||||||
|
"start": "next start -p 3001",
|
||||||
|
"test": "vitest",
|
||||||
|
"socket": "node server/server.js",
|
||||||
|
"serverBuild": "cd server && tsc"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 4: Delete Old Files
|
||||||
|
|
||||||
|
| File | Reason |
|
||||||
|
|------|--------|
|
||||||
|
| `vite.config.ts` | Replaced by next.config.js |
|
||||||
|
| `index.html` | Next.js generates HTML |
|
||||||
|
| `src/webApp.ts` | Entry point no longer needed |
|
||||||
|
| `src/react/webRouter.tsx` | Replaced by app/ routing |
|
||||||
|
| `src/react/webApp.tsx` | Logic moved to providers.tsx |
|
||||||
|
| `src/react/components/ProtectedRoute.tsx` | Replaced by ProtectedPage.tsx |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Critical Files to Modify
|
||||||
|
|
||||||
|
- `src/react/pages/vrExperience.tsx` - useParams -> props
|
||||||
|
- `src/react/pageHeader.tsx` - react-router Link -> Next.js Link
|
||||||
|
- `src/react/marketing/about.tsx` - useNavigate -> useRouter
|
||||||
|
- `src/react/webApp.tsx` - extract to providers.tsx
|
||||||
|
- `package.json` - scripts update
|
||||||
|
- `tsconfig.json` - path aliases
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Migration Order
|
||||||
|
|
||||||
|
1. Install next, create next.config.js
|
||||||
|
2. Update tsconfig.json
|
||||||
|
3. Create app/globals.css
|
||||||
|
4. Create src/react/providers.tsx
|
||||||
|
5. Create app/layout.tsx
|
||||||
|
6. Create app/api/claude/[...path]/route.ts
|
||||||
|
7. Create src/react/components/ProtectedPage.tsx
|
||||||
|
8. Modify vrExperience.tsx (accept props)
|
||||||
|
9. Create all app/*/page.tsx files
|
||||||
|
10. Modify pageHeader.tsx (Next.js Link)
|
||||||
|
11. Modify about.tsx (useRouter)
|
||||||
|
12. Update package.json scripts
|
||||||
|
13. Delete old files (vite.config.ts, index.html, webApp.ts, webRouter.tsx, webApp.tsx)
|
||||||
|
14. Test all routes
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- **Havok WASM**: Move `HavokPhysics.wasm` to `public/` folder
|
||||||
|
- **react-router-dom**: Can be removed from dependencies after migration
|
||||||
|
- **vite devDependencies**: Can be removed (vite, vite-plugin-cp)
|
||||||
224
ROADMAP.md
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
# Immersive - Product Roadmap
|
||||||
|
|
||||||
|
## Vision
|
||||||
|
Transform immersive into an accessible, intuitive WebXR diagramming platform that delivers a frictionless onboarding experience and sustainable growth path.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 1: Onboarding & User Experience (Q1 2025)
|
||||||
|
|
||||||
|
### 1.1 Frictionless Entry
|
||||||
|
**Goal:** Reduce barriers to entry for new users
|
||||||
|
|
||||||
|
- [ ] Redesign landing page to clearly guide users to immersive experience
|
||||||
|
- [ ] Create one-click "Enter VR" / "Try Demo" workflow
|
||||||
|
- [ ] Optimize initial load time and progressive loading
|
||||||
|
- [ ] Add clear device compatibility messaging (desktop/VR)
|
||||||
|
- [ ] Implement guest mode with no sign-in required for basic exploration
|
||||||
|
|
||||||
|
### 1.2 Marketing Content
|
||||||
|
**Goal:** Communicate value proposition effectively
|
||||||
|
|
||||||
|
- [ ] Create 3-5 demo videos showcasing key features (30-60 seconds each)
|
||||||
|
- Creating a basic diagram
|
||||||
|
- VR interaction showcase
|
||||||
|
- Collaboration features
|
||||||
|
- Template usage
|
||||||
|
- [ ] Develop tutorial video (2-3 minutes) explaining core workflows
|
||||||
|
- [ ] Autoplay video carousel on landing page
|
||||||
|
- [ ] Write marketing copy for landing page
|
||||||
|
- Hero section with clear value proposition
|
||||||
|
- Feature highlights
|
||||||
|
- Use case examples
|
||||||
|
- Call-to-action
|
||||||
|
|
||||||
|
### 1.3 In-Experience Tutorial
|
||||||
|
**Goal:** Replace external tutorial with immersive learning
|
||||||
|
|
||||||
|
- [ ] Remove existing external tutorial system
|
||||||
|
- [ ] Design in-VR tutorial experience with interactive steps
|
||||||
|
- [ ] Implement progressive disclosure (teach as users interact)
|
||||||
|
- [ ] Add contextual tooltips and hints in 3D space
|
||||||
|
- [ ] Create "first-time user" detection and guided walkthrough
|
||||||
|
- [ ] Add skip/replay tutorial options
|
||||||
|
|
||||||
|
### 1.4 Template System
|
||||||
|
**Goal:** Provide starting points for new users
|
||||||
|
|
||||||
|
- [ ] Design template/example diagram system
|
||||||
|
- [ ] Create 5-10 starter templates:
|
||||||
|
- Simple organizational chart
|
||||||
|
- Project workflow diagram
|
||||||
|
- Concept mapping example
|
||||||
|
- Architecture diagram
|
||||||
|
- Spatial layout example
|
||||||
|
- [ ] Build template browser UI (2D and VR)
|
||||||
|
- [ ] Implement "New from Template" workflow
|
||||||
|
- [ ] Add template preview/thumbnail generation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 2: Collaboration & Sync (Q2 2025)
|
||||||
|
|
||||||
|
### 2.1 Cross-Device Sharing
|
||||||
|
**Goal:** Enable seamless content sharing between desktop and Quest
|
||||||
|
|
||||||
|
- [ ] Research device-to-device sync options (WebRTC, local network)
|
||||||
|
- [ ] Design sync architecture without backend dependency
|
||||||
|
- [ ] Implement user content sync for signed-in users
|
||||||
|
- [ ] Add fallback to server-based sync when needed
|
||||||
|
- [ ] Create device pairing UI/workflow
|
||||||
|
- [ ] Test sync reliability across desktop ↔ Quest
|
||||||
|
- [ ] Add conflict resolution for simultaneous edits
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 3: Immersion & Environment (Q2-Q3 2025)
|
||||||
|
|
||||||
|
### 3.1 Audio Integration
|
||||||
|
**Goal:** Enhance presence with ambient soundscapes
|
||||||
|
|
||||||
|
- [ ] Source/create ambient audio assets
|
||||||
|
- Nature sounds (birds, wind, water)
|
||||||
|
- Office ambience
|
||||||
|
- Abstract/focus music
|
||||||
|
- [ ] Implement spatial audio system
|
||||||
|
- [ ] Add audio settings (volume, on/off, environment selection)
|
||||||
|
- [ ] Create audio manager for seamless transitions
|
||||||
|
- [ ] Add positional audio for collaboration (optional user voices)
|
||||||
|
|
||||||
|
### 3.2 Environment System
|
||||||
|
**Goal:** Provide varied immersive environments
|
||||||
|
|
||||||
|
- [ ] Design environment switching architecture
|
||||||
|
- [ ] Create environment presets:
|
||||||
|
- Outdoor/nature scene
|
||||||
|
- Modern office
|
||||||
|
- Abstract/minimal space
|
||||||
|
- Workshop/studio
|
||||||
|
- [ ] Implement skybox and lighting variations
|
||||||
|
- [ ] Build environment selector UI (2D and VR)
|
||||||
|
- [ ] Optimize environment assets for performance
|
||||||
|
- [ ] Add environment-specific audio pairing
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 4: User Feedback & Polish (Q3 2025)
|
||||||
|
|
||||||
|
### 4.1 In-VR Feedback Mechanism
|
||||||
|
**Goal:** Enable users to provide feedback without leaving VR
|
||||||
|
|
||||||
|
- [ ] Design in-VR feedback form/interface
|
||||||
|
- [ ] Implement voice-to-text option (VR accessibility)
|
||||||
|
- [ ] Add screenshot/recording attachment capability
|
||||||
|
- [ ] Create feedback submission backend
|
||||||
|
- [ ] Build feedback review dashboard
|
||||||
|
- [ ] Add "Report Bug" quick action in VR menu
|
||||||
|
|
||||||
|
### 4.2 Keyboard Improvements
|
||||||
|
**Goal:** Improve text input experience
|
||||||
|
|
||||||
|
- [ ] Test system keyboard integration (Quest/desktop)
|
||||||
|
- [ ] Evaluate custom keyboard vs. native keyboard UX
|
||||||
|
- [ ] Implement system keyboard fallback where supported
|
||||||
|
- [ ] Optimize keyboard positioning in VR space
|
||||||
|
- [ ] Add keyboard shortcuts for power users (desktop)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 5: Growth & Monetization (Q4 2025)
|
||||||
|
|
||||||
|
### 5.1 Marketing Roadmap
|
||||||
|
**Goal:** Build sustainable user acquisition
|
||||||
|
|
||||||
|
- [ ] Define target audience segments
|
||||||
|
- Educators
|
||||||
|
- Remote teams
|
||||||
|
- Designers/architects
|
||||||
|
- Knowledge workers
|
||||||
|
- [ ] Create content marketing strategy
|
||||||
|
- Blog posts on use cases
|
||||||
|
- Social media showcase
|
||||||
|
- Community building (Discord/Reddit)
|
||||||
|
- [ ] Develop SEO optimization plan
|
||||||
|
- [ ] Plan partnership outreach (VR communities, productivity tools)
|
||||||
|
- [ ] Create referral/sharing incentives
|
||||||
|
- [ ] Build analytics dashboard for user metrics
|
||||||
|
|
||||||
|
### 5.2 Monetization Strategy
|
||||||
|
**Goal:** Establish path to sustainability
|
||||||
|
|
||||||
|
**Potential Revenue Streams:**
|
||||||
|
- [ ] Freemium model research
|
||||||
|
- Free tier: Limited diagrams, basic features
|
||||||
|
- Pro tier: Unlimited diagrams, advanced features, collaboration
|
||||||
|
- [ ] Team/Enterprise pricing
|
||||||
|
- Private deployment options
|
||||||
|
- Admin controls
|
||||||
|
- Priority support
|
||||||
|
- [ ] Template marketplace
|
||||||
|
- Premium templates
|
||||||
|
- Community submissions (revenue share)
|
||||||
|
- [ ] Educational licensing
|
||||||
|
- Institutional pricing
|
||||||
|
- Classroom management features
|
||||||
|
|
||||||
|
**Implementation:**
|
||||||
|
- [ ] Define pricing tiers and feature gates
|
||||||
|
- [ ] Integrate payment processing (Stripe)
|
||||||
|
- [ ] Build subscription management UI
|
||||||
|
- [ ] Implement feature flags for tier differentiation
|
||||||
|
- [ ] Create upgrade prompts and conversion flow
|
||||||
|
- [ ] Add usage analytics for pricing optimization
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Success Metrics
|
||||||
|
|
||||||
|
### Phase 1-2 (Onboarding)
|
||||||
|
- Time to first diagram creation < 2 minutes
|
||||||
|
- Tutorial completion rate > 60%
|
||||||
|
- Return user rate (7-day) > 30%
|
||||||
|
|
||||||
|
### Phase 3-4 (Engagement)
|
||||||
|
- Average session duration > 15 minutes
|
||||||
|
- User satisfaction score > 4/5
|
||||||
|
- Feedback submission rate (active users) > 10%
|
||||||
|
|
||||||
|
### Phase 5 (Growth)
|
||||||
|
- Monthly active users growth > 20% MoM
|
||||||
|
- Free-to-paid conversion rate > 5%
|
||||||
|
- Customer acquisition cost < lifetime value
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Technical Debt & Infrastructure
|
||||||
|
|
||||||
|
### Ongoing Priorities
|
||||||
|
- [ ] Migration from legacy ConfigType to AppConfig
|
||||||
|
- [ ] Performance optimization (target 90fps in VR)
|
||||||
|
- [ ] Accessibility improvements (WCAG compliance)
|
||||||
|
- [ ] Testing coverage > 70%
|
||||||
|
- [ ] Documentation for contributors
|
||||||
|
- [ ] CI/CD pipeline enhancements
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
**Dependencies:**
|
||||||
|
- Auth0 for user authentication
|
||||||
|
- PouchDB/CouchDB for data persistence
|
||||||
|
- BabylonJS 8.x for rendering
|
||||||
|
- Vite for build tooling
|
||||||
|
|
||||||
|
**Platform Support:**
|
||||||
|
- Desktop browsers (Chrome, Firefox, Edge)
|
||||||
|
- Meta Quest 2/3/Pro
|
||||||
|
- Future: PSVR2, Vision Pro (evaluate demand)
|
||||||
|
|
||||||
|
**Review Cadence:** Quarterly roadmap review and adjustment based on user feedback and metrics.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Last Updated: 2025-11-19*
|
||||||
150
SHARING_PLAN.md
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
# Self-Hosted Diagram Sharing with Express-PouchDB
|
||||||
|
|
||||||
|
## Requirements (Confirmed)
|
||||||
|
- **Storage**: In-memory (ephemeral) - lost on server restart
|
||||||
|
- **Content**: Copy current diagram entities when creating share
|
||||||
|
- **Expiration**: No expiration - links work until server restart
|
||||||
|
- **Encryption**: None - keep it simple, anyone with link can access
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────┐
|
||||||
|
│ Express Server (port 3001) │
|
||||||
|
├─────────────────────────────────────────────────┤
|
||||||
|
│ /api/share/* → Share management API │
|
||||||
|
│ /pouchdb/* → express-pouchdb (sync) │
|
||||||
|
│ /share/:uuid → Client share route │
|
||||||
|
│ /api/* → Existing API routes │
|
||||||
|
│ /* → Vite static files │
|
||||||
|
└─────────────────────────────────────────────────┘
|
||||||
|
│
|
||||||
|
└── In-Memory PouchDB (per share UUID)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implementation Steps
|
||||||
|
|
||||||
|
### Phase 1: Server-Side Setup
|
||||||
|
|
||||||
|
#### 1.1 Add Dependencies
|
||||||
|
```bash
|
||||||
|
npm install express-pouchdb pouchdb-adapter-memory
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 1.2 Create PouchDB Server Service
|
||||||
|
**New file: `server/services/pouchdbServer.js`**
|
||||||
|
- Initialize PouchDB with memory adapter
|
||||||
|
- Track active share databases in a Map
|
||||||
|
- Export `getShareDB(shareId)`, `shareExists(shareId)`, `createPouchDBMiddleware()`
|
||||||
|
|
||||||
|
#### 1.3 Create Share API
|
||||||
|
**New file: `server/api/share.js`**
|
||||||
|
- `POST /api/share/create` - Generate UUID, create in-memory DB, copy entities
|
||||||
|
- `GET /api/share/:id/exists` - Check if share exists
|
||||||
|
- `GET /api/share/stats` - Debug endpoint for active shares
|
||||||
|
|
||||||
|
#### 1.4 Update API Router
|
||||||
|
**Edit: `server/api/index.js`**
|
||||||
|
- Add `import shareRouter from "./share.js"`
|
||||||
|
- Mount at `router.use("/share", shareRouter)`
|
||||||
|
|
||||||
|
#### 1.5 Mount Express-PouchDB
|
||||||
|
**Edit: `server.js`**
|
||||||
|
- Import `createPouchDBMiddleware` from pouchdbServer.js
|
||||||
|
- Mount at `app.use("/pouchdb", createPouchDBMiddleware())`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 2: Client-Side Integration
|
||||||
|
|
||||||
|
#### 2.1 Update URL Parsing
|
||||||
|
**Edit: `src/util/functions/getPath.ts`**
|
||||||
|
- Add `getPathInfo()` function returning `{ dbName, isShare, shareId }`
|
||||||
|
- Detect `/share/:uuid` pattern
|
||||||
|
|
||||||
|
#### 2.2 Update PouchDB Persistence Manager
|
||||||
|
**Edit: `src/integration/database/pouchdbPersistenceManager.ts`**
|
||||||
|
|
||||||
|
In `initLocal()`:
|
||||||
|
- Call `getPathInfo()` to detect share URLs
|
||||||
|
- If share: use `share-{uuid}` as local DB name, call `beginShareSync()`
|
||||||
|
|
||||||
|
Add new method `beginShareSync(shareId)`:
|
||||||
|
- Check share exists via `/api/share/:id/exists`
|
||||||
|
- Connect to `${origin}/pouchdb/share-${shareId}`
|
||||||
|
- Set up presence with `share-${shareId}` as DB name
|
||||||
|
- Begin live sync (no encryption)
|
||||||
|
|
||||||
|
#### 2.3 Add React Route
|
||||||
|
**Edit: `src/react/webRouter.tsx`**
|
||||||
|
- Add route `{ path: "/share/:uuid", element: <VrExperience isShare={true} /> }`
|
||||||
|
- No ProtectedRoute wrapper (public access)
|
||||||
|
|
||||||
|
#### 2.4 Add Share Button Handler
|
||||||
|
**Edit: `src/react/pages/vrExperience.tsx`**
|
||||||
|
- Add `isShare` prop
|
||||||
|
- Add `handleShare()` function:
|
||||||
|
1. Get all entities from local PouchDB
|
||||||
|
2. POST to `/api/share/create` with entities
|
||||||
|
3. Copy resulting URL to clipboard
|
||||||
|
4. Show confirmation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Phase 3: Presence Integration
|
||||||
|
|
||||||
|
The WebSocket presence system already routes by database name. Since shares use `share-{uuid}` as the database name, presence works automatically.
|
||||||
|
|
||||||
|
**Edit: `server/server.js`** (WebSocket server)
|
||||||
|
- Update `originIsAllowed()` to allow localhost for development
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Files to Modify
|
||||||
|
|
||||||
|
| File | Action | Purpose |
|
||||||
|
|------|--------|---------|
|
||||||
|
| `package.json` | Edit | Add express-pouchdb, pouchdb-adapter-memory |
|
||||||
|
| `server.js` | Edit | Mount /pouchdb middleware |
|
||||||
|
| `server/api/index.js` | Edit | Add share router |
|
||||||
|
| `server/services/pouchdbServer.js` | Create | PouchDB memory initialization |
|
||||||
|
| `server/api/share.js` | Create | Share API endpoints |
|
||||||
|
| `server/server.js` | Edit | Allow localhost origins |
|
||||||
|
| `src/util/functions/getPath.ts` | Edit | Add getPathInfo() |
|
||||||
|
| `src/integration/database/pouchdbPersistenceManager.ts` | Edit | Add share sync logic |
|
||||||
|
| `src/react/webRouter.tsx` | Edit | Add /share/:uuid route |
|
||||||
|
| `src/react/pages/vrExperience.tsx` | Edit | Add share button handler |
|
||||||
|
| `src/util/featureConfig.ts` | Edit | Enable shareCollaborate feature |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## User Flow
|
||||||
|
|
||||||
|
### Creating a Share
|
||||||
|
1. User has a diagram open at `/db/public/mydiagram`
|
||||||
|
2. Clicks "Share" button
|
||||||
|
3. Client fetches all entities from local PouchDB
|
||||||
|
4. POSTs to `/api/share/create` with entities
|
||||||
|
5. Server creates in-memory DB, copies entities, returns UUID
|
||||||
|
6. Client copies `https://server.com/share/{uuid}` to clipboard
|
||||||
|
7. User shares link with collaborators
|
||||||
|
|
||||||
|
### Joining a Share
|
||||||
|
1. User navigates to `https://server.com/share/{uuid}`
|
||||||
|
2. React Router renders VrExperience with `isShare=true`
|
||||||
|
3. PouchdbPersistenceManager detects share URL
|
||||||
|
4. Checks `/api/share/:uuid/exists` - returns true
|
||||||
|
5. Creates local PouchDB `share-{uuid}`
|
||||||
|
6. Connects to `/pouchdb/share-{uuid}` for sync
|
||||||
|
7. Entities replicate to local, render in scene
|
||||||
|
8. Presence WebSocket connects with `share-{uuid}` as room
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Future Authentication (Not Implemented Now)
|
||||||
|
|
||||||
|
Structure allows easy addition later:
|
||||||
|
- express-pouchdb middleware can be wrapped with auth middleware
|
||||||
|
- Share API can require JWT/session tokens
|
||||||
|
- Could add password-protected shares
|
||||||
|
- Could add read-only vs read-write permissions
|
||||||
179
SYNC_PLAN.md
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
# Future Sync Strategy: Keeping Local and Public Clones in Sync
|
||||||
|
|
||||||
|
## Current State (v1)
|
||||||
|
|
||||||
|
- Sharing creates a **ONE-TIME COPY** from local to public
|
||||||
|
- Copies diverge independently after sharing
|
||||||
|
- No automatic sync between local and public versions
|
||||||
|
- Local diagrams are browser-only (IndexedDB via PouchDB)
|
||||||
|
- Public diagrams sync with server via express-pouchdb
|
||||||
|
|
||||||
|
### URL Scheme
|
||||||
|
|
||||||
|
| Route | Sync | Access | Status |
|
||||||
|
|-------|------|--------|--------|
|
||||||
|
| `/db/local/:id` | None | Browser-only | Implemented |
|
||||||
|
| `/db/public/:id` | Yes | Anyone | Implemented |
|
||||||
|
| `/db/private/:id` | Yes | Authorized users | Route only (no auth) |
|
||||||
|
|
||||||
|
## Future Options
|
||||||
|
|
||||||
|
### Option 1: Manual Push/Pull (Recommended for v2)
|
||||||
|
|
||||||
|
Add explicit user-triggered sync between local and public copies.
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- "Push to Public" button - sends local changes to public copy
|
||||||
|
- "Pull from Public" button - gets public changes into local
|
||||||
|
- Track `lastSyncedAt` timestamp
|
||||||
|
- Show indicator when copies have diverged
|
||||||
|
- Conflict resolution: Last write wins (simple) or user choice (advanced)
|
||||||
|
|
||||||
|
**Pros:**
|
||||||
|
- User stays in control
|
||||||
|
- Clear mental model
|
||||||
|
- Simple to implement incrementally
|
||||||
|
|
||||||
|
**Cons:**
|
||||||
|
- Manual effort required
|
||||||
|
- Risk of forgetting to sync
|
||||||
|
|
||||||
|
### Option 2: Automatic Background Sync
|
||||||
|
|
||||||
|
Continuous bidirectional sync between local and public copies.
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- Real-time sync like Google Docs
|
||||||
|
- Works across devices
|
||||||
|
- Offline-first with automatic merge
|
||||||
|
|
||||||
|
**Pros:**
|
||||||
|
- Seamless experience
|
||||||
|
- Always up to date
|
||||||
|
|
||||||
|
**Cons:**
|
||||||
|
- Complex conflict resolution (may need CRDTs)
|
||||||
|
- Higher performance overhead
|
||||||
|
- Harder to reason about state
|
||||||
|
|
||||||
|
### Option 3: Fork/Branch Model
|
||||||
|
|
||||||
|
One-way relationship: local is "draft", public is "published".
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- Push only (local → public)
|
||||||
|
- No pull mechanism
|
||||||
|
- Public is the "source of truth" once published
|
||||||
|
|
||||||
|
**Pros:**
|
||||||
|
- Clear mental model
|
||||||
|
- No merge conflicts
|
||||||
|
- Simple implementation
|
||||||
|
|
||||||
|
**Cons:**
|
||||||
|
- Cannot incorporate public changes back to local
|
||||||
|
- Multiple people can't collaborate on draft
|
||||||
|
|
||||||
|
## Recommended Implementation (v2)
|
||||||
|
|
||||||
|
Implement **Option 1 (Manual Push/Pull)** as it provides the best balance of user control and simplicity.
|
||||||
|
|
||||||
|
### Data Model Changes
|
||||||
|
|
||||||
|
Add to diagram directory entry:
|
||||||
|
```typescript
|
||||||
|
interface DiagramEntry {
|
||||||
|
_id: string;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
storageType: 'local' | 'public' | 'private';
|
||||||
|
createdAt: string;
|
||||||
|
|
||||||
|
// New fields for sync tracking
|
||||||
|
publicCopyId?: string; // ID of the public clone (if shared)
|
||||||
|
lastPushedAt?: string; // When changes were last pushed to public
|
||||||
|
lastPulledAt?: string; // When public changes were last pulled
|
||||||
|
publicVersion?: number; // Version number of public copy at last sync
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### API Endpoints
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Push local changes to public
|
||||||
|
POST /api/sync/push
|
||||||
|
Body: { localDbName: string, publicDbName: string }
|
||||||
|
Response: { success: boolean, documentsUpdated: number }
|
||||||
|
|
||||||
|
// Pull public changes to local
|
||||||
|
POST /api/sync/pull
|
||||||
|
Body: { localDbName: string, publicDbName: string }
|
||||||
|
Response: { success: boolean, documentsUpdated: number }
|
||||||
|
|
||||||
|
// Check if copies have diverged
|
||||||
|
GET /api/sync/status?local={localDbName}&public={publicDbName}
|
||||||
|
Response: {
|
||||||
|
diverged: boolean,
|
||||||
|
localChanges: number,
|
||||||
|
publicChanges: number,
|
||||||
|
lastSyncedAt: string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### UI Components
|
||||||
|
|
||||||
|
1. **Sync Status Indicator**
|
||||||
|
- Shows in header when viewing a local diagram that has a public copy
|
||||||
|
- Green check: In sync
|
||||||
|
- Orange dot: Changes pending
|
||||||
|
- Red warning: Conflicts detected
|
||||||
|
|
||||||
|
2. **Push/Pull Buttons**
|
||||||
|
- In hamburger menu under "Share" section
|
||||||
|
- "Push to Public" - shows confirmation with change count
|
||||||
|
- "Pull from Public" - shows confirmation with change count
|
||||||
|
|
||||||
|
3. **Divergence Warning Badge**
|
||||||
|
- Shows on diagram card in Manage Diagrams modal
|
||||||
|
- Indicates when local and public have diverged
|
||||||
|
|
||||||
|
4. **Conflict Resolution Dialog**
|
||||||
|
- Shows when both local and public have changes to same entity
|
||||||
|
- Options: Keep Local, Keep Public, Keep Both (creates duplicate)
|
||||||
|
|
||||||
|
### Implementation Phases
|
||||||
|
|
||||||
|
**Phase 1: Tracking**
|
||||||
|
- Add `publicCopyId` when sharing local → public
|
||||||
|
- Track sharing relationship in directory
|
||||||
|
|
||||||
|
**Phase 2: Push**
|
||||||
|
- Implement push from local to public
|
||||||
|
- Overwrite public with local changes
|
||||||
|
- Update `lastPushedAt` timestamp
|
||||||
|
|
||||||
|
**Phase 3: Pull**
|
||||||
|
- Implement pull from public to local
|
||||||
|
- Merge public changes into local
|
||||||
|
- Update `lastPulledAt` timestamp
|
||||||
|
|
||||||
|
**Phase 4: Status**
|
||||||
|
- Implement divergence detection
|
||||||
|
- Add UI indicators
|
||||||
|
- Show sync status in Manage Diagrams
|
||||||
|
|
||||||
|
**Phase 5: Conflict Resolution**
|
||||||
|
- Detect entity-level conflicts
|
||||||
|
- Show resolution dialog
|
||||||
|
- Allow user to choose resolution strategy
|
||||||
|
|
||||||
|
## Migration Notes
|
||||||
|
|
||||||
|
Existing diagrams without `storageType` are treated as `public` for backwards compatibility. When such diagrams are loaded, the UI should work correctly but sync tracking features won't be available until the diagram metadata is updated.
|
||||||
|
|
||||||
|
## Security Considerations
|
||||||
|
|
||||||
|
- Push/pull operations should validate that the user has access to both databases
|
||||||
|
- Public databases remain world-readable/writable
|
||||||
|
- Private database sync will require authentication tokens
|
||||||
|
- Rate limiting should be applied to sync operations
|
||||||
297
VRCONFIGPLAN.md
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
# VR Configuration Panel Implementation Plan
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Create an immersive WebXR configuration panel that mirrors the 2D ConfigModal functionality using BabylonJS AdvancedDynamicTexture (ADT). The panel will allow users to adjust all application settings directly in VR.
|
||||||
|
|
||||||
|
## Recommended Approach: AdvancedDynamicTexture (ADT)
|
||||||
|
|
||||||
|
**Why ADT?**
|
||||||
|
- Most common approach for WebXR UI in BabylonJS
|
||||||
|
- Existing pattern in codebase (see `src/menus/configMenu.ts`)
|
||||||
|
- Good balance of simplicity and functionality
|
||||||
|
- Native support for text, buttons, sliders, and dropdowns
|
||||||
|
- Easy integration with existing Handle pattern
|
||||||
|
|
||||||
|
**Estimated Effort**: 150-200 lines of code, 4-8 hours implementation time
|
||||||
|
|
||||||
|
## File Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
src/menus/
|
||||||
|
├── vrConfigPanel.ts (NEW - main implementation)
|
||||||
|
└── configMenu.ts (REFERENCE - existing VR config example)
|
||||||
|
|
||||||
|
src/diagram/
|
||||||
|
└── diagramMenuManager.ts (MODIFY - add toolbox button)
|
||||||
|
|
||||||
|
src/util/
|
||||||
|
└── appConfig.ts (USE - singleton for config management)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Implementation Phases
|
||||||
|
|
||||||
|
### Phase 1: Core Panel Setup
|
||||||
|
- [ ] Create `src/menus/vrConfigPanel.ts` file
|
||||||
|
- [ ] Implement class structure following Handle pattern:
|
||||||
|
```typescript
|
||||||
|
export class VRConfigPanel {
|
||||||
|
private _scene: Scene;
|
||||||
|
private _handleMesh: Mesh;
|
||||||
|
private _advancedTexture: AdvancedDynamicTexture;
|
||||||
|
private _configObserver: Observer<AppConfigType>;
|
||||||
|
|
||||||
|
constructor(scene: Scene) {
|
||||||
|
// Initialize panel
|
||||||
|
}
|
||||||
|
|
||||||
|
public get handleMesh(): Mesh {
|
||||||
|
return this._handleMesh;
|
||||||
|
}
|
||||||
|
|
||||||
|
public show(): void {
|
||||||
|
this._handleMesh.setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public hide(): void {
|
||||||
|
this._handleMesh.setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public dispose(): void {
|
||||||
|
// Cleanup
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- [ ] Create base mesh (plane) for panel backing
|
||||||
|
- [ ] Set up AdvancedDynamicTexture with appropriate resolution (1024x1024 or 2048x2048)
|
||||||
|
- [ ] Position panel at comfortable viewing distance (0.5-0.7m from camera)
|
||||||
|
- [ ] Make panel grabbable via Handle pattern
|
||||||
|
|
||||||
|
**Reference Files**:
|
||||||
|
- `src/menus/inputTextView.ts` - Handle pattern implementation
|
||||||
|
- `src/menus/configMenu.ts` - ADT usage example
|
||||||
|
|
||||||
|
### Phase 2: UI Layout Structure
|
||||||
|
- [ ] Create main container (StackPanel for vertical layout)
|
||||||
|
- [ ] Add title text at top ("Configuration")
|
||||||
|
- [ ] Create 5 section containers (one for each config group):
|
||||||
|
1. Location Snap
|
||||||
|
2. Rotation Snap
|
||||||
|
3. Fly Mode
|
||||||
|
4. Snap Turn
|
||||||
|
5. Label Rendering Mode
|
||||||
|
- [ ] Style containers with padding and spacing
|
||||||
|
- [ ] Add visual separators between sections
|
||||||
|
|
||||||
|
**ADT Components to Use**:
|
||||||
|
- `StackPanel` - Main vertical container
|
||||||
|
- `TextBlock` - Labels and section titles
|
||||||
|
- `Rectangle` - Containers and separators
|
||||||
|
|
||||||
|
**Reference**: `src/menus/configMenu.ts:44-89` for existing layout patterns
|
||||||
|
|
||||||
|
### Phase 3: Location Snap Section
|
||||||
|
- [ ] Add "Location Snap" label
|
||||||
|
- [ ] Create enable/disable toggle button
|
||||||
|
- Shows "Enabled" or "Disabled"
|
||||||
|
- Updates `appConfigInstance` on click
|
||||||
|
- [ ] Add RadioGroup for snap values:
|
||||||
|
- Options: 1cm (.01), 5cm (.05), 10cm (.1), 50cm (.5), 1m (1)
|
||||||
|
- Default: 10cm (.1)
|
||||||
|
- Disable when snap is off
|
||||||
|
- [ ] Wire up to `appConfigInstance.setGridSnap(value)`
|
||||||
|
- [ ] Subscribe to config changes to update UI
|
||||||
|
|
||||||
|
**ADT Components**:
|
||||||
|
- `Button` - Toggle switch
|
||||||
|
- `RadioButton` + `TextBlock` - Value selection
|
||||||
|
- Color coding: enabled (green/myColor), disabled (gray)
|
||||||
|
|
||||||
|
**Reference ConfigModal**: `src/react/pages/configModal.tsx:83-94`
|
||||||
|
|
||||||
|
### Phase 4: Rotation Snap Section
|
||||||
|
- [ ] Add "Rotation Snap" label
|
||||||
|
- [ ] Create enable/disable toggle button
|
||||||
|
- [ ] Add RadioGroup for rotation values:
|
||||||
|
- Options: 22.5°, 45°, 90°, 180°, 360°
|
||||||
|
- Default: 90°
|
||||||
|
- Disable when snap is off
|
||||||
|
- [ ] Wire up to `appConfigInstance.setRotateSnap(value)`
|
||||||
|
- [ ] Subscribe to config changes to update UI
|
||||||
|
|
||||||
|
**Reference ConfigModal**: `src/react/pages/configModal.tsx:96-108`
|
||||||
|
|
||||||
|
### Phase 5: Fly Mode Section
|
||||||
|
- [ ] Add "Fly Mode" label
|
||||||
|
- [ ] Create toggle button
|
||||||
|
- Shows "Fly Mode Enabled" or "Fly Mode Disabled"
|
||||||
|
- [ ] Wire up to `appConfigInstance.setFlyMode(value)`
|
||||||
|
- [ ] Subscribe to config changes to update UI
|
||||||
|
|
||||||
|
**Reference ConfigModal**: `src/react/pages/configModal.tsx:109-112`
|
||||||
|
|
||||||
|
### Phase 6: Snap Turn Section
|
||||||
|
- [ ] Add "Snap Turn" label
|
||||||
|
- [ ] Create enable/disable toggle button
|
||||||
|
- [ ] Add RadioGroup for snap turn angles:
|
||||||
|
- Options: 22.5°, 45°, 90°, 180°, 360°
|
||||||
|
- Default: 45°
|
||||||
|
- Disable when snap is off
|
||||||
|
- [ ] Wire up to `appConfigInstance.setTurnSnap(value)`
|
||||||
|
- [ ] Subscribe to config changes to update UI
|
||||||
|
|
||||||
|
**Reference ConfigModal**: `src/react/pages/configModal.tsx:113-125`
|
||||||
|
|
||||||
|
### Phase 7: Label Rendering Mode Section
|
||||||
|
- [ ] Add "Label Rendering Mode" label
|
||||||
|
- [ ] Create RadioGroup for rendering modes:
|
||||||
|
- Fixed
|
||||||
|
- Billboard (Always Face Camera)
|
||||||
|
- Dynamic (Coming Soon) - disabled
|
||||||
|
- Distance-based (Coming Soon) - disabled
|
||||||
|
- [ ] Wire up to `appConfigInstance.setLabelRenderingMode(value)`
|
||||||
|
- [ ] Subscribe to config changes to update UI
|
||||||
|
- [ ] Style disabled options with gray text
|
||||||
|
|
||||||
|
**Reference ConfigModal**: `src/react/pages/configModal.tsx:126-135`
|
||||||
|
|
||||||
|
### Phase 8: Integration with Toolbox
|
||||||
|
- [ ] Modify `src/diagram/diagramMenuManager.ts` to instantiate VRConfigPanel
|
||||||
|
- [ ] Add "Config" button to toolbox (similar to "Exit VR" button pattern)
|
||||||
|
- [ ] Wire up button click to show/hide panel
|
||||||
|
- [ ] Position panel relative to camera when shown (see `positionComponentsRelativeToCamera`)
|
||||||
|
- [ ] Add parent relationship to platform for movement tracking
|
||||||
|
|
||||||
|
**Reference**:
|
||||||
|
- `src/diagram/diagramMenuManager.ts:85-97` - Exit button creation
|
||||||
|
- `src/util/functions/groundMeshObserver.ts:127-222` - Component positioning
|
||||||
|
|
||||||
|
### Phase 9: Observable Integration
|
||||||
|
- [ ] Subscribe to `appConfigInstance.onConfigChangedObservable` in constructor
|
||||||
|
- [ ] Update all UI elements when config changes externally
|
||||||
|
- [ ] Ensure Observable cleanup in dispose() method
|
||||||
|
- [ ] Test config changes from both VR panel and 2D ConfigModal
|
||||||
|
|
||||||
|
**Pattern**:
|
||||||
|
```typescript
|
||||||
|
this._configObserver = appConfigInstance.onConfigChangedObservable.add((config) => {
|
||||||
|
// Update UI elements to reflect new config
|
||||||
|
this.updateLocationSnapUI(config.locationSnap);
|
||||||
|
this.updateRotationSnapUI(config.rotateSnap);
|
||||||
|
// ... etc
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 10: Testing & Polish
|
||||||
|
- [ ] Test all toggle switches update config correctly
|
||||||
|
- [ ] Test all radio button selections update config correctly
|
||||||
|
- [ ] Verify config changes propagate to DiagramObjects (label mode, snap behavior)
|
||||||
|
- [ ] Test panel positioning in VR (comfortable viewing distance)
|
||||||
|
- [ ] Test panel grabbability via Handle
|
||||||
|
- [ ] Verify panel follows platform movement
|
||||||
|
- [ ] Test config persistence (localStorage)
|
||||||
|
- [ ] Test config synchronization between VR panel and 2D ConfigModal
|
||||||
|
- [ ] Add visual feedback for button clicks (color changes, animations)
|
||||||
|
- [ ] Ensure proper cleanup on panel disposal
|
||||||
|
- [ ] Test in both WebXR and desktop modes
|
||||||
|
|
||||||
|
## Code Patterns to Follow
|
||||||
|
|
||||||
|
### 1. Toggle Button Pattern
|
||||||
|
```typescript
|
||||||
|
const toggleButton = Button.CreateSimpleButton("toggle", "Enabled");
|
||||||
|
toggleButton.width = "200px";
|
||||||
|
toggleButton.height = "40px";
|
||||||
|
toggleButton.color = "white";
|
||||||
|
toggleButton.background = "green";
|
||||||
|
toggleButton.onPointerClickObservable.add(() => {
|
||||||
|
const newValue = !currentValue;
|
||||||
|
toggleButton.textBlock.text = newValue ? "Enabled" : "Disabled";
|
||||||
|
toggleButton.background = newValue ? "green" : "gray";
|
||||||
|
appConfigInstance.setSomeSetting(newValue);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. RadioGroup Pattern
|
||||||
|
```typescript
|
||||||
|
const radioGroup = new SelectionPanel("snapValues");
|
||||||
|
const options = [
|
||||||
|
{ value: 0.01, label: "1cm" },
|
||||||
|
{ value: 0.1, label: "10cm" },
|
||||||
|
// ... more options
|
||||||
|
];
|
||||||
|
|
||||||
|
options.forEach(option => {
|
||||||
|
const radio = new RadioButton();
|
||||||
|
radio.width = "20px";
|
||||||
|
radio.height = "20px";
|
||||||
|
radio.isChecked = (option.value === currentValue);
|
||||||
|
radio.onIsCheckedChangedObservable.add((checked) => {
|
||||||
|
if (checked) {
|
||||||
|
appConfigInstance.setGridSnap(option.value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// Add label next to radio button
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Config Observer Pattern
|
||||||
|
```typescript
|
||||||
|
this._configObserver = appConfigInstance.onConfigChangedObservable.add((config) => {
|
||||||
|
this.updateUIFromConfig(config);
|
||||||
|
});
|
||||||
|
|
||||||
|
// In dispose():
|
||||||
|
if (this._configObserver) {
|
||||||
|
appConfigInstance.onConfigChangedObservable.remove(this._configObserver);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Key Integration Points
|
||||||
|
|
||||||
|
### AppConfig Singleton
|
||||||
|
- Import: `import {appConfigInstance} from "../util/appConfig";`
|
||||||
|
- Read: `appConfigInstance.current.locationSnap`
|
||||||
|
- Write: `appConfigInstance.setGridSnap(0.1)`
|
||||||
|
- Subscribe: `appConfigInstance.onConfigChangedObservable.add(callback)`
|
||||||
|
|
||||||
|
### DiagramMenuManager
|
||||||
|
- Instantiate panel: `this._vrConfigPanel = new VRConfigPanel(this._scene);`
|
||||||
|
- Add button to toolbox: Follow exit button pattern in `setupExitButton()`
|
||||||
|
- Show panel: `this._vrConfigPanel.show();`
|
||||||
|
- Position panel: Follow pattern in `groundMeshObserver.ts:127-222`
|
||||||
|
|
||||||
|
### Handle Pattern
|
||||||
|
- Make panel grabbable by controllers
|
||||||
|
- Parent to platform for world movement
|
||||||
|
- Use `_handleMesh` as root for entire panel UI
|
||||||
|
|
||||||
|
## Reference Files
|
||||||
|
|
||||||
|
1. **src/menus/configMenu.ts** - Existing VR config implementation with ADT
|
||||||
|
2. **src/menus/inputTextView.ts** - Handle pattern and ADT setup
|
||||||
|
3. **src/react/pages/configModal.tsx** - UI structure and config sections
|
||||||
|
4. **src/util/appConfig.ts** - Config singleton and setter methods
|
||||||
|
5. **src/diagram/diagramMenuManager.ts** - Toolbox button creation
|
||||||
|
6. **src/util/functions/groundMeshObserver.ts** - Component positioning
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
|
||||||
|
- [ ] All 5 config sections implemented and functional
|
||||||
|
- [ ] Config changes in VR panel update appConfigInstance
|
||||||
|
- [ ] Config changes propagate to all DiagramObjects
|
||||||
|
- [ ] Panel is grabbable and repositionable
|
||||||
|
- [ ] Panel follows platform movement
|
||||||
|
- [ ] Config persists to localStorage
|
||||||
|
- [ ] Synchronized with 2D ConfigModal
|
||||||
|
- [ ] Comfortable viewing experience in VR
|
||||||
|
- [ ] No memory leaks (proper Observable cleanup)
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Start hidden (only show when user clicks toolbox button)
|
||||||
|
- Position at ~0.5m in front of camera when opened
|
||||||
|
- Use Y-axis billboard mode to keep panel upright but allow rotation
|
||||||
|
- Consider adding "Close" button at bottom of panel
|
||||||
|
- Match color scheme with existing UI (myColor theme)
|
||||||
|
- Test with both left and right controller grabbing
|
||||||
138
docs/NAMING_CONVENTIONS.md
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
# Naming Conventions
|
||||||
|
|
||||||
|
## Tool and Material Naming
|
||||||
|
|
||||||
|
This document describes the naming conventions used for tools, materials, and related entities in the immersive WebXR application.
|
||||||
|
|
||||||
|
## Material Naming
|
||||||
|
|
||||||
|
Materials follow a consistent naming pattern based on their color:
|
||||||
|
|
||||||
|
**Format:** `material-{color}`
|
||||||
|
|
||||||
|
**Where:**
|
||||||
|
- `{color}` is the hex string representation of the material's color (e.g., `#ff0000` for red)
|
||||||
|
|
||||||
|
**Examples:**
|
||||||
|
- `material-#ff0000` - Red material
|
||||||
|
- `material-#00ff00` - Green material
|
||||||
|
- `material-#222222` - Dark gray material
|
||||||
|
|
||||||
|
**Implementation:**
|
||||||
|
```typescript
|
||||||
|
const material = new StandardMaterial("material-" + color.toHexString(), scene);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Location:** Materials are created in:
|
||||||
|
- `src/toolbox/functions/buildColor.ts` - For toolbox color swatches
|
||||||
|
- `src/diagram/functions/buildMeshFromDiagramEntity.ts` - Fallback material creation via `buildMissingMaterial()`
|
||||||
|
|
||||||
|
## Tool Mesh Naming
|
||||||
|
|
||||||
|
Tool meshes use a compound naming pattern that includes both the tool type and color:
|
||||||
|
|
||||||
|
**Format:** `tool-{toolId}`
|
||||||
|
|
||||||
|
**Where:**
|
||||||
|
- `{toolId}` = `{toolType}-{color}`
|
||||||
|
- `{toolType}` is a value from the `ToolType` enum (e.g., `BOX`, `SPHERE`, `CYLINDER`, `CONE`, `PLANE`, `PERSON`)
|
||||||
|
- `{color}` is the hex string representation of the tool's color
|
||||||
|
|
||||||
|
**Examples:**
|
||||||
|
- `tool-BOX-#ff0000` - Red box tool
|
||||||
|
- `tool-SPHERE-#00ff00` - Green sphere tool
|
||||||
|
- `tool-CYLINDER-#0000ff` - Blue cylinder tool
|
||||||
|
- `tool-PLANE-#ffff00` - Yellow plane tool
|
||||||
|
|
||||||
|
**Implementation:**
|
||||||
|
```typescript
|
||||||
|
function toolId(tool: ToolType, color: Color3) {
|
||||||
|
return tool + "-" + color.toHexString();
|
||||||
|
}
|
||||||
|
|
||||||
|
const newItem = await buildMesh(tool, `tool-${id}`, colorParent.getScene());
|
||||||
|
// For example: `tool-BOX-#ff0000`
|
||||||
|
```
|
||||||
|
|
||||||
|
**Location:** Tool meshes are created in `src/toolbox/functions/buildTool.ts`
|
||||||
|
|
||||||
|
## Tool Colors
|
||||||
|
|
||||||
|
The application uses 16 predefined colors for the toolbox:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const colors: string[] = [
|
||||||
|
"#222222", "#8b4513", "#006400", "#778899", // Row 1: Dark gray, Brown, Dark green, Light slate gray
|
||||||
|
"#4b0082", "#ff0000", "#ffa500", "#ffff00", // Row 2: Indigo, Red, Orange, Yellow
|
||||||
|
"#00ff00", "#00ffff", "#0000ff", "#ff00ff", // Row 3: Green, Cyan, Blue, Magenta
|
||||||
|
"#1e90ff", "#98fb98", "#ffe4b5", "#ff69b4" // Row 4: Dodger blue, Pale green, Moccasin, Hot pink
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tool Types
|
||||||
|
|
||||||
|
Available tool types from the `ToolType` enum:
|
||||||
|
- `BOX` - Cube mesh
|
||||||
|
- `SPHERE` - Sphere mesh
|
||||||
|
- `CYLINDER` - Cylinder mesh
|
||||||
|
- `CONE` - Cone mesh
|
||||||
|
- `PLANE` - Flat plane mesh
|
||||||
|
- `PERSON` - Person/avatar mesh
|
||||||
|
|
||||||
|
## Material Color Access
|
||||||
|
|
||||||
|
When accessing material colors, use this priority order to handle both current and legacy materials:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// For StandardMaterial
|
||||||
|
const stdMat = material as StandardMaterial;
|
||||||
|
const materialColor = stdMat.emissiveColor || stdMat.diffuseColor;
|
||||||
|
|
||||||
|
// Current rendering uses emissiveColor
|
||||||
|
// Legacy materials may have diffuseColor instead
|
||||||
|
```
|
||||||
|
|
||||||
|
## Rendering Modes
|
||||||
|
|
||||||
|
Materials can be rendered in three different modes, affecting how color properties are used:
|
||||||
|
|
||||||
|
### 1. Lightmap with Lighting
|
||||||
|
- Uses `diffuseColor` + `lightmapTexture`
|
||||||
|
- `disableLighting = false`
|
||||||
|
- Most expensive performance-wise
|
||||||
|
- Provides lighting illusion with actual lighting calculations
|
||||||
|
|
||||||
|
### 2. Unlit with Emissive Texture (Default)
|
||||||
|
- Uses `emissiveColor` + `emissiveTexture` (lightmap)
|
||||||
|
- `disableLighting = true`
|
||||||
|
- Best balance of visual quality and performance
|
||||||
|
- Provides lighting illusion without lighting calculations
|
||||||
|
|
||||||
|
### 3. Flat Emissive
|
||||||
|
- Uses only `emissiveColor`
|
||||||
|
- `disableLighting = true`
|
||||||
|
- Best performance
|
||||||
|
- No lighting illusion, flat shading
|
||||||
|
|
||||||
|
## Instance Naming
|
||||||
|
|
||||||
|
Instanced meshes (created from tool templates) follow this pattern:
|
||||||
|
|
||||||
|
**Format:** `tool-instance-{toolId}`
|
||||||
|
|
||||||
|
**Example:**
|
||||||
|
```typescript
|
||||||
|
const instance = new InstancedMesh("tool-instance-" + id, newItem);
|
||||||
|
// For example: `tool-instance-BOX-#ff0000`
|
||||||
|
```
|
||||||
|
|
||||||
|
These instances share materials with their source mesh and are used for visual feedback before creating actual diagram entities.
|
||||||
|
|
||||||
|
## Related Files
|
||||||
|
|
||||||
|
- `src/toolbox/functions/buildTool.ts` - Tool mesh creation and naming
|
||||||
|
- `src/toolbox/functions/buildColor.ts` - Material creation and color management
|
||||||
|
- `src/diagram/functions/buildMeshFromDiagramEntity.ts` - Diagram entity instantiation
|
||||||
|
- `src/toolbox/types/toolType.ts` - ToolType enum definition
|
||||||
|
- `src/util/lightmapGenerator.ts` - Lightmap texture generation and caching
|
||||||
|
- `src/util/renderingMode.ts` - Rendering mode enum and labels
|
||||||
121
index.html
@ -2,95 +2,58 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Deep Diagram</title>
|
<meta content="width=device-width, initial-scale=1" name="viewport"/>
|
||||||
|
<meta content="An immersive vr diagramming experience based using webxr version 0.0.8-14 (2024-12-29Z)"
|
||||||
|
name="description">
|
||||||
|
<meta content="width=device-width, initial-scale=1, height=device-height" name="viewport">
|
||||||
|
<!--<link href="/styles.css" rel="stylesheet"> -->
|
||||||
|
<link href="/assets/dasfad/favicon-32x32.png" rel="icon" sizes="32x32" type="image/png">
|
||||||
|
<link href="/assets/dasfad/favicon-16x16.png" rel="icon" sizes="16x16" type="image/png">
|
||||||
|
<link href="/assets/dasfad/favicon-96x96.png" rel="icon" sizes="96x96" type="image/png">
|
||||||
|
<link as="fetch" href="/node_modules/.vite/deps/HavokPhysics.wasm" rel="preload">
|
||||||
|
<title>DASFAD</title>
|
||||||
|
<!-- <link as="script" href="/newRelic.js" rel="preload">
|
||||||
|
<script defer src="/newRelic.js"></script> -->
|
||||||
|
|
||||||
|
|
||||||
|
<link href="/manifest.webmanifest" rel="manifest"/>
|
||||||
|
<!--<script src='/niceware.js'></script>-->
|
||||||
<style>
|
<style>
|
||||||
body {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.loader {
|
|
||||||
position: fixed;
|
|
||||||
left: 0px;
|
|
||||||
top: 0px;
|
|
||||||
width: 200px;
|
|
||||||
height: 200px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-content: center;
|
|
||||||
flex-direction: column;
|
|
||||||
z-index: -1;
|
|
||||||
/*noinspection CssUnknownTarget*/
|
|
||||||
background: url("/spinner.gif");
|
|
||||||
background-position: center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#questLaunch {
|
|
||||||
position: fixed;
|
|
||||||
top: 30px;
|
|
||||||
right: 30px;
|
|
||||||
padding: 10px;
|
|
||||||
background: #000;
|
|
||||||
color: #fff;
|
|
||||||
font-size: 1.5em;
|
|
||||||
z-index: 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
#download {
|
|
||||||
position: fixed;
|
|
||||||
z-index: 11;
|
|
||||||
width: 200px;
|
|
||||||
height: 20px;
|
|
||||||
position: absolute;
|
|
||||||
bottom: 50px;
|
|
||||||
left: 16px;
|
|
||||||
padding: 10px;
|
|
||||||
background: #000;
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<link as="script" href="/newRelic.js" rel="preload">
|
|
||||||
<script src="/newRelic.js"></script>
|
|
||||||
<meta content="width=device-width, initial-scale=1, height=device-height" name="viewport">
|
|
||||||
<meta content="An immersive vr diagramming experience based on a-frame and webxr" name="description">
|
|
||||||
<link href="/assets/favicon-16x16.png" rel=icon sizes="16x16" type="image/png">
|
|
||||||
<link href="/assets/favicon-32x32.png" rel=icon sizes="32x32" type="image/png">
|
|
||||||
<link href="/assets/favicon-96x96.png" rel=icon sizes="96x96" type="image/png">
|
|
||||||
<link href="/manifest.webmanifest" rel="manifest"/>
|
|
||||||
<script src='/niceware.js'></script>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!--<div class="loader" id="loader">Loading...</div> -->
|
|
||||||
<div id="questLaunch"><a href="https://www.oculus.com/open_url/?url=https://www.deepdiagram.com/" target="_blank">Launch
|
|
||||||
On Quest</a>
|
|
||||||
</div>
|
|
||||||
<div id="download"><a href="#" id="downloadLink">Download Model</a></div>
|
|
||||||
<script>
|
<script>
|
||||||
/*
|
|
||||||
var SpeechRecognition = SpeechRecognition || webkitSpeechRecognition
|
/* if (typeof navigator.serviceWorker !== 'undefined') {
|
||||||
var SpeechGrammarList = SpeechGrammarList || window.webkitSpeechGrammarList
|
|
||||||
var SpeechRecognitionEvent = SpeechRecognitionEvent || webkitSpeechRecognitionEvent
|
|
||||||
var recognition = new SpeechRecognition();
|
if (localStorage.getItem('serviceWorkerVersion') !== '11') {
|
||||||
recognition.continuous = false;
|
caches.keys().then(cacheNames => {
|
||||||
recognition.lang = 'en-US';
|
cacheNames.forEach(cacheName => {
|
||||||
recognition.interimResults = true;
|
caches.delete(cacheName);
|
||||||
recognition.maxAlternatives = 1;
|
});
|
||||||
recognition.onresult = function(event) {
|
});
|
||||||
console.log(event.results[0][0].transcript);
|
localStorage.setItem('serviceWorkerVersion', '11');
|
||||||
}
|
}
|
||||||
recognition.onend = function() {
|
navigator.serviceWorker.register('/sw.js', {updateViaCache: 'none'});
|
||||||
console.log("recognition ended");
|
|
||||||
recognition.start();
|
|
||||||
}
|
}
|
||||||
console.log("starting recognition");
|
|
||||||
recognition.start();
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<script type="module" src="./src/app.ts"></script>
|
<div class="webApp" id="webApp">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<script defer src="/src/webApp.ts" type="module"></script>
|
||||||
|
|
||||||
|
<!--<video id="feed" controls="" autoplay="" name="media"><source src="https://listen.broadcastify.com/1drb2xhywkg8nvz.mp3?nc=49099&xan=xtf9912b41c" type="audio/mpeg"></video> -->
|
||||||
|
<!--
|
||||||
|
<div class="scene">
|
||||||
|
<canvas id="gameCanvas"></canvas>
|
||||||
|
</div>
|
||||||
|
-->
|
||||||
|
<!--<script defer src="/src/vrApp.ts" type="module"></script>-->
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@ -1,49 +0,0 @@
|
|||||||
import {Handler, HandlerContext, HandlerEvent} from "@netlify/functions";
|
|
||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
export const handler: Handler = async (event: HandlerEvent, context: HandlerContext) => {
|
|
||||||
try {
|
|
||||||
switch (event.httpMethod) {
|
|
||||||
case 'POST':
|
|
||||||
const apiKey = event.headers['api-key'];
|
|
||||||
const query = event.body;
|
|
||||||
const response = await axios.post('https://api.newrelic.com/graphql',
|
|
||||||
query,
|
|
||||||
{headers: {'Api-Key': apiKey, 'Content-Type': 'application/json'}});
|
|
||||||
const data = await response.data;
|
|
||||||
return {
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'Access-Control-Allow-Origin': 'https://cameras.immersiveidea.com',
|
|
||||||
'Access-Control-Allow-Credentials': 'true'
|
|
||||||
},
|
|
||||||
statusCode: 200,
|
|
||||||
body: JSON.stringify(data)
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'OPTIONS':
|
|
||||||
const headers = {
|
|
||||||
'Access-Control-Allow-Origin': 'https://cameras.immersiveidea.com',
|
|
||||||
'Access-Control-Allow-Credentials': 'true',
|
|
||||||
'Access-Control-Allow-Headers': 'content-type, api-key',
|
|
||||||
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE'
|
|
||||||
};
|
|
||||||
return {
|
|
||||||
statusCode: 204,
|
|
||||||
headers
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return {
|
|
||||||
statusCode: 405,
|
|
||||||
body: 'Method Not Allowed'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
return {
|
|
||||||
statusCode: 500,
|
|
||||||
body: JSON.stringify(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "esnext",
|
|
||||||
"module": "esnext",
|
|
||||||
"moduleResolution": "Node",
|
|
||||||
"lib": [
|
|
||||||
"esnext"
|
|
||||||
],
|
|
||||||
"types": [
|
|
||||||
"@cloudflare/workers-types"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
import {Handler, HandlerContext, HandlerEvent} from "@netlify/functions";
|
|
||||||
import axios from 'axios';
|
|
||||||
|
|
||||||
export const handler: Handler = async (event: HandlerEvent, context: HandlerContext) => {
|
|
||||||
try {
|
|
||||||
const response = await axios.post('https://api.assemblyai.com/v2/realtime/token', // use account token to get a temp user token
|
|
||||||
{expires_in: 3600}, // can set a TTL timer in seconds.
|
|
||||||
{headers: {authorization: process.env.VOICE_TOKEN}});
|
|
||||||
|
|
||||||
const data = await response.data;
|
|
||||||
return {
|
|
||||||
headers: {'Content-Type': 'application/json'},
|
|
||||||
statusCode: 200,
|
|
||||||
body: JSON.stringify(data)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
return {
|
|
||||||
statusCode: 500,
|
|
||||||
body: JSON.stringify(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
48
newrelic.cjs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
'use strict'
|
||||||
|
// Load .env.local first (has the secrets), then .env as fallback
|
||||||
|
require('dotenv').config({ path: '.env.local' });
|
||||||
|
require('dotenv').config();
|
||||||
|
/**
|
||||||
|
* New Relic Node.js APM Configuration
|
||||||
|
*
|
||||||
|
* This file configures the New Relic agent for backend monitoring.
|
||||||
|
* Requires NEW_RELIC_LICENSE_KEY environment variable to be set.
|
||||||
|
*
|
||||||
|
* Distributed tracing is enabled to correlate with browser agent traces.
|
||||||
|
*/
|
||||||
|
|
||||||
|
exports.config = {
|
||||||
|
app_name: ['dasfad-backend'],
|
||||||
|
license_key: process.env.NEW_RELIC_LICENSE_KEY,
|
||||||
|
distributed_tracing: {
|
||||||
|
enabled: true
|
||||||
|
},
|
||||||
|
logging: {
|
||||||
|
level: 'info'
|
||||||
|
},
|
||||||
|
application_logging: {
|
||||||
|
enabled: true,
|
||||||
|
forwarding: {
|
||||||
|
enabled: true,
|
||||||
|
max_samples_stored: 10000
|
||||||
|
},
|
||||||
|
local_decorating: {
|
||||||
|
enabled: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
allow_all_headers: true,
|
||||||
|
attributes: {
|
||||||
|
exclude: [
|
||||||
|
'request.headers.cookie',
|
||||||
|
'request.headers.authorization',
|
||||||
|
'request.headers.proxyAuthorization',
|
||||||
|
'request.headers.setCookie*',
|
||||||
|
'request.headers.x*',
|
||||||
|
'response.headers.cookie',
|
||||||
|
'response.headers.authorization',
|
||||||
|
'response.headers.proxyAuthorization',
|
||||||
|
'response.headers.setCookie*',
|
||||||
|
'response.headers.x*'
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
9801
package-lock.json
generated
88
package.json
@ -1,48 +1,82 @@
|
|||||||
{
|
{
|
||||||
"name": "immersive",
|
"name": "immersive",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.1",
|
"version": "0.0.8-48",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18.0.0"
|
"node": ">=18.0.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "node -r newrelic server.js",
|
||||||
"build": "vite build",
|
"test": "vitest",
|
||||||
"preview": "vite preview",
|
"build": "node versionBump.js && vite build",
|
||||||
"serve": "node server.js",
|
"start": "NODE_ENV=production node -r newrelic server.js",
|
||||||
|
"start:api": "API_ONLY=true node -r newrelic server.js",
|
||||||
|
"socket": "node server/server.js",
|
||||||
"serverBuild": "cd server && tsc",
|
"serverBuild": "cd server && tsc",
|
||||||
"havok": "cp ./node_modules/@babylonjs/havok/lib/esm/HavokPhysics.wasm ./node_modules/.vite/deps"
|
"havok": "cp ./node_modules/@babylonjs/havok/lib/esm/HavokPhysics.wasm ./node_modules/.vite/deps"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babylonjs/core": "^6.21.2",
|
"@auth0/auth0-react": "^2.2.4",
|
||||||
"@babylonjs/gui": "^6.21.2",
|
"@babylonjs/core": "^8.16.2",
|
||||||
"@babylonjs/havok": "1.1.4",
|
"@babylonjs/gui": "^8.16.2",
|
||||||
"@babylonjs/inspector": "^6.21.2",
|
"@babylonjs/havok": "1.3.4",
|
||||||
"@babylonjs/loaders": "^6.21.2",
|
"@babylonjs/inspector": "^8.16.2",
|
||||||
"@babylonjs/serializers": "^6.21.2",
|
"@babylonjs/loaders": "^8.16.2",
|
||||||
"@cloudflare/workers-types": "^4.20230821.0",
|
"@babylonjs/materials": "^8.16.2",
|
||||||
"@netlify/functions": "^1.6.0",
|
"@babylonjs/serializers": "^8.16.2",
|
||||||
"@typed-mxgraph/typed-mxgraph": "^1.0.8",
|
"@emotion/react": "^11.13.0",
|
||||||
|
"@giphy/js-fetch-api": "^5.6.0",
|
||||||
|
"@giphy/react-components": "^9.6.0",
|
||||||
|
"@mantine/core": "^7.17.8",
|
||||||
|
"@mantine/form": "^7.17.8",
|
||||||
|
"@mantine/hooks": "^7.17.8",
|
||||||
|
"@maptiler/client": "1.8.1",
|
||||||
|
"@newrelic/browser-agent": "^1.306.0",
|
||||||
|
"@picovoice/cobra-web": "^2.0.3",
|
||||||
|
"@picovoice/eagle-web": "^1.0.0",
|
||||||
|
"@picovoice/web-voice-processor": "^4.0.9",
|
||||||
|
"@tabler/icons-react": "^3.14.0",
|
||||||
"@types/node": "^18.14.0",
|
"@types/node": "^18.14.0",
|
||||||
"dexie": "^3.2.4",
|
"@types/react": "^18.2.72",
|
||||||
"dexie-observable": "^4.0.1-beta.13",
|
"@types/react-dom": "^18.2.22",
|
||||||
"earcut": "^2.2.4",
|
"axios": "^1.10.0",
|
||||||
|
"canvas-hypertxt": "1.0.3",
|
||||||
|
"cors": "^2.8.5",
|
||||||
|
"dotenv": "^17.2.3",
|
||||||
|
"events": "^3.3.0",
|
||||||
|
"express": "^5.2.1",
|
||||||
|
"express-pouchdb": "^4.2.0",
|
||||||
|
"hash-wasm": "4.11.0",
|
||||||
"hls.js": "^1.1.4",
|
"hls.js": "^1.1.4",
|
||||||
"loglevel": "^1.8.1",
|
"js-crypto-aes": "1.0.6",
|
||||||
"mxgraph": "^4.2.2",
|
"leveldown": "^6.1.1",
|
||||||
"niceware": "^4.0.0",
|
"loglevel": "^1.9.1",
|
||||||
"p2p-data-channel": "^1.10.7",
|
"meaningful-string": "^1.4.0",
|
||||||
|
"newrelic": "^13.9.1",
|
||||||
|
"peer-lite": "2.0.2",
|
||||||
|
"pouchdb": "^8.0.1",
|
||||||
|
"pouchdb-adapter-leveldb": "^9.0.0",
|
||||||
|
"pouchdb-adapter-memory": "^9.0.0",
|
||||||
|
"pouchdb-find": "^8.0.1",
|
||||||
"query-string": "^8.1.0",
|
"query-string": "^8.1.0",
|
||||||
"recordrtc": "^5.6.2",
|
"react-router-dom": "^6.26.1",
|
||||||
"ring-client-api": "11.7.7",
|
"recordrtc": "^5.6.0",
|
||||||
|
"rfc4648": "^1.5.3",
|
||||||
"round": "^2.0.1",
|
"round": "^2.0.1",
|
||||||
"uuid": "^9.0.0"
|
"uint8-to-b64": "^1.0.2",
|
||||||
|
"use-pouchdb": "^2.0.2",
|
||||||
|
"uuid": "^9.0.1",
|
||||||
|
"vite-express": "^0.21.1",
|
||||||
|
"websocket": "^1.0.34",
|
||||||
|
"websocket-ts": "^2.1.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@types/dom-to-image": "^2.6.7",
|
||||||
"typescript": "^4.9.5",
|
"typescript": "^4.9.5",
|
||||||
"vite": "^4.4.9",
|
"vite": "^5.2.9",
|
||||||
"vite-plugin-api": "^0.1.11",
|
"vite-plugin-cp": "^1.0.0",
|
||||||
"vite-plugin-cp": "^1.0.0"
|
"vitest": "^1.4.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
1
public/_redirects
Normal file
@ -0,0 +1 @@
|
|||||||
|
/db/* /index.html 200
|
||||||
BIN
public/android-icon-192x192.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
public/assets/Android.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
public/assets/dasfad-logo.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
40
public/assets/dasfad-logo.svg
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="3200"
|
||||||
|
height="1355.480324331485" viewBox="0 0 3200 1355.480324331485">
|
||||||
|
|
||||||
|
<g transform="scale(10) translate(10, 10)">
|
||||||
|
<defs id="SvgjsDefs1385">
|
||||||
|
<linearGradient id="SvgjsLinearGradient1390">
|
||||||
|
<stop id="SvgjsStop1391" stop-color="#905e26" offset="0"></stop>
|
||||||
|
<stop id="SvgjsStop1392" stop-color="#f5ec9b" offset="0.5"></stop>
|
||||||
|
<stop id="SvgjsStop1393" stop-color="#905e26" offset="1"></stop>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="SvgjsLinearGradient1394">
|
||||||
|
<stop id="SvgjsStop1395" stop-color="#905e26" offset="0"></stop>
|
||||||
|
<stop id="SvgjsStop1396" stop-color="#f5ec9b" offset="0.5"></stop>
|
||||||
|
<stop id="SvgjsStop1397" stop-color="#905e26" offset="1"></stop>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<g id="SvgjsG1386" featureKey="aMgJeN-0"
|
||||||
|
transform="matrix(1.5610770874511997,0,0,1.5610770874511997,71.94613967240352,-53.841545411371435)"
|
||||||
|
fill="url(#SvgjsLinearGradient1390)">
|
||||||
|
<path xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="M29.399,57.112c0.615,0.308,1.077,0.846,1.077,1.383c0,1.039-1.038,1.961-1.999,1.462l-15.223-7.881 c-0.846-0.499-1.576-0.808-1.576-1.884c0-1.115,0.692-1.385,1.576-1.922l15.223-7.881c1.038-0.346,1.999,0.424,1.999,1.462 c0,0.575-0.461,1.114-1.077,1.423l-13.761,6.918L29.399,57.112z"></path>
|
||||||
|
<path xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="M29.033,60.209c-0.208,0-0.413-0.052-0.608-0.152l-15.223-7.881c-0.086-0.05-0.165-0.095-0.242-0.141 c-0.748-0.431-1.395-0.804-1.395-1.843c0-1.057,0.594-1.406,1.346-1.849c0.093-0.054,0.187-0.11,0.284-0.169l15.229-7.885 c0.195-0.066,0.377-0.097,0.558-0.097c0.9,0,1.606,0.728,1.606,1.658c0,0.587-0.437,1.172-1.139,1.522l-13.562,6.818l13.562,6.818 c0.691,0.346,1.139,0.93,1.139,1.484C30.588,59.407,29.861,60.209,29.033,60.209z M28.982,40.419c-0.156,0-0.314,0.026-0.47,0.078 L13.306,48.37c-0.091,0.057-0.188,0.113-0.281,0.167c-0.743,0.439-1.234,0.728-1.234,1.655c0,0.91,0.537,1.219,1.281,1.648 c0.079,0.045,0.158,0.09,0.239,0.139l15.217,7.877c0.162,0.084,0.332,0.127,0.504,0.127c0.697,0,1.33-0.709,1.33-1.488 c0-0.465-0.407-0.98-1.014-1.283l-13.962-7.02l13.962-7.02c0.616-0.308,1.014-0.828,1.014-1.321 C30.363,41.048,29.756,40.419,28.982,40.419z"></path>
|
||||||
|
<path xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="M46.385,64.416c-0.231,0.691-0.922,1.077-1.614,0.961c-0.769-0.153-1.269-0.885-1.154-1.692 c0-0.076,0.039-0.191,0.077-0.307l9.88-27.831c0.23-0.692,0.922-1.038,1.614-0.923c0.73,0.154,1.269,0.885,1.153,1.652 c0,0.078-0.039,0.193-0.077,0.27L46.385,64.416z"></path>
|
||||||
|
<path xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="M45.016,65.511c-0.088,0-0.177-0.008-0.263-0.022c-0.837-0.167-1.371-0.949-1.247-1.819 c-0.001-0.076,0.038-0.193,0.079-0.318l9.883-27.842c0.207-0.618,0.778-1.02,1.457-1.02c0.094,0,0.188,0.009,0.281,0.023 c0.812,0.172,1.369,0.97,1.247,1.781c0.001,0.086-0.047,0.22-0.087,0.302l-9.875,27.856C46.281,65.084,45.688,65.511,45.016,65.511z M54.925,34.715c-0.58,0-1.068,0.34-1.244,0.867l-9.88,27.833c-0.035,0.104-0.07,0.213-0.07,0.27 c-0.108,0.767,0.349,1.439,1.062,1.582c0.071,0.012,0.147,0.018,0.223,0.018c0.575,0,1.083-0.363,1.263-0.904l9.881-27.872 c0.041-0.085,0.07-0.182,0.07-0.231c0.105-0.712-0.373-1.396-1.064-1.543C55.089,34.722,55.007,34.715,54.925,34.715z"></path>
|
||||||
|
<path xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="M84.362,50.192L70.6,43.274c-0.614-0.309-1.075-0.848-1.075-1.423c0-1.038,1.037-1.962,1.998-1.462l15.223,7.881 c0.885,0.537,1.576,0.807,1.576,1.922c0,1.076-0.73,1.385-1.576,1.884l-15.223,7.881c-0.961,0.499-1.998-0.423-1.998-1.462 c0-0.537,0.461-1.075,1.075-1.383L84.362,50.192z"></path>
|
||||||
|
<path xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="M70.967,60.209c-0.828,0-1.556-0.802-1.556-1.714c0-0.555,0.447-1.139,1.139-1.484l13.562-6.818L70.55,43.374 c-0.702-0.352-1.139-0.936-1.139-1.522c0-0.913,0.728-1.714,1.556-1.714c0.209,0,0.413,0.051,0.608,0.152l15.223,7.881 c0.104,0.062,0.198,0.119,0.29,0.173c0.753,0.442,1.347,0.792,1.347,1.849c0,1.039-0.646,1.412-1.395,1.843 c-0.078,0.046-0.157,0.091-0.237,0.138l-15.228,7.884C71.38,60.157,71.176,60.209,70.967,60.209z M70.967,40.363 c-0.696,0-1.33,0.709-1.33,1.488c0,0.493,0.397,1.013,1.014,1.321l13.962,7.02l-13.962,7.02c-0.606,0.305-1.014,0.82-1.014,1.283 c0,0.779,0.634,1.488,1.33,1.488c0.172,0,0.342-0.043,0.504-0.127l15.223-7.88c0.075-0.046,0.155-0.091,0.233-0.136 c0.744-0.43,1.282-0.738,1.282-1.648c0-0.928-0.491-1.216-1.235-1.655c-0.093-0.054-0.188-0.11-0.287-0.17l-15.216-7.876 C71.309,40.405,71.139,40.363,70.967,40.363z"></path>
|
||||||
|
</g>
|
||||||
|
<g id="SvgjsG1387" featureKey="8L6ael-0"
|
||||||
|
transform="matrix(3.168568052463937,0,0,3.168568052463937,-5.1330821487142195,52.17667742743372)"
|
||||||
|
fill="url(#SvgjsLinearGradient1394)">
|
||||||
|
<path d="M10.56 5.52 q1.22 0.46 2.2 1.48 q2.1 2.12 2.1 5.41 t-2.1 5.43 q-0.98 1.02 -2.2 1.48 q-1.26 0.52 -2.58 0.52 l-5.66 0 q-0.28 0 -0.49 -0.21 t-0.21 -0.49 l0 -1.44 q0 -0.3 0.21 -0.51 t0.49 -0.21 l5.6 0 q1.78 0 2.89 -1.3 t1.11 -3.26 t-1.11 -3.26 t-2.89 -1.3 l-5.6 0 q-0.28 0 -0.49 -0.21 t-0.21 -0.51 l0 -1.44 q0 -0.28 0.21 -0.49 t0.49 -0.21 l5.66 0 q1.32 0 2.58 0.52 z M32.208 18.86 q0.14 0.32 -0.07 0.65 t-0.57 0.33 l-13.32 0 q-0.18 0 -0.34 -0.09 t-0.24 -0.23 q-0.22 -0.32 -0.06 -0.66 l0.62 -1.46 q0.08 -0.2 0.26 -0.32 t0.38 -0.12 l9.32 0 l-3.28 -7.84 l-2.68 6.4 q-0.08 0.2 -0.25 0.31 t-0.39 0.11 l-1.68 0 q-0.38 0 -0.6 -0.32 q-0.08 -0.14 -0.1 -0.32 t0.04 -0.34 l4.06 -9.52 q0.08 -0.2 0.25 -0.32 t0.39 -0.12 l1.92 0 q0.22 0 0.39 0.12 t0.25 0.32 z M45.535999999999994 12.42 q0.86 0.54 1.31 1.32 t0.45 1.74 q0 2.26 -1.72 3.46 q-1.16 0.84 -3.14 1.06 l-0.08 0 q-0.28 0 -0.46 -0.18 q-0.24 -0.22 -0.24 -0.52 l0 -1.44 q0 -0.26 0.18 -0.46 t0.44 -0.24 q0.94 -0.1 1.46 -0.44 q0.4 -0.24 0.54 -0.62 q0.08 -0.22 0.08 -0.56 q0 -0.22 -0.08 -0.38 t-0.28 -0.3 q-0.58 -0.4 -1.5 -0.68 l-0.32 -0.1 q-1 -0.28 -1.84 -0.46 q-0.18 -0.04 -0.58 -0.14 l-0.22 -0.06 q-0.72 -0.18 -1.52 -0.5 q-1.18 -0.5 -1.94 -1.26 q-0.88 -0.88 -0.88 -2.32 q0 -1.98 1.52 -3.22 q1.04 -0.88 2.92 -1.12 q0.32 -0.04 0.55 0.18 t0.23 0.52 l0 1.44 q0 0.26 -0.16 0.46 t-0.41 0.23 t-0.51 0.11 q-0.58 0.22 -0.82 0.43 t-0.34 0.43 q-0.1 0.34 -0.1 0.64 q0 0.16 0.18 0.34 q0.28 0.28 0.82 0.5 q0.24 0.1 0.84 0.3 l2.5 0.62 l0.12 0.04 q0.94 0.26 1.36 0.4 q0.94 0.32 1.64 0.78 z M42.196 7.9 q-0.24 -0.06 -0.39 -0.25 t-0.15 -0.45 l0 -1.46 q0 -0.32 0.26 -0.54 q0.1 -0.1 0.26 -0.13 t0.3 -0.01 q1.76 0.28 2.86 1.2 q1.5 1.24 1.6 3.16 q0.02 0.28 -0.19 0.51 t-0.51 0.23 l-1.56 0 q-0.26 0 -0.46 -0.18 t-0.22 -0.44 q-0.08 -0.52 -0.34 -0.86 q-0.42 -0.5 -1.3 -0.76 q-0.06 0 -0.08 -0.02 l-0.08 0 z M39.855999999999995 17.08 q0.24 0.04 0.4 0.24 t0.16 0.44 l0 1.48 q0 0.32 -0.24 0.54 q-0.2 0.16 -0.46 0.16 l-0.1 0 q-1.86 -0.28 -3.06 -1.22 q-1.56 -1.24 -1.76 -3.46 q-0.04 -0.32 0.18 -0.55 t0.52 -0.23 l1.58 0 q0.28 0 0.48 0.19 t0.22 0.47 q0.06 1 0.98 1.54 q0.42 0.24 1.1 0.4 z M60.88399999999999 11.12 q0.3 0 0.51 0.21 t0.21 0.51 l0 1.48 q0 0.28 -0.21 0.49 t-0.51 0.21 l-6.58 0 l0 5.12 q0 0.28 -0.2 0.49 t-0.5 0.21 l-1.52 0 q-0.28 0 -0.49 -0.21 t-0.21 -0.49 l0 -7.3 q0 -0.3 0.21 -0.51 t0.49 -0.21 l8.8 0 z M61.78399999999999 5 q0.28 0 0.49 0.21 t0.21 0.49 l0 1.46 q0 0.3 -0.21 0.51 t-0.49 0.21 l-9.7 0 q-0.28 0 -0.49 -0.21 t-0.21 -0.51 l0 -1.46 q0 -0.28 0.21 -0.49 t0.49 -0.21 l9.7 0 z M79.512 18.86 q0.14 0.32 -0.07 0.65 t-0.57 0.33 l-13.32 0 q-0.18 0 -0.34 -0.09 t-0.24 -0.23 q-0.22 -0.32 -0.06 -0.66 l0.62 -1.46 q0.08 -0.2 0.26 -0.32 t0.38 -0.12 l9.32 0 l-3.28 -7.84 l-2.68 6.4 q-0.08 0.2 -0.25 0.31 t-0.39 0.11 l-1.68 0 q-0.38 0 -0.6 -0.32 q-0.08 -0.14 -0.1 -0.32 t0.04 -0.34 l4.06 -9.52 q0.08 -0.2 0.25 -0.32 t0.39 -0.12 l1.92 0 q0.22 0 0.39 0.12 t0.25 0.32 z M92 5.52 q1.22 0.46 2.2 1.48 q2.1 2.12 2.1 5.41 t-2.1 5.43 q-0.98 1.02 -2.2 1.48 q-1.26 0.52 -2.58 0.52 l-5.66 0 q-0.28 0 -0.49 -0.21 t-0.21 -0.49 l0 -1.44 q0 -0.3 0.21 -0.51 t0.49 -0.21 l5.6 0 q1.78 0 2.89 -1.3 t1.11 -3.26 t-1.11 -3.26 t-2.89 -1.3 l-5.6 0 q-0.28 0 -0.49 -0.21 t-0.21 -0.51 l0 -1.44 q0 -0.28 0.21 -0.49 t0.49 -0.21 l5.66 0 q1.32 0 2.58 0.52 z"></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 8.1 KiB |
BIN
public/assets/dasfad/194885175_padded_logo.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
@ -0,0 +1,912 @@
|
|||||||
|
%!PS-Adobe-3.0 EPSF-3.0
|
||||||
|
%Produced by poppler pdftops version: 22.05.0 (http://poppler.freedesktop.org)
|
||||||
|
%%Creator: Chromium
|
||||||
|
%%LanguageLevel: 3
|
||||||
|
%%DocumentSuppliedResources: (atend)
|
||||||
|
%%BoundingBox: 0 0 2400 1018
|
||||||
|
%%HiResBoundingBox: 0 0 2400 1017.12
|
||||||
|
%%DocumentSuppliedResources: (atend)
|
||||||
|
%%EndComments
|
||||||
|
%%BeginProlog
|
||||||
|
%%BeginResource: procset xpdf 3.00 0
|
||||||
|
%%Copyright: Copyright 1996-2011, 2022 Glyph & Cog, LLC
|
||||||
|
/xpdf 75 dict def xpdf begin
|
||||||
|
% PDF special state
|
||||||
|
/pdfDictSize 15 def
|
||||||
|
/pdfSetup {
|
||||||
|
/setpagedevice where {
|
||||||
|
pop 2 dict begin
|
||||||
|
/Policies 1 dict dup begin /PageSize 6 def end def
|
||||||
|
{ /Duplex true def } if
|
||||||
|
currentdict end setpagedevice
|
||||||
|
} {
|
||||||
|
pop
|
||||||
|
} ifelse
|
||||||
|
} def
|
||||||
|
/pdfSetupPaper {
|
||||||
|
% Change paper size, but only if different from previous paper size otherwise
|
||||||
|
% duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size
|
||||||
|
% so we use the same when checking if the size changes.
|
||||||
|
/setpagedevice where {
|
||||||
|
pop currentpagedevice
|
||||||
|
/PageSize known {
|
||||||
|
2 copy
|
||||||
|
currentpagedevice /PageSize get aload pop
|
||||||
|
exch 4 1 roll
|
||||||
|
sub abs 5 gt
|
||||||
|
3 1 roll
|
||||||
|
sub abs 5 gt
|
||||||
|
or
|
||||||
|
} {
|
||||||
|
true
|
||||||
|
} ifelse
|
||||||
|
{
|
||||||
|
2 array astore
|
||||||
|
2 dict begin
|
||||||
|
/PageSize exch def
|
||||||
|
/ImagingBBox null def
|
||||||
|
currentdict end
|
||||||
|
setpagedevice
|
||||||
|
} {
|
||||||
|
pop pop
|
||||||
|
} ifelse
|
||||||
|
} {
|
||||||
|
pop
|
||||||
|
} ifelse
|
||||||
|
} def
|
||||||
|
/pdfStartPage {
|
||||||
|
pdfDictSize dict begin
|
||||||
|
/pdfFillCS [] def
|
||||||
|
/pdfFillXform {} def
|
||||||
|
/pdfStrokeCS [] def
|
||||||
|
/pdfStrokeXform {} def
|
||||||
|
/pdfFill [0] def
|
||||||
|
/pdfStroke [0] def
|
||||||
|
/pdfFillOP false def
|
||||||
|
/pdfStrokeOP false def
|
||||||
|
/pdfOPM false def
|
||||||
|
/pdfLastFill false def
|
||||||
|
/pdfLastStroke false def
|
||||||
|
/pdfTextMat [1 0 0 1 0 0] def
|
||||||
|
/pdfFontSize 0 def
|
||||||
|
/pdfCharSpacing 0 def
|
||||||
|
/pdfTextRender 0 def
|
||||||
|
/pdfPatternCS false def
|
||||||
|
/pdfTextRise 0 def
|
||||||
|
/pdfWordSpacing 0 def
|
||||||
|
/pdfHorizScaling 1 def
|
||||||
|
/pdfTextClipPath [] def
|
||||||
|
} def
|
||||||
|
/pdfEndPage { end } def
|
||||||
|
% PDF color state
|
||||||
|
/opm { dup /pdfOPM exch def
|
||||||
|
/setoverprintmode where{pop setoverprintmode}{pop}ifelse } def
|
||||||
|
/cs { /pdfFillXform exch def dup /pdfFillCS exch def
|
||||||
|
setcolorspace } def
|
||||||
|
/CS { /pdfStrokeXform exch def dup /pdfStrokeCS exch def
|
||||||
|
setcolorspace } def
|
||||||
|
/sc { pdfLastFill not { pdfFillCS setcolorspace } if
|
||||||
|
dup /pdfFill exch def aload pop pdfFillXform setcolor
|
||||||
|
/pdfLastFill true def /pdfLastStroke false def } def
|
||||||
|
/SC { pdfLastStroke not { pdfStrokeCS setcolorspace } if
|
||||||
|
dup /pdfStroke exch def aload pop pdfStrokeXform setcolor
|
||||||
|
/pdfLastStroke true def /pdfLastFill false def } def
|
||||||
|
/op { /pdfFillOP exch def
|
||||||
|
pdfLastFill { pdfFillOP setoverprint } if } def
|
||||||
|
/OP { /pdfStrokeOP exch def
|
||||||
|
pdfLastStroke { pdfStrokeOP setoverprint } if } def
|
||||||
|
/fCol {
|
||||||
|
pdfLastFill not {
|
||||||
|
pdfFillCS setcolorspace
|
||||||
|
pdfFill aload pop pdfFillXform setcolor
|
||||||
|
pdfFillOP setoverprint
|
||||||
|
/pdfLastFill true def /pdfLastStroke false def
|
||||||
|
} if
|
||||||
|
} def
|
||||||
|
/sCol {
|
||||||
|
pdfLastStroke not {
|
||||||
|
pdfStrokeCS setcolorspace
|
||||||
|
pdfStroke aload pop pdfStrokeXform setcolor
|
||||||
|
pdfStrokeOP setoverprint
|
||||||
|
/pdfLastStroke true def /pdfLastFill false def
|
||||||
|
} if
|
||||||
|
} def
|
||||||
|
% build a font
|
||||||
|
/pdfMakeFont {
|
||||||
|
4 3 roll findfont
|
||||||
|
4 2 roll matrix scale makefont
|
||||||
|
dup length dict begin
|
||||||
|
{ 1 index /FID ne { def } { pop pop } ifelse } forall
|
||||||
|
/Encoding exch def
|
||||||
|
currentdict
|
||||||
|
end
|
||||||
|
definefont pop
|
||||||
|
} def
|
||||||
|
/pdfMakeFont16 {
|
||||||
|
exch findfont
|
||||||
|
dup length dict begin
|
||||||
|
{ 1 index /FID ne { def } { pop pop } ifelse } forall
|
||||||
|
/WMode exch def
|
||||||
|
currentdict
|
||||||
|
end
|
||||||
|
definefont pop
|
||||||
|
} def
|
||||||
|
/pdfMakeFont16L3 {
|
||||||
|
1 index /CIDFont resourcestatus {
|
||||||
|
pop pop 1 index /CIDFont findresource /CIDFontType known
|
||||||
|
} {
|
||||||
|
false
|
||||||
|
} ifelse
|
||||||
|
{
|
||||||
|
0 eq { /Identity-H } { /Identity-V } ifelse
|
||||||
|
exch 1 array astore composefont pop
|
||||||
|
} {
|
||||||
|
pdfMakeFont16
|
||||||
|
} ifelse
|
||||||
|
} def
|
||||||
|
% graphics state operators
|
||||||
|
/q { gsave pdfDictSize dict begin } def
|
||||||
|
/Q {
|
||||||
|
end grestore
|
||||||
|
/pdfLastFill where {
|
||||||
|
pop
|
||||||
|
pdfLastFill {
|
||||||
|
pdfFillOP setoverprint
|
||||||
|
} {
|
||||||
|
pdfStrokeOP setoverprint
|
||||||
|
} ifelse
|
||||||
|
} if
|
||||||
|
/pdfOPM where {
|
||||||
|
pop
|
||||||
|
pdfOPM /setoverprintmode where{pop setoverprintmode}{pop}ifelse
|
||||||
|
} if
|
||||||
|
} def
|
||||||
|
/cm { concat } def
|
||||||
|
/d { setdash } def
|
||||||
|
/i { setflat } def
|
||||||
|
/j { setlinejoin } def
|
||||||
|
/J { setlinecap } def
|
||||||
|
/M { setmiterlimit } def
|
||||||
|
/w { setlinewidth } def
|
||||||
|
% path segment operators
|
||||||
|
/m { moveto } def
|
||||||
|
/l { lineto } def
|
||||||
|
/c { curveto } def
|
||||||
|
/re { 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto
|
||||||
|
neg 0 rlineto closepath } def
|
||||||
|
/h { closepath } def
|
||||||
|
% path painting operators
|
||||||
|
/S { sCol stroke } def
|
||||||
|
/Sf { fCol stroke } def
|
||||||
|
/f { fCol fill } def
|
||||||
|
/f* { fCol eofill } def
|
||||||
|
% clipping operators
|
||||||
|
/W { clip newpath } def
|
||||||
|
/W* { eoclip newpath } def
|
||||||
|
/Ws { strokepath clip newpath } def
|
||||||
|
% text state operators
|
||||||
|
/Tc { /pdfCharSpacing exch def } def
|
||||||
|
/Tf { dup /pdfFontSize exch def
|
||||||
|
dup pdfHorizScaling mul exch matrix scale
|
||||||
|
pdfTextMat matrix concatmatrix dup 4 0 put dup 5 0 put
|
||||||
|
exch findfont exch makefont setfont } def
|
||||||
|
/Tr { /pdfTextRender exch def } def
|
||||||
|
/Tp { /pdfPatternCS exch def } def
|
||||||
|
/Ts { /pdfTextRise exch def } def
|
||||||
|
/Tw { /pdfWordSpacing exch def } def
|
||||||
|
/Tz { /pdfHorizScaling exch def } def
|
||||||
|
% text positioning operators
|
||||||
|
/Td { pdfTextMat transform moveto } def
|
||||||
|
/Tm { /pdfTextMat exch def } def
|
||||||
|
% text string operators
|
||||||
|
/xyshow where {
|
||||||
|
pop
|
||||||
|
/xyshow2 {
|
||||||
|
dup length array
|
||||||
|
0 2 2 index length 1 sub {
|
||||||
|
2 index 1 index 2 copy get 3 1 roll 1 add get
|
||||||
|
pdfTextMat dtransform
|
||||||
|
4 2 roll 2 copy 6 5 roll put 1 add 3 1 roll dup 4 2 roll put
|
||||||
|
} for
|
||||||
|
exch pop
|
||||||
|
xyshow
|
||||||
|
} def
|
||||||
|
}{
|
||||||
|
/xyshow2 {
|
||||||
|
currentfont /FontType get 0 eq {
|
||||||
|
0 2 3 index length 1 sub {
|
||||||
|
currentpoint 4 index 3 index 2 getinterval show moveto
|
||||||
|
2 copy get 2 index 3 2 roll 1 add get
|
||||||
|
pdfTextMat dtransform rmoveto
|
||||||
|
} for
|
||||||
|
} {
|
||||||
|
0 1 3 index length 1 sub {
|
||||||
|
currentpoint 4 index 3 index 1 getinterval show moveto
|
||||||
|
2 copy 2 mul get 2 index 3 2 roll 2 mul 1 add get
|
||||||
|
pdfTextMat dtransform rmoveto
|
||||||
|
} for
|
||||||
|
} ifelse
|
||||||
|
pop pop
|
||||||
|
} def
|
||||||
|
} ifelse
|
||||||
|
/cshow where {
|
||||||
|
pop
|
||||||
|
/xycp {
|
||||||
|
0 3 2 roll
|
||||||
|
{
|
||||||
|
pop pop currentpoint 3 2 roll
|
||||||
|
1 string dup 0 4 3 roll put false charpath moveto
|
||||||
|
2 copy get 2 index 2 index 1 add get
|
||||||
|
pdfTextMat dtransform rmoveto
|
||||||
|
2 add
|
||||||
|
} exch cshow
|
||||||
|
pop pop
|
||||||
|
} def
|
||||||
|
}{
|
||||||
|
/xycp {
|
||||||
|
currentfont /FontType get 0 eq {
|
||||||
|
0 2 3 index length 1 sub {
|
||||||
|
currentpoint 4 index 3 index 2 getinterval false charpath moveto
|
||||||
|
2 copy get 2 index 3 2 roll 1 add get
|
||||||
|
pdfTextMat dtransform rmoveto
|
||||||
|
} for
|
||||||
|
} {
|
||||||
|
0 1 3 index length 1 sub {
|
||||||
|
currentpoint 4 index 3 index 1 getinterval false charpath moveto
|
||||||
|
2 copy 2 mul get 2 index 3 2 roll 2 mul 1 add get
|
||||||
|
pdfTextMat dtransform rmoveto
|
||||||
|
} for
|
||||||
|
} ifelse
|
||||||
|
pop pop
|
||||||
|
} def
|
||||||
|
} ifelse
|
||||||
|
/Tj {
|
||||||
|
fCol
|
||||||
|
0 pdfTextRise pdfTextMat dtransform rmoveto
|
||||||
|
currentpoint 4 2 roll
|
||||||
|
pdfTextRender 1 and 0 eq {
|
||||||
|
2 copy xyshow2
|
||||||
|
} if
|
||||||
|
pdfTextRender 3 and dup 1 eq exch 2 eq or {
|
||||||
|
3 index 3 index moveto
|
||||||
|
2 copy
|
||||||
|
currentfont /FontType get 3 eq { fCol } { sCol } ifelse
|
||||||
|
xycp currentpoint stroke moveto
|
||||||
|
} if
|
||||||
|
pdfTextRender 4 and 0 ne {
|
||||||
|
4 2 roll moveto xycp
|
||||||
|
/pdfTextClipPath [ pdfTextClipPath aload pop
|
||||||
|
{/moveto cvx}
|
||||||
|
{/lineto cvx}
|
||||||
|
{/curveto cvx}
|
||||||
|
{/closepath cvx}
|
||||||
|
pathforall ] def
|
||||||
|
currentpoint newpath moveto
|
||||||
|
} {
|
||||||
|
pop pop pop pop
|
||||||
|
} ifelse
|
||||||
|
0 pdfTextRise neg pdfTextMat dtransform rmoveto
|
||||||
|
} def
|
||||||
|
/TJm { 0.001 mul pdfFontSize mul pdfHorizScaling mul neg 0
|
||||||
|
pdfTextMat dtransform rmoveto } def
|
||||||
|
/TJmV { 0.001 mul pdfFontSize mul neg 0 exch
|
||||||
|
pdfTextMat dtransform rmoveto } def
|
||||||
|
/Tclip { pdfTextClipPath cvx exec clip newpath
|
||||||
|
/pdfTextClipPath [] def } def
|
||||||
|
/Tclip* { pdfTextClipPath cvx exec eoclip newpath
|
||||||
|
/pdfTextClipPath [] def } def
|
||||||
|
% Level 2/3 image operators
|
||||||
|
/pdfImBuf 100 string def
|
||||||
|
/pdfImStr {
|
||||||
|
2 copy exch length lt {
|
||||||
|
2 copy get exch 1 add exch
|
||||||
|
} {
|
||||||
|
()
|
||||||
|
} ifelse
|
||||||
|
} def
|
||||||
|
/skipEOD {
|
||||||
|
{ currentfile pdfImBuf readline
|
||||||
|
not { pop exit } if
|
||||||
|
(%-EOD-) eq { exit } if } loop
|
||||||
|
} def
|
||||||
|
/pdfIm { image skipEOD } def
|
||||||
|
/pdfMask {
|
||||||
|
/ReusableStreamDecode filter
|
||||||
|
skipEOD
|
||||||
|
/maskStream exch def
|
||||||
|
} def
|
||||||
|
/pdfMaskEnd { maskStream closefile } def
|
||||||
|
/pdfMaskInit {
|
||||||
|
/maskArray exch def
|
||||||
|
/maskIdx 0 def
|
||||||
|
} def
|
||||||
|
/pdfMaskSrc {
|
||||||
|
maskIdx maskArray length lt {
|
||||||
|
maskArray maskIdx get
|
||||||
|
/maskIdx maskIdx 1 add def
|
||||||
|
} {
|
||||||
|
()
|
||||||
|
} ifelse
|
||||||
|
} def
|
||||||
|
/pdfImM { fCol imagemask skipEOD } def
|
||||||
|
/pr { 2 index 2 index 3 2 roll putinterval 4 add } def
|
||||||
|
/pdfImClip {
|
||||||
|
gsave
|
||||||
|
0 2 4 index length 1 sub {
|
||||||
|
dup 4 index exch 2 copy
|
||||||
|
get 5 index div put
|
||||||
|
1 add 3 index exch 2 copy
|
||||||
|
get 3 index div put
|
||||||
|
} for
|
||||||
|
pop pop rectclip
|
||||||
|
} def
|
||||||
|
/pdfImClipEnd { grestore } def
|
||||||
|
% shading operators
|
||||||
|
/colordelta {
|
||||||
|
false 0 1 3 index length 1 sub {
|
||||||
|
dup 4 index exch get 3 index 3 2 roll get sub abs 0.004 gt {
|
||||||
|
pop true
|
||||||
|
} if
|
||||||
|
} for
|
||||||
|
exch pop exch pop
|
||||||
|
} def
|
||||||
|
/funcCol { func n array astore } def
|
||||||
|
/funcSH {
|
||||||
|
dup 0 eq {
|
||||||
|
true
|
||||||
|
} {
|
||||||
|
dup 6 eq {
|
||||||
|
false
|
||||||
|
} {
|
||||||
|
4 index 4 index funcCol dup
|
||||||
|
6 index 4 index funcCol dup
|
||||||
|
3 1 roll colordelta 3 1 roll
|
||||||
|
5 index 5 index funcCol dup
|
||||||
|
3 1 roll colordelta 3 1 roll
|
||||||
|
6 index 8 index funcCol dup
|
||||||
|
3 1 roll colordelta 3 1 roll
|
||||||
|
colordelta or or or
|
||||||
|
} ifelse
|
||||||
|
} ifelse
|
||||||
|
{
|
||||||
|
1 add
|
||||||
|
4 index 3 index add 0.5 mul exch 4 index 3 index add 0.5 mul exch
|
||||||
|
6 index 6 index 4 index 4 index 4 index funcSH
|
||||||
|
2 index 6 index 6 index 4 index 4 index funcSH
|
||||||
|
6 index 2 index 4 index 6 index 4 index funcSH
|
||||||
|
5 3 roll 3 2 roll funcSH pop pop
|
||||||
|
} {
|
||||||
|
pop 3 index 2 index add 0.5 mul 3 index 2 index add 0.5 mul
|
||||||
|
funcCol sc
|
||||||
|
dup 4 index exch mat transform m
|
||||||
|
3 index 3 index mat transform l
|
||||||
|
1 index 3 index mat transform l
|
||||||
|
mat transform l pop pop h f*
|
||||||
|
} ifelse
|
||||||
|
} def
|
||||||
|
/axialCol {
|
||||||
|
dup 0 lt {
|
||||||
|
pop t0
|
||||||
|
} {
|
||||||
|
dup 1 gt {
|
||||||
|
pop t1
|
||||||
|
} {
|
||||||
|
dt mul t0 add
|
||||||
|
} ifelse
|
||||||
|
} ifelse
|
||||||
|
func n array astore
|
||||||
|
} def
|
||||||
|
/axialSH {
|
||||||
|
dup 0 eq {
|
||||||
|
true
|
||||||
|
} {
|
||||||
|
dup 8 eq {
|
||||||
|
false
|
||||||
|
} {
|
||||||
|
2 index axialCol 2 index axialCol colordelta
|
||||||
|
} ifelse
|
||||||
|
} ifelse
|
||||||
|
{
|
||||||
|
1 add 3 1 roll 2 copy add 0.5 mul
|
||||||
|
dup 4 3 roll exch 4 index axialSH
|
||||||
|
exch 3 2 roll axialSH
|
||||||
|
} {
|
||||||
|
pop 2 copy add 0.5 mul
|
||||||
|
axialCol sc
|
||||||
|
exch dup dx mul x0 add exch dy mul y0 add
|
||||||
|
3 2 roll dup dx mul x0 add exch dy mul y0 add
|
||||||
|
dx abs dy abs ge {
|
||||||
|
2 copy yMin sub dy mul dx div add yMin m
|
||||||
|
yMax sub dy mul dx div add yMax l
|
||||||
|
2 copy yMax sub dy mul dx div add yMax l
|
||||||
|
yMin sub dy mul dx div add yMin l
|
||||||
|
h f*
|
||||||
|
} {
|
||||||
|
exch 2 copy xMin sub dx mul dy div add xMin exch m
|
||||||
|
xMax sub dx mul dy div add xMax exch l
|
||||||
|
exch 2 copy xMax sub dx mul dy div add xMax exch l
|
||||||
|
xMin sub dx mul dy div add xMin exch l
|
||||||
|
h f*
|
||||||
|
} ifelse
|
||||||
|
} ifelse
|
||||||
|
} def
|
||||||
|
/radialCol {
|
||||||
|
dup t0 lt {
|
||||||
|
pop t0
|
||||||
|
} {
|
||||||
|
dup t1 gt {
|
||||||
|
pop t1
|
||||||
|
} if
|
||||||
|
} ifelse
|
||||||
|
func n array astore
|
||||||
|
} def
|
||||||
|
/radialSH {
|
||||||
|
dup 0 eq {
|
||||||
|
true
|
||||||
|
} {
|
||||||
|
dup 8 eq {
|
||||||
|
false
|
||||||
|
} {
|
||||||
|
2 index dt mul t0 add radialCol
|
||||||
|
2 index dt mul t0 add radialCol colordelta
|
||||||
|
} ifelse
|
||||||
|
} ifelse
|
||||||
|
{
|
||||||
|
1 add 3 1 roll 2 copy add 0.5 mul
|
||||||
|
dup 4 3 roll exch 4 index radialSH
|
||||||
|
exch 3 2 roll radialSH
|
||||||
|
} {
|
||||||
|
pop 2 copy add 0.5 mul dt mul t0 add
|
||||||
|
radialCol sc
|
||||||
|
encl {
|
||||||
|
exch dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add
|
||||||
|
0 360 arc h
|
||||||
|
dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add
|
||||||
|
360 0 arcn h f
|
||||||
|
} {
|
||||||
|
2 copy
|
||||||
|
dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add
|
||||||
|
a1 a2 arcn
|
||||||
|
dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add
|
||||||
|
a2 a1 arcn h
|
||||||
|
dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add
|
||||||
|
a1 a2 arc
|
||||||
|
dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add
|
||||||
|
a2 a1 arc h f
|
||||||
|
} ifelse
|
||||||
|
} ifelse
|
||||||
|
} def
|
||||||
|
end
|
||||||
|
%%EndResource
|
||||||
|
/CIDInit /ProcSet findresource begin
|
||||||
|
10 dict begin
|
||||||
|
begincmap
|
||||||
|
/CMapType 1 def
|
||||||
|
/CMapName /Identity-H def
|
||||||
|
/CIDSystemInfo 3 dict dup begin
|
||||||
|
/Registry (Adobe) def
|
||||||
|
/Ordering (Identity) def
|
||||||
|
/Supplement 0 def
|
||||||
|
end def
|
||||||
|
1 begincodespacerange
|
||||||
|
<0000> <ffff>
|
||||||
|
endcodespacerange
|
||||||
|
0 usefont
|
||||||
|
1 begincidrange
|
||||||
|
<0000> <ffff> 0
|
||||||
|
endcidrange
|
||||||
|
endcmap
|
||||||
|
currentdict CMapName exch /CMap defineresource pop
|
||||||
|
end
|
||||||
|
10 dict begin
|
||||||
|
begincmap
|
||||||
|
/CMapType 1 def
|
||||||
|
/CMapName /Identity-V def
|
||||||
|
/CIDSystemInfo 3 dict dup begin
|
||||||
|
/Registry (Adobe) def
|
||||||
|
/Ordering (Identity) def
|
||||||
|
/Supplement 0 def
|
||||||
|
end def
|
||||||
|
/WMode 1 def
|
||||||
|
1 begincodespacerange
|
||||||
|
<0000> <ffff>
|
||||||
|
endcodespacerange
|
||||||
|
0 usefont
|
||||||
|
1 begincidrange
|
||||||
|
<0000> <ffff> 0
|
||||||
|
endcidrange
|
||||||
|
endcmap
|
||||||
|
currentdict CMapName exch /CMap defineresource pop
|
||||||
|
end
|
||||||
|
end
|
||||||
|
%%EndProlog
|
||||||
|
%%BeginSetup
|
||||||
|
xpdf begin
|
||||||
|
%%EndSetup
|
||||||
|
pdfStartPage
|
||||||
|
%%EndPageSetup
|
||||||
|
[] 0 d
|
||||||
|
1 i
|
||||||
|
0 j
|
||||||
|
0 J
|
||||||
|
10 M
|
||||||
|
1 w
|
||||||
|
/DeviceGray {} cs
|
||||||
|
[0] sc
|
||||||
|
/DeviceGray {} CS
|
||||||
|
[0] SC
|
||||||
|
false op
|
||||||
|
false OP
|
||||||
|
{} settransfer
|
||||||
|
0 0 2400 1017.12 re
|
||||||
|
W
|
||||||
|
q
|
||||||
|
[0.24 0 0 -0.24 0 1017.12] cm
|
||||||
|
q
|
||||||
|
0 0 10000 4234.375 re
|
||||||
|
W*
|
||||||
|
q
|
||||||
|
[48.783241 0 0 48.766369 2560.8376 -1369.56274] cm
|
||||||
|
29.399 57.112 m
|
||||||
|
30.014 57.419998 30.476 57.958 30.476 58.494999 c
|
||||||
|
30.476 59.534 29.438 60.455997 28.476999 59.957001 c
|
||||||
|
13.254 52.076 l
|
||||||
|
12.408 51.577 11.678 51.268002 11.678 50.192001 c
|
||||||
|
11.678 49.077 12.37 48.807003 13.254 48.27 c
|
||||||
|
28.476999 40.389 l
|
||||||
|
29.514999 40.042999 30.476 40.813 30.476 41.851002 c
|
||||||
|
30.476 42.426003 30.014999 42.965 29.399 43.274002 c
|
||||||
|
15.638 50.192001 l
|
||||||
|
29.399 57.112 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
29.033001 60.209 m
|
||||||
|
28.825001 60.209 28.620001 60.157001 28.425001 60.056999 c
|
||||||
|
13.202002 52.175999 l
|
||||||
|
13.116001 52.125999 13.037002 52.080997 12.960002 52.035 c
|
||||||
|
12.212002 51.604 11.565002 51.230999 11.565002 50.192001 c
|
||||||
|
11.565002 49.135002 12.159002 48.786003 12.911002 48.343002 c
|
||||||
|
13.004003 48.289001 13.098002 48.233002 13.195003 48.174004 c
|
||||||
|
28.424004 40.289001 l
|
||||||
|
28.619003 40.223 28.801004 40.192001 28.982004 40.192001 c
|
||||||
|
29.882004 40.192001 30.588005 40.920002 30.588005 41.850002 c
|
||||||
|
30.588005 42.437004 30.151005 43.022003 29.449005 43.372002 c
|
||||||
|
15.887005 50.190002 l
|
||||||
|
29.449005 57.008003 l
|
||||||
|
30.140005 57.354004 30.588005 57.938004 30.588005 58.492004 c
|
||||||
|
30.587999 59.407001 29.861 60.209 29.033001 60.209 c
|
||||||
|
h
|
||||||
|
28.982 40.418999 m
|
||||||
|
28.826 40.418999 28.668001 40.445 28.512001 40.496998 c
|
||||||
|
13.306 48.369999 l
|
||||||
|
13.215 48.426998 13.118 48.482998 13.025 48.536999 c
|
||||||
|
12.282 48.975998 11.790999 49.264999 11.790999 50.191998 c
|
||||||
|
11.790999 51.101997 12.327999 51.410999 13.072 51.839996 c
|
||||||
|
13.151 51.884995 13.23 51.929996 13.311 51.978996 c
|
||||||
|
28.528 59.855995 l
|
||||||
|
28.690001 59.939995 28.860001 59.982994 29.032 59.982994 c
|
||||||
|
29.729 59.982994 30.362 59.273994 30.362 58.494995 c
|
||||||
|
30.362 58.029995 29.955 57.514996 29.348 57.211994 c
|
||||||
|
15.386 50.191994 l
|
||||||
|
29.348 43.171993 l
|
||||||
|
29.963999 42.863995 30.362 42.343994 30.362 41.850994 c
|
||||||
|
30.363001 41.048 29.756001 40.418999 28.982 40.418999 c
|
||||||
|
h
|
||||||
|
f
|
||||||
|
46.384998 64.416 m
|
||||||
|
46.153999 65.107002 45.462997 65.493004 44.771 65.376999 c
|
||||||
|
44.001999 65.223999 43.501999 64.491997 43.617001 63.684998 c
|
||||||
|
43.617001 63.608997 43.656002 63.493996 43.694 63.377998 c
|
||||||
|
53.574001 35.546997 l
|
||||||
|
53.804001 34.854996 54.496002 34.508999 55.188 34.623997 c
|
||||||
|
55.917999 34.777996 56.457001 35.508995 56.341 36.275997 c
|
||||||
|
56.341 36.353996 56.301998 36.468998 56.264 36.545998 c
|
||||||
|
46.384998 64.416 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
45.015999 65.511002 m
|
||||||
|
44.927998 65.511002 44.839001 65.502998 44.752998 65.488998 c
|
||||||
|
43.915997 65.321999 43.382 64.540001 43.505997 63.669998 c
|
||||||
|
43.504997 63.593998 43.543995 63.476997 43.584995 63.351997 c
|
||||||
|
53.467995 35.509998 l
|
||||||
|
53.674995 34.891998 54.245995 34.489998 54.924995 34.489998 c
|
||||||
|
55.018997 34.489998 55.112995 34.498997 55.205994 34.512997 c
|
||||||
|
56.017994 34.684998 56.574993 35.482998 56.452995 36.293995 c
|
||||||
|
56.453995 36.379993 56.405994 36.513996 56.365993 36.595993 c
|
||||||
|
46.490993 64.451996 l
|
||||||
|
46.280998 65.084 45.688 65.511002 45.015999 65.511002 c
|
||||||
|
h
|
||||||
|
54.924999 34.715 m
|
||||||
|
54.344997 34.715 53.856998 35.055 53.681 35.582001 c
|
||||||
|
43.800999 63.415001 l
|
||||||
|
43.765999 63.519001 43.730999 63.628002 43.730999 63.685001 c
|
||||||
|
43.622997 64.452003 44.079998 65.124001 44.792999 65.266998 c
|
||||||
|
44.863998 65.278999 44.939999 65.284996 45.015999 65.284996 c
|
||||||
|
45.591 65.284996 46.098999 64.921997 46.278999 64.380997 c
|
||||||
|
56.16 36.508995 l
|
||||||
|
56.201 36.423996 56.23 36.326996 56.23 36.277996 c
|
||||||
|
56.334999 35.565994 55.856998 34.881996 55.166 34.734997 c
|
||||||
|
55.089001 34.722 55.007 34.715 54.924999 34.715 c
|
||||||
|
h
|
||||||
|
f
|
||||||
|
84.362 50.192001 m
|
||||||
|
70.599998 43.273998 l
|
||||||
|
69.986 42.964996 69.525002 42.425999 69.525002 41.850998 c
|
||||||
|
69.525002 40.813 70.562004 39.888996 71.523003 40.388996 c
|
||||||
|
86.746002 48.269997 l
|
||||||
|
87.631004 48.806995 88.321999 49.076996 88.321999 50.191998 c
|
||||||
|
88.321999 51.267998 87.591995 51.576996 86.746002 52.075996 c
|
||||||
|
71.523003 59.956997 l
|
||||||
|
70.562004 60.455997 69.525002 59.533997 69.525002 58.494995 c
|
||||||
|
69.525002 57.957996 69.986 57.419994 70.599998 57.111996 c
|
||||||
|
84.362 50.192001 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
70.967003 60.209 m
|
||||||
|
70.139 60.209 69.411003 59.407001 69.411003 58.494999 c
|
||||||
|
69.411003 57.939999 69.858002 57.355999 70.550003 57.010998 c
|
||||||
|
84.112 50.192997 l
|
||||||
|
70.550003 43.374001 l
|
||||||
|
69.848 43.021999 69.411003 42.438 69.411003 41.852001 c
|
||||||
|
69.411003 40.939003 70.139 40.138 70.967003 40.138 c
|
||||||
|
71.176003 40.138 71.380005 40.188999 71.575005 40.290001 c
|
||||||
|
86.798004 48.171001 l
|
||||||
|
86.902 48.233002 86.996002 48.290001 87.088005 48.344002 c
|
||||||
|
87.841003 48.786003 88.435005 49.136002 88.435005 50.193001 c
|
||||||
|
88.435005 51.232002 87.789001 51.605 87.040009 52.035999 c
|
||||||
|
86.962006 52.082001 86.883011 52.126999 86.803009 52.174 c
|
||||||
|
71.575012 60.057999 l
|
||||||
|
71.379997 60.157001 71.176003 60.209 70.967003 60.209 c
|
||||||
|
h
|
||||||
|
70.967003 40.362999 m
|
||||||
|
70.271004 40.362999 69.637001 41.071999 69.637001 41.850998 c
|
||||||
|
69.637001 42.343998 70.034004 42.863998 70.651001 43.171997 c
|
||||||
|
84.612999 50.191998 l
|
||||||
|
70.651001 57.211998 l
|
||||||
|
70.044998 57.516998 69.637001 58.031998 69.637001 58.494999 c
|
||||||
|
69.637001 59.273998 70.271004 59.982998 70.967003 59.982998 c
|
||||||
|
71.139 59.982998 71.309006 59.939999 71.471001 59.855999 c
|
||||||
|
86.694 51.975998 l
|
||||||
|
86.768997 51.929996 86.848999 51.884998 86.927002 51.839996 c
|
||||||
|
87.671005 51.409996 88.209 51.101997 88.209 50.191998 c
|
||||||
|
88.209 49.263996 87.718002 48.975998 86.973999 48.536999 c
|
||||||
|
86.880997 48.482998 86.785995 48.426998 86.686996 48.367001 c
|
||||||
|
71.470993 40.491001 l
|
||||||
|
71.308998 40.404999 71.139 40.362999 70.967003 40.362999 c
|
||||||
|
h
|
||||||
|
f
|
||||||
|
Q
|
||||||
|
q
|
||||||
|
[99.016907 0 0 98.982658 152.13266 1942.3326] cm
|
||||||
|
10.56 5.52 m
|
||||||
|
11.373334 5.826667 12.106668 6.32 12.76 7 c
|
||||||
|
14.160001 8.413333 14.860001 10.216666 14.860001 12.41 c
|
||||||
|
14.860001 14.603334 14.160001 16.413334 12.76 17.84 c
|
||||||
|
12.106668 18.52 11.373334 19.013334 10.56 19.32 c
|
||||||
|
9.72 19.666666 8.860001 19.84 7.980001 19.84 c
|
||||||
|
2.320001 19.84 l
|
||||||
|
2.133334 19.84 1.970001 19.77 1.830001 19.630001 c
|
||||||
|
1.690001 19.490002 1.620001 19.326668 1.620001 19.140001 c
|
||||||
|
1.620001 17.700001 l
|
||||||
|
1.620001 17.500002 1.690001 17.330002 1.830001 17.190001 c
|
||||||
|
1.970001 17.049999 2.133334 16.98 2.320001 16.980001 c
|
||||||
|
7.920001 16.980001 l
|
||||||
|
9.106668 16.980001 10.070001 16.546669 10.81 15.680001 c
|
||||||
|
11.55 14.813335 11.92 13.726667 11.92 12.420001 c
|
||||||
|
11.92 11.113335 11.55 10.026668 10.81 9.160001 c
|
||||||
|
10.070001 8.293334 9.106668 7.860001 7.920001 7.860001 c
|
||||||
|
2.320001 7.860001 l
|
||||||
|
2.133334 7.860001 1.970001 7.79 1.830001 7.650001 c
|
||||||
|
1.690001 7.510001 1.620001 7.340001 1.620001 7.14 c
|
||||||
|
1.620001 5.7 l
|
||||||
|
1.620001 5.513333 1.690001 5.35 1.830001 5.21 c
|
||||||
|
1.970001 5.07 2.133334 5 2.320001 5 c
|
||||||
|
7.980001 5 l
|
||||||
|
8.860001 5 9.72 5.173333 10.56 5.52 c
|
||||||
|
h
|
||||||
|
32.208 18.860001 m
|
||||||
|
32.301334 19.073334 32.278 19.290001 32.138 19.51 c
|
||||||
|
31.998001 19.73 31.808001 19.84 31.568001 19.84 c
|
||||||
|
18.248001 19.84 l
|
||||||
|
18.128 19.84 18.014668 19.809999 17.908001 19.75 c
|
||||||
|
17.801334 19.690001 17.721334 19.613333 17.668001 19.52 c
|
||||||
|
17.521336 19.306667 17.501335 19.086668 17.608002 18.860001 c
|
||||||
|
18.228003 17.400002 l
|
||||||
|
18.281336 17.266668 18.368002 17.160002 18.488003 17.080002 c
|
||||||
|
18.608004 17.000002 18.734669 16.960003 18.868002 16.960001 c
|
||||||
|
28.188002 16.960001 l
|
||||||
|
24.908001 9.120001 l
|
||||||
|
22.228001 15.520001 l
|
||||||
|
22.174667 15.653334 22.091333 15.756667 21.978001 15.830001 c
|
||||||
|
21.864668 15.903335 21.734667 15.940002 21.588001 15.940001 c
|
||||||
|
19.908001 15.940001 l
|
||||||
|
19.654669 15.940001 19.454668 15.833334 19.308001 15.620001 c
|
||||||
|
19.254667 15.526668 19.221334 15.420001 19.208 15.300001 c
|
||||||
|
19.194666 15.180001 19.208 15.066669 19.248001 14.960001 c
|
||||||
|
23.308001 5.440001 l
|
||||||
|
23.361334 5.306667 23.444668 5.200001 23.558001 5.12 c
|
||||||
|
23.671333 5.04 23.801334 5 23.948 5.000001 c
|
||||||
|
25.868 5.000001 l
|
||||||
|
26.014666 5.000001 26.144667 5.04 26.257999 5.12 c
|
||||||
|
26.371332 5.2 26.454666 5.306667 26.507999 5.440001 c
|
||||||
|
32.208 18.860001 l
|
||||||
|
h
|
||||||
|
45.535999 12.42 m
|
||||||
|
46.109333 12.78 46.546001 13.22 46.846001 13.74 c
|
||||||
|
47.146 14.259999 47.296001 14.839999 47.296001 15.48 c
|
||||||
|
47.296001 16.986666 46.722668 18.139999 45.576 18.939999 c
|
||||||
|
44.802666 19.499998 43.756001 19.853333 42.436001 19.999998 c
|
||||||
|
42.355999 19.999998 l
|
||||||
|
42.169334 19.999998 42.015999 19.939999 41.896 19.819998 c
|
||||||
|
41.736 19.673332 41.655998 19.499998 41.655998 19.299997 c
|
||||||
|
41.655998 17.859997 l
|
||||||
|
41.655998 17.686663 41.716 17.533331 41.835999 17.399998 c
|
||||||
|
41.955997 17.266665 42.102665 17.186665 42.275997 17.159998 c
|
||||||
|
42.902664 17.09333 43.389328 16.946665 43.735996 16.719997 c
|
||||||
|
44.002663 16.559998 44.182663 16.353331 44.275997 16.099997 c
|
||||||
|
44.32933 15.95333 44.355999 15.766663 44.355999 15.539996 c
|
||||||
|
44.355999 15.39333 44.32933 15.266663 44.275997 15.159996 c
|
||||||
|
44.222664 15.05333 44.12933 14.953329 43.995998 14.859996 c
|
||||||
|
43.609329 14.593329 43.109329 14.366663 42.495998 14.179996 c
|
||||||
|
42.175999 14.079995 l
|
||||||
|
41.509331 13.893329 40.896 13.739995 40.335999 13.619995 c
|
||||||
|
40.216 13.593328 40.022663 13.546661 39.755997 13.479995 c
|
||||||
|
39.535995 13.419994 l
|
||||||
|
39.055996 13.299995 38.549328 13.133327 38.015995 12.919994 c
|
||||||
|
37.229328 12.586661 36.582661 12.166661 36.075996 11.659994 c
|
||||||
|
35.48933 11.073327 35.195995 10.299995 35.195995 9.339994 c
|
||||||
|
35.195995 8.019995 35.702663 6.946661 36.715996 6.119994 c
|
||||||
|
37.409328 5.533328 38.382664 5.159994 39.635994 4.999994 c
|
||||||
|
39.849327 4.973328 40.032661 5.033328 40.185993 5.179994 c
|
||||||
|
40.339325 5.326661 40.415993 5.499994 40.415993 5.699994 c
|
||||||
|
40.415993 7.139994 l
|
||||||
|
40.415993 7.313327 40.362659 7.466661 40.255993 7.599994 c
|
||||||
|
40.149326 7.733328 40.012661 7.809994 39.845993 7.829994 c
|
||||||
|
39.679325 7.849994 39.509327 7.886661 39.335995 7.939994 c
|
||||||
|
38.949326 8.08666 38.675995 8.229994 38.515995 8.369994 c
|
||||||
|
38.355995 8.509995 38.242664 8.653328 38.175995 8.799995 c
|
||||||
|
38.109329 9.026661 38.075996 9.239995 38.075996 9.439995 c
|
||||||
|
38.075996 9.546661 38.135998 9.659995 38.255997 9.779995 c
|
||||||
|
38.442661 9.966662 38.715996 10.133328 39.075996 10.279995 c
|
||||||
|
39.235996 10.346662 39.515999 10.446662 39.915997 10.579995 c
|
||||||
|
42.415997 11.199995 l
|
||||||
|
42.535995 11.239995 l
|
||||||
|
43.162663 11.413328 43.615993 11.546661 43.895996 11.639995 c
|
||||||
|
44.522663 11.853328 45.069328 12.113328 45.535995 12.419994 c
|
||||||
|
45.535999 12.42 l
|
||||||
|
h
|
||||||
|
42.195999 7.9 m
|
||||||
|
42.035999 7.86 41.905998 7.776667 41.806 7.65 c
|
||||||
|
41.706001 7.523334 41.656002 7.373334 41.655998 7.2 c
|
||||||
|
41.655998 5.74 l
|
||||||
|
41.655998 5.526667 41.742664 5.346667 41.915997 5.2 c
|
||||||
|
41.982662 5.133334 42.069328 5.09 42.175995 5.07 c
|
||||||
|
42.282661 5.05 42.38266 5.046667 42.475994 5.06 c
|
||||||
|
43.649326 5.246667 44.602661 5.646667 45.335995 6.26 c
|
||||||
|
46.335995 7.086667 46.869328 8.14 46.935993 9.42 c
|
||||||
|
46.949326 9.606667 46.885994 9.776667 46.745995 9.93 c
|
||||||
|
46.605995 10.083334 46.435997 10.160001 46.235996 10.16 c
|
||||||
|
44.675995 10.16 l
|
||||||
|
44.502663 10.16 44.349331 10.099999 44.215996 9.98 c
|
||||||
|
44.082661 9.86 44.009327 9.713333 43.995995 9.54 c
|
||||||
|
43.942661 9.193334 43.829327 8.906667 43.655994 8.68 c
|
||||||
|
43.375996 8.346667 42.942661 8.093333 42.355995 7.92 c
|
||||||
|
42.315994 7.92 42.289326 7.913333 42.275993 7.9 c
|
||||||
|
42.195992 7.9 l
|
||||||
|
42.195999 7.9 l
|
||||||
|
h
|
||||||
|
39.855999 17.08 m
|
||||||
|
40.015999 17.106667 40.149334 17.186666 40.256001 17.32 c
|
||||||
|
40.362667 17.453333 40.416 17.599998 40.416 17.76 c
|
||||||
|
40.416 19.24 l
|
||||||
|
40.416 19.453333 40.335999 19.633333 40.175999 19.780001 c
|
||||||
|
40.042664 19.886667 39.889332 19.940001 39.716 19.940001 c
|
||||||
|
39.616001 19.940001 l
|
||||||
|
38.375999 19.753334 37.355999 19.346666 36.556 18.720001 c
|
||||||
|
35.515999 17.893335 34.929333 16.740002 34.796001 15.260001 c
|
||||||
|
34.769333 15.046668 34.829334 14.863335 34.976002 14.710001 c
|
||||||
|
35.122669 14.556667 35.296001 14.480001 35.496002 14.480001 c
|
||||||
|
37.076004 14.480001 l
|
||||||
|
37.262669 14.480001 37.422668 14.543335 37.556004 14.670001 c
|
||||||
|
37.689339 14.796667 37.762672 14.953334 37.776005 15.140001 c
|
||||||
|
37.816006 15.806668 38.142673 16.320002 38.756004 16.68 c
|
||||||
|
39.036003 16.84 39.402668 16.973333 39.856003 17.08 c
|
||||||
|
39.855999 17.08 l
|
||||||
|
h
|
||||||
|
60.883999 11.12 m
|
||||||
|
61.084 11.12 61.253998 11.19 61.393997 11.33 c
|
||||||
|
61.533997 11.47 61.603996 11.64 61.603996 11.84 c
|
||||||
|
61.603996 13.32 l
|
||||||
|
61.603996 13.506666 61.533997 13.669999 61.393997 13.81 c
|
||||||
|
61.253998 13.95 61.084 14.02 60.883999 14.02 c
|
||||||
|
54.304001 14.02 l
|
||||||
|
54.304001 19.139999 l
|
||||||
|
54.304001 19.326666 54.237335 19.49 54.104 19.629999 c
|
||||||
|
53.970665 19.769999 53.804001 19.839998 53.604 19.839998 c
|
||||||
|
52.084 19.839998 l
|
||||||
|
51.897335 19.839998 51.734001 19.769999 51.593998 19.629999 c
|
||||||
|
51.453995 19.49 51.383995 19.326666 51.383999 19.139999 c
|
||||||
|
51.383999 11.839999 l
|
||||||
|
51.383999 11.639999 51.453999 11.469999 51.593998 11.329999 c
|
||||||
|
51.733997 11.189999 51.897331 11.119999 52.084 11.119999 c
|
||||||
|
60.883999 11.119999 l
|
||||||
|
60.883999 11.12 l
|
||||||
|
h
|
||||||
|
61.784 5 m
|
||||||
|
61.970665 5 62.133999 5.07 62.274002 5.21 c
|
||||||
|
62.414005 5.35 62.484005 5.513333 62.484001 5.7 c
|
||||||
|
62.484001 7.16 l
|
||||||
|
62.484001 7.36 62.414001 7.53 62.274002 7.67 c
|
||||||
|
62.134003 7.81 61.970669 7.88 61.784 7.88 c
|
||||||
|
52.084 7.88 l
|
||||||
|
51.897335 7.88 51.734001 7.81 51.593998 7.67 c
|
||||||
|
51.453995 7.53 51.383995 7.36 51.383999 7.16 c
|
||||||
|
51.383999 5.7 l
|
||||||
|
51.383999 5.513333 51.453999 5.35 51.593998 5.21 c
|
||||||
|
51.733997 5.07 51.897331 5.000001 52.084 5 c
|
||||||
|
61.784 5 l
|
||||||
|
h
|
||||||
|
79.512001 18.860001 m
|
||||||
|
79.605331 19.073334 79.582001 19.290001 79.442001 19.51 c
|
||||||
|
79.302002 19.73 79.112 19.84 78.872002 19.84 c
|
||||||
|
65.552002 19.84 l
|
||||||
|
65.431999 19.84 65.318672 19.809999 65.212006 19.75 c
|
||||||
|
65.105339 19.690001 65.025345 19.613333 64.972008 19.52 c
|
||||||
|
64.82534 19.306667 64.805344 19.086668 64.91201 18.860001 c
|
||||||
|
65.532013 17.400002 l
|
||||||
|
65.58535 17.266668 65.672012 17.160002 65.792015 17.080002 c
|
||||||
|
65.912018 17.000002 66.038681 16.960003 66.172012 16.960001 c
|
||||||
|
75.492012 16.960001 l
|
||||||
|
72.212013 9.120001 l
|
||||||
|
69.532013 15.520001 l
|
||||||
|
69.478676 15.653334 69.395348 15.756667 69.282013 15.830001 c
|
||||||
|
69.168678 15.903335 69.038681 15.940002 68.892014 15.940001 c
|
||||||
|
67.212013 15.940001 l
|
||||||
|
66.958679 15.940001 66.758682 15.833334 66.612015 15.620001 c
|
||||||
|
66.558678 15.526668 66.525345 15.420001 66.512016 15.300001 c
|
||||||
|
66.498688 15.180001 66.512016 15.066669 66.552017 14.960001 c
|
||||||
|
70.612015 5.440001 l
|
||||||
|
70.665352 5.306667 70.74868 5.200001 70.862015 5.12 c
|
||||||
|
70.975349 5.04 71.105347 5 71.252014 5.000001 c
|
||||||
|
73.172012 5.000001 l
|
||||||
|
73.31868 5.000001 73.448677 5.04 73.562012 5.12 c
|
||||||
|
73.675346 5.2 73.758675 5.306667 73.812012 5.440001 c
|
||||||
|
79.512001 18.860001 l
|
||||||
|
h
|
||||||
|
92 5.52 m
|
||||||
|
92.813332 5.826667 93.546669 6.32 94.199997 7 c
|
||||||
|
95.599998 8.413333 96.299995 10.216666 96.299995 12.41 c
|
||||||
|
96.299995 14.603334 95.599998 16.413334 94.199997 17.84 c
|
||||||
|
93.546661 18.52 92.813332 19.013334 92 19.32 c
|
||||||
|
91.159996 19.666666 90.299995 19.84 89.419998 19.84 c
|
||||||
|
83.759995 19.84 l
|
||||||
|
83.573326 19.84 83.409996 19.77 83.269997 19.630001 c
|
||||||
|
83.129997 19.490002 83.059998 19.326668 83.059998 19.140001 c
|
||||||
|
83.059998 17.700001 l
|
||||||
|
83.059998 17.500002 83.129997 17.330002 83.269997 17.190001 c
|
||||||
|
83.409996 17.049999 83.573326 16.98 83.759995 16.980001 c
|
||||||
|
89.359993 16.980001 l
|
||||||
|
90.546661 16.980001 91.509995 16.546669 92.249992 15.680001 c
|
||||||
|
92.98999 14.813335 93.359993 13.726667 93.359993 12.420001 c
|
||||||
|
93.359993 11.113335 92.98999 10.026668 92.249992 9.160001 c
|
||||||
|
91.509995 8.293334 90.546661 7.860001 89.359993 7.860001 c
|
||||||
|
83.759995 7.860001 l
|
||||||
|
83.573326 7.860001 83.409996 7.79 83.269997 7.650001 c
|
||||||
|
83.129997 7.510001 83.059998 7.340001 83.059998 7.14 c
|
||||||
|
83.059998 5.7 l
|
||||||
|
83.059998 5.513333 83.129997 5.35 83.269997 5.21 c
|
||||||
|
83.409996 5.07 83.573326 5 83.759995 5 c
|
||||||
|
89.419998 5 l
|
||||||
|
90.299995 5 91.159996 5.173333 92 5.52 c
|
||||||
|
h
|
||||||
|
f
|
||||||
|
Q
|
||||||
|
Q
|
||||||
|
Q
|
||||||
|
showpage
|
||||||
|
%%PageTrailer
|
||||||
|
pdfEndPage
|
||||||
|
%%Trailer
|
||||||
|
end
|
||||||
|
%%DocumentSuppliedResources:
|
||||||
|
%%EOF
|
||||||
@ -0,0 +1,920 @@
|
|||||||
|
%!PS-Adobe-3.0 EPSF-3.0
|
||||||
|
%Produced by poppler pdftops version: 22.05.0 (http://poppler.freedesktop.org)
|
||||||
|
%%Creator: Chromium
|
||||||
|
%%LanguageLevel: 3
|
||||||
|
%%DocumentSuppliedResources: (atend)
|
||||||
|
%%BoundingBox: 0 0 2400 1018
|
||||||
|
%%HiResBoundingBox: 0 0 2400 1017.12
|
||||||
|
%%DocumentSuppliedResources: (atend)
|
||||||
|
%%EndComments
|
||||||
|
%%BeginProlog
|
||||||
|
%%BeginResource: procset xpdf 3.00 0
|
||||||
|
%%Copyright: Copyright 1996-2011, 2022 Glyph & Cog, LLC
|
||||||
|
/xpdf 75 dict def xpdf begin
|
||||||
|
% PDF special state
|
||||||
|
/pdfDictSize 15 def
|
||||||
|
/pdfSetup {
|
||||||
|
/setpagedevice where {
|
||||||
|
pop 2 dict begin
|
||||||
|
/Policies 1 dict dup begin /PageSize 6 def end def
|
||||||
|
{ /Duplex true def } if
|
||||||
|
currentdict end setpagedevice
|
||||||
|
} {
|
||||||
|
pop
|
||||||
|
} ifelse
|
||||||
|
} def
|
||||||
|
/pdfSetupPaper {
|
||||||
|
% Change paper size, but only if different from previous paper size otherwise
|
||||||
|
% duplex fails. PLRM specifies a tolerance of 5 pts when matching paper size
|
||||||
|
% so we use the same when checking if the size changes.
|
||||||
|
/setpagedevice where {
|
||||||
|
pop currentpagedevice
|
||||||
|
/PageSize known {
|
||||||
|
2 copy
|
||||||
|
currentpagedevice /PageSize get aload pop
|
||||||
|
exch 4 1 roll
|
||||||
|
sub abs 5 gt
|
||||||
|
3 1 roll
|
||||||
|
sub abs 5 gt
|
||||||
|
or
|
||||||
|
} {
|
||||||
|
true
|
||||||
|
} ifelse
|
||||||
|
{
|
||||||
|
2 array astore
|
||||||
|
2 dict begin
|
||||||
|
/PageSize exch def
|
||||||
|
/ImagingBBox null def
|
||||||
|
currentdict end
|
||||||
|
setpagedevice
|
||||||
|
} {
|
||||||
|
pop pop
|
||||||
|
} ifelse
|
||||||
|
} {
|
||||||
|
pop
|
||||||
|
} ifelse
|
||||||
|
} def
|
||||||
|
/pdfStartPage {
|
||||||
|
pdfDictSize dict begin
|
||||||
|
/pdfFillCS [] def
|
||||||
|
/pdfFillXform {} def
|
||||||
|
/pdfStrokeCS [] def
|
||||||
|
/pdfStrokeXform {} def
|
||||||
|
/pdfFill [0] def
|
||||||
|
/pdfStroke [0] def
|
||||||
|
/pdfFillOP false def
|
||||||
|
/pdfStrokeOP false def
|
||||||
|
/pdfOPM false def
|
||||||
|
/pdfLastFill false def
|
||||||
|
/pdfLastStroke false def
|
||||||
|
/pdfTextMat [1 0 0 1 0 0] def
|
||||||
|
/pdfFontSize 0 def
|
||||||
|
/pdfCharSpacing 0 def
|
||||||
|
/pdfTextRender 0 def
|
||||||
|
/pdfPatternCS false def
|
||||||
|
/pdfTextRise 0 def
|
||||||
|
/pdfWordSpacing 0 def
|
||||||
|
/pdfHorizScaling 1 def
|
||||||
|
/pdfTextClipPath [] def
|
||||||
|
} def
|
||||||
|
/pdfEndPage { end } def
|
||||||
|
% PDF color state
|
||||||
|
/opm { dup /pdfOPM exch def
|
||||||
|
/setoverprintmode where{pop setoverprintmode}{pop}ifelse } def
|
||||||
|
/cs { /pdfFillXform exch def dup /pdfFillCS exch def
|
||||||
|
setcolorspace } def
|
||||||
|
/CS { /pdfStrokeXform exch def dup /pdfStrokeCS exch def
|
||||||
|
setcolorspace } def
|
||||||
|
/sc { pdfLastFill not { pdfFillCS setcolorspace } if
|
||||||
|
dup /pdfFill exch def aload pop pdfFillXform setcolor
|
||||||
|
/pdfLastFill true def /pdfLastStroke false def } def
|
||||||
|
/SC { pdfLastStroke not { pdfStrokeCS setcolorspace } if
|
||||||
|
dup /pdfStroke exch def aload pop pdfStrokeXform setcolor
|
||||||
|
/pdfLastStroke true def /pdfLastFill false def } def
|
||||||
|
/op { /pdfFillOP exch def
|
||||||
|
pdfLastFill { pdfFillOP setoverprint } if } def
|
||||||
|
/OP { /pdfStrokeOP exch def
|
||||||
|
pdfLastStroke { pdfStrokeOP setoverprint } if } def
|
||||||
|
/fCol {
|
||||||
|
pdfLastFill not {
|
||||||
|
pdfFillCS setcolorspace
|
||||||
|
pdfFill aload pop pdfFillXform setcolor
|
||||||
|
pdfFillOP setoverprint
|
||||||
|
/pdfLastFill true def /pdfLastStroke false def
|
||||||
|
} if
|
||||||
|
} def
|
||||||
|
/sCol {
|
||||||
|
pdfLastStroke not {
|
||||||
|
pdfStrokeCS setcolorspace
|
||||||
|
pdfStroke aload pop pdfStrokeXform setcolor
|
||||||
|
pdfStrokeOP setoverprint
|
||||||
|
/pdfLastStroke true def /pdfLastFill false def
|
||||||
|
} if
|
||||||
|
} def
|
||||||
|
% build a font
|
||||||
|
/pdfMakeFont {
|
||||||
|
4 3 roll findfont
|
||||||
|
4 2 roll matrix scale makefont
|
||||||
|
dup length dict begin
|
||||||
|
{ 1 index /FID ne { def } { pop pop } ifelse } forall
|
||||||
|
/Encoding exch def
|
||||||
|
currentdict
|
||||||
|
end
|
||||||
|
definefont pop
|
||||||
|
} def
|
||||||
|
/pdfMakeFont16 {
|
||||||
|
exch findfont
|
||||||
|
dup length dict begin
|
||||||
|
{ 1 index /FID ne { def } { pop pop } ifelse } forall
|
||||||
|
/WMode exch def
|
||||||
|
currentdict
|
||||||
|
end
|
||||||
|
definefont pop
|
||||||
|
} def
|
||||||
|
/pdfMakeFont16L3 {
|
||||||
|
1 index /CIDFont resourcestatus {
|
||||||
|
pop pop 1 index /CIDFont findresource /CIDFontType known
|
||||||
|
} {
|
||||||
|
false
|
||||||
|
} ifelse
|
||||||
|
{
|
||||||
|
0 eq { /Identity-H } { /Identity-V } ifelse
|
||||||
|
exch 1 array astore composefont pop
|
||||||
|
} {
|
||||||
|
pdfMakeFont16
|
||||||
|
} ifelse
|
||||||
|
} def
|
||||||
|
% graphics state operators
|
||||||
|
/q { gsave pdfDictSize dict begin } def
|
||||||
|
/Q {
|
||||||
|
end grestore
|
||||||
|
/pdfLastFill where {
|
||||||
|
pop
|
||||||
|
pdfLastFill {
|
||||||
|
pdfFillOP setoverprint
|
||||||
|
} {
|
||||||
|
pdfStrokeOP setoverprint
|
||||||
|
} ifelse
|
||||||
|
} if
|
||||||
|
/pdfOPM where {
|
||||||
|
pop
|
||||||
|
pdfOPM /setoverprintmode where{pop setoverprintmode}{pop}ifelse
|
||||||
|
} if
|
||||||
|
} def
|
||||||
|
/cm { concat } def
|
||||||
|
/d { setdash } def
|
||||||
|
/i { setflat } def
|
||||||
|
/j { setlinejoin } def
|
||||||
|
/J { setlinecap } def
|
||||||
|
/M { setmiterlimit } def
|
||||||
|
/w { setlinewidth } def
|
||||||
|
% path segment operators
|
||||||
|
/m { moveto } def
|
||||||
|
/l { lineto } def
|
||||||
|
/c { curveto } def
|
||||||
|
/re { 4 2 roll moveto 1 index 0 rlineto 0 exch rlineto
|
||||||
|
neg 0 rlineto closepath } def
|
||||||
|
/h { closepath } def
|
||||||
|
% path painting operators
|
||||||
|
/S { sCol stroke } def
|
||||||
|
/Sf { fCol stroke } def
|
||||||
|
/f { fCol fill } def
|
||||||
|
/f* { fCol eofill } def
|
||||||
|
% clipping operators
|
||||||
|
/W { clip newpath } def
|
||||||
|
/W* { eoclip newpath } def
|
||||||
|
/Ws { strokepath clip newpath } def
|
||||||
|
% text state operators
|
||||||
|
/Tc { /pdfCharSpacing exch def } def
|
||||||
|
/Tf { dup /pdfFontSize exch def
|
||||||
|
dup pdfHorizScaling mul exch matrix scale
|
||||||
|
pdfTextMat matrix concatmatrix dup 4 0 put dup 5 0 put
|
||||||
|
exch findfont exch makefont setfont } def
|
||||||
|
/Tr { /pdfTextRender exch def } def
|
||||||
|
/Tp { /pdfPatternCS exch def } def
|
||||||
|
/Ts { /pdfTextRise exch def } def
|
||||||
|
/Tw { /pdfWordSpacing exch def } def
|
||||||
|
/Tz { /pdfHorizScaling exch def } def
|
||||||
|
% text positioning operators
|
||||||
|
/Td { pdfTextMat transform moveto } def
|
||||||
|
/Tm { /pdfTextMat exch def } def
|
||||||
|
% text string operators
|
||||||
|
/xyshow where {
|
||||||
|
pop
|
||||||
|
/xyshow2 {
|
||||||
|
dup length array
|
||||||
|
0 2 2 index length 1 sub {
|
||||||
|
2 index 1 index 2 copy get 3 1 roll 1 add get
|
||||||
|
pdfTextMat dtransform
|
||||||
|
4 2 roll 2 copy 6 5 roll put 1 add 3 1 roll dup 4 2 roll put
|
||||||
|
} for
|
||||||
|
exch pop
|
||||||
|
xyshow
|
||||||
|
} def
|
||||||
|
}{
|
||||||
|
/xyshow2 {
|
||||||
|
currentfont /FontType get 0 eq {
|
||||||
|
0 2 3 index length 1 sub {
|
||||||
|
currentpoint 4 index 3 index 2 getinterval show moveto
|
||||||
|
2 copy get 2 index 3 2 roll 1 add get
|
||||||
|
pdfTextMat dtransform rmoveto
|
||||||
|
} for
|
||||||
|
} {
|
||||||
|
0 1 3 index length 1 sub {
|
||||||
|
currentpoint 4 index 3 index 1 getinterval show moveto
|
||||||
|
2 copy 2 mul get 2 index 3 2 roll 2 mul 1 add get
|
||||||
|
pdfTextMat dtransform rmoveto
|
||||||
|
} for
|
||||||
|
} ifelse
|
||||||
|
pop pop
|
||||||
|
} def
|
||||||
|
} ifelse
|
||||||
|
/cshow where {
|
||||||
|
pop
|
||||||
|
/xycp {
|
||||||
|
0 3 2 roll
|
||||||
|
{
|
||||||
|
pop pop currentpoint 3 2 roll
|
||||||
|
1 string dup 0 4 3 roll put false charpath moveto
|
||||||
|
2 copy get 2 index 2 index 1 add get
|
||||||
|
pdfTextMat dtransform rmoveto
|
||||||
|
2 add
|
||||||
|
} exch cshow
|
||||||
|
pop pop
|
||||||
|
} def
|
||||||
|
}{
|
||||||
|
/xycp {
|
||||||
|
currentfont /FontType get 0 eq {
|
||||||
|
0 2 3 index length 1 sub {
|
||||||
|
currentpoint 4 index 3 index 2 getinterval false charpath moveto
|
||||||
|
2 copy get 2 index 3 2 roll 1 add get
|
||||||
|
pdfTextMat dtransform rmoveto
|
||||||
|
} for
|
||||||
|
} {
|
||||||
|
0 1 3 index length 1 sub {
|
||||||
|
currentpoint 4 index 3 index 1 getinterval false charpath moveto
|
||||||
|
2 copy 2 mul get 2 index 3 2 roll 2 mul 1 add get
|
||||||
|
pdfTextMat dtransform rmoveto
|
||||||
|
} for
|
||||||
|
} ifelse
|
||||||
|
pop pop
|
||||||
|
} def
|
||||||
|
} ifelse
|
||||||
|
/Tj {
|
||||||
|
fCol
|
||||||
|
0 pdfTextRise pdfTextMat dtransform rmoveto
|
||||||
|
currentpoint 4 2 roll
|
||||||
|
pdfTextRender 1 and 0 eq {
|
||||||
|
2 copy xyshow2
|
||||||
|
} if
|
||||||
|
pdfTextRender 3 and dup 1 eq exch 2 eq or {
|
||||||
|
3 index 3 index moveto
|
||||||
|
2 copy
|
||||||
|
currentfont /FontType get 3 eq { fCol } { sCol } ifelse
|
||||||
|
xycp currentpoint stroke moveto
|
||||||
|
} if
|
||||||
|
pdfTextRender 4 and 0 ne {
|
||||||
|
4 2 roll moveto xycp
|
||||||
|
/pdfTextClipPath [ pdfTextClipPath aload pop
|
||||||
|
{/moveto cvx}
|
||||||
|
{/lineto cvx}
|
||||||
|
{/curveto cvx}
|
||||||
|
{/closepath cvx}
|
||||||
|
pathforall ] def
|
||||||
|
currentpoint newpath moveto
|
||||||
|
} {
|
||||||
|
pop pop pop pop
|
||||||
|
} ifelse
|
||||||
|
0 pdfTextRise neg pdfTextMat dtransform rmoveto
|
||||||
|
} def
|
||||||
|
/TJm { 0.001 mul pdfFontSize mul pdfHorizScaling mul neg 0
|
||||||
|
pdfTextMat dtransform rmoveto } def
|
||||||
|
/TJmV { 0.001 mul pdfFontSize mul neg 0 exch
|
||||||
|
pdfTextMat dtransform rmoveto } def
|
||||||
|
/Tclip { pdfTextClipPath cvx exec clip newpath
|
||||||
|
/pdfTextClipPath [] def } def
|
||||||
|
/Tclip* { pdfTextClipPath cvx exec eoclip newpath
|
||||||
|
/pdfTextClipPath [] def } def
|
||||||
|
% Level 2/3 image operators
|
||||||
|
/pdfImBuf 100 string def
|
||||||
|
/pdfImStr {
|
||||||
|
2 copy exch length lt {
|
||||||
|
2 copy get exch 1 add exch
|
||||||
|
} {
|
||||||
|
()
|
||||||
|
} ifelse
|
||||||
|
} def
|
||||||
|
/skipEOD {
|
||||||
|
{ currentfile pdfImBuf readline
|
||||||
|
not { pop exit } if
|
||||||
|
(%-EOD-) eq { exit } if } loop
|
||||||
|
} def
|
||||||
|
/pdfIm { image skipEOD } def
|
||||||
|
/pdfMask {
|
||||||
|
/ReusableStreamDecode filter
|
||||||
|
skipEOD
|
||||||
|
/maskStream exch def
|
||||||
|
} def
|
||||||
|
/pdfMaskEnd { maskStream closefile } def
|
||||||
|
/pdfMaskInit {
|
||||||
|
/maskArray exch def
|
||||||
|
/maskIdx 0 def
|
||||||
|
} def
|
||||||
|
/pdfMaskSrc {
|
||||||
|
maskIdx maskArray length lt {
|
||||||
|
maskArray maskIdx get
|
||||||
|
/maskIdx maskIdx 1 add def
|
||||||
|
} {
|
||||||
|
()
|
||||||
|
} ifelse
|
||||||
|
} def
|
||||||
|
/pdfImM { fCol imagemask skipEOD } def
|
||||||
|
/pr { 2 index 2 index 3 2 roll putinterval 4 add } def
|
||||||
|
/pdfImClip {
|
||||||
|
gsave
|
||||||
|
0 2 4 index length 1 sub {
|
||||||
|
dup 4 index exch 2 copy
|
||||||
|
get 5 index div put
|
||||||
|
1 add 3 index exch 2 copy
|
||||||
|
get 3 index div put
|
||||||
|
} for
|
||||||
|
pop pop rectclip
|
||||||
|
} def
|
||||||
|
/pdfImClipEnd { grestore } def
|
||||||
|
% shading operators
|
||||||
|
/colordelta {
|
||||||
|
false 0 1 3 index length 1 sub {
|
||||||
|
dup 4 index exch get 3 index 3 2 roll get sub abs 0.004 gt {
|
||||||
|
pop true
|
||||||
|
} if
|
||||||
|
} for
|
||||||
|
exch pop exch pop
|
||||||
|
} def
|
||||||
|
/funcCol { func n array astore } def
|
||||||
|
/funcSH {
|
||||||
|
dup 0 eq {
|
||||||
|
true
|
||||||
|
} {
|
||||||
|
dup 6 eq {
|
||||||
|
false
|
||||||
|
} {
|
||||||
|
4 index 4 index funcCol dup
|
||||||
|
6 index 4 index funcCol dup
|
||||||
|
3 1 roll colordelta 3 1 roll
|
||||||
|
5 index 5 index funcCol dup
|
||||||
|
3 1 roll colordelta 3 1 roll
|
||||||
|
6 index 8 index funcCol dup
|
||||||
|
3 1 roll colordelta 3 1 roll
|
||||||
|
colordelta or or or
|
||||||
|
} ifelse
|
||||||
|
} ifelse
|
||||||
|
{
|
||||||
|
1 add
|
||||||
|
4 index 3 index add 0.5 mul exch 4 index 3 index add 0.5 mul exch
|
||||||
|
6 index 6 index 4 index 4 index 4 index funcSH
|
||||||
|
2 index 6 index 6 index 4 index 4 index funcSH
|
||||||
|
6 index 2 index 4 index 6 index 4 index funcSH
|
||||||
|
5 3 roll 3 2 roll funcSH pop pop
|
||||||
|
} {
|
||||||
|
pop 3 index 2 index add 0.5 mul 3 index 2 index add 0.5 mul
|
||||||
|
funcCol sc
|
||||||
|
dup 4 index exch mat transform m
|
||||||
|
3 index 3 index mat transform l
|
||||||
|
1 index 3 index mat transform l
|
||||||
|
mat transform l pop pop h f*
|
||||||
|
} ifelse
|
||||||
|
} def
|
||||||
|
/axialCol {
|
||||||
|
dup 0 lt {
|
||||||
|
pop t0
|
||||||
|
} {
|
||||||
|
dup 1 gt {
|
||||||
|
pop t1
|
||||||
|
} {
|
||||||
|
dt mul t0 add
|
||||||
|
} ifelse
|
||||||
|
} ifelse
|
||||||
|
func n array astore
|
||||||
|
} def
|
||||||
|
/axialSH {
|
||||||
|
dup 0 eq {
|
||||||
|
true
|
||||||
|
} {
|
||||||
|
dup 8 eq {
|
||||||
|
false
|
||||||
|
} {
|
||||||
|
2 index axialCol 2 index axialCol colordelta
|
||||||
|
} ifelse
|
||||||
|
} ifelse
|
||||||
|
{
|
||||||
|
1 add 3 1 roll 2 copy add 0.5 mul
|
||||||
|
dup 4 3 roll exch 4 index axialSH
|
||||||
|
exch 3 2 roll axialSH
|
||||||
|
} {
|
||||||
|
pop 2 copy add 0.5 mul
|
||||||
|
axialCol sc
|
||||||
|
exch dup dx mul x0 add exch dy mul y0 add
|
||||||
|
3 2 roll dup dx mul x0 add exch dy mul y0 add
|
||||||
|
dx abs dy abs ge {
|
||||||
|
2 copy yMin sub dy mul dx div add yMin m
|
||||||
|
yMax sub dy mul dx div add yMax l
|
||||||
|
2 copy yMax sub dy mul dx div add yMax l
|
||||||
|
yMin sub dy mul dx div add yMin l
|
||||||
|
h f*
|
||||||
|
} {
|
||||||
|
exch 2 copy xMin sub dx mul dy div add xMin exch m
|
||||||
|
xMax sub dx mul dy div add xMax exch l
|
||||||
|
exch 2 copy xMax sub dx mul dy div add xMax exch l
|
||||||
|
xMin sub dx mul dy div add xMin exch l
|
||||||
|
h f*
|
||||||
|
} ifelse
|
||||||
|
} ifelse
|
||||||
|
} def
|
||||||
|
/radialCol {
|
||||||
|
dup t0 lt {
|
||||||
|
pop t0
|
||||||
|
} {
|
||||||
|
dup t1 gt {
|
||||||
|
pop t1
|
||||||
|
} if
|
||||||
|
} ifelse
|
||||||
|
func n array astore
|
||||||
|
} def
|
||||||
|
/radialSH {
|
||||||
|
dup 0 eq {
|
||||||
|
true
|
||||||
|
} {
|
||||||
|
dup 8 eq {
|
||||||
|
false
|
||||||
|
} {
|
||||||
|
2 index dt mul t0 add radialCol
|
||||||
|
2 index dt mul t0 add radialCol colordelta
|
||||||
|
} ifelse
|
||||||
|
} ifelse
|
||||||
|
{
|
||||||
|
1 add 3 1 roll 2 copy add 0.5 mul
|
||||||
|
dup 4 3 roll exch 4 index radialSH
|
||||||
|
exch 3 2 roll radialSH
|
||||||
|
} {
|
||||||
|
pop 2 copy add 0.5 mul dt mul t0 add
|
||||||
|
radialCol sc
|
||||||
|
encl {
|
||||||
|
exch dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add
|
||||||
|
0 360 arc h
|
||||||
|
dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add
|
||||||
|
360 0 arcn h f
|
||||||
|
} {
|
||||||
|
2 copy
|
||||||
|
dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add
|
||||||
|
a1 a2 arcn
|
||||||
|
dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add
|
||||||
|
a2 a1 arcn h
|
||||||
|
dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add
|
||||||
|
a1 a2 arc
|
||||||
|
dup dx mul x0 add exch dup dy mul y0 add exch dr mul r0 add
|
||||||
|
a2 a1 arc h f
|
||||||
|
} ifelse
|
||||||
|
} ifelse
|
||||||
|
} def
|
||||||
|
end
|
||||||
|
%%EndResource
|
||||||
|
/CIDInit /ProcSet findresource begin
|
||||||
|
10 dict begin
|
||||||
|
begincmap
|
||||||
|
/CMapType 1 def
|
||||||
|
/CMapName /Identity-H def
|
||||||
|
/CIDSystemInfo 3 dict dup begin
|
||||||
|
/Registry (Adobe) def
|
||||||
|
/Ordering (Identity) def
|
||||||
|
/Supplement 0 def
|
||||||
|
end def
|
||||||
|
1 begincodespacerange
|
||||||
|
<0000> <ffff>
|
||||||
|
endcodespacerange
|
||||||
|
0 usefont
|
||||||
|
1 begincidrange
|
||||||
|
<0000> <ffff> 0
|
||||||
|
endcidrange
|
||||||
|
endcmap
|
||||||
|
currentdict CMapName exch /CMap defineresource pop
|
||||||
|
end
|
||||||
|
10 dict begin
|
||||||
|
begincmap
|
||||||
|
/CMapType 1 def
|
||||||
|
/CMapName /Identity-V def
|
||||||
|
/CIDSystemInfo 3 dict dup begin
|
||||||
|
/Registry (Adobe) def
|
||||||
|
/Ordering (Identity) def
|
||||||
|
/Supplement 0 def
|
||||||
|
end def
|
||||||
|
/WMode 1 def
|
||||||
|
1 begincodespacerange
|
||||||
|
<0000> <ffff>
|
||||||
|
endcodespacerange
|
||||||
|
0 usefont
|
||||||
|
1 begincidrange
|
||||||
|
<0000> <ffff> 0
|
||||||
|
endcidrange
|
||||||
|
endcmap
|
||||||
|
currentdict CMapName exch /CMap defineresource pop
|
||||||
|
end
|
||||||
|
end
|
||||||
|
%%EndProlog
|
||||||
|
%%BeginSetup
|
||||||
|
xpdf begin
|
||||||
|
%%EndSetup
|
||||||
|
pdfStartPage
|
||||||
|
%%EndPageSetup
|
||||||
|
[] 0 d
|
||||||
|
1 i
|
||||||
|
0 j
|
||||||
|
0 J
|
||||||
|
10 M
|
||||||
|
1 w
|
||||||
|
/DeviceGray {} cs
|
||||||
|
[0] sc
|
||||||
|
/DeviceGray {} CS
|
||||||
|
[0] SC
|
||||||
|
false op
|
||||||
|
false OP
|
||||||
|
{} settransfer
|
||||||
|
0 0 2400 1017.12 re
|
||||||
|
W
|
||||||
|
q
|
||||||
|
[0.24 0 0 -0.24 0 1017.12] cm
|
||||||
|
q
|
||||||
|
0 0 10000 4234.375 re
|
||||||
|
W*
|
||||||
|
q
|
||||||
|
[48.783241 0 0 48.766369 2560.8376 -1369.56274] cm
|
||||||
|
/DeviceRGB {} CS
|
||||||
|
[1 1 1] SC
|
||||||
|
/DeviceRGB {} cs
|
||||||
|
[1 1 1] sc
|
||||||
|
29.399 57.112 m
|
||||||
|
30.014 57.419998 30.476 57.958 30.476 58.494999 c
|
||||||
|
30.476 59.534 29.438 60.455997 28.476999 59.957001 c
|
||||||
|
13.254 52.076 l
|
||||||
|
12.408 51.577 11.678 51.268002 11.678 50.192001 c
|
||||||
|
11.678 49.077 12.37 48.807003 13.254 48.27 c
|
||||||
|
28.476999 40.389 l
|
||||||
|
29.514999 40.042999 30.476 40.813 30.476 41.851002 c
|
||||||
|
30.476 42.426003 30.014999 42.965 29.399 43.274002 c
|
||||||
|
15.638 50.192001 l
|
||||||
|
29.399 57.112 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
29.033001 60.209 m
|
||||||
|
28.825001 60.209 28.620001 60.157001 28.425001 60.056999 c
|
||||||
|
13.202002 52.175999 l
|
||||||
|
13.116001 52.125999 13.037002 52.080997 12.960002 52.035 c
|
||||||
|
12.212002 51.604 11.565002 51.230999 11.565002 50.192001 c
|
||||||
|
11.565002 49.135002 12.159002 48.786003 12.911002 48.343002 c
|
||||||
|
13.004003 48.289001 13.098002 48.233002 13.195003 48.174004 c
|
||||||
|
28.424004 40.289001 l
|
||||||
|
28.619003 40.223 28.801004 40.192001 28.982004 40.192001 c
|
||||||
|
29.882004 40.192001 30.588005 40.920002 30.588005 41.850002 c
|
||||||
|
30.588005 42.437004 30.151005 43.022003 29.449005 43.372002 c
|
||||||
|
15.887005 50.190002 l
|
||||||
|
29.449005 57.008003 l
|
||||||
|
30.140005 57.354004 30.588005 57.938004 30.588005 58.492004 c
|
||||||
|
30.587999 59.407001 29.861 60.209 29.033001 60.209 c
|
||||||
|
h
|
||||||
|
28.982 40.418999 m
|
||||||
|
28.826 40.418999 28.668001 40.445 28.512001 40.496998 c
|
||||||
|
13.306 48.369999 l
|
||||||
|
13.215 48.426998 13.118 48.482998 13.025 48.536999 c
|
||||||
|
12.282 48.975998 11.790999 49.264999 11.790999 50.191998 c
|
||||||
|
11.790999 51.101997 12.327999 51.410999 13.072 51.839996 c
|
||||||
|
13.151 51.884995 13.23 51.929996 13.311 51.978996 c
|
||||||
|
28.528 59.855995 l
|
||||||
|
28.690001 59.939995 28.860001 59.982994 29.032 59.982994 c
|
||||||
|
29.729 59.982994 30.362 59.273994 30.362 58.494995 c
|
||||||
|
30.362 58.029995 29.955 57.514996 29.348 57.211994 c
|
||||||
|
15.386 50.191994 l
|
||||||
|
29.348 43.171993 l
|
||||||
|
29.963999 42.863995 30.362 42.343994 30.362 41.850994 c
|
||||||
|
30.363001 41.048 29.756001 40.418999 28.982 40.418999 c
|
||||||
|
h
|
||||||
|
f
|
||||||
|
46.384998 64.416 m
|
||||||
|
46.153999 65.107002 45.462997 65.493004 44.771 65.376999 c
|
||||||
|
44.001999 65.223999 43.501999 64.491997 43.617001 63.684998 c
|
||||||
|
43.617001 63.608997 43.656002 63.493996 43.694 63.377998 c
|
||||||
|
53.574001 35.546997 l
|
||||||
|
53.804001 34.854996 54.496002 34.508999 55.188 34.623997 c
|
||||||
|
55.917999 34.777996 56.457001 35.508995 56.341 36.275997 c
|
||||||
|
56.341 36.353996 56.301998 36.468998 56.264 36.545998 c
|
||||||
|
46.384998 64.416 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
45.015999 65.511002 m
|
||||||
|
44.927998 65.511002 44.839001 65.502998 44.752998 65.488998 c
|
||||||
|
43.915997 65.321999 43.382 64.540001 43.505997 63.669998 c
|
||||||
|
43.504997 63.593998 43.543995 63.476997 43.584995 63.351997 c
|
||||||
|
53.467995 35.509998 l
|
||||||
|
53.674995 34.891998 54.245995 34.489998 54.924995 34.489998 c
|
||||||
|
55.018997 34.489998 55.112995 34.498997 55.205994 34.512997 c
|
||||||
|
56.017994 34.684998 56.574993 35.482998 56.452995 36.293995 c
|
||||||
|
56.453995 36.379993 56.405994 36.513996 56.365993 36.595993 c
|
||||||
|
46.490993 64.451996 l
|
||||||
|
46.280998 65.084 45.688 65.511002 45.015999 65.511002 c
|
||||||
|
h
|
||||||
|
54.924999 34.715 m
|
||||||
|
54.344997 34.715 53.856998 35.055 53.681 35.582001 c
|
||||||
|
43.800999 63.415001 l
|
||||||
|
43.765999 63.519001 43.730999 63.628002 43.730999 63.685001 c
|
||||||
|
43.622997 64.452003 44.079998 65.124001 44.792999 65.266998 c
|
||||||
|
44.863998 65.278999 44.939999 65.284996 45.015999 65.284996 c
|
||||||
|
45.591 65.284996 46.098999 64.921997 46.278999 64.380997 c
|
||||||
|
56.16 36.508995 l
|
||||||
|
56.201 36.423996 56.23 36.326996 56.23 36.277996 c
|
||||||
|
56.334999 35.565994 55.856998 34.881996 55.166 34.734997 c
|
||||||
|
55.089001 34.722 55.007 34.715 54.924999 34.715 c
|
||||||
|
h
|
||||||
|
f
|
||||||
|
84.362 50.192001 m
|
||||||
|
70.599998 43.273998 l
|
||||||
|
69.986 42.964996 69.525002 42.425999 69.525002 41.850998 c
|
||||||
|
69.525002 40.813 70.562004 39.888996 71.523003 40.388996 c
|
||||||
|
86.746002 48.269997 l
|
||||||
|
87.631004 48.806995 88.321999 49.076996 88.321999 50.191998 c
|
||||||
|
88.321999 51.267998 87.591995 51.576996 86.746002 52.075996 c
|
||||||
|
71.523003 59.956997 l
|
||||||
|
70.562004 60.455997 69.525002 59.533997 69.525002 58.494995 c
|
||||||
|
69.525002 57.957996 69.986 57.419994 70.599998 57.111996 c
|
||||||
|
84.362 50.192001 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
70.967003 60.209 m
|
||||||
|
70.139 60.209 69.411003 59.407001 69.411003 58.494999 c
|
||||||
|
69.411003 57.939999 69.858002 57.355999 70.550003 57.010998 c
|
||||||
|
84.112 50.192997 l
|
||||||
|
70.550003 43.374001 l
|
||||||
|
69.848 43.021999 69.411003 42.438 69.411003 41.852001 c
|
||||||
|
69.411003 40.939003 70.139 40.138 70.967003 40.138 c
|
||||||
|
71.176003 40.138 71.380005 40.188999 71.575005 40.290001 c
|
||||||
|
86.798004 48.171001 l
|
||||||
|
86.902 48.233002 86.996002 48.290001 87.088005 48.344002 c
|
||||||
|
87.841003 48.786003 88.435005 49.136002 88.435005 50.193001 c
|
||||||
|
88.435005 51.232002 87.789001 51.605 87.040009 52.035999 c
|
||||||
|
86.962006 52.082001 86.883011 52.126999 86.803009 52.174 c
|
||||||
|
71.575012 60.057999 l
|
||||||
|
71.379997 60.157001 71.176003 60.209 70.967003 60.209 c
|
||||||
|
h
|
||||||
|
70.967003 40.362999 m
|
||||||
|
70.271004 40.362999 69.637001 41.071999 69.637001 41.850998 c
|
||||||
|
69.637001 42.343998 70.034004 42.863998 70.651001 43.171997 c
|
||||||
|
84.612999 50.191998 l
|
||||||
|
70.651001 57.211998 l
|
||||||
|
70.044998 57.516998 69.637001 58.031998 69.637001 58.494999 c
|
||||||
|
69.637001 59.273998 70.271004 59.982998 70.967003 59.982998 c
|
||||||
|
71.139 59.982998 71.309006 59.939999 71.471001 59.855999 c
|
||||||
|
86.694 51.975998 l
|
||||||
|
86.768997 51.929996 86.848999 51.884998 86.927002 51.839996 c
|
||||||
|
87.671005 51.409996 88.209 51.101997 88.209 50.191998 c
|
||||||
|
88.209 49.263996 87.718002 48.975998 86.973999 48.536999 c
|
||||||
|
86.880997 48.482998 86.785995 48.426998 86.686996 48.367001 c
|
||||||
|
71.470993 40.491001 l
|
||||||
|
71.308998 40.404999 71.139 40.362999 70.967003 40.362999 c
|
||||||
|
h
|
||||||
|
f
|
||||||
|
Q
|
||||||
|
q
|
||||||
|
[99.016907 0 0 98.982658 152.13266 1942.3326] cm
|
||||||
|
/DeviceRGB {} CS
|
||||||
|
[1 1 1] SC
|
||||||
|
/DeviceRGB {} cs
|
||||||
|
[1 1 1] sc
|
||||||
|
10.56 5.52 m
|
||||||
|
11.373334 5.826667 12.106668 6.32 12.76 7 c
|
||||||
|
14.160001 8.413333 14.860001 10.216666 14.860001 12.41 c
|
||||||
|
14.860001 14.603334 14.160001 16.413334 12.76 17.84 c
|
||||||
|
12.106668 18.52 11.373334 19.013334 10.56 19.32 c
|
||||||
|
9.72 19.666666 8.860001 19.84 7.980001 19.84 c
|
||||||
|
2.320001 19.84 l
|
||||||
|
2.133334 19.84 1.970001 19.77 1.830001 19.630001 c
|
||||||
|
1.690001 19.490002 1.620001 19.326668 1.620001 19.140001 c
|
||||||
|
1.620001 17.700001 l
|
||||||
|
1.620001 17.500002 1.690001 17.330002 1.830001 17.190001 c
|
||||||
|
1.970001 17.049999 2.133334 16.98 2.320001 16.980001 c
|
||||||
|
7.920001 16.980001 l
|
||||||
|
9.106668 16.980001 10.070001 16.546669 10.81 15.680001 c
|
||||||
|
11.55 14.813335 11.92 13.726667 11.92 12.420001 c
|
||||||
|
11.92 11.113335 11.55 10.026668 10.81 9.160001 c
|
||||||
|
10.070001 8.293334 9.106668 7.860001 7.920001 7.860001 c
|
||||||
|
2.320001 7.860001 l
|
||||||
|
2.133334 7.860001 1.970001 7.79 1.830001 7.650001 c
|
||||||
|
1.690001 7.510001 1.620001 7.340001 1.620001 7.14 c
|
||||||
|
1.620001 5.7 l
|
||||||
|
1.620001 5.513333 1.690001 5.35 1.830001 5.21 c
|
||||||
|
1.970001 5.07 2.133334 5 2.320001 5 c
|
||||||
|
7.980001 5 l
|
||||||
|
8.860001 5 9.72 5.173333 10.56 5.52 c
|
||||||
|
h
|
||||||
|
32.208 18.860001 m
|
||||||
|
32.301334 19.073334 32.278 19.290001 32.138 19.51 c
|
||||||
|
31.998001 19.73 31.808001 19.84 31.568001 19.84 c
|
||||||
|
18.248001 19.84 l
|
||||||
|
18.128 19.84 18.014668 19.809999 17.908001 19.75 c
|
||||||
|
17.801334 19.690001 17.721334 19.613333 17.668001 19.52 c
|
||||||
|
17.521336 19.306667 17.501335 19.086668 17.608002 18.860001 c
|
||||||
|
18.228003 17.400002 l
|
||||||
|
18.281336 17.266668 18.368002 17.160002 18.488003 17.080002 c
|
||||||
|
18.608004 17.000002 18.734669 16.960003 18.868002 16.960001 c
|
||||||
|
28.188002 16.960001 l
|
||||||
|
24.908001 9.120001 l
|
||||||
|
22.228001 15.520001 l
|
||||||
|
22.174667 15.653334 22.091333 15.756667 21.978001 15.830001 c
|
||||||
|
21.864668 15.903335 21.734667 15.940002 21.588001 15.940001 c
|
||||||
|
19.908001 15.940001 l
|
||||||
|
19.654669 15.940001 19.454668 15.833334 19.308001 15.620001 c
|
||||||
|
19.254667 15.526668 19.221334 15.420001 19.208 15.300001 c
|
||||||
|
19.194666 15.180001 19.208 15.066669 19.248001 14.960001 c
|
||||||
|
23.308001 5.440001 l
|
||||||
|
23.361334 5.306667 23.444668 5.200001 23.558001 5.12 c
|
||||||
|
23.671333 5.04 23.801334 5 23.948 5.000001 c
|
||||||
|
25.868 5.000001 l
|
||||||
|
26.014666 5.000001 26.144667 5.04 26.257999 5.12 c
|
||||||
|
26.371332 5.2 26.454666 5.306667 26.507999 5.440001 c
|
||||||
|
32.208 18.860001 l
|
||||||
|
h
|
||||||
|
45.535999 12.42 m
|
||||||
|
46.109333 12.78 46.546001 13.22 46.846001 13.74 c
|
||||||
|
47.146 14.259999 47.296001 14.839999 47.296001 15.48 c
|
||||||
|
47.296001 16.986666 46.722668 18.139999 45.576 18.939999 c
|
||||||
|
44.802666 19.499998 43.756001 19.853333 42.436001 19.999998 c
|
||||||
|
42.355999 19.999998 l
|
||||||
|
42.169334 19.999998 42.015999 19.939999 41.896 19.819998 c
|
||||||
|
41.736 19.673332 41.655998 19.499998 41.655998 19.299997 c
|
||||||
|
41.655998 17.859997 l
|
||||||
|
41.655998 17.686663 41.716 17.533331 41.835999 17.399998 c
|
||||||
|
41.955997 17.266665 42.102665 17.186665 42.275997 17.159998 c
|
||||||
|
42.902664 17.09333 43.389328 16.946665 43.735996 16.719997 c
|
||||||
|
44.002663 16.559998 44.182663 16.353331 44.275997 16.099997 c
|
||||||
|
44.32933 15.95333 44.355999 15.766663 44.355999 15.539996 c
|
||||||
|
44.355999 15.39333 44.32933 15.266663 44.275997 15.159996 c
|
||||||
|
44.222664 15.05333 44.12933 14.953329 43.995998 14.859996 c
|
||||||
|
43.609329 14.593329 43.109329 14.366663 42.495998 14.179996 c
|
||||||
|
42.175999 14.079995 l
|
||||||
|
41.509331 13.893329 40.896 13.739995 40.335999 13.619995 c
|
||||||
|
40.216 13.593328 40.022663 13.546661 39.755997 13.479995 c
|
||||||
|
39.535995 13.419994 l
|
||||||
|
39.055996 13.299995 38.549328 13.133327 38.015995 12.919994 c
|
||||||
|
37.229328 12.586661 36.582661 12.166661 36.075996 11.659994 c
|
||||||
|
35.48933 11.073327 35.195995 10.299995 35.195995 9.339994 c
|
||||||
|
35.195995 8.019995 35.702663 6.946661 36.715996 6.119994 c
|
||||||
|
37.409328 5.533328 38.382664 5.159994 39.635994 4.999994 c
|
||||||
|
39.849327 4.973328 40.032661 5.033328 40.185993 5.179994 c
|
||||||
|
40.339325 5.326661 40.415993 5.499994 40.415993 5.699994 c
|
||||||
|
40.415993 7.139994 l
|
||||||
|
40.415993 7.313327 40.362659 7.466661 40.255993 7.599994 c
|
||||||
|
40.149326 7.733328 40.012661 7.809994 39.845993 7.829994 c
|
||||||
|
39.679325 7.849994 39.509327 7.886661 39.335995 7.939994 c
|
||||||
|
38.949326 8.08666 38.675995 8.229994 38.515995 8.369994 c
|
||||||
|
38.355995 8.509995 38.242664 8.653328 38.175995 8.799995 c
|
||||||
|
38.109329 9.026661 38.075996 9.239995 38.075996 9.439995 c
|
||||||
|
38.075996 9.546661 38.135998 9.659995 38.255997 9.779995 c
|
||||||
|
38.442661 9.966662 38.715996 10.133328 39.075996 10.279995 c
|
||||||
|
39.235996 10.346662 39.515999 10.446662 39.915997 10.579995 c
|
||||||
|
42.415997 11.199995 l
|
||||||
|
42.535995 11.239995 l
|
||||||
|
43.162663 11.413328 43.615993 11.546661 43.895996 11.639995 c
|
||||||
|
44.522663 11.853328 45.069328 12.113328 45.535995 12.419994 c
|
||||||
|
45.535999 12.42 l
|
||||||
|
h
|
||||||
|
42.195999 7.9 m
|
||||||
|
42.035999 7.86 41.905998 7.776667 41.806 7.65 c
|
||||||
|
41.706001 7.523334 41.656002 7.373334 41.655998 7.2 c
|
||||||
|
41.655998 5.74 l
|
||||||
|
41.655998 5.526667 41.742664 5.346667 41.915997 5.2 c
|
||||||
|
41.982662 5.133334 42.069328 5.09 42.175995 5.07 c
|
||||||
|
42.282661 5.05 42.38266 5.046667 42.475994 5.06 c
|
||||||
|
43.649326 5.246667 44.602661 5.646667 45.335995 6.26 c
|
||||||
|
46.335995 7.086667 46.869328 8.14 46.935993 9.42 c
|
||||||
|
46.949326 9.606667 46.885994 9.776667 46.745995 9.93 c
|
||||||
|
46.605995 10.083334 46.435997 10.160001 46.235996 10.16 c
|
||||||
|
44.675995 10.16 l
|
||||||
|
44.502663 10.16 44.349331 10.099999 44.215996 9.98 c
|
||||||
|
44.082661 9.86 44.009327 9.713333 43.995995 9.54 c
|
||||||
|
43.942661 9.193334 43.829327 8.906667 43.655994 8.68 c
|
||||||
|
43.375996 8.346667 42.942661 8.093333 42.355995 7.92 c
|
||||||
|
42.315994 7.92 42.289326 7.913333 42.275993 7.9 c
|
||||||
|
42.195992 7.9 l
|
||||||
|
42.195999 7.9 l
|
||||||
|
h
|
||||||
|
39.855999 17.08 m
|
||||||
|
40.015999 17.106667 40.149334 17.186666 40.256001 17.32 c
|
||||||
|
40.362667 17.453333 40.416 17.599998 40.416 17.76 c
|
||||||
|
40.416 19.24 l
|
||||||
|
40.416 19.453333 40.335999 19.633333 40.175999 19.780001 c
|
||||||
|
40.042664 19.886667 39.889332 19.940001 39.716 19.940001 c
|
||||||
|
39.616001 19.940001 l
|
||||||
|
38.375999 19.753334 37.355999 19.346666 36.556 18.720001 c
|
||||||
|
35.515999 17.893335 34.929333 16.740002 34.796001 15.260001 c
|
||||||
|
34.769333 15.046668 34.829334 14.863335 34.976002 14.710001 c
|
||||||
|
35.122669 14.556667 35.296001 14.480001 35.496002 14.480001 c
|
||||||
|
37.076004 14.480001 l
|
||||||
|
37.262669 14.480001 37.422668 14.543335 37.556004 14.670001 c
|
||||||
|
37.689339 14.796667 37.762672 14.953334 37.776005 15.140001 c
|
||||||
|
37.816006 15.806668 38.142673 16.320002 38.756004 16.68 c
|
||||||
|
39.036003 16.84 39.402668 16.973333 39.856003 17.08 c
|
||||||
|
39.855999 17.08 l
|
||||||
|
h
|
||||||
|
60.883999 11.12 m
|
||||||
|
61.084 11.12 61.253998 11.19 61.393997 11.33 c
|
||||||
|
61.533997 11.47 61.603996 11.64 61.603996 11.84 c
|
||||||
|
61.603996 13.32 l
|
||||||
|
61.603996 13.506666 61.533997 13.669999 61.393997 13.81 c
|
||||||
|
61.253998 13.95 61.084 14.02 60.883999 14.02 c
|
||||||
|
54.304001 14.02 l
|
||||||
|
54.304001 19.139999 l
|
||||||
|
54.304001 19.326666 54.237335 19.49 54.104 19.629999 c
|
||||||
|
53.970665 19.769999 53.804001 19.839998 53.604 19.839998 c
|
||||||
|
52.084 19.839998 l
|
||||||
|
51.897335 19.839998 51.734001 19.769999 51.593998 19.629999 c
|
||||||
|
51.453995 19.49 51.383995 19.326666 51.383999 19.139999 c
|
||||||
|
51.383999 11.839999 l
|
||||||
|
51.383999 11.639999 51.453999 11.469999 51.593998 11.329999 c
|
||||||
|
51.733997 11.189999 51.897331 11.119999 52.084 11.119999 c
|
||||||
|
60.883999 11.119999 l
|
||||||
|
60.883999 11.12 l
|
||||||
|
h
|
||||||
|
61.784 5 m
|
||||||
|
61.970665 5 62.133999 5.07 62.274002 5.21 c
|
||||||
|
62.414005 5.35 62.484005 5.513333 62.484001 5.7 c
|
||||||
|
62.484001 7.16 l
|
||||||
|
62.484001 7.36 62.414001 7.53 62.274002 7.67 c
|
||||||
|
62.134003 7.81 61.970669 7.88 61.784 7.88 c
|
||||||
|
52.084 7.88 l
|
||||||
|
51.897335 7.88 51.734001 7.81 51.593998 7.67 c
|
||||||
|
51.453995 7.53 51.383995 7.36 51.383999 7.16 c
|
||||||
|
51.383999 5.7 l
|
||||||
|
51.383999 5.513333 51.453999 5.35 51.593998 5.21 c
|
||||||
|
51.733997 5.07 51.897331 5.000001 52.084 5 c
|
||||||
|
61.784 5 l
|
||||||
|
h
|
||||||
|
79.512001 18.860001 m
|
||||||
|
79.605331 19.073334 79.582001 19.290001 79.442001 19.51 c
|
||||||
|
79.302002 19.73 79.112 19.84 78.872002 19.84 c
|
||||||
|
65.552002 19.84 l
|
||||||
|
65.431999 19.84 65.318672 19.809999 65.212006 19.75 c
|
||||||
|
65.105339 19.690001 65.025345 19.613333 64.972008 19.52 c
|
||||||
|
64.82534 19.306667 64.805344 19.086668 64.91201 18.860001 c
|
||||||
|
65.532013 17.400002 l
|
||||||
|
65.58535 17.266668 65.672012 17.160002 65.792015 17.080002 c
|
||||||
|
65.912018 17.000002 66.038681 16.960003 66.172012 16.960001 c
|
||||||
|
75.492012 16.960001 l
|
||||||
|
72.212013 9.120001 l
|
||||||
|
69.532013 15.520001 l
|
||||||
|
69.478676 15.653334 69.395348 15.756667 69.282013 15.830001 c
|
||||||
|
69.168678 15.903335 69.038681 15.940002 68.892014 15.940001 c
|
||||||
|
67.212013 15.940001 l
|
||||||
|
66.958679 15.940001 66.758682 15.833334 66.612015 15.620001 c
|
||||||
|
66.558678 15.526668 66.525345 15.420001 66.512016 15.300001 c
|
||||||
|
66.498688 15.180001 66.512016 15.066669 66.552017 14.960001 c
|
||||||
|
70.612015 5.440001 l
|
||||||
|
70.665352 5.306667 70.74868 5.200001 70.862015 5.12 c
|
||||||
|
70.975349 5.04 71.105347 5 71.252014 5.000001 c
|
||||||
|
73.172012 5.000001 l
|
||||||
|
73.31868 5.000001 73.448677 5.04 73.562012 5.12 c
|
||||||
|
73.675346 5.2 73.758675 5.306667 73.812012 5.440001 c
|
||||||
|
79.512001 18.860001 l
|
||||||
|
h
|
||||||
|
92 5.52 m
|
||||||
|
92.813332 5.826667 93.546669 6.32 94.199997 7 c
|
||||||
|
95.599998 8.413333 96.299995 10.216666 96.299995 12.41 c
|
||||||
|
96.299995 14.603334 95.599998 16.413334 94.199997 17.84 c
|
||||||
|
93.546661 18.52 92.813332 19.013334 92 19.32 c
|
||||||
|
91.159996 19.666666 90.299995 19.84 89.419998 19.84 c
|
||||||
|
83.759995 19.84 l
|
||||||
|
83.573326 19.84 83.409996 19.77 83.269997 19.630001 c
|
||||||
|
83.129997 19.490002 83.059998 19.326668 83.059998 19.140001 c
|
||||||
|
83.059998 17.700001 l
|
||||||
|
83.059998 17.500002 83.129997 17.330002 83.269997 17.190001 c
|
||||||
|
83.409996 17.049999 83.573326 16.98 83.759995 16.980001 c
|
||||||
|
89.359993 16.980001 l
|
||||||
|
90.546661 16.980001 91.509995 16.546669 92.249992 15.680001 c
|
||||||
|
92.98999 14.813335 93.359993 13.726667 93.359993 12.420001 c
|
||||||
|
93.359993 11.113335 92.98999 10.026668 92.249992 9.160001 c
|
||||||
|
91.509995 8.293334 90.546661 7.860001 89.359993 7.860001 c
|
||||||
|
83.759995 7.860001 l
|
||||||
|
83.573326 7.860001 83.409996 7.79 83.269997 7.650001 c
|
||||||
|
83.129997 7.510001 83.059998 7.340001 83.059998 7.14 c
|
||||||
|
83.059998 5.7 l
|
||||||
|
83.059998 5.513333 83.129997 5.35 83.269997 5.21 c
|
||||||
|
83.409996 5.07 83.573326 5 83.759995 5 c
|
||||||
|
89.419998 5 l
|
||||||
|
90.299995 5 91.159996 5.173333 92 5.52 c
|
||||||
|
h
|
||||||
|
f
|
||||||
|
Q
|
||||||
|
Q
|
||||||
|
Q
|
||||||
|
showpage
|
||||||
|
%%PageTrailer
|
||||||
|
pdfEndPage
|
||||||
|
%%Trailer
|
||||||
|
end
|
||||||
|
%%DocumentSuppliedResources:
|
||||||
|
%%EOF
|
||||||
BIN
public/assets/dasfad/Logo Files/For Web/Favicons/Android.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
public/assets/dasfad/Logo Files/For Web/Favicons/browser.png
Normal file
|
After Width: | Height: | Size: 143 B |
BIN
public/assets/dasfad/Logo Files/For Web/Favicons/iPhone.png
Normal file
|
After Width: | Height: | Size: 1014 B |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 332 KiB |
|
After Width: | Height: | Size: 349 KiB |
|
After Width: | Height: | Size: 86 KiB |
@ -0,0 +1,40 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="3200"
|
||||||
|
height="1355.480324331485" viewBox="0 0 3200 1355.480324331485">
|
||||||
|
|
||||||
|
<g transform="scale(10) translate(10, 10)">
|
||||||
|
<defs id="SvgjsDefs1385">
|
||||||
|
<linearGradient id="SvgjsLinearGradient1390">
|
||||||
|
<stop id="SvgjsStop1391" stop-color="#905e26" offset="0"/>
|
||||||
|
<stop id="SvgjsStop1392" stop-color="#f5ec9b" offset="0.5"/>
|
||||||
|
<stop id="SvgjsStop1393" stop-color="#905e26" offset="1"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="SvgjsLinearGradient1394">
|
||||||
|
<stop id="SvgjsStop1395" stop-color="#905e26" offset="0"/>
|
||||||
|
<stop id="SvgjsStop1396" stop-color="#f5ec9b" offset="0.5"/>
|
||||||
|
<stop id="SvgjsStop1397" stop-color="#905e26" offset="1"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<g id="SvgjsG1386" featureKey="aMgJeN-0"
|
||||||
|
transform="matrix(1.5610770874511997,0,0,1.5610770874511997,71.94613967240352,-53.841545411371435)"
|
||||||
|
fill="#000">
|
||||||
|
<path xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="M29.399,57.112c0.615,0.308,1.077,0.846,1.077,1.383c0,1.039-1.038,1.961-1.999,1.462l-15.223-7.881 c-0.846-0.499-1.576-0.808-1.576-1.884c0-1.115,0.692-1.385,1.576-1.922l15.223-7.881c1.038-0.346,1.999,0.424,1.999,1.462 c0,0.575-0.461,1.114-1.077,1.423l-13.761,6.918L29.399,57.112z"/>
|
||||||
|
<path xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="M29.033,60.209c-0.208,0-0.413-0.052-0.608-0.152l-15.223-7.881c-0.086-0.05-0.165-0.095-0.242-0.141 c-0.748-0.431-1.395-0.804-1.395-1.843c0-1.057,0.594-1.406,1.346-1.849c0.093-0.054,0.187-0.11,0.284-0.169l15.229-7.885 c0.195-0.066,0.377-0.097,0.558-0.097c0.9,0,1.606,0.728,1.606,1.658c0,0.587-0.437,1.172-1.139,1.522l-13.562,6.818l13.562,6.818 c0.691,0.346,1.139,0.93,1.139,1.484C30.588,59.407,29.861,60.209,29.033,60.209z M28.982,40.419c-0.156,0-0.314,0.026-0.47,0.078 L13.306,48.37c-0.091,0.057-0.188,0.113-0.281,0.167c-0.743,0.439-1.234,0.728-1.234,1.655c0,0.91,0.537,1.219,1.281,1.648 c0.079,0.045,0.158,0.09,0.239,0.139l15.217,7.877c0.162,0.084,0.332,0.127,0.504,0.127c0.697,0,1.33-0.709,1.33-1.488 c0-0.465-0.407-0.98-1.014-1.283l-13.962-7.02l13.962-7.02c0.616-0.308,1.014-0.828,1.014-1.321 C30.363,41.048,29.756,40.419,28.982,40.419z"/>
|
||||||
|
<path xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="M46.385,64.416c-0.231,0.691-0.922,1.077-1.614,0.961c-0.769-0.153-1.269-0.885-1.154-1.692 c0-0.076,0.039-0.191,0.077-0.307l9.88-27.831c0.23-0.692,0.922-1.038,1.614-0.923c0.73,0.154,1.269,0.885,1.153,1.652 c0,0.078-0.039,0.193-0.077,0.27L46.385,64.416z"/>
|
||||||
|
<path xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="M45.016,65.511c-0.088,0-0.177-0.008-0.263-0.022c-0.837-0.167-1.371-0.949-1.247-1.819 c-0.001-0.076,0.038-0.193,0.079-0.318l9.883-27.842c0.207-0.618,0.778-1.02,1.457-1.02c0.094,0,0.188,0.009,0.281,0.023 c0.812,0.172,1.369,0.97,1.247,1.781c0.001,0.086-0.047,0.22-0.087,0.302l-9.875,27.856C46.281,65.084,45.688,65.511,45.016,65.511z M54.925,34.715c-0.58,0-1.068,0.34-1.244,0.867l-9.88,27.833c-0.035,0.104-0.07,0.213-0.07,0.27 c-0.108,0.767,0.349,1.439,1.062,1.582c0.071,0.012,0.147,0.018,0.223,0.018c0.575,0,1.083-0.363,1.263-0.904l9.881-27.872 c0.041-0.085,0.07-0.182,0.07-0.231c0.105-0.712-0.373-1.396-1.064-1.543C55.089,34.722,55.007,34.715,54.925,34.715z"/>
|
||||||
|
<path xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="M84.362,50.192L70.6,43.274c-0.614-0.309-1.075-0.848-1.075-1.423c0-1.038,1.037-1.962,1.998-1.462l15.223,7.881 c0.885,0.537,1.576,0.807,1.576,1.922c0,1.076-0.73,1.385-1.576,1.884l-15.223,7.881c-0.961,0.499-1.998-0.423-1.998-1.462 c0-0.537,0.461-1.075,1.075-1.383L84.362,50.192z"/>
|
||||||
|
<path xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="M70.967,60.209c-0.828,0-1.556-0.802-1.556-1.714c0-0.555,0.447-1.139,1.139-1.484l13.562-6.818L70.55,43.374 c-0.702-0.352-1.139-0.936-1.139-1.522c0-0.913,0.728-1.714,1.556-1.714c0.209,0,0.413,0.051,0.608,0.152l15.223,7.881 c0.104,0.062,0.198,0.119,0.29,0.173c0.753,0.442,1.347,0.792,1.347,1.849c0,1.039-0.646,1.412-1.395,1.843 c-0.078,0.046-0.157,0.091-0.237,0.138l-15.228,7.884C71.38,60.157,71.176,60.209,70.967,60.209z M70.967,40.363 c-0.696,0-1.33,0.709-1.33,1.488c0,0.493,0.397,1.013,1.014,1.321l13.962,7.02l-13.962,7.02c-0.606,0.305-1.014,0.82-1.014,1.283 c0,0.779,0.634,1.488,1.33,1.488c0.172,0,0.342-0.043,0.504-0.127l15.223-7.88c0.075-0.046,0.155-0.091,0.233-0.136 c0.744-0.43,1.282-0.738,1.282-1.648c0-0.928-0.491-1.216-1.235-1.655c-0.093-0.054-0.188-0.11-0.287-0.17l-15.216-7.876 C71.309,40.405,71.139,40.363,70.967,40.363z"/>
|
||||||
|
</g>
|
||||||
|
<g id="SvgjsG1387" featureKey="8L6ael-0"
|
||||||
|
transform="matrix(3.168568052463937,0,0,3.168568052463937,-5.1330821487142195,52.17667742743372)"
|
||||||
|
fill="#000">
|
||||||
|
<path d="M10.56 5.52 q1.22 0.46 2.2 1.48 q2.1 2.12 2.1 5.41 t-2.1 5.43 q-0.98 1.02 -2.2 1.48 q-1.26 0.52 -2.58 0.52 l-5.66 0 q-0.28 0 -0.49 -0.21 t-0.21 -0.49 l0 -1.44 q0 -0.3 0.21 -0.51 t0.49 -0.21 l5.6 0 q1.78 0 2.89 -1.3 t1.11 -3.26 t-1.11 -3.26 t-2.89 -1.3 l-5.6 0 q-0.28 0 -0.49 -0.21 t-0.21 -0.51 l0 -1.44 q0 -0.28 0.21 -0.49 t0.49 -0.21 l5.66 0 q1.32 0 2.58 0.52 z M32.208 18.86 q0.14 0.32 -0.07 0.65 t-0.57 0.33 l-13.32 0 q-0.18 0 -0.34 -0.09 t-0.24 -0.23 q-0.22 -0.32 -0.06 -0.66 l0.62 -1.46 q0.08 -0.2 0.26 -0.32 t0.38 -0.12 l9.32 0 l-3.28 -7.84 l-2.68 6.4 q-0.08 0.2 -0.25 0.31 t-0.39 0.11 l-1.68 0 q-0.38 0 -0.6 -0.32 q-0.08 -0.14 -0.1 -0.32 t0.04 -0.34 l4.06 -9.52 q0.08 -0.2 0.25 -0.32 t0.39 -0.12 l1.92 0 q0.22 0 0.39 0.12 t0.25 0.32 z M45.535999999999994 12.42 q0.86 0.54 1.31 1.32 t0.45 1.74 q0 2.26 -1.72 3.46 q-1.16 0.84 -3.14 1.06 l-0.08 0 q-0.28 0 -0.46 -0.18 q-0.24 -0.22 -0.24 -0.52 l0 -1.44 q0 -0.26 0.18 -0.46 t0.44 -0.24 q0.94 -0.1 1.46 -0.44 q0.4 -0.24 0.54 -0.62 q0.08 -0.22 0.08 -0.56 q0 -0.22 -0.08 -0.38 t-0.28 -0.3 q-0.58 -0.4 -1.5 -0.68 l-0.32 -0.1 q-1 -0.28 -1.84 -0.46 q-0.18 -0.04 -0.58 -0.14 l-0.22 -0.06 q-0.72 -0.18 -1.52 -0.5 q-1.18 -0.5 -1.94 -1.26 q-0.88 -0.88 -0.88 -2.32 q0 -1.98 1.52 -3.22 q1.04 -0.88 2.92 -1.12 q0.32 -0.04 0.55 0.18 t0.23 0.52 l0 1.44 q0 0.26 -0.16 0.46 t-0.41 0.23 t-0.51 0.11 q-0.58 0.22 -0.82 0.43 t-0.34 0.43 q-0.1 0.34 -0.1 0.64 q0 0.16 0.18 0.34 q0.28 0.28 0.82 0.5 q0.24 0.1 0.84 0.3 l2.5 0.62 l0.12 0.04 q0.94 0.26 1.36 0.4 q0.94 0.32 1.64 0.78 z M42.196 7.9 q-0.24 -0.06 -0.39 -0.25 t-0.15 -0.45 l0 -1.46 q0 -0.32 0.26 -0.54 q0.1 -0.1 0.26 -0.13 t0.3 -0.01 q1.76 0.28 2.86 1.2 q1.5 1.24 1.6 3.16 q0.02 0.28 -0.19 0.51 t-0.51 0.23 l-1.56 0 q-0.26 0 -0.46 -0.18 t-0.22 -0.44 q-0.08 -0.52 -0.34 -0.86 q-0.42 -0.5 -1.3 -0.76 q-0.06 0 -0.08 -0.02 l-0.08 0 z M39.855999999999995 17.08 q0.24 0.04 0.4 0.24 t0.16 0.44 l0 1.48 q0 0.32 -0.24 0.54 q-0.2 0.16 -0.46 0.16 l-0.1 0 q-1.86 -0.28 -3.06 -1.22 q-1.56 -1.24 -1.76 -3.46 q-0.04 -0.32 0.18 -0.55 t0.52 -0.23 l1.58 0 q0.28 0 0.48 0.19 t0.22 0.47 q0.06 1 0.98 1.54 q0.42 0.24 1.1 0.4 z M60.88399999999999 11.12 q0.3 0 0.51 0.21 t0.21 0.51 l0 1.48 q0 0.28 -0.21 0.49 t-0.51 0.21 l-6.58 0 l0 5.12 q0 0.28 -0.2 0.49 t-0.5 0.21 l-1.52 0 q-0.28 0 -0.49 -0.21 t-0.21 -0.49 l0 -7.3 q0 -0.3 0.21 -0.51 t0.49 -0.21 l8.8 0 z M61.78399999999999 5 q0.28 0 0.49 0.21 t0.21 0.49 l0 1.46 q0 0.3 -0.21 0.51 t-0.49 0.21 l-9.7 0 q-0.28 0 -0.49 -0.21 t-0.21 -0.51 l0 -1.46 q0 -0.28 0.21 -0.49 t0.49 -0.21 l9.7 0 z M79.512 18.86 q0.14 0.32 -0.07 0.65 t-0.57 0.33 l-13.32 0 q-0.18 0 -0.34 -0.09 t-0.24 -0.23 q-0.22 -0.32 -0.06 -0.66 l0.62 -1.46 q0.08 -0.2 0.26 -0.32 t0.38 -0.12 l9.32 0 l-3.28 -7.84 l-2.68 6.4 q-0.08 0.2 -0.25 0.31 t-0.39 0.11 l-1.68 0 q-0.38 0 -0.6 -0.32 q-0.08 -0.14 -0.1 -0.32 t0.04 -0.34 l4.06 -9.52 q0.08 -0.2 0.25 -0.32 t0.39 -0.12 l1.92 0 q0.22 0 0.39 0.12 t0.25 0.32 z M92 5.52 q1.22 0.46 2.2 1.48 q2.1 2.12 2.1 5.41 t-2.1 5.43 q-0.98 1.02 -2.2 1.48 q-1.26 0.52 -2.58 0.52 l-5.66 0 q-0.28 0 -0.49 -0.21 t-0.21 -0.49 l0 -1.44 q0 -0.3 0.21 -0.51 t0.49 -0.21 l5.6 0 q1.78 0 2.89 -1.3 t1.11 -3.26 t-1.11 -3.26 t-2.89 -1.3 l-5.6 0 q-0.28 0 -0.49 -0.21 t-0.21 -0.51 l0 -1.44 q0 -0.28 0.21 -0.49 t0.49 -0.21 l5.66 0 q1.32 0 2.58 0.52 z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 8.0 KiB |
@ -0,0 +1,40 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="3200"
|
||||||
|
height="1355.480324331485" viewBox="0 0 3200 1355.480324331485">
|
||||||
|
|
||||||
|
<g transform="scale(10) translate(10, 10)">
|
||||||
|
<defs id="SvgjsDefs1385">
|
||||||
|
<linearGradient id="SvgjsLinearGradient1390">
|
||||||
|
<stop id="SvgjsStop1391" stop-color="#905e26" offset="0"></stop>
|
||||||
|
<stop id="SvgjsStop1392" stop-color="#f5ec9b" offset="0.5"></stop>
|
||||||
|
<stop id="SvgjsStop1393" stop-color="#905e26" offset="1"></stop>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="SvgjsLinearGradient1394">
|
||||||
|
<stop id="SvgjsStop1395" stop-color="#905e26" offset="0"></stop>
|
||||||
|
<stop id="SvgjsStop1396" stop-color="#f5ec9b" offset="0.5"></stop>
|
||||||
|
<stop id="SvgjsStop1397" stop-color="#905e26" offset="1"></stop>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<g id="SvgjsG1386" featureKey="aMgJeN-0"
|
||||||
|
transform="matrix(1.5610770874511997,0,0,1.5610770874511997,71.94613967240352,-53.841545411371435)"
|
||||||
|
fill="url(#SvgjsLinearGradient1390)">
|
||||||
|
<path xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="M29.399,57.112c0.615,0.308,1.077,0.846,1.077,1.383c0,1.039-1.038,1.961-1.999,1.462l-15.223-7.881 c-0.846-0.499-1.576-0.808-1.576-1.884c0-1.115,0.692-1.385,1.576-1.922l15.223-7.881c1.038-0.346,1.999,0.424,1.999,1.462 c0,0.575-0.461,1.114-1.077,1.423l-13.761,6.918L29.399,57.112z"></path>
|
||||||
|
<path xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="M29.033,60.209c-0.208,0-0.413-0.052-0.608-0.152l-15.223-7.881c-0.086-0.05-0.165-0.095-0.242-0.141 c-0.748-0.431-1.395-0.804-1.395-1.843c0-1.057,0.594-1.406,1.346-1.849c0.093-0.054,0.187-0.11,0.284-0.169l15.229-7.885 c0.195-0.066,0.377-0.097,0.558-0.097c0.9,0,1.606,0.728,1.606,1.658c0,0.587-0.437,1.172-1.139,1.522l-13.562,6.818l13.562,6.818 c0.691,0.346,1.139,0.93,1.139,1.484C30.588,59.407,29.861,60.209,29.033,60.209z M28.982,40.419c-0.156,0-0.314,0.026-0.47,0.078 L13.306,48.37c-0.091,0.057-0.188,0.113-0.281,0.167c-0.743,0.439-1.234,0.728-1.234,1.655c0,0.91,0.537,1.219,1.281,1.648 c0.079,0.045,0.158,0.09,0.239,0.139l15.217,7.877c0.162,0.084,0.332,0.127,0.504,0.127c0.697,0,1.33-0.709,1.33-1.488 c0-0.465-0.407-0.98-1.014-1.283l-13.962-7.02l13.962-7.02c0.616-0.308,1.014-0.828,1.014-1.321 C30.363,41.048,29.756,40.419,28.982,40.419z"></path>
|
||||||
|
<path xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="M46.385,64.416c-0.231,0.691-0.922,1.077-1.614,0.961c-0.769-0.153-1.269-0.885-1.154-1.692 c0-0.076,0.039-0.191,0.077-0.307l9.88-27.831c0.23-0.692,0.922-1.038,1.614-0.923c0.73,0.154,1.269,0.885,1.153,1.652 c0,0.078-0.039,0.193-0.077,0.27L46.385,64.416z"></path>
|
||||||
|
<path xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="M45.016,65.511c-0.088,0-0.177-0.008-0.263-0.022c-0.837-0.167-1.371-0.949-1.247-1.819 c-0.001-0.076,0.038-0.193,0.079-0.318l9.883-27.842c0.207-0.618,0.778-1.02,1.457-1.02c0.094,0,0.188,0.009,0.281,0.023 c0.812,0.172,1.369,0.97,1.247,1.781c0.001,0.086-0.047,0.22-0.087,0.302l-9.875,27.856C46.281,65.084,45.688,65.511,45.016,65.511z M54.925,34.715c-0.58,0-1.068,0.34-1.244,0.867l-9.88,27.833c-0.035,0.104-0.07,0.213-0.07,0.27 c-0.108,0.767,0.349,1.439,1.062,1.582c0.071,0.012,0.147,0.018,0.223,0.018c0.575,0,1.083-0.363,1.263-0.904l9.881-27.872 c0.041-0.085,0.07-0.182,0.07-0.231c0.105-0.712-0.373-1.396-1.064-1.543C55.089,34.722,55.007,34.715,54.925,34.715z"></path>
|
||||||
|
<path xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="M84.362,50.192L70.6,43.274c-0.614-0.309-1.075-0.848-1.075-1.423c0-1.038,1.037-1.962,1.998-1.462l15.223,7.881 c0.885,0.537,1.576,0.807,1.576,1.922c0,1.076-0.73,1.385-1.576,1.884l-15.223,7.881c-0.961,0.499-1.998-0.423-1.998-1.462 c0-0.537,0.461-1.075,1.075-1.383L84.362,50.192z"></path>
|
||||||
|
<path xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="M70.967,60.209c-0.828,0-1.556-0.802-1.556-1.714c0-0.555,0.447-1.139,1.139-1.484l13.562-6.818L70.55,43.374 c-0.702-0.352-1.139-0.936-1.139-1.522c0-0.913,0.728-1.714,1.556-1.714c0.209,0,0.413,0.051,0.608,0.152l15.223,7.881 c0.104,0.062,0.198,0.119,0.29,0.173c0.753,0.442,1.347,0.792,1.347,1.849c0,1.039-0.646,1.412-1.395,1.843 c-0.078,0.046-0.157,0.091-0.237,0.138l-15.228,7.884C71.38,60.157,71.176,60.209,70.967,60.209z M70.967,40.363 c-0.696,0-1.33,0.709-1.33,1.488c0,0.493,0.397,1.013,1.014,1.321l13.962,7.02l-13.962,7.02c-0.606,0.305-1.014,0.82-1.014,1.283 c0,0.779,0.634,1.488,1.33,1.488c0.172,0,0.342-0.043,0.504-0.127l15.223-7.88c0.075-0.046,0.155-0.091,0.233-0.136 c0.744-0.43,1.282-0.738,1.282-1.648c0-0.928-0.491-1.216-1.235-1.655c-0.093-0.054-0.188-0.11-0.287-0.17l-15.216-7.876 C71.309,40.405,71.139,40.363,70.967,40.363z"></path>
|
||||||
|
</g>
|
||||||
|
<g id="SvgjsG1387" featureKey="8L6ael-0"
|
||||||
|
transform="matrix(3.168568052463937,0,0,3.168568052463937,-5.1330821487142195,52.17667742743372)"
|
||||||
|
fill="url(#SvgjsLinearGradient1394)">
|
||||||
|
<path d="M10.56 5.52 q1.22 0.46 2.2 1.48 q2.1 2.12 2.1 5.41 t-2.1 5.43 q-0.98 1.02 -2.2 1.48 q-1.26 0.52 -2.58 0.52 l-5.66 0 q-0.28 0 -0.49 -0.21 t-0.21 -0.49 l0 -1.44 q0 -0.3 0.21 -0.51 t0.49 -0.21 l5.6 0 q1.78 0 2.89 -1.3 t1.11 -3.26 t-1.11 -3.26 t-2.89 -1.3 l-5.6 0 q-0.28 0 -0.49 -0.21 t-0.21 -0.51 l0 -1.44 q0 -0.28 0.21 -0.49 t0.49 -0.21 l5.66 0 q1.32 0 2.58 0.52 z M32.208 18.86 q0.14 0.32 -0.07 0.65 t-0.57 0.33 l-13.32 0 q-0.18 0 -0.34 -0.09 t-0.24 -0.23 q-0.22 -0.32 -0.06 -0.66 l0.62 -1.46 q0.08 -0.2 0.26 -0.32 t0.38 -0.12 l9.32 0 l-3.28 -7.84 l-2.68 6.4 q-0.08 0.2 -0.25 0.31 t-0.39 0.11 l-1.68 0 q-0.38 0 -0.6 -0.32 q-0.08 -0.14 -0.1 -0.32 t0.04 -0.34 l4.06 -9.52 q0.08 -0.2 0.25 -0.32 t0.39 -0.12 l1.92 0 q0.22 0 0.39 0.12 t0.25 0.32 z M45.535999999999994 12.42 q0.86 0.54 1.31 1.32 t0.45 1.74 q0 2.26 -1.72 3.46 q-1.16 0.84 -3.14 1.06 l-0.08 0 q-0.28 0 -0.46 -0.18 q-0.24 -0.22 -0.24 -0.52 l0 -1.44 q0 -0.26 0.18 -0.46 t0.44 -0.24 q0.94 -0.1 1.46 -0.44 q0.4 -0.24 0.54 -0.62 q0.08 -0.22 0.08 -0.56 q0 -0.22 -0.08 -0.38 t-0.28 -0.3 q-0.58 -0.4 -1.5 -0.68 l-0.32 -0.1 q-1 -0.28 -1.84 -0.46 q-0.18 -0.04 -0.58 -0.14 l-0.22 -0.06 q-0.72 -0.18 -1.52 -0.5 q-1.18 -0.5 -1.94 -1.26 q-0.88 -0.88 -0.88 -2.32 q0 -1.98 1.52 -3.22 q1.04 -0.88 2.92 -1.12 q0.32 -0.04 0.55 0.18 t0.23 0.52 l0 1.44 q0 0.26 -0.16 0.46 t-0.41 0.23 t-0.51 0.11 q-0.58 0.22 -0.82 0.43 t-0.34 0.43 q-0.1 0.34 -0.1 0.64 q0 0.16 0.18 0.34 q0.28 0.28 0.82 0.5 q0.24 0.1 0.84 0.3 l2.5 0.62 l0.12 0.04 q0.94 0.26 1.36 0.4 q0.94 0.32 1.64 0.78 z M42.196 7.9 q-0.24 -0.06 -0.39 -0.25 t-0.15 -0.45 l0 -1.46 q0 -0.32 0.26 -0.54 q0.1 -0.1 0.26 -0.13 t0.3 -0.01 q1.76 0.28 2.86 1.2 q1.5 1.24 1.6 3.16 q0.02 0.28 -0.19 0.51 t-0.51 0.23 l-1.56 0 q-0.26 0 -0.46 -0.18 t-0.22 -0.44 q-0.08 -0.52 -0.34 -0.86 q-0.42 -0.5 -1.3 -0.76 q-0.06 0 -0.08 -0.02 l-0.08 0 z M39.855999999999995 17.08 q0.24 0.04 0.4 0.24 t0.16 0.44 l0 1.48 q0 0.32 -0.24 0.54 q-0.2 0.16 -0.46 0.16 l-0.1 0 q-1.86 -0.28 -3.06 -1.22 q-1.56 -1.24 -1.76 -3.46 q-0.04 -0.32 0.18 -0.55 t0.52 -0.23 l1.58 0 q0.28 0 0.48 0.19 t0.22 0.47 q0.06 1 0.98 1.54 q0.42 0.24 1.1 0.4 z M60.88399999999999 11.12 q0.3 0 0.51 0.21 t0.21 0.51 l0 1.48 q0 0.28 -0.21 0.49 t-0.51 0.21 l-6.58 0 l0 5.12 q0 0.28 -0.2 0.49 t-0.5 0.21 l-1.52 0 q-0.28 0 -0.49 -0.21 t-0.21 -0.49 l0 -7.3 q0 -0.3 0.21 -0.51 t0.49 -0.21 l8.8 0 z M61.78399999999999 5 q0.28 0 0.49 0.21 t0.21 0.49 l0 1.46 q0 0.3 -0.21 0.51 t-0.49 0.21 l-9.7 0 q-0.28 0 -0.49 -0.21 t-0.21 -0.51 l0 -1.46 q0 -0.28 0.21 -0.49 t0.49 -0.21 l9.7 0 z M79.512 18.86 q0.14 0.32 -0.07 0.65 t-0.57 0.33 l-13.32 0 q-0.18 0 -0.34 -0.09 t-0.24 -0.23 q-0.22 -0.32 -0.06 -0.66 l0.62 -1.46 q0.08 -0.2 0.26 -0.32 t0.38 -0.12 l9.32 0 l-3.28 -7.84 l-2.68 6.4 q-0.08 0.2 -0.25 0.31 t-0.39 0.11 l-1.68 0 q-0.38 0 -0.6 -0.32 q-0.08 -0.14 -0.1 -0.32 t0.04 -0.34 l4.06 -9.52 q0.08 -0.2 0.25 -0.32 t0.39 -0.12 l1.92 0 q0.22 0 0.39 0.12 t0.25 0.32 z M92 5.52 q1.22 0.46 2.2 1.48 q2.1 2.12 2.1 5.41 t-2.1 5.43 q-0.98 1.02 -2.2 1.48 q-1.26 0.52 -2.58 0.52 l-5.66 0 q-0.28 0 -0.49 -0.21 t-0.21 -0.49 l0 -1.44 q0 -0.3 0.21 -0.51 t0.49 -0.21 l5.6 0 q1.78 0 2.89 -1.3 t1.11 -3.26 t-1.11 -3.26 t-2.89 -1.3 l-5.6 0 q-0.28 0 -0.49 -0.21 t-0.21 -0.51 l0 -1.44 q0 -0.28 0.21 -0.49 t0.49 -0.21 l5.66 0 q1.32 0 2.58 0.52 z"></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 8.1 KiB |
@ -0,0 +1,40 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="3200"
|
||||||
|
height="1355.480324331485" viewBox="0 0 3200 1355.480324331485">
|
||||||
|
<rect fill="#292929" width="3200" height="1355.480324331485"/>
|
||||||
|
<g transform="scale(10) translate(10, 10)">
|
||||||
|
<defs id="SvgjsDefs1385">
|
||||||
|
<linearGradient id="SvgjsLinearGradient1390">
|
||||||
|
<stop id="SvgjsStop1391" stop-color="#905e26" offset="0"></stop>
|
||||||
|
<stop id="SvgjsStop1392" stop-color="#f5ec9b" offset="0.5"></stop>
|
||||||
|
<stop id="SvgjsStop1393" stop-color="#905e26" offset="1"></stop>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="SvgjsLinearGradient1394">
|
||||||
|
<stop id="SvgjsStop1395" stop-color="#905e26" offset="0"></stop>
|
||||||
|
<stop id="SvgjsStop1396" stop-color="#f5ec9b" offset="0.5"></stop>
|
||||||
|
<stop id="SvgjsStop1397" stop-color="#905e26" offset="1"></stop>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<g id="SvgjsG1386" featureKey="aMgJeN-0"
|
||||||
|
transform="matrix(1.5610770874511997,0,0,1.5610770874511997,71.94613967240352,-53.841545411371435)"
|
||||||
|
fill="url(#SvgjsLinearGradient1390)">
|
||||||
|
<path xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="M29.399,57.112c0.615,0.308,1.077,0.846,1.077,1.383c0,1.039-1.038,1.961-1.999,1.462l-15.223-7.881 c-0.846-0.499-1.576-0.808-1.576-1.884c0-1.115,0.692-1.385,1.576-1.922l15.223-7.881c1.038-0.346,1.999,0.424,1.999,1.462 c0,0.575-0.461,1.114-1.077,1.423l-13.761,6.918L29.399,57.112z"></path>
|
||||||
|
<path xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="M29.033,60.209c-0.208,0-0.413-0.052-0.608-0.152l-15.223-7.881c-0.086-0.05-0.165-0.095-0.242-0.141 c-0.748-0.431-1.395-0.804-1.395-1.843c0-1.057,0.594-1.406,1.346-1.849c0.093-0.054,0.187-0.11,0.284-0.169l15.229-7.885 c0.195-0.066,0.377-0.097,0.558-0.097c0.9,0,1.606,0.728,1.606,1.658c0,0.587-0.437,1.172-1.139,1.522l-13.562,6.818l13.562,6.818 c0.691,0.346,1.139,0.93,1.139,1.484C30.588,59.407,29.861,60.209,29.033,60.209z M28.982,40.419c-0.156,0-0.314,0.026-0.47,0.078 L13.306,48.37c-0.091,0.057-0.188,0.113-0.281,0.167c-0.743,0.439-1.234,0.728-1.234,1.655c0,0.91,0.537,1.219,1.281,1.648 c0.079,0.045,0.158,0.09,0.239,0.139l15.217,7.877c0.162,0.084,0.332,0.127,0.504,0.127c0.697,0,1.33-0.709,1.33-1.488 c0-0.465-0.407-0.98-1.014-1.283l-13.962-7.02l13.962-7.02c0.616-0.308,1.014-0.828,1.014-1.321 C30.363,41.048,29.756,40.419,28.982,40.419z"></path>
|
||||||
|
<path xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="M46.385,64.416c-0.231,0.691-0.922,1.077-1.614,0.961c-0.769-0.153-1.269-0.885-1.154-1.692 c0-0.076,0.039-0.191,0.077-0.307l9.88-27.831c0.23-0.692,0.922-1.038,1.614-0.923c0.73,0.154,1.269,0.885,1.153,1.652 c0,0.078-0.039,0.193-0.077,0.27L46.385,64.416z"></path>
|
||||||
|
<path xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="M45.016,65.511c-0.088,0-0.177-0.008-0.263-0.022c-0.837-0.167-1.371-0.949-1.247-1.819 c-0.001-0.076,0.038-0.193,0.079-0.318l9.883-27.842c0.207-0.618,0.778-1.02,1.457-1.02c0.094,0,0.188,0.009,0.281,0.023 c0.812,0.172,1.369,0.97,1.247,1.781c0.001,0.086-0.047,0.22-0.087,0.302l-9.875,27.856C46.281,65.084,45.688,65.511,45.016,65.511z M54.925,34.715c-0.58,0-1.068,0.34-1.244,0.867l-9.88,27.833c-0.035,0.104-0.07,0.213-0.07,0.27 c-0.108,0.767,0.349,1.439,1.062,1.582c0.071,0.012,0.147,0.018,0.223,0.018c0.575,0,1.083-0.363,1.263-0.904l9.881-27.872 c0.041-0.085,0.07-0.182,0.07-0.231c0.105-0.712-0.373-1.396-1.064-1.543C55.089,34.722,55.007,34.715,54.925,34.715z"></path>
|
||||||
|
<path xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="M84.362,50.192L70.6,43.274c-0.614-0.309-1.075-0.848-1.075-1.423c0-1.038,1.037-1.962,1.998-1.462l15.223,7.881 c0.885,0.537,1.576,0.807,1.576,1.922c0,1.076-0.73,1.385-1.576,1.884l-15.223,7.881c-0.961,0.499-1.998-0.423-1.998-1.462 c0-0.537,0.461-1.075,1.075-1.383L84.362,50.192z"></path>
|
||||||
|
<path xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="M70.967,60.209c-0.828,0-1.556-0.802-1.556-1.714c0-0.555,0.447-1.139,1.139-1.484l13.562-6.818L70.55,43.374 c-0.702-0.352-1.139-0.936-1.139-1.522c0-0.913,0.728-1.714,1.556-1.714c0.209,0,0.413,0.051,0.608,0.152l15.223,7.881 c0.104,0.062,0.198,0.119,0.29,0.173c0.753,0.442,1.347,0.792,1.347,1.849c0,1.039-0.646,1.412-1.395,1.843 c-0.078,0.046-0.157,0.091-0.237,0.138l-15.228,7.884C71.38,60.157,71.176,60.209,70.967,60.209z M70.967,40.363 c-0.696,0-1.33,0.709-1.33,1.488c0,0.493,0.397,1.013,1.014,1.321l13.962,7.02l-13.962,7.02c-0.606,0.305-1.014,0.82-1.014,1.283 c0,0.779,0.634,1.488,1.33,1.488c0.172,0,0.342-0.043,0.504-0.127l15.223-7.88c0.075-0.046,0.155-0.091,0.233-0.136 c0.744-0.43,1.282-0.738,1.282-1.648c0-0.928-0.491-1.216-1.235-1.655c-0.093-0.054-0.188-0.11-0.287-0.17l-15.216-7.876 C71.309,40.405,71.139,40.363,70.967,40.363z"></path>
|
||||||
|
</g>
|
||||||
|
<g id="SvgjsG1387" featureKey="8L6ael-0"
|
||||||
|
transform="matrix(3.168568052463937,0,0,3.168568052463937,-5.1330821487142195,52.17667742743372)"
|
||||||
|
fill="url(#SvgjsLinearGradient1394)">
|
||||||
|
<path d="M10.56 5.52 q1.22 0.46 2.2 1.48 q2.1 2.12 2.1 5.41 t-2.1 5.43 q-0.98 1.02 -2.2 1.48 q-1.26 0.52 -2.58 0.52 l-5.66 0 q-0.28 0 -0.49 -0.21 t-0.21 -0.49 l0 -1.44 q0 -0.3 0.21 -0.51 t0.49 -0.21 l5.6 0 q1.78 0 2.89 -1.3 t1.11 -3.26 t-1.11 -3.26 t-2.89 -1.3 l-5.6 0 q-0.28 0 -0.49 -0.21 t-0.21 -0.51 l0 -1.44 q0 -0.28 0.21 -0.49 t0.49 -0.21 l5.66 0 q1.32 0 2.58 0.52 z M32.208 18.86 q0.14 0.32 -0.07 0.65 t-0.57 0.33 l-13.32 0 q-0.18 0 -0.34 -0.09 t-0.24 -0.23 q-0.22 -0.32 -0.06 -0.66 l0.62 -1.46 q0.08 -0.2 0.26 -0.32 t0.38 -0.12 l9.32 0 l-3.28 -7.84 l-2.68 6.4 q-0.08 0.2 -0.25 0.31 t-0.39 0.11 l-1.68 0 q-0.38 0 -0.6 -0.32 q-0.08 -0.14 -0.1 -0.32 t0.04 -0.34 l4.06 -9.52 q0.08 -0.2 0.25 -0.32 t0.39 -0.12 l1.92 0 q0.22 0 0.39 0.12 t0.25 0.32 z M45.535999999999994 12.42 q0.86 0.54 1.31 1.32 t0.45 1.74 q0 2.26 -1.72 3.46 q-1.16 0.84 -3.14 1.06 l-0.08 0 q-0.28 0 -0.46 -0.18 q-0.24 -0.22 -0.24 -0.52 l0 -1.44 q0 -0.26 0.18 -0.46 t0.44 -0.24 q0.94 -0.1 1.46 -0.44 q0.4 -0.24 0.54 -0.62 q0.08 -0.22 0.08 -0.56 q0 -0.22 -0.08 -0.38 t-0.28 -0.3 q-0.58 -0.4 -1.5 -0.68 l-0.32 -0.1 q-1 -0.28 -1.84 -0.46 q-0.18 -0.04 -0.58 -0.14 l-0.22 -0.06 q-0.72 -0.18 -1.52 -0.5 q-1.18 -0.5 -1.94 -1.26 q-0.88 -0.88 -0.88 -2.32 q0 -1.98 1.52 -3.22 q1.04 -0.88 2.92 -1.12 q0.32 -0.04 0.55 0.18 t0.23 0.52 l0 1.44 q0 0.26 -0.16 0.46 t-0.41 0.23 t-0.51 0.11 q-0.58 0.22 -0.82 0.43 t-0.34 0.43 q-0.1 0.34 -0.1 0.64 q0 0.16 0.18 0.34 q0.28 0.28 0.82 0.5 q0.24 0.1 0.84 0.3 l2.5 0.62 l0.12 0.04 q0.94 0.26 1.36 0.4 q0.94 0.32 1.64 0.78 z M42.196 7.9 q-0.24 -0.06 -0.39 -0.25 t-0.15 -0.45 l0 -1.46 q0 -0.32 0.26 -0.54 q0.1 -0.1 0.26 -0.13 t0.3 -0.01 q1.76 0.28 2.86 1.2 q1.5 1.24 1.6 3.16 q0.02 0.28 -0.19 0.51 t-0.51 0.23 l-1.56 0 q-0.26 0 -0.46 -0.18 t-0.22 -0.44 q-0.08 -0.52 -0.34 -0.86 q-0.42 -0.5 -1.3 -0.76 q-0.06 0 -0.08 -0.02 l-0.08 0 z M39.855999999999995 17.08 q0.24 0.04 0.4 0.24 t0.16 0.44 l0 1.48 q0 0.32 -0.24 0.54 q-0.2 0.16 -0.46 0.16 l-0.1 0 q-1.86 -0.28 -3.06 -1.22 q-1.56 -1.24 -1.76 -3.46 q-0.04 -0.32 0.18 -0.55 t0.52 -0.23 l1.58 0 q0.28 0 0.48 0.19 t0.22 0.47 q0.06 1 0.98 1.54 q0.42 0.24 1.1 0.4 z M60.88399999999999 11.12 q0.3 0 0.51 0.21 t0.21 0.51 l0 1.48 q0 0.28 -0.21 0.49 t-0.51 0.21 l-6.58 0 l0 5.12 q0 0.28 -0.2 0.49 t-0.5 0.21 l-1.52 0 q-0.28 0 -0.49 -0.21 t-0.21 -0.49 l0 -7.3 q0 -0.3 0.21 -0.51 t0.49 -0.21 l8.8 0 z M61.78399999999999 5 q0.28 0 0.49 0.21 t0.21 0.49 l0 1.46 q0 0.3 -0.21 0.51 t-0.49 0.21 l-9.7 0 q-0.28 0 -0.49 -0.21 t-0.21 -0.51 l0 -1.46 q0 -0.28 0.21 -0.49 t0.49 -0.21 l9.7 0 z M79.512 18.86 q0.14 0.32 -0.07 0.65 t-0.57 0.33 l-13.32 0 q-0.18 0 -0.34 -0.09 t-0.24 -0.23 q-0.22 -0.32 -0.06 -0.66 l0.62 -1.46 q0.08 -0.2 0.26 -0.32 t0.38 -0.12 l9.32 0 l-3.28 -7.84 l-2.68 6.4 q-0.08 0.2 -0.25 0.31 t-0.39 0.11 l-1.68 0 q-0.38 0 -0.6 -0.32 q-0.08 -0.14 -0.1 -0.32 t0.04 -0.34 l4.06 -9.52 q0.08 -0.2 0.25 -0.32 t0.39 -0.12 l1.92 0 q0.22 0 0.39 0.12 t0.25 0.32 z M92 5.52 q1.22 0.46 2.2 1.48 q2.1 2.12 2.1 5.41 t-2.1 5.43 q-0.98 1.02 -2.2 1.48 q-1.26 0.52 -2.58 0.52 l-5.66 0 q-0.28 0 -0.49 -0.21 t-0.21 -0.49 l0 -1.44 q0 -0.3 0.21 -0.51 t0.49 -0.21 l5.6 0 q1.78 0 2.89 -1.3 t1.11 -3.26 t-1.11 -3.26 t-2.89 -1.3 l-5.6 0 q-0.28 0 -0.49 -0.21 t-0.21 -0.51 l0 -1.44 q0 -0.28 0.21 -0.49 t0.49 -0.21 l5.66 0 q1.32 0 2.58 0.52 z"></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 8.2 KiB |
@ -0,0 +1,40 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="3200"
|
||||||
|
height="1355.480324331485" viewBox="0 0 3200 1355.480324331485">
|
||||||
|
|
||||||
|
<g transform="scale(10) translate(10, 10)">
|
||||||
|
<defs id="SvgjsDefs1385">
|
||||||
|
<linearGradient id="SvgjsLinearGradient1390">
|
||||||
|
<stop id="SvgjsStop1391" stop-color="#905e26" offset="0"/>
|
||||||
|
<stop id="SvgjsStop1392" stop-color="#f5ec9b" offset="0.5"/>
|
||||||
|
<stop id="SvgjsStop1393" stop-color="#905e26" offset="1"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="SvgjsLinearGradient1394">
|
||||||
|
<stop id="SvgjsStop1395" stop-color="#905e26" offset="0"/>
|
||||||
|
<stop id="SvgjsStop1396" stop-color="#f5ec9b" offset="0.5"/>
|
||||||
|
<stop id="SvgjsStop1397" stop-color="#905e26" offset="1"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
<g id="SvgjsG1386" featureKey="aMgJeN-0"
|
||||||
|
transform="matrix(1.5610770874511997,0,0,1.5610770874511997,71.94613967240352,-53.841545411371435)"
|
||||||
|
fill="#fff">
|
||||||
|
<path xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="M29.399,57.112c0.615,0.308,1.077,0.846,1.077,1.383c0,1.039-1.038,1.961-1.999,1.462l-15.223-7.881 c-0.846-0.499-1.576-0.808-1.576-1.884c0-1.115,0.692-1.385,1.576-1.922l15.223-7.881c1.038-0.346,1.999,0.424,1.999,1.462 c0,0.575-0.461,1.114-1.077,1.423l-13.761,6.918L29.399,57.112z"/>
|
||||||
|
<path xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="M29.033,60.209c-0.208,0-0.413-0.052-0.608-0.152l-15.223-7.881c-0.086-0.05-0.165-0.095-0.242-0.141 c-0.748-0.431-1.395-0.804-1.395-1.843c0-1.057,0.594-1.406,1.346-1.849c0.093-0.054,0.187-0.11,0.284-0.169l15.229-7.885 c0.195-0.066,0.377-0.097,0.558-0.097c0.9,0,1.606,0.728,1.606,1.658c0,0.587-0.437,1.172-1.139,1.522l-13.562,6.818l13.562,6.818 c0.691,0.346,1.139,0.93,1.139,1.484C30.588,59.407,29.861,60.209,29.033,60.209z M28.982,40.419c-0.156,0-0.314,0.026-0.47,0.078 L13.306,48.37c-0.091,0.057-0.188,0.113-0.281,0.167c-0.743,0.439-1.234,0.728-1.234,1.655c0,0.91,0.537,1.219,1.281,1.648 c0.079,0.045,0.158,0.09,0.239,0.139l15.217,7.877c0.162,0.084,0.332,0.127,0.504,0.127c0.697,0,1.33-0.709,1.33-1.488 c0-0.465-0.407-0.98-1.014-1.283l-13.962-7.02l13.962-7.02c0.616-0.308,1.014-0.828,1.014-1.321 C30.363,41.048,29.756,40.419,28.982,40.419z"/>
|
||||||
|
<path xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="M46.385,64.416c-0.231,0.691-0.922,1.077-1.614,0.961c-0.769-0.153-1.269-0.885-1.154-1.692 c0-0.076,0.039-0.191,0.077-0.307l9.88-27.831c0.23-0.692,0.922-1.038,1.614-0.923c0.73,0.154,1.269,0.885,1.153,1.652 c0,0.078-0.039,0.193-0.077,0.27L46.385,64.416z"/>
|
||||||
|
<path xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="M45.016,65.511c-0.088,0-0.177-0.008-0.263-0.022c-0.837-0.167-1.371-0.949-1.247-1.819 c-0.001-0.076,0.038-0.193,0.079-0.318l9.883-27.842c0.207-0.618,0.778-1.02,1.457-1.02c0.094,0,0.188,0.009,0.281,0.023 c0.812,0.172,1.369,0.97,1.247,1.781c0.001,0.086-0.047,0.22-0.087,0.302l-9.875,27.856C46.281,65.084,45.688,65.511,45.016,65.511z M54.925,34.715c-0.58,0-1.068,0.34-1.244,0.867l-9.88,27.833c-0.035,0.104-0.07,0.213-0.07,0.27 c-0.108,0.767,0.349,1.439,1.062,1.582c0.071,0.012,0.147,0.018,0.223,0.018c0.575,0,1.083-0.363,1.263-0.904l9.881-27.872 c0.041-0.085,0.07-0.182,0.07-0.231c0.105-0.712-0.373-1.396-1.064-1.543C55.089,34.722,55.007,34.715,54.925,34.715z"/>
|
||||||
|
<path xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="M84.362,50.192L70.6,43.274c-0.614-0.309-1.075-0.848-1.075-1.423c0-1.038,1.037-1.962,1.998-1.462l15.223,7.881 c0.885,0.537,1.576,0.807,1.576,1.922c0,1.076-0.73,1.385-1.576,1.884l-15.223,7.881c-0.961,0.499-1.998-0.423-1.998-1.462 c0-0.537,0.461-1.075,1.075-1.383L84.362,50.192z"/>
|
||||||
|
<path xmlns="http://www.w3.org/2000/svg"
|
||||||
|
d="M70.967,60.209c-0.828,0-1.556-0.802-1.556-1.714c0-0.555,0.447-1.139,1.139-1.484l13.562-6.818L70.55,43.374 c-0.702-0.352-1.139-0.936-1.139-1.522c0-0.913,0.728-1.714,1.556-1.714c0.209,0,0.413,0.051,0.608,0.152l15.223,7.881 c0.104,0.062,0.198,0.119,0.29,0.173c0.753,0.442,1.347,0.792,1.347,1.849c0,1.039-0.646,1.412-1.395,1.843 c-0.078,0.046-0.157,0.091-0.237,0.138l-15.228,7.884C71.38,60.157,71.176,60.209,70.967,60.209z M70.967,40.363 c-0.696,0-1.33,0.709-1.33,1.488c0,0.493,0.397,1.013,1.014,1.321l13.962,7.02l-13.962,7.02c-0.606,0.305-1.014,0.82-1.014,1.283 c0,0.779,0.634,1.488,1.33,1.488c0.172,0,0.342-0.043,0.504-0.127l15.223-7.88c0.075-0.046,0.155-0.091,0.233-0.136 c0.744-0.43,1.282-0.738,1.282-1.648c0-0.928-0.491-1.216-1.235-1.655c-0.093-0.054-0.188-0.11-0.287-0.17l-15.216-7.876 C71.309,40.405,71.139,40.363,70.967,40.363z"/>
|
||||||
|
</g>
|
||||||
|
<g id="SvgjsG1387" featureKey="8L6ael-0"
|
||||||
|
transform="matrix(3.168568052463937,0,0,3.168568052463937,-5.1330821487142195,52.17667742743372)"
|
||||||
|
fill="#fff">
|
||||||
|
<path d="M10.56 5.52 q1.22 0.46 2.2 1.48 q2.1 2.12 2.1 5.41 t-2.1 5.43 q-0.98 1.02 -2.2 1.48 q-1.26 0.52 -2.58 0.52 l-5.66 0 q-0.28 0 -0.49 -0.21 t-0.21 -0.49 l0 -1.44 q0 -0.3 0.21 -0.51 t0.49 -0.21 l5.6 0 q1.78 0 2.89 -1.3 t1.11 -3.26 t-1.11 -3.26 t-2.89 -1.3 l-5.6 0 q-0.28 0 -0.49 -0.21 t-0.21 -0.51 l0 -1.44 q0 -0.28 0.21 -0.49 t0.49 -0.21 l5.66 0 q1.32 0 2.58 0.52 z M32.208 18.86 q0.14 0.32 -0.07 0.65 t-0.57 0.33 l-13.32 0 q-0.18 0 -0.34 -0.09 t-0.24 -0.23 q-0.22 -0.32 -0.06 -0.66 l0.62 -1.46 q0.08 -0.2 0.26 -0.32 t0.38 -0.12 l9.32 0 l-3.28 -7.84 l-2.68 6.4 q-0.08 0.2 -0.25 0.31 t-0.39 0.11 l-1.68 0 q-0.38 0 -0.6 -0.32 q-0.08 -0.14 -0.1 -0.32 t0.04 -0.34 l4.06 -9.52 q0.08 -0.2 0.25 -0.32 t0.39 -0.12 l1.92 0 q0.22 0 0.39 0.12 t0.25 0.32 z M45.535999999999994 12.42 q0.86 0.54 1.31 1.32 t0.45 1.74 q0 2.26 -1.72 3.46 q-1.16 0.84 -3.14 1.06 l-0.08 0 q-0.28 0 -0.46 -0.18 q-0.24 -0.22 -0.24 -0.52 l0 -1.44 q0 -0.26 0.18 -0.46 t0.44 -0.24 q0.94 -0.1 1.46 -0.44 q0.4 -0.24 0.54 -0.62 q0.08 -0.22 0.08 -0.56 q0 -0.22 -0.08 -0.38 t-0.28 -0.3 q-0.58 -0.4 -1.5 -0.68 l-0.32 -0.1 q-1 -0.28 -1.84 -0.46 q-0.18 -0.04 -0.58 -0.14 l-0.22 -0.06 q-0.72 -0.18 -1.52 -0.5 q-1.18 -0.5 -1.94 -1.26 q-0.88 -0.88 -0.88 -2.32 q0 -1.98 1.52 -3.22 q1.04 -0.88 2.92 -1.12 q0.32 -0.04 0.55 0.18 t0.23 0.52 l0 1.44 q0 0.26 -0.16 0.46 t-0.41 0.23 t-0.51 0.11 q-0.58 0.22 -0.82 0.43 t-0.34 0.43 q-0.1 0.34 -0.1 0.64 q0 0.16 0.18 0.34 q0.28 0.28 0.82 0.5 q0.24 0.1 0.84 0.3 l2.5 0.62 l0.12 0.04 q0.94 0.26 1.36 0.4 q0.94 0.32 1.64 0.78 z M42.196 7.9 q-0.24 -0.06 -0.39 -0.25 t-0.15 -0.45 l0 -1.46 q0 -0.32 0.26 -0.54 q0.1 -0.1 0.26 -0.13 t0.3 -0.01 q1.76 0.28 2.86 1.2 q1.5 1.24 1.6 3.16 q0.02 0.28 -0.19 0.51 t-0.51 0.23 l-1.56 0 q-0.26 0 -0.46 -0.18 t-0.22 -0.44 q-0.08 -0.52 -0.34 -0.86 q-0.42 -0.5 -1.3 -0.76 q-0.06 0 -0.08 -0.02 l-0.08 0 z M39.855999999999995 17.08 q0.24 0.04 0.4 0.24 t0.16 0.44 l0 1.48 q0 0.32 -0.24 0.54 q-0.2 0.16 -0.46 0.16 l-0.1 0 q-1.86 -0.28 -3.06 -1.22 q-1.56 -1.24 -1.76 -3.46 q-0.04 -0.32 0.18 -0.55 t0.52 -0.23 l1.58 0 q0.28 0 0.48 0.19 t0.22 0.47 q0.06 1 0.98 1.54 q0.42 0.24 1.1 0.4 z M60.88399999999999 11.12 q0.3 0 0.51 0.21 t0.21 0.51 l0 1.48 q0 0.28 -0.21 0.49 t-0.51 0.21 l-6.58 0 l0 5.12 q0 0.28 -0.2 0.49 t-0.5 0.21 l-1.52 0 q-0.28 0 -0.49 -0.21 t-0.21 -0.49 l0 -7.3 q0 -0.3 0.21 -0.51 t0.49 -0.21 l8.8 0 z M61.78399999999999 5 q0.28 0 0.49 0.21 t0.21 0.49 l0 1.46 q0 0.3 -0.21 0.51 t-0.49 0.21 l-9.7 0 q-0.28 0 -0.49 -0.21 t-0.21 -0.51 l0 -1.46 q0 -0.28 0.21 -0.49 t0.49 -0.21 l9.7 0 z M79.512 18.86 q0.14 0.32 -0.07 0.65 t-0.57 0.33 l-13.32 0 q-0.18 0 -0.34 -0.09 t-0.24 -0.23 q-0.22 -0.32 -0.06 -0.66 l0.62 -1.46 q0.08 -0.2 0.26 -0.32 t0.38 -0.12 l9.32 0 l-3.28 -7.84 l-2.68 6.4 q-0.08 0.2 -0.25 0.31 t-0.39 0.11 l-1.68 0 q-0.38 0 -0.6 -0.32 q-0.08 -0.14 -0.1 -0.32 t0.04 -0.34 l4.06 -9.52 q0.08 -0.2 0.25 -0.32 t0.39 -0.12 l1.92 0 q0.22 0 0.39 0.12 t0.25 0.32 z M92 5.52 q1.22 0.46 2.2 1.48 q2.1 2.12 2.1 5.41 t-2.1 5.43 q-0.98 1.02 -2.2 1.48 q-1.26 0.52 -2.58 0.52 l-5.66 0 q-0.28 0 -0.49 -0.21 t-0.21 -0.49 l0 -1.44 q0 -0.3 0.21 -0.51 t0.49 -0.21 l5.6 0 q1.78 0 2.89 -1.3 t1.11 -3.26 t-1.11 -3.26 t-2.89 -1.3 l-5.6 0 q-0.28 0 -0.49 -0.21 t-0.21 -0.51 l0 -1.44 q0 -0.28 0.21 -0.49 t0.49 -0.21 l5.66 0 q1.32 0 2.58 0.52 z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 8.0 KiB |
BIN
public/assets/dasfad/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 679 B |
BIN
public/assets/dasfad/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 896 B |
BIN
public/assets/dasfad/favicon-512x512.png
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
BIN
public/assets/dasfad/favicon-96x96.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
43
public/assets/ddd.svg
Normal file
|
After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 669 B After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 7.8 KiB After Width: | Height: | Size: 15 KiB |
BIN
public/assets/grid.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
public/assets/grid2.png
Normal file
|
After Width: | Height: | Size: 867 KiB |
BIN
public/assets/grid3.jpg
Normal file
|
After Width: | Height: | Size: 661 KiB |
BIN
public/assets/grid4.jpg
Normal file
|
After Width: | Height: | Size: 256 KiB |
BIN
public/assets/grid6.jpg
Normal file
|
After Width: | Height: | Size: 103 KiB |
BIN
public/assets/iPhone.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 32 KiB |
BIN
public/assets/icons/point.png
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
BIN
public/assets/icons/video.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
8
public/assets/icons/video.svg
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||||
|
<g data-name="Layer 2">
|
||||||
|
<g data-name="video">
|
||||||
|
<rect width="24" height="24" opacity="0"/>
|
||||||
|
<path d="M21 7.15a1.7 1.7 0 0 0-1.85.3l-2.15 2V8a3 3 0 0 0-3-3H5a3 3 0 0 0-3 3v8a3 3 0 0 0 3 3h9a3 3 0 0 0 3-3v-1.45l2.16 2a1.74 1.74 0 0 0 1.16.45 1.68 1.68 0 0 0 .69-.15 1.6 1.6 0 0 0 1-1.48V8.63A1.6 1.6 0 0 0 21 7.15z"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 438 B |
237
public/assets/models/SoccerManB.gltf
Normal file
BIN
public/assets/models/model.glb
Normal file
BIN
public/assets/models/person.stl
Normal file
BIN
public/assets/models/player1glb.glb
Normal file
BIN
public/assets/models/server_racking_system.glb
Normal file
300
public/assets/models/tinker.obj
Normal file
@ -0,0 +1,300 @@
|
|||||||
|
# Object Export From Tinkercad Server 2015
|
||||||
|
|
||||||
|
mtllib obj.mtl
|
||||||
|
|
||||||
|
o obj_0
|
||||||
|
v 4 3.5 4
|
||||||
|
v 4 3.568 3.482
|
||||||
|
v 4 3.768 3
|
||||||
|
v 4 4.086 2.586
|
||||||
|
v 4 4.5 2.268
|
||||||
|
v 4 4.982 2.068
|
||||||
|
v 4 5.5 2
|
||||||
|
v 4 6.018 2.068
|
||||||
|
v 4 6.5 2.268
|
||||||
|
v 4 6.914 2.586
|
||||||
|
v 4 7.232 3
|
||||||
|
v 4 7.432 3.482
|
||||||
|
v 4 7.5 4
|
||||||
|
v 4 7.432 4.518
|
||||||
|
v 4 7.232 5
|
||||||
|
v 4 6.914 5.414
|
||||||
|
v 4 6.5 5.732
|
||||||
|
v 4 6.018 5.932
|
||||||
|
v 4 5.5 6
|
||||||
|
v 4 4.982 5.932
|
||||||
|
v 4 4.5 5.732
|
||||||
|
v 4 4.086 5.414
|
||||||
|
v 4 3.768 5
|
||||||
|
v 4 3.568 4.518
|
||||||
|
v -8.308 4.5 7.952
|
||||||
|
v 1 4.7272 4.2072
|
||||||
|
v -8.587 4.5 7.81
|
||||||
|
v 1 4.7 4
|
||||||
|
v -8.81 4.5 7.587
|
||||||
|
v 1 4.8072 4.4
|
||||||
|
v -8.952 4.5 7.308
|
||||||
|
v 1 4.9344 4.5656
|
||||||
|
v -9 4.5 7.0043
|
||||||
|
v 0.952 6.5 7.308
|
||||||
|
v 1 5.1 4.6928
|
||||||
|
v 1 6.5 7.0043
|
||||||
|
v 0.81 6.5 7.587
|
||||||
|
v 1 5.2928 4.7728
|
||||||
|
v 0.587 6.5 7.81
|
||||||
|
v 0.308 6.5 7.952
|
||||||
|
v 1 5.5 4.8
|
||||||
|
v 0.0033 6.5 8
|
||||||
|
v 1 5.7072 4.7728
|
||||||
|
v 1 5.9 4.6928
|
||||||
|
v 1 6.0656 4.5656
|
||||||
|
v 1 6.1928 4.4
|
||||||
|
v 1 4.5 7.0043
|
||||||
|
v 0.952 4.5 7.308
|
||||||
|
v 0.81 4.5 7.587
|
||||||
|
v 0.587 4.5 7.81
|
||||||
|
v 0.308 4.5 7.952
|
||||||
|
v 1 6.2728 4.2072
|
||||||
|
v 0.0033 4.5 8
|
||||||
|
v 1 6.3 4
|
||||||
|
v -8.0043 4.5 0
|
||||||
|
v -8.952 4.5 0.692
|
||||||
|
v -9 4.5 0.9967
|
||||||
|
v 1 6.2728 3.7928
|
||||||
|
v -8.81 4.5 0.413
|
||||||
|
v -8.587 4.5 0.19
|
||||||
|
v 1 6.1928 3.6
|
||||||
|
v -8.308 4.5 0.048
|
||||||
|
v 1 6.0656 3.4344
|
||||||
|
v 1 5.9 3.3072
|
||||||
|
v -8.0043 6.5 8
|
||||||
|
v -8.308 6.5 7.952
|
||||||
|
v 1 5.7072 3.2272
|
||||||
|
v -8.587 6.5 7.81
|
||||||
|
v 1 5.5 3.2
|
||||||
|
v -8.81 6.5 7.587
|
||||||
|
v -8.952 6.5 7.308
|
||||||
|
v 1 5.2928 3.2272
|
||||||
|
v -9 6.5 7.0043
|
||||||
|
v 1 5.1 3.3072
|
||||||
|
v 1 4.9344 3.4344
|
||||||
|
v 1 4.8072 3.6
|
||||||
|
v 1 4.7272 3.7928
|
||||||
|
v -9 6.5 0.9967
|
||||||
|
v -8.0043 4.5 8
|
||||||
|
v 0.81 6.5 0.413
|
||||||
|
v 0.587 6.5 0.19
|
||||||
|
v 0.952 6.5 0.692
|
||||||
|
v 1 6.5 0.9967
|
||||||
|
v 0.0033 6.5 0
|
||||||
|
v 0.0033 4.5 0
|
||||||
|
v 0.308 4.5 0.048
|
||||||
|
v -8.0043 6.5 0
|
||||||
|
v 0.587 4.5 0.19
|
||||||
|
v 0.81 4.5 0.413
|
||||||
|
v 0.952 4.5 0.692
|
||||||
|
v 1 4.5 0.9967
|
||||||
|
v -8.952 6.5 0.692
|
||||||
|
v -8.81 6.5 0.413
|
||||||
|
v -8.587 6.5 0.19
|
||||||
|
v -8.308 6.5 0.048
|
||||||
|
v 0.308 6.5 0.048
|
||||||
|
# 96 vertices
|
||||||
|
|
||||||
|
g group_0_8273816
|
||||||
|
|
||||||
|
usemtl color_8273816
|
||||||
|
s 0
|
||||||
|
|
||||||
|
f 1 2 3
|
||||||
|
f 1 3 4
|
||||||
|
f 1 4 5
|
||||||
|
f 1 5 6
|
||||||
|
f 1 6 7
|
||||||
|
f 1 7 8
|
||||||
|
f 1 8 9
|
||||||
|
f 1 9 10
|
||||||
|
f 1 10 11
|
||||||
|
f 1 11 12
|
||||||
|
f 1 12 13
|
||||||
|
f 1 13 14
|
||||||
|
f 1 14 15
|
||||||
|
f 1 15 16
|
||||||
|
f 1 16 17
|
||||||
|
f 1 17 18
|
||||||
|
f 1 18 19
|
||||||
|
f 1 19 20
|
||||||
|
f 1 20 21
|
||||||
|
f 1 21 22
|
||||||
|
f 1 22 23
|
||||||
|
f 1 23 24
|
||||||
|
f 28 1 26
|
||||||
|
f 24 26 1
|
||||||
|
f 23 30 26
|
||||||
|
f 23 26 24
|
||||||
|
f 22 32 30
|
||||||
|
f 22 30 23
|
||||||
|
f 32 22 35
|
||||||
|
f 21 35 22
|
||||||
|
f 49 48 37
|
||||||
|
f 35 21 38
|
||||||
|
f 20 38 21
|
||||||
|
f 19 41 38
|
||||||
|
f 19 38 20
|
||||||
|
f 41 19 43
|
||||||
|
f 18 43 19
|
||||||
|
f 34 48 36
|
||||||
|
f 17 44 43
|
||||||
|
f 17 43 18
|
||||||
|
f 34 37 48
|
||||||
|
f 16 45 44
|
||||||
|
f 16 44 17
|
||||||
|
f 49 37 39
|
||||||
|
f 45 16 46
|
||||||
|
f 15 46 16
|
||||||
|
f 53 40 42
|
||||||
|
f 36 48 47
|
||||||
|
f 49 39 50
|
||||||
|
f 51 50 40
|
||||||
|
f 39 40 50
|
||||||
|
f 46 15 52
|
||||||
|
f 14 52 15
|
||||||
|
f 40 53 51
|
||||||
|
f 52 54 36
|
||||||
|
f 83 36 54
|
||||||
|
f 54 58 83
|
||||||
|
f 58 61 83
|
||||||
|
f 61 63 83
|
||||||
|
f 93 60 59
|
||||||
|
f 64 83 63
|
||||||
|
f 69 72 91
|
||||||
|
f 72 74 91
|
||||||
|
f 73 33 71
|
||||||
|
f 74 75 91
|
||||||
|
f 75 76 91
|
||||||
|
f 25 65 66
|
||||||
|
f 66 68 27
|
||||||
|
f 66 27 25
|
||||||
|
f 29 27 70
|
||||||
|
f 68 70 27
|
||||||
|
f 91 76 77
|
||||||
|
f 91 77 28
|
||||||
|
f 70 71 31
|
||||||
|
f 70 31 29
|
||||||
|
f 71 33 31
|
||||||
|
f 65 25 79
|
||||||
|
f 57 33 78
|
||||||
|
f 73 78 33
|
||||||
|
f 42 65 53
|
||||||
|
f 53 65 79
|
||||||
|
f 81 88 86
|
||||||
|
f 80 89 88
|
||||||
|
f 80 88 81
|
||||||
|
f 89 80 90
|
||||||
|
f 82 90 80
|
||||||
|
f 82 83 90
|
||||||
|
f 91 90 83
|
||||||
|
f 84 86 85
|
||||||
|
f 55 87 85
|
||||||
|
f 84 85 87
|
||||||
|
f 95 87 55
|
||||||
|
f 86 91 85
|
||||||
|
f 88 91 86
|
||||||
|
f 89 90 88
|
||||||
|
f 91 88 90
|
||||||
|
f 92 56 78
|
||||||
|
f 93 56 92
|
||||||
|
f 94 60 93
|
||||||
|
f 94 95 60
|
||||||
|
f 62 60 95
|
||||||
|
f 56 55 57
|
||||||
|
f 91 57 85
|
||||||
|
f 62 95 55
|
||||||
|
f 55 85 57
|
||||||
|
f 59 55 56
|
||||||
|
f 60 62 59
|
||||||
|
f 55 59 62
|
||||||
|
f 78 56 57
|
||||||
|
f 93 59 56
|
||||||
|
f 29 31 27
|
||||||
|
f 33 27 31
|
||||||
|
f 27 33 25
|
||||||
|
f 13 54 52
|
||||||
|
f 13 52 14
|
||||||
|
f 54 13 58
|
||||||
|
f 12 58 13
|
||||||
|
f 49 50 57
|
||||||
|
f 51 57 50
|
||||||
|
f 53 57 51
|
||||||
|
f 79 33 53
|
||||||
|
f 61 58 11
|
||||||
|
f 12 11 58
|
||||||
|
f 63 61 10
|
||||||
|
f 11 10 61
|
||||||
|
f 10 9 64
|
||||||
|
f 10 64 63
|
||||||
|
f 49 57 48
|
||||||
|
f 91 47 57
|
||||||
|
f 25 33 79
|
||||||
|
f 57 53 33
|
||||||
|
f 47 48 57
|
||||||
|
f 9 8 67
|
||||||
|
f 9 67 64
|
||||||
|
f 96 73 81
|
||||||
|
f 84 73 96
|
||||||
|
f 69 67 7
|
||||||
|
f 8 7 67
|
||||||
|
f 80 81 73
|
||||||
|
f 7 6 72
|
||||||
|
f 7 72 69
|
||||||
|
f 74 72 5
|
||||||
|
f 6 5 72
|
||||||
|
f 93 92 94
|
||||||
|
f 78 94 92
|
||||||
|
f 95 78 87
|
||||||
|
f 65 42 83
|
||||||
|
f 75 74 4
|
||||||
|
f 5 4 74
|
||||||
|
f 66 83 68
|
||||||
|
f 70 68 83
|
||||||
|
f 4 3 76
|
||||||
|
f 4 76 75
|
||||||
|
f 39 37 40
|
||||||
|
f 42 40 37
|
||||||
|
f 42 37 34
|
||||||
|
f 3 2 77
|
||||||
|
f 3 77 76
|
||||||
|
f 34 36 42
|
||||||
|
f 28 77 1
|
||||||
|
f 2 1 77
|
||||||
|
f 36 83 42
|
||||||
|
f 65 83 66
|
||||||
|
f 71 70 83
|
||||||
|
f 73 71 83
|
||||||
|
f 82 80 73
|
||||||
|
f 83 82 73
|
||||||
|
f 87 78 84
|
||||||
|
f 94 78 95
|
||||||
|
f 73 84 78
|
||||||
|
f 96 86 84
|
||||||
|
f 81 86 96
|
||||||
|
f 67 69 83
|
||||||
|
f 64 67 83
|
||||||
|
f 69 91 83
|
||||||
|
f 91 28 47
|
||||||
|
f 28 26 47
|
||||||
|
f 26 30 47
|
||||||
|
f 30 32 47
|
||||||
|
f 38 41 47
|
||||||
|
f 32 35 47
|
||||||
|
f 35 38 47
|
||||||
|
f 41 36 47
|
||||||
|
f 41 43 36
|
||||||
|
f 43 44 36
|
||||||
|
f 44 45 36
|
||||||
|
f 45 46 36
|
||||||
|
f 46 52 36
|
||||||
|
# 188 faces
|
||||||
|
|
||||||
|
#end of obj_0
|
||||||
|
|
||||||
10
public/assets/objects.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"objects": [
|
||||||
|
{
|
||||||
|
"name": "Server",
|
||||||
|
"id": "server",
|
||||||
|
"type": "glb",
|
||||||
|
"path": "models/server_racking_system.glb"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
public/assets/screenshot1.png
Normal file
|
After Width: | Height: | Size: 260 KiB |
BIN
public/assets/screenshot2.png
Normal file
|
After Width: | Height: | Size: 291 KiB |
BIN
public/assets/screenshot3.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
public/assets/sounds/noise.mp3
Normal file
BIN
public/assets/sounds/untitled.mp3
Normal file
BIN
public/assets/textures/arrow.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
public/assets/textures/flare.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
BIN
public/assets/textures/grassnormal.png
Normal file
|
After Width: | Height: | Size: 821 KiB |
BIN
public/assets/textures/keyboard.jpg
Normal file
|
After Width: | Height: | Size: 8.8 MiB |
BIN
public/assets/textures/keyboardhelp.jpg
Normal file
|
After Width: | Height: | Size: 8.8 MiB |
BIN
public/assets/textures/keyboardhelp2.jpg
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
public/assets/textures/mouse.jpg
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
public/assets/textures/mousehelp.jpg
Normal file
|
After Width: | Height: | Size: 65 KiB |
BIN
public/assets/textures/rockford.jpeg
Normal file
|
After Width: | Height: | Size: 688 KiB |
3
public/b2b2c.csv
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
"ID","NAME","POSITION-X","POSITION-Y","POSITION-Z", "ROTATION-X","ROTATION-Y","ROTATION-Z", "SCALE-X","SCALE-Y","SCALE-Z"
|
||||||
|
"id2533e7b6-118a-46d0-bad9-11e73462798b","Commerce",1,0,0,0,0,0,.1,.1,.1,
|
||||||
|
,Platform,1.3,0,0,0,0,0,.1,.1,.1,
|
||||||
|
Can't render this file because it has a wrong number of fields in line 2.
|
|
Before Width: | Height: | Size: 32 KiB |
@ -1,16 +1,17 @@
|
|||||||
{
|
{
|
||||||
"name": "Deep Diagram",
|
"name": "Deep Diagram",
|
||||||
"display": "standalone",
|
"display": "standalone",
|
||||||
"start_url": "https://deepdiagram.com",
|
"start_url": "https://www.deepdiagram.com",
|
||||||
"scope": "https://deepdiagram.com",
|
"id": "com.deepdiagram",
|
||||||
|
"scope": "https://www.deepdiagram.com",
|
||||||
"short_name": "Deep Diagram",
|
"short_name": "Deep Diagram",
|
||||||
"theme_color": "#000000",
|
"theme_color": "#000000",
|
||||||
"background_color": "#000000",
|
"background_color": "#000000",
|
||||||
"description": "Immersive diagraming tool to dig into deeper meaning behind your ideas",
|
"description": "An immersive mind mapping and diagramming tool that lets you create, share, and visualize data on the Oculus Quest and desktop browsers.",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "/assets/android-icon-192x192.png",
|
"src": "/assets/Android.png",
|
||||||
"sizes": "192x192",
|
"sizes": "196x196",
|
||||||
"type": "image/png",
|
"type": "image/png",
|
||||||
"purpose": "any"
|
"purpose": "any"
|
||||||
},
|
},
|
||||||
@ -28,22 +29,22 @@
|
|||||||
],
|
],
|
||||||
"screenshots": [
|
"screenshots": [
|
||||||
{
|
{
|
||||||
"src": "/assets/com.oculus.browser-20230121-110512.jpg",
|
"src": "/assets/screenshot1.png",
|
||||||
"sizes": "1024x1024",
|
"sizes": "1024x1024",
|
||||||
"type": "image/jpeg",
|
"type": "image/png",
|
||||||
"label": "Example 1"
|
"label": "Example of a few boxes on the web"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/assets/com.oculus.browser-20230121-110746.jpg",
|
"src": "/assets/screenshot2.png",
|
||||||
"sizes": "1024x1024",
|
"sizes": "1024x1024",
|
||||||
"type": "image/jpeg",
|
"type": "image/png",
|
||||||
"label": "Example 2"
|
"label": "Example of menus on the web"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "/assets/com.oculus.browser-20230121-111323.jpg",
|
"src": "/assets/screenshot3.png",
|
||||||
"sizes": "1024x1024",
|
"sizes": "1024x1024",
|
||||||
"type": "image/jpeg",
|
"type": "image/png",
|
||||||
"label": "Complex Architecture Diagram"
|
"label": "Example showing the web app on the Oculus Quest"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
141
public/pages/privacy.html
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
<html lang="">
|
||||||
|
<head>
|
||||||
|
<title>Deep Diagram Privacy Policy</title>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta content="width=device-width, initial-scale=1" name="viewport"/>
|
||||||
|
<meta content="width=device-width, initial-scale=1, height=device-height" name="viewport">
|
||||||
|
<link href="/styles.css" rel="stylesheet">
|
||||||
|
<link href="/assets/favicon-32x32.png" rel="icon" sizes="32x32" type="image/png">
|
||||||
|
<link href="/assets/favicon-16x16.png" rel="icon" sizes="16x16" type="image/png">
|
||||||
|
<link href="/assets/favicon-96x96.png" rel="icon" sizes="96x96" type="image/png">
|
||||||
|
<link as="script" href="/newRelic.js" rel="preload">
|
||||||
|
<script src="/newRelic.js"></script>
|
||||||
|
<style>
|
||||||
|
body * {
|
||||||
|
color: #FFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#privacyPolicy {
|
||||||
|
z-index: 1;
|
||||||
|
position: absolute;
|
||||||
|
width: 80%;
|
||||||
|
height: 80%;
|
||||||
|
top: 9%;
|
||||||
|
left: 9%;
|
||||||
|
overflow-y: scroll;
|
||||||
|
background-color: rgba(0, 0, .2, 0.6);
|
||||||
|
padding: 40px;
|
||||||
|
border-radius: 30px;
|
||||||
|
border-color: #FFD700;
|
||||||
|
border-style: solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: #FFf0d0;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
font-size: smaller;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<img alt="background grid" id="loadingGrid" src="/assets/grid3.jpg"/>
|
||||||
|
<div id="privacyPolicy">
|
||||||
|
<p>
|
||||||
|
This privacy policy ("policy") will help you understand how Immersive Idea LLC. ("us", "we",
|
||||||
|
"our") uses and protects the data you provide to us when you visit and use https://www.deepdiagram.com
|
||||||
|
website.
|
||||||
|
</p>
|
||||||
|
We reserve the right to change this policy at any given time, of which you will be
|
||||||
|
promptly updated. If you want to make sure that you are up to date with the latest
|
||||||
|
changes, we advise you to frequently visit this page.
|
||||||
|
<p>
|
||||||
|
<h1>What User Data We Collect</h1>
|
||||||
|
<p>
|
||||||
|
When you visit the website, we may collect the following data:
|
||||||
|
<ul>
|
||||||
|
<li>Your IP address.</li>
|
||||||
|
<li>Your contact information and email address.</li>
|
||||||
|
<li>Other information such as interests and preferences.</li>
|
||||||
|
<li>Data profile regarding your online behavior on our website.</li>
|
||||||
|
</ul>
|
||||||
|
<h1>Why We Collect Your Data</h1>
|
||||||
|
<p>
|
||||||
|
We are collecting your data for several reasons:
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>To better understand your needs.</li>
|
||||||
|
<li>To improve our services and products.</li>
|
||||||
|
<li>To send you promotional emails containing the information we think you will find interesting.</li>
|
||||||
|
<li>To contact you to fill out surveys and participate in other types of market
|
||||||
|
research.
|
||||||
|
</li>
|
||||||
|
<li>To customize our website according to your online behavior and personal
|
||||||
|
preferences.
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<h1>
|
||||||
|
Safeguarding and Securing the Data
|
||||||
|
</h1>
|
||||||
|
<p>Immersive Idea LLC is committed to securing your data and keeping it confidential.</p>
|
||||||
|
<p>Immersive Idea LLC has done all in its power to prevent data theft, unauthorized access,
|
||||||
|
and disclosure by implementing the latest technologies and software, which help us
|
||||||
|
safeguard all the information we collect online.
|
||||||
|
</p>
|
||||||
|
<h1>Our Cookie Policy</h1>
|
||||||
|
<p>
|
||||||
|
Once you agree to allow our website to use cookies, you also agree to use the data it
|
||||||
|
collects regarding your online behavior (analyze web traffic, web pages you visit and
|
||||||
|
spend the most time on).
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
The data we collect by using cookies is used to customize our website to your needs.
|
||||||
|
After we use the data for statistical analysis, the data is completely removed from our
|
||||||
|
systems.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Please note that cookies don't allow us to gain control of your computer in any way.
|
||||||
|
They are strictly used to monitor which pages you find useful and which you do not so
|
||||||
|
that we can provide a better experience for you.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
|
||||||
|
If you want to disable cookies, you can do it by accessing the settings of your internet
|
||||||
|
browser. You can visit https://www.internetcookies.com, which contains comprehensive
|
||||||
|
information on how to do this on a wide variety of browsers and devices.
|
||||||
|
</p>
|
||||||
|
<h1>Links to Other Websites</h1>
|
||||||
|
<p>
|
||||||
|
Our website may contain links that lead to other websites. If you click on these links
|
||||||
|
Immersive Idea LLC is not held responsible for your data and privacy protection. Visiting
|
||||||
|
those websites is not governed by this privacy policy agreement. Make sure to read the
|
||||||
|
privacy policy documentation of the website you go to from our website.
|
||||||
|
</p>
|
||||||
|
<h1>
|
||||||
|
Restricting the Collection of your Personal Data
|
||||||
|
</h1>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
|
||||||
|
At some point, you might wish to restrict the use and collection of your personal data.
|
||||||
|
You can achieve this by doing the following:</p>
|
||||||
|
<p>
|
||||||
|
When you are filling the forms on the website, make sure to check if there is a box
|
||||||
|
which you can leave unchecked, if you don't want to disclose your personal information.
|
||||||
|
If you have already agreed to share your information with us, feel free to contact us via
|
||||||
|
email (support@immersiveidea.com) and we will be more than happy to change this for you.</p>
|
||||||
|
<p>
|
||||||
|
Immersive Idea LLC will not lease, sell or distribute your personal information to any third
|
||||||
|
parties, unless we have your permission. We might do so if the law forces us. Your
|
||||||
|
personal information will be used when we need to send you promotional materials if
|
||||||
|
you agree to this privacy policy.
|
||||||
|
</p>
|
||||||
|
<a href="https://www.deepdiagram.com">Home</a>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 630 KiB |
35
public/styles.css
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
body {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background-color: #000;
|
||||||
|
background-image: url("/assets/grid6.jpg");
|
||||||
|
aspect-ratio: auto;
|
||||||
|
font-family: Roboto, sans-serif;
|
||||||
|
font-size: large;
|
||||||
|
color: #4444ee;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.scene {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gameCanvas {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#loadingGrid {
|
||||||
|
position: relative;
|
||||||
|
z-index: -1;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
88
public/sw.js
@ -1,12 +1,25 @@
|
|||||||
const VERSION = '0';
|
importScripts('https://storage.googleapis.com/workbox-cdn/releases/7.1.0/workbox-sw.js');
|
||||||
const CACHE = "pwabuilder-offline";
|
const VERSION = '0.0.8-19';
|
||||||
const PRECACHE_ASSETS = [
|
const CACHE = "deepdiagram";
|
||||||
'/grass1.jpeg',
|
const IMAGEDELIVERY_CACHE = "deepdiagram-images";
|
||||||
'/loading-loading-forever.gif',
|
const MAPTILE_CACHE = 'maptiler';
|
||||||
'/outdoor_field2.jpeg'
|
|
||||||
]
|
|
||||||
importScripts('https://storage.googleapis.com/workbox-cdn/releases/6.4.1/workbox-sw.js');
|
|
||||||
|
|
||||||
|
// TODO: replace the following with the correct offline fallback page i.e.: const offlineFallbackPage = "offline.html";
|
||||||
|
const offlineFallbackPage = "/";
|
||||||
|
/*self.addEventListener('install', async (event) => {
|
||||||
|
self.skipWaiting();
|
||||||
|
});
|
||||||
|
self.addEventListener('activate', async (event) => {
|
||||||
|
self.skipWaiting();
|
||||||
|
self.clients.matchAll({
|
||||||
|
type: 'window'
|
||||||
|
}).then(windowClients => {
|
||||||
|
windowClients.forEach((windowClient) => {
|
||||||
|
windowClient.navigate(windowClient.url);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
*/
|
||||||
self.addEventListener("message", (event) => {
|
self.addEventListener("message", (event) => {
|
||||||
if (event.data && event.data.type === "SKIP_WAITING") {
|
if (event.data && event.data.type === "SKIP_WAITING") {
|
||||||
self.skipWaiting();
|
self.skipWaiting();
|
||||||
@ -14,33 +27,74 @@ self.addEventListener("message", (event) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
/*self.addEventListener('install', async (event) => {
|
||||||
|
event.waitUntil(
|
||||||
|
caches.open(CACHE)
|
||||||
|
.then((cache) => cache.add(offlineFallbackPage))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
*/
|
||||||
|
/*if (workbox.navigationPreload.isSupported()) {
|
||||||
|
workbox.navigationPreload.enable();
|
||||||
|
}*/
|
||||||
|
|
||||||
workbox.routing.registerRoute(
|
workbox.routing.registerRoute(
|
||||||
new RegExp('/.*\\.png'),
|
new RegExp('/.*\\.wasm'),
|
||||||
new workbox.strategies.StaleWhileRevalidate({
|
new workbox.strategies.StaleWhileRevalidate({
|
||||||
cacheName: CACHE
|
cacheName: CACHE
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
workbox.routing.registerRoute(
|
workbox.routing.registerRoute(
|
||||||
new RegExp('/.*\\.jpeg'),
|
new RegExp('.*api.maptiler.com/.*'),
|
||||||
|
new workbox.strategies.CacheFirst({
|
||||||
|
plugins: [
|
||||||
|
new workbox.expiration.ExpirationPlugin({
|
||||||
|
maxEntries: 256,
|
||||||
|
maxAgeSeconds: 60 * 60 * 24 * 30,
|
||||||
|
purgeOnQuotaError: true,
|
||||||
|
matchOptions: {
|
||||||
|
ignoreVary: true
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
new workbox.cacheableResponse.CacheableResponsePlugin({
|
||||||
|
statuses: [0, 200]
|
||||||
|
})
|
||||||
|
],
|
||||||
|
cacheName: MAPTILE_CACHE
|
||||||
|
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
/*workbox.routing.registerRoute(
|
||||||
|
new RegExp('/assets/.*'),
|
||||||
new workbox.strategies.StaleWhileRevalidate({
|
new workbox.strategies.StaleWhileRevalidate({
|
||||||
cacheName: CACHE
|
cacheName: CACHE
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
workbox.routing.registerRoute(
|
*/
|
||||||
new RegExp('/.*\\.jpg'),
|
|
||||||
|
/*workbox.routing.registerRoute(
|
||||||
|
new RegExp('/db/.*'),
|
||||||
new workbox.strategies.StaleWhileRevalidate({
|
new workbox.strategies.StaleWhileRevalidate({
|
||||||
cacheName: CACHE
|
cacheName: CACHE
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
workbox.routing.registerRoute(
|
workbox.routing.registerRoute(
|
||||||
new RegExp('/.*\\.glb'),
|
new RegExp('/.*\\.glb'),
|
||||||
new workbox.strategies.StaleWhileRevalidate({
|
new workbox.strategies.StaleWhileRevalidate({
|
||||||
cacheName: CACHE
|
cacheName: CACHE
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
/*
|
||||||
|
|
||||||
workbox.routing.registerRoute(
|
workbox.routing.registerRoute(
|
||||||
new RegExp('/login'),
|
new RegExp('/.*\\.css'),
|
||||||
new workbox.strategies.NetworkFirst()
|
new workbox.strategies.StaleWhileRevalidate({
|
||||||
)
|
cacheName: CACHE
|
||||||
|
})
|
||||||
|
);
|
||||||
|
*/
|
||||||
|
|
||||||
|
|||||||
245
public/templates/demo.json
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
{
|
||||||
|
"name": "demo",
|
||||||
|
"dbName": "demo",
|
||||||
|
"exportDate": "2025-11-20T14:32:58.031Z",
|
||||||
|
"version": "1.0",
|
||||||
|
"entities": [
|
||||||
|
{
|
||||||
|
"id": "id0c8fd8ad-7dc8-41fa-b61f-b22c2d2b3eb8",
|
||||||
|
"position": {
|
||||||
|
"x": 0.4000000059604645,
|
||||||
|
"y": 1,
|
||||||
|
"z": 2.9000000953674316
|
||||||
|
},
|
||||||
|
"rotation": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 3.141592653589793,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"last_seen": "2025-11-20T14:30:54.865Z",
|
||||||
|
"template": "#cylinder-template",
|
||||||
|
"scale": {
|
||||||
|
"x": 0.1,
|
||||||
|
"y": 0.1,
|
||||||
|
"z": 0.1
|
||||||
|
},
|
||||||
|
"color": "#FF00FF",
|
||||||
|
"text": "db",
|
||||||
|
"_id": "id0c8fd8ad-7dc8-41fa-b61f-b22c2d2b3eb8"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from": "ide476ec05-9aac-42c9-87f1-ba7f18141767",
|
||||||
|
"to": "idb75dff6c-e056-4a15-b179-adfb2bec793a",
|
||||||
|
"type": "entity",
|
||||||
|
"template": "#connection-template",
|
||||||
|
"color": "#000000",
|
||||||
|
"id": "id18bf9938-a0b7-4e65-bf91-38697064698a",
|
||||||
|
"_id": "id18bf9938-a0b7-4e65-bf91-38697064698a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from": "id5c0dab44-2ef8-406e-b0ca-3aeea5b820b9",
|
||||||
|
"to": "idb75dff6c-e056-4a15-b179-adfb2bec793a",
|
||||||
|
"type": "entity",
|
||||||
|
"template": "#connection-template",
|
||||||
|
"color": "#000000",
|
||||||
|
"id": "id47bf19b7-6263-44fa-b289-1f9f63aa6aff",
|
||||||
|
"_id": "id47bf19b7-6263-44fa-b289-1f9f63aa6aff"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from": "id5c0dab44-2ef8-406e-b0ca-3aeea5b820b9",
|
||||||
|
"to": "idac543949-c285-4f4b-ab07-d6fd2bbf7bb5",
|
||||||
|
"type": "entity",
|
||||||
|
"template": "#connection-template",
|
||||||
|
"color": "#000000",
|
||||||
|
"id": "id50594820-ace6-44c7-be5c-2b0747549c75",
|
||||||
|
"_id": "id50594820-ace6-44c7-be5c-2b0747549c75"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from": "id5c0dab44-2ef8-406e-b0ca-3aeea5b820b9",
|
||||||
|
"to": "id5c0dab44-2ef8-406e-b0ca-3aeea5b820b9",
|
||||||
|
"type": "entity",
|
||||||
|
"template": "#connection-template",
|
||||||
|
"color": "#000000",
|
||||||
|
"id": "id562bf787-0d11-413c-bc2d-194bf05275fd",
|
||||||
|
"_id": "id562bf787-0d11-413c-bc2d-194bf05275fd"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from": "idb75dff6c-e056-4a15-b179-adfb2bec793a",
|
||||||
|
"to": "id0c8fd8ad-7dc8-41fa-b61f-b22c2d2b3eb8",
|
||||||
|
"type": "entity",
|
||||||
|
"template": "#connection-template",
|
||||||
|
"color": "#000000",
|
||||||
|
"id": "id5b622c06-95f1-4d04-b023-b1a6851d2107",
|
||||||
|
"_id": "id5b622c06-95f1-4d04-b023-b1a6851d2107"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "id5c0dab44-2ef8-406e-b0ca-3aeea5b820b9",
|
||||||
|
"position": {
|
||||||
|
"x": 0.4000000059604645,
|
||||||
|
"y": 1.7000000476837158,
|
||||||
|
"z": 2.9000000953674316
|
||||||
|
},
|
||||||
|
"rotation": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 3.141592653589793,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"last_seen": "2025-11-20T14:28:52.061Z",
|
||||||
|
"template": "#sphere-template",
|
||||||
|
"text": "browser",
|
||||||
|
"scale": {
|
||||||
|
"x": 0.1,
|
||||||
|
"y": 0.1,
|
||||||
|
"z": 0.1
|
||||||
|
},
|
||||||
|
"color": "#8B4513",
|
||||||
|
"_id": "id5c0dab44-2ef8-406e-b0ca-3aeea5b820b9"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from": "idac543949-c285-4f4b-ab07-d6fd2bbf7bb5",
|
||||||
|
"to": "id5c0dab44-2ef8-406e-b0ca-3aeea5b820b9",
|
||||||
|
"type": "entity",
|
||||||
|
"template": "#connection-template",
|
||||||
|
"color": "#000000",
|
||||||
|
"id": "id6f4208a8-9b17-45a8-b030-83a80d7e09bf",
|
||||||
|
"_id": "id6f4208a8-9b17-45a8-b030-83a80d7e09bf"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from": "idf1cf90c7-cc2f-4ccc-9bd6-274752f5b66f",
|
||||||
|
"to": "id5c0dab44-2ef8-406e-b0ca-3aeea5b820b9",
|
||||||
|
"type": "entity",
|
||||||
|
"template": "#connection-template",
|
||||||
|
"color": "#000000",
|
||||||
|
"id": "id7205d022-db34-4705-8e3b-930ae0351376",
|
||||||
|
"_id": "id7205d022-db34-4705-8e3b-930ae0351376"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from": "idb75dff6c-e056-4a15-b179-adfb2bec793a",
|
||||||
|
"to": "ide476ec05-9aac-42c9-87f1-ba7f18141767",
|
||||||
|
"type": "entity",
|
||||||
|
"template": "#connection-template",
|
||||||
|
"color": "#000000",
|
||||||
|
"id": "id74b9b638-8148-4d98-a715-981f7aebd3bb",
|
||||||
|
"_id": "id74b9b638-8148-4d98-a715-981f7aebd3bb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from": "id0c8fd8ad-7dc8-41fa-b61f-b22c2d2b3eb8",
|
||||||
|
"to": "idb75dff6c-e056-4a15-b179-adfb2bec793a",
|
||||||
|
"type": "entity",
|
||||||
|
"template": "#connection-template",
|
||||||
|
"color": "#000000",
|
||||||
|
"id": "id7af34b2d-f790-45c9-9fce-6c627de1410e",
|
||||||
|
"_id": "id7af34b2d-f790-45c9-9fce-6c627de1410e"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from": "id5c0dab44-2ef8-406e-b0ca-3aeea5b820b9",
|
||||||
|
"to": "idf1cf90c7-cc2f-4ccc-9bd6-274752f5b66f",
|
||||||
|
"type": "entity",
|
||||||
|
"template": "#connection-template",
|
||||||
|
"color": "#000000",
|
||||||
|
"id": "id8bdd38de-6ac9-44c1-95e9-015ed85c0b7a",
|
||||||
|
"_id": "id8bdd38de-6ac9-44c1-95e9-015ed85c0b7a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "idac543949-c285-4f4b-ab07-d6fd2bbf7bb5",
|
||||||
|
"position": {
|
||||||
|
"x": -0.6000000238418579,
|
||||||
|
"y": 1.7000000476837158,
|
||||||
|
"z": 2.799999952316284
|
||||||
|
},
|
||||||
|
"rotation": {
|
||||||
|
"x": -2.4492937051703357e-16,
|
||||||
|
"y": 3.141592653589793,
|
||||||
|
"z": -2.4492937051703357e-16
|
||||||
|
},
|
||||||
|
"last_seen": "2025-11-20T14:32:19.261Z",
|
||||||
|
"template": "#box-template",
|
||||||
|
"text": "api",
|
||||||
|
"scale": {
|
||||||
|
"x": 0.1,
|
||||||
|
"y": 0.1,
|
||||||
|
"z": 0.1
|
||||||
|
},
|
||||||
|
"color": "#0000FF",
|
||||||
|
"_id": "idac543949-c285-4f4b-ab07-d6fd2bbf7bb5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "idb75dff6c-e056-4a15-b179-adfb2bec793a",
|
||||||
|
"position": {
|
||||||
|
"x": 0.4000000059604645,
|
||||||
|
"y": 1.2999999523162842,
|
||||||
|
"z": 2.9000000953674316
|
||||||
|
},
|
||||||
|
"rotation": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 3.141592653589793,
|
||||||
|
"z": 0
|
||||||
|
},
|
||||||
|
"last_seen": "2025-11-20T14:28:36.027Z",
|
||||||
|
"template": "#box-template",
|
||||||
|
"text": "server",
|
||||||
|
"scale": {
|
||||||
|
"x": 0.1,
|
||||||
|
"y": 0.1,
|
||||||
|
"z": 0.1
|
||||||
|
},
|
||||||
|
"color": "#006400",
|
||||||
|
"_id": "idb75dff6c-e056-4a15-b179-adfb2bec793a"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from": "idb75dff6c-e056-4a15-b179-adfb2bec793a",
|
||||||
|
"to": "id5c0dab44-2ef8-406e-b0ca-3aeea5b820b9",
|
||||||
|
"type": "entity",
|
||||||
|
"template": "#connection-template",
|
||||||
|
"color": "#000000",
|
||||||
|
"id": "idd6098a95-f534-4126-9aad-9948fdc724c6",
|
||||||
|
"_id": "idd6098a95-f534-4126-9aad-9948fdc724c6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ide476ec05-9aac-42c9-87f1-ba7f18141767",
|
||||||
|
"position": {
|
||||||
|
"x": -0.6000000238418579,
|
||||||
|
"y": 1.2999999523162842,
|
||||||
|
"z": 2.799999952316284
|
||||||
|
},
|
||||||
|
"rotation": {
|
||||||
|
"x": -2.4492931757747437e-16,
|
||||||
|
"y": 3.141592653589793,
|
||||||
|
"z": -6.429647808784774e-40
|
||||||
|
},
|
||||||
|
"last_seen": "2025-11-20T14:30:59.486Z",
|
||||||
|
"template": "#box-template",
|
||||||
|
"scale": {
|
||||||
|
"x": 0.1,
|
||||||
|
"y": 0.1,
|
||||||
|
"z": 0.1
|
||||||
|
},
|
||||||
|
"color": "#0000FF",
|
||||||
|
"text": "api",
|
||||||
|
"_id": "ide476ec05-9aac-42c9-87f1-ba7f18141767"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "idf1cf90c7-cc2f-4ccc-9bd6-274752f5b66f",
|
||||||
|
"position": {
|
||||||
|
"x": 0.4000000059604645,
|
||||||
|
"y": 2.200000047683716,
|
||||||
|
"z": 3
|
||||||
|
},
|
||||||
|
"rotation": {
|
||||||
|
"x": -2.4492937051703357e-16,
|
||||||
|
"y": 3.141592653589793,
|
||||||
|
"z": -2.4492937051703357e-16
|
||||||
|
},
|
||||||
|
"last_seen": "2025-11-20T14:28:58.876Z",
|
||||||
|
"template": "#person-template",
|
||||||
|
"text": "user",
|
||||||
|
"scale": {
|
||||||
|
"x": 0.1,
|
||||||
|
"y": 0.1,
|
||||||
|
"z": 0.1
|
||||||
|
},
|
||||||
|
"color": "#FFE4B5",
|
||||||
|
"_id": "idf1cf90c7-cc2f-4ccc-9bd6-274752f5b66f"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
public/voice/eagle_params.pv
Normal file
124
server.js
@ -1,15 +1,133 @@
|
|||||||
import express from "express";
|
import express from "express";
|
||||||
import ViteExpress from "vite-express";
|
import ViteExpress from "vite-express";
|
||||||
|
import cors from "cors";
|
||||||
import dotenv from "dotenv";
|
import dotenv from "dotenv";
|
||||||
import expressProxy from "express-http-proxy";
|
import newrelic from "newrelic";
|
||||||
|
import apiRoutes from "./server/api/index.js";
|
||||||
|
import { pouchApp, PouchDB } from "./server/services/databaseService.js";
|
||||||
|
import { dbAuthMiddleware } from "./server/middleware/dbAuth.js";
|
||||||
|
|
||||||
|
// Load .env.local first, then fall back to .env
|
||||||
|
dotenv.config({ path: '.env.local' });
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
|
// Console shim to forward logs to New Relic while preserving local output
|
||||||
|
const originalConsole = {
|
||||||
|
log: console.log.bind(console),
|
||||||
|
error: console.error.bind(console),
|
||||||
|
warn: console.warn.bind(console),
|
||||||
|
info: console.info.bind(console)
|
||||||
|
};
|
||||||
|
|
||||||
|
function forwardToNewRelic(level, args) {
|
||||||
|
const message = args.map(arg =>
|
||||||
|
typeof arg === 'object' ? JSON.stringify(arg) : String(arg)
|
||||||
|
).join(' ');
|
||||||
|
|
||||||
|
newrelic.recordLogEvent({
|
||||||
|
message,
|
||||||
|
level,
|
||||||
|
timestamp: Date.now()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log = (...args) => {
|
||||||
|
forwardToNewRelic('info', args);
|
||||||
|
originalConsole.log(...args);
|
||||||
|
};
|
||||||
|
|
||||||
|
console.error = (...args) => {
|
||||||
|
forwardToNewRelic('error', args);
|
||||||
|
originalConsole.error(...args);
|
||||||
|
};
|
||||||
|
|
||||||
|
console.warn = (...args) => {
|
||||||
|
forwardToNewRelic('warn', args);
|
||||||
|
originalConsole.warn(...args);
|
||||||
|
};
|
||||||
|
|
||||||
|
console.info = (...args) => {
|
||||||
|
forwardToNewRelic('info', args);
|
||||||
|
originalConsole.info(...args);
|
||||||
|
};
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
|
|
||||||
app.use("/api", expressProxy("local.immersiveidea.com"));
|
// CORS configuration for split deployment
|
||||||
|
// In combined mode, same-origin requests don't need CORS
|
||||||
|
const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(",") || [];
|
||||||
|
if (allowedOrigins.length > 0) {
|
||||||
|
app.use(cors({
|
||||||
|
origin: allowedOrigins,
|
||||||
|
credentials: true,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse JSON for all routes EXCEPT /pouchdb (express-pouchdb handles its own body parsing)
|
||||||
|
app.use((req, res, next) => {
|
||||||
|
if (req.path.startsWith('/pouchdb')) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
express.json()(req, res, next);
|
||||||
|
});
|
||||||
|
|
||||||
|
// API routes
|
||||||
|
app.use("/api", apiRoutes);
|
||||||
|
|
||||||
ViteExpress.listen(app, process.env.PORT || 3001, () => console.log("Server is listening..."));
|
// Test endpoint to verify PouchDB is working
|
||||||
|
app.get("/pouchdb-test/:dbname", async (req, res) => {
|
||||||
|
try {
|
||||||
|
const dbName = req.params.dbname;
|
||||||
|
console.log(`[Test] Creating database: ${dbName}`);
|
||||||
|
const db = new PouchDB(dbName);
|
||||||
|
const info = await db.info();
|
||||||
|
console.log(`[Test] Database info:`, info);
|
||||||
|
|
||||||
|
// Try to add a test doc
|
||||||
|
const result = await db.put({ _id: 'test-doc', hello: 'world' });
|
||||||
|
console.log(`[Test] Added doc:`, result);
|
||||||
|
|
||||||
|
// Read it back
|
||||||
|
const doc = await db.get('test-doc');
|
||||||
|
console.log(`[Test] Got doc:`, doc);
|
||||||
|
|
||||||
|
res.json({ success: true, info, doc });
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`[Test] Error:`, err);
|
||||||
|
res.status(500).json({ error: err.message, stack: err.stack });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// PouchDB database sync endpoint with auth middleware
|
||||||
|
// Public databases (/pouchdb/public-*) are accessible without auth
|
||||||
|
// Private databases (/pouchdb/private-*) require authentication
|
||||||
|
// Patch req.query for Express 5 compatibility with express-pouchdb
|
||||||
|
app.use("/pouchdb", dbAuthMiddleware, (req, res, next) => {
|
||||||
|
// Express 5 makes req.query read-only, but express-pouchdb needs to write to it
|
||||||
|
// Redefine as writable property
|
||||||
|
Object.defineProperty(req, 'query', {
|
||||||
|
value: { ...req.query },
|
||||||
|
writable: true,
|
||||||
|
configurable: true
|
||||||
|
});
|
||||||
|
next();
|
||||||
|
}, pouchApp, (err, req, res, next) => {
|
||||||
|
|
||||||
|
console.error('[PouchDB Error]', err);
|
||||||
|
res.status(500).json({ error: err.message, stack: err.stack });
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if running in API-only mode (split deployment)
|
||||||
|
const apiOnly = process.env.API_ONLY === "true";
|
||||||
|
|
||||||
|
if (apiOnly) {
|
||||||
|
// API-only mode: no static file serving
|
||||||
|
app.listen(process.env.PORT || 3000, () => {
|
||||||
|
console.log(`API server running on port ${process.env.PORT || 3000}`);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Combined mode: Vite handles static files + SPA
|
||||||
|
ViteExpress.listen(app, process.env.PORT || 3001, () => {
|
||||||
|
console.log(`Server running on port ${process.env.PORT || 3001}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
178
server/api/claude.js
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
import { Router } from "express";
|
||||||
|
import { getSession, addMessage, getConversationForAPI } from "../services/sessionStore.js";
|
||||||
|
import { trackUsage, getUsageSummary, formatCost, getSessionUsage } from "../services/usageTracker.js";
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
const ANTHROPIC_API_URL = "https://api.anthropic.com";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build entity context string for the system prompt
|
||||||
|
*/
|
||||||
|
function buildEntityContext(entities) {
|
||||||
|
if (!entities || entities.length === 0) {
|
||||||
|
return "\n\nThe diagram is currently empty.";
|
||||||
|
}
|
||||||
|
|
||||||
|
const entityList = entities.map(e => {
|
||||||
|
const shape = e.template?.replace('#', '').replace('-template', '') || 'unknown';
|
||||||
|
const pos = e.position || { x: 0, y: 0, z: 0 };
|
||||||
|
return `- ${e.text || '(no label)'} (${shape}, ${e.color || 'unknown'}) at (${pos.x?.toFixed(1)}, ${pos.y?.toFixed(1)}, ${pos.z?.toFixed(1)})`;
|
||||||
|
}).join('\n');
|
||||||
|
|
||||||
|
return `\n\n## Current Diagram State\nThe diagram currently contains ${entities.length} entities:\n${entityList}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Express 5 uses named parameters for wildcards
|
||||||
|
router.post("/*path", async (req, res) => {
|
||||||
|
const requestStart = Date.now();
|
||||||
|
console.log(`[Claude API] ========== REQUEST START ==========`);
|
||||||
|
|
||||||
|
const apiKey = process.env.ANTHROPIC_API_KEY;
|
||||||
|
|
||||||
|
if (!apiKey) {
|
||||||
|
console.error(`[Claude API] ERROR: API key not configured`);
|
||||||
|
return res.status(500).json({ error: "API key not configured" });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the path after /api/claude (e.g., /v1/messages)
|
||||||
|
// Express 5 returns path segments as an array
|
||||||
|
const pathParam = req.params.path;
|
||||||
|
const path = "/" + (Array.isArray(pathParam) ? pathParam.join("/") : pathParam || "");
|
||||||
|
console.log(`[Claude API] Path: ${path}`);
|
||||||
|
|
||||||
|
// Check for session-based request
|
||||||
|
const { sessionId, ...requestBody } = req.body;
|
||||||
|
let modifiedBody = requestBody;
|
||||||
|
console.log(`[Claude API] Session ID: ${sessionId || 'none'}`);
|
||||||
|
console.log(`[Claude API] Model: ${requestBody.model}`);
|
||||||
|
console.log(`[Claude API] Messages count: ${requestBody.messages?.length || 0}`);
|
||||||
|
|
||||||
|
if (sessionId) {
|
||||||
|
const session = getSession(sessionId);
|
||||||
|
if (session) {
|
||||||
|
console.log(`[Claude API] Session found: ${session.entities.length} entities, ${session.conversationHistory.length} messages in history`);
|
||||||
|
|
||||||
|
// Inject entity context into system prompt
|
||||||
|
if (modifiedBody.system) {
|
||||||
|
const entityContext = buildEntityContext(session.entities);
|
||||||
|
console.log(`[Claude API] Entity context added (${entityContext.length} chars)`);
|
||||||
|
modifiedBody.system += entityContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get conversation history and merge with current messages
|
||||||
|
const historyMessages = getConversationForAPI(sessionId);
|
||||||
|
if (historyMessages.length > 0 && modifiedBody.messages) {
|
||||||
|
// Filter out any duplicate messages (in case client sent history too)
|
||||||
|
const currentContent = modifiedBody.messages[modifiedBody.messages.length - 1]?.content;
|
||||||
|
const filteredHistory = historyMessages.filter(msg => msg.content !== currentContent);
|
||||||
|
modifiedBody.messages = [...filteredHistory, ...modifiedBody.messages];
|
||||||
|
console.log(`[Claude API] Merged ${filteredHistory.length} history + ${modifiedBody.messages.length - filteredHistory.length} new = ${modifiedBody.messages.length} total messages`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(`[Claude API] WARNING: Session ${sessionId} not found`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log(`[Claude API] Sending request to Anthropic API...`);
|
||||||
|
const fetchStart = Date.now();
|
||||||
|
|
||||||
|
const response = await fetch(`${ANTHROPIC_API_URL}${path}`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"x-api-key": apiKey,
|
||||||
|
"anthropic-version": "2023-06-01",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(modifiedBody),
|
||||||
|
});
|
||||||
|
|
||||||
|
const fetchDuration = Date.now() - fetchStart;
|
||||||
|
console.log(`[Claude API] Response received in ${fetchDuration}ms, status: ${response.status}`);
|
||||||
|
|
||||||
|
console.log(`[Claude API] Parsing response JSON...`);
|
||||||
|
const data = await response.json();
|
||||||
|
console.log(`[Claude API] Response parsed. Stop reason: ${data.stop_reason}, content blocks: ${data.content?.length || 0}`);
|
||||||
|
|
||||||
|
// Track and log token usage
|
||||||
|
if (data.usage) {
|
||||||
|
// Extract content for detailed tracking
|
||||||
|
const userMessage = requestBody.messages?.[requestBody.messages.length - 1];
|
||||||
|
const inputText = typeof userMessage?.content === 'string' ? userMessage.content : null;
|
||||||
|
|
||||||
|
const outputText = data.content
|
||||||
|
?.filter(c => c.type === 'text')
|
||||||
|
.map(c => c.text)
|
||||||
|
.join('\n') || null;
|
||||||
|
|
||||||
|
const toolCalls = data.content
|
||||||
|
?.filter(c => c.type === 'tool_use')
|
||||||
|
.map(c => ({ name: c.name, input: c.input })) || [];
|
||||||
|
|
||||||
|
const usageRecord = trackUsage(sessionId, modifiedBody.model, data.usage, {
|
||||||
|
inputText,
|
||||||
|
outputText,
|
||||||
|
toolCalls
|
||||||
|
});
|
||||||
|
console.log(`[Claude API] REQUEST USAGE: ${getUsageSummary(usageRecord)}`);
|
||||||
|
|
||||||
|
// Log cumulative session usage if session exists
|
||||||
|
if (sessionId) {
|
||||||
|
const sessionStats = getSessionUsage(sessionId);
|
||||||
|
if (sessionStats) {
|
||||||
|
console.log(`[Claude API] SESSION TOTALS (${sessionStats.requestCount} requests):`);
|
||||||
|
console.log(`[Claude API] Total input: ${sessionStats.totalInputTokens} tokens`);
|
||||||
|
console.log(`[Claude API] Total output: ${sessionStats.totalOutputTokens} tokens`);
|
||||||
|
console.log(`[Claude API] Total cost: ${formatCost(sessionStats.totalCost)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.error) {
|
||||||
|
console.error(`[Claude API] API returned error:`, data.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If session exists and response is successful, store messages
|
||||||
|
if (sessionId && response.ok && data.content) {
|
||||||
|
const session = getSession(sessionId);
|
||||||
|
if (session) {
|
||||||
|
// Store the user message if it was new (only if it's a string, not tool results)
|
||||||
|
const userMessage = requestBody.messages?.[requestBody.messages.length - 1];
|
||||||
|
if (userMessage && userMessage.role === 'user' && typeof userMessage.content === 'string') {
|
||||||
|
addMessage(sessionId, {
|
||||||
|
role: 'user',
|
||||||
|
content: userMessage.content
|
||||||
|
});
|
||||||
|
console.log(`[Claude API] Stored user message to session`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the assistant response (text only, not tool use blocks)
|
||||||
|
const assistantContent = data.content
|
||||||
|
.filter(c => c.type === 'text')
|
||||||
|
.map(c => c.text)
|
||||||
|
.join('\n');
|
||||||
|
|
||||||
|
if (assistantContent) {
|
||||||
|
addMessage(sessionId, {
|
||||||
|
role: 'assistant',
|
||||||
|
content: assistantContent
|
||||||
|
});
|
||||||
|
console.log(`[Claude API] Stored assistant response to session (${assistantContent.length} chars)`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalDuration = Date.now() - requestStart;
|
||||||
|
console.log(`[Claude API] ========== REQUEST COMPLETE (${totalDuration}ms) ==========`);
|
||||||
|
res.status(response.status).json(data);
|
||||||
|
} catch (error) {
|
||||||
|
const totalDuration = Date.now() - requestStart;
|
||||||
|
console.error(`[Claude API] ========== REQUEST FAILED (${totalDuration}ms) ==========`);
|
||||||
|
console.error(`[Claude API] Error:`, error);
|
||||||
|
console.error(`[Claude API] Error message:`, error.message);
|
||||||
|
console.error(`[Claude API] Error stack:`, error.stack);
|
||||||
|
res.status(500).json({ error: "Failed to proxy request to Claude API", details: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
213
server/api/cloudflare.js
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
import { Router } from "express";
|
||||||
|
import { getSession, addMessage, getConversationForAPI } from "../services/sessionStore.js";
|
||||||
|
import { trackUsage, getUsageSummary, formatCost, getSessionUsage } from "../services/usageTracker.js";
|
||||||
|
import { getCloudflareAccountId, getCloudflareApiToken } from "../services/providerConfig.js";
|
||||||
|
import {
|
||||||
|
claudeToolsToCloudflare,
|
||||||
|
claudeMessagesToCloudflare,
|
||||||
|
cloudflareResponseToClaude
|
||||||
|
} from "../services/toolConverter.js";
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build entity context string for the system prompt
|
||||||
|
*/
|
||||||
|
function buildEntityContext(entities) {
|
||||||
|
if (!entities || entities.length === 0) {
|
||||||
|
return "\n\nThe diagram is currently empty.";
|
||||||
|
}
|
||||||
|
|
||||||
|
const entityList = entities.map(e => {
|
||||||
|
const shape = e.template?.replace('#', '').replace('-template', '') || 'unknown';
|
||||||
|
const pos = e.position || { x: 0, y: 0, z: 0 };
|
||||||
|
return `- ${e.text || '(no label)'} (${shape}, ${e.color || 'unknown'}) at (${pos.x?.toFixed(1)}, ${pos.y?.toFixed(1)}, ${pos.z?.toFixed(1)})`;
|
||||||
|
}).join('\n');
|
||||||
|
|
||||||
|
return `\n\n## Current Diagram State\nThe diagram currently contains ${entities.length} entities:\n${entityList}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Express 5 uses named parameters for wildcards
|
||||||
|
router.post("/*path", async (req, res) => {
|
||||||
|
const requestStart = Date.now();
|
||||||
|
console.log(`[Cloudflare API] ========== REQUEST START ==========`);
|
||||||
|
|
||||||
|
const accountId = getCloudflareAccountId();
|
||||||
|
const apiToken = getCloudflareApiToken();
|
||||||
|
|
||||||
|
if (!accountId) {
|
||||||
|
console.error(`[Cloudflare API] ERROR: Account ID not configured`);
|
||||||
|
return res.status(500).json({ error: "Cloudflare account ID not configured" });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!apiToken) {
|
||||||
|
console.error(`[Cloudflare API] ERROR: API token not configured`);
|
||||||
|
return res.status(500).json({ error: "Cloudflare API token not configured" });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for session-based request
|
||||||
|
const { sessionId, ...requestBody } = req.body;
|
||||||
|
let modifiedBody = { ...requestBody };
|
||||||
|
const model = requestBody.model;
|
||||||
|
|
||||||
|
console.log(`[Cloudflare API] Session ID: ${sessionId || 'none'}`);
|
||||||
|
console.log(`[Cloudflare API] Model: ${model}`);
|
||||||
|
console.log(`[Cloudflare API] Messages count: ${requestBody.messages?.length || 0}`);
|
||||||
|
|
||||||
|
// Build system prompt with entity context
|
||||||
|
let systemPrompt = modifiedBody.system || '';
|
||||||
|
|
||||||
|
if (sessionId) {
|
||||||
|
const session = getSession(sessionId);
|
||||||
|
if (session) {
|
||||||
|
console.log(`[Cloudflare API] Session found: ${session.entities.length} entities, ${session.conversationHistory.length} messages in history`);
|
||||||
|
|
||||||
|
// Inject entity context into system prompt
|
||||||
|
const entityContext = buildEntityContext(session.entities);
|
||||||
|
console.log(`[Cloudflare API] Entity context added (${entityContext.length} chars)`);
|
||||||
|
systemPrompt += entityContext;
|
||||||
|
|
||||||
|
// Get conversation history and merge with current messages
|
||||||
|
const historyMessages = getConversationForAPI(sessionId);
|
||||||
|
if (historyMessages.length > 0 && modifiedBody.messages) {
|
||||||
|
const currentContent = modifiedBody.messages[modifiedBody.messages.length - 1]?.content;
|
||||||
|
const filteredHistory = historyMessages.filter(msg => msg.content !== currentContent);
|
||||||
|
modifiedBody.messages = [...filteredHistory, ...modifiedBody.messages];
|
||||||
|
console.log(`[Cloudflare API] Merged ${filteredHistory.length} history + ${modifiedBody.messages.length - filteredHistory.length} new = ${modifiedBody.messages.length} total messages`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(`[Cloudflare API] WARNING: Session ${sessionId} not found`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Convert to Cloudflare format
|
||||||
|
const cfMessages = claudeMessagesToCloudflare(modifiedBody.messages || [], systemPrompt);
|
||||||
|
const cfTools = modifiedBody.tools ? claudeToolsToCloudflare(modifiedBody.tools) : undefined;
|
||||||
|
|
||||||
|
// Build Cloudflare request body
|
||||||
|
const cfRequestBody = {
|
||||||
|
messages: cfMessages,
|
||||||
|
max_tokens: modifiedBody.max_tokens || 1024
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only include tools if the model supports them
|
||||||
|
if (cfTools && cfTools.length > 0) {
|
||||||
|
cfRequestBody.tools = cfTools;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cloudflare endpoint: https://api.cloudflare.com/client/v4/accounts/{account_id}/ai/run/{model}
|
||||||
|
const endpoint = `https://api.cloudflare.com/client/v4/accounts/${accountId}/ai/run/${model}`;
|
||||||
|
|
||||||
|
console.log(`[Cloudflare API] Sending request to: ${endpoint}`);
|
||||||
|
console.log(`[Cloudflare API] Request body messages: ${cfMessages.length}, tools: ${cfTools?.length || 0}`);
|
||||||
|
const requestBodyJson = JSON.stringify(cfRequestBody);
|
||||||
|
console.log(`[Cloudflare API] Full request body (${requestBodyJson.length} bytes):`);
|
||||||
|
console.log(requestBodyJson);
|
||||||
|
const fetchStart = Date.now();
|
||||||
|
|
||||||
|
const response = await fetch(endpoint, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Authorization": `Bearer ${apiToken}`,
|
||||||
|
},
|
||||||
|
body: JSON.stringify(cfRequestBody),
|
||||||
|
});
|
||||||
|
|
||||||
|
const fetchDuration = Date.now() - fetchStart;
|
||||||
|
console.log(`[Cloudflare API] Response received in ${fetchDuration}ms, status: ${response.status}`);
|
||||||
|
|
||||||
|
console.log(`[Cloudflare API] Parsing response JSON...`);
|
||||||
|
const cfData = await response.json();
|
||||||
|
|
||||||
|
if (!cfData.success) {
|
||||||
|
console.error(`[Cloudflare API] API returned error:`, cfData.errors);
|
||||||
|
return res.status(response.status).json({
|
||||||
|
error: cfData.errors?.[0]?.message || "Cloudflare API error",
|
||||||
|
details: cfData.errors
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert Cloudflare response to Claude format
|
||||||
|
const data = cloudflareResponseToClaude(cfData, model);
|
||||||
|
console.log(`[Cloudflare API] Response converted. Stop reason: ${data.stop_reason}, content blocks: ${data.content?.length || 0}`);
|
||||||
|
|
||||||
|
// Track and log token usage
|
||||||
|
if (data.usage) {
|
||||||
|
// Extract content for detailed tracking
|
||||||
|
const userMessage = requestBody.messages?.[requestBody.messages.length - 1];
|
||||||
|
const inputText = typeof userMessage?.content === 'string' ? userMessage.content : null;
|
||||||
|
|
||||||
|
const outputText = data.content
|
||||||
|
?.filter(c => c.type === 'text')
|
||||||
|
.map(c => c.text)
|
||||||
|
.join('\n') || null;
|
||||||
|
|
||||||
|
const toolCalls = data.content
|
||||||
|
?.filter(c => c.type === 'tool_use')
|
||||||
|
.map(c => ({ name: c.name, input: c.input })) || [];
|
||||||
|
|
||||||
|
const usageRecord = trackUsage(sessionId, model, data.usage, {
|
||||||
|
inputText,
|
||||||
|
outputText,
|
||||||
|
toolCalls
|
||||||
|
});
|
||||||
|
console.log(`[Cloudflare API] REQUEST USAGE: ${getUsageSummary(usageRecord)}`);
|
||||||
|
|
||||||
|
// Log cumulative session usage if session exists
|
||||||
|
if (sessionId) {
|
||||||
|
const sessionStats = getSessionUsage(sessionId);
|
||||||
|
if (sessionStats) {
|
||||||
|
console.log(`[Cloudflare API] SESSION TOTALS (${sessionStats.requestCount} requests):`);
|
||||||
|
console.log(`[Cloudflare API] Total input: ${sessionStats.totalInputTokens} tokens`);
|
||||||
|
console.log(`[Cloudflare API] Total output: ${sessionStats.totalOutputTokens} tokens`);
|
||||||
|
console.log(`[Cloudflare API] Total cost: ${formatCost(sessionStats.totalCost)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If session exists and response is successful, store messages
|
||||||
|
if (sessionId && response.ok && data.content) {
|
||||||
|
const session = getSession(sessionId);
|
||||||
|
if (session) {
|
||||||
|
// Store the user message if it was new (only if it's a string, not tool results)
|
||||||
|
const userMessage = requestBody.messages?.[requestBody.messages.length - 1];
|
||||||
|
if (userMessage && userMessage.role === 'user' && typeof userMessage.content === 'string') {
|
||||||
|
addMessage(sessionId, {
|
||||||
|
role: 'user',
|
||||||
|
content: userMessage.content
|
||||||
|
});
|
||||||
|
console.log(`[Cloudflare API] Stored user message to session`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the assistant response (text only, not tool use blocks)
|
||||||
|
const assistantContent = data.content
|
||||||
|
.filter(c => c.type === 'text')
|
||||||
|
.map(c => c.text)
|
||||||
|
.join('\n');
|
||||||
|
|
||||||
|
if (assistantContent) {
|
||||||
|
addMessage(sessionId, {
|
||||||
|
role: 'assistant',
|
||||||
|
content: assistantContent
|
||||||
|
});
|
||||||
|
console.log(`[Cloudflare API] Stored assistant response to session (${assistantContent.length} chars)`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalDuration = Date.now() - requestStart;
|
||||||
|
console.log(`[Cloudflare API] ========== REQUEST COMPLETE (${totalDuration}ms) ==========`);
|
||||||
|
res.status(response.status).json(data);
|
||||||
|
} catch (error) {
|
||||||
|
const totalDuration = Date.now() - requestStart;
|
||||||
|
console.error(`[Cloudflare API] ========== REQUEST FAILED (${totalDuration}ms) ==========`);
|
||||||
|
console.error(`[Cloudflare API] Error:`, error);
|
||||||
|
console.error(`[Cloudflare API] Error message:`, error.message);
|
||||||
|
console.error(`[Cloudflare API] Error stack:`, error.stack);
|
||||||
|
res.status(500).json({ error: "Failed to proxy request to Cloudflare API", details: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||
30
server/api/index.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { Router } from "express";
|
||||||
|
import claudeRouter from "./claude.js";
|
||||||
|
import ollamaRouter from "./ollama.js";
|
||||||
|
import cloudflareRouter from "./cloudflare.js";
|
||||||
|
import sessionRouter from "./session.js";
|
||||||
|
import userRouter from "./user.js";
|
||||||
|
|
||||||
|
const router = Router();
|
||||||
|
|
||||||
|
// Session management
|
||||||
|
router.use("/session", sessionRouter);
|
||||||
|
|
||||||
|
// User features
|
||||||
|
router.use("/user", userRouter);
|
||||||
|
|
||||||
|
// Claude API proxy
|
||||||
|
router.use("/claude", claudeRouter);
|
||||||
|
|
||||||
|
// Ollama API proxy
|
||||||
|
router.use("/ollama", ollamaRouter);
|
||||||
|
|
||||||
|
// Cloudflare Workers AI proxy
|
||||||
|
router.use("/cloudflare", cloudflareRouter);
|
||||||
|
|
||||||
|
// Health check
|
||||||
|
router.get("/health", (req, res) => {
|
||||||
|
res.json({ status: "ok" });
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
||||||