Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
eef9a62
feat: tighten pencil engine, redesign arrow, refresh toolbar icons
opensourcebharat May 20, 2026
e46e653
feat: permission-aware screenshot + remember save folder
opensourcebharat May 20, 2026
8aefd00
fix: portal-mounted modal/toast invisible against dark toolbar
opensourcebharat May 20, 2026
a35a635
refactor: permission + save errors become side panels, success uses t…
opensourcebharat May 20, 2026
e73aee2
fix: status side panel actually shows + stop leaking focus listeners
opensourcebharat May 20, 2026
c3521cb
fix: macOS recheck saw stale cached 'denied' after permission granted
opensourcebharat May 20, 2026
902d39f
fix: when getSources() throws, promote Relaunch over Recheck
opensourcebharat May 20, 2026
38d4180
fix: snip live border + action menu; exclude toolbar from screenshots
opensourcebharat May 20, 2026
71bfe95
fix: snip Copy / Save now exits drawMode so user can paste / move on
opensourcebharat May 20, 2026
aeea643
perf: binary IPC + createImageBitmap + toBlob for snip + screenshot
opensourcebharat May 20, 2026
6af8aa4
chore: wire build/icon.png as the project logo (with SVG fallback)
opensourcebharat May 20, 2026
dd124c7
refactor: hover-hint + settings + status-dot move to bottom footer
opensourcebharat May 20, 2026
afbc462
fix: collapsed pill — click anywhere to restore, full logo visible
opensourcebharat May 20, 2026
2b8f08c
fix: distinct pen / pencil icons + footer no longer clips after restore
opensourcebharat May 20, 2026
7b47f89
feat: AI integration scaffolding — providers, credentials, IPC
opensourcebharat May 21, 2026
58771b4
feat: Ask AI UI — Settings section, ChatPanel, snip-menu button
opensourcebharat May 21, 2026
88e459b
ai integration
opensourcebharat May 29, 2026
da88bd6
Merge origin/main into feat/ai-integration
opensourcebharat May 29, 2026
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
66 changes: 66 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
name: release

# Triggered by the tags that scripts/release.sh pushes (vX.Y.Z). Builds
# installers on each OS and publishes them to a GitHub Release, then
# flips that release from draft to public once every OS has finished.
on:
push:
tags:
- 'v*'

permissions:
contents: write # create the release and upload assets

concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false

jobs:
build:
name: build (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [macos-latest, windows-latest, ubuntu-latest]
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node 22
uses: actions/setup-node@v4
with:
node-version: '22'
cache: npm

- name: Install dependencies
run: npm ci

- name: Build and publish to the draft release
run: npm run release:ci
env:
# Lets electron-builder create/upload to the GitHub Release.
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Optional macOS code signing + notarization. These only take
# effect when the secrets exist; without them the macOS build
# is unsigned (and macOS auto-update stays disabled until they
# are added). Windows/Linux publish and auto-update regardless.
CSC_LINK: ${{ secrets.CSC_LINK }}
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}

publish:
name: publish release
needs: build
runs-on: ubuntu-latest
permissions:
contents: write
steps:
# All OS builds succeeded and uploaded their assets to the draft
# release — flip it public and mark it the latest.
- name: Promote draft to published
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh release edit "${GITHUB_REF_NAME}" --repo "${GITHUB_REPOSITORY}" --draft=false --latest
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### AI (new)
- **Ask AI on a snip** — capture a region and get it *solved/answered*
(math, code, questions, errors), not just described. A dockable chat
panel streams the response.
- **Local-first routing** — on-device models via [Ollama](https://ollama.com)
with a first-run setup wizard; optional cloud providers as fallback.
- **Providers**: Ollama (local), Anthropic Claude, OpenAI, Google Gemini,
DeepSeek (text), and Sarvam AI (Indic-strong Vision OCR → LLM solve).
- **Autocorrect** for typed text and recognized handwriting (per-kind
toggles), **handwriting recognition** (drawn ink → text via a vision
model), and **trader chart analysis** from drawn levels.
- **On-device learning (RAG)** — accepted corrections are remembered
locally to personalize suggestions; nothing leaves the machine.
- **Per-profile** system prompts and model overrides in Settings → AI.
- Follow-up questions retain full conversation context (image/OCR carried
across turns) until a new snip starts a fresh conversation.

### Auto-update (new)
- Background auto-update from GitHub Releases via `electron-updater`:
downloads in the background and applies on quit. Settings → Updates adds
an automatic-updates toggle, a manual check, and restart-to-update.
(macOS auto-update activates once the build is signed + notarized.)

### Build & release (new)
- Tag-driven release automation: `npm run release[:minor|:major]` bumps
the version, rolls the changelog, commits, tags, and pushes.
- GitHub Actions builds macOS / Windows / Linux on a `v*` tag and
publishes installers + update manifests to GitHub Releases.
- macOS builds now also emit a `.zip` (for Squirrel.Mac auto-update).

## [1.0.0] — 2026-05-20

Initial open-source release of Lekhini, by
Expand Down
105 changes: 90 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ content without switching apps.
by default with a hotkey to toggle drawing.
- **Undo/redo, clear, screenshot**, all from the toolbar or global
hotkeys.
- **AI, local-first.** Snip anything and **Ask AI** to solve/explain it,
autocorrect handwriting and typed text, and analyze trader charts —
running on-device via Ollama by default, with optional cloud providers.
Everything is opt-in and configured in **Settings → AI**. See
[docs/AI.md](./docs/AI.md).

## Tech stack

Expand All @@ -48,8 +53,12 @@ content without switching apps.
- **Zustand** vanilla store with snapshot-based undo/redo history
- **`electron-store`** for persisted orientation / theme / per-tool
widths / active tool / color, with schema-tolerant hydration
- **`electron-builder`** for notarized `.dmg` (and `nsis` /
`AppImage` for Windows / Linux)
- **Local-first AI** — [Ollama](https://ollama.com) for on-device models,
with optional cloud providers (Anthropic, OpenAI, Gemini, DeepSeek,
Sarvam AI). See [docs/AI.md](./docs/AI.md).
- **`electron-builder`** for `.dmg` + `.zip` (macOS), `nsis` (Windows),
`AppImage` (Linux), and **`electron-updater`** for background
auto-updates from GitHub Releases

## Architecture

Expand Down Expand Up @@ -154,29 +163,95 @@ After granting, quit and relaunch the app.

Switch profile from **Settings → Profile**. The choice is remembered.

## AI

Lekhini's AI is **local-first and entirely opt-in**. With **Local AI**
on, snips and text never leave your machine; cloud providers are an
optional fallback you configure with your own API key. Nothing AI-related
is enabled until you set one of them up.

**What you can do**

- **Ask AI about a snip** — drag a region, click **Ask AI**, and a chat
panel opens. It *solves/answers* what's in the image (math, code,
questions, errors), not just describes it. Follow-up questions keep the
full conversation context until you start a new snip.
- **Autocorrect** — typed text and recognized handwriting can be cleaned
up automatically (toggle per kind in Settings).
- **Handwriting recognition** — drawn ink is transcribed to text on
device via a vision model.
- **Trader analysis** — the Trader profile can hand your drawn levels to
the AI for a written read.
- **On-device learning (RAG)** — accepted corrections are remembered
locally to personalize future suggestions. Stored only on your machine.

**Providers**

| Provider | Kind | Vision | Notes |
| --- | --- | --- | --- |
| **Ollama (Local)** | on-device | yes | Default. Private, free, no key. |
| **Anthropic Claude** | cloud | yes | API key required |
| **OpenAI** | cloud | yes | API key required |
| **Google Gemini** | cloud | yes | API key required |
| **DeepSeek** | cloud | no (text) | Strong reasoning; image snips answer from text |
| **Sarvam AI** | cloud | yes (OCR→LLM) | Indic-strong document OCR, then solves |

**Configure** in **Settings → AI**: enable Local AI (a first-run wizard
installs Ollama + recommended models), or pick a cloud provider and paste
its key. Routing is local-first — if Local AI is on and a suitable model
is installed it's used; otherwise the configured cloud provider is.

Full details — architecture, the resolver, per-profile prompts/models,
privacy, and how each provider is wired — are in
**[docs/AI.md](./docs/AI.md)**.

## Updates

Installed builds **auto-update from GitHub Releases** via
`electron-updater`. By default new versions download in the background
and apply on the next quit/relaunch. Manage this in **Settings →
Updates**: toggle **Automatic updates**, **Check for updates** on demand,
or **Restart to update** once a version is downloaded.

> macOS auto-update requires a signed + notarized build. Until signing
> is configured, macOS users update manually (Settings → Updates links to
> the latest GitHub Release); Windows and Linux auto-update out of the box.

## Building installers

Build for the **current OS** (most reliable locally):

```bash
# macOS — set these in your shell for signed/notarized builds
export APPLE_ID="you@example.com"
export APPLE_APP_SPECIFIC_PASSWORD="xxxx-xxxx-xxxx-xxxx"
export APPLE_TEAM_ID="ABCDE12345"
export CSC_LINK="path/to/DeveloperIDApplication.p12"
export CSC_KEY_PASSWORD="..."
npm run build # installers for this OS → release/
npm run build:unpacked # unpacked app dir, no installer (fastest)
```

npm run build:mac # produces release/Lekhini-1.0.0-arm64.dmg (+ x64)
npm run build:win # produces release/Lekhini Setup 1.0.0.exe
npm run build:linux # produces release/Lekhini-1.0.0.AppImage
Per-OS targets (cross-OS locally needs the right toolchains — CI is the
supported path for all three at once):

```bash
npm run build:mac # release/Lekhini-<ver>-arm64.dmg (+ x64) + .zip
npm run build:win # release/Lekhini Setup <ver>.exe
npm run build:linux # release/Lekhini-<ver>.AppImage
npm run build:all # attempt mac + win + linux (-mwl)
```

Unsigned local builds (no notarization):
Optional **macOS signing + notarization** — set these in your shell (or
as CI secrets) and the build signs automatically; omit them for an
unsigned build:

```bash
npm run build:unpacked
export APPLE_ID="you@example.com"
export APPLE_APP_SPECIFIC_PASSWORD="xxxx-xxxx-xxxx-xxxx"
export APPLE_TEAM_ID="ABCDE12345"
export CSC_LINK="path/to/DeveloperIDApplication.p12"
export CSC_KEY_PASSWORD="..."
```

GitHub Actions on `macos-14` is the recommended CI target — same
`npm run build` command, with the secrets above set as repo secrets.
**Automated multi-OS builds + releases** run in CI — pushing a `vX.Y.Z`
tag builds macOS / Windows / Linux in parallel and publishes them to
GitHub Releases. See [RELEASING.md](./RELEASING.md); the one-liner is
`npm run release` (patch) / `release:minor` / `release:major`.

## Hard constraint: macOS fullscreen Spaces

Expand Down
89 changes: 54 additions & 35 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,42 +45,61 @@ Bug fixes and small polish that don't change behavior intentionally:
- Dependency bumps that don't change behavior.
- Documentation-only changes.

## Cutting a release

1. Make sure `main` is green: `npm run typecheck` and `npm run build`
succeed locally. CI on the release commit must also be green.
2. Decide the version bump (major / minor / patch) per the policy above.
3. Update `CHANGELOG.md`:
- Move items from `[Unreleased]` into a new versioned section.
- Add a dated heading: `## [X.Y.Z] — YYYY-MM-DD`.
- Update the link references at the bottom of the file.
4. Bump `package.json`'s `version` field to the new version. Do NOT
use `npm version` if your workflow doesn't also tag — keep these
steps explicit.
5. Commit:
## Cutting a release (automated)

Releases are **tag-driven**. One command bumps the version, rolls the
changelog, commits, tags, and pushes — then CI builds every OS and
publishes the GitHub Release. You do not build or upload anything by hand.

1. Make sure the branch is clean and green (`npm run typecheck`, and CI
on the latest commit is passing). Land all release-worthy changes
first, with notes under `## [Unreleased]` in `CHANGELOG.md`.
2. Run the release script with the bump type:
```bash
npm run release # patch (X.Y.Z+1) — the default
npm run release:minor # X.Y+1.0
npm run release:major # X+1.0.0
# or an exact version:
bash scripts/release.sh 1.4.0
```
git add CHANGELOG.md package.json package-lock.json
git commit -m "chore: release vX.Y.Z"
```
6. Tag the commit:
```
git tag -a vX.Y.Z -m "Lekhini vX.Y.Z"
```
7. Push commit and tag:
```
git push origin main
git push origin vX.Y.Z
```
8. Build the installers (signed where applicable):
```
npm run build:mac # produces release/Lekhini-X.Y.Z-arm64.dmg
npm run build:win # produces release/Lekhini Setup X.Y.Z.exe
npm run build:linux # produces release/Lekhini-X.Y.Z.AppImage
```
9. Create a GitHub Release from the `vX.Y.Z` tag:
- Title: `vX.Y.Z`
- Body: copy the relevant CHANGELOG section.
- Attach the installers from step 8.
This (see `scripts/release.sh`):
- refuses to run on a dirty tree,
- validates with `npm run prebuild` (typecheck + build),
- bumps `package.json` + `package-lock.json` (no tag yet),
- rolls `CHANGELOG.md`: `[Unreleased]` → a dated `[X.Y.Z]` section and
updates the link refs (`scripts/update-changelog.mjs`),
- commits `chore(release): vX.Y.Z`, tags `vX.Y.Z`, and pushes both.
3. The pushed tag triggers **`.github/workflows/release.yml`**, which:
- builds installers on macOS, Windows, and Linux in parallel
(`npm run release:ci` → `electron-builder --publish always`),
- uploads them plus the `latest*.yml` update manifests to a **draft**
GitHub Release for the tag,
- flips the release **public** once all three OSes succeed.
4. Watch it at <https://github.com/opensourcebharat/lekhini/actions>.
When green, the release is live and installed apps will auto-update.

### macOS signing (optional)

The workflow signs + notarizes macOS builds **only when** these repo
secrets exist; otherwise the macOS build is unsigned (and macOS
auto-update stays disabled until they're added — Windows/Linux are
unaffected): `CSC_LINK`, `CSC_KEY_PASSWORD`, `APPLE_ID`,
`APPLE_APP_SPECIFIC_PASSWORD`, `APPLE_TEAM_ID`.

### Local build (optional)

To produce installers without releasing, use `npm run build` (current
OS) or `npm run build:mac|win|linux`. These write to `release/` and do
**not** publish.

## Auto-update

Installed apps check GitHub Releases via `electron-updater`
(`src/main/updater.ts`), download in the background, and apply on quit.
Users control this in **Settings → Updates** (toggle, manual check,
restart-to-update). Because the feed is GitHub Releases, **every public
release is automatically an update** for existing installs — so prefer
small, frequent patch releases in the early stage.

## Tag naming

Expand Down
Loading
Loading