feat: add LinkedIn jargon translator web app
Single-page interactive translator that converts plain language into LinkedIn-style corporate posts and decodes them back to human speak. - Two translation modes: to LinkedIn and to Human - Cringe intensity slider (1-5) - 3 variation cards per translation with copy buttons - Dual engine support: direct API key or local proxy server - i18n toggle for Portuguese and English (UI + AI prompts) - Dark theme with LinkedIn blue accent, fully responsive - Local proxy server (server.js) bridges to the LLM CLI
This commit is contained in:
parent
904ee1f49a
commit
0e9f6952b4
4 changed files with 1197 additions and 1 deletions
BIN
Jargonify.gif
Normal file
BIN
Jargonify.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.5 MiB |
74
README.md
74
README.md
|
|
@ -1 +1,73 @@
|
|||
# Jargonify
|
||||
# Jargonify
|
||||
|
||||
**The LinkedIn post translator nobody asked for, but everyone needs.**
|
||||
|
||||
Ever read a LinkedIn post and thought "what does this even mean?" Or maybe you got fired and need to spin it into a *✨ career transition ✨*? Jargonify has you covered.
|
||||
|
||||

|
||||
|
||||
## What it does
|
||||
|
||||
**→ LinkedIn Mode**: Type something honest like "I got fired" and watch it transform into a 3-paragraph motivational odyssey about resilience, growth mindset, and embracing new cycles. Hashtags included. Emojis mandatory.
|
||||
|
||||
**→ Human Mode**: Paste a LinkedIn post dripping with corporate jargon and get back what the person *actually* meant. Three translations ranging from polite to brutally honest.
|
||||
|
||||
**Cringe Intensity Slider**: Because sometimes you need a tasteful professional update (level 1), and sometimes you need to go full thought-leader-guru-who-cried-at-a-conference (level 5).
|
||||
|
||||
## Running it
|
||||
|
||||
You need [Node.js](https://nodejs.org) installed. That's it. No frameworks. No build step. No 400MB `node_modules`. Just vibes.
|
||||
|
||||
```bash
|
||||
git clone <this-repo>
|
||||
cd Jargonify
|
||||
node server.js
|
||||
```
|
||||
|
||||
Open `http://localhost:3000` in your browser. Done.
|
||||
|
||||
The server is ~80 lines of plain Node.js with zero dependencies. It talks to the AI through the CLI on your machine, which means **you need an active LLM CLI subscription** (Pro or Max plan) already set up and authenticated.
|
||||
|
||||
### Alternative: API Key mode
|
||||
|
||||
If you have an API key from a supported provider, you can skip the server entirely:
|
||||
|
||||
1. Open `index.html` directly in your browser
|
||||
2. Click "API Key" in the engine selector
|
||||
3. Paste your key
|
||||
4. Translate away
|
||||
|
||||
## Why is this not hosted online?
|
||||
|
||||
Money. Specifically, the lack of it being allocated to this project.
|
||||
|
||||
Every translation costs real API tokens. Three variations per click, creative writing prompts, emojis — it adds up. Hosting this publicly would be like opening a free restaurant and hoping people tip. They won't. They'll translate "I hate my job" into LinkedIn poetry 47 times and leave.
|
||||
|
||||
So it runs locally, using your own subscription. Think of it as BYOK — Bring Your Own Key(board and subscription).
|
||||
|
||||
## Features
|
||||
|
||||
- Two translation modes with animated toggle
|
||||
- Cringe intensity slider (1-5) for LinkedIn mode
|
||||
- 3 variations per translation with individual copy buttons
|
||||
- Dual engine: local proxy server or direct API key
|
||||
- Full i18n: Portuguese and English (UI + AI prompts)
|
||||
- Dark theme with that unmistakable LinkedIn blue
|
||||
- Responsive layout
|
||||
- Ctrl/Cmd+Enter keyboard shortcut
|
||||
- Zero dependencies on the server side
|
||||
|
||||
## Tech stack
|
||||
|
||||
- **Frontend**: One HTML file. HTML, CSS, JS. Like it's 2009 and I'm proud of it.
|
||||
- **Backend**: One JS file. Node.js `http` module. No Express. No framework. Just `req` and `res` doing honest work.
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This project is satire. If your LinkedIn posts sound like Jargonify output at level 5, that's between you and your network.
|
||||
|
||||
No motivational gurus were harmed in the making of this application. Several were accurately parodied.
|
||||
|
||||
## License
|
||||
|
||||
MIT — feel free to fork it, deploy it, or translate your resignation letter with it.
|
||||
|
|
|
|||
1028
index.html
Normal file
1028
index.html
Normal file
File diff suppressed because it is too large
Load diff
96
server.js
Normal file
96
server.js
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
const http = require("http");
|
||||
const { spawn } = require("child_process");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const PORT = 3000;
|
||||
|
||||
const server = http.createServer((req, res) => {
|
||||
// CORS
|
||||
res.setHeader("Access-Control-Allow-Origin", "*");
|
||||
res.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS");
|
||||
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
||||
|
||||
if (req.method === "OPTIONS") {
|
||||
res.writeHead(204);
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
// Serve index.html
|
||||
if ((req.method === "GET" || req.method === "HEAD") && (req.url === "/" || req.url === "/index.html")) {
|
||||
const filePath = path.join(__dirname, "index.html");
|
||||
fs.readFile(filePath, (err, data) => {
|
||||
if (err) {
|
||||
res.writeHead(500);
|
||||
res.end("Erro ao carregar index.html");
|
||||
return;
|
||||
}
|
||||
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
||||
res.end(data);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Translation endpoint
|
||||
if (req.method === "POST" && req.url === "/translate") {
|
||||
let body = "";
|
||||
req.on("data", (chunk) => { body += chunk; });
|
||||
req.on("end", () => {
|
||||
let parsed;
|
||||
try {
|
||||
parsed = JSON.parse(body);
|
||||
} catch (e) {
|
||||
res.writeHead(400, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ error: "JSON inválido" }));
|
||||
return;
|
||||
}
|
||||
|
||||
const { prompt } = parsed;
|
||||
if (!prompt || typeof prompt !== "string") {
|
||||
res.writeHead(400, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ error: "Campo 'prompt' é obrigatório" }));
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`[Jargonify] Traduzindo (${prompt.length} chars)...`);
|
||||
|
||||
const claudePath = "/opt/homebrew/bin/claude";
|
||||
const childEnv = { ...process.env, PATH: process.env.PATH + ":/opt/homebrew/bin", LANG: "en_US.UTF-8", LC_ALL: "en_US.UTF-8" };
|
||||
delete childEnv.CLAUDECODE;
|
||||
delete childEnv.CLAUDE_CODE_ENTRYPOINT;
|
||||
|
||||
const child = spawn(claudePath, ["-p", "-"], { encoding: "utf-8", env: childEnv, timeout: 60000 });
|
||||
let stdout = "";
|
||||
let stderr = "";
|
||||
child.stdout.on("data", (data) => { stdout += data; });
|
||||
child.stderr.on("data", (data) => { stderr += data; });
|
||||
child.on("close", (code) => {
|
||||
if (code !== 0) {
|
||||
console.error("[Jargonify] Erro (code " + code + "):", stderr);
|
||||
res.writeHead(500, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ error: `Erro ao executar Claude CLI: ${stderr || "exit code " + code}` }));
|
||||
return;
|
||||
}
|
||||
res.writeHead(200, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ result: stdout.trim() }));
|
||||
});
|
||||
child.on("error", (err) => {
|
||||
console.error("[Jargonify] Spawn erro:", err.message);
|
||||
res.writeHead(500, { "Content-Type": "application/json" });
|
||||
res.end(JSON.stringify({ error: `Erro ao iniciar Claude CLI: ${err.message}` }));
|
||||
});
|
||||
child.stdin.write(prompt);
|
||||
child.stdin.end();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
res.writeHead(404);
|
||||
res.end("Not found");
|
||||
});
|
||||
|
||||
server.listen(PORT, () => {
|
||||
console.log(`\n ✨ Jargonify Server rodando em http://localhost:${PORT}\n`);
|
||||
console.log(` Abra no browser para usar com traduções via Claude.\n`);
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue