history improvements: backspace passthrough, hint rearranged
This commit is contained in:
parent
fb700c91cf
commit
b9b1226e60
6
ambient.d.ts
vendored
6
ambient.d.ts
vendored
|
@ -350,6 +350,8 @@ interface Utils {
|
||||||
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
|
||||||
scroll(): true
|
scroll(): true
|
||||||
|
get scrolled(): boolean
|
||||||
|
rescroll(func: (_: undefined) => unknown): true
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Complete {
|
interface Complete {
|
||||||
|
@ -448,7 +450,7 @@ interface InputHistory {
|
||||||
/**
|
/**
|
||||||
* Specific key handlers.
|
* Specific key handlers.
|
||||||
*/
|
*/
|
||||||
key: Record<string, (textarea: HTMLTextAreaElement) => true>
|
key: Record<string, (event: KeyboardEvent, textarea: HTMLTextAreaElement) => true>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set or unset the readonly mode on the input.
|
* Set or unset the readonly mode on the input.
|
||||||
|
@ -463,7 +465,7 @@ interface InputHistory {
|
||||||
/**
|
/**
|
||||||
* Exit the history mode and proc the key event.
|
* Exit the history mode and proc the key event.
|
||||||
*/
|
*/
|
||||||
exit(textarea: HTMLTextAreaElement, key: KeyboardEvent['key']): true
|
exit(textarea: HTMLTextAreaElement, e: KeyboardEvent): true
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME spread around readonly where appropriate
|
// FIXME spread around readonly where appropriate
|
||||||
|
|
26
mbchc.mjs
26
mbchc.mjs
|
@ -159,6 +159,8 @@ const/**@type {Utils}*/U = { remove_loader_hook: $, RGB: {Polly: '#81b1e7', Mute
|
||||||
})},
|
})},
|
||||||
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 : ' '}`),
|
||||||
scroll: () => yes(void W.ElementScrollToEnd('TextAreaChatLog')),
|
scroll: () => yes(void W.ElementScrollToEnd('TextAreaChatLog')),
|
||||||
|
get scrolled() {return W.ElementIsScrolledToEnd('TextAreaChatLog')},
|
||||||
|
rescroll: f => cur(U.scrolled, s => yes(f, s && U.scroll())),
|
||||||
}
|
}
|
||||||
|
|
||||||
const/**@type {SUBCOMMANDS}*/SUBCOMMANDS_MBCHC = {
|
const/**@type {SUBCOMMANDS}*/SUBCOMMANDS_MBCHC = {
|
||||||
|
@ -178,7 +180,7 @@ const/**@type {Complete}*/C = { S_OPTS: {behavior: 'instant'},
|
||||||
hint: cs => m_t(cs) || yes(_ => {
|
hint: cs => m_t(cs) || yes(_ => {
|
||||||
yes(C.e.style.display = 'block') && C.div?.replaceChildren(...[...cs].sort().reverse().map(U.mkdiv))
|
yes(C.e.style.display = 'block') && C.div?.replaceChildren(...[...cs].sort().reverse().map(U.mkdiv))
|
||||||
W.ElementSetDataAttribute(C.e.id, 'colortheme', W.Player.ChatSettings?.ColorTheme ?? 'Light')
|
W.ElementSetDataAttribute(C.e.id, 'colortheme', W.Player.ChatSettings?.ColorTheme ?? 'Light')
|
||||||
cur(W.ElementIsScrolledToEnd('TextAreaChatLog'), r => yes(void W.ChatRoomResize(false)) && r && U.scroll())
|
U.rescroll(_ => void W.ChatRoomResize(false))
|
||||||
C.div?.lastElementChild?.scrollIntoView(C.S_OPTS)
|
C.div?.lastElementChild?.scrollIntoView(C.S_OPTS)
|
||||||
}),
|
}),
|
||||||
get hidden() {return C.e.parentElement === null || C.e.style.display === 'none'},
|
get hidden() {return C.e.parentElement === null || C.e.style.display === 'none'},
|
||||||
|
@ -204,12 +206,12 @@ const/**@type {Complete}*/C = { S_OPTS: {behavior: 'instant'},
|
||||||
|
|
||||||
const/**@type {InputHistory}*/H = { input: undefined, ids: undefined, bottom: undefined, // FIXME ids don't need to be a set, but I'm too tired right now
|
const/**@type {InputHistory}*/H = { input: undefined, ids: undefined, bottom: undefined, // FIXME ids don't need to be a set, but I'm too tired right now
|
||||||
key: {
|
key: {
|
||||||
Escape: ic => yes(val(H.input, i => ic.value = i)),
|
Escape: (_, ic) => yes(val(H.input, i => ic.value = i)),
|
||||||
ArrowLeft: ic => yes(void ic.setSelectionRange(0, 0)),
|
ArrowLeft: (_, ic) => yes(void ic.setSelectionRange(0, 0)),
|
||||||
},
|
},
|
||||||
icro: (ic, ro) => yes(ic.readOnly = ro, val(ic.parentElement?.parentElement?.dataset, d => ro ? d['mbchcMode'] = 'h' : del(d, 'mbchcMode'))),
|
icro: (ic, ro) => yes(ic.readOnly = ro, val(ic.parentElement?.parentElement?.dataset, d => ro ? d['mbchcMode'] = 'h' : del(d, 'mbchcMode'))),
|
||||||
enter: (ic, i, b, is) => yes(H.input = i, H.bottom = b, H.ids = is, H.icro(ic, true), b && U.scroll()),
|
enter: (ic, i, b, is) => yes(H.input = i, H.bottom = b, H.ids = is, H.icro(ic, true), b && U.scroll()),
|
||||||
exit: (ic, k) => yes(H.icro(ic, false), H.key[k]?.(ic), val(H.bottom, b => b && U.scroll()), W.ChatRoomLastMessageIndex = W.ChatRoomLastMessage.length)
|
exit: (ic, e) => yes(H.icro(ic, false), H.key[e.key]?.(e, ic), val(H.bottom, b => b && U.scroll()), W.ChatRoomLastMessageIndex = W.ChatRoomLastMessage.length)
|
||||||
}
|
}
|
||||||
|
|
||||||
ass('MBCHC found, aborting loading', W.MBCHC === $)
|
ass('MBCHC found, aborting loading', W.MBCHC === $)
|
||||||
|
@ -478,7 +480,7 @@ const/**@type {SDK.Hook}*/after = (name, f) => mod.hookFunction(name, 0, (na, n)
|
||||||
// this.COMP_HINT.innerHTML = '<div>' + options.sort().reverse().map(s => `<div>${s}</div>`).join($S) + '</div>'
|
// this.COMP_HINT.innerHTML = '<div>' + options.sort().reverse().map(s => `<div>${s}</div>`).join($S) + '</div>'
|
||||||
// this.COMP_HINT.style.display = 'block'
|
// this.COMP_HINT.style.display = 'block'
|
||||||
// W.ElementSetDataAttribute(this.COMP_HINT.id, 'colortheme', (W.Player.ChatSettings?.ColorTheme || 'Light'))
|
// W.ElementSetDataAttribute(this.COMP_HINT.id, 'colortheme', (W.Player.ChatSettings?.ColorTheme || 'Light'))
|
||||||
// const rescroll = W.ElementIsScrolledToEnd('TextAreaChatLog')
|
// const rescroll = U.scrolled
|
||||||
// W.ChatRoomResize(false)
|
// W.ChatRoomResize(false)
|
||||||
// if (rescroll) U.scroll()
|
// if (rescroll) U.scroll()
|
||||||
// this.COMP_HINT.firstChild?.lastChild?.scrollIntoView({behaviour: 'instant'})
|
// this.COMP_HINT.firstChild?.lastChild?.scrollIntoView({behaviour: 'instant'})
|
||||||
|
@ -582,7 +584,7 @@ const/**@type {SDK.Hook}*/after = (name, f) => mod.hookFunction(name, 0, (na, n)
|
||||||
{Tag: 'activity', Description: '[Message]: Send a custom activity (or "@@Message", or "@Message" as yourself)', Action: this.command_activity},
|
{Tag: 'activity', Description: '[Message]: Send a custom activity (or "@@Message", or "@Message" as yourself)', Action: this.command_activity},
|
||||||
{Tag: 'do', Description: ': Do an activity, as if clicked on its button ("/do" for help)', Action: this.command_do, AutoComplete: U.complete_do},
|
{Tag: 'do', Description: ': Do an activity, as if clicked on its button ("/do" for help)', Action: this.command_do, AutoComplete: U.complete_do},
|
||||||
]
|
]
|
||||||
mut(D.createElement('style'), c => void D.head.append(c), c => c.textContent = `
|
U.rescroll(_ => mut(D.createElement('style'), c => void D.head.append(c), c => c.textContent = `
|
||||||
#TextAreaChatLog .mbchc { background-color: ${U.RGB.Polly}; margin-left: -0.4em; padding-left: 0.4em; }
|
#TextAreaChatLog .mbchc { background-color: ${U.RGB.Polly}; margin-left: -0.4em; padding-left: 0.4em; }
|
||||||
#TextAreaChatLog[data-colortheme^="dark"] .mbchc { background-color: ${U.RGB.Mute}; }
|
#TextAreaChatLog[data-colortheme^="dark"] .mbchc { background-color: ${U.RGB.Mute}; }
|
||||||
#${C.e.id} { display: none; text-align: right; }
|
#${C.e.id} { display: none; text-align: right; }
|
||||||
|
@ -591,12 +593,12 @@ const/**@type {SDK.Hook}*/after = (name, f) => mod.hookFunction(name, 0, (na, n)
|
||||||
#${C.e.id} > div div { margin: 0.25ex 0; }
|
#${C.e.id} > div div { margin: 0.25ex 0; }
|
||||||
#chat-room-div #TextAreaChatLog::before { content: ''; display: block; height: 100%; }
|
#chat-room-div #TextAreaChatLog::before { content: ''; display: block; height: 100%; }
|
||||||
#chat-room-div[data-mbchc-mode="h"] #TextAreaChatLog::after {
|
#chat-room-div[data-mbchc-mode="h"] #TextAreaChatLog::after {
|
||||||
content: '𝗵𝗶𝘀𝘁𝗼𝗿𝘆 ⟨𝘗𝘨𝘜𝘱/𝘋𝘯/↕⟩ 𝗌𝖼𝗋𝗈𝗅𝗅 ⇅ ⟨𝘌𝘯𝘵𝘦𝘳⟩ 𝗌𝖾𝗇𝖽 ↵ ⟨𝘛𝘢𝘣/↔⟩ 𝖾𝖽𝗂𝗍 ⌨ ⟨𝘌𝘴𝘤⟩ 𝖺𝖻𝗈𝗋𝗍 ⟲\\A' attr(data-mbchc-h-h); whitespace: pre;
|
content: '⟨𝘗𝘨𝘜𝘱/𝘋𝘯/↕⟩ 𝗌𝖼𝗋𝗈𝗅𝗅 ⇅ ⟨𝘌𝘯𝘵𝘦𝘳⟩ 𝗌𝖾𝗇𝖽 ↵ ⟨𝘛𝘢𝘣/↔/⌫⟩ 𝖾𝖽𝗂𝗍 ⌨ ⟨𝘌𝘴𝘤⟩ 𝖺𝖻𝗈𝗋𝗍 ⟲\\A' attr(data-mbchc-h-h); whitespace: pre;
|
||||||
display: block; position: sticky; z-index: 1; bottom: 0; background: black; color: orange; padding: 0 0.2ex; animation: 0.2s cubic-bezier(0.19, 1, 0.22, 1) mbchc_hh_show;
|
display: block; position: sticky; z-index: 1; bottom: 0; background: black; color: orange; padding: 0 0.2ex; animation: 0.2s cubic-bezier(0.19, 1, 0.22, 1) mbchc_hh_show;
|
||||||
}
|
}
|
||||||
#InputChat:read-only { background: black; color: orange; }
|
#InputChat:read-only { background: black; color: orange; }
|
||||||
@keyframes mbchc_hh_show { from {transform: translateX(-100%);} to {transform: translateX(0);} }
|
@keyframes mbchc_hh_show { from {transform: translateX(-100%);} to {transform: translateX(0);} }
|
||||||
`)
|
`))
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
this.calculate_maps()
|
this.calculate_maps()
|
||||||
|
@ -637,12 +639,12 @@ const/**@type {SDK.Hook}*/after = (name, f) => mod.hookFunction(name, 0, (na, n)
|
||||||
const/**@type {(this: HTMLTextAreaElement, e: KeyboardEvent) => void}*/ickd = function(e) {
|
const/**@type {(this: HTMLTextAreaElement, e: KeyboardEvent) => void}*/ickd = function(e) {
|
||||||
C.hide()
|
C.hide()
|
||||||
const ic = this // eslint-disable-line unicorn/no-this-assignment,@typescript-eslint/no-this-alias
|
const ic = this // eslint-disable-line unicorn/no-this-assignment,@typescript-eslint/no-this-alias
|
||||||
if (ic.readOnly) switch (e.key) {
|
if (ic.readOnly && !e.repeat) switch (e.key) { // FIXME maybe deal with modifiers
|
||||||
case 'ArrowUp': case 'ArrowDown': W.ChatRoomScrollHistory(e.key === 'ArrowUp'); break
|
case 'ArrowUp': case 'ArrowDown': W.ChatRoomScrollHistory(e.key === 'ArrowUp'); break
|
||||||
case 'Escape': case 'Tab': case 'ArrowRight': case 'ArrowLeft':
|
case 'Escape': case 'Tab': case 'ArrowRight': case 'ArrowLeft':
|
||||||
e.stopImmediatePropagation()
|
e.stopImmediatePropagation()
|
||||||
e.preventDefault() // falls through
|
e.preventDefault() // falls through
|
||||||
case 'Enter': H.exit(ic, e.key) // no default
|
case 'Enter': case 'Backspace': H.exit(ic, e) // no default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
after('ChatRoomCreateElement', () => { // This thing runs on every frame actually.
|
after('ChatRoomCreateElement', () => { // This thing runs on every frame actually.
|
||||||
|
@ -675,8 +677,8 @@ const/**@type {SDK.Hook}*/after = (name, f) => mod.hookFunction(name, 0, (na, n)
|
||||||
const/**@type {Map<string,number>}*/map = new Map()
|
const/**@type {Map<string,number>}*/map = new Map()
|
||||||
const/**@type {(l: string, i: number, I: string) => boolean}*/ cond = m_t(input) ? (_, i, __) => i > 0 : (l, _, i) => l !== i && l.startsWith(i)
|
const/**@type {(l: string, i: number, I: string) => boolean}*/ cond = m_t(input) ? (_, i, __) => i > 0 : (l, _, i) => l !== i && l.startsWith(i)
|
||||||
if (m_t(history.reduce((ax, l, i) => cond(l, i, input) ? ax.set(l, i) : ax, map))) return U.bell()
|
if (m_t(history.reduce((ax, l, i) => cond(l, i, input) ? ax.set(l, i) : ax, map))) return U.bell()
|
||||||
val(asa(HTMLDivElement, D.querySelector('#TextAreaChatLog')), t => t.dataset['mbchcHH'] = m_t(input) ? 'All history' : `Prefix: ${input}`)
|
val(asa(HTMLDivElement, D.querySelector('#TextAreaChatLog')), t => t.dataset['mbchcHH'] = `𝗵𝗶𝘀𝘁𝗼𝗿𝘆: ${m_t(input) ? 'Everything' : `Prefix: ${input}`}`)
|
||||||
H.enter(ic, input, W.ElementIsScrolledToEnd('TextAreaChatLog'), new Set(map.values()))
|
H.enter(ic, input, U.scrolled, new Set(map.values()))
|
||||||
}
|
}
|
||||||
if (ic.readOnly) { // this can't be an else, because we mutate state above. To be honest, this will always be true, but I want to make sure.
|
if (ic.readOnly) { // this can't be an else, because we mutate state above. To be honest, this will always be true, but I want to make sure.
|
||||||
if (H.ids === $) return U.bell() // shouldn't happen?
|
if (H.ids === $) return U.bell() // shouldn't happen?
|
||||||
|
|
Loading…
Reference in New Issue
Block a user