Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import sitemap from "@astrojs/sitemap";
import partytown from "@astrojs/partytown";
import node from "@astrojs/node";
import react from "@astrojs/react";
// Removed astro-iconify import
import Icons from "unplugin-icons/vite";
import starlightLlmsTxt from "starlight-llms-txt";
import { sidebar } from "./astro.sidebar.ts";
Expand Down Expand Up @@ -95,7 +96,7 @@ export default defineConfig({
},
components: {
Head: "./src/starlight-overrides/Head.astro",
// Header: "./src/starlight-overrides/Header.astro",
Header: "./src/starlight-overrides/Header.astro",
LanguageSelect: "./src/starlight-overrides/LanguageSelect.astro",
MobileMenuToggle: "./src/starlight-overrides/MobileMenuToggle.astro",
PageFrame: "./src/starlight-overrides/PageFrame.astro",
Expand Down Expand Up @@ -161,6 +162,7 @@ export default defineConfig({
experimentalReactChildren: true,
include: ["**/GraphQLEditor.tsx"],
}),
// Removed astro-iconify integration
],
adapter: process.env.VERCEL
? vercel({
Expand Down
31 changes: 31 additions & 0 deletions src/components/NetworkProvider.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
import { DEFAULT_NETWORK } from "../config/networks";
---

<script is:inline define:vars={{ defaultNetwork: DEFAULT_NETWORK }}>
window.StarlightNetworkProvider = (() => {
function setNetworkPreference() {
// Check for cookie
const cookieMatch = document.cookie.match(/preferred_network=([^;]+)/);
const preferredNetwork = cookieMatch ? cookieMatch[1] : defaultNetwork;

// Store the preference in a data attribute on the document
document.documentElement.dataset.preferredNetwork = preferredNetwork;

return preferredNetwork;
}

// Set the network preference immediately
const networkPreference = setNetworkPreference();

return {
updatePickers: function (network = networkPreference) {
document.documentElement.dataset.preferredNetwork = network;
document.querySelectorAll("starlight-network-select").forEach((picker) => {
const select = picker.querySelector("select");
if (select) select.value = network;
});
},
};
})();
</script>
66 changes: 66 additions & 0 deletions src/components/NetworkSelect.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
import Select from "@astrojs/starlight/components/Select.astro";
import { MOVE_REFERENCE_BRANCHES } from "../content.config";
import { DEFAULT_NETWORK } from "../config/networks";

const networks = MOVE_REFERENCE_BRANCHES.map(network => ({
label: network.label,
value: network.name,
selected: network.name === DEFAULT_NETWORK
}));
---

<starlight-network-select>
<Select
icon="setting"
label="Network"
value={DEFAULT_NETWORK}
options={networks}
width="7.5em"
/>
</starlight-network-select>

{/* Inlined to avoid FOUC. Uses global scope from `NetworkProvider.astro` */}
<script is:inline>
window.StarlightNetworkProvider.updatePickers();
</script>

<script>
declare global {
interface Window {
StarlightNetworkProvider: {
updatePickers: (network?: string) => void;
};
}
}

class StarlightNetworkSelect extends HTMLElement {
constructor() {
super();
const select = this.querySelector('select');
if (!select) return;

select.addEventListener('change', (e) => {
if (e.currentTarget instanceof HTMLSelectElement) {
// Get the selected network
const network = e.currentTarget.value;

// Set the cookie with a 1-year expiration
const expiryDate = new Date();
expiryDate.setFullYear(expiryDate.getFullYear() + 1);
document.cookie = `preferred_network=${network}; expires=${expiryDate.toUTCString()}; path=/; SameSite=Lax`;

console.log(`Set network preference cookie: preferred_network=${network}`);

// Update all pickers
window.StarlightNetworkProvider.updatePickers(network);

// Reload the page to apply the network change
// This will trigger the middleware to redirect to the correct network
window.location.reload();
}
});
}
}
customElements.define('starlight-network-select', StarlightNetworkSelect);
</script>
3 changes: 3 additions & 0 deletions src/config/networks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const NETWORK_NAMES = ["mainnet", "testnet", "devnet"] as const;
export const DEFAULT_NETWORK = "mainnet";
export type NetworkName = (typeof NETWORK_NAMES)[number];
1 change: 1 addition & 0 deletions src/globals.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
@import "tailwindcss";
@custom-variant dark (&:where([data-theme=dark], [data-theme=dark] *));
@plugin "@astrojs/starlight-tailwind";
@import "./styles/network-select.css";

/* Register your custom font family and tell the browser where to find it. */
@font-face {
Expand Down
53 changes: 53 additions & 0 deletions src/middlewares/network-redirect.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { NETWORK_NAMES, DEFAULT_NETWORK, type NetworkName } from "../config/networks";

// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
export default function middleware(request: Request): Response | void {
const url = new URL(request.url);
const pathname = url.pathname;

// Check for cookie
const cookies = request.headers.get("cookie") ?? "";
const networkCookieMatch = /preferred_network=([a-z-]+)/.exec(cookies);
let preferredNetwork: NetworkName = DEFAULT_NETWORK;

// Validate that the cookie value is a valid network
if (networkCookieMatch && NETWORK_NAMES.includes(networkCookieMatch[1] as NetworkName)) {
preferredNetwork = networkCookieMatch[1] as NetworkName;
}

// Only process move-reference paths
if (!pathname.startsWith("/move-reference")) {
return undefined;
}

// Check for language prefix
const langPathMatch = /^\/([a-z]{2})(?:\/|$)/.exec(pathname);
// Ensure langPrefix is always a string
let langPrefix = "";
if (langPathMatch?.[1]) {
langPrefix = "/" + langPathMatch[1];
}
const pathWithoutLang = langPathMatch ? pathname.substring(langPathMatch[0].length) : pathname;

// Check if the path already includes a network
const networkPathMatch = /^\/move-reference\/([a-z-]+)(\/.*|$)/.exec(pathWithoutLang);

if (networkPathMatch) {
const pathNetwork = networkPathMatch[1];
const remainingPath = networkPathMatch[2] ?? "/";

// If the network in the path is valid but different from the preferred one, redirect
if (NETWORK_NAMES.includes(pathNetwork as NetworkName) && pathNetwork !== preferredNetwork) {
// Construct the new path without template literals
url.pathname = langPrefix + "/move-reference/" + preferredNetwork + remainingPath;
return Response.redirect(url);
}
} else if (pathWithoutLang === "/move-reference" || pathWithoutLang === "/move-reference/") {
// If no network is specified in the path, redirect to the preferred network
// Construct the new path without template literals
url.pathname = langPrefix + "/move-reference/" + preferredNetwork + "/";
return Response.redirect(url);
}

return undefined;
}
2 changes: 2 additions & 0 deletions src/starlight-overrides/Head.astro
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Default from "@astrojs/starlight/components/Head.astro";
// import { ClientRouter } from "astro:transitions";
import { Schema } from "astro-seo-schema";
import TextSizeProvider from "../components/TextSizeProvider.astro";
import NetworkProvider from "../components/NetworkProvider.astro";
import { SUPPORTED_LANGUAGES } from "../config/locales";
import { getImageUrl } from "~/lib/og-image/getImageUrl";

Expand Down Expand Up @@ -167,6 +168,7 @@ const lastUpdatedDate = lastUpdated ? lastUpdated.toISOString() : new Date().toI
/>

<TextSizeProvider />
<NetworkProvider />
<Default {...Astro.props}><slot /></Default>

<!-- <meta property="article:published_time" content={entryData.publishDate} /> -->
Expand Down
2 changes: 2 additions & 0 deletions src/starlight-overrides/Header.astro
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import SiteTitle from "@astrojs/starlight/components/SiteTitle.astro";
import SocialIcons from "@astrojs/starlight/components/SocialIcons.astro";
import ThemeSelect from "@astrojs/starlight/components/ThemeSelect.astro";
// import TextSizeToggle from "../components/TextSizeToggle.astro";
import NetworkSelect from "../components/NetworkSelect.astro";
import LanguageSelect from "./LanguageSelect.astro";
---

Expand All @@ -21,6 +22,7 @@ import LanguageSelect from "./LanguageSelect.astro";
<!-- <TextSizeToggle /> -->
<ThemeSelect />
<LanguageSelect />
<NetworkSelect />
</div>
</div>

Expand Down
24 changes: 24 additions & 0 deletions src/styles/network-select.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/* Network selector styles */
:root[data-preferred-network="mainnet"] {
--network-color: var(--sl-color-green-high);
}

:root[data-preferred-network="testnet"] {
--network-color: var(--sl-color-orange-high);
}

:root[data-preferred-network="devnet"] {
--network-color: var(--sl-color-blue-high);
}

/* Add a subtle indicator to the network select */
starlight-network-select::after {
content: "";
position: absolute;
bottom: -2px;
left: 0;
width: 100%;
height: 2px;
background-color: var(--network-color, transparent);
border-radius: 1px;
}
2 changes: 2 additions & 0 deletions src/vercel-middleware.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/no-invalid-void-type */
// Edge-compatible middleware that implements a middleware chain pattern
import i18nRedirect from "./middlewares/i18n-redirect";
import networkRedirect from "./middlewares/network-redirect";
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore - auto-generated import
import { matcher } from "./middlewares/matcher-routes-dynamic";
Expand Down Expand Up @@ -30,6 +31,7 @@ async function applyMiddleware(
export default async function middleware(req: Request) {
return await applyMiddleware(req, [
i18nRedirect,
networkRedirect,
// Add more middleware functions here as needed
]);
}
Loading