Node/Express backend with a pluggable Adapter contract, a folder adapter (safe non-destructive reject-to-trash, undo, keep/reject/skip), and a vanilla JS swipe UI + generic settings form driven by each adapter's configSchema.
3.4 KiB
3.4 KiB
Contributing
Adapters are the whole point of this project — the swipe UI, keyboard shortcuts, drag gestures, undo stack, and settings form are all generic and work for any adapter that implements the contract below.
Writing a new adapter
-
Create
adapters/your-adapter.jsand extend the base class:const { Adapter } = require('./base'); class YourAdapter extends Adapter { static id = 'your-adapter'; // used in swipeanything.config.json static label = 'Your Adapter'; // shown in the settings UI static description = 'One line describing what this swipes through.'; // Settings UI is generated from this — no frontend code needed. static configSchema = [ { key: 'someSetting', label: 'Some setting', type: 'text', required: true }, ]; // Optional: override the default keep/reject pair, e.g. add a third // "later" action. Each needs a distinct key and direction. static actions = [ { id: 'keep', label: 'Keep', key: 'ArrowRight', direction: 'right' }, { id: 'reject', label: 'Reject', key: 'ArrowLeft', direction: 'left', isDestructive: true }, ]; async init() { // Validate settings / open a connection. Throw a descriptive Error // to surface a message in the settings UI. } async list() { // Return the full queue: [{ id, title, subtitle?, previewType?, meta? }] } async applyAction(item, actionId) { // Perform the effect. Return whatever undo() needs, or null/undefined. } async undo(record) { // Reverse applyAction() using the record it returned. } // Optional, only if items have a file-like preview: async resolvePreviewPath(itemId) { return null; } describeSource() { return ''; // short string shown in the UI header } } module.exports = { YourAdapter }; -
Register it in
adapters/registry.js:const { YourAdapter } = require('./your-adapter'); const ADAPTERS = { [FolderAdapter.id]: FolderAdapter, [YourAdapter.id]: YourAdapter, }; -
That's it — it now shows up in the Settings page automatically, with a generated form from
configSchema, and the swipe UI works against it.
Design constraints to keep in mind
- Non-destructive by default. Follow the folder adapter's lead: prefer
"move to a recoverable place" / "mark as read" / "flag a row" over
irreversible deletes, and make
undo()actually reverse it. - No adapter-specific frontend code. If you find yourself editing
public/app.jsto special-case your adapter, the contract is probably missing something generic — open an issue/PR to discuss extendingconfigSchema,actions, or the item shape instead of forking the UI. - Validate in
init(), notlist(). Throwing frominit()surfaces a clean error in the settings form before anything gets saved. - Keep
list()fast enough for a session. It's called once per session (or on "Rescan"), not per card — pagination/streaming can come later if a real adapter needs it.
Local development
npm install
npm start
There's no build step — edit files under public/ or adapters/ and
refresh the browser.
Pull requests
Keep adapters self-contained in their own file. Include a short section in the PR description covering: what it swipes through, what each action does, and how undo works.