From 709d80b9985c0a02c48abb911a1ada93034542fe Mon Sep 17 00:00:00 2001 From: Alexander zur Bonsen Date: Fri, 16 May 2025 10:37:01 +0200 Subject: [PATCH 01/18] chore: clarify the definition of dataReloadRatio The SWD model defines the dataCacheRatio which is the complement of the dataReloadRatio, i.e. 1 - dataCacheRatio. Clarify that in the description. Signed-off-by: Alexander zur Bonsen --- src/docs/co2js/methods.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/docs/co2js/methods.md b/src/docs/co2js/methods.md index e20d85f..8b00154 100644 --- a/src/docs/co2js/methods.md +++ b/src/docs/co2js/methods.md @@ -134,7 +134,7 @@ The `perVisitTrace()` function accepts the following parameters: The `options` parameter can contain any of the following keys. These can be used to adjust the values used by the Sustainable Web Design Model's calculation. -- `dataReloadRatio` Optional – a `number` between 0 and 1 representing the percentage of data that is downloaded by return visitors. +- `dataReloadRatio` Optional – a `number` between 0 and 1 representing the percentage of data that is downloaded by return visitors. This is the complement of the data cache ratio from the Sustainable Web Design model, i.e. dataReloadRatio = 1 - dataCacheRatio. - `firstVisitPercentage` Optional – a `number` between 0 and 1 representing the percentage of new visitors. - `returnVisitPercentage` Optional – a `number` between 0 and 1 representing the percentage of returning visitors. - `greenHostingFactor`
Sustainable Web Design Model v4 only
- The portion of hosting services powered by renewable or zero-carbon energy, between 0 and 1. If the `green hosting` boolean above is set to `true` then the `greenHostingFactor` will always be `1`. @@ -173,6 +173,6 @@ The `perVisitTrace()` function returns an object with the following keys: - `dataCenter` – A `number` representing the carbon intensity for this segment (in grams per kilowatt-hour) used in the calculation. - `networks` – A `number` representing the carbon intensity for this segment (in grams per kilowatt-hour) used in the calculation. - `production` - A `number` representing the carbon intensity for this segment (in grams per kilowatt-hour) used in the calculation. - - `dataReloadRatio` – a `number` between 0 and 1 representing the percentage of data that is downloaded by return visitors. + - `dataReloadRatio` – a `number` between 0 and 1 representing the percentage of data that is downloaded by return visitors. This is the complement of the data cache ratio from the Sustainable Web Design model, i.e. dataReloadRatio = 1 - dataCacheRatio. - `firstVisitPercentage` – a `number` between 0 and 1 representing the percentage of new visitors. - `returnVisitPercentage` – a `number` between 0 and 1 representing the percentage of returning visitors. From b650eec1cd398aabedc6143892b8a832ef933082 Mon Sep 17 00:00:00 2001 From: fershad <27988517+fershad@users.noreply.github.com> Date: Sat, 17 May 2025 18:24:42 +0800 Subject: [PATCH 02/18] update tutorial to use "auto" function --- .../grid-aware-tutorial-cloudflare-workers.md | 288 +++++++++--------- 1 file changed, 138 insertions(+), 150 deletions(-) diff --git a/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md b/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md index e8e7df6..15aa208 100644 --- a/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md +++ b/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md @@ -19,7 +19,6 @@ In this tutorial, you will learn how to: - Create a new Cloudflare Workers project - Add Grid-aware Websites to the Cloudflare Worker -- Use the Grid-aware Websites library with the Electricity Maps API to determine if grid-aware view is recommended - Use the HTMLRewriter API to remove content from the page when a grid-aware view is recommended - Publish the Cloudflare Worker to target a specific route on your website @@ -41,30 +40,25 @@ You should also be aware of the limits and pricing of Cloudflare Workers, availa ## Creating a new Cloudflare Workers project -To begin using Grid-aware Websites on an existing website through Cloudflare Workers, we will first create a new Cloudflare Workers project on our development machine. You can do that by following the steps below, or by visiting the [Cloudflare Workers documentation](https://developers.cloudflare.com/workers/get-started/guide/). - -In a new terminal window, run the command below. We've called our project `grid-aware-worker`, but you can give it whatever name you like. +To begin using Grid-aware Websites on an existing website through Cloudflare Workers, we will first create a new Cloudflare Workers project on our development machine. You can do this by using the Cloudflare Wrangler CLI to setup a project using a template which we have prepared. ```bash -npm create cloudflare@latest -- grid-aware-worker +npm create cloudflare@latest -- --template thegreenwebfoundation/gaw-cloudflare-template ``` -Running this command will present you with a series of prompts to setup your Cloudflare Worker project. Once you have completed the prompts, you can move into the project folder. - -```bash -cd grid-aware-worker -``` +This will clone the template onto your computer. You can replace `` with the name of your project. During the setup process you will be asked `Do you want to use git for version control?`, select `yes` and `Do you want to deploy your application?`, select `no`. -Your project will have a folder structure like this: +After the project is cloned, you can navigate to the destination folder. Your project will have a folder structure like this: ```text grid-aware-worker/ ├── src/ -│ └── index.js -├── wrangler.json -├── package.json -├── package-lock.json -└── node_modules/ +│ └── index.js +├── wrangler.json +├── package.json +├── package-lock.json +├── .dev.vars +└── node_modules/ ``` The `src/index.js` file contains the Worker code that we will modify to add grid awareness to our website. @@ -112,16 +106,16 @@ Now that we have the main dependencies for this project installed, we can write 1. Get the location (country) of a website request 2. Run grid-awareness checks of that country's energy grid 3. Based on the result of that check: - 1. Return the website as usual - 2. Modify the website before returning it to the user + 1. Return the website as usual + 2. Modify the website before returning it to the user ### Importing our dependencies At the top of the `src/index.js` file, we will first import the Grid-aware Websites library and Cloudflare Workers plugin. ```js -import { PowerBreakdown } from '@greenweb/grid-aware-websites'; -import { getLocation } from '@greenweb/gaw-plugin-cloudflare-workers'; +import { PowerBreakdown } from "@greenweb/grid-aware-websites"; +import { getLocation } from "@greenweb/gaw-plugin-cloudflare-workers"; ``` Here, we are importing the `PowerBreakdown` class from the main library. This class allows us set specific conditions for running grid-aware checks and also gives us functions to fetch data about the fuel-mix of a country's electricity grid from the Electricity Maps API. Fuel-mix is a term used to describe the balance of renewable, low-carbon, and fossil fuel energy used to generate the electricity of a particular region or electricity grid. @@ -131,14 +125,12 @@ The `getLocation` function that we import from the Cloudflare Workers plugin wil Further down in the `src/index.js` file, you should see some boilerplate for a Cloudflare Worker. Delete everything that is inside the `async fetch(request, env, ctx)` function. You should end up with a file that looks like this: ```js -import { PowerBreakdown } from '@greenweb/grid-aware-websites'; -import { getLocation } from '@greenweb/gaw-plugin-cloudflare-workers'; +import { PowerBreakdown } from "@greenweb/grid-aware-websites"; +import { getLocation } from "@greenweb/gaw-plugin-cloudflare-workers"; export default { - async fetch(request, env, ctx) { - - } -} + async fetch(request, env, ctx) {}, +}; ``` ### Getting a website visitor's country @@ -154,40 +146,39 @@ Your Workers `fetch` function should look like this: ```js export default { - async fetch(request, env, ctx) { - - const powerBreakdown = new PowerBreakdown({ - mode: 'low-carbon', - apiKey: env.EMAPS_API_KEY - }); - - // First fetch the request - const response = await fetch(request.url); - // Then check if the request content type is HTML. - const contentType = response.headers.get('content-type'); - - // If the content is not HTML, then return the response without any changes. - if (!contentType || !contentType.includes('text/html')) { - return new Response(response.body, { - ...response, - }); - } - - // If the content type is HTML, we get the country the request came from - const location = await getLocation(request); - const { country } = location; - - // If the country data does not exist, then return the response without any changes. - if (!country) { - return new Response(response.body, { - ...response, - }); - } + async fetch(request, env, ctx) { + const powerBreakdown = new PowerBreakdown({ + mode: "low-carbon", + apiKey: env.EMAPS_API_KEY, + }); - // We return a response - this is just to check that it works. We'll remove it soon. - return new Response(`Request from country code ${country}.`) - } -} + // First fetch the request + const response = await fetch(request.url); + // Then check if the request content type is HTML. + const contentType = response.headers.get("content-type"); + + // If the content is not HTML, then return the response without any changes. + if (!contentType || !contentType.includes("text/html")) { + return new Response(response.body, { + ...response, + }); + } + + // If the content type is HTML, we get the country the request came from + const location = await getLocation(request); + const { country } = location; + + // If the country data does not exist, then return the response without any changes. + if (!country) { + return new Response(response.body, { + ...response, + }); + } + + // We return a response - this is just to check that it works. We'll remove it soon. + return new Response(`Request from country code ${country}.`); + }, +}; ``` Let's step through this code. @@ -234,17 +225,17 @@ Replace it with the code below: const gridData = await powerBreakdown.check(country); // If there's an error getting data, return the web page without any modifications -if (gridData.status === 'error') { - return new Response(response.body, { - ...response, - headers: { - ...response.headers, - }, - }); +if (gridData.status === "error") { + return new Response(response.body, { + ...response, + headers: { + ...response.headers, + }, + }); } // Otherwise, return the grid data in the response -return new Response(`Grid data: ${JSON.stringify(gridData, null, 2)}`) +return new Response(`Grid data: ${JSON.stringify(gridData, null, 2)}`); ``` In the code above, we pass the request country into the `powerBreakdown.check` function. This function will return information about the energy grid we have requested data for, as well as a `gridAware` flag - a boolean value indicating whether grid-aware changes should be made to the website. Earlier in our code, we created a new instance of `PowerBreakdown` and specified that it use the "low-carbon" `mode`. This means that when the function runs a check against data from Electricity Maps it will refer to "low-carbon" energy data (that is renewables + nuclear). You can learn more about the modes available, and other options, on the [Getting started page](/grid-aware-websites/getting-started/#using-the-gridawarepower-function). @@ -270,31 +261,30 @@ Replace it with the HTMLRewriter code below: ```js // If the grid aware flag is triggered (gridAware === true), then we'll return a modified HTML page to the user. if (gridData.gridAware) { - const modifyHTML = new HTMLRewriter() - .on('iframe', { - element(element) { - element.remove(); - }, - }) - - // Transform the response using the HTMLRewriter API, and set appropriate headers. - let modifiedResponse = new Response(modifyHTML.transform(response).body, { - ...response, - headers: { - ...response.headers, - 'Content-Type': 'text/html;charset=UTF-8' - }, - }); + const modifyHTML = new HTMLRewriter().on("iframe", { + element(element) { + element.remove(); + }, + }); + + // Transform the response using the HTMLRewriter API, and set appropriate headers. + let modifiedResponse = new Response(modifyHTML.transform(response).body, { + ...response, + headers: { + ...response.headers, + "Content-Type": "text/html;charset=UTF-8", + }, + }); - return modifiedResponse + return modifiedResponse; } return new Response(response.body, { ...response, headers: { - ...response.headers, + ...response.headers, }, - }); +}); ``` The code above creates a new instance of the HTMLRewriter API that looks for and removes all `iframe` elements. You can chain these steps to make other changes to a web page, even adding content. @@ -308,81 +298,79 @@ Otherwise, if the `gridData.gridAware` flag is returned as `false`, we just retu The final code in your Cloudflare Worker should look like this: ```js -import { PowerBreakdown } from '@greenweb/grid-aware-websites'; -import { getLocation } from '@greenweb/gaw-plugin-cloudflare-workers'; - +import { PowerBreakdown } from "@greenweb/grid-aware-websites"; +import { getLocation } from "@greenweb/gaw-plugin-cloudflare-workers"; export default { async fetch(request, env, ctx) { + const powerBreakdown = new PowerBreakdown({ + mode: "low-carbon", + apiKey: env.EMAPS_API_KEY, + }); - const powerBreakdown = new PowerBreakdown({ - mode: 'low-carbon', - apiKey: env.EMAPS_API_KEY - }); - // First fetch the request - const response = await fetch(request.url); - // Then check if the request content type is HTML. - const contentType = response.headers.get('content-type'); - - // If the content is not HTML, then return the response without any changes. - if (!contentType || !contentType.includes('text/html')) { - return new Response(response.body, { - ...response, - }); - } - - // If the content type is HTML, we get the country the request came from - const location = await getLocation(request); - const { country } = location; - - // If the country data does not exist, then return the response without any changes. - if (!country) { - return new Response(response.body, { - ...response, - }); - } - - const gridData = await powerBreakdown.check(country); - - // If there's an error getting data, return the web page without any modifications - if (gridData.status === 'error') { - return new Response(response.body, { - ...response, - headers: { - ...response.headers, - }, - }); - } + const response = await fetch(request.url); + // Then check if the request content type is HTML. + const contentType = response.headers.get("content-type"); + + // If the content is not HTML, then return the response without any changes. + if (!contentType || !contentType.includes("text/html")) { + return new Response(response.body, { + ...response, + }); + } + + // If the content type is HTML, we get the country the request came from + const location = await getLocation(request); + const { country } = location; + + // If the country data does not exist, then return the response without any changes. + if (!country) { + return new Response(response.body, { + ...response, + }); + } + + const gridData = await powerBreakdown.check(country); + + // If there's an error getting data, return the web page without any modifications + if (gridData.status === "error") { + return new Response(response.body, { + ...response, + headers: { + ...response.headers, + }, + }); + } + + // If the grid aware flag is triggered (gridAware === true), then we'll return a modified HTML page to the user. + if (gridData.gridAware) { + const modifyHTML = new HTMLRewriter().on("iframe", { + element(element) { + element.remove(); + }, + }); + + // Transform the response using the HTMLRewriter API, and set appropriate headers. + let modifiedResponse = new Response(modifyHTML.transform(response).body, { + ...response, + headers: { + ...response.headers, + "Content-Type": "text/html;charset=UTF-8", + }, + }); + + return modifiedResponse; + } - // If the grid aware flag is triggered (gridAware === true), then we'll return a modified HTML page to the user. - if (gridData.gridAware) { - const modifyHTML = new HTMLRewriter().on('iframe', { - element(element) { - element.remove(); - }, - }) - - // Transform the response using the HTMLRewriter API, and set appropriate headers. - let modifiedResponse = new Response(modifyHTML.transform(response).body, { + return new Response(response.body, { ...response, headers: { - ...response.headers, - 'Content-Type': 'text/html;charset=UTF-8' + ...response.headers, }, }); - - return modifiedResponse - } - - return new Response(response.body, { - ...response, - headers: { - ...response.headers, - }, - }); - } -} + }, +}; ``` ### Testing the completed worker From 0fe03a57f4b8995ffd9c7a63dccf724ba7ac955c Mon Sep 17 00:00:00 2001 From: fershad <27988517+fershad@users.noreply.github.com> Date: Sat, 17 May 2025 18:32:56 +0800 Subject: [PATCH 03/18] update tutorial to use "auto" function --- .../grid-aware-tutorial-cloudflare-workers.md | 497 ++---------------- 1 file changed, 58 insertions(+), 439 deletions(-) diff --git a/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md b/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md index 15aa208..58e543a 100644 --- a/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md +++ b/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md @@ -19,7 +19,7 @@ In this tutorial, you will learn how to: - Create a new Cloudflare Workers project - Add Grid-aware Websites to the Cloudflare Worker -- Use the HTMLRewriter API to remove content from the page when a grid-aware view is recommended +- Use the HTMLRewriter API to make a change to the page - Publish the Cloudflare Worker to target a specific route on your website ## Before starting @@ -67,7 +67,7 @@ The `src/index.js` file contains the Worker code that we will modify to add grid ### Setting routes -Before we start writing code, we'll first configure our worker to run on the route we want it to apply to. We want to apply this Worker to the `/tools/grid-aware-websites/` path on the `thegreenwebfoundation.org` domain. To do that, we include the following configuration inside of the `wrangler.jsonc` file. You should replace the `pattern`, and `zone_name` with your own desired route. +Before we start writing code, we'll first configure our worker to run on the route we want it to apply to. In our case, we want to apply this Worker to the `/tools/grid-aware-websites/` path on the `thegreenwebfoundation.org` domain. To do that, we include the following configuration inside of the `wrangler.json` file. _Note: You should replace the `pattern`, and `zone_name` with your own desired route._ ```json "routes": [ @@ -82,7 +82,9 @@ For more information about routes, and how to configure them for Cloudflare Work ### Adding the Electricity Maps API for development -Later in the project, we'll use the Electricity Maps API to get information about the power breakdown of a country's energy grid. For this, you'll need an Electricity Maps API key added to your project. We'll first set this up for our development environment, and later in this tutorial we'll set it up for production. To do this securely, we'll create a `.dev.vars` file in the root directory of our project. Inside that file you can add your Electricity Maps API key as a variable - here we've named the variable `EMAPS_API_KEY`. +Later in the project, we'll use the Electricity Maps API to get information about the power breakdown of a country's energy grid. For this, you'll need an Electricity Maps API key added to your project. We'll first set this up for our development environment, and later in this tutorial we'll set it up for production. + +Open the `.dev.vars` file in the root of the project. There you will see a variable `EMAPS_API_KEY` already present. Update the value of that variable to your own Electricity Maps API key. ```txt EMAPS_API_KEY="" @@ -90,112 +92,77 @@ EMAPS_API_KEY="" ## Adding Grid-aware Websites to the Worker -Before we begin writing code, we will first install the Grid-aware Websites core library and the Cloudflare Workers plugin into our project. In a terminal window run the following NPM commands: +Before we begin writing code, we will install the Cloudflare Workers specific plugin for Grid-aware Websites. Since we already have that setup in our template, we can install it by running the `npm install` command in our project. -```bash -npm install @greenweb/grid-aware-websites -npm install @greenweb/gaw-plugin-cloudflare-workers -``` - -The Grid-aware Websites library's main function is to fetch data about a given location from a specified data source (in our case the Electricity Maps API), and based on that information return a flag indicating if grid-aware changes should be applied to a website. The Cloudflare Workers plugin has some specific functionality that makes it easier to work with Cloudflare Workers. +The Cloudflare Workers plugin contains a handy function that takes care of setting up and executing the Grid-aware Websites code in our Worker. ## Writing some code -Now that we have the main dependencies for this project installed, we can write some code in the `src/index.js` file. What we will do is: - -1. Get the location (country) of a website request -2. Run grid-awareness checks of that country's energy grid -3. Based on the result of that check: - 1. Return the website as usual - 2. Modify the website before returning it to the user - -### Importing our dependencies - -At the top of the `src/index.js` file, we will first import the Grid-aware Websites library and Cloudflare Workers plugin. - -```js -import { PowerBreakdown } from "@greenweb/grid-aware-websites"; -import { getLocation } from "@greenweb/gaw-plugin-cloudflare-workers"; -``` - -Here, we are importing the `PowerBreakdown` class from the main library. This class allows us set specific conditions for running grid-aware checks and also gives us functions to fetch data about the fuel-mix of a country's electricity grid from the Electricity Maps API. Fuel-mix is a term used to describe the balance of renewable, low-carbon, and fossil fuel energy used to generate the electricity of a particular region or electricity grid. - -The `getLocation` function that we import from the Cloudflare Workers plugin will be used to return the country code of the incoming website request. We'll use this country code to fetch the fuel-mix data mentioned above. - -Further down in the `src/index.js` file, you should see some boilerplate for a Cloudflare Worker. Delete everything that is inside the `async fetch(request, env, ctx)` function. You should end up with a file that looks like this: +Now that we have the main dependencies for this project installed, we can write some code in the `src/index.js` file. First, though, let's walk through the code that is already there in that file. ```js -import { PowerBreakdown } from "@greenweb/grid-aware-websites"; -import { getLocation } from "@greenweb/gaw-plugin-cloudflare-workers"; +import gridAwareAuto from "@greenweb/gaw-plugin-cloudflare-workers"; export default { - async fetch(request, env, ctx) {}, + async fetch(request, env, ctx) { + return gridAwareAuto(request, env, ctx, { + gawDataApiKey: env.EMAPS_API_KEY, + }); + }, }; ``` -### Getting a website visitor's country +- On the first line of our file, we import the `gridAwareAuto` function from the Grid-aware Websites Cloudflare Workers plugin. +- Later on in the file, we return the result of the `gridAwareAuto` function from our Worker. We can do this because the function is setup to return a response even if it encounters an error. +- When using the function we pass in the Cloudflare Workers parameters (`request`, `env`, and `ctx`) as well as an options object. Inside of this object we configure our worker to use our Electricity Maps API key. + +### Further configuration -We'll now add some code into that `fetch` function to: +The worker we have setup will run on our assigned route, but it will just return the original page regardless of the results of the grid-aware checks that it runs. The `gridAwareAuto` function accepts an options object as the fourth parameter. This allows for some configuration to be made to the implementation. Accepted options values are: -- Fetch the requested URL -- Check that the response is a HTML page -- Get the country of the visitor making the request -- Return that data in a response +| Option | Type | Default | Possible values | Description | +| ----------------- | ------------ | -------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `contentType` | String[] | `['text/html']` | Example: ['text/html', 'text/css'] | Defines the content types that should be processed | +| `ignoreRoutes` | String[] | `[]` | Example: ['/wp-admin', '/assets/js'] | A list of routes where grid-aware code should not be applied | +| `ignoreGawCookie` | String | `'gaw-ignore'` | "gaw-ignore" | A cookie that when present will result in grid-aware code being skipped | +| `locationType` | String | `'country'` | "country", "latlon" | Indicates the geolocation data to use for grid-aware checks. | +| `htmlChanges` | HTMLRewriter | `null` | See code example below | HTMLRewriter functions which can be used to make adjustments to the page when grid-aware changes need to be appplied. | +| `gawDataSource` | String | `'electricity maps'` | "electricity maps" | The data source to use from the core [Grid-aware Websites](https://github.com/thegreenwebfoundation/grid-aware-websites?tab=readme-ov-file#working-with-this-library) library. | +| `gawDataApiKey` | String | `''` | "xyz123" | The API key (if any) for the chosen data source. | +| `gawDataType` | String | `'power'` | "power", "carbon" | The data type to use from the core Grid-aware Websites library. | +| `kvCacheData` | Boolean | `false` | true, false | Indicate if grid data from the API should be cached in Cloudflare Workers KV for one hour. Read [setup instructions](#cache-grid-data-in-cloudflare-workers-kv). | +| `kvCachePage` | Boolean | `false` | true, false | Indicates if the modified grid-aware page should be cached in Cloudflare Workers KV for 24 hours. Read [setup instructions](#cache-grid-data-in-cloudflare-workers-kv) | +| `debug` | String | "none" | "none", "full", "headers", "logs" | Activates debug mode which outputs logs and returns additional response headers. | -Your Workers `fetch` function should look like this: +In this tutorial, we want to make a change to the page which will be applied when the grid-aware checks return a result that indicate the grid is dirtier than normal. The `gridAwareAuto` function will perform these checks for us, so we can use the `htmlChanges` option to pass it the changes we want applied. ```js +import gridAwareAuto from "@greenweb/gaw-plugin-cloudflare-workers"; + export default { async fetch(request, env, ctx) { - const powerBreakdown = new PowerBreakdown({ - mode: "low-carbon", - apiKey: env.EMAPS_API_KEY, + return gridAwareAuto(request, env, ctx, { + // Use this API key that has been saved as a secret + gawDataApiKey: env.EMAPS_API_KEY, + debug: "full", + // Make these changes to the web page using HTMLRewriter + htmlChanges: new HTMLRewriter().on("html", { + element(element) { + element.setAttribute("data-grid-aware", "true"); + }, + }), + }).on("h1", { + element(element) { + element.setInnerContent("Grid-aware changes applied"); + }, }); - - // First fetch the request - const response = await fetch(request.url); - // Then check if the request content type is HTML. - const contentType = response.headers.get("content-type"); - - // If the content is not HTML, then return the response without any changes. - if (!contentType || !contentType.includes("text/html")) { - return new Response(response.body, { - ...response, - }); - } - - // If the content type is HTML, we get the country the request came from - const location = await getLocation(request); - const { country } = location; - - // If the country data does not exist, then return the response without any changes. - if (!country) { - return new Response(response.body, { - ...response, - }); - } - - // We return a response - this is just to check that it works. We'll remove it soon. - return new Response(`Request from country code ${country}.`); }, }; ``` -Let's step through this code. - -**Create a new instance of PowerBreakdown** - -We first create a new instance of that class and specify that we want to use the 'low-carbon' data as the basis for our grid-aware checks, and set our API key as well. +In the example above, we use the `htmlChanges` option to pass a new instance of the HTMLRewriter to the function. We use the HTMLRewriter to add a data attribute (`data-grid-aware="true"`) to the HTML tag of the page, and to also change the content of the H1 element on the page. You can learn more about [Cloudflare's HTMLRewriter](https://developers.cloudflare.com/workers/runtime-apis/html-rewriter/) on their documentation site. -**Fetch the requested URL and check if it is a HTML page** - -We start by fetching the requested URL and checking its mime-type. We do this because, for this tutorial, we are only worried about modifying the content of the web page the user has requested. We _could_ apply this Cloudflare Worker to an API response instead, in which case we might check if the content type is `application/json`. - -We do these steps first because if the response is not a HTML page, then we want to return it back to the browser as soon as possible without running any other code. - -**Get the request's country** - -We then use the `gaw-plugin-cloudflare-workers` to get the location from the Cloudflare `request` object that we have access to via the `fetch` function. +We have also set the `debug: "full"` option for visibility during testing. ### Testing the code @@ -207,175 +174,7 @@ npx wrangler dev This command will download and run Cloudflare's Wrangler tool from NPM. If you have never used Wrangler before, it will open your web browser so you can login to your Cloudflare account. -Go to [http://localhost:8787](http://localhost:8787) to view your Worker. If everything works, you should see the `Request from country code ` output in the browser. - -### Checking if grid-awareness should be applied - -Now that we're getting the country of the request, we can use that value with the Grid-aware Websites library to get information about the fuel mix of that country's energy grid. The Grid-aware Websites library will then return a flag indicating if grid-aware changes should be made to the web page or not. - -Let's start by removing the response that returns the country code. - -```diff -- return new Response(`Request from country code ${country}.`) -``` - -Replace it with the code below: - -```js -const gridData = await powerBreakdown.check(country); - -// If there's an error getting data, return the web page without any modifications -if (gridData.status === "error") { - return new Response(response.body, { - ...response, - headers: { - ...response.headers, - }, - }); -} - -// Otherwise, return the grid data in the response -return new Response(`Grid data: ${JSON.stringify(gridData, null, 2)}`); -``` - -In the code above, we pass the request country into the `powerBreakdown.check` function. This function will return information about the energy grid we have requested data for, as well as a `gridAware` flag - a boolean value indicating whether grid-aware changes should be made to the website. Earlier in our code, we created a new instance of `PowerBreakdown` and specified that it use the "low-carbon" `mode`. This means that when the function runs a check against data from Electricity Maps it will refer to "low-carbon" energy data (that is renewables + nuclear). You can learn more about the modes available, and other options, on the [Getting started page](/grid-aware-websites/getting-started/#using-the-gridawarepower-function). - -Again, we can test that everything works so far by running the `npx wrangler dev` command in our project. Now, when you go to [http://localhost:8787](http://localhost:8787), you should see the contents of the `gridData` object in the browser. - -### Making changes to the web page if grid-aware changes are recommended - -If the `gridData.gridAware` flag returns as `true`, that indicates to us that the website visitor is in a region where more than 50% of the energy is being delivered by fossil fuel sources. - -In this case, we will want to make some changes to our web page to make it less power hungry on the users device. To do this quickly in a Cloudflare Worker, we can use the [HTMLRewriter API](https://developers.cloudflare.com/workers/runtime-apis/html-rewriter/). This API allows us to parse the HTML in the response, manipulate it, and then return the adjusted HTML back to the user in the response. - -If you're not familiar with the HTMLRewriter, you should check out the docs linked to above. In our Cloudflare Worker, we will write some code to remove an `iframe` that we have on the `/tools/grid-aware-websites` page of our website at the time of writing. - -Remove the `Grid data` response at the end of the Worker. - -```diff -- return new Response(`Grid data: ${JSON.stringify(gridData, null, 2)}`) -``` - -Replace it with the HTMLRewriter code below: - -```js -// If the grid aware flag is triggered (gridAware === true), then we'll return a modified HTML page to the user. -if (gridData.gridAware) { - const modifyHTML = new HTMLRewriter().on("iframe", { - element(element) { - element.remove(); - }, - }); - - // Transform the response using the HTMLRewriter API, and set appropriate headers. - let modifiedResponse = new Response(modifyHTML.transform(response).body, { - ...response, - headers: { - ...response.headers, - "Content-Type": "text/html;charset=UTF-8", - }, - }); - - return modifiedResponse; -} - -return new Response(response.body, { - ...response, - headers: { - ...response.headers, - }, -}); -``` - -The code above creates a new instance of the HTMLRewriter API that looks for and removes all `iframe` elements. You can chain these steps to make other changes to a web page, even adding content. - -Then, pass the response into the instance of the HTMLRewriter that we've just created, and return the modified response alongside additional headers. - -Otherwise, if the `gridData.gridAware` flag is returned as `false`, we just return the initial response without any modifications. - -## Putting it all together - -The final code in your Cloudflare Worker should look like this: - -```js -import { PowerBreakdown } from "@greenweb/grid-aware-websites"; -import { getLocation } from "@greenweb/gaw-plugin-cloudflare-workers"; - -export default { - async fetch(request, env, ctx) { - const powerBreakdown = new PowerBreakdown({ - mode: "low-carbon", - apiKey: env.EMAPS_API_KEY, - }); - - // First fetch the request - const response = await fetch(request.url); - // Then check if the request content type is HTML. - const contentType = response.headers.get("content-type"); - - // If the content is not HTML, then return the response without any changes. - if (!contentType || !contentType.includes("text/html")) { - return new Response(response.body, { - ...response, - }); - } - - // If the content type is HTML, we get the country the request came from - const location = await getLocation(request); - const { country } = location; - - // If the country data does not exist, then return the response without any changes. - if (!country) { - return new Response(response.body, { - ...response, - }); - } - - const gridData = await powerBreakdown.check(country); - - // If there's an error getting data, return the web page without any modifications - if (gridData.status === "error") { - return new Response(response.body, { - ...response, - headers: { - ...response.headers, - }, - }); - } - - // If the grid aware flag is triggered (gridAware === true), then we'll return a modified HTML page to the user. - if (gridData.gridAware) { - const modifyHTML = new HTMLRewriter().on("iframe", { - element(element) { - element.remove(); - }, - }); - - // Transform the response using the HTMLRewriter API, and set appropriate headers. - let modifiedResponse = new Response(modifyHTML.transform(response).body, { - ...response, - headers: { - ...response.headers, - "Content-Type": "text/html;charset=UTF-8", - }, - }); - - return modifiedResponse; - } - - return new Response(response.body, { - ...response, - headers: { - ...response.headers, - }, - }); - }, -}; -``` - -### Testing the completed worker - -Now, when you run `npx wrangler dev` and visit [http://localhost:8787](http://localhost:8787), you should see the web page showing the domain you configured at the start of this tutorial. You can then use the address bar to navigate to the path on which you configured the Worker code to execute. For us, that is `http://localhost:8787/tools/grid-aware-websites/`. +Go to [http://localhost:8787](http://localhost:8787) to view your Worker. In your terminal, you should see log outputs including information about the grid data that has been used to make perform the grid-aware checks. In order to test that the changes you have made are working, you'll need some way to test your Worker from different locations around the World. At the time of writing, there is no way to change the geolocation of a request within the Wrangler API. You can, instead, resort to one or more of the following workarounds: @@ -398,191 +197,11 @@ You'll then be prompted to select the Cloudflare account to add this to - it mus Now, you can run `npx wrangler deploy` in your terminal to deploy your Worker to production. ---- - -## Advanced - -In this next section, we will cover some more advanced functionality which can be added to the Cloudflare Worker we've just created. That is: - -- How to store and use data from Electricity Maps API to avoid making multiple requests for the same data. -- How to store and retrieve the modified version of the web page to avoid running HTMLRewriter each time. - -These are quality of life improvements to our code but while they are not critical, they may have performance and usage benefits especially for websites that received a lot of traffic. You can implement none, one, or both of these additional bits of functionality in your project. As such, each section below is written as individual components, so apologies in advance for any duplicated content. - -### Storing and reusing live grid data - -In the example below, we will update our Cloudflare Worker function to store the data response from the Electricity Maps API for one hour, and reuse that data for subsequent requests. To do this, we will use another Cloudflare product called [Workers KV](https://developers.cloudflare.com/kv/) - a low-latency key-value store. Workers KV has a generous free plan, however you [should be aware of the limitations](https://developers.cloudflare.com/kv/platform/limits/) when using it. - -We will use Workers KV to store the response from the Electricity Maps API in the following format - `key: zone ID, value: API data`. Then, the next time we have a request from a location with the same zone ID, we can use this stored data rather than making another outbound API request. - -To do this, we will use some helper functions that are part of the `greenweb/gaw-plugins-cloudflare-workers` library. These functions are - `saveDataToKv` and `fetchDataFromKv`. - -#### Storing data in Workers KV - -To setup saving grid data to Cloudflare Workers KV, you will need to create a KV namespace and bind it to your project. In your project, run the following command: - -```bash -npx wrangler kv namespace create GAW_DATA_KV -``` - -If created successfully, you will receive instructions in your terminal for how to update your project's `wrangler.json` configuration file so that it binds to the new KV store you've just created. - - - -Now, we are ready to start modifying our Workers code to put data in the `GAW_DATA_KV` store. In the `src/index.js` of your project, change the import statements at the start of the file, and then make the code changes below: - -```diff -- import { getLocation } from '@greenweb/gaw-plugin-cloudflare-workers'; -+ import { getLocation, saveDataToKv, fetchDataFromKv } from '@greenweb/gaw-plugin-cloudflare-workers'; -``` - -```diff - const gridData = await powerBreakdown.check(country); - - // If there's an error getting data, return the web page without any modifications - if (gridData.status === 'error') { - return new Response(response.body, { - ...response, - headers: { - ...response.headers, - }, - }); - } - -+ // Save the gridData to the KV store. By default, data is cached for 1 hour. -+ await saveDataToKv(env, country, JSON.stringify(gridData)) -``` - -Here, we import the two functions we'll need into our project. Then further down in our code, we use the `saveDataToKv` function to store the `gridData` JSON which we've got from the API. The key we use to store this is the `country` constant. By default, this data will be stored for one (1) hour before it expires. This duration can be shortened or extended by passing in an additional options parameter into the `saveDataToKv` function. [Learn more about how to do that in the plugin docs](/grid-aware-websites/plugins/cloudflare-workers/#store-grid-data). - -#### Fetching data in Workers KV - -Now that we are storing data for one hour, we can start to use it in our Worker to avoid making repeated outbound API calls. To do this, make the following changes to your code: - -```diff -- const gridData = await powerBreakdown.check(country); -+ // First check if the there's data for the country saved to KV -+ let gridData = await fetchDataFromKv(env, country); - -+ // If no cached data, fetch it from the API -+ if (!gridData) { -+ gridData = await powerBreakdown.check(country); -+ } - - // If there's an error getting data, return the web page without any modifications - if (gridData.status === 'error') { - return new Response(response.body, { - ...response, - headers: { - ...response.headers, - }, - }); - } - - // Save the gridData to the KV store. By default, data is cached for 1 hour. - await saveDataToKv(env, country, JSON.stringify(gridData)) -``` +Once your worker has been successfully deployed, it will be active and running on routes you configured earlier in this tutorial. You can find information about your deployed worker including logs, mertics, settings etc. via your Cloudflare dashboard. -Here, we first check the `GAW_DATA_KV` to see if there's information stored for based on the value of the `country` parameter we pass it. If there is not, only then do we use the `gridAwarePower` function to make a request to the Electricity Maps API for data about that country's grid. +## Real world examples -### Storing and reusing modified page content - -In the example below, we will update our Cloudflare Worker function to store the modified page content, and reuse that as the response for subsequent requests where it is needed. To do this, we will use another Cloudflare product called [Workers KV](https://developers.cloudflare.com/kv/) - a low-latency key-value store. Workers KV has a generous free plan, however you [should be aware of the limitations](https://developers.cloudflare.com/kv/platform/limits/) when using it. - -We will use Workers KV to store the HTML content that is modified using HTMLRewriter in the following format - `key: page URL, value: modified HTML content`. Then, the next time we need to return a modified version for that page, we can use this stored data rather than rerunning the HTMLRewriter again. - -To do this, we will use some helper functions that are part of the `greenweb/gaw-plugins-cloudflare-workers` library. These functions are - `savePageToKv` and `fetchPageFromKv`. - -#### Storing modified pages in Workers KV - -To setup saving modified page content to Cloudflare Workers KV, you will need to create a KV namespace and bind it to your project. In your project, run the following command: - -```bash -npx wrangler kv namespace create GAW_PAGE_KV -``` - -If created successfully, you will receive instructions in your terminal for how to update your project's `wrangler.json` configuration file so that it binds to the new KV store you've just created. - - - -Now, we are ready to start modifying our Workers code to put data in the `GAW_PAGE_KV` store. In the `src/index.js` of your project, change the import statements at the start of the file, and then make the code changes below: - -```diff -- import { getLocation } from '@greenweb/gaw-plugin-cloudflare-workers'; -+ import { getLocation, savePageToKv, fetchPageFromKv } from '@greenweb/gaw-plugin-cloudflare-workers'; -``` - -```diff - if (gridData.gridAware) { - const modifyHTML = new HTMLRewriter().on('iframe', { - element(element) { - element.remove(); - }, - }) - - // Transform the response using the HTMLRewriter API, and set appropriate headers. - let modifiedResponse = new Response(modifyHTML.transform(response).body, { - ...response, - headers: { - ...response.headers, - 'Content-Type': 'text/html;charset=UTF-8' - }, - }); - -+ // Store the modified response in the KV. By default, data is cached for 24 hours. -+ await savePageToKv(env, request.url, modifiedResponse.clone()); - - return modifiedResponse - } -``` - -Here, we import the two functions we'll need into our project. Then further down in our code, we use the `savePageToKv` function to store a clone of the modified response. The key we use to store this is the `request.url` value. By default, this data will be stored for 24 hours before it expires. This duration can be shortened or extended by passing in an additional options parameter into the `savePageToKv` function. [Learn more about how to do that in the plugin docs](/grid-aware-websites/plugins/cloudflare-workers/#store-modified-pages-in-workers-kv). - -#### Fetching and returning a modified page from KV - -Now that we are storing a modified version of the page for 24 hours, we can start to return it the next time we need to make grid-aware changes. This avoids us having to repeatedly run the HTMLRewriter API to return the same content. To do this, make the following changes to your code: - -```diff - if (gridData.gridAware) { - -+ // Check if the response is already stored in KV -+ const cachedResponse = await fetchPageFromKv(env, request.url); -+ -+ // If there's a cached response, return that -+ if (cachedResponse) { -+ return new Response(cachedResponse, { -+ ...response, -+ headers: { -+ ...response.headers, -+ 'Content-Type': 'text/html;charset=UTF-8', -+ } -+ }); -+ } - - const modifyHTML = new HTMLRewriter().on('iframe', { - element(element) { - element.remove(); - }, - }) - - // Transform the response using the HTMLRewriter API, and set appropriate headers. - let modifiedResponse = new Response(modifyHTML.transform(response).body, { - ...response, - headers: { - ...response.headers, - 'Content-Type': 'text/html;charset=UTF-8' - }, - }); - - // Store the modified response in the KV. By default, data is cached for 24 hours. - await savePageToKv(env, request.url, modifiedResponse.clone()); - - return modifiedResponse - } -``` +We use this function on our own Green Web Foundation Grid-aware Websites project page. -Here, we first check the `GAW_PAGE_KV` to see if there's a modified version of the page stored based on the value of the `request.url` parameter we pass it. If there is, we return the content from the KV. If there is not, only then do we use the HTMLRewriter API modify the page before returning it. +- View [the page](https://www.thegreenwebfoundation.org/tools/grid-aware-websites/) +- View [Cloudflare Workers source code](https://github.com/thegreenwebfoundation/gwf-gaw-cloudflare-worker/blob/main/src/index.js) From cda47b6c2ba2ace3e98a00c833b0046932a531c8 Mon Sep 17 00:00:00 2001 From: fershad <27988517+fershad@users.noreply.github.com> Date: Sat, 17 May 2025 18:37:36 +0800 Subject: [PATCH 04/18] simplify deployment target --- .../grid-aware-tutorial-cloudflare-workers.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md b/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md index 58e543a..2a025bb 100644 --- a/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md +++ b/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md @@ -67,12 +67,12 @@ The `src/index.js` file contains the Worker code that we will modify to add grid ### Setting routes -Before we start writing code, we'll first configure our worker to run on the route we want it to apply to. In our case, we want to apply this Worker to the `/tools/grid-aware-websites/` path on the `thegreenwebfoundation.org` domain. To do that, we include the following configuration inside of the `wrangler.json` file. _Note: You should replace the `pattern`, and `zone_name` with your own desired route._ +Before we start writing code, we'll first configure our worker to run on the route we want it to apply to. For demonstration purposes, we will show this grid-aware code being applied to our entire Green Web Foundation website. To do that, we include the following configuration inside of the `wrangler.json` file. _Note: You should replace the `pattern`, and `zone_name` with your own desired route._ ```json "routes": [ { - "pattern": "www.thegreenwebfoundation.org/tools/grid-aware-websites/", + "pattern": "www.thegreenwebfoundation.org/*", "zone_name": "thegreenwebfoundation.org" } ] @@ -151,16 +151,12 @@ export default { element.setAttribute("data-grid-aware", "true"); }, }), - }).on("h1", { - element(element) { - element.setInnerContent("Grid-aware changes applied"); - }, }); }, }; ``` -In the example above, we use the `htmlChanges` option to pass a new instance of the HTMLRewriter to the function. We use the HTMLRewriter to add a data attribute (`data-grid-aware="true"`) to the HTML tag of the page, and to also change the content of the H1 element on the page. You can learn more about [Cloudflare's HTMLRewriter](https://developers.cloudflare.com/workers/runtime-apis/html-rewriter/) on their documentation site. +In the example above, we use the `htmlChanges` option to pass a new instance of the HTMLRewriter to the function. We use the HTMLRewriter to add a data attribute (`data-grid-aware="true"`) to the HTML tag of the page. You can learn more about [Cloudflare's HTMLRewriter](https://developers.cloudflare.com/workers/runtime-apis/html-rewriter/) on their documentation site. We have also set the `debug: "full"` option for visibility during testing. From 31a7ca0a4b5e6a05bfa7060e76d5ac5749c3ead1 Mon Sep 17 00:00:00 2001 From: fershad <27988517+fershad@users.noreply.github.com> Date: Sat, 17 May 2025 18:44:10 +0800 Subject: [PATCH 05/18] Add "share" cta --- .../tutorials/grid-aware-tutorial-cloudflare-workers.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md b/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md index 2a025bb..95102bb 100644 --- a/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md +++ b/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md @@ -201,3 +201,7 @@ We use this function on our own Green Web Foundation Grid-aware Websites project - View [the page](https://www.thegreenwebfoundation.org/tools/grid-aware-websites/) - View [Cloudflare Workers source code](https://github.com/thegreenwebfoundation/gwf-gaw-cloudflare-worker/blob/main/src/index.js) + +### Share your grid-aware website + +If you've deployed Grid-aware Websites to your own site we'd love to hear from you! Share your experience with us using the [contact form](https://www.thegreenwebfoundation.org/support-form/?wpf2192_9=Another%20subject) via our website. From 3cd7bdd431644f4dc8fd46bde97c7589947f1e8e Mon Sep 17 00:00:00 2001 From: fershad <27988517+fershad@users.noreply.github.com> Date: Sat, 17 May 2025 18:44:18 +0800 Subject: [PATCH 06/18] add note on error handling --- .../tutorials/grid-aware-tutorial-cloudflare-workers.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md b/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md index 95102bb..4adf94b 100644 --- a/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md +++ b/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md @@ -179,6 +179,11 @@ In order to test that the changes you have made are working, you'll need some wa 3. Share your code with a colleague in another location and have them test it 4. Test in production. YOLO. (this suggestion is made in jest, we do not recommend it.) + + ## Deploying to production When you're ready, you can deploy your worker to run on your website for the actual path you've configured. From b0be4a8e07d1170d29a266a8be6bf4401c4633af Mon Sep 17 00:00:00 2001 From: fershad <27988517+fershad@users.noreply.github.com> Date: Sun, 25 May 2025 12:05:30 +0900 Subject: [PATCH 07/18] improve reability --- .../tutorials/grid-aware-tutorial-cloudflare-workers.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md b/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md index 4adf94b..92c7353 100644 --- a/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md +++ b/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md @@ -46,7 +46,10 @@ To begin using Grid-aware Websites on an existing website through Cloudflare Wor npm create cloudflare@latest -- --template thegreenwebfoundation/gaw-cloudflare-template ``` -This will clone the template onto your computer. You can replace `` with the name of your project. During the setup process you will be asked `Do you want to use git for version control?`, select `yes` and `Do you want to deploy your application?`, select `no`. +This will clone the template onto your computer. You can replace `` with the name of your project. During the setup process you will be asked: + +- `Do you want to use git for version control?`, select `yes` +- `Do you want to deploy your application?`, select `no` After the project is cloned, you can navigate to the destination folder. Your project will have a folder structure like this: From 68b99783ac24da9d028fcdb064c34a7d94e1ed15 Mon Sep 17 00:00:00 2001 From: fershad <27988517+fershad@users.noreply.github.com> Date: Tue, 3 Jun 2025 16:35:20 +0800 Subject: [PATCH 08/18] add redirects for carbon.txt 404 --- public/_redirects | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 public/_redirects diff --git a/public/_redirects b/public/_redirects new file mode 100644 index 0000000..4f6cb4a --- /dev/null +++ b/public/_redirects @@ -0,0 +1,2 @@ +/carbon.txt /carbon.txt 404 +/.well-known/carbon.txt /.well-known/carbon.txt 404 From f7c2ffd863af23b333f2b1c62737e2288376e317 Mon Sep 17 00:00:00 2001 From: fershad <27988517+fershad@users.noreply.github.com> Date: Tue, 3 Jun 2025 16:40:53 +0800 Subject: [PATCH 09/18] Update _redirects --- public/_redirects | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/_redirects b/public/_redirects index 4f6cb4a..718072d 100644 --- a/public/_redirects +++ b/public/_redirects @@ -1,2 +1,2 @@ -/carbon.txt /carbon.txt 404 -/.well-known/carbon.txt /.well-known/carbon.txt 404 +/carbon.txt / 404 +/.well-known/carbon.txt / 404 From 0c48e2f7cbab719ed58c5f27c50d1955f58633fd Mon Sep 17 00:00:00 2001 From: Bertrand Keller Date: Thu, 5 Jun 2025 13:28:04 +0200 Subject: [PATCH 10/18] Update carbon-txt-validator-architecture.md Typofix --- .../carbon-txt/explainer/carbon-txt-validator-architecture.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/docs/carbon-txt/explainer/carbon-txt-validator-architecture.md b/src/docs/carbon-txt/explainer/carbon-txt-validator-architecture.md index ec2d10d..9cd4cc4 100644 --- a/src/docs/carbon-txt/explainer/carbon-txt-validator-architecture.md +++ b/src/docs/carbon-txt/explainer/carbon-txt-validator-architecture.md @@ -29,7 +29,7 @@ The following diagrams use the [C4 model](https://c4model.com/) for describing s ![Context - a user sends a domain to lookup to the carbon.txt validator, which then fetches the carbon.txt file from that domain. The validator sends back validation results to the user.](https://carbon-txt-validator.readthedocs.io/en/latest/_images/1-a-context.jpg) -The context view demonstrate how we expect the the validator to be used by end-users. +The context view demonstrate how we expect the validator to be used by end-users. These might be people using the validator via the [carbon.txt website](https://carbontxt.org/tools/validator), as command line tool, or in a data pipeline, @@ -86,4 +86,4 @@ So, for a CSRD report that is written to fit pre-agreed standards, like being wr The In our CSRD Processor uses [Arelle](https://arelle.readthedocs.io/), an open source library for working with XBRL documents, to turn XBRL-formatted CSRD reports into datastructures that can be manipulated in Python, and that can be checked for the existence of specific data points. -The validation results are returned in API respones, or the output in CLI commands. +The validation results are returned in API responses, or the output in CLI commands. From b7b751c7cff7864278abf2a5681501d899bdf30d Mon Sep 17 00:00:00 2001 From: fershad <27988517+fershad@users.noreply.github.com> Date: Wed, 18 Jun 2025 16:45:46 +0800 Subject: [PATCH 11/18] fix horizontal scroll for table --- .../grid-aware-tutorial-cloudflare-workers.md | 106 +++++- src/styles/index.css | 359 +++++++++--------- 2 files changed, 275 insertions(+), 190 deletions(-) diff --git a/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md b/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md index 92c7353..9665558 100644 --- a/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md +++ b/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md @@ -123,19 +123,99 @@ export default { The worker we have setup will run on our assigned route, but it will just return the original page regardless of the results of the grid-aware checks that it runs. The `gridAwareAuto` function accepts an options object as the fourth parameter. This allows for some configuration to be made to the implementation. Accepted options values are: -| Option | Type | Default | Possible values | Description | -| ----------------- | ------------ | -------------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `contentType` | String[] | `['text/html']` | Example: ['text/html', 'text/css'] | Defines the content types that should be processed | -| `ignoreRoutes` | String[] | `[]` | Example: ['/wp-admin', '/assets/js'] | A list of routes where grid-aware code should not be applied | -| `ignoreGawCookie` | String | `'gaw-ignore'` | "gaw-ignore" | A cookie that when present will result in grid-aware code being skipped | -| `locationType` | String | `'country'` | "country", "latlon" | Indicates the geolocation data to use for grid-aware checks. | -| `htmlChanges` | HTMLRewriter | `null` | See code example below | HTMLRewriter functions which can be used to make adjustments to the page when grid-aware changes need to be appplied. | -| `gawDataSource` | String | `'electricity maps'` | "electricity maps" | The data source to use from the core [Grid-aware Websites](https://github.com/thegreenwebfoundation/grid-aware-websites?tab=readme-ov-file#working-with-this-library) library. | -| `gawDataApiKey` | String | `''` | "xyz123" | The API key (if any) for the chosen data source. | -| `gawDataType` | String | `'power'` | "power", "carbon" | The data type to use from the core Grid-aware Websites library. | -| `kvCacheData` | Boolean | `false` | true, false | Indicate if grid data from the API should be cached in Cloudflare Workers KV for one hour. Read [setup instructions](#cache-grid-data-in-cloudflare-workers-kv). | -| `kvCachePage` | Boolean | `false` | true, false | Indicates if the modified grid-aware page should be cached in Cloudflare Workers KV for 24 hours. Read [setup instructions](#cache-grid-data-in-cloudflare-workers-kv) | -| `debug` | String | "none" | "none", "full", "headers", "logs" | Activates debug mode which outputs logs and returns additional response headers. | +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Configuration options for the gridAwareAuto function
OptionTypeDefaultPossible valuesDescription
contentTypeString[]['text/html']Example: ['text/html', 'text/css']Defines the content types that should be processed
ignoreRoutesString[][]Example: ['/wp-admin', '/assets/js']A list of routes where grid-aware code should not be applied
ignoreGawCookieString'gaw-ignore'"gaw-ignore"A cookie that when present will result in grid-aware code being skipped
locationTypeString'country'"country", "latlon"Indicates the geolocation data to use for grid-aware checks.
htmlChangesHTMLRewriternullSee code example belowHTMLRewriter functions which can be used to make adjustments to the page when grid-aware changes need to be appplied.
gawDataSourceString'electricity maps'"electricity maps"The data source to use from the core Grid-aware Websites library.
gawDataApiKeyString''"xyz123"The API key (if any) for the chosen data source.
gawDataTypeString'power'"power", "carbon"The data type to use from the core Grid-aware Websites library.
kvCacheDataBooleanfalsetrue, falseIndicate if grid data from the API should be cached in Cloudflare Workers KV for one hour.
kvCachePageBooleanfalsetrue, falseIndicates if the modified grid-aware page should be cached in Cloudflare Workers KV for 24 hours.
debugString"none""none", "full", "headers", "logs"Activates debug mode which outputs logs and returns additional response headers.
+
In this tutorial, we want to make a change to the page which will be applied when the grid-aware checks return a result that indicate the grid is dirtier than normal. The `gridAwareAuto` function will perform these checks for us, so we can use the `htmlChanges` option to pass it the changes we want applied. diff --git a/src/styles/index.css b/src/styles/index.css index 7cffedd..d5f90a7 100644 --- a/src/styles/index.css +++ b/src/styles/index.css @@ -3,28 +3,28 @@ @tailwind utilities; .menu .menu-title { - opacity: 1; + opacity: 1; } .menu .menu-title > * { } .footer .footer-title { - /* @apply text-white; */ - opacity: 1; + /* @apply text-white; */ + opacity: 1; } .collapse:focus-within:not(.collapse-close) .collapse-content { - max-height: 9000px; + max-height: 9000px; } .collapse-arrow:focus-within:not(.collapse-close) .collapse-title:after { - transform: rotate(225deg); + transform: rotate(225deg); } .collapse:focus:not(.collapse-close) .collapse-title .collapse-title-text, .collapse:focus-within:not(.collapse-close) - .collapse-title - .collapse-title-text { - font-weight: 700; + .collapse-title + .collapse-title-text { + font-weight: 700; } h2[id], @@ -32,16 +32,16 @@ h3[id], h4[id], h5[id], h6[id] { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 0.75rem; - scroll-margin-block-start: 1rem; + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.75rem; + scroll-margin-block-start: 1rem; } .header-anchor { - display: grid; - place-items: center; + display: grid; + place-items: center; } h2[id] > .header-anchor > span, @@ -49,8 +49,8 @@ h3[id] > .header-anchor > span, h4[id] > .header-anchor > span, h5[id] > .header-anchor > span, h6[id] > .header-anchor > span { - opacity: 0.25; - display: inline-block; + opacity: 0.25; + display: inline-block; } h2[id]:hover span, @@ -58,7 +58,7 @@ h3[id]:hover span, h4[id]:hover span, h5[id]:hover span, h6[id]:hover span { - opacity: 1; + opacity: 1; } /** @@ -70,64 +70,64 @@ h6[id]:hover span { code[class*="language-"], pre[class*="language-"], code:not([class]) { - color: #f8f8f2; - background: none; - font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; - text-align: left; - white-space: pre; - word-spacing: normal; - word-break: normal; - word-wrap: normal; - line-height: 1.5; - font-size: inherit; - - -moz-tab-size: 4; - -o-tab-size: 4; - tab-size: 4; - - -webkit-hyphens: none; - -moz-hyphens: none; - -ms-hyphens: none; - hyphens: none; + color: #f8f8f2; + background: none; + font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + font-size: inherit; + + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; } /* Code blocks */ pre[class*="language-"] { - padding: 1em; - margin: 0.5em 0; - overflow: auto; - border-radius: 0.3em; + padding: 1em; + margin: 0.5em 0; + overflow: auto; + border-radius: 0.3em; } :not(pre) > code[class*="language-"], pre[class*="language-"] { - background: #2b2b2b; + background: #2b2b2b; } /* Inline code */ :not(pre) > code[class*="language-"] { - padding: 0.3em 0.4em; - border-radius: 0.3em; - white-space: normal; + padding: 0.3em 0.4em; + border-radius: 0.3em; + white-space: normal; } code:not([class]) { - padding: 0.15rem; - border-radius: 0.1em; - white-space: normal; - @apply bg-zinc-200; - color: #2b2b2b; + padding: 0.15rem; + border-radius: 0.1em; + white-space: normal; + @apply bg-zinc-200; + color: #2b2b2b; } .token.comment, .token.prolog, .token.doctype, .token.cdata { - color: #d4d0ab; + color: #d4d0ab; } .token.punctuation { - color: #fefefe; + color: #fefefe; } .token.property, @@ -135,12 +135,12 @@ code:not([class]) { .token.constant, .token.symbol, .token.deleted { - color: #ffa07a; + color: #ffa07a; } .token.boolean, .token.number { - color: #00e0e0; + color: #00e0e0; } .token.selector, @@ -149,7 +149,7 @@ code:not([class]) { .token.char, .token.builtin, .token.inserted { - color: #abe338; + color: #abe338; } .token.operator, @@ -158,201 +158,206 @@ code:not([class]) { .language-css .token.string, .style .token.string, .token.variable { - color: #00e0e0; + color: #00e0e0; } .token.atrule, .token.attr-value, .token.function { - color: #ffd700; + color: #ffd700; } .token.keyword { - color: #00e0e0; + color: #00e0e0; } .token.regex, .token.important { - color: #ffd700; + color: #ffd700; } .token.important, .token.bold { - font-weight: bold; + font-weight: bold; } .token.italic { - font-style: italic; + font-style: italic; } .token.entity { - cursor: help; + cursor: help; } @media screen and (-ms-high-contrast: active) { - code[class*="language-"], - pre[class*="language-"] { - color: windowText; - background: window; - } - - :not(pre) > code[class*="language-"], - pre[class*="language-"] { - background: window; - } - - .token.important { - background: highlight; - color: window; - font-weight: normal; - } - - .token.atrule, - .token.attr-value, - .token.function, - .token.keyword, - .token.operator, - .token.selector { - font-weight: bold; - } - - .token.attr-value, - .token.comment, - .token.doctype, - .token.function, - .token.keyword, - .token.operator, - .token.property, - .token.string { - color: highlight; - } - - .token.attr-value, - .token.url { - font-weight: normal; - } + code[class*="language-"], + pre[class*="language-"] { + color: windowText; + background: window; + } + + :not(pre) > code[class*="language-"], + pre[class*="language-"] { + background: window; + } + + .token.important { + background: highlight; + color: window; + font-weight: normal; + } + + .token.atrule, + .token.attr-value, + .token.function, + .token.keyword, + .token.operator, + .token.selector { + font-weight: bold; + } + + .token.attr-value, + .token.comment, + .token.doctype, + .token.function, + .token.keyword, + .token.operator, + .token.property, + .token.string { + color: highlight; + } + + .token.attr-value, + .token.url { + font-weight: normal; + } } seven-minute-tabs { - display: block; - padding: 0 1rem; - border-radius: 0.5rem; - /* margin: 0 -1rem 1rem; */ - border: 2px solid #111; - padding-bottom: 1rem; + display: block; + padding: 0 1rem; + border-radius: 0.5rem; + /* margin: 0 -1rem 1rem; */ + border: 2px solid #111; + padding-bottom: 1rem; } @media (prefers-color-scheme: dark) { - seven-minute-tabs { - border-color: #555; - } + seven-minute-tabs { + border-color: #555; + } } seven-minute-tabs [role="tablist"] { - list-style: none; - padding: 0 1rem; - margin: 0 -1rem; - font-size: 0.8125em; /* 13px /16 */ - line-height: 1.8; - text-align: left; + list-style: none; + padding: 0 1rem; + margin: 0 -1rem; + font-size: 0.8125em; /* 13px /16 */ + line-height: 1.8; + text-align: left; } seven-minute-tabs [role="tablist"] > * { - display: inline-block; - margin: 0 0.5rem; + display: inline-block; + margin: 0 0.5rem; } seven-minute-tabs [role="tablist"]:first-child { - margin-bottom: 0.5rem; + margin-bottom: 0.5rem; } seven-minute-tabs [role="tablist"]:last-child { - margin-top: 0.5rem; + margin-top: 0.5rem; } seven-minute-tabs [role="tab"] { - display: inline-block; - font-weight: 500; - padding: 0 0.65em; - margin: 0 0.1em; - text-decoration: none; - border-bottom: 5px solid transparent; - padding-top: 0.25rem; + display: inline-block; + font-weight: 500; + padding: 0 0.65em; + margin: 0 0.1em; + text-decoration: none; + border-bottom: 5px solid transparent; + padding-top: 0.25rem; } seven-minute-tabs [role="tab"][aria-selected="true"] { - border-color: hsl(var(--s)); - background-color: hsl(var(--p) / 0.25); - color: hsl(var(--pc)); + border-color: hsl(var(--s)); + background-color: hsl(var(--p) / 0.25); + color: hsl(var(--pc)); } /* Code samples in tabs */ seven-minute-tabs [role="tabpanel"] pre { - border-radius: 0; + border-radius: 0; } seven-minute-tabs [role="tabpanel"] pre:last-child { - margin-bottom: 0; + margin-bottom: 0; } .alert > p { - display: block; + display: block; } .roadmap-issue-contributor img { - display: inline-block; - height: 2.5ch; - border-radius: 50%; - margin-block: 0; + display: inline-block; + height: 2.5ch; + border-radius: 50%; + margin-block: 0; } @layer components { - .alert { - @apply border-4; - } - .alert-info { - @apply bg-info/5 border-info; - } - - .badge { - @apply font-normal; - } - .badge-secondary, - .badge-warning, - .badge-error { - @apply text-white; - } - - .btn { - @apply rounded-full border-0 no-underline font-bold px-5; - } - .btn-lg { - @apply px-7; - } - .btn-primary, - .btn-secondary { - @apply hover:text-white hover:bg-accent; - } - .btn-black { - @apply bg-black text-white; - @apply hover:text-black hover:bg-secondary; - } + .alert { + @apply border-4; + } + .alert-info { + @apply bg-info/5 border-info; + } + + .badge { + @apply font-normal; + } + .badge-secondary, + .badge-warning, + .badge-error { + @apply text-white; + } + + .btn { + @apply rounded-full border-0 no-underline font-bold px-5; + } + .btn-lg { + @apply px-7; + } + .btn-primary, + .btn-secondary { + @apply hover:text-white hover:bg-accent; + } + .btn-black { + @apply bg-black text-white; + @apply hover:text-black hover:bg-secondary; + } } .roadmap-list > li:nth-child(odd) { - @apply bg-neutral-100; - @apply border-y-neutral-300; - @apply border-y; - @apply border-solid; + @apply bg-neutral-100; + @apply border-y-neutral-300; + @apply border-y; + @apply border-solid; } .roadmap-list > li > h3:first-child { - margin-block-start: 1.25rem; + margin-block-start: 1.25rem; } .roadmap-list > li > div:last-child { - margin-block-end: 1.25rem; + margin-block-end: 1.25rem; } .language-toml .table { - display: initial; + display: initial; } aside + aside { - margin-top: 1rem; + margin-top: 1rem; +} + +.table-wrapper { + overflow: auto; + max-width: 100vw; } From b37a7bba476eb3545cd4ccca437d1a80a9c33621 Mon Sep 17 00:00:00 2001 From: fershad <27988517+fershad@users.noreply.github.com> Date: Thu, 10 Jul 2025 23:04:13 +0200 Subject: [PATCH 12/18] Add more config options to cf worker tutorial --- .../grid-aware-tutorial-cloudflare-workers.md | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md b/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md index 9665558..def6b61 100644 --- a/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md +++ b/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md @@ -213,6 +213,27 @@ The worker we have setup will run on our assigned route, but it will just return "none", "full", "headers", "logs" Activates debug mode which outputs logs and returns additional response headers. + + dev + Boolean + false + true, false + Enables development mode, which redirects requests to a local development server. + + + devConfig + Object + {} + {hostname: "localhost", port: "8080", protocol: "http"} + Configuration options for the development mode, including hostname, port, and protocol. + + + infoBarTarget + String + '' + Example: "header", "#info-container" + CSS selector for the element where the grid-aware info bar will be inserted. + @@ -227,6 +248,8 @@ export default { return gridAwareAuto(request, env, ctx, { // Use this API key that has been saved as a secret gawDataApiKey: env.EMAPS_API_KEY, + // A CSS selector for the element where the grid-aware info bar will be inserted. + infoBarTarget: "#gaw-info-bar", debug: "full", // Make these changes to the web page using HTMLRewriter htmlChanges: new HTMLRewriter().on("html", { From ed1a8c234e9960b70b655c5d0eb8785fac54d63c Mon Sep 17 00:00:00 2001 From: fershad <27988517+fershad@users.noreply.github.com> Date: Tue, 22 Jul 2025 17:03:50 +0800 Subject: [PATCH 13/18] update config table --- .../grid-aware-tutorial-cloudflare-workers.md | 256 +++++++++++------- 1 file changed, 156 insertions(+), 100 deletions(-) diff --git a/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md b/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md index def6b61..5c60396 100644 --- a/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md +++ b/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md @@ -136,105 +136,154 @@ The worker we have setup will run on our assigned route, but it will just return - - contentType - String[] - ['text/html'] - Example: ['text/html', 'text/css'] - Defines the content types that should be processed - - - ignoreRoutes - String[] - [] - Example: ['/wp-admin', '/assets/js'] - A list of routes where grid-aware code should not be applied - - - ignoreGawCookie - String - 'gaw-ignore' - "gaw-ignore" - A cookie that when present will result in grid-aware code being skipped - - - locationType - String - 'country' - "country", "latlon" - Indicates the geolocation data to use for grid-aware checks. - - - htmlChanges - HTMLRewriter - null - See code example below - HTMLRewriter functions which can be used to make adjustments to the page when grid-aware changes need to be appplied. - - - gawDataSource - String - 'electricity maps' - "electricity maps" - The data source to use from the core Grid-aware Websites library. - - - gawDataApiKey - String - '' - "xyz123" - The API key (if any) for the chosen data source. - - - gawDataType - String - 'power' - "power", "carbon" - The data type to use from the core Grid-aware Websites library. - - - kvCacheData - Boolean - false - true, false - Indicate if grid data from the API should be cached in Cloudflare Workers KV for one hour. - - - kvCachePage - Boolean - false - true, false - Indicates if the modified grid-aware page should be cached in Cloudflare Workers KV for 24 hours. - - - debug - String - "none" - "none", "full", "headers", "logs" - Activates debug mode which outputs logs and returns additional response headers. - - - dev - Boolean - false - true, false - Enables development mode, which redirects requests to a local development server. - - - devConfig - Object - {} - {hostname: "localhost", port: "8080", protocol: "http"} - Configuration options for the development mode, including hostname, port, and protocol. - - - infoBarTarget - String - '' - Example: "header", "#info-container" - CSS selector for the element where the grid-aware info bar will be inserted. - - + + contentType + String[] + ['text/html'] + Example: ['text/html', 'text/css'] + Defines the content types that should be processed + + + ignoreRoutes + String[] + [] + Example: ['/wp-admin', '/assets/js'] + A list of routes where grid-aware code should not be applied + + + ignoreGawCookie + String + 'gaw-ignore' + "gaw-ignore" + A cookie that when present will result in grid-aware code being skipped + + + userOptIn + Boolean + false + true, false + Allows developers to specify if users are required to opt-in to the grid-aware website experience + + + locationType + String + 'latlon' + "latlon", "country" + Type of location data to use + + + htmlChanges + Object + {} + {"low": HTMLRewriter, "moderate": HTMLRewriter, "high": HTMLRewriter} + An object to capture the different HTML changes that are applied at each different grid intesity level + + + htmlChanges.low + HTMLRewriter + null + Custom HTMLRewriter for page modification at low grid intensity level + + + + htmlChanges.moderate + HTMLRewriter + null + Custom HTMLRewriter for page modification at moderate grid intensity level + + + + htmlChanges.high + HTMLRewriter + null + Custom HTMLRewriter for page modification at high grid intensity level + + + + defaultView + String/null + null + null, "low", "moderate", "high" + Default view for the grid-aware website experience + + + gawDataApiKey + String + '' + "xyz123" + API key for the data source + + + infoBar + Object + {} + {target: "", version: "latest", learnMoreLink: "#", popoverText: ""} + Configuration for the info bar element + + + infoBar.target + String + '' + Example: "header", "#info-container" + Target element for the info bar + + + infoBar.version + String + 'latest' + "latest", "1.0.0" + Version of the info bar to use + + + infoBar.learnMoreLink + String + '#' + Example: "https://example.com/learn-more" + Link to learn more about the info bar + + + infoBar.popoverText + String + '' + Example: "This website adapts based on carbon intensity" + Provide a custom string of text to be used in the info bar popover element + + + kvCacheData + Boolean + false + true, false + Whether to cache grid data in KV store. Read setup instructions + + + kvCachePage + Boolean + false + true, false + Whether to cache modified pages in KV store. Read setup instructions + + + debug + String + "none" + "none", "full", "headers", "logs" + Activates debug mode which outputs logs and returns additional response headers + + + dev + Boolean + false + true, false + Whether to enable development mode + + + devConfig + Object + {} + {hostname: "localhost", port: "8080", protocol: "http"} + Configuration for development mode + + @@ -249,7 +298,14 @@ export default { // Use this API key that has been saved as a secret gawDataApiKey: env.EMAPS_API_KEY, // A CSS selector for the element where the grid-aware info bar will be inserted. - infoBarTarget: "#gaw-info-bar", + infoBar: { + target: "#gaw-info-bar", + learnMoreLink: + "https://www.thegreenwebfoundation.org/tools/grid-aware-websites/", + version: "latest", + popoverText: + "This website adapts based on your local electricity grid's carbon intensity", + }, debug: "full", // Make these changes to the web page using HTMLRewriter htmlChanges: new HTMLRewriter().on("html", { From b72b2d4d3d4e867cd005c2023aa59f962d740a0b Mon Sep 17 00:00:00 2001 From: fershad <27988517+fershad@users.noreply.github.com> Date: Tue, 22 Jul 2025 17:07:48 +0800 Subject: [PATCH 14/18] add branch case study --- src/_data/caseStudies.js | 64 +++++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 21 deletions(-) diff --git a/src/_data/caseStudies.js b/src/_data/caseStudies.js index 7558d21..4f9fde3 100644 --- a/src/_data/caseStudies.js +++ b/src/_data/caseStudies.js @@ -1,29 +1,51 @@ -const dev = process.env.NODE_ENV !== 'production' +const dev = process.env.NODE_ENV !== "production"; const caseStudies = async () => { - const { devData } = await import('../helpers/dev/caseStudies.js'); + const { devData } = await import("../helpers/dev/caseStudies.js"); - // if (dev) { - // return devData; - // } + // if (dev) { + // return devData; + // } - const co2js = await fetch('https://www.thegreenwebfoundation.org/wp-json/wp/v2/posts?per_page=100&categories=43,34').then(resp => resp.json()).then(data => data.filter(item => item.categories.includes(43) && item.categories.includes(34))) + const co2js = await fetch( + "https://www.thegreenwebfoundation.org/wp-json/wp/v2/posts?per_page=100&categories=43,34", + ) + .then((resp) => resp.json()) + .then((data) => + data.filter( + (item) => item.categories.includes(43) && item.categories.includes(34), + ), + ); - // This is temporary until we've got more case studies. Since they won't all live on our website, we need to figure out a way to get them all in one place. - const gaw = [ - { - featured_media: "https://browser-worker.fershad.workers.dev/?title=Making+this+website+respond+to+your+local+energy+grid&page=https://fershad.com/writing/making-this-website-grid-aware/", - title: { - rendered: "Making this website respond to your local energy grid" - }, - excerpt: { - rendered: "Fershad Irani covers the technical steps I've taken to make this website respond to a visitor's energy grid. It also touches on the design changes that happen as a result, and the performance and energy impacts of those changes." - }, - link: "https://fershad.com/writing/making-this-website-grid-aware/" - } - ] + // This is temporary until we've got more case studies. Since they won't all live on our website, we need to figure out a way to get them all in one place. + const gaw = [ + { + featured_media: + "https://branch.climateaction.tech/wp-content/uploads/2025/07/Branch-in-grid-aware-high-intensity-mode.jpg", + title: { + rendered: "Designing a Grid-Aware Branch", + }, + excerpt: { + rendered: + "Issue 9 of Branch arrives with a fresh site redesign. This new design builds on valuable feedback we’ve received from you, our community, and some brand-new ideas we’ve been exploring as part of Green Web Foundation’s Grid-aware Websites project.", + }, + link: "https://branch.climateaction.tech/issues/issue-9/designing-a-grid-aware-branch/", + }, + { + featured_media: + "https://browser-worker.fershad.workers.dev/?title=Making+this+website+respond+to+your+local+energy+grid&page=https://fershad.com/writing/making-this-website-grid-aware/", + title: { + rendered: "Making this website respond to your local energy grid", + }, + excerpt: { + rendered: + "Fershad Irani covers the technical steps I've taken to make this website respond to a visitor's energy grid. It also touches on the design changes that happen as a result, and the performance and energy impacts of those changes.", + }, + link: "https://fershad.com/writing/making-this-website-grid-aware/", + }, + ]; - return {co2js, gaw}; -} + return { co2js, gaw }; +}; module.exports = caseStudies; From 23c2e39f4609e78b735d31efd6403e4d0bcda702 Mon Sep 17 00:00:00 2001 From: fershad <27988517+fershad@users.noreply.github.com> Date: Wed, 23 Jul 2025 17:01:36 +0800 Subject: [PATCH 15/18] update media --- src/_data/caseStudies.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_data/caseStudies.js b/src/_data/caseStudies.js index 4f9fde3..bdc993e 100644 --- a/src/_data/caseStudies.js +++ b/src/_data/caseStudies.js @@ -21,7 +21,7 @@ const caseStudies = async () => { const gaw = [ { featured_media: - "https://branch.climateaction.tech/wp-content/uploads/2025/07/Branch-in-grid-aware-high-intensity-mode.jpg", + "https://branch.climateaction.tech/wp-content/uploads/2025/07/Branch-medium-intensity-mode-2048x1206.jpg", title: { rendered: "Designing a Grid-Aware Branch", }, From ff2110ef6bdf6324bcfa4c5ada065096872855ea Mon Sep 17 00:00:00 2001 From: fershad <27988517+fershad@users.noreply.github.com> Date: Thu, 24 Jul 2025 09:09:02 +0800 Subject: [PATCH 16/18] update emaps note --- .../tutorials/grid-aware-tutorial-cloudflare-workers.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md b/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md index 5c60396..9e28a0a 100644 --- a/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md +++ b/src/docs/grid-aware-websites/tutorials/grid-aware-tutorial-cloudflare-workers.md @@ -32,7 +32,14 @@ You should have: You should also be aware of the limits and pricing of Cloudflare Workers, available on the [Cloudflare website](https://developers.cloudflare.com/workers/platform/). -{% include 'partials/gaw-emaps-note.njk' %} +