# Custom iTerm2 Option+Arrow keybindings (placed at the very end) bindkey "\e[1;3C" forward-word # Option + Right Arrow bindkey "\e[1;3D" backward-word # Option + Left Arrow
class Programmer implements ANiceHumble, Person {
Code as we know it. Open source stuff goes here
Thursday, July 17, 2025
iTerm2 one word to the right/left
Thursday, June 26, 2025
React: Memoized unchanged properties
import { memo, useMemo, useState } from 'react';
import './App.css';
type Person = {
firstName: string;
lastName: string;
};
type WithInitial = Person & {
initials: string;
};
const initialPeople: Person[] = [
{ firstName: 'John', lastName: 'Lennon' },
{ firstName: 'Paul', lastName: 'McCartney' },
{ firstName: 'George', lastName: 'Harrison' },
{ firstName: 'Ringo', lastName: 'Starr' },
];
function App() {
const [theme, setTheme] = useState('light');
const [people, setPeople] = useState(initialPeople);
const [counter, setCounter] = useState(0);
return (
<div>
<button onClick={() => setCounter((prev) => prev + 1)}>
Increase {counter}
</button>
<button onClick={toggleTheme}>Toggle {theme}</button>
<button onClick={addRandomPerson}>Add Random Person</button>
<ListComponentNoMemo {...{ theme, people }} />
<ListComponentWithMemo {...{ theme, people }} />
<MemoListComponentWithMemo {...{ theme, people }} />
</div>
);
function toggleTheme() {
setTheme((prev) => (prev === 'light' ? 'dark' : 'light'));
}
function addRandomPerson() {
setPeople((prev) => [
...prev,
{
firstName: Math.ceil(Math.random() * 100).toString(),
lastName: Math.ceil(Math.random() * 100).toString(),
},
]);
}
}
function ListComponentNoMemo({
theme,
people,
}: {
theme: string;
people: Person[];
}) {
console.log('ListComponentNoMemo');
const withInitials = processInitials('noMemo', people);
return (
<>
<p>Theme is {theme}</p>
<ul>
{withInitials.map((e, index) => (
<li key={index}>
{e.firstName} {e.lastName} {e.initials}
</li>
))}
</ul>
</>
);
}
function ListComponentWithMemo({
theme,
people,
}: {
theme: string;
people: Person[];
}) {
console.log('ListComponenWithMemo');
const withInitials = useMemo(
() => processInitials('withMemo', people),
[people]
);
return (
<>
<p>Theme is {theme}</p>
<ul>
{withInitials.map((e, index) => (
<li key={index}>
{e.firstName} {e.lastName} {e.initials}
</li>
))}
</ul>
</>
);
}
const MemoListComponentWithMemo = memo(function ({
theme,
people,
}: {
theme: string;
people: Person[];
}) {
console.log('MemoListComponenWithMemo');
const withInitials = useMemo(
() => processInitials('memoWithMemo', people),
[people]
);
return (
<>
<p>Theme is {theme}</p>
<ul>
{withInitials.map((e, index) => (
<li key={index}>
{e.firstName} {e.lastName} {e.initials}
</li>
))}
</ul>
</>
);
});
function processInitials(calledFrom: string, people: Person[]): WithInitial[] {
console.log({ calledFrom });
return people.map((e) => ({
firstName: e.firstName,
lastName: e.lastName,
initials: toInitials(e),
}));
function toInitials(p: Person) {
console.log('emphasized expensiveness');
return p.firstName[0] + p.lastName[0];
}
}
export default App;
Live: https://stackblitz.com/edit/vitejs-vite-drznsqwc?file=src%2FApp.tsx
Strongly-typed MUI Form Helper
import React from 'react';
type UnifiedChangeEvent =
| React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
| React.ChangeEvent<{ name?: string; value: unknown }>
| (Event & { target: { name: string; value: unknown } });
export type ErrorObject<T> = Partial<Record<keyof T, string | boolean>>;
function handleInputChange<T>(
values: T,
setValues: (o: T) => void,
fieldName: keyof T
) {
return {
onChange(event: UnifiedChangeEvent) {
const { name, value } = event.target;
setValues({ ...values, [name!]: value });
},
name: fieldName,
value: values[fieldName],
};
}
export function setupInputChange<T>(values: T, setValues: (o: T) => void) {
return (fieldName: keyof T) =>
handleInputChange(values, setValues, fieldName);
}
function handleErrorChange<T>(errors: T, fieldName: keyof T) {
return errors[fieldName] && { error: true, helperText: errors[fieldName] };
}
export function setupErrorChange<T>(errors: T) {
return (fieldName: keyof T) => handleErrorChange(errors, fieldName);
}
export function isValid<T>(errors: ErrorObject<T>): boolean {
for (const v of Object.values(errors)) {
if (v) {
return false;
}
}
return true;
}
export function hasErrors<T>(errors: ErrorObject<T>) {
return !isValid(errors);
}
export function setupInputProps<T, U>(
values: T,
setValues: (o: T) => void,
errors: ErrorObject<U>,
setErrors: (o: ErrorObject<U>) => void
) {
return {
handleInputProps: (fieldName: keyof T & keyof U) => ({
...handleInputChange(values, setValues, fieldName),
...handleErrorChange(errors, fieldName),
}),
checkValidators: (errors: ErrorObject<U>) => {
setErrors(errors);
return isValid(errors);
},
};
}
Example use:
import { FormEvent, useState } from 'react';
import {
Button,
TextField,
} from '@mui/material';
import './App.css';
import { setupInputProps, type ErrorObject } from './utils/form';
class DonationCandidateDto {
donationCandidateId = 0;
fullname = '';
mobile = '';
email = '';
age = 0;
bloodGroup = '';
address = '';
active = false;
}
const initialFieldValues: DonationCandidateDto = {
donationCandidateId: 0,
fullname: '',
mobile: '',
email: '',
age: 0,
bloodGroup: '',
address: '',
active: false,
};
type FormType = typeof initialFieldValues;
// out-of-band validations
type OtherStates = {
areZombiesInLab: boolean;
day?: number;
};
function App() {
const [values, setValues] = useState(structuredClone(initialFieldValues));
const [errors, setErrors] = useState<ErrorObject<FormType & OtherStates>>({});
const { handleInputProps, checkValidators } = setupInputProps(
values,
setValues,
errors,
setErrors
);
return (
<div>
<h1>Hello world</h1>
<form autoComplete="off" noValidate onSubmit={handleSubmit}>
<div>
<TextField
label="Full name"
required={true}
{...handleInputProps('fullname')}
/>
</div>
<br />
<div>
<TextField label="Age" {...handleInputProps('age')} />
</div>
<div>
{errors.areZombiesInLab && (
<div style={{ color: 'red' }}>{errors.areZombiesInLab}</div>
)}
</div>
<br />
<div>
<Button color="primary" type="submit" variant="contained">
Submit
</Button>
<Button type="submit" color="inherit" variant="contained">
Reset
</Button>
</div>
</form>
<hr />
{/* {JSON.stringify(values)} */}
</div>
);
function handleSubmit(event: FormEvent<HTMLFormElement>) {
event.preventDefault();
const isValid = checkValidators({
fullname: !values.fullname && 'Must have full name',
age: !(values.age >= 18) && 'Must be an adult',
// areZombiesInLab: true && 'Zombies in lab',
});
if (!isValid) {
return;
}
// POST/PUT here
}
}
export default App;
If the name is not existing from the DTO, it will not compile:
Monday, June 23, 2025
macOS vs Windows keys
Function | macOS | Windows |
Beginning of Page | Command+Up (Home if non-typing like browser page) | Ctrl+Home |
Ending of Page | Command+Down (End if non-typing like browser page) | Ctrl+End |
Beginning of line on screen | Command+Left (or fn+Left) | Home |
Ending of line on screen | Command+Right (or fn+Right) | End |
Beginning of line | Control+A | Not supported |
Ending of line | Control+E | Not supported |
Page Up | Fn+Up (Page Up if available) (Alt+Up if non-typing like browser page) | Page Up |
Page Down | Fn+Down (Page Down if available) (Alt+Down if non-typing like browser page) | Page Down |
macOS when on typing (e.g., IDE, word processor)
Keys | Function |
Home | Beginning of line on screen |
End | Ending of line on screen |
Alt+Up | VSCode: Move one line up. Word: Go to previous paragraph |
Alt+Down | VSCode: Move one line down. Word: Go to next paragraph |
JetBrains: | Beginning of Page |
JetBrains: | Ending of Page |
