Initial commit of Edison Voice menu-bar dictation app

This commit is contained in:
2026-09-09 23:49:51 +02:00
commit ad02cfb638
40 changed files with 3305 additions and 0 deletions
@@ -0,0 +1,353 @@
{
"$comment": [
"Shared behavioural contract for the dictionary correction pass.",
"",
"The macOS app (Swift) and the Windows app (C#) implement this logic independently.",
"Independent implementations drift. This file is the thing that stops them: both must",
"run every case below and produce exactly the stated output. A change to correction",
"semantics starts here, not in either implementation.",
"",
"Rules under test:",
" 1. Whole matches only - a rule must never bite into a longer word.",
" 2. Case-insensitive - the trigger matches in any case; output is verbatim.",
" 3. Longest match first - a longer trigger wins over a shorter overlapping one.",
" 4. Glued words match - parts may be separated by any number of spaces or hyphens,",
" including none at all.",
" 5. Disabled is inert - a disabled entry changes nothing."
],
"version": 1,
"cases": [
{
"name": "basic phrase replacement",
"entries": [
{
"kind": "correction",
"hear": "cloud code",
"write": "Claude Code"
}
],
"input": "I use cloud code every day.",
"expected": "I use Claude Code every day.",
"expectedCorrections": [
{
"to": "Claude Code",
"count": 1
}
]
},
{
"name": "glued together with no separator",
"entries": [
{
"kind": "correction",
"hear": "cloud code",
"write": "Claude Code"
}
],
"input": "I use CloudCode every day.",
"expected": "I use Claude Code every day.",
"expectedCorrections": [
{
"to": "Claude Code",
"count": 1
}
]
},
{
"name": "joined by a hyphen",
"entries": [
{
"kind": "correction",
"hear": "cloud code",
"write": "Claude Code"
}
],
"input": "I use Cloud-Code every day.",
"expected": "I use Claude Code every day.",
"expectedCorrections": [
{
"to": "Claude Code",
"count": 1
}
]
},
{
"name": "multiple spaces between parts",
"entries": [
{
"kind": "correction",
"hear": "cloud code",
"write": "Claude Code"
}
],
"input": "I use cloud code every day.",
"expected": "I use Claude Code every day.",
"expectedCorrections": [
{
"to": "Claude Code",
"count": 1
}
]
},
{
"name": "trigger matches regardless of case",
"entries": [
{
"kind": "correction",
"hear": "cloud code",
"write": "Claude Code"
}
],
"input": "CLOUD CODE is great.",
"expected": "Claude Code is great.",
"expectedCorrections": [
{
"to": "Claude Code",
"count": 1
}
]
},
{
"name": "must not corrupt a longer word that starts the same way",
"entries": [
{
"kind": "correction",
"hear": "cloud code",
"write": "Claude Code"
}
],
"input": "Cloudflare fronts the cloudcodex service.",
"expected": "Cloudflare fronts the cloudcodex service.",
"expectedCorrections": []
},
{
"name": "must not touch the ordinary word alone",
"entries": [
{
"kind": "correction",
"hear": "cloud code",
"write": "Claude Code"
}
],
"input": "The cloud is fine and the code is fine.",
"expected": "The cloud is fine and the code is fine.",
"expectedCorrections": []
},
{
"name": "longest trigger wins over a shorter overlapping one",
"entries": [
{
"kind": "correction",
"hear": "cloud",
"write": "Claude"
},
{
"kind": "correction",
"hear": "cloud code",
"write": "Claude Code"
}
],
"input": "cloud code",
"expected": "Claude Code",
"expectedCorrections": [
{
"to": "Claude Code",
"count": 1
}
]
},
{
"name": "counts repeated hits",
"entries": [
{
"kind": "correction",
"hear": "clawed code",
"write": "Claude Code"
}
],
"input": "clawed code, then clawed code again, and clawed code.",
"expected": "Claude Code, then Claude Code again, and Claude Code.",
"expectedCorrections": [
{
"to": "Claude Code",
"count": 3
}
]
},
{
"name": "punctuation is a boundary",
"entries": [
{
"kind": "correction",
"hear": "cloud code",
"write": "Claude Code"
}
],
"input": "(cloud code), \"cloud code\"; cloud code!",
"expected": "(Claude Code), \"Claude Code\"; Claude Code!",
"expectedCorrections": [
{
"to": "Claude Code",
"count": 3
}
]
},
{
"name": "a possessive still matches - the apostrophe is not a letter",
"entries": [
{
"kind": "correction",
"hear": "cloud code",
"write": "Claude Code"
}
],
"input": "cloud code's output",
"expected": "Claude Code's output",
"expectedCorrections": [
{
"to": "Claude Code",
"count": 1
}
]
},
{
"name": "a disabled entry changes nothing",
"entries": [
{
"kind": "correction",
"hear": "cloud code",
"write": "Claude Code",
"isEnabled": false
}
],
"input": "I use cloud code every day.",
"expected": "I use cloud code every day.",
"expectedCorrections": []
},
{
"name": "a term entry never rewrites text",
"entries": [
{
"kind": "term",
"write": "Anthropic"
}
],
"input": "anthropic makes models.",
"expected": "anthropic makes models.",
"expectedCorrections": []
},
{
"name": "single word trigger is honoured when asked for",
"entries": [
{
"kind": "correction",
"hear": "vercell",
"write": "Vercel"
}
],
"input": "Deployed on Vercell today.",
"expected": "Deployed on Vercel today.",
"expectedCorrections": [
{
"to": "Vercel",
"count": 1
}
]
},
{
"name": "several independent rules in one pass",
"entries": [
{
"kind": "correction",
"hear": "clawed code",
"write": "Claude Code"
},
{
"kind": "correction",
"hear": "whisper flow",
"write": "Wispr Flow"
}
],
"input": "clawed code beat whisper flow.",
"expected": "Claude Code beat Wispr Flow.",
"expectedCorrections": [
{
"to": "Claude Code",
"count": 1
},
{
"to": "Wispr Flow",
"count": 1
}
]
},
{
"name": "empty input is left alone",
"entries": [
{
"kind": "correction",
"hear": "cloud code",
"write": "Claude Code"
}
],
"input": "",
"expected": "",
"expectedCorrections": []
},
{
"name": "replacement containing regex metacharacters is literal",
"entries": [
{
"kind": "correction",
"hear": "c plus plus",
"write": "C++"
}
],
"input": "I write c plus plus.",
"expected": "I write C++.",
"expectedCorrections": [
{
"to": "C++",
"count": 1
}
]
},
{
"name": "trigger containing regex metacharacters is literal",
"entries": [
{
"kind": "correction",
"hear": "dot net",
"write": ".NET"
}
],
"input": "Built with dot net.",
"expected": "Built with .NET.",
"expectedCorrections": [
{
"to": ".NET",
"count": 1
}
]
},
{
"name": "accented trigger matches decomposed input (NFC normalization)",
"$comment": "macOS hands back NFD strings from several APIs. Without normalizing both sides, an accented trigger silently never fires. Input here is deliberately NFD.",
"entries": [
{
"kind": "correction",
"hear": "café racer",
"write": "Café Racer"
}
],
"input": "a café racer bike",
"expected": "a Café Racer bike",
"expectedCorrections": [
{
"to": "Café Racer",
"count": 1
}
]
}
]
}