Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
**A command palette for every repo you own — one that reads each project and
hands you its actual scripts, builds, and sub-packages.**

![dev-prompt demo](docs/img/0_dev-prompt_demo.gif)

Press a global shortcut from anywhere and a fuzzy-searchable list of every
repository under your code folders appears. Type a few letters, hit `Enter`, and
you're in a terminal at its root.
Expand All @@ -26,7 +28,7 @@ WebView.
## What it does

- **Global-hotkey overlay** — frameless, centered, dismiss-on-blur. Toggle with
`Ctrl/Cmd+Shift+Space` (rebindable via a click-to-record field in Settings).
`Ctrl/Cmd+Backslash` (rebindable via a click-to-record field in Settings).
- **Multi-root scan + cache** — walks the directories you list, respects
`.gitignore`, collapses nested repos, caches the result for instant startup and
refreshes in the background when stale.
Expand Down Expand Up @@ -121,8 +123,8 @@ not Flathub — see [`docs/linux-distribution/phase-4-flatpak.md`](docs/linux-di

## Configuration

First run creates two files in your OS config directory (`%APPDATA%\dev-prompt\`,
`~/.config/dev-prompt/`, or `~/Library/Application Support/dev-prompt/`):
First run creates two files in your OS config directory (`%APPDATA%\dev-prompt\`
or `~/.config/dev-prompt/`):

- **`config.yaml`** — your settings (hotkey, roots, scan depth, cache lifetime).
Managed entirely by the **Settings** screen; you don't normally touch it.
Expand All @@ -136,7 +138,9 @@ build rule, disable a built-in, change the Enter action) —
[`docs/rules-engine.md`](docs/rules-engine.md); the canonical schema with inline
docs is [`src-tauri/src/default_config.yaml`](src-tauri/src/default_config.yaml).

The discovered repo list is cached at `<OS cache dir>/dev-prompt/repos.json`.
The discovered repo list is cached at `<OS cache dir>/dev-prompt/repos.json`; the
installed-app list (`>` scope) is cached alongside it at
`<OS cache dir>/dev-prompt/apps.json`.

### How detection works

Expand Down
12 changes: 7 additions & 5 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@ Two global hotkeys, set from **Settings ▸ Global hotkeys**:

```yaml
# config.yaml
hotkey: CmdOrCtrl+Shift+Space # opens the overlay on the repo browser
apps_hotkey: CmdOrCtrl+Shift+Period # opens straight into the ">" app launcher
# ("" = off; the Settings toggle writes it)
hotkey: CmdOrCtrl+Backslash # opens the overlay on the repo browser
apps_hotkey: CmdOrCtrl+Period # opens straight into the ">" app launcher
# ("" = off; the Settings toggle writes it)
```

`apps_hotkey` is a convenience — it's the same as pressing the main hotkey then
typing `>`. It's on by default (`Ctrl+Shift+.`, i.e. `Ctrl+>`); the **App
launcher** field has a *turn off* link. The two must be different.
typing `>`. It's on by default (`Ctrl+.`); the **App launcher** field has a
*turn off* link. The two must be different.

The recorder (click a field, press a combination) checks what you pick against a
built-in list: it **refuses** combos the OS reserves or that can't be
Expand Down Expand Up @@ -172,6 +172,8 @@ apps:
Installer stubs, updaters, redistributables, crash handlers and OS components
under `\Windows\` are filtered out automatically; `exclude` is for the rest.

The discovered app list is cached at `<OS cache dir>/dev-prompt/apps.json`.

---

## Roots, scan depth, cache
Expand Down
Binary file added docs/img/0_dev-prompt_demo.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions docs/img/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

Public image assets referenced by docs and packaging metadata.

## Demo

`0_dev-prompt_demo.gif` is embedded at the top of the root `README.md` — the
hotkey → fuzzy search → action-menu flow, captured end to end.

## Screenshots

`packaging/linux/io.github.seraphx2.devprompt.metainfo.xml` references these as
Expand Down
6 changes: 3 additions & 3 deletions src-tauri/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ impl Default for Config {
// Only used if the embedded default_config.yaml fails to parse (a test
// guards against that).
Config {
hotkey: "CmdOrCtrl+Shift+Space".into(),
hotkey: "CmdOrCtrl+Backslash".into(),
apps_hotkey: None,
roots: Vec::new(),
scan: ScanConfig::default(),
Expand Down Expand Up @@ -585,7 +585,7 @@ pub fn save_user(u: &UserConfig) -> AppResult<()> {

fn first_run_user() -> UserConfig {
UserConfig {
hotkey: Some("CmdOrCtrl+Shift+Space".into()),
hotkey: Some("CmdOrCtrl+Backslash".into()),
// The default lives in default_config.yaml so it applies to existing
// configs too; only write here if the user turns it off.
apps_hotkey: None,
Expand Down Expand Up @@ -846,7 +846,7 @@ mod tests {
#[test]
fn apps_hotkey_defaults_on_and_treats_empty_as_off() {
let mut cfg = bundled_defaults();
assert_eq!(cfg.apps_hotkey.as_deref(), Some("CmdOrCtrl+Shift+Period"));
assert_eq!(cfg.apps_hotkey.as_deref(), Some("CmdOrCtrl+Period"));

merge_settings(&mut cfg, serde_yaml_ng::from_str("apps_hotkey: Alt+Space").unwrap());
assert_eq!(cfg.apps_hotkey.as_deref(), Some("Alt+Space"));
Expand Down
8 changes: 4 additions & 4 deletions src-tauri/src/default_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@
# {{name}} project/repo name {{file}} matched file {{file.name}} {{file.stem}}
# {{env:VAR}} {{<program-key>}} a resolved program

hotkey: CmdOrCtrl+Shift+Space
# Second global hotkey — opens straight into the ">" app launcher. Same key as
# ">". Set to "" in config.yaml (or "turn off" in Settings) to disable it.
apps_hotkey: CmdOrCtrl+Shift+Period
hotkey: CmdOrCtrl+Backslash
# Second global hotkey — opens straight into the ">" app launcher. Set to ""
# in config.yaml (or "turn off" in Settings) to disable it.
apps_hotkey: CmdOrCtrl+Period
roots: []
scan:
max_depth: 4
Expand Down
11 changes: 5 additions & 6 deletions src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -269,9 +269,7 @@
mode === "repo-list" && appScope
? appStatus
: mode === "settings"
? [status, apps.length ? `${apps.length} apps` : ""]
.filter(Boolean)
.join(" · ")
? [status, appStatus].filter(Boolean).join(" · ")
: status,
);

Expand Down Expand Up @@ -320,7 +318,7 @@
async function rescan() {
if (scanning) return;
scanning = true;
status = "Scanning…";
status = "Scanning repos…";
try {
const payload = await rescanRepos();
status = `${payload.repos.length} repos`;
Expand All @@ -346,7 +344,7 @@
async function rescanAppsNow() {
if (appsScanning) return;
appsScanning = true;
appStatus = "Scanning for apps…";
appStatus = "Scanning apps…";
try {
const p = await rescanApps();
apps = p.apps;
Expand Down Expand Up @@ -698,12 +696,13 @@
onback={backToList}
ondirtychange={(d) => (settingsDirty = d)}
onsaved={() => {
rescan();
void getConfig().then((c) => {
dismissMode = c.dismiss ?? "always";
appsEnabled = c.apps?.enabled ?? true;
});
}}
onrescanrepos={rescan}
onrescanapps={rescanAppsNow}
/>
{/if}

Expand Down
2 changes: 1 addition & 1 deletion src/lib/components/AppList.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
<div bind:this={container} class="scroll-thin flex-1 overflow-y-auto px-2 py-2">
{#if entries.length === 0}
<div class="px-3 py-8 text-center text-[13px] text-white/30">
{scanning ? "Scanning for installed apps…" : "No apps match."}
{scanning ? "Scanning apps…" : "No apps match."}
</div>
{:else}
{#each entries as e, i (e.app.exec)}
Expand Down
21 changes: 15 additions & 6 deletions src/lib/components/Settings.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
pickDirectories,
reloadConfig,
repoRuleTrace,
rescanApps,
saveConfig,
setAutostart,
setAutostartPortal,
Expand All @@ -42,10 +41,18 @@
let {
onback,
onsaved,
onrescanrepos,
onrescanapps,
ondirtychange,
}: {
onback: () => void;
/** Fires after every successful save — App.svelte re-syncs the config
* fields it caches (dismiss mode, apps enabled). Never rescans on its
* own; that's `onrescanrepos` / `onrescanapps`, so a save that only
* touches unrelated settings doesn't kick off a scan of either kind. */
onsaved: () => void;
onrescanrepos: () => void;
onrescanapps: () => void;
/** Fires whenever `dirty` changes, so the parent can gate other ways to
* leave (Esc, the mouse back-button) that don't go through `onback`. */
ondirtychange?: (dirty: boolean) => void;
Expand Down Expand Up @@ -393,25 +400,27 @@
const { ok, appsChanged } = await persist();
if (!ok) return;
note("Saved.");
onsaved(); // App.svelte re-scans repos
if (appsChanged) void rescanApps();
onsaved();
onrescanrepos();
if (appsChanged) onrescanapps();
}

// The contextual "Rescan" buttons save first, so they act on what's on screen
// rather than whatever was last saved.
async function saveAndRescanRepos() {
const { ok } = await persist();
if (!ok) return;
note("Saved — rescanning repositories…");
note("Saved — Scanning repos…");
onsaved();
onrescanrepos();
}

async function saveAndRescanApps() {
const { ok } = await persist();
if (!ok) return;
note("Saved — rescanning apps…");
note("Saved — Scanning apps…");
onsaved(); // App.svelte refreshes its cached apps.enabled, among other things
await rescanApps();
onrescanapps();
}

/** Discard edits back to the last-saved values. Stays on this screen. */
Expand Down
6 changes: 3 additions & 3 deletions src/lib/hotkeys.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ describe("classifyHotkey", () => {
});

it("accepts the shipped defaults and other roomy combos", () => {
expect(level("CmdOrCtrl+Shift+Space")).toBe("ok"); // repo browser default
expect(level("CmdOrCtrl+Shift+Period")).toBe("ok"); // app launcher default
expect(level("CmdOrCtrl+Backslash")).toBe("ok"); // repo browser default
expect(level("CmdOrCtrl+Period")).toBe("ok"); // app launcher default
expect(level("CmdOrCtrl+Alt+J")).toBe("ok");
expect(level("CmdOrCtrl+Shift+F9")).toBe("ok");
});
Expand All @@ -51,6 +51,6 @@ describe("classifyHotkey", () => {
it("carries a reason for non-ok verdicts", () => {
expect(classifyHotkey("Alt+Tab").reason).toBeTruthy();
expect(classifyHotkey("CmdOrCtrl+Shift+N").reason).toBeTruthy();
expect(classifyHotkey("CmdOrCtrl+Shift+Space").reason).toBeUndefined();
expect(classifyHotkey("CmdOrCtrl+Backslash").reason).toBeUndefined();
});
});