feat: skeleton loaders, Sentry source maps, auto-deploy via webhook
RoomSkeleton: shimmer skeleton matching Room header/timeline/input layout, used as Suspense fallback for all three Room routes (home/direct/space) Sentry source maps: @sentry/vite-plugin uploads 72 hidden source map files to Sentry on each build then deletes them from dist — stack traces now show real file/line numbers instead of minified bundle positions. Auth token loaded from /etc/lotus-deploy.env (not in git). Auto-deploy: webhook receiver on port 9001, nginx proxies /hooks/lotus-deploy, HMAC-SHA256 verified, triggers on lotus branch push. Deploy script: git reset --hard + npm ci + npm run build + rsync to webroot. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Generated
+359
-2
@@ -75,6 +75,7 @@
|
||||
"@rollup/plugin-wasm": "6.1.1",
|
||||
"@semantic-release/exec": "7.1.0",
|
||||
"@semantic-release/git": "10.0.1",
|
||||
"@sentry/vite-plugin": "5.3.0",
|
||||
"@types/chroma-js": "3.1.1",
|
||||
"@types/file-saver": "2.0.5",
|
||||
"@types/is-hotkey": "0.1.10",
|
||||
@@ -5924,6 +5925,16 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/babel-plugin-component-annotate": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-5.3.0.tgz",
|
||||
"integrity": "sha512-p4q8gn8wcFqZGP/s2MnJCAAd8fTikaU6A0mM97RDHQgStcrYiaS0Sc5zUNfb1V+UOLPuvdEdL6MwyxfzjYJQTA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/browser": {
|
||||
"version": "10.53.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-10.53.1.tgz",
|
||||
@@ -5940,6 +5951,276 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/bundler-plugin-core": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/bundler-plugin-core/-/bundler-plugin-core-5.3.0.tgz",
|
||||
"integrity": "sha512-L5T60sWdAI3qWwdg3Ptwek/0TY59PERrxyqp4XMUkroayQvGd9r5dIW9Q1kSeXX9iJ442nXbFZKAOyCKV4Z13Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.18.5",
|
||||
"@sentry/babel-plugin-component-annotate": "5.3.0",
|
||||
"@sentry/cli": "^2.58.5",
|
||||
"dotenv": "^16.3.1",
|
||||
"find-up": "^5.0.0",
|
||||
"glob": "^13.0.6",
|
||||
"magic-string": "~0.30.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/bundler-plugin-core/node_modules/balanced-match": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
|
||||
"integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/bundler-plugin-core/node_modules/brace-expansion": {
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz",
|
||||
"integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/bundler-plugin-core/node_modules/glob": {
|
||||
"version": "13.0.6",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz",
|
||||
"integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"minimatch": "^10.2.2",
|
||||
"minipass": "^7.1.3",
|
||||
"path-scurry": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/bundler-plugin-core/node_modules/magic-string": {
|
||||
"version": "0.30.21",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
||||
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/sourcemap-codec": "^1.5.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/bundler-plugin-core/node_modules/minimatch": {
|
||||
"version": "10.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
|
||||
"integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^5.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/bundler-plugin-core/node_modules/minipass": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
|
||||
"integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
|
||||
"dev": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/cli": {
|
||||
"version": "2.58.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-2.58.6.tgz",
|
||||
"integrity": "sha512-baBcNPLLfUi9WuL+Tpri9BFaAdvugZIKelC5X0tt0Zdy+K0K+PCVSrnNmwMWU/HyaF/SEv6b6UHnXIdqanBlcg==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "FSL-1.1-MIT",
|
||||
"dependencies": {
|
||||
"https-proxy-agent": "^5.0.0",
|
||||
"node-fetch": "^2.6.7",
|
||||
"progress": "^2.0.3",
|
||||
"proxy-from-env": "^1.1.0",
|
||||
"which": "^2.0.2"
|
||||
},
|
||||
"bin": {
|
||||
"sentry-cli": "bin/sentry-cli"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@sentry/cli-darwin": "2.58.6",
|
||||
"@sentry/cli-linux-arm": "2.58.6",
|
||||
"@sentry/cli-linux-arm64": "2.58.6",
|
||||
"@sentry/cli-linux-i686": "2.58.6",
|
||||
"@sentry/cli-linux-x64": "2.58.6",
|
||||
"@sentry/cli-win32-arm64": "2.58.6",
|
||||
"@sentry/cli-win32-i686": "2.58.6",
|
||||
"@sentry/cli-win32-x64": "2.58.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/cli-darwin": {
|
||||
"version": "2.58.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/cli-darwin/-/cli-darwin-2.58.6.tgz",
|
||||
"integrity": "sha512-udAVvcyfNa0R+95GvPz/+43/N3TC0TYKdkQ7D7jhPSzbcMc7l2fxRNN5yB3UpCA5fWFnW4toeaqwDBhb/Wh3LA==",
|
||||
"dev": true,
|
||||
"license": "FSL-1.1-MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/cli-linux-arm": {
|
||||
"version": "2.58.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm/-/cli-linux-arm-2.58.6.tgz",
|
||||
"integrity": "sha512-pD0LAt5PcUzAinBwvDqc66x9+2CabHEv486yP0gRjWO7SakbaxmfVq/EXd8VLq/Tzi39LAu422UYK1lpW3MILw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "FSL-1.1-MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux",
|
||||
"freebsd",
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/cli-linux-arm64": {
|
||||
"version": "2.58.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/cli-linux-arm64/-/cli-linux-arm64-2.58.6.tgz",
|
||||
"integrity": "sha512-q8mEcNNmeXMy5i+jWT30TVpH7LcP4HD21CD5XRSPAd/a912HF6EpK0ybf/1USO14WOhoXbAGi9txwaWabSe33g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "FSL-1.1-MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux",
|
||||
"freebsd",
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/cli-linux-i686": {
|
||||
"version": "2.58.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/cli-linux-i686/-/cli-linux-i686-2.58.6.tgz",
|
||||
"integrity": "sha512-q8vNJi1eOV/4vxAFWBsEwLHoSYapaZHIf4j76KJGJXFKTkEbsjCOOsKbwUIBTQQhRgV4DFWh3ryfsPS/que4Kg==",
|
||||
"cpu": [
|
||||
"x86",
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "FSL-1.1-MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux",
|
||||
"freebsd",
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/cli-linux-x64": {
|
||||
"version": "2.58.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/cli-linux-x64/-/cli-linux-x64-2.58.6.tgz",
|
||||
"integrity": "sha512-DZu956Mhi3ZRjTBe1WdbGV46ldVbA8d2rgp/fh51GsI25zjBHah4wZnPTSzpc+YqxU6pJpg579B/r3jrIK530Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "FSL-1.1-MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux",
|
||||
"freebsd",
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/cli-win32-arm64": {
|
||||
"version": "2.58.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/cli-win32-arm64/-/cli-win32-arm64-2.58.6.tgz",
|
||||
"integrity": "sha512-nj0Ff/kmAB73EPDhR8B4O9r+NUHK5GkPCkGWC+kXVemqAJWL5jcJ5KdxG0l/S0z6RoEoltID8/43/B+TaMlT7A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "FSL-1.1-MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/cli-win32-i686": {
|
||||
"version": "2.58.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/cli-win32-i686/-/cli-win32-i686-2.58.6.tgz",
|
||||
"integrity": "sha512-WNZiDzPbgsEMQWq4avsQ391v/xWKJDIWWWo9GYl+N/w5qcYKkoDW7wQG7T9FasI6ENn68phChTOAPXXxbfAdOg==",
|
||||
"cpu": [
|
||||
"x86",
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "FSL-1.1-MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/cli-win32-x64": {
|
||||
"version": "2.58.6",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/cli-win32-x64/-/cli-win32-x64-2.58.6.tgz",
|
||||
"integrity": "sha512-R35WJ17oF4D2eqI1DR2sQQqr0fjRTt5xoP16WrTu91XM2lndRMFsnjh+/GttbxapLCBNlrjzia99MJ0PZHZpgA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "FSL-1.1-MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/core": {
|
||||
"version": "10.53.1",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-10.53.1.tgz",
|
||||
@@ -5965,6 +6246,52 @@
|
||||
"react": "^16.14.0 || 17.x || 18.x || 19.x"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/rollup-plugin": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/rollup-plugin/-/rollup-plugin-5.3.0.tgz",
|
||||
"integrity": "sha512-hgPGPYdQJ/G1cGYOxAb7d4z3V+/k/E5/P/5TFPEEBLuIbFFk+JG0CISUDJdzXJjO382Lb99PBJuXGbueBmO79w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry/bundler-plugin-core": "5.3.0",
|
||||
"magic-string": "~0.30.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"rollup": ">=3.2.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"rollup": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/rollup-plugin/node_modules/magic-string": {
|
||||
"version": "0.30.21",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
||||
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/sourcemap-codec": "^1.5.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@sentry/vite-plugin": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@sentry/vite-plugin/-/vite-plugin-5.3.0.tgz",
|
||||
"integrity": "sha512-qcoSzo4n2MulVQ70UUPLq6dTleb2a2HwL2wuwvAgWhPChrYTuk6A6mDg6aQb9fairPAwFPiU9PzOANpoDJcz1A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@sentry/bundler-plugin-core": "5.3.0",
|
||||
"@sentry/rollup-plugin": "5.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/@simple-libs/stream-utils": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@simple-libs/stream-utils/-/stream-utils-1.2.0.tgz",
|
||||
@@ -6960,7 +7287,7 @@
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
|
||||
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
|
||||
"optional": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"debug": "4"
|
||||
},
|
||||
@@ -8961,6 +9288,19 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "16.6.1",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
|
||||
"integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://dotenvx.com"
|
||||
}
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
@@ -11306,7 +11646,7 @@
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
|
||||
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
|
||||
"optional": true,
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"agent-base": "6",
|
||||
"debug": "4"
|
||||
@@ -16834,6 +17174,16 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/progress": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
|
||||
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prop-types": {
|
||||
"version": "15.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||
@@ -16851,6 +17201,13 @@
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/punycode": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||
|
||||
@@ -128,6 +128,7 @@
|
||||
"@rollup/plugin-wasm": "6.1.1",
|
||||
"@semantic-release/exec": "7.1.0",
|
||||
"@semantic-release/git": "10.0.1",
|
||||
"@sentry/vite-plugin": "5.3.0",
|
||||
"@types/chroma-js": "3.1.1",
|
||||
"@types/file-saver": "2.0.5",
|
||||
"@types/is-hotkey": "0.1.10",
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
import React, { useId } from 'react';
|
||||
|
||||
const MESSAGES = [
|
||||
{ showAvatar: true, lines: [{ w: '55%' }, { w: '35%' }] },
|
||||
{ showAvatar: false, lines: [{ w: '72%' }] },
|
||||
{ showAvatar: false, lines: [{ w: '48%' }, { w: '60%' }] },
|
||||
{ showAvatar: true, lines: [{ w: '80%' }] },
|
||||
{ showAvatar: false, lines: [{ w: '40%' }] },
|
||||
{ showAvatar: true, lines: [{ w: '65%' }, { w: '50%' }, { w: '30%' }] },
|
||||
{ showAvatar: false, lines: [{ w: '58%' }] },
|
||||
{ showAvatar: true, lines: [{ w: '45%' }] },
|
||||
{ showAvatar: false, lines: [{ w: '70%' }, { w: '25%' }] },
|
||||
];
|
||||
|
||||
export function RoomSkeleton() {
|
||||
const id = useId().replace(/:/g, '');
|
||||
const shimmerKeyframes = `
|
||||
@keyframes shimmer-${id} {
|
||||
0% { background-position: -400px 0; }
|
||||
100% { background-position: 400px 0; }
|
||||
}
|
||||
`;
|
||||
|
||||
const shimmer = {
|
||||
background: 'linear-gradient(90deg, var(--skeleton-base) 25%, var(--skeleton-shine) 50%, var(--skeleton-base) 75%)',
|
||||
backgroundSize: '800px 100%',
|
||||
animation: `shimmer-${id} 1.6s ease-in-out infinite`,
|
||||
borderRadius: '4px',
|
||||
} as React.CSSProperties;
|
||||
|
||||
return (
|
||||
<>
|
||||
<style>{shimmerKeyframes}</style>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexGrow: 1,
|
||||
height: '100%',
|
||||
overflow: 'hidden',
|
||||
// CSS vars resolve against both light and dark themes automatically
|
||||
'--skeleton-base': 'color-mix(in srgb, currentColor 8%, transparent)',
|
||||
'--skeleton-shine': 'color-mix(in srgb, currentColor 15%, transparent)',
|
||||
} as React.CSSProperties}
|
||||
>
|
||||
{/* Header — matches PageHeader size="600" (56px) */}
|
||||
<div
|
||||
style={{
|
||||
height: '56px',
|
||||
borderBottom: '1px solid color-mix(in srgb, currentColor 10%, transparent)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '12px',
|
||||
padding: '0 16px',
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
{/* Avatar */}
|
||||
<div style={{ ...shimmer, width: '32px', height: '32px', borderRadius: '50%', flexShrink: 0 }} />
|
||||
{/* Room name */}
|
||||
<div style={{ ...shimmer, width: '140px', height: '16px' }} />
|
||||
{/* Spacer */}
|
||||
<div style={{ flex: 1 }} />
|
||||
{/* Icon buttons */}
|
||||
<div style={{ ...shimmer, width: '24px', height: '24px', borderRadius: '4px' }} />
|
||||
<div style={{ ...shimmer, width: '24px', height: '24px', borderRadius: '4px' }} />
|
||||
</div>
|
||||
|
||||
{/* Timeline */}
|
||||
<div style={{ flex: 1, overflowY: 'hidden', padding: '16px 0' }}>
|
||||
{MESSAGES.map((msg, i) => (
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
<div key={i} style={{ display: 'flex', gap: '12px', padding: '4px 16px', alignItems: 'flex-start', marginBottom: msg.showAvatar ? '8px' : '2px' }}>
|
||||
{/* Avatar — only shown on first message in a group */}
|
||||
<div style={{ width: '36px', flexShrink: 0 }}>
|
||||
{msg.showAvatar && (
|
||||
<div style={{ ...shimmer, width: '36px', height: '36px', borderRadius: '50%' }} />
|
||||
)}
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '6px', flex: 1 }}>
|
||||
{/* Username on first in group */}
|
||||
{msg.showAvatar && (
|
||||
<div style={{ ...shimmer, width: '90px', height: '12px', marginBottom: '2px' }} />
|
||||
)}
|
||||
{msg.lines.map((line, j) => (
|
||||
// eslint-disable-next-line react/no-array-index-key
|
||||
<div key={j} style={{ ...shimmer, width: line.w, height: '14px' }} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Input bar */}
|
||||
<div
|
||||
style={{
|
||||
borderTop: '1px solid color-mix(in srgb, currentColor 10%, transparent)',
|
||||
padding: '12px 16px',
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
<div style={{ ...shimmer, width: '100%', height: '44px', borderRadius: '8px' }} />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import { RoomSkeleton } from '../components/RoomSkeleton';
|
||||
import {
|
||||
Outlet,
|
||||
Route,
|
||||
@@ -186,7 +187,7 @@ export const createRouter = (clientConfig: ClientConfig, screenSize: ScreenSize)
|
||||
path={_ROOM_PATH}
|
||||
element={
|
||||
<HomeRouteRoomProvider>
|
||||
<React.Suspense fallback={null}>
|
||||
<React.Suspense fallback={<RoomSkeleton />}>
|
||||
<Room />
|
||||
</React.Suspense>
|
||||
</HomeRouteRoomProvider>
|
||||
@@ -213,7 +214,7 @@ export const createRouter = (clientConfig: ClientConfig, screenSize: ScreenSize)
|
||||
path={_ROOM_PATH}
|
||||
element={
|
||||
<DirectRouteRoomProvider>
|
||||
<React.Suspense fallback={null}>
|
||||
<React.Suspense fallback={<RoomSkeleton />}>
|
||||
<Room />
|
||||
</React.Suspense>
|
||||
</DirectRouteRoomProvider>
|
||||
@@ -255,7 +256,7 @@ export const createRouter = (clientConfig: ClientConfig, screenSize: ScreenSize)
|
||||
path={_ROOM_PATH}
|
||||
element={
|
||||
<SpaceRouteRoomProvider>
|
||||
<React.Suspense fallback={null}>
|
||||
<React.Suspense fallback={<RoomSkeleton />}>
|
||||
<Room />
|
||||
</React.Suspense>
|
||||
</SpaceRouteRoomProvider>
|
||||
|
||||
+13
-1
@@ -1,5 +1,6 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { sentryVitePlugin } from '@sentry/vite-plugin';
|
||||
import { wasm } from '@rollup/plugin-wasm';
|
||||
import { viteStaticCopy } from 'vite-plugin-static-copy';
|
||||
import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin';
|
||||
@@ -94,6 +95,17 @@ export default defineConfig({
|
||||
vanillaExtractPlugin(),
|
||||
wasm(),
|
||||
react(),
|
||||
// Upload source maps to Sentry when auth token is present, then delete maps from dist
|
||||
...(process.env.SENTRY_AUTH_TOKEN ? [sentryVitePlugin({
|
||||
org: 'lotus-guild',
|
||||
project: 'javascript-react',
|
||||
authToken: process.env.SENTRY_AUTH_TOKEN,
|
||||
sourcemaps: {
|
||||
filesToDeleteAfterUpload: ['./dist/**/*.map'],
|
||||
},
|
||||
release: { name: process.env.VITE_APP_VERSION ?? 'lotus' },
|
||||
telemetry: false,
|
||||
})] : []),
|
||||
VitePWA({
|
||||
srcDir: 'src',
|
||||
filename: 'sw.ts',
|
||||
@@ -125,7 +137,7 @@ export default defineConfig({
|
||||
},
|
||||
build: {
|
||||
outDir: 'dist',
|
||||
sourcemap: false,
|
||||
sourcemap: process.env.SENTRY_AUTH_TOKEN ? 'hidden' : false,
|
||||
copyPublicDir: false,
|
||||
rollupOptions: {
|
||||
plugins: [inject({ Buffer: ['buffer', 'Buffer'] })],
|
||||
|
||||
Reference in New Issue
Block a user