# QR Editor (qr.giit.ch) > Free QR code generator and editor in the browser, no sign-up, no tracking, no upload. Makes static QR codes (they never expire: the content is in the code, no redirect) for links, text, WiFi, contacts (vCard, MeCard), email, SMS, phone, location, calendar events, crypto payments, SEPA payments (EPC GiroCode) and Swiss QR bills. Design with colors, gradients, module and eye shapes, a logo, caption and frame; every code is read back to check that it scans; export SVG, PNG, JPEG, WebP, PDF and EPS; reads QR codes from pictures and the camera; makes many codes at once from a table. - Language: English at /, 13 other languages at /de/, /fr/, /it/, /es/, /pt/, /nl/, /pl/, /tr/, /ru/, /uk/, /zh/, /ja/, /ko/ - Cost: free - Data: documents stay in the browser (localStorage) unless the user sets up a webhook to their own server. Pictures and the camera image read for codes never leave the browser. - No files of a document: a picture logo is part of the QR document as a data URI; there are no `assets` fields in any message. ## The QR document (.qr) Plain text: optional design lines `key: value`, a line `---`, then the content exactly as it goes into the code (one final line break is not part of it). Text without such a header is all content, so a plain link is a valid document. Lines starting with `#` in the header are comments. color: #1d2b53 modules: rounded logo: wifi ecc: Q caption: Guest WiFi --- WIFI:T:WPA;S:Guest;P:sunny-day-42;; Design keys (all optional, default in brackets): `ecc` L|M|Q|H (M), `boost` yes|no (yes), `version` auto|1-40 (auto), `mask` auto|0-7 (auto), `margin` 0-20 modules (4), `color` #rgb/#rrggbb (#000000), `color2` (none), `gradient` none|linear|radial (none), `angle` 0-360 (45), `background` color or transparent (#ffffff), `modules` square|rounded|dots|soft|diamond|bars-v|bars-h (square), `eye-frame` square|rounded|circle|leaf (square), `eye-ball` square|rounded|circle|diamond|leaf (square), `eye-color` (the dark color), `logo` none|wifi|web|mail|phone|chat|pin|calendar|user|pay|star|heart or `data:image/(png|jpeg|gif|webp|svg+xml);base64,…` (none), `logo-size` 5-30 percent (20), `logo-clear` yes|no (yes), `caption` one line (none), `caption-position` below|above (below), `frame` none|line|filled (none), `frame-color` (the dark color). Unknown keys and invalid values are kept and reported; the default is used. ## Content formats The type is read from the content: `https://…` link; `WIFI:T:WPA;S:;P:;H:true;;` (T:WPA also for WPA3, T:WEP, T:nopass; escape `\ ; , : "`); vCard 3.0 (`BEGIN:VCARD` … `END:VCARD`) or `MECARD:N:Last,First;TEL:…;;`; `mailto:?subject=…&body=…`; `SMSTO::`; `tel:`; `geo:,`; `BEGIN:VEVENT` … `END:VEVENT`; `bitcoin:`/`litecoin:`/`dogecoin:`/`bitcoincash:` (BIP21) and `ethereum:?value=` (EIP-681); EPC069-12 SEPA (`BCD`, `002`, `1`, `SCT`, BIC, name, IBAN, `EUR`, purpose, RF reference or text; at most 331 bytes; error correction M); Swiss QR bill (`SPC`, `0200`, `1`, IBAN CH/LI, structured address `S`, amount, CHF/EUR, debtor, `QRR`/`SCOR`/`NON`, reference, message, `EPD`; drawn with the Swiss cross, error correction M, design lines ignored; EUR only with SCOR or NON). Everything else is text. `{{column}}` placeholders are filled by "Many codes from a table". ## Features - Code (the QR document), the drawn code and a side panel: Content (the type and its form, writing the content in its canonical form), Design (colors, shapes, logo, caption, frame, error correction), Check. - Scan check: after each change the drawn code is read back with jsQR in the browser, normal, small (3 pixels per module) and blurred with less contrast; the result is shown at the code and as a problem. - Problems with their line: content too long, contrast below 3:1 (error) or 4.5:1 (warning), light on dark, transparent background, quiet zone below 4 modules, a logo hiding more than the error correction repairs, unknown keys, invalid values, and the checks of each type (IBAN check digits, QR-IBAN only with a QR reference, QR reference check digit, RF creditor reference, EPC length, WiFi password, link shorteners and redirect services). - Print size: from the scanning distance the smallest size (distance / 10), and the size of one module for a printed width. - Export: SVG, PNG (64-8192 pixels, dpi written into the file), JPEG, WebP, PDF and EPS (vectors, width in mm, gradients as shadings), the content as text, the .qr document, all in one ZIP; copy as picture. - Many codes from a table: CSV/TSV with a header row, `{{column}}` in the content and caption, a ZIP of SVG or PNG files (named from a column) or one PDF with a page per row; at most 5000 rows. - Reading codes: open, drop or paste a picture (PNG, JPEG, GIF, WebP, BMP, SVG) or use the camera ("Scan"); light-on-dark codes too. The content becomes a new document. - Caption edited in place on the drawing. - My documents: any number of QR documents in the browser, with a picture of each code. ## Writing for a user (for AI assistants) Any AI can put a QR document into the user's open editor with a link; no server, no API key: - `https://qr.giit.ch/#b64=` - `https://qr.giit.ch/#qr=` - `https://qr.giit.ch/#z=` (shorter) - Add `&file=new&name=` to make a new document next to the user's others, or `&file=` to change that one. Without `&file=` the open document changes. - Long documents (a picture logo): split the encoded text and send one link per part with `&part=N&of=M&id=`. Write the complete QR document; keep the dark color dark and the background light. The user can undo every change. In the editor, "AI Assistant" > "Copy prompt" gives the AI these instructions together with the current document. ## Saving on your own server (webhook) Set under "Save to server" (cloud button), or for one document with `?webhook=`: only for that one document and only while the page is open - a link never stores a webhook. At the top level the user is asked first; inside a page (iframe or opener) it only works together with `?src=` and only for the document opened with it. On "Save" (Ctrl+S), or automatically a few seconds after each change if chosen, the browser sends: POST Content-Type: application/json Authorization: Bearer (only if a token is set) {"editor":"qr","event":"save","id":"","name":"", "content":"","updated":""} One request carries at most 40 MB; the server's error text (e.g. for 413) is shown to the user. The server must answer CORS requests from https://qr.giit.ch (Access-Control-Allow-Origin, and Access-Control-Allow-Headers: Content-Type, Authorization). `?src=` opens a .qr file (or any text, which becomes the content) from a URL (CORS needed), `&name=` names it. The content is the QR document exactly as it is in the editor: design lines, `---`, the content. ## Embedding in a website Messages (postMessage; check `event.origin === "https://qr.giit.ch"` and `event.source === iframe.contentWindow`): - Editor -> page: `{type:"qreditor:ready", version:2, editor:"qr", window:false}` when it can take a document. - Page -> editor: `{type:"qreditor:load", name, content}` opens the document (a document of that name from your page is replaced, otherwise made). - Editor -> page: `{type:"qreditor:loaded", id, name}`, then `{type:"qreditor:change", id, name, content}` on each edit (not after load), and `{type:"qreditor:save", id, name, content, editor, event, updated}` when the user clicks Save or presses Ctrl+S. - Page -> editor: `{type:"qreditor:get"}` asks for a `qreditor:save` message; `{type:"qreditor:save"}` saves as if the user had clicked Save. In embedded mode the page text below the editor is hidden and a Save button is shown. The other giit editors (latex.giit.ch, svg.giit.ch and more) speak the same protocol with their own prefix (`latexeditor:`, `svgeditor:` …). Optional fields of `qreditor:load`: - `context: {host, title, rules}` - plain text about the page (rules up to 2000 characters). "Copy prompt" in the AI Assistant then tells the AI that the document belongs to that page and is saved back there, includes the rules, and leaves out how to build a webhook server. - `hook: {url, token}` - a webhook (https) for this document only, used on Save in addition to the message; kept in memory only (never stored, gone on reload). If the server answers with an error, its `{"error": "…"}` (or `message`, or a short text) is shown to the user. AI Assistant in a frame: add `allow="clipboard-write"` to the iframe so "Copy prompt" can use the clipboard (without it the editor falls back to the old copy command or shows the prompt to copy by hand). Links from an AI cannot reach an editor inside another website (the browser keeps its storage and channels apart), so in a frame the prompt and the dialog ask to paste the AI's whole answer into the editor; use the own window (below) if links should work. `qreditor:get` is always answered with a `qreditor:save` message, also when nothing changed. ### All messages at a glance Editor -> page (sent to the page around the editor, or to window.opener in its own window): - `qreditor:ready` {version:2, editor, window} - can take a document - `qreditor:loaded` {id, name, target?} - after each load; remember the id (target:"new" for a new document from the AI, which is not yours) - `qreditor:change` {id, name, content} - a real edit of your document (not after load) - `qreditor:save` {editor, event, id, name, content, updated} - Save / Ctrl+S (from anywhere in the editor), or the answer to get - `qreditor:closing` {id, name, unsaved, content?} - own window closes (always sent; content only when unsaved, id null before the first load) - `qreditor:reload` {id, unsaved} - inside your page, F5 / Ctrl+R / Cmd+R do not reload the editor; ask the user yourself (Ctrl+W cannot be caught by a page) - `qreditor:key-request` {reason:"missing"} - the editor has no AI key in this session yet `?embed=1&save=0` hides the editor's Save button (Ctrl+S still sends `qreditor:save`) - for pages with their own Save button. `?theme=light`, `?theme=dark` or `?theme=auto` (with or without `embed`) fixes the color theme for that page load and hides the theme switch; the user's own stored choice is not changed. Other values are ignored. The address sets the language of the interface and of the page: `/` English, `/de/` German, `/fr/`, `/it/`, `/es/`, `/pt/`, `/nl/`, `/pl/`, `/tr/`, `/ru/`, `/uk/`, `/zh/`, `/ja/`, `/ko/` (`?lang=xx` leads there); the choice is remembered in the browser. The AI prompt is always English. Page -> editor (only from the page around the editor or the opener; after its first message only from that origin; pages without a real origin are ignored): - `qreditor:load` {name, content, context?, hook?, ai?, from?, target?} - open your document; from:"ai" shows a note, target:"new" opens it as a separate document in the user's browser - `qreditor:get` - answered with `qreditor:save` of your document (id and content null before the first load) - `qreditor:save` - press Save - `qreditor:ai` {label?, prompt?, endpoint?, key?, base?} or {clear:true} - AI through your server Your document is the one your page loaded (or opened with ?src=). change, save, closing and get are only ever about it - never about other documents in the user's browser, also when the user opens them in the editor. A load replaces only a document that came from your page before (same name and origin), never the user's own; the AI settings and the hook from load apply only while your document is open. Your document is kept in memory only - it is never stored in the user's browser and is gone when the editor closes. ### AI through your own server (prompt and session key) The editor has no backend; your site keeps the keys and the results. Hand the editor your prompt, an endpoint and a key for this editor session - in `qreditor:load` as `ai: {…}`, or at any time as its own message: {type:"qreditor:ai", label:"Example Admin", prompt:"", endpoint:"https://your-site.com/api/ai-result", key:"", base:true} Fields left out keep their value; `{type:"qreditor:ai", clear:true}` ends it. One key per editor session: once the editor has a key it keeps it until the session ends or `clear`, and does not take a different one. `base:false` leaves out the editor's own instructions. The AI dialog shows "Connected to