style: remove em/en dashes repo-wide, add CLAUDE.md rule against them (#890)

Em dashes (—) and en dashes (–) had spread across comments, docs, tests,
and a few UI strings, reading as AI-generated boilerplate rather than
house style. Replaced each with punctuation matching its context: colon
for explanatory clauses, comma for asides, plain hyphen for numeric/legal
ranges (e.g. "21-23§"), "to"/"till" for date ranges, parentheses for
paired-dash asides. messages/en.json and messages/sv.json were fixed by
hand together to keep sv/en in sync.

Left untouched where the dash is the functional subject rather than
decorative punctuation: date-range-parser.ts's separator regex,
charset-repair.ts's CP1252 byte-mapping table (and its test), the SIE
encoding mojibake docs, generic-csv.ts's minus-sign normalizer, the
agent system-prompt files that already instruct against em dashes, and
a golden iXBRL test fixture compared byte-for-byte.

Also fixes two bugs surfaced along the way: an off-by-one in
ApiKeysPanel's scope-label split (a leftover from an earlier partial
pass), and a charset-repair test that had lost the literal en-dash it
exists to verify.

Regenerated the agent atom seed migration (skills:generate) since 27
SKILL.md files changed. Added a CLAUDE.md rule against em/en dashes,
with an explicit carve-out for the functional-dash cases above.

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-07-04 15:58:06 +02:00
committed by GitHub
co-authored by Claude Sonnet 5
parent a5154ee884
commit ec27228a8e
1588 changed files with 28388 additions and 10890 deletions
+2 -2
View File
@@ -4,7 +4,7 @@ import { cn } from '@/lib/utils'
interface Props {
// When true, render the dot. The parent owns the "did this value come from
// AI extraction?" question — usually by comparing the current input value
// AI extraction?" question: usually by comparing the current input value
// against the originally-prefilled value, and clearing the flag once the
// user edits the field.
active: boolean
@@ -19,7 +19,7 @@ interface Props {
// Tiny indicator that a form field's value was pre-filled by the AI
// extraction pipeline. Sits to the right of the field label or inside the
// input's right padding. Fades when the user edits the field — see how
// input's right padding. Fades when the user edits the field: see how
// supplier-invoices/new wires it.
//
// Design: a small filled dot, success color when the extraction succeeded.
+1 -1
View File
@@ -2,7 +2,7 @@ import * as React from "react"
import { cn } from "@/lib/utils"
/**
* DataList — unified list surface (one bordered container, hairline rows).
* DataList: unified list surface (one bordered container, hairline rows).
*
* Replaces the per-row Card pattern across Granskning, Transactions, and
* similar list pages. Visual contract is flat-with-hairlines per CLAUDE.md:
+1 -1
View File
@@ -117,7 +117,7 @@ interface UseDestructiveConfirmReturn {
/**
* Hook that returns a `confirm()` function as a drop-in replacement for `window.confirm()`.
* Returns `Promise<boolean>` — true if user confirms, false if they cancel.
* Returns `Promise<boolean>`: true if user confirms, false if they cancel.
*
* Usage:
* ```
+1 -1
View File
@@ -33,7 +33,7 @@ interface EmptyStateProps {
}
/**
* EmptyState — friendly placeholder shown when there is no data.
* EmptyState: friendly placeholder shown when there is no data.
*/
export function EmptyState({
icon: Icon,
+2 -2
View File
@@ -12,7 +12,7 @@ interface Props {
// Inline status indicator for an AI extraction in flight. Sits next to a
// freshly-attached document in upload flows. Five visual states map to the
// useDocumentExtraction hook output. "disabled" renders nothing — the free
// useDocumentExtraction hook output. "disabled" renders nothing: the free
// tier has no AI extraction and shouldn't see scary UI.
//
// Copy is intentionally short and Swedish. The status changes inline; the
@@ -73,7 +73,7 @@ export default function ExtractionStatus({ status, elapsedMs = 0, className }: P
)}
>
<AlertCircle className="h-3.5 w-3.5" />
Kunde inte läsa automatiskt — fyll i manuellt
Kunde inte läsa automatiskt: fyll i manuellt
</span>
)
}
+1 -1
View File
@@ -16,7 +16,7 @@ const TooltipContent = React.forwardRef<
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
>(({ className, sideOffset = 4, ...props }, ref) => (
// Portal the content to document.body so the tooltip is never clipped by an
// ancestor with overflow (e.g. a scrollable DialogContent — the send-invoice
// ancestor with overflow (e.g. a scrollable DialogContent: the send-invoice
// and journal-review dialogs use overflow-y-auto, which otherwise crops it).
<TooltipPrimitive.Portal>
<TooltipPrimitive.Content
+1 -1
View File
@@ -18,7 +18,7 @@ interface RetentionNoticeProps {
*
* Swedish Bokföringslagen (BFL) 7 kap. 2§ requires räkenskapsinformation
* to be retained for 7 years. Accounted is the system of record, so deleting
* a company or an account does not remove the underlying data — it only
* a company or an account does not remove the underlying data: it only
* hides it from the UI and anonymizes PII where applicable.
*/
export function RetentionNotice({ variant, className }: RetentionNoticeProps) {
+1 -1
View File
@@ -7,7 +7,7 @@ import type { ReactNode } from 'react'
interface SortableRowProps<T> {
/** Identity used by Reorder to track this item across reorders. */
value: T
/** The row's existing markup — rendered untouched beside the drag handle. */
/** The row's existing markup: rendered untouched beside the drag handle. */
children: ReactNode
/** Localized aria-label for the drag handle. */
handleLabel: string
+1 -1
View File
@@ -45,7 +45,7 @@ const TabsContent = React.forwardRef<
className={cn(
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
// When a consumer passes `forceMount`, Radix keeps inactive panels in the
// DOM (present is always true) but does NOT hide them itself — hide them
// DOM (present is always true) but does NOT hide them itself: hide them
// here. No-op for non-forceMount tabs, which unmount inactive content.
"data-[state=inactive]:hidden",
className