Compare commits
3 Commits
fix/loadin
...
fix/code-h
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d73896b24c | ||
|
|
1ab581358e | ||
|
|
9fca21a3f8 |
@@ -1,5 +0,0 @@
|
||||
---
|
||||
"cojson": patch
|
||||
---
|
||||
|
||||
Correctly load CoValues after they are marked as unavailable and improve timeout management
|
||||
5
.changeset/good-parrots-rhyme.md
Normal file
5
.changeset/good-parrots-rhyme.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"multiauth": patch
|
||||
---
|
||||
|
||||
Use the new Resolve API
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -24,6 +24,4 @@ test-results
|
||||
|
||||
.vscode/settings.json
|
||||
|
||||
.svelte-kit
|
||||
|
||||
.idea
|
||||
.svelte-kit
|
||||
@@ -1,14 +1,5 @@
|
||||
# chat-rn-clerk
|
||||
|
||||
## 1.0.92
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.12.2
|
||||
- jazz-react-native-auth-clerk@0.12.2
|
||||
- jazz-tools@0.12.2
|
||||
- jazz-react-native-media-images@0.12.2
|
||||
|
||||
## 1.0.91
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "chat-rn-clerk",
|
||||
"main": "index.js",
|
||||
"version": "1.0.92",
|
||||
"version": "1.0.91",
|
||||
"scripts": {
|
||||
"build": "expo export -p ios",
|
||||
"start": "expo start",
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
# chat-rn
|
||||
|
||||
## 1.0.88
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react-native@0.12.2
|
||||
- jazz-tools@0.12.2
|
||||
|
||||
## 1.0.87
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chat-rn",
|
||||
"version": "1.0.88",
|
||||
"version": "1.0.87",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "expo export -p ios",
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
# chat-vue
|
||||
|
||||
## 0.0.73
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cc684eb]
|
||||
- jazz-browser@0.12.2
|
||||
- jazz-vue@0.12.2
|
||||
- jazz-tools@0.12.2
|
||||
|
||||
## 0.0.72
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "chat-vue",
|
||||
"version": "0.0.73",
|
||||
"version": "0.0.72",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
# jazz-example-chat
|
||||
|
||||
## 0.0.170
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [8a71835]
|
||||
- jazz-inspector@0.12.2
|
||||
- jazz-react@0.12.2
|
||||
- jazz-tools@0.12.2
|
||||
|
||||
## 0.0.169
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-chat",
|
||||
"private": true,
|
||||
"version": "0.0.170",
|
||||
"version": "0.0.169",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
# minimal-auth-clerk
|
||||
|
||||
## 0.0.69
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react@0.12.2
|
||||
- jazz-react-auth-clerk@0.12.2
|
||||
- jazz-tools@0.12.2
|
||||
|
||||
## 0.0.68
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "clerk",
|
||||
"private": true,
|
||||
"version": "0.0.69",
|
||||
"version": "0.0.68",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -13,7 +13,7 @@
|
||||
"dependencies": {
|
||||
"@clerk/clerk-react": "^5.4.1",
|
||||
"jazz-react": "workspace:*",
|
||||
"jazz-react-auth-clerk": "workspace:0.12.2",
|
||||
"jazz-react-auth-clerk": "workspace:0.12.1",
|
||||
"jazz-tools": "workspace:*",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1"
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
# file-share-svelte
|
||||
|
||||
## 0.0.53
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.12.2
|
||||
- jazz-tools@0.12.2
|
||||
|
||||
## 0.0.52
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "file-share-svelte",
|
||||
"version": "0.0.53",
|
||||
"version": "0.0.52",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
# jazz-tailwind-demo-auth-starter
|
||||
|
||||
## 0.0.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react@0.12.2
|
||||
- jazz-tools@0.12.2
|
||||
|
||||
## 0.0.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "filestream",
|
||||
"private": true,
|
||||
"version": "0.0.9",
|
||||
"version": "0.0.8",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
# form
|
||||
|
||||
## 0.1.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react@0.12.2
|
||||
- jazz-tools@0.12.2
|
||||
|
||||
## 0.1.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "form",
|
||||
"private": true,
|
||||
"version": "0.1.11",
|
||||
"version": "0.1.10",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
# image-upload
|
||||
|
||||
## 0.0.67
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react@0.12.2
|
||||
- jazz-tools@0.12.2
|
||||
|
||||
## 0.0.66
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "image-upload",
|
||||
"private": true,
|
||||
"version": "0.0.67",
|
||||
"version": "0.0.66",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,16 +1,5 @@
|
||||
# jazz-example-inspector
|
||||
|
||||
## 0.0.120
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 8a71835: use goober for css
|
||||
- Updated dependencies [8a71835]
|
||||
- Updated dependencies [c2f4827]
|
||||
- jazz-inspector@0.12.2
|
||||
- cojson@0.12.2
|
||||
- cojson-transport-ws@0.12.2
|
||||
|
||||
## 0.0.119
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-inspector-app",
|
||||
"private": true,
|
||||
"version": "0.0.120",
|
||||
"version": "0.0.119",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -13,8 +13,8 @@
|
||||
"dependencies": {
|
||||
"jazz-inspector": "workspace:*",
|
||||
"clsx": "^2.0.0",
|
||||
"cojson": "workspace:0.12.2",
|
||||
"cojson-transport-ws": "workspace:0.12.2",
|
||||
"cojson": "workspace:0.12.1",
|
||||
"cojson-transport-ws": "workspace:0.12.1",
|
||||
"hash-slash": "workspace:0.2.2",
|
||||
"lucide-react": "^0.274.0",
|
||||
"react": "^18.3.1",
|
||||
|
||||
@@ -12,7 +12,6 @@ import { WasmCrypto } from "cojson/crypto/WasmCrypto";
|
||||
import {
|
||||
Breadcrumbs,
|
||||
Button,
|
||||
GlobalStyles,
|
||||
Icon,
|
||||
Input,
|
||||
PageStack,
|
||||
@@ -127,7 +126,7 @@ export default function CoJsonViewerApp() {
|
||||
}
|
||||
|
||||
return (
|
||||
<GlobalStyles
|
||||
<div
|
||||
className={clsx(
|
||||
"h-screen overflow-hidden flex flex-col",
|
||||
" text-stone-700 bg-white",
|
||||
@@ -203,7 +202,7 @@ export default function CoJsonViewerApp() {
|
||||
</form>
|
||||
)}
|
||||
</PageStack>
|
||||
</GlobalStyles>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
# multi-cursors
|
||||
|
||||
## 0.0.63
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react@0.12.2
|
||||
- jazz-tools@0.12.2
|
||||
|
||||
## 0.0.62
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "multi-cursors",
|
||||
"private": true,
|
||||
"version": "0.0.63",
|
||||
"version": "0.0.62",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
# multiauth
|
||||
|
||||
## 0.0.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- b108c61: Use the new Resolve API
|
||||
- jazz-react@0.12.2
|
||||
- jazz-react-auth-clerk@0.12.2
|
||||
- jazz-tools@0.12.2
|
||||
|
||||
## 0.0.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "multiauth",
|
||||
"private": true,
|
||||
"version": "0.0.10",
|
||||
"version": "0.0.9",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
# jazz-example-musicplayer
|
||||
|
||||
## 0.0.91
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [8a71835]
|
||||
- jazz-inspector@0.12.2
|
||||
- jazz-react@0.12.2
|
||||
- jazz-tools@0.12.2
|
||||
|
||||
## 0.0.90
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-music-player",
|
||||
"private": true,
|
||||
"version": "0.0.91",
|
||||
"version": "0.0.90",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -22,8 +22,8 @@
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.0.0",
|
||||
"jazz-inspector": "workspace:*",
|
||||
"jazz-react": "workspace:0.12.2",
|
||||
"jazz-tools": "workspace:0.12.2",
|
||||
"jazz-react": "workspace:0.12.1",
|
||||
"jazz-tools": "workspace:0.12.1",
|
||||
"lucide-react": "^0.274.0",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
# organization
|
||||
|
||||
## 0.0.63
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react@0.12.2
|
||||
- jazz-tools@0.12.2
|
||||
|
||||
## 0.0.62
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "organization",
|
||||
"private": true,
|
||||
"version": "0.0.63",
|
||||
"version": "0.0.62",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
# passkey-svelte
|
||||
|
||||
## 0.0.57
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-svelte@0.12.2
|
||||
|
||||
## 0.0.56
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "passkey-svelte",
|
||||
"version": "0.0.57",
|
||||
"version": "0.0.56",
|
||||
"type": "module",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
# minimal-auth-passkey
|
||||
|
||||
## 0.0.68
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react@0.12.2
|
||||
- jazz-tools@0.12.2
|
||||
|
||||
## 0.0.67
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "passkey",
|
||||
"private": true,
|
||||
"version": "0.0.68",
|
||||
"version": "0.0.67",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
# passphrase
|
||||
|
||||
## 0.0.65
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react@0.12.2
|
||||
- jazz-tools@0.12.2
|
||||
|
||||
## 0.0.64
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "passphrase",
|
||||
"private": true,
|
||||
"version": "0.0.65",
|
||||
"version": "0.0.64",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
# jazz-password-manager
|
||||
|
||||
## 0.0.89
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react@0.12.2
|
||||
- jazz-tools@0.12.2
|
||||
|
||||
## 0.0.88
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-password-manager",
|
||||
"private": true,
|
||||
"version": "0.0.89",
|
||||
"version": "0.0.88",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -12,8 +12,8 @@
|
||||
"clean-install": "rm -rf node_modules pnpm-lock.yaml && pnpm install"
|
||||
},
|
||||
"dependencies": {
|
||||
"jazz-react": "workspace:0.12.2",
|
||||
"jazz-tools": "workspace:0.12.2",
|
||||
"jazz-react": "workspace:0.12.1",
|
||||
"jazz-tools": "workspace:0.12.1",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-hook-form": "^7.41.5",
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
# jazz-example-pets
|
||||
|
||||
## 0.0.187
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react@0.12.2
|
||||
- jazz-tools@0.12.2
|
||||
|
||||
## 0.0.186
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-pets",
|
||||
"private": true,
|
||||
"version": "0.0.187",
|
||||
"version": "0.0.186",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -19,8 +19,8 @@
|
||||
"@radix-ui/react-toast": "^1.1.4",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.0.0",
|
||||
"jazz-react": "workspace:0.12.2",
|
||||
"jazz-tools": "workspace:0.12.2",
|
||||
"jazz-react": "workspace:0.12.1",
|
||||
"jazz-tools": "workspace:0.12.1",
|
||||
"lucide-react": "^0.274.0",
|
||||
"qrcode": "^1.5.3",
|
||||
"react": "^18.3.1",
|
||||
@@ -40,7 +40,7 @@
|
||||
"@vitejs/plugin-react-swc": "^3.3.2",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"is-ci": "^3.0.1",
|
||||
"jazz-run": "workspace:0.12.2",
|
||||
"jazz-run": "workspace:0.12.1",
|
||||
"postcss": "^8.4.27",
|
||||
"tailwindcss": "^3.4.17",
|
||||
"typescript": "~5.6.2",
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
# reactions
|
||||
|
||||
## 0.0.67
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react@0.12.2
|
||||
- jazz-tools@0.12.2
|
||||
|
||||
## 0.0.66
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "reactions",
|
||||
"private": true,
|
||||
"version": "0.0.67",
|
||||
"version": "0.0.66",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
# todo-vue
|
||||
|
||||
## 0.0.71
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cc684eb]
|
||||
- jazz-browser@0.12.2
|
||||
- jazz-vue@0.12.2
|
||||
- jazz-tools@0.12.2
|
||||
|
||||
## 0.0.70
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "todo-vue",
|
||||
"version": "0.0.71",
|
||||
"version": "0.0.70",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
# jazz-example-todo
|
||||
|
||||
## 0.0.186
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- jazz-react@0.12.2
|
||||
- jazz-tools@0.12.2
|
||||
|
||||
## 0.0.185
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "jazz-example-todo",
|
||||
"private": true,
|
||||
"version": "0.0.186",
|
||||
"version": "0.0.185",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@@ -16,8 +16,8 @@
|
||||
"@radix-ui/react-toast": "^1.1.4",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.0.0",
|
||||
"jazz-react": "workspace:0.12.2",
|
||||
"jazz-tools": "workspace:0.12.2",
|
||||
"jazz-react": "workspace:0.12.1",
|
||||
"jazz-tools": "workspace:0.12.1",
|
||||
"lucide-react": "^0.274.0",
|
||||
"qrcode": "^1.5.3",
|
||||
"react": "^18.3.1",
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
# version-history
|
||||
|
||||
## 0.0.64
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [8a71835]
|
||||
- jazz-inspector@0.12.2
|
||||
- jazz-react@0.12.2
|
||||
- jazz-tools@0.12.2
|
||||
|
||||
## 0.0.63
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "version-history",
|
||||
"private": true,
|
||||
"version": "0.0.64",
|
||||
"version": "0.0.63",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
# cojson-storage-indexeddb
|
||||
|
||||
## 0.12.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c2f4827]
|
||||
- cojson@0.12.2
|
||||
- cojson-storage@0.12.2
|
||||
|
||||
## 0.12.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cojson-storage-indexeddb",
|
||||
"version": "0.12.2",
|
||||
"version": "0.12.1",
|
||||
"main": "dist/index.js",
|
||||
"type": "module",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
# cojson-storage-sqlite
|
||||
|
||||
## 0.12.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c2f4827]
|
||||
- cojson@0.12.2
|
||||
- cojson-storage@0.12.2
|
||||
|
||||
## 0.12.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "cojson-storage-rn-sqlite",
|
||||
"type": "module",
|
||||
"version": "0.12.2",
|
||||
"version": "0.12.1",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
# cojson-storage-sqlite
|
||||
|
||||
## 0.12.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c2f4827]
|
||||
- cojson@0.12.2
|
||||
- cojson-storage@0.12.2
|
||||
|
||||
## 0.12.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "cojson-storage-sqlite",
|
||||
"type": "module",
|
||||
"version": "0.12.2",
|
||||
"version": "0.12.1",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"better-sqlite3": "^11.7.0",
|
||||
"cojson": "workspace:0.12.2",
|
||||
"cojson": "workspace:0.12.1",
|
||||
"cojson-storage": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
# cojson-storage
|
||||
|
||||
## 0.12.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c2f4827]
|
||||
- cojson@0.12.2
|
||||
|
||||
## 0.12.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cojson-storage",
|
||||
"version": "0.12.2",
|
||||
"version": "0.12.1",
|
||||
"main": "dist/index.js",
|
||||
"type": "module",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
# cojson-transport-nodejs-ws
|
||||
|
||||
## 0.12.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [c2f4827]
|
||||
- cojson@0.12.2
|
||||
|
||||
## 0.12.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "cojson-transport-ws",
|
||||
"type": "module",
|
||||
"version": "0.12.2",
|
||||
"version": "0.12.1",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
logger,
|
||||
} from "cojson";
|
||||
import { BatchedOutgoingMessages } from "./BatchedOutgoingMessages.js";
|
||||
import { deserializeMessages } from "./serialization.js";
|
||||
import { deserializeMessages, getErrorMessage } from "./serialization.js";
|
||||
import type { AnyWebSocket } from "./types.js";
|
||||
|
||||
export const BUFFER_LIMIT = 100_000;
|
||||
@@ -20,16 +20,11 @@ export type CreateWebSocketPeerOpts = {
|
||||
expectPings?: boolean;
|
||||
batchingByDefault?: boolean;
|
||||
deletePeerStateOnClose?: boolean;
|
||||
pingTimeout?: number;
|
||||
onClose?: () => void;
|
||||
onSuccess?: () => void;
|
||||
};
|
||||
|
||||
function createPingTimeoutListener(
|
||||
enabled: boolean,
|
||||
timeout: number,
|
||||
callback: () => void,
|
||||
) {
|
||||
function createPingTimeoutListener(enabled: boolean, callback: () => void) {
|
||||
if (!enabled) {
|
||||
return {
|
||||
reset() {},
|
||||
@@ -44,7 +39,7 @@ function createPingTimeoutListener(
|
||||
pingTimeout && clearTimeout(pingTimeout);
|
||||
pingTimeout = setTimeout(() => {
|
||||
callback();
|
||||
}, timeout);
|
||||
}, 10_000);
|
||||
},
|
||||
clear() {
|
||||
pingTimeout && clearTimeout(pingTimeout);
|
||||
@@ -133,7 +128,6 @@ export function createWebSocketPeer({
|
||||
expectPings = true,
|
||||
batchingByDefault = true,
|
||||
deletePeerStateOnClose = false,
|
||||
pingTimeout = 10_000,
|
||||
onSuccess,
|
||||
onClose,
|
||||
}: CreateWebSocketPeerOpts): Peer {
|
||||
@@ -162,18 +156,14 @@ export function createWebSocketPeer({
|
||||
handleClose();
|
||||
});
|
||||
|
||||
const pingTimeoutListener = createPingTimeoutListener(
|
||||
expectPings,
|
||||
pingTimeout,
|
||||
() => {
|
||||
incoming
|
||||
.push("PingTimeout")
|
||||
.catch((e) =>
|
||||
logger.error("Error while pushing ping timeout", { err: e }),
|
||||
);
|
||||
emitClosedEvent();
|
||||
},
|
||||
);
|
||||
const pingTimeout = createPingTimeoutListener(expectPings, () => {
|
||||
incoming
|
||||
.push("PingTimeout")
|
||||
.catch((e) =>
|
||||
logger.error("Error while pushing ping timeout", { err: e }),
|
||||
);
|
||||
emitClosedEvent();
|
||||
});
|
||||
|
||||
const outgoingMessages = createOutgoingMessagesManager(
|
||||
websocket,
|
||||
@@ -182,8 +172,6 @@ export function createWebSocketPeer({
|
||||
let isFirstMessage = true;
|
||||
|
||||
function handleIncomingMsg(event: { data: unknown }) {
|
||||
pingTimeoutListener.reset();
|
||||
|
||||
if (event.data === "") {
|
||||
return;
|
||||
}
|
||||
@@ -209,6 +197,8 @@ export function createWebSocketPeer({
|
||||
outgoingMessages.setBatchingEnabled(true);
|
||||
}
|
||||
|
||||
pingTimeout.reset();
|
||||
|
||||
for (const msg of messages) {
|
||||
if (msg && "action" in msg) {
|
||||
incoming
|
||||
@@ -232,7 +222,7 @@ export function createWebSocketPeer({
|
||||
|
||||
websocket.removeEventListener("message", handleIncomingMsg);
|
||||
websocket.removeEventListener("close", handleClose);
|
||||
pingTimeoutListener.clear();
|
||||
pingTimeout.clear();
|
||||
emitClosedEvent();
|
||||
|
||||
if (websocket.readyState === 0) {
|
||||
|
||||
@@ -4,7 +4,6 @@ import { afterEach, beforeEach, describe, expect, test } from "vitest";
|
||||
import { WebSocket } from "ws";
|
||||
import { createWebSocketPeer } from "../createWebSocketPeer";
|
||||
import { startSyncServer } from "./syncServer";
|
||||
import { waitFor } from "./utils";
|
||||
|
||||
describe("WebSocket Peer Integration", () => {
|
||||
let server: any;
|
||||
@@ -130,35 +129,4 @@ describe("WebSocket Peer Integration", () => {
|
||||
expect(disconnectCalled).toBe(true);
|
||||
expect(ws.readyState).toBe(WebSocket.CLOSED);
|
||||
});
|
||||
|
||||
test("should trigger a timeout if the server does not respond", async () => {
|
||||
const clientAgent = crypto.newRandomAgentSecret();
|
||||
const clientNode = new LocalNode(
|
||||
new ControlledAgent(clientAgent, crypto),
|
||||
crypto.newRandomSessionID(crypto.getAgentID(clientAgent)),
|
||||
crypto,
|
||||
);
|
||||
|
||||
const ws = new WebSocket(syncServerUrl);
|
||||
let disconnectCalled = false;
|
||||
|
||||
const peer = createWebSocketPeer({
|
||||
id: "test-client",
|
||||
websocket: ws,
|
||||
role: "server",
|
||||
pingTimeout: 5,
|
||||
onClose: () => {
|
||||
disconnectCalled = true;
|
||||
},
|
||||
});
|
||||
|
||||
clientNode.syncManager.addPeer(peer);
|
||||
|
||||
// Wait for connection to establish and the timeout to kick in
|
||||
await waitFor(() => {
|
||||
expect(disconnectCalled).toBe(true);
|
||||
});
|
||||
|
||||
expect(ws.readyState).toBe(WebSocket.CLOSED);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
# cojson
|
||||
|
||||
## 0.12.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- c2f4827: StreamingHash: Remove redundant clone and skip double hash generation when creating a local transaction
|
||||
|
||||
## 0.12.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
},
|
||||
"type": "module",
|
||||
"license": "MIT",
|
||||
"version": "0.12.2",
|
||||
"version": "0.12.1",
|
||||
"devDependencies": {
|
||||
"@opentelemetry/sdk-metrics": "^2.0.0",
|
||||
"typescript": "~5.6.2",
|
||||
|
||||
@@ -199,7 +199,6 @@ export class CoValueCore {
|
||||
givenExpectedNewHash: Hash | undefined,
|
||||
newSignature: Signature,
|
||||
skipVerify: boolean = false,
|
||||
givenNewStreamingHash?: StreamingHash,
|
||||
): Result<true, TryAddTransactionsError> {
|
||||
return this.node
|
||||
.resolveAccountAgent(
|
||||
@@ -209,55 +208,42 @@ export class CoValueCore {
|
||||
.andThen((agent) => {
|
||||
const signerID = this.crypto.getAgentSignerID(agent);
|
||||
|
||||
if (
|
||||
skipVerify === true &&
|
||||
givenNewStreamingHash &&
|
||||
givenExpectedNewHash
|
||||
) {
|
||||
this.doAddTransactions(
|
||||
sessionID,
|
||||
newTransactions,
|
||||
newSignature,
|
||||
givenExpectedNewHash,
|
||||
givenNewStreamingHash,
|
||||
"immediate",
|
||||
);
|
||||
} else {
|
||||
const { expectedNewHash, newStreamingHash } =
|
||||
this.expectedNewHashAfter(sessionID, newTransactions);
|
||||
const { expectedNewHash, newStreamingHash } = this.expectedNewHashAfter(
|
||||
sessionID,
|
||||
newTransactions,
|
||||
);
|
||||
|
||||
if (
|
||||
givenExpectedNewHash &&
|
||||
givenExpectedNewHash !== expectedNewHash
|
||||
) {
|
||||
return err({
|
||||
type: "InvalidHash",
|
||||
id: this.id,
|
||||
expectedNewHash,
|
||||
givenExpectedNewHash,
|
||||
} satisfies InvalidHashError);
|
||||
}
|
||||
|
||||
if (!this.crypto.verify(newSignature, expectedNewHash, signerID)) {
|
||||
return err({
|
||||
type: "InvalidSignature",
|
||||
id: this.id,
|
||||
newSignature,
|
||||
sessionID,
|
||||
signerID,
|
||||
} satisfies InvalidSignatureError);
|
||||
}
|
||||
|
||||
this.doAddTransactions(
|
||||
sessionID,
|
||||
newTransactions,
|
||||
newSignature,
|
||||
if (givenExpectedNewHash && givenExpectedNewHash !== expectedNewHash) {
|
||||
return err({
|
||||
type: "InvalidHash",
|
||||
id: this.id,
|
||||
expectedNewHash,
|
||||
newStreamingHash,
|
||||
"immediate",
|
||||
);
|
||||
givenExpectedNewHash,
|
||||
} satisfies InvalidHashError);
|
||||
}
|
||||
|
||||
if (
|
||||
skipVerify !== true &&
|
||||
!this.crypto.verify(newSignature, expectedNewHash, signerID)
|
||||
) {
|
||||
return err({
|
||||
type: "InvalidSignature",
|
||||
id: this.id,
|
||||
newSignature,
|
||||
sessionID,
|
||||
signerID,
|
||||
} satisfies InvalidSignatureError);
|
||||
}
|
||||
|
||||
this.doAddTransactions(
|
||||
sessionID,
|
||||
newTransactions,
|
||||
newSignature,
|
||||
expectedNewHash,
|
||||
newStreamingHash,
|
||||
"immediate",
|
||||
);
|
||||
|
||||
return ok(true as const);
|
||||
});
|
||||
}
|
||||
@@ -384,14 +370,40 @@ export class CoValueCore {
|
||||
const streamingHash =
|
||||
this.sessionLogs.get(sessionID)?.streamingHash.clone() ??
|
||||
new StreamingHash(this.crypto);
|
||||
|
||||
for (const transaction of newTransactions) {
|
||||
streamingHash.update(transaction);
|
||||
}
|
||||
|
||||
const newStreamingHash = streamingHash.clone();
|
||||
|
||||
return {
|
||||
expectedNewHash: streamingHash.digest(),
|
||||
newStreamingHash: streamingHash,
|
||||
newStreamingHash,
|
||||
};
|
||||
}
|
||||
|
||||
async expectedNewHashAfterAsync(
|
||||
sessionID: SessionID,
|
||||
newTransactions: Transaction[],
|
||||
): Promise<{ expectedNewHash: Hash; newStreamingHash: StreamingHash }> {
|
||||
const streamingHash =
|
||||
this.sessionLogs.get(sessionID)?.streamingHash.clone() ??
|
||||
new StreamingHash(this.crypto);
|
||||
let before = performance.now();
|
||||
for (const transaction of newTransactions) {
|
||||
streamingHash.update(transaction);
|
||||
const after = performance.now();
|
||||
if (after - before > 1) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
before = performance.now();
|
||||
}
|
||||
}
|
||||
|
||||
const newStreamingHash = streamingHash.clone();
|
||||
|
||||
return {
|
||||
expectedNewHash: streamingHash.digest(),
|
||||
newStreamingHash,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -440,10 +452,9 @@ export class CoValueCore {
|
||||
) as SessionID)
|
||||
: this.node.currentSessionID;
|
||||
|
||||
const { expectedNewHash, newStreamingHash } = this.expectedNewHashAfter(
|
||||
sessionID,
|
||||
[transaction],
|
||||
);
|
||||
const { expectedNewHash } = this.expectedNewHashAfter(sessionID, [
|
||||
transaction,
|
||||
]);
|
||||
|
||||
const signature = this.crypto.sign(
|
||||
this.node.account.currentSignerSecret(),
|
||||
@@ -456,7 +467,6 @@ export class CoValueCore {
|
||||
expectedNewHash,
|
||||
signature,
|
||||
true,
|
||||
newStreamingHash,
|
||||
)._unsafeUnwrap({ withStackTrace: true });
|
||||
|
||||
if (success) {
|
||||
|
||||
@@ -176,12 +176,11 @@ export class CoValueState {
|
||||
async loadFromPeers(peers: PeerState[]) {
|
||||
const state = this.state;
|
||||
|
||||
if (state.type === "loading" || state.type === "available") {
|
||||
if (state.type !== "unknown" && state.type !== "unavailable") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (peers.length === 0) {
|
||||
this.moveToState(new CoValueUnavailableState());
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -193,11 +192,7 @@ export class CoValueState {
|
||||
|
||||
// If we are in the loading state we move to a new loading state
|
||||
// to reset all the loading promises
|
||||
if (
|
||||
this.state.type === "loading" ||
|
||||
this.state.type === "unknown" ||
|
||||
this.state.type === "unavailable"
|
||||
) {
|
||||
if (this.state.type === "loading" || this.state.type === "unknown") {
|
||||
this.moveToState(
|
||||
new CoValueLoadingState(peersWithoutErrors.map((p) => p.id)),
|
||||
);
|
||||
@@ -313,19 +308,6 @@ async function loadCoValueFromPeers(
|
||||
}
|
||||
|
||||
if (coValueEntry.state.type === "loading") {
|
||||
const { promise, resolve } = createResolvablePromise<void>();
|
||||
|
||||
/**
|
||||
* Use a very long timeout for storage peers, because under pressure
|
||||
* they may take a long time to consume the messages queue
|
||||
*
|
||||
* TODO: Track errors on storage and do not rely on timeout
|
||||
*/
|
||||
const timeoutDuration =
|
||||
peer.role === "storage"
|
||||
? CO_VALUE_LOADING_CONFIG.TIMEOUT * 10
|
||||
: CO_VALUE_LOADING_CONFIG.TIMEOUT;
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
if (coValueEntry.state.type === "loading") {
|
||||
logger.warn("Failed to load coValue from peer", {
|
||||
@@ -337,10 +319,9 @@ async function loadCoValueFromPeers(
|
||||
type: "not-found-in-peer",
|
||||
peerId: peer.id,
|
||||
});
|
||||
resolve();
|
||||
}
|
||||
}, timeoutDuration);
|
||||
await Promise.race([promise, coValueEntry.state.waitForPeer(peer.id)]);
|
||||
}, CO_VALUE_LOADING_CONFIG.TIMEOUT);
|
||||
await coValueEntry.state.waitForPeer(peer.id);
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,10 @@ export function emptyKnownState(id: RawCoID): CoValueKnownState {
|
||||
};
|
||||
}
|
||||
|
||||
function getErrorMessage(e: unknown) {
|
||||
return e instanceof Error ? e.message : "Unknown error";
|
||||
}
|
||||
|
||||
export type SyncMessage =
|
||||
| LoadMessage
|
||||
| KnownStateMessage
|
||||
@@ -411,20 +415,7 @@ export class SyncManager {
|
||||
entry.loadFromPeers([peer]).catch((e) => {
|
||||
logger.error("Error loading coValue in handleLoad", { err: e });
|
||||
});
|
||||
} else {
|
||||
// We don't have any eligible peers to load the coValue from
|
||||
// so we send a known state back to the sender to let it know
|
||||
// that the coValue is unavailable
|
||||
this.trySendToPeer(peer, {
|
||||
action: "known",
|
||||
id: msg.id,
|
||||
header: false,
|
||||
sessions: {},
|
||||
}).catch((e) => {
|
||||
logger.error("Error sending known state back", { err: e });
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
} else {
|
||||
this.local.loadCoValueCore(msg.id, peer.id).catch((e) => {
|
||||
@@ -469,13 +460,6 @@ export class SyncManager {
|
||||
err: e,
|
||||
});
|
||||
});
|
||||
} else if (entry.state.type === "unavailable") {
|
||||
this.trySendToPeer(peer, {
|
||||
action: "known",
|
||||
id: msg.id,
|
||||
header: false,
|
||||
sessions: {},
|
||||
});
|
||||
}
|
||||
|
||||
if (entry.state.type === "available") {
|
||||
@@ -620,12 +604,39 @@ export class SyncManager {
|
||||
continue;
|
||||
}
|
||||
|
||||
const before = performance.now();
|
||||
// eslint-disable-next-line neverthrow/must-use-result
|
||||
const result = coValue.tryAddTransactions(
|
||||
sessionID,
|
||||
newTransactions,
|
||||
undefined,
|
||||
newContentForSession.lastSignature,
|
||||
);
|
||||
const after = performance.now();
|
||||
if (after - before > 80) {
|
||||
const totalTxLength = newTransactions
|
||||
.map((t) =>
|
||||
t.privacy === "private"
|
||||
? t.encryptedChanges.length
|
||||
: t.changes.length,
|
||||
)
|
||||
.reduce((a, b) => a + b, 0);
|
||||
logger.debug(
|
||||
`Adding incoming transactions took ${(after - before).toFixed(
|
||||
2,
|
||||
)}ms for ${totalTxLength} bytes = bandwidth: ${(
|
||||
(1000 * totalTxLength) / (after - before) / (1024 * 1024)
|
||||
).toFixed(2)} MB/s`,
|
||||
);
|
||||
}
|
||||
|
||||
// const theirTotalnTxs = Object.values(
|
||||
// peer.optimisticKnownStates[msg.id]?.sessions || {},
|
||||
// ).reduce((sum, nTxs) => sum + nTxs, 0);
|
||||
// const ourTotalnTxs = [...coValue.sessionLogs.values()].reduce(
|
||||
// (sum, session) => sum + session.transactions.length,
|
||||
// 0,
|
||||
// );
|
||||
|
||||
if (result.isErr()) {
|
||||
logger.error("Failed to add transactions", {
|
||||
@@ -724,10 +735,16 @@ export class SyncManager {
|
||||
}
|
||||
|
||||
async actuallySyncCoValue(coValue: CoValueCore) {
|
||||
// let blockingSince = performance.now();
|
||||
for (const peer of this.peersInPriorityOrder()) {
|
||||
if (peer.closed) continue;
|
||||
if (peer.erroredCoValues.has(coValue.id)) continue;
|
||||
|
||||
// if (performance.now() - blockingSince > 5) {
|
||||
// await new Promise<void>((resolve) => {
|
||||
// setTimeout(resolve, 0);
|
||||
// });
|
||||
// blockingSince = performance.now();
|
||||
// }
|
||||
if (peer.optimisticKnownStates.has(coValue.id)) {
|
||||
await this.tellUntoldKnownStateIncludingDependencies(coValue.id, peer);
|
||||
await this.sendNewContentIncludingDependencies(coValue.id, peer);
|
||||
|
||||
@@ -956,6 +956,27 @@ test.skip("When a peer's incoming/readable stream closes, we remove the peer", a
|
||||
*/
|
||||
});
|
||||
|
||||
test("If we start loading a coValue before connecting to a peer that has it, it will load it once we connect", async () => {
|
||||
const { node: node1 } = await createConnectedTestNode();
|
||||
|
||||
const group = node1.createGroup();
|
||||
|
||||
const map = group.createMap();
|
||||
map.set("hello", "world", "trusting");
|
||||
|
||||
const node2 = createTestNode();
|
||||
|
||||
const mapOnNode2Promise = loadCoValueOrFail(node2, map.id);
|
||||
|
||||
expect(node2.coValuesStore.get(map.core.id).state.type).toEqual("unknown");
|
||||
|
||||
connectNodeToSyncServer(node2);
|
||||
|
||||
const mapOnNode2 = await mapOnNode2Promise;
|
||||
|
||||
expect(mapOnNode2.get("hello")).toEqual("world");
|
||||
});
|
||||
|
||||
test("should keep the peer state when the peer closes", async () => {
|
||||
const client = createTestNode();
|
||||
|
||||
@@ -1705,45 +1726,6 @@ describe("loadCoValueCore with retry", () => {
|
||||
await expect(promise1).resolves.not.toBe("unavailable");
|
||||
await expect(promise2).resolves.not.toBe("unavailable");
|
||||
});
|
||||
|
||||
test("should load unavailable coValues after they are synced", async () => {
|
||||
const bob = createTestNode();
|
||||
const alice = createTestNode();
|
||||
|
||||
// Create a group and map on anotherClient
|
||||
const group = alice.createGroup();
|
||||
const map = group.createMap();
|
||||
map.set("key1", "value1", "trusting");
|
||||
|
||||
// Start loading before syncing
|
||||
const result = await bob.loadCoValueCore(map.id);
|
||||
|
||||
expect(result).toBe("unavailable");
|
||||
|
||||
connectTwoPeers(alice, bob, "server", "server");
|
||||
|
||||
const result2 = await bob.loadCoValueCore(map.id);
|
||||
|
||||
expect(result2).not.toBe("unavailable");
|
||||
});
|
||||
|
||||
test("should successfully mark a coValue as unavailable if the server does not have it", async () => {
|
||||
const bob = createTestNode();
|
||||
const alice = createTestNode();
|
||||
const charlie = createTestNode();
|
||||
|
||||
connectTwoPeers(bob, charlie, "client", "server");
|
||||
|
||||
// Create a group and map on anotherClient
|
||||
const group = alice.createGroup();
|
||||
const map = group.createMap();
|
||||
map.set("key1", "value1", "trusting");
|
||||
|
||||
// Start loading before syncing
|
||||
const result = await bob.loadCoValueCore(map.id);
|
||||
|
||||
expect(result).toBe("unavailable");
|
||||
});
|
||||
});
|
||||
|
||||
describe("waitForSyncWithPeer", () => {
|
||||
@@ -1912,8 +1894,6 @@ describe("sync protocol", () => {
|
||||
const map = group.createMap();
|
||||
map.set("hello", "world", "trusting");
|
||||
|
||||
await map.core.waitForSync();
|
||||
|
||||
const mapOnJazzCloud = await loadCoValueOrFail(jazzCloud, map.id);
|
||||
expect(mapOnJazzCloud.get("hello")).toEqual("world");
|
||||
|
||||
@@ -2058,29 +2038,6 @@ describe("sync protocol", () => {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
from: "server",
|
||||
msg: {
|
||||
action: "known",
|
||||
header: true,
|
||||
id: map.id,
|
||||
sessions: {
|
||||
[client.currentSessionID]: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
from: "server",
|
||||
msg: {
|
||||
action: "known",
|
||||
asDependencyOf: undefined,
|
||||
header: true,
|
||||
id: map.id,
|
||||
sessions: {
|
||||
[client.currentSessionID]: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
# jazz-browser-media-images
|
||||
|
||||
## 0.12.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cc684eb]
|
||||
- Updated dependencies [c2f4827]
|
||||
- jazz-browser@0.12.2
|
||||
- cojson@0.12.2
|
||||
- jazz-tools@0.12.2
|
||||
|
||||
## 0.12.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"name": "jazz-auth-clerk",
|
||||
"version": "0.12.2",
|
||||
"version": "0.12.1",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cojson": "workspace:0.12.2",
|
||||
"jazz-browser": "workspace:0.12.2",
|
||||
"jazz-tools": "workspace:0.12.2"
|
||||
"cojson": "workspace:0.12.1",
|
||||
"jazz-browser": "workspace:0.12.1",
|
||||
"jazz-tools": "workspace:0.12.1"
|
||||
},
|
||||
"scripts": {
|
||||
"format-and-lint": "biome check .",
|
||||
|
||||
@@ -1,13 +1,5 @@
|
||||
# jazz-browser-media-images
|
||||
|
||||
## 0.12.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [cc684eb]
|
||||
- jazz-browser@0.12.2
|
||||
- jazz-tools@0.12.2
|
||||
|
||||
## 0.12.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-browser-media-images",
|
||||
"version": "0.12.2",
|
||||
"version": "0.12.1",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
@@ -8,8 +8,8 @@
|
||||
"dependencies": {
|
||||
"@types/image-blob-reduce": "^4.1.1",
|
||||
"image-blob-reduce": "^4.1.0",
|
||||
"jazz-browser": "workspace:0.12.2",
|
||||
"jazz-tools": "workspace:0.12.2",
|
||||
"jazz-browser": "workspace:0.12.1",
|
||||
"jazz-tools": "workspace:0.12.1",
|
||||
"pica": "^9.0.1",
|
||||
"typescript": "~5.6.2"
|
||||
},
|
||||
|
||||
@@ -1,16 +1,5 @@
|
||||
# jazz-browser
|
||||
|
||||
## 0.12.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- cc684eb: PasskeyAuth: Change the private attributes visibility to protected
|
||||
- Updated dependencies [c2f4827]
|
||||
- cojson@0.12.2
|
||||
- cojson-storage-indexeddb@0.12.2
|
||||
- cojson-transport-ws@0.12.2
|
||||
- jazz-tools@0.12.2
|
||||
|
||||
## 0.12.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-browser",
|
||||
"version": "0.12.2",
|
||||
"version": "0.12.1",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
|
||||
@@ -19,9 +19,9 @@ import {
|
||||
*/
|
||||
export class BrowserPasskeyAuth {
|
||||
constructor(
|
||||
protected crypto: CryptoProvider,
|
||||
protected authenticate: AuthenticateAccountFunction,
|
||||
protected authSecretStorage: AuthSecretStorage,
|
||||
private crypto: CryptoProvider,
|
||||
private authenticate: AuthenticateAccountFunction,
|
||||
private authSecretStorage: AuthSecretStorage,
|
||||
public appName: string,
|
||||
public appHostname: string = window.location.hostname,
|
||||
) {}
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
# jazz-inspector
|
||||
|
||||
## 0.12.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 8a71835: use goober for css
|
||||
- Updated dependencies [c2f4827]
|
||||
- cojson@0.12.2
|
||||
- jazz-react-core@0.12.2
|
||||
- jazz-tools@0.12.2
|
||||
|
||||
## 0.12.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "jazz-inspector",
|
||||
"version": "0.12.2",
|
||||
"version": "0.12.1",
|
||||
"type": "module",
|
||||
"main": "./dist/app.js",
|
||||
"types": "./dist/app.d.ts",
|
||||
@@ -16,9 +16,11 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@twind/core": "^1.1.3",
|
||||
"@twind/preset-autoprefix": "^1.0.7",
|
||||
"@twind/preset-tailwind": "^1.1.4",
|
||||
"clsx": "^2.0.0",
|
||||
"cojson": "workspace:*",
|
||||
"goober": "^2.1.16",
|
||||
"jazz-react-core": "workspace:*",
|
||||
"jazz-tools": "workspace:*"
|
||||
},
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from "react";
|
||||
// Import Twind setup
|
||||
import "./twind.js";
|
||||
|
||||
export { JazzInspector } from "./viewer/new-app.js";
|
||||
export { PageStack } from "./viewer/page-stack.js";
|
||||
@@ -8,7 +9,6 @@ export { Button } from "./ui/button.js";
|
||||
export { Input } from "./ui/input.js";
|
||||
export { Select } from "./ui/select.js";
|
||||
export { Icon } from "./ui/icon.js";
|
||||
export { GlobalStyles } from "./ui/global-styles.js";
|
||||
|
||||
export {
|
||||
resolveCoValue,
|
||||
@@ -16,7 +16,3 @@ export {
|
||||
} from "./viewer/use-resolve-covalue.js";
|
||||
|
||||
export type { PageInfo } from "./viewer/types.js";
|
||||
|
||||
import { setup } from "goober";
|
||||
|
||||
setup(React.createElement);
|
||||
|
||||
55
packages/jazz-inspector/src/twind.config.ts
Normal file
55
packages/jazz-inspector/src/twind.config.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
import { defineConfig } from "@twind/core";
|
||||
import presetAutoprefix from "@twind/preset-autoprefix";
|
||||
import presetTailwind from "@twind/preset-tailwind";
|
||||
|
||||
const stonePalette = {
|
||||
50: "oklch(0.988281 0.002 75)",
|
||||
100: "oklch(0.980563 0.002 75)",
|
||||
200: "oklch(0.917969 0.002 75)",
|
||||
300: "oklch(0.853516 0.002 75)",
|
||||
400: "oklch(0.789063 0.002 75)",
|
||||
500: "oklch(0.726563 0.002 75)",
|
||||
600: "oklch(0.613281 0.002 75)",
|
||||
700: "oklch(0.523438 0.002 75)",
|
||||
800: "oklch(0.412109 0.002 75)",
|
||||
900: "oklch(0.302734 0.002 75)",
|
||||
925: "oklch(0.220000 0.002 75)",
|
||||
950: "oklch(0.193359 0.002 75)",
|
||||
};
|
||||
|
||||
const stonePaletteWithAlpha = { ...stonePalette };
|
||||
|
||||
Object.keys(stonePalette).forEach((key) => {
|
||||
// @ts-ignore
|
||||
stonePaletteWithAlpha[key] = stonePaletteWithAlpha[key].replace(
|
||||
")",
|
||||
"/ <alpha-value>)",
|
||||
);
|
||||
});
|
||||
|
||||
export default defineConfig({
|
||||
hash: true,
|
||||
presets: [presetAutoprefix(), presetTailwind()],
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
stone: stonePaletteWithAlpha,
|
||||
gray: stonePaletteWithAlpha,
|
||||
blue: {
|
||||
50: "#f5f7ff",
|
||||
100: "#ebf0fe",
|
||||
200: "#d6e0fd",
|
||||
300: "#b3c7fc",
|
||||
400: "#8aa6f9",
|
||||
500: "#5870F1",
|
||||
600: "#3651E7",
|
||||
700: "#3313F7",
|
||||
800: "#2A12BE",
|
||||
900: "#12046A",
|
||||
950: "#1e1b4b",
|
||||
DEFAULT: "#3313F7",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
8
packages/jazz-inspector/src/twind.ts
Normal file
8
packages/jazz-inspector/src/twind.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { setup } from "@twind/core";
|
||||
import config from "./twind.config";
|
||||
|
||||
export const tw = setup(
|
||||
config,
|
||||
undefined,
|
||||
document.getElementById("__jazz_inspector")!,
|
||||
);
|
||||
@@ -1,24 +0,0 @@
|
||||
import { styled } from "goober";
|
||||
|
||||
const StyledBadge = styled("span")<{ className?: string }>`
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
padding: 0.125rem 0.25rem;
|
||||
margin-left: -0.125rem;
|
||||
border-radius: var(--j-radius-sm);
|
||||
background-color: var(--j-neutral-200);
|
||||
display: inline-block;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
color: var(--j-text-color-strong);
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
background-color: var(--j-neutral-900);
|
||||
}
|
||||
`;
|
||||
|
||||
export function Badge({
|
||||
children,
|
||||
className,
|
||||
}: React.PropsWithChildren<{ className?: string }>) {
|
||||
return <StyledBadge className={className}>{children}</StyledBadge>;
|
||||
}
|
||||
@@ -1,63 +1,20 @@
|
||||
import { styled } from "goober";
|
||||
import { forwardRef } from "react";
|
||||
import { classNames } from "../utils.js";
|
||||
|
||||
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
variant?: "primary" | "secondary" | "link" | "plain";
|
||||
variant?: "primary" | "secondary" | "tertiary" | "destructive" | "plain";
|
||||
size?: "sm" | "md" | "lg";
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const StyledButton = styled("button")<{ variant: string; disabled?: boolean }>`
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
text-align: center;
|
||||
transition: colors 0.2s;
|
||||
border-radius: var(--j-radius-lg);
|
||||
pointer-events: ${(props) => (props.disabled ? "none" : "auto")};
|
||||
opacity: ${(props) => (props.disabled ? 0.5 : 1)};
|
||||
cursor: ${(props) => (props.disabled ? "not-allowed" : "pointer")};
|
||||
|
||||
${(props) => {
|
||||
switch (props.variant) {
|
||||
case "primary":
|
||||
return `
|
||||
padding: 0.375rem 0.75rem;
|
||||
background-color: var(--j-primary-color);
|
||||
border-color: var(--j-primary-color);
|
||||
color: white;
|
||||
font-weight: 500;
|
||||
`;
|
||||
case "secondary":
|
||||
return `
|
||||
padding: 0.375rem 0.75rem;
|
||||
color: var(--j-text-color-strong);
|
||||
border: 1px solid var(--j-border-color);
|
||||
font-weight: 500;
|
||||
&:hover {
|
||||
border-color: var(--j-border-color-hover);
|
||||
}
|
||||
`;
|
||||
case "link":
|
||||
return `
|
||||
color: var(--j-link-color);
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
`;
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}}
|
||||
`;
|
||||
|
||||
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
(
|
||||
{
|
||||
className,
|
||||
children,
|
||||
size = "md",
|
||||
variant = "primary",
|
||||
disabled,
|
||||
type = "button",
|
||||
@@ -65,17 +22,44 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const sizeClasses = {
|
||||
sm: "text-sm py-1 px-2",
|
||||
md: "py-1.5 px-3",
|
||||
lg: "md:text-lg py-2 px-3 md:px-8 md:py-3",
|
||||
};
|
||||
|
||||
const variantClasses = {
|
||||
primary:
|
||||
"bg-blue border-blue text-white font-medium bg-blue hover:bg-blue-800 hover:border-blue-800",
|
||||
secondary:
|
||||
"text-stone-900 border font-medium hover:border-stone-300 hover:dark:border-stone-700 dark:text-white",
|
||||
tertiary: "text-blue underline underline-offset-4",
|
||||
destructive:
|
||||
"bg-red-600 border-red-600 text-white font-medium hover:bg-red-700 hover:border-red-700",
|
||||
};
|
||||
|
||||
const classes =
|
||||
variant === "plain"
|
||||
? className
|
||||
: classNames(
|
||||
className,
|
||||
"inline-flex items-center justify-center gap-2 rounded-lg text-center transition-colors",
|
||||
"disabled:pointer-events-none disabled:opacity-70",
|
||||
sizeClasses[size],
|
||||
variantClasses[variant],
|
||||
disabled && "opacity-50 cursor-not-allowed pointer-events-none",
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledButton
|
||||
<button
|
||||
ref={ref}
|
||||
{...buttonProps}
|
||||
disabled={disabled}
|
||||
className={className}
|
||||
className={classes}
|
||||
type={type}
|
||||
variant={variant}
|
||||
>
|
||||
{children}
|
||||
</StyledButton>
|
||||
</button>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
import { styled } from "goober";
|
||||
|
||||
export const GlobalStyles = styled("div")`
|
||||
/* Colors */
|
||||
--j-primary-color: #3313F7;
|
||||
--j-link-color: var(--j-primary-color);
|
||||
|
||||
/* Neutral Colors */
|
||||
--j-neutral-100: #faf8f8;
|
||||
--j-neutral-200: #e5e3e4;
|
||||
--j-neutral-300: #d0cecf;
|
||||
--j-neutral-400: #bbbaba;
|
||||
--j-neutral-500: #a8a6a6;
|
||||
--j-neutral-600: #858484;
|
||||
--j-neutral-700: #6b696a;
|
||||
--j-neutral-900: #2f2e2e;
|
||||
--j-neutral-925: #1b1a1a;
|
||||
--j-neutral-950: #151414;
|
||||
|
||||
/* Text Colors */
|
||||
--j-text-color: var(--j-neutral-700);
|
||||
--j-text-color-strong: var(--j-neutral-900);
|
||||
|
||||
/* Border Colors */
|
||||
--j-border-color: var(--j-neutral-200);
|
||||
--j-border-color-hover: var(--j-neutral-300);
|
||||
--j-border-dark: var(--j-neutral-900);
|
||||
--j-border-focus: var(--j-primary-color);
|
||||
|
||||
/* Background Colors */
|
||||
--j-background: #FFFFFF;
|
||||
--j-foreground: var(--j-neutral-100);
|
||||
|
||||
/* Border Radius */
|
||||
--j-radius-sm: 0.25rem;
|
||||
--j-radius-md: 0.375rem;
|
||||
--j-radius-lg: 0.5rem;
|
||||
|
||||
/* Shadows */
|
||||
--j-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
--j-text-color: var(--j-neutral-400);
|
||||
--j-link-color: #5870f1;
|
||||
--j-border-color: var(--j-neutral-900);
|
||||
--j-background: var(--j-neutral-950);
|
||||
--j-foreground: var(--j-neutral-925);
|
||||
--j-border-color-hover: var(--j-neutral-700);
|
||||
--j-text-color-strong: var(--j-neutral-100);
|
||||
}
|
||||
|
||||
*:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
*:focus-visible {
|
||||
box-shadow: 0 0 0 2px var(--j-link-color);
|
||||
}
|
||||
|
||||
.j-sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
`;
|
||||
@@ -1,15 +0,0 @@
|
||||
import { styled } from "goober";
|
||||
|
||||
const StyledHeading = styled("h1")<{ className?: string }>`
|
||||
font-size: 1.125rem;
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
color: var(--j-text-color-strong);
|
||||
`;
|
||||
|
||||
export function Heading({
|
||||
children,
|
||||
className,
|
||||
}: React.PropsWithChildren<{ className?: string }>) {
|
||||
return <StyledHeading className={className}>{children}</StyledHeading>;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import { classNames } from "../utils.js";
|
||||
import { ChevronDownIcon } from "./icons/chevron-down-icon.js";
|
||||
import { DeleteIcon } from "./icons/delete-icon.js";
|
||||
import { LinkIcon } from "./icons/link-icon.js";
|
||||
@@ -51,6 +52,7 @@ export function Icon({
|
||||
}: {
|
||||
name?: keyof typeof icons;
|
||||
size?: keyof typeof sizes;
|
||||
className?: string;
|
||||
} & React.SVGProps<SVGSVGElement>) {
|
||||
if (!name || !icons.hasOwnProperty(name)) {
|
||||
throw new Error(`Icon not found: ${name}`);
|
||||
@@ -65,6 +67,7 @@ export function Icon({
|
||||
size={sizes[size]}
|
||||
strokeWidth={strokeWidths[size]}
|
||||
strokeLinecap="round"
|
||||
className={classNames(className)}
|
||||
{...svgProps}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
export function LinkIcon(props: React.SVGProps<SVGSVGElement>) {
|
||||
import { classNames } from "../../utils.js";
|
||||
|
||||
export function LinkIcon() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -6,7 +8,7 @@ export function LinkIcon(props: React.SVGProps<SVGSVGElement>) {
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={1.5}
|
||||
stroke="currentColor"
|
||||
{...props}
|
||||
className={classNames("w-3 h-3")}
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
|
||||
@@ -1,51 +1,40 @@
|
||||
import { styled } from "goober";
|
||||
import { forwardRef, useId } from "react";
|
||||
|
||||
interface LabelProps {
|
||||
hideLabel?: boolean;
|
||||
}
|
||||
|
||||
interface InputProps
|
||||
extends React.InputHTMLAttributes<HTMLInputElement>,
|
||||
LabelProps {
|
||||
import { classNames } from "../utils.js";
|
||||
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
|
||||
// label can be hidden with a "label:sr-only" className
|
||||
label: string;
|
||||
className?: string;
|
||||
id?: string;
|
||||
hideLabel?: boolean;
|
||||
}
|
||||
|
||||
const Container = styled("div")`
|
||||
display: grid;
|
||||
gap: 0.25rem;
|
||||
`;
|
||||
|
||||
const StyledInput = styled("input")`
|
||||
width: 100%;
|
||||
border-radius: var(--j-radius-md);
|
||||
border: 1px solid var(--j-border-color);
|
||||
padding: 0.5rem 0.875rem;
|
||||
box-shadow: var(--j-shadow-sm);
|
||||
font-weight: 500;
|
||||
background-color: white;
|
||||
color: var(--text-color-strong);
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
background-color: var(--j-foreground);
|
||||
}
|
||||
`;
|
||||
|
||||
export const Input = forwardRef<HTMLInputElement, InputProps>(
|
||||
({ label, className, hideLabel, id: customId, ...inputProps }, ref) => {
|
||||
const generatedId = useId();
|
||||
const id = customId || generatedId;
|
||||
|
||||
const inputClassName = classNames(
|
||||
"w-full rounded-md border px-3.5 py-2 shadow-sm",
|
||||
"font-medium text-stone-900",
|
||||
"dark:text-white dark:bg-stone-925",
|
||||
);
|
||||
|
||||
const containerClassName = classNames("grid gap-1", className);
|
||||
|
||||
return (
|
||||
<Container className={className}>
|
||||
<label htmlFor={id} className={hideLabel ? "j-sr-only" : ""}>
|
||||
<div className={containerClassName}>
|
||||
<label
|
||||
htmlFor={id}
|
||||
className={classNames(
|
||||
"text-stone-600 dark:text-stone-300",
|
||||
hideLabel && "sr-only",
|
||||
)}
|
||||
>
|
||||
{label}
|
||||
</label>
|
||||
<StyledInput ref={ref} {...inputProps} id={id} />
|
||||
</Container>
|
||||
|
||||
<input ref={ref} {...inputProps} id={id} className={inputClassName} />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,48 +1,7 @@
|
||||
import { styled } from "goober";
|
||||
import { useId } from "react";
|
||||
import { classNames } from "../utils.js";
|
||||
import { Icon } from "./icon.js";
|
||||
|
||||
const SelectContainer = styled("div")<{ className?: string }>`
|
||||
display: grid;
|
||||
gap: 0.25rem;
|
||||
`;
|
||||
|
||||
const SelectWrapper = styled("div")`
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const StyledSelect = styled("select")`
|
||||
width: 100%;
|
||||
border-radius: var(--j-radius-md);
|
||||
border: 1px solid var(--j-border-color);
|
||||
padding: 0.5rem 0.875rem 0.5rem 0.875rem;
|
||||
padding-right: 2rem;
|
||||
box-shadow: var(--j-shadow-sm);
|
||||
font-weight: 500;
|
||||
color: var(--j-text-color-strong);
|
||||
appearance: none;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
background-color: var(--j-foreground);
|
||||
}
|
||||
`;
|
||||
|
||||
const SelectIcon = styled("span")`
|
||||
position: absolute;
|
||||
right: 0.5em;
|
||||
color: var(--j-neutral-400);
|
||||
pointer-events: none;
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
color: var(--j-neutral-900);
|
||||
}
|
||||
`;
|
||||
|
||||
export function Select(
|
||||
props: React.SelectHTMLAttributes<HTMLSelectElement> & {
|
||||
label: string;
|
||||
@@ -53,21 +12,40 @@ export function Select(
|
||||
const generatedId = useId();
|
||||
const id = customId || generatedId;
|
||||
|
||||
const containerClassName = classNames("grid gap-1", className);
|
||||
|
||||
const selectClassName = classNames(
|
||||
"w-full rounded-md border pl-3.5 py-2 pr-8 shadow-sm",
|
||||
"font-medium text-stone-900",
|
||||
"dark:text-white dark:bg-stone-925",
|
||||
"appearance-none",
|
||||
"truncate",
|
||||
);
|
||||
|
||||
return (
|
||||
<SelectContainer className={className}>
|
||||
<label htmlFor={id} className={hideLabel ? "j-sr-only" : ""}>
|
||||
<div className={classNames(containerClassName)}>
|
||||
<label
|
||||
htmlFor={id}
|
||||
className={classNames("text-stone-600 dark:text-stone-300", {
|
||||
"sr-only": hideLabel,
|
||||
})}
|
||||
>
|
||||
{label}
|
||||
</label>
|
||||
|
||||
<SelectWrapper>
|
||||
<StyledSelect {...selectProps} id={id}>
|
||||
<div className={classNames("relative flex items-center")}>
|
||||
<select {...selectProps} id={id} className={selectClassName}>
|
||||
{props.children}
|
||||
</StyledSelect>
|
||||
</select>
|
||||
|
||||
<SelectIcon>
|
||||
<Icon name="chevronDown" size="sm" />
|
||||
</SelectIcon>
|
||||
</SelectWrapper>
|
||||
</SelectContainer>
|
||||
<Icon
|
||||
name="chevronDown"
|
||||
className={classNames(
|
||||
"absolute right-[0.5em] text-stone-400 dark:text-stone-600",
|
||||
)}
|
||||
size="sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
import { styled } from "goober";
|
||||
|
||||
const StyledTable = styled("table")`
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
const StyledThead = styled("thead")`
|
||||
text-align: left;
|
||||
border-bottom: 1px solid var(--j-border-color);
|
||||
background-color: var(--j-neutral-100);
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
background-color: var(--j-neutral-925);
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledTbody = styled("tbody")`
|
||||
tr {
|
||||
border-bottom: 1px solid var(--j-border-color);
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledTh = styled("th")`
|
||||
font-weight: 500;
|
||||
padding: 0.5rem 0.75rem;
|
||||
color: var(--j-text-color-strong);
|
||||
`;
|
||||
|
||||
const StyledTd = styled("td")`
|
||||
padding: 0.5rem 0.75rem;
|
||||
`;
|
||||
|
||||
export function Table({ children }: React.PropsWithChildren<{}>) {
|
||||
return <StyledTable>{children}</StyledTable>;
|
||||
}
|
||||
|
||||
export function TableHead({ children }: React.PropsWithChildren<{}>) {
|
||||
return <StyledThead>{children}</StyledThead>;
|
||||
}
|
||||
|
||||
export function TableBody({ children }: React.PropsWithChildren<{}>) {
|
||||
return <StyledTbody>{children}</StyledTbody>;
|
||||
}
|
||||
|
||||
export function TableRow({ children }: React.PropsWithChildren<{}>) {
|
||||
return <tr>{children}</tr>;
|
||||
}
|
||||
|
||||
export function TableHeader({ children }: React.PropsWithChildren<{}>) {
|
||||
return <StyledTh>{children}</StyledTh>;
|
||||
}
|
||||
|
||||
export function TableCell({ children }: React.PropsWithChildren<{}>) {
|
||||
return <StyledTd>{children}</StyledTd>;
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
import { styled } from "goober";
|
||||
import React from "react";
|
||||
|
||||
interface TextProps extends React.HTMLAttributes<HTMLParagraphElement> {
|
||||
muted?: boolean;
|
||||
strong?: boolean;
|
||||
small?: boolean;
|
||||
inline?: boolean;
|
||||
}
|
||||
|
||||
const BaseText = React.forwardRef<HTMLParagraphElement, TextProps>(
|
||||
({ muted, strong, small, inline, ...rest }, ref) => <p ref={ref} {...rest} />,
|
||||
);
|
||||
|
||||
const StyledText = styled(BaseText)<TextProps>`
|
||||
${(props) =>
|
||||
props.muted &&
|
||||
`
|
||||
color: var(--j-neutral-500);
|
||||
`}
|
||||
|
||||
${(props) =>
|
||||
props.strong &&
|
||||
`
|
||||
font-weight: 500;
|
||||
color: var(--j-text-color-strong);
|
||||
`}
|
||||
|
||||
${(props) =>
|
||||
props.small &&
|
||||
`
|
||||
font-size: 0.875rem;
|
||||
`}
|
||||
|
||||
${(props) =>
|
||||
props.inline &&
|
||||
`
|
||||
display: inline;
|
||||
`}
|
||||
`;
|
||||
|
||||
export function Text({
|
||||
children,
|
||||
className,
|
||||
muted,
|
||||
strong,
|
||||
inline,
|
||||
small,
|
||||
}: React.PropsWithChildren<{
|
||||
className?: string;
|
||||
muted?: boolean;
|
||||
strong?: boolean;
|
||||
inline?: boolean;
|
||||
small?: boolean;
|
||||
}>) {
|
||||
return (
|
||||
<StyledText
|
||||
className={className}
|
||||
muted={muted}
|
||||
strong={strong}
|
||||
inline={inline}
|
||||
small={small}
|
||||
>
|
||||
{children}
|
||||
</StyledText>
|
||||
);
|
||||
}
|
||||
6
packages/jazz-inspector/src/utils.ts
Normal file
6
packages/jazz-inspector/src/utils.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { ClassValue, clsx } from "clsx";
|
||||
import { tw } from "./twind.js";
|
||||
|
||||
export const classNames = (...inputs: ClassValue[]) => {
|
||||
return tw(clsx(inputs));
|
||||
};
|
||||
@@ -1,19 +1,8 @@
|
||||
import { styled } from "goober";
|
||||
import React from "react";
|
||||
import { Button } from "../ui/button.js";
|
||||
import { PageInfo } from "./types.js";
|
||||
|
||||
const BreadcrumbsContainer = styled("div")`
|
||||
position: relative;
|
||||
z-index: 20;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
const Separator = styled("span")`
|
||||
padding: 0 0.125rem;
|
||||
`;
|
||||
import { classNames } from "../utils.js";
|
||||
|
||||
interface BreadcrumbsProps {
|
||||
path: PageInfo[];
|
||||
@@ -25,10 +14,10 @@ export const Breadcrumbs: React.FC<BreadcrumbsProps> = ({
|
||||
onBreadcrumbClick,
|
||||
}) => {
|
||||
return (
|
||||
<BreadcrumbsContainer>
|
||||
<div className={classNames("relative z-20 flex-1 flex items-center")}>
|
||||
<Button
|
||||
variant="link"
|
||||
style={{ padding: "0 0.25rem" }}
|
||||
variant="plain"
|
||||
className={classNames("text-blue px-1 dark:text-blue-400")}
|
||||
onClick={() => onBreadcrumbClick(-1)}
|
||||
>
|
||||
Home
|
||||
@@ -36,10 +25,17 @@ export const Breadcrumbs: React.FC<BreadcrumbsProps> = ({
|
||||
{path.map((page, index) => {
|
||||
return (
|
||||
<React.Fragment key={page.coId}>
|
||||
<Separator aria-hidden>/</Separator>
|
||||
<span
|
||||
aria-hidden
|
||||
className={classNames(
|
||||
"text-stone-400 dark:text-stone-600 px-0.5",
|
||||
)}
|
||||
>
|
||||
/
|
||||
</span>
|
||||
<Button
|
||||
variant="link"
|
||||
style={{ padding: "0 0.25rem" }}
|
||||
variant="plain"
|
||||
className={classNames("text-blue px-1 dark:text-blue-400")}
|
||||
onClick={() => onBreadcrumbClick(index)}
|
||||
>
|
||||
{index === 0 ? page.name || "Root" : page.name}
|
||||
@@ -47,6 +43,6 @@ export const Breadcrumbs: React.FC<BreadcrumbsProps> = ({
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</BreadcrumbsContainer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -8,13 +8,13 @@ import {
|
||||
import { base64URLtoBytes } from "cojson";
|
||||
import { BinaryStreamItem, BinaryStreamStart, CoStreamItem } from "cojson";
|
||||
import type { JsonObject, JsonValue } from "cojson";
|
||||
import { styled } from "goober";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Badge } from "../ui/badge.js";
|
||||
import { Button } from "../ui/button.js";
|
||||
import { PageInfo } from "./types.js";
|
||||
import { AccountOrGroupPreview } from "./value-renderer.js";
|
||||
|
||||
import { classNames } from "../utils.js";
|
||||
|
||||
// typeguard for BinaryStreamStart
|
||||
function isBinaryStreamStart(item: unknown): item is BinaryStreamStart {
|
||||
return (
|
||||
@@ -156,53 +156,6 @@ const BinaryDownloadButton = ({
|
||||
);
|
||||
};
|
||||
|
||||
const LabelContentPairContainer = styled("div")`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.375rem;
|
||||
`;
|
||||
|
||||
const BinaryStreamContainer = styled("div")`
|
||||
margin-top: 2rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2rem;
|
||||
`;
|
||||
|
||||
const BinaryStreamGrid = styled("div")`
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 0.5rem;
|
||||
max-width: 48rem;
|
||||
`;
|
||||
|
||||
const ImagePreviewContainer = styled("div")`
|
||||
background-color: rgb(249 250 251);
|
||||
padding: 0.75rem;
|
||||
border-radius: var(--j-radius-md);
|
||||
@media (prefers-color-scheme: dark) {
|
||||
background-color: rgb(28 25 23);
|
||||
}
|
||||
`;
|
||||
|
||||
const CoStreamGrid = styled("div")`
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 0.5rem;
|
||||
`;
|
||||
|
||||
const CoStreamItemContainer = styled("div")`
|
||||
padding: 0.75rem;
|
||||
border-radius: var(--j-radius-lg);
|
||||
overflow: hidden;
|
||||
border: 1px solid rgb(229 231 235);
|
||||
cursor: pointer;
|
||||
box-shadow: var(--j-shadow-sm);
|
||||
&:hover {
|
||||
background-color: rgb(243 244 246 / 0.05);
|
||||
}
|
||||
`;
|
||||
|
||||
const LabelContentPair = ({
|
||||
label,
|
||||
content,
|
||||
@@ -211,10 +164,10 @@ const LabelContentPair = ({
|
||||
content: React.ReactNode;
|
||||
}) => {
|
||||
return (
|
||||
<LabelContentPairContainer>
|
||||
<div className={classNames("flex flex-col gap-1.5")}>
|
||||
<span>{label}</span>
|
||||
<span>{content}</span>
|
||||
</LabelContentPairContainer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -269,11 +222,19 @@ function RenderCoBinaryStream({
|
||||
const sizeInKB = (file.totalSize || 0) / 1024;
|
||||
|
||||
return (
|
||||
<BinaryStreamContainer>
|
||||
<BinaryStreamGrid>
|
||||
<div className={classNames("mt-8 flex flex-col gap-8")}>
|
||||
<div className={classNames("grid grid-cols-3 gap-2 max-w-3xl")}>
|
||||
<LabelContentPair
|
||||
label="Mime Type"
|
||||
content={<Badge>{mimeType || "No mime type"}</Badge>}
|
||||
content={
|
||||
<span
|
||||
className={classNames(
|
||||
"font-mono bg-gray-100 rounded px-2 py-1 text-sm dark:bg-stone-900",
|
||||
)}
|
||||
>
|
||||
{mimeType || "No mime type"}
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
<LabelContentPair
|
||||
label="Size"
|
||||
@@ -294,18 +255,20 @@ function RenderCoBinaryStream({
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</BinaryStreamGrid>
|
||||
</div>
|
||||
{mimeType === "image/png" || mimeType === "image/jpeg" ? (
|
||||
<LabelContentPair
|
||||
label="Preview"
|
||||
content={
|
||||
<ImagePreviewContainer>
|
||||
<div
|
||||
className={classNames("bg-gray-50 dark:bg-gray-925 p-3 rounded")}
|
||||
>
|
||||
<RenderBlobImage blob={blob} />
|
||||
</ImagePreviewContainer>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
) : null}
|
||||
</BinaryStreamContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -320,9 +283,14 @@ function RenderCoStream({
|
||||
const userCoIds = streamPerUser.map((stream) => stream.split("_session")[0]);
|
||||
|
||||
return (
|
||||
<CoStreamGrid>
|
||||
<div className={classNames("grid grid-cols-3 gap-2")}>
|
||||
{userCoIds.map((id, idx) => (
|
||||
<CoStreamItemContainer key={id}>
|
||||
<div
|
||||
className={classNames(
|
||||
"p-3 rounded-lg overflow-hidden border border-gray-200 cursor-pointer shadow-sm hover:bg-gray-100/5",
|
||||
)}
|
||||
key={id}
|
||||
>
|
||||
<AccountOrGroupPreview coId={id as CoID<RawCoValue>} node={node} />
|
||||
{/* @ts-expect-error - TODO: fix types */}
|
||||
{value.items[streamPerUser[idx]]?.map(
|
||||
@@ -333,9 +301,9 @@ function RenderCoStream({
|
||||
</div>
|
||||
),
|
||||
)}
|
||||
</CoStreamItemContainer>
|
||||
</div>
|
||||
))}
|
||||
</CoStreamGrid>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,66 +1,11 @@
|
||||
import { CoID, LocalNode, RawCoValue } from "cojson";
|
||||
import { JsonObject } from "cojson";
|
||||
import { styled } from "goober";
|
||||
import { Button } from "../ui/button.js";
|
||||
import { ResolveIcon } from "./type-icon.js";
|
||||
import { PageInfo, isCoId } from "./types.js";
|
||||
import { CoMapPreview, ValueRenderer } from "./value-renderer.js";
|
||||
|
||||
import { Badge } from "../ui/badge.js";
|
||||
import { Text } from "../ui/text.js";
|
||||
|
||||
const GridContainer = styled("div")`
|
||||
display: grid;
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
gap: 1rem;
|
||||
|
||||
@media (min-width: 768px) {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
@media (min-width: 1280px) {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
`;
|
||||
|
||||
const GridItem = styled(
|
||||
({
|
||||
isCoId,
|
||||
...rest
|
||||
}: { isCoId: boolean } & React.ButtonHTMLAttributes<HTMLButtonElement>) => (
|
||||
<button {...rest} />
|
||||
),
|
||||
)`
|
||||
padding: 0.75rem;
|
||||
text-align: left;
|
||||
border-radius: var(--j-radius-lg);
|
||||
overflow: hidden;
|
||||
transition: background-color 0.2s;
|
||||
cursor: ${(props) => (props.isCoId ? "pointer" : "default")};
|
||||
|
||||
${(props) =>
|
||||
props.isCoId
|
||||
? `
|
||||
border: 1px solid var(--j-border-color);
|
||||
box-shadow: var(--j-shadow-sm);
|
||||
`
|
||||
: `
|
||||
background-color: var(--j-foreground);
|
||||
`}
|
||||
`;
|
||||
|
||||
const TitleContainer = styled("h3")`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: var(--j-text-color-strong);
|
||||
`;
|
||||
|
||||
const ContentContainer = styled("div")`
|
||||
margin-top: 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
`;
|
||||
import { classNames } from "../utils.js";
|
||||
|
||||
export function GridView({
|
||||
data,
|
||||
@@ -74,29 +19,49 @@ export function GridView({
|
||||
const entries = Object.entries(data);
|
||||
|
||||
return (
|
||||
<GridContainer>
|
||||
<div
|
||||
className={classNames(
|
||||
"grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4",
|
||||
)}
|
||||
>
|
||||
{entries.map(([key, child], childIndex) => (
|
||||
<GridItem
|
||||
<Button
|
||||
variant="plain"
|
||||
key={childIndex}
|
||||
isCoId={isCoId(child)}
|
||||
className={classNames(
|
||||
`p-3 text-left rounded-lg overflow-hidden transition-colors ${
|
||||
isCoId(child)
|
||||
? "border border-gray-200 shadow-sm hover:bg-gray-100/5"
|
||||
: "bg-gray-50 dark:bg-gray-925 cursor-default"
|
||||
}`,
|
||||
)}
|
||||
onClick={() =>
|
||||
isCoId(child) &&
|
||||
onNavigate([{ coId: child as CoID<RawCoValue>, name: key }])
|
||||
}
|
||||
>
|
||||
<TitleContainer>
|
||||
{isCoId(child) ? (
|
||||
<>
|
||||
<Text strong>{key}</Text>
|
||||
<Badge>
|
||||
<ResolveIcon coId={child as CoID<RawCoValue>} node={node} />
|
||||
</Badge>
|
||||
</>
|
||||
) : (
|
||||
<Text strong>{key}</Text>
|
||||
<h3
|
||||
className={classNames(
|
||||
"overflow-hidden text-ellipsis whitespace-nowrap",
|
||||
)}
|
||||
</TitleContainer>
|
||||
<ContentContainer>
|
||||
>
|
||||
{isCoId(child) ? (
|
||||
<span className={classNames("font-medium flex justify-between")}>
|
||||
{key}
|
||||
|
||||
<div
|
||||
className={classNames(
|
||||
"py-1 px-2 text-sm bg-gray-100 rounded dark:bg-gray-900",
|
||||
)}
|
||||
>
|
||||
<ResolveIcon coId={child as CoID<RawCoValue>} node={node} />
|
||||
</div>
|
||||
</span>
|
||||
) : (
|
||||
<span>{key}</span>
|
||||
)}
|
||||
</h3>
|
||||
<div className={classNames("mt-2 text-sm")}>
|
||||
{isCoId(child) ? (
|
||||
<CoMapPreview coId={child as CoID<RawCoValue>} node={node} />
|
||||
) : (
|
||||
@@ -107,9 +72,9 @@ export function GridView({
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</ContentContainer>
|
||||
</GridItem>
|
||||
</div>
|
||||
</Button>
|
||||
))}
|
||||
</GridContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
import { styled } from "goober";
|
||||
import React from "react";
|
||||
|
||||
export type Position =
|
||||
| "bottom right"
|
||||
| "bottom left"
|
||||
| "top right"
|
||||
| "top left"
|
||||
| "right"
|
||||
| "left";
|
||||
|
||||
const StyledInspectorButton = styled("button")<{ position: Position }>`
|
||||
position: fixed;
|
||||
width: 2.5rem;
|
||||
height: 2.5rem;
|
||||
background-color: white;
|
||||
box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
|
||||
margin: 1rem;
|
||||
padding: 0.5rem !important;
|
||||
border: 1px solid #e5e3e4;
|
||||
border-radius: 0.375rem;
|
||||
|
||||
${(props) => {
|
||||
switch (props.position) {
|
||||
case "bottom right":
|
||||
return "bottom: 0; right: 0;";
|
||||
case "bottom left":
|
||||
return "bottom: 0; left: 0;";
|
||||
case "top right":
|
||||
return "top: 0; right: 0;";
|
||||
case "top left":
|
||||
return "top: 0; left: 0;";
|
||||
case "right":
|
||||
return "right: 0; top: 50%; transform: translateY(-50%);";
|
||||
case "left":
|
||||
return "left: 0; top: 50%; transform: translateY(-50%);";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}}
|
||||
`;
|
||||
|
||||
const JazzIcon = styled("svg")`
|
||||
width: 100%;
|
||||
height: auto;
|
||||
position: relative;
|
||||
left: -1px;
|
||||
color: #3313F7;
|
||||
`;
|
||||
|
||||
export function InspectorButton({
|
||||
position = "right",
|
||||
...buttonProps
|
||||
}: React.ComponentPropsWithoutRef<"button"> & { position?: Position }) {
|
||||
return (
|
||||
<StyledInspectorButton position={position} {...buttonProps}>
|
||||
<JazzIcon
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="119"
|
||||
height="115"
|
||||
viewBox="0 0 119 115"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M118.179 23.8277V0.167999C99.931 7.5527 79.9854 11.6192 59.0897 11.6192C47.1466 11.6192 35.5138 10.2908 24.331 7.7737V30.4076V60.1508C23.2955 59.4385 22.1568 58.8458 20.9405 58.3915C18.1732 57.358 15.128 57.0876 12.1902 57.6145C9.2524 58.1414 6.5539 59.4419 4.4358 61.3516C2.3178 63.2613 0.875401 65.6944 0.291001 68.3433C-0.293399 70.9921 0.00659978 73.7377 1.1528 76.2329C2.2991 78.728 4.2403 80.861 6.7308 82.361C9.2214 83.862 12.1495 84.662 15.1448 84.662C15.6054 84.662 15.8365 84.662 16.0314 84.659C26.5583 84.449 35.042 75.9656 35.2513 65.4386C35.2534 65.3306 35.2544 65.2116 35.2548 65.0486L35.2552 64.7149V64.5521V61.0762V32.1993C43.0533 33.2324 51.0092 33.7656 59.0897 33.7656C59.6696 33.7656 60.2489 33.7629 60.8276 33.7574V89.696C59.792 88.983 58.6533 88.391 57.437 87.936C54.6697 86.903 51.6246 86.632 48.6867 87.159C45.7489 87.686 43.0504 88.987 40.9323 90.896C38.8143 92.806 37.3719 95.239 36.7875 97.888C36.2032 100.537 36.5031 103.283 37.6494 105.778C38.7956 108.273 40.7368 110.405 43.2273 111.906C45.7179 113.406 48.646 114.207 51.6414 114.207C52.1024 114.207 52.3329 114.207 52.5279 114.203C63.0548 113.994 71.5385 105.51 71.7478 94.983C71.7517 94.788 71.7517 94.558 71.7517 94.097V90.621V33.3266C83.962 32.4768 95.837 30.4075 107.255 27.2397V59.9017C106.219 59.1894 105.081 58.5966 103.864 58.1424C101.097 57.1089 98.052 56.8384 95.114 57.3653C92.176 57.8922 89.478 59.1927 87.36 61.1025C85.242 63.0122 83.799 65.4453 83.215 68.0941C82.631 70.743 82.931 73.4886 84.077 75.9837C85.223 78.4789 87.164 80.612 89.655 82.112C92.145 83.612 95.073 84.413 98.069 84.413C98.53 84.413 98.76 84.413 98.955 84.409C109.482 84.2 117.966 75.7164 118.175 65.1895C118.179 64.9945 118.179 64.764 118.179 64.3029V60.8271V23.8277Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</JazzIcon>
|
||||
<span
|
||||
style={{
|
||||
position: "absolute",
|
||||
width: "1px",
|
||||
height: "1px",
|
||||
padding: "0",
|
||||
margin: "-1px",
|
||||
overflow: "hidden",
|
||||
clip: "rect(0, 0, 0, 0)",
|
||||
whiteSpace: "nowrap",
|
||||
border: "0",
|
||||
}}
|
||||
>
|
||||
Open Jazz Inspector
|
||||
</span>
|
||||
</StyledInspectorButton>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import { CoID, RawCoValue } from "cojson";
|
||||
import { styled } from "goober";
|
||||
import { useAccount } from "jazz-react-core";
|
||||
import React, { useState } from "react";
|
||||
import { Button } from "../ui/button.js";
|
||||
@@ -8,55 +7,15 @@ import { Breadcrumbs } from "./breadcrumbs.js";
|
||||
import { PageStack } from "./page-stack.js";
|
||||
import { usePagePath } from "./use-page-path.js";
|
||||
|
||||
import { GlobalStyles } from "../ui/global-styles.js";
|
||||
import { Heading } from "../ui/heading.js";
|
||||
import { InspectorButton, type Position } from "./inpsector-button.js";
|
||||
import { classNames } from "../utils.js";
|
||||
|
||||
const InspectorContainer = styled("div")`
|
||||
position: fixed;
|
||||
height: calc(100% - 12rem);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background-color: white;
|
||||
border-top: 1px solid var(--j-border-color);
|
||||
color: var(--j-text-color);
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
background-color: var(--j-background);
|
||||
}
|
||||
`;
|
||||
|
||||
const HeaderContainer = styled("div")`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
padding: 0 0.75rem;
|
||||
margin: 0.75rem 0;
|
||||
`;
|
||||
|
||||
const Form = styled("form")`
|
||||
width: 24rem;
|
||||
`;
|
||||
|
||||
const InitialForm = styled("form")`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
position: relative;
|
||||
top: -1.5rem;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
max-width: 24rem;
|
||||
margin: 0 auto;
|
||||
`;
|
||||
|
||||
const OrText = styled("p")`
|
||||
text-align: center;
|
||||
`;
|
||||
type Position =
|
||||
| "bottom right"
|
||||
| "bottom left"
|
||||
| "top right"
|
||||
| "top left"
|
||||
| "right"
|
||||
| "left";
|
||||
|
||||
export function JazzInspector({ position = "right" }: { position?: Position }) {
|
||||
const [open, setOpen] = useState(false);
|
||||
@@ -76,32 +35,73 @@ export function JazzInspector({ position = "right" }: { position?: Position }) {
|
||||
setCoValueId("");
|
||||
};
|
||||
|
||||
const positionClasses = {
|
||||
"bottom right": "bottom-0 right-0",
|
||||
"bottom left": "bottom-0 left-0",
|
||||
"top right": "top-0 right-0",
|
||||
"top left": "top-0 left-0",
|
||||
right: "right-0 top-1/2 -translate-y-1/2",
|
||||
left: "left-0 top-1/2 -translate-y-1/2",
|
||||
};
|
||||
|
||||
if (!open) {
|
||||
// not sure if this will work, probably is better to use inline styles for the button, but please check.
|
||||
|
||||
return (
|
||||
<InspectorButton position={position} onClick={() => setOpen(true)} />
|
||||
<Button
|
||||
id="__jazz_inspector"
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
onClick={() => setOpen(true)}
|
||||
className={classNames(
|
||||
`fixed w-10 h-10 bg-white shadow-sm bottom-0 right-0 m-4 p-1.5 ${positionClasses[position]}`,
|
||||
)}
|
||||
>
|
||||
<svg
|
||||
className={classNames("w-full h-auto relative -left-px text-blue")}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="119"
|
||||
height="115"
|
||||
viewBox="0 0 119 115"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M118.179 23.8277V0.167999C99.931 7.5527 79.9854 11.6192 59.0897 11.6192C47.1466 11.6192 35.5138 10.2908 24.331 7.7737V30.4076V60.1508C23.2955 59.4385 22.1568 58.8458 20.9405 58.3915C18.1732 57.358 15.128 57.0876 12.1902 57.6145C9.2524 58.1414 6.5539 59.4419 4.4358 61.3516C2.3178 63.2613 0.875401 65.6944 0.291001 68.3433C-0.293399 70.9921 0.00659978 73.7377 1.1528 76.2329C2.2991 78.728 4.2403 80.861 6.7308 82.361C9.2214 83.862 12.1495 84.662 15.1448 84.662C15.6054 84.662 15.8365 84.662 16.0314 84.659C26.5583 84.449 35.042 75.9656 35.2513 65.4386C35.2534 65.3306 35.2544 65.2116 35.2548 65.0486L35.2552 64.7149V64.5521V61.0762V32.1993C43.0533 33.2324 51.0092 33.7656 59.0897 33.7656C59.6696 33.7656 60.2489 33.7629 60.8276 33.7574V89.696C59.792 88.983 58.6533 88.391 57.437 87.936C54.6697 86.903 51.6246 86.632 48.6867 87.159C45.7489 87.686 43.0504 88.987 40.9323 90.896C38.8143 92.806 37.3719 95.239 36.7875 97.888C36.2032 100.537 36.5031 103.283 37.6494 105.778C38.7956 108.273 40.7368 110.405 43.2273 111.906C45.7179 113.406 48.646 114.207 51.6414 114.207C52.1024 114.207 52.3329 114.207 52.5279 114.203C63.0548 113.994 71.5385 105.51 71.7478 94.983C71.7517 94.788 71.7517 94.558 71.7517 94.097V90.621V33.3266C83.962 32.4768 95.837 30.4075 107.255 27.2397V59.9017C106.219 59.1894 105.081 58.5966 103.864 58.1424C101.097 57.1089 98.052 56.8384 95.114 57.3653C92.176 57.8922 89.478 59.1927 87.36 61.1025C85.242 63.0122 83.799 65.4453 83.215 68.0941C82.631 70.743 82.931 73.4886 84.077 75.9837C85.223 78.4789 87.164 80.612 89.655 82.112C92.145 83.612 95.073 84.413 98.069 84.413C98.53 84.413 98.76 84.413 98.955 84.409C109.482 84.2 117.966 75.7164 118.175 65.1895C118.179 64.9945 118.179 64.764 118.179 64.3029V60.8271V23.8277Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
<span className={classNames("sr-only")}>Open Jazz Inspector</span>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<InspectorContainer as={GlobalStyles} id="__jazz_inspector">
|
||||
<HeaderContainer>
|
||||
<div
|
||||
className={classNames(
|
||||
"fixed h-[calc(100%-12rem)] flex flex-col bottom-0 left-0 w-full bg-white border-t border-gray-200 dark:border-stone-900 dark:bg-stone-925",
|
||||
)}
|
||||
id="__jazz_inspector"
|
||||
>
|
||||
<div className={classNames("flex items-center gap-4 px-3 my-3")}>
|
||||
<Breadcrumbs path={path} onBreadcrumbClick={goToIndex} />
|
||||
<Form onSubmit={handleCoValueIdSubmit}>
|
||||
<form onSubmit={handleCoValueIdSubmit} className={classNames("w-96")}>
|
||||
{path.length !== 0 && (
|
||||
<Input
|
||||
label="CoValue ID"
|
||||
style={{ fontFamily: "monospace" }}
|
||||
className={classNames("font-mono")}
|
||||
hideLabel
|
||||
placeholder="co_z1234567890abcdef123456789"
|
||||
value={coValueId}
|
||||
onChange={(e) => setCoValueId(e.target.value as CoID<RawCoValue>)}
|
||||
/>
|
||||
)}
|
||||
</Form>
|
||||
</form>
|
||||
<Button variant="plain" type="button" onClick={() => setOpen(false)}>
|
||||
Close
|
||||
</Button>
|
||||
</HeaderContainer>
|
||||
</div>
|
||||
|
||||
<PageStack
|
||||
path={path}
|
||||
@@ -110,14 +110,23 @@ export function JazzInspector({ position = "right" }: { position?: Position }) {
|
||||
addPages={addPages}
|
||||
>
|
||||
{path.length <= 0 && (
|
||||
<InitialForm
|
||||
<form
|
||||
onSubmit={handleCoValueIdSubmit}
|
||||
aria-hidden={path.length !== 0}
|
||||
className={classNames(
|
||||
"flex flex-col relative -top-6 justify-center gap-2 h-full w-full max-w-sm mx-auto",
|
||||
)}
|
||||
>
|
||||
<Heading>Jazz CoValue Inspector</Heading>
|
||||
<h2
|
||||
className={classNames(
|
||||
"text-lg text-center font-medium mb-4 text-stone-900 dark:text-white",
|
||||
)}
|
||||
>
|
||||
Jazz CoValue Inspector
|
||||
</h2>
|
||||
<Input
|
||||
label="CoValue ID"
|
||||
style={{ minWidth: "21rem", fontFamily: "monospace" }}
|
||||
className={classNames("min-w-[21rem] font-mono")}
|
||||
hideLabel
|
||||
placeholder="co_z1234567890abcdef123456789"
|
||||
value={coValueId}
|
||||
@@ -127,7 +136,7 @@ export function JazzInspector({ position = "right" }: { position?: Position }) {
|
||||
Inspect CoValue
|
||||
</Button>
|
||||
|
||||
<OrText>or</OrText>
|
||||
<p className={classNames("text-center")}>or</p>
|
||||
|
||||
<Button
|
||||
variant="secondary"
|
||||
@@ -138,9 +147,9 @@ export function JazzInspector({ position = "right" }: { position?: Position }) {
|
||||
>
|
||||
Inspect my account
|
||||
</Button>
|
||||
</InitialForm>
|
||||
</form>
|
||||
)}
|
||||
</PageStack>
|
||||
</InspectorContainer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { CoID, LocalNode, RawCoValue } from "cojson";
|
||||
import { styled } from "goober";
|
||||
import { Page } from "./page.js";
|
||||
import { Page } from "./page.js"; // Assuming you have a Page component
|
||||
|
||||
import { classNames } from "../utils.js";
|
||||
// Define the structure of a page in the path
|
||||
interface PageInfo {
|
||||
coId: CoID<RawCoValue>;
|
||||
@@ -17,15 +17,6 @@ interface PageStackProps {
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
const PageStackContainer = styled("div")`
|
||||
position: relative;
|
||||
padding: 0 0.75rem;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
color: var(--j-text-color);
|
||||
font-size: 16px;
|
||||
`;
|
||||
|
||||
export function PageStack({
|
||||
path,
|
||||
node,
|
||||
@@ -37,20 +28,22 @@ export function PageStack({
|
||||
const index = path.length - 1;
|
||||
|
||||
return (
|
||||
<>
|
||||
<PageStackContainer>
|
||||
{children}
|
||||
{node && page && (
|
||||
<Page
|
||||
coId={page.coId}
|
||||
node={node}
|
||||
name={page.name || page.coId}
|
||||
onHeaderClick={goBack}
|
||||
onNavigate={addPages}
|
||||
isTopLevel={index === path.length - 1}
|
||||
/>
|
||||
)}
|
||||
</PageStackContainer>
|
||||
</>
|
||||
<div
|
||||
className={classNames(
|
||||
"relative px-3 overflow-y-auto flex-1 text-stone-700 dark:text-stone-400",
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
{node && page && (
|
||||
<Page
|
||||
coId={page.coId}
|
||||
node={node}
|
||||
name={page.name || page.coId}
|
||||
onHeaderClick={goBack}
|
||||
onNavigate={addPages}
|
||||
isTopLevel={index === path.length - 1}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
import { CoID, LocalNode, RawCoStream, RawCoValue } from "cojson";
|
||||
import { styled } from "goober";
|
||||
import { useMemo } from "react";
|
||||
import React from "react";
|
||||
import { Badge } from "../ui/badge.js";
|
||||
import { Heading } from "../ui/heading.js";
|
||||
import { Text } from "../ui/text.js";
|
||||
import { classNames } from "../utils.js";
|
||||
import { CoStreamView } from "./co-stream-view.js";
|
||||
import { GridView } from "./grid-view.js";
|
||||
import { TableView } from "./table-viewer.js";
|
||||
@@ -13,65 +9,6 @@ import { PageInfo } from "./types.js";
|
||||
import { useResolvedCoValue } from "./use-resolve-covalue.js";
|
||||
import { AccountOrGroupPreview } from "./value-renderer.js";
|
||||
|
||||
interface PageContainerProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
isTopLevel?: boolean;
|
||||
}
|
||||
|
||||
const BasePageContainer = React.forwardRef<HTMLDivElement, PageContainerProps>(
|
||||
({ isTopLevel, ...rest }, ref) => <div ref={ref} {...rest} />,
|
||||
);
|
||||
|
||||
const PageContainer = styled(BasePageContainer)<PageContainerProps>`
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0 0.75rem;
|
||||
`;
|
||||
|
||||
const BackButton = styled("div")`
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
height: 2.5rem;
|
||||
`;
|
||||
|
||||
const HeaderContainer = styled("div")`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
`;
|
||||
|
||||
const TitleContainer = styled("div")`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
`;
|
||||
|
||||
const Title = styled(Heading)`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 0.25rem;
|
||||
`;
|
||||
|
||||
const BadgeContainer = styled("div")`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
`;
|
||||
|
||||
const ContentContainer = styled("div")`
|
||||
overflow: auto;
|
||||
`;
|
||||
|
||||
const OwnerText = styled(Text)`
|
||||
margin-top: 1rem;
|
||||
`;
|
||||
|
||||
type PageProps = {
|
||||
coId: CoID<RawCoValue>;
|
||||
node: LocalNode;
|
||||
@@ -115,38 +52,54 @@ export function Page({
|
||||
}
|
||||
|
||||
return (
|
||||
<PageContainer style={style} className={className} isTopLevel={isTopLevel}>
|
||||
<div
|
||||
style={style}
|
||||
className={className + "absolute z-10 inset-0 w-full h-full px-3"}
|
||||
>
|
||||
{!isTopLevel && (
|
||||
<BackButton
|
||||
<div
|
||||
className={classNames("absolute left-0 right-0 top-0 h-10")}
|
||||
aria-label="Back"
|
||||
onClick={() => {
|
||||
onHeaderClick?.();
|
||||
}}
|
||||
aria-hidden="true"
|
||||
></BackButton>
|
||||
></div>
|
||||
)}
|
||||
<HeaderContainer>
|
||||
<TitleContainer>
|
||||
<Title>
|
||||
<div className={classNames("flex justify-between items-center mb-4")}>
|
||||
<div className={classNames("flex items-center gap-3")}>
|
||||
<h2
|
||||
className={classNames(
|
||||
"text-lg font-medium flex flex-col items-start gap-1 text-stone-900 dark:text-white",
|
||||
)}
|
||||
>
|
||||
<span>
|
||||
{name}
|
||||
{typeof snapshot === "object" && "name" in snapshot ? (
|
||||
<span style={{ color: "#57534e", fontWeight: 500 }}>
|
||||
<span className={classNames("text-gray-600 font-medium")}>
|
||||
{" "}
|
||||
{(snapshot as { name: string }).name}
|
||||
</span>
|
||||
) : null}
|
||||
</span>
|
||||
</Title>
|
||||
<BadgeContainer>
|
||||
<Badge>
|
||||
{type && <TypeIcon type={type} extendedType={extendedType} />}
|
||||
</Badge>
|
||||
<Badge>{coId}</Badge>
|
||||
</BadgeContainer>
|
||||
</TitleContainer>
|
||||
</HeaderContainer>
|
||||
<ContentContainer>
|
||||
</h2>
|
||||
<span
|
||||
className={classNames(
|
||||
"text-sm text-gray-700 font-medium py-0.5 px-1 -ml-0.5 rounded bg-gray-700/5 inline-block font-mono",
|
||||
)}
|
||||
>
|
||||
{type && <TypeIcon type={type} extendedType={extendedType} />}
|
||||
</span>
|
||||
<span
|
||||
className={classNames(
|
||||
"text-sm text-gray-700 font-medium py-0.5 px-1 -ml-0.5 rounded bg-gray-700/5 inline-block font-mono",
|
||||
)}
|
||||
>
|
||||
{coId}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classNames("overflow-auto")}>
|
||||
{type === "costream" ? (
|
||||
<CoStreamView
|
||||
data={snapshot}
|
||||
@@ -160,7 +113,7 @@ export function Page({
|
||||
<TableView data={snapshot} node={node} onNavigate={onNavigate} />
|
||||
)}
|
||||
{extendedType !== "account" && extendedType !== "group" && (
|
||||
<OwnerText muted>
|
||||
<div className={classNames("text-sm text-gray-500 mt-4")}>
|
||||
Owned by{" "}
|
||||
<AccountOrGroupPreview
|
||||
coId={value.group.id}
|
||||
@@ -170,9 +123,9 @@ export function Page({
|
||||
onNavigate([{ coId: value.group.id, name: "owner" }]);
|
||||
}}
|
||||
/>
|
||||
</OwnerText>
|
||||
</div>
|
||||
)}
|
||||
</ContentContainer>
|
||||
</PageContainer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,34 +1,13 @@
|
||||
import { CoID, LocalNode, RawCoValue } from "cojson";
|
||||
import type { JsonObject } from "cojson";
|
||||
import { styled } from "goober";
|
||||
import { useMemo, useState } from "react";
|
||||
import { Button } from "../ui/button.js";
|
||||
import { Icon } from "../ui/icon.js";
|
||||
import { classNames } from "../utils.js";
|
||||
import { PageInfo } from "./types.js";
|
||||
import { useResolvedCoValues } from "./use-resolve-covalue.js";
|
||||
import { ValueRenderer } from "./value-renderer.js";
|
||||
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "../ui/table.js";
|
||||
import { Text } from "../ui/text.js";
|
||||
|
||||
const TableContainer = styled("div")`
|
||||
margin-top: 2rem;
|
||||
`;
|
||||
|
||||
const PaginationContainer = styled("div")`
|
||||
padding: 1rem 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0.5rem;
|
||||
`;
|
||||
|
||||
export function TableView({
|
||||
data,
|
||||
node,
|
||||
@@ -73,20 +52,46 @@ export function TableView({
|
||||
};
|
||||
|
||||
return (
|
||||
<TableContainer>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
{[...keys, "Action"].map((key) => (
|
||||
<TableHeader key={key}>{key}</TableHeader>
|
||||
<div>
|
||||
<table
|
||||
className={classNames(
|
||||
"min-w-full text-sm border-spacing-0 border-collapse",
|
||||
)}
|
||||
>
|
||||
<thead className={classNames("sticky top-0 border-b border-gray-200")}>
|
||||
<tr>
|
||||
{["", ...keys].map((key) => (
|
||||
<th
|
||||
key={key}
|
||||
className={classNames(
|
||||
"p-3 bg-gray-50 dark:bg-gray-925 text-left font-medium rounded",
|
||||
)}
|
||||
>
|
||||
{key}
|
||||
</th>
|
||||
))}
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className={classNames(" border-t border-gray-200")}>
|
||||
{resolvedRows.slice(0, visibleRowsCount).map((item, index) => (
|
||||
<TableRow key={index}>
|
||||
<tr key={index}>
|
||||
<td className={classNames("p-1")}>
|
||||
<Button
|
||||
variant="tertiary"
|
||||
onClick={() =>
|
||||
onNavigate([
|
||||
{
|
||||
coId: item.value!.id,
|
||||
name: index.toString(),
|
||||
},
|
||||
])
|
||||
}
|
||||
>
|
||||
<Icon name="link" />
|
||||
</Button>
|
||||
</td>
|
||||
{keys.map((key) => (
|
||||
<TableCell key={key}>
|
||||
<td key={key} className={classNames("p-4 whitespace-nowrap")}>
|
||||
<ValueRenderer
|
||||
json={(item.snapshot as JsonObject)[key]}
|
||||
onCoIDClick={(coId) => {
|
||||
@@ -106,39 +111,35 @@ export function TableView({
|
||||
handleClick();
|
||||
}}
|
||||
/>
|
||||
</TableCell>
|
||||
</td>
|
||||
))}
|
||||
|
||||
<TableCell>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() =>
|
||||
onNavigate([
|
||||
{
|
||||
coId: item.value!.id,
|
||||
name: index.toString(),
|
||||
},
|
||||
])
|
||||
}
|
||||
>
|
||||
View
|
||||
</Button>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</tr>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
<PaginationContainer>
|
||||
<Text muted small>
|
||||
</tbody>
|
||||
</table>
|
||||
<div
|
||||
className={classNames(
|
||||
"py-4 text-gray-500 flex items-center justify-between gap-2",
|
||||
)}
|
||||
>
|
||||
<span>
|
||||
Showing {Math.min(visibleRowsCount, coIdArray.length)} of{" "}
|
||||
{coIdArray.length}
|
||||
</Text>
|
||||
</span>
|
||||
{hasMore && (
|
||||
<Button variant="secondary" onClick={loadMore}>
|
||||
Load more
|
||||
</Button>
|
||||
<div className={classNames("text-center")}>
|
||||
<Button
|
||||
variant="plain"
|
||||
onClick={loadMore}
|
||||
className={classNames(
|
||||
"px-4 py-2 bg-blue text-white rounded hover:bg-blue-800",
|
||||
)}
|
||||
>
|
||||
Load more
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</PaginationContainer>
|
||||
</TableContainer>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,24 +1,11 @@
|
||||
import { CoID, LocalNode, RawCoValue } from "cojson";
|
||||
import { styled } from "goober";
|
||||
import {
|
||||
CoJsonType,
|
||||
ExtendedCoJsonType,
|
||||
useResolvedCoValue,
|
||||
} from "./use-resolve-covalue.js";
|
||||
|
||||
const IconText = styled("span")`
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
`;
|
||||
|
||||
const UnavailableText = styled("div")`
|
||||
font-weight: 500;
|
||||
`;
|
||||
|
||||
const EmptySpace = styled("div")`
|
||||
white-space: pre;
|
||||
width: 3.5rem;
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
`;
|
||||
import { classNames } from "../utils.js";
|
||||
|
||||
export const TypeIcon = ({
|
||||
type,
|
||||
@@ -41,7 +28,7 @@ export const TypeIcon = ({
|
||||
const iconKey = extendedType || type;
|
||||
const icon = iconMap[iconKey as keyof typeof iconMap];
|
||||
|
||||
return icon ? <IconText>{icon}</IconText> : null;
|
||||
return icon ? <span className={classNames("font-mono")}>{icon}</span> : null;
|
||||
};
|
||||
|
||||
export const ResolveIcon = ({
|
||||
@@ -54,10 +41,13 @@ export const ResolveIcon = ({
|
||||
const { type, extendedType, snapshot } = useResolvedCoValue(coId, node);
|
||||
|
||||
if (snapshot === "unavailable" && !type) {
|
||||
return <UnavailableText>Unavailable</UnavailableText>;
|
||||
return (
|
||||
<div className={classNames("text-gray-600 font-medium")}>Unavailable</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!type) return <EmptySpace> </EmptySpace>;
|
||||
if (!type)
|
||||
return <div className={classNames("whitespace-pre w-14 font-mono")}> </div>;
|
||||
|
||||
return <TypeIcon type={type} extendedType={extendedType} />;
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user