Skip to content
Merged
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
26 changes: 26 additions & 0 deletions .fallowrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"$schema": "https://raw.githubusercontent.com/fallow-rs/fallow/main/schema.json",
"minimumVersion": "3.32.0",
// Registered by URL string in components/ServiceWorker.tsx, invisible to the import graph.
"dynamicallyLoaded": ["public/sw.js"],
"duplicates": {
// Any clone fails: 0.01% is under one 5-line block of this repo.
"threshold": 0.01
},
"health": {
// Industry defaults (SonarQube's cognitive ceiling is 15). The big React components
// already exceed them; `fallow audit` reports only what a change makes worse.
"maxCyclomatic": 20,
"maxCognitive": 15,
// Pairs with "complexity-crap": "off". Off alone still titles cognitive findings
// "High CRAP score" in annotations (fallow 3.32); out of reach, CRAP never matches.
"maxCrap": 1000000
},
"rules": {
// Complexity advises; dead code and duplication block (their default severity).
"complexity-cyclomatic": "warn",
"complexity-cognitive": "warn",
// CRAP needs coverage data; this repo has none, so every branchy helper would read as 0% covered.
"complexity-crap": "off"
}
}
8 changes: 8 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
with:
# Full history: the fallow step diffs this branch against origin/main.
fetch-depth: 0

- name: Setup Node
uses: actions/setup-node@v4
Expand All @@ -43,6 +46,11 @@ jobs:
- name: Self-checks
run: npm run check

# Fails on dead code or duplication this branch introduces; complexity is only
# annotated (rules in .fallowrc.json). Findings show inline on the PR diff.
- name: Code quality (fallow)
run: npx fallow audit --base origin/main --format github-annotations

- name: Build static export
env:
# Build under the production subpath for parity with deploy.yml.
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ Type in a reference value and a tolerance, then touch parts one after another. E
part that settles gets a PASS or FAIL against the band, lands in a table, and can
optionally beep. The pass and fail tones are far apart in pitch and length so you can
tell them apart without looking at the screen, which is the point when you are sorting
a tray of resistors. Verdicts export to their own CSV, separate from the data log.
a tray of resistors. Verdicts export to their own CSV, separate from the data log but in the same units the
meter showed (`3.000,MOM`), so the two files line up.

Some honest limits. It covers resistance, diode, and capacitance only: voltage and
current have no overload reading on lifted probes, so there is no reliable way to tell
Expand Down
715 changes: 148 additions & 567 deletions app/page.tsx

Large diffs are not rendered by default.

28 changes: 21 additions & 7 deletions components/Controls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,27 @@
);
}

export function RecordButton({
recording,
canRecord,
onToggle,
}: {
recording: boolean;
canRecord: boolean;
onToggle: () => void;
}) {
return (
<ActionButton
onClick={onToggle}
icon={recording ? <Square size={12} /> : <Play size={12} />}
label={recording ? 'Stop Logging' : 'Start Logging'}
variant={recording ? 'danger' : 'success'}
disabled={!recording && !canRecord}
/>
);
}

export function Controls({

Check warning on line 62 in components/Controls.tsx

View workflow job for this annotation

GitHub Actions / verify

High cognitive complexity (moderate)

Function 'Controls' is hard to understand (cognitive: 22, threshold: 15). • Severity: moderate • Cyclomatic: 5 • Cognitive: 22 • Lines: 153 High cognitive complexity means deeply nested or interleaved logic. Consider flattening control flow or extracting helper functions.

Check warning on line 62 in components/Controls.tsx

View workflow job for this annotation

GitHub Actions / verify

High cognitive complexity (moderate)

Function 'Controls' is hard to understand (cognitive: 22, threshold: 15). • Severity: moderate • Cyclomatic: 5 • Cognitive: 22 • Lines: 153 High cognitive complexity means deeply nested or interleaved logic. Consider flattening control flow or extracting helper functions.
rangeMin,
rangeMax,
rangeInvalid,
Expand Down Expand Up @@ -171,13 +191,7 @@
</div>

<div className="space-y-1.5">
<ActionButton
onClick={onToggleRecord}
icon={recording ? <Square size={12} /> : <Play size={12} />}
label={recording ? 'Stop Logging' : 'Start Logging'}
variant={recording ? 'danger' : 'success'}
disabled={!recording && !canRecord}
/>
<RecordButton recording={recording} canRecord={canRecord} onToggle={onToggleRecord} />
<ActionButton
onClick={onExportCsv}
icon={<Download size={12} />}
Expand Down
23 changes: 9 additions & 14 deletions components/DataLog.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
'use client';

import { memo, useEffect, useMemo, useRef, useState } from 'react';
import { Search, Download, Square, Play, Trash2 } from 'lucide-react';
import { Search, Download, Trash2 } from 'lucide-react';
import { clsx } from 'clsx';
import { MODE_LABELS, SCALE, type Mode, type Reading, displayUnit } from '@/lib/parser';
import { MODES, UNITS, type SampleStore } from '@/lib/samples';
import { couldMatchIso, couldMatchValue, createIsoFormatter } from '@/lib/search';
import { StatisticsPanel, type SessionStats } from '@/components/StatisticsPanel';
import { ActionButton } from '@/components/Controls';
import { couldMatchIso, couldMatchValue, createIsoFormatter, localIso } from '@/lib/search';
import { StatisticsPanel } from '@/components/StatisticsPanel';
import type { SessionStats } from '@/lib/capture';
import { ActionButton, RecordButton } from '@/components/Controls';

/**
* Exactly what one rendered row needs, as SCALARS. Deliberately not the store's `Sample`
Expand All @@ -27,7 +28,7 @@

/** The row timestamp as the table and the CSV both render it. Formatted where it is used,
* never stored — at most 500 rows are visible, and the session may hold millions. */
const rowIso = (ts: number): string => new Date(ts).toISOString();
const rowIso = localIso;

/** The value as the meter reported it: base value back through its unit's factor, at the
* meter's own digit count. `toFixed`, never `String` — a number has no memory of trailing
Expand Down Expand Up @@ -74,7 +75,7 @@
// the CSV export); only rendering is windowed to the most recent N for performance.
const MAX_RENDERED = 500;

export function DataLog({

Check warning on line 78 in components/DataLog.tsx

View workflow job for this annotation

GitHub Actions / verify

High cognitive complexity (high)

Function 'DataLog' is hard to understand (cognitive: 30, threshold: 15). • Severity: high • Cyclomatic: 15 • Cognitive: 30 • Lines: 322 High cognitive complexity means deeply nested or interleaved logic. Consider flattening control flow or extracting helper functions.

Check warning on line 78 in components/DataLog.tsx

View workflow job for this annotation

GitHub Actions / verify

High cognitive complexity (high)

Function 'DataLog' is hard to understand (cognitive: 30, threshold: 15). • Severity: high • Cyclomatic: 15 • Cognitive: 30 • Lines: 322 High cognitive complexity means deeply nested or interleaved logic. Consider flattening control flow or extracting helper functions.
reading,
recording,
store,
Expand Down Expand Up @@ -154,7 +155,7 @@
// The price is that with a query active the total match count is unknowable, so the header
// reports the displayed count instead (see `truncated` below). With no query the count is
// exact, because it is just the store's.
const { visible, matched, truncated, scanMs } = useMemo(() => {

Check warning on line 158 in components/DataLog.tsx

View workflow job for this annotation

GitHub Actions / verify

High cognitive complexity (moderate)

Function '<arrow>' is hard to understand (cognitive: 20, threshold: 15). • Severity: moderate • Cyclomatic: 15 • Cognitive: 20 • Lines: 80 High cognitive complexity means deeply nested or interleaved logic. Consider flattening control flow or extracting helper functions.

Check warning on line 158 in components/DataLog.tsx

View workflow job for this annotation

GitHub Actions / verify

High cognitive complexity (moderate)

Function '<arrow>' is hard to understand (cognitive: 20, threshold: 15). • Severity: moderate • Cyclomatic: 15 • Cognitive: 20 • Lines: 80 High cognitive complexity means deeply nested or interleaved logic. Consider flattening control flow or extracting helper functions.
const q = appliedQuery.trim().toLowerCase();
const out: RowData[] = [];
const read = (i: number): RowData => {
Expand Down Expand Up @@ -194,12 +195,12 @@
// Skip a predicate entirely when its output cannot contain the query.
const testValue = couldMatchValue(q);
const testIso = couldMatchIso(q);
// `toISOString` emits upper-case T/Z; matching against the raw string saves a per-row
// `localIso` emits an upper-case T; matching against the raw string saves a per-row
// `toLowerCase()` allocation.
const upper = q.toUpperCase();
// ~3 samples share a wall-clock second, so the second-prefix is rebuilt a third as often.
// Formats a timestamp per scanned row, caching the per-second prefix. Defined in
// lib/search.ts so `scripts/check-search.mts` can assert it equals `toISOString()`.
// lib/search.ts so `scripts/check-search.mts` can assert it equals `localIso()`.
const isoOf = createIsoFormatter();

let scanned = 0;
Expand Down Expand Up @@ -376,13 +377,7 @@
<div>
<h3 className="mb-3 text-xs font-semibold text-fg">Logging</h3>
<div className="space-y-1.5">
<ActionButton
onClick={onToggleRecord}
icon={recording ? <Square size={12} /> : <Play size={12} />}
label={recording ? 'Stop Logging' : 'Start Logging'}
variant={recording ? 'danger' : 'success'}
disabled={!recording && !canRecord}
/>
<RecordButton recording={recording} canRecord={canRecord} onToggle={onToggleRecord} />
<ActionButton
onClick={onClear}
icon={<Trash2 size={12} />}
Expand Down
Loading
Loading