-
Notifications
You must be signed in to change notification settings - Fork 3
Extension Development
Extensions adds functionality that enhances specific workflows i.e. stock photo management, ebook viewing. Where as the main program seeks to implement functionality common to all workflows, as well as a platform on which extensions may be built.
An extension is comprised of an ES6 or UMD module and optional CSS stylesheets
- All extensions are loaded via the inconel dynamic extension loading library
- For requirements of an extension's module, see the Extension interface
- For properties specific to this project, see the Project Specific Extension interface
- To affect changes in the UI, an extension may export its own ReactJS
components
- To store state that lives beyond that of UI components and communicate with other components, the extension is expected to create
services
- Asset loading is currently not supported, please do not attempt to make assumptions about placement for your files
An extension may define 3 different types of ReactJS based UI components:
- Menus - Examples of menus include the top level buttons for
filtering
,thumbnails
, andordering
- Modes - Top level activities such as the
gallery
&stage
are builtin modes - Extras - Specific to extensions, these are always visible overlays that are not required to conform any specific appearance requirements
Component definitions should be exported from your extension's module
export const menus = [
MenuDefinition,
];
export const extras = [
OverlayDefinition,
];
export const modes = [
ActivityDefinition,
];
Note that the program exposes its React instance at the window level, so please ensure you do not try to bundle your own
All components may be stateful
- However this state is lost when the component is detached from the DOM, which typically happens when a menu hides or the active mode changes
- Less ephemeral state should be kept in
services
, which a component may declare its intention to access via their definition objects- These services are then bound by name to the component's props
Menus exists for one of two purposes:
- Displaying information about the current file or mode
- Editing user preferences
- Menus are the only components exported by an extension that may change preferences
- As a security measure, extensions may only change preferences prefixed by its own namespace
A Menu can be associated with multiple mode
s and are aware of the active mode
- It is possible for a menu to exhibit Mode specific behavior
- i.e. Batch tagging is only available in gallery mode
Example definition:
export const Definition = {
id: "my-menu",
icon: mdiAlphaMBox,
label: "My Menu",
services: ["browsing"],
component: Component,
selectPreferences: ({
somePreference,
}): PreferenceMappedProps => ({
somePreference,
}),
};
Commanding the majority of the user's attention, modes are top level activities
- Only one mode is ever displayed at a time, granting it exclusive access to the entire client area
- A mode must be defined with a unique
path
, which serves as its ID in navigation as well as allowingmenus
&extras
to target it for composition - Navigating to a different mode can be performed by the
onNavigate
function passed to the mode's component via its props
Example definition:
export const Definition = {
id: "my-activity",
path: "/my-activity",
services: ["tagging"],
component: Component,
selectPreferences: ({
somePreference,
}): PreferenceMappedProps => ({
somePreference,
}),
}
WIP: Not yet available, This is still being designed
Extension components must observe the following guidelines on how to acquire and react to focus loss in order to ensure they behave intuitively when a user stops interacting with them.
Services are the longest living components exported by an extension, matching that of the program itself.
- Services may have dependencies against other services
- Dependencies are considered construction preconditions, thus any cyclic dependency is considered a fatal runtime error
- UI components declares their dependencies against services in their definitions
- However it is completely normal for a service to be used solely by other services
Unlike components, service classes are directly exported from the application
export const services = [
Service,
];
class Service {
static readonly shortName = "service";
static readonly dependencies = ["ipc", "reader"];
constructor([ipc, reader]: [ipc.Service, io.Reader]) {
...
}
}
The application offers several "builtin" services that allows your extension to extend the behaviors of the application
This section is WIP, expect surface level details only
- Centralizes file ordering, filtering, and selection
- Controls tag assignment, creation, modification, and deletion
- Performs caching and debouncing to eliminate unnecessary disk usage
- Enables your program to communicate with other programs
- Adds support for multiplexed binary RPC via unix domain sockets as well as IO streams
- Can execute external commands for operations that can't be supported in an extension
- Being an "inter process communication" service, there is no way to establish a connection to another computer
This program has aspirations to become a hybrid application. Thus resource intensive RPC frameworks such as JSON are out of the window for, what one consider obvious, performance and battery consumption reasons
- Instead we use a very minimal, enveloped protocol
- Every message contains at least two fields:
- uint32 sequenceNum - The ID that would be used as a response
- uint32 messageSize - This includes the bytes used by this envelope
- Response messages (i.e. procedure call responses) shall be sent back with the same sequence number as the request that lead to the response
- To avoid sequence number synchronization related race conditions, the least significant bit of the sequence number indicates which party initiated a particular interaction
- If set (odd numbers) - the interaction is started by this program
- For an example of using this protocol see fs-viewer-curator-extension
- Exposes the platform specific path joining function
- Permits implementation of line by line text file parsing via a reduction algorithm
- Enables read access to
- files' attributes
- arbitrary POJO objects serialized to files
- arbitrary text files
- Permits the ability to set and remove attributes from files
- Enables very efficient incremental writes to
- arbitrary POJO objects serialized to files
- arbitrary text files
- An event emitter that triggers whenever application preferences changes
To improve load speed and reduce bandwidth usage in distribution, it is recommended all extensions are bundled with a modern bundler such as Webpack or Rollup
- Webpack users consider using the config builder used for this project
It is recommended that all styles declared by your extension be qualified by one "root" class bearing some abbreviated version of your extension's name. This minimizes the likelihood that styles between extensions will interact with each other
Dedicated constants files are considered anti-patterns in the main codebase, preference IDs in extensions is an exception as this reduces the labor of maintaining consistency between components where owner of the preference is not clear