Commit Graph
13 Commits
Author SHA1 Message Date
Amit Haridas 3ec9ff0130 chore(release): bump v4.12.0 → v4.13.0
v4.13.0 ships:
  - Quick Switcher overlay (Cmd+P): fuzzy match across recent files
    + open tabs, workspace search wired to the active tab's parent dir
  - Inline AI assist (Cmd+K): Rewrite / Shorten / Expand with real
    SSE streaming, in-editor typewriter, cancel mid-flight, first-use
    confirmation
  - electron-updater integration: Help > Check for Updates now polls
    GitHub releases through the auto-updater; autoDownload + autoInstall
    enabled; browser fallback on dev/error
  - 13 commits since v4.12.0, 112 new tests (1187 → 1299), lint+format
    green throughout.

Amit Haridas
2026-09-30 21:02:46 +05:30
Amit Haridas e826c0e624 feat(ai-assist): first-use confirmation + wire Check-for-Updates to updater
Inline AI first-use confirmation (v4.13.0):
  - inline-ai-controller accepts optional confirmFirstUse + getProviderLabel
    deps. On the first action of the session, calls confirmFirstUse with
    {providerLabel, selectionLength, action}; if it returns false, aborts
    and shows error state. Once confirmed, never re-prompts for the
    session.
  - new 'ai-assistant:confirm-info' invoke handler returns
    {provider, model} (no key material) so the renderer can label the
    confirm dialog.
  - electronAPI.aiAssistant.confirmInfo() exposed via preload.
  - renderer.js wires window.confirm() with the provider label as the
    v1 prompt UX; can be upgraded to a custom modal later without
    touching the controller.

Check-for-Updates menu (v4.13.0):
  - Help > Check for Updates now calls ensureAutoUpdater().check()
    instead of opening the Releases page in the browser.
  - Falls back to the browser page if the check throws (dev runs with
    no app-update.yml, network failures, etc.) so the user always has
    a way to update manually.

4 new controller tests cover: confirm called once with provider label
and selection length; declining aborts + surfaces error state; confirm
not asked twice in a session; missing confirmFirstUse dep skips the
prompt and streams normally.

Amit Haridas
2026-09-30 21:02:28 +05:30
Amit Haridas 47315add80 feat(updater): electron-updater wiring + IPC bridge
electron-updater integration so users get fixes automatically:
  - new dependency electron-updater@^6.6.2 in package.json
  - electron-builder.config.js publishes to GitHub releases
    (owner: concreteinfo, repo: markdownconverter)
  - new src/main/auto-updater.js wraps autoUpdater: wires the six
    lifecycle events to a 'updates:status' IPC channel, exposes
    check() and install() helpers, dev-gates via isDev(), and
    swallows the 'missing app-update.yml' error that dev runs hit
  - main.js: ensureAutoUpdater() forwards status to the renderer
    via mainWindow.webContents.send; ipcMain handlers for
    'updates:check' (invoke) and 'updates:install' (invoke)
  - preload.js: 'updates:check' / 'updates:install' added to
    ALLOWED_SEND_CHANNELS; 'updates:status' added to receive
    channels; electronAPI.updates.{check, install, onStatus}
    exposed

7 new tests cover event registration, dev-gate behaviour,
'not-available' fallback for missing app-update.yml, typed event
payloads, error forwarding, and install() quitAndInstall.

Amit Haridas
2026-09-30 20:30:19 +05:30
Amit Haridas 0d3df32140 feat(quick-switcher): derive workspace dir from active tab
The explorer panel's explorerCurrentDir is hardcoded to null, so
the Quick Switcher's workspace toggle was a no-op. Rather than fix
the broken explorer flow, derive the workspace from the active
tab's filePath's parent directory — the most reliable source we
have without restructuring that panel.

deriveWorkspaceDir() handles POSIX and Windows paths (including
mixed separators and trailing separators), returns null for
untitled / empty inputs so the workspace toggle silently stays
off in those cases.

10 new tests cover POSIX, Windows, mixed-separator, trailing
separator, UNC, and invalid input paths.

Amit Haridas
2026-09-30 20:28:22 +05:30
Amit Haridas b36d0919a3 feat(ai-assist): renderer controller + Cmd+K wiring (end-to-end)
createInlineAiController() glues the popover, the streaming bridge,
and the CodeMirror editor together. Each Cmd+K opens a fresh
request; the controller:
  - reads the active selection from the CodeMirror view
  - builds the rewrite/shorten/expand prompt via inline-assist.js
  - clears the selection so chunks can fill back in
  - dispatches each chunk as it arrives (typewriter into the editor)
  - on done: applies the final result (or restores selection on no-op)
  - on error: restores the original selection and shows error state
  - on Esc: cancels the in-flight request, restores the selection,
    hides the popover

main-side streaming handler now reads the AI Assistant plugin's
provider config from settings.json under plugins.ai-assistant.* —
keys never cross the IPC boundary.

renderer.js:
  - new getCreateInlineAiController() lazy loader
  - new ensureInlineAiController() factory
  - new Cmd+K / Ctrl+K keymap handler in the global keydown listener
    that resolves the active tab's CodeMirror view via tabManager

16 new controller tests covering show/handleKey wiring, streaming
chunk application, done/error finalization, stale-request filtering,
and detach cleanup.

Amit Haridas
2026-09-30 20:17:57 +05:30
Amit Haridas 72ca2cb46f feat(ai-assist): floating popover UI + state machine
createInlineAiPopover() mounts a floating toolbar anchored to the
current text selection. Three actions (Rewrite / Shorten / Expand)
plus loading spinner with cancel, and an error state with retry and
dismiss. Click handlers dispatch via onAction/onCancel/onRetry.

Positioning flips below the line if there is no room above and
clamps horizontally so the popover stays inside the viewport.

16 new tests covering mount/visibility, idle actions dispatching the
selection text, state transitions (idle / loading / error), Retry
and Dismiss callbacks, and positioning edge cases.

Amit Haridas
2026-09-30 20:14:13 +05:30
Amit Haridas 1a25669967 feat(ai-assist): SSE streaming + IPC handler
AiProviders.completeStream() — async iterable over provider chunks.
  - OpenAI-style (openai, ollama, lmstudio, *-compatible): parses
    SSE data: {choices:[{delta:{content}}]} payloads.
  - Anthropic: parses event: content_block_delta with delta.text.
  - Honours caller AbortSignal + internal timeout via AbortController.
  - shared parseSseStream() helper handles [DONE] sentinel, partial
    lines across chunk boundaries, reader.releaseLock() in finally.

main.js IPC:
  - 'ai-assist-stream:start' (send) registers a per-requestId entry
    in a Map with an AbortController, iterates completeStream, and
    forwards each chunk via 'ai-assist-stream:chunk' (send).
  - 'ai-assist-stream:cancel' (send) aborts the in-flight request.
  - Terminal 'done' or 'error' (with code+message) is sent after
    each stream; renderer can correlate by requestId.

preload.js exposes aiAssist.{start, cancel, onChunk, onDone, onError}.

5 new tests covering OpenAI delta parsing, Anthropic content_block_delta
parsing, and three synchronous-error paths (no fetch, no messages,
missing key).

Amit Haridas
2026-09-30 20:04:32 +05:30
Amit Haridas cb0b2eac3d feat(ai-assist): pure prompt builder + result applier
buildAssistPrompt(action, selection) returns the {system, messages}
payload for rewrite/shorten/expand. Enforces 8KB selection cap and
typed errors with codes (unknown_action, bad_selection,
empty_selection, selection_too_large) so the IPC layer can map them
to user-friendly messages.

applyAssistResult(original, newText) trims the response and rejects
empty/unchanged payloads so a no-op streaming chunk doesn't churn
the editor.

15 new tests cover action coverage, prompt content, all four error
codes, cap-edge (exactly 8KB OK, 8KB+1 rejected), and trim semantics.

Amit Haridas
2026-09-30 20:01:54 +05:30
Amit Haridas b70dce9574 feat(quick-switcher): wire Cmd+P into File menu + renderer
End-to-end wiring of the Quick Switcher overlay:
  - main.js File menu: new 'Quick Switcher...' item with Cmd+P
    accelerator (moved from Print Preview; Print Preview stays in
    File > Print submenu but loses its shortcut). Recent Files
    submenu and its buildRecentFilesMenu() helper removed.
  - new 'recent-files:get' invoke IPC handler returns the recent
    files array on demand.
  - preload.js: 'recent-files:get' + 'show-quick-switcher' added
    to channel whitelists; quickSwitcher.getRecentFiles() exposed.
  - renderer.js: lazy-mounts the overlay on first 'show-quick-switcher'
    message, fetches recent files via IPC, and shows the overlay.
    File-open goes through the existing 'open-file-path' channel.

Workspace dir is intentionally null for v4.13.0 — the workspace
search toggle is wired but a no-op until the explorer panel exposes
its current directory (next iteration).

Amit Haridas
2026-09-30 20:01:16 +05:30
Amit Haridas 3bd958c064 feat(quick-switcher): modal overlay UI + keyboard nav
createQuickSwitcherOverlay() mounts a centered modal with:
  - input box with autocomplete=off
  - workspace toggle (off by default — recent + open tabs only)
  - keyboard nav: ArrowDown/Up move selection, Enter opens,
    Escape and backdrop-click hide
  - mouse hover also moves selection
  - debounced refresh (default 80ms; 0 in tests)
  - destroy() cleanly removes DOM + listeners

Rank/filter logic delegated to fuzzy-matcher.js. Recent and open tabs
are always in the candidate set; workspace files are added only when
the toggle is on. Selection clamps to bounds after every refresh.

23 new tests covering mount/focus/destroy, initial render, typing &
filtering, full keyboard nav, workspace toggle behavior, and the
selected-path escape hatch.

Amit Haridas
2026-09-30 19:58:18 +05:30
Amit Haridas 33fa14c11a feat(quick-switcher): IPC handler for workspace file listing
listWorkspaceFiles() walks a directory recursively, returning
[{path, name}] for files matching a configurable extension allowlist.
Skips hidden dirs, node_modules, dist/build/coverage, and walks past
permission-denied subdirs instead of throwing. Default allowlist is
markdown variants + .txt; default maxResults caps pathological
workspaces at 2000 entries.

Wired through 'quick-switcher:list-files' invoke channel + a
quickSwitcher.listFiles() convenience method on the preload bridge.
Main-process errors collapse to [] rather than crashing the overlay.

16 new tests covering flat dirs, recursion, skip lists, custom
extensions, maxResults cap, and validation errors.

Amit Haridas
2026-09-30 19:56:50 +05:30
Amit Haridas de7ebecad4 feat(quick-switcher): pure fuzzy matcher + ranker
Tiered scoring (exact 1.0 / stem 1.0 / prefix 0.8 / subseq 0.5+bonuses
capped 0.79) with leftmost+rightmost greedy alignment — rightmost wins
for extension/suffix queries ('md' against 'readme.md'), leftmost wins
for prefix queries ('re' against 'readme.md'). Boost-aware rankResults
applies recency x2 and open-tabs x1.5 multiplicatively; empty query
falls back to boosts alone.

Pure module, no DOM/IPC/Node — runs in renderer or main. 22 new tests.

Amit Haridas
2026-09-30 19:53:44 +05:30
Amit Haridas 02c9250c0d docs(readme): ASCII Art Generator now 17 hand-coded + 400+ FIGlet fonts; insert/copy/save
Amit Haridas
2026-09-14 21:14:36 +05:30