experiments in a post-browser web
peek docs cmd.md
6.7 kB
Markdown
at main

Cmd Extension #

Command palette for quick command access via keyboard shortcut.

Overview #

The cmd extension provides a command palette interface accessible via a global keyboard shortcut (default: Alt+Space). Commands can be typed, selected from a dropdown, and executed. Commands from other extensions can be registered with the cmd system.

Features #

  • Global keyboard shortcut for quick access
  • Type-ahead filtering with adaptive matching
  • Command chaining with typed data flow (MIME types)
  • Preview pane for viewing command output
  • Output selection mode for array results

Command Chaining #

Commands can be composed into pipelines where the output of one command becomes the input of the next. This enables powerful data transformation workflows.

Example Flow #

  1. Run lists command → produces JSON array output
  2. Select an item from the output list (arrow keys + Enter)
  3. Run csv command → converts JSON to CSV format
  4. Run save command → prompts to save the file

How It Works #

Commands declare what MIME types they accept and produce:

export default {
  name: 'csv',
  description: 'Convert JSON to CSV format',
  accepts: ['application/json'],  // Input types this command handles
  produces: ['text/csv'],         // Output type this command generates

  execute: async (ctx) => {
    // ctx.input - data from previous command
    // ctx.inputMimeType - MIME type of input
    // ctx.inputTitle - human-readable title

    return {
      success: true,
      output: {
        data: csvString,
        mimeType: 'text/csv',
        title: 'CSV Output'
      }
    };
  }
};

Command Types #

Producer Commands - Start chains, produce output

  • accepts: [] (empty or omitted)
  • produces: ['application/json']
  • Example: lists

Transformer Commands - Accept input, produce output

  • accepts: ['application/json']
  • produces: ['text/csv']
  • Example: csv

Consumer Commands - Accept input, end chains

  • accepts: ['*/*'] (or specific types)
  • produces: [] (empty or omitted)
  • Example: save

MIME Type Matching #

The chaining system supports wildcards:

  • */* - matches any MIME type
  • text/* - matches any text type (text/plain, text/csv, etc.)
  • application/json - exact match

Output Selection Mode #

When a command produces an array of items, the panel enters "output selection mode":

  • Items are displayed in the results dropdown
  • Navigate with arrow keys (up/down)
  • Select with Enter or Right Arrow
  • The selected item becomes input for the next command
  • ESC exits selection mode

Chain Mode UI #

When in chain mode:

  • A chain indicator shows the current MIME type and title
  • Only commands that accept the current MIME type are shown
  • A preview pane displays the current data
  • ESC exits chain mode first, then closes panel

Available Commands #

lists #

Produces sample list data for chaining demonstration.

lists           - produce sample list data

Output: application/json array

csv #

Converts JSON data to CSV format.

csv             - convert JSON input to CSV

Accepts: application/json Produces: text/csv

save #

Saves data to a file using the native save dialog.

save            - save with auto-generated filename
save myfile.csv - save with specified filename

Accepts: */* (any MIME type) Produces: nothing (end of chain)

Execution Context #

Commands receive an execution context object:

{
  typed: 'csv',           // Full typed string
  name: 'csv',            // Command name
  params: [],             // Array of parameters
  search: null,           // Text after command name

  // Chain mode only:
  input: {...},           // Input data from previous command
  inputMimeType: 'application/json',
  inputTitle: 'Sample list',
  inputSource: 'lists'    // Source command name
}

Keyboard Shortcuts #

Key Action
Arrow Down Show results / navigate down
Arrow Up Navigate up in results
Enter Execute selected command / select output item
Tab Autocomplete command name
ESC Exit chain mode → close panel
Right Arrow Select output item (in selection mode)

Settings #

Configure via Settings UI:

  • Shortcut Key: Global shortcut to open cmd panel (default: Alt+Space)
  • Width: Panel window width (default: 600)
  • Height: Panel window height (default: 400)

Registering Commands #

Extensions can register commands by publishing to the cmd:register topic:

api.publish('cmd:register', {
  name: 'my-command',
  description: 'Does something',
  source: 'my-extension',
  accepts: ['application/json'],
  produces: ['text/plain']
}, api.scopes.GLOBAL);

Or using the commands API:

api.commands.register({
  name: 'my-command',
  description: 'Does something',
  execute: async (ctx) => { ... }
});

Architecture #

Files #

File Purpose
background.js Command registry, shortcut handling, save file coordination
panel.js Panel UI, chain mode logic, output selection, preview rendering
panel.html Panel layout and styles
commands.js Command proxy/dispatch to background
config.js Extension configuration and defaults
download.html Save dialog window (non-modal for native dialog)
apps/desktop/features/lists/home.js The lists command, registered by the lists feature
apps/desktop/features/files/background.js The csv and save commands, registered by the files feature

Provider Pattern #

The cmd extension uses the Provider pattern:

  • Owns the command registry
  • Subscribes to cmd:register, cmd:unregister for command management
  • Subscribes to cmd:query for late-arriving consumers
  • Publishes cmd:ready when fully initialized

Save Dialog Flow #

The save command uses a separate window approach to avoid modal blur issues:

  1. save command publishes cmd:save-file to background
  2. Background stores data in pendingDownloads Map
  3. Background opens download.html window with download ID
  4. Download window subscribes to cmd:download-data:{id}
  5. Download window publishes cmd:download-ready
  6. Background sends data to download window
  7. Download window calls api.files.save() for native dialog

This approach avoids a modal command window competing with the native save dialog for focus. There is no per-window blur-close handler left to worry about either way — ipc.ts windowOpenHandler no longer installs one for any window, modal or not; a transient window's dismissal now runs only through the window kernel's selectTransientAutoclose.