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
109 changes: 109 additions & 0 deletions .github/workflows/build-and-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
name: build-and-release

# Builds the x64 plugin, runs the Catch2 test suite, and packages a
# Plugins-Admin-ready zip.
#
# * push / PR -> build + test + upload the zip as a CI artifact
# * push a "v*" tag -> additionally publish a GitHub Release with the zip
# and print the SHA-256 needed for the nppPluginList PR
#
# The DLL is placed at the ROOT of the zip and named exactly LLMAssistant.dll,
# which is what Notepad++ Plugins Admin requires.

on:
push:
branches: [main]
tags: ["v*"]
pull_request:
workflow_dispatch:

permissions:
contents: write

jobs:
build:
runs-on: windows-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Add MSBuild to PATH
uses: microsoft/setup-msbuild@v2

- name: Build plugin (x64 Release)
run: >
msbuild LLMAssistant.vcxproj
/p:Configuration=Release
/p:Platform=x64
/p:PlatformToolset=v143
/p:WindowsTargetPlatformVersion=10.0
/m

- name: Configure tests (CMake)
run: cmake -S . -B CMakeBuild

- name: Build tests
run: cmake --build CMakeBuild --config Debug

- name: Run tests
run: ctest --test-dir CMakeBuild --output-on-failure -C Debug

- name: Package Plugins-Admin zip
id: package
shell: pwsh
run: |
if ("${{ github.ref_type }}" -eq "tag") {
$ver = "${{ github.ref_name }}" -replace '^v', ''
} else {
$ver = "dev"
}
$zip = "LLMAssistant_v${ver}_x64.zip"

# DLL must sit at the zip root and be named exactly LLMAssistant.dll.
Copy-Item "x64\Release\LLMAssistant.dll" ".\LLMAssistant.dll" -Force
if (Test-Path $zip) { Remove-Item $zip -Force }
Compress-Archive -Path ".\LLMAssistant.dll" -DestinationPath $zip -Force

$hash = (Get-FileHash $zip -Algorithm SHA256).Hash.ToUpper()
"zip=$zip" >> $env:GITHUB_OUTPUT
"version=$ver" >> $env:GITHUB_OUTPUT
"sha256=$hash" >> $env:GITHUB_OUTPUT

Write-Host "==============================================="
Write-Host "Package : $zip"
Write-Host "SHA-256 : $hash <-- use as \"id\" in pl.x64.json"
Write-Host "==============================================="

- name: Upload CI artifact
uses: actions/upload-artifact@v4
with:
name: LLMAssistant-x64
path: ${{ steps.package.outputs.zip }}
if-no-files-found: error

- name: Publish GitHub Release
if: github.ref_type == 'tag'
uses: softprops/action-gh-release@v2
with:
files: ${{ steps.package.outputs.zip }}
generate_release_notes: true
body: |
### Notepad++ Plugins Admin metadata

Add / update this in `nppPluginList` (`src/pl.x64.json`):

```json
{
"folder-name": "LLMAssistant",
"display-name": "LLM Assistant",
"version": "${{ steps.package.outputs.version }}",
"id": "${{ steps.package.outputs.sha256 }}",
"repository": "${{ github.server_url }}/${{ github.repository }}/releases/download/${{ github.ref_name }}/${{ steps.package.outputs.zip }}",
"description": "AI-powered text processing (enhance, translate, summarize, grammar, code) via the OpenAI API, directly inside Notepad++.",
"author": "Gvo87",
"homepage": "${{ github.server_url }}/${{ github.repository }}"
}
```

`id` is the SHA-256 of the attached zip: `${{ steps.package.outputs.sha256 }}`
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@
x64/
CMakeBuild/
_deps/

# Release packaging output
dist/
*.zip
7 changes: 7 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,12 @@ target_include_directories(LLMAssistantTests PRIVATE src)
target_compile_features(LLMAssistantTests PRIVATE cxx_std_17)
target_link_libraries(LLMAssistantTests PRIVATE Catch2::Catch2WithMain)

# The source files are UTF-8. Without /utf-8, MSVC decodes narrow/wide string
# literals using the system ANSI code page (e.g. CP1252 on CI runners), which
# corrupts non-ASCII literals like L"Café 🙂" and breaks the Unicode tests.
if(MSVC)
target_compile_options(LLMAssistantTests PRIVATE /utf-8)
endif()

include(Catch)
catch_discover_tests(LLMAssistantTests)
2 changes: 2 additions & 0 deletions LLMAssistant.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;LLMASSISTANT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions>
<AdditionalIncludeDirectories>$(ProjectDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
Expand All @@ -79,6 +80,7 @@
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;LLMASSISTANT_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalOptions>/utf-8 %(AdditionalOptions)</AdditionalOptions>
<AdditionalIncludeDirectories>$(ProjectDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
Expand Down
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,18 @@ A powerful Notepad++ plugin that integrates Large Language Model (LLM) capabilit

## 📦 Installation

### Quick Installation
### Via Plugins Admin (recommended)
Once the plugin is accepted into the official
[nppPluginList](https://github.com/notepad-plus-plus/nppPluginList), you can install
it directly from Notepad++:
1. Open **Plugins → Plugins Admin…**
2. On the **Available** tab, search for **LLM Assistant**
3. Check the box and click **Install** — Notepad++ handles the download and future updates automatically

Maintainers: see [`packaging/PLUGINS_ADMIN_SUBMISSION.md`](packaging/PLUGINS_ADMIN_SUBMISSION.md)
for how releases and the Plugins Admin listing are produced.

### Quick Installation (manual)
1. **Download** the plugin: `LLMAssistant.dll`
2. **Create directory**: `C:\Program Files\Notepad++\plugins\LLMAssistant\`
3. **Copy file**: Place `LLMAssistant.dll` in the created directory
Expand Down
108 changes: 108 additions & 0 deletions packaging/PLUGINS_ADMIN_SUBMISSION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Getting LLM Assistant into Notepad++ Plugins Admin

This guide takes the plugin from source to being searchable, installable, and
auto-updatable inside **Notepad++ → Plugins → Plugins Admin**.

Plugins Admin is driven by the official
[`notepad-plus-plus/nppPluginList`](https://github.com/notepad-plus-plus/nppPluginList)
repository. A plugin appears there only after a small JSON entry (pointing at a
downloadable release zip) is merged into `src/pl.x64.json`. This repo already
contains everything needed to produce that entry.

---

## What Plugins Admin requires

| Requirement | How this repo satisfies it |
| --- | --- |
| DLL carries a real binary version (for update detection) | `src/Version.h` + `VERSIONINFO` in `src/LLMAssistant.rc` |
| A downloadable `.zip` with the DLL **at the zip root**, named exactly `LLMAssistant.dll` | `packaging/make-release-zip.ps1` and the release workflow |
| SHA-256 of that zip (used as `id`) | printed by the script and the workflow |
| `folder-name` == DLL name (`LLMAssistant`) | enforced in the JSON entry |
| A stable download URL | GitHub Release asset created by the workflow |

---

## Step 1 — Cut a versioned release of the plugin

1. Set the version in `src/Version.h` (e.g. `1.0`) — this is the single source of
truth and gets compiled into the DLL.
2. Commit, then tag and push:

```bash
git tag v1.0
git push origin v1.0
```

3. The [`build-and-release`](../.github/workflows/build-and-release.yml) workflow
will build the x64 Release DLL, run the test suite, package
`LLMAssistant_v1.0_x64.zip` (DLL at the zip root), publish a **GitHub Release**,
and print the **SHA-256** in both the logs and the release notes.

> No CI? Build locally with `build.bat`, then run:
>
> ```powershell
> pwsh packaging\make-release-zip.ps1 -Version 1.0
> ```
>
> It writes `dist\LLMAssistant_v1.0_x64.zip`, prints the SHA-256, and generates the
> exact JSON entry to paste.

## Step 2 — Fill in the nppPluginList entry

Use `packaging/nppPluginList-entry.json` as the template. Replace `id` with the
SHA-256 from Step 1 and make sure `repository` points at the uploaded release zip:

```json
{
"folder-name": "LLMAssistant",
"display-name": "LLM Assistant",
"version": "1.0",
"id": "<SHA-256 OF THE RELEASE ZIP>",
"repository": "https://github.com/Gvo87/NotepadPlusPlus-LLM-Plugin/releases/download/v1.0/LLMAssistant_v1.0_x64.zip",
"description": "AI-powered text processing (enhance, translate, summarize, grammar, code) via the OpenAI API, directly inside Notepad++.",
"author": "Gvo87",
"homepage": "https://github.com/Gvo87/NotepadPlusPlus-LLM-Plugin"
}
```

## Step 3 — Test locally before the PR (required)

1. Download a **debug** build of Notepad++ (x64) — see the
[official instructions](https://npp-user-manual.org/docs/plugins/#test-your-plugins-locally).
2. Grab `src/pl.x64.json` from your fork of `nppPluginList` and save it to
`<npp>\plugins\Config\nppPluginList.json`.
3. Add your entry to the `npp-plugins` array in that file.
4. Launch `notepad++.x64.dbg.exe` and open **Plugins → Plugins Admin**.
5. Confirm the plugin shows under **Available**, installs correctly, and appears
under **Installed**.

## Step 4 — Open the PR

Fork [`notepad-plus-plus/nppPluginList`](https://github.com/notepad-plus-plus/nppPluginList),
add the entry to `src/pl.x64.json` (keep the array sorted/consistent with
neighbors), commit, and open a PR that **only** modifies that JSON file. Once
merged and the signed `nppPluginList.dll` ships, everyone can search and install
LLM Assistant from Plugins Admin.

---

## How auto-update works afterwards

Plugins Admin compares the **installed DLL's binary version** with the `version`
in `pl.x64.json`. To ship an update:

1. Bump `src/Version.h`.
2. Tag `vX.Y` and push → the workflow builds and publishes a new release zip.
3. Open a follow-up PR to `nppPluginList` updating that plugin's `version`, `id`
(new SHA-256), and `repository` URL.

Users then see the new version under the **Updates** tab.

## Notes / limits

- This plugin ships **x64 only**, so it goes in `pl.x64.json`. To also list it for
32-bit or ARM64 Notepad++, add the corresponding build configurations to the
project and workflow and submit entries to `pl.x86.json` / `pl.arm64.json`.
- If the plugin ever adds/removes Notepad++ API calls with version constraints,
set `npp-compatible-versions` (and `old-versions-compatibility`) in the entry.
105 changes: 105 additions & 0 deletions packaging/make-release-zip.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<#
.SYNOPSIS
Builds a Notepad++ Plugins-Admin-ready zip for LLM Assistant and prints the
SHA-256 + a ready-to-paste nppPluginList (pl.x64.json) entry.

.DESCRIPTION
Notepad++ Plugins Admin requires:
* a downloadable .zip,
* with the plugin DLL at the ROOT of the zip,
* named exactly LLMAssistant.dll,
* and the SHA-256 of that zip recorded as "id" in nppPluginList.

This script packages an already-built DLL (x64\Release\LLMAssistant.dll by
default) into that exact layout, then emits the metadata you need for the PR.

.PARAMETER Version
Plugin version, e.g. "1.0". Must match the DLL's binary version resource and
the git tag (vVersion). Defaults to reading src\Version.h.

.PARAMETER DllPath
Path to the built DLL. Defaults to x64\Release\LLMAssistant.dll.

.PARAMETER OutDir
Where to write the zip. Defaults to .\dist.

.EXAMPLE
pwsh packaging\make-release-zip.ps1 -Version 1.0
#>
[CmdletBinding()]
param(
[string]$Version,
[string]$DllPath = "x64\Release\LLMAssistant.dll",
[string]$OutDir = "dist"
)

$ErrorActionPreference = "Stop"
$repoRoot = Split-Path -Parent $PSScriptRoot
Set-Location $repoRoot

function Read-VersionFromHeader {
$header = Join-Path $repoRoot "src\Version.h"
if (-not (Test-Path $header)) { return $null }
$text = Get-Content $header -Raw
$maj = [regex]::Match($text, 'LLM_VERSION_MAJOR\s+(\d+)').Groups[1].Value
$min = [regex]::Match($text, 'LLM_VERSION_MINOR\s+(\d+)').Groups[1].Value
if ($maj -and $min) { return "$maj.$min" }
return $null
}

if (-not $Version) {
$Version = Read-VersionFromHeader
if (-not $Version) { throw "Could not determine version. Pass -Version explicitly (e.g. -Version 1.0)." }
Write-Host "Using version from src\Version.h: $Version"
}

if (-not (Test-Path $DllPath)) {
throw "DLL not found at '$DllPath'. Build the Release|x64 configuration first (build.bat), or pass -DllPath."
}

New-Item -ItemType Directory -Force -Path $OutDir | Out-Null

# Stage the DLL at the zip root under the exact required name.
$staging = Join-Path $OutDir "_staging"
if (Test-Path $staging) { Remove-Item $staging -Recurse -Force }
New-Item -ItemType Directory -Force -Path $staging | Out-Null
Copy-Item $DllPath (Join-Path $staging "LLMAssistant.dll") -Force

$zipName = "LLMAssistant_v${Version}_x64.zip"
$zipPath = Join-Path $OutDir $zipName
if (Test-Path $zipPath) { Remove-Item $zipPath -Force }
Compress-Archive -Path (Join-Path $staging "*") -DestinationPath $zipPath -Force
Remove-Item $staging -Recurse -Force

$sha = (Get-FileHash $zipPath -Algorithm SHA256).Hash.ToUpper()

# Guard the git lookup: on machines without Git in PATH this must not abort the
# script (ErrorActionPreference is "Stop"); fall back to the known repo URL.
$repoUrl = $null
if (Get-Command git -ErrorAction SilentlyContinue) {
try { $repoUrl = (& git config --get remote.origin.url) 2>$null } catch { $repoUrl = $null }
}
if ($repoUrl) { $repoUrl = $repoUrl -replace '\.git$', '' } else { $repoUrl = "https://github.com/Gvo87/NotepadPlusPlus-LLM-Plugin" }
$downloadUrl = "$repoUrl/releases/download/v$Version/$zipName"

Write-Host ""
Write-Host "===================================================================="
Write-Host " Package : $zipPath"
Write-Host " SHA-256 : $sha"
Write-Host "===================================================================="
Write-Host ""
Write-Host "Add this entry to nppPluginList -> src/pl.x64.json (npp-plugins array):"
Write-Host ""

$entry = [ordered]@{
"folder-name" = "LLMAssistant"
"display-name" = "LLM Assistant"
"version" = $Version
"id" = $sha
"repository" = $downloadUrl
"description" = "AI-powered text processing (enhance, translate, summarize, grammar, code) via the OpenAI API, directly inside Notepad++."
"author" = "Gvo87"
"homepage" = $repoUrl
}

$entry | ConvertTo-Json | Write-Output
10 changes: 10 additions & 0 deletions packaging/nppPluginList-entry.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"folder-name": "LLMAssistant",
"display-name": "LLM Assistant",
"version": "1.0",
"id": "REPLACE_WITH_SHA256_OF_THE_RELEASE_ZIP",
"repository": "https://github.com/Gvo87/NotepadPlusPlus-LLM-Plugin/releases/download/v1.0/LLMAssistant_v1.0_x64.zip",
"description": "AI-powered text processing (enhance, translate, summarize, grammar, code) via the OpenAI API, directly inside Notepad++.",
"author": "Gvo87",
"homepage": "https://github.com/Gvo87/NotepadPlusPlus-LLM-Plugin"
}
Loading
Loading