Skip to content

Commit

Permalink
feat(docs): Add dynamic hardware list component.
Browse files Browse the repository at this point in the history
  • Loading branch information
think4tomorrow committed Sep 11, 2021
1 parent 6938c68 commit 2274a85
Show file tree
Hide file tree
Showing 7 changed files with 214 additions and 64 deletions.
56 changes: 0 additions & 56 deletions docs/docs/hardware.md

This file was deleted.

26 changes: 26 additions & 0 deletions docs/docs/hardware.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
title: Supported Hardware
sidebar_label: Supported Hardware
---

import HardwareList from "@site/src/components/hardware-list";
import Metadata from "@site/src/data/hardware-metadata.json";

With the solid technical foundation of Zephyr™ RTOS, ZMK can support a wide diversity of hardware targets.
That being said, there are currently only a few specific [boards](/docs/faq#what-is-a-board)/[shields](faq.md#what-is-a-shield) that have been implemented and tested by the ZMK contributors.

<HardwareList items={Metadata} />

## Other Hardware

In addition to the basic keyboard functionality, there is some initial support for additional keyboard hardware:

- Encoders
- Displays
- RGB Underglow

Until detailed documentation is available, feel free to ask questions about how these are supported in the [Discord server](https://zmk.dev/community/discord/invite).

## Contributing

If you'd like to add support for a new keyboard shield, head over to the [New Keyboard Shield](development/new-shield.md) documentation.
2 changes: 1 addition & 1 deletion docs/docusaurus.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module.exports = {
plugins: [
path.resolve(__dirname, "src/docusaurus-tree-sitter-plugin"),
path.resolve(__dirname, "src/hardware-metadata-collection-plugin"),
path.resolve(__dirname, "src/hardware-schema-typescript-plugin")
path.resolve(__dirname, "src/hardware-schema-typescript-plugin"),
],
themeConfig: {
colorMode: {
Expand Down
176 changes: 176 additions & 0 deletions docs/src/components/hardware-list.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import React from "react";

import {
Board,
HardwareMetadata,
Interconnect,
Shield,
} from "../hardware-metadata";

interface HardwareListProps {
items: HardwareMetadata[];
}

type ElementOrString = JSX.Element | string;

function itemHasMultiple(item: HardwareMetadata) {
return (
(item.type == "board" || item.type == "shield") &&
(item.siblings?.length ?? 1) > 1
);
}

function itemIds(item: HardwareMetadata) {
if (item.type == "board" || item.type == "shield") {
let nodes = (item.siblings ?? [item.id])
.map<ElementOrString>((id) => <code key={id}>{id}</code>)
.reduce(
(prev, curr, index) => [...prev, index > 0 ? ", " : "", curr],
[] as ElementOrString[]
);
return <span key={item.id}>{nodes}</span>;
} else {
return <code key={item.id}>{item.id}</code>;
}
}

const TYPE_LABELS: Record<
HardwareMetadata["type"],
Record<"singular" | "plural", string>
> = {
board: { plural: "Boards: ", singular: "Board: " },
shield: { singular: "Shield: ", plural: "Shields: " },
interconnect: { singular: "Interconnect: ", plural: "Interconnects: " },
};

function HardwareLineItem({ item }: { item: HardwareMetadata }) {
return (
<li>
<a href={item.url}>{item.name}</a> (
{TYPE_LABELS[item.type][itemHasMultiple(item) ? "plural" : "singular"]}{" "}
{itemIds(item)})
</li>
);
}

interface InterconnectDetails {
interconnect?: Interconnect;
boards: Board[];
shields: Shield[];
}

function mapInterconnect({
interconnect,
boards,
shields,
}: InterconnectDetails) {
if (!interconnect) {
return null;
}

return (
<div key={interconnect.id}>
<h4>{interconnect.name} Keyboards</h4>
{interconnect.description && <p>{interconnect.description}</p>}
<h5>Boards</h5>
<ul>
{boards.map((s) => (
<HardwareLineItem key={s.id} item={s} />
))}
</ul>
<h5>Shields</h5>
<ul>
{shields.map((s) => (
<HardwareLineItem key={s.id} item={s} />
))}
</ul>
</div>
);
}

interface GroupedMetadata {
onboard: Board[];
interconnects: Record<string, InterconnectDetails>;
}

function groupedBoard(agg: GroupedMetadata, board: Board) {
if (board.features?.includes("keys")) {
agg.onboard.push(board);
} else if (board.exposes) {
board.exposes.forEach((element) => {
let ic = agg.interconnects[element] ?? {
boards: [],
shields: [],
};
ic.boards.push(board);
agg.interconnects[element] = ic;
});
} else {
console.error("Board without keys or interconnect");
}

return agg;
}

function groupedShield(agg: GroupedMetadata, shield: Shield) {
shield.requires.forEach((id) => {
let ic = agg.interconnects[id] ?? { boards: [], shields: [] };
ic.shields.push(shield);
agg.interconnects[id] = ic;
});

return agg;
}

function groupedInterconnect(agg: GroupedMetadata, item: Interconnect) {
let ic = agg.interconnects[item.id] ?? { boards: [], shields: [] };
ic.interconnect = item;
agg.interconnects[item.id] = ic;

return agg;
}

function HardwareList({ items }: HardwareListProps) {
let grouped = items.reduce<GroupedMetadata>(
(agg, hm) => {
switch (hm.type) {
case "board":
return groupedBoard(agg, hm);
case "shield":
return groupedShield(agg, hm);
case "interconnect":
return groupedInterconnect(agg, hm);
}
},
{ onboard: [] as Board[], interconnects: {} }
);

return (
<>
<h2>Keyboards</h2>
<h3>Onboard Controller Boards</h3>
<p>
Keyboards with onboard controllers are single PCBs that contain all the
components of a keyboard, including the controller chip, switch
footprints, etc.
</p>
<ul>
{grouped["onboard"]
.sort((a, b) => a.name.localeCompare(b.name))
.map((s) => (
<HardwareLineItem key={s.id} item={s} />
))}
</ul>
<h3>Composite Boards</h3>
<p>
Composite keyboards are composed of two main PCBs: a small controller
board with exposed pads, and a larger keyboard PCB (a shield, in ZMK
lingo) with switch footprints and location a where the controller is
added.
</p>
{Object.values(grouped.interconnects).map(mapInterconnect)}
</>
);
}

export default HardwareList;
12 changes: 8 additions & 4 deletions docs/src/hardware-metadata-collection-plugin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@ var PrebuildPlugin = require("prebuild-webpack-plugin");
const fs = require("fs");
const yaml = require("js-yaml");
const glob = require("glob");
const { compile, compileFromFile } = require('json-schema-to-typescript');

function generateHardwareMetadataAggregate() {
glob("../app/boards/**/*.zmk.yml", (error, files) => {
const aggregated = files.flatMap(f => yaml.safeLoadAll(fs.readFileSync(f, "utf8")));
fs.writeFileSync("src/data/hardware-metadata.json", JSON.stringify(aggregated));
const aggregated = files.flatMap((f) =>
yaml.safeLoadAll(fs.readFileSync(f, "utf8"))
);
fs.writeFileSync(
"src/data/hardware-metadata.json",
JSON.stringify(aggregated)
);
});
}

Expand All @@ -30,4 +34,4 @@ module.exports = function () {
};
},
};
};
};
4 changes: 2 additions & 2 deletions docs/src/hardware-schema-typescript-plugin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

var PrebuildPlugin = require("prebuild-webpack-plugin");
const fs = require("fs");
const { compileFromFile } = require('json-schema-to-typescript');
const { compileFromFile } = require("json-schema-to-typescript");

async function generateHardwareMetadataTypescript() {
const ts = await compileFromFile("../schema/hardware-metadata.schema.json");
Expand All @@ -26,4 +26,4 @@ module.exports = function () {
};
},
};
};
};
2 changes: 1 addition & 1 deletion docs/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"extends": "@tsconfig/docusaurus/tsconfig.json",
"include": ["src/"]
}
}

0 comments on commit 2274a85

Please sign in to comment.