Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
eb7020f
docs(plugins): add marketplace plan
devnull03 Sep 6, 2026
834f482
docs(plugins): detail marketplace repository work
devnull03 Sep 8, 2026
cd29d90
docs(plugins): define registry license policy
devnull03 Sep 8, 2026
37edc06
feat(plugins): add verified package installer
devnull03 Sep 8, 2026
888b143
feat(plugins): add marketplace installation UI
devnull03 Sep 8, 2026
05a80d7
feat(plugins): manage packages and install links
devnull03 Sep 8, 2026
476234f
feat(packaging): register plugin install links
devnull03 Sep 8, 2026
077e3d1
docs(plugins): document marketplace workflows
devnull03 Sep 8, 2026
751ea6d
fix(plugins): keep direct review state compact
devnull03 Sep 8, 2026
ac0bf43
feat(plugins): route and search marketplace requests
devnull03 Sep 8, 2026
714ba44
feat(plugins): show marketplace compatibility state
devnull03 Sep 8, 2026
8f94357
fix(plugins): align package ID validation
devnull03 Sep 8, 2026
6d92b68
feat(plugins): enforce installed package contracts
devnull03 Sep 8, 2026
4f9d3a3
fix(plugins): validate packages before activation
devnull03 Sep 8, 2026
42b6b20
fix(plugins): use Cloudflare catalog endpoint
devnull03 Sep 8, 2026
32592c3
feat: add host-mediated plugin JSON exports
devnull03 Sep 8, 2026
d912eb2
fix: allow bounded full-table export work
devnull03 Sep 8, 2026
ee872f8
fix: address plugin marketplace review
devnull03 Sep 9, 2026
f75e47d
fix(plugins): embed direct installs in settings
devnull03 Sep 9, 2026
e85d8af
fix(plugins): restore settings interactions and diagnostics
devnull03 Sep 9, 2026
73da211
dev: add one-command Windows plugin runner
devnull03 Sep 9, 2026
f3b9f1a
dev: route site services through localhost
devnull03 Sep 10, 2026
b1ff62f
test: cover release site origin
devnull03 Sep 10, 2026
d915731
feat: allow self-hosted site origins
devnull03 Sep 11, 2026
f22d648
fix(plugins): compact review window and fix settings installer
devnull03 Sep 11, 2026
f3ad8e8
fix(plugins): finish installation UX
devnull03 Sep 13, 2026
6fb6512
fix(plugins): show package names instead of IDs in settings
devnull03 Sep 13, 2026
44e7594
feat(settings): add group dividers and a theme picker
devnull03 Sep 13, 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
6 changes: 5 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ env:
# and nothing more — see docs/dev/site-oauth-handoff.md. Absent: the endpoint answers 401 and the
# app carries on with the pair above.
QRATE_GOOGLE_CONFIG_TOKEN: ${{ secrets.QRATE_GOOGLE_CONFIG_TOKEN }}
# Public Ed25519 key for the independently signed plugin catalog. Release builds embed it; local
# builds can set the same variable at compile time or use it at runtime for development.
QRATE_PLUGIN_CATALOG_PUBLIC_KEY: ${{ vars.QRATE_PLUGIN_CATALOG_PUBLIC_KEY }}

jobs:
# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -312,7 +315,8 @@ jobs:
chmod +x scripts/fetch-agent-runtime.sh
./scripts/fetch-agent-runtime.sh "$stage" linux-x64
printf '%s\n' '[Desktop Entry]' 'Type=Application' 'Name=qrate' \
'Exec=qrate' 'Icon=qrate' 'Categories=Office;Database;' \
'Exec=qrate %u' 'Icon=qrate' 'Terminal=false' \
'Categories=Office;Database;' 'MimeType=x-scheme-handler/qrate;' \
> "$stage/qrate.desktop"
mkdir -p dist
tar -czf "dist/$stage.tar.gz" "$stage"
Expand Down
13 changes: 8 additions & 5 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ executable, optionally — a package built without them installs a working, degr
- Only `crates/app` initializes logging (`crates/app/src/logging.rs`, called first in `main`). It writes `%LOCALAPPDATA%\qrate\logs\qrate.log` (previous run rotated to `qrate.old.log`) and installs the panic hook.
- That file is also what Help ▸ Copy Debug Info / Report an Issue paste. Anything logged at `error`/`warn` will end up in a user's bug report, so write the message for the person reading the report — name what failed, not just the error value.

## Plugin API — it lives in three repos, change all of them
## Plugin contracts — keep all repositories synchronized

The Lua plugin surface (`crates/plugin-host`, `crates/plugin-api`) is published outside this repo,
and Luau has no package manager — a plugin is a folder somebody drops in by hand, so the type
Expand All @@ -163,7 +163,8 @@ everyone's editor completion, and nothing in CI would say so.
|---|---|---|
| [`qrate-plugin-template`](https://github.com/devnull03/qrate-plugin-template) | `../qrate-plugin-template` | `types/qrate.lua` — the canonical copy — and the example plugin |
| [`qrate-islandora-plugin`](https://github.com/devnull03/qrate-islandora-plugin) | `plugins/islandora` (gitignored here) | its own copy of `types/qrate.lua` |
| `qrate-site` | `../qrate-site` — the `site` branch of *this* repo, checked out separately | nothing about plugins yet — include it once it documents them |
| [`qrate-plugin-registry`](https://github.com/devnull03/qrate-plugin-registry) | `../qrate-plugin-registry` | package and listing schemas, reviewed records, and the signed catalog |
| `qrate-site` | `../qrate-site` — the `site` branch of *this* repo, checked out separately | catalog-backed marketplace and submission documentation |

**When you add, rename, or remove anything a plugin can see** — a host function, a descriptor field,
a `SettingKind`, a hook's arguments, what a scope contains:
Expand All @@ -179,9 +180,11 @@ a `SettingKind`, a hook's arguments, what a scope contains:
from disk, so they are the check that the copies still work. They are ignored, so nothing else
will catch it.

The two plugin repos have their own git history and their own commit. Do not add them as
submodules — qrate neither pins nor carries them. `qrate-site` is a branch here, so its commit
lands on this remote.
The plugin repositories have their own git history and commits. Do not add them as submodules —
qrate neither pins nor carries them. A package-manifest change must also update the registry's
`package.schema.json` and template checks. A catalog contract change must update the registry,
`plugin-package`, and the site's catalog loader. `qrate-site` is a branch here, so its commit lands
on this remote.

## gpui test modules

Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ CI cancels older runs for the same branch when a newer commit arrives. A passing

Keep pull requests small enough to review. Explain user-visible changes, note limitations, and include screenshots or a short recording for visual changes when practical. Do not commit secrets, private collection data, or generated build output.

Plugins are a public compatibility surface. If a contribution changes the plugin API, read the repository guidance in [`CLAUDE.md`](CLAUDE.md) before making the change; the API has matching type definitions in companion repositories.
Plugins are a public compatibility surface. If a contribution changes the plugin API, read the repository guidance in [`CLAUDE.md`](CLAUDE.md) before making the change; the API has matching type definitions in companion repositories. Start new plugins from [`qrate-plugin-template`](https://github.com/devnull03/qrate-plugin-template), and submit versioned release packages through [`qrate-plugin-registry`](https://github.com/devnull03/qrate-plugin-registry).

## License

Expand Down
64 changes: 62 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ members = [
"crates/diagnostics",
"crates/plugin-api",
"crates/plugin-host",
"crates/plugin-package",
"crates/preview",
"crates/project-wizard",
"crates/settings",
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ qrate works without an account. Google Sheets integration is off until you enabl
- [Columns](docs/columns.md) — types, authority lists, and project settings
- [Export and Google Sheets](docs/export-and-sync.md) — move data in and out
- [Agent panel](docs/agent-panel.md) — review data with an optional local agent
- [Plugins](docs/plugins/index.md) — extend qrate locally
- [Plugins](docs/plugins/index.md) — discover, install, or write extensions

## Contributing

Expand Down
11 changes: 11 additions & 0 deletions assets/macos/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,17 @@
<string>__VERSION__</string>
<key>CFBundleVersion</key>
<string>__VERSION__</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>io.github.devnull03.qrate.plugin-install</string>
<key>CFBundleURLSchemes</key>
<array>
<string>qrate</string>
</array>
</dict>
</array>
<key>LSMinimumSystemVersion</key>
<string>10.15</string>
<key>NSHighResolutionCapable</key>
Expand Down
3 changes: 3 additions & 0 deletions crates/app/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ simplelog.workspace = true
os_info.workspace = true
sysinfo.workspace = true
async-channel = "2"
single-instance = "0.3.3"
semver.workspace = true
serde.workspace = true
serde_json.workspace = true
schemars.workspace = true
tempfile.workspace = true
# The agent bridge's request/response types; the transport lives here because only the
# composition root links both the contract and the live table.
ai = { path = "../ai" }
Expand Down Expand Up @@ -46,6 +48,7 @@ updater = { path = "../updater", features = ["client"] }
plugin-api = { path = "../plugin-api" }
# Plugins are loaded here and nowhere else — see `plugin_host::reload`.
plugin-host = { path = "../plugin-host" }
plugin-package = { path = "../plugin-package" }
workspace = { path = "../workspace" }

[build-dependencies]
Expand Down
54 changes: 41 additions & 13 deletions crates/app/src/app_menus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use gpui::*;
use window_wrapper::OpenBrowser;

use crate::actions::{NewProject, Save, ToggleBottomDock, ToggleLeftDock, ToggleRightDock};
use crate::export::{EXPORT_FORMATS, Export};
use crate::export::{EXPORT_FORMATS, Export, PluginExport};
use crate::theming::{SwitchTheme, theme_choices};

// The Edit menu's items act on the grid, so they're the grid's actions — `table` declares and
Expand All @@ -20,6 +20,8 @@ actions!(
/// The same window as `OpenSettings`, opened on the Columns page — the Data menu promises
/// a column surface, and landing on whatever page was last used does not keep that promise.
OpenColumnSettings,
DiscoverPlugins,
ManagePlugins,
OpenPluginsFolder,
ReloadPlugins,
Quit,
Expand Down Expand Up @@ -49,6 +51,38 @@ fn planned(name: &'static str) -> MenuItem {
}
}

fn export_items(cx: &gpui::App) -> Vec<MenuItem> {
let mut items: Vec<MenuItem> = EXPORT_FORMATS
.iter()
.map(|(format, label, _)| MenuItem::Action {
name: (*label).into(),
action: Box::new(Export { format: *format }),
os_action: None,
checked: false,
disabled: false,
})
.collect();
let plugin_exports = plugin_host::export_specs(cx);
if !plugin_exports.is_empty() {
items.push(MenuItem::Separator);
items.extend(
plugin_exports
.into_iter()
.map(|(plugin, export)| MenuItem::Action {
name: export.label,
action: Box::new(PluginExport {
plugin: plugin.to_string(),
export: export.id.to_string(),
}),
os_action: None,
checked: false,
disabled: false,
}),
);
}
items
}

/// Installs the menu bar. Called at startup and again once the theme registry has loaded, since
/// the Theme submenu lists what the registry holds — see `theming::on_themes_loaded`.
///
Expand All @@ -73,16 +107,7 @@ fn app_menus(cx: &gpui::App) -> Vec<Menu> {
MenuItem::submenu(Menu {
name: "Export".into(),
disabled: false,
items: EXPORT_FORMATS
.iter()
.map(|(format, label, _)| MenuItem::Action {
name: (*label).into(),
action: Box::new(Export { format: *format }),
os_action: None,
checked: false,
disabled: false,
})
.collect(),
items: export_items(cx),
}),
MenuItem::Separator,
MenuItem::action("Settings", OpenSettings),
Expand Down Expand Up @@ -179,9 +204,12 @@ fn app_menus(cx: &gpui::App) -> Vec<Menu> {
],
},
Menu {
name: "Extensions".into(),
name: "Plugins".into(),
disabled: false,
items: vec![
MenuItem::action("Discover Plugins…", DiscoverPlugins),
MenuItem::action("Manage Plugins…", ManagePlugins),
MenuItem::Separator,
MenuItem::action("Plugins Folder", OpenPluginsFolder),
MenuItem::action("Reload Plugins", ReloadPlugins),
MenuItem::Separator,
Expand Down Expand Up @@ -220,7 +248,7 @@ fn app_menus(cx: &gpui::App) -> Vec<Menu> {
MenuItem::action(
"Releases",
OpenBrowser {
url: "https://qrate.dvnl.work/releases".into(),
url: crate::site::url("/releases"),
},
),
MenuItem::Separator,
Expand Down
Loading