Skip to content

New resource type: Launcher — text input + Go button to open URLs or run parameterized commands #24

Description

@abeckDev

Summary

Add a new Launcher resource type that displays a text input field and a Go button in the menu bar popup. When the user types/pastes a value and clicks Go (or presses Enter), BarKeeper executes a configured command template, substituting the user input into the command.

Motivation

Common workflow: paste an MSX record ID (e.g. 7-3H6CBCNA7Q), click Go, and a browser tab opens at the correct Dynamics 365 search URL. Currently this requires switching to a browser, navigating to MSX, finding the search bar, pasting, and clicking. A Launcher resource turns this into a one-step action from the menu bar.

This pattern is generic — it works for any "paste an identifier and open/run something":

  • Open an MSX record by ID → open "https://microsoftsales.crm.dynamics.com/main.aspx?forceUCI=1&pagetype=search&searchText={input}"
  • Open a Jira ticket → open "https://jira.example.com/browse/{input}"
  • SSH into a host by name
  • Look up a package → open "https://www.npmjs.com/package/{input}"

Design

Resource model changes (Resource.swift)

Add a new case to ResourceType:

case launcher

Add a new optional field to Resource:

/// Placeholder text shown in the Launcher input field.
var inputPlaceholder: String?

The actionScript field (already on Resource) is reused. It must contain the {input} placeholder token, which is replaced at runtime with the sanitized user input.

Config schema

{
  "id": "...",
  "name": "MSX Lookup",
  "type": "launcher",
  "actionScript": "open \"https://microsoftsales.crm.dynamics.com/main.aspx?forceUCI=1&pagetype=search&searchText={input}\"",
  "inputPlaceholder": "Paste MSX record ID"
}
Field Type Description
actionScript String Shell command with {input} placeholder.
inputPlaceholder String? Hint text for the input field. Defaults to "Enter value…" if nil.

Menu bar row (ResourceRowView.swift)

The Launcher row should render:

  1. A 🚀 (rocket) or 🔗 (link) system icon as the status indicator (use link SF Symbol).
  2. The resource name as a label.
  3. A TextField (compact, ~150pt wide) with the configured placeholder.
  4. A Go button (small, bordered) that triggers execution. The Go Button should look similar that the one in ResourceType Button
  5. Pressing Enter in the text field should also trigger execution.

The text field value is local @State — it does not persist across menu open/close.

Execution (ResourceManager.swift)

Add a new method:

func runLauncher(_ resourceId: UUID, input: String)

Logic:

  1. Guard the resource is .launcher and actionScript is non-empty.
  2. Sanitize input: trim whitespace, reject empty input.
  3. Replace all occurrences of {input} in actionScript with the shell-escaped input value.
  4. Execute via ShellExecutor.run(...).
  5. Handle success/failure the same way runAction does (notifications, error state).

Security: The input value must be shell-escaped before substitution to prevent command injection. Use a helper that wraps the value in single quotes with internal single quotes properly escaped, or use Process arguments array to avoid shell interpretation entirely.

Editor (ResourceEditorView.swift)

When type == .launcher:

  • Show an Action Script text editor (reuse existing scriptEditor) with placeholder: Command with {input} placeholder, e.g. open "https://example.com/{input}"
  • Show an Input Placeholder TextField for customizing the hint text shown in the menu bar input field.

Settings picker (ResourceEditorView.swift)

Add to the type Picker:

Label("Launcher", systemImage: "link").tag(ResourceType.launcher)

buildResource() in ResourceEditorView.swift

Include inputPlaceholder for .launcher type (add a new @State variable and wire it up).

README updates

  • Add Launcher to the "Resource Types" section with a description and example.
  • Add a row to the "JSON Schema" table.
  • Update the architecture ASCII tree if needed.
  • Add Launcher to the "Script Convention" table: Exit code 0 = success, non-zero = error shown in tooltip.

Acceptance criteria

  • New launcher case in ResourceType enum.
  • inputPlaceholder optional field on Resource with backward-compatible decoding.
  • Launcher row renders in menu bar with text field + Go button.
  • Enter key in text field triggers execution.
  • {input} in actionScript is replaced with shell-escaped user input.
  • Empty input is rejected (Go button disabled or no-op).
  • ResourceEditorView shows appropriate fields for Launcher type.
  • Launcher appears in type picker in Settings.
  • Config import/export round-trips the new type.
  • README documents the Launcher resource type.
  • Existing resource types are unaffected (backward compatible).
  • Project compiles with xcodegen generate && xcodebuild build -scheme BarKeeper -configuration Debug.

Metadata

Metadata

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions