<commit_before><template>
	<form id="form" method="post" action="" @submit.prevent="onSubmit">
		<span id="upload-progressbar" />
		<span id="nick">{{ network.nick }}</span>
		<textarea
			id="input"
			ref="input"
			dir="auto"
			class="mousetrap"
			enterkeyhint="send"
			:value="channel.pendingMessage"
			:placeholder="getInputPlaceholder(channel)"
			:aria-label="getInputPlaceholder(channel)"
			@input="setPendingMessage"
			@keypress.enter.exact.prevent="onSubmit"
		/>
		<span
			v-if="$store.state.serverConfiguration.fileUpload"
			id="upload-tooltip"
			class="tooltipped tooltipped-w tooltipped-no-touch"
			aria-label="Upload file"
			@click="openFileUpload"
		>
			<input
				id="upload-input"
				ref="uploadInput"
				type="file"
				aria-labelledby="upload"
				multiple
				@change="onUploadInputChange"
			/>
			<button
				id="upload"
				type="button"
				aria-label="Upload file"
				:disabled="!$store.state.isConnected"
			/>
		</span>
		<span
			id="submit-tooltip"
			class="tooltipped tooltipped-w tooltipped-no-touch"
			aria-label="Send message"
		>
			<button
				id="submit"
				type="submit"
				aria-label="Send message"
				:disabled="!$store.state.isConnected"
			/>
		</span>
	</form>
</template>

<script>
import Mousetrap from "mousetrap";
import {wrapCursor} from "undate";
import autocompletion from "../js/autocompletion";
import commands from "../js/commands/index";
import socket from "../js/socket";
import upload from "../js/upload";
import eventbus from "../js/eventbus";

const formattingHotkeys = {
	"mod+k": "\x03",
	"mod+b": "\x02",
	"mod+u": "\x1F",
	"mod+i": "\x1D",
	"mod+o": "\x0F",
	"mod+s": "\x1e",
	"mod+m": "\x11",
};

// Autocomplete bracket and quote characters like in a modern IDE
// For example, select `text`, press `[` key, and it becomes `[text]`
const bracketWraps = {
	'"': '"',
	"'": "'",
	"(": ")",
	"<": ">",
	"[": "]",
	"{": "}",
	"*": "*",
	"`": "`",
	"~": "~",
	_: "_",
};

let autocompletionRef = null;

export default {
	name: "ChatInput",
	props: {
		network: Object,
		channel: Object,
	},
	watch: {
		"channel.id"() {
			if (autocompletionRef) {
				autocompletionRef.hide();
			}
		},
		"channel.pendingMessage"() {
			this.setInputSize();
		},
	},
	mounted() {
		eventbus.on("escapekey", this.blurInput);

		if (this.$store.state.settings.autocomplete) {
			autocompletionRef = autocompletion(this.$refs.input);
		}

		const inputTrap = Mousetrap(this.$refs.input);

		inputTrap.bind(Object.keys(formattingHotkeys), function (e, key) {
			const modifier = formattingHotkeys[key];

			wrapCursor(
				e.target,
				modifier,
				e.target.selectionStart === e.target.selectionEnd ? "" : modifier
			);

			return false;
		});

		inputTrap.bind(Object.keys(bracketWraps), function (e, key) {
			if (e.target.selectionStart !== e.target.selectionEnd) {
				wrapCursor(e.target, key, bracketWraps[key]);

				return false;
			}
		});

		inputTrap.bind(["up", "down"], (e, key) => {
			if (
				this.$store.state.isAutoCompleting ||
				e.target.selectionStart !== e.target.selectionEnd
			) {
				return;
			}

			const {channel} = this;

			if (channel.inputHistoryPosition === 0) {
				channel.inputHistory[channel.inputHistoryPosition] = channel.pendingMessage;
			}

			if (key === "up") {
				if (channel.inputHistoryPosition < channel.inputHistory.length - 1) {
					channel.inputHistoryPosition++;
				}
			} else if (channel.inputHistoryPosition > 0) {
				channel.inputHistoryPosition--;
			}

			channel.pendingMessage = channel.inputHistory[channel.inputHistoryPosition];
			this.$refs.input.value = channel.pendingMessage;
			this.setInputSize();

			return false;
		});

		if (this.$store.state.serverConfiguration.fileUpload) {
			upload.mounted();
		}<commit_msg>Only scroll history when cursor is on first or last row<commit_after><template>
	<form id="form" method="post" action="" @submit.prevent="onSubmit">
		<span id="upload-progressbar" />
		<span id="nick">{{ network.nick }}</span>
		<textarea
			id="input"
			ref="input"
			dir="auto"
			class="mousetrap"
			enterkeyhint="send"
			:value="channel.pendingMessage"
			:placeholder="getInputPlaceholder(channel)"
			:aria-label="getInputPlaceholder(channel)"
			@input="setPendingMessage"
			@keypress.enter.exact.prevent="onSubmit"
		/>
		<span
			v-if="$store.state.serverConfiguration.fileUpload"
			id="upload-tooltip"
			class="tooltipped tooltipped-w tooltipped-no-touch"
			aria-label="Upload file"
			@click="openFileUpload"
		>
			<input
				id="upload-input"
				ref="uploadInput"
				type="file"
				aria-labelledby="upload"
				multiple
				@change="onUploadInputChange"
			/>
			<button
				id="upload"
				type="button"
				aria-label="Upload file"
				:disabled="!$store.state.isConnected"
			/>
		</span>
		<span
			id="submit-tooltip"
			class="tooltipped tooltipped-w tooltipped-no-touch"
			aria-label="Send message"
		>
			<button
				id="submit"
				type="submit"
				aria-label="Send message"
				:disabled="!$store.state.isConnected"
			/>
		</span>
	</form>
</template>

<script>
import Mousetrap from "mousetrap";
import {wrapCursor} from "undate";
import autocompletion from "../js/autocompletion";
import commands from "../js/commands/index";
import socket from "../js/socket";
import upload from "../js/upload";
import eventbus from "../js/eventbus";

const formattingHotkeys = {
	"mod+k": "\x03",
	"mod+b": "\x02",
	"mod+u": "\x1F",
	"mod+i": "\x1D",
	"mod+o": "\x0F",
	"mod+s": "\x1e",
	"mod+m": "\x11",
};

// Autocomplete bracket and quote characters like in a modern IDE
// For example, select `text`, press `[` key, and it becomes `[text]`
const bracketWraps = {
	'"': '"',
	"'": "'",
	"(": ")",
	"<": ">",
	"[": "]",
	"{": "}",
	"*": "*",
	"`": "`",
	"~": "~",
	_: "_",
};

let autocompletionRef = null;

export default {
	name: "ChatInput",
	props: {
		network: Object,
		channel: Object,
	},
	watch: {
		"channel.id"() {
			if (autocompletionRef) {
				autocompletionRef.hide();
			}
		},
		"channel.pendingMessage"() {
			this.setInputSize();
		},
	},
	mounted() {
		eventbus.on("escapekey", this.blurInput);

		if (this.$store.state.settings.autocomplete) {
			autocompletionRef = autocompletion(this.$refs.input);
		}

		const inputTrap = Mousetrap(this.$refs.input);

		inputTrap.bind(Object.keys(formattingHotkeys), function (e, key) {
			const modifier = formattingHotkeys[key];

			wrapCursor(
				e.target,
				modifier,
				e.target.selectionStart === e.target.selectionEnd ? "" : modifier
			);

			return false;
		});

		inputTrap.bind(Object.keys(bracketWraps), function (e, key) {
			if (e.target.selectionStart !== e.target.selectionEnd) {
				wrapCursor(e.target, key, bracketWraps[key]);

				return false;
			}
		});

		inputTrap.bind(["up", "down"], (e, key) => {
			if (
				this.$store.state.isAutoCompleting ||
				e.target.selectionStart !== e.target.selectionEnd
			) {
				return;
			}

			const onRow = (
				this.$refs.input.value.slice(null, this.$refs.input.selectionStart).match(/\n/g) ||
				[]
			).length;
			const totalRows = (this.$refs.input.value.match(/\n/g) || []).length;

			const {channel} = this;

			if (channel.inputHistoryPosition === 0) {
				channel.inputHistory[channel.inputHistoryPosition] = channel.pendingMessage;
			}

			if (key === "up" && onRow === 0) {
				if (channel.inputHistoryPosition < channel.inputHistory.length - 1) {
					channel.inputHistoryPosition++;
				} else {
					return;
				}
			} else if (key === "down" && channel.inputHistoryPosition > 0 && onRow === totalRows) {
				channel.inputHistoryPosition--;
			} else {
				return;
			}

			channel.pendingMessage = channel.inputHistory[channel.inputHistoryPosition];
			this.$refs.input.value = channel.pendingMessage;
			this.setInputSize();

			return false;
		});

		if (this.$store.state.serverConfiguration.fileUpload) {
			upload.mounted();
		}
{'fraction_non_alphanumeric': 0.18122853872567724, 'fraction_numerical': 0.0034338038916444107, 'mean_word_length': 3.5774155995343424, 'pattern_counts': {'":': 30, '<': 39, '<?xml version=': 0, '>': 41, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1186321', 'n_tokens_mistral': 3247, 'n_tokens_neox': 2710, 'n_words': 479}