~/skills/csharp
C#
for desktop tools that need to be fast, local and a bit strict.
CHC Review Studio is C# and WPF. the idea it is built on: a finding should always point at the exact text that caused it.
used in CHC Review Studio
c# · illustrative model
review desk
findings 2
nothing selected
The ferry left at dawn, carrying nine passengers and a crate of bees. Marta had counted eleven tickets, but she wrote nine in the log anyway. By noon the bees were louder than the engine, and nobody mentioned it. The captain insisted the route was only forty minutes long; it took six hours. Nobody on board was surprised, least of all the bees.
drag across text, or tab to a phrase: arrows move, space selects, shift extends, c creates a finding.tap a phrase to select it, or press and hold to pick your own words.
inspector document
- document
- ferry.txt (made up)
- characters
- 345
- words
- 63
- phrases
- 10
- findings
- 2 (1 open)
- high, open
- 0
- click a phrase, a finding or this panel's title to see its properties.
pick some text, then create a finding.2 findings · 1 open · 0 high
← → move between phrasesspace selectshift + arrows extendc create findingesc clear
how this works
an illustrative C# model written for this page, not CHC Review Studio source. the desk beside it runs in the browser on a made-up paragraph; its rules (evidence must sit inside the text, open counts by severity) mirror Review.cs and are tested against it.
// illustrative c# model for this page: not CHC Review Studio source.// the desk on the right follows these types; a finding always carries the exact text that caused it.using System;using System.Collections.Generic;using System.Linq; public enum Severity { Low, Medium, High }public enum Status { Open, Resolved } /// <summary>an exact span of the source text: the proof a finding points at.</summary>public readonly record struct Anchor(int Start, int Length){ public string Quote(string source) => source.Substring(Start, Length);} public sealed record Finding(int Id, Anchor Evidence, string Comment, Severity Severity, Status Status){ public Finding Resolve() => this with { Status = Status.Resolved };} public sealed class Review{ private readonly List<Finding> _findings = new(); private int _nextId = 1; public Review(string source) => Source = source; public string Source { get; } public IReadOnlyList<Finding> Findings => _findings; public Finding Add(Anchor evidence, string comment, Severity severity) { if (evidence.Start < 0 || evidence.Length <= 0 || evidence.Start + evidence.Length > Source.Length) throw new ArgumentOutOfRangeException(nameof(evidence), "evidence must sit inside the source"); var finding = new Finding(_nextId++, evidence, comment, severity, Status.Open); _findings.Add(finding); return finding; } public void Resolve(int id) { var at = _findings.FindIndex(f => f.Id == id); if (at >= 0) _findings[at] = _findings[at].Resolve(); } public int OpenCount(Severity atLeast) => _findings.Count(f => f.Status == Status.Open && f.Severity >= atLeast);}