From a8cc1aa5df2cda4574185da5229a27cda3893a48 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Tue, 18 Nov 2025 19:43:32 -0600 Subject: [PATCH 01/80] scaffold ui library --- ui/index.html | 13 + ui/package.json | 19 + ui/pnpm-lock.yaml | 1355 +++++++++++++++++++++++++++++++++++ ui/src/client.ts | 1 + ui/src/elements/hexboard.ts | 5 + ui/src/index.ts | 1 + ui/tsconfig.json | 25 + ui/tsconfig.node.json | 11 + ui/vite.config.ts | 29 + 9 files changed, 1459 insertions(+) create mode 100644 ui/index.html create mode 100644 ui/package.json create mode 100644 ui/pnpm-lock.yaml create mode 100644 ui/src/client.ts create mode 100644 ui/src/elements/hexboard.ts create mode 100644 ui/src/index.ts create mode 100644 ui/tsconfig.json create mode 100644 ui/tsconfig.node.json create mode 100644 ui/vite.config.ts diff --git a/ui/index.html b/ui/index.html new file mode 100644 index 00000000..9442a024 --- /dev/null +++ b/ui/index.html @@ -0,0 +1,13 @@ + + + + + + Hexchess UI + + +
+ + + + diff --git a/ui/package.json b/ui/package.json new file mode 100644 index 00000000..b3d60f20 --- /dev/null +++ b/ui/package.json @@ -0,0 +1,19 @@ +{ + "name": "@bedard/hexchess-ui", + "version": "0.1.0", + "type": "module", + "main": "dist/index.js", + "module": "dist/index.js", + "types": "dist/index.d.ts", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "devDependencies": { + "@types/node": "^22.17.1", + "typescript": "^5.9.2", + "vite": "^6.3.5", + "vite-plugin-dts": "^4.5.4" + } +} diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml new file mode 100644 index 00000000..09905cc8 --- /dev/null +++ b/ui/pnpm-lock.yaml @@ -0,0 +1,1355 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + '@types/node': + specifier: ^22.17.1 + version: 22.19.1 + typescript: + specifier: ^5.9.2 + version: 5.9.3 + vite: + specifier: ^6.3.5 + version: 6.4.1(@types/node@22.19.1) + vite-plugin-dts: + specifier: ^4.5.4 + version: 4.5.4(@types/node@22.19.1)(rollup@4.53.2)(typescript@5.9.3)(vite@6.4.1(@types/node@22.19.1)) + +packages: + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.28.5': + resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/types@7.28.5': + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} + engines: {node: '>=6.9.0'} + + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@isaacs/balanced-match@4.0.1': + resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} + engines: {node: 20 || >=22} + + '@isaacs/brace-expansion@5.0.0': + resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} + engines: {node: 20 || >=22} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@microsoft/api-extractor-model@7.32.0': + resolution: {integrity: sha512-QIVJSreb8fGGJy1Qx0yzGVXxvHJN1WXgkFNHFheVv1iBJNqgvp+xeT3ienJmRwXmPPc5Es/cxBrXtKZJR3i7iw==} + + '@microsoft/api-extractor@7.55.0': + resolution: {integrity: sha512-TYc5OtAK/9E3HGgd2bIfSjQDYIwPc0dysf9rPiwXZGsq916I6W2oww9bhm1OxPOeg6rMfOX3PoroGd7oCryYog==} + hasBin: true + + '@microsoft/tsdoc-config@0.18.0': + resolution: {integrity: sha512-8N/vClYyfOH+l4fLkkr9+myAoR6M7akc8ntBJ4DJdWH2b09uVfr71+LTMpNyG19fNqWDg8KEDZhx5wxuqHyGjw==} + + '@microsoft/tsdoc@0.16.0': + resolution: {integrity: sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==} + + '@rollup/pluginutils@5.3.0': + resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/rollup-android-arm-eabi@4.53.2': + resolution: {integrity: sha512-yDPzwsgiFO26RJA4nZo8I+xqzh7sJTZIWQOxn+/XOdPE31lAvLIYCKqjV+lNH/vxE2L2iH3plKxDCRK6i+CwhA==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.53.2': + resolution: {integrity: sha512-k8FontTxIE7b0/OGKeSN5B6j25EuppBcWM33Z19JoVT7UTXFSo3D9CdU39wGTeb29NO3XxpMNauh09B+Ibw+9g==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.53.2': + resolution: {integrity: sha512-A6s4gJpomNBtJ2yioj8bflM2oogDwzUiMl2yNJ2v9E7++sHrSrsQ29fOfn5DM/iCzpWcebNYEdXpaK4tr2RhfQ==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.53.2': + resolution: {integrity: sha512-e6XqVmXlHrBlG56obu9gDRPW3O3hLxpwHpLsBJvuI8qqnsrtSZ9ERoWUXtPOkY8c78WghyPHZdmPhHLWNdAGEw==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.53.2': + resolution: {integrity: sha512-v0E9lJW8VsrwPux5Qe5CwmH/CF/2mQs6xU1MF3nmUxmZUCHazCjLgYvToOk+YuuUqLQBio1qkkREhxhc656ViA==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.53.2': + resolution: {integrity: sha512-ClAmAPx3ZCHtp6ysl4XEhWU69GUB1D+s7G9YjHGhIGCSrsg00nEGRRZHmINYxkdoJehde8VIsDC5t9C0gb6yqA==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.53.2': + resolution: {integrity: sha512-EPlb95nUsz6Dd9Qy13fI5kUPXNSljaG9FiJ4YUGU1O/Q77i5DYFW5KR8g1OzTcdZUqQQ1KdDqsTohdFVwCwjqg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.53.2': + resolution: {integrity: sha512-BOmnVW+khAUX+YZvNfa0tGTEMVVEerOxN0pDk2E6N6DsEIa2Ctj48FOMfNDdrwinocKaC7YXUZ1pHlKpnkja/Q==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.53.2': + resolution: {integrity: sha512-Xt2byDZ+6OVNuREgBXr4+CZDJtrVso5woFtpKdGPhpTPHcNG7D8YXeQzpNbFRxzTVqJf7kvPMCub/pcGUWgBjA==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.53.2': + resolution: {integrity: sha512-+LdZSldy/I9N8+klim/Y1HsKbJ3BbInHav5qE9Iy77dtHC/pibw1SR/fXlWyAk0ThnpRKoODwnAuSjqxFRDHUQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.53.2': + resolution: {integrity: sha512-8ms8sjmyc1jWJS6WdNSA23rEfdjWB30LH8Wqj0Cqvv7qSHnvw6kgMMXRdop6hkmGPlyYBdRPkjJnj3KCUHV/uQ==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.53.2': + resolution: {integrity: sha512-3HRQLUQbpBDMmzoxPJYd3W6vrVHOo2cVW8RUo87Xz0JPJcBLBr5kZ1pGcQAhdZgX9VV7NbGNipah1omKKe23/g==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.53.2': + resolution: {integrity: sha512-fMjKi+ojnmIvhk34gZP94vjogXNNUKMEYs+EDaB/5TG/wUkoeua7p7VCHnE6T2Tx+iaghAqQX8teQzcvrYpaQA==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.53.2': + resolution: {integrity: sha512-XuGFGU+VwUUV5kLvoAdi0Wz5Xbh2SrjIxCtZj6Wq8MDp4bflb/+ThZsVxokM7n0pcbkEr2h5/pzqzDYI7cCgLQ==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.53.2': + resolution: {integrity: sha512-w6yjZF0P+NGzWR3AXWX9zc0DNEGdtvykB03uhonSHMRa+oWA6novflo2WaJr6JZakG2ucsyb+rvhrKac6NIy+w==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.53.2': + resolution: {integrity: sha512-yo8d6tdfdeBArzC7T/PnHd7OypfI9cbuZzPnzLJIyKYFhAQ8SvlkKtKBMbXDxe1h03Rcr7u++nFS7tqXz87Gtw==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.53.2': + resolution: {integrity: sha512-ah59c1YkCxKExPP8O9PwOvs+XRLKwh/mV+3YdKqQ5AMQ0r4M4ZDuOrpWkUaqO7fzAHdINzV9tEVu8vNw48z0lA==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openharmony-arm64@4.53.2': + resolution: {integrity: sha512-4VEd19Wmhr+Zy7hbUsFZ6YXEiP48hE//KPLCSVNY5RMGX2/7HZ+QkN55a3atM1C/BZCGIgqN+xrVgtdak2S9+A==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.53.2': + resolution: {integrity: sha512-IlbHFYc/pQCgew/d5fslcy1KEaYVCJ44G8pajugd8VoOEI8ODhtb/j8XMhLpwHCMB3yk2J07ctup10gpw2nyMA==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.53.2': + resolution: {integrity: sha512-lNlPEGgdUfSzdCWU176ku/dQRnA7W+Gp8d+cWv73jYrb8uT7HTVVxq62DUYxjbaByuf1Yk0RIIAbDzp+CnOTFg==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.53.2': + resolution: {integrity: sha512-S6YojNVrHybQis2lYov1sd+uj7K0Q05NxHcGktuMMdIQ2VixGwAfbJ23NnlvvVV1bdpR2m5MsNBViHJKcA4ADw==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.53.2': + resolution: {integrity: sha512-k+/Rkcyx//P6fetPoLMb8pBeqJBNGx81uuf7iljX9++yNBVRDQgD04L+SVXmXmh5ZP4/WOp4mWF0kmi06PW2tA==} + cpu: [x64] + os: [win32] + + '@rushstack/node-core-library@5.18.0': + resolution: {integrity: sha512-XDebtBdw5S3SuZIt+Ra2NieT8kQ3D2Ow1HxhDQ/2soinswnOu9e7S69VSwTOLlQnx5mpWbONu+5JJjDxMAb6Fw==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/problem-matcher@0.1.1': + resolution: {integrity: sha512-Fm5XtS7+G8HLcJHCWpES5VmeMyjAKaWeyZU5qPzZC+22mPlJzAsOxymHiWIfuirtPckX3aptWws+K2d0BzniJA==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/rig-package@0.6.0': + resolution: {integrity: sha512-ZQmfzsLE2+Y91GF15c65L/slMRVhF6Hycq04D4TwtdGaUAbIXXg9c5pKA5KFU7M4QMaihoobp9JJYpYcaY3zOw==} + + '@rushstack/terminal@0.19.3': + resolution: {integrity: sha512-0P8G18gK9STyO+CNBvkKPnWGMxESxecTYqOcikHOVIHXa9uAuTK+Fw8TJq2Gng1w7W6wTC9uPX6hGNvrMll2wA==} + peerDependencies: + '@types/node': '*' + peerDependenciesMeta: + '@types/node': + optional: true + + '@rushstack/ts-command-line@5.1.3': + resolution: {integrity: sha512-Kdv0k/BnnxIYFlMVC1IxrIS0oGQd4T4b7vKfx52Y2+wk2WZSDFIvedr7JrhenzSlm3ou5KwtoTGTGd5nbODRug==} + + '@types/argparse@1.0.38': + resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/node@22.19.1': + resolution: {integrity: sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==} + + '@volar/language-core@2.4.23': + resolution: {integrity: sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==} + + '@volar/source-map@2.4.23': + resolution: {integrity: sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q==} + + '@volar/typescript@2.4.23': + resolution: {integrity: sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==} + + '@vue/compiler-core@3.5.24': + resolution: {integrity: sha512-eDl5H57AOpNakGNAkFDH+y7kTqrQpJkZFXhWZQGyx/5Wh7B1uQYvcWkvZi11BDhscPgj8N7XV3oRwiPnx1Vrig==} + + '@vue/compiler-dom@3.5.24': + resolution: {integrity: sha512-1QHGAvs53gXkWdd3ZMGYuvQFXHW4ksKWPG8HP8/2BscrbZ0brw183q2oNWjMrSWImYLHxHrx1ItBQr50I/q2zw==} + + '@vue/compiler-vue2@2.7.16': + resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} + + '@vue/language-core@2.2.0': + resolution: {integrity: sha512-O1ZZFaaBGkKbsRfnVH1ifOK1/1BUkyK+3SQsfnh6PmMmD4qJcTU8godCeA96jjDRTL6zgnK7YzCHfaUlH2r0Mw==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@vue/shared@3.5.24': + resolution: {integrity: sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==} + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv-draft-04@1.0.0: + resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} + peerDependencies: + ajv: ^8.5.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv@8.12.0: + resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} + + ajv@8.13.0: + resolution: {integrity: sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==} + + alien-signals@0.4.14: + resolution: {integrity: sha512-itUAVzhczTmP2U5yX67xVpsbbOiquusbWVyA9N+sy6+r6YVbFkahXvNCeEPWEOMhwDYwbVbGHFkVL03N9I5g+Q==} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + compare-versions@6.1.1: + resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==} + + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + + confbox@0.2.2: + resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} + + de-indent@1.0.2: + resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + diff@8.0.2: + resolution: {integrity: sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==} + engines: {node: '>=0.3.1'} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + engines: {node: '>=18'} + hasBin: true + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + exsolve@1.0.8: + resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fs-extra@11.3.2: + resolution: {integrity: sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==} + engines: {node: '>=14.14'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + + import-lazy@4.0.0: + resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} + engines: {node: '>=8'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + jju@1.4.0: + resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + jsonfile@6.2.0: + resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} + + kolorist@1.8.0: + resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + + local-pkg@1.1.2: + resolution: {integrity: sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==} + engines: {node: '>=14'} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + minimatch@10.0.3: + resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==} + engines: {node: 20 || >=22} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + mlly@1.8.0: + resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + muggle-string@0.4.1: + resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + path-browserify@1.0.1: + resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + + pkg-types@2.3.0: + resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + quansync@0.2.11: + resolution: {integrity: sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + engines: {node: '>= 0.4'} + hasBin: true + + rollup@4.53.2: + resolution: {integrity: sha512-MHngMYwGJVi6Fmnk6ISmnk7JAHRNF0UkuucA0CUW3N3a4KnONPEZz+vUanQP/ZC/iY1Qkf3bwPWzyY84wEks1g==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + typescript@5.8.2: + resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==} + engines: {node: '>=14.17'} + hasBin: true + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + ufo@1.6.1: + resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + vite-plugin-dts@4.5.4: + resolution: {integrity: sha512-d4sOM8M/8z7vRXHHq/ebbblfaxENjogAAekcfcDCCwAyvGqnPrc7f4NZbvItS+g4WTgerW0xDwSz5qz11JT3vg==} + peerDependencies: + typescript: '*' + vite: '*' + peerDependenciesMeta: + vite: + optional: true + + vite@6.4.1: + resolution: {integrity: sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vscode-uri@3.1.0: + resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + +snapshots: + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/parser@7.28.5': + dependencies: + '@babel/types': 7.28.5 + + '@babel/types@7.28.5': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@esbuild/aix-ppc64@0.25.12': + optional: true + + '@esbuild/android-arm64@0.25.12': + optional: true + + '@esbuild/android-arm@0.25.12': + optional: true + + '@esbuild/android-x64@0.25.12': + optional: true + + '@esbuild/darwin-arm64@0.25.12': + optional: true + + '@esbuild/darwin-x64@0.25.12': + optional: true + + '@esbuild/freebsd-arm64@0.25.12': + optional: true + + '@esbuild/freebsd-x64@0.25.12': + optional: true + + '@esbuild/linux-arm64@0.25.12': + optional: true + + '@esbuild/linux-arm@0.25.12': + optional: true + + '@esbuild/linux-ia32@0.25.12': + optional: true + + '@esbuild/linux-loong64@0.25.12': + optional: true + + '@esbuild/linux-mips64el@0.25.12': + optional: true + + '@esbuild/linux-ppc64@0.25.12': + optional: true + + '@esbuild/linux-riscv64@0.25.12': + optional: true + + '@esbuild/linux-s390x@0.25.12': + optional: true + + '@esbuild/linux-x64@0.25.12': + optional: true + + '@esbuild/netbsd-arm64@0.25.12': + optional: true + + '@esbuild/netbsd-x64@0.25.12': + optional: true + + '@esbuild/openbsd-arm64@0.25.12': + optional: true + + '@esbuild/openbsd-x64@0.25.12': + optional: true + + '@esbuild/openharmony-arm64@0.25.12': + optional: true + + '@esbuild/sunos-x64@0.25.12': + optional: true + + '@esbuild/win32-arm64@0.25.12': + optional: true + + '@esbuild/win32-ia32@0.25.12': + optional: true + + '@esbuild/win32-x64@0.25.12': + optional: true + + '@isaacs/balanced-match@4.0.1': {} + + '@isaacs/brace-expansion@5.0.0': + dependencies: + '@isaacs/balanced-match': 4.0.1 + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@microsoft/api-extractor-model@7.32.0(@types/node@22.19.1)': + dependencies: + '@microsoft/tsdoc': 0.16.0 + '@microsoft/tsdoc-config': 0.18.0 + '@rushstack/node-core-library': 5.18.0(@types/node@22.19.1) + transitivePeerDependencies: + - '@types/node' + + '@microsoft/api-extractor@7.55.0(@types/node@22.19.1)': + dependencies: + '@microsoft/api-extractor-model': 7.32.0(@types/node@22.19.1) + '@microsoft/tsdoc': 0.16.0 + '@microsoft/tsdoc-config': 0.18.0 + '@rushstack/node-core-library': 5.18.0(@types/node@22.19.1) + '@rushstack/rig-package': 0.6.0 + '@rushstack/terminal': 0.19.3(@types/node@22.19.1) + '@rushstack/ts-command-line': 5.1.3(@types/node@22.19.1) + diff: 8.0.2 + lodash: 4.17.21 + minimatch: 10.0.3 + resolve: 1.22.11 + semver: 7.5.4 + source-map: 0.6.1 + typescript: 5.8.2 + transitivePeerDependencies: + - '@types/node' + + '@microsoft/tsdoc-config@0.18.0': + dependencies: + '@microsoft/tsdoc': 0.16.0 + ajv: 8.12.0 + jju: 1.4.0 + resolve: 1.22.11 + + '@microsoft/tsdoc@0.16.0': {} + + '@rollup/pluginutils@5.3.0(rollup@4.53.2)': + dependencies: + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 4.0.3 + optionalDependencies: + rollup: 4.53.2 + + '@rollup/rollup-android-arm-eabi@4.53.2': + optional: true + + '@rollup/rollup-android-arm64@4.53.2': + optional: true + + '@rollup/rollup-darwin-arm64@4.53.2': + optional: true + + '@rollup/rollup-darwin-x64@4.53.2': + optional: true + + '@rollup/rollup-freebsd-arm64@4.53.2': + optional: true + + '@rollup/rollup-freebsd-x64@4.53.2': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.53.2': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.53.2': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.53.2': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.53.2': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.53.2': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.53.2': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.53.2': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.53.2': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.53.2': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.53.2': + optional: true + + '@rollup/rollup-linux-x64-musl@4.53.2': + optional: true + + '@rollup/rollup-openharmony-arm64@4.53.2': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.53.2': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.53.2': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.53.2': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.53.2': + optional: true + + '@rushstack/node-core-library@5.18.0(@types/node@22.19.1)': + dependencies: + ajv: 8.13.0 + ajv-draft-04: 1.0.0(ajv@8.13.0) + ajv-formats: 3.0.1(ajv@8.13.0) + fs-extra: 11.3.2 + import-lazy: 4.0.0 + jju: 1.4.0 + resolve: 1.22.11 + semver: 7.5.4 + optionalDependencies: + '@types/node': 22.19.1 + + '@rushstack/problem-matcher@0.1.1(@types/node@22.19.1)': + optionalDependencies: + '@types/node': 22.19.1 + + '@rushstack/rig-package@0.6.0': + dependencies: + resolve: 1.22.11 + strip-json-comments: 3.1.1 + + '@rushstack/terminal@0.19.3(@types/node@22.19.1)': + dependencies: + '@rushstack/node-core-library': 5.18.0(@types/node@22.19.1) + '@rushstack/problem-matcher': 0.1.1(@types/node@22.19.1) + supports-color: 8.1.1 + optionalDependencies: + '@types/node': 22.19.1 + + '@rushstack/ts-command-line@5.1.3(@types/node@22.19.1)': + dependencies: + '@rushstack/terminal': 0.19.3(@types/node@22.19.1) + '@types/argparse': 1.0.38 + argparse: 1.0.10 + string-argv: 0.3.2 + transitivePeerDependencies: + - '@types/node' + + '@types/argparse@1.0.38': {} + + '@types/estree@1.0.8': {} + + '@types/node@22.19.1': + dependencies: + undici-types: 6.21.0 + + '@volar/language-core@2.4.23': + dependencies: + '@volar/source-map': 2.4.23 + + '@volar/source-map@2.4.23': {} + + '@volar/typescript@2.4.23': + dependencies: + '@volar/language-core': 2.4.23 + path-browserify: 1.0.1 + vscode-uri: 3.1.0 + + '@vue/compiler-core@3.5.24': + dependencies: + '@babel/parser': 7.28.5 + '@vue/shared': 3.5.24 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + + '@vue/compiler-dom@3.5.24': + dependencies: + '@vue/compiler-core': 3.5.24 + '@vue/shared': 3.5.24 + + '@vue/compiler-vue2@2.7.16': + dependencies: + de-indent: 1.0.2 + he: 1.2.0 + + '@vue/language-core@2.2.0(typescript@5.9.3)': + dependencies: + '@volar/language-core': 2.4.23 + '@vue/compiler-dom': 3.5.24 + '@vue/compiler-vue2': 2.7.16 + '@vue/shared': 3.5.24 + alien-signals: 0.4.14 + minimatch: 9.0.5 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + optionalDependencies: + typescript: 5.9.3 + + '@vue/shared@3.5.24': {} + + acorn@8.15.0: {} + + ajv-draft-04@1.0.0(ajv@8.13.0): + optionalDependencies: + ajv: 8.13.0 + + ajv-formats@3.0.1(ajv@8.13.0): + optionalDependencies: + ajv: 8.13.0 + + ajv@8.12.0: + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + + ajv@8.13.0: + dependencies: + fast-deep-equal: 3.1.3 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + uri-js: 4.4.1 + + alien-signals@0.4.14: {} + + argparse@1.0.10: + dependencies: + sprintf-js: 1.0.3 + + balanced-match@1.0.2: {} + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + compare-versions@6.1.1: {} + + confbox@0.1.8: {} + + confbox@0.2.2: {} + + de-indent@1.0.2: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + diff@8.0.2: {} + + entities@4.5.0: {} + + esbuild@0.25.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 + + estree-walker@2.0.2: {} + + exsolve@1.0.8: {} + + fast-deep-equal@3.1.3: {} + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + fs-extra@11.3.2: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.2.0 + universalify: 2.0.1 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + graceful-fs@4.2.11: {} + + has-flag@4.0.0: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + he@1.2.0: {} + + import-lazy@4.0.0: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + jju@1.4.0: {} + + json-schema-traverse@1.0.0: {} + + jsonfile@6.2.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + kolorist@1.8.0: {} + + local-pkg@1.1.2: + dependencies: + mlly: 1.8.0 + pkg-types: 2.3.0 + quansync: 0.2.11 + + lodash@4.17.21: {} + + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + minimatch@10.0.3: + dependencies: + '@isaacs/brace-expansion': 5.0.0 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + mlly@1.8.0: + dependencies: + acorn: 8.15.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.1 + + ms@2.1.3: {} + + muggle-string@0.4.1: {} + + nanoid@3.3.11: {} + + path-browserify@1.0.1: {} + + path-parse@1.0.7: {} + + pathe@2.0.3: {} + + picocolors@1.1.1: {} + + picomatch@4.0.3: {} + + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.8.0 + pathe: 2.0.3 + + pkg-types@2.3.0: + dependencies: + confbox: 0.2.2 + exsolve: 1.0.8 + pathe: 2.0.3 + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + punycode@2.3.1: {} + + quansync@0.2.11: {} + + require-from-string@2.0.2: {} + + resolve@1.22.11: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + rollup@4.53.2: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.53.2 + '@rollup/rollup-android-arm64': 4.53.2 + '@rollup/rollup-darwin-arm64': 4.53.2 + '@rollup/rollup-darwin-x64': 4.53.2 + '@rollup/rollup-freebsd-arm64': 4.53.2 + '@rollup/rollup-freebsd-x64': 4.53.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.53.2 + '@rollup/rollup-linux-arm-musleabihf': 4.53.2 + '@rollup/rollup-linux-arm64-gnu': 4.53.2 + '@rollup/rollup-linux-arm64-musl': 4.53.2 + '@rollup/rollup-linux-loong64-gnu': 4.53.2 + '@rollup/rollup-linux-ppc64-gnu': 4.53.2 + '@rollup/rollup-linux-riscv64-gnu': 4.53.2 + '@rollup/rollup-linux-riscv64-musl': 4.53.2 + '@rollup/rollup-linux-s390x-gnu': 4.53.2 + '@rollup/rollup-linux-x64-gnu': 4.53.2 + '@rollup/rollup-linux-x64-musl': 4.53.2 + '@rollup/rollup-openharmony-arm64': 4.53.2 + '@rollup/rollup-win32-arm64-msvc': 4.53.2 + '@rollup/rollup-win32-ia32-msvc': 4.53.2 + '@rollup/rollup-win32-x64-gnu': 4.53.2 + '@rollup/rollup-win32-x64-msvc': 4.53.2 + fsevents: 2.3.3 + + semver@7.5.4: + dependencies: + lru-cache: 6.0.0 + + source-map-js@1.2.1: {} + + source-map@0.6.1: {} + + sprintf-js@1.0.3: {} + + string-argv@0.3.2: {} + + strip-json-comments@3.1.1: {} + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + typescript@5.8.2: {} + + typescript@5.9.3: {} + + ufo@1.6.1: {} + + undici-types@6.21.0: {} + + universalify@2.0.1: {} + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + vite-plugin-dts@4.5.4(@types/node@22.19.1)(rollup@4.53.2)(typescript@5.9.3)(vite@6.4.1(@types/node@22.19.1)): + dependencies: + '@microsoft/api-extractor': 7.55.0(@types/node@22.19.1) + '@rollup/pluginutils': 5.3.0(rollup@4.53.2) + '@volar/typescript': 2.4.23 + '@vue/language-core': 2.2.0(typescript@5.9.3) + compare-versions: 6.1.1 + debug: 4.4.3 + kolorist: 1.8.0 + local-pkg: 1.1.2 + magic-string: 0.30.21 + typescript: 5.9.3 + optionalDependencies: + vite: 6.4.1(@types/node@22.19.1) + transitivePeerDependencies: + - '@types/node' + - rollup + - supports-color + + vite@6.4.1(@types/node@22.19.1): + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.53.2 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 22.19.1 + fsevents: 2.3.3 + + vscode-uri@3.1.0: {} + + yallist@4.0.0: {} diff --git a/ui/src/client.ts b/ui/src/client.ts new file mode 100644 index 00000000..57d2a94f --- /dev/null +++ b/ui/src/client.ts @@ -0,0 +1 @@ +console.log('hello') \ No newline at end of file diff --git a/ui/src/elements/hexboard.ts b/ui/src/elements/hexboard.ts new file mode 100644 index 00000000..7348c626 --- /dev/null +++ b/ui/src/elements/hexboard.ts @@ -0,0 +1,5 @@ +export class Hexboard extends HTMLElement { + constructor() { + super(); + } +} diff --git a/ui/src/index.ts b/ui/src/index.ts new file mode 100644 index 00000000..6715c289 --- /dev/null +++ b/ui/src/index.ts @@ -0,0 +1 @@ +export { Hexboard } from './elements/hexboard' diff --git a/ui/tsconfig.json b/ui/tsconfig.json new file mode 100644 index 00000000..ca37e762 --- /dev/null +++ b/ui/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "declaration": true, + "declarationMap": true, + "outDir": "dist", + "rootDir": "src" + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} + diff --git a/ui/tsconfig.node.json b/ui/tsconfig.node.json new file mode 100644 index 00000000..e428d506 --- /dev/null +++ b/ui/tsconfig.node.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} + diff --git a/ui/vite.config.ts b/ui/vite.config.ts new file mode 100644 index 00000000..6c14f0da --- /dev/null +++ b/ui/vite.config.ts @@ -0,0 +1,29 @@ +import { defineConfig } from 'vite' +import { fileURLToPath } from 'node:url' +import { resolve } from 'node:path' +import dts from 'vite-plugin-dts' + +const __dirname = fileURLToPath(new URL('.', import.meta.url)) + +export default defineConfig({ + plugins: [ + dts({ + include: ['src/**/*'], + outDir: 'dist', + rollupTypes: true + }) + ], + build: { + lib: { + entry: resolve(__dirname, 'src/index.ts'), + name: '@bedard/hexchess-ui', + fileName: 'index', + formats: ['es'] + }, + }, + server: { + port: 3000, + open: true + } +}) + From cc3c0e6a9cd465799485c863b98813a794d7d0c9 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Tue, 18 Nov 2025 20:24:57 -0600 Subject: [PATCH 02/80] wip --- ui/src/index.ts | 1 - ui/src/{elements => lib}/hexboard.ts | 0 ui/src/lib/index.ts | 1 + ui/vite.config.ts | 2 +- 4 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 ui/src/index.ts rename ui/src/{elements => lib}/hexboard.ts (100%) create mode 100644 ui/src/lib/index.ts diff --git a/ui/src/index.ts b/ui/src/index.ts deleted file mode 100644 index 6715c289..00000000 --- a/ui/src/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { Hexboard } from './elements/hexboard' diff --git a/ui/src/elements/hexboard.ts b/ui/src/lib/hexboard.ts similarity index 100% rename from ui/src/elements/hexboard.ts rename to ui/src/lib/hexboard.ts diff --git a/ui/src/lib/index.ts b/ui/src/lib/index.ts new file mode 100644 index 00000000..17b9b0c5 --- /dev/null +++ b/ui/src/lib/index.ts @@ -0,0 +1 @@ +export { Hexboard } from './hexboard' diff --git a/ui/vite.config.ts b/ui/vite.config.ts index 6c14f0da..5b51fc69 100644 --- a/ui/vite.config.ts +++ b/ui/vite.config.ts @@ -15,7 +15,7 @@ export default defineConfig({ ], build: { lib: { - entry: resolve(__dirname, 'src/index.ts'), + entry: resolve(__dirname, 'src/lib/index.ts'), name: '@bedard/hexchess-ui', fileName: 'index', formats: ['es'] From d4bd30efef6c0bf0cdb9c45beec5e53c286693fd Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Tue, 18 Nov 2025 21:13:39 -0600 Subject: [PATCH 03/80] continue scaffolding --- ui/package.json | 5 +- ui/pnpm-lock.yaml | 202 +++++++++++++ ui/src/App.vue | 6 + ui/src/client.ts | 8 +- ui/src/lib/hexboard.ts | 5 - ui/src/lib/hexboard/constants.ts | 472 +++++++++++++++++++++++++++++++ ui/src/lib/hexboard/geometry.ts | 78 +++++ ui/src/lib/hexboard/index.ts | 13 + ui/src/lib/hexboard/types.ts | 6 + ui/vite.config.ts | 10 +- 10 files changed, 797 insertions(+), 8 deletions(-) create mode 100644 ui/src/App.vue delete mode 100644 ui/src/lib/hexboard.ts create mode 100644 ui/src/lib/hexboard/constants.ts create mode 100644 ui/src/lib/hexboard/geometry.ts create mode 100644 ui/src/lib/hexboard/index.ts create mode 100644 ui/src/lib/hexboard/types.ts diff --git a/ui/package.json b/ui/package.json index b3d60f20..5ae3920d 100644 --- a/ui/package.json +++ b/ui/package.json @@ -12,8 +12,11 @@ }, "devDependencies": { "@types/node": "^22.17.1", + "@vitejs/plugin-vue": "^6.0.2", "typescript": "^5.9.2", "vite": "^6.3.5", - "vite-plugin-dts": "^4.5.4" + "vite-plugin-dts": "^4.5.4", + "vue": "3.6.0-alpha.4", + "vue-tsc": "^3.1.4" } } diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index 09905cc8..929f07f3 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -8,9 +8,15 @@ importers: .: devDependencies: + '@bedard/hexchess': + specifier: file:../js + version: file:../js '@types/node': specifier: ^22.17.1 version: 22.19.1 + '@vitejs/plugin-vue': + specifier: ^6.0.2 + version: 6.0.2(vite@6.4.1(@types/node@22.19.1))(vue@3.6.0-alpha.4(typescript@5.9.3)) typescript: specifier: ^5.9.2 version: 5.9.3 @@ -20,6 +26,12 @@ importers: vite-plugin-dts: specifier: ^4.5.4 version: 4.5.4(@types/node@22.19.1)(rollup@4.53.2)(typescript@5.9.3)(vite@6.4.1(@types/node@22.19.1)) + vue: + specifier: 3.6.0-alpha.4 + version: 3.6.0-alpha.4(typescript@5.9.3) + vue-tsc: + specifier: ^3.1.4 + version: 3.1.4(typescript@5.9.3) packages: @@ -40,6 +52,9 @@ packages: resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} engines: {node: '>=6.9.0'} + '@bedard/hexchess@file:../js': + resolution: {directory: ../js, type: directory} + '@esbuild/aix-ppc64@0.25.12': resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} engines: {node: '>=18'} @@ -220,6 +235,9 @@ packages: '@microsoft/tsdoc@0.16.0': resolution: {integrity: sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==} + '@rolldown/pluginutils@1.0.0-beta.50': + resolution: {integrity: sha512-5e76wQiQVeL1ICOZVUg4LSOVYg9jyhGCin+icYozhsUzM+fHE7kddi1bdiE0jwVqTfkjba3jUFbEkoC9WkdvyA==} + '@rollup/pluginutils@5.3.0': resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} engines: {node: '>=14.0.0'} @@ -378,6 +396,13 @@ packages: '@types/node@22.19.1': resolution: {integrity: sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==} + '@vitejs/plugin-vue@6.0.2': + resolution: {integrity: sha512-iHmwV3QcVGGvSC1BG5bZ4z6iwa1SOpAPWmnjOErd4Ske+lZua5K9TtAVdx0gMBClJ28DViCbSmZitjWZsWO3LA==} + engines: {node: ^20.19.0 || >=22.12.0} + peerDependencies: + vite: ^5.0.0 || ^6.0.0 || ^7.0.0 + vue: ^3.2.25 + '@volar/language-core@2.4.23': resolution: {integrity: sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==} @@ -390,9 +415,24 @@ packages: '@vue/compiler-core@3.5.24': resolution: {integrity: sha512-eDl5H57AOpNakGNAkFDH+y7kTqrQpJkZFXhWZQGyx/5Wh7B1uQYvcWkvZi11BDhscPgj8N7XV3oRwiPnx1Vrig==} + '@vue/compiler-core@3.6.0-alpha.4': + resolution: {integrity: sha512-Z2yBj9sxi/C46qKw549dqpb0ZqIH/5FGeLT5inV8cXbbgBn9AfH6+3aIWVd0HJ0oSnHSr/sPE+pn0PXrWOnwoA==} + '@vue/compiler-dom@3.5.24': resolution: {integrity: sha512-1QHGAvs53gXkWdd3ZMGYuvQFXHW4ksKWPG8HP8/2BscrbZ0brw183q2oNWjMrSWImYLHxHrx1ItBQr50I/q2zw==} + '@vue/compiler-dom@3.6.0-alpha.4': + resolution: {integrity: sha512-PHM00EYjcd1YPyRx9Pe1UbtDKbY3Y7VvuCZFFAMjNJfV9Wx/OfBDBf9F4ChOt+kSD/RZEQfELsSBOWBLrpuS5w==} + + '@vue/compiler-sfc@3.6.0-alpha.4': + resolution: {integrity: sha512-vCjb9thJAqxA6iFDKdD+CM+o5w+NnfcDUZqJxRZniUCVrRFCLEmzN0EN7tn7ksh/suHasxecss/g7Z+ucu6EhQ==} + + '@vue/compiler-ssr@3.6.0-alpha.4': + resolution: {integrity: sha512-p0BOmwfMtcTToqueac4IQvtBTsmtvBn0wRMC8hewjqKhHx5wqd7T+1i9X5oCNx9pEA62HQlrYKGFp6tRDoL57w==} + + '@vue/compiler-vapor@3.6.0-alpha.4': + resolution: {integrity: sha512-DXlmmuNkoXUBY7YqJIG7zvmeueJ23y3FQzpFjiFY63cW/Eefk1pfidn29R/NL7wiQDIzrve92325B74sVcWguQ==} + '@vue/compiler-vue2@2.7.16': resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} @@ -404,9 +444,39 @@ packages: typescript: optional: true + '@vue/language-core@3.1.4': + resolution: {integrity: sha512-n/58wm8SkmoxMWkUNUH/PwoovWe4hmdyPJU2ouldr3EPi1MLoS7iDN46je8CsP95SnVBs2axInzRglPNKvqMcg==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@vue/reactivity@3.6.0-alpha.4': + resolution: {integrity: sha512-aStBkVTA+GeaMhyiO26fW8tqQtw88XTKFbcAOs0QvksNG7Qh7NxWmbJiXIcsB1PhEQvEywkOJo88TBAJmPAtpg==} + + '@vue/runtime-core@3.6.0-alpha.4': + resolution: {integrity: sha512-QtmBbJL8kBfG/TfXkq47ajDJd/N8UvH5jSZHZ1pwiRoVkDLccqjiRCpeJMTbCYJoRw4KhbCiIs4TQR08atHYUA==} + + '@vue/runtime-dom@3.6.0-alpha.4': + resolution: {integrity: sha512-bqfLJDX1t1bB07Jd96MNewgZrIGgg6iDbP9ql/sZ/LvQLLbxs0v8JPbe6D+Os/P+shQ9vl4PgYmpg7g59WqINA==} + + '@vue/runtime-vapor@3.6.0-alpha.4': + resolution: {integrity: sha512-GqObmtDRxkezsvmUAxeTlPcDxT7ywHRh5UNgttV6xH4ae+kd3NsER4mHThwhYnrH+AIu1OeNLG+Eqfh9B2sKmg==} + peerDependencies: + '@vue/runtime-dom': 3.6.0-alpha.4 + + '@vue/server-renderer@3.6.0-alpha.4': + resolution: {integrity: sha512-+3e6sqwmCEjEqhjjHz8BB2V7oL8HJUKO/kaRSNeK0jh8Z/AjKUP9e5VaOwn0PF6oP3r1hAlyuXTA/HD2BN1Fag==} + peerDependencies: + vue: 3.6.0-alpha.4 + '@vue/shared@3.5.24': resolution: {integrity: sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==} + '@vue/shared@3.6.0-alpha.4': + resolution: {integrity: sha512-UWhZp4a/52k/0ptTzDxU2K6pFhUfJXVF+SIPqkIvjF0eKgvrPaLhdeRZ0PH+XxzMmm3tYAP3R78IqioxbLv0hw==} + acorn@8.15.0: resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} engines: {node: '>=0.4.0'} @@ -437,6 +507,9 @@ packages: alien-signals@0.4.14: resolution: {integrity: sha512-itUAVzhczTmP2U5yX67xVpsbbOiquusbWVyA9N+sy6+r6YVbFkahXvNCeEPWEOMhwDYwbVbGHFkVL03N9I5g+Q==} + alien-signals@3.1.0: + resolution: {integrity: sha512-yufC6VpSy8tK3I0lO67pjumo5JvDQVQyr38+3OHqe6CHl1t2VZekKZ7EKKZSqk0cRmE7U7tfZbpXiKNzuc+ckg==} + argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} @@ -455,6 +528,9 @@ packages: confbox@0.2.2: resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + de-indent@1.0.2: resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} @@ -739,6 +815,20 @@ packages: vscode-uri@3.1.0: resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} + vue-tsc@3.1.4: + resolution: {integrity: sha512-GsRJxttj4WkmXW/zDwYPGMJAN3np/4jTzoDFQTpTsI5Vg/JKMWamBwamlmLihgSVHO66y9P7GX+uoliYxeI4Hw==} + hasBin: true + peerDependencies: + typescript: '>=5.0.0' + + vue@3.6.0-alpha.4: + resolution: {integrity: sha512-3Q5/IdK6c4Ro7s9qblbWnyfWs5Zvk4/Gtg4LnXRy22FVQ2LTLlrujhn+mBH67njrFRd9U1dhGlzeHl9rp516ZQ==} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} @@ -757,6 +847,8 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@bedard/hexchess@file:../js': {} + '@esbuild/aix-ppc64@0.25.12': optional: true @@ -879,6 +971,8 @@ snapshots: '@microsoft/tsdoc@0.16.0': {} + '@rolldown/pluginutils@1.0.0-beta.50': {} + '@rollup/pluginutils@5.3.0(rollup@4.53.2)': dependencies: '@types/estree': 1.0.8 @@ -1000,6 +1094,12 @@ snapshots: dependencies: undici-types: 6.21.0 + '@vitejs/plugin-vue@6.0.2(vite@6.4.1(@types/node@22.19.1))(vue@3.6.0-alpha.4(typescript@5.9.3))': + dependencies: + '@rolldown/pluginutils': 1.0.0-beta.50 + vite: 6.4.1(@types/node@22.19.1) + vue: 3.6.0-alpha.4(typescript@5.9.3) + '@volar/language-core@2.4.23': dependencies: '@volar/source-map': 2.4.23 @@ -1020,11 +1120,50 @@ snapshots: estree-walker: 2.0.2 source-map-js: 1.2.1 + '@vue/compiler-core@3.6.0-alpha.4': + dependencies: + '@babel/parser': 7.28.5 + '@vue/shared': 3.6.0-alpha.4 + entities: 4.5.0 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + '@vue/compiler-dom@3.5.24': dependencies: '@vue/compiler-core': 3.5.24 '@vue/shared': 3.5.24 + '@vue/compiler-dom@3.6.0-alpha.4': + dependencies: + '@vue/compiler-core': 3.6.0-alpha.4 + '@vue/shared': 3.6.0-alpha.4 + + '@vue/compiler-sfc@3.6.0-alpha.4': + dependencies: + '@babel/parser': 7.28.5 + '@vue/compiler-core': 3.6.0-alpha.4 + '@vue/compiler-dom': 3.6.0-alpha.4 + '@vue/compiler-ssr': 3.6.0-alpha.4 + '@vue/compiler-vapor': 3.6.0-alpha.4 + '@vue/shared': 3.6.0-alpha.4 + estree-walker: 2.0.2 + magic-string: 0.30.21 + postcss: 8.5.6 + source-map-js: 1.2.1 + + '@vue/compiler-ssr@3.6.0-alpha.4': + dependencies: + '@vue/compiler-dom': 3.6.0-alpha.4 + '@vue/shared': 3.6.0-alpha.4 + + '@vue/compiler-vapor@3.6.0-alpha.4': + dependencies: + '@babel/parser': 7.28.5 + '@vue/compiler-dom': 3.6.0-alpha.4 + '@vue/shared': 3.6.0-alpha.4 + estree-walker: 2.0.2 + source-map-js: 1.2.1 + '@vue/compiler-vue2@2.7.16': dependencies: de-indent: 1.0.2 @@ -1043,8 +1182,50 @@ snapshots: optionalDependencies: typescript: 5.9.3 + '@vue/language-core@3.1.4(typescript@5.9.3)': + dependencies: + '@volar/language-core': 2.4.23 + '@vue/compiler-dom': 3.5.24 + '@vue/shared': 3.5.24 + alien-signals: 3.1.0 + muggle-string: 0.4.1 + path-browserify: 1.0.1 + picomatch: 4.0.3 + optionalDependencies: + typescript: 5.9.3 + + '@vue/reactivity@3.6.0-alpha.4': + dependencies: + '@vue/shared': 3.6.0-alpha.4 + + '@vue/runtime-core@3.6.0-alpha.4': + dependencies: + '@vue/reactivity': 3.6.0-alpha.4 + '@vue/shared': 3.6.0-alpha.4 + + '@vue/runtime-dom@3.6.0-alpha.4': + dependencies: + '@vue/reactivity': 3.6.0-alpha.4 + '@vue/runtime-core': 3.6.0-alpha.4 + '@vue/shared': 3.6.0-alpha.4 + csstype: 3.2.3 + + '@vue/runtime-vapor@3.6.0-alpha.4(@vue/runtime-dom@3.6.0-alpha.4)': + dependencies: + '@vue/reactivity': 3.6.0-alpha.4 + '@vue/runtime-dom': 3.6.0-alpha.4 + '@vue/shared': 3.6.0-alpha.4 + + '@vue/server-renderer@3.6.0-alpha.4(vue@3.6.0-alpha.4(typescript@5.9.3))': + dependencies: + '@vue/compiler-ssr': 3.6.0-alpha.4 + '@vue/shared': 3.6.0-alpha.4 + vue: 3.6.0-alpha.4(typescript@5.9.3) + '@vue/shared@3.5.24': {} + '@vue/shared@3.6.0-alpha.4': {} + acorn@8.15.0: {} ajv-draft-04@1.0.0(ajv@8.13.0): @@ -1071,6 +1252,8 @@ snapshots: alien-signals@0.4.14: {} + alien-signals@3.1.0: {} + argparse@1.0.10: dependencies: sprintf-js: 1.0.3 @@ -1087,6 +1270,8 @@ snapshots: confbox@0.2.2: {} + csstype@3.2.3: {} + de-indent@1.0.2: {} debug@4.4.3: @@ -1352,4 +1537,21 @@ snapshots: vscode-uri@3.1.0: {} + vue-tsc@3.1.4(typescript@5.9.3): + dependencies: + '@volar/typescript': 2.4.23 + '@vue/language-core': 3.1.4(typescript@5.9.3) + typescript: 5.9.3 + + vue@3.6.0-alpha.4(typescript@5.9.3): + dependencies: + '@vue/compiler-dom': 3.6.0-alpha.4 + '@vue/compiler-sfc': 3.6.0-alpha.4 + '@vue/runtime-dom': 3.6.0-alpha.4 + '@vue/runtime-vapor': 3.6.0-alpha.4(@vue/runtime-dom@3.6.0-alpha.4) + '@vue/server-renderer': 3.6.0-alpha.4(vue@3.6.0-alpha.4(typescript@5.9.3)) + '@vue/shared': 3.6.0-alpha.4 + optionalDependencies: + typescript: 5.9.3 + yallist@4.0.0: {} diff --git a/ui/src/App.vue b/ui/src/App.vue new file mode 100644 index 00000000..c7a89ce4 --- /dev/null +++ b/ui/src/App.vue @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/ui/src/client.ts b/ui/src/client.ts index 57d2a94f..26fe50a0 100644 --- a/ui/src/client.ts +++ b/ui/src/client.ts @@ -1 +1,7 @@ -console.log('hello') \ No newline at end of file +import { createApp } from 'vue' +import { Hexboard } from './lib' +import App from './App.vue' + +customElements.define('hexchess-board', Hexboard) + +createApp(App).mount('#app') diff --git a/ui/src/lib/hexboard.ts b/ui/src/lib/hexboard.ts deleted file mode 100644 index 7348c626..00000000 --- a/ui/src/lib/hexboard.ts +++ /dev/null @@ -1,5 +0,0 @@ -export class Hexboard extends HTMLElement { - constructor() { - super(); - } -} diff --git a/ui/src/lib/hexboard/constants.ts b/ui/src/lib/hexboard/constants.ts new file mode 100644 index 00000000..a1fdf8ef --- /dev/null +++ b/ui/src/lib/hexboard/constants.ts @@ -0,0 +1,472 @@ +import { bilerp, flip, hexagon, pivot, reflect, sum } from './geometry' +import type { Vec } from './types' + +/** svg viewbox, increasing this zooms out away from the board */ +export const box = 23.6 + +/** position colors */ +export const colors: Vec<3, string> = [ + 'oklch(0.9015 0.0729 70.7)', + 'oklch(0.8366 0.1165 66.29)', + 'oklch(0.6806 0.1423 75.83)', +] + +/** empty position */ +export const emptyPosition = '1/3/5/7/9/11/11/11/11/11/11 w - 0 1' + +/** initial position */ +export const initialPosition = 'b/qbk/n1b1n/r5r/ppppppppp/11/5P5/4P1P4/3P1B1P3/2P2B2P2/1PRNQBKNRP1 w - 0 1' + +/** label offset */ +export const labelOffset = 1.7 + +/** piece size */ +export const pieceSize = 1.7 + +/** named positions in fen order */ +export const positions = [ + 'f11', + 'e10', + 'f10', + 'g10', + 'd9', + 'e9', + 'f9', + 'g9', + 'h9', + 'c8', + 'd8', + 'e8', + 'f8', + 'g8', + 'h8', + 'i8', + 'b7', + 'c7', + 'd7', + 'e7', + 'f7', + 'g7', + 'h7', + 'i7', + 'k7', + 'a6', + 'b6', + 'c6', + 'd6', + 'e6', + 'f6', + 'g6', + 'h6', + 'i6', + 'k6', + 'l6', + 'a5', + 'b5', + 'c5', + 'd5', + 'e5', + 'f5', + 'g5', + 'h5', + 'i5', + 'k5', + 'l5', + 'a4', + 'b4', + 'c4', + 'd4', + 'e4', + 'f4', + 'g4', + 'h4', + 'i4', + 'k4', + 'l4', + 'a3', + 'b3', + 'c3', + 'd3', + 'e3', + 'f3', + 'g3', + 'h3', + 'i3', + 'k3', + 'l3', + 'a2', + 'b2', + 'c2', + 'd2', + 'e2', + 'f2', + 'g2', + 'h2', + 'i2', + 'k2', + 'l2', + 'a1', + 'b1', + 'c1', + 'd1', + 'e1', + 'f1', + 'g1', + 'h1', + 'i1', + 'k1', + 'l1', +] as const + +/** side length of regular hexagon circumscribed around a unit circle */ +export const sideLength = 2 / Math.sqrt(3) + +// distance between the origin of neighboring hexagons +// one = direct neighbors, two = neighbor of neighbor, etc... +export const one = sideLength * (Math.sqrt(3) / 2) * 2 // <- inscribed radius * 2 +export const two = one * 2 +export const three = one * 3 +export const four = one * 4 +export const five = one * 5 + +// calculate the center for each position, using f6 as the origin +const f6: Vec<2> = [0, 0] + +// the 6th rank is calculated as angled distances from F6 +const a6 = pivot(f6, 150, five) +const b6 = pivot(f6, 150, four) +const c6 = pivot(f6, 150, three) +const d6 = pivot(f6, 150, two) +const e6 = pivot(f6, 150, one) +const g6 = pivot(f6, 30, one) +const h6 = pivot(f6, 30, two) +const i6 = pivot(f6, 30, three) +const k6 = pivot(f6, 30, four) +const l6 = pivot(f6, 30, five) + +// other ranks are calculated by vertically translating the 6th rank +const a1 = sum(a6, [0, -five]) +const b1 = sum(b6, [0, -five]) +const c1 = sum(c6, [0, -five]) +const d1 = sum(d6, [0, -five]) +const e1 = sum(e6, [0, -five]) +const f1 = sum(f6, [0, -five]) +const g1 = sum(g6, [0, -five]) +const h1 = sum(h6, [0, -five]) +const i1 = sum(i6, [0, -five]) +const k1 = sum(k6, [0, -five]) +const l1 = sum(l6, [0, -five]) + +const a2 = sum(a6, [0, -four]) +const b2 = sum(b6, [0, -four]) +const c2 = sum(c6, [0, -four]) +const d2 = sum(d6, [0, -four]) +const e2 = sum(e6, [0, -four]) +const f2 = sum(f6, [0, -four]) +const g2 = sum(g6, [0, -four]) +const h2 = sum(h6, [0, -four]) +const i2 = sum(i6, [0, -four]) +const k2 = sum(k6, [0, -four]) +const l2 = sum(l6, [0, -four]) + +const a3 = sum(a6, [0, -three]) +const b3 = sum(b6, [0, -three]) +const c3 = sum(c6, [0, -three]) +const d3 = sum(d6, [0, -three]) +const e3 = sum(e6, [0, -three]) +const f3 = sum(f6, [0, -three]) +const g3 = sum(g6, [0, -three]) +const h3 = sum(h6, [0, -three]) +const i3 = sum(i6, [0, -three]) +const k3 = sum(k6, [0, -three]) +const l3 = sum(l6, [0, -three]) + +const a4 = sum(a6, [0, -two]) +const b4 = sum(b6, [0, -two]) +const c4 = sum(c6, [0, -two]) +const d4 = sum(d6, [0, -two]) +const e4 = sum(e6, [0, -two]) +const f4 = sum(f6, [0, -two]) +const g4 = sum(g6, [0, -two]) +const h4 = sum(h6, [0, -two]) +const i4 = sum(i6, [0, -two]) +const k4 = sum(k6, [0, -two]) +const l4 = sum(l6, [0, -two]) + +const a5 = sum(a6, [0, -one]) +const b5 = sum(b6, [0, -one]) +const c5 = sum(c6, [0, -one]) +const d5 = sum(d6, [0, -one]) +const e5 = sum(e6, [0, -one]) +const f5 = sum(f6, [0, -one]) +const g5 = sum(g6, [0, -one]) +const h5 = sum(h6, [0, -one]) +const i5 = sum(i6, [0, -one]) +const k5 = sum(k6, [0, -one]) +const l5 = sum(l6, [0, -one]) + +// 7th rank and higher start to have fewer files +const b7 = sum(b6, [0, one]) +const c7 = sum(c6, [0, one]) +const d7 = sum(d6, [0, one]) +const e7 = sum(e6, [0, one]) +const f7 = sum(f6, [0, one]) +const g7 = sum(g6, [0, one]) +const h7 = sum(h6, [0, one]) +const i7 = sum(i6, [0, one]) +const k7 = sum(k6, [0, one]) + +const c8 = sum(c6, [0, two]) +const d8 = sum(d6, [0, two]) +const e8 = sum(e6, [0, two]) +const f8 = sum(f6, [0, two]) +const g8 = sum(g6, [0, two]) +const h8 = sum(h6, [0, two]) +const i8 = sum(i6, [0, two]) + +const d9 = sum(d6, [0, three]) +const e9 = sum(e6, [0, three]) +const f9 = sum(f6, [0, three]) +const g9 = sum(g6, [0, three]) +const h9 = sum(h6, [0, three]) + +const e10 = sum(e6, [0, four]) +const f10 = sum(f6, [0, four]) +const g10 = sum(g6, [0, four]) + +const f11 = sum(f6, [0, five]) + +/** board position data, sorted by fen order */ +export const board: { + color: number + origin: [Vec<2>, Vec<2>] + path: [Vec<6, Vec<2>>, Vec<6, Vec<2>>] +}[] = [ + { color: 2, origin: [f11, reflect(f11)], path: [hexagon(f11), flip(hexagon(f11))] }, + { color: 1, origin: [e10, reflect(e10)], path: [hexagon(e10), flip(hexagon(e10))] }, + { color: 0, origin: [f10, reflect(f10)], path: [hexagon(f10), flip(hexagon(f10))] }, + { color: 1, origin: [g10, reflect(g10)], path: [hexagon(g10), flip(hexagon(g10))] }, + { color: 0, origin: [d9, reflect(d9)], path: [hexagon(d9), flip(hexagon(d9))] }, + { color: 2, origin: [e9, reflect(e9)], path: [hexagon(e9), flip(hexagon(e9))] }, + { color: 1, origin: [f9, reflect(f9)], path: [hexagon(f9), flip(hexagon(f9))] }, + { color: 2, origin: [g9, reflect(g9)], path: [hexagon(g9), flip(hexagon(g9))] }, + { color: 0, origin: [h9, reflect(h9)], path: [hexagon(h9), flip(hexagon(h9))] }, + { color: 2, origin: [c8, reflect(c8)], path: [hexagon(c8), flip(hexagon(c8))] }, + { color: 1, origin: [d8, reflect(d8)], path: [hexagon(d8), flip(hexagon(d8))] }, + { color: 0, origin: [e8, reflect(e8)], path: [hexagon(e8), flip(hexagon(e8))] }, + { color: 2, origin: [f8, reflect(f8)], path: [hexagon(f8), flip(hexagon(f8))] }, + { color: 0, origin: [g8, reflect(g8)], path: [hexagon(g8), flip(hexagon(g8))] }, + { color: 1, origin: [h8, reflect(h8)], path: [hexagon(h8), flip(hexagon(h8))] }, + { color: 2, origin: [i8, reflect(i8)], path: [hexagon(i8), flip(hexagon(i8))] }, + { color: 1, origin: [b7, reflect(b7)], path: [hexagon(b7), flip(hexagon(b7))] }, + { color: 0, origin: [c7, reflect(c7)], path: [hexagon(c7), flip(hexagon(c7))] }, + { color: 2, origin: [d7, reflect(d7)], path: [hexagon(d7), flip(hexagon(d7))] }, + { color: 1, origin: [e7, reflect(e7)], path: [hexagon(e7), flip(hexagon(e7))] }, + { color: 0, origin: [f7, reflect(f7)], path: [hexagon(f7), flip(hexagon(f7))] }, + { color: 1, origin: [g7, reflect(g7)], path: [hexagon(g7), flip(hexagon(g7))] }, + { color: 2, origin: [h7, reflect(h7)], path: [hexagon(h7), flip(hexagon(h7))] }, + { color: 0, origin: [i7, reflect(i7)], path: [hexagon(i7), flip(hexagon(i7))] }, + { color: 1, origin: [k7, reflect(k7)], path: [hexagon(k7), flip(hexagon(k7))] }, + { color: 0, origin: [a6, reflect(a6)], path: [hexagon(a6), flip(hexagon(a6))] }, + { color: 2, origin: [b6, reflect(b6)], path: [hexagon(b6), flip(hexagon(b6))] }, + { color: 1, origin: [c6, reflect(c6)], path: [hexagon(c6), flip(hexagon(c6))] }, + { color: 0, origin: [d6, reflect(d6)], path: [hexagon(d6), flip(hexagon(d6))] }, + { color: 2, origin: [e6, reflect(e6)], path: [hexagon(e6), flip(hexagon(e6))] }, + { color: 1, origin: [f6, reflect(f6)], path: [hexagon(f6), flip(hexagon(f6))] }, + { color: 2, origin: [g6, reflect(g6)], path: [hexagon(g6), flip(hexagon(g6))] }, + { color: 0, origin: [h6, reflect(h6)], path: [hexagon(h6), flip(hexagon(h6))] }, + { color: 1, origin: [i6, reflect(i6)], path: [hexagon(i6), flip(hexagon(i6))] }, + { color: 2, origin: [k6, reflect(k6)], path: [hexagon(k6), flip(hexagon(k6))] }, + { color: 0, origin: [l6, reflect(l6)], path: [hexagon(l6), flip(hexagon(l6))] }, + { color: 1, origin: [a5, reflect(a5)], path: [hexagon(a5), flip(hexagon(a5))] }, + { color: 0, origin: [b5, reflect(b5)], path: [hexagon(b5), flip(hexagon(b5))] }, + { color: 2, origin: [c5, reflect(c5)], path: [hexagon(c5), flip(hexagon(c5))] }, + { color: 1, origin: [d5, reflect(d5)], path: [hexagon(d5), flip(hexagon(d5))] }, + { color: 0, origin: [e5, reflect(e5)], path: [hexagon(e5), flip(hexagon(e5))] }, + { color: 2, origin: [f5, reflect(f5)], path: [hexagon(f5), flip(hexagon(f5))] }, + { color: 0, origin: [g5, reflect(g5)], path: [hexagon(g5), flip(hexagon(g5))] }, + { color: 1, origin: [h5, reflect(h5)], path: [hexagon(h5), flip(hexagon(h5))] }, + { color: 2, origin: [i5, reflect(i5)], path: [hexagon(i5), flip(hexagon(i5))] }, + { color: 0, origin: [k5, reflect(k5)], path: [hexagon(k5), flip(hexagon(k5))] }, + { color: 1, origin: [l5, reflect(l5)], path: [hexagon(l5), flip(hexagon(l5))] }, + { color: 2, origin: [a4, reflect(a4)], path: [hexagon(a4), flip(hexagon(a4))] }, + { color: 1, origin: [b4, reflect(b4)], path: [hexagon(b4), flip(hexagon(b4))] }, + { color: 0, origin: [c4, reflect(c4)], path: [hexagon(c4), flip(hexagon(c4))] }, + { color: 2, origin: [d4, reflect(d4)], path: [hexagon(d4), flip(hexagon(d4))] }, + { color: 1, origin: [e4, reflect(e4)], path: [hexagon(e4), flip(hexagon(e4))] }, + { color: 0, origin: [f4, reflect(f4)], path: [hexagon(f4), flip(hexagon(f4))] }, + { color: 1, origin: [g4, reflect(g4)], path: [hexagon(g4), flip(hexagon(g4))] }, + { color: 2, origin: [h4, reflect(h4)], path: [hexagon(h4), flip(hexagon(h4))] }, + { color: 0, origin: [i4, reflect(i4)], path: [hexagon(i4), flip(hexagon(i4))] }, + { color: 1, origin: [k4, reflect(k4)], path: [hexagon(k4), flip(hexagon(k4))] }, + { color: 2, origin: [l4, reflect(l4)], path: [hexagon(l4), flip(hexagon(l4))] }, + { color: 0, origin: [a3, reflect(a3)], path: [hexagon(a3), flip(hexagon(a3))] }, + { color: 2, origin: [b3, reflect(b3)], path: [hexagon(b3), flip(hexagon(b3))] }, + { color: 1, origin: [c3, reflect(c3)], path: [hexagon(c3), flip(hexagon(c3))] }, + { color: 0, origin: [d3, reflect(d3)], path: [hexagon(d3), flip(hexagon(d3))] }, + { color: 2, origin: [e3, reflect(e3)], path: [hexagon(e3), flip(hexagon(e3))] }, + { color: 1, origin: [f3, reflect(f3)], path: [hexagon(f3), flip(hexagon(f3))] }, + { color: 2, origin: [g3, reflect(g3)], path: [hexagon(g3), flip(hexagon(g3))] }, + { color: 0, origin: [h3, reflect(h3)], path: [hexagon(h3), flip(hexagon(h3))] }, + { color: 1, origin: [i3, reflect(i3)], path: [hexagon(i3), flip(hexagon(i3))] }, + { color: 2, origin: [k3, reflect(k3)], path: [hexagon(k3), flip(hexagon(k3))] }, + { color: 0, origin: [l3, reflect(l3)], path: [hexagon(l3), flip(hexagon(l3))] }, + { color: 1, origin: [a2, reflect(a2)], path: [hexagon(a2), flip(hexagon(a2))] }, + { color: 0, origin: [b2, reflect(b2)], path: [hexagon(b2), flip(hexagon(b2))] }, + { color: 2, origin: [c2, reflect(c2)], path: [hexagon(c2), flip(hexagon(c2))] }, + { color: 1, origin: [d2, reflect(d2)], path: [hexagon(d2), flip(hexagon(d2))] }, + { color: 0, origin: [e2, reflect(e2)], path: [hexagon(e2), flip(hexagon(e2))] }, + { color: 2, origin: [f2, reflect(f2)], path: [hexagon(f2), flip(hexagon(f2))] }, + { color: 0, origin: [g2, reflect(g2)], path: [hexagon(g2), flip(hexagon(g2))] }, + { color: 1, origin: [h2, reflect(h2)], path: [hexagon(h2), flip(hexagon(h2))] }, + { color: 2, origin: [i2, reflect(i2)], path: [hexagon(i2), flip(hexagon(i2))] }, + { color: 0, origin: [k2, reflect(k2)], path: [hexagon(k2), flip(hexagon(k2))] }, + { color: 1, origin: [l2, reflect(l2)], path: [hexagon(l2), flip(hexagon(l2))] }, + { color: 2, origin: [a1, reflect(a1)], path: [hexagon(a1), flip(hexagon(a1))] }, + { color: 1, origin: [b1, reflect(b1)], path: [hexagon(b1), flip(hexagon(b1))] }, + { color: 0, origin: [c1, reflect(c1)], path: [hexagon(c1), flip(hexagon(c1))] }, + { color: 2, origin: [d1, reflect(d1)], path: [hexagon(d1), flip(hexagon(d1))] }, + { color: 1, origin: [e1, reflect(e1)], path: [hexagon(e1), flip(hexagon(e1))] }, + { color: 0, origin: [f1, reflect(f1)], path: [hexagon(f1), flip(hexagon(f1))] }, + { color: 1, origin: [g1, reflect(g1)], path: [hexagon(g1), flip(hexagon(g1))] }, + { color: 2, origin: [h1, reflect(h1)], path: [hexagon(h1), flip(hexagon(h1))] }, + { color: 0, origin: [i1, reflect(i1)], path: [hexagon(i1), flip(hexagon(i1))] }, + { color: 1, origin: [k1, reflect(k1)], path: [hexagon(k1), flip(hexagon(k1))] }, + { color: 2, origin: [l1, reflect(l1)], path: [hexagon(l1), flip(hexagon(l1))] }, +] + +/** board labels */ +export const labels: [string, Vec<2>, Vec<2>][] = [ + ['11', bilerp(g10, f11, labelOffset), reflect(bilerp(g10, f11, labelOffset))], + ['10', bilerp(f10, e10, labelOffset), reflect(bilerp(f10, e10, labelOffset))], + ['9', bilerp(e9, d9, labelOffset), reflect(bilerp(e9, d9, labelOffset))], + ['8', bilerp(d8, c8, labelOffset), reflect(bilerp(d8, c8, labelOffset))], + ['7', bilerp(c7, b7, labelOffset), reflect(bilerp(c7, b7, labelOffset))], + ['6', bilerp(b6, a6, labelOffset), reflect(bilerp(b6, a6, labelOffset))], + ['5', bilerp(b5, a5, labelOffset), reflect(bilerp(b5, a5, labelOffset))], + ['4', bilerp(b4, a4, labelOffset), reflect(bilerp(b4, a4, labelOffset))], + ['3', bilerp(b3, a3, labelOffset), reflect(bilerp(b3, a3, labelOffset))], + ['2', bilerp(b2, a2, labelOffset), reflect(bilerp(b2, a2, labelOffset))], + ['1', bilerp(b1, a1, labelOffset), reflect(bilerp(b1, a1, labelOffset))], + + ['a', bilerp(a2, a1, labelOffset), reflect(bilerp(a2, a1, labelOffset))], + ['b', bilerp(b2, b1, labelOffset), reflect(bilerp(b2, b1, labelOffset))], + ['c', bilerp(c2, c1, labelOffset), reflect(bilerp(c2, c1, labelOffset))], + ['d', bilerp(d2, d1, labelOffset), reflect(bilerp(d2, d1, labelOffset))], + ['e', bilerp(e2, e1, labelOffset), reflect(bilerp(e2, e1, labelOffset))], + ['f', bilerp(f2, f1, labelOffset), reflect(bilerp(f2, f1, labelOffset))], + ['g', bilerp(g2, g1, labelOffset), reflect(bilerp(g2, g1, labelOffset))], + ['h', bilerp(h2, h1, labelOffset), reflect(bilerp(h2, h1, labelOffset))], + ['i', bilerp(i2, i1, labelOffset), reflect(bilerp(i2, i1, labelOffset))], + ['k', bilerp(k2, k1, labelOffset), reflect(bilerp(k2, k1, labelOffset))], + ['l', bilerp(l2, l1, labelOffset), reflect(bilerp(l2, l1, labelOffset))], + + ['1', bilerp(k1, l1, labelOffset), reflect(bilerp(k1, l1, labelOffset))], + ['2', bilerp(k2, l2, labelOffset), reflect(bilerp(k2, l2, labelOffset))], + ['3', bilerp(k3, l3, labelOffset), reflect(bilerp(k3, l3, labelOffset))], + ['4', bilerp(k4, l4, labelOffset), reflect(bilerp(k4, l4, labelOffset))], + ['5', bilerp(k5, l5, labelOffset), reflect(bilerp(k5, l5, labelOffset))], + ['6', bilerp(k6, l6, labelOffset), reflect(bilerp(k6, l6, labelOffset))], + ['7', bilerp(i7, k7, labelOffset), reflect(bilerp(i7, k7, labelOffset))], + ['8', bilerp(h8, i8, labelOffset), reflect(bilerp(h8, i8, labelOffset))], + ['9', bilerp(g9, h9, labelOffset), reflect(bilerp(g9, h9, labelOffset))], + ['10', bilerp(f10, g10, labelOffset), reflect(bilerp(f10, g10, labelOffset))], + ['11', bilerp(e10, f11, labelOffset), reflect(bilerp(e10, f11, labelOffset))], +] + +/** perimeter of the board, used to create a constant background for light/dark mode */ +export const perimeter: Vec<93, Vec<2>> = [ + board[positions.indexOf('f11')]!.path[0][5], + board[positions.indexOf('f11')]!.path[0][0], + board[positions.indexOf('f11')]!.path[0][1], + board[positions.indexOf('g10')]!.path[0][5], + board[positions.indexOf('g10')]!.path[0][0], + board[positions.indexOf('g10')]!.path[0][1], + board[positions.indexOf('h9')]!.path[0][5], + board[positions.indexOf('h9')]!.path[0][0], + board[positions.indexOf('h9')]!.path[0][1], + board[positions.indexOf('i8')]!.path[0][5], + board[positions.indexOf('i8')]!.path[0][0], + board[positions.indexOf('i8')]!.path[0][1], + board[positions.indexOf('k7')]!.path[0][5], + board[positions.indexOf('k7')]!.path[0][0], + board[positions.indexOf('k7')]!.path[0][1], + board[positions.indexOf('l6')]!.path[0][5], + board[positions.indexOf('l6')]!.path[0][0], + board[positions.indexOf('l6')]!.path[0][1], + board[positions.indexOf('l6')]!.path[0][2], + board[positions.indexOf('l5')]!.path[0][0], + board[positions.indexOf('l5')]!.path[0][1], + board[positions.indexOf('l5')]!.path[0][2], + board[positions.indexOf('l4')]!.path[0][0], + board[positions.indexOf('l4')]!.path[0][1], + board[positions.indexOf('l4')]!.path[0][2], + board[positions.indexOf('l3')]!.path[0][0], + board[positions.indexOf('l3')]!.path[0][1], + board[positions.indexOf('l3')]!.path[0][2], + board[positions.indexOf('l2')]!.path[0][0], + board[positions.indexOf('l2')]!.path[0][1], + board[positions.indexOf('l2')]!.path[0][2], + board[positions.indexOf('l1')]!.path[0][0], + board[positions.indexOf('l1')]!.path[0][1], + board[positions.indexOf('l1')]!.path[0][2], + board[positions.indexOf('l1')]!.path[0][3], + board[positions.indexOf('k1')]!.path[0][2], + board[positions.indexOf('k1')]!.path[0][3], + board[positions.indexOf('k1')]!.path[0][4], + board[positions.indexOf('i1')]!.path[0][1], + board[positions.indexOf('i1')]!.path[0][2], + board[positions.indexOf('i1')]!.path[0][3], + board[positions.indexOf('h1')]!.path[0][1], + board[positions.indexOf('h1')]!.path[0][2], + board[positions.indexOf('h1')]!.path[0][3], + board[positions.indexOf('g1')]!.path[0][1], + board[positions.indexOf('g1')]!.path[0][2], + board[positions.indexOf('g1')]!.path[0][3], + board[positions.indexOf('f1')]!.path[0][1], + board[positions.indexOf('f1')]!.path[0][2], + board[positions.indexOf('f1')]!.path[0][3], + board[positions.indexOf('f1')]!.path[0][4], + board[positions.indexOf('e1')]!.path[0][3], + board[positions.indexOf('e1')]!.path[0][4], + board[positions.indexOf('d1')]!.path[0][2], + board[positions.indexOf('d1')]!.path[0][3], + board[positions.indexOf('d1')]!.path[0][4], + board[positions.indexOf('c1')]!.path[0][2], + board[positions.indexOf('c1')]!.path[0][3], + board[positions.indexOf('c1')]!.path[0][4], + board[positions.indexOf('b1')]!.path[0][2], + board[positions.indexOf('b1')]!.path[0][3], + board[positions.indexOf('b1')]!.path[0][4], + board[positions.indexOf('a1')]!.path[0][2], + board[positions.indexOf('a1')]!.path[0][3], + board[positions.indexOf('a1')]!.path[0][4], + board[positions.indexOf('a1')]!.path[0][5], + board[positions.indexOf('a2')]!.path[0][3], + board[positions.indexOf('a2')]!.path[0][4], + board[positions.indexOf('a2')]!.path[0][5], + board[positions.indexOf('a3')]!.path[0][3], + board[positions.indexOf('a3')]!.path[0][4], + board[positions.indexOf('a3')]!.path[0][5], + board[positions.indexOf('a4')]!.path[0][3], + board[positions.indexOf('a4')]!.path[0][4], + board[positions.indexOf('a4')]!.path[0][5], + board[positions.indexOf('a5')]!.path[0][3], + board[positions.indexOf('a5')]!.path[0][4], + board[positions.indexOf('a5')]!.path[0][5], + board[positions.indexOf('a6')]!.path[0][3], + board[positions.indexOf('a6')]!.path[0][4], + board[positions.indexOf('a6')]!.path[0][5], + board[positions.indexOf('b7')]!.path[0][4], + board[positions.indexOf('b7')]!.path[0][5], + board[positions.indexOf('b7')]!.path[0][0], + board[positions.indexOf('c8')]!.path[0][4], + board[positions.indexOf('c8')]!.path[0][5], + board[positions.indexOf('c8')]!.path[0][0], + board[positions.indexOf('d9')]!.path[0][4], + board[positions.indexOf('d9')]!.path[0][5], + board[positions.indexOf('d9')]!.path[0][0], + board[positions.indexOf('e10')]!.path[0][4], + board[positions.indexOf('e10')]!.path[0][5], + board[positions.indexOf('e10')]!.path[0][0], +] diff --git a/ui/src/lib/hexboard/geometry.ts b/ui/src/lib/hexboard/geometry.ts new file mode 100644 index 00000000..d6ce5acf --- /dev/null +++ b/ui/src/lib/hexboard/geometry.ts @@ -0,0 +1,78 @@ +import { box } from './constants' +import { sideLength } from './constants' +import type { Vec } from './types' + +/** bilinearly interpolate between two points */ +export function bilerp(p1: Vec<2>, p2: Vec<2>, t: number): Vec<2> { + return [ + p1[0] + (t * (p2[0] - p1[0])), + p1[1] + (t * (p2[1] - p1[1])), + ] +} + +/** create svg vector path */ +export function d(arr: [number, number][]) { + const p = 4 // <- svg rounding precision + + const [origin, ...points] = arr + + let path = `M ${x(origin[0]).toFixed(p)} ${y(origin[1]).toFixed(p)} L ` + + for (const point of points) { + path += `${x(point[0]).toFixed(p)} ${y(point[1]).toFixed(p)} ` + } + + return `${path} Z` +} + +/** reflect a path of points across the origin */ +export function flip[]>(path: T) { + return path.map(reflect) as T +} + +/** calculate hexagon vertices around a given point */ +export function hexagon(origin: Vec<2>): Vec<6, Vec<2>> { + return [ + pivot(origin, -300, sideLength), + pivot(origin, 0, sideLength), + pivot(origin, -60, sideLength), + pivot(origin, -120, sideLength), + pivot(origin, -180, sideLength), + pivot(origin, -240, sideLength), + ] +} + +/** pivot counter-clockwise around a point */ +export function pivot(point: Vec<2>, deg: number, distance: number): Vec<2> { + const rad = radians(deg) + + return [ + (distance * Math.cos(rad)) + point[0], + (distance * Math.sin(rad)) + point[1], + ] +} + +/** convert degrees to radians */ +export function radians(deg: number) { + return deg * (Math.PI / 180) +} + +/** reflect a point across the origin */ +export function reflect(v: Vec<2>): Vec<2> { + return [-v[0], -v[1]] +} + +/** sum two vectors */ +export function sum(v1: Vec<2>, v2: Vec<2>): Vec<2> { + return [v1[0] + v2[0], v1[1] + v2[1]] +} + +/** convert cartesian X value to SVG coordinate */ +export function x(n: number) { + return (box / 2) + n +} + +/** convert cartesian Y value to SVG coordinate */ +export function y(n: number) { + return (box / 2) - n +} diff --git a/ui/src/lib/hexboard/index.ts b/ui/src/lib/hexboard/index.ts new file mode 100644 index 00000000..84b1ece1 --- /dev/null +++ b/ui/src/lib/hexboard/index.ts @@ -0,0 +1,13 @@ +import { board } from './constants' + +export class Hexboard extends HTMLElement { + constructor() { + super(); + } + + connectedCallback() { + this.innerHTML = '
soon...
'; + + console.log({ board }) + } +} diff --git a/ui/src/lib/hexboard/types.ts b/ui/src/lib/hexboard/types.ts new file mode 100644 index 00000000..e2037a67 --- /dev/null +++ b/ui/src/lib/hexboard/types.ts @@ -0,0 +1,6 @@ +/** uniform tuple of length `T`, `number` by default */ +export type Vec< + T extends number, + U = number, + V extends unknown[] = [] +> = V['length'] extends T ? V : Vec diff --git a/ui/vite.config.ts b/ui/vite.config.ts index 5b51fc69..e3b462f3 100644 --- a/ui/vite.config.ts +++ b/ui/vite.config.ts @@ -2,6 +2,7 @@ import { defineConfig } from 'vite' import { fileURLToPath } from 'node:url' import { resolve } from 'node:path' import dts from 'vite-plugin-dts' +import vue from '@vitejs/plugin-vue' const __dirname = fileURLToPath(new URL('.', import.meta.url)) @@ -11,7 +12,14 @@ export default defineConfig({ include: ['src/**/*'], outDir: 'dist', rollupTypes: true - }) + }), + vue({ + template: { + compilerOptions: { + isCustomElement: tag => ['hexchess-board'].includes(tag) + } + } + }), ], build: { lib: { From 12f7a3b9e688ea6ec72b374b6ed63186f05ff857 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Tue, 18 Nov 2025 22:09:25 -0600 Subject: [PATCH 04/80] setup constants --- ui/src/lib/hexboard/constants.ts | 477 +++++++++++++------------------ 1 file changed, 192 insertions(+), 285 deletions(-) diff --git a/ui/src/lib/hexboard/constants.ts b/ui/src/lib/hexboard/constants.ts index a1fdf8ef..897bc43a 100644 --- a/ui/src/lib/hexboard/constants.ts +++ b/ui/src/lib/hexboard/constants.ts @@ -23,101 +23,6 @@ export const labelOffset = 1.7 /** piece size */ export const pieceSize = 1.7 -/** named positions in fen order */ -export const positions = [ - 'f11', - 'e10', - 'f10', - 'g10', - 'd9', - 'e9', - 'f9', - 'g9', - 'h9', - 'c8', - 'd8', - 'e8', - 'f8', - 'g8', - 'h8', - 'i8', - 'b7', - 'c7', - 'd7', - 'e7', - 'f7', - 'g7', - 'h7', - 'i7', - 'k7', - 'a6', - 'b6', - 'c6', - 'd6', - 'e6', - 'f6', - 'g6', - 'h6', - 'i6', - 'k6', - 'l6', - 'a5', - 'b5', - 'c5', - 'd5', - 'e5', - 'f5', - 'g5', - 'h5', - 'i5', - 'k5', - 'l5', - 'a4', - 'b4', - 'c4', - 'd4', - 'e4', - 'f4', - 'g4', - 'h4', - 'i4', - 'k4', - 'l4', - 'a3', - 'b3', - 'c3', - 'd3', - 'e3', - 'f3', - 'g3', - 'h3', - 'i3', - 'k3', - 'l3', - 'a2', - 'b2', - 'c2', - 'd2', - 'e2', - 'f2', - 'g2', - 'h2', - 'i2', - 'k2', - 'l2', - 'a1', - 'b1', - 'c1', - 'd1', - 'e1', - 'f1', - 'g1', - 'h1', - 'i1', - 'k1', - 'l1', -] as const - /** side length of regular hexagon circumscribed around a unit circle */ export const sideLength = 2 / Math.sqrt(3) @@ -237,102 +142,104 @@ const g10 = sum(g6, [0, four]) const f11 = sum(f6, [0, five]) /** board position data, sorted by fen order */ -export const board: { - color: number - origin: [Vec<2>, Vec<2>] - path: [Vec<6, Vec<2>>, Vec<6, Vec<2>>] -}[] = [ - { color: 2, origin: [f11, reflect(f11)], path: [hexagon(f11), flip(hexagon(f11))] }, - { color: 1, origin: [e10, reflect(e10)], path: [hexagon(e10), flip(hexagon(e10))] }, - { color: 0, origin: [f10, reflect(f10)], path: [hexagon(f10), flip(hexagon(f10))] }, - { color: 1, origin: [g10, reflect(g10)], path: [hexagon(g10), flip(hexagon(g10))] }, - { color: 0, origin: [d9, reflect(d9)], path: [hexagon(d9), flip(hexagon(d9))] }, - { color: 2, origin: [e9, reflect(e9)], path: [hexagon(e9), flip(hexagon(e9))] }, - { color: 1, origin: [f9, reflect(f9)], path: [hexagon(f9), flip(hexagon(f9))] }, - { color: 2, origin: [g9, reflect(g9)], path: [hexagon(g9), flip(hexagon(g9))] }, - { color: 0, origin: [h9, reflect(h9)], path: [hexagon(h9), flip(hexagon(h9))] }, - { color: 2, origin: [c8, reflect(c8)], path: [hexagon(c8), flip(hexagon(c8))] }, - { color: 1, origin: [d8, reflect(d8)], path: [hexagon(d8), flip(hexagon(d8))] }, - { color: 0, origin: [e8, reflect(e8)], path: [hexagon(e8), flip(hexagon(e8))] }, - { color: 2, origin: [f8, reflect(f8)], path: [hexagon(f8), flip(hexagon(f8))] }, - { color: 0, origin: [g8, reflect(g8)], path: [hexagon(g8), flip(hexagon(g8))] }, - { color: 1, origin: [h8, reflect(h8)], path: [hexagon(h8), flip(hexagon(h8))] }, - { color: 2, origin: [i8, reflect(i8)], path: [hexagon(i8), flip(hexagon(i8))] }, - { color: 1, origin: [b7, reflect(b7)], path: [hexagon(b7), flip(hexagon(b7))] }, - { color: 0, origin: [c7, reflect(c7)], path: [hexagon(c7), flip(hexagon(c7))] }, - { color: 2, origin: [d7, reflect(d7)], path: [hexagon(d7), flip(hexagon(d7))] }, - { color: 1, origin: [e7, reflect(e7)], path: [hexagon(e7), flip(hexagon(e7))] }, - { color: 0, origin: [f7, reflect(f7)], path: [hexagon(f7), flip(hexagon(f7))] }, - { color: 1, origin: [g7, reflect(g7)], path: [hexagon(g7), flip(hexagon(g7))] }, - { color: 2, origin: [h7, reflect(h7)], path: [hexagon(h7), flip(hexagon(h7))] }, - { color: 0, origin: [i7, reflect(i7)], path: [hexagon(i7), flip(hexagon(i7))] }, - { color: 1, origin: [k7, reflect(k7)], path: [hexagon(k7), flip(hexagon(k7))] }, - { color: 0, origin: [a6, reflect(a6)], path: [hexagon(a6), flip(hexagon(a6))] }, - { color: 2, origin: [b6, reflect(b6)], path: [hexagon(b6), flip(hexagon(b6))] }, - { color: 1, origin: [c6, reflect(c6)], path: [hexagon(c6), flip(hexagon(c6))] }, - { color: 0, origin: [d6, reflect(d6)], path: [hexagon(d6), flip(hexagon(d6))] }, - { color: 2, origin: [e6, reflect(e6)], path: [hexagon(e6), flip(hexagon(e6))] }, - { color: 1, origin: [f6, reflect(f6)], path: [hexagon(f6), flip(hexagon(f6))] }, - { color: 2, origin: [g6, reflect(g6)], path: [hexagon(g6), flip(hexagon(g6))] }, - { color: 0, origin: [h6, reflect(h6)], path: [hexagon(h6), flip(hexagon(h6))] }, - { color: 1, origin: [i6, reflect(i6)], path: [hexagon(i6), flip(hexagon(i6))] }, - { color: 2, origin: [k6, reflect(k6)], path: [hexagon(k6), flip(hexagon(k6))] }, - { color: 0, origin: [l6, reflect(l6)], path: [hexagon(l6), flip(hexagon(l6))] }, - { color: 1, origin: [a5, reflect(a5)], path: [hexagon(a5), flip(hexagon(a5))] }, - { color: 0, origin: [b5, reflect(b5)], path: [hexagon(b5), flip(hexagon(b5))] }, - { color: 2, origin: [c5, reflect(c5)], path: [hexagon(c5), flip(hexagon(c5))] }, - { color: 1, origin: [d5, reflect(d5)], path: [hexagon(d5), flip(hexagon(d5))] }, - { color: 0, origin: [e5, reflect(e5)], path: [hexagon(e5), flip(hexagon(e5))] }, - { color: 2, origin: [f5, reflect(f5)], path: [hexagon(f5), flip(hexagon(f5))] }, - { color: 0, origin: [g5, reflect(g5)], path: [hexagon(g5), flip(hexagon(g5))] }, - { color: 1, origin: [h5, reflect(h5)], path: [hexagon(h5), flip(hexagon(h5))] }, - { color: 2, origin: [i5, reflect(i5)], path: [hexagon(i5), flip(hexagon(i5))] }, - { color: 0, origin: [k5, reflect(k5)], path: [hexagon(k5), flip(hexagon(k5))] }, - { color: 1, origin: [l5, reflect(l5)], path: [hexagon(l5), flip(hexagon(l5))] }, - { color: 2, origin: [a4, reflect(a4)], path: [hexagon(a4), flip(hexagon(a4))] }, - { color: 1, origin: [b4, reflect(b4)], path: [hexagon(b4), flip(hexagon(b4))] }, - { color: 0, origin: [c4, reflect(c4)], path: [hexagon(c4), flip(hexagon(c4))] }, - { color: 2, origin: [d4, reflect(d4)], path: [hexagon(d4), flip(hexagon(d4))] }, - { color: 1, origin: [e4, reflect(e4)], path: [hexagon(e4), flip(hexagon(e4))] }, - { color: 0, origin: [f4, reflect(f4)], path: [hexagon(f4), flip(hexagon(f4))] }, - { color: 1, origin: [g4, reflect(g4)], path: [hexagon(g4), flip(hexagon(g4))] }, - { color: 2, origin: [h4, reflect(h4)], path: [hexagon(h4), flip(hexagon(h4))] }, - { color: 0, origin: [i4, reflect(i4)], path: [hexagon(i4), flip(hexagon(i4))] }, - { color: 1, origin: [k4, reflect(k4)], path: [hexagon(k4), flip(hexagon(k4))] }, - { color: 2, origin: [l4, reflect(l4)], path: [hexagon(l4), flip(hexagon(l4))] }, - { color: 0, origin: [a3, reflect(a3)], path: [hexagon(a3), flip(hexagon(a3))] }, - { color: 2, origin: [b3, reflect(b3)], path: [hexagon(b3), flip(hexagon(b3))] }, - { color: 1, origin: [c3, reflect(c3)], path: [hexagon(c3), flip(hexagon(c3))] }, - { color: 0, origin: [d3, reflect(d3)], path: [hexagon(d3), flip(hexagon(d3))] }, - { color: 2, origin: [e3, reflect(e3)], path: [hexagon(e3), flip(hexagon(e3))] }, - { color: 1, origin: [f3, reflect(f3)], path: [hexagon(f3), flip(hexagon(f3))] }, - { color: 2, origin: [g3, reflect(g3)], path: [hexagon(g3), flip(hexagon(g3))] }, - { color: 0, origin: [h3, reflect(h3)], path: [hexagon(h3), flip(hexagon(h3))] }, - { color: 1, origin: [i3, reflect(i3)], path: [hexagon(i3), flip(hexagon(i3))] }, - { color: 2, origin: [k3, reflect(k3)], path: [hexagon(k3), flip(hexagon(k3))] }, - { color: 0, origin: [l3, reflect(l3)], path: [hexagon(l3), flip(hexagon(l3))] }, - { color: 1, origin: [a2, reflect(a2)], path: [hexagon(a2), flip(hexagon(a2))] }, - { color: 0, origin: [b2, reflect(b2)], path: [hexagon(b2), flip(hexagon(b2))] }, - { color: 2, origin: [c2, reflect(c2)], path: [hexagon(c2), flip(hexagon(c2))] }, - { color: 1, origin: [d2, reflect(d2)], path: [hexagon(d2), flip(hexagon(d2))] }, - { color: 0, origin: [e2, reflect(e2)], path: [hexagon(e2), flip(hexagon(e2))] }, - { color: 2, origin: [f2, reflect(f2)], path: [hexagon(f2), flip(hexagon(f2))] }, - { color: 0, origin: [g2, reflect(g2)], path: [hexagon(g2), flip(hexagon(g2))] }, - { color: 1, origin: [h2, reflect(h2)], path: [hexagon(h2), flip(hexagon(h2))] }, - { color: 2, origin: [i2, reflect(i2)], path: [hexagon(i2), flip(hexagon(i2))] }, - { color: 0, origin: [k2, reflect(k2)], path: [hexagon(k2), flip(hexagon(k2))] }, - { color: 1, origin: [l2, reflect(l2)], path: [hexagon(l2), flip(hexagon(l2))] }, - { color: 2, origin: [a1, reflect(a1)], path: [hexagon(a1), flip(hexagon(a1))] }, - { color: 1, origin: [b1, reflect(b1)], path: [hexagon(b1), flip(hexagon(b1))] }, - { color: 0, origin: [c1, reflect(c1)], path: [hexagon(c1), flip(hexagon(c1))] }, - { color: 2, origin: [d1, reflect(d1)], path: [hexagon(d1), flip(hexagon(d1))] }, - { color: 1, origin: [e1, reflect(e1)], path: [hexagon(e1), flip(hexagon(e1))] }, - { color: 0, origin: [f1, reflect(f1)], path: [hexagon(f1), flip(hexagon(f1))] }, - { color: 1, origin: [g1, reflect(g1)], path: [hexagon(g1), flip(hexagon(g1))] }, - { color: 2, origin: [h1, reflect(h1)], path: [hexagon(h1), flip(hexagon(h1))] }, - { color: 0, origin: [i1, reflect(i1)], path: [hexagon(i1), flip(hexagon(i1))] }, - { color: 1, origin: [k1, reflect(k1)], path: [hexagon(k1), flip(hexagon(k1))] }, - { color: 2, origin: [l1, reflect(l1)], path: [hexagon(l1), flip(hexagon(l1))] }, +export const board: [ + color: number, + origin: Vec<2>, + reflectedOrigin: Vec<2>, + path: Vec<6, Vec<2>>, + reflectedPath: Vec<6, Vec<2>> +][] = [ + [2, f11, reflect(f11), hexagon(f11), flip(hexagon(f11))], + [1, e10, reflect(e10), hexagon(e10), flip(hexagon(e10))], + [0, f10, reflect(f10), hexagon(f10), flip(hexagon(f10))], + [1, g10, reflect(g10), hexagon(g10), flip(hexagon(g10))], + [0, d9, reflect(d9), hexagon(d9), flip(hexagon(d9))], + [2, e9, reflect(e9), hexagon(e9), flip(hexagon(e9))], + [1, f9, reflect(f9), hexagon(f9), flip(hexagon(f9))], + [2, g9, reflect(g9), hexagon(g9), flip(hexagon(g9))], + [0, h9, reflect(h9), hexagon(h9), flip(hexagon(h9))], + [2, c8, reflect(c8), hexagon(c8), flip(hexagon(c8))], + [1, d8, reflect(d8), hexagon(d8), flip(hexagon(d8))], + [0, e8, reflect(e8), hexagon(e8), flip(hexagon(e8))], + [2, f8, reflect(f8), hexagon(f8), flip(hexagon(f8))], + [0, g8, reflect(g8), hexagon(g8), flip(hexagon(g8))], + [1, h8, reflect(h8), hexagon(h8), flip(hexagon(h8))], + [2, i8, reflect(i8), hexagon(i8), flip(hexagon(i8))], + [1, b7, reflect(b7), hexagon(b7), flip(hexagon(b7))], + [0, c7, reflect(c7), hexagon(c7), flip(hexagon(c7))], + [2, d7, reflect(d7), hexagon(d7), flip(hexagon(d7))], + [1, e7, reflect(e7), hexagon(e7), flip(hexagon(e7))], + [0, f7, reflect(f7), hexagon(f7), flip(hexagon(f7))], + [1, g7, reflect(g7), hexagon(g7), flip(hexagon(g7))], + [2, h7, reflect(h7), hexagon(h7), flip(hexagon(h7))], + [0, i7, reflect(i7), hexagon(i7), flip(hexagon(i7))], + [1, k7, reflect(k7), hexagon(k7), flip(hexagon(k7))], + [0, a6, reflect(a6), hexagon(a6), flip(hexagon(a6))], + [2, b6, reflect(b6), hexagon(b6), flip(hexagon(b6))], + [1, c6, reflect(c6), hexagon(c6), flip(hexagon(c6))], + [0, d6, reflect(d6), hexagon(d6), flip(hexagon(d6))], + [2, e6, reflect(e6), hexagon(e6), flip(hexagon(e6))], + [1, f6, reflect(f6), hexagon(f6), flip(hexagon(f6))], + [2, g6, reflect(g6), hexagon(g6), flip(hexagon(g6))], + [0, h6, reflect(h6), hexagon(h6), flip(hexagon(h6))], + [1, i6, reflect(i6), hexagon(i6), flip(hexagon(i6))], + [2, k6, reflect(k6), hexagon(k6), flip(hexagon(k6))], + [0, l6, reflect(l6), hexagon(l6), flip(hexagon(l6))], + [1, a5, reflect(a5), hexagon(a5), flip(hexagon(a5))], + [0, b5, reflect(b5), hexagon(b5), flip(hexagon(b5))], + [2, c5, reflect(c5), hexagon(c5), flip(hexagon(c5))], + [1, d5, reflect(d5), hexagon(d5), flip(hexagon(d5))], + [0, e5, reflect(e5), hexagon(e5), flip(hexagon(e5))], + [2, f5, reflect(f5), hexagon(f5), flip(hexagon(f5))], + [0, g5, reflect(g5), hexagon(g5), flip(hexagon(g5))], + [1, h5, reflect(h5), hexagon(h5), flip(hexagon(h5))], + [2, i5, reflect(i5), hexagon(i5), flip(hexagon(i5))], + [0, k5, reflect(k5), hexagon(k5), flip(hexagon(k5))], + [1, l5, reflect(l5), hexagon(l5), flip(hexagon(l5))], + [2, a4, reflect(a4), hexagon(a4), flip(hexagon(a4))], + [1, b4, reflect(b4), hexagon(b4), flip(hexagon(b4))], + [0, c4, reflect(c4), hexagon(c4), flip(hexagon(c4))], + [2, d4, reflect(d4), hexagon(d4), flip(hexagon(d4))], + [1, e4, reflect(e4), hexagon(e4), flip(hexagon(e4))], + [0, f4, reflect(f4), hexagon(f4), flip(hexagon(f4))], + [1, g4, reflect(g4), hexagon(g4), flip(hexagon(g4))], + [2, h4, reflect(h4), hexagon(h4), flip(hexagon(h4))], + [0, i4, reflect(i4), hexagon(i4), flip(hexagon(i4))], + [1, k4, reflect(k4), hexagon(k4), flip(hexagon(k4))], + [2, l4, reflect(l4), hexagon(l4), flip(hexagon(l4))], + [0, a3, reflect(a3), hexagon(a3), flip(hexagon(a3))], + [2, b3, reflect(b3), hexagon(b3), flip(hexagon(b3))], + [1, c3, reflect(c3), hexagon(c3), flip(hexagon(c3))], + [0, d3, reflect(d3), hexagon(d3), flip(hexagon(d3))], + [2, e3, reflect(e3), hexagon(e3), flip(hexagon(e3))], + [1, f3, reflect(f3), hexagon(f3), flip(hexagon(f3))], + [2, g3, reflect(g3), hexagon(g3), flip(hexagon(g3))], + [0, h3, reflect(h3), hexagon(h3), flip(hexagon(h3))], + [1, i3, reflect(i3), hexagon(i3), flip(hexagon(i3))], + [2, k3, reflect(k3), hexagon(k3), flip(hexagon(k3))], + [0, l3, reflect(l3), hexagon(l3), flip(hexagon(l3))], + [1, a2, reflect(a2), hexagon(a2), flip(hexagon(a2))], + [0, b2, reflect(b2), hexagon(b2), flip(hexagon(b2))], + [2, c2, reflect(c2), hexagon(c2), flip(hexagon(c2))], + [1, d2, reflect(d2), hexagon(d2), flip(hexagon(d2))], + [0, e2, reflect(e2), hexagon(e2), flip(hexagon(e2))], + [2, f2, reflect(f2), hexagon(f2), flip(hexagon(f2))], + [0, g2, reflect(g2), hexagon(g2), flip(hexagon(g2))], + [1, h2, reflect(h2), hexagon(h2), flip(hexagon(h2))], + [2, i2, reflect(i2), hexagon(i2), flip(hexagon(i2))], + [0, k2, reflect(k2), hexagon(k2), flip(hexagon(k2))], + [1, l2, reflect(l2), hexagon(l2), flip(hexagon(l2))], + [2, a1, reflect(a1), hexagon(a1), flip(hexagon(a1))], + [1, b1, reflect(b1), hexagon(b1), flip(hexagon(b1))], + [0, c1, reflect(c1), hexagon(c1), flip(hexagon(c1))], + [2, d1, reflect(d1), hexagon(d1), flip(hexagon(d1))], + [1, e1, reflect(e1), hexagon(e1), flip(hexagon(e1))], + [0, f1, reflect(f1), hexagon(f1), flip(hexagon(f1))], + [1, g1, reflect(g1), hexagon(g1), flip(hexagon(g1))], + [2, h1, reflect(h1), hexagon(h1), flip(hexagon(h1))], + [0, i1, reflect(i1), hexagon(i1), flip(hexagon(i1))], + [1, k1, reflect(k1), hexagon(k1), flip(hexagon(k1))], + [2, l1, reflect(l1), hexagon(l1), flip(hexagon(l1))], ] /** board labels */ @@ -374,99 +281,99 @@ export const labels: [string, Vec<2>, Vec<2>][] = [ ['11', bilerp(e10, f11, labelOffset), reflect(bilerp(e10, f11, labelOffset))], ] -/** perimeter of the board, used to create a constant background for light/dark mode */ +/** perimeter of the board */ export const perimeter: Vec<93, Vec<2>> = [ - board[positions.indexOf('f11')]!.path[0][5], - board[positions.indexOf('f11')]!.path[0][0], - board[positions.indexOf('f11')]!.path[0][1], - board[positions.indexOf('g10')]!.path[0][5], - board[positions.indexOf('g10')]!.path[0][0], - board[positions.indexOf('g10')]!.path[0][1], - board[positions.indexOf('h9')]!.path[0][5], - board[positions.indexOf('h9')]!.path[0][0], - board[positions.indexOf('h9')]!.path[0][1], - board[positions.indexOf('i8')]!.path[0][5], - board[positions.indexOf('i8')]!.path[0][0], - board[positions.indexOf('i8')]!.path[0][1], - board[positions.indexOf('k7')]!.path[0][5], - board[positions.indexOf('k7')]!.path[0][0], - board[positions.indexOf('k7')]!.path[0][1], - board[positions.indexOf('l6')]!.path[0][5], - board[positions.indexOf('l6')]!.path[0][0], - board[positions.indexOf('l6')]!.path[0][1], - board[positions.indexOf('l6')]!.path[0][2], - board[positions.indexOf('l5')]!.path[0][0], - board[positions.indexOf('l5')]!.path[0][1], - board[positions.indexOf('l5')]!.path[0][2], - board[positions.indexOf('l4')]!.path[0][0], - board[positions.indexOf('l4')]!.path[0][1], - board[positions.indexOf('l4')]!.path[0][2], - board[positions.indexOf('l3')]!.path[0][0], - board[positions.indexOf('l3')]!.path[0][1], - board[positions.indexOf('l3')]!.path[0][2], - board[positions.indexOf('l2')]!.path[0][0], - board[positions.indexOf('l2')]!.path[0][1], - board[positions.indexOf('l2')]!.path[0][2], - board[positions.indexOf('l1')]!.path[0][0], - board[positions.indexOf('l1')]!.path[0][1], - board[positions.indexOf('l1')]!.path[0][2], - board[positions.indexOf('l1')]!.path[0][3], - board[positions.indexOf('k1')]!.path[0][2], - board[positions.indexOf('k1')]!.path[0][3], - board[positions.indexOf('k1')]!.path[0][4], - board[positions.indexOf('i1')]!.path[0][1], - board[positions.indexOf('i1')]!.path[0][2], - board[positions.indexOf('i1')]!.path[0][3], - board[positions.indexOf('h1')]!.path[0][1], - board[positions.indexOf('h1')]!.path[0][2], - board[positions.indexOf('h1')]!.path[0][3], - board[positions.indexOf('g1')]!.path[0][1], - board[positions.indexOf('g1')]!.path[0][2], - board[positions.indexOf('g1')]!.path[0][3], - board[positions.indexOf('f1')]!.path[0][1], - board[positions.indexOf('f1')]!.path[0][2], - board[positions.indexOf('f1')]!.path[0][3], - board[positions.indexOf('f1')]!.path[0][4], - board[positions.indexOf('e1')]!.path[0][3], - board[positions.indexOf('e1')]!.path[0][4], - board[positions.indexOf('d1')]!.path[0][2], - board[positions.indexOf('d1')]!.path[0][3], - board[positions.indexOf('d1')]!.path[0][4], - board[positions.indexOf('c1')]!.path[0][2], - board[positions.indexOf('c1')]!.path[0][3], - board[positions.indexOf('c1')]!.path[0][4], - board[positions.indexOf('b1')]!.path[0][2], - board[positions.indexOf('b1')]!.path[0][3], - board[positions.indexOf('b1')]!.path[0][4], - board[positions.indexOf('a1')]!.path[0][2], - board[positions.indexOf('a1')]!.path[0][3], - board[positions.indexOf('a1')]!.path[0][4], - board[positions.indexOf('a1')]!.path[0][5], - board[positions.indexOf('a2')]!.path[0][3], - board[positions.indexOf('a2')]!.path[0][4], - board[positions.indexOf('a2')]!.path[0][5], - board[positions.indexOf('a3')]!.path[0][3], - board[positions.indexOf('a3')]!.path[0][4], - board[positions.indexOf('a3')]!.path[0][5], - board[positions.indexOf('a4')]!.path[0][3], - board[positions.indexOf('a4')]!.path[0][4], - board[positions.indexOf('a4')]!.path[0][5], - board[positions.indexOf('a5')]!.path[0][3], - board[positions.indexOf('a5')]!.path[0][4], - board[positions.indexOf('a5')]!.path[0][5], - board[positions.indexOf('a6')]!.path[0][3], - board[positions.indexOf('a6')]!.path[0][4], - board[positions.indexOf('a6')]!.path[0][5], - board[positions.indexOf('b7')]!.path[0][4], - board[positions.indexOf('b7')]!.path[0][5], - board[positions.indexOf('b7')]!.path[0][0], - board[positions.indexOf('c8')]!.path[0][4], - board[positions.indexOf('c8')]!.path[0][5], - board[positions.indexOf('c8')]!.path[0][0], - board[positions.indexOf('d9')]!.path[0][4], - board[positions.indexOf('d9')]!.path[0][5], - board[positions.indexOf('d9')]!.path[0][0], - board[positions.indexOf('e10')]!.path[0][4], - board[positions.indexOf('e10')]!.path[0][5], - board[positions.indexOf('e10')]!.path[0][0], + board[0 /* f11 */][3][5], + board[0 /* f11 */][3][0], + board[0 /* f11 */][3][1], + board[3 /* g10 */][3][5], + board[3 /* g10 */][3][0], + board[3 /* g10 */][3][1], + board[8 /* h9 */][3][5], + board[8 /* h9 */][3][0], + board[8 /* h9 */][3][1], + board[15 /* i8 */][3][5], + board[15 /* i8 */][3][0], + board[15 /* i8 */][3][1], + board[24 /* k7 */][3][5], + board[24 /* k7 */][3][0], + board[24 /* k7 */][3][1], + board[35 /* l6 */][3][5], + board[35 /* l6 */][3][0], + board[35 /* l6 */][3][1], + board[35 /* l6 */][3][2], + board[46 /* l5 */][3][0], + board[46 /* l5 */][3][1], + board[46 /* l5 */][3][2], + board[57 /* l4 */][3][0], + board[57 /* l4 */][3][1], + board[57 /* l4 */][3][2], + board[68 /* l3 */][3][0], + board[68 /* l3 */][3][1], + board[68 /* l3 */][3][2], + board[79 /* l2 */][3][0], + board[79 /* l2 */][3][1], + board[79 /* l2 */][3][2], + board[90 /* l1 */][3][0], + board[90 /* l1 */][3][1], + board[90 /* l1 */][3][2], + board[90 /* l1 */][3][3], + board[89 /* k1 */][3][2], + board[89 /* k1 */][3][3], + board[89 /* k1 */][3][4], + board[88 /* i1 */][3][1], + board[88 /* i1 */][3][2], + board[88 /* i1 */][3][3], + board[87 /* h1 */][3][1], + board[87 /* h1 */][3][2], + board[87 /* h1 */][3][3], + board[86 /* g1 */][3][1], + board[86 /* g1 */][3][2], + board[86 /* g1 */][3][3], + board[85 /* f1 */][3][1], + board[85 /* f1 */][3][2], + board[85 /* f1 */][3][3], + board[85 /* f1 */][3][4], + board[84 /* e1 */][3][3], + board[84 /* e1 */][3][4], + board[83 /* d1 */][3][2], + board[83 /* d1 */][3][3], + board[83 /* d1 */][3][4], + board[82 /* c1 */][3][2], + board[82 /* c1 */][3][3], + board[82 /* c1 */][3][4], + board[81 /* b1 */][3][2], + board[81 /* b1 */][3][3], + board[81 /* b1 */][3][4], + board[80 /* a1 */][3][2], + board[80 /* a1 */][3][3], + board[80 /* a1 */][3][4], + board[80 /* a1 */][3][5], + board[69 /* a2 */][3][3], + board[69 /* a2 */][3][4], + board[69 /* a2 */][3][5], + board[58 /* a3 */][3][3], + board[58 /* a3 */][3][4], + board[58 /* a3 */][3][5], + board[47 /* a4 */][3][3], + board[47 /* a4 */][3][4], + board[47 /* a4 */][3][5], + board[36 /* a5 */][3][3], + board[36 /* a5 */][3][4], + board[36 /* a5 */][3][5], + board[25 /* a6 */][3][3], + board[25 /* a6 */][3][4], + board[25 /* a6 */][3][5], + board[16 /* b7 */][3][4], + board[16 /* b7 */][3][5], + board[16 /* b7 */][3][0], + board[9 /* c8 */][3][4], + board[9 /* c8 */][3][5], + board[9 /* c8 */][3][0], + board[4 /* d9 */][3][4], + board[4 /* d9 */][3][5], + board[4 /* d9 */][3][0], + board[1 /* e10 */][3][4], + board[1 /* e10 */][3][5], + board[1 /* e10 */][3][0], ] From 7a90b3d46312744f07bd9759fff3710e26297587 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Tue, 18 Nov 2025 23:26:07 -0600 Subject: [PATCH 05/80] wip --- ui/src/App.vue | 5 +- ui/src/client.ts | 4 +- ui/src/lib/{hexboard => board}/constants.ts | 0 ui/src/lib/board/dom.ts | 45 +++++++++++++++ ui/src/lib/{hexboard => board}/geometry.ts | 15 ----- ui/src/lib/board/index.ts | 64 +++++++++++++++++++++ ui/src/lib/{hexboard => board}/types.ts | 0 ui/src/lib/hexboard/index.ts | 13 ----- ui/src/lib/index.ts | 2 +- 9 files changed, 115 insertions(+), 33 deletions(-) rename ui/src/lib/{hexboard => board}/constants.ts (100%) create mode 100644 ui/src/lib/board/dom.ts rename ui/src/lib/{hexboard => board}/geometry.ts (81%) create mode 100644 ui/src/lib/board/index.ts rename ui/src/lib/{hexboard => board}/types.ts (100%) delete mode 100644 ui/src/lib/hexboard/index.ts diff --git a/ui/src/App.vue b/ui/src/App.vue index c7a89ce4..a64f371b 100644 --- a/ui/src/App.vue +++ b/ui/src/App.vue @@ -1,6 +1,7 @@ \ No newline at end of file diff --git a/ui/src/client.ts b/ui/src/client.ts index 26fe50a0..7724ac67 100644 --- a/ui/src/client.ts +++ b/ui/src/client.ts @@ -1,7 +1,7 @@ import { createApp } from 'vue' -import { Hexboard } from './lib' +import { HexchessBoard } from './lib' import App from './App.vue' -customElements.define('hexchess-board', Hexboard) +customElements.define('hexchess-board', HexchessBoard) createApp(App).mount('#app') diff --git a/ui/src/lib/hexboard/constants.ts b/ui/src/lib/board/constants.ts similarity index 100% rename from ui/src/lib/hexboard/constants.ts rename to ui/src/lib/board/constants.ts diff --git a/ui/src/lib/board/dom.ts b/ui/src/lib/board/dom.ts new file mode 100644 index 00000000..5da846f3 --- /dev/null +++ b/ui/src/lib/board/dom.ts @@ -0,0 +1,45 @@ +import { x, y } from './geometry' +import type { Vec } from './types' + +/** svg namespace */ +const ns = 'http://www.w3.org/2000/svg' + +/** create svg vector path */ +export function d(arr: [number, number][]) { + if (arr.length === 0) return '' + + const [origin, ...points] = arr + + let path = `M ${x(origin[0])} ${y(origin[1])} L ` + + for (const point of points) { + path += `${x(point[0])} ${y(point[1])} ` + } + + return `${path} Z` +} + +/** create svg path element */ +export function path(points: Vec<2>[]) { + const el = svgEl('path') + el.setAttribute('d', d(points)) + return el +} + +/** create slot element */ +export function slot(name: string) { + const slotEl = document.createElement('slot') + slotEl.name = name + return slotEl +} + +/** create svg element */ +export function svg() { + const el = svgEl('svg') + el.setAttribute('xmlns', ns) + return el +} + +function svgEl(tag: T) { + return document.createElementNS(ns, tag) +} diff --git a/ui/src/lib/hexboard/geometry.ts b/ui/src/lib/board/geometry.ts similarity index 81% rename from ui/src/lib/hexboard/geometry.ts rename to ui/src/lib/board/geometry.ts index d6ce5acf..e424ed86 100644 --- a/ui/src/lib/hexboard/geometry.ts +++ b/ui/src/lib/board/geometry.ts @@ -10,21 +10,6 @@ export function bilerp(p1: Vec<2>, p2: Vec<2>, t: number): Vec<2> { ] } -/** create svg vector path */ -export function d(arr: [number, number][]) { - const p = 4 // <- svg rounding precision - - const [origin, ...points] = arr - - let path = `M ${x(origin[0]).toFixed(p)} ${y(origin[1]).toFixed(p)} L ` - - for (const point of points) { - path += `${x(point[0]).toFixed(p)} ${y(point[1]).toFixed(p)} ` - } - - return `${path} Z` -} - /** reflect a path of points across the origin */ export function flip[]>(path: T) { return path.map(reflect) as T diff --git a/ui/src/lib/board/index.ts b/ui/src/lib/board/index.ts new file mode 100644 index 00000000..796f30f6 --- /dev/null +++ b/ui/src/lib/board/index.ts @@ -0,0 +1,64 @@ +import { board, box, colors, perimeter } from './constants' +import { path, slot, svg } from './dom' + +export class HexchessBoard extends HTMLElement { + private perimeterEl: SVGPathElement + + private positionEls: SVGPathElement[] = [] + + private svgEl: SVGSVGElement + + /** + * Create element + */ + constructor() { + super(); + + this.attachShadow({ mode: 'open' }) + + // root + this.svgEl = svg() + this.svgEl.setAttribute('viewBox', `0 0 ${box} ${box}`) + + // perimeter + this.perimeterEl = path(perimeter) + this.perimeterEl.setAttribute('fill', colors[1]) + + // position 's + const points = this.isFlipped() ? 4 : 3 + + for (const position of board) { + const el = path(position[points]) + el.setAttribute('fill', colors[position[0]]) + this.positionEls.push(el) + } + + // construct dom tree + this.shadowRoot?.append( + this.svgEl, + slot('p'), + slot('n'), + slot('b'), + slot('r'), + slot('q'), + slot('k'), + slot('P'), + slot('N'), + slot('B'), + slot('R'), + slot('Q'), + slot('K'), + ) + + this.svgEl.append(this.perimeterEl, ...this.positionEls) + } + + connectedCallback() { + // ... + } + + isFlipped() { + const val = this.getAttribute('flipped') + return val === 'true' || val === '' + } +} diff --git a/ui/src/lib/hexboard/types.ts b/ui/src/lib/board/types.ts similarity index 100% rename from ui/src/lib/hexboard/types.ts rename to ui/src/lib/board/types.ts diff --git a/ui/src/lib/hexboard/index.ts b/ui/src/lib/hexboard/index.ts deleted file mode 100644 index 84b1ece1..00000000 --- a/ui/src/lib/hexboard/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { board } from './constants' - -export class Hexboard extends HTMLElement { - constructor() { - super(); - } - - connectedCallback() { - this.innerHTML = '
soon...
'; - - console.log({ board }) - } -} diff --git a/ui/src/lib/index.ts b/ui/src/lib/index.ts index 17b9b0c5..878240d1 100644 --- a/ui/src/lib/index.ts +++ b/ui/src/lib/index.ts @@ -1 +1 @@ -export { Hexboard } from './hexboard' +export { HexchessBoard } from './board' From 388dd8f9b92b67aedfeb9d3595e1b64a31b729d9 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Wed, 19 Nov 2025 21:51:30 -0600 Subject: [PATCH 06/80] export raw vue component --- ui/package.json | 3 + ui/pnpm-lock.yaml | 12 ++-- ui/src/App.vue | 62 ++++++++++++++++++-- ui/src/client.ts | 5 +- ui/src/lib/board/dom.ts | 45 --------------- ui/src/lib/board/index.ts | 64 --------------------- ui/src/lib/hexboard/Hexboard.vue | 48 ++++++++++++++++ ui/src/lib/{board => hexboard}/constants.ts | 0 ui/src/lib/hexboard/dom.ts | 19 ++++++ ui/src/lib/{board => hexboard}/geometry.ts | 0 ui/src/lib/{board => hexboard}/types.ts | 0 ui/src/lib/index.ts | 2 +- 12 files changed, 135 insertions(+), 125 deletions(-) delete mode 100644 ui/src/lib/board/dom.ts delete mode 100644 ui/src/lib/board/index.ts create mode 100644 ui/src/lib/hexboard/Hexboard.vue rename ui/src/lib/{board => hexboard}/constants.ts (100%) create mode 100644 ui/src/lib/hexboard/dom.ts rename ui/src/lib/{board => hexboard}/geometry.ts (100%) rename ui/src/lib/{board => hexboard}/types.ts (100%) diff --git a/ui/package.json b/ui/package.json index 5ae3920d..96705f27 100644 --- a/ui/package.json +++ b/ui/package.json @@ -18,5 +18,8 @@ "vite-plugin-dts": "^4.5.4", "vue": "3.6.0-alpha.4", "vue-tsc": "^3.1.4" + }, + "dependencies": { + "@bedard/hexchess": "link:../js/src" } } diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index 929f07f3..b8333266 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -7,10 +7,11 @@ settings: importers: .: - devDependencies: + dependencies: '@bedard/hexchess': - specifier: file:../js - version: file:../js + specifier: link:../js/src + version: link:../js/src + devDependencies: '@types/node': specifier: ^22.17.1 version: 22.19.1 @@ -52,9 +53,6 @@ packages: resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} engines: {node: '>=6.9.0'} - '@bedard/hexchess@file:../js': - resolution: {directory: ../js, type: directory} - '@esbuild/aix-ppc64@0.25.12': resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} engines: {node: '>=18'} @@ -847,8 +845,6 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 - '@bedard/hexchess@file:../js': {} - '@esbuild/aix-ppc64@0.25.12': optional: true diff --git a/ui/src/App.vue b/ui/src/App.vue index a64f371b..f707f6f6 100644 --- a/ui/src/App.vue +++ b/ui/src/App.vue @@ -1,7 +1,61 @@ \ No newline at end of file + + + \ No newline at end of file diff --git a/ui/src/client.ts b/ui/src/client.ts index 7724ac67..716f720b 100644 --- a/ui/src/client.ts +++ b/ui/src/client.ts @@ -1,7 +1,6 @@ import { createApp } from 'vue' -import { HexchessBoard } from './lib' import App from './App.vue' -customElements.define('hexchess-board', HexchessBoard) - createApp(App).mount('#app') + +console.log('hey') \ No newline at end of file diff --git a/ui/src/lib/board/dom.ts b/ui/src/lib/board/dom.ts deleted file mode 100644 index 5da846f3..00000000 --- a/ui/src/lib/board/dom.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { x, y } from './geometry' -import type { Vec } from './types' - -/** svg namespace */ -const ns = 'http://www.w3.org/2000/svg' - -/** create svg vector path */ -export function d(arr: [number, number][]) { - if (arr.length === 0) return '' - - const [origin, ...points] = arr - - let path = `M ${x(origin[0])} ${y(origin[1])} L ` - - for (const point of points) { - path += `${x(point[0])} ${y(point[1])} ` - } - - return `${path} Z` -} - -/** create svg path element */ -export function path(points: Vec<2>[]) { - const el = svgEl('path') - el.setAttribute('d', d(points)) - return el -} - -/** create slot element */ -export function slot(name: string) { - const slotEl = document.createElement('slot') - slotEl.name = name - return slotEl -} - -/** create svg element */ -export function svg() { - const el = svgEl('svg') - el.setAttribute('xmlns', ns) - return el -} - -function svgEl(tag: T) { - return document.createElementNS(ns, tag) -} diff --git a/ui/src/lib/board/index.ts b/ui/src/lib/board/index.ts deleted file mode 100644 index 796f30f6..00000000 --- a/ui/src/lib/board/index.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { board, box, colors, perimeter } from './constants' -import { path, slot, svg } from './dom' - -export class HexchessBoard extends HTMLElement { - private perimeterEl: SVGPathElement - - private positionEls: SVGPathElement[] = [] - - private svgEl: SVGSVGElement - - /** - * Create element - */ - constructor() { - super(); - - this.attachShadow({ mode: 'open' }) - - // root - this.svgEl = svg() - this.svgEl.setAttribute('viewBox', `0 0 ${box} ${box}`) - - // perimeter - this.perimeterEl = path(perimeter) - this.perimeterEl.setAttribute('fill', colors[1]) - - // position 's - const points = this.isFlipped() ? 4 : 3 - - for (const position of board) { - const el = path(position[points]) - el.setAttribute('fill', colors[position[0]]) - this.positionEls.push(el) - } - - // construct dom tree - this.shadowRoot?.append( - this.svgEl, - slot('p'), - slot('n'), - slot('b'), - slot('r'), - slot('q'), - slot('k'), - slot('P'), - slot('N'), - slot('B'), - slot('R'), - slot('Q'), - slot('K'), - ) - - this.svgEl.append(this.perimeterEl, ...this.positionEls) - } - - connectedCallback() { - // ... - } - - isFlipped() { - const val = this.getAttribute('flipped') - return val === 'true' || val === '' - } -} diff --git a/ui/src/lib/hexboard/Hexboard.vue b/ui/src/lib/hexboard/Hexboard.vue new file mode 100644 index 00000000..a02532f6 --- /dev/null +++ b/ui/src/lib/hexboard/Hexboard.vue @@ -0,0 +1,48 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/board/constants.ts b/ui/src/lib/hexboard/constants.ts similarity index 100% rename from ui/src/lib/board/constants.ts rename to ui/src/lib/hexboard/constants.ts diff --git a/ui/src/lib/hexboard/dom.ts b/ui/src/lib/hexboard/dom.ts new file mode 100644 index 00000000..c8d94fb3 --- /dev/null +++ b/ui/src/lib/hexboard/dom.ts @@ -0,0 +1,19 @@ +import { x, y } from './geometry' +import type { Vec } from './types' + +/** create svg vector path */ +export function d(arr: Vec<2>[]) { + if (arr.length === 0) { + return '' + } + + const [origin, ...points] = arr + + let path = `M ${x(origin[0])} ${y(origin[1])} L ` + + for (const point of points) { + path += `${x(point[0])} ${y(point[1])} ` + } + + return `${path} Z` +} diff --git a/ui/src/lib/board/geometry.ts b/ui/src/lib/hexboard/geometry.ts similarity index 100% rename from ui/src/lib/board/geometry.ts rename to ui/src/lib/hexboard/geometry.ts diff --git a/ui/src/lib/board/types.ts b/ui/src/lib/hexboard/types.ts similarity index 100% rename from ui/src/lib/board/types.ts rename to ui/src/lib/hexboard/types.ts diff --git a/ui/src/lib/index.ts b/ui/src/lib/index.ts index 878240d1..5b355f3a 100644 --- a/ui/src/lib/index.ts +++ b/ui/src/lib/index.ts @@ -1 +1 @@ -export { HexchessBoard } from './board' +export { default as Hexboard } from './hexboard/Hexboard.vue' From f15140726c30b440ce35a118318fe96c0c0dd4ac Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Thu, 20 Nov 2025 19:19:18 -0600 Subject: [PATCH 07/80] render pieces --- ui/package.json | 6 +- ui/pnpm-lock.yaml | 154 +++++++++++------------------- ui/src/App.vue | 63 ++---------- ui/src/lib/hexboard/Hexboard.vue | 70 ++++++++++---- ui/src/lib/index.ts | 1 + ui/src/lib/pieces/GiocoPieces.vue | 22 +++++ 6 files changed, 139 insertions(+), 177 deletions(-) create mode 100644 ui/src/lib/pieces/GiocoPieces.vue diff --git a/ui/package.json b/ui/package.json index 96705f27..5b67e27a 100644 --- a/ui/package.json +++ b/ui/package.json @@ -11,15 +11,13 @@ "preview": "vite preview" }, "devDependencies": { + "@bedard/hexchess": "^2.5.0", "@types/node": "^22.17.1", "@vitejs/plugin-vue": "^6.0.2", "typescript": "^5.9.2", "vite": "^6.3.5", "vite-plugin-dts": "^4.5.4", - "vue": "3.6.0-alpha.4", + "vue": "^3.5.24", "vue-tsc": "^3.1.4" - }, - "dependencies": { - "@bedard/hexchess": "link:../js/src" } } diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index b8333266..56330795 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -7,17 +7,16 @@ settings: importers: .: - dependencies: - '@bedard/hexchess': - specifier: link:../js/src - version: link:../js/src devDependencies: + '@bedard/hexchess': + specifier: ^2.5.0 + version: 2.5.0 '@types/node': specifier: ^22.17.1 version: 22.19.1 '@vitejs/plugin-vue': specifier: ^6.0.2 - version: 6.0.2(vite@6.4.1(@types/node@22.19.1))(vue@3.6.0-alpha.4(typescript@5.9.3)) + version: 6.0.2(vite@6.4.1(@types/node@22.19.1))(vue@3.5.24(typescript@5.9.3)) typescript: specifier: ^5.9.2 version: 5.9.3 @@ -28,8 +27,8 @@ importers: specifier: ^4.5.4 version: 4.5.4(@types/node@22.19.1)(rollup@4.53.2)(typescript@5.9.3)(vite@6.4.1(@types/node@22.19.1)) vue: - specifier: 3.6.0-alpha.4 - version: 3.6.0-alpha.4(typescript@5.9.3) + specifier: ^3.5.24 + version: 3.5.24(typescript@5.9.3) vue-tsc: specifier: ^3.1.4 version: 3.1.4(typescript@5.9.3) @@ -53,6 +52,9 @@ packages: resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} engines: {node: '>=6.9.0'} + '@bedard/hexchess@2.5.0': + resolution: {integrity: sha512-KwThpuP8zD2tk8NytA+U/6pZqmZUMdWh8xH3LFLWYa9GvFBwZwqbW1q+tGqytBxFmFKoryV3hr9yN6PtC8B2lA==} + '@esbuild/aix-ppc64@0.25.12': resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} engines: {node: '>=18'} @@ -413,23 +415,14 @@ packages: '@vue/compiler-core@3.5.24': resolution: {integrity: sha512-eDl5H57AOpNakGNAkFDH+y7kTqrQpJkZFXhWZQGyx/5Wh7B1uQYvcWkvZi11BDhscPgj8N7XV3oRwiPnx1Vrig==} - '@vue/compiler-core@3.6.0-alpha.4': - resolution: {integrity: sha512-Z2yBj9sxi/C46qKw549dqpb0ZqIH/5FGeLT5inV8cXbbgBn9AfH6+3aIWVd0HJ0oSnHSr/sPE+pn0PXrWOnwoA==} - '@vue/compiler-dom@3.5.24': resolution: {integrity: sha512-1QHGAvs53gXkWdd3ZMGYuvQFXHW4ksKWPG8HP8/2BscrbZ0brw183q2oNWjMrSWImYLHxHrx1ItBQr50I/q2zw==} - '@vue/compiler-dom@3.6.0-alpha.4': - resolution: {integrity: sha512-PHM00EYjcd1YPyRx9Pe1UbtDKbY3Y7VvuCZFFAMjNJfV9Wx/OfBDBf9F4ChOt+kSD/RZEQfELsSBOWBLrpuS5w==} + '@vue/compiler-sfc@3.5.24': + resolution: {integrity: sha512-8EG5YPRgmTB+YxYBM3VXy8zHD9SWHUJLIGPhDovo3Z8VOgvP+O7UP5vl0J4BBPWYD9vxtBabzW1EuEZ+Cqs14g==} - '@vue/compiler-sfc@3.6.0-alpha.4': - resolution: {integrity: sha512-vCjb9thJAqxA6iFDKdD+CM+o5w+NnfcDUZqJxRZniUCVrRFCLEmzN0EN7tn7ksh/suHasxecss/g7Z+ucu6EhQ==} - - '@vue/compiler-ssr@3.6.0-alpha.4': - resolution: {integrity: sha512-p0BOmwfMtcTToqueac4IQvtBTsmtvBn0wRMC8hewjqKhHx5wqd7T+1i9X5oCNx9pEA62HQlrYKGFp6tRDoL57w==} - - '@vue/compiler-vapor@3.6.0-alpha.4': - resolution: {integrity: sha512-DXlmmuNkoXUBY7YqJIG7zvmeueJ23y3FQzpFjiFY63cW/Eefk1pfidn29R/NL7wiQDIzrve92325B74sVcWguQ==} + '@vue/compiler-ssr@3.5.24': + resolution: {integrity: sha512-trOvMWNBMQ/odMRHW7Ae1CdfYx+7MuiQu62Jtu36gMLXcaoqKvAyh+P73sYG9ll+6jLB6QPovqoKGGZROzkFFg==} '@vue/compiler-vue2@2.7.16': resolution: {integrity: sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==} @@ -450,31 +443,23 @@ packages: typescript: optional: true - '@vue/reactivity@3.6.0-alpha.4': - resolution: {integrity: sha512-aStBkVTA+GeaMhyiO26fW8tqQtw88XTKFbcAOs0QvksNG7Qh7NxWmbJiXIcsB1PhEQvEywkOJo88TBAJmPAtpg==} + '@vue/reactivity@3.5.24': + resolution: {integrity: sha512-BM8kBhtlkkbnyl4q+HiF5R5BL0ycDPfihowulm02q3WYp2vxgPcJuZO866qa/0u3idbMntKEtVNuAUp5bw4teg==} - '@vue/runtime-core@3.6.0-alpha.4': - resolution: {integrity: sha512-QtmBbJL8kBfG/TfXkq47ajDJd/N8UvH5jSZHZ1pwiRoVkDLccqjiRCpeJMTbCYJoRw4KhbCiIs4TQR08atHYUA==} + '@vue/runtime-core@3.5.24': + resolution: {integrity: sha512-RYP/byyKDgNIqfX/gNb2PB55dJmM97jc9wyF3jK7QUInYKypK2exmZMNwnjueWwGceEkP6NChd3D2ZVEp9undQ==} - '@vue/runtime-dom@3.6.0-alpha.4': - resolution: {integrity: sha512-bqfLJDX1t1bB07Jd96MNewgZrIGgg6iDbP9ql/sZ/LvQLLbxs0v8JPbe6D+Os/P+shQ9vl4PgYmpg7g59WqINA==} + '@vue/runtime-dom@3.5.24': + resolution: {integrity: sha512-Z8ANhr/i0XIluonHVjbUkjvn+CyrxbXRIxR7wn7+X7xlcb7dJsfITZbkVOeJZdP8VZwfrWRsWdShH6pngMxRjw==} - '@vue/runtime-vapor@3.6.0-alpha.4': - resolution: {integrity: sha512-GqObmtDRxkezsvmUAxeTlPcDxT7ywHRh5UNgttV6xH4ae+kd3NsER4mHThwhYnrH+AIu1OeNLG+Eqfh9B2sKmg==} + '@vue/server-renderer@3.5.24': + resolution: {integrity: sha512-Yh2j2Y4G/0/4z/xJ1Bad4mxaAk++C2v4kaa8oSYTMJBJ00/ndPuxCnWeot0/7/qafQFLh5pr6xeV6SdMcE/G1w==} peerDependencies: - '@vue/runtime-dom': 3.6.0-alpha.4 - - '@vue/server-renderer@3.6.0-alpha.4': - resolution: {integrity: sha512-+3e6sqwmCEjEqhjjHz8BB2V7oL8HJUKO/kaRSNeK0jh8Z/AjKUP9e5VaOwn0PF6oP3r1hAlyuXTA/HD2BN1Fag==} - peerDependencies: - vue: 3.6.0-alpha.4 + vue: 3.5.24 '@vue/shared@3.5.24': resolution: {integrity: sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==} - '@vue/shared@3.6.0-alpha.4': - resolution: {integrity: sha512-UWhZp4a/52k/0ptTzDxU2K6pFhUfJXVF+SIPqkIvjF0eKgvrPaLhdeRZ0PH+XxzMmm3tYAP3R78IqioxbLv0hw==} - acorn@8.15.0: resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} engines: {node: '>=0.4.0'} @@ -819,8 +804,8 @@ packages: peerDependencies: typescript: '>=5.0.0' - vue@3.6.0-alpha.4: - resolution: {integrity: sha512-3Q5/IdK6c4Ro7s9qblbWnyfWs5Zvk4/Gtg4LnXRy22FVQ2LTLlrujhn+mBH67njrFRd9U1dhGlzeHl9rp516ZQ==} + vue@3.5.24: + resolution: {integrity: sha512-uTHDOpVQTMjcGgrqFPSb8iO2m1DUvo+WbGqoXQz8Y1CeBYQ0FXf2z1gLRaBtHjlRz7zZUBHxjVB5VTLzYkvftg==} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -845,6 +830,8 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@bedard/hexchess@2.5.0': {} + '@esbuild/aix-ppc64@0.25.12': optional: true @@ -1090,11 +1077,11 @@ snapshots: dependencies: undici-types: 6.21.0 - '@vitejs/plugin-vue@6.0.2(vite@6.4.1(@types/node@22.19.1))(vue@3.6.0-alpha.4(typescript@5.9.3))': + '@vitejs/plugin-vue@6.0.2(vite@6.4.1(@types/node@22.19.1))(vue@3.5.24(typescript@5.9.3))': dependencies: '@rolldown/pluginutils': 1.0.0-beta.50 vite: 6.4.1(@types/node@22.19.1) - vue: 3.6.0-alpha.4(typescript@5.9.3) + vue: 3.5.24(typescript@5.9.3) '@volar/language-core@2.4.23': dependencies: @@ -1116,49 +1103,27 @@ snapshots: estree-walker: 2.0.2 source-map-js: 1.2.1 - '@vue/compiler-core@3.6.0-alpha.4': - dependencies: - '@babel/parser': 7.28.5 - '@vue/shared': 3.6.0-alpha.4 - entities: 4.5.0 - estree-walker: 2.0.2 - source-map-js: 1.2.1 - '@vue/compiler-dom@3.5.24': dependencies: '@vue/compiler-core': 3.5.24 '@vue/shared': 3.5.24 - '@vue/compiler-dom@3.6.0-alpha.4': - dependencies: - '@vue/compiler-core': 3.6.0-alpha.4 - '@vue/shared': 3.6.0-alpha.4 - - '@vue/compiler-sfc@3.6.0-alpha.4': + '@vue/compiler-sfc@3.5.24': dependencies: '@babel/parser': 7.28.5 - '@vue/compiler-core': 3.6.0-alpha.4 - '@vue/compiler-dom': 3.6.0-alpha.4 - '@vue/compiler-ssr': 3.6.0-alpha.4 - '@vue/compiler-vapor': 3.6.0-alpha.4 - '@vue/shared': 3.6.0-alpha.4 + '@vue/compiler-core': 3.5.24 + '@vue/compiler-dom': 3.5.24 + '@vue/compiler-ssr': 3.5.24 + '@vue/shared': 3.5.24 estree-walker: 2.0.2 magic-string: 0.30.21 postcss: 8.5.6 source-map-js: 1.2.1 - '@vue/compiler-ssr@3.6.0-alpha.4': - dependencies: - '@vue/compiler-dom': 3.6.0-alpha.4 - '@vue/shared': 3.6.0-alpha.4 - - '@vue/compiler-vapor@3.6.0-alpha.4': + '@vue/compiler-ssr@3.5.24': dependencies: - '@babel/parser': 7.28.5 - '@vue/compiler-dom': 3.6.0-alpha.4 - '@vue/shared': 3.6.0-alpha.4 - estree-walker: 2.0.2 - source-map-js: 1.2.1 + '@vue/compiler-dom': 3.5.24 + '@vue/shared': 3.5.24 '@vue/compiler-vue2@2.7.16': dependencies: @@ -1190,38 +1155,30 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@vue/reactivity@3.6.0-alpha.4': + '@vue/reactivity@3.5.24': dependencies: - '@vue/shared': 3.6.0-alpha.4 + '@vue/shared': 3.5.24 - '@vue/runtime-core@3.6.0-alpha.4': + '@vue/runtime-core@3.5.24': dependencies: - '@vue/reactivity': 3.6.0-alpha.4 - '@vue/shared': 3.6.0-alpha.4 + '@vue/reactivity': 3.5.24 + '@vue/shared': 3.5.24 - '@vue/runtime-dom@3.6.0-alpha.4': + '@vue/runtime-dom@3.5.24': dependencies: - '@vue/reactivity': 3.6.0-alpha.4 - '@vue/runtime-core': 3.6.0-alpha.4 - '@vue/shared': 3.6.0-alpha.4 + '@vue/reactivity': 3.5.24 + '@vue/runtime-core': 3.5.24 + '@vue/shared': 3.5.24 csstype: 3.2.3 - '@vue/runtime-vapor@3.6.0-alpha.4(@vue/runtime-dom@3.6.0-alpha.4)': - dependencies: - '@vue/reactivity': 3.6.0-alpha.4 - '@vue/runtime-dom': 3.6.0-alpha.4 - '@vue/shared': 3.6.0-alpha.4 - - '@vue/server-renderer@3.6.0-alpha.4(vue@3.6.0-alpha.4(typescript@5.9.3))': + '@vue/server-renderer@3.5.24(vue@3.5.24(typescript@5.9.3))': dependencies: - '@vue/compiler-ssr': 3.6.0-alpha.4 - '@vue/shared': 3.6.0-alpha.4 - vue: 3.6.0-alpha.4(typescript@5.9.3) + '@vue/compiler-ssr': 3.5.24 + '@vue/shared': 3.5.24 + vue: 3.5.24(typescript@5.9.3) '@vue/shared@3.5.24': {} - '@vue/shared@3.6.0-alpha.4': {} - acorn@8.15.0: {} ajv-draft-04@1.0.0(ajv@8.13.0): @@ -1539,14 +1496,13 @@ snapshots: '@vue/language-core': 3.1.4(typescript@5.9.3) typescript: 5.9.3 - vue@3.6.0-alpha.4(typescript@5.9.3): + vue@3.5.24(typescript@5.9.3): dependencies: - '@vue/compiler-dom': 3.6.0-alpha.4 - '@vue/compiler-sfc': 3.6.0-alpha.4 - '@vue/runtime-dom': 3.6.0-alpha.4 - '@vue/runtime-vapor': 3.6.0-alpha.4(@vue/runtime-dom@3.6.0-alpha.4) - '@vue/server-renderer': 3.6.0-alpha.4(vue@3.6.0-alpha.4(typescript@5.9.3)) - '@vue/shared': 3.6.0-alpha.4 + '@vue/compiler-dom': 3.5.24 + '@vue/compiler-sfc': 3.5.24 + '@vue/runtime-dom': 3.5.24 + '@vue/server-renderer': 3.5.24(vue@3.5.24(typescript@5.9.3)) + '@vue/shared': 3.5.24 optionalDependencies: typescript: 5.9.3 diff --git a/ui/src/App.vue b/ui/src/App.vue index f707f6f6..b6262e3d 100644 --- a/ui/src/App.vue +++ b/ui/src/App.vue @@ -1,61 +1,18 @@ \ No newline at end of file diff --git a/ui/src/lib/hexboard/Hexboard.vue b/ui/src/lib/hexboard/Hexboard.vue index a02532f6..10c4325f 100644 --- a/ui/src/lib/hexboard/Hexboard.vue +++ b/ui/src/lib/hexboard/Hexboard.vue @@ -3,46 +3,74 @@ - - - - - - - - - - - - - - - - + + + + + + + + + \ No newline at end of file diff --git a/ui/src/lib/index.ts b/ui/src/lib/index.ts index 5b355f3a..8071aa5e 100644 --- a/ui/src/lib/index.ts +++ b/ui/src/lib/index.ts @@ -1 +1,2 @@ +export { default as GiocoPieces } from './pieces/GiocoPieces.vue' export { default as Hexboard } from './hexboard/Hexboard.vue' diff --git a/ui/src/lib/pieces/GiocoPieces.vue b/ui/src/lib/pieces/GiocoPieces.vue new file mode 100644 index 00000000..4c1c98d9 --- /dev/null +++ b/ui/src/lib/pieces/GiocoPieces.vue @@ -0,0 +1,22 @@ + + + From 6ded079109e9f78f3967cc1732a9bea73d04781e Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Thu, 20 Nov 2025 19:22:14 -0600 Subject: [PATCH 08/80] cleanup --- ui/src/lib/hexboard/Hexboard.vue | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/ui/src/lib/hexboard/Hexboard.vue b/ui/src/lib/hexboard/Hexboard.vue index 10c4325f..db7bcd92 100644 --- a/ui/src/lib/hexboard/Hexboard.vue +++ b/ui/src/lib/hexboard/Hexboard.vue @@ -23,18 +23,18 @@ - - + + From 89ba699ceef3dc2b2f325a1898792c1333ef58a7 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Thu, 20 Nov 2025 19:32:19 -0600 Subject: [PATCH 09/80] install tailwind --- ui/index.html | 1 + ui/package.json | 1 + ui/pnpm-lock.yaml | 360 ++++++++++++++++++++++++++++++++++++++++++++-- ui/src/client.ts | 2 - ui/src/style.css | 1 + ui/vite.config.ts | 2 + 6 files changed, 357 insertions(+), 10 deletions(-) create mode 100644 ui/src/style.css diff --git a/ui/index.html b/ui/index.html index 9442a024..57c2c89d 100644 --- a/ui/index.html +++ b/ui/index.html @@ -4,6 +4,7 @@ Hexchess UI +
diff --git a/ui/package.json b/ui/package.json index 5b67e27a..1aaaa745 100644 --- a/ui/package.json +++ b/ui/package.json @@ -12,6 +12,7 @@ }, "devDependencies": { "@bedard/hexchess": "^2.5.0", + "@tailwindcss/vite": "^4.1.17", "@types/node": "^22.17.1", "@vitejs/plugin-vue": "^6.0.2", "typescript": "^5.9.2", diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index 56330795..2cb19796 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -11,21 +11,24 @@ importers: '@bedard/hexchess': specifier: ^2.5.0 version: 2.5.0 + '@tailwindcss/vite': + specifier: ^4.1.17 + version: 4.1.17(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)) '@types/node': specifier: ^22.17.1 version: 22.19.1 '@vitejs/plugin-vue': specifier: ^6.0.2 - version: 6.0.2(vite@6.4.1(@types/node@22.19.1))(vue@3.5.24(typescript@5.9.3)) + version: 6.0.2(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))(vue@3.5.24(typescript@5.9.3)) typescript: specifier: ^5.9.2 version: 5.9.3 vite: specifier: ^6.3.5 - version: 6.4.1(@types/node@22.19.1) + version: 6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2) vite-plugin-dts: specifier: ^4.5.4 - version: 4.5.4(@types/node@22.19.1)(rollup@4.53.2)(typescript@5.9.3)(vite@6.4.1(@types/node@22.19.1)) + version: 4.5.4(@types/node@22.19.1)(rollup@4.53.2)(typescript@5.9.3)(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)) vue: specifier: ^3.5.24 version: 3.5.24(typescript@5.9.3) @@ -219,9 +222,22 @@ packages: resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} engines: {node: 20 || >=22} + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@microsoft/api-extractor-model@7.32.0': resolution: {integrity: sha512-QIVJSreb8fGGJy1Qx0yzGVXxvHJN1WXgkFNHFheVv1iBJNqgvp+xeT3ienJmRwXmPPc5Es/cxBrXtKZJR3i7iw==} @@ -387,6 +403,96 @@ packages: '@rushstack/ts-command-line@5.1.3': resolution: {integrity: sha512-Kdv0k/BnnxIYFlMVC1IxrIS0oGQd4T4b7vKfx52Y2+wk2WZSDFIvedr7JrhenzSlm3ou5KwtoTGTGd5nbODRug==} + '@tailwindcss/node@4.1.17': + resolution: {integrity: sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==} + + '@tailwindcss/oxide-android-arm64@4.1.17': + resolution: {integrity: sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.1.17': + resolution: {integrity: sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.1.17': + resolution: {integrity: sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.1.17': + resolution: {integrity: sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.17': + resolution: {integrity: sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.17': + resolution: {integrity: sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.1.17': + resolution: {integrity: sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.1.17': + resolution: {integrity: sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.1.17': + resolution: {integrity: sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.1.17': + resolution: {integrity: sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.17': + resolution: {integrity: sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.1.17': + resolution: {integrity: sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.1.17': + resolution: {integrity: sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA==} + engines: {node: '>= 10'} + + '@tailwindcss/vite@4.1.17': + resolution: {integrity: sha512-4+9w8ZHOiGnpcGI6z1TVVfWaX/koK7fKeSYF3qlYg2xpBtbteP2ddBxiarL+HVgfSJGeK5RIxRQmKm4rTJJAwA==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 + '@types/argparse@1.0.38': resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} @@ -526,10 +632,18 @@ packages: supports-color: optional: true + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + diff@8.0.2: resolution: {integrity: sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==} engines: {node: '>=0.3.1'} + enhanced-resolve@5.18.3: + resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} + engines: {node: '>=10.13.0'} + entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -592,6 +706,10 @@ packages: resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} engines: {node: '>= 0.4'} + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + hasBin: true + jju@1.4.0: resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} @@ -604,6 +722,76 @@ packages: kolorist@1.8.0: resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==} + lightningcss-android-arm64@1.30.2: + resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.30.2: + resolution: {integrity: sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.30.2: + resolution: {integrity: sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.30.2: + resolution: {integrity: sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.30.2: + resolution: {integrity: sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.30.2: + resolution: {integrity: sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.30.2: + resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.30.2: + resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.30.2: + resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.30.2: + resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.30.2: + resolution: {integrity: sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.30.2: + resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==} + engines: {node: '>= 12.0.0'} + local-pkg@1.1.2: resolution: {integrity: sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==} engines: {node: '>=14'} @@ -719,6 +907,13 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + tailwindcss@4.1.17: + resolution: {integrity: sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==} + + tapable@2.3.0: + resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} + engines: {node: '>=6'} + tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} @@ -916,8 +1111,25 @@ snapshots: dependencies: '@isaacs/balanced-match': 4.0.1 + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + '@microsoft/api-extractor-model@7.32.0(@types/node@22.19.1)': dependencies: '@microsoft/tsdoc': 0.16.0 @@ -1069,6 +1281,74 @@ snapshots: transitivePeerDependencies: - '@types/node' + '@tailwindcss/node@4.1.17': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.18.3 + jiti: 2.6.1 + lightningcss: 1.30.2 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.1.17 + + '@tailwindcss/oxide-android-arm64@4.1.17': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.1.17': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.1.17': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.1.17': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.17': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.17': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.1.17': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.1.17': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.1.17': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.1.17': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.17': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.1.17': + optional: true + + '@tailwindcss/oxide@4.1.17': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.17 + '@tailwindcss/oxide-darwin-arm64': 4.1.17 + '@tailwindcss/oxide-darwin-x64': 4.1.17 + '@tailwindcss/oxide-freebsd-x64': 4.1.17 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.17 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.17 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.17 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.17 + '@tailwindcss/oxide-linux-x64-musl': 4.1.17 + '@tailwindcss/oxide-wasm32-wasi': 4.1.17 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.17 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.17 + + '@tailwindcss/vite@4.1.17(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))': + dependencies: + '@tailwindcss/node': 4.1.17 + '@tailwindcss/oxide': 4.1.17 + tailwindcss: 4.1.17 + vite: 6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2) + '@types/argparse@1.0.38': {} '@types/estree@1.0.8': {} @@ -1077,10 +1357,10 @@ snapshots: dependencies: undici-types: 6.21.0 - '@vitejs/plugin-vue@6.0.2(vite@6.4.1(@types/node@22.19.1))(vue@3.5.24(typescript@5.9.3))': + '@vitejs/plugin-vue@6.0.2(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))(vue@3.5.24(typescript@5.9.3))': dependencies: '@rolldown/pluginutils': 1.0.0-beta.50 - vite: 6.4.1(@types/node@22.19.1) + vite: 6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2) vue: 3.5.24(typescript@5.9.3) '@volar/language-core@2.4.23': @@ -1231,8 +1511,15 @@ snapshots: dependencies: ms: 2.1.3 + detect-libc@2.1.2: {} + diff@8.0.2: {} + enhanced-resolve@5.18.3: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.0 + entities@4.5.0: {} esbuild@0.25.12: @@ -1301,6 +1588,8 @@ snapshots: dependencies: hasown: 2.0.2 + jiti@2.6.1: {} + jju@1.4.0: {} json-schema-traverse@1.0.0: {} @@ -1313,6 +1602,55 @@ snapshots: kolorist@1.8.0: {} + lightningcss-android-arm64@1.30.2: + optional: true + + lightningcss-darwin-arm64@1.30.2: + optional: true + + lightningcss-darwin-x64@1.30.2: + optional: true + + lightningcss-freebsd-x64@1.30.2: + optional: true + + lightningcss-linux-arm-gnueabihf@1.30.2: + optional: true + + lightningcss-linux-arm64-gnu@1.30.2: + optional: true + + lightningcss-linux-arm64-musl@1.30.2: + optional: true + + lightningcss-linux-x64-gnu@1.30.2: + optional: true + + lightningcss-linux-x64-musl@1.30.2: + optional: true + + lightningcss-win32-arm64-msvc@1.30.2: + optional: true + + lightningcss-win32-x64-msvc@1.30.2: + optional: true + + lightningcss@1.30.2: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.30.2 + lightningcss-darwin-arm64: 1.30.2 + lightningcss-darwin-x64: 1.30.2 + lightningcss-freebsd-x64: 1.30.2 + lightningcss-linux-arm-gnueabihf: 1.30.2 + lightningcss-linux-arm64-gnu: 1.30.2 + lightningcss-linux-arm64-musl: 1.30.2 + lightningcss-linux-x64-gnu: 1.30.2 + lightningcss-linux-x64-musl: 1.30.2 + lightningcss-win32-arm64-msvc: 1.30.2 + lightningcss-win32-x64-msvc: 1.30.2 + local-pkg@1.1.2: dependencies: mlly: 1.8.0 @@ -1438,6 +1776,10 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + tailwindcss@4.1.17: {} + + tapable@2.3.0: {} + tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.3) @@ -1457,7 +1799,7 @@ snapshots: dependencies: punycode: 2.3.1 - vite-plugin-dts@4.5.4(@types/node@22.19.1)(rollup@4.53.2)(typescript@5.9.3)(vite@6.4.1(@types/node@22.19.1)): + vite-plugin-dts@4.5.4(@types/node@22.19.1)(rollup@4.53.2)(typescript@5.9.3)(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)): dependencies: '@microsoft/api-extractor': 7.55.0(@types/node@22.19.1) '@rollup/pluginutils': 5.3.0(rollup@4.53.2) @@ -1470,13 +1812,13 @@ snapshots: magic-string: 0.30.21 typescript: 5.9.3 optionalDependencies: - vite: 6.4.1(@types/node@22.19.1) + vite: 6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2) transitivePeerDependencies: - '@types/node' - rollup - supports-color - vite@6.4.1(@types/node@22.19.1): + vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2): dependencies: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) @@ -1487,6 +1829,8 @@ snapshots: optionalDependencies: '@types/node': 22.19.1 fsevents: 2.3.3 + jiti: 2.6.1 + lightningcss: 1.30.2 vscode-uri@3.1.0: {} diff --git a/ui/src/client.ts b/ui/src/client.ts index 716f720b..01433bca 100644 --- a/ui/src/client.ts +++ b/ui/src/client.ts @@ -2,5 +2,3 @@ import { createApp } from 'vue' import App from './App.vue' createApp(App).mount('#app') - -console.log('hey') \ No newline at end of file diff --git a/ui/src/style.css b/ui/src/style.css new file mode 100644 index 00000000..f1d8c73c --- /dev/null +++ b/ui/src/style.css @@ -0,0 +1 @@ +@import "tailwindcss"; diff --git a/ui/vite.config.ts b/ui/vite.config.ts index e3b462f3..1c59967e 100644 --- a/ui/vite.config.ts +++ b/ui/vite.config.ts @@ -2,6 +2,7 @@ import { defineConfig } from 'vite' import { fileURLToPath } from 'node:url' import { resolve } from 'node:path' import dts from 'vite-plugin-dts' +import tailwindcss from '@tailwindcss/vite' import vue from '@vitejs/plugin-vue' const __dirname = fileURLToPath(new URL('.', import.meta.url)) @@ -13,6 +14,7 @@ export default defineConfig({ outDir: 'dist', rollupTypes: true }), + tailwindcss(), vue({ template: { compilerOptions: { From 5b3713738c9e8b54e12e98f7e7acf7cfc9afde8c Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Thu, 20 Nov 2025 19:53:00 -0600 Subject: [PATCH 10/80] fix flipped --- ui/src/App.vue | 9 +++++-- ui/src/components/Checkbox.vue | 42 ++++++++++++++++++++++++++++++++ ui/src/components/index.ts | 1 + ui/src/lib/hexboard/Hexboard.vue | 4 +-- 4 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 ui/src/components/Checkbox.vue create mode 100644 ui/src/components/index.ts diff --git a/ui/src/App.vue b/ui/src/App.vue index b6262e3d..05440e36 100644 --- a/ui/src/App.vue +++ b/ui/src/App.vue @@ -1,9 +1,13 @@ \ No newline at end of file diff --git a/ui/src/components/index.ts b/ui/src/components/index.ts new file mode 100644 index 00000000..8c28c286 --- /dev/null +++ b/ui/src/components/index.ts @@ -0,0 +1 @@ +export { default as Checkbox } from './Checkbox.vue' diff --git a/ui/src/lib/hexboard/Hexboard.vue b/ui/src/lib/hexboard/Hexboard.vue index db7bcd92..8c73f0a5 100644 --- a/ui/src/lib/hexboard/Hexboard.vue +++ b/ui/src/lib/hexboard/Hexboard.vue @@ -32,8 +32,8 @@ :height="pieceSize" :type="piece" :width="pieceSize" - :x="x(board[index][flipped ? 1 : 2][0] - (pieceSize / 2))" - :y="y(board[index][flipped ? 1 : 2][1] + (pieceSize / 2))" + :x="x(board[index][flipped ? 2 : 1][0] - (pieceSize / 2))" + :y="y(board[index][flipped ? 2 : 1][1] + (pieceSize / 2))" />
From 4a24c3a4aa647b2112c3b657638081adebb1b968 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Thu, 20 Nov 2025 20:14:23 -0600 Subject: [PATCH 11/80] alpha --- ui/package.json | 5 + ui/pnpm-lock.yaml | 715 ++++++++++++++++++++++++++++++ ui/src/App.vue | 9 +- ui/src/lib/index.ts | 1 + ui/src/lib/pieces/AlphaPieces.vue | 23 + ui/vitest.config.ts | 37 ++ 6 files changed, 788 insertions(+), 2 deletions(-) create mode 100644 ui/src/lib/pieces/AlphaPieces.vue create mode 100644 ui/vitest.config.ts diff --git a/ui/package.json b/ui/package.json index 1aaaa745..7c94393e 100644 --- a/ui/package.json +++ b/ui/package.json @@ -15,9 +15,14 @@ "@tailwindcss/vite": "^4.1.17", "@types/node": "^22.17.1", "@vitejs/plugin-vue": "^6.0.2", + "@vitest/browser": "^4.0.12", + "@vitest/ui": "^4.0.12", + "@vue/test-utils": "^2.4.6", + "playwright": "^1.56.1", "typescript": "^5.9.2", "vite": "^6.3.5", "vite-plugin-dts": "^4.5.4", + "vitest": "^4.0.12", "vue": "^3.5.24", "vue-tsc": "^3.1.4" } diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index 2cb19796..b2b05c98 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -20,6 +20,18 @@ importers: '@vitejs/plugin-vue': specifier: ^6.0.2 version: 6.0.2(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))(vue@3.5.24(typescript@5.9.3)) + '@vitest/browser': + specifier: ^4.0.12 + version: 4.0.12(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))(vitest@4.0.12(@types/node@22.19.1)(@vitest/ui@4.0.12)(jiti@2.6.1)(lightningcss@1.30.2)) + '@vitest/ui': + specifier: ^4.0.12 + version: 4.0.12(vitest@4.0.12) + '@vue/test-utils': + specifier: ^2.4.6 + version: 2.4.6 + playwright: + specifier: ^1.56.1 + version: 1.56.1 typescript: specifier: ^5.9.2 version: 5.9.3 @@ -29,6 +41,9 @@ importers: vite-plugin-dts: specifier: ^4.5.4 version: 4.5.4(@types/node@22.19.1)(rollup@4.53.2)(typescript@5.9.3)(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)) + vitest: + specifier: ^4.0.12 + version: 4.0.12(@types/node@22.19.1)(@vitest/ui@4.0.12)(jiti@2.6.1)(lightningcss@1.30.2) vue: specifier: ^3.5.24 version: 3.5.24(typescript@5.9.3) @@ -222,6 +237,10 @@ packages: resolution: {integrity: sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==} engines: {node: 20 || >=22} + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} @@ -251,6 +270,16 @@ packages: '@microsoft/tsdoc@0.16.0': resolution: {integrity: sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==} + '@one-ini/wasm@0.1.1': + resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + '@rolldown/pluginutils@1.0.0-beta.50': resolution: {integrity: sha512-5e76wQiQVeL1ICOZVUg4LSOVYg9jyhGCin+icYozhsUzM+fHE7kddi1bdiE0jwVqTfkjba3jUFbEkoC9WkdvyA==} @@ -403,6 +432,9 @@ packages: '@rushstack/ts-command-line@5.1.3': resolution: {integrity: sha512-Kdv0k/BnnxIYFlMVC1IxrIS0oGQd4T4b7vKfx52Y2+wk2WZSDFIvedr7JrhenzSlm3ou5KwtoTGTGd5nbODRug==} + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + '@tailwindcss/node@4.1.17': resolution: {integrity: sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==} @@ -496,6 +528,12 @@ packages: '@types/argparse@1.0.38': resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} @@ -509,6 +547,45 @@ packages: vite: ^5.0.0 || ^6.0.0 || ^7.0.0 vue: ^3.2.25 + '@vitest/browser@4.0.12': + resolution: {integrity: sha512-8zE2ksJ7V4B7Mc++L6rBRZOZHnE/f9URvj7oLYKIS5wcDaSi6EhfalN0EG6+R/OlTYZarbK6RqmhKDLYNC9KfQ==} + peerDependencies: + vitest: 4.0.12 + + '@vitest/expect@4.0.12': + resolution: {integrity: sha512-is+g0w8V3/ZhRNrRizrJNr8PFQKwYmctWlU4qg8zy5r9aIV5w8IxXLlfbbxJCwSpsVl2PXPTm2/zruqTqz3QSg==} + + '@vitest/mocker@4.0.12': + resolution: {integrity: sha512-GsmA/tD5Ht3RUFoz41mZsMU1AXch3lhmgbTnoSPTdH231g7S3ytNN1aU0bZDSyxWs8WA7KDyMPD5L4q6V6vj9w==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.0.12': + resolution: {integrity: sha512-R7nMAcnienG17MvRN8TPMJiCG8rrZJblV9mhT7oMFdBXvS0x+QD6S1G4DxFusR2E0QIS73f7DqSR1n87rrmE+g==} + + '@vitest/runner@4.0.12': + resolution: {integrity: sha512-hDlCIJWuwlcLumfukPsNfPDOJokTv79hnOlf11V+n7E14rHNPz0Sp/BO6h8sh9qw4/UjZiKyYpVxK2ZNi+3ceQ==} + + '@vitest/snapshot@4.0.12': + resolution: {integrity: sha512-2jz9zAuBDUSbnfyixnyOd1S2YDBrZO23rt1bicAb6MA/ya5rHdKFRikPIDpBj/Dwvh6cbImDmudegnDAkHvmRQ==} + + '@vitest/spy@4.0.12': + resolution: {integrity: sha512-GZjI9PPhiOYNX8Nsyqdw7JQB+u0BptL5fSnXiottAUBHlcMzgADV58A7SLTXXQwcN1yZ6gfd1DH+2bqjuUlCzw==} + + '@vitest/ui@4.0.12': + resolution: {integrity: sha512-RCqeApCnbwd5IFvxk6OeKMXTvzHU/cVqY8HAW0gWk0yAO6wXwQJMKhDfDtk2ss7JCy9u7RNC3kyazwiaDhBA/g==} + peerDependencies: + vitest: 4.0.12 + + '@vitest/utils@4.0.12': + resolution: {integrity: sha512-DVS/TLkLdvGvj1avRy0LSmKfrcI9MNFvNGN6ECjTUHWJdlcgPDOXhjMis5Dh7rBH62nAmSXnkPbE+DZ5YD75Rw==} + '@volar/language-core@2.4.23': resolution: {integrity: sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==} @@ -566,6 +643,13 @@ packages: '@vue/shared@3.5.24': resolution: {integrity: sha512-9cwHL2EsJBdi8NY22pngYYWzkTDhld6fAD6jlaeloNGciNSJL6bLpbxVgXl96X00Jtc6YWQv96YA/0sxex/k1A==} + '@vue/test-utils@2.4.6': + resolution: {integrity: sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==} + + abbrev@2.0.0: + resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + acorn@8.15.0: resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} engines: {node: '>=0.4.0'} @@ -599,15 +683,50 @@ packages: alien-signals@3.1.0: resolution: {integrity: sha512-yufC6VpSy8tK3I0lO67pjumo5JvDQVQyr38+3OHqe6CHl1t2VZekKZ7EKKZSqk0cRmE7U7tfZbpXiKNzuc+ckg==} + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} + engines: {node: '>=12'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + argparse@1.0.10: resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} brace-expansion@2.0.2: resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + chai@6.2.1: + resolution: {integrity: sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==} + engines: {node: '>=18'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + compare-versions@6.1.1: resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==} @@ -617,6 +736,13 @@ packages: confbox@0.2.2: resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} + config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} @@ -640,6 +766,20 @@ packages: resolution: {integrity: sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==} engines: {node: '>=0.3.1'} + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + editorconfig@1.0.4: + resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==} + engines: {node: '>=14'} + hasBin: true + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + enhanced-resolve@5.18.3: resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} engines: {node: '>=10.13.0'} @@ -648,6 +788,9 @@ packages: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + esbuild@0.25.12: resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} engines: {node: '>=18'} @@ -656,6 +799,13 @@ packages: estree-walker@2.0.2: resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + expect-type@1.2.2: + resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} + engines: {node: '>=12.0.0'} + exsolve@1.0.8: resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} @@ -671,10 +821,25 @@ packages: picomatch: optional: true + fflate@0.8.2: + resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + fs-extra@11.3.2: resolution: {integrity: sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==} engines: {node: '>=14.14'} + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -683,6 +848,10 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + glob@10.5.0: + resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} + hasBin: true + graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -702,10 +871,23 @@ packages: resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} engines: {node: '>=8'} + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + is-core-module@2.16.1: resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} engines: {node: '>= 0.4'} + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jiti@2.6.1: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true @@ -713,6 +895,15 @@ packages: jju@1.4.0: resolution: {integrity: sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==} + js-beautify@1.15.4: + resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==} + engines: {node: '>=14'} + hasBin: true + + js-cookie@3.0.5: + resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} + engines: {node: '>=14'} + json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} @@ -799,6 +990,9 @@ packages: lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} @@ -810,13 +1004,25 @@ packages: resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==} engines: {node: 20 || >=22} + minimatch@9.0.1: + resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==} + engines: {node: '>=16 || 14 >=14.17'} + minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + mlly@1.8.0: resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -828,12 +1034,28 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nopt@7.2.1: + resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -844,16 +1066,37 @@ packages: resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} engines: {node: '>=12'} + pixelmatch@7.1.0: + resolution: {integrity: sha512-1wrVzJ2STrpmONHKBy228LM1b84msXDUoAzVEl0R8Mz4Ce6EPr+IVtxm8+yvrqLYMHswREkjYFaMxnyGnaY3Ng==} + hasBin: true + pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} pkg-types@2.3.0: resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==} + playwright-core@1.56.1: + resolution: {integrity: sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.56.1: + resolution: {integrity: sha512-aFi5B0WovBHTEvpM3DzXTUaeN6eN0qWnTkKx4NQaH4Wvcmc153PdaY2UBdSYKaGYw+UyWXSVyxDUg5DoPEttjw==} + engines: {node: '>=18'} + hasBin: true + + pngjs@7.0.0: + resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} + engines: {node: '>=14.19.0'} + postcss@8.5.6: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} + proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -880,6 +1123,25 @@ packages: engines: {node: '>=10'} hasBin: true + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + sirv@3.0.2: + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} + engines: {node: '>=18'} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -891,10 +1153,32 @@ packages: sprintf-js@1.0.3: resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + string-argv@0.3.2: resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} engines: {node: '>=0.6.19'} + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} + engines: {node: '>=12'} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -914,10 +1198,24 @@ packages: resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} engines: {node: '>=6'} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + tinyglobby@0.2.15: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} + tinyrainbow@3.0.3: + resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} + engines: {node: '>=14.0.0'} + + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + typescript@5.8.2: resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==} engines: {node: '>=14.17'} @@ -990,9 +1288,49 @@ packages: yaml: optional: true + vitest@4.0.12: + resolution: {integrity: sha512-pmW4GCKQ8t5Ko1jYjC3SqOr7TUKN7uHOHB/XGsAIb69eYu6d1ionGSsb5H9chmPf+WeXt0VE7jTXsB1IvWoNbw==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/debug': ^4.1.12 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.0.12 + '@vitest/browser-preview': 4.0.12 + '@vitest/browser-webdriverio': 4.0.12 + '@vitest/ui': 4.0.12 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + vscode-uri@3.1.0: resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} + vue-component-type-helpers@2.2.12: + resolution: {integrity: sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==} + vue-tsc@3.1.4: resolution: {integrity: sha512-GsRJxttj4WkmXW/zDwYPGMJAN3np/4jTzoDFQTpTsI5Vg/JKMWamBwamlmLihgSVHO66y9P7GX+uoliYxeI4Hw==} hasBin: true @@ -1007,6 +1345,36 @@ packages: typescript: optional: true + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} @@ -1111,6 +1479,15 @@ snapshots: dependencies: '@isaacs/balanced-match': 4.0.1 + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.2 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -1166,6 +1543,13 @@ snapshots: '@microsoft/tsdoc@0.16.0': {} + '@one-ini/wasm@0.1.1': {} + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@polka/url@1.0.0-next.29': {} + '@rolldown/pluginutils@1.0.0-beta.50': {} '@rollup/pluginutils@5.3.0(rollup@4.53.2)': @@ -1281,6 +1665,8 @@ snapshots: transitivePeerDependencies: - '@types/node' + '@standard-schema/spec@1.0.0': {} + '@tailwindcss/node@4.1.17': dependencies: '@jridgewell/remapping': 2.3.5 @@ -1351,6 +1737,13 @@ snapshots: '@types/argparse@1.0.38': {} + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/deep-eql@4.0.2': {} + '@types/estree@1.0.8': {} '@types/node@22.19.1': @@ -1363,6 +1756,73 @@ snapshots: vite: 6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2) vue: 3.5.24(typescript@5.9.3) + '@vitest/browser@4.0.12(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))(vitest@4.0.12(@types/node@22.19.1)(@vitest/ui@4.0.12)(jiti@2.6.1)(lightningcss@1.30.2))': + dependencies: + '@vitest/mocker': 4.0.12(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)) + '@vitest/utils': 4.0.12 + magic-string: 0.30.21 + pixelmatch: 7.1.0 + pngjs: 7.0.0 + sirv: 3.0.2 + tinyrainbow: 3.0.3 + vitest: 4.0.12(@types/node@22.19.1)(@vitest/ui@4.0.12)(jiti@2.6.1)(lightningcss@1.30.2) + ws: 8.18.3 + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + + '@vitest/expect@4.0.12': + dependencies: + '@standard-schema/spec': 1.0.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.0.12 + '@vitest/utils': 4.0.12 + chai: 6.2.1 + tinyrainbow: 3.0.3 + + '@vitest/mocker@4.0.12(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))': + dependencies: + '@vitest/spy': 4.0.12 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2) + + '@vitest/pretty-format@4.0.12': + dependencies: + tinyrainbow: 3.0.3 + + '@vitest/runner@4.0.12': + dependencies: + '@vitest/utils': 4.0.12 + pathe: 2.0.3 + + '@vitest/snapshot@4.0.12': + dependencies: + '@vitest/pretty-format': 4.0.12 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.0.12': {} + + '@vitest/ui@4.0.12(vitest@4.0.12)': + dependencies: + '@vitest/utils': 4.0.12 + fflate: 0.8.2 + flatted: 3.3.3 + pathe: 2.0.3 + sirv: 3.0.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.0.3 + vitest: 4.0.12(@types/node@22.19.1)(@vitest/ui@4.0.12)(jiti@2.6.1)(lightningcss@1.30.2) + + '@vitest/utils@4.0.12': + dependencies: + '@vitest/pretty-format': 4.0.12 + tinyrainbow: 3.0.3 + '@volar/language-core@2.4.23': dependencies: '@volar/source-map': 2.4.23 @@ -1459,6 +1919,13 @@ snapshots: '@vue/shared@3.5.24': {} + '@vue/test-utils@2.4.6': + dependencies: + js-beautify: 1.15.4 + vue-component-type-helpers: 2.2.12 + + abbrev@2.0.0: {} + acorn@8.15.0: {} ajv-draft-04@1.0.0(ajv@8.13.0): @@ -1487,22 +1954,55 @@ snapshots: alien-signals@3.1.0: {} + ansi-regex@5.0.1: {} + + ansi-regex@6.2.2: {} + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.3: {} + argparse@1.0.10: dependencies: sprintf-js: 1.0.3 + assertion-error@2.0.1: {} + balanced-match@1.0.2: {} brace-expansion@2.0.2: dependencies: balanced-match: 1.0.2 + chai@6.2.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + commander@10.0.1: {} + compare-versions@6.1.1: {} confbox@0.1.8: {} confbox@0.2.2: {} + config-chain@1.1.13: + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + csstype@3.2.3: {} de-indent@1.0.2: {} @@ -1515,6 +2015,19 @@ snapshots: diff@8.0.2: {} + eastasianwidth@0.2.0: {} + + editorconfig@1.0.4: + dependencies: + '@one-ini/wasm': 0.1.1 + commander: 10.0.1 + minimatch: 9.0.1 + semver: 7.5.4 + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + enhanced-resolve@5.18.3: dependencies: graceful-fs: 4.2.11 @@ -1522,6 +2035,8 @@ snapshots: entities@4.5.0: {} + es-module-lexer@1.7.0: {} + esbuild@0.25.12: optionalDependencies: '@esbuild/aix-ppc64': 0.25.12 @@ -1553,6 +2068,12 @@ snapshots: estree-walker@2.0.2: {} + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + expect-type@1.2.2: {} + exsolve@1.0.8: {} fast-deep-equal@3.1.3: {} @@ -1561,17 +2082,38 @@ snapshots: optionalDependencies: picomatch: 4.0.3 + fflate@0.8.2: {} + + flatted@3.3.3: {} + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + fs-extra@11.3.2: dependencies: graceful-fs: 4.2.11 jsonfile: 6.2.0 universalify: 2.0.1 + fsevents@2.3.2: + optional: true + fsevents@2.3.3: optional: true function-bind@1.1.2: {} + glob@10.5.0: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + graceful-fs@4.2.11: {} has-flag@4.0.0: {} @@ -1584,14 +2126,36 @@ snapshots: import-lazy@4.0.0: {} + ini@1.3.8: {} + is-core-module@2.16.1: dependencies: hasown: 2.0.2 + is-fullwidth-code-point@3.0.0: {} + + isexe@2.0.0: {} + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + jiti@2.6.1: {} jju@1.4.0: {} + js-beautify@1.15.4: + dependencies: + config-chain: 1.1.13 + editorconfig: 1.0.4 + glob: 10.5.0 + js-cookie: 3.0.5 + nopt: 7.2.1 + + js-cookie@3.0.5: {} + json-schema-traverse@1.0.0: {} jsonfile@6.2.0: @@ -1659,6 +2223,8 @@ snapshots: lodash@4.17.21: {} + lru-cache@10.4.3: {} + lru-cache@6.0.0: dependencies: yallist: 4.0.0 @@ -1671,10 +2237,16 @@ snapshots: dependencies: '@isaacs/brace-expansion': 5.0.0 + minimatch@9.0.1: + dependencies: + brace-expansion: 2.0.2 + minimatch@9.0.5: dependencies: brace-expansion: 2.0.2 + minipass@7.1.2: {} + mlly@1.8.0: dependencies: acorn: 8.15.0 @@ -1682,22 +2254,41 @@ snapshots: pkg-types: 1.3.1 ufo: 1.6.1 + mrmime@2.0.1: {} + ms@2.1.3: {} muggle-string@0.4.1: {} nanoid@3.3.11: {} + nopt@7.2.1: + dependencies: + abbrev: 2.0.0 + + package-json-from-dist@1.0.1: {} + path-browserify@1.0.1: {} + path-key@3.1.1: {} + path-parse@1.0.7: {} + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + pathe@2.0.3: {} picocolors@1.1.1: {} picomatch@4.0.3: {} + pixelmatch@7.1.0: + dependencies: + pngjs: 7.0.0 + pkg-types@1.3.1: dependencies: confbox: 0.1.8 @@ -1710,12 +2301,24 @@ snapshots: exsolve: 1.0.8 pathe: 2.0.3 + playwright-core@1.56.1: {} + + playwright@1.56.1: + dependencies: + playwright-core: 1.56.1 + optionalDependencies: + fsevents: 2.3.2 + + pngjs@7.0.0: {} + postcss@8.5.6: dependencies: nanoid: 3.3.11 picocolors: 1.1.1 source-map-js: 1.2.1 + proto-list@1.2.4: {} + punycode@2.3.1: {} quansync@0.2.11: {} @@ -1760,14 +2363,54 @@ snapshots: dependencies: lru-cache: 6.0.0 + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + siginfo@2.0.0: {} + + signal-exit@4.1.0: {} + + sirv@3.0.2: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + source-map-js@1.2.1: {} source-map@0.6.1: {} sprintf-js@1.0.3: {} + stackback@0.0.2: {} + + std-env@3.10.0: {} + string-argv@0.3.2: {} + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.2 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.2: + dependencies: + ansi-regex: 6.2.2 + strip-json-comments@3.1.1: {} supports-color@8.1.1: @@ -1780,11 +2423,19 @@ snapshots: tapable@2.3.0: {} + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + tinyglobby@0.2.15: dependencies: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 + tinyrainbow@3.0.3: {} + + totalist@3.0.1: {} + typescript@5.8.2: {} typescript@5.9.3: {} @@ -1832,8 +2483,49 @@ snapshots: jiti: 2.6.1 lightningcss: 1.30.2 + vitest@4.0.12(@types/node@22.19.1)(@vitest/ui@4.0.12)(jiti@2.6.1)(lightningcss@1.30.2): + dependencies: + '@vitest/expect': 4.0.12 + '@vitest/mocker': 4.0.12(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)) + '@vitest/pretty-format': 4.0.12 + '@vitest/runner': 4.0.12 + '@vitest/snapshot': 4.0.12 + '@vitest/spy': 4.0.12 + '@vitest/utils': 4.0.12 + debug: 4.4.3 + es-module-lexer: 1.7.0 + expect-type: 1.2.2 + magic-string: 0.30.21 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.0.3 + vite: 6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.19.1 + '@vitest/ui': 4.0.12(vitest@4.0.12) + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + vscode-uri@3.1.0: {} + vue-component-type-helpers@2.2.12: {} + vue-tsc@3.1.4(typescript@5.9.3): dependencies: '@volar/typescript': 2.4.23 @@ -1850,4 +2542,27 @@ snapshots: optionalDependencies: typescript: 5.9.3 + which@2.0.2: + dependencies: + isexe: 2.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 5.1.2 + strip-ansi: 7.1.2 + + ws@8.18.3: {} + yallist@4.0.0: {} diff --git a/ui/src/App.vue b/ui/src/App.vue index 05440e36..10ff1fed 100644 --- a/ui/src/App.vue +++ b/ui/src/App.vue @@ -9,15 +9,20 @@ \ No newline at end of file diff --git a/ui/src/lib/index.ts b/ui/src/lib/index.ts index 8071aa5e..8e9b6039 100644 --- a/ui/src/lib/index.ts +++ b/ui/src/lib/index.ts @@ -1,2 +1,3 @@ +export { default as AlphaPieces } from './pieces/AlphaPieces.vue' export { default as GiocoPieces } from './pieces/GiocoPieces.vue' export { default as Hexboard } from './hexboard/Hexboard.vue' diff --git a/ui/src/lib/pieces/AlphaPieces.vue b/ui/src/lib/pieces/AlphaPieces.vue new file mode 100644 index 00000000..619b92ca --- /dev/null +++ b/ui/src/lib/pieces/AlphaPieces.vue @@ -0,0 +1,23 @@ + + + + diff --git a/ui/vitest.config.ts b/ui/vitest.config.ts new file mode 100644 index 00000000..3f18c2fb --- /dev/null +++ b/ui/vitest.config.ts @@ -0,0 +1,37 @@ +import { defineConfig } from 'vitest/config' +import { fileURLToPath } from 'node:url' +import { resolve } from 'node:path' +import vue from '@vitejs/plugin-vue' +import tailwindcss from '@tailwindcss/vite' + +const __dirname = fileURLToPath(new URL('.', import.meta.url)) + +export default defineConfig({ + plugins: [ + tailwindcss(), + vue({ + template: { + compilerOptions: { + isCustomElement: tag => ['hexchess-board'].includes(tag) + } + } + }), + ], + test: { + browser: { + enabled: true, + name: 'chromium', + provider: 'playwright', + headless: true, + }, + environment: 'jsdom', + globals: true, + setupFiles: ['./src/test/setup.ts'], + }, + resolve: { + alias: { + '@': resolve(__dirname, './src'), + }, + }, +}) + From 04c7563d2056f3e44c47c12c85b0c471c2a0b596 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Thu, 20 Nov 2025 20:38:11 -0600 Subject: [PATCH 12/80] combobox --- ui/package.json | 2 + ui/pnpm-lock.yaml | 41 +++++++++++++++++++ ui/src/App.vue | 27 +++++++++---- ui/src/components/Combobox.vue | 74 ++++++++++++++++++++++++++++++++++ ui/src/components/index.ts | 1 + ui/src/lib/index.ts | 4 +- 6 files changed, 140 insertions(+), 9 deletions(-) create mode 100644 ui/src/components/Combobox.vue diff --git a/ui/package.json b/ui/package.json index 7c94393e..65bda67b 100644 --- a/ui/package.json +++ b/ui/package.json @@ -12,6 +12,8 @@ }, "devDependencies": { "@bedard/hexchess": "^2.5.0", + "@headlessui/vue": "^1.7.23", + "@heroicons/vue": "^2.2.0", "@tailwindcss/vite": "^4.1.17", "@types/node": "^22.17.1", "@vitejs/plugin-vue": "^6.0.2", diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index b2b05c98..0a12faa9 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -11,6 +11,12 @@ importers: '@bedard/hexchess': specifier: ^2.5.0 version: 2.5.0 + '@headlessui/vue': + specifier: ^1.7.23 + version: 1.7.23(vue@3.5.24(typescript@5.9.3)) + '@heroicons/vue': + specifier: ^2.2.0 + version: 2.2.0(vue@3.5.24(typescript@5.9.3)) '@tailwindcss/vite': specifier: ^4.1.17 version: 4.1.17(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)) @@ -229,6 +235,17 @@ packages: cpu: [x64] os: [win32] + '@headlessui/vue@1.7.23': + resolution: {integrity: sha512-JzdCNqurrtuu0YW6QaDtR2PIYCKPUWq28csDyMvN4zmGccmE7lz40Is6hc3LA4HFeCI7sekZ/PQMTNmn9I/4Wg==} + engines: {node: '>=10'} + peerDependencies: + vue: ^3.2.0 + + '@heroicons/vue@2.2.0': + resolution: {integrity: sha512-G3dbSxoeEKqbi/DFalhRxJU4mTXJn7GwZ7ae8NuEQzd1bqdd0jAbdaBZlHPcvPD2xI1iGzNVB4k20Un2AguYPw==} + peerDependencies: + vue: '>= 3' + '@isaacs/balanced-match@4.0.1': resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} engines: {node: 20 || >=22} @@ -525,6 +542,14 @@ packages: peerDependencies: vite: ^5.2.0 || ^6 || ^7 + '@tanstack/virtual-core@3.13.12': + resolution: {integrity: sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==} + + '@tanstack/vue-virtual@3.13.12': + resolution: {integrity: sha512-vhF7kEU9EXWXh+HdAwKJ2m3xaOnTTmgcdXcF2pim8g4GvI7eRrk2YRuV5nUlZnd/NbCIX4/Ja2OZu5EjJL06Ww==} + peerDependencies: + vue: ^2.7.0 || ^3.0.0 + '@types/argparse@1.0.38': resolution: {integrity: sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==} @@ -1473,6 +1498,15 @@ snapshots: '@esbuild/win32-x64@0.25.12': optional: true + '@headlessui/vue@1.7.23(vue@3.5.24(typescript@5.9.3))': + dependencies: + '@tanstack/vue-virtual': 3.13.12(vue@3.5.24(typescript@5.9.3)) + vue: 3.5.24(typescript@5.9.3) + + '@heroicons/vue@2.2.0(vue@3.5.24(typescript@5.9.3))': + dependencies: + vue: 3.5.24(typescript@5.9.3) + '@isaacs/balanced-match@4.0.1': {} '@isaacs/brace-expansion@5.0.0': @@ -1735,6 +1769,13 @@ snapshots: tailwindcss: 4.1.17 vite: 6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2) + '@tanstack/virtual-core@3.13.12': {} + + '@tanstack/vue-virtual@3.13.12(vue@3.5.24(typescript@5.9.3))': + dependencies: + '@tanstack/virtual-core': 3.13.12 + vue: 3.5.24(typescript@5.9.3) + '@types/argparse@1.0.38': {} '@types/chai@5.2.3': diff --git a/ui/src/App.vue b/ui/src/App.vue index 10ff1fed..8205c3d2 100644 --- a/ui/src/App.vue +++ b/ui/src/App.vue @@ -4,25 +4,36 @@ + + \ No newline at end of file diff --git a/ui/src/components/Combobox.vue b/ui/src/components/Combobox.vue new file mode 100644 index 00000000..8ab003a4 --- /dev/null +++ b/ui/src/components/Combobox.vue @@ -0,0 +1,74 @@ + + + diff --git a/ui/src/components/index.ts b/ui/src/components/index.ts index 8c28c286..b6029cf2 100644 --- a/ui/src/components/index.ts +++ b/ui/src/components/index.ts @@ -1 +1,2 @@ export { default as Checkbox } from './Checkbox.vue' +export { default as Combobox } from './Combobox.vue' \ No newline at end of file diff --git a/ui/src/lib/index.ts b/ui/src/lib/index.ts index 8e9b6039..d23f7647 100644 --- a/ui/src/lib/index.ts +++ b/ui/src/lib/index.ts @@ -1,3 +1,5 @@ export { default as AlphaPieces } from './pieces/AlphaPieces.vue' +export { default as AnarcandyPieces } from './pieces/AnarcandyPieces.vue' +export { default as CalientePieces } from './pieces/CalientePieces.vue' export { default as GiocoPieces } from './pieces/GiocoPieces.vue' -export { default as Hexboard } from './hexboard/Hexboard.vue' +export { default as Hexboard } from './hexboard/Hexboard.vue' \ No newline at end of file From 5159ffdefbdac1774d34040938f6da936c55319d Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Thu, 20 Nov 2025 20:41:20 -0600 Subject: [PATCH 13/80] fix fragments --- ui/src/lib/pieces/AnarcandyPieces.vue | 22 ++++++++++++++++++++++ ui/src/lib/pieces/CalientePieces.vue | 22 ++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 ui/src/lib/pieces/AnarcandyPieces.vue create mode 100644 ui/src/lib/pieces/CalientePieces.vue diff --git a/ui/src/lib/pieces/AnarcandyPieces.vue b/ui/src/lib/pieces/AnarcandyPieces.vue new file mode 100644 index 00000000..d1714a27 --- /dev/null +++ b/ui/src/lib/pieces/AnarcandyPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/CalientePieces.vue b/ui/src/lib/pieces/CalientePieces.vue new file mode 100644 index 00000000..883a7d61 --- /dev/null +++ b/ui/src/lib/pieces/CalientePieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file From b08ce44b42aa849d420d4c5fdab362f07ae37675 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Thu, 20 Nov 2025 20:46:57 -0600 Subject: [PATCH 14/80] more sets --- ui/src/App.vue | 5 ++++- ui/src/lib/index.ts | 3 +++ ui/src/lib/pieces/CaliforniaPieces.vue | 22 ++++++++++++++++++++++ ui/src/lib/pieces/CardinalPieces.vue | 22 ++++++++++++++++++++++ ui/src/lib/pieces/CburnettPieces.vue | 22 ++++++++++++++++++++++ 5 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 ui/src/lib/pieces/CaliforniaPieces.vue create mode 100644 ui/src/lib/pieces/CardinalPieces.vue create mode 100644 ui/src/lib/pieces/CburnettPieces.vue diff --git a/ui/src/App.vue b/ui/src/App.vue index 8205c3d2..3ff37b07 100644 --- a/ui/src/App.vue +++ b/ui/src/App.vue @@ -21,13 +21,16 @@ \ No newline at end of file diff --git a/ui/src/lib/pieces/CardinalPieces.vue b/ui/src/lib/pieces/CardinalPieces.vue new file mode 100644 index 00000000..2d43dcef --- /dev/null +++ b/ui/src/lib/pieces/CardinalPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/CburnettPieces.vue b/ui/src/lib/pieces/CburnettPieces.vue new file mode 100644 index 00000000..666f323e --- /dev/null +++ b/ui/src/lib/pieces/CburnettPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file From e823d943c2b73afd8d0cb7d5cd89452c722d51d8 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Thu, 20 Nov 2025 20:55:35 -0600 Subject: [PATCH 15/80] add piece sets --- ui/src/App.vue | 75 ++++++++++++++++++++++++- ui/src/lib/index.ts | 34 ++++++++++- ui/src/lib/pieces/CelticPieces.vue | 22 ++++++++ ui/src/lib/pieces/Chess7Pieces.vue | 22 ++++++++ ui/src/lib/pieces/ChessnutPieces.vue | 22 ++++++++ ui/src/lib/pieces/CompanionPieces.vue | 22 ++++++++ ui/src/lib/pieces/CookePieces.vue | 22 ++++++++ ui/src/lib/pieces/DisguisedPieces.vue | 22 ++++++++ ui/src/lib/pieces/DubrovnyPieces.vue | 22 ++++++++ ui/src/lib/pieces/FantasyPieces.vue | 22 ++++++++ ui/src/lib/pieces/FiriPieces.vue | 22 ++++++++ ui/src/lib/pieces/FrescaPieces.vue | 22 ++++++++ ui/src/lib/pieces/GovernorPieces.vue | 22 ++++++++ ui/src/lib/pieces/HorseyPieces.vue | 22 ++++++++ ui/src/lib/pieces/IcpiecesPieces.vue | 22 ++++++++ ui/src/lib/pieces/KiwenSuwiPieces.vue | 22 ++++++++ ui/src/lib/pieces/KosalPieces.vue | 22 ++++++++ ui/src/lib/pieces/LeipzigPieces.vue | 22 ++++++++ ui/src/lib/pieces/LetterPieces.vue | 22 ++++++++ ui/src/lib/pieces/MaestroPieces.vue | 22 ++++++++ ui/src/lib/pieces/MeridaPieces.vue | 22 ++++++++ ui/src/lib/pieces/MonarchyPieces.vue | 22 ++++++++ ui/src/lib/pieces/MonoPieces.vue | 22 ++++++++ ui/src/lib/pieces/MpchessPieces.vue | 22 ++++++++ ui/src/lib/pieces/PirouettiPieces.vue | 22 ++++++++ ui/src/lib/pieces/PixelPieces.vue | 22 ++++++++ ui/src/lib/pieces/ReillycraigPieces.vue | 22 ++++++++ ui/src/lib/pieces/RhosgfxPieces.vue | 22 ++++++++ ui/src/lib/pieces/RiohachaPieces.vue | 22 ++++++++ ui/src/lib/pieces/ShapesPieces.vue | 22 ++++++++ ui/src/lib/pieces/SpatialPieces.vue | 22 ++++++++ ui/src/lib/pieces/StauntyPieces.vue | 22 ++++++++ ui/src/lib/pieces/TatianaPieces.vue | 22 ++++++++ ui/src/lib/pieces/XkcdPieces.vue | 22 ++++++++ 34 files changed, 811 insertions(+), 2 deletions(-) create mode 100644 ui/src/lib/pieces/CelticPieces.vue create mode 100644 ui/src/lib/pieces/Chess7Pieces.vue create mode 100644 ui/src/lib/pieces/ChessnutPieces.vue create mode 100644 ui/src/lib/pieces/CompanionPieces.vue create mode 100644 ui/src/lib/pieces/CookePieces.vue create mode 100644 ui/src/lib/pieces/DisguisedPieces.vue create mode 100644 ui/src/lib/pieces/DubrovnyPieces.vue create mode 100644 ui/src/lib/pieces/FantasyPieces.vue create mode 100644 ui/src/lib/pieces/FiriPieces.vue create mode 100644 ui/src/lib/pieces/FrescaPieces.vue create mode 100644 ui/src/lib/pieces/GovernorPieces.vue create mode 100644 ui/src/lib/pieces/HorseyPieces.vue create mode 100644 ui/src/lib/pieces/IcpiecesPieces.vue create mode 100644 ui/src/lib/pieces/KiwenSuwiPieces.vue create mode 100644 ui/src/lib/pieces/KosalPieces.vue create mode 100644 ui/src/lib/pieces/LeipzigPieces.vue create mode 100644 ui/src/lib/pieces/LetterPieces.vue create mode 100644 ui/src/lib/pieces/MaestroPieces.vue create mode 100644 ui/src/lib/pieces/MeridaPieces.vue create mode 100644 ui/src/lib/pieces/MonarchyPieces.vue create mode 100644 ui/src/lib/pieces/MonoPieces.vue create mode 100644 ui/src/lib/pieces/MpchessPieces.vue create mode 100644 ui/src/lib/pieces/PirouettiPieces.vue create mode 100644 ui/src/lib/pieces/PixelPieces.vue create mode 100644 ui/src/lib/pieces/ReillycraigPieces.vue create mode 100644 ui/src/lib/pieces/RhosgfxPieces.vue create mode 100644 ui/src/lib/pieces/RiohachaPieces.vue create mode 100644 ui/src/lib/pieces/ShapesPieces.vue create mode 100644 ui/src/lib/pieces/SpatialPieces.vue create mode 100644 ui/src/lib/pieces/StauntyPieces.vue create mode 100644 ui/src/lib/pieces/TatianaPieces.vue create mode 100644 ui/src/lib/pieces/XkcdPieces.vue diff --git a/ui/src/App.vue b/ui/src/App.vue index 3ff37b07..1a93b435 100644 --- a/ui/src/App.vue +++ b/ui/src/App.vue @@ -21,7 +21,48 @@ \ No newline at end of file diff --git a/ui/src/lib/pieces/Chess7Pieces.vue b/ui/src/lib/pieces/Chess7Pieces.vue new file mode 100644 index 00000000..b4ad3086 --- /dev/null +++ b/ui/src/lib/pieces/Chess7Pieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/ChessnutPieces.vue b/ui/src/lib/pieces/ChessnutPieces.vue new file mode 100644 index 00000000..ac75916b --- /dev/null +++ b/ui/src/lib/pieces/ChessnutPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/CompanionPieces.vue b/ui/src/lib/pieces/CompanionPieces.vue new file mode 100644 index 00000000..95291016 --- /dev/null +++ b/ui/src/lib/pieces/CompanionPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/CookePieces.vue b/ui/src/lib/pieces/CookePieces.vue new file mode 100644 index 00000000..642e783f --- /dev/null +++ b/ui/src/lib/pieces/CookePieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/DisguisedPieces.vue b/ui/src/lib/pieces/DisguisedPieces.vue new file mode 100644 index 00000000..757c7810 --- /dev/null +++ b/ui/src/lib/pieces/DisguisedPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/DubrovnyPieces.vue b/ui/src/lib/pieces/DubrovnyPieces.vue new file mode 100644 index 00000000..6e7ceee4 --- /dev/null +++ b/ui/src/lib/pieces/DubrovnyPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/FantasyPieces.vue b/ui/src/lib/pieces/FantasyPieces.vue new file mode 100644 index 00000000..b248c1c1 --- /dev/null +++ b/ui/src/lib/pieces/FantasyPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/FiriPieces.vue b/ui/src/lib/pieces/FiriPieces.vue new file mode 100644 index 00000000..fc7fa35e --- /dev/null +++ b/ui/src/lib/pieces/FiriPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/FrescaPieces.vue b/ui/src/lib/pieces/FrescaPieces.vue new file mode 100644 index 00000000..0038ea80 --- /dev/null +++ b/ui/src/lib/pieces/FrescaPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/GovernorPieces.vue b/ui/src/lib/pieces/GovernorPieces.vue new file mode 100644 index 00000000..35fa68f9 --- /dev/null +++ b/ui/src/lib/pieces/GovernorPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/HorseyPieces.vue b/ui/src/lib/pieces/HorseyPieces.vue new file mode 100644 index 00000000..0b37f53b --- /dev/null +++ b/ui/src/lib/pieces/HorseyPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/IcpiecesPieces.vue b/ui/src/lib/pieces/IcpiecesPieces.vue new file mode 100644 index 00000000..4ad03d49 --- /dev/null +++ b/ui/src/lib/pieces/IcpiecesPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/KiwenSuwiPieces.vue b/ui/src/lib/pieces/KiwenSuwiPieces.vue new file mode 100644 index 00000000..8f565d3c --- /dev/null +++ b/ui/src/lib/pieces/KiwenSuwiPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/KosalPieces.vue b/ui/src/lib/pieces/KosalPieces.vue new file mode 100644 index 00000000..7df88d15 --- /dev/null +++ b/ui/src/lib/pieces/KosalPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/LeipzigPieces.vue b/ui/src/lib/pieces/LeipzigPieces.vue new file mode 100644 index 00000000..a8b1838e --- /dev/null +++ b/ui/src/lib/pieces/LeipzigPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/LetterPieces.vue b/ui/src/lib/pieces/LetterPieces.vue new file mode 100644 index 00000000..b15736fe --- /dev/null +++ b/ui/src/lib/pieces/LetterPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/MaestroPieces.vue b/ui/src/lib/pieces/MaestroPieces.vue new file mode 100644 index 00000000..ec80461a --- /dev/null +++ b/ui/src/lib/pieces/MaestroPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/MeridaPieces.vue b/ui/src/lib/pieces/MeridaPieces.vue new file mode 100644 index 00000000..a5ea6bba --- /dev/null +++ b/ui/src/lib/pieces/MeridaPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/MonarchyPieces.vue b/ui/src/lib/pieces/MonarchyPieces.vue new file mode 100644 index 00000000..259f6fe2 --- /dev/null +++ b/ui/src/lib/pieces/MonarchyPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/MonoPieces.vue b/ui/src/lib/pieces/MonoPieces.vue new file mode 100644 index 00000000..757c7810 --- /dev/null +++ b/ui/src/lib/pieces/MonoPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/MpchessPieces.vue b/ui/src/lib/pieces/MpchessPieces.vue new file mode 100644 index 00000000..8d699fd2 --- /dev/null +++ b/ui/src/lib/pieces/MpchessPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/PirouettiPieces.vue b/ui/src/lib/pieces/PirouettiPieces.vue new file mode 100644 index 00000000..76135412 --- /dev/null +++ b/ui/src/lib/pieces/PirouettiPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/PixelPieces.vue b/ui/src/lib/pieces/PixelPieces.vue new file mode 100644 index 00000000..0df28eb3 --- /dev/null +++ b/ui/src/lib/pieces/PixelPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/ReillycraigPieces.vue b/ui/src/lib/pieces/ReillycraigPieces.vue new file mode 100644 index 00000000..360e05de --- /dev/null +++ b/ui/src/lib/pieces/ReillycraigPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/RhosgfxPieces.vue b/ui/src/lib/pieces/RhosgfxPieces.vue new file mode 100644 index 00000000..ff06c676 --- /dev/null +++ b/ui/src/lib/pieces/RhosgfxPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/RiohachaPieces.vue b/ui/src/lib/pieces/RiohachaPieces.vue new file mode 100644 index 00000000..ab942054 --- /dev/null +++ b/ui/src/lib/pieces/RiohachaPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/ShapesPieces.vue b/ui/src/lib/pieces/ShapesPieces.vue new file mode 100644 index 00000000..c3741dfa --- /dev/null +++ b/ui/src/lib/pieces/ShapesPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/SpatialPieces.vue b/ui/src/lib/pieces/SpatialPieces.vue new file mode 100644 index 00000000..88bc4e87 --- /dev/null +++ b/ui/src/lib/pieces/SpatialPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/StauntyPieces.vue b/ui/src/lib/pieces/StauntyPieces.vue new file mode 100644 index 00000000..cf5aeeef --- /dev/null +++ b/ui/src/lib/pieces/StauntyPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/TatianaPieces.vue b/ui/src/lib/pieces/TatianaPieces.vue new file mode 100644 index 00000000..49dc52e5 --- /dev/null +++ b/ui/src/lib/pieces/TatianaPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/ui/src/lib/pieces/XkcdPieces.vue b/ui/src/lib/pieces/XkcdPieces.vue new file mode 100644 index 00000000..4791625f --- /dev/null +++ b/ui/src/lib/pieces/XkcdPieces.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file From 10f4aece43a507d4b8fe2983692ed37fb366103a Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Thu, 20 Nov 2025 21:31:16 -0600 Subject: [PATCH 16/80] remove metadata --- ui/src/lib/pieces/MpchessPieces.vue | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/ui/src/lib/pieces/MpchessPieces.vue b/ui/src/lib/pieces/MpchessPieces.vue index 8d699fd2..4658603d 100644 --- a/ui/src/lib/pieces/MpchessPieces.vue +++ b/ui/src/lib/pieces/MpchessPieces.vue @@ -1,16 +1,16 @@ \ No newline at end of file diff --git a/ui/src/components/Select.vue b/ui/src/components/Select.vue new file mode 100644 index 00000000..864a35f7 --- /dev/null +++ b/ui/src/components/Select.vue @@ -0,0 +1,39 @@ + + + \ No newline at end of file diff --git a/ui/src/components/index.ts b/ui/src/components/index.ts index b6029cf2..9d9fcada 100644 --- a/ui/src/components/index.ts +++ b/ui/src/components/index.ts @@ -1,2 +1,3 @@ export { default as Checkbox } from './Checkbox.vue' -export { default as Combobox } from './Combobox.vue' \ No newline at end of file +export { default as Combobox } from './Combobox.vue' +export { default as Select } from './Select.vue' From c368d45979ab79bef818e431ac457c8fd29e052a Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Thu, 20 Nov 2025 21:56:36 -0600 Subject: [PATCH 18/80] test setup --- ui/package.json | 7 +- ui/pnpm-lock.yaml | 384 +++++++++++++++++++++++++++++++- ui/src/test/hello-world.test.ts | 9 + ui/src/test/setup.ts | 13 ++ ui/vite.config.ts | 8 +- ui/vitest.config.ts | 18 +- 6 files changed, 414 insertions(+), 25 deletions(-) create mode 100644 ui/src/test/hello-world.test.ts create mode 100644 ui/src/test/setup.ts diff --git a/ui/package.json b/ui/package.json index 65bda67b..ee76ed81 100644 --- a/ui/package.json +++ b/ui/package.json @@ -8,7 +8,9 @@ "scripts": { "dev": "vite", "build": "vite build", - "preview": "vite preview" + "preview": "vite preview", + "test": "vitest", + "test:ui": "vitest --ui" }, "devDependencies": { "@bedard/hexchess": "^2.5.0", @@ -18,13 +20,16 @@ "@types/node": "^22.17.1", "@vitejs/plugin-vue": "^6.0.2", "@vitest/browser": "^4.0.12", + "@vitest/browser-playwright": "^4.0.12", "@vitest/ui": "^4.0.12", "@vue/test-utils": "^2.4.6", + "jsdom": "^27.2.0", "playwright": "^1.56.1", "typescript": "^5.9.2", "vite": "^6.3.5", "vite-plugin-dts": "^4.5.4", "vitest": "^4.0.12", + "vitest-browser-vue": "^2.0.1", "vue": "^3.5.24", "vue-tsc": "^3.1.4" } diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index 0a12faa9..bb98ed93 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -28,13 +28,19 @@ importers: version: 6.0.2(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))(vue@3.5.24(typescript@5.9.3)) '@vitest/browser': specifier: ^4.0.12 - version: 4.0.12(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))(vitest@4.0.12(@types/node@22.19.1)(@vitest/ui@4.0.12)(jiti@2.6.1)(lightningcss@1.30.2)) + version: 4.0.12(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))(vitest@4.0.12(@types/node@22.19.1)(@vitest/browser-playwright@4.0.12)(@vitest/ui@4.0.12)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2)) + '@vitest/browser-playwright': + specifier: ^4.0.12 + version: 4.0.12(playwright@1.56.1)(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))(vitest@4.0.12) '@vitest/ui': specifier: ^4.0.12 version: 4.0.12(vitest@4.0.12) '@vue/test-utils': specifier: ^2.4.6 version: 2.4.6 + jsdom: + specifier: ^27.2.0 + version: 27.2.0 playwright: specifier: ^1.56.1 version: 1.56.1 @@ -49,7 +55,10 @@ importers: version: 4.5.4(@types/node@22.19.1)(rollup@4.53.2)(typescript@5.9.3)(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)) vitest: specifier: ^4.0.12 - version: 4.0.12(@types/node@22.19.1)(@vitest/ui@4.0.12)(jiti@2.6.1)(lightningcss@1.30.2) + version: 4.0.12(@types/node@22.19.1)(@vitest/browser-playwright@4.0.12)(@vitest/ui@4.0.12)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2) + vitest-browser-vue: + specifier: ^2.0.1 + version: 2.0.1(vitest@4.0.12(@types/node@22.19.1)(@vitest/browser-playwright@4.0.12)(@vitest/ui@4.0.12)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2))(vue@3.5.24(typescript@5.9.3)) vue: specifier: ^3.5.24 version: 3.5.24(typescript@5.9.3) @@ -59,6 +68,18 @@ importers: packages: + '@acemir/cssom@0.9.23': + resolution: {integrity: sha512-2kJ1HxBKzPLbmhZpxBiTZggjtgCwKg1ma5RHShxvd6zgqhDEdEkzpiwe7jLkI2p2BrZvFCXIihdoMkl1H39VnA==} + + '@asamuzakjp/css-color@4.1.0': + resolution: {integrity: sha512-9xiBAtLn4aNsa4mDnpovJvBn72tNEIACyvlqaNJ+ADemR+yeMJWnBudOi2qGDviJa7SwcDOU/TRh5dnET7qk0w==} + + '@asamuzakjp/dom-selector@6.7.4': + resolution: {integrity: sha512-buQDjkm+wDPXd6c13534URWZqbz0RP5PAhXZ+LIoa5LgwInT9HVJvGIJivg75vi8I13CxDGdTnz+aY5YUJlIAA==} + + '@asamuzakjp/nwsapi@2.3.9': + resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} + '@babel/helper-string-parser@7.27.1': resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} @@ -79,6 +100,38 @@ packages: '@bedard/hexchess@2.5.0': resolution: {integrity: sha512-KwThpuP8zD2tk8NytA+U/6pZqmZUMdWh8xH3LFLWYa9GvFBwZwqbW1q+tGqytBxFmFKoryV3hr9yN6PtC8B2lA==} + '@csstools/color-helpers@5.1.0': + resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} + engines: {node: '>=18'} + + '@csstools/css-calc@2.1.4': + resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-color-parser@3.1.0': + resolution: {integrity: sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-parser-algorithms@3.0.5': + resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-syntax-patches-for-csstree@1.0.16': + resolution: {integrity: sha512-2SpS4/UaWQaGpBINyG5ZuCHnUDeVByOhvbkARwfmnfxDvTaj80yOI1cD8Tw93ICV5Fx4fnyDKWQZI1CDtcWyUg==} + engines: {node: '>=18'} + + '@csstools/css-tokenizer@3.0.4': + resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} + engines: {node: '>=18'} + '@esbuild/aix-ppc64@0.25.12': resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} engines: {node: '>=18'} @@ -572,6 +625,12 @@ packages: vite: ^5.0.0 || ^6.0.0 || ^7.0.0 vue: ^3.2.25 + '@vitest/browser-playwright@4.0.12': + resolution: {integrity: sha512-TCFuvEHVLHDuK4HmKJNSuYQwefpXBnWNuf3J8pLsXF3Q2sgRZJs+L+aUF2xjtJT5OBEJsgX5jENvlGKc596XNw==} + peerDependencies: + playwright: '*' + vitest: 4.0.12 + '@vitest/browser@4.0.12': resolution: {integrity: sha512-8zE2ksJ7V4B7Mc++L6rBRZOZHnE/f9URvj7oLYKIS5wcDaSi6EhfalN0EG6+R/OlTYZarbK6RqmhKDLYNC9KfQ==} peerDependencies: @@ -680,6 +739,10 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + ajv-draft-04@1.0.0: resolution: {integrity: sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==} peerDependencies: @@ -734,6 +797,9 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + bidi-js@1.0.3: + resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + brace-expansion@2.0.2: resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} @@ -768,9 +834,21 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} + css-tree@3.1.0: + resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + cssstyle@5.3.3: + resolution: {integrity: sha512-OytmFH+13/QXONJcC75QNdMtKpceNk3u8ThBjyyYjkEcy/ekBwR1mMAuNvi3gdBPW3N5TlCzQ0WZw8H0lN/bDw==} + engines: {node: '>=20'} + csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + data-urls@6.0.0: + resolution: {integrity: sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA==} + engines: {node: '>=20'} + de-indent@1.0.2: resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} @@ -783,6 +861,9 @@ packages: supports-color: optional: true + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} @@ -813,6 +894,10 @@ packages: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} @@ -892,6 +977,22 @@ packages: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true + html-encoding-sniffer@4.0.0: + resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} + engines: {node: '>=18'} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + import-lazy@4.0.0: resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} engines: {node: '>=8'} @@ -907,6 +1008,9 @@ packages: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} @@ -929,6 +1033,15 @@ packages: resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} engines: {node: '>=14'} + jsdom@27.2.0: + resolution: {integrity: sha512-454TI39PeRDW1LgpyLPyURtB4Zx1tklSr6+OFOipsxGUH1WMTvk6C65JQdrj455+DP2uJ1+veBEHTGFKWVLFoA==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} @@ -1018,6 +1131,10 @@ packages: lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + lru-cache@11.2.2: + resolution: {integrity: sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==} + engines: {node: 20 || >=22} + lru-cache@6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} @@ -1025,6 +1142,9 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + mdn-data@2.12.2: + resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} + minimatch@10.0.3: resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==} engines: {node: 20 || >=22} @@ -1067,6 +1187,9 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + parse5@8.0.0: + resolution: {integrity: sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==} + path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} @@ -1143,6 +1266,13 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + semver@7.5.4: resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} engines: {node: '>=10'} @@ -1216,6 +1346,9 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + tailwindcss@4.1.17: resolution: {integrity: sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==} @@ -1237,10 +1370,25 @@ packages: resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} engines: {node: '>=14.0.0'} + tldts-core@7.0.18: + resolution: {integrity: sha512-jqJC13oP4FFAahv4JT/0WTDrCF9Okv7lpKtOZUGPLiAnNbACcSg8Y8T+Z9xthOmRBqi/Sob4yi0TE0miRCvF7Q==} + + tldts@7.0.18: + resolution: {integrity: sha512-lCcgTAgMxQ1JKOWrVGo6E69Ukbnx4Gc1wiYLRf6J5NN4HRYJtCby1rPF8rkQ4a6qqoFBK5dvjJ1zJ0F7VfDSvw==} + hasBin: true + totalist@3.0.1: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} + tough-cookie@6.0.0: + resolution: {integrity: sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==} + engines: {node: '>=16'} + + tr46@6.0.0: + resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==} + engines: {node: '>=20'} + typescript@5.8.2: resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==} engines: {node: '>=14.17'} @@ -1313,6 +1461,12 @@ packages: yaml: optional: true + vitest-browser-vue@2.0.1: + resolution: {integrity: sha512-IfTJY3Olr27AXCVAhOqx4g5iUgzdWLmYr40svg7rOrunjsqc9trrzi/eI3i11+UYddbKXu3HJl2bo8o8lqKm4A==} + peerDependencies: + vitest: ^4.0.0-0 + vue: ^3.0.0 + vitest@4.0.12: resolution: {integrity: sha512-pmW4GCKQ8t5Ko1jYjC3SqOr7TUKN7uHOHB/XGsAIb69eYu6d1ionGSsb5H9chmPf+WeXt0VE7jTXsB1IvWoNbw==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -1370,6 +1524,26 @@ packages: typescript: optional: true + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + + webidl-conversions@8.0.0: + resolution: {integrity: sha512-n4W4YFyz5JzOfQeA8oN7dUYpR+MBP3PIUsn2jLjWXwK5ASUzt0Jc/A5sAUZoCYFJRGF0FBKJ+1JjN43rNdsQzA==} + engines: {node: '>=20'} + + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-url@15.1.0: + resolution: {integrity: sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==} + engines: {node: '>=20'} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -1400,11 +1574,38 @@ packages: utf-8-validate: optional: true + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + yallist@4.0.0: resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} snapshots: + '@acemir/cssom@0.9.23': {} + + '@asamuzakjp/css-color@4.1.0': + dependencies: + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + lru-cache: 11.2.2 + + '@asamuzakjp/dom-selector@6.7.4': + dependencies: + '@asamuzakjp/nwsapi': 2.3.9 + bidi-js: 1.0.3 + css-tree: 3.1.0 + is-potential-custom-element-name: 1.0.1 + lru-cache: 11.2.2 + + '@asamuzakjp/nwsapi@2.3.9': {} + '@babel/helper-string-parser@7.27.1': {} '@babel/helper-validator-identifier@7.28.5': {} @@ -1420,6 +1621,28 @@ snapshots: '@bedard/hexchess@2.5.0': {} + '@csstools/color-helpers@5.1.0': {} + + '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-color-parser@3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/color-helpers': 5.1.0 + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-syntax-patches-for-csstree@1.0.16': {} + + '@csstools/css-tokenizer@3.0.4': {} + '@esbuild/aix-ppc64@0.25.12': optional: true @@ -1797,7 +2020,20 @@ snapshots: vite: 6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2) vue: 3.5.24(typescript@5.9.3) - '@vitest/browser@4.0.12(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))(vitest@4.0.12(@types/node@22.19.1)(@vitest/ui@4.0.12)(jiti@2.6.1)(lightningcss@1.30.2))': + '@vitest/browser-playwright@4.0.12(playwright@1.56.1)(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))(vitest@4.0.12)': + dependencies: + '@vitest/browser': 4.0.12(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))(vitest@4.0.12(@types/node@22.19.1)(@vitest/browser-playwright@4.0.12)(@vitest/ui@4.0.12)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2)) + '@vitest/mocker': 4.0.12(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)) + playwright: 1.56.1 + tinyrainbow: 3.0.3 + vitest: 4.0.12(@types/node@22.19.1)(@vitest/browser-playwright@4.0.12)(@vitest/ui@4.0.12)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2) + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + + '@vitest/browser@4.0.12(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))(vitest@4.0.12(@types/node@22.19.1)(@vitest/browser-playwright@4.0.12)(@vitest/ui@4.0.12)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2))': dependencies: '@vitest/mocker': 4.0.12(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)) '@vitest/utils': 4.0.12 @@ -1806,7 +2042,7 @@ snapshots: pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.0.3 - vitest: 4.0.12(@types/node@22.19.1)(@vitest/ui@4.0.12)(jiti@2.6.1)(lightningcss@1.30.2) + vitest: 4.0.12(@types/node@22.19.1)(@vitest/browser-playwright@4.0.12)(@vitest/ui@4.0.12)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2) ws: 8.18.3 transitivePeerDependencies: - bufferutil @@ -1857,7 +2093,7 @@ snapshots: sirv: 3.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vitest: 4.0.12(@types/node@22.19.1)(@vitest/ui@4.0.12)(jiti@2.6.1)(lightningcss@1.30.2) + vitest: 4.0.12(@types/node@22.19.1)(@vitest/browser-playwright@4.0.12)(@vitest/ui@4.0.12)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2) '@vitest/utils@4.0.12': dependencies: @@ -1969,6 +2205,8 @@ snapshots: acorn@8.15.0: {} + agent-base@7.1.4: {} + ajv-draft-04@1.0.0(ajv@8.13.0): optionalDependencies: ajv: 8.13.0 @@ -2013,6 +2251,10 @@ snapshots: balanced-match@1.0.2: {} + bidi-js@1.0.3: + dependencies: + require-from-string: 2.0.2 + brace-expansion@2.0.2: dependencies: balanced-match: 1.0.2 @@ -2044,14 +2286,32 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + css-tree@3.1.0: + dependencies: + mdn-data: 2.12.2 + source-map-js: 1.2.1 + + cssstyle@5.3.3: + dependencies: + '@asamuzakjp/css-color': 4.1.0 + '@csstools/css-syntax-patches-for-csstree': 1.0.16 + css-tree: 3.1.0 + csstype@3.2.3: {} + data-urls@6.0.0: + dependencies: + whatwg-mimetype: 4.0.0 + whatwg-url: 15.1.0 + de-indent@1.0.2: {} debug@4.4.3: dependencies: ms: 2.1.3 + decimal.js@10.6.0: {} + detect-libc@2.1.2: {} diff@8.0.2: {} @@ -2076,6 +2336,8 @@ snapshots: entities@4.5.0: {} + entities@6.0.1: {} + es-module-lexer@1.7.0: {} esbuild@0.25.12: @@ -2165,6 +2427,28 @@ snapshots: he@1.2.0: {} + html-encoding-sniffer@4.0.0: + dependencies: + whatwg-encoding: 3.1.1 + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + import-lazy@4.0.0: {} ini@1.3.8: {} @@ -2175,6 +2459,8 @@ snapshots: is-fullwidth-code-point@3.0.0: {} + is-potential-custom-element-name@1.0.1: {} + isexe@2.0.0: {} jackspeak@3.4.3: @@ -2197,6 +2483,33 @@ snapshots: js-cookie@3.0.5: {} + jsdom@27.2.0: + dependencies: + '@acemir/cssom': 0.9.23 + '@asamuzakjp/dom-selector': 6.7.4 + cssstyle: 5.3.3 + data-urls: 6.0.0 + decimal.js: 10.6.0 + html-encoding-sniffer: 4.0.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + is-potential-custom-element-name: 1.0.1 + parse5: 8.0.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 6.0.0 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 8.0.0 + whatwg-encoding: 3.1.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 15.1.0 + ws: 8.18.3 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + json-schema-traverse@1.0.0: {} jsonfile@6.2.0: @@ -2266,6 +2579,8 @@ snapshots: lru-cache@10.4.3: {} + lru-cache@11.2.2: {} + lru-cache@6.0.0: dependencies: yallist: 4.0.0 @@ -2274,6 +2589,8 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + mdn-data@2.12.2: {} + minimatch@10.0.3: dependencies: '@isaacs/brace-expansion': 5.0.0 @@ -2309,6 +2626,10 @@ snapshots: package-json-from-dist@1.0.1: {} + parse5@8.0.0: + dependencies: + entities: 6.0.1 + path-browserify@1.0.1: {} path-key@3.1.1: {} @@ -2400,6 +2721,12 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.53.2 fsevents: 2.3.3 + safer-buffer@2.1.2: {} + + saxes@6.0.0: + dependencies: + xmlchars: 2.2.0 + semver@7.5.4: dependencies: lru-cache: 6.0.0 @@ -2460,6 +2787,8 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + symbol-tree@3.2.4: {} + tailwindcss@4.1.17: {} tapable@2.3.0: {} @@ -2475,8 +2804,22 @@ snapshots: tinyrainbow@3.0.3: {} + tldts-core@7.0.18: {} + + tldts@7.0.18: + dependencies: + tldts-core: 7.0.18 + totalist@3.0.1: {} + tough-cookie@6.0.0: + dependencies: + tldts: 7.0.18 + + tr46@6.0.0: + dependencies: + punycode: 2.3.1 + typescript@5.8.2: {} typescript@5.9.3: {} @@ -2524,7 +2867,13 @@ snapshots: jiti: 2.6.1 lightningcss: 1.30.2 - vitest@4.0.12(@types/node@22.19.1)(@vitest/ui@4.0.12)(jiti@2.6.1)(lightningcss@1.30.2): + vitest-browser-vue@2.0.1(vitest@4.0.12(@types/node@22.19.1)(@vitest/browser-playwright@4.0.12)(@vitest/ui@4.0.12)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2))(vue@3.5.24(typescript@5.9.3)): + dependencies: + '@vue/test-utils': 2.4.6 + vitest: 4.0.12(@types/node@22.19.1)(@vitest/browser-playwright@4.0.12)(@vitest/ui@4.0.12)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2) + vue: 3.5.24(typescript@5.9.3) + + vitest@4.0.12(@types/node@22.19.1)(@vitest/browser-playwright@4.0.12)(@vitest/ui@4.0.12)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2): dependencies: '@vitest/expect': 4.0.12 '@vitest/mocker': 4.0.12(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)) @@ -2548,7 +2897,9 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.19.1 + '@vitest/browser-playwright': 4.0.12(playwright@1.56.1)(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))(vitest@4.0.12) '@vitest/ui': 4.0.12(vitest@4.0.12) + jsdom: 27.2.0 transitivePeerDependencies: - jiti - less @@ -2583,6 +2934,23 @@ snapshots: optionalDependencies: typescript: 5.9.3 + w3c-xmlserializer@5.0.0: + dependencies: + xml-name-validator: 5.0.0 + + webidl-conversions@8.0.0: {} + + whatwg-encoding@3.1.1: + dependencies: + iconv-lite: 0.6.3 + + whatwg-mimetype@4.0.0: {} + + whatwg-url@15.1.0: + dependencies: + tr46: 6.0.0 + webidl-conversions: 8.0.0 + which@2.0.2: dependencies: isexe: 2.0.0 @@ -2606,4 +2974,8 @@ snapshots: ws@8.18.3: {} + xml-name-validator@5.0.0: {} + + xmlchars@2.2.0: {} + yallist@4.0.0: {} diff --git a/ui/src/test/hello-world.test.ts b/ui/src/test/hello-world.test.ts new file mode 100644 index 00000000..5fff0cfc --- /dev/null +++ b/ui/src/test/hello-world.test.ts @@ -0,0 +1,9 @@ +import { expect, test } from 'vitest' +import { render } from 'vitest-browser-vue' +import App from '../App.vue' + +test('properly handles form inputs', async () => { + const screen = render(App) + + expect(screen.container.querySelector('svg')).not.toBeNull() +}) \ No newline at end of file diff --git a/ui/src/test/setup.ts b/ui/src/test/setup.ts new file mode 100644 index 00000000..6cbbe401 --- /dev/null +++ b/ui/src/test/setup.ts @@ -0,0 +1,13 @@ +// Vitest setup file +// This file runs before each test file + +// Make page available globally in browser mode +import { page } from 'vitest/browser' + +declare global { + // eslint-disable-next-line no-var + var page: import('playwright').Page +} + +;(globalThis as any).page = page + diff --git a/ui/vite.config.ts b/ui/vite.config.ts index 1c59967e..f92086b4 100644 --- a/ui/vite.config.ts +++ b/ui/vite.config.ts @@ -15,13 +15,7 @@ export default defineConfig({ rollupTypes: true }), tailwindcss(), - vue({ - template: { - compilerOptions: { - isCustomElement: tag => ['hexchess-board'].includes(tag) - } - } - }), + vue(), ], build: { lib: { diff --git a/ui/vitest.config.ts b/ui/vitest.config.ts index 3f18c2fb..79be48b3 100644 --- a/ui/vitest.config.ts +++ b/ui/vitest.config.ts @@ -1,28 +1,24 @@ import { defineConfig } from 'vitest/config' import { fileURLToPath } from 'node:url' +import { playwright } from '@vitest/browser-playwright' import { resolve } from 'node:path' -import vue from '@vitejs/plugin-vue' import tailwindcss from '@tailwindcss/vite' +import vue from '@vitejs/plugin-vue' const __dirname = fileURLToPath(new URL('.', import.meta.url)) export default defineConfig({ plugins: [ tailwindcss(), - vue({ - template: { - compilerOptions: { - isCustomElement: tag => ['hexchess-board'].includes(tag) - } - } - }), + vue(), ], test: { browser: { + provider: playwright(), enabled: true, - name: 'chromium', - provider: 'playwright', - headless: true, + instances: [ + { browser: 'chromium' }, + ], }, environment: 'jsdom', globals: true, From 4e155ca24560f0d0fb565fd1e7a35ff4f7d7479e Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Thu, 20 Nov 2025 22:05:33 -0600 Subject: [PATCH 19/80] testing --- ui/src/App.vue | 2 +- ui/src/test/setup.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/src/App.vue b/ui/src/App.vue index 2c9d3eae..e608d397 100644 --- a/ui/src/App.vue +++ b/ui/src/App.vue @@ -1,6 +1,6 @@ diff --git a/ui/src/lib/hexboard/Hexboard.vue b/ui/src/lib/hexboard/Hexboard.vue index 4287c1b9..627293ea 100644 --- a/ui/src/lib/hexboard/Hexboard.vue +++ b/ui/src/lib/hexboard/Hexboard.vue @@ -6,7 +6,7 @@ @@ -22,6 +22,24 @@ @mouseleave="onMouseleavePosition" /> + + + + +
{{ { mousePosition } }}
\ No newline at end of file diff --git a/ui/src/lib/hexboard/constants.ts b/ui/src/lib/hexboard/constants.ts index 71f2e5dc..253b5928 100644 --- a/ui/src/lib/hexboard/constants.ts +++ b/ui/src/lib/hexboard/constants.ts @@ -10,9 +10,9 @@ export const defaultOptions: HexboardOptions = { 'oklch(0.8366 0.1165 66.29)', 'oklch(0.6806 0.1423 75.83)', ], - labelActiveColor: 'green', - labelColor: 'red', - labelInactiveColor: 'blue', + labelActiveColor: 'oklch(66.6% 0.179 58.318)', + labelColor: 'oklch(55.4% 0.046 257.417)', + labelInactiveColor: 'oklch(70.4% 0.04 256.788)', } as const /** empty position */ From 3a017bc286ebb556f7a50ec6f896b28db15dc209 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Sat, 22 Nov 2025 18:08:33 -0600 Subject: [PATCH 29/80] gioco --- ui/src/App.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/App.vue b/ui/src/App.vue index 1720dfd0..f3e444f0 100644 --- a/ui/src/App.vue +++ b/ui/src/App.vue @@ -130,7 +130,7 @@ const pieceItems = computed(() => })) ) -const selectedSet = ref<{ display: string; value: string }>({ display: 'alpha', value: 'alpha' }) +const selectedSet = ref<{ display: string; value: string }>({ display: 'gioco', value: 'gioco' }) const pieces = computed(() => allPieces[selectedSet.value.value as keyof typeof allPieces]) From b80abd2030b76d076bcafa7b84a7686b96256dbf Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Sat, 22 Nov 2025 18:20:37 -0600 Subject: [PATCH 30/80] labels option --- ui/package.json | 17 +- ui/pnpm-lock.yaml | 578 ++++++++++++++++---------- ui/src/lib/hexboard/Hexboard.vue | 18 +- ui/src/lib/hexboard/constants.ts | 1 + ui/src/lib/hexboard/types.ts | 2 + ui/src/test/user-interaction.test.tsx | 11 +- 6 files changed, 392 insertions(+), 235 deletions(-) diff --git a/ui/package.json b/ui/package.json index ab08ce20..ae939b93 100644 --- a/ui/package.json +++ b/ui/package.json @@ -17,22 +17,23 @@ "@headlessui/vue": "^1.7.23", "@heroicons/vue": "^2.2.0", "@tailwindcss/vite": "^4.1.17", - "@types/node": "^22.17.1", + "@types/node": "^22.19.1", "@vitejs/plugin-vue": "^6.0.2", "@vitejs/plugin-vue-jsx": "^5.1.2", - "@vitest/browser": "^4.0.12", - "@vitest/browser-playwright": "^4.0.12", - "@vitest/ui": "^4.0.12", + "@vitest/browser": "^4.0.13", + "@vitest/browser-playwright": "^4.0.13", + "@vitest/coverage-v8": "^4.0.13", + "@vitest/ui": "^4.0.13", "@vue/test-utils": "^2.4.6", "es-toolkit": "^1.42.0", "jsdom": "^27.2.0", "playwright": "^1.56.1", - "typescript": "^5.9.2", - "vite": "^6.3.5", + "typescript": "^5.9.3", + "vite": "^6.4.1", "vite-plugin-dts": "^4.5.4", - "vitest": "^4.0.12", + "vitest": "^4.0.13", "vitest-browser-vue": "^2.0.1", "vue": "^3.5.24", - "vue-tsc": "^3.1.4" + "vue-tsc": "^3.1.5" } } diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index 3b186ca3..bac0b0a2 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -21,7 +21,7 @@ importers: specifier: ^4.1.17 version: 4.1.17(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)) '@types/node': - specifier: ^22.17.1 + specifier: ^22.19.1 version: 22.19.1 '@vitejs/plugin-vue': specifier: ^6.0.2 @@ -30,14 +30,17 @@ importers: specifier: ^5.1.2 version: 5.1.2(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))(vue@3.5.24(typescript@5.9.3)) '@vitest/browser': - specifier: ^4.0.12 - version: 4.0.12(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))(vitest@4.0.12(@types/node@22.19.1)(@vitest/browser-playwright@4.0.12)(@vitest/ui@4.0.12)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2)) + specifier: ^4.0.13 + version: 4.0.13(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))(vitest@4.0.13(@types/node@22.19.1)(@vitest/browser-playwright@4.0.13)(@vitest/ui@4.0.13)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2)) '@vitest/browser-playwright': - specifier: ^4.0.12 - version: 4.0.12(playwright@1.56.1)(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))(vitest@4.0.12) + specifier: ^4.0.13 + version: 4.0.13(playwright@1.56.1)(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))(vitest@4.0.13) + '@vitest/coverage-v8': + specifier: ^4.0.13 + version: 4.0.13(@vitest/browser@4.0.13(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))(vitest@4.0.13(@types/node@22.19.1)(@vitest/browser-playwright@4.0.13)(@vitest/ui@4.0.13)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2)))(vitest@4.0.13(@types/node@22.19.1)(@vitest/browser-playwright@4.0.13)(@vitest/ui@4.0.13)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2)) '@vitest/ui': - specifier: ^4.0.12 - version: 4.0.12(vitest@4.0.12) + specifier: ^4.0.13 + version: 4.0.13(vitest@4.0.13) '@vue/test-utils': specifier: ^2.4.6 version: 2.4.6 @@ -51,31 +54,31 @@ importers: specifier: ^1.56.1 version: 1.56.1 typescript: - specifier: ^5.9.2 + specifier: ^5.9.3 version: 5.9.3 vite: - specifier: ^6.3.5 + specifier: ^6.4.1 version: 6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2) vite-plugin-dts: specifier: ^4.5.4 - version: 4.5.4(@types/node@22.19.1)(rollup@4.53.2)(typescript@5.9.3)(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)) + version: 4.5.4(@types/node@22.19.1)(rollup@4.53.3)(typescript@5.9.3)(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)) vitest: - specifier: ^4.0.12 - version: 4.0.12(@types/node@22.19.1)(@vitest/browser-playwright@4.0.12)(@vitest/ui@4.0.12)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2) + specifier: ^4.0.13 + version: 4.0.13(@types/node@22.19.1)(@vitest/browser-playwright@4.0.13)(@vitest/ui@4.0.13)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2) vitest-browser-vue: specifier: ^2.0.1 - version: 2.0.1(vitest@4.0.12(@types/node@22.19.1)(@vitest/browser-playwright@4.0.12)(@vitest/ui@4.0.12)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2))(vue@3.5.24(typescript@5.9.3)) + version: 2.0.1(vitest@4.0.13(@types/node@22.19.1)(@vitest/browser-playwright@4.0.13)(@vitest/ui@4.0.13)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2))(vue@3.5.24(typescript@5.9.3)) vue: specifier: ^3.5.24 version: 3.5.24(typescript@5.9.3) vue-tsc: - specifier: ^3.1.4 - version: 3.1.4(typescript@5.9.3) + specifier: ^3.1.5 + version: 3.1.5(typescript@5.9.3) packages: - '@acemir/cssom@0.9.23': - resolution: {integrity: sha512-2kJ1HxBKzPLbmhZpxBiTZggjtgCwKg1ma5RHShxvd6zgqhDEdEkzpiwe7jLkI2p2BrZvFCXIihdoMkl1H39VnA==} + '@acemir/cssom@0.9.24': + resolution: {integrity: sha512-5YjgMmAiT2rjJZU7XK1SNI7iqTy92DpaYVgG6x63FxkJ11UpYfLndHJATtinWJClAXiOlW9XWaUyAQf8pMrQPg==} '@asamuzakjp/css-color@4.1.0': resolution: {integrity: sha512-9xiBAtLn4aNsa4mDnpovJvBn72tNEIACyvlqaNJ+ADemR+yeMJWnBudOi2qGDviJa7SwcDOU/TRh5dnET7qk0w==} @@ -203,6 +206,10 @@ packages: resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} engines: {node: '>=6.9.0'} + '@bcoe/v8-coverage@1.0.2': + resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} + engines: {node: '>=18'} + '@bedard/hexchess@2.5.0': resolution: {integrity: sha512-KwThpuP8zD2tk8NytA+U/6pZqmZUMdWh8xH3LFLWYa9GvFBwZwqbW1q+tGqytBxFmFKoryV3hr9yN6PtC8B2lA==} @@ -230,8 +237,8 @@ packages: peerDependencies: '@csstools/css-tokenizer': ^3.0.4 - '@csstools/css-syntax-patches-for-csstree@1.0.16': - resolution: {integrity: sha512-2SpS4/UaWQaGpBINyG5ZuCHnUDeVByOhvbkARwfmnfxDvTaj80yOI1cD8Tw93ICV5Fx4fnyDKWQZI1CDtcWyUg==} + '@csstools/css-syntax-patches-for-csstree@1.0.17': + resolution: {integrity: sha512-LCC++2h8pLUSPY+EsZmrrJ1EOUu+5iClpEiDhhdw3zRJpPbABML/N5lmRuBHjxtKm9VnRcsUzioyD0sekFMF0A==} engines: {node: '>=18'} '@csstools/css-tokenizer@3.0.4': @@ -433,11 +440,11 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@microsoft/api-extractor-model@7.32.0': - resolution: {integrity: sha512-QIVJSreb8fGGJy1Qx0yzGVXxvHJN1WXgkFNHFheVv1iBJNqgvp+xeT3ienJmRwXmPPc5Es/cxBrXtKZJR3i7iw==} + '@microsoft/api-extractor-model@7.32.1': + resolution: {integrity: sha512-u4yJytMYiUAnhcNQcZDTh/tVtlrzKlyKrQnLOV+4Qr/5gV+cpufWzCYAB1Q23URFqD6z2RoL2UYncM9xJVGNKA==} - '@microsoft/api-extractor@7.55.0': - resolution: {integrity: sha512-TYc5OtAK/9E3HGgd2bIfSjQDYIwPc0dysf9rPiwXZGsq916I6W2oww9bhm1OxPOeg6rMfOX3PoroGd7oCryYog==} + '@microsoft/api-extractor@7.55.1': + resolution: {integrity: sha512-l8Z+8qrLkZFM3HM95Dbpqs6G39fpCa7O5p8A7AkA6hSevxkgwsOlLrEuPv0ADOyj5dI1Af5WVDiwpKG/ya5G3w==} hasBin: true '@microsoft/tsdoc-config@0.18.0': @@ -459,6 +466,9 @@ packages: '@rolldown/pluginutils@1.0.0-beta.50': resolution: {integrity: sha512-5e76wQiQVeL1ICOZVUg4LSOVYg9jyhGCin+icYozhsUzM+fHE7kddi1bdiE0jwVqTfkjba3jUFbEkoC9WkdvyA==} + '@rolldown/pluginutils@1.0.0-beta.51': + resolution: {integrity: sha512-51/8cNXMrqWqX3o8DZidhwz1uYq0BhHDDSfVygAND1Skx5s1TDw3APSSxCMcFFedwgqGcx34gRouwY+m404BBQ==} + '@rollup/pluginutils@5.3.0': resolution: {integrity: sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==} engines: {node: '>=14.0.0'} @@ -468,118 +478,118 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.53.2': - resolution: {integrity: sha512-yDPzwsgiFO26RJA4nZo8I+xqzh7sJTZIWQOxn+/XOdPE31lAvLIYCKqjV+lNH/vxE2L2iH3plKxDCRK6i+CwhA==} + '@rollup/rollup-android-arm-eabi@4.53.3': + resolution: {integrity: sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.53.2': - resolution: {integrity: sha512-k8FontTxIE7b0/OGKeSN5B6j25EuppBcWM33Z19JoVT7UTXFSo3D9CdU39wGTeb29NO3XxpMNauh09B+Ibw+9g==} + '@rollup/rollup-android-arm64@4.53.3': + resolution: {integrity: sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.53.2': - resolution: {integrity: sha512-A6s4gJpomNBtJ2yioj8bflM2oogDwzUiMl2yNJ2v9E7++sHrSrsQ29fOfn5DM/iCzpWcebNYEdXpaK4tr2RhfQ==} + '@rollup/rollup-darwin-arm64@4.53.3': + resolution: {integrity: sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.53.2': - resolution: {integrity: sha512-e6XqVmXlHrBlG56obu9gDRPW3O3hLxpwHpLsBJvuI8qqnsrtSZ9ERoWUXtPOkY8c78WghyPHZdmPhHLWNdAGEw==} + '@rollup/rollup-darwin-x64@4.53.3': + resolution: {integrity: sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.53.2': - resolution: {integrity: sha512-v0E9lJW8VsrwPux5Qe5CwmH/CF/2mQs6xU1MF3nmUxmZUCHazCjLgYvToOk+YuuUqLQBio1qkkREhxhc656ViA==} + '@rollup/rollup-freebsd-arm64@4.53.3': + resolution: {integrity: sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.53.2': - resolution: {integrity: sha512-ClAmAPx3ZCHtp6ysl4XEhWU69GUB1D+s7G9YjHGhIGCSrsg00nEGRRZHmINYxkdoJehde8VIsDC5t9C0gb6yqA==} + '@rollup/rollup-freebsd-x64@4.53.3': + resolution: {integrity: sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.53.2': - resolution: {integrity: sha512-EPlb95nUsz6Dd9Qy13fI5kUPXNSljaG9FiJ4YUGU1O/Q77i5DYFW5KR8g1OzTcdZUqQQ1KdDqsTohdFVwCwjqg==} + '@rollup/rollup-linux-arm-gnueabihf@4.53.3': + resolution: {integrity: sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.53.2': - resolution: {integrity: sha512-BOmnVW+khAUX+YZvNfa0tGTEMVVEerOxN0pDk2E6N6DsEIa2Ctj48FOMfNDdrwinocKaC7YXUZ1pHlKpnkja/Q==} + '@rollup/rollup-linux-arm-musleabihf@4.53.3': + resolution: {integrity: sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.53.2': - resolution: {integrity: sha512-Xt2byDZ+6OVNuREgBXr4+CZDJtrVso5woFtpKdGPhpTPHcNG7D8YXeQzpNbFRxzTVqJf7kvPMCub/pcGUWgBjA==} + '@rollup/rollup-linux-arm64-gnu@4.53.3': + resolution: {integrity: sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.53.2': - resolution: {integrity: sha512-+LdZSldy/I9N8+klim/Y1HsKbJ3BbInHav5qE9Iy77dtHC/pibw1SR/fXlWyAk0ThnpRKoODwnAuSjqxFRDHUQ==} + '@rollup/rollup-linux-arm64-musl@4.53.3': + resolution: {integrity: sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loong64-gnu@4.53.2': - resolution: {integrity: sha512-8ms8sjmyc1jWJS6WdNSA23rEfdjWB30LH8Wqj0Cqvv7qSHnvw6kgMMXRdop6hkmGPlyYBdRPkjJnj3KCUHV/uQ==} + '@rollup/rollup-linux-loong64-gnu@4.53.3': + resolution: {integrity: sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.53.2': - resolution: {integrity: sha512-3HRQLUQbpBDMmzoxPJYd3W6vrVHOo2cVW8RUo87Xz0JPJcBLBr5kZ1pGcQAhdZgX9VV7NbGNipah1omKKe23/g==} + '@rollup/rollup-linux-ppc64-gnu@4.53.3': + resolution: {integrity: sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.53.2': - resolution: {integrity: sha512-fMjKi+ojnmIvhk34gZP94vjogXNNUKMEYs+EDaB/5TG/wUkoeua7p7VCHnE6T2Tx+iaghAqQX8teQzcvrYpaQA==} + '@rollup/rollup-linux-riscv64-gnu@4.53.3': + resolution: {integrity: sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.53.2': - resolution: {integrity: sha512-XuGFGU+VwUUV5kLvoAdi0Wz5Xbh2SrjIxCtZj6Wq8MDp4bflb/+ThZsVxokM7n0pcbkEr2h5/pzqzDYI7cCgLQ==} + '@rollup/rollup-linux-riscv64-musl@4.53.3': + resolution: {integrity: sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.53.2': - resolution: {integrity: sha512-w6yjZF0P+NGzWR3AXWX9zc0DNEGdtvykB03uhonSHMRa+oWA6novflo2WaJr6JZakG2ucsyb+rvhrKac6NIy+w==} + '@rollup/rollup-linux-s390x-gnu@4.53.3': + resolution: {integrity: sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.53.2': - resolution: {integrity: sha512-yo8d6tdfdeBArzC7T/PnHd7OypfI9cbuZzPnzLJIyKYFhAQ8SvlkKtKBMbXDxe1h03Rcr7u++nFS7tqXz87Gtw==} + '@rollup/rollup-linux-x64-gnu@4.53.3': + resolution: {integrity: sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.53.2': - resolution: {integrity: sha512-ah59c1YkCxKExPP8O9PwOvs+XRLKwh/mV+3YdKqQ5AMQ0r4M4ZDuOrpWkUaqO7fzAHdINzV9tEVu8vNw48z0lA==} + '@rollup/rollup-linux-x64-musl@4.53.3': + resolution: {integrity: sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==} cpu: [x64] os: [linux] - '@rollup/rollup-openharmony-arm64@4.53.2': - resolution: {integrity: sha512-4VEd19Wmhr+Zy7hbUsFZ6YXEiP48hE//KPLCSVNY5RMGX2/7HZ+QkN55a3atM1C/BZCGIgqN+xrVgtdak2S9+A==} + '@rollup/rollup-openharmony-arm64@4.53.3': + resolution: {integrity: sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.53.2': - resolution: {integrity: sha512-IlbHFYc/pQCgew/d5fslcy1KEaYVCJ44G8pajugd8VoOEI8ODhtb/j8XMhLpwHCMB3yk2J07ctup10gpw2nyMA==} + '@rollup/rollup-win32-arm64-msvc@4.53.3': + resolution: {integrity: sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.53.2': - resolution: {integrity: sha512-lNlPEGgdUfSzdCWU176ku/dQRnA7W+Gp8d+cWv73jYrb8uT7HTVVxq62DUYxjbaByuf1Yk0RIIAbDzp+CnOTFg==} + '@rollup/rollup-win32-ia32-msvc@4.53.3': + resolution: {integrity: sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.53.2': - resolution: {integrity: sha512-S6YojNVrHybQis2lYov1sd+uj7K0Q05NxHcGktuMMdIQ2VixGwAfbJ23NnlvvVV1bdpR2m5MsNBViHJKcA4ADw==} + '@rollup/rollup-win32-x64-gnu@4.53.3': + resolution: {integrity: sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.53.2': - resolution: {integrity: sha512-k+/Rkcyx//P6fetPoLMb8pBeqJBNGx81uuf7iljX9++yNBVRDQgD04L+SVXmXmh5ZP4/WOp4mWF0kmi06PW2tA==} + '@rollup/rollup-win32-x64-msvc@4.53.3': + resolution: {integrity: sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==} cpu: [x64] os: [win32] - '@rushstack/node-core-library@5.18.0': - resolution: {integrity: sha512-XDebtBdw5S3SuZIt+Ra2NieT8kQ3D2Ow1HxhDQ/2soinswnOu9e7S69VSwTOLlQnx5mpWbONu+5JJjDxMAb6Fw==} + '@rushstack/node-core-library@5.19.0': + resolution: {integrity: sha512-BxAopbeWBvNJ6VGiUL+5lbJXywTdsnMeOS8j57Cn/xY10r6sV/gbsTlfYKjzVCUBZATX2eRzJHSMCchsMTGN6A==} peerDependencies: '@types/node': '*' peerDependenciesMeta: @@ -597,16 +607,16 @@ packages: '@rushstack/rig-package@0.6.0': resolution: {integrity: sha512-ZQmfzsLE2+Y91GF15c65L/slMRVhF6Hycq04D4TwtdGaUAbIXXg9c5pKA5KFU7M4QMaihoobp9JJYpYcaY3zOw==} - '@rushstack/terminal@0.19.3': - resolution: {integrity: sha512-0P8G18gK9STyO+CNBvkKPnWGMxESxecTYqOcikHOVIHXa9uAuTK+Fw8TJq2Gng1w7W6wTC9uPX6hGNvrMll2wA==} + '@rushstack/terminal@0.19.4': + resolution: {integrity: sha512-f4XQk02CrKfrMgyOfhYd3qWI944dLC21S4I/LUhrlAP23GTMDNG6EK5effQtFkISwUKCgD9vMBrJZaPSUquxWQ==} peerDependencies: '@types/node': '*' peerDependenciesMeta: '@types/node': optional: true - '@rushstack/ts-command-line@5.1.3': - resolution: {integrity: sha512-Kdv0k/BnnxIYFlMVC1IxrIS0oGQd4T4b7vKfx52Y2+wk2WZSDFIvedr7JrhenzSlm3ou5KwtoTGTGd5nbODRug==} + '@rushstack/ts-command-line@5.1.4': + resolution: {integrity: sha512-H0I6VdJ6sOUbktDFpP2VW5N29w8v4hRoNZOQz02vtEi6ZTYL1Ju8u+TcFiFawUDrUsx/5MQTUhd79uwZZVwVlA==} '@standard-schema/spec@1.0.0': resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} @@ -738,22 +748,31 @@ packages: vite: ^5.0.0 || ^6.0.0 || ^7.0.0 vue: ^3.2.25 - '@vitest/browser-playwright@4.0.12': - resolution: {integrity: sha512-TCFuvEHVLHDuK4HmKJNSuYQwefpXBnWNuf3J8pLsXF3Q2sgRZJs+L+aUF2xjtJT5OBEJsgX5jENvlGKc596XNw==} + '@vitest/browser-playwright@4.0.13': + resolution: {integrity: sha512-oaRY+/pvwS4/sN2rE2aZh9jdli8EkXm5AidmXEbWRu2wW0omG9PmgChWCX2jsD9qRLQxXTSLl5oKezANNF6LnQ==} peerDependencies: playwright: '*' - vitest: 4.0.12 + vitest: 4.0.13 - '@vitest/browser@4.0.12': - resolution: {integrity: sha512-8zE2ksJ7V4B7Mc++L6rBRZOZHnE/f9URvj7oLYKIS5wcDaSi6EhfalN0EG6+R/OlTYZarbK6RqmhKDLYNC9KfQ==} + '@vitest/browser@4.0.13': + resolution: {integrity: sha512-lruSgrYPVAJzKmX6EJYCg9nY+0A4VkeTLpTzf1jRD/XMjNbzD9yy7D499xmVKglwJczANYJXBvZSPGcRlon+0w==} peerDependencies: - vitest: 4.0.12 + vitest: 4.0.13 - '@vitest/expect@4.0.12': - resolution: {integrity: sha512-is+g0w8V3/ZhRNrRizrJNr8PFQKwYmctWlU4qg8zy5r9aIV5w8IxXLlfbbxJCwSpsVl2PXPTm2/zruqTqz3QSg==} + '@vitest/coverage-v8@4.0.13': + resolution: {integrity: sha512-w77N6bmtJ3CFnL/YHiYotwW/JI3oDlR3K38WEIqegRfdMSScaYxwYKB/0jSNpOTZzUjQkG8HHEz4sdWQMWpQ5g==} + peerDependencies: + '@vitest/browser': 4.0.13 + vitest: 4.0.13 + peerDependenciesMeta: + '@vitest/browser': + optional: true - '@vitest/mocker@4.0.12': - resolution: {integrity: sha512-GsmA/tD5Ht3RUFoz41mZsMU1AXch3lhmgbTnoSPTdH231g7S3ytNN1aU0bZDSyxWs8WA7KDyMPD5L4q6V6vj9w==} + '@vitest/expect@4.0.13': + resolution: {integrity: sha512-zYtcnNIBm6yS7Gpr7nFTmq8ncowlMdOJkWLqYvhr/zweY6tFbDkDi8BPPOeHxEtK1rSI69H7Fd4+1sqvEGli6w==} + + '@vitest/mocker@4.0.13': + resolution: {integrity: sha512-eNCwzrI5djoauklwP1fuslHBjrbR8rqIVbvNlAnkq1OTa6XT+lX68mrtPirNM9TnR69XUPt4puBCx2Wexseylg==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0-0 @@ -763,25 +782,25 @@ packages: vite: optional: true - '@vitest/pretty-format@4.0.12': - resolution: {integrity: sha512-R7nMAcnienG17MvRN8TPMJiCG8rrZJblV9mhT7oMFdBXvS0x+QD6S1G4DxFusR2E0QIS73f7DqSR1n87rrmE+g==} + '@vitest/pretty-format@4.0.13': + resolution: {integrity: sha512-ooqfze8URWbI2ozOeLDMh8YZxWDpGXoeY3VOgcDnsUxN0jPyPWSUvjPQWqDGCBks+opWlN1E4oP1UYl3C/2EQA==} - '@vitest/runner@4.0.12': - resolution: {integrity: sha512-hDlCIJWuwlcLumfukPsNfPDOJokTv79hnOlf11V+n7E14rHNPz0Sp/BO6h8sh9qw4/UjZiKyYpVxK2ZNi+3ceQ==} + '@vitest/runner@4.0.13': + resolution: {integrity: sha512-9IKlAru58wcVaWy7hz6qWPb2QzJTKt+IOVKjAx5vb5rzEFPTL6H4/R9BMvjZ2ppkxKgTrFONEJFtzvnyEpiT+A==} - '@vitest/snapshot@4.0.12': - resolution: {integrity: sha512-2jz9zAuBDUSbnfyixnyOd1S2YDBrZO23rt1bicAb6MA/ya5rHdKFRikPIDpBj/Dwvh6cbImDmudegnDAkHvmRQ==} + '@vitest/snapshot@4.0.13': + resolution: {integrity: sha512-hb7Usvyika1huG6G6l191qu1urNPsq1iFc2hmdzQY3F5/rTgqQnwwplyf8zoYHkpt7H6rw5UfIw6i/3qf9oSxQ==} - '@vitest/spy@4.0.12': - resolution: {integrity: sha512-GZjI9PPhiOYNX8Nsyqdw7JQB+u0BptL5fSnXiottAUBHlcMzgADV58A7SLTXXQwcN1yZ6gfd1DH+2bqjuUlCzw==} + '@vitest/spy@4.0.13': + resolution: {integrity: sha512-hSu+m4se0lDV5yVIcNWqjuncrmBgwaXa2utFLIrBkQCQkt+pSwyZTPFQAZiiF/63j8jYa8uAeUZ3RSfcdWaYWw==} - '@vitest/ui@4.0.12': - resolution: {integrity: sha512-RCqeApCnbwd5IFvxk6OeKMXTvzHU/cVqY8HAW0gWk0yAO6wXwQJMKhDfDtk2ss7JCy9u7RNC3kyazwiaDhBA/g==} + '@vitest/ui@4.0.13': + resolution: {integrity: sha512-MFV6GhTflgBj194+vowTB2iLI5niMZhqiW7/NV7U4AfWbX/IAtsq4zA+gzCLyGzpsQUdJlX26hrQ1vuWShq2BQ==} peerDependencies: - vitest: 4.0.12 + vitest: 4.0.13 - '@vitest/utils@4.0.12': - resolution: {integrity: sha512-DVS/TLkLdvGvj1avRy0LSmKfrcI9MNFvNGN6ECjTUHWJdlcgPDOXhjMis5Dh7rBH62nAmSXnkPbE+DZ5YD75Rw==} + '@vitest/utils@4.0.13': + resolution: {integrity: sha512-ydozWyQ4LZuu8rLp47xFUWis5VOKMdHjXCWhs1LuJsTNKww+pTHQNK4e0assIB9K80TxFyskENL6vCu3j34EYA==} '@volar/language-core@2.4.23': resolution: {integrity: sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==} @@ -831,8 +850,8 @@ packages: typescript: optional: true - '@vue/language-core@3.1.4': - resolution: {integrity: sha512-n/58wm8SkmoxMWkUNUH/PwoovWe4hmdyPJU2ouldr3EPi1MLoS7iDN46je8CsP95SnVBs2axInzRglPNKvqMcg==} + '@vue/language-core@3.1.5': + resolution: {integrity: sha512-FMcqyzWN+sYBeqRMWPGT2QY0mUasZMVIuHvmb5NT3eeqPrbHBYtCP8JWEUCDCgM+Zr62uuWY/qoeBrPrzfa78w==} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -923,6 +942,9 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} + ast-v8-to-istanbul@0.3.8: + resolution: {integrity: sha512-szgSZqUxI5T8mLKvS7WTjF9is+MVbOeLADU73IseOcrqhxr/VAvy6wfoVE39KnKzA7JRhjF5eUagNlHwvZPlKQ==} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -1139,6 +1161,9 @@ packages: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} engines: {node: '>=18'} + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + http-proxy-agent@7.0.2: resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} engines: {node: '>= 14'} @@ -1172,6 +1197,22 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-lib-source-maps@5.0.6: + resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} + engines: {node: '>=10'} + + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} + jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} @@ -1194,6 +1235,9 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + jsdom@27.2.0: resolution: {integrity: sha512-454TI39PeRDW1LgpyLPyURtB4Zx1tklSr6+OFOipsxGUH1WMTvk6C65JQdrj455+DP2uJ1+veBEHTGFKWVLFoA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} @@ -1316,6 +1360,13 @@ packages: magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + magicast@0.5.1: + resolution: {integrity: sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw==} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + mdn-data@2.12.2: resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==} @@ -1438,8 +1489,8 @@ packages: engines: {node: '>= 0.4'} hasBin: true - rollup@4.53.2: - resolution: {integrity: sha512-MHngMYwGJVi6Fmnk6ISmnk7JAHRNF0UkuucA0CUW3N3a4KnONPEZz+vUanQP/ZC/iY1Qkf3bwPWzyY84wEks1g==} + rollup@4.53.3: + resolution: {integrity: sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -1459,6 +1510,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} + hasBin: true + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -1519,6 +1575,10 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + supports-color@8.1.1: resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} engines: {node: '>=10'} @@ -1551,11 +1611,11 @@ packages: resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} engines: {node: '>=14.0.0'} - tldts-core@7.0.18: - resolution: {integrity: sha512-jqJC13oP4FFAahv4JT/0WTDrCF9Okv7lpKtOZUGPLiAnNbACcSg8Y8T+Z9xthOmRBqi/Sob4yi0TE0miRCvF7Q==} + tldts-core@7.0.19: + resolution: {integrity: sha512-lJX2dEWx0SGH4O6p+7FPwYmJ/bu1JbcGJ8RLaG9b7liIgZ85itUVEPbMtWRVrde/0fnDPEPHW10ZsKW3kVsE9A==} - tldts@7.0.18: - resolution: {integrity: sha512-lCcgTAgMxQ1JKOWrVGo6E69Ukbnx4Gc1wiYLRf6J5NN4HRYJtCby1rPF8rkQ4a6qqoFBK5dvjJ1zJ0F7VfDSvw==} + tldts@7.0.19: + resolution: {integrity: sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA==} hasBin: true totalist@3.0.1: @@ -1654,8 +1714,8 @@ packages: vitest: ^4.0.0-0 vue: ^3.0.0 - vitest@4.0.12: - resolution: {integrity: sha512-pmW4GCKQ8t5Ko1jYjC3SqOr7TUKN7uHOHB/XGsAIb69eYu6d1ionGSsb5H9chmPf+WeXt0VE7jTXsB1IvWoNbw==} + vitest@4.0.13: + resolution: {integrity: sha512-QSD4I0fN6uZQfftryIXuqvqgBxTvJ3ZNkF6RWECd82YGAYAfhcppBLFXzXJHQAAhVFyYEuFTrq6h0hQqjB7jIQ==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: @@ -1663,10 +1723,10 @@ packages: '@opentelemetry/api': ^1.9.0 '@types/debug': ^4.1.12 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.0.12 - '@vitest/browser-preview': 4.0.12 - '@vitest/browser-webdriverio': 4.0.12 - '@vitest/ui': 4.0.12 + '@vitest/browser-playwright': 4.0.13 + '@vitest/browser-preview': 4.0.13 + '@vitest/browser-webdriverio': 4.0.13 + '@vitest/ui': 4.0.13 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -1697,8 +1757,8 @@ packages: vue-component-type-helpers@2.2.12: resolution: {integrity: sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==} - vue-tsc@3.1.4: - resolution: {integrity: sha512-GsRJxttj4WkmXW/zDwYPGMJAN3np/4jTzoDFQTpTsI5Vg/JKMWamBwamlmLihgSVHO66y9P7GX+uoliYxeI4Hw==} + vue-tsc@3.1.5: + resolution: {integrity: sha512-L/G9IUjOWhBU0yun89rv8fKqmKC+T0HfhrFjlIml71WpfBv9eb4E9Bev8FMbyueBIU9vxQqbd+oOsVcDa5amGw==} hasBin: true peerDependencies: typescript: '>=5.0.0' @@ -1776,7 +1836,7 @@ packages: snapshots: - '@acemir/cssom@0.9.23': {} + '@acemir/cssom@0.9.24': {} '@asamuzakjp/css-color@4.1.0': dependencies: @@ -1963,6 +2023,8 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@bcoe/v8-coverage@1.0.2': {} + '@bedard/hexchess@2.5.0': {} '@csstools/color-helpers@5.1.0': {} @@ -1983,7 +2045,7 @@ snapshots: dependencies: '@csstools/css-tokenizer': 3.0.4 - '@csstools/css-syntax-patches-for-csstree@1.0.16': {} + '@csstools/css-syntax-patches-for-csstree@1.0.17': {} '@csstools/css-tokenizer@3.0.4': {} @@ -2108,23 +2170,23 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@microsoft/api-extractor-model@7.32.0(@types/node@22.19.1)': + '@microsoft/api-extractor-model@7.32.1(@types/node@22.19.1)': dependencies: '@microsoft/tsdoc': 0.16.0 '@microsoft/tsdoc-config': 0.18.0 - '@rushstack/node-core-library': 5.18.0(@types/node@22.19.1) + '@rushstack/node-core-library': 5.19.0(@types/node@22.19.1) transitivePeerDependencies: - '@types/node' - '@microsoft/api-extractor@7.55.0(@types/node@22.19.1)': + '@microsoft/api-extractor@7.55.1(@types/node@22.19.1)': dependencies: - '@microsoft/api-extractor-model': 7.32.0(@types/node@22.19.1) + '@microsoft/api-extractor-model': 7.32.1(@types/node@22.19.1) '@microsoft/tsdoc': 0.16.0 '@microsoft/tsdoc-config': 0.18.0 - '@rushstack/node-core-library': 5.18.0(@types/node@22.19.1) + '@rushstack/node-core-library': 5.19.0(@types/node@22.19.1) '@rushstack/rig-package': 0.6.0 - '@rushstack/terminal': 0.19.3(@types/node@22.19.1) - '@rushstack/ts-command-line': 5.1.3(@types/node@22.19.1) + '@rushstack/terminal': 0.19.4(@types/node@22.19.1) + '@rushstack/ts-command-line': 5.1.4(@types/node@22.19.1) diff: 8.0.2 lodash: 4.17.21 minimatch: 10.0.3 @@ -2153,81 +2215,83 @@ snapshots: '@rolldown/pluginutils@1.0.0-beta.50': {} - '@rollup/pluginutils@5.3.0(rollup@4.53.2)': + '@rolldown/pluginutils@1.0.0-beta.51': {} + + '@rollup/pluginutils@5.3.0(rollup@4.53.3)': dependencies: '@types/estree': 1.0.8 estree-walker: 2.0.2 picomatch: 4.0.3 optionalDependencies: - rollup: 4.53.2 + rollup: 4.53.3 - '@rollup/rollup-android-arm-eabi@4.53.2': + '@rollup/rollup-android-arm-eabi@4.53.3': optional: true - '@rollup/rollup-android-arm64@4.53.2': + '@rollup/rollup-android-arm64@4.53.3': optional: true - '@rollup/rollup-darwin-arm64@4.53.2': + '@rollup/rollup-darwin-arm64@4.53.3': optional: true - '@rollup/rollup-darwin-x64@4.53.2': + '@rollup/rollup-darwin-x64@4.53.3': optional: true - '@rollup/rollup-freebsd-arm64@4.53.2': + '@rollup/rollup-freebsd-arm64@4.53.3': optional: true - '@rollup/rollup-freebsd-x64@4.53.2': + '@rollup/rollup-freebsd-x64@4.53.3': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.53.2': + '@rollup/rollup-linux-arm-gnueabihf@4.53.3': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.53.2': + '@rollup/rollup-linux-arm-musleabihf@4.53.3': optional: true - '@rollup/rollup-linux-arm64-gnu@4.53.2': + '@rollup/rollup-linux-arm64-gnu@4.53.3': optional: true - '@rollup/rollup-linux-arm64-musl@4.53.2': + '@rollup/rollup-linux-arm64-musl@4.53.3': optional: true - '@rollup/rollup-linux-loong64-gnu@4.53.2': + '@rollup/rollup-linux-loong64-gnu@4.53.3': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.53.2': + '@rollup/rollup-linux-ppc64-gnu@4.53.3': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.53.2': + '@rollup/rollup-linux-riscv64-gnu@4.53.3': optional: true - '@rollup/rollup-linux-riscv64-musl@4.53.2': + '@rollup/rollup-linux-riscv64-musl@4.53.3': optional: true - '@rollup/rollup-linux-s390x-gnu@4.53.2': + '@rollup/rollup-linux-s390x-gnu@4.53.3': optional: true - '@rollup/rollup-linux-x64-gnu@4.53.2': + '@rollup/rollup-linux-x64-gnu@4.53.3': optional: true - '@rollup/rollup-linux-x64-musl@4.53.2': + '@rollup/rollup-linux-x64-musl@4.53.3': optional: true - '@rollup/rollup-openharmony-arm64@4.53.2': + '@rollup/rollup-openharmony-arm64@4.53.3': optional: true - '@rollup/rollup-win32-arm64-msvc@4.53.2': + '@rollup/rollup-win32-arm64-msvc@4.53.3': optional: true - '@rollup/rollup-win32-ia32-msvc@4.53.2': + '@rollup/rollup-win32-ia32-msvc@4.53.3': optional: true - '@rollup/rollup-win32-x64-gnu@4.53.2': + '@rollup/rollup-win32-x64-gnu@4.53.3': optional: true - '@rollup/rollup-win32-x64-msvc@4.53.2': + '@rollup/rollup-win32-x64-msvc@4.53.3': optional: true - '@rushstack/node-core-library@5.18.0(@types/node@22.19.1)': + '@rushstack/node-core-library@5.19.0(@types/node@22.19.1)': dependencies: ajv: 8.13.0 ajv-draft-04: 1.0.0(ajv@8.13.0) @@ -2249,17 +2313,17 @@ snapshots: resolve: 1.22.11 strip-json-comments: 3.1.1 - '@rushstack/terminal@0.19.3(@types/node@22.19.1)': + '@rushstack/terminal@0.19.4(@types/node@22.19.1)': dependencies: - '@rushstack/node-core-library': 5.18.0(@types/node@22.19.1) + '@rushstack/node-core-library': 5.19.0(@types/node@22.19.1) '@rushstack/problem-matcher': 0.1.1(@types/node@22.19.1) supports-color: 8.1.1 optionalDependencies: '@types/node': 22.19.1 - '@rushstack/ts-command-line@5.1.3(@types/node@22.19.1)': + '@rushstack/ts-command-line@5.1.4(@types/node@22.19.1)': dependencies: - '@rushstack/terminal': 0.19.3(@types/node@22.19.1) + '@rushstack/terminal': 0.19.4(@types/node@22.19.1) '@types/argparse': 1.0.38 argparse: 1.0.10 string-argv: 0.3.2 @@ -2363,7 +2427,7 @@ snapshots: '@babel/core': 7.28.5 '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5) '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.28.5) - '@rolldown/pluginutils': 1.0.0-beta.50 + '@rolldown/pluginutils': 1.0.0-beta.51 '@vue/babel-plugin-jsx': 2.0.1(@babel/core@7.28.5) vite: 6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2) vue: 3.5.24(typescript@5.9.3) @@ -2376,29 +2440,29 @@ snapshots: vite: 6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2) vue: 3.5.24(typescript@5.9.3) - '@vitest/browser-playwright@4.0.12(playwright@1.56.1)(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))(vitest@4.0.12)': + '@vitest/browser-playwright@4.0.13(playwright@1.56.1)(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))(vitest@4.0.13)': dependencies: - '@vitest/browser': 4.0.12(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))(vitest@4.0.12(@types/node@22.19.1)(@vitest/browser-playwright@4.0.12)(@vitest/ui@4.0.12)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2)) - '@vitest/mocker': 4.0.12(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)) + '@vitest/browser': 4.0.13(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))(vitest@4.0.13(@types/node@22.19.1)(@vitest/browser-playwright@4.0.13)(@vitest/ui@4.0.13)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2)) + '@vitest/mocker': 4.0.13(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)) playwright: 1.56.1 tinyrainbow: 3.0.3 - vitest: 4.0.12(@types/node@22.19.1)(@vitest/browser-playwright@4.0.12)(@vitest/ui@4.0.12)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2) + vitest: 4.0.13(@types/node@22.19.1)(@vitest/browser-playwright@4.0.13)(@vitest/ui@4.0.13)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2) transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite - '@vitest/browser@4.0.12(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))(vitest@4.0.12(@types/node@22.19.1)(@vitest/browser-playwright@4.0.12)(@vitest/ui@4.0.12)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2))': + '@vitest/browser@4.0.13(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))(vitest@4.0.13(@types/node@22.19.1)(@vitest/browser-playwright@4.0.13)(@vitest/ui@4.0.13)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2))': dependencies: - '@vitest/mocker': 4.0.12(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)) - '@vitest/utils': 4.0.12 + '@vitest/mocker': 4.0.13(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)) + '@vitest/utils': 4.0.13 magic-string: 0.30.21 pixelmatch: 7.1.0 pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.0.3 - vitest: 4.0.12(@types/node@22.19.1)(@vitest/browser-playwright@4.0.12)(@vitest/ui@4.0.12)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2) + vitest: 4.0.13(@types/node@22.19.1)(@vitest/browser-playwright@4.0.13)(@vitest/ui@4.0.13)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2) ws: 8.18.3 transitivePeerDependencies: - bufferutil @@ -2406,54 +2470,73 @@ snapshots: - utf-8-validate - vite - '@vitest/expect@4.0.12': + '@vitest/coverage-v8@4.0.13(@vitest/browser@4.0.13(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))(vitest@4.0.13(@types/node@22.19.1)(@vitest/browser-playwright@4.0.13)(@vitest/ui@4.0.13)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2)))(vitest@4.0.13(@types/node@22.19.1)(@vitest/browser-playwright@4.0.13)(@vitest/ui@4.0.13)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2))': + dependencies: + '@bcoe/v8-coverage': 1.0.2 + '@vitest/utils': 4.0.13 + ast-v8-to-istanbul: 0.3.8 + debug: 4.4.3 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 5.0.6 + istanbul-reports: 3.2.0 + magicast: 0.5.1 + std-env: 3.10.0 + tinyrainbow: 3.0.3 + vitest: 4.0.13(@types/node@22.19.1)(@vitest/browser-playwright@4.0.13)(@vitest/ui@4.0.13)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2) + optionalDependencies: + '@vitest/browser': 4.0.13(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))(vitest@4.0.13(@types/node@22.19.1)(@vitest/browser-playwright@4.0.13)(@vitest/ui@4.0.13)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2)) + transitivePeerDependencies: + - supports-color + + '@vitest/expect@4.0.13': dependencies: '@standard-schema/spec': 1.0.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.0.12 - '@vitest/utils': 4.0.12 + '@vitest/spy': 4.0.13 + '@vitest/utils': 4.0.13 chai: 6.2.1 tinyrainbow: 3.0.3 - '@vitest/mocker@4.0.12(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))': + '@vitest/mocker@4.0.13(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))': dependencies: - '@vitest/spy': 4.0.12 + '@vitest/spy': 4.0.13 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: vite: 6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2) - '@vitest/pretty-format@4.0.12': + '@vitest/pretty-format@4.0.13': dependencies: tinyrainbow: 3.0.3 - '@vitest/runner@4.0.12': + '@vitest/runner@4.0.13': dependencies: - '@vitest/utils': 4.0.12 + '@vitest/utils': 4.0.13 pathe: 2.0.3 - '@vitest/snapshot@4.0.12': + '@vitest/snapshot@4.0.13': dependencies: - '@vitest/pretty-format': 4.0.12 + '@vitest/pretty-format': 4.0.13 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.0.12': {} + '@vitest/spy@4.0.13': {} - '@vitest/ui@4.0.12(vitest@4.0.12)': + '@vitest/ui@4.0.13(vitest@4.0.13)': dependencies: - '@vitest/utils': 4.0.12 + '@vitest/utils': 4.0.13 fflate: 0.8.2 flatted: 3.3.3 pathe: 2.0.3 sirv: 3.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vitest: 4.0.12(@types/node@22.19.1)(@vitest/browser-playwright@4.0.12)(@vitest/ui@4.0.12)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2) + vitest: 4.0.13(@types/node@22.19.1)(@vitest/browser-playwright@4.0.13)(@vitest/ui@4.0.13)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2) - '@vitest/utils@4.0.12': + '@vitest/utils@4.0.13': dependencies: - '@vitest/pretty-format': 4.0.12 + '@vitest/pretty-format': 4.0.13 tinyrainbow: 3.0.3 '@volar/language-core@2.4.23': @@ -2545,7 +2628,7 @@ snapshots: optionalDependencies: typescript: 5.9.3 - '@vue/language-core@3.1.4(typescript@5.9.3)': + '@vue/language-core@3.1.5(typescript@5.9.3)': dependencies: '@volar/language-core': 2.4.23 '@vue/compiler-dom': 3.5.24 @@ -2634,6 +2717,12 @@ snapshots: assertion-error@2.0.1: {} + ast-v8-to-istanbul@0.3.8: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + estree-walker: 3.0.3 + js-tokens: 9.0.1 + balanced-match@1.0.2: {} baseline-browser-mapping@2.8.30: {} @@ -2693,7 +2782,7 @@ snapshots: cssstyle@5.3.3: dependencies: '@asamuzakjp/css-color': 4.1.0 - '@csstools/css-syntax-patches-for-csstree': 1.0.16 + '@csstools/css-syntax-patches-for-csstree': 1.0.17 css-tree: 3.1.0 csstype@3.2.3: {} @@ -2722,7 +2811,7 @@ snapshots: '@one-ini/wasm': 0.1.1 commander: 10.0.1 minimatch: 9.0.1 - semver: 7.5.4 + semver: 7.7.3 electron-to-chromium@1.5.259: {} @@ -2838,6 +2927,8 @@ snapshots: dependencies: whatwg-encoding: 3.1.1 + html-escaper@2.0.2: {} + http-proxy-agent@7.0.2: dependencies: agent-base: 7.1.4 @@ -2870,6 +2961,27 @@ snapshots: isexe@2.0.0: {} + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-lib-source-maps@5.0.6: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + debug: 4.4.3 + istanbul-lib-coverage: 3.2.2 + transitivePeerDependencies: + - supports-color + + istanbul-reports@3.2.0: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + jackspeak@3.4.3: dependencies: '@isaacs/cliui': 8.0.2 @@ -2892,9 +3004,11 @@ snapshots: js-tokens@4.0.0: {} + js-tokens@9.0.1: {} + jsdom@27.2.0: dependencies: - '@acemir/cssom': 0.9.23 + '@acemir/cssom': 0.9.24 '@asamuzakjp/dom-selector': 6.7.4 cssstyle: 5.3.3 data-urls: 6.0.0 @@ -3006,6 +3120,16 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 + magicast@0.5.1: + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + source-map-js: 1.2.1 + + make-dir@4.0.0: + dependencies: + semver: 7.7.3 + mdn-data@2.12.2: {} minimatch@10.0.3: @@ -3112,32 +3236,32 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - rollup@4.53.2: + rollup@4.53.3: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.53.2 - '@rollup/rollup-android-arm64': 4.53.2 - '@rollup/rollup-darwin-arm64': 4.53.2 - '@rollup/rollup-darwin-x64': 4.53.2 - '@rollup/rollup-freebsd-arm64': 4.53.2 - '@rollup/rollup-freebsd-x64': 4.53.2 - '@rollup/rollup-linux-arm-gnueabihf': 4.53.2 - '@rollup/rollup-linux-arm-musleabihf': 4.53.2 - '@rollup/rollup-linux-arm64-gnu': 4.53.2 - '@rollup/rollup-linux-arm64-musl': 4.53.2 - '@rollup/rollup-linux-loong64-gnu': 4.53.2 - '@rollup/rollup-linux-ppc64-gnu': 4.53.2 - '@rollup/rollup-linux-riscv64-gnu': 4.53.2 - '@rollup/rollup-linux-riscv64-musl': 4.53.2 - '@rollup/rollup-linux-s390x-gnu': 4.53.2 - '@rollup/rollup-linux-x64-gnu': 4.53.2 - '@rollup/rollup-linux-x64-musl': 4.53.2 - '@rollup/rollup-openharmony-arm64': 4.53.2 - '@rollup/rollup-win32-arm64-msvc': 4.53.2 - '@rollup/rollup-win32-ia32-msvc': 4.53.2 - '@rollup/rollup-win32-x64-gnu': 4.53.2 - '@rollup/rollup-win32-x64-msvc': 4.53.2 + '@rollup/rollup-android-arm-eabi': 4.53.3 + '@rollup/rollup-android-arm64': 4.53.3 + '@rollup/rollup-darwin-arm64': 4.53.3 + '@rollup/rollup-darwin-x64': 4.53.3 + '@rollup/rollup-freebsd-arm64': 4.53.3 + '@rollup/rollup-freebsd-x64': 4.53.3 + '@rollup/rollup-linux-arm-gnueabihf': 4.53.3 + '@rollup/rollup-linux-arm-musleabihf': 4.53.3 + '@rollup/rollup-linux-arm64-gnu': 4.53.3 + '@rollup/rollup-linux-arm64-musl': 4.53.3 + '@rollup/rollup-linux-loong64-gnu': 4.53.3 + '@rollup/rollup-linux-ppc64-gnu': 4.53.3 + '@rollup/rollup-linux-riscv64-gnu': 4.53.3 + '@rollup/rollup-linux-riscv64-musl': 4.53.3 + '@rollup/rollup-linux-s390x-gnu': 4.53.3 + '@rollup/rollup-linux-x64-gnu': 4.53.3 + '@rollup/rollup-linux-x64-musl': 4.53.3 + '@rollup/rollup-openharmony-arm64': 4.53.3 + '@rollup/rollup-win32-arm64-msvc': 4.53.3 + '@rollup/rollup-win32-ia32-msvc': 4.53.3 + '@rollup/rollup-win32-x64-gnu': 4.53.3 + '@rollup/rollup-win32-x64-msvc': 4.53.3 fsevents: 2.3.3 safer-buffer@2.1.2: {} @@ -3152,6 +3276,8 @@ snapshots: dependencies: lru-cache: 6.0.0 + semver@7.7.3: {} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -3202,6 +3328,10 @@ snapshots: strip-json-comments@3.1.1: {} + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + supports-color@8.1.1: dependencies: has-flag: 4.0.0 @@ -3225,17 +3355,17 @@ snapshots: tinyrainbow@3.0.3: {} - tldts-core@7.0.18: {} + tldts-core@7.0.19: {} - tldts@7.0.18: + tldts@7.0.19: dependencies: - tldts-core: 7.0.18 + tldts-core: 7.0.19 totalist@3.0.1: {} tough-cookie@6.0.0: dependencies: - tldts: 7.0.18 + tldts: 7.0.19 tr46@6.0.0: dependencies: @@ -3261,10 +3391,10 @@ snapshots: dependencies: punycode: 2.3.1 - vite-plugin-dts@4.5.4(@types/node@22.19.1)(rollup@4.53.2)(typescript@5.9.3)(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)): + vite-plugin-dts@4.5.4(@types/node@22.19.1)(rollup@4.53.3)(typescript@5.9.3)(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)): dependencies: - '@microsoft/api-extractor': 7.55.0(@types/node@22.19.1) - '@rollup/pluginutils': 5.3.0(rollup@4.53.2) + '@microsoft/api-extractor': 7.55.1(@types/node@22.19.1) + '@rollup/pluginutils': 5.3.0(rollup@4.53.3) '@volar/typescript': 2.4.23 '@vue/language-core': 2.2.0(typescript@5.9.3) compare-versions: 6.1.1 @@ -3286,7 +3416,7 @@ snapshots: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 - rollup: 4.53.2 + rollup: 4.53.3 tinyglobby: 0.2.15 optionalDependencies: '@types/node': 22.19.1 @@ -3294,21 +3424,21 @@ snapshots: jiti: 2.6.1 lightningcss: 1.30.2 - vitest-browser-vue@2.0.1(vitest@4.0.12(@types/node@22.19.1)(@vitest/browser-playwright@4.0.12)(@vitest/ui@4.0.12)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2))(vue@3.5.24(typescript@5.9.3)): + vitest-browser-vue@2.0.1(vitest@4.0.13(@types/node@22.19.1)(@vitest/browser-playwright@4.0.13)(@vitest/ui@4.0.13)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2))(vue@3.5.24(typescript@5.9.3)): dependencies: '@vue/test-utils': 2.4.6 - vitest: 4.0.12(@types/node@22.19.1)(@vitest/browser-playwright@4.0.12)(@vitest/ui@4.0.12)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2) + vitest: 4.0.13(@types/node@22.19.1)(@vitest/browser-playwright@4.0.13)(@vitest/ui@4.0.13)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2) vue: 3.5.24(typescript@5.9.3) - vitest@4.0.12(@types/node@22.19.1)(@vitest/browser-playwright@4.0.12)(@vitest/ui@4.0.12)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2): + vitest@4.0.13(@types/node@22.19.1)(@vitest/browser-playwright@4.0.13)(@vitest/ui@4.0.13)(jiti@2.6.1)(jsdom@27.2.0)(lightningcss@1.30.2): dependencies: - '@vitest/expect': 4.0.12 - '@vitest/mocker': 4.0.12(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)) - '@vitest/pretty-format': 4.0.12 - '@vitest/runner': 4.0.12 - '@vitest/snapshot': 4.0.12 - '@vitest/spy': 4.0.12 - '@vitest/utils': 4.0.12 + '@vitest/expect': 4.0.13 + '@vitest/mocker': 4.0.13(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2)) + '@vitest/pretty-format': 4.0.13 + '@vitest/runner': 4.0.13 + '@vitest/snapshot': 4.0.13 + '@vitest/spy': 4.0.13 + '@vitest/utils': 4.0.13 debug: 4.4.3 es-module-lexer: 1.7.0 expect-type: 1.2.2 @@ -3324,8 +3454,8 @@ snapshots: why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.19.1 - '@vitest/browser-playwright': 4.0.12(playwright@1.56.1)(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))(vitest@4.0.12) - '@vitest/ui': 4.0.12(vitest@4.0.12) + '@vitest/browser-playwright': 4.0.13(playwright@1.56.1)(vite@6.4.1(@types/node@22.19.1)(jiti@2.6.1)(lightningcss@1.30.2))(vitest@4.0.13) + '@vitest/ui': 4.0.13(vitest@4.0.13) jsdom: 27.2.0 transitivePeerDependencies: - jiti @@ -3345,10 +3475,10 @@ snapshots: vue-component-type-helpers@2.2.12: {} - vue-tsc@3.1.4(typescript@5.9.3): + vue-tsc@3.1.5(typescript@5.9.3): dependencies: '@volar/typescript': 2.4.23 - '@vue/language-core': 3.1.4(typescript@5.9.3) + '@vue/language-core': 3.1.5(typescript@5.9.3) typescript: 5.9.3 vue@3.5.24(typescript@5.9.3): diff --git a/ui/src/lib/hexboard/Hexboard.vue b/ui/src/lib/hexboard/Hexboard.vue index 80893fbc..ea90b98a 100644 --- a/ui/src/lib/hexboard/Hexboard.vue +++ b/ui/src/lib/hexboard/Hexboard.vue @@ -28,6 +28,7 @@ + + + -
{{ { mousePosition } }}
+
{{ { normalizedOptions } }}
@@ -82,9 +94,10 @@ const props = withDefaults( defineProps<{ active?: boolean flipped?: boolean + options?: Partial pieces?: Component position?: string - options?: Partial + targets?: number[] }>(), { active: false, @@ -92,6 +105,7 @@ const props = withDefaults( options: () => ({}), pieces: () => GiocoPieces, position: initialPosition, + targets: () => [], } ) diff --git a/ui/src/lib/hexboard/constants.ts b/ui/src/lib/hexboard/constants.ts index 253b5928..9eabc2c1 100644 --- a/ui/src/lib/hexboard/constants.ts +++ b/ui/src/lib/hexboard/constants.ts @@ -13,6 +13,7 @@ export const defaultOptions: HexboardOptions = { labelActiveColor: 'oklch(66.6% 0.179 58.318)', labelColor: 'oklch(55.4% 0.046 257.417)', labelInactiveColor: 'oklch(70.4% 0.04 256.788)', + labels: true, } as const /** empty position */ diff --git a/ui/src/lib/hexboard/types.ts b/ui/src/lib/hexboard/types.ts index 22091393..1d059a0c 100644 --- a/ui/src/lib/hexboard/types.ts +++ b/ui/src/lib/hexboard/types.ts @@ -8,6 +8,8 @@ export interface HexboardOptions { labelColor: string /** fill color of inactive label relative to mouseover */ labelInactiveColor: string + /** show labels */ + labels: boolean } /** uniform tuple of length `T`, `number` by default */ diff --git a/ui/src/test/user-interaction.test.tsx b/ui/src/test/user-interaction.test.tsx index c2f4fb06..842f01dd 100644 --- a/ui/src/test/user-interaction.test.tsx +++ b/ui/src/test/user-interaction.test.tsx @@ -90,13 +90,15 @@ test('custom colors', async () => { await expect(page.getByTestId('position-c8')).toHaveStyle({ fill: 'blue' }) }) -test('label colors', async () => { +test('labels and label colors', async () => { const active = ref(false) + const labels = ref(false) setup(() => { return () => { /> }) + // Labels only show when enabled + await expect.element(page.getByTestId('position-a1')).toBeInTheDocument() + await expect.element(page.getByTestId('label-a')).not.toBeInTheDocument() + labels.value = true + await nextTick() + await expect.element(page.getByTestId('label-a')).toBeVisible() + // When no mouseover, all labels should have default labelColor (red) await expect.element(page.getByTestId('label-a')).toHaveStyle({ fill: 'red' }) await expect.element(page.getByTestId('label-b')).toHaveStyle({ fill: 'red' }) From 3ec012db2a029bafe6dadeffbfcfbb928668b992 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Sat, 22 Nov 2025 18:28:04 -0600 Subject: [PATCH 31/80] targets --- ui/src/lib/hexboard/Hexboard.vue | 3 ++- ui/src/lib/hexboard/constants.ts | 1 + ui/src/lib/hexboard/types.ts | 2 ++ ui/src/test/user-interaction.test.tsx | 23 ++++++++++++++++++++++- 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/ui/src/lib/hexboard/Hexboard.vue b/ui/src/lib/hexboard/Hexboard.vue index ea90b98a..eadb92a2 100644 --- a/ui/src/lib/hexboard/Hexboard.vue +++ b/ui/src/lib/hexboard/Hexboard.vue @@ -64,11 +64,12 @@ diff --git a/ui/src/lib/hexboard/constants.ts b/ui/src/lib/hexboard/constants.ts index 9eabc2c1..2d4cacf1 100644 --- a/ui/src/lib/hexboard/constants.ts +++ b/ui/src/lib/hexboard/constants.ts @@ -14,6 +14,7 @@ export const defaultOptions: HexboardOptions = { labelColor: 'oklch(55.4% 0.046 257.417)', labelInactiveColor: 'oklch(70.4% 0.04 256.788)', labels: true, + targetColor: 'oklch(63.7% 0.237 25.331)', } as const /** empty position */ diff --git a/ui/src/lib/hexboard/types.ts b/ui/src/lib/hexboard/types.ts index 1d059a0c..8a81499a 100644 --- a/ui/src/lib/hexboard/types.ts +++ b/ui/src/lib/hexboard/types.ts @@ -10,6 +10,8 @@ export interface HexboardOptions { labelInactiveColor: string /** show labels */ labels: boolean + /** color of target circles */ + targetColor: string } /** uniform tuple of length `T`, `number` by default */ diff --git a/ui/src/test/user-interaction.test.tsx b/ui/src/test/user-interaction.test.tsx index 842f01dd..1840c49e 100644 --- a/ui/src/test/user-interaction.test.tsx +++ b/ui/src/test/user-interaction.test.tsx @@ -137,4 +137,25 @@ test('labels and label colors', async () => { await expect.element(page.getByTestId('label-a')).toHaveStyle({ fill: 'blue' }) await expect.element(page.getByTestId('label-1').first()).toHaveStyle({ fill: 'blue' }) await expect.element(page.getByTestId('label-1').last()).toHaveStyle({ fill: 'blue' }) -}) \ No newline at end of file +}) + +test('targets array controls rendering of target circles', async () => { + const targets = ref([]) + + setup(() => { + return () => + }) + + await expect.element(page.getByTestId('target-a1')).not.toBeInTheDocument() + + targets.value = [index('a1')] + + await expect.element(page.getByTestId('target-a1')).toBeVisible() + await expect.element(page.getByTestId('target-a1')).toHaveStyle({ fill: 'red' }) +}) From a69ad2ec7031377dd7deef5873c25ede6ef7a718 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Sat, 22 Nov 2025 18:30:22 -0600 Subject: [PATCH 32/80] fix test --- ui/src/lib/hexboard/Hexboard.vue | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/src/lib/hexboard/Hexboard.vue b/ui/src/lib/hexboard/Hexboard.vue index eadb92a2..4bb4e9df 100644 --- a/ui/src/lib/hexboard/Hexboard.vue +++ b/ui/src/lib/hexboard/Hexboard.vue @@ -31,9 +31,9 @@ v-if="normalizedOptions.labels" v-for="[text, p, positionFlipped], i in labels" v-text="text" - :data-testid="`label-${text}`" dominant-baseline="central" text-anchor="middle" + :data-testid="`label-${text}`" :key="`label-${i}`" :style="{ fill: getLabelFill(text), @@ -64,12 +64,12 @@ From 3a9b95c830b22ae19084df7fc5b25f9a6356b1ec Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Sat, 22 Nov 2025 18:36:17 -0600 Subject: [PATCH 33/80] add selected model --- ui/src/lib/hexboard/Hexboard.vue | 4 ++++ ui/src/test/user-interaction.test.tsx | 28 +++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/ui/src/lib/hexboard/Hexboard.vue b/ui/src/lib/hexboard/Hexboard.vue index 4bb4e9df..4276fd04 100644 --- a/ui/src/lib/hexboard/Hexboard.vue +++ b/ui/src/lib/hexboard/Hexboard.vue @@ -116,6 +116,8 @@ const props = withDefaults( const mouseoverPosition = defineModel('mouseover-position', { default: null, required: false }) +const selected = defineModel('selected', { default: null, required: false }) + // // state // @@ -186,6 +188,8 @@ function getLabelFill(text: string) { /** click position */ function onClickPosition(index: number) { + selected.value = index + emit('clickPosition', index) } diff --git a/ui/src/test/user-interaction.test.tsx b/ui/src/test/user-interaction.test.tsx index 1840c49e..5c3f5853 100644 --- a/ui/src/test/user-interaction.test.tsx +++ b/ui/src/test/user-interaction.test.tsx @@ -159,3 +159,31 @@ test('targets array controls rendering of target circles', async () => { await expect.element(page.getByTestId('target-a1')).toBeVisible() await expect.element(page.getByTestId('target-a1')).toHaveStyle({ fill: 'red' }) }) + +test('selected model is set when clicking a position', async () => { + const active = ref(false) + const selected = ref(null) + + setup(() => { + return () => + }) + + // Clicking when inactive should not set selected + await page.getByTestId('position-f6').click() + await expect(selected.value).toBeNull() + + // Activate the board + active.value = true + await nextTick() + + // Clicking when active should set selected + await page.getByTestId('position-f6').click() + await expect(selected.value).toBe(index('f6')) + + // Clicking another position should update selected + await page.getByTestId('position-a1').click() + await expect(selected.value).toBe(index('a1')) +}) From 499d9f2235d3b567cf17874793080d711f3982ac Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Sat, 22 Nov 2025 18:42:35 -0600 Subject: [PATCH 34/80] selected --- ui/src/lib/hexboard/Hexboard.vue | 9 +++++++++ ui/src/lib/hexboard/constants.ts | 3 ++- ui/src/lib/hexboard/types.ts | 2 ++ ui/src/test/user-interaction.test.tsx | 13 +++++++++++++ 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/ui/src/lib/hexboard/Hexboard.vue b/ui/src/lib/hexboard/Hexboard.vue index 4276fd04..37b7eb1d 100644 --- a/ui/src/lib/hexboard/Hexboard.vue +++ b/ui/src/lib/hexboard/Hexboard.vue @@ -26,6 +26,15 @@ :key="index" /> + + + { return () => }) + // Initially, no selected path should be in the document + await expect.element(page.getByTestId('selected-f6')).not.toBeInTheDocument() + await expect.element(page.getByTestId('selected-a1')).not.toBeInTheDocument() + // Clicking when inactive should not set selected await page.getByTestId('position-f6').click() await expect(selected.value).toBeNull() + await expect.element(page.getByTestId('selected-f6')).not.toBeInTheDocument() // Activate the board active.value = true @@ -182,8 +190,13 @@ test('selected model is set when clicking a position', async () => { // Clicking when active should set selected await page.getByTestId('position-f6').click() await expect(selected.value).toBe(index('f6')) + await expect.element(page.getByTestId('selected-f6')).toBeVisible() + await expect.element(page.getByTestId('selected-f6')).toHaveStyle({ fill: 'red' }) // Clicking another position should update selected await page.getByTestId('position-a1').click() await expect(selected.value).toBe(index('a1')) + await expect.element(page.getByTestId('selected-f6')).not.toBeInTheDocument() + await expect.element(page.getByTestId('selected-a1')).toBeVisible() + await expect.element(page.getByTestId('selected-a1')).toHaveStyle({ fill: 'red' }) }) From 34bfe617ed64b57dc1979e6901580dae477cac0c Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Sat, 22 Nov 2025 18:56:12 -0600 Subject: [PATCH 35/80] highlights --- ui/src/lib/hexboard/Hexboard.vue | 12 +++++++++ ui/src/lib/hexboard/constants.ts | 7 ++--- ui/src/lib/hexboard/types.ts | 2 ++ ui/src/test/user-interaction.test.tsx | 38 +++++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 3 deletions(-) diff --git a/ui/src/lib/hexboard/Hexboard.vue b/ui/src/lib/hexboard/Hexboard.vue index 37b7eb1d..d0a9f059 100644 --- a/ui/src/lib/hexboard/Hexboard.vue +++ b/ui/src/lib/hexboard/Hexboard.vue @@ -26,6 +26,16 @@ :key="index" /> + + + pieces?: Component position?: string @@ -112,6 +123,7 @@ const props = withDefaults( { active: false, flipped: false, + highlight: () => [], options: () => ({}), pieces: () => GiocoPieces, position: initialPosition, diff --git a/ui/src/lib/hexboard/constants.ts b/ui/src/lib/hexboard/constants.ts index fda5cc68..f45c2fc1 100644 --- a/ui/src/lib/hexboard/constants.ts +++ b/ui/src/lib/hexboard/constants.ts @@ -10,9 +10,10 @@ export const defaultOptions: HexboardOptions = { 'oklch(0.8366 0.1165 66.29)', 'oklch(0.6806 0.1423 75.83)', ], - labelActiveColor: 'oklch(66.6% 0.179 58.318)', - labelColor: 'oklch(55.4% 0.046 257.417)', - labelInactiveColor: 'oklch(70.4% 0.04 256.788)', + highlightColor: 'oklch(90.5% 0.182 98.111 / 75%)', // yellow-300 / 75% opacity + labelActiveColor: 'oklch(70.5% 0.213 47.604)', // orange-500 + labelColor: 'oklch(55.4% 0.046 257.417)', // slate-500 + labelInactiveColor: 'oklch(70.4% 0.04 256.788)', // slate-400 labels: true, selectedColor: 'oklch(63.7% 0.237 25.331)', // red-500 targetColor: 'oklch(63.7% 0.237 25.331)', // red-500 diff --git a/ui/src/lib/hexboard/types.ts b/ui/src/lib/hexboard/types.ts index 3640182b..56995e99 100644 --- a/ui/src/lib/hexboard/types.ts +++ b/ui/src/lib/hexboard/types.ts @@ -2,6 +2,8 @@ export interface HexboardOptions { /** position colors */ colors: [string, string, string] + /** color of highlighted position */ + highlightColor: string /** color of active label relative to mouseover */ labelActiveColor: string /** label color */ diff --git a/ui/src/test/user-interaction.test.tsx b/ui/src/test/user-interaction.test.tsx index dacb6c2e..0935c51a 100644 --- a/ui/src/test/user-interaction.test.tsx +++ b/ui/src/test/user-interaction.test.tsx @@ -200,3 +200,41 @@ test('selected model is set when clicking a position', async () => { await expect.element(page.getByTestId('selected-a1')).toBeVisible() await expect.element(page.getByTestId('selected-a1')).toHaveStyle({ fill: 'red' }) }) + +test('highlight array controls rendering of highlight paths', async () => { + const highlight = ref([]) + + setup(() => { + return () => + }) + + // Initially, no highlight paths should be in the document + await expect.element(page.getByTestId('highlight-f6')).not.toBeInTheDocument() + await expect.element(page.getByTestId('highlight-a1')).not.toBeInTheDocument() + + // Setting highlight to a single position should render one path + highlight.value = [index('f6')] + await nextTick() + await expect.element(page.getByTestId('highlight-f6')).toBeVisible() + await expect.element(page.getByTestId('highlight-f6')).toHaveStyle({ fill: 'pink' }) + await expect.element(page.getByTestId('highlight-a1')).not.toBeInTheDocument() + + // Setting highlight to multiple positions should render multiple paths + highlight.value = [index('f6'), index('a1')] + await nextTick() + await expect.element(page.getByTestId('highlight-f6')).toBeVisible() + await expect.element(page.getByTestId('highlight-f6')).toHaveStyle({ fill: 'pink' }) + await expect.element(page.getByTestId('highlight-a1')).toBeVisible() + await expect.element(page.getByTestId('highlight-a1')).toHaveStyle({ fill: 'pink' }) + + // Clearing highlight should remove all paths + highlight.value = [] + await nextTick() + await expect.element(page.getByTestId('highlight-f6')).not.toBeInTheDocument() + await expect.element(page.getByTestId('highlight-a1')).not.toBeInTheDocument() +}) From 073faacd253842089d434491aaf13f34d1117ac8 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Sat, 22 Nov 2025 19:18:20 -0600 Subject: [PATCH 36/80] remove peer deps from build --- ui/package.json | 4 ++++ ui/vite.config.ts | 3 +++ 2 files changed, 7 insertions(+) diff --git a/ui/package.json b/ui/package.json index ae939b93..fe318f51 100644 --- a/ui/package.json +++ b/ui/package.json @@ -12,6 +12,10 @@ "test": "vitest", "test:ui": "vitest --ui" }, + "peerDependencies": { + "@bedard/hexchess": "^2.5.0", + "vue": "^3.5.0" + }, "devDependencies": { "@bedard/hexchess": "^2.5.0", "@headlessui/vue": "^1.7.23", diff --git a/ui/vite.config.ts b/ui/vite.config.ts index f92086b4..b79fad2c 100644 --- a/ui/vite.config.ts +++ b/ui/vite.config.ts @@ -24,6 +24,9 @@ export default defineConfig({ fileName: 'index', formats: ['es'] }, + rollupOptions: { + external: ['@bedard/hexchess', 'vue'] + } }, server: { port: 3000, From 30c24beb0d7444ab78f74fc354d67e7311350719 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Sat, 22 Nov 2025 19:22:58 -0600 Subject: [PATCH 37/80] tweak color --- ui/src/lib/hexboard/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/src/lib/hexboard/constants.ts b/ui/src/lib/hexboard/constants.ts index f45c2fc1..c43d8baf 100644 --- a/ui/src/lib/hexboard/constants.ts +++ b/ui/src/lib/hexboard/constants.ts @@ -11,7 +11,7 @@ export const defaultOptions: HexboardOptions = { 'oklch(0.6806 0.1423 75.83)', ], highlightColor: 'oklch(90.5% 0.182 98.111 / 75%)', // yellow-300 / 75% opacity - labelActiveColor: 'oklch(70.5% 0.213 47.604)', // orange-500 + labelActiveColor: 'oklch(76.9% 0.188 70.08)', // amber-500 labelColor: 'oklch(55.4% 0.046 257.417)', // slate-500 labelInactiveColor: 'oklch(70.4% 0.04 256.788)', // slate-400 labels: true, From 0ce47ffa097ba71b75eec2de6ee071bf1dbf6d67 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Sat, 22 Nov 2025 22:29:48 -0600 Subject: [PATCH 38/80] starting color --- ui/src/App.vue | 2 +- ui/src/lib/hexboard/Hexboard.vue | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ui/src/App.vue b/ui/src/App.vue index f3e444f0..4c56015a 100644 --- a/ui/src/App.vue +++ b/ui/src/App.vue @@ -135,6 +135,6 @@ const selectedSet = ref<{ display: string; value: string }>({ display: 'gioco', const pieces = computed(() => allPieces[selectedSet.value.value as keyof typeof allPieces]) function onClickPosition(position: number) { - console.log('onClickPosition', position) + // ... } \ No newline at end of file diff --git a/ui/src/lib/hexboard/Hexboard.vue b/ui/src/lib/hexboard/Hexboard.vue index d0a9f059..e0cb8a84 100644 --- a/ui/src/lib/hexboard/Hexboard.vue +++ b/ui/src/lib/hexboard/Hexboard.vue @@ -101,7 +101,7 @@ import { board, box, defaultOptions, initialPosition, labels, pieceSize, perimeter } from './constants' import { computed, onMounted, onUnmounted, shallowRef, watch, type Component } from 'vue' import { d } from './dom' -import { Hexchess, position as indexToPosition } from '@bedard/hexchess' +import { Hexchess, position as indexToPosition, type Color } from '@bedard/hexchess' import { x, y } from './geometry' import GiocoPieces from '../pieces/GiocoPieces.vue' import type { HexboardOptions } from './types' @@ -117,6 +117,7 @@ const props = withDefaults( highlight?: number[] options?: Partial pieces?: Component + playing?: Color | boolean position?: string targets?: number[] }>(), @@ -126,6 +127,7 @@ const props = withDefaults( highlight: () => [], options: () => ({}), pieces: () => GiocoPieces, + playing: false, position: initialPosition, targets: () => [], } @@ -231,7 +233,7 @@ function onMouseleavePosition() { /** mouseup position */ function onMouseupPosition(index: number) { - console.log('onMouseupPosition', index) + // ... } /** mousemove window */ From 7395faa34d62a7a6c4435a9477f07d53e9541a9a Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Sat, 22 Nov 2025 23:28:11 -0600 Subject: [PATCH 39/80] add playing dropdown --- ui/src/App.vue | 50 ++++++++++++++++++++++++------------ ui/src/components/Select.vue | 16 +++++++----- 2 files changed, 43 insertions(+), 23 deletions(-) diff --git a/ui/src/App.vue b/ui/src/App.vue index 4c56015a..91469f01 100644 --- a/ui/src/App.vue +++ b/ui/src/App.vue @@ -1,11 +1,19 @@ @@ -133,13 +134,27 @@ const props = withDefaults( } ) +// +// events +// + +const emit = defineEmits<{ + clickPosition: [position: number] +}>() + // // models // -const mouseoverPosition = defineModel('mouseover-position', { default: null, required: false }) +const mouseoverPosition = defineModel('mouseover-position', { + default: null, + required: false, +}) -const selected = defineModel('selected', { default: null, required: false }) +const selected = defineModel('selected', { + default: null, + required: false, +}) // // state @@ -148,16 +163,28 @@ const selected = defineModel('selected', { default: null, require const mousePosition = shallowRef<{ x: number; y: number } | null>(null) // -// events +// computed // -const emit = defineEmits<{ - clickPosition: [position: number] -}>() +const cursor = computed(() => { + if ( + !props.active || + !props.playing || + !mouseoverColor.value || + mouseoverPosition.value === null + ) { + return undefined + } -// -// computed -// + if ( + hexchess.value.turn === mouseoverColor.value && + (props.playing === true || props.playing === mouseoverColor.value) + ) { + return 'grab' + } + + return 'pointer' +}) /** current hexchess state */ const hexchess = computed(() => Hexchess.parse(props.position)) @@ -167,6 +194,24 @@ const normalizedOptions = computed(() => { return { ...defaultOptions, ...props.options } }) +/** color of piece at mouseover position */ +const mouseoverColor = computed(() => { + if (!mouseoverPiece.value) { + return null + } + + return mouseoverPiece.value === mouseoverPiece.value.toLowerCase() ? 'b' : 'w' +}) + +/** piece at mouseover position */ +const mouseoverPiece = computed(() => { + if (mouseoverPosition.value === null) { + return null + } + + return hexchess.value.board[mouseoverPosition.value] +}) + // // lifecycle // From 93cd96e0f7caf7f9568f33cec984cfd27ef8f2a6 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Sun, 23 Nov 2025 13:46:02 -0600 Subject: [PATCH 41/80] autoselect --- ui/src/App.vue | 10 +++-- ui/src/lib/hexboard/Hexboard.vue | 61 ++++++++++++++++++--------- ui/src/test/user-interaction.test.tsx | 46 ++++++++++++-------- 3 files changed, 78 insertions(+), 39 deletions(-) diff --git a/ui/src/App.vue b/ui/src/App.vue index 915731e4..6b802df4 100644 --- a/ui/src/App.vue +++ b/ui/src/App.vue @@ -27,7 +27,9 @@ + + {{ { selected } }} @@ -56,7 +60,7 @@ import { ChessnutPieces, CompanionPieces, // CookePieces, - DisguisedPieces, + // DisguisedPieces, DubrovnyPieces, // FantasyPieces, FiriPieces, @@ -140,6 +144,8 @@ const active = ref(true) const flipped = ref(false) +const selected = ref(null) + const pieceItems: Array<{ display: string value: Component @@ -149,8 +155,6 @@ const selectedPieces = shallowRef(pieces.gioco) const playing = ref(true) -// const pieces = computed(() => pieces[selectedSet.value.value as keyof typeof pieces]) - function onClickPosition(position: number) { // ... } diff --git a/ui/src/lib/hexboard/Hexboard.vue b/ui/src/lib/hexboard/Hexboard.vue index 0b10c5ce..523f8428 100644 --- a/ui/src/lib/hexboard/Hexboard.vue +++ b/ui/src/lib/hexboard/Hexboard.vue @@ -17,7 +17,7 @@ + }) // Initially, no selected path should be in the document @@ -188,17 +197,20 @@ test('selected model is set when clicking a position', async () => { await nextTick() // Clicking when active should set selected - await page.getByTestId('position-f6').click() - await expect(selected.value).toBe(index('f6')) - await expect.element(page.getByTestId('selected-f6')).toBeVisible() - await expect.element(page.getByTestId('selected-f6')).toHaveStyle({ fill: 'red' }) + await page.getByTestId('position-f5').click() + await expect(selected.value).toBe(index('f5')) + await expect.element(page.getByTestId('selected-f5')).toBeVisible() + await expect.element(page.getByTestId('selected-f5')).toHaveStyle({ fill: 'red' }) - // Clicking another position should update selected + // Clicking an unoccupied position should update selected await page.getByTestId('position-a1').click() - await expect(selected.value).toBe(index('a1')) - await expect.element(page.getByTestId('selected-f6')).not.toBeInTheDocument() - await expect.element(page.getByTestId('selected-a1')).toBeVisible() - await expect.element(page.getByTestId('selected-a1')).toHaveStyle({ fill: 'red' }) + await expect.element(page.getByTestId('assertion')).toHaveTextContent('') + + // Escape should clear selected + await page.getByTestId('position-f5').click() + await expect.element(page.getByTestId('assertion')).toHaveTextContent(index('f5')) + userEvent.keyboard('{Escape}') + await expect.element(page.getByTestId('assertion')).toHaveTextContent('') }) test('highlight array controls rendering of highlight paths', async () => { From 8e009c36fe2c8d9786ec2dc5433179dd82f1acae Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Sun, 23 Nov 2025 13:52:37 -0600 Subject: [PATCH 42/80] autoselect targets --- ui/src/lib/hexboard/Hexboard.vue | 15 ++++++++++++--- ui/src/test/user-interaction.test.tsx | 15 +++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/ui/src/lib/hexboard/Hexboard.vue b/ui/src/lib/hexboard/Hexboard.vue index 523f8428..a39f96e7 100644 --- a/ui/src/lib/hexboard/Hexboard.vue +++ b/ui/src/lib/hexboard/Hexboard.vue @@ -121,7 +121,6 @@ const props = withDefaults( pieces?: Component playing?: Color | boolean position?: string - targets?: number[] }>(), { active: false, @@ -132,7 +131,6 @@ const props = withDefaults( pieces: () => GiocoPieces, playing: false, position: initialPosition, - targets: () => [], } ) @@ -158,6 +156,11 @@ const selected = defineModel('selected', { required: false, }) +const targets = defineModel('targets', { + default: () => [], + required: false, +}) + // // state // @@ -267,9 +270,13 @@ function listen() { function onClickPosition(index: number, e: MouseEvent) { emit('clickPosition', index) - if (props.autoselect && hexchess.value.board[index]) { + if ( + props.autoselect && + hexchess.value.board[index] + ) { e.stopPropagation() selected.value = index + targets.value = hexchess.value.movesFrom(index).map(san => san.to) } } @@ -277,6 +284,7 @@ function onClickPosition(index: number, e: MouseEvent) { function onKeyupWindow(evt: KeyboardEvent) { if (props.autoselect && evt.key === 'Escape') { selected.value = null + targets.value = [] } } @@ -309,6 +317,7 @@ function onMousemoveWindow(evt: MouseEvent) { function onClickWindow() { if (props.autoselect) { selected.value = null + targets.value = [] } } diff --git a/ui/src/test/user-interaction.test.tsx b/ui/src/test/user-interaction.test.tsx index 1d59c76e..6baf1b16 100644 --- a/ui/src/test/user-interaction.test.tsx +++ b/ui/src/test/user-interaction.test.tsx @@ -161,6 +161,21 @@ test('targets array controls rendering of target circles', async () => { await expect.element(page.getByTestId('target-a1')).toHaveStyle({ fill: 'red' }) }) +test('autoselect targets', async () => { + const targets = ref([]) + + setup(() => { + return () => + }) + + await page.getByTestId('position-f5').click() + await expect.element(page.getByTestId('target-f6')).toBeVisible() +}) + test('select options and logic', async () => { const active = ref(false) const selected = ref(null) From 32bb4441c491899f4277fd20f228b53ee898b9a5 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Sun, 23 Nov 2025 13:55:26 -0600 Subject: [PATCH 43/80] cleanup --- ui/src/App.vue | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ui/src/App.vue b/ui/src/App.vue index 6b802df4..11a73ad1 100644 --- a/ui/src/App.vue +++ b/ui/src/App.vue @@ -1,7 +1,7 @@ From 5586155892b1b00d531dbddb098048c6b4f0a111 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Sun, 23 Nov 2025 14:01:55 -0600 Subject: [PATCH 44/80] move to hexchess cli --- hexchess.js | 48 +++++++++++++++++++++++++++++++++++++++++++-- scripts/build-ui.js | 11 +++++++++++ scripts/test.js | 23 ++++++++++++++++++++++ ui/package.json | 7 ------- 4 files changed, 80 insertions(+), 9 deletions(-) create mode 100644 scripts/build-ui.js diff --git a/hexchess.js b/hexchess.js index 2cc54dcd..f6b8f08d 100755 --- a/hexchess.js +++ b/hexchess.js @@ -1,9 +1,10 @@ import { buildEngine } from './scripts/build-engine.js' import { buildJs } from './scripts/build-js.js' import { buildRust } from './scripts/build-rust.js' -import { dim, execAsync, green } from './scripts/utils.js' +import { buildUi } from './scripts/build-ui.js' +import { dim, execAsync, green, resolve } from './scripts/utils.js' import { program } from 'commander' -import { testEngine, testJs, testPhp, testRust } from './scripts/test.js' +import { testEngine, testJs, testPhp, testRust, testUi } from './scripts/test.js' import { versionCheck } from './scripts/version-check.js' import { version } from './scripts/version.js' import ora from 'ora' @@ -46,6 +47,11 @@ program .description('Build Rust crate') .action(buildRust) +program + .command('build:ui') + .description('Build UI package') + .action(buildUi) + // // docs // @@ -171,6 +177,44 @@ program }) }) +program + .command('test:ui') + .description('Run UI tests') + .option('-c, --coverage', 'Generate coverage report') + .option('-f, --filter ', 'Filter tests by name') + .option('-s, --silent', 'Run tests silently') + .option('-u, --ui', 'Run tests with UI') + .action(async (options) => { + await testUi({ + coverage: options?.coverage, + filter: options?.filter, + silent: options?.silent, + ui: options?.ui, + }) + }) + +// +// ui +// + +program + .command('ui:dev') + .description('Develop UI') + .action(async () => { + await execAsync('npx', ['vite'], { + cwd: resolve('ui'), + }) + }) + +program + .command('ui:preview') + .description('Preview UI build') + .action(async () => { + await execAsync('npx', ['vite', 'preview'], { + cwd: resolve('ui'), + }) + }) + // // version // diff --git a/scripts/build-ui.js b/scripts/build-ui.js new file mode 100644 index 00000000..323e4175 --- /dev/null +++ b/scripts/build-ui.js @@ -0,0 +1,11 @@ +import { execAsync, resolve } from './utils.js' + +/** build ui library */ +export async function buildUi(options) { + // build ui package + await execAsync('npx', ['vite', 'build'], { + cwd: resolve('ui'), + silent: options?.silent, + }) +} + diff --git a/scripts/test.js b/scripts/test.js index 2450fd5d..c5af36a3 100644 --- a/scripts/test.js +++ b/scripts/test.js @@ -99,3 +99,26 @@ export async function testRust(options) { silent: options?.silent, }) } + +export async function testUi(options) { + const args = [ + 'vitest', + ] + + if (options?.filter) { + args.push(options.filter) + } + + if (options?.coverage) { + args.push('--coverage') + } + + if (options?.ui) { + args.push('--ui') + } + + await execAsync('npx', args, { + cwd: resolve('ui'), + silent: options?.silent, + }) +} diff --git a/ui/package.json b/ui/package.json index fe318f51..7cd5efe0 100644 --- a/ui/package.json +++ b/ui/package.json @@ -5,13 +5,6 @@ "main": "dist/index.js", "module": "dist/index.js", "types": "dist/index.d.ts", - "scripts": { - "dev": "vite", - "build": "vite build", - "preview": "vite preview", - "test": "vitest", - "test:ui": "vitest --ui" - }, "peerDependencies": { "@bedard/hexchess": "^2.5.0", "vue": "^3.5.0" From a1734859a437f65cdf99fcf3626e3c5bc671c615 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Sun, 23 Nov 2025 14:05:40 -0600 Subject: [PATCH 45/80] add ui to shared commands --- hexchess.js | 10 ++++++++++ scripts/test.js | 1 + 2 files changed, 11 insertions(+) diff --git a/hexchess.js b/hexchess.js index f6b8f08d..1e1125a0 100755 --- a/hexchess.js +++ b/hexchess.js @@ -28,6 +28,11 @@ program await buildRust({ silent: true }) rust.succeed(`Rust crate ${dim(`(${Date.now() - rustStart}ms)`)}`) + const ui = ora('Building UI library...').start() + const uiStart = Date.now() + await buildUi({ silent: true }) + ui.succeed(`UI package ${dim(`(${Date.now() - uiStart}ms)`)}`) + console.log() console.log(green('Done'), dim(`(${Date.now() - startAt}ms)`)) }) @@ -118,6 +123,11 @@ program await testEngine({ silent: true }) engine.succeed(`Engine ${dim(`(${Date.now() - engineStartAt}ms)`)}`) + const uiStartAt = Date.now() + const ui = ora('UI...').start() + await testUi({ silent: true }) + ui.succeed(`UI ${dim(`(${Date.now() - uiStartAt}ms)`)}`) + console.log() console.log(green('Done'), dim(`(${Date.now() - startAt}ms)`)) }) diff --git a/scripts/test.js b/scripts/test.js index c5af36a3..99cc9cc3 100644 --- a/scripts/test.js +++ b/scripts/test.js @@ -103,6 +103,7 @@ export async function testRust(options) { export async function testUi(options) { const args = [ 'vitest', + '--browser.headless' ] if (options?.filter) { From 5dc22d8cda51e54494c359748c9859d947f28bbf Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Sun, 23 Nov 2025 14:35:33 -0600 Subject: [PATCH 46/80] build --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4f6345ee..9da74650 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -137,9 +137,9 @@ jobs: - name: Install Playwright browsers run: cd ui && pnpm exec playwright install --with-deps chromium - name: Build - run: cd ui && pnpm build + run: node hexchess build:ui - name: Test - run: cd ui && pnpm test + run: node hexchess test:ui php: name: PHP From 8763ac21a7c7787f42191fa333a61768a2d00d96 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Tue, 25 Nov 2025 20:12:55 -0600 Subject: [PATCH 47/80] dragging --- ui/src/lib/hexboard/Hexboard.vue | 112 +++++++++++++++++++++++++------ 1 file changed, 92 insertions(+), 20 deletions(-) diff --git a/ui/src/lib/hexboard/Hexboard.vue b/ui/src/lib/hexboard/Hexboard.vue index a39f96e7..5106c88d 100644 --- a/ui/src/lib/hexboard/Hexboard.vue +++ b/ui/src/lib/hexboard/Hexboard.vue @@ -1,10 +1,9 @@ \ No newline at end of file From 14db960e23707a1e1e0fab1616b559b16b02a1c0 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Sat, 29 Nov 2025 13:54:26 -0600 Subject: [PATCH 50/80] improve cursor behavior --- ui/src/lib/hexboard/Hexboard.vue | 37 ++++++-- ui/src/test/user-interaction.test.tsx | 132 +++++++++++++++++++++++++- 2 files changed, 155 insertions(+), 14 deletions(-) diff --git a/ui/src/lib/hexboard/Hexboard.vue b/ui/src/lib/hexboard/Hexboard.vue index 806e2971..fbbbdbaf 100644 --- a/ui/src/lib/hexboard/Hexboard.vue +++ b/ui/src/lib/hexboard/Hexboard.vue @@ -213,14 +213,15 @@ const cursor = computed(() => { if ( !props.active || - !props.playing || - !mouseoverColor.value || + !mouseoverPiece.value || mouseoverPosition.value === null ) { return undefined } + // When playing is true or a color, check if piece is draggable if ( + props.playing && hexchess.value.turn === mouseoverColor.value && (props.playing === true || props.playing === mouseoverColor.value) ) { @@ -333,6 +334,12 @@ function onClickPosition(index: number, evt: MouseEvent) { return } + // If autoselect is enabled and clicking an unoccupied position, deselect + if (props.autoselect && !hexchess.value.board[index]) { + selected.value = null + targets.value = [] + } + emit('clickPosition', index) } @@ -346,19 +353,29 @@ function onKeyupWindow(evt: KeyboardEvent) { /** mousedown on position */ function onMousedownPosition(index: number) { - mousedownPosition.value = index - - if (svgEl.value instanceof Element) { - svgRect.value = svgEl.value.getBoundingClientRect() + const piece = hexchess.value.board[index] + + if (!piece) { + return } - if ( - props.autoselect && - hexchess.value.board[index] - ) { + const pieceColor: Color = piece === piece.toLowerCase() ? 'b' : 'w' + const isPlayingColor = props.playing === true || props.playing === pieceColor + + if (props.autoselect) { selected.value = index targets.value = hexchess.value.movesFrom(index).map(san => san.to) } + + if (!isPlayingColor) { + return + } + + mousedownPosition.value = index + + if (svgEl.value instanceof Element) { + svgRect.value = svgEl.value.getBoundingClientRect() + } } /** mouseenter position */ diff --git a/ui/src/test/user-interaction.test.tsx b/ui/src/test/user-interaction.test.tsx index 6baf1b16..6ddc2f1d 100644 --- a/ui/src/test/user-interaction.test.tsx +++ b/ui/src/test/user-interaction.test.tsx @@ -145,7 +145,7 @@ test('targets array controls rendering of target circles', async () => { setup(() => { return () => { await expect.element(page.getByTestId('selected-f5')).toBeVisible() await expect.element(page.getByTestId('selected-f5')).toHaveStyle({ fill: 'red' }) - // Clicking an unoccupied position should update selected + // Clicking an unoccupied position should deselect await page.getByTestId('position-a1').click() - await expect.element(page.getByTestId('assertion')).toHaveTextContent('') + await expect.element(page.getByTestId('assertion')).toBeEmptyDOMElement() // Escape should clear selected await page.getByTestId('position-f5').click() await expect.element(page.getByTestId('assertion')).toHaveTextContent(index('f5')) userEvent.keyboard('{Escape}') - await expect.element(page.getByTestId('assertion')).toHaveTextContent('') + await expect.element(page.getByTestId('assertion')).toBeEmptyDOMElement() }) test('highlight array controls rendering of highlight paths', async () => { @@ -265,3 +265,127 @@ test('highlight array controls rendering of highlight paths', async () => { await expect.element(page.getByTestId('highlight-f6')).not.toBeInTheDocument() await expect.element(page.getByTestId('highlight-a1')).not.toBeInTheDocument() }) + +test('cursor shows grab for playable pieces', async () => { + setup(() => { + return () => + }) + + const svg = page.getByTestId('position-f5').element().closest('svg') as SVGElement + + // Hover over a white piece (initial position has white to move) + // f5 should have a white piece in initial position + await page.getByTestId('position-f5').hover() + await nextTick() + await expect(svg).toHaveStyle({ cursor: 'grab' }) // White's turn, so grab +}) + +test('cursor shows grab only when user can drag piece', async () => { + setup(() => { + return () => + }) + + // Hover over a white piece when it's white's turn and user is playing white + // Should show "grab" cursor + await page.getByTestId('position-f5').hover() + const svg = page.getByTestId('position-f5').element().closest('svg') as SVGElement + await expect(svg).toHaveStyle({ cursor: 'grab' }) + + // Hover over an empty position when user is only playing white + // Should show "auto" cursor (no piece to interact with) + await page.getByTestId('position-a6').hover() + await expect(svg).toHaveStyle({ cursor: 'auto' }) + + // Hover over a black piece when user is only playing white + // Should show "pointer" cursor (can't drag black piece as white) + await page.getByTestId('position-f7').hover() + await expect(svg).toHaveStyle({ cursor: 'pointer' }) +}) + +test('cursor behavior when playing both colors', async () => { + setup(() => { + return () => + }) + + const svg = page.getByTestId('position-f5').element().closest('svg') as SVGElement + + // Initial position is white's turn, so white pieces show "grab" + await page.getByTestId('position-f5').hover() + await expect(svg).toHaveStyle({ cursor: 'grab' }) + + // Black pieces show "pointer" because it's not black's turn + await page.getByTestId('position-b7').hover() + await expect(svg).toHaveStyle({ cursor: 'pointer' }) + + // Empty positions show "auto" because there's no piece + await page.getByTestId('position-a6').hover() + await expect(svg).toHaveStyle({ cursor: 'auto' }) +}) + +test('cursor shows pointer when user is not playing', async () => { + setup(() => { + return () => + }) + + // When playing is false, should show pointer for any piece + await page.getByTestId('position-f5').hover() + const svg = page.getByTestId('position-f5').element().closest('svg') as SVGElement + await expect(svg).toHaveStyle({ cursor: 'pointer' }) + + await page.getByTestId('position-f7').hover() + await expect(svg).toHaveStyle({ cursor: 'pointer' }) +}) + +test('pieces of any color can be selected, but only playing color is draggable', async () => { + const selected = ref(null) + const targets = ref([]) + + setup(() => { + return () => <> + +
+ + }) + + const svg = page.getByTestId('position-f5').element().closest('svg') as SVGElement + + // White piece shows grab cursor (user can drag) + const whitePiecePosition = page.getByTestId('position-f5') + await whitePiecePosition.hover() + await expect(svg).toHaveStyle({ cursor: 'grab' }) + + // Black piece shows pointer cursor (user cannot drag) + const blackPiecePosition = page.getByTestId('position-b7') + await blackPiecePosition.hover() + await expect(svg).toHaveStyle({ cursor: 'pointer' }) + + // Both pieces can be selected + await whitePiecePosition.click() + await expect.element(page.getByTestId('selected-f5')).toBeVisible() + await expect.element(page.getByTestId('assertion')).toHaveTextContent(index('f5')) + + await blackPiecePosition.click() + await expect.element(page.getByTestId('selected-b7')).toBeVisible() + await expect.element(page.getByTestId('assertion')).toHaveTextContent(index('b7')) +}) + From 07b5b036b2c10a52617715560688b4975ae9d302 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Sat, 29 Nov 2025 13:59:33 -0600 Subject: [PATCH 51/80] cleanup --- ui/src/App.vue | 2 +- ui/src/lib/hexboard/Hexboard.vue | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ui/src/App.vue b/ui/src/App.vue index 11a73ad1..ad8352b0 100644 --- a/ui/src/App.vue +++ b/ui/src/App.vue @@ -153,7 +153,7 @@ const selectedPieces = shallowRef(pieces.gioco) const playing = ref(true) -function onClickPosition(position: number) { +function onClickPosition() { // ... } \ No newline at end of file diff --git a/ui/src/lib/hexboard/Hexboard.vue b/ui/src/lib/hexboard/Hexboard.vue index fbbbdbaf..22160bd4 100644 --- a/ui/src/lib/hexboard/Hexboard.vue +++ b/ui/src/lib/hexboard/Hexboard.vue @@ -16,11 +16,11 @@ Date: Sat, 29 Nov 2025 14:19:40 -0600 Subject: [PATCH 52/80] drag off board test --- ui/src/lib/hexboard/Hexboard.vue | 1 + ui/src/test/user-interaction.test.tsx | 44 +++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/ui/src/lib/hexboard/Hexboard.vue b/ui/src/lib/hexboard/Hexboard.vue index 22160bd4..085cee6b 100644 --- a/ui/src/lib/hexboard/Hexboard.vue +++ b/ui/src/lib/hexboard/Hexboard.vue @@ -98,6 +98,7 @@ + + }) + + const whitePiecePosition = page.getByTestId('position-f5') + + // Start dragging the piece (mousedown) + await whitePiecePosition.element().dispatchEvent(new MouseEvent('mousedown', { bubbles: true })) + await nextTick() + + // Verify dragging started - draggable piece SVG should be visible + await expect.element(page.getByTestId('drag-piece')).toBeVisible() + + // Move mouse off the board (simulate mousemove on window) + window.dispatchEvent(new MouseEvent('mousemove', { clientX: 0, clientY: 0, bubbles: true })) + await nextTick() + + // Release mouse (mouseup on window) - this should reset dragging state + window.dispatchEvent(new MouseEvent('mouseup', { bubbles: true })) + await nextTick() + + // Verify piece is still selected (autoselect should have set it on mousedown) + await expect.element(page.getByTestId('selected-f5')).toBeVisible() + await expect.element(page.getByTestId('assertion')).toHaveTextContent(index('f5')) + + // Verify dragging state is reset - draggable piece SVG should no longer exist + await expect.element(page.getByTestId('drag-piece')).not.toBeInTheDocument() +}) + From 5499e75866157ba3da1fe176968080c31b17251c Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Sat, 29 Nov 2025 14:57:28 -0600 Subject: [PATCH 53/80] move tests --- ui/src/lib/hexboard/Hexboard.vue | 95 ++++++++++++++++++--- ui/src/test/user-interaction.test.tsx | 116 +++++++++++++++++++++++++- 2 files changed, 200 insertions(+), 11 deletions(-) diff --git a/ui/src/lib/hexboard/Hexboard.vue b/ui/src/lib/hexboard/Hexboard.vue index 085cee6b..e6907e4c 100644 --- a/ui/src/lib/hexboard/Hexboard.vue +++ b/ui/src/lib/hexboard/Hexboard.vue @@ -20,7 +20,7 @@ onMousedown: () => onMousedownPosition(index), onMouseenter: () => onMouseenterPosition(index), onMouseleave: () => onMouseleavePosition(), - onMouseup: () => onMouseupPosition(), + onMouseup: () => onMouseupPosition(index), } : {}" :d="d(flipped ? position[4] : position[3])" :data-testid="`position-${indexToPosition(index)}`" @@ -128,7 +128,7 @@ import { board, box, defaultOptions, initialPosition, labels, pieceSize, perimeter } from './constants' import { computed, onMounted, onUnmounted, shallowRef, useTemplateRef, watch, type Component } from 'vue' import { d } from './dom' -import { Hexchess, position as indexToPosition, type Color } from '@bedard/hexchess' +import { Hexchess, position as indexToPosition, San, type Color } from '@bedard/hexchess' import { x, y } from './geometry' import GiocoPieces from '../pieces/GiocoPieces.vue' import type { HexboardOptions } from './types' @@ -166,6 +166,7 @@ const props = withDefaults( const emit = defineEmits<{ clickPosition: [position: number] + move: [san: San] }>() // @@ -212,11 +213,27 @@ const cursor = computed(() => { return 'grabbing' // global cursor } + if (!props.active || mouseoverPosition.value === null) { + return undefined + } + + // If piece is selected and hovering over a target, show pointer if ( - !props.active || - !mouseoverPiece.value || - mouseoverPosition.value === null + selected.value !== null && + targets.value.includes(mouseoverPosition.value) ) { + const selectedPiece = hexchess.value.board[selected.value] + if (selectedPiece) { + const selectedPieceColor: Color = selectedPiece === selectedPiece.toLowerCase() ? 'b' : 'w' + const isSelectedTurn = hexchess.value.turn === selectedPieceColor + + if (isSelectedTurn && isPlayingPosition(selected.value)) { + return 'pointer' + } + } + } + + if (!mouseoverPiece.value) { return undefined } @@ -304,6 +321,17 @@ watch(() => props.active, active => { // methods // +/** check if user is playing the color at a position */ +function isPlayingPosition(index: number): boolean { + const piece = hexchess.value.board[index] + if (!piece) { + return false + } + + const pieceColor: Color = piece === piece.toLowerCase() ? 'b' : 'w' + return props.playing === true || props.playing === pieceColor +} + /** get fill color of label */ function getLabelFill(text: string) { if (mouseoverPosition.value === null) { @@ -335,6 +363,12 @@ function onClickPosition(index: number) { return } + // If there's a selected piece and clicking a target, attempt to move + if (selected.value !== null && targets.value.includes(index)) { + attemptMove(selected.value, index) + return + } + // If autoselect is enabled and clicking an unoccupied position, deselect if (props.autoselect && !hexchess.value.board[index]) { selected.value = null @@ -360,15 +394,20 @@ function onMousedownPosition(index: number) { return } - const pieceColor: Color = piece === piece.toLowerCase() ? 'b' : 'w' - const isPlayingColor = props.playing === true || props.playing === pieceColor - if (props.autoselect) { selected.value = index targets.value = hexchess.value.movesFrom(index).map(san => san.to) } - if (!isPlayingColor) { + if (!isPlayingPosition(index)) { + return + } + + // Only allow dragging if it's the piece's turn + const pieceColor: Color = piece === piece.toLowerCase() ? 'b' : 'w' + const isCurrentTurn = hexchess.value.turn === pieceColor + + if (!isCurrentTurn) { return } @@ -389,8 +428,44 @@ function onMouseleavePosition() { mouseoverPosition.value = null } +/** handle piece move */ +function onPieceMove(from: number, to: number) { + const san = new San({ from, to }) + emit('move', san) +} + +/** attempt to move piece from source to target position */ +function attemptMove(from: number, to: number): boolean { + // Check if target is valid + if (!targets.value.includes(to)) { + return false + } + + const piece = hexchess.value.board[from] + + if (!piece) { + return false + } + + const pieceColor: Color = piece === piece.toLowerCase() ? 'b' : 'w' + const isCurrentTurn = hexchess.value.turn === pieceColor + + // Only call onPieceMove if playing this color and it's their turn + if (isPlayingPosition(from) && isCurrentTurn) { + onPieceMove(from, to) + return true + } + + return false +} + /** mouseup position */ -function onMouseupPosition() { +function onMouseupPosition(index: number) { + // Check if we're dropping a piece on a valid target + if (mousedownPosition.value !== null) { + attemptMove(mousedownPosition.value, index) + } + resetState() } diff --git a/ui/src/test/user-interaction.test.tsx b/ui/src/test/user-interaction.test.tsx index a050c7c7..17e5a42e 100644 --- a/ui/src/test/user-interaction.test.tsx +++ b/ui/src/test/user-interaction.test.tsx @@ -1,7 +1,7 @@ /** @jsxImportSource vue */ import { expect, test, vi } from 'vitest' import { Hexboard } from '../lib' -import { index } from '@bedard/hexchess' +import { index, San } from '@bedard/hexchess' import { ref, nextTick } from 'vue' import { userEvent } from 'vitest/browser' @@ -433,3 +433,117 @@ test('dragging piece off board results in selection only, dragging state resets' await expect.element(page.getByTestId('drag-piece')).not.toBeInTheDocument() }) +test('drag and drop piece emits move event', async () => { + const selected = ref(null) + const targets = ref([]) + const onMove = vi.fn() + + setup(() => { + return () => <> + + + }) + + const fromPosition = page.getByTestId('position-f5') + const toPosition = page.getByTestId('position-f6') + + // Start dragging the piece (mousedown) + await fromPosition.element().dispatchEvent(new MouseEvent('mousedown', { bubbles: true })) + await nextTick() + + // Verify dragging started + await expect.element(page.getByTestId('drag-piece')).toBeVisible() + + // Move mouse to target position and release (mouseup) + await toPosition.element().dispatchEvent(new MouseEvent('mouseup', { bubbles: true })) + await nextTick() + + // Verify move event was emitted with correct San object + await expect(onMove).toHaveBeenCalledOnce() + await expect(onMove).toHaveBeenCalledWith(expect.any(San)) + const san = onMove.mock.calls[0][0] as San + await expect(san.from).toBe(index('f5')) + await expect(san.to).toBe(index('f6')) +}) + +test('click to move piece emits move event', async () => { + const selected = ref(null) + const targets = ref([]) + const onMove = vi.fn() + + setup(() => { + return () => <> + + + }) + + // Click on a piece to select it (f5 should have a white piece) + await page.getByTestId('position-f5').click() + await nextTick() + + // Verify piece is selected and targets are shown + await expect.element(page.getByTestId('selected-f5')).toBeVisible() + await expect.element(page.getByTestId('target-f6')).toBeVisible() + + // Click on a target position to move + await page.getByTestId('position-f6').click() + await nextTick() + + // Verify move event was emitted with correct San object + await expect(onMove).toHaveBeenCalledOnce() + await expect(onMove).toHaveBeenCalledWith(expect.any(San)) + const san = onMove.mock.calls[0][0] as San + await expect(san.from).toBe(index('f5')) + await expect(san.to).toBe(index('f6')) +}) + +test('cannot move piece of other turn color', async () => { + const selected = ref(null) + const targets = ref([]) + const onMove = vi.fn() + + setup(() => { + return () => <> + + + }) + + // Initial position is white's turn, so try to move a black piece + // Click on a black piece (b7 should have a black rook in initial position) + await page.getByTestId('position-b7').click() + await nextTick() + + // Verify piece is selected and targets are shown + await expect.element(page.getByTestId('selected-b7')).toBeVisible() + await expect.element(page.getByTestId('target-b6')).toBeVisible() + + // Try to click on a target position to move + await page.getByTestId('position-b6').click() + await nextTick() + + // Verify move event was NOT called (can't move black piece on white's turn) + await expect(onMove).not.toHaveBeenCalled() +}) + + From cd7149581fbf2b129137877151a27a2301bda6f1 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Sat, 29 Nov 2025 15:01:22 -0600 Subject: [PATCH 54/80] test other browsers --- .github/workflows/build.yml | 9 +++++++-- ui/vitest.config.ts | 5 ++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9da74650..5a67d028 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -116,8 +116,11 @@ jobs: run: node hexchess test:engine ui: - name: UI + name: UI (${{ matrix.browser }}) runs-on: ubuntu-latest + strategy: + matrix: + browser: [chromium, firefox, webkit] steps: - uses: actions/checkout@v4 - name: Setup Node @@ -135,10 +138,12 @@ jobs: cd ui pnpm install --no-frozen-lockfile - name: Install Playwright browsers - run: cd ui && pnpm exec playwright install --with-deps chromium + run: cd ui && pnpm exec playwright install --with-deps ${{ matrix.browser }} - name: Build run: node hexchess build:ui - name: Test + env: + VITEST_BROWSER: ${{ matrix.browser }} run: node hexchess test:ui php: diff --git a/ui/vitest.config.ts b/ui/vitest.config.ts index 39f2df93..088ad263 100644 --- a/ui/vitest.config.ts +++ b/ui/vitest.config.ts @@ -8,6 +8,9 @@ import vueJsx from '@vitejs/plugin-vue-jsx' const __dirname = fileURLToPath(new URL('.', import.meta.url)) +// Get browser from environment variable, default to chromium +const browser = (process.env.VITEST_BROWSER || 'chromium') as 'chromium' | 'firefox' | 'webkit' + export default defineConfig({ plugins: [ tailwindcss(), @@ -19,7 +22,7 @@ export default defineConfig({ provider: playwright(), enabled: true, instances: [ - { browser: 'chromium' }, + { browser }, ], }, environment: 'jsdom', From 592792f64eb09dc1dc1d3577d8a9b1cf21c39409 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Sat, 29 Nov 2025 15:36:07 -0600 Subject: [PATCH 55/80] tweak job name --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5a67d028..18622f76 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -116,7 +116,7 @@ jobs: run: node hexchess test:engine ui: - name: UI (${{ matrix.browser }}) + name: ${{ matrix.browser }} runs-on: ubuntu-latest strategy: matrix: From 0140dcc45651703f052e97d5c27b4e3ec5960b72 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Sun, 30 Nov 2025 11:03:09 -0600 Subject: [PATCH 56/80] 2.5.1 --- ui/package.json | 2 +- ui/pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ui/package.json b/ui/package.json index 7cd5efe0..7639f05d 100644 --- a/ui/package.json +++ b/ui/package.json @@ -10,7 +10,7 @@ "vue": "^3.5.0" }, "devDependencies": { - "@bedard/hexchess": "^2.5.0", + "@bedard/hexchess": "^2.5.1", "@headlessui/vue": "^1.7.23", "@heroicons/vue": "^2.2.0", "@tailwindcss/vite": "^4.1.17", diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index bac0b0a2..1d710552 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -9,8 +9,8 @@ importers: .: devDependencies: '@bedard/hexchess': - specifier: ^2.5.0 - version: 2.5.0 + specifier: ^2.5.1 + version: 2.5.1 '@headlessui/vue': specifier: ^1.7.23 version: 1.7.23(vue@3.5.24(typescript@5.9.3)) @@ -210,8 +210,8 @@ packages: resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} - '@bedard/hexchess@2.5.0': - resolution: {integrity: sha512-KwThpuP8zD2tk8NytA+U/6pZqmZUMdWh8xH3LFLWYa9GvFBwZwqbW1q+tGqytBxFmFKoryV3hr9yN6PtC8B2lA==} + '@bedard/hexchess@2.5.1': + resolution: {integrity: sha512-vuuL1CmGWL/kPrGPSi2wZcZahabp7cF2O6f0azsSHnc5YJxsDO8m5dRRyv/qNjCxF7L9An+TIFO2tExOA5YEJA==} '@csstools/color-helpers@5.1.0': resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} @@ -2025,7 +2025,7 @@ snapshots: '@bcoe/v8-coverage@1.0.2': {} - '@bedard/hexchess@2.5.0': {} + '@bedard/hexchess@2.5.1': {} '@csstools/color-helpers@5.1.0': {} From cb78185397becf394e88ab8d89a5ad0d391bfbc2 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Sun, 30 Nov 2025 11:18:19 -0600 Subject: [PATCH 57/80] abc --- ui/src/lib/hexboard/Hexboard.vue | 52 ++++++++++++++++---------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/ui/src/lib/hexboard/Hexboard.vue b/ui/src/lib/hexboard/Hexboard.vue index e6907e4c..7289c931 100644 --- a/ui/src/lib/hexboard/Hexboard.vue +++ b/ui/src/lib/hexboard/Hexboard.vue @@ -128,7 +128,7 @@ import { board, box, defaultOptions, initialPosition, labels, pieceSize, perimeter } from './constants' import { computed, onMounted, onUnmounted, shallowRef, useTemplateRef, watch, type Component } from 'vue' import { d } from './dom' -import { Hexchess, position as indexToPosition, San, type Color } from '@bedard/hexchess' +import { isPromotionPosition, Hexchess, position as indexToPosition, San, type Color } from '@bedard/hexchess' import { x, y } from './geometry' import GiocoPieces from '../pieces/GiocoPieces.vue' import type { HexboardOptions } from './types' @@ -321,6 +321,31 @@ watch(() => props.active, active => { // methods // +/** attempt to move piece from source to target position */ +function attemptMove(from: number, to: number): boolean { + // Check if target is valid + if (!targets.value.includes(to)) { + return false + } + + const piece = hexchess.value.board[from] + + if (!piece) { + return false + } + + const pieceColor: Color = piece === piece.toLowerCase() ? 'b' : 'w' + const isCurrentTurn = hexchess.value.turn === pieceColor + + // Only call onPieceMove if playing this color and it's their turn + if (isPlayingPosition(from) && isCurrentTurn) { + onPieceMove(from, to) + return true + } + + return false +} + /** check if user is playing the color at a position */ function isPlayingPosition(index: number): boolean { const piece = hexchess.value.board[index] @@ -434,31 +459,6 @@ function onPieceMove(from: number, to: number) { emit('move', san) } -/** attempt to move piece from source to target position */ -function attemptMove(from: number, to: number): boolean { - // Check if target is valid - if (!targets.value.includes(to)) { - return false - } - - const piece = hexchess.value.board[from] - - if (!piece) { - return false - } - - const pieceColor: Color = piece === piece.toLowerCase() ? 'b' : 'w' - const isCurrentTurn = hexchess.value.turn === pieceColor - - // Only call onPieceMove if playing this color and it's their turn - if (isPlayingPosition(from) && isCurrentTurn) { - onPieceMove(from, to) - return true - } - - return false -} - /** mouseup position */ function onMouseupPosition(index: number) { // Check if we're dropping a piece on a valid target From fba3b09c7427f2bc677e43024cbfa20454ba2455 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Mon, 1 Dec 2025 21:46:18 -0600 Subject: [PATCH 58/80] working on piece movement --- ui/src/App.vue | 16 ++++-- ui/src/lib/hexboard/Hexboard.vue | 71 ++++++++++++++++++--------- ui/src/test/user-interaction.test.tsx | 19 +++++++ ui/src/test/utils.ts | 25 ++++++++++ 4 files changed, 105 insertions(+), 26 deletions(-) create mode 100644 ui/src/test/utils.ts diff --git a/ui/src/App.vue b/ui/src/App.vue index ad8352b0..16f0e71a 100644 --- a/ui/src/App.vue +++ b/ui/src/App.vue @@ -27,6 +27,7 @@
\ No newline at end of file diff --git a/ui/src/lib/hexboard/Hexboard.vue b/ui/src/lib/hexboard/Hexboard.vue index 7289c931..fc9d29d1 100644 --- a/ui/src/lib/hexboard/Hexboard.vue +++ b/ui/src/lib/hexboard/Hexboard.vue @@ -1,5 +1,5 @@ @@ -199,19 +201,56 @@ const mouseCoords = shallowRef({ x: 0, y: 0 }) /** fen position of mousedown */ const mousedownPosition = shallowRef(null) +/** staging display data */ +const staging = shallowRef<{ + hexchess: Hexchess | null + selected: number | null +}>({ + hexchess: null, + selected: null, +}) + /** svg rect */ const svgEl = useTemplateRef('svgEl') /** rect of svg element on mousedown */ const svgRect = shallowRef(new DOMRect()) -/** whether a pawn is promoting */ -const isPromoting = shallowRef(false) - // // computed // +/** current targets */ +const currentTargets = computed(() => { + if (staging.value.hexchess) { + return [] + } + + return targets.value +}) + +/** current hexchess state */ +const currentHexchess = computed(() => { + if (staging.value.hexchess) { + return staging.value.hexchess + } + + if (props.hexchess) { + return props.hexchess + } + + return Hexchess.init() +}) + +/** current selected position */ +const currentSelected = computed(() => { + if (typeof staging.value.selected === 'number') { + return staging.value.selected + } + + return selected.value +}) + const cursor = computed(() => { if (dragPiece.value) { return 'grabbing' // global cursor @@ -226,10 +265,10 @@ const cursor = computed(() => { selected.value !== null && targets.value.includes(mouseoverPosition.value) ) { - const selectedPiece = currentHexchess.value.board[selected.value] + const selectedPiece = currentHexchess.value?.board[selected.value] if (selectedPiece) { const selectedPieceColor: Color = selectedPiece === selectedPiece.toLowerCase() ? 'b' : 'w' - const isSelectedTurn = currentHexchess.value.turn === selectedPieceColor + const isSelectedTurn = currentHexchess.value?.turn === selectedPieceColor if (isSelectedTurn && isPlayingPosition(selected.value)) { return 'pointer' @@ -244,7 +283,7 @@ const cursor = computed(() => { // When playing is true or a color, check if piece is draggable if ( props.playing && - currentHexchess.value.turn === mouseoverColor.value && + mouseoverColor.value === currentHexchess.value?.turn && (props.playing === true || props.playing === mouseoverColor.value) ) { return 'grab' @@ -263,20 +302,15 @@ const dragCoords = computed(() => { /** piece being dragged */ const dragPiece = computed(() => { - if (mousedownPosition.value === null) { + if ( + !props.hexchess || + staging.value.hexchess || + mousedownPosition.value === null + ) { return null } - return currentHexchess.value.board[mousedownPosition.value] -}) - -/** current hexchess state */ -const currentHexchess = computed(() => { - if (props.hexchess) { - return props.hexchess - } - - return Hexchess.init() + return props.hexchess.board[mousedownPosition.value] }) /** normalized options */ @@ -299,7 +333,7 @@ const mouseoverPiece = computed(() => { return null } - return currentHexchess.value.board[mouseoverPosition.value] + return currentHexchess.value?.board[mouseoverPosition.value] ?? null }) // @@ -338,7 +372,7 @@ function attemptMove(from: number, to: number) { return } - const piece = currentHexchess.value.board[from] + const piece = props.hexchess?.board[from] if (!piece) { return @@ -346,12 +380,22 @@ function attemptMove(from: number, to: number) { const pieceColor = piece === piece.toLowerCase() ? 'b' : 'w' - const isCurrentTurn = currentHexchess.value.turn === pieceColor + const isCurrentTurn = props.hexchess?.turn === pieceColor // Check if this is a pawn promotion move - if ((piece === 'p' || piece === 'P') && isPromotionPosition(to, pieceColor)) { - isPromoting.value = true - // ... + if ( + props.hexchess && + (piece === 'p' || piece === 'P') && + isPromotionPosition(to, pieceColor) + ) { + const clone = props.hexchess.clone() + clone.board[from] = null + clone.board[to] = piece + staging.value = { + hexchess: clone, + selected: to, + } + console.log('staging', staging.value) return } @@ -363,7 +407,7 @@ function attemptMove(from: number, to: number) { /** check if user is playing the color at a position */ function isPlayingPosition(index: number): boolean { - const piece = currentHexchess.value.board[index] + const piece = props.hexchess?.board[index] if (!piece) { return false @@ -412,7 +456,7 @@ function onClickPosition(index: number) { } // If autoselect is enabled and clicking an unoccupied position, deselect - if (props.autoselect && !currentHexchess.value.board[index]) { + if (props.autoselect && !props.hexchess?.board[index]) { selected.value = null targets.value = [] } @@ -430,7 +474,7 @@ function onKeyupWindow(evt: KeyboardEvent) { /** mousedown on position */ function onMousedownPosition(index: number) { - const piece = currentHexchess.value.board[index] + const piece = props.hexchess?.board[index] if (!piece) { return @@ -438,7 +482,7 @@ function onMousedownPosition(index: number) { if (props.autoselect) { selected.value = index - targets.value = currentHexchess.value.movesFrom(index).map(san => san.to) + targets.value = props.hexchess?.movesFrom(index).map(san => san.to) ?? [] } if (!isPlayingPosition(index)) { @@ -447,7 +491,7 @@ function onMousedownPosition(index: number) { // Only allow dragging if it's the piece's turn const pieceColor: Color = piece === piece.toLowerCase() ? 'b' : 'w' - const isCurrentTurn = currentHexchess.value.turn === pieceColor + const isCurrentTurn = props.hexchess?.turn === pieceColor if (!isCurrentTurn) { return @@ -484,12 +528,19 @@ function onPieceMove(from: number, to: number) { } /** mouseup position */ -function onMouseupPosition(index: number) { +function onMouseupPosition(index: number, evt: MouseEvent) { + evt.stopPropagation() + // Check if we're dropping a piece on a valid target if (mousedownPosition.value !== null) { attemptMove(mousedownPosition.value, index) } - + + /** do nothing if staging is set */ + if (staging.value.hexchess) { + return + } + resetState() } @@ -505,9 +556,13 @@ function onMouseupWindow() { /** reset state */ function resetState() { + console.log('resetState') document.body.style.setProperty('cursor', null) - isPromoting.value = false mousedownPosition.value = null + staging.value = { + hexchess: null, + selected: null, + } svgRect.value = new DOMRect() } From 4f48ec7bd90d6f20d66dbda09c0da0f6aeca6ff2 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Tue, 2 Dec 2025 19:49:58 -0600 Subject: [PATCH 61/80] create promotion slot --- ui/src/App.vue | 7 +- ui/src/lib/hexboard/Hexboard.vue | 169 +++++++++++++++++++------------ 2 files changed, 111 insertions(+), 65 deletions(-) diff --git a/ui/src/App.vue b/ui/src/App.vue index fb63d679..b301c8e1 100644 --- a/ui/src/App.vue +++ b/ui/src/App.vue @@ -39,8 +39,11 @@ }" :playing @click-position="onClickPosition" - @move="onMove" - /> + @move="onMove"> + + diff --git a/ui/src/lib/hexboard/Hexboard.vue b/ui/src/lib/hexboard/Hexboard.vue index 6d69c0d9..351590a7 100644 --- a/ui/src/lib/hexboard/Hexboard.vue +++ b/ui/src/lib/hexboard/Hexboard.vue @@ -16,13 +16,14 @@ -
{{ { staging } }}
+
+ +
+ +
{{  { mouseCoords } }}
@@ -201,6 +217,12 @@ const mouseCoords = shallowRef({ x: 0, y: 0 }) /** fen position of mousedown */ const mousedownPosition = shallowRef(null) +/** rect of promotion anchor element */ +const promotionRect = shallowRef(new DOMRect()) + +/** selected element */ +const selectedEl = useTemplateRef('selectedEl') + /** staging display data */ const staging = shallowRef<{ hexchess: Hexchess | null @@ -245,12 +267,13 @@ const currentHexchess = computed(() => { /** current selected position */ const currentSelected = computed(() => { if (typeof staging.value.selected === 'number') { - return staging.value.selected + return null } return selected.value }) +/** cursor type */ const cursor = computed(() => { if (dragPiece.value) { return 'grabbing' // global cursor @@ -356,17 +379,14 @@ watch(cursor, val => { document.body.style.setProperty('cursor', val === 'grabbing' ? 'grabbing' : null) }) -watch(() => props.active, active => { - if (active) listen() - else unlisten() -}) +watch(() => props.active, val => val ? listen() : unlisten()) // // methods // /** attempt to move piece from source to target position */ -function attemptMove(from: number, to: number) { +function attemptMove(from: number, to: number, evt: MouseEvent) { // Check if target is valid if (!targets.value.includes(to)) { return @@ -395,7 +415,11 @@ function attemptMove(from: number, to: number) { hexchess: clone, selected: to, } - console.log('staging', staging.value) + + if (evt.target instanceof Element) { + promotionRect.value = evt.target.getBoundingClientRect() + } + return } @@ -439,19 +463,30 @@ function listen() { mouseCoords.value = { x: 0, y: 0 } window.addEventListener('keyup', onKeyupWindow) - window.addEventListener('mousemove', onMousemoveWindow) - window.addEventListener('mouseup', onMouseupWindow) + window.addEventListener('pointermove', onMousemoveWindow) + window.addEventListener('pointerup', onMouseupWindow) + window.addEventListener('resize', measurePromotionRect) + window.addEventListener('scroll', measurePromotionRect) +} + + + +/** measure promotion element rect */ +function measurePromotionRect() { + if (selectedEl.value instanceof Element) { + promotionRect.value = selectedEl.value.getBoundingClientRect() + } } /** click position */ -function onClickPosition(index: number) { +function onClickPosition(index: number, evt: MouseEvent) { if (!props.active) { return } // If there's a selected piece and clicking a target, attempt to move if (selected.value !== null && targets.value.includes(index)) { - attemptMove(selected.value, index) + attemptMove(selected.value, index, evt) return } @@ -472,48 +507,6 @@ function onKeyupWindow(evt: KeyboardEvent) { } } -/** mousedown on position */ -function onMousedownPosition(index: number) { - const piece = props.hexchess?.board[index] - - if (!piece) { - return - } - - if (props.autoselect) { - selected.value = index - targets.value = props.hexchess?.movesFrom(index).map(san => san.to) ?? [] - } - - if (!isPlayingPosition(index)) { - return - } - - // Only allow dragging if it's the piece's turn - const pieceColor: Color = piece === piece.toLowerCase() ? 'b' : 'w' - const isCurrentTurn = props.hexchess?.turn === pieceColor - - if (!isCurrentTurn) { - return - } - - mousedownPosition.value = index - - if (svgEl.value instanceof Element) { - svgRect.value = svgEl.value.getBoundingClientRect() - } -} - -/** mouseenter position */ -function onMouseenterPosition(index: number) { - mouseoverPosition.value = index -} - -/** mouseleave position */ -function onMouseleavePosition() { - mouseoverPosition.value = null -} - /** handle piece move */ function onPieceMove(from: number, to: number) { const san = new San({ from, to }) @@ -533,7 +526,7 @@ function onMouseupPosition(index: number, evt: MouseEvent) { // Check if we're dropping a piece on a valid target if (mousedownPosition.value !== null) { - attemptMove(mousedownPosition.value, index) + attemptMove(mousedownPosition.value, index, evt) } /** do nothing if staging is set */ @@ -546,6 +539,10 @@ function onMouseupPosition(index: number, evt: MouseEvent) { /** mousemove window */ function onMousemoveWindow(evt: MouseEvent) { + if (!props.active) { + return + } + mouseCoords.value = { x: evt.clientX, y: evt.clientY } } @@ -554,6 +551,50 @@ function onMouseupWindow() { resetState() } +/** pointerdown on position */ +function onPointerdownPosition(index: number, evt: PointerEvent) { + evt.preventDefault() + + const piece = props.hexchess?.board[index] + + if (!piece) { + return + } + + if (props.autoselect) { + selected.value = index + targets.value = props.hexchess?.movesFrom(index).map(san => san.to) ?? [] + } + + if (!isPlayingPosition(index)) { + return + } + + // Only allow dragging if it's the piece's turn + const pieceColor: Color = piece === piece.toLowerCase() ? 'b' : 'w' + const isCurrentTurn = props.hexchess?.turn === pieceColor + + if (!isCurrentTurn) { + return + } + + mousedownPosition.value = index + + if (svgEl.value instanceof Element) { + svgRect.value = svgEl.value.getBoundingClientRect() + } +} + +/** mouseenter position */ +function onPointerenter(index: number) { + mouseoverPosition.value = index +} + +/** mouseleave position */ +function onPointerleave() { + mouseoverPosition.value = null +} + /** reset state */ function resetState() { console.log('resetState') @@ -570,7 +611,9 @@ function resetState() { function unlisten() { resetState() window.removeEventListener('keyup', onKeyupWindow) - window.removeEventListener('mousemove', onMousemoveWindow) - window.removeEventListener('mouseup', onMouseupWindow) + window.removeEventListener('pointermove', onMousemoveWindow) + window.removeEventListener('pointerup', onMouseupWindow) + window.removeEventListener('resize', measurePromotionRect) + window.removeEventListener('scroll', measurePromotionRect) } - \ No newline at end of file + From 594ef7d562846c7f722e499a2ddd072a328494b6 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Sun, 7 Dec 2025 21:11:29 -0600 Subject: [PATCH 62/80] basic movement --- ui/package.json | 1 - ui/pnpm-lock.yaml | 8 -- ui/src/App.vue | 100 +++++++++++++++++++++--- ui/src/composables/use-dom-rect.ts | 30 +++++++ ui/src/lib/hexboard/Hexboard.vue | 91 ++++++++++++++------- ui/src/lib/pieces/AlphaPieces.vue | 24 +++--- ui/src/lib/pieces/AnarcandyPieces.vue | 24 +++--- ui/src/lib/pieces/CalientePieces.vue | 24 +++--- ui/src/lib/pieces/CaliforniaPieces.vue | 24 +++--- ui/src/lib/pieces/CardinalPieces.vue | 24 +++--- ui/src/lib/pieces/CburnettPieces.vue | 24 +++--- ui/src/lib/pieces/CelticPieces.vue | 24 +++--- ui/src/lib/pieces/Chess7Pieces.vue | 24 +++--- ui/src/lib/pieces/ChessnutPieces.vue | 24 +++--- ui/src/lib/pieces/CompanionPieces.vue | 24 +++--- ui/src/lib/pieces/CookePieces.vue | 24 +++--- ui/src/lib/pieces/DisguisedPieces.vue | 24 +++--- ui/src/lib/pieces/DubrovnyPieces.vue | 24 +++--- ui/src/lib/pieces/FantasyPieces.vue | 24 +++--- ui/src/lib/pieces/FiriPieces.vue | 24 +++--- ui/src/lib/pieces/FrescaPieces.vue | 24 +++--- ui/src/lib/pieces/GiocoPieces.vue | 24 +++--- ui/src/lib/pieces/GovernorPieces.vue | 24 +++--- ui/src/lib/pieces/HorseyPieces.vue | 24 +++--- ui/src/lib/pieces/IcpiecesPieces.vue | 24 +++--- ui/src/lib/pieces/KiwenSuwiPieces.vue | 24 +++--- ui/src/lib/pieces/KosalPieces.vue | 24 +++--- ui/src/lib/pieces/LeipzigPieces.vue | 24 +++--- ui/src/lib/pieces/LetterPieces.vue | 24 +++--- ui/src/lib/pieces/MaestroPieces.vue | 24 +++--- ui/src/lib/pieces/MeridaPieces.vue | 24 +++--- ui/src/lib/pieces/MonarchyPieces.vue | 24 +++--- ui/src/lib/pieces/MonoPieces.vue | 24 +++--- ui/src/lib/pieces/MpchessPieces.vue | 24 +++--- ui/src/lib/pieces/PirouettiPieces.vue | 24 +++--- ui/src/lib/pieces/PixelPieces.vue | 24 +++--- ui/src/lib/pieces/ReillycraigPieces.vue | 24 +++--- ui/src/lib/pieces/RhosgfxPieces.vue | 24 +++--- ui/src/lib/pieces/RiohachaPieces.vue | 24 +++--- ui/src/lib/pieces/ShapesPieces.vue | 24 +++--- ui/src/lib/pieces/SpatialPieces.vue | 24 +++--- ui/src/lib/pieces/StauntyPieces.vue | 24 +++--- ui/src/lib/pieces/TatianaPieces.vue | 24 +++--- ui/src/lib/pieces/XkcdPieces.vue | 24 +++--- 44 files changed, 649 insertions(+), 517 deletions(-) create mode 100644 ui/src/composables/use-dom-rect.ts diff --git a/ui/package.json b/ui/package.json index 7639f05d..bf7b7a34 100644 --- a/ui/package.json +++ b/ui/package.json @@ -22,7 +22,6 @@ "@vitest/coverage-v8": "^4.0.13", "@vitest/ui": "^4.0.13", "@vue/test-utils": "^2.4.6", - "es-toolkit": "^1.42.0", "jsdom": "^27.2.0", "playwright": "^1.56.1", "typescript": "^5.9.3", diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index 1d710552..9bfe51a7 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -44,9 +44,6 @@ importers: '@vue/test-utils': specifier: ^2.4.6 version: 2.4.6 - es-toolkit: - specifier: ^1.42.0 - version: 1.42.0 jsdom: specifier: ^27.2.0 version: 27.2.0 @@ -1070,9 +1067,6 @@ packages: es-module-lexer@1.7.0: resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} - es-toolkit@1.42.0: - resolution: {integrity: sha512-SLHIyY7VfDJBM8clz4+T2oquwTQxEzu263AyhVK4jREOAwJ+8eebaa4wM3nlvnAqhDrMm2EsA6hWHaQsMPQ1nA==} - esbuild@0.25.12: resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} engines: {node: '>=18'} @@ -2830,8 +2824,6 @@ snapshots: es-module-lexer@1.7.0: {} - es-toolkit@1.42.0: {} - esbuild@0.25.12: optionalDependencies: '@esbuild/aix-ppc64': 0.25.12 diff --git a/ui/src/App.vue b/ui/src/App.vue index b301c8e1..ef6f3f62 100644 --- a/ui/src/App.vue +++ b/ui/src/App.vue @@ -26,6 +26,8 @@ +
+ - \ No newline at end of file + diff --git a/ui/src/composables/use-dom-rect.ts b/ui/src/composables/use-dom-rect.ts new file mode 100644 index 00000000..a96690e0 --- /dev/null +++ b/ui/src/composables/use-dom-rect.ts @@ -0,0 +1,30 @@ +import { onMounted, onUnmounted, shallowRef, type TemplateRef } from 'vue' + +export function useDomRect(el: TemplateRef) { + const rect = shallowRef(new DOMRect()) + + function measure() { + rect.value = el.value?.getBoundingClientRect() ?? new DOMRect() + } + + function reset() { + rect.value = new DOMRect() + } + + onMounted(() => { + measure() + window.addEventListener('resize', measure) + window.addEventListener('scroll', measure) + }) + + onUnmounted(() => { + window.removeEventListener('resize', measure) + window.removeEventListener('scroll', measure) + }) + + return { + measure, + rect, + reset, + } +} \ No newline at end of file diff --git a/ui/src/lib/hexboard/Hexboard.vue b/ui/src/lib/hexboard/Hexboard.vue index 351590a7..654c54c2 100644 --- a/ui/src/lib/hexboard/Hexboard.vue +++ b/ui/src/lib/hexboard/Hexboard.vue @@ -125,20 +125,21 @@
- + }" + @pointerup.stop> +
- -
{{  { mouseCoords } }}
@@ -220,15 +221,18 @@ const mousedownPosition = shallowRef(null) /** rect of promotion anchor element */ const promotionRect = shallowRef(new DOMRect()) -/** selected element */ -const selectedEl = useTemplateRef('selectedEl') - /** staging display data */ const staging = shallowRef<{ hexchess: Hexchess | null + promotionEl: Element | null + promotionFrom: number | null + promotionTo: number | null selected: number | null }>({ hexchess: null, + promotionEl: null, + promotionFrom: null, + promotionTo: null, selected: null, }) @@ -386,15 +390,20 @@ watch(() => props.active, val => val ? listen() : unlisten()) // /** attempt to move piece from source to target position */ -function attemptMove(from: number, to: number, evt: MouseEvent) { +function attemptMove( + san: San, + evt?: MouseEvent +) { // Check if target is valid - if (!targets.value.includes(to)) { + if (!targets.value.includes(san.to)) { + console.log('target not found', san.to) return } - const piece = props.hexchess?.board[from] + const piece = props.hexchess?.board[san.from] if (!piece) { + console.log('piece not found', san.from) return } @@ -406,17 +415,20 @@ function attemptMove(from: number, to: number, evt: MouseEvent) { if ( props.hexchess && (piece === 'p' || piece === 'P') && - isPromotionPosition(to, pieceColor) + isPromotionPosition(san.to, pieceColor) ) { const clone = props.hexchess.clone() - clone.board[from] = null - clone.board[to] = piece + clone.board[san.from] = null + clone.board[san.to] = piece staging.value = { hexchess: clone, - selected: to, + promotionEl: evt?.target instanceof Element ? evt.target : null, + promotionFrom: san.from, + promotionTo: san.to, + selected: san.to, } - if (evt.target instanceof Element) { + if (evt?.target instanceof Element) { promotionRect.value = evt.target.getBoundingClientRect() } @@ -424,8 +436,8 @@ function attemptMove(from: number, to: number, evt: MouseEvent) { } // Only call onPieceMove if playing this color and it's their turn - if (isPlayingPosition(from) && isCurrentTurn) { - onPieceMove(from, to) + if (isPlayingPosition(san.from) && isCurrentTurn) { + onPieceMove(san) } } @@ -469,13 +481,9 @@ function listen() { window.addEventListener('scroll', measurePromotionRect) } - - /** measure promotion element rect */ function measurePromotionRect() { - if (selectedEl.value instanceof Element) { - promotionRect.value = selectedEl.value.getBoundingClientRect() - } + promotionRect.value = staging.value.promotionEl?.getBoundingClientRect() ?? new DOMRect() } /** click position */ @@ -486,7 +494,8 @@ function onClickPosition(index: number, evt: MouseEvent) { // If there's a selected piece and clicking a target, attempt to move if (selected.value !== null && targets.value.includes(index)) { - attemptMove(selected.value, index, evt) + const san = new San({ from: selected.value, to: index }) + attemptMove(san, evt) return } @@ -508,8 +517,8 @@ function onKeyupWindow(evt: KeyboardEvent) { } /** handle piece move */ -function onPieceMove(from: number, to: number) { - const san = new San({ from, to }) +function onPieceMove(san: San) { + console.log('onPieceMove', san) emit('move', san) @@ -526,7 +535,8 @@ function onMouseupPosition(index: number, evt: MouseEvent) { // Check if we're dropping a piece on a valid target if (mousedownPosition.value !== null) { - attemptMove(mousedownPosition.value, index, evt) + const san = new San({ from: mousedownPosition.value, to: index }) + attemptMove(san, evt) } /** do nothing if staging is set */ @@ -595,13 +605,34 @@ function onPointerleave() { mouseoverPosition.value = null } +/** promote piece */ +function promote(promotion: 'n' | 'b' | 'r' | 'q') { + if ( + typeof staging.value.promotionFrom === 'number' && + isPlayingPosition(staging.value.promotionFrom) + ) { + const san = new San({ + from: staging.value.promotionFrom ?? 0, + to: staging.value.promotionTo ?? 0, + promotion: promotion, + }) + + emit('move', san) + + resetState() + } +} + /** reset state */ function resetState() { - console.log('resetState') document.body.style.setProperty('cursor', null) mousedownPosition.value = null + selected.value = null staging.value = { hexchess: null, + promotionEl: null, + promotionFrom: null, + promotionTo: null, selected: null, } svgRect.value = new DOMRect() diff --git a/ui/src/lib/pieces/AlphaPieces.vue b/ui/src/lib/pieces/AlphaPieces.vue index 619b92ca..64910086 100644 --- a/ui/src/lib/pieces/AlphaPieces.vue +++ b/ui/src/lib/pieces/AlphaPieces.vue @@ -1,16 +1,16 @@ diff --git a/ui/src/lib/hexboard/Hexboard.vue b/ui/src/lib/hexboard/Hexboard.vue index 412e444d..7579c212 100644 --- a/ui/src/lib/hexboard/Hexboard.vue +++ b/ui/src/lib/hexboard/Hexboard.vue @@ -147,12 +147,6 @@ :r="promotionPieces.r" :rank="Number(indexToPosition(staging.selected).slice(1))" /> - -
{{ {
-      selected,
-      targets,
-      playing,
-    } }}
diff --git a/ui/src/test/user-interaction.test.tsx b/ui/src/test/user-interaction.test.tsx index 502bb2d2..c8ec0870 100644 --- a/ui/src/test/user-interaction.test.tsx +++ b/ui/src/test/user-interaction.test.tsx @@ -573,6 +573,53 @@ test('promotion', async () => { await expect(page.getByTestId('piece-f11')).toHaveAttribute('data-piece-type', 'Q') }) +test('canceled promotion', async () => { + const selected = ref(null) + const onMove = vi.fn() + + setup(() => { + const hexchess = ref(Hexchess.parse('1/1P1/5/7/9/11/11/11/11/11/11 w - 0 1')) + + return () => <> + {{ + promotion: ({ cancel }: any) => + }} + + }) + + // Move pawn to promotion square + await movePiece(page, 'f10', 'f11') + + // Promotion UI should be visible + await expect.element(page.getByTestId('cancel')).toBeVisible() + + // Cancel the promotion + await page.getByTestId('cancel').click() + await nextTick() + + // Move should not have been emitted + await expect(onMove).not.toHaveBeenCalled() + + // Original piece should still be selected + await expect(selected.value).toBe(index('f10')) + await expect.element(page.getByTestId('selected-f10')).toBeVisible() + + // Pawn should still be at original position + await expect.element(page.getByTestId('piece-f10')).toHaveAttribute('data-piece-type', 'P') +}) + + + test('ignoreTurn allows moving pieces out of turn', async () => { const ignoreTurn = ref(false) const onMove = vi.fn() From 7ca5dd499edde82f763a57630f7c1ad80aed0412 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Tue, 9 Dec 2025 20:23:40 -0600 Subject: [PATCH 74/80] cleanup --- ui/src/App.vue | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/ui/src/App.vue b/ui/src/App.vue index 9e0c9366..e7cdf7ca 100644 --- a/ui/src/App.vue +++ b/ui/src/App.vue @@ -26,8 +26,6 @@ -
- @@ -94,8 +92,6 @@
- -
From 3b3d56907ad437f0b82fddfa341c931108b83b04 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Tue, 9 Dec 2025 20:34:02 -0600 Subject: [PATCH 75/80] cleanup naming --- ui/src/lib/hexboard/Hexboard.vue | 58 ++++++++++++++++---------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/ui/src/lib/hexboard/Hexboard.vue b/ui/src/lib/hexboard/Hexboard.vue index 7579c212..f856ce32 100644 --- a/ui/src/lib/hexboard/Hexboard.vue +++ b/ui/src/lib/hexboard/Hexboard.vue @@ -17,10 +17,10 @@ v-for="position, index in board" v-bind="active && !staging.hexchess ? { onClick: evt => onClickPosition(index, evt), + onMouseenter: () => onMouseenter(index), + onMouseleave: () => onMouseleave(), onPointerdown: evt => onPointerdownPosition(index, evt), - onPointerenter: () => onPointerenter(index), - onPointerleave: () => onPointerleave(), - onPointerup: evt => onMouseupPosition(index, evt), + onPointerup: evt => onPointerupPosition(index, evt), } : {}" :d="d(flipped ? position[4] : position[3])" :data-hexboard-position="index" @@ -73,7 +73,7 @@ v-if="currentHexchess" v-for="piece, index in currentHexchess.board"> ('targets', { // state // -/** current mouse coordinates */ -const mouseCoords = shallowRef({ x: 0, y: 0 }) +/** current pointer coordinates */ +const pointerCoords = shallowRef({ x: 0, y: 0 }) -/** fen position of mousedown */ -const mousedownPosition = shallowRef(null) +/** fen position of pointerdown */ +const pointerdownPosition = shallowRef(null) /** rect of promotion anchor element */ const promotionRect = shallowRef(new DOMRect()) @@ -249,7 +249,7 @@ const staging = shallowRef<{ /** svg rect */ const svgEl = useTemplateRef('svgEl') -/** rect of svg element on mousedown */ +/** rect of svg element on pointerdown */ const svgRect = shallowRef(new DOMRect()) // @@ -338,8 +338,8 @@ const cursor = computed(() => { /** coordinates of drag transformation */ const dragCoords = computed(() => { return { - x: mouseCoords.value.x - (svgRect.value.width / 2), - y: mouseCoords.value.y - (svgRect.value.height / 2), + x: pointerCoords.value.x - (svgRect.value.width / 2), + y: pointerCoords.value.y - (svgRect.value.height / 2), } }) @@ -348,12 +348,12 @@ const dragPiece = computed(() => { if ( !props.hexchess || staging.value.hexchess || - mousedownPosition.value === null + pointerdownPosition.value === null ) { return null } - return props.hexchess.board[mousedownPosition.value] + return props.hexchess.board[pointerdownPosition.value] }) /** normalized options */ @@ -503,7 +503,7 @@ function getLabelFill(text: string) { /** listen for events */ function listen() { - mouseCoords.value = { x: 0, y: 0 } + pointerCoords.value = { x: 0, y: 0 } window.addEventListener('keyup', onKeyupWindow) window.addEventListener('pointermove', onPointermoveWindow) @@ -563,13 +563,13 @@ function onPieceMove(san: San) { resetState() } -/** mouseup position */ -function onMouseupPosition(index: number, evt: MouseEvent) { +/** pointerup position */ +function onPointerupPosition(index: number, evt: MouseEvent) { evt.stopPropagation() // Check if we're dropping a piece on a valid target (drag and drop) - if (mousedownPosition.value !== null) { - const san = new San({ from: mousedownPosition.value, to: index }) + if (pointerdownPosition.value !== null) { + const san = new San({ from: pointerdownPosition.value, to: index }) attemptMove(san, evt) } // Check if clicking on a target while a piece is selected (click to move) @@ -585,7 +585,7 @@ function onMouseupPosition(index: number, evt: MouseEvent) { // If clicking on any piece, keep the selection (it was set in pointerdown) if (props.hexchess?.board[index]) { - mousedownPosition.value = null + pointerdownPosition.value = null svgRect.value = new DOMRect() return } @@ -611,7 +611,7 @@ function cancelPromotion() { targets.value = props.hexchess.movesFrom(from).map(san => san.to) ?? [] } - mousedownPosition.value = null + pointerdownPosition.value = null } /** pointerdown on position */ @@ -641,7 +641,7 @@ function onPointerdownPosition(index: number, evt: PointerEvent) { return } - mousedownPosition.value = index + pointerdownPosition.value = index if (svgEl.value instanceof Element) { svgRect.value = svgEl.value.getBoundingClientRect() @@ -649,25 +649,25 @@ function onPointerdownPosition(index: number, evt: PointerEvent) { } /** mouseenter position */ -function onPointerenter(index: number) { +function onMouseenter(index: number) { mouseoverPosition.value = index } /** mouseleave position */ -function onPointerleave() { +function onMouseleave() { mouseoverPosition.value = null } -/** mousemove window */ +/** pointermove window */ function onPointermoveWindow(evt: MouseEvent) { if (!props.active) { return } - mouseCoords.value = { x: evt.clientX, y: evt.clientY } + pointerCoords.value = { x: evt.clientX, y: evt.clientY } } -/** mouseup window */ +/** pointerup window */ function onPointerupWindow() { // If staging a promotion, cancel it but keep the original piece selected if (staging.value.hexchess) { @@ -676,8 +676,8 @@ function onPointerupWindow() { } // If dragging a piece, keep the selection but reset drag state - if (mousedownPosition.value !== null) { - mousedownPosition.value = null + if (pointerdownPosition.value !== null) { + pointerdownPosition.value = null svgRect.value = new DOMRect() return } @@ -704,7 +704,7 @@ function promote(promotion: 'n' | 'b' | 'r' | 'q') { /** reset state */ function resetState() { document.body.style.setProperty('cursor', null) - mousedownPosition.value = null + pointerdownPosition.value = null selected.value = null staging.value = { hexchess: null, From 7917db0712426530dd372ecdb506b4a6426b416c Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Tue, 9 Dec 2025 20:40:48 -0600 Subject: [PATCH 76/80] drag consistency --- ui/src/lib/hexboard/Hexboard.vue | 22 ++++++++++++++-- ui/src/test/user-interaction.test.tsx | 37 +++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/ui/src/lib/hexboard/Hexboard.vue b/ui/src/lib/hexboard/Hexboard.vue index f856ce32..fb832abe 100644 --- a/ui/src/lib/hexboard/Hexboard.vue +++ b/ui/src/lib/hexboard/Hexboard.vue @@ -571,11 +571,30 @@ function onPointerupPosition(index: number, evt: MouseEvent) { if (pointerdownPosition.value !== null) { const san = new San({ from: pointerdownPosition.value, to: index }) attemptMove(san, evt) + + // If staging a promotion, don't reset + if (staging.value.hexchess) { + return + } + + // Keep selection but reset drag state + pointerdownPosition.value = null + svgRect.value = new DOMRect() + return } + // Check if clicking on a target while a piece is selected (click to move) - else if (selected.value !== null && targets.value.includes(index)) { + if (selected.value !== null && targets.value.includes(index)) { const san = new San({ from: selected.value, to: index }) attemptMove(san, evt) + + // If staging a promotion, don't reset + if (staging.value.hexchess) { + return + } + + // Move was made, reset state + return } /** do nothing if staging is set */ @@ -715,7 +734,6 @@ function resetState() { } svgRect.value = new DOMRect() targets.value = [] - console.log('resetState') } /** stop listening for events */ diff --git a/ui/src/test/user-interaction.test.tsx b/ui/src/test/user-interaction.test.tsx index c8ec0870..60f5d637 100644 --- a/ui/src/test/user-interaction.test.tsx +++ b/ui/src/test/user-interaction.test.tsx @@ -650,3 +650,40 @@ test('ignoreTurn allows moving pieces out of turn', async () => { })) }) +test('dragging piece to non-target position keeps selection', async () => { + const selected = ref(null) + const targets = ref([]) + + setup(() => { + return () => <> + +
+ + }) + + const piecePosition = page.getByTestId('position-f5') + const nonTargetPosition = page.getByTestId('position-a1') + + // Start dragging the piece + await piecePosition.element().dispatchEvent(new PointerEvent('pointerdown', { bubbles: true })) + await nextTick() + + // Verify piece is selected + await expect.element(page.getByTestId('selected-f5')).toBeVisible() + await expect(selected.value).toBe(index('f5')) + + // Release on a non-target position + await nonTargetPosition.element().dispatchEvent(new PointerEvent('pointerup', { bubbles: true })) + await nextTick() + + // Piece should still be selected + await expect.element(page.getByTestId('selected-f5')).toBeVisible() + await expect(selected.value).toBe(index('f5')) +}) + From dabf417388c773dff8e69cf6e50114fff82f85c7 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Tue, 9 Dec 2025 21:13:30 -0600 Subject: [PATCH 77/80] improve consistency --- ui/src/lib/hexboard/Hexboard.vue | 28 ++++++++++++++++-- ui/src/test/user-interaction.test.tsx | 41 +++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/ui/src/lib/hexboard/Hexboard.vue b/ui/src/lib/hexboard/Hexboard.vue index fb832abe..17916447 100644 --- a/ui/src/lib/hexboard/Hexboard.vue +++ b/ui/src/lib/hexboard/Hexboard.vue @@ -15,7 +15,7 @@ onClickPosition(index, evt), onMouseenter: () => onMouseenter(index), onMouseleave: () => onMouseleave(), @@ -252,6 +252,9 @@ const svgEl = useTemplateRef('svgEl') /** rect of svg element on pointerdown */ const svgRect = shallowRef(new DOMRect()) +/** flag to skip click handling after promotion cancel */ +let skipNextClick = false + // // computed // @@ -523,6 +526,20 @@ function onClickPosition(index: number, evt: MouseEvent) { return } + // Skip this click if we just canceled a promotion (handled by pointerup) + if (skipNextClick) { + skipNextClick = false + return + } + + // If staging a promotion, cancel it unless clicking on the promotion position + if (staging.value.hexchess) { + if (staging.value.selected !== index) { + cancelPromotion() + } + return + } + // If there's a selected piece and clicking a target, attempt to move if (selected.value !== null && targets.value.includes(index)) { const san = new San({ from: selected.value, to: index }) @@ -597,8 +614,9 @@ function onPointerupPosition(index: number, evt: MouseEvent) { return } - /** do nothing if staging is set */ + // If staging a promotion and clicking on a non-target, cancel the promotion if (staging.value.hexchess) { + cancelPromotion() return } @@ -631,12 +649,18 @@ function cancelPromotion() { } pointerdownPosition.value = null + skipNextClick = true } /** pointerdown on position */ function onPointerdownPosition(index: number, evt: PointerEvent) { evt.preventDefault() + // Don't start new interactions during promotion + if (staging.value.hexchess) { + return + } + const piece = props.hexchess?.board[index] if (!piece) { diff --git a/ui/src/test/user-interaction.test.tsx b/ui/src/test/user-interaction.test.tsx index 60f5d637..3dedfa0d 100644 --- a/ui/src/test/user-interaction.test.tsx +++ b/ui/src/test/user-interaction.test.tsx @@ -618,7 +618,48 @@ test('canceled promotion', async () => { await expect.element(page.getByTestId('piece-f10')).toHaveAttribute('data-piece-type', 'P') }) +test('clicking position during promotion cancels it', async () => { + const selected = ref(null) + const onMove = vi.fn() + + setup(() => { + const hexchess = ref(Hexchess.parse('1/1P1/5/7/9/11/11/11/11/11/11 w - 0 1')) + + return () => <> + {{ + promotion: () => + }} + + }) + // Move pawn to promotion square + await movePiece(page, 'f10', 'f11') + + // Promotion UI should be visible + await expect.element(page.getByTestId('promote')).toBeVisible() + + // Click on a different position to cancel the promotion + await page.getByTestId('position-a1').click() + await nextTick() + + // Promotion UI should be gone + await expect.element(page.getByTestId('promote')).not.toBeInTheDocument() + + // Move should not have been emitted + await expect(onMove).not.toHaveBeenCalled() + + // Original piece should still be selected + await expect.element(page.getByTestId('selected-f10')).toBeVisible() + + // Pawn should still be at original position + await expect.element(page.getByTestId('piece-f10')).toHaveAttribute('data-piece-type', 'P') +}) test('ignoreTurn allows moving pieces out of turn', async () => { const ignoreTurn = ref(false) From a026655a54d946c9d51f4ea9eddf0db75226aa7f Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Tue, 9 Dec 2025 22:49:12 -0600 Subject: [PATCH 78/80] use ui component in sandbox --- docs/components/hexboard/Hexboard.vue | 135 ----- docs/components/hexboard/Piece.vue | 677 -------------------------- docs/components/hexboard/constants.ts | 372 -------------- docs/components/hexboard/geometry.ts | 78 --- docs/components/hexboard/types.ts | 6 - docs/sandbox/Sandbox.vue | 128 +++-- 6 files changed, 62 insertions(+), 1334 deletions(-) delete mode 100644 docs/components/hexboard/Hexboard.vue delete mode 100644 docs/components/hexboard/Piece.vue delete mode 100644 docs/components/hexboard/constants.ts delete mode 100644 docs/components/hexboard/geometry.ts delete mode 100644 docs/components/hexboard/types.ts diff --git a/docs/components/hexboard/Hexboard.vue b/docs/components/hexboard/Hexboard.vue deleted file mode 100644 index c013a6d7..00000000 --- a/docs/components/hexboard/Hexboard.vue +++ /dev/null @@ -1,135 +0,0 @@ - - - \ No newline at end of file diff --git a/docs/components/hexboard/Piece.vue b/docs/components/hexboard/Piece.vue deleted file mode 100644 index 16ea80c2..00000000 --- a/docs/components/hexboard/Piece.vue +++ /dev/null @@ -1,677 +0,0 @@ - - - - \ No newline at end of file diff --git a/docs/components/hexboard/constants.ts b/docs/components/hexboard/constants.ts deleted file mode 100644 index 06283d6c..00000000 --- a/docs/components/hexboard/constants.ts +++ /dev/null @@ -1,372 +0,0 @@ -import { positions } from '../../../js/src/constants' -import { bilerp, flip, hexagon, pivot, reflect, sum } from './geometry' -import type { Vec } from './types' - -/** svg viewbox, increasing this zooms out away from the board */ -export const box = 23.6 - -/** position colors */ -export const colors: Vec<3, string> = [ - 'oklch(0.9015 0.0729 70.7)', - 'oklch(0.8366 0.1165 66.29)', - 'oklch(0.6806 0.1423 75.83)', -] - -/** label offset */ -export const labelOffset = 1.7 - -/** piece size */ -export const pieceSize = 1.7 - -/** side length of regular hexagon circumscribed around a unit circle */ -export const sideLength = 2 / Math.sqrt(3) - -// distance between the origin of neighboring hexagons -// one = direct neighbors, two = neighbor of neighbor, etc... -export const one = sideLength * (Math.sqrt(3) / 2) * 2 // <- inscribed radius * 2 -export const two = one * 2 -export const three = one * 3 -export const four = one * 4 -export const five = one * 5 - -// calculate the center for each position, using f6 as the origin -const f6: Vec<2> = [0, 0] - -// the 6th rank is calculated as angled distances from F6 -const a6 = pivot(f6, 150, five) -const b6 = pivot(f6, 150, four) -const c6 = pivot(f6, 150, three) -const d6 = pivot(f6, 150, two) -const e6 = pivot(f6, 150, one) -const g6 = pivot(f6, 30, one) -const h6 = pivot(f6, 30, two) -const i6 = pivot(f6, 30, three) -const k6 = pivot(f6, 30, four) -const l6 = pivot(f6, 30, five) - -// other ranks are calculated by vertically translating the 6th rank -const a1 = sum(a6, [0, -five]) -const b1 = sum(b6, [0, -five]) -const c1 = sum(c6, [0, -five]) -const d1 = sum(d6, [0, -five]) -const e1 = sum(e6, [0, -five]) -const f1 = sum(f6, [0, -five]) -const g1 = sum(g6, [0, -five]) -const h1 = sum(h6, [0, -five]) -const i1 = sum(i6, [0, -five]) -const k1 = sum(k6, [0, -five]) -const l1 = sum(l6, [0, -five]) - -const a2 = sum(a6, [0, -four]) -const b2 = sum(b6, [0, -four]) -const c2 = sum(c6, [0, -four]) -const d2 = sum(d6, [0, -four]) -const e2 = sum(e6, [0, -four]) -const f2 = sum(f6, [0, -four]) -const g2 = sum(g6, [0, -four]) -const h2 = sum(h6, [0, -four]) -const i2 = sum(i6, [0, -four]) -const k2 = sum(k6, [0, -four]) -const l2 = sum(l6, [0, -four]) - -const a3 = sum(a6, [0, -three]) -const b3 = sum(b6, [0, -three]) -const c3 = sum(c6, [0, -three]) -const d3 = sum(d6, [0, -three]) -const e3 = sum(e6, [0, -three]) -const f3 = sum(f6, [0, -three]) -const g3 = sum(g6, [0, -three]) -const h3 = sum(h6, [0, -three]) -const i3 = sum(i6, [0, -three]) -const k3 = sum(k6, [0, -three]) -const l3 = sum(l6, [0, -three]) - -const a4 = sum(a6, [0, -two]) -const b4 = sum(b6, [0, -two]) -const c4 = sum(c6, [0, -two]) -const d4 = sum(d6, [0, -two]) -const e4 = sum(e6, [0, -two]) -const f4 = sum(f6, [0, -two]) -const g4 = sum(g6, [0, -two]) -const h4 = sum(h6, [0, -two]) -const i4 = sum(i6, [0, -two]) -const k4 = sum(k6, [0, -two]) -const l4 = sum(l6, [0, -two]) - -const a5 = sum(a6, [0, -one]) -const b5 = sum(b6, [0, -one]) -const c5 = sum(c6, [0, -one]) -const d5 = sum(d6, [0, -one]) -const e5 = sum(e6, [0, -one]) -const f5 = sum(f6, [0, -one]) -const g5 = sum(g6, [0, -one]) -const h5 = sum(h6, [0, -one]) -const i5 = sum(i6, [0, -one]) -const k5 = sum(k6, [0, -one]) -const l5 = sum(l6, [0, -one]) - -// 7th rank and higher start to have fewer files -const b7 = sum(b6, [0, one]) -const c7 = sum(c6, [0, one]) -const d7 = sum(d6, [0, one]) -const e7 = sum(e6, [0, one]) -const f7 = sum(f6, [0, one]) -const g7 = sum(g6, [0, one]) -const h7 = sum(h6, [0, one]) -const i7 = sum(i6, [0, one]) -const k7 = sum(k6, [0, one]) - -const c8 = sum(c6, [0, two]) -const d8 = sum(d6, [0, two]) -const e8 = sum(e6, [0, two]) -const f8 = sum(f6, [0, two]) -const g8 = sum(g6, [0, two]) -const h8 = sum(h6, [0, two]) -const i8 = sum(i6, [0, two]) - -const d9 = sum(d6, [0, three]) -const e9 = sum(e6, [0, three]) -const f9 = sum(f6, [0, three]) -const g9 = sum(g6, [0, three]) -const h9 = sum(h6, [0, three]) - -const e10 = sum(e6, [0, four]) -const f10 = sum(f6, [0, four]) -const g10 = sum(g6, [0, four]) - -const f11 = sum(f6, [0, five]) - -/** board position data, sorted by fen order */ -export const board: { - color: number - origin: [Vec<2>, Vec<2>] - path: [Vec<6, Vec<2>>, Vec<6, Vec<2>>] -}[] = [ - { color: 2, origin: [f11, reflect(f11)], path: [hexagon(f11), flip(hexagon(f11))] }, - { color: 1, origin: [e10, reflect(e10)], path: [hexagon(e10), flip(hexagon(e10))] }, - { color: 0, origin: [f10, reflect(f10)], path: [hexagon(f10), flip(hexagon(f10))] }, - { color: 1, origin: [g10, reflect(g10)], path: [hexagon(g10), flip(hexagon(g10))] }, - { color: 0, origin: [d9, reflect(d9)], path: [hexagon(d9), flip(hexagon(d9))] }, - { color: 2, origin: [e9, reflect(e9)], path: [hexagon(e9), flip(hexagon(e9))] }, - { color: 1, origin: [f9, reflect(f9)], path: [hexagon(f9), flip(hexagon(f9))] }, - { color: 2, origin: [g9, reflect(g9)], path: [hexagon(g9), flip(hexagon(g9))] }, - { color: 0, origin: [h9, reflect(h9)], path: [hexagon(h9), flip(hexagon(h9))] }, - { color: 2, origin: [c8, reflect(c8)], path: [hexagon(c8), flip(hexagon(c8))] }, - { color: 1, origin: [d8, reflect(d8)], path: [hexagon(d8), flip(hexagon(d8))] }, - { color: 0, origin: [e8, reflect(e8)], path: [hexagon(e8), flip(hexagon(e8))] }, - { color: 2, origin: [f8, reflect(f8)], path: [hexagon(f8), flip(hexagon(f8))] }, - { color: 0, origin: [g8, reflect(g8)], path: [hexagon(g8), flip(hexagon(g8))] }, - { color: 1, origin: [h8, reflect(h8)], path: [hexagon(h8), flip(hexagon(h8))] }, - { color: 2, origin: [i8, reflect(i8)], path: [hexagon(i8), flip(hexagon(i8))] }, - { color: 1, origin: [b7, reflect(b7)], path: [hexagon(b7), flip(hexagon(b7))] }, - { color: 0, origin: [c7, reflect(c7)], path: [hexagon(c7), flip(hexagon(c7))] }, - { color: 2, origin: [d7, reflect(d7)], path: [hexagon(d7), flip(hexagon(d7))] }, - { color: 1, origin: [e7, reflect(e7)], path: [hexagon(e7), flip(hexagon(e7))] }, - { color: 0, origin: [f7, reflect(f7)], path: [hexagon(f7), flip(hexagon(f7))] }, - { color: 1, origin: [g7, reflect(g7)], path: [hexagon(g7), flip(hexagon(g7))] }, - { color: 2, origin: [h7, reflect(h7)], path: [hexagon(h7), flip(hexagon(h7))] }, - { color: 0, origin: [i7, reflect(i7)], path: [hexagon(i7), flip(hexagon(i7))] }, - { color: 1, origin: [k7, reflect(k7)], path: [hexagon(k7), flip(hexagon(k7))] }, - { color: 0, origin: [a6, reflect(a6)], path: [hexagon(a6), flip(hexagon(a6))] }, - { color: 2, origin: [b6, reflect(b6)], path: [hexagon(b6), flip(hexagon(b6))] }, - { color: 1, origin: [c6, reflect(c6)], path: [hexagon(c6), flip(hexagon(c6))] }, - { color: 0, origin: [d6, reflect(d6)], path: [hexagon(d6), flip(hexagon(d6))] }, - { color: 2, origin: [e6, reflect(e6)], path: [hexagon(e6), flip(hexagon(e6))] }, - { color: 1, origin: [f6, reflect(f6)], path: [hexagon(f6), flip(hexagon(f6))] }, - { color: 2, origin: [g6, reflect(g6)], path: [hexagon(g6), flip(hexagon(g6))] }, - { color: 0, origin: [h6, reflect(h6)], path: [hexagon(h6), flip(hexagon(h6))] }, - { color: 1, origin: [i6, reflect(i6)], path: [hexagon(i6), flip(hexagon(i6))] }, - { color: 2, origin: [k6, reflect(k6)], path: [hexagon(k6), flip(hexagon(k6))] }, - { color: 0, origin: [l6, reflect(l6)], path: [hexagon(l6), flip(hexagon(l6))] }, - { color: 1, origin: [a5, reflect(a5)], path: [hexagon(a5), flip(hexagon(a5))] }, - { color: 0, origin: [b5, reflect(b5)], path: [hexagon(b5), flip(hexagon(b5))] }, - { color: 2, origin: [c5, reflect(c5)], path: [hexagon(c5), flip(hexagon(c5))] }, - { color: 1, origin: [d5, reflect(d5)], path: [hexagon(d5), flip(hexagon(d5))] }, - { color: 0, origin: [e5, reflect(e5)], path: [hexagon(e5), flip(hexagon(e5))] }, - { color: 2, origin: [f5, reflect(f5)], path: [hexagon(f5), flip(hexagon(f5))] }, - { color: 0, origin: [g5, reflect(g5)], path: [hexagon(g5), flip(hexagon(g5))] }, - { color: 1, origin: [h5, reflect(h5)], path: [hexagon(h5), flip(hexagon(h5))] }, - { color: 2, origin: [i5, reflect(i5)], path: [hexagon(i5), flip(hexagon(i5))] }, - { color: 0, origin: [k5, reflect(k5)], path: [hexagon(k5), flip(hexagon(k5))] }, - { color: 1, origin: [l5, reflect(l5)], path: [hexagon(l5), flip(hexagon(l5))] }, - { color: 2, origin: [a4, reflect(a4)], path: [hexagon(a4), flip(hexagon(a4))] }, - { color: 1, origin: [b4, reflect(b4)], path: [hexagon(b4), flip(hexagon(b4))] }, - { color: 0, origin: [c4, reflect(c4)], path: [hexagon(c4), flip(hexagon(c4))] }, - { color: 2, origin: [d4, reflect(d4)], path: [hexagon(d4), flip(hexagon(d4))] }, - { color: 1, origin: [e4, reflect(e4)], path: [hexagon(e4), flip(hexagon(e4))] }, - { color: 0, origin: [f4, reflect(f4)], path: [hexagon(f4), flip(hexagon(f4))] }, - { color: 1, origin: [g4, reflect(g4)], path: [hexagon(g4), flip(hexagon(g4))] }, - { color: 2, origin: [h4, reflect(h4)], path: [hexagon(h4), flip(hexagon(h4))] }, - { color: 0, origin: [i4, reflect(i4)], path: [hexagon(i4), flip(hexagon(i4))] }, - { color: 1, origin: [k4, reflect(k4)], path: [hexagon(k4), flip(hexagon(k4))] }, - { color: 2, origin: [l4, reflect(l4)], path: [hexagon(l4), flip(hexagon(l4))] }, - { color: 0, origin: [a3, reflect(a3)], path: [hexagon(a3), flip(hexagon(a3))] }, - { color: 2, origin: [b3, reflect(b3)], path: [hexagon(b3), flip(hexagon(b3))] }, - { color: 1, origin: [c3, reflect(c3)], path: [hexagon(c3), flip(hexagon(c3))] }, - { color: 0, origin: [d3, reflect(d3)], path: [hexagon(d3), flip(hexagon(d3))] }, - { color: 2, origin: [e3, reflect(e3)], path: [hexagon(e3), flip(hexagon(e3))] }, - { color: 1, origin: [f3, reflect(f3)], path: [hexagon(f3), flip(hexagon(f3))] }, - { color: 2, origin: [g3, reflect(g3)], path: [hexagon(g3), flip(hexagon(g3))] }, - { color: 0, origin: [h3, reflect(h3)], path: [hexagon(h3), flip(hexagon(h3))] }, - { color: 1, origin: [i3, reflect(i3)], path: [hexagon(i3), flip(hexagon(i3))] }, - { color: 2, origin: [k3, reflect(k3)], path: [hexagon(k3), flip(hexagon(k3))] }, - { color: 0, origin: [l3, reflect(l3)], path: [hexagon(l3), flip(hexagon(l3))] }, - { color: 1, origin: [a2, reflect(a2)], path: [hexagon(a2), flip(hexagon(a2))] }, - { color: 0, origin: [b2, reflect(b2)], path: [hexagon(b2), flip(hexagon(b2))] }, - { color: 2, origin: [c2, reflect(c2)], path: [hexagon(c2), flip(hexagon(c2))] }, - { color: 1, origin: [d2, reflect(d2)], path: [hexagon(d2), flip(hexagon(d2))] }, - { color: 0, origin: [e2, reflect(e2)], path: [hexagon(e2), flip(hexagon(e2))] }, - { color: 2, origin: [f2, reflect(f2)], path: [hexagon(f2), flip(hexagon(f2))] }, - { color: 0, origin: [g2, reflect(g2)], path: [hexagon(g2), flip(hexagon(g2))] }, - { color: 1, origin: [h2, reflect(h2)], path: [hexagon(h2), flip(hexagon(h2))] }, - { color: 2, origin: [i2, reflect(i2)], path: [hexagon(i2), flip(hexagon(i2))] }, - { color: 0, origin: [k2, reflect(k2)], path: [hexagon(k2), flip(hexagon(k2))] }, - { color: 1, origin: [l2, reflect(l2)], path: [hexagon(l2), flip(hexagon(l2))] }, - { color: 2, origin: [a1, reflect(a1)], path: [hexagon(a1), flip(hexagon(a1))] }, - { color: 1, origin: [b1, reflect(b1)], path: [hexagon(b1), flip(hexagon(b1))] }, - { color: 0, origin: [c1, reflect(c1)], path: [hexagon(c1), flip(hexagon(c1))] }, - { color: 2, origin: [d1, reflect(d1)], path: [hexagon(d1), flip(hexagon(d1))] }, - { color: 1, origin: [e1, reflect(e1)], path: [hexagon(e1), flip(hexagon(e1))] }, - { color: 0, origin: [f1, reflect(f1)], path: [hexagon(f1), flip(hexagon(f1))] }, - { color: 1, origin: [g1, reflect(g1)], path: [hexagon(g1), flip(hexagon(g1))] }, - { color: 2, origin: [h1, reflect(h1)], path: [hexagon(h1), flip(hexagon(h1))] }, - { color: 0, origin: [i1, reflect(i1)], path: [hexagon(i1), flip(hexagon(i1))] }, - { color: 1, origin: [k1, reflect(k1)], path: [hexagon(k1), flip(hexagon(k1))] }, - { color: 2, origin: [l1, reflect(l1)], path: [hexagon(l1), flip(hexagon(l1))] }, -] - -/** board labels */ -export const labels: [string, Vec<2>, Vec<2>][] = [ - ['11', bilerp(g10, f11, labelOffset), reflect(bilerp(g10, f11, labelOffset))], - ['10', bilerp(f10, e10, labelOffset), reflect(bilerp(f10, e10, labelOffset))], - ['9', bilerp(e9, d9, labelOffset), reflect(bilerp(e9, d9, labelOffset))], - ['8', bilerp(d8, c8, labelOffset), reflect(bilerp(d8, c8, labelOffset))], - ['7', bilerp(c7, b7, labelOffset), reflect(bilerp(c7, b7, labelOffset))], - ['6', bilerp(b6, a6, labelOffset), reflect(bilerp(b6, a6, labelOffset))], - ['5', bilerp(b5, a5, labelOffset), reflect(bilerp(b5, a5, labelOffset))], - ['4', bilerp(b4, a4, labelOffset), reflect(bilerp(b4, a4, labelOffset))], - ['3', bilerp(b3, a3, labelOffset), reflect(bilerp(b3, a3, labelOffset))], - ['2', bilerp(b2, a2, labelOffset), reflect(bilerp(b2, a2, labelOffset))], - ['1', bilerp(b1, a1, labelOffset), reflect(bilerp(b1, a1, labelOffset))], - - ['a', bilerp(a2, a1, labelOffset), reflect(bilerp(a2, a1, labelOffset))], - ['b', bilerp(b2, b1, labelOffset), reflect(bilerp(b2, b1, labelOffset))], - ['c', bilerp(c2, c1, labelOffset), reflect(bilerp(c2, c1, labelOffset))], - ['d', bilerp(d2, d1, labelOffset), reflect(bilerp(d2, d1, labelOffset))], - ['e', bilerp(e2, e1, labelOffset), reflect(bilerp(e2, e1, labelOffset))], - ['f', bilerp(f2, f1, labelOffset), reflect(bilerp(f2, f1, labelOffset))], - ['g', bilerp(g2, g1, labelOffset), reflect(bilerp(g2, g1, labelOffset))], - ['h', bilerp(h2, h1, labelOffset), reflect(bilerp(h2, h1, labelOffset))], - ['i', bilerp(i2, i1, labelOffset), reflect(bilerp(i2, i1, labelOffset))], - ['k', bilerp(k2, k1, labelOffset), reflect(bilerp(k2, k1, labelOffset))], - ['l', bilerp(l2, l1, labelOffset), reflect(bilerp(l2, l1, labelOffset))], - - ['1', bilerp(k1, l1, labelOffset), reflect(bilerp(k1, l1, labelOffset))], - ['2', bilerp(k2, l2, labelOffset), reflect(bilerp(k2, l2, labelOffset))], - ['3', bilerp(k3, l3, labelOffset), reflect(bilerp(k3, l3, labelOffset))], - ['4', bilerp(k4, l4, labelOffset), reflect(bilerp(k4, l4, labelOffset))], - ['5', bilerp(k5, l5, labelOffset), reflect(bilerp(k5, l5, labelOffset))], - ['6', bilerp(k6, l6, labelOffset), reflect(bilerp(k6, l6, labelOffset))], - ['7', bilerp(i7, k7, labelOffset), reflect(bilerp(i7, k7, labelOffset))], - ['8', bilerp(h8, i8, labelOffset), reflect(bilerp(h8, i8, labelOffset))], - ['9', bilerp(g9, h9, labelOffset), reflect(bilerp(g9, h9, labelOffset))], - ['10', bilerp(f10, g10, labelOffset), reflect(bilerp(f10, g10, labelOffset))], - ['11', bilerp(e10, f11, labelOffset), reflect(bilerp(e10, f11, labelOffset))], -] - -/** perimeter of the board, used to create a constant background for light/dark mode */ -export const perimeter: Vec<93, Vec<2>> = [ - board[positions.indexOf('f11')]!.path[0][5], - board[positions.indexOf('f11')]!.path[0][0], - board[positions.indexOf('f11')]!.path[0][1], - board[positions.indexOf('g10')]!.path[0][5], - board[positions.indexOf('g10')]!.path[0][0], - board[positions.indexOf('g10')]!.path[0][1], - board[positions.indexOf('h9')]!.path[0][5], - board[positions.indexOf('h9')]!.path[0][0], - board[positions.indexOf('h9')]!.path[0][1], - board[positions.indexOf('i8')]!.path[0][5], - board[positions.indexOf('i8')]!.path[0][0], - board[positions.indexOf('i8')]!.path[0][1], - board[positions.indexOf('k7')]!.path[0][5], - board[positions.indexOf('k7')]!.path[0][0], - board[positions.indexOf('k7')]!.path[0][1], - board[positions.indexOf('l6')]!.path[0][5], - board[positions.indexOf('l6')]!.path[0][0], - board[positions.indexOf('l6')]!.path[0][1], - board[positions.indexOf('l6')]!.path[0][2], - board[positions.indexOf('l5')]!.path[0][0], - board[positions.indexOf('l5')]!.path[0][1], - board[positions.indexOf('l5')]!.path[0][2], - board[positions.indexOf('l4')]!.path[0][0], - board[positions.indexOf('l4')]!.path[0][1], - board[positions.indexOf('l4')]!.path[0][2], - board[positions.indexOf('l3')]!.path[0][0], - board[positions.indexOf('l3')]!.path[0][1], - board[positions.indexOf('l3')]!.path[0][2], - board[positions.indexOf('l2')]!.path[0][0], - board[positions.indexOf('l2')]!.path[0][1], - board[positions.indexOf('l2')]!.path[0][2], - board[positions.indexOf('l1')]!.path[0][0], - board[positions.indexOf('l1')]!.path[0][1], - board[positions.indexOf('l1')]!.path[0][2], - board[positions.indexOf('l1')]!.path[0][3], - board[positions.indexOf('k1')]!.path[0][2], - board[positions.indexOf('k1')]!.path[0][3], - board[positions.indexOf('k1')]!.path[0][4], - board[positions.indexOf('i1')]!.path[0][1], - board[positions.indexOf('i1')]!.path[0][2], - board[positions.indexOf('i1')]!.path[0][3], - board[positions.indexOf('h1')]!.path[0][1], - board[positions.indexOf('h1')]!.path[0][2], - board[positions.indexOf('h1')]!.path[0][3], - board[positions.indexOf('g1')]!.path[0][1], - board[positions.indexOf('g1')]!.path[0][2], - board[positions.indexOf('g1')]!.path[0][3], - board[positions.indexOf('f1')]!.path[0][1], - board[positions.indexOf('f1')]!.path[0][2], - board[positions.indexOf('f1')]!.path[0][3], - board[positions.indexOf('f1')]!.path[0][4], - board[positions.indexOf('e1')]!.path[0][3], - board[positions.indexOf('e1')]!.path[0][4], - board[positions.indexOf('d1')]!.path[0][2], - board[positions.indexOf('d1')]!.path[0][3], - board[positions.indexOf('d1')]!.path[0][4], - board[positions.indexOf('c1')]!.path[0][2], - board[positions.indexOf('c1')]!.path[0][3], - board[positions.indexOf('c1')]!.path[0][4], - board[positions.indexOf('b1')]!.path[0][2], - board[positions.indexOf('b1')]!.path[0][3], - board[positions.indexOf('b1')]!.path[0][4], - board[positions.indexOf('a1')]!.path[0][2], - board[positions.indexOf('a1')]!.path[0][3], - board[positions.indexOf('a1')]!.path[0][4], - board[positions.indexOf('a1')]!.path[0][5], - board[positions.indexOf('a2')]!.path[0][3], - board[positions.indexOf('a2')]!.path[0][4], - board[positions.indexOf('a2')]!.path[0][5], - board[positions.indexOf('a3')]!.path[0][3], - board[positions.indexOf('a3')]!.path[0][4], - board[positions.indexOf('a3')]!.path[0][5], - board[positions.indexOf('a4')]!.path[0][3], - board[positions.indexOf('a4')]!.path[0][4], - board[positions.indexOf('a4')]!.path[0][5], - board[positions.indexOf('a5')]!.path[0][3], - board[positions.indexOf('a5')]!.path[0][4], - board[positions.indexOf('a5')]!.path[0][5], - board[positions.indexOf('a6')]!.path[0][3], - board[positions.indexOf('a6')]!.path[0][4], - board[positions.indexOf('a6')]!.path[0][5], - board[positions.indexOf('b7')]!.path[0][4], - board[positions.indexOf('b7')]!.path[0][5], - board[positions.indexOf('b7')]!.path[0][0], - board[positions.indexOf('c8')]!.path[0][4], - board[positions.indexOf('c8')]!.path[0][5], - board[positions.indexOf('c8')]!.path[0][0], - board[positions.indexOf('d9')]!.path[0][4], - board[positions.indexOf('d9')]!.path[0][5], - board[positions.indexOf('d9')]!.path[0][0], - board[positions.indexOf('e10')]!.path[0][4], - board[positions.indexOf('e10')]!.path[0][5], - board[positions.indexOf('e10')]!.path[0][0], -] diff --git a/docs/components/hexboard/geometry.ts b/docs/components/hexboard/geometry.ts deleted file mode 100644 index d6ce5acf..00000000 --- a/docs/components/hexboard/geometry.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { box } from './constants' -import { sideLength } from './constants' -import type { Vec } from './types' - -/** bilinearly interpolate between two points */ -export function bilerp(p1: Vec<2>, p2: Vec<2>, t: number): Vec<2> { - return [ - p1[0] + (t * (p2[0] - p1[0])), - p1[1] + (t * (p2[1] - p1[1])), - ] -} - -/** create svg vector path */ -export function d(arr: [number, number][]) { - const p = 4 // <- svg rounding precision - - const [origin, ...points] = arr - - let path = `M ${x(origin[0]).toFixed(p)} ${y(origin[1]).toFixed(p)} L ` - - for (const point of points) { - path += `${x(point[0]).toFixed(p)} ${y(point[1]).toFixed(p)} ` - } - - return `${path} Z` -} - -/** reflect a path of points across the origin */ -export function flip[]>(path: T) { - return path.map(reflect) as T -} - -/** calculate hexagon vertices around a given point */ -export function hexagon(origin: Vec<2>): Vec<6, Vec<2>> { - return [ - pivot(origin, -300, sideLength), - pivot(origin, 0, sideLength), - pivot(origin, -60, sideLength), - pivot(origin, -120, sideLength), - pivot(origin, -180, sideLength), - pivot(origin, -240, sideLength), - ] -} - -/** pivot counter-clockwise around a point */ -export function pivot(point: Vec<2>, deg: number, distance: number): Vec<2> { - const rad = radians(deg) - - return [ - (distance * Math.cos(rad)) + point[0], - (distance * Math.sin(rad)) + point[1], - ] -} - -/** convert degrees to radians */ -export function radians(deg: number) { - return deg * (Math.PI / 180) -} - -/** reflect a point across the origin */ -export function reflect(v: Vec<2>): Vec<2> { - return [-v[0], -v[1]] -} - -/** sum two vectors */ -export function sum(v1: Vec<2>, v2: Vec<2>): Vec<2> { - return [v1[0] + v2[0], v1[1] + v2[1]] -} - -/** convert cartesian X value to SVG coordinate */ -export function x(n: number) { - return (box / 2) + n -} - -/** convert cartesian Y value to SVG coordinate */ -export function y(n: number) { - return (box / 2) - n -} diff --git a/docs/components/hexboard/types.ts b/docs/components/hexboard/types.ts deleted file mode 100644 index e2037a67..00000000 --- a/docs/components/hexboard/types.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** uniform tuple of length `T`, `number` by default */ -export type Vec< - T extends number, - U = number, - V extends unknown[] = [] -> = V['length'] extends T ? V : Vec diff --git a/docs/sandbox/Sandbox.vue b/docs/sandbox/Sandbox.vue index 7ecba77c..84bb69f1 100644 --- a/docs/sandbox/Sandbox.vue +++ b/docs/sandbox/Sandbox.vue @@ -57,23 +57,69 @@
+ :highlight + :playing="true" + @move="onMove"> + + From a3a67e9799b2571057b99a599cfc0c53e9bc6e81 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Tue, 9 Dec 2025 22:52:00 -0600 Subject: [PATCH 79/80] dont commit browser screenshots --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index c070757c..d5aa5cb5 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,5 @@ php/junit.xml rust/README.md sandbox.mjs target +ui/src/test/__screenshots__ vendor \ No newline at end of file From d125f4f6d1bc4f3c08cc9a08bc194becb27a8b83 Mon Sep 17 00:00:00 2001 From: Scott Bedard Date: Tue, 9 Dec 2025 23:09:11 -0600 Subject: [PATCH 80/80] @bedard/hexchess --- package.json | 1 + pnpm-lock.yaml | 3 +++ 2 files changed, 4 insertions(+) diff --git a/package.json b/package.json index 91f37c1d..b5f16504 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { "devDependencies": { + "@bedard/hexchess": "link:js", "@tailwindcss/vite": "^4.1.16", "@types/node": "^24.9.1", "@vueuse/core": "^14.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c6609a81..18d6153f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: devDependencies: + '@bedard/hexchess': + specifier: link:js + version: link:js '@tailwindcss/vite': specifier: ^4.1.16 version: 4.1.16(vite@7.1.12(@types/node@24.9.1)(jiti@2.6.1)(lightningcss@1.30.2))