Chonky v2.1.0 Docs
IntroductionInstallation & usageFile Browser demosMigrating from 1.xFAQ
Basics

Defining custom actions

File action definitions

File actions are JS objects that follow the FileAction interface. You can see all supported fields in the FileAction API reference. I suggest you check out the API reference before reading this page to get more context. Note that id is the only required file action field - all other fields are strictly optional.

In their essence, file action definitions are plain JS objects, but it is recommended to define them using the defineFileAction helper method. It does some runtime checks and Typescript magic to produce a type-safe file action. The simplified signature of this method looks like this:

defineFileAction(action: FileAction, effect?: FileActionEffect) => FileAction;

You can ignore the effect parameter for now - it's an advanced feature, and understanding it requires some more context about how Chonky works. The Understanding effects page talks about it in detail.


Let's look at a real example. Below you can see the definition for the SortFilesBySize file action. It's a real built-in file action that is enabled by default.

import { defineFileAction, FileData } from 'chonky';
import { Nullable } from 'tsdef';
const SortFilesBySize = defineFileAction({
id: 'sort_files_by_size',
sortKeySelector: (file: Nullable<FileData>) => (file ? file.size : undefined),
button: {
name: 'Sort by size',
toolbar: true,
group: 'Options',
},
} as const);

As you can see from the definition, it provides a button field with button.toolbar set to true, meaning the button for this action will appear in the toolbar. It also defines a sortKeySelector method, which will be used to sort the files when user triggers the action by pressing its toolbar button.

After the action is triggered, Chonky starts processing it internally. Chonky executes the action effect along with other operations the action might define - sorting, changing file view, updating selection, etc. Finally, when all of this internal processing is finished, Chonky calls the user-defined file action handler.

What is as const?

If you're new to Typescript, the as const suffix after the object definition in the example above might seem unusual to you:

{
id: 'sort_files_by_size',
// ...
} as const

The as const suffix represents a const assertion. It tells Typescript to bake-in the exact type of the object, which helps you write type-safe code later down the line.

Defining your own action

Let's assume you want to define a new file action. You can start from this very simple (yet valid) definition:

import { defineFileAction } from 'chonky';
const action = defineFileAction({
id: 'my_custom_action',
// other fields will go here
});

For now, the only field that is defined on your action is action.id. The next few sections on this page will walk you through all the different fields you can define on your action. You can also read the FileAction API reference for a short description of each field.

Specifying action triggers

You can explicitly specify three ways to trigger your action - via a keyboard shortcut, via a toolbar button, or via a context menu button. There are some other ways to trigger actions as covered on Triggering actions page, but you don't have control over them in your action definition.

action.button

Setting the button property in your action definition will add a button to Chonky UI that can be used to trigger your action. The value for the button field should be an object following the FileActionButton interface:

export interface FileActionButton {
name: string; // Button name
toolbar?: boolean; // Whether to show the button in the toolbar
contextMenu?: boolean; // Whether to show the button in the context menu
group?: string; // Button group (dropdown in toolbar or section in context menu)
tooltip?: string; // Help tooltip text
icon?: ChonkyIconName | string | any; // Icon name
iconOnly?: boolean; // Whether to only display the icon
}

Note that you should set either button.toolbar or button.contextMenu (or both) to true for your button to actually appear in the UI. The icon field follows Chonky's approach to icons, as laid out on Using icons page.

action.hotkeys

Keyboard shortcuts are defined using the hotkeys field. It should be an array of strings, where each string represents a shortcut, e.g. ctrl+q or just space. See hotkeys-js documentation for more information on how to define keys.

Please respect your users' needs when defining custom shortcuts. For example, don't override the ctrl+r hotkey that already has a well-established meaning (reload page).

action.requiresSelection and action.fileFilter

There are actions that only make sense when user has selected some files - like downloading or deleting files. For such actions, you can set requiresSelection field to true - this will prevent the action from being triggered until user makes a non-empty selection. Action buttons will also be rendered in disabled state.

If you want to narrow down the selection for a particular action, you can specify the fileFilter field. It's a predicate function that will be used to filter selected files. It should follow the FileFilter type:

export type FileFilter = (file: Nullable<FileData>) => boolean;

Action triggers example

We define two actions, both of which have toolbar and context menu buttons. You can right click on a file entry to bring up the context menu. Note that Delete hidden files is disabled until you select a hidden file (or right-click on it). You can also try pressing Ctrl+O or Ctrl+U on your keyboard to activate the actions.

Last custom action ID: None

0 items

Changing internal Chonky state: Basics

"Internal Chonky state" includes a lot of things, some of which are pretty low-level and complicated. However, there are also simpler concepts like current sorting function, current file view (grid or list), boolean options, and current file selection. The file action framework exposes simple mechanisms for you to change these 4 parts of Chonky's state.

action.sortKeySelector

sortKeySelector should follow the FileSortKeySelector type shown below. It is a function used to determine what key your files will be sorted on. If you specify a sortKeySelector, when your action is activated for the first time, the files will be sorted on your sort key in ascending order. On subsequent activations of your action, sort order will be toggled between ascending and descending.

export type FileSortKeySelector = (file: Nullable<FileData>) => any;

action.fileViewConfig

fileViewConfig should follow the FileViewConfig type shown below. It is used to switch between List and Grid views, as well as to set things like list row height or grid thumbnail size. Note that FileViewMode.Compact is still experimental, and should not be used in production.

export type FileViewConfig = FileViewConfigList | FileViewConfigGrid;
export enum FileViewMode {
List = 'list',
Compact = 'compact',
Grid = 'grid',
}
export type FileViewConfigList = {
mode: FileViewMode.List;
entryHeight: number;
};
export type FileViewConfigGrid = {
mode: FileViewMode.Compact | FileViewMode.Grid;
entryWidth: number;
entryHeight: number;
};

action.option

option should follow the FileActionOption type shown below. It represents some boolean flag associated with your action. Internal Chonky components or effects of other actions can use this boolean flag to change their behaviour. option.id will be used to represent this flag in Chonky's internal state. The flag will be set to the option.defaultValue you provided on initial component mount. On every activation of your action, the flag will be toggled.

export interface FileActionOption {
id: string; // Unique option ID
defaultValue: boolean; // Whether the option is enabled by default (required)
}

action.selectionTransform

selectionTransform should follow the FileSelectionTransform type shown below. As the name implies, it can be used to transform the selection in some way. The obvious use cases are to select all files or clear the selection (and there are default built-in actions that do exactly that). However, you can also get much more creative, like the select the most recently edited files.

Note that the file selection in this function is represented as an ES6 set. If an ID of a particular file is in the set, it means this file is selected. prevSelection represents the file selection before the transform was called, and your selectionTransform function should either return the new selection or null if you want to leave the selection unchanged.

There are 3 other parametrs provided for your convenience. fileIds represents the IDs of all files supplied to Chonky. fileMap represents a mapping from file ID to file object - this is useful if you want to check some property of the file, e.g. fileMap[fileId].name === 'MyFile.txt'. hiddenFileIds represents all files that currently not visible to the user, e.g. because Show hidden files option is false or because user is currently using file search. For best user experience, you should avoid selecting files in hiddenFileIds.

export type FileSelectionTransform = (data: {
prevSelection: Set<string>;
fileIds: ReadonlyArray<string>;
fileMap: Readonly<FileMap>;
hiddenFileIds: Set<string>;
}) => Nullable<Set<string>>;

Example of changing Chonky's state

0 items

Changing internal Chonky state: Advanced topics

As stated before, the "advanced" way to change Chonky state is to use file action effects. See Understanding effects page for more information.

Action payload type and extra state type

You might have noticed the action.__payloadType and action.__extraStateType fields. Extra state is still an experimental feature, so you can ignore it for now. On the other hand, the payload type is already used by some built-in actions. It is used in part for ensuring type-safe definitions and in part for runtime payload validation.

Note that actions that define a payload type cannot have action.button or action.hotkeys specified, because buttons and hotkeys can only dispatch actions with a non-null payload.

The way to define action.__payloadType is slightly unusual. You must ensure that Typescript infers the correct type for that field. Consider the definition for the OpenFiles action:

import { defineFileAction } from 'chonky';
import { OpenFilesPayload } from 'chonky/lib/types/action-payloads.types';
const OpenFiles = defineFileAction({
id: 'open_files',
__payloadType: {} as OpenFilesPayload,
} as const);

If we wouldn't provide as OpenFilesPayload suffix, Typescript would infer the type of __payloadType as an empty object. As an aside: in theory, the payload could be of any type, but it's best stick to plain JS objects.

If you have a question, want to request a feature or report a bug, please create an issue on GitHub. You can also report inaccurate or unclear documentation on Chonky's Discord server.