Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
568b7d5
feat: Tolgee Apps PoC — project dashboard page extension
JanCizmar May 14, 2026
06f7141
chore: capture apps TODO notes for future work
JanCizmar May 14, 2026
86e7dfc
feat: app scopes, consent flow, and intersection auth interceptor
JanCizmar May 14, 2026
c258882
feat: complete iframe + token + REST API end-to-end
JanCizmar May 14, 2026
e4adc16
feat: switch app context to postMessage handshake
JanCizmar May 14, 2026
9b4ff41
feat: app webhooks + clientId/clientSecret/webhookSecret + dev-plugin…
JanCizmar May 15, 2026
62f6398
feat: per-key activity sparkline in dev plugin
JanCizmar May 15, 2026
d553d66
feat: translation-tools-panel app extension point
JanCizmar May 22, 2026
d049a09
feat: key & translation action decorators with key-edit-tab module
JanCizmar May 22, 2026
2bc9105
fix: surface decoratorsUrl on registration response + regenerate schema
JanCizmar May 22, 2026
8d794ba
fix: include user JWT when minting install token for decorators
JanCizmar May 26, 2026
a20523e
fix: drop credentials:include on install-token fetch to avoid CORS co…
JanCizmar May 26, 2026
99c8926
fix: translation panel decorator focuses the cell and opens the panel
JanCizmar May 26, 2026
e33fd15
feat: decorator visibility (manifest + dynamic) and count badge
JanCizmar May 26, 2026
b6b8cd9
chore: type the dev-plugin webhook handler against @tginternal/client
JanCizmar May 26, 2026
7821d87
chore: add onWebhook utility for typed webhook dispatch in dev-plugin
JanCizmar May 26, 2026
ee75916
refactor: split dev-plugin server by responsibility, move data to .data/
JanCizmar May 26, 2026
4421ecd
feat: modal action type + 5 trigger surfaces; open all webhook events
JanCizmar May 26, 2026
b52d50c
feat: native icon support for plugin manifests
JanCizmar May 26, 2026
193a437
refactor: drop key-edit-footer-action trigger surface
JanCizmar May 26, 2026
f439cbe
docs: explain how to fetch batch modifications via revisionId
JanCizmar May 26, 2026
658e4c8
feat: PATCH manifest-url + Cloudflare tunnel in dev-plugin
JanCizmar May 27, 2026
5790649
chore: move plugin code under apps/ and add SDK skeleton
JanCizmar May 27, 2026
1f0ad83
feat: extract @tolgee/apps-sdk and consume it from dev-plugin
JanCizmar May 27, 2026
4f17749
feat: create-tolgee-app CLI generator
JanCizmar May 27, 2026
9a857ca
feat: skip cloudflared tunnel when Tolgee is local
JanCizmar May 27, 2026
3a055b9
fix: skip cloudflared tunnel in register against local Tolgee
JanCizmar May 27, 2026
e1769fb
feat: env-overridable ports so plugins run side-by-side
JanCizmar Jun 2, 2026
20f6b69
feat: back-translate example app — Claude-powered semantic check
JanCizmar Jun 2, 2026
64e043b
chore: gitignore *.tsbuildinfo in template + back-translate
JanCizmar Jun 2, 2026
66f81f8
fix(back-translate): drop redundant input rows from panel
JanCizmar Jun 2, 2026
230b714
chore(back-translate): SwitchHorizontal01 panel icon + drop inner title
JanCizmar Jun 2, 2026
a659215
feat(back-translate): webhook-driven analysis + row-level decorators
JanCizmar Jun 2, 2026
98ef9e9
feat(apps): browser-based plugin install + self-service manifest-url
JanCizmar Jun 3, 2026
f051f2a
fix(apps): SSRF guard, no silent scope escalation, acting-as bounds
JanCizmar Jun 8, 2026
ca7d0a1
feat(apps): apps-sdk runtime helpers + typed manifest, apps-dev CLI, …
JanCizmar Jun 8, 2026
f9c2116
chore(apps): regenerate API schema for apps endpoints after rebase on…
JanCizmar Jun 9, 2026
073d78a
chore(apps): set up Lerna + make apps packages publishable (alpha)
JanCizmar Jun 9, 2026
48e1bad
chore(apps): first alpha published — bump to 0.0.1-alpha.0
JanCizmar Jun 9, 2026
36af13d
feat(apps): generated apps ship CLAUDE.md, typecheck, and pull-context
JanCizmar Jun 9, 2026
05a28b7
fix(apps-dev): register reuses dev's live tunnel + retries manifest r…
JanCizmar Jun 9, 2026
453820c
fix(apps): dashboard-page iframe fills the available area
JanCizmar Jun 10, 2026
a9ba445
fix(apps): render app icons at the native size in panel header and pr…
JanCizmar Jun 10, 2026
2b96bd5
test: fix AuthenticationFilter test instantiation after rebase onto main
JanCizmar Jun 10, 2026
150a235
chore: regenerate API schema after rebase onto main
JanCizmar Jun 10, 2026
97231d0
test: stub app-token validation in AuthenticationFilter tests + ktlin…
JanCizmar Jun 10, 2026
70f4980
fix(apps): reconcile app_install changelog with entities
JanCizmar Jun 10, 2026
613c39b
feat(apps): translation-tools-panel-empty extension point + selected …
JanCizmar Jun 10, 2026
a45a988
chore(apps): publish alpha.5
JanCizmar Jun 10, 2026
2a70e84
feat(apps): pass host theme (light/dark + palette) to app iframes
JanCizmar Jun 11, 2026
26a0d4d
chore(apps): publish alpha.7
JanCizmar Jun 11, 2026
4cc34c1
chore(apps): add tolgee.apps.allow-local-addresses for local manifest…
JanCizmar Jun 11, 2026
035c7ca
feat(apps): organization-level authorization for apps (glossary)
JanCizmar Jun 11, 2026
f72a5d8
feat(apps): glossary-keeper example app
JanCizmar Jun 11, 2026
139b83f
fix(apps): allow app iframes to open links in a new tab
JanCizmar Jun 11, 2026
851004c
fix(apps): allow user-activated top navigation from app iframes
JanCizmar Jun 12, 2026
89e329d
Merge branch 'jancizmar/tolgee-apps-poc' into jancizmar/apps-org-glos…
JanCizmar Jun 12, 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
1 change: 1 addition & 0 deletions .claude/tolgee/.github-username
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
jancizmar
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ ngrok.local.yml
# Worktree
/worktree-env.sh

# Workspace package node_modules (apps/*, apps/example-apps/*)
**/node_modules/

# mcp-publisher auth tokens
/.mcpregistry_github_token
/.mcpregistry_registry_token
15 changes: 15 additions & 0 deletions apps/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# apps/

Code related to the Tolgee Apps plugin system lives here.

## Layout

- `tolgee-apps-sdk/` — the SDK package that plugin authors install
(`@tolgee/apps-sdk`). Currently a skeleton; will hold the typed
postMessage handshake, manifest schema, webhook signature verifier,
and JWT context helpers. To be published from this directory once
the contract stabilises.
- `example-apps/` — runnable example plugins built against the SDK.
- `dev-plugin/` — the in-tree reference plugin used during PoC
development. Demonstrates every module type and serves as the
template `create-tolgee-app` will copy from.
1 change: 1 addition & 0 deletions apps/create-tolgee-app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dist
77 changes: 77 additions & 0 deletions apps/create-tolgee-app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# create-tolgee-app

Scaffolds a new Tolgee Apps plugin with a working dev environment in one
command.

> **Status: PoC**. Not yet published. The generated project resolves
> `@tolgee/apps-sdk` via npm workspaces, so today the generator only
> works when invoked inside this repo. Run it under `apps/example-apps/`
> and the SDK symlink will pick up automatically.

## What you get

A project pre-wired with:

- **Vite + React + TypeScript** for iframe modules
- **Express + tsx watch** for the webhook + decorator + manifest server
- **Cloudflare quick tunnel** (`scripts/dev.ts`) that boots on `npm run dev`,
captures a `*.trycloudflare.com` URL, and PATCHes your Tolgee install
so iframe loads and webhook deliveries reach the local services
- A `manifest.template.json` whose base URL is substituted at request time —
no manual editing on every restart
- **`@tolgee/apps-sdk`** wired in for the postMessage handshake, REST
client, webhook signature verification, and typed event dispatch

## Wizard

`npm run dev` (or once published, `npm create tolgee-app@latest`) walks
you through:

1. **App identifier** — kebab-case slug used as `manifest.id`, the
target directory name, and the `name` in `package.json`.
2. **Display name** — shown in the Tolgee admin UI.
3. **Modules** — pick any subset of the 10 module types.
Iframe-bearing modules (`project-dashboard-page`, `translation-tools-panel`,
`key-edit-tab`, `modal`) get source-file stubs; the rest get manifest
entries pointing back at those iframes.
4. **Webhook events** — pick from a curated list of common
`ActivityType` values. The webhook route gets a typed `onWebhook(…)`
stub per event.
5. **Tolgee URL** — defaults to `https://apps.preview.tolgee.io`.
6. **Install deps** + **git init** prompts.

## After generation

```bash
cd <your-app>
npm install # if you skipped it
npm run register # one-time: pick org, register the plugin
npm run dev # starts vite + express + tunnel, PATCHes manifest URL
```

Subsequent `npm run dev` restarts re-PATCH the install with the new
tunnel URL automatically — no manual reinstall.

## Layout

```
apps/create-tolgee-app/
├── src/
│ ├── index.ts — entry, runs wizard, orchestrates copy
│ ├── wizard.ts — @clack/prompts flow
│ ├── copy.ts — recursive copy with mustache substitution
│ ├── manifest.ts — synthesizes manifest.template.json
│ └── registry.ts — module + webhook-event registry
├── template/
│ ├── base/ — files every project gets
│ └── modules/<key>/ — iframe stubs per module type
└── package.json
```

## Local build

```bash
npm run --workspace=create-tolgee-app build
```

Outputs an ESM shebang script at `dist/index.js`.
31 changes: 31 additions & 0 deletions apps/create-tolgee-app/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "create-tolgee-app",
"version": "0.0.1-alpha.7",
"description": "Scaffold a new Tolgee Apps plugin with a working dev environment in one command.",
"type": "module",
"bin": {
"create-tolgee-app": "./dist/index.js"
},
"files": [
"dist",
"template"
],
"scripts": {
"build": "tsup",
"dev": "tsx src/index.ts"
},
"dependencies": {
"@clack/prompts": "^0.11.0",
"picocolors": "^1.1.1"
},
"devDependencies": {
"@tolgee/apps-sdk": "*",
"@types/node": "^24.12.3",
"tsup": "^8.3.5",
"tsx": "^4.19.2",
"typescript": "~6.0.2"
},
"engines": {
"node": ">=20"
}
}
91 changes: 91 additions & 0 deletions apps/create-tolgee-app/src/copy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { mkdir, readdir, readFile, writeFile } from 'node:fs/promises'
import { dirname, join, relative } from 'node:path'

type Vars = Record<string, string>

const isProbablyBinary = (filename: string): boolean => {
return /\.(png|jpg|jpeg|gif|svg|ico|webp|woff2?|ttf|otf|eot|mp4|webm)$/i.test(
filename
)
}

/**
* Mustache-style replacement: `{{name}}` → vars.name. Only single-word
* keys are substituted; missing keys leave the placeholder intact (so
* literal `{{` appearances in user-facing copy survive).
*/
const substitute = (text: string, vars: Vars): string => {
return text.replace(/\{\{(\w+)\}\}/g, (match, key: string) => {
return key in vars ? vars[key]! : match
})
}

/**
* Renames template-only file conventions to their real names:
* `_package.json` → `package.json` (avoids npm seeing the template as a real package)
* `_X` → `.X` (lets the template ship dotfiles)
*/
const targetFilename = (name: string): string => {
if (name === '_package.json') return 'package.json'
return name.startsWith('_') ? '.' + name.slice(1) : name
}

export type CopyOptions = {
/** Source directory inside the template. */
src: string
/** Destination directory for the generated project. */
dst: string
/** Variables substituted into text files. */
vars: Vars
}

/**
* Recursively copies `src` → `dst`, substituting `{{var}}` placeholders
* in text files and renaming `_*` files to `.*`.
*/
export async function copyTree(opts: CopyOptions): Promise<void> {
const entries = await readdir(opts.src, { withFileTypes: true })
await mkdir(opts.dst, { recursive: true })
for (const entry of entries) {
const srcPath = join(opts.src, entry.name)
const dstPath = join(opts.dst, targetFilename(entry.name))
if (entry.isDirectory()) {
await copyTree({ ...opts, src: srcPath, dst: dstPath })
} else if (entry.isFile()) {
if (isProbablyBinary(entry.name)) {
const buf = await readFile(srcPath)
await mkdir(dirname(dstPath), { recursive: true })
await writeFile(dstPath, buf)
} else {
const text = await readFile(srcPath, 'utf8')
await mkdir(dirname(dstPath), { recursive: true })
await writeFile(dstPath, substitute(text, opts.vars), 'utf8')
}
}
}
}

/**
* Like {@link copyTree} but takes a list of relative source files.
* Useful when only some files from a directory should be copied.
*/
export async function copyFiles(
baseSrc: string,
baseDst: string,
relativePaths: string[],
vars: Vars
): Promise<void> {
for (const relPath of relativePaths) {
const segments = relPath.split('/').map((s) => targetFilename(s))
const srcPath = join(baseSrc, relPath)
const dstPath = join(baseDst, segments.join('/'))
const text = await readFile(srcPath, 'utf8')
await mkdir(dirname(dstPath), { recursive: true })
await writeFile(dstPath, substitute(text, vars), 'utf8')
}
}

/** For diagnostics only — relative path from a template root. */
export const fromTemplate = (templateRoot: string, abs: string): string => {
return relative(templateRoot, abs)
}
Loading