chore: replace em dashes with hyphens, add no-em-dash rule to CLAUDE.md
Chart Release / chart (push) Successful in 20s
Server Deploy / deploy (push) Failing after 1m52s

This commit is contained in:
2026-09-10 09:18:55 +00:00
parent b36a696d0e
commit 6ee203f5e9
172 changed files with 860 additions and 856 deletions
+2 -2
View File
@@ -66,7 +66,7 @@ export function EmptyState({
/*
* `loading` matters rather than being decoration: the empty-state button is
* usually the one that creates the first of something, and without it a
* double click creates two. A link action takes neither there is no
* double click creates two. A link action takes neither - there is no
* pending state to show for a navigation.
*/
action?:
@@ -194,7 +194,7 @@ export function friendlyMessage(error: unknown): string {
// The backend writes its 4xx messages for an operator and they are usually
// the most specific thing available ("default steps cannot be edited",
// "vulnerability scanning is not licensed"). Keep them; only replace the
// ones that are a status code wearing a coat "HTTP 409", or the bare
// ones that are a status code wearing a coat - "HTTP 409", or the bare
// reason phrase fetch() falls back to when the body was empty.
const useful = raw && !/^HTTP \d{3}$/.test(raw) && !STATUS_TEXT.has(raw) ? raw : "";
+2 -2
View File
@@ -11,7 +11,7 @@ import { Modal } from "./Modal";
* the delete then fails, and it offers the same two buttons whether the action
* removes one key or an entire secret group.
*
* `requireTyped` is for the cases with no undo deleting a secret group, a
* `requireTyped` is for the cases with no undo - deleting a secret group, a
* step used by every workflow. Typing the name is not friction for its own
* sake: it is what stops a muscle-memory Enter from destroying something whose
* name the operator never actually read.
@@ -43,7 +43,7 @@ export function ConfirmDialog({
const [typed, setTyped] = useState("");
const inputId = useId();
// A reopened dialog must not carry the previous attempt's typing nor may
// A reopened dialog must not carry the previous attempt's typing - nor may
// a row reused for a different item stay armed with the name it matched
// before, which is why `requireTyped` is a dependency and not just `open`.
useEffect(() => {
+6 -6
View File
@@ -13,8 +13,8 @@ const stack: symbol[] = [];
/*
* The scroll lock is refcounted rather than saved and restored per dialog.
* Per-instance save/restore breaks when the outer dialog unmounts first which
* a dialog that navigates away on success does since the outer's cleanup then
* Per-instance save/restore breaks when the outer dialog unmounts first - which
* a dialog that navigates away on success does - since the outer's cleanup then
* releases the lock while the inner one is still on screen.
*/
let lockCount = 0;
@@ -27,7 +27,7 @@ let hidden: HTMLElement[] = [];
* the aria-hidden sweep below skips it. Exported because the toast layer needs
* the same exemption: a confirmation raised by a dialog is raised *before* that
* dialog closes, so a toast rendered inside the app tree would be inserted into
* a hidden subtree and never announced and un-hiding a live region later does
* a hidden subtree and never announced - and un-hiding a live region later does
* not replay what it missed.
*/
export const DIALOG_LAYER_ATTR = "data-vantage-dialog";
@@ -47,7 +47,7 @@ function lockScroll() {
/*
* aria-modal is a claim, not a mechanism. Portalled to the body, the
* app tree is a plain sibling of the dialog, so a screen reader's
* virtual cursor happily browses the page underneath which is the
* virtual cursor happily browses the page underneath - which is the
* exact thing the overlay exists to prevent. Hiding the siblings is
* what makes the claim true.
*/
@@ -105,7 +105,7 @@ export function Modal({
/*
* onClose is an inline arrow at every call site, so its identity changes on
* each render of the parent and a parent re-renders on every react-query
* each render of the parent - and a parent re-renders on every react-query
* poll and every mutation state flip. Holding it in a ref is what keeps the
* effect below keyed on `open` alone: depending on the handler tore the
* whole thing down and rebuilt it mid-interaction, which yanked focus out
@@ -154,7 +154,7 @@ export function Modal({
const last = items[items.length - 1];
const active = document.activeElement as HTMLElement | null;
// Focus can be outside the panel entirely on <body> after a
// Focus can be outside the panel entirely - on <body> after a
// control unmounted, or on the page behind. Pull it back rather
// than letting Tab continue out into content the overlay covers.
if (!active || !panel.contains(active)) {
+1 -1
View File
@@ -8,7 +8,7 @@ import { DIALOG_LAYER_ATTR } from "./Modal";
/*
* Mutations succeeded silently. Copying an install one-liner, generating a key,
* restarting a container, rotating the ESO token all of them changed
* restarting a container, rotating the ESO token - all of them changed
* something and said nothing, so the only way to know it worked was to watch
* for the list to redraw. Failures were worse: each page wired its own
* `onError: setError` into its own inline div, so an error raised by a modal