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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion .vale.ini
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,21 @@ StylesPath = .styles
MinAlertLevel = warning
Packages = Google, write-good

# Frcsoftware.Spelling (below) is our spellcheck rule; it replaces Vale's
# built-in Vale.Spelling so we control the dictionary in use. The 'Vale'
# style itself must stay in BasedOnStyles since it's what makes the Vocab
# below (and Vale.Terms/Vale.Avoid) take effect.
#
# To allow a word through spellcheck, or to enforce that a term always
# appears with a specific casing (e.g. "WPILib", not "wpilib"), add it to
# vale-accept-words.txt or src/data/glossary.ts (see setup-vale.ts).
Vocab = Frcsoftware

[*.mdx]
BasedOnStyles = Google, write-good
BasedOnStyles = Vale, Google, write-good, Frcsoftware

Vale.Spelling = NO
Frcsoftware.Spelling = YES

# Google style - selectively enable useful rules
Google.GenderBias = error
Expand Down
125 changes: 93 additions & 32 deletions scripts/setup-vale.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
import { createWriteStream, writeFileSync, existsSync, mkdirSync } from 'fs';
import { resolve, dirname } from 'path';
import {
createWriteStream,
writeFileSync,
readFileSync,
copyFileSync,
existsSync,
mkdirSync,
} from 'fs';
import { resolve } from 'path';
import { fileURLToPath } from 'url';
import { glossaryTerms } from '../src/data/glossary';
import { pipeline } from 'stream/promises';

// Update glossary terms
const ROOT = fileURLToPath(new URL('..', import.meta.url));
const OUTPUT = resolve(ROOT, '.styles/config/ignore/glossary.txt');
const OUTPUT_DIR = dirname(OUTPUT);

if (!existsSync(OUTPUT_DIR)) {
mkdirSync(OUTPUT_DIR, { recursive: true });
}

const terms = [...new Set(glossaryTerms.map(({ term }) => term))].sort((a, b) =>
a.toLowerCase().localeCompare(b.toLowerCase()),
);
const DICT_DIR = resolve(ROOT, '.styles/config/dictionaries');
const DIC_PATH = resolve(DICT_DIR, 'en_US.dic');
const AFF_PATH = resolve(DICT_DIR, 'en_US.aff');

const content = terms.join('\n') + '\n';
writeFileSync(OUTPUT, content);
const ACCEPT_WORDS_PATH = resolve(ROOT, 'vale-accept-words.txt');
const SPELLING_RULE_PATH = resolve(ROOT, 'scripts/vale-spelling-rule.yml');

console.log(`Wrote ${terms.length} glossary terms to ${OUTPUT}.`);
const VOCAB_DIR = resolve(ROOT, '.styles/config/vocabularies/Frcsoftware');
const STYLE_DIR = resolve(ROOT, '.styles/Frcsoftware');

// If not already present, download dictionary
async function downloadFile(url: string, path: string) {
const response = await fetch(url);
if (!response.ok)
Expand All @@ -38,20 +38,81 @@ async function downloadFile(url: string, path: string) {
await pipeline(response.body, fileStream);
}

const dictsToDownload = [
{
path: resolve(ROOT, '.styles/config/dictionaries/en_US.dic'),
url: 'https://raw.githubusercontent.com/LibreOffice/dictionaries/refs/tags/libreoffice-26.2.5.1/en/en_US.dic',
},
{
path: resolve(ROOT, '.styles/config/dictionaries/en_US.aff'),
url: 'https://raw.githubusercontent.com/LibreOffice/dictionaries/refs/tags/libreoffice-26.2.5.1/en/en_US.aff',
},
];

dictsToDownload.forEach((dict) => {
if (!existsSync(dict.path)) {
mkdirSync(dirname(dict.path), { recursive: true });
downloadFile(dict.url, dict.path);
// Vale's spelling check needs a Hunspell dictionary; we pin a specific
// LibreOffice release rather than relying on whatever Vale bundles.
async function ensureDictionaries() {
if (existsSync(DIC_PATH) && existsSync(AFF_PATH)) return;

mkdirSync(DICT_DIR, { recursive: true });
const version = 'libreoffice-26.2.5.1';
await Promise.all([
downloadFile(
`https://raw.githubusercontent.com/LibreOffice/dictionaries/refs/tags/${version}/en/en_US.dic`,
DIC_PATH,
),
downloadFile(
`https://raw.githubusercontent.com/LibreOffice/dictionaries/refs/tags/${version}/en/en_US.aff`,
AFF_PATH,
),
]);
}

// Hunspell .dic files are "<word count>\n<word>/<affix flags>\n...".
function loadDictionaryWords(): Set<string> {
const [, ...lines] = readFileSync(DIC_PATH, 'utf-8').split('\n');
const words = new Set<string>();
for (const line of lines) {
const word = (line.split('/')[0] ?? '').trim();
if (word) words.add(word.toLowerCase());
}
});
return words;
}

function escapeRegExp(value: string): string {
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

// Builds Vale's Vocab accept list (`.styles/config/vocabularies/Frcsoftware/accept.txt`)
// from two committed sources:
// - vale-accept-words.txt: plain jargon words, always accepted regardless of casing.
// - src/data/glossary.ts: terms with tooltip definitions. A term marked
// `caseSensitive: true` is enforced with its exact casing (e.g. "WPILib"),
// UNLESS its lowercase form is itself a real English word (e.g. "CAN"),
// in which case enforcing casing would flag ordinary prose ("can you...")
// as an error, so it falls back to case-insensitive acceptance.
function buildAcceptEntries(dictionaryWords: Set<string>): string[] {
const entries = new Set<string>();

for (const { term, caseSensitive } of glossaryTerms) {
const enforceCase =
caseSensitive && !dictionaryWords.has(term.toLowerCase());
entries.add(
enforceCase ? escapeRegExp(term) : `(?i)${escapeRegExp(term)}`,
);
}

const customWords = readFileSync(ACCEPT_WORDS_PATH, 'utf-8')
.split('\n')
.map((line) => line.trim())
.filter((line) => line && !line.startsWith('#'));
for (const word of customWords) {
entries.add(`(?i)${escapeRegExp(word)}`);
}

return [...entries].sort((a, b) =>
a.toLowerCase().localeCompare(b.toLowerCase()),
);
}

await ensureDictionaries();

const acceptEntries = buildAcceptEntries(loadDictionaryWords());
mkdirSync(VOCAB_DIR, { recursive: true });
writeFileSync(
resolve(VOCAB_DIR, 'accept.txt'),
acceptEntries.join('\n') + '\n',
);
console.log(`Wrote ${acceptEntries.length} accepted terms to Vale vocabulary.`);

mkdirSync(STYLE_DIR, { recursive: true });
copyFileSync(SPELLING_RULE_PATH, resolve(STYLE_DIR, 'Spelling.yml'));
5 changes: 5 additions & 0 deletions scripts/vale-spelling-rule.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
extends: spelling
message: "Did you really mean '%s'?"
level: warning
dictionaries:
- en_US
2 changes: 1 addition & 1 deletion src/content/docs/best-practices/git-usage.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ For advanced users, the command line offers more capabilities.

The `main` branch is where the working, tested version of the code lives during the build season.
When multiple programmers are working on different changes, creating separate development branches can help prevent merge conflicts.
For example, creating a seprate branch for vision code ensures that the code on the drivetrain branch isn't affected.
For example, creating a separate branch for vision code ensures that the code on the drivetrain branch isn't affected.
Making branches for each competition helps isolate fixes and ensures that code is still reviewed before merging to `main`.

To maintain a branch, you must stay up to date with `main`.
Expand Down
2 changes: 1 addition & 1 deletion src/content/docs/contribution/methodsOfContributing.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ You can get a local hosted version of the website to have a live preview of the

1. Open the repository in VS Code (doesn't matter which branch)
2. Toggle the bottom panel on if there isn't one with the shortcut `Ctrl + J`
3. Click the dropdown next to the + on the top righthand side of the bottom panel and click "Terminal"
3. Click the dropdown next to the + on the top right-hand side of the bottom panel and click "Terminal"
4. Run the command `pnpm install` to install all needed packages (FIRST TIME)
5. Run the command `pnpm dev` to start the development server
6. If everything went smoothly it should say its serving on something like `http://localhost:4321`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ You can find the download link [here](https://github.com/wpilibsuite/allwpilib/r
tools and not 2026 WPILib tools.
</Aside>

Once you have downloaded the 2027 WPILib tools, you can follow the instructions on how to set up the WPILib tools here [here](https://docs.wpilib.org/en/stable/docs/zero-to-robot/step-2/wpilib-setup.html).
Once you have downloaded the 2027 WPILib tools, you can follow the instructions on how to set up the WPILib tools [here](https://docs.wpilib.org/en/stable/docs/zero-to-robot/step-2/wpilib-setup.html).

The WPILib tool package also includes different programs that are useful for data logging, simulation, dashboards, and more.
We will use some of these tools in later stages.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ VS Code's layout is categorized into a few key regions:
The editor is star of the show and it is where you can view and edit files.
- Next, the **Panel** is at the bottom of the screen.
It has four main views, which are the Terminal, Problems, Output and Debug Console.
- Finally, the **Command Palette** is accesed through `Ctrl+Shift+P` / `Cmd+Shift+P` and it allows for you to search and run any command in VS Code.
- Finally, the **Command Palette** is accessed through `Ctrl+Shift+P` / `Cmd+Shift+P` and it allows for you to search and run any command in VS Code.

<ContentFigure
width="564"
Expand Down Expand Up @@ -82,7 +82,7 @@ A dot on a file's tab indicates **unsaved changes**; it turns into an "X" (close
## WPILib VS Code

For FRC, you use a separate version of VS Code developed by WPILib which is part of the WPILib installer.
If you already have VS Code installed on your computer, the WPILib VS Code installs seperately with the WPILib extension already installed and some settings changed.
If you already have VS Code installed on your computer, the WPILib VS Code installs separately with the WPILib extension already installed and some settings changed.

### WPILib Commands

Expand Down
2 changes: 1 addition & 1 deletion src/content/docs/learning-course/stage0/operators.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ codeRegionSources:
default: stage0/snippets/Operators.java
---

In Java, we use operators to change or compare the values of variables.
Java uses operators to change or compare the values of variables.
There four different types of operators are:

- Arithmetic Operators
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Then, create a new file in the `mechanisms` package named `Feeder.java`.
Define a class named `Feeder` that implements `Mechanism`, then add the following:

1. A private `TalonFX` motor controller object, named `motor`, with an ID of 5.
It is connected to a systemcore CANBus with ID 0.
It is connected to a Systemcore CANBus with ID 0.
2. A `feed()` command that sets the throttle of the motor to 0.75 forever.
3. An `intake()` command that sets the throttle of the motor to -1 forever.
4. An `outtake()` command that sets the throttle of the motor to 1 forever.
Expand Down Expand Up @@ -113,7 +113,7 @@ Create a file named `IntakeLauncher.java` in the `mechanisms` package.
Define a class named `IntakeLauncher` that implements `Mechanism`; then, add the following:

1. A private `TalonFX` motor controller object, named `motor`, with an ID of 4.
It is connected to a systemcore CANBus with ID 0.
It is connected to a Systemcore CANBus with ID 0.
2. A `shoot()` command that waits 2 seconds, then sets the throttle of the motor to 0.9 forever.
3. A `intake()` command that sets throttle to 0.8 forever.
4. A `outtake()` command that sets throttle to -0.8 forever.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ set of instructions (execute this task, then this, and finally this).
However, robots (and humans!) don't just execute a set of tasks and shut down.

Imagine your daily routine.
When an alarm clock rings, you turn it off and tumble out of bed.
When your belly rumbles, you walk to the fridge and get a snack.
When an alarm clock rings, you turn it off, then tumble out of bed.
When your belly rumbles, you walk to the fridge, then get a snack.
When the clock strikes 8 AM,
you open the front door to leave for school.

Expand Down Expand Up @@ -48,7 +48,7 @@ However, the structure of using `Command`s to represent behaviors misses an impo

Think back to the human example: you’re walking to the fridge, but the clock hits 8 AM before you get there.
You can't do both at once, since both actions require your arms and legs.
So, you stop and leave for school.
So, you stop, then leave for school.

Next, the robot.
If the X and Y buttons are held, the shooter and intake motors should both run.
Expand All @@ -65,7 +65,7 @@ the mechanisms they require - thus, mechanisms are also called "requirements".

An intake is a mechanism because your code controls its speed.
On the other hand,
an apriltag camera wouldn't be because your code only reads data from it, but doesn't update its state.
an AprilTag camera wouldn't be because your code only reads data from it, but doesn't update its state.

<Aside type="note">
In the human case, your arms and legs would be mechanisms, but your ears
Expand Down
10 changes: 7 additions & 3 deletions src/content/docs/resources/glossary.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@ title: Glossary

{/* This file is auto-generated by scripts/generate-glossary.ts. Do not edit directly. */}

**AprilTag**

A fiducial marker (similar to a QR code) that WPILib and vendor vision tools can detect to determine the position and orientation of a camera relative to the tag

**CAN**

Controller Area Network: typically yellow and green cable used to communicate with motor controllers and sensors, can be run in various topographies instead of each cable needing to connect to SystemCore
Controller Area Network: typically yellow and green cable used to communicate with motor controllers and sensors, can be run in various topographies instead of each cable needing to connect to Systemcore

**CI**

Expand Down Expand Up @@ -36,7 +40,7 @@ Power Distribution Hub

**PWM**

Pulse Width Modulation: A communication spec used to communicate with motor controllers and sensors, needs to connect back to SystemCore
Pulse Width Modulation: A communication spec used to communicate with motor controllers and sensors, needs to connect back to Systemcore

**Repository**

Expand All @@ -47,7 +51,7 @@ Repositories are just folders that contain files and subfolders, and they can be

Motor controller for REV motors

**SystemCore**
**Systemcore**

Main processor for robot code, contains various IO

Expand Down
16 changes: 12 additions & 4 deletions src/data/glossary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
export interface GlossaryTerm {
/**
* The word/abbreviation to match (case-insensitive)
* @example "SystemCore"
* @example "Systemcore"
*/
term: string;
/**
Expand Down Expand Up @@ -44,19 +44,20 @@ export const glossaryTerms: GlossaryTerm[] = [
definition: 'Motor controller for CTRE motors',
},
{
term: 'SystemCore',
term: 'Systemcore',
definition: 'Main processor for robot code, contains various IO',
caseSensitive: true,
},
{
term: 'CAN',
definition:
'Controller Area Network: typically yellow and green cable used to communicate with motor controllers and sensors, can be run in various topographies instead of each cable needing to connect to SystemCore',
'Controller Area Network: typically yellow and green cable used to communicate with motor controllers and sensors, can be run in various topographies instead of each cable needing to connect to Systemcore',
caseSensitive: true,
},
{
term: 'PWM',
definition:
'Pulse Width Modulation: A communication spec used to communicate with motor controllers and sensors, needs to connect back to SystemCore',
'Pulse Width Modulation: A communication spec used to communicate with motor controllers and sensors, needs to connect back to Systemcore',
},
{
term: 'Main Breaker',
Expand Down Expand Up @@ -93,6 +94,13 @@ export const glossaryTerms: GlossaryTerm[] = [
term: 'WPILib',
definition:
'WPILib is the standard software library and toolsuite provided for teams to write, test, and debug code for their FIRST® Robotics Competition and FIRST® Tech Challenge robots',
caseSensitive: true,
},
{
term: 'AprilTag',
definition:
'A fiducial marker (similar to a QR code) that WPILib and vendor vision tools can detect to determine the position and orientation of a camera relative to the tag',
caseSensitive: true,
},
{
term: 'GitHub',
Expand Down
10 changes: 10 additions & 0 deletions src/plugins/mdast-hast-data.d.ts
Comment thread
spacey-sooty marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type {} from 'mdast';

// mdast-util-to-hast reads these off node.data to control the hast output,
// but doesn't ship a `Data` augmentation that our remark plugins pick up.
declare module 'mdast' {
interface Data {
hName?: string;
hProperties?: Record<string, unknown>;
}
}
Loading