Files
yaak-mountain-loop/src-web/components/core/Editor/singleLine.ts
Gregory Schier b4a1c418bb Run oxfmt across repo, add format script and docs
Add .oxfmtignore to skip generated bindings and wasm-pack output.
Add npm format script, update DEVELOPMENT.md for Vite+ toolchain,
and format all non-generated files with oxfmt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 10:15:49 -07:00

48 lines
1.6 KiB
TypeScript

import type { Extension, TransactionSpec } from "@codemirror/state";
import { EditorSelection, EditorState, Transaction } from "@codemirror/state";
/**
* A CodeMirror extension that forces single-line input by stripping
* all newline characters from user input, including pasted content.
*
* This extension uses a transaction filter to intercept user input,
* removes any newline characters, and adjusts the selection to the end
* of the inserted text.
*
* IME composition events are ignored to preserve proper input behavior
* for non-Latin languages.
*
* @returns A CodeMirror extension that enforces single-line editing.
*/
export function singleLineExtensions(): Extension {
return EditorState.transactionFilter.of(
(tr: Transaction): TransactionSpec | readonly TransactionSpec[] => {
if (!tr.isUserEvent("input") || tr.isUserEvent("input.type.compose")) return tr;
const changes: { from: number; to: number; insert: string }[] = [];
tr.changes.iterChanges((_fromA, toA, fromB, _toB, inserted) => {
let insert = "";
for (const line of inserted.iterLines()) {
insert += line.replace(/\n/g, "");
}
if (insert !== inserted.toString()) {
changes.push({ from: fromB, to: toA, insert });
}
});
const lastChange = changes[changes.length - 1];
if (lastChange == null) return tr;
const selection = EditorSelection.cursor(lastChange.from + lastChange.insert.length);
return {
changes,
selection,
userEvent: tr.annotation(Transaction.userEvent) ?? undefined,
};
},
);
}