feat(desktop): IRC-style s/old/new/ self-correction in the composer - #3801
Open
sw-square wants to merge 3 commits into
Open
feat(desktop): IRC-style s/old/new/ self-correction in the composer#3801sw-square wants to merge 3 commits into
sw-square wants to merge 3 commits into
Conversation
Sending a message whose entire body is a well-formed substitution command (e.g. `s/hullo/hello/`) now edits your most recent message instead of sending literal text — a keyboard-only shorthand for the existing right-click "Edit message" gesture. It is pure client-side sugar: the correction is republished through the same kind-40003 edit path a manual edit uses, so other clients just see a normal edit. No relay, schema, or Rust changes. - `selfCorrection.ts` (pure, unit-tested): parses `s<D>old<D>new<D>[flags]` as literal text (not regex), sed-style so any punctuation delimiter works (`s|a|b|`, `s#a#b#`), with `\`-escaping of the delimiter, an optional trailing delimiter (`s/u/e`), and `g` (global) / `i` (case-insensitive) flags; and applies it. - `selfCorrectionEdit.ts` (pure, unit-tested): resolves a command against the timeline — finds the author's most recent editable message (same eligibility as ArrowUp-to-edit) and rebuilds the edit through the existing imeta/emoji helpers so attachments and custom emoji are preserved. - `useSelfCorrectingSend` hook intercepts at the send boundary in ChannelPane: a command edits the previous message; anything that does not parse or match falls through and sends literally. Drafts with their own attachments are treated as ordinary sends. A typo-fix correction intentionally emits no new mention tags. Scoped to the main timeline; thread-reply correction can follow. Signed-off-by: Sam Westerman <swesterman@squareup.com>
Per review feedback: arbitrary punctuation delimiters widened the "is this a command?" gate with no functional gain. sed allows any delimiter only to avoid escaping the delimiter inside the pattern — which the existing `\/` backslash-escaping already covers. Fixing the delimiter to `/` matches the canonical IRC `s/old/new/` shorthand and shrinks the accidental-trigger surface (prose like `s: notes`, `s|foo` is no longer mistaken for a command). Also adds an explicit test proving the pattern and replacement are literal text, never regex: `s/*/x` replaces the first literal `*`, `.` is a literal dot (not a wildcard), and `$1`/`\d` in the replacement are inserted verbatim. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Sam Westerman <swesterman@squareup.com>
…path Collapses the IRC `s/old/new/` self-correction surface without changing behaviour (still main-composer-only, literal-not-regex, media/emoji preserved). - Drop the new `onEditSaveById` handler + prop threaded through ChannelScreen -> ChannelPane.types -> ChannelPane -> hook. Instead widen the existing `handleEditSave`/`onEditSave` with an optional explicit `eventId`, so self-correction rides the edit path already in place. Zero new cross-component props. - Reuse ChannelPane's existing `findLastOwnEditable` instead of the duplicate `findLastOwnCorrectable`, so "what counts as editable" has one definition shared with ArrowUp-to-edit. - Merge `selfCorrectionEdit.ts` into `selfCorrection.ts` as `buildSelfCorrectionEdit`; fold its tests into one file. - Slim `useSelfCorrectingSend` to pure state-wiring over the tested lib. 9 files -> 6, 736 -> 595 lines. Full desktop suite green (3855 tests), typecheck + biome + file-size/px/pubkey guards clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: Sam Westerman <swesterman@squareup.com>
Author
|
🤖 Pushed a refactor commit ( What shrank the surface (and the future-breakage risk):
Net: 9 files → 6, 736 → 595 lines. The remaining bulk is the parser + its tests (the escaping / optional-slash / literal-not-regex grammar); the actual wiring is now ~80 lines. Full desktop suite green (3855 tests), typecheck + biome + file-size/px/pubkey guards clean. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Sending a message whose entire body is a well-formed substitution command —
s/hullo/hello/— now edits your most recent message instead of sending literal text. It's a keyboard-only shorthand for the existing right-click "Edit message" gesture.If I type
hullo there!, notice the typo, and sends/u/e/, my previous message becomeshello there!.How
Pure client-side sugar. The correction is republished through the same kind-40003 edit path a manual edit uses, so other clients (and the relay) just see a normal edit. No relay, schema, or Rust changes.
selfCorrection.ts(pure, unit-tested) — parsess/old/new/[flags]as literal text, never regex:/— the canonical IRC/seds/old/new/shorthand. (sed permits arbitrary delimiters only to avoid escaping the delimiter inside the pattern; our\/escaping already covers that, so alternate delimiters would only widen the accidental-trigger surface for no gain.)\-escaping of the delimiter and backslash, sos/\//\/\//greplaces every/with//.s/u/e).g(all occurrences) andi(case-insensitive).s/*/xreplaces the first literal*;.is a literal dot, not a wildcard;$1/\din the replacement are inserted verbatim.selfCorrectionEdit.ts(pure, unit-tested) — resolves a command against the timeline: finds the author's most recent editable message (same eligibility as ArrowUp-to-edit — own, non-system, not pending) and rebuilds the edit through the existing imeta/emoji helpers, so attachments and custom emoji are preserved.useSelfCorrectingSend— intercepts at the send boundary inChannelPane. A valid command edits the previous message; anything that doesn't parse, or whose pattern isn't present in your last message, falls through and sends literally. Drafts that carry their own attachments are treated as ordinary sends (thens/a/b/is a caption).Accidental-trigger safety
Three independent guards keep ordinary prose from being eaten:
s/— prose likes3 bucket,s: notes,s|foonever qualifies.Behavior notes
@mentionwon't notify that user; acceptable for a quick-correction affordance.)Testing
selfCorrection(grammar/apply — incl.s/u/e,s/\//\/\//g, slash-only enforcement, and a dedicated literal-not-regex proof) andselfCorrectionEdit(target selection, fallthrough, media/emoji preservation).pnpm typecheck, biome, file-size guard, and the full desktop unit suite (3860 tests) pass locally.🐝 Built by Bumble in Buzz, from a conversation in #test-swesterman.