support multiline

This commit is contained in:
Sam Hoffman
2026-01-18 19:20:31 -05:00
parent 78a1be39eb
commit b05827712b
4 changed files with 107 additions and 16 deletions

View File

@@ -17,12 +17,16 @@ const accountStore = useAccountStore();
<BufferList /> <BufferList />
</v-sheet> </v-sheet>
<div class="messages d-flex flex-column"> <div class="messages d-flex flex-column">
<v-toolbar density="compact"> <v-card-title>
<v-toolbar-title> <v-row>
<p>{{ bufferStore.activeBufferName }}</p> <v-col cols="2">
{{ bufferStore.activeBufferName }}
</v-col>
<v-col cols="3">
{{ bufferStore.activeBuffer?.topic }} {{ bufferStore.activeBuffer?.topic }}
</v-toolbar-title> </v-col>
</v-toolbar> </v-row>
</v-card-title>
<MessageList <MessageList
:messages="bufferStore.activeBuffer?.messages" :messages="bufferStore.activeBuffer?.messages"
:me="accountStore.account.nick" :me="accountStore.account.nick"

View File

@@ -103,6 +103,7 @@ function tabComplete() {
const beforeCursor = text.value.splice(0, cursor); const beforeCursor = text.value.splice(0, cursor);
const afterCursor = text.value.slice(cursor); const afterCursor = text.value.slice(cursor);
} }
const rows = ref(1);
</script> </script>
<template> <template>
<v-menu <v-menu
@@ -114,16 +115,18 @@ function tabComplete() {
> >
<v-list v-bind="menuList" @click:select="clickItem" /> <v-list v-bind="menuList" @click:select="clickItem" />
</v-menu> </v-menu>
<v-text-field <v-textarea
:rows="rows"
auto-grow
v-model="text" v-model="text"
autofocus autofocus
hide-details hide-details
:placeholder="`Message ${bufferStore.activeBufferName}`" :placeholder="`Message ${bufferStore.activeBufferName}`"
@input="trigger" @input="trigger"
@keydown.enter.prevent="send" @keydown.enter.exact.prevent="send"
@keydown.tab.prevent="tabComplete" @keydown.tab.prevent="tabComplete"
variant="outlined" variant="outlined"
class="ma-1" class="ma-1"
role="irchad" role="irchad"
></v-text-field> />
</template> </template>

View File

@@ -61,7 +61,7 @@ watch(
><v-icon class="mr-2">mdi-eye</v-icon>Only visible to you ><v-icon class="mr-2">mdi-eye</v-icon>Only visible to you
</v-chip> </v-chip>
</v-list-item-title> </v-list-item-title>
{{ msg.message }} <pre style="white-space: pre">{{ msg.message }}</pre>
</v-list-item></template </v-list-item></template
> >
</v-virtual-scroll> </v-virtual-scroll>

View File

@@ -12,12 +12,19 @@ export enum HookStatus {
HOOK_EAT, HOOK_EAT,
} }
interface Batch {
type: string;
target: string;
messages: any[];
params: string[];
}
export const useIRCStore = defineStore("ircStore", () => { export const useIRCStore = defineStore("ircStore", () => {
const bufferStore = useBufferStore(); const bufferStore = useBufferStore();
const accountStore = useAccountStore(); const accountStore = useAccountStore();
const connected = ref(false); const connected = ref(false);
const { authError } = storeToRefs(accountStore); const { authError } = storeToRefs(accountStore);
const batches = new Map<string, Batch>();
const hooks = {} as Record<string, HookFunction[]>; const hooks = {} as Record<string, HookFunction[]>;
function registerHook(event: string, f: HookFunction) { function registerHook(event: string, f: HookFunction) {
@@ -103,6 +110,7 @@ export const useIRCStore = defineStore("ircStore", () => {
client.requestCap("draft/metadata-2"); client.requestCap("draft/metadata-2");
client.requestCap("echo-message"); client.requestCap("echo-message");
client.requestCap("chathistory"); client.requestCap("chathistory");
client.requestCap("draft/multiline");
const tls = location.protocol === "https:"; const tls = location.protocol === "https:";
const connectParams = { const connectParams = {
host: location.hostname, host: location.hostname,
@@ -127,9 +135,27 @@ export const useIRCStore = defineStore("ircStore", () => {
if (!bufferStore.activeBuffer) { if (!bufferStore.activeBuffer) {
return; return;
} }
if (bufferStore.activeBuffer.channel) send(bufferStore.activeBuffer?.name, message);
bufferStore.activeBuffer.channel.say(message); }
else client.say(bufferStore.activeBuffer.name, message);
function genBatchId(): string {
return Math.random().toString(36).substring(2, 10);
}
function send(target: string, message: string) {
if (!message.includes("\n")) {
client.say(target, message);
return;
}
const split = message.split("\n");
const batchId = genBatchId();
client.raw(`BATCH +${batchId} draft/multiline ${target}`);
split.forEach((line) => {
client.raw(`@batch=${batchId} PRIVMSG ${target} :${line}`);
});
client.raw(`BATCH -${batchId}`);
} }
function isMe(target: string) { function isMe(target: string) {
@@ -143,6 +169,7 @@ export const useIRCStore = defineStore("ircStore", () => {
} }
client.on("socket close", () => { client.on("socket close", () => {
batches.clear();
connected.value = false; connected.value = false;
}); });
@@ -242,7 +269,7 @@ export const useIRCStore = defineStore("ircStore", () => {
}, },
); );
client.on("message", function (message: { nick: string; target: string }) { function handleMessage(message: any) {
let buffer; let buffer;
const retVal = runHook("message", message); const retVal = runHook("message", message);
@@ -270,7 +297,25 @@ export const useIRCStore = defineStore("ircStore", () => {
) { ) {
buffer.resetLastSeen(); buffer.resetLastSeen();
} }
}); }
client.on(
"message",
function (message: {
nick: string;
target: string;
tags: Record<string, string>;
}) {
const batchId = message.tags["batch"];
if (batchId && batches.has(batchId)) {
batches.get(batchId)?.messages.push(message);
return;
}
handleMessage(message);
},
);
client.on("notice", function (message) { client.on("notice", function (message) {
const retVal = runHook("notice", message); const retVal = runHook("notice", message);
@@ -335,6 +380,45 @@ export const useIRCStore = defineStore("ircStore", () => {
buffer.users = ev.users; buffer.users = ev.users;
}); });
client.on("batch start", (event: any) => {
batches.set(event.id, {
type: event.type,
target: event.params[0],
messages: [],
params: event.params,
});
});
function handleChatHistory(batch: Batch) {
for (const message of batch.messages) {
handleMessage(message);
}
}
function handleMultiline(batch: Batch) {
console.log(batch);
let m = "";
for (const message of batch.messages) {
m += `${message.message}\n`;
}
handleMessage({ ...batch.messages[0], message: m });
}
client.on("batch end draft/multiline", (event: any) => {
const batch = batches.get(event.id);
if (!batch) return;
handleMultiline(batch);
batches.delete(event.id);
});
client.on("batch end chathistory", (event: any) => {
const batch = batches.get(event.id);
if (!batch) return;
handleChatHistory(batch);
batches.delete(event.id);
});
return { return {
connect, connect,
client, client,