diff --git a/ambient.d.ts b/ambient.d.ts index f354574..c5c7f58 100644 --- a/ambient.d.ts +++ b/ambient.d.ts @@ -349,7 +349,7 @@ interface Utils { complete_do_target(actions: {self: unknown, others: unknown}): Set complete_do(this: Optional): undefined replace_me(_match: string, _offset: number, whole: string): string - pad_chat(chat: HTMLDivElement): undefined + //pad_chat(chat: HTMLDivElement): undefined } interface Complete { @@ -357,7 +357,7 @@ interface Complete { /** * 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 @@ -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 * 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 - * 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 * 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 @@ -456,4 +456,4 @@ interface InputHistory { exit(textarea: HTMLTextAreaElement, restore_input: boolean): true } -// FIXME spread around readonlys where appropriate +// FIXME spread around readonly where appropriate diff --git a/mbchc.mjs b/mbchc.mjs index b8febcf..a87ebe0 100644 --- a/mbchc.mjs +++ b/mbchc.mjs @@ -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) 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 { /**@type {FP.Pipeline['proxy']}*/proxy = new Proxy(/**@type {this & Record}*/(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}`))} })() -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 val(v0.MBCHC.timezones, tz => this.save(v1 => v1.TZ = {...tz, ...v1.TZ})) 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(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))), - target2char(target) { let t = target.trim(); const /**@type {Set}*/f = new Set() // FIXME Target should be lowcase (take a look at this later) + target2char(target) { let t = target.trim(); const /**@type {Set}*/f = new Set() // FIXME Target should be low case (take a look at this later) if (m_t(t)) return W.Player 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) @@ -158,7 +158,7 @@ const/**@type {Utils}*/U = { remove_loader_hook: $, RGB: {Polly: '#81b1e7', Mute return $Ss })}, 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 = { @@ -550,7 +550,7 @@ const/**@type {SDK.Hook}*/after = (name, f) => mod.hookFunction(name, 0, (na, n) // const pose = tokens.at(-1).toLocaleLowerCase() // 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?.id !== 'MainCanvas') return false // 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}[data-colortheme^="dark"] > div { background-color: ${U.RGB.Mute}; color: white; } #${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; } + @keyframes mbchc_hist_hint_show { from {transform: translateX(-100%);} to {transform: translateX(0);} } `) // 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. - 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(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()) D.addEventListener('click', _ => C.hide()) // downstream handlers can capture clicks, but I can't be fucked to be honest