…by visiting the pub?
Working HTML - Verbose Comments
<!DOCTYPE html>
<html>
<head>
<title>Prompt With List (HTML)</title>
<style id="dynamic-styles">
/* This section is reserved for styles that are added dynamically at runtime */
/* Disallow text selection when dragging, enhancing the user experience */body.dragging {
user-select: none;
}
</style>
<script>
// Create a reference to KeyboardMaestro, this helps avoid unnecessary global scope pollution
var KeyboardMaestro = window.KeyboardMaestro;
// Initialize an array to keep track of indices of selected items
var selectedIndices = [];
// Initialize an array to hold the selected items themselves
var selectedItems = [];
// A flag to determine if an item is currently being dragged
var isDragging = false;
// A flag to determine if an item is being marked or selected
var marking = false;
/**
* Toggle the selection of a given item.
* @param {HTMLElement} item - The list item to select or unselect.
* @param {boolean} shouldSelect - Explicit instruction to select (true) or unselect (false). If undefined, toggle.
*/
function selectItem(item, shouldSelect) {
// Convert the NodeList of 'li' elements to an array and get the index of the provided item
var itemIndex = Array.from(document.querySelectorAll('li')).indexOf(item);
// Check if the item's index is already in the selectedIndices array
var index = selectedIndices.indexOf(itemIndex);
if (index > -1) {
// If the item is already selected
if (shouldSelect !== true) {
// If the function was called with explicit instruction not to select, remove the item from selections
selectedIndices.splice(index, 1);
item.firstChild.checked = false; // Uncheck the checkbox associated with the item
}
} else {
// If the item is not currently selected
if (shouldSelect !== false) {
// If the function was called without explicit instruction to unselect, add the item to selections
selectedIndices.push(itemIndex);
item.firstChild.checked = true; // Check the checkbox associated with the item
}
}
// Focus on the input element of type text, likely for further user input or interactions
document.querySelector('input[type="text"]').focus();
// Highlight the clicked item for visual feedback
var selectedItem = document.querySelector('li.selected');
if (selectedItem) {
// If an item is already highlighted, remove the 'selected' class
selectedItem.classList.remove('selected');
}
// Add the 'selected' class to the current item
item.classList.add('selected');
}
// Initialize variables to keep track of the starting and ending items when using shift+click for range selection
var shiftSelectStartItem = null;
var shiftSelectEndItem = null;
/**
* Handle keydown events for navigation and actions within the list.
* @param {Event} event - The keydown event.
*/
function keydown(event) {
// Convert the list items NodeList to an array
var items = Array.from(document.querySelectorAll('li'));
// Define keys of interest
var keys = ['ArrowDown', 'ArrowUp', 'Enter', 'Escape', 'PageUp', 'PageDown'];
// Prevent default actions for these key events to ensure custom behaviors
if (keys.includes(event.key)) {
event.preventDefault();
}
// Find the currently selected item
var selectedItem = document.querySelector('li.selected');
// Filter out non-visible list items
var visibleItems = items.filter(function(item) { return item.style.display !== 'none'; });
// Get the index of the currently selected item within the visible items array
var selectedIndex = visibleItems.indexOf(selectedItem);
// Handle arrow keys for navigation within the list
if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
// Calculate the new index based on the arrow key pressed
var newIndex = event.key === 'ArrowDown' ? selectedIndex + 1 : selectedIndex - 1;
// Check if the new index is within bounds
if (newIndex >= 0 && newIndex < visibleItems.length) {
// If shift key is pressed, mark a range of items
if (event.shiftKey) {
if (!window.selectedItemStart) {
window.selectedItemStart = selectedItem;
}
markRange(window.selectedItemStart, visibleItems[newIndex]);
} else {
removeHighlightFromAll();
}
// Update the selected state
selectedItem.classList.remove('selected');
visibleItems[newIndex].classList.add('selected');
scrollSelectedItemIntoView();
window.selectedItemStart = visibleItems[newIndex];
}
}
// Handle Enter key for marking items or submitting the selection
else if (event.key === 'Enter') {
// If shift key is pressed, handle range selection
if (event.shiftKey) {
if (shiftSelectStartItem && shiftSelectEndItem) {
// Logic to determine if all visible items in the range are marked
var allMarked = true;
var visibleIndices = visibleItems.map(function(visibleItem) {
return Array.from(document.querySelectorAll('li')).indexOf(visibleItem);
});
var startIndex = visibleIndices.indexOf(selectedIndices[selectedIndices.length - 1]);
var endIndex = visibleIndices.indexOf(visibleIndices[visibleIndices.length - 1]);
// Check each item in the range
for (var i = Math.min(startIndex, endIndex); i <= Math.max(startIndex, endIndex); i++) {
if (!document.querySelectorAll('li')[visibleIndices[i]].firstChild.checked) {
allMarked = false;
break;
}
}
// Toggle the mark state for items in the range
for (var i = Math.min(startIndex, endIndex); i <= Math.max(startIndex, endIndex); i++) {
selectItem(document.querySelectorAll('li')[visibleIndices[i]], !allMarked);
}
} else {
// If no range is defined, just mark the single selected item
selectItem(selectedItem);
}
} else {
// Handle single or multiple item selection and submit
if (selectedIndices.length === 0) {
selectedIndices.push(Array.from(document.querySelectorAll('li')).indexOf(selectedItem));
}
// Sort selectedIndices based on the original order in promptList
var promptList = KeyboardMaestro.GetVariable('Local__Prompt List').split('\n');
selectedIndices.sort(function(a, b) {
return a - b;
});
// Convert indices back to their respective items
var selectedItems = selectedIndices.map(function(index) {
return document.querySelectorAll('li')[index].dataset.fullText;
}).join('\n');
// Set and submit the selected items
KeyboardMaestro.SetVariable('Local__PromptChoice', selectedItems);
KeyboardMaestro.Submit('OK');
}
}
// Handle Escape key for canceling
else if (event.key === 'Escape') {
KeyboardMaestro.Submit('Cancel');
}
// Handle Page Up and Page Down for quick scrolling
if (event.key === 'PageUp' || event.key === 'PageDown') {
var list = document.querySelector('ul');
var visibleHeight = list.offsetHeight; // Get the currently visible height of the ul
// Adjust the scroll position based on the key pressed
if (event.key === 'PageUp') {
list.scrollTop -= visibleHeight;
} else if (event.key === 'PageDown') {
list.scrollTop += visibleHeight;
}
// Prevent default behavior
event.preventDefault();
}
}
/**
* Handle the logic for when an item in the list is clicked.
* @param {Event} event - The click event object.
*/
function handleItemClick(event) {
if (isDragging) {
return; // If currently dragging an item, exit without performing any action.
}
var clickedItem = event.currentTarget; // Get the item that was clicked.
var selectedItem = document.querySelector('li.selected'); // Find the currently selected item, if any.
// If the shift key is pressed and there's a starting item for range selection
if (event.shiftKey && window.selectedItemStart) {
markRange(window.selectedItemStart, clickedItem); // Mark all items in the range.
} else {
// If the shift key wasn't pressed or no initial item was set, this clicked item becomes the starting point for future shift+click actions.
window.selectedItemStart = clickedItem;
// If there's a previously selected item, remove its selected status.
if (selectedItem) {
selectedItem.classList.remove('selected');
}
// Set the clicked item as the selected item.
clickedItem.classList.add('selected');
}
}
/**
* Highlights a range of items between the start and end items.
* @param {HTMLElement} startItem - The item where the range starts.
* @param {HTMLElement} endItem - The item where the range ends.
*/
function highlightRange(startItem, endItem) {
var items = Array.from(document.querySelectorAll('li'));
var startIndex = items.indexOf(startItem);
var endIndex = items.indexOf(endItem);
// Clear highlights from all items.
items.forEach(function(item) {
item.classList.remove('highlighted');
});
// Apply the highlight to items within the defined range.
for (var i = Math.min(startIndex, endIndex); i <= Math.max(startIndex, endIndex); i++) {
items[i].classList.add('highlighted');
}
}
/**
* Removes highlights from all items in the list.
*/
function removeHighlightFromAll() {
var items = Array.from(document.querySelectorAll('li'));
// Clear highlights from all items.
items.forEach(function(item) {
item.classList.remove('highlighted');
});
}
/**
* Marks a range of visible items between the start and end items.
* @param {HTMLElement} startItem - The starting item of the range.
* @param {HTMLElement} endItem - The ending item of the range.
*/
function markRange(startItem, endItem) {
// Fetch all visible items from the list.
let visibleItems = Array.from(document.querySelectorAll('li')).filter(function(item) {
return item.style.display !== 'none';
});
let startIndex = visibleItems.indexOf(startItem);
let endIndex = visibleItems.indexOf(endItem);
// Ensure the start index is smaller than the end index.
if (startIndex > endIndex) {
[startIndex, endIndex] = [endIndex, startIndex]; // Swap the indices.
}
// Mark each item in the range.
for (let i = startIndex; i <= endIndex; i++) {
selectItem(visibleItems[i], true);
}
}
/**
* Filters the list based on the input provided by the user.
*/
function filterList() {
// Fetch the filter text and convert it to lowercase for case-insensitive searching.
var filter = document.querySelector('input').value.toLowerCase();
// Split the input text into individual terms for multiple keyword searching.
var terms = filter.split(' ');
var items = document.querySelectorAll('li');
// Loop through each list item.
for (var i = 0; i < items.length; i++) {
var itemText = items[i].textContent.toLowerCase(); // Get the text of the item and convert it to lowercase.
// Check if every term in the filter is present in the item's text.
if (terms.every(function(term) { return itemText.indexOf(term) > -1; })) {
// If all terms are found within the item text, display the item.
items[i].style.display = '';
} else {
// If any term is missing from the item text, hide the item.
items[i].style.display = 'none';
}
}
}
/**
* Initializes the application, sets up the list based on the KeyboardMaestro variables, and attaches event listeners.
*/
function init() {
// Start with no initial selected item.
window.selectedItemStart = null;
// Set the font and font-size for the body element from the KeyboardMaestro variables.
document.body.style.setProperty('--font', KeyboardMaestro.GetVariable('Local__Prompt Font'));
document.body.style.setProperty('--font-size', KeyboardMaestro.GetVariable('Local__Font Size') + 'px');
// Extract the dimensions of the prompt from the KeyboardMaestro variable and calculate widths and heights.
var promptSize = KeyboardMaestro.GetVariable('Local__Prompt Size').split(',');
var inputWidth = promptSize[0] - 40;
var listWidth = promptSize[0] - 40;
var listHeight = promptSize[1] - 100;
// Adjust the size of the input and set its placeholder.
var input = document.querySelector('input[type="text"]');
input.style.width = inputWidth + 'px';
input.placeholder = KeyboardMaestro.GetVariable('Local__Placeholder'); // Set placeholder
// Adjust the dimensions of the list.
var list = document.querySelector('ul');
list.style.width = listWidth + 'px';
list.style.height = listHeight + 'px';
// Process each line from the KeyboardMaestro prompt list variable.
var promptList = KeyboardMaestro.GetVariable('Local__Prompt List').split('\n');
for (var i = 0; i < promptList.length; i++) {
var parts = promptList[i].split('__'); // Split each line at '__'
// Create new list item and checkbox elements.
var li = document.createElement('li');
var checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.disabled = true; // Make sure checkbox is not interactive.
li.appendChild(checkbox);
// Add a space after the checkbox.
var space = document.createTextNode(' ');
li.appendChild(space);
// If '__' is present in the line, use the part after for display and store the part before in a data attribute.
// If not, use the whole line for both display and output.
var text;
if (parts[1]) {
text = document.createTextNode(parts[1]);
li.dataset.fullText = parts[0];
} else {
text = document.createTextNode(parts[0]);
li.dataset.fullText = parts[0];
}
li.appendChild(text);
// Attach a click event listener to the list item.
li.addEventListener('click', function(event) {
selectItem(event.currentTarget);
});
// Attach a double-click event listener to the list item.
li.addEventListener('dblclick', function(event) {
selectedItems = [event.currentTarget.dataset.fullText];
KeyboardMaestro.SetVariable('Local__PromptChoice', selectedItems.join('\n'));
KeyboardMaestro.Submit('OK');
});
// Add the created list item to the list.
list.appendChild(li);
}
// Mark the first item in the list as selected.
list.firstChild.classList.add('selected');
// Add event listeners to handle input changes and keydown events.
input.addEventListener('input', filterList);
window.addEventListener('keydown', keydown);
window.addEventListener('mouseup', handleMouseUp);
bindClickEvents();
}
/**
* Scrolls the list to ensure the selected item is visible in the view.
*/
function scrollSelectedItemIntoView() {
// Get the selected item and the list container.
var selectedItem = document.querySelector('li.selected');
var list = document.querySelector('ul');
// Calculate the positions and dimensions of the list and the selected item.
var listRect = list.getBoundingClientRect();
var itemRect = selectedItem.getBoundingClientRect();
// Check if the selected item is below the middle of the view.
if (itemRect.top > listRect.top + listRect.height / 2) {
// Adjust the scroll position to bring the item to the middle.
list.scrollTop += itemRect.top - listRect.top - listRect.height / 2 + itemRect.height / 2;
} else if (itemRect.bottom < listRect.top + listRect.height / 2) {
// If the selected item is above the middle of the view, adjust the scroll position to bring the item to the middle.
list.scrollTop -= listRect.top + listRect.height / 2 - itemRect.bottom + itemRect.height / 2;
}
}
// This function sets the theme of the page based on the user's preferred color scheme.
function setTheme() {
// Check if the user's device prefers a dark color scheme.
var isDarkMode = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
// Get the stylesheet where dynamic styles will be inserted.
var styles = document.getElementById('dynamic-styles');
// Check if the preferred color scheme is dark.
if (isDarkMode) {
// Insert styles for dark mode.
styles.innerHTML = `
/* Styles for the body element when the dark mode is active. */
body {
/* Font styles are determined by CSS variables. */
font-family: var(--font);
font-size: var(--font-size);
/* Background color is a near-black with 85% opacity. */
background-color: rgba(0, 0, 0, 0.85);
/* Text color is set to white. */
color: white;
/* Flex display for layout purposes. */
display: flex;
flex-direction: column;
align-items: center;
}
/* Styling for text input elements in dark mode. */
input[type="text"] {
width: 500px;
padding: 12px; /* Padding has been updated recently. */
margin: 10px 0;
box-sizing: border-box; /* This makes the padding and border included in the total width and height. */
border: none;
/* Explicitly setting the bottom border to none. */
border-bottom: none;
background-color: #3a3a3c;
color: white; /* Text color was added to be white. */
font-size: var(--font-size);
outline: none; /* Removes the browser default focus outline. */
}
/* Styling for unordered lists. */
ul {
list-style-type: none; /* Removes the default bullet points. */
margin: 0;
padding: 0;
width: 500px;
height: 425px; /* Height has been updated recently. */
overflow-y: auto; /* Allows for vertical scrolling if the content exceeds the height. This was added recently. */
}
/* Styling for list items. */
li {
padding: 6px; /* Padding was updated recently. */
cursor: pointer; /* Changes the cursor to a hand pointer on hover. */
font-size: var(--font-size);
line-height: 1; /* Ensures consistent line height. */
white-space: nowrap; /* Prevents the text from wrapping to the next line. */
overflow: hidden; /* Cuts off any overflow content. */
text-overflow: ellipsis; /* If the content is cut off, it ends with an ellipsis (three dots). */
}
/* Styling for a selected list item. */
li.selected {
background-color: #3a3a3c; /* Changes the background color. */
}
/* Styling for a highlighted list item. */
li.highlighted {
background-color: #3a3a3c; /* This color can be customized as needed. */
}
/* Ensures that list items and the body element (when dragging) cannot be selected by the user. */
li, body.dragging {
user-select: none;
}
/* Styling for checkboxes. */
input[type="checkbox"] {
position: relative;
appearance: none; /* Removes the browser default appearance. */
background-color: transparent;
color: white;
margin-right: 10px; /* Provides space to the right. */
width: 3px; /* Explicitly sets a narrow width. */
height: 6px; /* Explicitly sets a short height. */
vertical-align: middle; /* Vertically centers the checkbox. */
}
/* Styling for checked checkboxes. This creates a custom checkmark appearance. */
input[type="checkbox"]:checked::before {
content: "";
position: absolute;
top: calc(50% - 2px);
left: 50%;
transform: translate(-50%, -50%) rotate(45deg);
display: inline-block;
width: 3px;
height: 6px;
border-width: 0 2px 2px 0;
border-style: solid;
border-color: white;
}
/* Scrollbar styles specific to WebKit browsers. */
::-webkit-scrollbar {
width: 12px;
}
::-webkit-scrollbar-track {
border-radius: 10px; /* Gives the track a rounded appearance. */
background: #2f2f2f;
}
::-webkit-scrollbar-thumb {
border-radius: 10px; /* Gives the thumb (the draggable part of the scrollbar) a rounded appearance. */
background: #888;
}
/* Changes the thumb color when hovered over. */
::-webkit-scrollbar-thumb:hover {
background: #555;
}
}
`;
} else {
// Insert styles for light mode.
styles.innerHTML = `
/* Styles for the body element when the light mode is active. */
body {
/* Font styles are determined by CSS variables. */
font-family: var(--font);
font-size: var(--font-size);
/* The background color is a light gray with 95% opacity. */
background-color: rgba(192, 192, 192, 0.95);
/* Text color is set to a darker gray. */
color: #444444;
/* Utilizing flex for layout alignment purposes. */
display: flex;
flex-direction: column;
align-items: center;
}
/* Styling specific to text input elements in light mode. */
input[type="text"] {
width: 500px;
padding: 12px; /* Updated padding value. */
margin: 8px 0; /* Margin differs from dark mode. */
box-sizing: border-box;
border: none;
/* Explicitly setting the bottom border to none. */
border-bottom: none;
background-color: #FFFFFF;
color: #444444; /* Text color matches the body text color. */
font-size: var(--font-size);
outline: none; /* Removes the browser default focus outline. */
}
/* Styling for unordered lists. */
ul {
list-style-type: none; /* No bullets. */
margin: 0;
padding: 0;
width: 500px;
height: 425px; /* Updated height value. */
overflow-y: auto; /* Enables vertical scrolling. */
}
/* Styling for list items. */
li {
padding: 6px; /* Updated padding value. */
cursor: pointer; /* Cursor appears as a hand on hover. */
font-size: var(--font-size);
line-height: 1; /* Consistent line height across list items. */
white-space: nowrap; /* Text does not wrap to next line. */
overflow: hidden; /* Conceals overflowing content. */
text-overflow: ellipsis; /* Displays an ellipsis for overflowed content. */
}
/* Potential hover effect for list items - currently commented out. */
/* li:hover {
background-color: #FFFFFF;
} */
/* Background color for selected list items. */
li.selected {
background-color: #EFEFEF;
}
/* Background color for highlighted list items. */
li.highlighted {
background-color: #EFEFEF; /* This color is customizable. */
}
/* Ensures list items and the body element (during a dragging action) cannot be selected. */
li, body.dragging {
user-select: none;
}
/* Custom styling for checkboxes. */
input[type="checkbox"] {
position: relative;
appearance: none; /* Removes default browser styling. */
background-color: transparent;
color: white;
margin-right: 10px; /* Extra space to the right of the checkbox. */
width: 3px; /* Explicitly narrow width. */
height: 6px; /* Explicitly short height. */
vertical-align: middle; /* Centers the checkbox vertically. */
}
/* Styling for checked state of checkboxes, creating a custom blue checkmark. */
input[type="checkbox"]:checked::before {
content: "";
position: absolute;
top: calc(50% - 2px);
left: 50%;
transform: translate(-50%, -50%) rotate(45deg);
display: inline-block;
width: 3px;
height: 6px;
border-width: 0 2px 2px 0;
border-style: solid;
border-color: #3399FF; /* A shade of blue. */
}
/* Scrollbar styles specific to WebKit browsers. */
::-webkit-scrollbar {
width: 12px;
}
::-webkit-scrollbar-track {
border-radius: 10px; /* Rounded track. */
background: #888;
}
::-webkit-scrollbar-thumb {
border-radius: 10px; /* Rounded thumb. */
background: #666666; /* Darker thumb. */
}
/* Darker thumb color when hovered over. */
::-webkit-scrollbar-thumb:hover {
background: #555;
}
}
`;
}
}
// Listen for changes in the user's preferred color scheme and update the theme accordingly.
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', setTheme);
// This function binds various click events to the list items.
function bindClickEvents() {
// Get all list items on the page.
var listItems = document.querySelectorAll('li');
// Loop over each list item and bind events.
listItems.forEach(function(item) {
item.addEventListener('click', handleItemClick);
item.addEventListener('mousedown', handleMouseDown);
item.addEventListener('mouseup', handleMouseUp);
item.addEventListener('mouseover', handleMouseOver);
});
}
// Handle the mousedown event on a list item.
function handleMouseDown(event) {
event.preventDefault();
// Set the isDragging flag to true, indicating that dragging has started.
isDragging = true;
// Check or uncheck the checkbox inside the list item based on its current state.
marking = !event.target.querySelector('input[type="checkbox"]').checked;
// Mark or unmark the initial item where mousedown happened.
var checkbox = event.currentTarget.querySelector('input[type="checkbox"]');
checkbox.checked = marking;
// Add a class to the body indicating dragging is happening.
document.body.classList.add('dragging');
}
// Handle the mouseup event on a list item.
function handleMouseUp() {
// Set the isDragging flag to false, indicating that dragging has ended.
isDragging = false;
// Remove the dragging class from the body.
document.body.classList.remove('dragging');
}
// Handle the mouseover event on a list item.
function handleMouseOver(event) {
// Only proceed if dragging is in progress.
if (isDragging) {
// Check or uncheck the checkbox inside the list item based on the marking flag.
var checkbox = event.currentTarget.querySelector('input[type="checkbox"]');
checkbox.checked = marking;
}
}
</script>
</head>
<!-- When the body is loaded, three functions are triggered: -->
<!-- 1. The window size is adjusted based on the value of the "Local__Prompt Size" variable from KeyboardMaestro. -->
<!-- 2. The theme is set using the setTheme() function. -->
<!-- 3. The init() function is called to presumably initialize some other features or settings. -->
<body onload='window.KeyboardMaestro.ResizeWindow(window.KeyboardMaestro.GetVariable("Local__Prompt Size")); setTheme(); init()'>
<!-- This is a spacing div. It adds a 5px vertical space for layout purposes. -->
<!-- Note: The "updated" comment indicates that this div's style or its existence is probably a recent change. -->
<div style="height: 5px;"></div> <!-- updated -->
<!-- This is an input text field. -->
<!-- When the user types into this field, the filterList() function is called to presumably filter or search through a list. -->
<!-- The "autofocus" attribute ensures that this input field is focused on when the page loads, meaning the user can start typing immediately. -->
<input type="text" oninput="filterList()" autofocus>
<!-- An empty unordered list. This will likely be populated dynamically based on the input or other processes. -->
<ul></ul>
</body>
</html>
Hey Neil (@noisneil)
I think my emoji’s are speaking for me - giving you a reaction on the verbose commented version of your HTML Prompt code….
Now I understand why you are done with it for now and some time in the nearby future… this is - speaking for me here - quite a monster …
But - even though I the fact that this code is a monster for me - the amount of time you’ve put into commenting everything out is still quite helpful and give room for anyone who is interested in code like this for learning purposes and for people like me whose want to learn as much as possible and whose like to spend days building their own references while learning.
Since I currently have not much time in the moment I’ll just say THANKS for posting this delightful and with extra effort commented source code - also a big shout out to you, Jim (@_jims) for requesting this …
Neil (@noisneil), I hope you don’t forget about the features I be asked for … you don’t have to implement them now … but my wish is that you maybe implement them in the nearby future when something like this is going to be less more a hassle … maybe I get it until then how I could do this by my self, maybe not … I don’t know … but we’ll see … there are many projects on my to do list I need such a skill set for and I am on my way learning this stuff …
Some last words for you - you can be very proud of you writing something like this…
Thank you so much sharing your time, effort and knowledge here building crazy and absolutely beautiful and cool things like this Macro.
Greetings from Germany
Tobias
Awesome, @noisneil. This is such a great resource for those of us that want to sharpen our skills with the Custom HTML Prompt action.
Amazing work!
I second that!
Would you comment about what it took to get the commented version working? -- I'm trying to learn how to get useful help from ChatGPT and very much appreciate simply hearing about other people's experiences, good, bad, convoluted, etc.
Sure. All I did was use the syntax checker in BBEdit, which pointed out a few spots where GPT had missed out or added random curly brackets and other seemingly minor textual errors. Can't believe I didn't think of it sooner!
Hello Neil (@noisneil),
Just started looking further into this monster of code last night for 2-3 hours while I was taking a break of preparation of rebuilding my 1Password related Macro Workflow due to issues I ran into lately …
I have to repeat myself … this Code is quite a bummer though - very impressive work that you’ve done on this ….
I‘m taking my hat …
It‘s also quite snappy, too … the way this code works - even on my 13 years old MacBook Pro running High Sierra while having a heavy pressure on this little Baby pushing it to the max as far as I am able to …
Have a drink for me, too - the next time you’re going to a pub
cheers, my friend
Greetings from Germany
Tobias
Hey @noisneil, recently, as I’ve started to use your HTML prompt more and more, I’ve noticed that it is wrapping long lines, in spite of what I believe to be css
disabling that feature.
For instance, the following lines from the light and dark mode css
appear to be designed to prevent wrapping...
white-space: nowrap; /* Text does not wrap to next line. */
white-space: nowrap; /* Prevents the text from wrapping to the next line. */
But as you can see in the following screenshot, there are two addresses that were wrapped.
Any idea what the issue might be?
-Chris
Yep! I was retrieving the addresses from an sql database, and it was wrapping anything longer than 60 characters.
I modified the query to include the -wrap 0
like so...
sqlite3 /Users/cdthomer/Applications/Keyboard\ Maestro/Databases/Logs.db '.mode column -wrap 0' '.headers off' 'SELECT LOCATION FROM IST;'
...and it fixed the issue. Thanks man!
@noisneil Wow this is great, just what I was looking for. Thanks so much for sharing this.
I wonder if I could throw in another request? What about loading a list that has pre-selections? Perhaps similar to the Prompt for User Input Action with a 0 or 1 or something after the list item?
String1__String2|0|1
String1__String2|1|0
Thanks again for your work on this.
Hi. I don't fancy getting back into the weeds with this one I'm afraid. It was very tricky getting it to where it is now and I'm a bit reluctant to mess with it.
Sure no worries.
I did just notice that mouse click and dragging is checking the different items but not giving multiple results. Shift selecting with the Keyboard still seems ok. If I drag select with the mouse the checkbox appears. Then if I click one of the items I drag selected, the checkbox disappears and re-appears quickly (as though the checkbox was checked but the item was not selected or added to the 'selected' index).
Anyone else seeing this?
By the way I was seeing this issue before I made the modifications below Prompt With List... On Steroids! 💪🏼 - #55 by nok
If you or anyone else is interested, I made some minor changes to allow for 'pre-selecting' list items that have a '|1' at the end.
StringData1__StringDisplay1|1
StringData2|1
StringData3
etc.
Thanks a lot for this great work!
One question: Is there any chance to find out the content of the input, like you find the content of the input in the standard routine in the token "%PromptWithListText%" (token:PromptWithListText [Keyboard Maestro Wiki])?
The content of the input is user-defined. In this example, you can access it by referring to Local__List
.
Thank you for your answer, but I think there is a misunderstanding: I need what I have entered in the searchbar, not the list from which the entries are created. Just as what the user enters is stored in the original routine in the token "%PromptWithListText%".
The background is: I often call up my list several times in a row with the same search string, so that I want to preset the last search string in "Placeholder" when I call up the list again...
Oh, I see. No, that's not catered for I'm afraid. I could feasibly implement it, but it would mean complicating the output for the sake of very niche use cases, so I don't think it's justifiable.
OK and 1000 thanks for your efforts and support, anyway