From 7c9da8b5dc7b8ab8bb0a30b576672f79e272776f Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Wed, 2 Sep 2026 10:09:13 +0200 Subject: [PATCH] Added community alphabet and symbols to docs --- .../app-web-docs/sidebarItemsGenerator.ts | 1 + .../src/docs/user/alphabet-and-symbols.md | 87 +++++++++++++++++++ packages/tool-meta-updater/src/metaUpdater.ts | 7 ++ .../src/updateGraphemeDefaultSpokenFormsMd.ts | 80 +++++++++++++++++ 4 files changed, 175 insertions(+) create mode 100644 packages/app-web-docs/src/docs/user/alphabet-and-symbols.md create mode 100644 packages/tool-meta-updater/src/updateGraphemeDefaultSpokenFormsMd.ts diff --git a/packages/app-web-docs/sidebarItemsGenerator.ts b/packages/app-web-docs/sidebarItemsGenerator.ts index 2eaac3dbd7..6635f0ce23 100644 --- a/packages/app-web-docs/sidebarItemsGenerator.ts +++ b/packages/app-web-docs/sidebarItemsGenerator.ts @@ -23,6 +23,7 @@ const items: (Group | string)[] = [ "Targets", "Destinations", "Paired delimiters", + "Alphabet and symbols", ], }, "Supported languages", diff --git a/packages/app-web-docs/src/docs/user/alphabet-and-symbols.md b/packages/app-web-docs/src/docs/user/alphabet-and-symbols.md new file mode 100644 index 0000000000..692eff96e6 --- /dev/null +++ b/packages/app-web-docs/src/docs/user/alphabet-and-symbols.md @@ -0,0 +1,87 @@ +# Alphabet and symbols + +Cursorless uses the [Talon Community](https://github.com/talonhub/community) alphabet, digits, and symbol spoken forms via the [`user.any_alphanumeric_key`](https://github.com/talonhub/community/blob/607c3415f5f29a5f75db6fe5648e37f514f62ac5/core/keys/keys.py#L71-L74) capture. + +## Alphabet + +| Character | Default spoken form | +| --------- | ------------------- | +| `a` | `air` | +| `b` | `bat` | +| `c` | `cap` | +| `d` | `drum` | +| `e` | `each` | +| `f` | `fine` | +| `g` | `gust` | +| `h` | `harp` | +| `i` | `sit` | +| `j` | `jury` | +| `k` | `crunch` | +| `l` | `look` | +| `m` | `made` | +| `n` | `near` | +| `o` | `odd` | +| `p` | `pit` | +| `q` | `quench` | +| `r` | `red` | +| `s` | `sun` | +| `t` | `trap` | +| `u` | `urge` | +| `v` | `vest` | +| `w` | `whale` | +| `x` | `plex` | +| `y` | `yank` | +| `z` | `zip` | + +## Symbols + +| Character | Default spoken form | +| --------- | ------------------- | +| `.` | `dot` | +| `,` | `comma` | +| `;` | `semicolon` | +| `:` | `colon` | +| `!` | `bang` | +| `*` | `asterisk` | +| `@` | `at sign` | +| `&` | `ampersand` | +| `?` | `question` | +| `/` | `slash` | +| `\` | `backslash` | +| `-` | `dash` | +| `=` | `equals` | +| `+` | `plus` | +| `~` | `tilde` | +| `_` | `underscore` | +| `#` | `hash` | +| `%` | `percent` | +| `^` | `caret` | +| `\|` | `pipe` | +| `$` | `dollar` | +| `£` | `pound` | +| `'` | `quote` | +| `"` | `double quote` | +| `` ` `` | `back tick` | +| `(` | `paren` | +| `)` | `right paren` | +| `{` | `brace` | +| `}` | `right brace` | +| `[` | `square` | +| `]` | `right square` | +| `<` | `angle` | +| `>` | `right angle` | + +## Digits + +| Character | Default spoken form | +| --------- | ------------------- | +| `0` | `zero` | +| `1` | `one` | +| `2` | `two` | +| `3` | `three` | +| `4` | `four` | +| `5` | `five` | +| `6` | `six` | +| `7` | `seven` | +| `8` | `eight` | +| `9` | `nine` | diff --git a/packages/tool-meta-updater/src/metaUpdater.ts b/packages/tool-meta-updater/src/metaUpdater.ts index 794c862f6c..631e04e674 100644 --- a/packages/tool-meta-updater/src/metaUpdater.ts +++ b/packages/tool-meta-updater/src/metaUpdater.ts @@ -9,6 +9,7 @@ import type { ScopeTypeType } from "@cursorless/lib-common"; import { actionReferenceGroups, actionReferences, + graphemeDefaultSpokenForms, languageReferences, modifierReferenceGroups, modifierReferences, @@ -22,6 +23,7 @@ import type { RecordedTestPath } from "@cursorless/lib-node-common"; import type { Context } from "./Context"; import { createScopeFixtureGroups } from "./scopeFixtureGroups"; import { textFormat } from "./textFormat"; +import { updateGraphemeDefaultSpokenFormsMd } from "./updateGraphemeDefaultSpokenFormsMd"; import { updateLanguageMdx } from "./updateLanguageMdx"; import { updatePackageJson } from "./updatePackageJson"; import { updatePairedDelimitersMd } from "./updatePairedDelimitersMd"; @@ -78,6 +80,11 @@ export const updater = async (workspaceDir: string) => { null, pairedDelimiterReferences, ), + [`${userDir}/alphabet-and-symbols.md`]: + updateGraphemeDefaultSpokenFormsMd.bind( + null, + graphemeDefaultSpokenForms, + ), [`${userDir}/actions/README.md`]: updateReferenceReadmeMd.bind( null, "Actions", diff --git a/packages/tool-meta-updater/src/updateGraphemeDefaultSpokenFormsMd.ts b/packages/tool-meta-updater/src/updateGraphemeDefaultSpokenFormsMd.ts new file mode 100644 index 0000000000..db2a52f70b --- /dev/null +++ b/packages/tool-meta-updater/src/updateGraphemeDefaultSpokenFormsMd.ts @@ -0,0 +1,80 @@ +import type { FormatPluginFnOptions } from "@pnpm/meta-updater"; + +const HEADERS = ["Character", "Default spoken form"] as const; + +export function updateGraphemeDefaultSpokenFormsMd( + entries: Readonly>, + actual: string | null, + options: FormatPluginFnOptions, +): string | null { + if (options.manifest.name !== "@cursorless/app-web-docs") { + return null; + } + + const alphabet: string[][] = []; + const digits: string[][] = []; + const symbols: string[][] = []; + + for (const [character, spokenForm] of Object.entries(entries)) { + const row = [code(character), code(spokenForm)]; + + if (/^[a-z]$/u.test(character)) { + alphabet.push(row); + } else if (/^[0-9]$/u.test(character)) { + digits.push(row); + } else { + symbols.push(row); + } + } + + return [ + "# Alphabet and symbols", + "", + "Cursorless uses the [Talon Community](https://github.com/talonhub/community) alphabet, digits, and symbol spoken forms via the [`user.any_alphanumeric_key`](https://github.com/talonhub/community/blob/607c3415f5f29a5f75db6fe5648e37f514f62ac5/core/keys/keys.py#L71-L74) capture.", + "", + "## Alphabet", + "", + ...formatTable(HEADERS, alphabet), + "", + "## Symbols", + "", + ...formatTable(HEADERS, symbols), + "", + "## Digits", + "", + ...formatTable(HEADERS, digits), + "", + ].join("\n"); +} + +function code(value: string): string { + const escaped = value.replaceAll("|", String.raw`\|`); + + if (escaped.includes("`")) { + return `\`\` ${escaped} \`\``; + } + + return `\`${escaped}\``; +} + +function formatTable( + headers: readonly string[], + rows: readonly string[][], +): string[] { + const columnWidths = headers.map((header, index) => + Math.max(header.length, ...rows.map((row) => row[index].length)), + ); + const formatRow = (row: readonly string[]) => + `| ${row + .map( + (cell, index) => + `${cell}${" ".repeat(columnWidths[index] - cell.length)}`, + ) + .join(" | ")} |`; + + return [ + formatRow(headers), + formatRow(columnWidths.map((width) => "-".repeat(width))), + ...rows.map(formatRow), + ]; +}