Product design

Wispr Flow: keep control of the words after you speak

Wispr Flow turns speech into text where you are writing, then handles formatting and clear corrections. It helps people move from an idea to a readable draft while keeping a way to retrieve the original transcript.

Break down the problem and try your own design and implementation first. Then explore the reasoning to check states, edge cases, trade-offs, and ways to test your approach. The guide offers a reasoning path, not a complete implementation.

01
CODE EXERCISEAdvanced

How can selected edits preserve their original coordinates?

Scenario

Design a selective-acceptance module for your own dictation tool. An upstream process supplies a raw transcript and edit proposals. Users select some proposals and preview new text. Handle deterministic edit ranges only: speech recognition, intent detection and proposal generation are outside scope.

Input contract

  1. The raw transcript is a readonly array of at most 1,000,000 tokens. A token is an upstream-defined nonempty string that may contain Chinese text, emoji or spaces. Do not tokenize again or use characters, bytes or JavaScript string length as positions. An empty array is valid.
  2. There are at most 100,000 proposals. Each is a non-null object with a unique nonempty string id, integer start/end, and a replacement array of nonempty strings. Require 0 ≤ start ≤ end ≤ original length. Replacement items across all proposals total at most 1,000,000. Ignore other fields.
  3. Every half-open [start,end) refers to the original array. Equal endpoints mean insertion; an empty replacement means deletion. An empty insertion is a valid no-op. Proposals may arrive unordered. Accepting an earlier proposal never changes the coordinates of another.
  4. Nonempty ranges cannot overlap, but may touch. An insertion cannot lie strictly inside a nonempty range; it may be at a boundary. At most one insertion is allowed at each position, even if their content matches. Boundary insertion appears after the replacement ending there and before the replacement starting there.
  5. The accepted collection contains proposal IDs; unknown or repeated IDs are errors. Validate all proposals, including unselected ones. Any structure, type, range, limit or conflict error produces no partial result and leaves original input unchanged. For structure and field errors, report the input path, such as a raw-token index, proposal index/field or accepted-item index. Only a conflict between proposals additionally requires both proposal indices and IDs. Do not invent an ID when malformed input has none. An empty accepted collection returns a preview equal in content to the original.

Start with this example

Original tokens are ["周一","下午","三点","开会"] (Monday, afternoon, three o’clock, meet). A replaces [0,1) with ["周二"] (Tuesday); B inserts ["大约"] (about) at [2,2); C replaces [2,3) with ["四点"] (four o’clock). Work out the results for A/C, A/B/C, and B/C after A is deselected. Now add D over [1,3): explain why the entire input fails even if D is not selected.

Deliverables

Give the validation order, a range-index design and a strategy for assembling the preview. Work through the three valid accepted collections and the conflict introduced by D. Propose at least four invariants and boundary tests covering an empty original array, insertion at the end, touching replacements, duplicate insertions and emoji tokens. Explain time and space complexity for n original tokens, p proposals and r output tokens. Provide reasoning and verification methods only, without a full implementation or line-by-line pseudocode.

Explore the reasoning
  1. Anchor coordinates to the original. Acceptance decides which proposals contribute to output, not their coordinate base. Build every preview from the same raw array and current accepted collection, rather than editing text and applying stale offsets. Preserving input also keeps a failed operation from disturbing the previous preview.
  2. Validate the complete set before selection. Check objects, string arrays, integer bounds, unique IDs and limits; use an ID index to validate acceptance. Sort nonempty ranges by start to detect overlaps, and handle zero-length insertions separately. Repeated insertion positions and positions strictly inside a range conflict; boundary insertions are legal. Ordinary errors follow the input-path contract; only proposal conflicts add both original proposal indices and IDs.
  3. Combine unchanged spans and replacements in original order. Copy unchanged parts once. At a shared coordinate, insertion precedes the replacement starting there; a replacement ending there has already been emitted. Touching ranges are not overlaps. Empty replacement text still consumes the original range.
  4. Trace acceptance changes. A/C yields ["周二","下午","四点","开会"]; A/B/C yields ["周二","下午","大约","四点","开会"]; deselecting A while keeping B/C yields ["周一","下午","大约","四点","开会"]. D overlaps C, and B lies strictly inside D. Complete-set validation rejects the input even if D is unselected.
  5. Check invariants and cost. Original input stays unchanged; original tokens not covered by accepted edits retain their order; insertion consumes none; replacement consumes exactly its original half-open range; identical input and acceptance yield identical output. Sorting and scanning avoid pairwise comparisons. With L total replacement tokens across all proposals, validation and output can take O(n + L + p log p + r) time and O(p + r) extra space for indices and output. Include unselected replacements in validation cost. These checks establish editing structure, not semantic correctness.