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
10 changes: 5 additions & 5 deletions customization/appearance.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@ The editor on the right edits the theme selected in the slot named by **Editing*
|--------|--------|
| **+** menu | **Duplicate Selected** copies the selected theme; **Import…** reads a theme JSON file |
| **-** button | Deletes the selected theme. Custom themes only |
| **Gear** menu | **Duplicate** and **Export…** act on the selected theme; **Uninstall** removes the selected registry theme |
| **Gear** menu | **Duplicate** and **Export…** act on the selected theme |

A built-in or registry theme is read-only and shows a **Duplicate Theme** button, which makes "Name (Copy)" for you to edit.
A built-in theme is read-only and shows a **Duplicate Theme** button, which makes "Name (Copy)" for you to edit.

Fonts are not part of a theme. Editor Font lives in [Editor settings](/customization/editor-settings) and Data Grid Font in [Data settings](/customization/data-settings), and both stay on the Mac you set them on.

A theme file the app cannot read is listed under **Not Loaded** with the reason, rather than dropped without a word.

## Colors

Custom themes get color wells here. A built-in or registry theme shows a lock and a **Duplicate Theme** button instead.
Custom themes get color wells here. A built-in theme shows a lock and a **Duplicate Theme** button instead.

| Group | Controls |
|-------|----------|
Expand All @@ -48,12 +48,12 @@ A theme colors the panes that show database content. The sidebar, the toolbar, t

## Theme files

A theme is one JSON file. **Export…** in the gear menu writes every color the selected theme sets, which is the shortest way to start a new one; **Import…** in the **+** menu reads one back into `~/Library/Application Support/TablePro/Themes/`. Themes published to the [plugin registry](/features/plugins#themes) install from **Settings > Plugins > Browse**, Themes category.
A theme is one JSON file. **Export…** in the gear menu writes every color the selected theme sets, which is the shortest way to start a new one; **Import…** in the **+** menu reads one back into `~/Library/Application Support/TablePro/Themes/`.

| Key | Holds |
|-----|-------|
| `schema` | Format version. `2` is the version this release reads |
| `id`, `name`, `author` | Identity. An `id` that collides with a built-in, a registry theme, or one you already have is replaced on import |
| `id`, `name`, `author` | Identity. An `id` that collides with a built-in or with one you already have is replaced on import |
| `appearance` | `light` or `dark`. Decides which slot lists the theme |
| `content.editor` | Editor colors, with syntax colors nested under `syntax` |
| `content.dataGrid` | Grid colors |
Expand Down
2 changes: 1 addition & 1 deletion databases/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ The split is about size, not capability. Bundling every client library would put
megabytes of Oracle and MongoDB code in an app most people use for one database.

Plugins update on their own schedule, so a driver fix can reach you without waiting for an app
release. [Plugins & Themes](/features/plugins) covers installing, updating and removing them.
release. [Plugins](/features/plugins) covers installing, updating and removing them.

## When yours is not here

Expand Down
2 changes: 1 addition & 1 deletion databases/kafka.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -200,4 +200,4 @@ Addresses** to only use the bootstrap address.

- [SSH Tunneling](/connections/ssh-tunneling)
- [SSL/TLS](/connections/ssl)
- [Plugins & Themes](/features/plugins)
- [Plugins](/features/plugins)
2 changes: 1 addition & 1 deletion databases/sap-hana.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -165,4 +165,4 @@ The `SYS` catalog views show only the objects your user holds a privilege on. `C
- [SSL/TLS](/connections/ssl)
- [EXPLAIN Visualization](/features/explain-visualization)
- [Query Parameters](/features/query-parameters)
- [Plugins & Themes](/features/plugins)
- [Plugins](/features/plugins)
2 changes: 1 addition & 1 deletion development/plugin-development.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,4 @@ Two steps put a plugin in TablePro's own registry, and both happen in this repos
</Step>
</Steps>

CI signs with TablePro's certificate, so a plugin published there arrives through a pull request, not a tag of your own. The other route stays outside this repository: sign the bundle with your own Developer ID and serve it from a registry URL you host, and the app installs it once the user agrees to trust your team by name. [Plugin Registry](/development/plugin-registry) has the manifest format, the `metadata` block that renders your connection form before the download finishes, and how to point the app at a private registry.
CI signs with TablePro's certificate, so a plugin published there arrives through a pull request, not a tag of your own. The app loads only bundles TablePro signed. [Plugin Registry](/development/plugin-registry) has the manifest format, the `metadata` block that renders your connection form before the download finishes, and how to point the app at a private registry.
41 changes: 8 additions & 33 deletions development/plugin-registry.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ title: Plugin Registry
description: Registry manifest format, binary selection, publishing, and PluginKit compatibility
---

The official manifest is `plugins.json` in [TableProApp/plugins](https://github.com/TableProApp/plugins), and TablePro's release workflow writes it: a plugin gets there by being contributed to TablePro's own repository and tagged there. A plugin of yours ships the other way: signed with your own Developer ID, it installs from a file or from a manifest you host in the same format. For what install and update look like to a user, see [Plugins & Themes](/features/plugins); to build the plugin in the first place, [Plugin Development](/development/plugin-development).
The official manifest is `plugins.json` in [TableProApp/plugins](https://github.com/TableProApp/plugins), and TablePro's release workflow writes it: a plugin gets there by being contributed to TablePro's own repository and tagged there. The app loads only bundles TablePro signed, so there is no other route. For what install and update look like to a user, see [Plugins](/features/plugins); to build the plugin in the first place, [Plugin Development](/development/plugin-development).

The app fetches the manifest to fill **Settings > Plugins > Browse** and to auto-install a driver when someone picks a database type with no plugin loaded.
The app fetches the manifest to install a driver when someone picks a database type with no plugin loaded, to install Parquet from the Export dialog, and to offer updates.

## Manifest format

Expand All @@ -26,7 +26,7 @@ Schema version 2 is current. A manifest declaring a higher version is rejected a
| `summary` | string | Yes | One-line description |
| `author` | object | Yes | `{ "name": "…", "url": "…" }`, `url` optional |
| `homepage` | string | No | Project URL |
| `category` | string | Yes | `database-driver`, `export-format`, `import-format`, `theme`, `other` |
| `category` | string | Yes | `database-driver`, `export-format`, `import-format`, `other`. An unknown value reads as `other` |
| `databaseTypeIds` | [string] | No | `DatabaseType.pluginTypeId` values, which is what drives auto-install |
| `binaries` | [object] | Yes | Per-architecture binaries |
| `minAppVersion` | string | No | Below this the install fails before any download |
Expand All @@ -49,9 +49,7 @@ v1 manifests carried top-level `downloadURL`, `sha256`, and `minPluginKitVersion

## Binary selection

For a driver, the app filters `binaries` to the running architecture, keeps those whose `pluginKitVersion` falls inside `[minimumCompatiblePluginKitVersion, currentPluginKitVersion]`, and installs the highest. Both bounds are declared in `PluginManager.swift`. A driver binary with no `pluginKitVersion` never resolves, and the install fails with `noCompatibleBinary`.

Themes carry no native code, so they match on architecture alone.
The app filters `binaries` to the running architecture, keeps those whose `pluginKitVersion` falls inside `[minimumCompatiblePluginKitVersion, currentPluginKitVersion]`, and installs the highest. Both bounds are declared in `PluginManager.swift`. A driver binary with no `pluginKitVersion` never resolves, and the install fails with `noCompatibleBinary`.

## Example entry

Expand Down Expand Up @@ -121,7 +119,7 @@ Either way CI builds both architectures, signs and notarizes the bundles, checks

A published release is immutable, and a rebuild never matches its bytes. A run for a tag whose release is already published with other zips fails, so a new binary ships under the next patch version.

Bundled plugins ride with the app release. Ten of them also have an entry in `.github/plugin-registry.json` (SQLite, ClickHouse, Redis, XLSX export, XLSX import, MQL export, SQL import, HTML export, Markdown export, XML export), so a fix to one can be tagged and published to the registry. The app never installs or offers a registry update over a bundled plugin, so that build reaches only users who download its zip and install it from a file. Everyone else gets the fix with the next app release. A bulk ABI re-release skips them. The other seven have no entry and never reach `plugins.json`.
Bundled plugins ride with the app release. Eight of them also have an entry in `.github/plugin-registry.json` (SQLite, ClickHouse, Redis, XLSX export, XLSX import, MQL export, SQL import, Markdown export), so a fix to one can be tagged and published to the registry. The app never installs or offers a registry update over a bundled plugin, so that build reaches only users who download its zip and install it from a file. Everyone else gets the fix with the next app release. A bulk ABI re-release skips them. The other seven have no entry and never reach `plugins.json`.

## PluginKit compatibility

Expand All @@ -144,39 +142,16 @@ The app's own release workflow runs `scripts/check-registry-readiness.py --floor

## Caching

The app fetches the manifest from `raw.githubusercontent.com/TableProApp/plugins/main/plugins.json`, which caches at the edge for about five minutes. Every fetch revalidates conditionally, the list refreshes at launch and when the plugin browser opens (throttled to one check per five minutes), and an install prompt forces a fresh fetch before it reports a plugin missing. CI also purges the jsDelivr cache after each registry push, for older app versions that still fetch from there. A newly published plugin shows up in the app within minutes.

## Theme distribution

Themes use the same manifest with `category: "theme"`. Four things differ from a driver:

- Pure JSON data. No executable code, no code signing, no `.tableplugin` bundle
- The ZIP holds `.json` files, each a theme file in format `schema: 2`. Packs with several themes work
- They install to `~/Library/Application Support/TablePro/Themes/Registry/`
- No `pluginKitVersion` is needed. Set `minAppVersion` to the release that reads the format the theme is written in, because that is the only check an older TablePro runs before installing it

```json
{
"id": "com.example.monokai-theme",
"name": "Monokai Theme",
"version": "1.0.0",
"summary": "Classic Monokai color scheme for TablePro",
"author": { "name": "Theme Author" },
"category": "theme",
"downloadURL": "https://example.com/monokai-theme.zip",
"sha256": "<sha256-of-zip>",
"iconName": "paintpalette"
}
```
The app fetches the manifest from `raw.githubusercontent.com/TableProApp/plugins/main/plugins.json`, which caches at the edge for about five minutes. Every fetch revalidates conditionally, the list refreshes at launch and when **Settings > Plugins** opens (throttled to one check per five minutes), and an install prompt forces a fresh fetch before it reports a plugin missing. CI also purges the jsDelivr cache after each registry push, for older app versions that still fetch from there. A newly published plugin shows up in the app within minutes.

## Custom registry URL

Point the app at a private or enterprise manifest, which is also how a plugin signed with your own Developer ID reaches your users:
Point the app at a private or enterprise mirror of the manifest:

```bash
defaults write com.TablePro com.TablePro.customRegistryURL "https://your-registry.example.com/plugins.json"

defaults delete com.TablePro com.TablePro.customRegistryURL
```

The custom manifest replaces the official one rather than adding to it. HTTP caching keys on the full URL, so a changed registry URL takes effect on the next fetch. A plugin served this way installs once the user agrees to trust its signing team by name. Both commands are also listed in [Settings](/customization/settings).
The custom manifest replaces the official one rather than adding to it. HTTP caching keys on the full URL, so a changed registry URL takes effect on the next fetch. A mirror can serve only bundles TablePro signed, since the app refuses every other signature. Both commands are also listed in [Settings](/customization/settings).
10 changes: 5 additions & 5 deletions development/testing-plugins.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ A plugin's signature is checked only outside the app's own `PlugIns` directory.

The version keys go first either way: a plugin whose `TableProPluginKitVersion` falls outside the app's compatible range, or whose `TableProMinAppVersion` is newer than the running app, is rejected before anything looks at its signature.

The signature check has two passes. A bundle signed by the same Apple team as the running app loads with no further question, which is why your own debug build accepts what your own Xcode signed. A bundle carrying any other Developer ID loads only if the user has trusted that team by name, and that consent is collected during an install through a registry, nowhere else ([Plugins & Themes](/features/plugins#plugins-from-other-developers)). Hand-copy such a bundle into the user plugins directory and it fails to load, because nothing on that path asks.
The signature check accepts one signer: the Apple team that signed the running app, which for a release is TablePro. Your own debug build accepts what your own Xcode signed for the same reason. Every other signature, ad-hoc signing and an unsigned bundle are refused, and nothing asks.

Do not reuse a built-in plugin's bundle ID. Installing over one fails outright, and at discovery the higher version wins, with the built-in taking a tie and the losing user copy deleted from disk.

Expand Down Expand Up @@ -56,16 +56,16 @@ Then run `scripts/generate-project.sh` and build. XcodeGen puts the bundle in an

A Debug build skips signature verification altogether when `TABLEPRO_ALLOW_UNSIGNED_PLUGINS` is set to `1`. Set it under **Product > Scheme > Edit Scheme > Run > Arguments**, in the environment variables list.

This is the route for a bundle built on another machine, or for testing your own Developer ID signing before there is any registry entry to install from, since it also bypasses the developer-trust prompt. The bundle still has to be validly signed for dyld to load it at all. The escape hatch is compiled out of Release builds, so it cannot mask a signing problem that would reach a user.
This is the route for a bundle built on another machine or signed by another team. The bundle still has to be validly signed for dyld to load it at all. The escape hatch is compiled out of Release builds, so it cannot mask a signing problem that would reach a user.

## When it does not load

The reason lands in a **Plugin Installation Failed** alert when installing, and in the plugin's row under **Settings > Plugins > Installed** otherwise.
The reason lands in a **Plugin Installation Failed** alert when installing, and in the banner in **Settings > Plugins** otherwise.

| Message | What it means | What to do |
|---------|---------------|------------|
| **Plugin code signature verification failed: …** | Unsigned, ad-hoc signed, modified after signing, or signed by a certificate that is neither your app's team nor a Developer ID | Rebuild rather than patch. Any edit to a signed bundle breaks the seal |
| **This plugin is signed by …, a developer you have not trusted yet.** | A valid Developer ID that this Mac has not consented to | Install it through a registry so the trust prompt runs, or set `TABLEPRO_ALLOW_UNSIGNED_PLUGINS=1` in a Debug build |
| **Plugin code signature verification failed: bundle is not signed by TablePro** | A valid signature from a team other than the running app's | Sign with the app's team, or set `TABLEPRO_ALLOW_UNSIGNED_PLUGINS=1` in a Debug build |
| **Plugin code signature verification failed: …** | Unsigned, ad-hoc signed, modified after signing, or a symbolic link that points outside the bundle | Rebuild rather than patch. Any edit to a signed bundle breaks the seal |
| **Plugin was built for PluginKit version …; this release of TablePro needs version …** | The bundle predates a breaking ABI bump | Rebuild the plugin against the current TableProPluginKit |
| **Plugin requires app version … or later, but current version is …** | `TableProMinAppVersion` is ahead of the app | Raise `MARKETING_VERSION` in `Configs/Version.xcconfig` and rebuild |
| **A built-in plugin "…" already provides this bundle ID** | The bundle ID collides with a bundled plugin | Give your plugin its own bundle ID |
Expand Down
4 changes: 2 additions & 2 deletions faq.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ Drag the app to the Trash, then delete the Application Support folder and the pr
</Accordion>

<Accordion title="Why is my database not in the list?">
Its driver is probably not installed yet. The database type chooser badges registry drivers **Not Installed**; pick one anyway and the download runs before the connection opens, with no restart. **Settings > Plugins > Browse** holds the full catalog. See [Plugins & Themes](/features/plugins).
Its driver is probably not installed yet. The database type chooser badges registry drivers **Not Installed**; pick one anyway and the download runs before the connection opens, with no restart. See [Plugins](/features/plugins).
</Accordion>

<Accordion title="Why does a driver need downloading?">
Expand All @@ -70,7 +70,7 @@ Each one must match the SHA-256 checksum in the registry manifest and carry a va
</Accordion>

<Accordion title="Why does a plugin need updating after I update the app?">
An app update can raise the plugin interface version, and a plugin built against the old one is rejected at load rather than crashing. TablePro repairs it at the next launch by reinstalling a compatible build from the registry. Until then the plugin sits in a banner in **Settings > Plugins > Installed** with the reason and an action.
An app update can raise the plugin interface version, and a plugin built against the old one is rejected at load rather than crashing. TablePro repairs it at the next launch by reinstalling a compatible build from the registry. Until then the plugin sits in a banner in **Settings > Plugins** with the reason and an action.
</Accordion>

</AccordionGroup>
Expand Down
2 changes: 1 addition & 1 deletion features/data-files.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ A workbook is read-only. To keep a sheet, choose **File > Save As** and pick CSV

**File > Save As** (`Cmd+Shift+S`) writes a delimited file as CSV, TSV or pipe-separated text, and a JSON file as JSON, JSON Lines, CSV or TSV.

To write another format, choose **File > Export > Export Results…**, or right-click the grid and choose **Export Results…**. The export dialog offers every installed export format that works without a database, including CSV, JSON, SQL, XLSX, Markdown and HTML, and exports all rows, the rows passing the current filter, or the selected rows.
To write another format, choose **File > Export > Export Results…**, or right-click the grid and choose **Export Results…**. The export dialog offers every installed export format that works without a database, including CSV, JSON, SQL, XLSX and Markdown, and exports all rows, the rows passing the current filter, or the selected rows.

### Import into a table

Expand Down
Loading
Loading