xP: add basic buffer input history
Bind M-p and M-n as in xC. Also make all our bindings reachable on macOS.
This commit is contained in:
		
							parent
							
								
									4bc2f736f2
								
							
						
					
					
						commit
						7e3919e25d
					
				| @ -197,7 +197,11 @@ rpc.addEventListener('Ping', event => { | ||||
| rpc.addEventListener('BufferUpdate', event => { | ||||
| 	let e = event.detail, b = buffers.get(e.bufferName) | ||||
| 	if (b === undefined) { | ||||
| 		buffers.set(e.bufferName, (b = {lines: []})) | ||||
| 		buffers.set(e.bufferName, (b = { | ||||
| 			lines: [], | ||||
| 			history: [], | ||||
| 			historyAt: 0, | ||||
| 		})) | ||||
| 		bufferResetStats(b) | ||||
| 	} | ||||
| 	b.hideUnimportant = e.hideUnimportant | ||||
| @ -247,6 +251,9 @@ rpc.addEventListener('BufferActivate', event => { | ||||
| 		old.inputStart = textarea.selectionStart | ||||
| 		old.inputEnd = textarea.selectionEnd | ||||
| 		old.inputDirection = textarea.selectionDirection | ||||
| 		// Note that we effectively overwrite the newest line
 | ||||
| 		// with the current textarea contents, and jump there.
 | ||||
| 		old.historyAt = old.history.length | ||||
| 	} | ||||
| 
 | ||||
| 	textarea.value = '' | ||||
| @ -324,6 +331,15 @@ for (let i = 0; i < 24; i++) { | ||||
| 
 | ||||
| // ---- UI ---------------------------------------------------------------------
 | ||||
| 
 | ||||
| // On macOS, the Alt/Option key transforms characters, which basically breaks
 | ||||
| // all event.altKey shortcuts, so require combining them with Control as well
 | ||||
| // on that system.
 | ||||
| function hasShortcutModifiers(event) { | ||||
| 	// This method of detection only works with Blink browsers, as of writing.
 | ||||
| 	return event.altKey && !event.metaKey && | ||||
| 		(navigator.userAgentData?.platform === 'macOS') === event.ctrlKey | ||||
| } | ||||
| 
 | ||||
| let linkRE = [ | ||||
| 	/https?:\/\//, | ||||
| 	/([^\[\](){}<>"'\s]|\([^\[\](){}<>"'\s]*\))+/, | ||||
| @ -584,27 +600,71 @@ let Input = { | ||||
| 			bufferName: bufferCurrent, | ||||
| 			text: textarea.value, | ||||
| 		}) | ||||
| 
 | ||||
| 		// b.history[b.history.length] is virtual, and is represented
 | ||||
| 		// either by textarea contents when it's currently being edited,
 | ||||
| 		// or by b.input in all other cases.
 | ||||
| 		let b = buffers.get(bufferCurrent) | ||||
| 		b.history.push(textarea.value) | ||||
| 		b.historyAt = b.history.length | ||||
| 		textarea.value = '' | ||||
| 		return true | ||||
| 	}, | ||||
| 
 | ||||
| 	previous: textarea => { | ||||
| 		let b = buffers.get(bufferCurrent) | ||||
| 		if (b === undefined) | ||||
| 			return false | ||||
| 
 | ||||
| 		// TODO: Ding otherwise.
 | ||||
| 		if (b.historyAt > 0) { | ||||
| 			if (b.historyAt == b.history.length) | ||||
| 				b.input = textarea.value | ||||
| 			textarea.value = b.history[--b.historyAt] | ||||
| 		} | ||||
| 		return true | ||||
| 	}, | ||||
| 
 | ||||
| 	next: textarea => { | ||||
| 		let b = buffers.get(bufferCurrent) | ||||
| 		if (b === undefined) | ||||
| 			return false | ||||
| 
 | ||||
| 		// TODO: Ding otherwise.
 | ||||
| 		if (b.historyAt < b.history.length) { | ||||
| 			if (++b.historyAt == b.history.length) | ||||
| 				textarea.value = b.input | ||||
| 			else | ||||
| 				textarea.value = b.history[b.historyAt] | ||||
| 		} | ||||
| 		return true | ||||
| 	}, | ||||
| 
 | ||||
| 	onKeyDown: event => { | ||||
| 		// TODO: And perhaps on other actions, too.
 | ||||
| 		rpc.send({command: 'Active'}) | ||||
| 
 | ||||
| 		let textarea = event.currentTarget | ||||
| 		let handled = false | ||||
| 		switch (event.keyCode) { | ||||
| 		case 9: | ||||
| 			if (!event.ctrlKey && !event.metaKey && !event.altKey && | ||||
| 				!event.shiftKey) | ||||
| 		if (hasShortcutModifiers(event)) { | ||||
| 			switch (event.key) { | ||||
| 			case 'p': | ||||
| 				handled = Input.previous(textarea) | ||||
| 				break | ||||
| 			case 'n': | ||||
| 				handled = Input.next(textarea) | ||||
| 				break | ||||
| 			} | ||||
| 		} else if (!event.altKey && !event.ctrlKey && !event.metaKey && | ||||
| 				!event.shiftKey) { | ||||
| 			switch (event.keyCode) { | ||||
| 			case 9: | ||||
| 				handled = Input.complete(textarea) | ||||
| 			break | ||||
| 		case 13: | ||||
| 			if (!event.ctrlKey && !event.metaKey && !event.altKey && | ||||
| 				!event.shiftKey) | ||||
| 				break | ||||
| 			case 13: | ||||
| 				handled = Input.submit(textarea) | ||||
| 			break | ||||
| 				break | ||||
| 			} | ||||
| 		} | ||||
| 		if (handled) | ||||
| 			event.preventDefault() | ||||
| @ -636,28 +696,34 @@ let Main = { | ||||
| window.addEventListener('load', () => m.mount(document.body, Main)) | ||||
| 
 | ||||
| document.addEventListener('keydown', event => { | ||||
| 	if (rpc.ws == undefined || event.ctrlKey || event.metaKey) | ||||
| 	if (rpc.ws == undefined || !hasShortcutModifiers(event)) | ||||
| 		return | ||||
| 
 | ||||
| 	if (event.altKey && event.key == 'Tab') { | ||||
| 	switch (event.key) { | ||||
| 	case 'Tab': | ||||
| 		if (bufferLast !== undefined) | ||||
| 			bufferActivate(bufferLast) | ||||
| 	} else if (event.altKey && event.key == 'h') { | ||||
| 		break | ||||
| 	case 'h': | ||||
| 		bufferToggleLog() | ||||
| 	} else if (event.altKey && event.key == 'a') { | ||||
| 		break | ||||
| 	case 'a': | ||||
| 		for (const [name, b] of buffers) | ||||
| 			if (name !== bufferCurrent && b.newMessages) { | ||||
| 				bufferActivate(name) | ||||
| 				break | ||||
| 			} | ||||
| 	} else if (event.altKey && event.key == '!') { | ||||
| 		break | ||||
| 	case '!': | ||||
| 		for (const [name, b] of buffers) | ||||
| 			if (name !== bufferCurrent && b.highlighted) { | ||||
| 				bufferActivate(name) | ||||
| 				break | ||||
| 			} | ||||
| 	} else | ||||
| 		break | ||||
| 	default: | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	event.preventDefault() | ||||
| }) | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user