Adding elements to chat on every frame proved unwise, also made spellchecker happy.

This commit is contained in:
Mute 2024-08-24 03:31:50 +00:00
parent 966328327e
commit 1eaffc46be
2 changed files with 14 additions and 12 deletions

8
ambient.d.ts vendored
View File

@ -349,7 +349,7 @@ interface Utils {
complete_do_target(actions: {self: unknown, others: unknown}): Set<string> complete_do_target(actions: {self: unknown, others: unknown}): Set<string>
complete_do(this: Optional<ICommand, 'Tag'>): undefined complete_do(this: Optional<ICommand, 'Tag'>): undefined
replace_me(_match: string, _offset: number, whole: string): string replace_me(_match: string, _offset: number, whole: string): string
pad_chat(chat: HTMLDivElement): undefined //pad_chat(chat: HTMLDivElement): undefined
} }
interface Complete { interface Complete {
@ -357,7 +357,7 @@ interface Complete {
/** /**
* The suggestions panel. * The suggestions panel.
* Its structure is (outer div) -> (container div) -> (multiple suggestion divs) * Its structure is (outer div) -> (container div) -> (multiple suggestion div elements)
*/ */
e: HTMLDivElement e: HTMLDivElement
@ -424,7 +424,7 @@ interface Complete {
* We search up or down, using the index as a starting point for the next match, treating the set as a ring * We search up or down, using the index as a starting point for the next match, treating the set as a ring
* If the found line is the same as the current line, we bell out * If the found line is the same as the current line, we bell out
* Upon finding a next match, we replace the input with its text and set the index appropriately * Upon finding a next match, we replace the input with its text and set the index appropriately
* We exit the history mode using the inputchat keydown handler, on Tab, Escape or Enter * We exit the history mode using the InputChat keydown handler, on Tab, Escape or Enter
* Escape restores the saved input, discarding the history line * Escape restores the saved input, discarding the history line
* Tab keeps the current text and unlocks the element, allowing it to be edited * Tab keeps the current text and unlocks the element, allowing it to be edited
* Enter keeps the current text and sends it as the message as usual * Enter keeps the current text and sends it as the message as usual
@ -456,4 +456,4 @@ interface InputHistory {
exit(textarea: HTMLTextAreaElement, restore_input: boolean): true exit(textarea: HTMLTextAreaElement, restore_input: boolean): true
} }
// FIXME spread around readonlys where appropriate // FIXME spread around readonly where appropriate

View File

@ -32,7 +32,7 @@ const/**@type {FP.m_t}*/m_t = v => { if (typeof v === 'string') return v.length
|| (Object.getPrototypeOf(v) === Object.prototype && Reflect.ownKeys(v).length === 0) || (Object.getPrototypeOf(v) === Object.prototype && Reflect.ownKeys(v).length === 0)
return typeof v === 'boolean' && !v return typeof v === 'boolean' && !v
} }
const/**@type {FP.loo}*/loo = (m, c, a) => {let r; for (let n = 0; c($) && n < m; n++) r = a($); return r} //const/**@type {FP.loo}*/loo = (m, c, a) => {let r; for (let n = 0; c($) && n < m; n++) r = a($); return r}
/**@template T*/const Pipe = /**@implements {FP.Pipeline}*/class PipeClass { /**@template T*/const Pipe = /**@implements {FP.Pipeline}*/class PipeClass {
/**@type {FP.Pipeline<T>['proxy']}*/proxy = new Proxy(/**@type {this & Record<number, T | undefined>}*/(this), {get(t, n, r) {return cur(typeof n === 'string' && int(n), i => { /**@type {FP.Pipeline<T>['proxy']}*/proxy = new Proxy(/**@type {this & Record<number, T | undefined>}*/(this), {get(t, n, r) {return cur(typeof n === 'string' && int(n), i => {
@ -56,7 +56,7 @@ const/**@type {Cons.Wrap}*/CW = new (class {
/**@type {Cons.Wrap['gen']}*/gen(m) {return msg => yes(void console[this.ms[m]](`MBCHC: ${msg}`))} /**@type {Cons.Wrap['gen']}*/gen(m) {return msg => yes(void console[this.ms[m]](`MBCHC: ${msg}`))}
})() })()
const/**@type {Settings.Methods}*/Settings = { // FIXME separate a proper V1 type from an unknown object in the extensionsettings const/**@type {Settings.Methods}*/Settings = { // FIXME separate a proper V1 type from an unknown object in the ExtensionSettings
/**I hate change*/migrate_0_1(v0) { if (v0.MBCHC === $) return true /**I hate change*/migrate_0_1(v0) { if (v0.MBCHC === $) return true
val(v0.MBCHC.timezones, tz => this.save(v1 => v1.TZ = {...tz, ...v1.TZ})) val(v0.MBCHC.timezones, tz => this.save(v1 => v1.TZ = {...tz, ...v1.TZ}))
W.ServerAccountUpdate.QueueData({OnlineSettings: del(v0, 'MBCHC')}) W.ServerAccountUpdate.QueueData({OnlineSettings: del(v0, 'MBCHC')})
@ -93,7 +93,7 @@ const/**@type {Utils}*/U = { remove_loader_hook: $, RGB: {Polly: '#81b1e7', Mute
cur(ass(`failed to parse target "${t}"`, U.RE.REL.L.test(t) ? sub : U.RE.REL.R.test(t) ? add : $)(me, t.length), pos => cur(ass(`failed to parse target "${t}"`, U.RE.REL.L.test(t) ? sub : U.RE.REL.R.test(t) ? add : $)(me, t.length), pos =>
cur(pos % U.crc.length, p => U.abs2char(p < 0 ? p + U.crc.length : p)))), cur(pos % U.crc.length, p => U.abs2char(p < 0 ? p + U.crc.length : p)))),
cid2char: id => id === U.cid(W.Player) ? W.Player : ass(`character ${id} not found in the room`, U.crc.find(c => id === U.cid(c))), cid2char: id => id === U.cid(W.Player) ? W.Player : ass(`character ${id} not found in the room`, U.crc.find(c => id === U.cid(c))),
target2char(target) { let t = target.trim(); const /**@type {Set<Character>}*/f = new Set() // FIXME Target should be lowcase (take a look at this later) target2char(target) { let t = target.trim(); const /**@type {Set<Character>}*/f = new Set() // FIXME Target should be low case (take a look at this later)
if (m_t(t)) return W.Player if (m_t(t)) return W.Player
if (t.at(0) === '=') return U.cid2char(ass(`invalid member number "${target}"`, int(t.slice(1)))) if (t.at(0) === '=') return U.cid2char(ass(`invalid member number "${target}"`, int(t.slice(1))))
if ('<>'.includes(t.at(0) ?? '-')) return U.rel2char(t) if ('<>'.includes(t.at(0) ?? '-')) return U.rel2char(t)
@ -158,7 +158,7 @@ const/**@type {Utils}*/U = { remove_loader_hook: $, RGB: {Polly: '#81b1e7', Mute
return $Ss return $Ss
})}, })},
replace_me: (_, __, whole) => cur(whole.slice(1), t => `${U.ACT}<${U.cid(W.Player)}:>SourceCharacter${t.startsWith('\'') || t.startsWith(' ') ? $S : ' '}`), replace_me: (_, __, whole) => cur(whole.slice(1), t => `${U.ACT}<${U.cid(W.Player)}:>SourceCharacter${t.startsWith('\'') || t.startsWith(' ') ? $S : ' '}`),
pad_chat: c => loo(100, _ => c.scrollHeight <= c.clientHeight, _ => yes(void c.prepend(U.mkdiv('\u061C')))) && void W.ElementScrollToEnd('TextAreaChatLog') //pad_chat: c => loo(100, _ => c.scrollHeight <= c.clientHeight, _ => yes(void c.prepend(U.mkdiv('\u061C')))) && void W.ElementScrollToEnd('TextAreaChatLog')
} }
const/**@type {SUBCOMMANDS}*/SUBCOMMANDS_MBCHC = { const/**@type {SUBCOMMANDS}*/SUBCOMMANDS_MBCHC = {
@ -550,7 +550,7 @@ const/**@type {SDK.Hook}*/after = (name, f) => mod.hookFunction(name, 0, (na, n)
// const pose = tokens.at(-1).toLocaleLowerCase() // const pose = tokens.at(-1).toLocaleLowerCase()
// return mbchc.complete(W.PoseFemale3DCG.map(p => p.Name).filter(p => p.toLocaleLowerCase().startsWith(pose))) // return mbchc.complete(W.PoseFemale3DCG.map(p => p.Name).filter(p => p.toLocaleLowerCase().startsWith(pose)))
//}, //},
//focus_chat_checks() { // we only want to catch chatlog and canvas (no map though) keypresses //focus_chat_checks() { // we only want to catch chat log and canvas (no map though) keypresses
// if (D.activeElement === D.body) return true // if (D.activeElement === D.body) return true
// if (D.activeElement?.id !== 'MainCanvas') return false // if (D.activeElement?.id !== 'MainCanvas') return false
// return !W.ChatRoomMapViewIsActive() // return !W.ChatRoomMapViewIsActive()
@ -584,8 +584,10 @@ const/**@type {SDK.Hook}*/after = (name, f) => mod.hookFunction(name, 0, (na, n)
#${C.e.id} > div { overflow: auto; position: absolute; bottom: 0; right: 0; max-height: 100%; padding: 0 0.5ex; background-color: ${U.RGB.Polly}; color: black; } #${C.e.id} > div { overflow: auto; position: absolute; bottom: 0; right: 0; max-height: 100%; padding: 0 0.5ex; background-color: ${U.RGB.Polly}; color: black; }
#${C.e.id}[data-colortheme^="dark"] > div { background-color: ${U.RGB.Mute}; color: white; } #${C.e.id}[data-colortheme^="dark"] > div { background-color: ${U.RGB.Mute}; color: white; }
#${C.e.id} > div div { margin: 0.25ex 0; } #${C.e.id} > div div { margin: 0.25ex 0; }
#chat-room-div[data-mbchc-mode="h"] #TextAreaChatLog::after { content: '𝗵𝗶𝘀𝘁𝗼𝗿𝘆 ⟨𝘗𝘨𝘜𝘱/𝘋𝘯⟩ 𝗌𝖼𝗋𝗈𝗅𝗅 ⇅ ⟨𝘌𝘯𝘵𝘦𝘳⟩ 𝗌𝖾𝗇𝖽 ↵ ⟨𝘛𝘢𝘣⟩ 𝖾𝖽𝗂𝗍 ⌨ ⟨𝘌𝘴𝘤⟩ 𝖺𝖻𝗈𝗋𝗍 ⟲'; display: block; position: sticky; bottom: 0; background: black; color: orange; padding: 0 0.2ex; } #chat-room-div #TextAreaChatLog::before { content: ''; display: block; height: 100%; }
#chat-room-div[data-mbchc-mode="h"] #TextAreaChatLog::after { content: '𝗵𝗶𝘀𝘁𝗼𝗿𝘆 ⟨𝘗𝘨𝘜𝘱/𝘋𝘯⟩ 𝗌𝖼𝗋𝗈𝗅𝗅 ⇅ ⟨𝘌𝘯𝘵𝘦𝘳⟩ 𝗌𝖾𝗇𝖽 ↵ ⟨𝘛𝘢𝘣⟩ 𝖾𝖽𝗂𝗍 ⌨ ⟨𝘌𝘴𝘤⟩ 𝖺𝖻𝗈𝗋𝗍 ⟲'; display: block; position: sticky; bottom: 0; background: black; color: orange; padding: 0 0.2ex; animation: 0.2s cubic-bezier(0.19, 1, 0.22, 1) mbchc_hist_hint_show; }
#InputChat:read-only { background: black; color: orange; } #InputChat:read-only { background: black; color: orange; }
@keyframes mbchc_hist_hint_show { from {transform: translateX(-100%);} to {transform: translateX(0);} }
`) `)
// Actions // Actions
@ -636,9 +638,9 @@ const/**@type {SDK.Hook}*/after = (name, f) => mod.hookFunction(name, 0, (na, n)
} }
} }
after('ChatRoomCreateElement', () => { // This thing runs on every frame actually. after('ChatRoomCreateElement', () => { // This thing runs on every frame actually.
C.e.parentElement === null && void D.body.append(C.e) C.e.parentElement ?? D.body.append(C.e)
val(U.ic, ic => ic.dataset['mbchc'] ?? void ic.addEventListener('keydown', ickd) ?? (ic.dataset['mbchc'] = 'yes')) val(U.ic, ic => ic.dataset['mbchc'] ?? void ic.addEventListener('keydown', ickd) ?? (ic.dataset['mbchc'] = 'yes'))
val(asa(HTMLDivElement, D.querySelector('#TextAreaChatLog')), c => c.scrollHeight > c.clientHeight || void U.pad_chat(c)) //val(asa(HTMLDivElement, D.querySelector('#TextAreaChatLog')), c => c.scrollHeight > c.clientHeight || void U.pad_chat(c))
}) })
prior('ChatRoomClearAllElements', () => C.hide() && void C.e.remove()) prior('ChatRoomClearAllElements', () => C.hide() && void C.e.remove())
D.addEventListener('click', _ => C.hide()) // downstream handlers can capture clicks, but I can't be fucked to be honest D.addEventListener('click', _ => C.hide()) // downstream handlers can capture clicks, but I can't be fucked to be honest