update biome (support astro, css format)

This commit is contained in:
tequ
2025-11-04 22:30:25 +09:00
parent 575598f42e
commit 818cd021a1
21 changed files with 937 additions and 639 deletions

View File

@@ -2,7 +2,9 @@
"editor.formatOnSave": true,
"editor.defaultFormatter": "biomejs.biome",
"editor.codeActionsOnSave": {
"quickfix.biome": "explicit",
"source.organizeImports.biome": "explicit"
"source.fixAll.biome": "explicit"
},
"files.associations": {
"*.css": "tailwindcss"
}
}

View File

@@ -1,12 +1,12 @@
{
"$schema": "https://biomejs.dev/schemas/2.0.6/schema.json",
"$schema": "https://biomejs.dev/schemas/2.3.3/schema.json",
"vcs": {
"enabled": false,
"clientKind": "git",
"useIgnoreFile": false
},
"files": {
"includes": ["**", "!*/**/*.astro", "!.astro", "!dist"],
"includes": ["**", "!dist"],
"ignoreUnknown": false
},
"formatter": {
@@ -17,7 +17,24 @@
"linter": {
"enabled": true,
"rules": {
"recommended": true
"recommended": true,
"suspicious": {
"noExplicitAny": "info"
},
"complexity": {
"noBannedTypes": "info"
},
"correctness": {
"noUnusedImports": "off"
}
}
},
"html": {
"experimentalFullSupportEnabled": true,
"formatter": {
"enabled": true,
"indentScriptAndStyle": true,
"indentStyle": "space"
}
},
"javascript": {
@@ -26,6 +43,18 @@
"semicolons": "asNeeded"
}
},
"css": {
"parser": {
"tailwindDirectives": true,
"cssModules": true
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"quoteStyle": "single"
}
},
"assist": {
"enabled": true,
"actions": {

72
package-lock.json generated
View File

@@ -26,7 +26,7 @@
"vanilla-cookieconsent": "^3.1.0"
},
"devDependencies": {
"@biomejs/biome": "^2.0.6"
"@biomejs/biome": "^2.3.3"
}
},
"node_modules/@altcha/crypto": {
@@ -485,9 +485,9 @@
}
},
"node_modules/@biomejs/biome": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.0.6.tgz",
"integrity": "sha512-RRP+9cdh5qwe2t0gORwXaa27oTOiQRQvrFf49x2PA1tnpsyU7FIHX4ZOFMtBC4QNtyWsN7Dqkf5EDbg4X+9iqA==",
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.3.3.tgz",
"integrity": "sha512-zn/P1pRBCpDdhi+VNSMnpczOz9DnqzOA2c48K8xgxjDODvi5O8gs3a2H233rck/5HXpkFj6TmyoqVvxirZUnvg==",
"dev": true,
"license": "MIT OR Apache-2.0",
"bin": {
@@ -501,20 +501,20 @@
"url": "https://opencollective.com/biome"
},
"optionalDependencies": {
"@biomejs/cli-darwin-arm64": "2.0.6",
"@biomejs/cli-darwin-x64": "2.0.6",
"@biomejs/cli-linux-arm64": "2.0.6",
"@biomejs/cli-linux-arm64-musl": "2.0.6",
"@biomejs/cli-linux-x64": "2.0.6",
"@biomejs/cli-linux-x64-musl": "2.0.6",
"@biomejs/cli-win32-arm64": "2.0.6",
"@biomejs/cli-win32-x64": "2.0.6"
"@biomejs/cli-darwin-arm64": "2.3.3",
"@biomejs/cli-darwin-x64": "2.3.3",
"@biomejs/cli-linux-arm64": "2.3.3",
"@biomejs/cli-linux-arm64-musl": "2.3.3",
"@biomejs/cli-linux-x64": "2.3.3",
"@biomejs/cli-linux-x64-musl": "2.3.3",
"@biomejs/cli-win32-arm64": "2.3.3",
"@biomejs/cli-win32-x64": "2.3.3"
}
},
"node_modules/@biomejs/cli-darwin-arm64": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.0.6.tgz",
"integrity": "sha512-AzdiNNjNzsE6LfqWyBvcL29uWoIuZUkndu+wwlXW13EKcBHbbKjNQEZIJKYDc6IL+p7bmWGx3v9ZtcRyIoIz5A==",
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.3.3.tgz",
"integrity": "sha512-5+JtW6RKmjqL9un0UtHV0ezOslAyYBzyl5ZhYiu7GHesX2x8NCDl6tXYrenv9m7e1RLbkO5E5Kh04kseMtz6lw==",
"cpu": [
"arm64"
],
@@ -529,9 +529,9 @@
}
},
"node_modules/@biomejs/cli-darwin-x64": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.0.6.tgz",
"integrity": "sha512-wJjjP4E7bO4WJmiQaLnsdXMa516dbtC6542qeRkyJg0MqMXP0fvs4gdsHhZ7p9XWTAmGIjZHFKXdsjBvKGIJJQ==",
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.3.3.tgz",
"integrity": "sha512-UPmKRalkHicvIpeccuKqq+/gA2HYV8FUnAEDJnqYBlGlycKqe6xrovWqvWTE4TTNpIFf4UQyuaDzLkN6Kz6tbA==",
"cpu": [
"x64"
],
@@ -546,9 +546,9 @@
}
},
"node_modules/@biomejs/cli-linux-arm64": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.0.6.tgz",
"integrity": "sha512-ZSVf6TYo5rNMUHIW1tww+rs/krol7U5A1Is/yzWyHVZguuB0lBnIodqyFuwCNqG9aJGyk7xIMS8HG0qGUPz0SA==",
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.3.3.tgz",
"integrity": "sha512-zeiKwALNB/hax7+LLhCYqhqzlWdTfgE9BGkX2Z8S4VmCYnGFrf2fON/ec6KCos7mra5MDm6fYICsEWN2+HKZhw==",
"cpu": [
"arm64"
],
@@ -563,9 +563,9 @@
}
},
"node_modules/@biomejs/cli-linux-arm64-musl": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.0.6.tgz",
"integrity": "sha512-CVPEMlin3bW49sBqLBg2x016Pws7eUXA27XYDFlEtponD0luYjg2zQaMJ2nOqlkKG9fqzzkamdYxHdMDc2gZFw==",
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.3.3.tgz",
"integrity": "sha512-KhCDMV+V7Yu72v40ssGJTHuv/j0n7JQ6l0s/c+EMcX5zPYLMLr4XpmI+WXhp4Vfkz0T5Xnh5wbrTBI3f2UTpjQ==",
"cpu": [
"arm64"
],
@@ -580,9 +580,9 @@
}
},
"node_modules/@biomejs/cli-linux-x64": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.0.6.tgz",
"integrity": "sha512-geM1MkHTV1Kh2Cs/Xzot9BOF3WBacihw6bkEmxkz4nSga8B9/hWy5BDiOG3gHDGIBa8WxT0nzsJs2f/hPqQIQw==",
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.3.3.tgz",
"integrity": "sha512-05CjPLbvVVU8J6eaO6iSEoA0FXKy2l6ddL+1h/VpiosCmIp3HxRKLOa1hhC1n+D13Z8g9b1DtnglGtM5U3sTag==",
"cpu": [
"x64"
],
@@ -597,9 +597,9 @@
}
},
"node_modules/@biomejs/cli-linux-x64-musl": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.0.6.tgz",
"integrity": "sha512-mKHE/e954hR/hSnAcJSjkf4xGqZc/53Kh39HVW1EgO5iFi0JutTN07TSjEMg616julRtfSNJi0KNyxvc30Y4rQ==",
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.3.3.tgz",
"integrity": "sha512-IyqQ+jYzU5MVy9CK5NV0U+NnUMPUAhYMrB/x4QgL/Dl1MqzBVc61bHeyhLnKM6DSEk73/TQYrk/8/QmVHudLdQ==",
"cpu": [
"x64"
],
@@ -614,9 +614,9 @@
}
},
"node_modules/@biomejs/cli-win32-arm64": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.0.6.tgz",
"integrity": "sha512-290V4oSFoKaprKE1zkYVsDfAdn0An5DowZ+GIABgjoq1ndhvNxkJcpxPsiYtT7slbVe3xmlT0ncdfOsN7KruzA==",
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.3.3.tgz",
"integrity": "sha512-NtlLs3pdFqFAQYZjlEHKOwJEn3GEaz7rtR2oCrzaLT2Xt3Cfd55/VvodQ5V+X+KepLa956QJagckJrNL+DmumQ==",
"cpu": [
"arm64"
],
@@ -631,9 +631,9 @@
}
},
"node_modules/@biomejs/cli-win32-x64": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.0.6.tgz",
"integrity": "sha512-bfM1Bce0d69Ao7pjTjUS+AWSZ02+5UHdiAP85Th8e9yV5xzw6JrHXbL5YWlcEKQ84FIZMdDc7ncuti1wd2sdbw==",
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.3.3.tgz",
"integrity": "sha512-klJKPPQvUk9Rlp0Dd56gQw/+Wt6uUprHdHWtbDC93f3Iv+knA2tLWpcYoOZJgPV+9s+RBmYv0DGy4mUlr20esg==",
"cpu": [
"x64"
],

View File

@@ -7,7 +7,7 @@
"build": "astro build",
"preview": "astro preview",
"check": "biome check --write",
"ci": "biome ci"
"ci": "biome ci --diagnostic-level=warn"
},
"dependencies": {
"@astrojs/mdx": "^4.3.0",
@@ -28,6 +28,6 @@
"vanilla-cookieconsent": "^3.1.0"
},
"devDependencies": {
"@biomejs/biome": "^2.0.6"
"@biomejs/biome": "^2.3.3"
}
}

View File

@@ -1,10 +1,11 @@
---
import 'vanilla-cookieconsent/dist/cookieconsent.css'
import 'vanilla-cookieconsent/dist/cookieconsent.css'
---
<script>
import { run } from 'vanilla-cookieconsent';
import { config } from '../CookieConsentConfig';
run(config);
import { run } from 'vanilla-cookieconsent'
import { config } from '../CookieConsentConfig'
document.body.classList.add('cc--darkmode');
</script>
run(config)
document.body.classList.add('cc--darkmode')
</script>

View File

@@ -1,12 +1,8 @@
---
---
<script is:inline>
document.documentElement.dataset.theme = 'light';
document.documentElement.dataset.theme = 'light'
</script>
<style is:global>
.social-icons::after {
display: none;
}
</style>
</style>

View File

@@ -1,111 +1,110 @@
---
import {
Bars3Icon,
ChevronDownIcon,
XMarkIcon,
} from '@heroicons/react/20/solid'
import {
Bars3Icon,
ChevronDownIcon,
XMarkIcon,
} from '@heroicons/react/20/solid'
---
<starlight-menu-button class="print:hidden">
<button
aria-expanded="false"
aria-label={Astro.locals.t('menuButton.accessibleLabel')}
aria-controls="starlight__sidebar"
class="sl-flex md:sl-hidden"
>
<Bars3Icon name="open" className="open-menu" />
<XMarkIcon name="close" className="close-menu" />
</button>
<button
aria-expanded="false"
aria-label={Astro.locals.t('menuButton.accessibleLabel')}
aria-controls="starlight__sidebar"
class="sl-flex md:sl-hidden"
>
<Bars3Icon name="open" className="open-menu"/>
<XMarkIcon name="close" className="close-menu"/>
</button>
</starlight-menu-button>
<script>
class StarlightMenuButton extends HTMLElement {
btn = this.querySelector('button')!;
class StarlightMenuButton extends HTMLElement {
btn = this.querySelector('button') as HTMLButtonElement
constructor() {
super();
// Toggle `aria-expanded` state when a user clicks the button.
this.btn.addEventListener('click', () => this.toggleExpanded());
constructor() {
super()
// Toggle `aria-expanded` state when a user clicks the button.
this.btn.addEventListener('click', () => this.toggleExpanded())
// Close the menu when a user presses the escape key.
const parentNav = this.closest('nav');
if (parentNav) {
parentNav.addEventListener('keyup', (e) => this.closeOnEscape(e));
}
}
// Close the menu when a user presses the escape key.
const parentNav = this.closest('nav')
if (parentNav) {
parentNav.addEventListener('keyup', (e) => this.closeOnEscape(e))
}
}
setExpanded(expanded: boolean) {
this.setAttribute('aria-expanded', String(expanded));
document.body.toggleAttribute('data-mobile-menu-expanded', expanded);
}
setExpanded(expanded: boolean) {
this.setAttribute('aria-expanded', String(expanded))
document.body.toggleAttribute('data-mobile-menu-expanded', expanded)
}
toggleExpanded() {
this.setExpanded(this.getAttribute('aria-expanded') !== 'true');
}
toggleExpanded() {
this.setExpanded(this.getAttribute('aria-expanded') !== 'true')
}
closeOnEscape(e: KeyboardEvent) {
if (e.code === 'Escape') {
this.setExpanded(false);
this.btn.focus();
}
}
}
closeOnEscape(e: KeyboardEvent) {
if (e.code === 'Escape') {
this.setExpanded(false)
this.btn.focus()
}
}
}
customElements.define('starlight-menu-button', StarlightMenuButton);
customElements.define('starlight-menu-button', StarlightMenuButton)
</script>
<style>
@layer starlight.core {
button {
position: fixed;
bottom: var(--sl-nav-pad-y);
inset-inline-end: var(--sl-nav-pad-x);
z-index: var(--sl-z-index-navbar);
border: 0;
border-radius: 50%;
width: var(--sl-menu-button-size);
height: var(--sl-menu-button-size);
padding: 0.5rem;
background-color: var(--sl-color-white);
color: var(--sl-color-black);
box-shadow: var(--sl-shadow-md);
cursor: pointer;
}
@layer starlight.core {
button {
position: fixed;
bottom: var(--sl-nav-pad-y);
inset-inline-end: var(--sl-nav-pad-x);
z-index: var(--sl-z-index-navbar);
border: 0;
border-radius: 50%;
width: var(--sl-menu-button-size);
height: var(--sl-menu-button-size);
padding: 0.5rem;
background-color: var(--sl-color-white);
color: var(--sl-color-black);
box-shadow: var(--sl-shadow-md);
cursor: pointer;
}
[aria-expanded='true'] button {
background-color: var(--sl-color-gray-2);
box-shadow: none;
}
[aria-expanded='true'] button {
background-color: var(--sl-color-gray-2);
box-shadow: none;
}
[aria-expanded='true'] button .open-menu {
display: none;
}
[aria-expanded='true'] button .open-menu {
display: none;
}
:not([aria-expanded='true']) button .close-menu {
display: none;
}
:not([aria-expanded='true']) button .close-menu {
display: none;
}
:global button {
background-color: var(--sl-color-black);
color: var(--sl-color-white);
}
:global button {
background-color: var(--sl-color-black);
color: var(--sl-color-white);
}
:global [aria-expanded='true'] button {
background-color: var(--sl-color-gray-5);
}
}
:global [aria-expanded='true'] button {
background-color: var(--sl-color-gray-5);
}
}
</style>
<style is:global>
@layer starlight.core {
[data-mobile-menu-expanded] {
overflow: hidden;
}
@layer starlight.core {
[data-mobile-menu-expanded] {
overflow: hidden;
}
@media (min-width: 50rem) {
[data-mobile-menu-expanded] {
overflow: auto;
}
}
}
</style>
@media (min-width: 50rem) {
[data-mobile-menu-expanded] {
overflow: auto;
}
}
}
</style>

View File

@@ -1,55 +1,59 @@
---
import TableOfContents from '@astrojs/starlight/components/TableOfContents.astro'
import TableOfContents from '@astrojs/starlight/components/TableOfContents.astro'
---
{
Astro.locals.starlightRoute.toc && (
<>
<div class="right-sidebar-panel sl-hidden lg:sl-block">
<div class="sl-container">
<TableOfContents />
</div>
</div>
</>
)
Astro.locals.starlightRoute.toc && (
<>
<div class="right-sidebar-panel sl-hidden lg:sl-block">
<div class="sl-container">
<TableOfContents />
</div>
</div>
</>
)
}
<style>
@layer starlight.core {
.right-sidebar-panel {
padding: 1rem var(--sl-sidebar-pad-x);
}
.sl-container {
width: calc(var(--sl-sidebar-width) - 2 * var(--sl-sidebar-pad-x));
}
.right-sidebar-panel :global(h2) {
color: var(--sl-color-white);
font-size: var(--sl-text-h5);
font-weight: 600;
line-height: var(--sl-line-height-headings);
margin-bottom: 0.5rem;
}
.right-sidebar-panel :global(:where(a)) {
display: block;
font-size: var(--sl-text-xs);
text-decoration: none;
color: var(--sl-color-gray-3);
overflow-wrap: anywhere;
}
.right-sidebar-panel :global(:where(a):hover) {
color: var(--sl-color-white);
}
@media (min-width: 72rem) {
.sl-container {
max-width: calc(
(
(
100vw - var(--sl-sidebar-width) - 2 * var(--sl-content-pad-x) - 2 *
var(--sl-sidebar-pad-x)
) * 0.25 /* MAGIC NUMBER 🥲 */
)
);
}
}
}
</style>
@layer starlight.core {
.right-sidebar-panel {
padding: 1rem var(--sl-sidebar-pad-x);
}
.sl-container {
width: calc(var(--sl-sidebar-width) - 2 * var(--sl-sidebar-pad-x));
}
.right-sidebar-panel :global(h2) {
color: var(--sl-color-white);
font-size: var(--sl-text-h5);
font-weight: 600;
line-height: var(--sl-line-height-headings);
margin-bottom: 0.5rem;
}
.right-sidebar-panel :global(:where(a)) {
display: block;
font-size: var(--sl-text-xs);
text-decoration: none;
color: var(--sl-color-gray-3);
overflow-wrap: anywhere;
}
.right-sidebar-panel :global(:where(a):hover) {
color: var(--sl-color-white);
}
@media (min-width: 72rem) {
.sl-container {
max-width: calc(
(
(
100vw -
var(--sl-sidebar-width) -
2 *
var(--sl-content-pad-x) -
2 *
var(--sl-sidebar-pad-x)
) *
0.25 /* MAGIC NUMBER 🥲 */
)
);
}
}
}
</style>

View File

@@ -1,12 +1,11 @@
---
import SidebarPersister from '@astrojs/starlight/components/SidebarPersister.astro'
import SidebarSublist from '@astrojs/starlight/components/SidebarSublist.astro'
import Search from '@astrojs/starlight/components/Search.astro'
import Search from '@astrojs/starlight/components/Search.astro'
import SidebarPersister from '@astrojs/starlight/components/SidebarPersister.astro'
import SidebarSublist from '@astrojs/starlight/components/SidebarSublist.astro'
const { sidebar } = Astro.locals.starlightRoute
const _sidebar = Astro.locals.starlightRoute.sidebar
---
<SidebarPersister>
<Search class="hidden md:block" />
<SidebarSublist sublist={sidebar} />
</SidebarPersister>
<Search class="hidden md:block"/>
<SidebarSublist sublist={_sidebar}/>
</SidebarPersister>

View File

@@ -1,48 +1,54 @@
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
<div class="col-span-3"><slot /></div>
{
Astro.locals.starlightRoute.toc && (
<aside class="right-sidebar-container col-span-1 print:hidden">
<div class="right-sidebar">
<slot name="right-sidebar" />
</div>
</aside>
)
}
<div class="col-span-3">
<slot/>
</div>
{
Astro.locals.starlightRoute.toc && (
<aside class="right-sidebar-container col-span-1 print:hidden">
<div class="right-sidebar">
<slot name="right-sidebar" />
</div>
</aside>
)
}
</div>
<style>
@layer starlight.core {
@media (min-width: 72rem) {
.right-sidebar-container {
order: 2;
position: relative;
width: calc(
var(--sl-sidebar-width) + (100% - var(--sl-content-width) - var(--sl-sidebar-width)) / 2
);
}
@layer starlight.core {
@media (min-width: 72rem) {
.right-sidebar-container {
order: 2;
position: relative;
width: calc(
var(--sl-sidebar-width) +
(100% - var(--sl-content-width) - var(--sl-sidebar-width)) /
2
);
}
.right-sidebar {
position: fixed;
border-inline-start: 1px solid var(--sl-color-hairline);
width: 100%;
height: 100vh;
overflow-y: auto;
scrollbar-width: none;
}
.right-sidebar {
position: fixed;
border-inline-start: 1px solid var(--sl-color-hairline);
width: 100%;
height: 100vh;
overflow-y: auto;
scrollbar-width: none;
}
.main-pane {
width: 100%;
}
.main-pane {
width: 100%;
}
:global([data-has-sidebar][data-has-toc]) .main-pane {
--sl-content-margin-inline: auto 0;
:global([data-has-sidebar][data-has-toc]) .main-pane {
--sl-content-margin-inline: auto 0;
order: 1;
width: calc(
var(--sl-content-width) + (100% - var(--sl-content-width) - var(--sl-sidebar-width)) / 2
);
}
}
}
</style>
order: 1;
width: calc(
var(--sl-content-width) +
(100% - var(--sl-content-width) - var(--sl-sidebar-width)) /
2
);
}
}
}
</style>

View File

@@ -1,8 +1,12 @@
---
const { class: className, ...rest } = Astro.props
const { class: _className } = Astro.props
import { Image } from 'astro:assets'
import { Image } from 'astro:assets'
---
<a href={Astro.props.href} target="_blank" class:list={["flex hover:border-xahau-green hover:bg-gray-50 !text-black !no-underline items-center justify-center p-4 text-center bg-white font-bold border-black border-2",className]}>
<a
href={Astro.props.href}
target="_blank"
class:list={["flex hover:border-xahau-green hover:bg-gray-50 !text-black !no-underline items-center justify-center p-4 text-center bg-white font-bold border-black border-2",_className]}
>
{Astro.props.imageSrc ? <Image src={Astro.props.imageSrc} alt={Astro.props.title} class="w-auto h-auto max-h-2/5" /> : Astro.props.title}
</a>

View File

@@ -1,16 +1,28 @@
---
import { Image } from 'astro:assets'
import logo from '../assets/xahau-logo.svg'
import { Image } from 'astro:assets'
import logo from '../assets/xahau-logo.svg'
---
<footer class="bg-white text-base font-regular text-black **:text-black **:no-underline">
<div class="flex-none container mx-auto py-12 max-w-7xl p-6 grid grid-cols-1 md:grid-cols-10 gap-4">
<footer
class="bg-white text-base font-regular text-black **:text-black **:no-underline"
>
<div
class="flex-none container mx-auto py-12 max-w-7xl p-6 grid grid-cols-1 md:grid-cols-10 gap-4"
>
<div class="col-span-1 md:col-span-3 flex flex-col gap-3">
<a href="/"><Image src={logo} class="w-1/2" alt="Xahau" /><a>
<p>This website is open source and open for contributions <a href="https://github.com/Xahau/xahau-web" target="_blank">on GitHub</a></p>
<a href="/">
<Image src={logo} class="w-1/2" alt="Xahau"/>
</a>
<p>
This website is open source and open for contributions
<a href="https://github.com/Xahau/xahau-web" target="_blank"
>on GitHub</a
>
</p>
</div>
<div class="col-span-1 md:col-span-2 md:col-start-5 flex flex-col gap-3">
<p><strong>Xahau</strong></p>
<p>
<strong>Xahau</strong>
</p>
<a href="/about">About</a>
<a href="/features">Features</a>
<a href="/ecosystem">Ecosystem</a>
@@ -18,10 +30,12 @@ import logo from '../assets/xahau-logo.svg'
<a href="/docs/compliance/responsible-disclosure">Break Xahau</a>
<a href="/fraud-report">Report Fraud</a>
<a href="/docs/resources/media-kit">Media kit</a>
<a href="/privacy-policy">Privacy Policy</a>
<a href="/privacy-policy">Privacy Policy</a>
</div>
<div class="col-span-1 md:col-span-2 flex flex-col gap-3">
<p><strong>Documentation</strong></p>
<p>
<strong>Documentation</strong>
</p>
<a href="/docs">Get started</a>
<a href="/docs/protocol-reference/transactions">Protocol Reference</a>
<a href="/docs/hooks">Hooks</a>
@@ -30,10 +44,14 @@ import logo from '../assets/xahau-logo.svg'
<a href="/docs/resources/whitepaper">Whitepaper</a>
</div>
<div class="col-span-1 md:col-span-2 flex flex-col gap-3">
<p><strong>Connect</strong></p>
<p>
<strong>Connect</strong>
</p>
<a href="https://x.com/XahauNetwork" target="_blank">X</a>
<a href="https://github.com/Xahau" target="_blank">GitHub</a>
<a href="https://discord.com/invite/UzU58haAn4" target="_blank">Community Discord</a>
<a href="https://discord.com/invite/UzU58haAn4" target="_blank">
Community Discord
</a>
</div>
</div>
</footer>

View File

@@ -1,61 +1,59 @@
---
const { class: className, ...rest } = Astro.props
const { class: _className } = Astro.props
import { Image } from 'astro:assets'
import gem1 from '../assets/gems/1.png'
import gem2 from '../assets/gems/2.png'
import gem3 from '../assets/gems/3.png'
import gem4 from '../assets/gems/4.png'
import gem5 from '../assets/gems/5.png'
import gem6 from '../assets/gems/6.png'
import gem7 from '../assets/gems/7.png'
import gem8 from '../assets/gems/8.png'
import gem9 from '../assets/gems/9.png'
import gem10 from '../assets/gems/10.png'
import gem11 from '../assets/gems/11.png'
import gem12 from '../assets/gems/12.png'
import gem13 from '../assets/gems/13.png'
import gem14 from '../assets/gems/14.png'
import gem15 from '../assets/gems/15.png'
import gem16 from '../assets/gems/16.png'
import gem17 from '../assets/gems/17.png'
import { Image } from 'astro:assets'
import gem1 from '../assets/gems/1.png'
import gem2 from '../assets/gems/2.png'
import gem3 from '../assets/gems/3.png'
import gem4 from '../assets/gems/4.png'
import gem5 from '../assets/gems/5.png'
import gem6 from '../assets/gems/6.png'
import gem7 from '../assets/gems/7.png'
import gem8 from '../assets/gems/8.png'
import gem9 from '../assets/gems/9.png'
import gem10 from '../assets/gems/10.png'
import gem11 from '../assets/gems/11.png'
import gem12 from '../assets/gems/12.png'
import gem13 from '../assets/gems/13.png'
import gem14 from '../assets/gems/14.png'
import gem15 from '../assets/gems/15.png'
import gem16 from '../assets/gems/16.png'
import gem17 from '../assets/gems/17.png'
const gemMap = {
'1': gem1,
'2': gem2,
'3': gem3,
'4': gem4,
'5': gem5,
'6': gem6,
'7': gem7,
'8': gem8,
'9': gem9,
'10': gem10,
'11': gem11,
'12': gem12,
'13': gem13,
'14': gem14,
'15': gem15,
'16': gem16,
'17': gem17,
}
const gemMap = {
'1': gem1,
'2': gem2,
'3': gem3,
'4': gem4,
'5': gem5,
'6': gem6,
'7': gem7,
'8': gem8,
'9': gem9,
'10': gem10,
'11': gem11,
'12': gem12,
'13': gem13,
'14': gem14,
'15': gem15,
'16': gem16,
'17': gem17,
}
const gem = gemMap[Astro.props.gem as keyof typeof gemMap] || gem1
const _gem = gemMap[Astro.props.gem as keyof typeof gemMap] || gem1
var gemSize = 'size-80'
if (Astro.props.gemSize && Astro.props.gemSize === 'large') {
gemSize = 'size-120 -top-40'
}
var _gemSize = 'size-80'
if (Astro.props.gemSize && Astro.props.gemSize === 'large') {
_gemSize = 'size-120 -top-40'
}
---
<div class:list={["flex",className]}>
<div class="flex-none container mx-auto py-12 text-left max-w-7xl p-6">
{Astro.props.align ? (
<div class:list={["flex",_className]}>
<div class="flex-none container mx-auto py-12 text-left max-w-7xl p-6">
{Astro.props.align ? (
<div class="grid grid-cols-1 md:grid-cols-5" >
{Astro.props.align === "right" && Astro.props.gem && (
<div class="relative overflow-visible invisible md:visible">
<Image loading={Astro.props.loading} src={gem} alt="Gem" class={`overflow-visible ${gemSize} absolute inset-0 m-auto -left-60 object-cover`} />
<Image loading={Astro.props.loading} src={_gem} alt="Gem" class={`overflow-visible ${_gemSize} absolute inset-0 m-auto -left-60 object-cover`} />
</div>
)}
<div class={
@@ -69,12 +67,12 @@ if (Astro.props.gemSize && Astro.props.gemSize === 'large') {
</div>
{Astro.props.align === "left" && Astro.props.gem && (
<div class="relative overflow-visible invisible md:visible">
<Image loading={Astro.props.loading} src={gem} alt="Gem" class={`overflow-visible ${gemSize} absolute inset-0 m-auto -right-150 object-cover`} />
<Image loading={Astro.props.loading} src={_gem} alt="Gem" class={`overflow-visible ${_gemSize} absolute inset-0 m-auto -right-150 object-cover`} />
</div>
)}
</div>
) : (
<slot />
)}
</div>
</div>
</div>
</div>

View File

@@ -1,45 +1,47 @@
---
import CookieConsent from '../components/CookieConsent.astro'
import Footer from '../components/Footer.astro'
import Header from '../components/Header.jsx'
import CookieConsent from '../components/CookieConsent.astro'
import Footer from '../components/Footer.astro'
import Header from '../components/Header.jsx'
const canonicalURL = new URL(Astro.url.pathname, Astro.site)
const socialImageURL = new URL('/xahau-logo.png', Astro.site)
const title = Astro.props.frontmatter?.title
? `Xahau: ${Astro.props.frontmatter?.title}`
: 'Xahau'
const description = Astro.props.frontmatter?.description
? Astro.props.frontmatter?.description
: 'Xahau is a blockchain for the real world. It is a smart blockchain with account-based programmability. It is cheap and fast by design. It is built for the real world.'
const _canonicalURL = new URL(Astro.url.pathname, Astro.site)
const _socialImageURL = new URL('/xahau-logo.png', Astro.site)
const _title = Astro.props.frontmatter?.title
? `Xahau: ${Astro.props.frontmatter?.title}`
: 'Xahau'
const _description = Astro.props.frontmatter?.description
? Astro.props.frontmatter?.description
: 'Xahau is a blockchain for the real world. It is a smart blockchain with account-based programmability. It is cheap and fast by design. It is built for the real world.'
---
<html lang="en">
<head>
<link rel="shortcut icon" href="/favicon.svg" type="image/svg+xml">
<meta charset="UTF-8" />
<title>{title}</title>
<meta name="description" content={description} />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link href="https://fonts.googleapis.com/css2?family=Onest:wght@400;700&display=swap" rel="stylesheet" />
<link rel="canonical" href={canonicalURL} />
<meta property="og:title" content={title} />
<meta property="og:type" content="website" />
<meta property="og:description" content={description} />
<meta property="og:url" content={canonicalURL} />
<meta property="og:image" content={socialImageURL} />
<meta property="og:image:alt" content="Xahau logo" />
<meta name="twitter:card" content="summary_large_image" />
<meta property="twitter:domain" content="xahau.network" />
<meta property="twitter:url" content={canonicalURL} />
<meta name="twitter:title" content={title} />
<meta name="twitter:description" content={description} />
<meta name="twitter:image" content={socialImageURL} />
<meta charset="UTF-8">
<title>{_title}</title>
<meta name="description" content={_description}>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link
href="https://fonts.googleapis.com/css2?family=Onest:wght@400;700&display=swap"
rel="stylesheet"
>
<link rel="canonical" href={_canonicalURL}>
<meta property="og:title" content={_title}>
<meta property="og:type" content="website">
<meta property="og:description" content={_description}>
<meta property="og:url" content={_canonicalURL}>
<meta property="og:image" content={_socialImageURL}>
<meta property="og:image:alt" content="Xahau logo">
<meta name="twitter:card" content="summary_large_image">
<meta property="twitter:domain" content="xahau.network">
<meta property="twitter:url" content={_canonicalURL}>
<meta name="twitter:title" content={_title}>
<meta name="twitter:description" content={_description}>
<meta name="twitter:image" content={_socialImageURL}>
</head>
<body class="font-[Onest] bg-xahau-background min-h-screen flex flex-col">
<Header client:load url={Astro.url} />
<slot />
<Footer />
<CookieConsent />
<Header client:load url={Astro.url}/>
<slot/>
<Footer/>
<CookieConsent/>
</body>
</html>
</html>

View File

@@ -1,95 +1,98 @@
---
import '../styles/docs.css'
import CookieConsent from '../components/CookieConsent.astro'
import DocsMobileMenuToggle from '../components/DocsMobileMenuToggle.astro'
import Footer from '../components/Footer.astro'
import Header from '../components/Header.jsx'
import '../styles/docs.css'
import CookieConsent from '../components/CookieConsent.astro'
import DocsMobileMenuToggle from '../components/DocsMobileMenuToggle.astro'
import Footer from '../components/Footer.astro'
import Header from '../components/Header.jsx'
const { hasSidebar } = Astro.locals.starlightRoute
const _hasSidebar = Astro.locals.starlightRoute.hasSidebar
---
<link href="https://fonts.googleapis.com/css2?family=Onest:wght@400;700&display=swap" rel="stylesheet" />
<link
href="https://fonts.googleapis.com/css2?family=Onest:wght@400;700&display=swap"
rel="stylesheet"
>
<div class="font-[Onest] bg-xahau-background min-h-screen flex flex-col">
<Header client:load url={Astro.url} />
<Header client:load url={Astro.url}/>
<div class="page-content flex-1 container mx-auto p-6">
<div class="grid grid-cols-1 md:grid-cols-5 gap-4">
<div>
{
hasSidebar && (
<nav class="sidebar print:hidden" aria-label={Astro.locals.t('sidebarNav.accessibleLabel')}>
<DocsMobileMenuToggle />
<div id="starlight__sidebar" class="sidebar-pane">
<div class="sidebar-content sl-flex">
<slot name="sidebar" />
</div>
</div>
</nav>
)
}
{
_hasSidebar && (
<nav class="sidebar print:hidden" aria-label={Astro.locals.t('sidebarNav.accessibleLabel')}>
<DocsMobileMenuToggle />
<div id="starlight__sidebar" class="sidebar-pane">
<div class="sidebar-content sl-flex">
<slot name="sidebar" />
</div>
</div>
</nav>
)
}
</div>
<div class="col-span-4">
<slot />
<slot/>
</div>
</div>
</div>
<Footer />
<CookieConsent />
<Footer/>
<CookieConsent/>
</div>
<style>
@layer starlight.core {
.page {
flex-direction: column;
min-height: 100vh;
}
@layer starlight.core {
.page {
flex-direction: column;
min-height: 100vh;
}
.sidebar-pane {
visibility: var(--sl-sidebar-visibility, hidden);
position: fixed;
z-index: var(--sl-z-index-menu);
inset-block: var(--sl-nav-height) 0;
inset-inline-start: 0;
top: 0;
width: 100%;
background-color: var( --color-xahau-background);
overflow-y: auto;
}
.sidebar-pane {
visibility: var(--sl-sidebar-visibility, hidden);
position: fixed;
z-index: var(--sl-z-index-menu);
inset-block: var(--sl-nav-height) 0;
inset-inline-start: 0;
top: 0;
width: 100%;
background-color: var(--color-xahau-background);
overflow-y: auto;
}
:global([aria-expanded='true']) ~ .sidebar-pane {
--sl-sidebar-visibility: visible;
}
:global([aria-expanded='true']) ~ .sidebar-pane {
--sl-sidebar-visibility: visible;
}
.sidebar-content {
height: 100%;
min-height: max-content;
padding: 88px var(--sl-sidebar-pad-x) 0;
flex-direction: column;
gap: 1rem;
}
.sidebar-content {
height: 100%;
min-height: max-content;
padding: 88px var(--sl-sidebar-pad-x) 0;
flex-direction: column;
gap: 1rem;
}
@media (min-width: 50rem) {
.sidebar-content {
padding: 1rem var(--sl-sidebar-pad-x) 0;
}
}
@media (min-width: 50rem) {
.sidebar-content {
padding: 1rem var(--sl-sidebar-pad-x) 0;
}
}
@media (min-width: 50rem) {
.sidebar-content::after {
content: '';
padding-bottom: 1px;
}
}
@media (min-width: 50rem) {
.sidebar-content::after {
content: '';
padding-bottom: 1px;
}
}
.main-frame {
padding-top: calc(var(--sl-nav-height) + var(--sl-mobile-toc-height));
padding-inline-start: var(--sl-content-inline-start);
}
.main-frame {
padding-top: calc(var(--sl-nav-height) + var(--sl-mobile-toc-height));
padding-inline-start: var(--sl-content-inline-start);
}
@media (min-width: 50rem) {
.sidebar-pane {
--sl-sidebar-visibility: visible;
position: relative
border-inline-end: 1px solid var(--sl-color-hairline-shade);
}
}
}
@media (min-width: 50rem) {
.sidebar-pane {
--sl-sidebar-visibility: visible;
position: relative;
border-inline-end: 1px solid var(--sl-color-hairline-shade);
}
}
}
</style>

View File

@@ -1,86 +1,166 @@
---
import '../styles/main.css'
import worldmap from '../assets/worldmap.svg'
import BaseLayout from '../layouts/BaseLayout.astro'
import '../styles/main.css'
import worldmap from '../assets/worldmap.svg'
import BaseLayout from '../layouts/BaseLayout.astro'
const { frontmatter } = Astro.props
const _frontmatter = Astro.props.frontmatter
---
<BaseLayout frontmatter={frontmatter}>
<div class="relative flex flex-col items-center justify-center min-h-[80vh] py-40">
<div class="absolute inset-0 flex items-center justify-center pointer-events-none select-none">
<BaseLayout frontmatter={_frontmatter}>
<div
class="relative flex flex-col items-center justify-center min-h-[80vh] py-40"
>
<div
class="absolute inset-0 flex items-center justify-center pointer-events-none select-none"
>
<div class="w-full h-full mx-auto">
<img src={worldmap.src} alt="World map" class="w-full h-full object-contain" />
<img
src={worldmap.src}
alt="World map"
class="w-full h-full object-contain"
>
</div>
</div>
<div class="flex-none container mx-auto py-12 text-left max-w-7xl p-6 z-10">
<div class="flex flex-col items-center text-center">
<h1 class="text-4xl md:text-5xl font-extrabold text-black mb-4 max-w-2/3">The Smarter Blockchain with Account-Based Programmability</h1>
<p class="text-2xl md:text-2xl font-semibold text-black mb-8 max-w-2/3">Not just smart contracts smart accounts. Cheap and fast by design. Built for the real world.</p>
<h1
class="text-4xl md:text-5xl font-extrabold text-black mb-4 max-w-2/3"
>
The Smarter Blockchain with Account-Based Programmability
</h1>
<p class="text-2xl md:text-2xl font-semibold text-black mb-8 max-w-2/3">
Not just smart contracts smart accounts. Cheap and fast by design.
Built for the real world.
</p>
<div class="flex gap-4">
<a href="/about" class="px-10 h-12 flex items-center justify-center bg-xahau-green text-black text-base font-bold hover:bg-black hover:text-white transition">Learn the basics</a>
<a href="/docs" class="px-10 h-12 flex items-center justify-center bg-gray-300 text-black text-base font-bold hover:bg-black hover:text-white transition">View docs</a>
<a
href="/about"
class="px-10 h-12 flex items-center justify-center bg-xahau-green text-black text-base font-bold hover:bg-black hover:text-white transition"
>
Learn the basics
</a>
<a
href="/docs"
class="px-10 h-12 flex items-center justify-center bg-gray-300 text-black text-base font-bold hover:bg-black hover:text-white transition"
>
View docs
</a>
</div>
</div>
</div>
</div>
</div>
<div class="flex flex-col">
<div class="flex-none container mx-auto py-12 text-left max-w-7xl p-6">
<h1 class="text-3xl md:text-4xl font-extrabold text-black mb-4">The Xahau Network</h1>
<h2 class="text-xl md:text-2xl font-semibold text-black mb-8">Why is Xahau the right blockchain for your business?</h2>
<h1 class="text-3xl md:text-4xl font-extrabold text-black mb-4">
The Xahau Network
</h1>
<h2 class="text-xl md:text-2xl font-semibold text-black mb-8">
Why is Xahau the right blockchain for your business?
</h2>
<div class="items-start grid grid-cols-1 md:grid-cols-3 gap-8">
<div>
<h3 class="text-xl md:text-2xl font-bold text-black mb-2">1. Smart Contract Hooks</h3>
<p class="text-base font-light text-black">Xahau supports lightweight smart contracts directly on accounts, enabling automatic logic for approvals, rejections, or custom behavior without requiring users to call a contract.</p>
<h3 class="text-xl md:text-2xl font-bold text-black mb-2">
1. Smart Contract Hooks
</h3>
<p class="text-base font-light text-black">
Xahau supports lightweight smart contracts directly on accounts,
enabling automatic logic for approvals, rejections, or custom
behavior without requiring users to call a contract.
</p>
</div>
<div>
<h3 class="text-xl md:text-2xl font-bold text-black mb-2">2. Low Fees with Fee Burning</h3>
<p class="text-base font-light text-black">Xahau offers predictable, low transaction costs. A portion of each fee is burned, reducing spam and aligning incentives with long-term network health.</p>
<h3 class="text-xl md:text-2xl font-bold text-black mb-2">
2. Low Fees with Fee Burning
</h3>
<p class="text-base font-light text-black">
Xahau offers predictable, low transaction costs. A portion of each
fee is burned, reducing spam and aligning incentives with long-term
network health.
</p>
</div>
<div>
<h3 class="text-xl md:text-2xl font-bold text-black mb-2">3. Enterprise-Ready Governance</h3>
<p class="text-base font-light text-black">With structured governance and validator coordination, Xahau provides a stable environment for regulated and enterprise use, including supply chain, payments, and compliance applications.</p>
<h3 class="text-xl md:text-2xl font-bold text-black mb-2">
3. Enterprise-Ready Governance
</h3>
<p class="text-base font-light text-black">
With structured governance and validator coordination, Xahau
provides a stable environment for regulated and enterprise use,
including supply chain, payments, and compliance applications.
</p>
</div>
<div>
<h3 class="text-xl md:text-2xl font-bold text-black mb-2">4. Fast, Green, and Scalable</h3>
<p class="text-base font-light text-black">With an efficient consensus protocol, Xahau processes transactions quickly without mining—ideal for businesses needing performance and sustainability.</p>
<h3 class="text-xl md:text-2xl font-bold text-black mb-2">
4. Fast, Green, and Scalable
</h3>
<p class="text-base font-light text-black">
With an efficient consensus protocol, Xahau processes transactions
quickly without mining—ideal for businesses needing performance and
sustainability.
</p>
</div>
<div>
<h3 class="text-xl md:text-2xl font-bold text-black mb-2">5. Token and Asset Control by Design</h3>
<p class="text-base font-light text-black">Businesses can issue assets or tokens with fine-grained control over how theyre used or transferred, enforcing rules automatically at the ledger level.</p>
<h3 class="text-xl md:text-2xl font-bold text-black mb-2">
5. Token and Asset Control by Design
</h3>
<p class="text-base font-light text-black">
Businesses can issue assets or tokens with fine-grained control over
how theyre used or transferred, enforcing rules automatically at
the ledger level.
</p>
</div>
<a href="/features" class="self-end px-10 h-12 flex items-center justify-center bg-xahau-green text-black text-base font-bold hover:bg-black hover:text-white transition">Discover more features</a>
<a
href="/features"
class="self-end px-10 h-12 flex items-center justify-center bg-xahau-green text-black text-base font-bold hover:bg-black hover:text-white transition"
>Discover more features</a
>
</div>
</div>
</div>
<div class="flex flex-col items-end">
<div class="flex-none container mx-auto py-12 text-left max-w-7xl p-6">
<h1 class="text-3xl md:text-4xl font-extrabold text-black mb-4">Network statistics</h1>
<h2 class="text-xl md:text-2xl font-semibold text-black mb-8">Xahau in numbers</h2>
<h1 class="text-3xl md:text-4xl font-extrabold text-black mb-4">
Network statistics
</h1>
<h2 class="text-xl md:text-2xl font-semibold text-black mb-8">
Xahau in numbers
</h2>
<div class="items-end grid grid-cols-6 md:grid-cols-9 gap-4">
<div class="text-black flex flex-col col-span-4 md:col-span-3 min-h-40 items-start justify-end p-4 bg-white border-black border-2">
<div
class="text-black flex flex-col col-span-4 md:col-span-3 min-h-40 items-start justify-end p-4 bg-white border-black border-2"
>
<h3 class="text-xl md:text-2xl font-semibold">Accounts</h3>
<h4 class="text-4xl md:text-6xl font-extrabold">130K+</h4>
</div>
<div class="text-black flex flex-col col-span-2 md:col-span-2 min-h-40 items-start justify-end p-4 bg-white border-black border-2">
<div
class="text-black flex flex-col col-span-2 md:col-span-2 min-h-40 items-start justify-end p-4 bg-white border-black border-2"
>
<h3 class="text-xl md:text-2xl font-semibold">Hooks installed</h3>
<h4 class="text-4xl md:text-6xl font-extrabold">5K+</h4>
</div>
<div class="text-xahau-secondary flex flex-col col-span-2 md:col-span-2 min-h-40 items-start justify-end p-4 bg-xahau-gray border-black border-2">
<div
class="text-xahau-secondary flex flex-col col-span-2 md:col-span-2 min-h-40 items-start justify-end p-4 bg-xahau-gray border-black border-2"
>
<h3 class="text-xl md:text-2xl font-semibold">Nodes</h3>
<h4 class="text-4xl md:text-6xl font-extrabold">300+</h4>
</div>
<div class="text-xahau-secondary flex flex-col col-span-4 md:col-span-2 row-start-auto md:row-start-2 min-h-40 items-start justify-end p-4 bg-xahau-gray border-black border-2">
<div
class="text-xahau-secondary flex flex-col col-span-4 md:col-span-2 row-start-auto md:row-start-2 min-h-40 items-start justify-end p-4 bg-xahau-gray border-black border-2"
>
<h3 class="text-xl md:text-2xl font-semibold">Transactions/24h</h3>
<h4 class="text-4xl md:text-6xl font-extrabold">1.5M+</h4>
</div>
<div class="text-black flex flex-col col-span-6 md:col-span-3 row-start-auto md:row-start-2 min-h-40 items-start justify-end p-4 bg-white border-black border-2">
<div
class="text-black flex flex-col col-span-6 md:col-span-3 row-start-auto md:row-start-2 min-h-40 items-start justify-end p-4 bg-white border-black border-2"
>
<h3 class="text-xl md:text-2xl font-semibold">Ledgers closed</h3>
<h4 class="text-4xl md:text-6xl font-extrabold">16M+</h4>
</div>
<a href="/docs/infrastructure/system-requirements/" class="col-span-6 row-start-auto md:row-start-2 px-10 h-12 flex items-center justify-center bg-xahau-green text-black text-base font-bold hover:bg-black hover:text-white transition">Want to run a node?</a>
<a
href="/docs/infrastructure/system-requirements/"
class="col-span-6 row-start-auto md:row-start-2 px-10 h-12 flex items-center justify-center bg-xahau-green text-black text-base font-bold hover:bg-black hover:text-white transition"
>Want to run a node?</a
>
</div>
</div>
</div>
<slot />
<slot/>
</BaseLayout>

View File

@@ -1,14 +1,13 @@
---
import '../styles/main.css'
import BaseLayout from '../layouts/BaseLayout.astro'
import '../styles/main.css'
import BaseLayout from '../layouts/BaseLayout.astro'
const { frontmatter } = Astro.props
const _frontmatter = Astro.props.frontmatter
---
<BaseLayout frontmatter={frontmatter}>
<BaseLayout frontmatter={_frontmatter}>
<div class="flex-none container mx-auto py-12 text-left max-w-7xl p-6">
<h1 class="text-5xl font-extrabold mb-2">{frontmatter.title}</h1>
<h2 class="text-2xl font-semibold">{frontmatter.description}</h2>
<h1 class="text-5xl font-extrabold mb-2">{_frontmatter.title}</h1>
<h2 class="text-2xl font-semibold">{_frontmatter.description}</h2>
</div>
{Astro.props.wide ? <main class="page-content"><slot /></main> : <main class="page-content flex-1 container mx-auto max-w-7xl p-6"><slot /></main>}
</BaseLayout>
</BaseLayout>

View File

@@ -1,66 +1,139 @@
---
import '../styles/main.css'
import PageLayout from '../layouts/PageLayout.astro'
import PageSection from '../components/PageSection.astro'
import '../styles/main.css'
import PageSection from '../components/PageSection.astro'
import PageLayout from '../layouts/PageLayout.astro'
const frontmatter = {
title: 'Report Fraud',
description: 'Have you been scammed or hacked? Here is what to do!'
}
const _frontmatter = {
title: 'Report Fraud',
description: 'Have you been scammed or hacked? Here is what to do!',
}
---
<PageLayout frontmatter={_frontmatter} wide="true">
<PageSection align="center">
<p>
Xahau is a public blockchain, with no governing party that can freeze or
retrieve funds, close accounts, or otherwise keep people from their
assets.
</p>
<div class="p-4 bg-green-50 border border-green-200 rounded-lg my-4">
<strong>We can't reverse or cancel transactions, no-one can.</strong>
</div>
<p>We can flag accounts used for illicit activity, which will:</p>
<ul class="list-disc list-outside">
<li>
Be included in our API that exchanges and other entities are using for
AML compliance to monitor deposits, and possibly withhold illicit funds.
</li>
<li>
Movement of funds will be auto-traced and we will receive notifications
whenever they move, no matter how old the case is.
</li>
<li>
In case of a scam, warn other users through wallet software and
exchanges using our API, not to send funds to a flagged account.
</li>
</ul>
<h3 class="mt-4">1. Submit the address to Xahau Forensics</h3>
<p>
We maintain the largest fraudulent address registry on Xahau and is used
by several entities to combat illicit activity.
</p>
<h3 class="mt-4">2. Report your case to law enforcement</h3>
<p>
Report it to the local police and if your country has an online report
form for cybercrime or financial crime, report it there as well.
</p>
<h3 class="mt-4">3. Follow up on your police report</h3>
<p>
We work with law enforcement. Let them know that we have the information
the odds are that we are also in contact with other victims and can help
law enforcement combine cases across jurisdictions and provide actionable
intelligence.
</p>
<h2 class="mt-4">What can you expect?</h2>
<ul class="list-disc list-outside">
<li>We can't reverse or cancel transactions, no-one can.</li>
<li>
We do our best to have funds seized when they leave Xahau, by working
with exchanges and other off ramps both through our fraudulent address
registry API and by manually making contact.
</li>
<li>
When you report an account to us, you can expect us to treat your report
with as much attention than any other report.
</li>
<li>
We get many reports every single day. If an account is added to our
fraudulent address registry, we are taking the best care of it along
with all other cases.
</li>
<li>
If money is seized we will contact you (if you have left us a way to
contact you).
</li>
<li>
To reclaim funds you <strong>have</strong>to work with law enforcement
for paperwork
</li>
</ul>
<div class="p-4 bg-green-50 border border-green-200 rounded-lg my-4">
<strong
>We monitor hundreds of cases at the same time and can't hold hands on a
case-by-case basis. We will only contact you if we have good news!</strong
>
</div>
</PageSection>
<PageLayout frontmatter={frontmatter} wide="true">
<PageSection align="center">
<p>Xahau is a public blockchain, with no governing party that can freeze or retrieve funds, close accounts, or otherwise keep people from their assets.</p>
<div class="p-4 bg-green-50 border border-green-200 rounded-lg my-4">
<strong>We can't reverse or cancel transactions, no-one can.</strong>
</div>
<p>We can flag accounts used for illicit activity, which will:</p>
<ul class="list-disc list-outside">
<li>Be included in our API that exchanges and other entities are using for AML compliance to monitor deposits, and possibly withhold illicit funds.</li>
<li>Movement of funds will be auto-traced and we will receive notifications whenever they move, no matter how old the case is.</li>
<li>In case of a scam, warn other users through wallet software and exchanges using our API, not to send funds to a flagged account.</li>
</ul>
<h3 class="mt-4">1. Submit the address to Xahau Forensics</h3>
<p>We maintain the largest fraudulent address registry on Xahau and is used by several entities to combat illicit activity.</p>
<h3 class="mt-4">2. Report your case to law enforcement</h3>
<p>Report it to the local police and if your country has an online report form for cybercrime or financial crime, report it there as well.</p>
<h3 class="mt-4">3. Follow up on your police report</h3>
<p>We work with law enforcement. Let them know that we have the information the odds are that we are also in contact with other victims and can help law enforcement combine cases across jurisdictions and provide actionable intelligence.</p>
<h2 class="mt-4">What can you expect?</h2>
<ul class="list-disc list-outside">
<li>We can't reverse or cancel transactions, no-one can.</li>
<li>We do our best to have funds seized when they leave Xahau, by working with exchanges and other off ramps both through our fraudulent address registry API and by manually making contact.</li>
<li>When you report an account to us, you can expect us to treat your report with as much attention than any other report.</li>
<li>We get many reports every single day. If an account is added to our fraudulent address registry, we are taking the best care of it along with all other cases.</li>
<li>If money is seized we will contact you (if you have left us a way to contact you).</li>
<li>To reclaim funds you <strong>have</strong> to work with law enforcement for paperwork</li>
</ul>
<div class="p-4 bg-green-50 border border-green-200 rounded-lg my-4">
<strong>We monitor hundreds of cases at the same time and can't hold hands on a case-by-case basis. We will only contact you if we have good news!</strong>
</div>
</PageSection>
<main class="page-content flex-1 container mx-auto max-w-4xl p-6">
<div class="bg-white rounded-lg shadow-lg p-8">
<!-- Success message (hidden by default, shown via JS) -->
<div id="success-message" class="mb-6 p-6 bg-green-50 border-2 border-green-500 text-green-800 rounded-lg hidden shadow-md">
<div
id="success-message"
class="mb-6 p-6 bg-green-50 border-2 border-green-500 text-green-800 rounded-lg hidden shadow-md"
>
<div class="flex items-center mb-2">
<svg class="w-6 h-6 mr-2 text-green-500" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
<svg
class="w-6 h-6 mr-2 text-green-500"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
clip-rule="evenodd"
/>
</svg>
<p class="font-bold text-lg">Success!</p>
</div>
<p class="mb-2">Thank you for your report. It has been submitted successfully.</p>
<p class="text-sm mt-3 font-semibold">Report ID: <code id="report-id" class="bg-green-200 px-3 py-1 rounded text-green-900"></code></p>
<p class="mb-2">
Thank you for your report. It has been submitted successfully.
</p>
<p class="text-sm mt-3 font-semibold">
Report ID:
<code
id="report-id"
class="bg-green-200 px-3 py-1 rounded text-green-900"
/>
</p>
</div>
<!-- Error message (hidden by default, shown via JS) -->
<div id="error-message" class="mb-6 p-6 bg-red-50 border-2 border-red-500 text-red-800 rounded-lg hidden shadow-md">
<div
id="error-message"
class="mb-6 p-6 bg-red-50 border-2 border-red-500 text-red-800 rounded-lg hidden shadow-md"
>
<div class="flex items-center mb-2">
<svg class="w-6 h-6 mr-2 text-red-500" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/>
<svg
class="w-6 h-6 mr-2 text-red-500"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z"
clip-rule="evenodd"
/>
</svg>
<p class="font-bold text-lg">Error</p>
</div>
@@ -71,7 +144,10 @@ const frontmatter = {
<div id="form-container">
<form id="fraud-report-form" class="space-y-6">
<div>
<label for="address" class="block text-sm font-semibold text-gray-700 mb-2">
<label
for="address"
class="block text-sm font-semibold text-gray-700 mb-2"
>
Xahau Address <span class="text-red-500">*</span>
</label>
<input
@@ -81,12 +157,17 @@ const frontmatter = {
required
placeholder="rXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-xahau-green focus:border-transparent transition-all"
/>
<p class="text-sm text-gray-500 mt-1">Enter the Xahau address associated with the fraudulent activity</p>
>
<p class="text-sm text-gray-500 mt-1">
Enter the Xahau address associated with the fraudulent activity
</p>
</div>
<div>
<label for="description" class="block text-sm font-semibold text-gray-700 mb-2">
<label
for="description"
class="block text-sm font-semibold text-gray-700 mb-2"
>
Description <span class="text-red-500">*</span>
</label>
<textarea
@@ -97,11 +178,16 @@ const frontmatter = {
placeholder="Describe the fraudulent activity"
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-xahau-green focus:border-transparent transition-all"
></textarea>
<p class="text-sm text-gray-500 mt-1">Provide as much detail as possible to help us investigate</p>
<p class="text-sm text-gray-500 mt-1">
Provide as much detail as possible to help us investigate
</p>
</div>
<div>
<label for="url" class="block text-sm font-semibold text-gray-700 mb-2">
<label
for="url"
class="block text-sm font-semibold text-gray-700 mb-2"
>
URL <span class="text-gray-400 text-xs">(Optional)</span>
</label>
<input
@@ -110,13 +196,19 @@ const frontmatter = {
name="url"
placeholder="Optional URL related to the fraud"
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-xahau-green focus:border-transparent transition-all"
/>
<p class="text-sm text-gray-500 mt-1">Provide a URL if relevant (e.g., scam website, social media post)</p>
>
<p class="text-sm text-gray-500 mt-1">
Provide a URL if relevant (e.g., scam website, social media post)
</p>
</div>
<div>
<label for="category_suggested" class="block text-sm font-semibold text-gray-700 mb-2">
Suggested Category <span class="text-gray-400 text-xs">(Optional)</span>
<label
for="category_suggested"
class="block text-sm font-semibold text-gray-700 mb-2"
>
Suggested Category
<span class="text-gray-400 text-xs">(Optional)</span>
</label>
<select
id="category_suggested"
@@ -128,12 +220,18 @@ const frontmatter = {
<option value="theft">Theft</option>
<option value="other">Other</option>
</select>
<p class="text-sm text-gray-500 mt-1">Help us categorize the type of fraud</p>
<p class="text-sm text-gray-500 mt-1">
Help us categorize the type of fraud
</p>
</div>
<div>
<label for="reporter_contact" class="block text-sm font-semibold text-gray-700 mb-2">
Contact Information <span class="text-gray-400 text-xs">(Optional)</span>
<label
for="reporter_contact"
class="block text-sm font-semibold text-gray-700 mb-2"
>
Contact Information
<span class="text-gray-400 text-xs">(Optional)</span>
</label>
<input
type="text"
@@ -141,8 +239,11 @@ const frontmatter = {
name="reporter_contact"
placeholder="Optional contact information, for example e-mail, X handle or Telegram username"
class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-xahau-green focus:border-transparent transition-all"
/>
<p class="text-sm text-gray-500 mt-1">Provide contact info if you're willing to help with follow-up questions</p>
>
<p class="text-sm text-gray-500 mt-1">
Provide contact info if you're willing to help with follow-up
questions
</p>
</div>
<div class="bg-gray-50 p-4 rounded-lg border border-gray-200">
@@ -156,7 +257,7 @@ const frontmatter = {
<div class="flex items-center justify-between pt-4">
<p class="text-sm text-gray-600">
<span class="text-red-500">*</span> Required fields
<span class="text-red-500">*</span>Required fields
</p>
<button
type="submit"
@@ -180,29 +281,68 @@ const frontmatter = {
</div>
<div class="mt-8 bg-blue-50 border border-blue-200 rounded-lg p-6 pt-0">
<h3 class="text-lg font-semibold text-blue-900 mb-3">Privacy & Security</h3>
<h3 class="text-lg font-semibold text-blue-900 mb-3">
Privacy & Security
</h3>
<ul class="space-y-2 text-sm text-blue-800">
<li class="flex items-start">
<svg class="w-5 h-5 mr-2 mt-0.5 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
<svg
class="w-5 h-5 mr-2 mt-0.5 flex-shrink-0"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
clip-rule="evenodd"
/>
</svg>
<span>This form uses ALTCHA, a privacy-compliant CAPTCHA that doesn't track you</span>
<span
>This form uses ALTCHA, a privacy-compliant CAPTCHA that doesn't
track you</span
>
</li>
<li class="flex items-start">
<svg class="w-5 h-5 mr-2 mt-0.5 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
<svg
class="w-5 h-5 mr-2 mt-0.5 flex-shrink-0"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
clip-rule="evenodd"
/>
</svg>
<span>Your report is submitted securely to the Xahau Forensics network</span>
<span
>Your report is submitted securely to the Xahau Forensics network</span
>
</li>
<li class="flex items-start">
<svg class="w-5 h-5 mr-2 mt-0.5 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
<svg
class="w-5 h-5 mr-2 mt-0.5 flex-shrink-0"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
clip-rule="evenodd"
/>
</svg>
<span>Reports are reviewed and used to improve network security</span>
</li>
<li class="flex items-start">
<svg class="w-5 h-5 mr-2 mt-0.5 flex-shrink-0" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
<svg
class="w-5 h-5 mr-2 mt-0.5 flex-shrink-0"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fill-rule="evenodd"
d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z"
clip-rule="evenodd"
/>
</svg>
<span>No personal information is required to submit a report</span>
</li>
@@ -211,164 +351,182 @@ const frontmatter = {
<div class="mt-6 text-center text-sm text-gray-600">
<p>
Xahau Forensics is run by <a href="https://inftf.org" class="text-xahau-green-dark underline hover:text-black">INFTF</a>.
Xahau Forensics is run by
<a
href="https://inftf.org"
class="text-xahau-green-dark underline hover:text-black"
>INFTF</a
>.
</p>
</div>
</main>
<script is:inline type="module">
// Import ALTCHA widget
import 'https://cdn.jsdelivr.net/npm/altcha/dist/altcha.min.js';
import 'https://cdn.jsdelivr.net/npm/altcha/dist/altcha.min.js'
// Wait for the widget to be ready
document.addEventListener('DOMContentLoaded', () => {
const form = document.getElementById('fraud-report-form');
const altchaWidget = document.querySelector('altcha-widget');
const successMessage = document.getElementById('success-message');
const errorMessage = document.getElementById('error-message');
const errorText = document.getElementById('error-text');
const reportIdEl = document.getElementById('report-id');
const formContainer = document.getElementById('form-container');
const anotherReportBtn = document.getElementById('another-report-btn');
const form = document.getElementById('fraud-report-form')
const altchaWidget = document.querySelector('altcha-widget')
const successMessage = document.getElementById('success-message')
const errorMessage = document.getElementById('error-message')
const errorText = document.getElementById('error-text')
const reportIdEl = document.getElementById('report-id')
const formContainer = document.getElementById('form-container')
const anotherReportBtn = document.getElementById('another-report-btn')
if (!form || !altchaWidget) {
return;
return
}
// Listen for state changes on the ALTCHA widget
let isVerified = false;
let isVerified = false
altchaWidget.addEventListener('statechange', (event) => {
isVerified = event.detail.state === 'verified';
});
isVerified = event.detail.state === 'verified'
})
// Handle form submission
form.addEventListener('submit', async (e) => {
e.preventDefault();
e.preventDefault()
// Hide previous messages
errorMessage.classList.add('hidden');
successMessage.classList.add('hidden');
errorMessage.classList.add('hidden')
successMessage.classList.add('hidden')
// Check verification state
if (!isVerified) {
errorText.textContent = 'Please complete the CAPTCHA verification before submitting.';
errorMessage.classList.remove('hidden');
errorText.textContent =
'Please complete the CAPTCHA verification before submitting.'
errorMessage.classList.remove('hidden')
// Scroll to top of the white card container
const card = document.querySelector('.bg-white.rounded-lg.shadow-lg');
const card = document.querySelector('.bg-white.rounded-lg.shadow-lg')
if (card) {
card.scrollIntoView({ behavior: 'smooth', block: 'start' });
card.scrollIntoView({ behavior: 'smooth', block: 'start' })
}
return;
return
}
// Get ALTCHA payload - try multiple methods
let altchaSolution = null;
let altchaSolution = null
// Try getting from FormData first (if widget name attribute works)
const formData = new FormData(form);
altchaSolution = formData.get('altcha');
const formData = new FormData(form)
altchaSolution = formData.get('altcha')
// If not found, try getting directly from widget
if (!altchaSolution) {
// The widget stores the solution in a hidden input or as a property
const hiddenInput = form.querySelector('input[name="altcha"]');
const hiddenInput = form.querySelector('input[name="altcha"]')
if (hiddenInput) {
altchaSolution = hiddenInput.value;
altchaSolution = hiddenInput.value
}
}
// Try getting from data attribute
if (!altchaSolution) {
altchaSolution = altchaWidget.dataset.payload;
altchaSolution = altchaWidget.dataset.payload
}
if (!altchaSolution) {
errorText.textContent = 'CAPTCHA verification failed. Please complete the challenge and try again.';
errorMessage.classList.remove('hidden');
errorText.textContent =
'CAPTCHA verification failed. Please complete the challenge and try again.'
errorMessage.classList.remove('hidden')
// Scroll to top of the white card container
const card = document.querySelector('.bg-white.rounded-lg.shadow-lg');
const card = document.querySelector('.bg-white.rounded-lg.shadow-lg')
if (card) {
card.scrollIntoView({ behavior: 'smooth', block: 'start' });
card.scrollIntoView({ behavior: 'smooth', block: 'start' })
}
return;
return
}
const requestBody = {
address: formData.get('address'),
description: formData.get('description'),
altcha_solution: altchaSolution
};
altcha_solution: altchaSolution,
}
// Add optional fields
if (formData.get('url')) requestBody.url = formData.get('url');
if (formData.get('category_suggested')) requestBody.category_suggested = formData.get('category_suggested');
if (formData.get('reporter_contact')) requestBody.reporter_contact = formData.get('reporter_contact');
if (formData.get('url')) requestBody.url = formData.get('url')
if (formData.get('category_suggested'))
requestBody.category_suggested = formData.get('category_suggested')
if (formData.get('reporter_contact'))
requestBody.reporter_contact = formData.get('reporter_contact')
// Disable submit button and show loading state
const submitBtn = form.querySelector('button[type="submit"]');
const originalText = submitBtn.textContent;
submitBtn.disabled = true;
submitBtn.innerHTML = '<span class="flex items-center justify-center"><svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>Submitting...</span>';
const submitBtn = form.querySelector('button[type="submit"]')
const originalText = submitBtn.textContent
submitBtn.disabled = true
submitBtn.innerHTML =
'<span class="flex items-center justify-center"><svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>Submitting...</span>'
try {
const response = await fetch('https://api.analytics.xahau.network/ufr', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
const response = await fetch(
'https://api.analytics.xahau.network/ufr',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(requestBody),
},
body: JSON.stringify(requestBody)
});
const result = await response.json();
)
const result = await response.json()
if (response.ok) {
// Show success message
reportIdEl.textContent = result.reportId || 'unknown';
successMessage.classList.remove('hidden');
errorMessage.classList.add('hidden');
formContainer.style.display = 'none';
anotherReportBtn.classList.remove('hidden');
reportIdEl.textContent = result.reportId || 'unknown'
successMessage.classList.remove('hidden')
errorMessage.classList.add('hidden')
formContainer.style.display = 'none'
anotherReportBtn.classList.remove('hidden')
// Scroll to top of the white card container
const card = document.querySelector('.bg-white.rounded-lg.shadow-lg');
const card = document.querySelector(
'.bg-white.rounded-lg.shadow-lg',
)
if (card) {
card.scrollIntoView({ behavior: 'smooth', block: 'start' });
card.scrollIntoView({ behavior: 'smooth', block: 'start' })
}
} else {
// Show error message
const errorMsg = Array.isArray(result.message)
? result.message.join(', ')
: (result.message || 'Failed to submit report. Please try again.');
errorText.textContent = errorMsg;
errorMessage.classList.remove('hidden');
successMessage.classList.add('hidden');
submitBtn.disabled = false;
submitBtn.innerHTML = originalText;
const errorMsg = Array.isArray(result.message)
? result.message.join(', ')
: result.message || 'Failed to submit report. Please try again.'
errorText.textContent = errorMsg
errorMessage.classList.remove('hidden')
successMessage.classList.add('hidden')
submitBtn.disabled = false
submitBtn.innerHTML = originalText
// Scroll to top of the white card container
const card = document.querySelector('.bg-white.rounded-lg.shadow-lg');
const card = document.querySelector(
'.bg-white.rounded-lg.shadow-lg',
)
if (card) {
card.scrollIntoView({ behavior: 'smooth', block: 'start' });
card.scrollIntoView({ behavior: 'smooth', block: 'start' })
}
}
} catch (error) {
errorText.textContent = 'Network error: Unable to connect to the server. Please check your connection and try again.';
errorMessage.classList.remove('hidden');
successMessage.classList.add('hidden');
submitBtn.disabled = false;
submitBtn.innerHTML = originalText;
} catch (_error) {
errorText.textContent =
'Network error: Unable to connect to the server. Please check your connection and try again.'
errorMessage.classList.remove('hidden')
successMessage.classList.add('hidden')
submitBtn.disabled = false
submitBtn.innerHTML = originalText
// Scroll to top of the white card container
const card = document.querySelector('.bg-white.rounded-lg.shadow-lg');
const card = document.querySelector('.bg-white.rounded-lg.shadow-lg')
if (card) {
card.scrollIntoView({ behavior: 'smooth', block: 'start' });
card.scrollIntoView({ behavior: 'smooth', block: 'start' })
}
}
});
});
})
})
</script>
<style>
@@ -378,8 +536,9 @@ const frontmatter = {
--altcha-border-color: #e1e5e9;
--altcha-text-color: #333333;
--altcha-border-radius: 8px;
--altcha-font-family: 'Onest', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
--altcha-font-family:
'Onest', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
sans-serif;
}
</style>
</PageLayout>

View File

@@ -1,8 +1,8 @@
@layer base, starlight, theme, components, utilities;
@import "tailwindcss/theme.css" layer(theme);
@import "tailwindcss/utilities.css" layer(utilities);
@import "../styles/global.css";
@import 'tailwindcss/theme.css' layer(theme);
@import 'tailwindcss/utilities.css' layer(utilities);
@import '../styles/global.css';
header * {
line-height: 1.5;

View File

@@ -20,7 +20,6 @@ footer {
@media (prefers-reduced-motion: no-preference) {
@view-transition {
/* biome-ignore lint: noUnknownProperty */
navigation: auto;
}
}

View File

@@ -1,5 +1,5 @@
@import "tailwindcss";
@import "../styles/global.css";
@import 'tailwindcss';
@import '../styles/global.css';
.page-content h1 {
@apply text-3xl font-bold mb-2 mt-24;