diff --git a/README.md b/README.md index fd8d546..8b43fa8 100644 --- a/README.md +++ b/README.md @@ -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. @@ -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. @@ -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. @@ -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 `/dev-prompt/repos.json`. +The discovered repo list is cached at `/dev-prompt/repos.json`; the +installed-app list (`>` scope) is cached alongside it at +`/dev-prompt/apps.json`. ### How detection works diff --git a/docs/configuration.md b/docs/configuration.md index 0ae258e..e958397 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -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 @@ -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 `/dev-prompt/apps.json`. + --- ## Roots, scan depth, cache diff --git a/docs/img/0_dev-prompt_demo.gif b/docs/img/0_dev-prompt_demo.gif new file mode 100644 index 0000000..dd15bde Binary files /dev/null and b/docs/img/0_dev-prompt_demo.gif differ diff --git a/docs/img/README.md b/docs/img/README.md index 534b9d2..d6e5988 100644 --- a/docs/img/README.md +++ b/docs/img/README.md @@ -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 diff --git a/src-tauri/src/config.rs b/src-tauri/src/config.rs index f9d63f8..2b4136f 100644 --- a/src-tauri/src/config.rs +++ b/src-tauri/src/config.rs @@ -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(), @@ -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, @@ -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")); diff --git a/src-tauri/src/default_config.yaml b/src-tauri/src/default_config.yaml index 420b885..85ba92f 100644 --- a/src-tauri/src/default_config.yaml +++ b/src-tauri/src/default_config.yaml @@ -21,10 +21,10 @@ # {{name}} project/repo name {{file}} matched file {{file.name}} {{file.stem}} # {{env:VAR}} {{}} 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 diff --git a/src/App.svelte b/src/App.svelte index 430421d..94e6de4 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -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, ); @@ -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`; @@ -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; @@ -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} diff --git a/src/lib/components/AppList.svelte b/src/lib/components/AppList.svelte index c23bebc..d7721a6 100644 --- a/src/lib/components/AppList.svelte +++ b/src/lib/components/AppList.svelte @@ -37,7 +37,7 @@
{#if entries.length === 0}
- {scanning ? "Scanning for installed apps…" : "No apps match."} + {scanning ? "Scanning apps…" : "No apps match."}
{:else} {#each entries as e, i (e.app.exec)} diff --git a/src/lib/components/Settings.svelte b/src/lib/components/Settings.svelte index d29d2e2..466393e 100644 --- a/src/lib/components/Settings.svelte +++ b/src/lib/components/Settings.svelte @@ -16,7 +16,6 @@ pickDirectories, reloadConfig, repoRuleTrace, - rescanApps, saveConfig, setAutostart, setAutostartPortal, @@ -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; @@ -393,8 +400,9 @@ 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 @@ -402,16 +410,17 @@ 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. */ diff --git a/src/lib/hotkeys.test.ts b/src/lib/hotkeys.test.ts index 6d82ac3..636b3dc 100644 --- a/src/lib/hotkeys.test.ts +++ b/src/lib/hotkeys.test.ts @@ -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"); }); @@ -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(); }); });