Tuesday, January 9, 2024

Increase taskbar button width in Linux Mint 21

For my working VM, after ditching the sluggish Ubuntu 22 for the crazy good Mint Cinnamon 21, one of the customizations I wanted to make was the maximum width of the taskbar buttons. They seemed to narrow, while a lot of room was available.

After a couple minutes of searching, I found a direction in the Mint forums. The proposed solution is for Mint 20 – for Mint 21, it has a minor difference.

Edit, as root, the following file:

/usr/share/cinnamon/applets/grouped-window-list@cinnamon.org/constants.js

The value we’re after is MAX_BUTTON_WIDTH, which I increased from 150 to 210.

What caught my attention, though, is how all the layout building stuff is JavaScript.

Saturday, January 6, 2024

Installing dependencies of deb packages

A deb package can be installed on Linux by running:

sudo dpkg -i Package.deb

However, the installation may fail because dependencies are missing. In such cases, I found a tip which appears to work very well. After the failed dpkg command, run:

sudo apt-get -f install

This a shorthand for --fix-broken, and installs the dependencies and completes the aborted dpkg installation. Like magic.

Wednesday, November 29, 2023

Setting/resetting Vue reactive objects

Vue 3 has many idiosyncrasies, among them the overlapping ref and reactive constructs. One of the main differences is that reactive content cannot be replaced. One StackOverflow answer proposes using Object.assign, but it will replace all nested references, losing them all.

I ended up writing my own version of a recursive function to replace each value inside an object, so I won’t need to deal with the muddy details ever again:

/**
 * Recursively copies each field from src to dest, avoiding the loss of
 * reactivity. Used to copy values from an ordinary object to a reactive object.
 */
export function deepAssign<T extends object>(destObj: T, srcObj: T): void {
	const dest = destObj;
	const src = toRaw(srcObj);
	if (src instanceof Date) {
		throw new Error('[deepAssign] Dates must be copied manually.');
	} else if (Array.isArray(src)) {
		for (let i = 0; i < src.length; ++i) {
			if (src[i] === null) {
				(dest as any)[i] = null;
			} else if (src[i] instanceof Date) {
				(dest as any)[i] = new Date(src[i].getTime());
			} else if (Array.isArray(src[i])
					|| typeof src[i] === 'object') {
				deepAssign((dest as any)[i], src[i]);
			} else {
				(dest as any)[i] = toRaw(src[i]);
			}
		}
	} else if (typeof src === 'object') {
		for (const k in src) {
			if (src[k] === null) {
				(dest as any)[k] = null;
			} else if (src[k] instanceof Date) {
				(dest[k] as any) = new Date((src[k] as any).getTime());
			} else if (Array.isArray(src[k])
					|| typeof src[k] === 'object') {
				deepAssign(dest[k] as any, src[k] as any);
			} else {
				(dest[k] as any) = toRaw(src[k]);
			}
		}
	} else {
		throw new Error('[deepAssign] Unknown type: ' + (typeof src));
	}
}

Another problem with reactive is that, to avoid two variables pointing to the same point, we must deep clone an object when creating the object. I also wrote a function to deal with that:

/**
 * Deeply clones an object, eliminating common references. Used to create a
 * reactive object by copying from an ordinary object.
 */
export function deepClone<T>(origObj: T): T {
	const obj = toRaw(origObj);
	if (obj === undefined
			|| obj === null
			|| typeof obj === 'string'
			|| typeof obj === 'number'
			|| typeof obj === 'boolean') {
		return obj;
	} else if (Array.isArray(obj)) {
		return obj.reduce((acum, item) => [...acum, deepClone(item)], []);
	} else if (obj instanceof Date) {
		return new Date(obj.getTime()) as unknown as T;
	} else if (typeof obj === 'object') {
		return Object.entries(obj).reduce(
			(acum, [key, val]) => ({...acum, [key]: deepClone(val)}), {}) as T;
	} else {
		throw new Error('[deepClone] Tipo desconhecido: ' + (typeof obj));
	}
}

I remember from my MobX days some people saying that working with reactive variables can be tricky. Now I can clearly see why.

Tuesday, November 21, 2023

Zustand, devtools and immer wrapper

I’m considering a Vue 3 to React migration at work, and while writing a proof of concept, I’m standardizing the Zustand stores creation, which will all use devtools and immer middlewares. And this results in convoluted and error-prone code, which I’m trying to abstract away with a generator function. Such a function, however, has proven to be very hard to write with perfect TypeScript typings.

I had to resort to the great Daishi Kato again, which very patiently helped me to write. It indeed has a deep level of TypeScript black magic which I’m not familiar with:

import {create, StateCreator} from 'zustand';
import {devtools} from 'zustand/middleware';
import {immer} from 'zustand/middleware/immer';

export function createFullStore<T extends object, U extends object>(
	name: string,
	state: T,
	actions: StateCreator<
		T,
		[['zustand/devtools', never], ['zustand/immer', never]],
		[['zustand/immer', never], ['zustand/devtools', never]],
		U
	>,
) {
	return create<T & U>()(
		devtools(
			immer((...a) => Object.assign({}, state, (actions as any)(...a))),
			{name},
		),
	);
}

It works perfectly, with all the type inferences one would expect. I’m really thankful to the guy.

By the way this should’ve been published yesterday, but after a whole day without internet at home, I finally can do it.

Monday, November 13, 2023

TypeScript function to set value by key

While researching React stores automation, I wondered whether it would be possible to have a TypeScript function to set the value of an object by the name of its property, with correctly typed parameters, of course.

Turns out, it is

export function setField<
	T extends object,
	K extends keyof T,
>(o: T, k: K, v: T[K]): void {
	o[k] = v;
}

The function above works perfectly with interfaces:

interface Person {
	name: string;
	age: number;
}

const p: Person = { name: 'Joe', age: 42 };
setField(p, 'name', 'foo');

Saturday, November 11, 2023

Jotai utilities for read-only and write-only atoms

Three months ago I wrote an utility function to deal with write-only atoms in Jotai. To go with it, today I wrote a function to deal with read-only atoms, so now I have:

import {Atom, useAtom, WritableAtom} from 'jotai';

/**
 * Syntactic sugar to useAtom() for read-only atoms.
 */
export function useReadAtom<V>(a: Atom<V>) {
	const [val] = useAtom(a);
	return val;
}

/**
 * Syntactic sugar to useAtom() for write-only atoms.
 */
export function useWriteAtom<V, A extends unknown[], R>(a: WritableAtom<V, A, R>) {
	const [_, setFunc] = useAtom(a);
	return setFunc;
}

It’s worth mentioning that useReadAtom also works for derived atoms, used for computed values.

Friday, November 10, 2023

Zustand computed values, pt. 2

I’ve been trying to find a way for computed values in Zustand for a while. The general recommended way is using a custom hook, according to Daishi Kato himself. The issue with this approach, however, is that your computed logic will be completely alienated from the store itself.

I just found a neat approach which seems to work: using a nested object inside the store itself, as pointed in this highly cheered comment. Below is a fully typed example:

import {create} from 'zustand';
import {combine} from 'zustand/middleware';
import {immer} from 'zustand/middleware/immer';

export interface Person {
	name: string;
	age: number;
}

const usePeople = create(immer(
	combine({
		people: [] as Person[],
	},
	(set, get) => ({
		$computed: {
			get count(): number {
				return get().people.length;
			},
		},
		addNew(name: string, age: number): void {
			set(state => {
				state.people.push({name, age});
			});
		},
	})),
));

export default usePeople;

The tradeoff, as benchmarked by myself, is that this $computed getters are not cached and will run every time the state changes, in addition to every time they are requested – which happens when the component re-renders. The ordinary custom hook approach runs only when they are requested. So, there is a performance penalty, which can be huge if the logic is too demanding.

Saturday, September 23, 2023

Cross-compiling Rust for x32

While analyzing a bug in WinSafe, I had to test its compilation on the i686 platform. Without knowing, I already had it installed together with my MSVC toolchain, and I just needed a few commands in order to pull it out:

List all available toolchains:

rustup toolchain list

Which gave me:

stable-i686-pc-windows-msvc
stable-x86_64-pc-windows-msvc (default)
nightly-x86_64-pc-windows-msvc

Then choose other than the default toolchain:

cargo +stable-i686-pc-windows-msvc c

After performing all the tests, it’s a good idea to clean all the build artifacts:

cargo clean

Note that, if the toolchain is not present, it may have to be installed.

Saturday, August 19, 2023

Calling write-only atoms in Jotai

Every atom in Jotai, when called through its useAtom primitive, returns a tuple of value + setter, just like React’s own useState. In a Jotai write-only atom, however, there is no value, so the canonical call is:

const [_, setVal] = useAtom(myValAtom);

In order to get rid of the verbose underscore, and thus the whole useless tuple, I wrote the following function:

import {WritableAtom} from 'jotai';

/**
 * Syntactic sugar to useAtom() for write-only atoms.
 */
export function useWriteAtom<V, A extends unknown[], R>(a: WritableAtom<V, A, R>) {
	const [_, setFunc] = useAtom(a);
	return setFunc;
}

The atom type was copied straight from Jotai’s TypeScript definitions, so the type inference works seamlessly. The example now can be written as:

const setVal = useWriteAtom(myValAtom);

Saturday, July 29, 2023

Delegating Display/Debug trait implementations

Suppose you want implement Display and Debug traits for your struct, but the output is the same. Instead of copy & paste the implementation, you can delegate the implementation by casting the Self type to the trait type:

impl std::fmt::Debug for MyStruct {
	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
		write!(f, "MyStruct");
	}
}

impl std::fmt::Display for HRESULT {
	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
		<Self as std::fmt::Debug>::fmt(self, f) // delegate
	}
}

As a reminder, the Self casting can be useful in other situations.

Monday, June 26, 2023

A generic hook for useState and Immer

While prospecting the use of useState with the great Immer, I ended up finding the small use-immer package, which unifies both things. However, it doesn’t have proper TypeScript typings.

So I wrote my own hook to perform this task: just like useState, it returns a value and a setter, but the setter’s argument is a mutable state, automatically wired to produce, and all that using a generic argument for proper typing:

import {useCallback, useState} from 'react';
import {Draft, produce} from 'immer';

export function useStateImmer<T>(initialState: T | (() => T)): [
	T,
	(updater: (draft: Draft<T>) => void) => void,
] {
	const [obj, setObj] = useState(initialState);
	const setObjProduce = useCallback((updater: (draft: Draft<T>) => void): void => {
		setObj(curState => {
			return produce(curState, draft => {
				updater(draft);
			});
		});
	}, [setObj]);
	return [obj, setObjProduce];
}

Note that the returned setter function is identity, since it’s guarded by an useCallback call.

Thursday, June 22, 2023

Java Either class

Today, at work, I had the dire need of discriminated unions in Java, which of course doesn’t support it. Then I found the idea of the Either type, which I implemented myself using the Optional class from Java 8:

import java.util.Optional;
import java.util.function.Consumer;
	
/**
 * Guarda 3 elementos mutuamente exclusivos, isto é, apenas 1 dos 3 pode existir
 * dentro do objeto.
 */
public class Either3<T1, T2, T3> {
	
	private Optional<T1> v1 = Optional.empty();
		
	private Optional<T2> v2 = Optional.empty();
		
	private Optional<T3> v3 = Optional.empty();
	
	/** Cria um objeto com o valor do tipo 1. */
	public static <<1, T2, T3> Either3<T1, T2, T3> of1(T1 v1) {
		Either3<T1, T2, T3> obj = new Either3<>();
		obj.v1 = Optional.of(v1);
		return obj;
	}
	
	/** Cria um objeto com o valor do tipo 1. */
	public static <T1, T2, T3> Either3<T1, T2, T3> of2(T2 v2) {
		Either3<T1, T2, T3> obj = new Either3<>();
		obj.v2 = Optional.of(v2);
		return obj;
	}
	
	/** Cria um objeto com o valor do tipo 1. */
	public static <T1, T2, T3> Either3<T1, T2, T3> of3(T3 v3) {
		Either3<T1, T2, T3> obj = new Either3<
		obj.v3 = Optional.of(v3);
		return obj;
	}
	
	/** Retorna o valor do tipo 1; caso não exista, lança {@code NoSuchElementException}. */
	public T1 get1() {
		return v1.get();
	}
	
	/** Retorna o valor do tipo 2; caso não exista, lança {@code NoSuchElementException}. */
	public T2 get2() {
		return v2.get();
	}
	
	/** Retorna o valor do tipo 3; caso não exista, lança {@code NoSuchElementException}. */
	public T3 get3() {
		return v3.get();
	}
	
	/** Diz se o objeto possui um valor do tipo 1. */
	public boolean has1() {
		return v1.isPresent();
	}
	
	/** Diz se o objeto possui um valor do tipo 2. */
	public boolean has2() {
		return v2.isPresent();
	}
	
	/** Diz se o objeto possui um valor do tipo 3. */
	public boolean has3() {
		return v3.isPresent();
	}
	
	/** Executa a função caso haja um valor do tipo 1. */
	public void if1(Consumer<? super T1> consumer) {
		v1.ifPresent(consumer);
	}
	
	/** Executa a função caso haja um valor do tipo 2. */
	public void if2(Consumer<? super T2> consumer) {
		v2.ifPresent(consumer);
	}
	
	/** Executa a função caso haja um valor do tipo 3. */
	public void if3(Consumer<? super T3> consumer) {
		v3.ifPresent(consumer);
	}
}

Since Java doesn’t have variadic generic parameters, having one class to each amount is needed. Nonetheless, it’s doable and remarkably ergonomic.