feat(boards): unify homepage grid with flasher + clickable cards
Three changes, all from finally having working RAK4631 firmware and
noticing the homepage grid was hand-coded, stale, and disconnected from
the flasher.
1. RAK4631 display name. Silkscreen + RAK marketing call this module
'RAK4631'; 'WisBlock' is the ecosystem brand. The site was showing
'RAK WisBlock 4631', which matched neither the product name nor the
firmware commit message ('RAK4631 WisBlock bring-up'). It also
contradicted Uf2DownloadFlow.svelte, which already correctly told
users 'A USB drive named RAK4631 should appear'. Renamed to 'RAK4631
WisBlock' in boards.ts. Stable identifiers (board id, asset filename
pattern, driveName lookup) unchanged so v1.4.0 artifacts still
resolve.
2. Homepage grid is now data-driven from boards.ts. Previously a
hand-coded list that had silently drifted: missing the LilyGo T-Beam
classic, LilyGo T-Beam S3 Supreme, and the Heltec V3 UART variant.
Now maps over the same `boards` array the flasher uses, sorted
case-insensitively by display name. Adding a board in one place
updates both pages. `badgeText()` and `badgeColor()` lifted out of
BoardSelector.svelte into boards.ts so the label and accent color
have a single source of truth too.
3. Homepage cards are now links — clicking a card takes you to
/flash?board=<id> with that board pre-selected. Flasher.svelte reads
the param on mount and sets selectedBoard if the id resolves, so the
user lands on the version-picker step directly.
The flasher's BoardSelector keeps its richer UI (search filter,
MCU-family grouping) — only the simpler homepage grid was unified.
This commit is contained in:
4 files changed
+54
-54
No files matched your search
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { BoardDef } from "../lib/types";
|
import type { BoardDef } from "../lib/types";
|
||||||
import { boards } from "../lib/boards";
|
import { boards, badgeText, badgeColor } from "../lib/boards";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
selected: BoardDef | null;
|
selected: BoardDef | null;
|
||||||
@@ -11,28 +11,6 @@
|
|||||||
|
|
||||||
let query = $state("");
|
let query = $state("");
|
||||||
|
|
||||||
function badgeText(board: BoardDef): string {
|
|
||||||
switch (board.flashMethod) {
|
|
||||||
case "web-serial":
|
|
||||||
return "Web Flash";
|
|
||||||
case "uf2":
|
|
||||||
return "UF2 Download";
|
|
||||||
case "download-only":
|
|
||||||
return "CLI Only";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function badgeColor(board: BoardDef): string {
|
|
||||||
switch (board.flashMethod) {
|
|
||||||
case "web-serial":
|
|
||||||
return "text-accent";
|
|
||||||
case "uf2":
|
|
||||||
return "text-info";
|
|
||||||
case "download-only":
|
|
||||||
return "text-warn";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function matches(board: BoardDef, q: string): boolean {
|
function matches(board: BoardDef, q: string): boolean {
|
||||||
const needle = q.trim().toLowerCase();
|
const needle = q.trim().toLowerCase();
|
||||||
if (!needle) return true;
|
if (!needle) return true;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { BoardDef, Release } from "../lib/types";
|
import type { BoardDef, Release } from "../lib/types";
|
||||||
|
import { boards } from "../lib/boards";
|
||||||
import { fetchReleases } from "../lib/github";
|
import { fetchReleases } from "../lib/github";
|
||||||
import BoardSelector from "./BoardSelector.svelte";
|
import BoardSelector from "./BoardSelector.svelte";
|
||||||
import VersionSelector from "./VersionSelector.svelte";
|
import VersionSelector from "./VersionSelector.svelte";
|
||||||
@@ -29,6 +30,14 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Pre-select board from ?board=<id> deep link (e.g. from the homepage grid)
|
||||||
|
$effect(() => {
|
||||||
|
const boardId = new URLSearchParams(window.location.search).get("board");
|
||||||
|
if (!boardId) return;
|
||||||
|
const found = boards.find((b) => b.id === boardId);
|
||||||
|
if (found) selectedBoard = found;
|
||||||
|
});
|
||||||
|
|
||||||
function handleBoardSelect(board: BoardDef) {
|
function handleBoardSelect(board: BoardDef) {
|
||||||
selectedBoard = board;
|
selectedBoard = board;
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-1
@@ -67,12 +67,14 @@ export const boards: BoardDef[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "rak_wisblock_4631",
|
id: "rak_wisblock_4631",
|
||||||
name: "RAK WisBlock 4631",
|
name: "RAK4631 WisBlock",
|
||||||
mcu: "nRF52840",
|
mcu: "nRF52840",
|
||||||
radio: "SX1262",
|
radio: "SX1262",
|
||||||
family: "nrf52840",
|
family: "nrf52840",
|
||||||
flashMethod: "uf2",
|
flashMethod: "uf2",
|
||||||
assetPattern: "donglora-rak_wisblock_4631-v{version}.uf2",
|
assetPattern: "donglora-rak_wisblock_4631-v{version}.uf2",
|
||||||
|
notes:
|
||||||
|
"RAK4631 Core (nRF52840 + SX1262) on a RAK19007 base, optionally with the RAK1921 OLED. Flashes over native USB CDC-ACM via the Adafruit nRF52 UF2 bootloader — double-tap RESET to expose the RAK4631 drive.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "wio_tracker_l1",
|
id: "wio_tracker_l1",
|
||||||
@@ -98,6 +100,28 @@ export function getAssetName(board: BoardDef, version: string): string {
|
|||||||
return board.assetPattern.replace("{version}", version);
|
return board.assetPattern.replace("{version}", version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function badgeText(board: BoardDef): string {
|
||||||
|
switch (board.flashMethod) {
|
||||||
|
case "web-serial":
|
||||||
|
return "Web Flash";
|
||||||
|
case "uf2":
|
||||||
|
return "UF2 Download";
|
||||||
|
case "download-only":
|
||||||
|
return "CLI Only";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function badgeColor(board: BoardDef): string {
|
||||||
|
switch (board.flashMethod) {
|
||||||
|
case "web-serial":
|
||||||
|
return "text-accent";
|
||||||
|
case "uf2":
|
||||||
|
return "text-info";
|
||||||
|
case "download-only":
|
||||||
|
return "text-warn";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
import type { ReleaseAsset } from "./types";
|
import type { ReleaseAsset } from "./types";
|
||||||
|
|
||||||
export interface AssetUrls {
|
export interface AssetUrls {
|
||||||
|
|||||||
+19
-30
@@ -1,6 +1,11 @@
|
|||||||
---
|
---
|
||||||
import Base from "../layouts/Base.astro";
|
import Base from "../layouts/Base.astro";
|
||||||
import { Code } from "astro:components";
|
import { Code } from "astro:components";
|
||||||
|
import { boards, badgeText, badgeColor } from "../lib/boards";
|
||||||
|
|
||||||
|
const sortedBoards = [...boards].sort((a, b) =>
|
||||||
|
a.name.localeCompare(b.name, "en", { sensitivity: "base" }),
|
||||||
|
);
|
||||||
|
|
||||||
const quickStartCode = `import donglora as dl
|
const quickStartCode = `import donglora as dl
|
||||||
|
|
||||||
@@ -224,36 +229,20 @@ your code ──► client lib ──┘</pre>
|
|||||||
<section class="py-12">
|
<section class="py-12">
|
||||||
<h2 class="font-mono text-2xl font-bold mb-6 text-text">Supported Boards</h2>
|
<h2 class="font-mono text-2xl font-bold mb-6 text-text">Supported Boards</h2>
|
||||||
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
<div class="bg-bg-card border border-border p-5 rounded">
|
{
|
||||||
<h3 class="font-mono font-medium text-text mb-2">ELECROW ThinkNode-M2</h3>
|
sortedBoards.map((board) => (
|
||||||
<p class="text-sm text-text-muted">ESP32-S3 · SX1262</p>
|
<a
|
||||||
<p class="text-xs text-warn mt-2 font-mono">CLI Only</p>
|
href={`/flash?board=${board.id}`}
|
||||||
</div>
|
class="block bg-bg-card border border-border p-5 rounded transition-colors hover:border-border-bright hover:bg-bg-elevated no-underline"
|
||||||
<div class="bg-bg-card border border-border p-5 rounded">
|
>
|
||||||
<h3 class="font-mono font-medium text-text mb-2">Heltec V3</h3>
|
<h3 class="font-mono font-medium text-text mb-2">{board.name}</h3>
|
||||||
<p class="text-sm text-text-muted">ESP32-S3 · SX1262</p>
|
<p class="text-sm text-text-muted">
|
||||||
<p class="text-xs text-accent mt-2 font-mono">Web Flash</p>
|
{board.mcu} · {board.radio}
|
||||||
</div>
|
</p>
|
||||||
<div class="bg-bg-card border border-border p-5 rounded">
|
<p class={`text-xs ${badgeColor(board)} mt-2 font-mono`}>{badgeText(board)}</p>
|
||||||
<h3 class="font-mono font-medium text-text mb-2">Heltec V4</h3>
|
</a>
|
||||||
<p class="text-sm text-text-muted">ESP32-S3 · SX1262</p>
|
))
|
||||||
<p class="text-xs text-accent mt-2 font-mono">Web Flash</p>
|
}
|
||||||
</div>
|
|
||||||
<div class="bg-bg-card border border-border p-5 rounded">
|
|
||||||
<h3 class="font-mono font-medium text-text mb-2">RAK WisBlock 4631</h3>
|
|
||||||
<p class="text-sm text-text-muted">nRF52840 · SX1262</p>
|
|
||||||
<p class="text-xs text-info mt-2 font-mono">UF2 Download</p>
|
|
||||||
</div>
|
|
||||||
<div class="bg-bg-card border border-border p-5 rounded">
|
|
||||||
<h3 class="font-mono font-medium text-text mb-2">Wio Tracker L1</h3>
|
|
||||||
<p class="text-sm text-text-muted">nRF52840 · SX1262</p>
|
|
||||||
<p class="text-xs text-info mt-2 font-mono">UF2 Download</p>
|
|
||||||
</div>
|
|
||||||
<div class="bg-bg-card border border-border p-5 rounded">
|
|
||||||
<h3 class="font-mono font-medium text-text mb-2">Waveshare RP2040-LoRa</h3>
|
|
||||||
<p class="text-sm text-text-muted">RP2040 · SX1262</p>
|
|
||||||
<p class="text-xs text-info mt-2 font-mono">UF2 Download</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user