diff --git a/docs/FAQ.en.md b/docs/FAQ.en.md index 2e56e2f..090b15e 100644 --- a/docs/FAQ.en.md +++ b/docs/FAQ.en.md @@ -1,197 +1,199 @@ # BarcodeLabelGen — FAQ -The most common questions, ordered from beginner to advanced. Don't see an answer? Check [HELP.en.md](HELP.en.md) or write to dev@attv.uk. +The most common questions, from beginner to more advanced. Each answer is short — if you want step-by-step instructions with screenshots, check the [Help guide](HELP.en.md), the section noted in brackets. Can't find your answer? Write to **dev@attv.uk**. --- ## Basics ### What is this app for? -You build label templates here (size in mm, any text, barcodes, images), then generate **many labels from one template** — each with different data pulled from a spreadsheet or SQLite database. +You build label templates in it — size in millimetres, any text, barcodes, images — then generate **many labels from one template at once**, each with different data pulled from a spreadsheet (like Excel) or a database. (See *Help*, section 6.) ### Why did the app force me to change my password right after I signed in? -The administrator gave you a temporary password. Every first login forces you to set your own (minimum 10 characters). This happens once. +The administrator gave you a temporary password — a starter password meant to be used only once. Every first login forces you to set your own password (minimum 10 characters). This only happens once. (See *Help*, section 1.) -### Where is "New template"? +### Where is the "New template" button? Left menu → **Templates** → **New template** button in the top-right of the list. -### Can I change the label size after I created the template? -Yes. In the editor, click the **📐 {width}×{height}** button in the toolbar, type new dimensions in mm or pick a preset, and click **Apply**. Objects keep their positions in mm (they are not rescaled). +### Can I change the label size after I've created the template? +Yes. In the editor, click the **📐 {width}×{height}** button in the toolbar, type new dimensions in mm or pick a ready-made preset, and click **Apply**. Objects keep their positions in mm — they are never rescaled. (See *Help*, section 4.) ### Can I go back to an earlier version of a template? -Yes — **🕘 History** in the editor. Every manual save (Save/Ctrl+S) creates a version; click **Restore** next to the one you want. Autosave does not create a version, so the list stays short and readable (we keep the last 30). +Yes — click **🕘 History** in the editor. Every manual save (the Save button or Ctrl+S) creates a version, a snapshot of what the template looked like at that moment; click **Restore** next to the one you want. Autosave doesn't create a version, so the list stays short and easy to read — the app keeps the last 30. (See *Help*, section 4.) ### How do I save a template? -The editor saves on its own (autosave every few seconds — see the toolbar status). You can also press **Ctrl/Cmd + S** manually. +The editor saves on its own — that's autosave, every few seconds; you can see the status in the toolbar. You can also press **Ctrl/Cmd + S** manually. --- ## Editor and objects ### What's the difference between **Text** (T) and **Text block** (¶)? -- **Text** — single line, fixed font size, doesn't wrap. -- **Text block** — multi-line, wraps inside a frame of a given width. You can also enable **Auto-fit** which scales the font to fit the content (useful when database names vary in length). +- **Text** — a single line, fixed font size, doesn't wrap. +- **Text block** — multiple lines, wraps inside a frame of a given width. You can also turn on **auto-fit**, which adjusts the font size to match the length of the text — useful when names coming from a database vary between short and long. ### What does `{{column_name}}` mean in a text field? -It's a **placeholder**. When you generate a series, it'll be replaced with the value from the matching column in your spreadsheet/database. Works in Text **and** in the Barcode object's *Data* field. +It's a **placeholder** — a spot where the app will automatically insert data from the matching column in your spreadsheet or database, but only when you generate a series of labels. It works both in a Text field and in the Barcode object's *Data* field. (See *Help*, section 6.) ### What does the green chip under a text field mean? -A green chip marks a **date placeholder** (e.g. `{{date+14d}}`) and immediately shows the calculated value. Purple chips are regular spreadsheet columns. For the date syntax details, see the guide, section 7. +A green "chip" (a small coloured tag) marks a **date placeholder** (e.g. `{{date+14d}}`) and immediately shows you which date it will produce. Purple chips are ordinary spreadsheet columns. For the date syntax details, see *Help*, section 7. ### How do I add a table? -Left panel → **▦ Table**. Edit the cell contents, the number of rows/columns and the column widths in the right panel. Placeholders work inside cells — `{{column}}` and dates `{{date+x}}` — and when you generate a series the columns are substituted just like in regular text. +Left panel → **▦ Table**. Set the cell content, the number of rows/columns, and the column widths in the right panel. Placeholders work inside cells too — `{{column}}` and dates `{{date+x}}` — and when you generate a series, the columns get substituted just like in regular text. (See *Help*, section 3.) -### Polish characters in the PDF used to come out as little boxes — is that fixed? -Yes (since v0.13.0). The PDF now embeds fonts with the full Polish character set (ż, ł, ć, ę, ą, ź, ń, ś). If you still see boxes, make sure you're running version ≥0.13.0 (`/api/health`). +### Polish characters in the PDF used to come out as little boxes — is that fixed now? +Yes, since v0.13.0. The PDF now embeds fonts with the full Polish character set (ż, ł, ć, ę, ą, ź, ń, ś). If you still see boxes, check `/api/health` to confirm you're running version 0.13.0 or newer. -### How do I insert a logo that appears on every label? -Left panel → **🖼 Image** → pick a PNG/JPG/SVG. The logo will print on every generated label. +### How do I insert a logo that prints on every label? +Left panel → **🖼 Image** → pick a PNG, JPG or SVG file. The logo will print on every generated label. ### What's the difference between **🖼 Image** and **🌄 Background (reference)**? -- **🖼 Image** — a regular image. Prints in the PDF. -- **🌄 Background** — full-canvas image, **locked** (can't be moved) and **NOT printed** in the PDF. Use this when your labels came from the print shop with a logo already pre-printed: you upload a scan as a layout reference, position your new text against it, and the final PDF carries only your additions — the printer doesn't double-print the logo. +- **🖼 Image** — a regular image, prints in the PDF. +- **🌄 Background** — a full-size image, **locked** (can't be moved) and **NOT printed** in the PDF. Use it when your labels came from the print shop with a logo already printed, and you just need to position your new text correctly — in the editor you see the background as a visual guide, but the final PDF only contains your additions. (See *Help*, section 3.) -### How do I keep an object from being printed in the PDF? -Select the object → in the right panel, at the top, uncheck **🖨 Print in PDF**. The object goes to 50% opacity in the editor (so you spot it) and the renderer skips it. +### How do I stop an object from printing in the PDF? +Select the object → in the right panel, at the very top, uncheck **🖨 Print in PDF**. The object turns faded in the editor — a signal that it's preview-only — and the app skips it when printing. -### How do I lock an object so it can't be moved? -Select it → in the right panel, check **🔒 Lock position**. Handles disappear, drag and resize are off — but you can still select the object and edit its font, colour, etc. Uncheck the box to unlock. +### How do I lock an object so it doesn't get moved by accident? +Select it → in the right panel, check **🔒 Lock position**. The handles disappear, and you can't drag or resize it — but you can still select the object and change its font, colour, and so on. To unlock, uncheck the same box. ### How do I change the order of objects (which one is on top)? -Alignment bar above the canvas, **Layer** group: -- ⤓ to back, ↓ backward, ↑ forward, ⤒ to front. +The alignment bar above the canvas, **Layer** group: ⤓ to back, ↓ backward, ↑ forward, ⤒ to front. -### How do I distribute 5 objects evenly across the page? -Select all 5 (Shift + click) → alignment bar → **Distribute horizontally** button (works for 3+ objects). +### How do I distribute several objects evenly across the page? +Select all of them (Shift + click) → alignment bar → **Distribute horizontally** button (works with 3 or more selected objects). ### I undid too much. How do I get it back? **Ctrl/Cmd + Shift + Z** or **Ctrl/Cmd + Y**. -### How do I quickly duplicate an object? +### How do I quickly make a copy of an object? Two ways: -- **Alt + drag** — hold Alt (Option on Mac) and drag a selected object. The original stays put; the clone lands where you release. Works for multi-select too — relative positions are preserved. -- **Ctrl/Cmd + D** — duplicate in place with a +5 mm offset. Selection jumps to the clones, so a repeated Ctrl+D builds a staircase of copies. +- **Alt + drag** — hold Alt (Option on Mac) and drag a selected object. The original stays put, the copy lands under the cursor. This also works with several objects selected at once. +- **Ctrl/Cmd + D** — makes a copy in place, offset by 5 mm. The selection jumps to the copy, so a repeated Ctrl+D builds a staircase of copies. -The clone inherits every setting (font, colour, lock, *Print in PDF*); images share the same Asset. +The copy inherits every setting (font, colour, lock, *Print in PDF*); images share the same source file. --- ## Series generation (CSV / Excel) -### What files can I upload? -CSV, XLS, XLSX. Up to **10 MB** and **1,000 rows** per file (MVP cap). +### Which files can I upload? +CSV, XLS or XLSX. Up to **10 MB** and **1,000 rows** per file (the current version's cap). -### Should the first row be column headers? -Yes — the first row must contain column names. Those become the values you can map to as `{{name}}`. +### Does the first row of the spreadsheet need to be a header? +Yes — the first row must contain column names. Those become available as `{{name}}` when mapping in Step 2. -### I have more than 1,000 rows. What now? -Split your spreadsheet into batches of up to 1,000 rows each and generate several PDFs. +### I have more than 1,000 rows. What do I do? +Split your spreadsheet into batches of up to 1,000 rows each and generate several separate PDFs. ### Mapping didn't find my column. -Check that the placeholder name (`{{...}}`) matches the column header exactly — case-sensitive, no extra spaces. If they differ (e.g. placeholder `{{name}}` but column `Product Name`), pick the mapping manually in Step 2. +Check that the placeholder name (`{{...}}`) matches the column header exactly — capitalisation matters, and extra spaces cause mismatches too. If the names differ (e.g. placeholder `{{name}}` but a column called `Product Name`), pick the mapping manually from the list in Step 2. -### My PDF came out with `{{name}}` in the text instead of the actual name. -That means the placeholder didn't get mapped. In Step 2 (Map), every placeholder needs a column. +### My PDF came out with `{{name}}` in the text instead of the real name. +That means the placeholder didn't get mapped. In Step 2 (Map), every placeholder needs a column assigned to it. -### Can I include only some of the rows? -Yes — Step 3 (Filter). Pick a column, an operator (equals / contains / greater than / etc.) and a value. Click **Test filter** to see how many rows match. +### Can I generate labels for only some of the rows? +Yes — Step 3 (Filter). Pick a column, a condition (e.g. "equals", "contains", "greater than") and a value. Click **Test filter** to see how many rows match. --- ## Folders and the Library ### How do I organise templates into folders? -**Templates** page → rail on the left → **New folder**. Then hover a template card → **⚙** → pick a folder → Save. Folders are private (everyone has their own) and single-level. +**Templates** page → the rail on the left → **New folder**. Then hover over a template's card → **⚙** → pick a folder → **Save**. Folders are private (everyone has their own) and single-level. -### I deleted a folder — what happens to its templates? -Nothing bad: they go back to "No folder". +### I deleted a folder — what happens to the templates inside it? +Nothing bad — they simply move back to "No folder". No template disappears. -### How do I share a template with others? -Card → **⚙** → **"Share in the Library"**. Others will see it in the **Library** and can clone it with the "Use" button — only the owner can edit it. Untick to withdraw it. +### How do I share a template with colleagues? +Template card → **⚙** → tick **"Share in the Library"**. Others will see it in the **Library** and can clone it with the "Use" button — only the owner can edit the original. Untick the box to withdraw the share. -### Does "Use" in the Library change the original? -No — "Use" always creates your own independent copy (with a "(copy)" suffix). Images are copied into your file library. +### Does the "Use" button in the Library change the original? +No — "Use" always creates your own independent copy (with a "(copy)" suffix). Images from the template are copied into your own file library. -### Where do the "Ready-made projects" come from? -They're starters built into the app (updated together with it). They contain `{{...}}` fields and `{{date+x}}` dates — after cloning, replace the sample values with your own. +### Where do the "Ready-made projects" in the Library come from? +They're starter designs built into the app (updated along with it). They contain sample `{{...}}` placeholders and `{{date+x}}` dates — after cloning, just replace the sample values with your own. --- ## Date placeholders ### How do I insert a best-before date of "today + 30 days"? -In a text field (or in barcode data), type `{{date+30d}}`. When the PDF/ZPL is generated, the app substitutes the date 30 days from today, e.g. `03.08.2026`. +In a text field (or in barcode data), type `{{date+30d}}`. When the PDF or ZPL is generated, the app substitutes the date 30 days after today, e.g. `03.08.2026`. -### Which offsets can I use? -`d` = days, `m` = months, `y` = years, with plus or minus: `{{date+14d}}`, `{{date-7d}}`, `{{date+3m}}`, `{{date+1y}}`. A bare `{{date}}` is today's date. +### Which date offsets can I use? +`d` = days, `m` = months, `y` = years — with a plus or minus sign: `{{date+14d}}`, `{{date-7d}}`, `{{date+3m}}`, `{{date+1y}}`. A bare `{{date}}` is today's date. -### How do I change the date format? +### How do I change the format the date is displayed in? Add a format after a colon, built from the DD/MM/YY/YYYY blocks: `{{date+14d:DD/MM/YY}}` → `18/07/26`, `{{date:YYYY-MM-DD}}` → `2026-07-04`. Without a format you get `DD.MM.YYYY`. ### When exactly is the date calculated? -At **generation time** (PDF or ZPL), using the server's date — not when you write the template. The green chip in the editor is only a preview for today. +At **generation time** (of the PDF or ZPL), using the server's clock — not when you write the template. The green chip in the editor is only a preview for today, so you can see right away how it will look. -### What if I add 1 month to 31 January? -You get 28 (or 29) February — the app never produces dates that don't exist. +### What happens if I add 1 month to 31 January? +You get 28 (or 29) February — the app never creates dates that don't exist. -### My spreadsheet has a column called `date`. Which one wins? -For a bare `{{date}}`, the **spreadsheet column** wins (as before). Forms with an offset or format (`{{date+14d}}`, `{{date:YYYY-MM-DD}}`) are always calculated automatically. +### My spreadsheet has a column named `date`. Which one wins? +For a bare `{{date}}`, the **spreadsheet column** wins (as before). Forms with an offset or a format (`{{date+14d}}`, `{{date:YYYY-MM-DD}}`) are always calculated automatically, regardless of the column. ### Why doesn't the `{{date}}` field require mapping in the series wizard? -Because when unmapped, the app substitutes today's date. You only map it if you want to take dates from a spreadsheet column. +Because when it's unmapped, the app substitutes today's date on its own. You only map it if you want to pull dates from a spreadsheet column instead. --- -## ZPL / Zebra printers +## ZPL and TSPL / label printers ### What is ZPL and why should I care? -ZPL is the language of label printers (Zebra and compatibles). If you print on such a printer, or receive ready-made ZPL labels from another system, the app can **import them into the editor** and **export your design as ZPL**. +**ZPL** is a special language that Zebra-brand label printers (and compatible models) speak. If you print on such a printer, or receive ready-made ZPL labels from another system, the app can **import them into the editor** and **export your design as ZPL**. (See *Help*, section 7a.) ### How do I import a ZPL label? Editor → toolbar → **⤓ Import ZPL** → paste the code → **Analyze** → **Import**. Careful: the import replaces the current canvas content. ### I don't know the DPI of the printer the code came from. -Leave the **Auto-detect** option in the import dialog — the app compares the dimensions in the code (`^PW`/`^LL`) with your label size and picks 203 or 300 dpi. +Leave the **Auto-detect** option on in the import dialog. **DPI** is print density — how many dots per millimetre the printer produces; the app compares the dimensions in the code (`^PW`/`^LL`) with your label size and picks 203 or 300 dpi on its own. ### What happens to variables like `{NAZWA}` in single braces? -They pass through untouched in both directions (import and export) — those are your system's printer variables. Double braces `{{...}}` are this app's placeholders. +They pass through untouched in both directions (import and export) — those are your own system's printer variables, unrelated to this app's placeholders. Double braces `{{...}}` are BarcodeLabelGen's placeholders. ### What's the difference between the "Template (variables)" and "Batch (dataset)" export? -- **Template** — one ZPL code; column placeholders stay in the code, dates are calculated right away. Made for pasting into your own system. -- **Batch** — you pick an uploaded data file and get one `.zpl` with a label for every row (everything substituted). +- **Template** — one ZPL code; column placeholders stay in the code (you substitute them in your own system), and dates are calculated right away. +- **Batch** — you pick a previously uploaded data file and get one `.zpl` file with a separate label for every row (everything already substituted). + +### Does the app also support TSC or Toshiba printers? +Yes — through **TSPL**, the equivalent of ZPL for those brands. In the editor click **⤒ TSPL**, pick a DPI (203 or 300), and download or copy the generated code. This is a simpler feature than ZPL for now: export only works for a single label (no Batch mode), there's no import yet, and direct printing through the connector currently supports ZPL only, not TSPL. ### Can I print directly to a Zebra printer from the app? -Yes — through the **connector** (`blg-connector`), a small program you install on a computer on the same network as your printers. Set it up once (**Devices** page → token + `config.yaml` file), then in the editor click **🖨 Print**, pick a device and a printer — the label goes into a queue, the agent picks it up and sends it to the printer. Instructions: `connector/README.md` in the repository. +Yes — through the **connector** (`blg-connector`), a small program you install on a computer on the same network as your printers, which links the app to the printer. Set it up once (**Devices** page → a token, a unique access code, plus a `config.yaml` file), then in the editor click **🖨 Print**, pick the device and printer — the label goes into a queue, the agent picks it up and sends it to the printer. Setup instructions: the `connector/README.md` file in the project repository. ### The Print button says the device is offline. -The agent on that computer hasn't checked in for over a minute — make sure `blg-connector` is running and can reach the server. You can still submit the job: it will wait in the queue until the agent comes back. +The agent (the connector program) on that computer hasn't checked in for over a minute — make sure `blg-connector` is running and can reach the server. You can still submit the print job: it will wait in the queue until the agent comes back. ### A print job failed with a "printer unreachable" error. -The agent couldn't connect to the printer over TCP (port 9100). Check the printer's IP in the agent's `config.yaml` and make sure the printer is switched on; then submit the job again. +The agent couldn't connect to the printer over the network. Check the printer's IP address in the agent's `config.yaml` file and make sure the printer is switched on, then submit the job again. -### How do I move a label from an old program (ERP/Word) into the editor? -Set up the connector's **virtual printer** (the `capture` section in `config.yaml` plus a ZDesigner printer on a "Standard TCP/IP" port pointed at `127.0.0.1:9101` in Windows — step by step in `connector/README.md`). Print the label from the old program to that printer and it will appear in **Devices → Inbox**, from where you can open it in the editor. +### How do I move a label from an old program (warehouse system/Word) into the editor? +Set up the connector's **virtual printer** — step-by-step instructions are in `connector/README.md`. Print the label from the old program to that virtual printer, and it will appear in **Devices → Inbox**, from where you can open it in the editor. ### A captured label has no logo/graphics in the editor. -Driver bitmaps (`^GF`) come through as a non-editable passthrough — they will print correctly, but the editor only shows the texts, barcodes and shapes it can model. Binary-mode graphics (`^GFB`) are not supported — leave the driver in ASCII/hex mode. +Graphics coming from the printer driver pass through as a non-editable element — they print correctly, but the editor only shows the text, barcodes and shapes it can recognise and let you edit. ### I printed something to the virtual printer and nothing arrived. -Check the agent's log. The most common causes: the job contained no `^XA` (the driver isn't producing ZPL — use ZDesigner), the job exceeded 2 MB, or the server was unreachable — in that case the job waits in the agent's local spool and is sent automatically within about 30 s of connectivity coming back. +Check the log (the activity record) of the agent on the computer running the connector. The most common causes: the job didn't contain valid printer code, it was too large, or the server was briefly unreachable — in that last case, the job waits locally and sends itself automatically once the connection comes back. --- ## Series generation (SQLite) ### How do I upload a SQLite database? -Step 1 of the Generate Series wizard — pick a file with the `.db`, `.sqlite` or `.sqlite3` extension. Limit **50 MB**. +**SQLite** is a file that stores an entire database in one file. Step 1 of the Generate Series wizard — pick a file with the `.db`, `.sqlite` or `.sqlite3` extension. Size limit: **50 MB**. -### What will I see after the upload? -A list of tables in the database, sorted with the most-rows tables at the top. Each entry shows the column count and row count. +### What will I see after uploading? +A list of tables from the database, sorted so the tables with the most rows come first. Each entry shows its column count and row count. -### Why did I pick a table the first time and get `table 'X' returned 0 rows`? -You picked an empty table (e.g. `basket_contents` with 0 rows). The table needs at least one row to generate from. Pick a different one — the sort should put non-empty tables on top. +### I picked a table and got a message saying it has 0 rows. +You picked an empty table. It needs at least 1 row of data for there to be anything to generate — pick a different one; the sorting should push tables with data to the top of the list. -### How do I write my own SELECT query? +### How do I write my own query against the database? Below the table list, expand **Show advanced: custom SQL query** and type something like: ```sql SELECT sku, UPPER(name) AS name, price FROM products WHERE price > 10 @@ -199,111 +201,111 @@ SELECT sku, UPPER(name) AS name, price FROM products WHERE price > 10 Click **Use this source**. ### Which queries are allowed? -Only a **single SELECT** (optionally prefixed with `WITH ... AS (...)`). The connection is read-only. Blocked: `INSERT`, `UPDATE`, `DELETE`, `DROP`, `ALTER`, `CREATE`, `ATTACH`, `DETACH`, `PRAGMA`, `VACUUM`, `REINDEX`, transactions. +Only a **single data-reading query (SELECT)**. The database connection is read-only — no command that could change or delete anything will ever run, even if you try to type one. -### I got `result exceeds 1000-row limit`. What now? -Your SELECT returned more than 1,000 rows (MVP cap). Add `WHERE` to narrow it, or `LIMIT 1000` at the end. +### I got a message saying the result exceeds the 1,000-row limit. What now? +Your query returned more than 1,000 rows. Add a `WHERE` condition to narrow it down, or add `LIMIT 1000` at the end. -### Can I use JOIN? -Yes — JOIN is standard SELECT syntax. Example: +### Can I combine data from two tables (JOIN)? +Yes, that's a standard SQL query feature. Example: ```sql SELECT p.sku, p.name, c.category_description FROM products p JOIN categories c ON p.category_id = c.category_id ``` -### Are my changes to the database saved? -**No.** The connection is read-only — no modifying statement will run, even if it slipped past the validator. +### Are any changes I try to make to the database saved? +**No.** The connection is read-only — no modifying command will ever run. -### My .db file shows "file is not a valid SQLite database". -The file probably isn't actually SQLite (e.g. it has the `.db` extension but is a different format). Check the file's source. +### My .db file shows an error saying "this is not a valid SQLite database". +The file probably isn't actually in SQLite format (e.g. it has a `.db` extension but something else inside). Check where the file came from. --- ## Importing / exporting templates -### Why export a template to a file? -Three main reasons: **backup** (save the file before a big change), **cloning** (export + import with a size override = a ready-made template for another format), **moving** (between instances or between users). +### Why would I export a template to a file? +Three main reasons: **backup** (keep the file in case something goes wrong later), **cloning** (export + import with a size override gives you a ready-made template for a different label format), **moving** (between installations of the app, or between users). ### Where's the export button? -- In the template list (tile) — the **⬇** icon at the bottom-right (appears on hover). -- In the editor toolbar — the **⬇ Export** button next to *Download PDF*. +- In the template list: hover over a card, click the **⬇** icon in the bottom-right corner. +- In the editor: toolbar → **⬇ Export** button, next to *Download PDF*. ### What exactly is in the .blg-template.json file? -Label size, every object (text, barcode, rectangle, line, image) with its exact position and all settings, and **every image** base64-encoded inside the file itself. The file is self-contained — you don't need anything else besides this one JSON. +The label size, every object (text, barcode, rectangle, line, image) with its exact position and all its settings, and every image encoded right inside the file. The file is self-contained — you don't need anything else besides this one file. -### Can I import a template from a different BarcodeLabelGen instance? -Yes. The file format (`$schema: "blg-template/v1"`) is stable. If the target instance doesn't have the same label format as the source, you get a warning and the program falls back to the "Custom" format. +### Can I import a template from a different BarcodeLabelGen installation? +Yes, the file format is stable across versions. If the destination installation doesn't have exactly the same label format as the one you exported from, you'll get a warning and the app will use the "Custom" format instead. -### Can I import only some of the objects? -Yes — in the second step of the import modal there's a **checklist**. Everything is checked by default; uncheck what you don't want. Skipped `image` objects don't create unused images in your asset library. +### Can I import only some of the objects from a file? +Yes — the second step of the import dialog has a checklist. Everything is checked by default; uncheck what you don't want. Skipped images don't create unused files in your library. ### What happens if the file contains an image I already have? -The app detects duplicates by SHA-256 hash and asks you: **Reuse existing** (FK points to the existing image, no disk duplicates) or **Create new copy** (fresh entry with the same content — useful when you want a separately editable copy). +The app checks whether it's the same file (by comparing its digital "fingerprint") and asks you: **Reuse existing** (no duplicate files on disk) or **Create new copy** (useful when you want a separate, independently editable copy). -### Can I import a template with a different size? -Yes — the second step has **Width/Height** fields. Leave blank to keep the original, or type new values. Objects keep their positions in mm, so the layout transfers but the format is different. +### Can I import a template while also changing its size? +Yes — the second step has Width/Height fields. Leave them blank to keep the original size, or type new values. Objects keep their positions in mm, so the layout stays the same, just on a different format. -### I'm getting "Couldn't read the file" -The file is not a valid JSON (e.g. corrupted, opened in an editor and saved with errors). Try re-exporting from the source template. +### I get a "Couldn't read the file" message. +The file isn't a valid JSON file — it may be corrupted, or it was edited manually and saved with an error. Try re-exporting the source template. -### I'm getting "sha256 mismatch" -The base64 content doesn't match the declared hash — the file was manually modified. The app deliberately refuses such files (it could hide a swapped image). Re-export from the source. +### I get a checksum (sha256) mismatch message. +The image content in the file doesn't match the checksum recorded in it — a digital "fingerprint" that confirms the file hasn't been altered. This means the file was manually modified. The app deliberately rejects such files — it could otherwise hide a swapped-in image. Re-export the file from the source. -### Limits? -File ≤ 20 MB, template ≤ 50 objects, ≤ 20 images, each image ≤ 5 MB. +### What are the limits? +File up to 20 MB, template up to 50 objects, up to 20 images, each image up to 5 MB. ## Accounts and security -### How does an admin add a new user? -**Administration → Users → Create account**. Enter email, a temporary password (minimum 10 characters) and a role. After creation the password is shown **once** — copy it and pass it to the user. +### How does an administrator add a new user? +**Administration → Users → Create account**. They enter an email, a temporary password (minimum 10 characters) and a role. Once the account is created, the password is shown **only once** — it needs to be copied and handed to the user right away. -### What are the roles and what can each do? -- **Administrator** — everything, plus user management. +### What are the roles and what can each one do? +- **Administrator** — everything, including account management. - **Editor** — creates and edits their own templates and datasets, generates PDFs. - **Viewer** — can browse and view, but cannot save changes. ### I forgot my password. -Ask an admin for a reset (Administration → Users → **Reset password**). You'll get a new temporary password — at login the app will force you to set your own. +Ask an administrator to reset it (**Administration → Users → Reset password**). You'll get a new temporary password — at your next login the app will ask you to set your own. ### Why can't I deactivate my own account? -Because you'd lock yourself out with no way to undo it. A second administrator can deactivate another administrator. +Because you'd lock yourself out of the app with no way to undo it yourself. A second administrator can deactivate someone else, but not themselves. ### Where do I find previously generated PDFs? -Menu → **History**. It keeps every generated file (single labels and series, PDF as well as batch ZPL) for 30 days — click **Download** to fetch it again without regenerating from scratch. +Menu → **History**. The app keeps every generated file there (single labels and whole series, PDFs as well as batch ZPL files) for 30 days — click **Download** to fetch the file again without regenerating it. ## Technical issues ### "Session expired — please refresh the page" -The CSRF token has expired (usually after a long idle period). Press F5 and sign in again. +A security token (a small, temporary code that protects your session) has expired — usually after a period of inactivity. Refresh the page (F5) and sign in again. -### The editor shows "Failed to load template." -The template was probably deleted, or you don't have access. Go back to **Templates** and check the list. +### The editor shows "Failed to load template" +The template may have been deleted, or you don't have access to it. Go back to the **Templates** page and check the list. -### I downloaded a PDF and got `pdf_render_failed`. -Something went wrong server-side (usually invalid data in an object). Check that you don't have a column placeholder `{{...}}` in a single label (columns only get substituted during series generation; in a single PDF they stay as literal text; date placeholders are calculated everywhere). +### I download a PDF and get a "pdf_render_failed" error +Something went wrong on the server side — usually invalid data in one of the objects. Check that you don't have a column placeholder `{{...}}` in a single label — columns only work during series generation; in a single PDF they stay as plain text (date placeholders are calculated everywhere). -### I generated a series and saw `no_rows: filter matched no rows` -The filter in Step 3 didn't catch any rows. Go back and loosen it or disable it. +### I generate a series and see a message that the filter matched no rows +The filter in Step 3 was too strict. Go back and loosen it, or turn it off. -### Autosave is stuck on "Unsaved changes" and won't move. -The network probably dropped. Check your connection and click **Save** manually. +### Autosave is stuck on "Unsaved changes" and nothing happens +Your network connection probably dropped. Check your connection and click **Save** manually. -### An object I see in the editor is missing in the generated PDF. -Check the right panel — if **🖨 Print in PDF** is unchecked, the object is preview-only. +### An object I see in the editor is missing from the generated PDF +Check whether **🖨 Print in PDF** is unchecked for that object in the right panel — if so, it's preview-only. -### Text in a block is getting cut off. -After PDF generation you'll see **N warnings**. Two options: -1. Enlarge the block frame. -2. Enable **Auto-fit font** in the right panel and set a sensible minimum size. +### Text in a block is getting cut off +After generating the PDF you'll see an **"N warnings"** chip. You have two options: +1. Make the text block's frame bigger. +2. Turn on **Auto-fit** in the right panel and set a sensible minimum font size. -### The app is in Polish but I want English. -The **PL/EN** language switcher is in the top-right corner of the header (also on the login page). +### The app is in English but I want Polish +The **PL/EN** language switcher is in the top-right corner of the header (also available on the login page). -### The label came out with yesterday's/tomorrow's date instead of today's. -The date is calculated using the **server's** clock. If the skew keeps happening, ask your administrator to check the server's timezone (the `TZ` variable in the configuration). +### The label came out with yesterday's or tomorrow's date instead of today's +The date is calculated using the clock of the **server** the app runs on. If this keeps happening, ask your administrator to check the server's timezone settings. --- ## Questions not on the list -Write to **dev@attv.uk** — describe what you were trying to do and what you saw. Screenshots welcome. +Write to **dev@attv.uk** — describe what you were trying to do and what you saw. A screenshot really helps. diff --git a/docs/FAQ.pl.md b/docs/FAQ.pl.md index f17aea3..906c83d 100644 --- a/docs/FAQ.pl.md +++ b/docs/FAQ.pl.md @@ -1,311 +1,311 @@ # BarcodeLabelGen — FAQ -Najczęstsze pytania pogrupowane od najprostszych do zaawansowanych. Brak odpowiedzi tutaj? Zajrzyj do [HELP.pl.md](HELP.pl.md) albo napisz na dev@attv.uk. +Najczęstsze pytania, od najprostszych do bardziej zaawansowanych. Każda odpowiedź jest krótka — jeśli chcesz zobaczyć konkretne kroki ze zrzutami ekranu, zajrzyj do [przewodnika Pomoc](HELP.pl.md), do sekcji podanej w nawiasie. Nie znalazłaś/eś odpowiedzi? Napisz na **dev@attv.uk**. --- ## Podstawy ### Po co jest ten program? -Robisz w nim szablony etykiet (rozmiar w mm, dowolny tekst, kody kreskowe, obrazy) i potem generujesz **wiele etykiet z jednego szablonu** — każdą z innymi danymi z arkusza lub bazy SQLite. +Tworzysz w nim szablony etykiet — rozmiar w milimetrach, dowolny tekst, kody kreskowe, obrazy — a potem generujesz **wiele etykiet z jednego szablonu naraz**, każdą z innymi danymi pobranymi z arkusza (np. Excela) albo z bazy danych. (Zobacz *Pomoc*, sekcja 6.) -### Czemu po pierwszym logowaniu kazano mi zmienić hasło? -Administrator dał ci hasło tymczasowe. Pierwsze logowanie zawsze wymusza zmianę na własne (min. 10 znaków). To jednorazowo. +### Czemu po pierwszym logowaniu program kazał mi zmienić hasło? +Administrator dał Ci hasło tymczasowe — czyli takie na start, do jednorazowego użycia. Pierwsze logowanie zawsze wymusza ustawienie własnego hasła (minimum 10 znaków). To zdarza się tylko raz. (Zobacz *Pomoc*, sekcja 1.) -### Gdzie jest "Nowy szablon"? +### Gdzie jest przycisk „Nowy szablon"? Lewe menu → **Szablony** → przycisk **Nowy szablon** w prawym górnym rogu listy. ### Czy mogę zmienić rozmiar etykiety po utworzeniu szablonu? -Tak. W edytorze kliknij w toolbarze przycisk **📐 {szerokość}×{wysokość}**, wpisz nowe wymiary w mm albo wybierz preset i kliknij **Zastosuj**. Obiekty zachowują pozycje w mm (nie są przeskalowywane). +Tak. W edytorze kliknij przycisk **📐 {szerokość}×{wysokość}** w pasku narzędzi, wpisz nowe wymiary w mm albo wybierz gotowy preset i kliknij **Zastosuj**. Obiekty zachowują swoje pozycje w mm — nie są przeskalowywane. (Zobacz *Pomoc*, sekcja 4.) ### Czy mogę cofnąć się do wcześniejszej wersji szablonu? -Tak — **🕘 Historia** w edytorze. Każdy ręczny zapis (Zapisz/Ctrl+S) tworzy wersję; kliknij **Przywróć** przy wybranej. Autozapis nie tworzy wersji, więc lista jest krótka i czytelna (trzymamy 30 ostatnich). +Tak — kliknij **🕘 Historia** w edytorze. Każdy ręczny zapis (przycisk Zapisz albo Ctrl+S) tworzy wersję, czyli migawkę tego, jak wyglądał szablon w danym momencie; kliknij **Przywróć** przy tej, do której chcesz wrócić. Autozapis nie tworzy wersji, więc lista jest krótka i czytelna — program trzyma 30 ostatnich. (Zobacz *Pomoc*, sekcja 4.) ### Jak zapisać szablon? -Edytor zapisuje sam (autozapis co kilka sekund — status widać w toolbarze). Możesz też ręcznie **Ctrl/Cmd + S**. +Edytor zapisuje sam — to tzw. autozapis, co kilka sekund; status widać w pasku narzędzi. Możesz też ręcznie nacisnąć **Ctrl/Cmd + S**. --- ## Edytor i obiekty ### Jaka jest różnica między **Tekst** (T) a **Blok tekstu** (¶)? -- **Tekst** — jedna linia, stały rozmiar fontu, nie zawija. -- **Blok tekstu** — wieloliniowy, zawija się w ramce o zadanej szerokości. Dodatkowo można włączyć **Auto-skalowanie**, które dopasowuje font do długości tekstu (potrzebne gdy z bazy przychodzą krótkie i długie nazwy). +- **Tekst** — jedna linia, stały rozmiar fontu, nie zawija się. +- **Blok tekstu** — wiele linii, zawija się w ramce o zadanej szerokości. Można dodatkowo włączyć **auto-skalowanie**, które samo dopasowuje rozmiar fontu do długości tekstu — przydatne, gdy z bazy przychodzą raz krótkie, raz długie nazwy. ### Co znaczy `{{nazwa_kolumny}}` w polu tekstowym? -To **placeholder**. Przy generowaniu serii zostanie podmieniony wartością z odpowiedniej kolumny w arkuszu/bazie. Działa w polu Text **i** w polu *Dane* obiektu Barcode. +To **placeholder** — miejsce, w które program sam wstawi dane z odpowiedniej kolumny Twojego arkusza lub bazy, ale dopiero podczas generowania serii etykiet. Działa zarówno w polu Tekst, jak i w polu *Dane* obiektu Kod kreskowy. (Zobacz *Pomoc*, sekcja 6.) ### Co znaczy zielony chip pod polem tekstowym? -Zielony chip oznacza **placeholder daty** (np. `{{date+14d}}`) i od razu pokazuje obliczoną wartość. Fioletowe chipy to zwykłe kolumny z arkusza. Szczegóły składni dat: przewodnik, sekcja 7. +Zielony „chip" (mały kolorowy znacznik) oznacza **placeholder daty** (np. `{{date+14d}}`) i od razu pokazuje, jaka data z niego wyjdzie. Fioletowe chipy to zwykłe kolumny z arkusza. Szczegóły zapisu dat: *Pomoc*, sekcja 7. ### Jak dodać tabelę? -Lewy panel → **▦ Tabela**. Treść komórek, liczbę wierszy/kolumn i szerokości kolumn edytujesz w prawym panelu. W komórkach działają placeholdery `{{kolumna}}` i daty `{{date+x}}` — przy generowaniu serii kolumny podmieniają się jak w zwykłym tekście. +Lewy panel → **▦ Tabela**. Treść komórek, liczbę wierszy/kolumn i szerokości kolumn ustawiasz w prawym panelu. W komórkach działają placeholdery `{{kolumna}}` i daty `{{date+x}}` — przy generowaniu serii kolumny podmieniają się tak samo jak w zwykłym tekście. (Zobacz *Pomoc*, sekcja 3.) ### Polskie znaki w PDF wychodziły jako kwadraciki — czy to naprawione? -Tak (od v0.13.0). PDF osadza teraz fonty z pełnym zestawem polskich znaków (ż, ł, ć, ę, ą, ź, ń, ś). Jeśli widzisz kwadraciki, upewnij się, że działasz na wersji ≥0.13.0 (`/api/health`). +Tak, od wersji v0.13.0. PDF osadza teraz czcionki z pełnym zestawem polskich znaków (ż, ł, ć, ę, ą, ź, ń, ś). Jeśli nadal widzisz kwadraciki, sprawdź na stronie `/api/health`, czy działasz na wersji 0.13.0 lub nowszej. -### Jak wstawić logo, które jest na każdej etykiecie? -Lewy panel → **🖼 Obraz** → wybierz plik PNG/JPG/SVG. Logo będzie się drukować na każdej etykiecie. +### Jak wstawić logo, które ma się drukować na każdej etykiecie? +Lewy panel → **🖼 Obraz** → wybierz plik PNG, JPG lub SVG. Logo będzie drukować się na każdej wygenerowanej etykiecie. ### Jaka jest różnica między **🖼 Obraz** a **🌄 Tło (referencja)**? - **🖼 Obraz** — zwykły obraz, drukuje się w PDF. -- **🌄 Tło** — pełnowymiarowy obraz, **zablokowany** (nie da się przesunąć) i **NIE drukuje się** w PDF. Używaj gdy etykiety przyszły z drukarni z już nadrukowanym logo i chcesz tylko pozycjonować nowy tekst — tło widzisz w edytorze jako wzorzec, ale finalny PDF zawiera tylko twoje dodatki. +- **🌄 Tło** — obraz na cały rozmiar etykiety, **zablokowany** (nie da się go przesunąć) i **NIE drukuje się** w PDF. Używaj go, gdy Twoje etykiety przyszły z drukarni z już wydrukowanym logo, a Ty chcesz tylko poprawnie ustawić nowy tekst — w edytorze widzisz tło jako wzorzec, ale finalny PDF zawiera wyłącznie Twoje dodatki. (Zobacz *Pomoc*, sekcja 3.) ### Jak nie drukować jakiegoś obiektu w PDF? -Zaznacz obiekt → w prawym panelu, na samej górze, odznacz **🖨 Drukuj w PDF**. Obiekt zostanie wyblakły w edytorze (sygnał że jest tylko podglądowy) i renderer go pominie. +Zaznacz obiekt → w prawym panelu, na samej górze, odznacz **🖨 Drukuj w PDF**. Obiekt zrobi się wyblakły w edytorze — to sygnał, że jest tylko podglądowy — a program pominie go w wydruku. -### Jak zablokować obiekt żeby się nie przesuwał? -Zaznacz → w prawym panelu zaznacz **🔒 Zablokuj pozycję**. Uchwyty znikną, nie da się przeciągać ani skalować — ale dalej można zaznaczyć i zmieniać font/kolor. Żeby odblokować — odznacz checkbox. +### Jak zablokować obiekt, żeby przypadkiem się nie przesunął? +Zaznacz go → w prawym panelu zaznacz **🔒 Zablokuj pozycję**. Uchwyty znikną, nie da się przeciągać ani skalować — ale wciąż można zaznaczyć obiekt i zmienić np. font czy kolor. Żeby odblokować, odznacz ten sam checkbox. ### Jak zmienić kolejność obiektów (co jest na wierzchu)? -Pasek wyrównania nad canvasem, grupa **Warstwa**: -- ⤓ na sam dół, ↓ niżej, ↑ wyżej, ⤒ na sam wierzch. +Pasek wyrównania nad canvasem, grupa **Warstwa**: ⤓ na sam dół, ↓ niżej, ↑ wyżej, ⤒ na sam wierzch. -### Jak rozłożyć równomiernie 5 obiektów w poziomie? -Zaznacz wszystkie 5 (Shift + klik) → pasek wyrównania → przycisk **Rozłóż poziomo** (działa od 3+ obiektów). +### Jak rozłożyć równomiernie kilka obiektów w poziomie? +Zaznacz je wszystkie (Shift + klik) → pasek wyrównania → przycisk **Rozłóż poziomo** (działa od 3 zaznaczonych obiektów wzwyż). -### Cofnąłem za dużo. Jak to przywrócić? -**Ctrl/Cmd + Shift + Z** lub **Ctrl/Cmd + Y**. +### Cofnąłem/am za dużo. Jak to przywrócić? +**Ctrl/Cmd + Shift + Z** albo **Ctrl/Cmd + Y**. ### Jak szybko zrobić kopię obiektu? Dwa sposoby: -- **Alt + przeciąganie** — przytrzymaj Alt (Option na Mac) i przeciągnij zaznaczony obiekt. Oryginał zostaje, klon ląduje pod kursorem. Działa też dla multi-select — zachowuje względne pozycje. -- **Ctrl/Cmd + D** — duplikuje w miejscu z przesunięciem +5 mm. Selekcja przeskakuje na klony, więc kolejne Ctrl+D buduje schodek kopii. +- **Alt + przeciąganie** — przytrzymaj Alt (na Mac: Option) i przeciągnij zaznaczony obiekt. Oryginał zostaje na miejscu, kopia ląduje pod kursorem. Działa też dla kilku zaznaczonych obiektów naraz. +- **Ctrl/Cmd + D** — robi kopię w miejscu, przesuniętą o 5 mm. Zaznaczenie przeskakuje na kopię, więc kolejne Ctrl+D buduje schodek kopii. -Klon dziedziczy wszystkie ustawienia (font, kolor, lock, *Drukuj w PDF*); obrazy współdzielą Asset. +Kopia dziedziczy wszystkie ustawienia (font, kolor, blokadę, *Drukuj w PDF*); obrazy współdzielą jeden plik źródłowy. --- ## Generowanie serii (CSV / Excel) ### Jakie pliki mogę wgrać? -CSV, XLS, XLSX. Maksymalnie **10 MB** i **1000 wierszy** na plik (limit MVP). +CSV, XLS lub XLSX. Maksymalnie **10 MB** i **1000 wierszy** na plik (limit obecnej wersji programu). -### Pierwszy wiersz arkusza to nagłówki? -Tak, pierwszy wiersz musi zawierać nazwy kolumn — to one stają się dostępne jako `{{nazwa}}` w mapowaniu. +### Czy pierwszy wiersz arkusza musi być nagłówkiem? +Tak — pierwszy wiersz musi zawierać nazwy kolumn. To one stają się dostępne jako `{{nazwa}}` przy mapowaniu w Kroku 2. ### Mam więcej niż 1000 wierszy. Co zrobić? -Podziel arkusz na partie po max 1000 wierszy i wygeneruj kilka PDF. +Podziel arkusz na partie po maksymalnie 1000 wierszy i wygeneruj kilka osobnych PDF-ów. ### Mapowanie nie znalazło mojej kolumny. -Sprawdź czy nazwa placeholdera (`{{...}}`) odpowiada dokładnie nagłówkowi kolumny — case-sensitive, bez spacji ekstra. Jeśli nazwy są różne (np. placeholder `{{name}}`, kolumna `Product Name`), wybierz mapowanie ręcznie z listy w Kroku 2. +Sprawdź, czy nazwa placeholdera (`{{...}}`) odpowiada dokładnie nagłówkowi kolumny — wielkość liter ma znaczenie, a dodatkowe spacje przeszkadzają. Jeśli nazwy się różnią (np. placeholder `{{name}}`, a kolumna `Nazwa produktu`), wybierz mapowanie ręcznie z listy w Kroku 2. -### PDF wyszedł z `{{name}}` w treści zamiast prawdziwą nazwą. -To znaczy że placeholder się nie zmapował. W Kroku 2 (Mapowanie) musisz dla każdego placeholdera wybrać kolumnę. +### PDF wyszedł z `{{name}}` w treści zamiast prawdziwej nazwy. +To znaczy, że placeholder się nie zmapował. W Kroku 2 (Mapowanie) musisz dla każdego placeholdera wybrać kolumnę. -### Czy mogę odsiać tylko niektóre wiersze? -Tak — Krok 3 (Filtr). Wybierz kolumnę, operator (równe / zawiera / większe niż / itd.) i wartość. Klik **Sprawdź filtr** żeby zobaczyć ile wierszy się załapie. +### Czy mogę wygenerować etykiety tylko dla niektórych wierszy? +Tak — Krok 3 (Filtr). Wybierz kolumnę, warunek (np. „równe", „zawiera", „większe niż") i wartość. Kliknij **Sprawdź filtr**, żeby zobaczyć, ile wierszy się załapie. --- ## Foldery i Biblioteka ### Jak uporządkować szablony w foldery? -Strona **Szablony** → pasek po lewej → **Nowy folder**. Potem najedź na kafelek szablonu → **⚙** → wybierz folder → Zapisz. Foldery są prywatne (każdy ma swoje) i jednopoziomowe. +Strona **Szablony** → pasek po lewej → **Nowy folder**. Potem najedź na kafelek szablonu → **⚙** → wybierz folder → **Zapisz**. Foldery są prywatne (każdy ma swoje) i jednopoziomowe. -### Usunąłem folder — co z szablonami? -Nic im nie jest: wracają do „Bez folderu". +### Usunęłam/em folder — co się stanie z szablonami w środku? +Nic złego — po prostu wracają do „Bez folderu". Żaden szablon nie znika. -### Jak podzielić się szablonem z innymi? -Kafelek → **⚙** → **„Udostępnij w Bibliotece"**. Inni zobaczą go w **Bibliotece** i sklonują przyciskiem „Użyj" — edytować może tylko właściciel. Odznacz, aby wycofać. +### Jak podzielić się szablonem z innymi w firmie? +Kafelek szablonu → **⚙** → zaznacz **„Udostępnij w Bibliotece"**. Inni zobaczą go w **Bibliotece** i będą mogli sklonować go przyciskiem „Użyj" — edytować oryginał może tylko właściciel. Odznacz pole, żeby wycofać udostępnienie. -### Czy „Użyj" w Bibliotece zmienia oryginał? -Nie — „Użyj" zawsze tworzy Twoją niezależną kopię (z dopiskiem „(kopia)"). Obrazki są kopiowane do Twojej biblioteki plików. +### Czy przycisk „Użyj" w Bibliotece zmienia oryginał? +Nie — „Użyj" zawsze tworzy Twoją niezależną kopię (z dopiskiem „(kopia)"). Obrazy z szablonu są kopiowane do Twojej własnej biblioteki plików. -### Skąd się biorą „Gotowe projekty"? -To startery wbudowane w aplikację (aktualizowane razem z nią). Zawierają pola `{{...}}` i daty `{{date+x}}` — po sklonowaniu podmień przykładowe wartości na swoje. +### Skąd się biorą „Gotowe projekty" w Bibliotece? +To gotowe wzory wbudowane w aplikację (aktualizowane razem z nią). Zawierają przykładowe placeholdery `{{...}}` i daty `{{date+x}}` — po sklonowaniu po prostu podmień przykładowe wartości na własne. --- ## Placeholdery daty ### Jak wstawić datę przydatności „dziś + 30 dni"? -W polu tekstowym (albo w danych kodu kreskowego) wpisz `{{date+30d}}`. Przy generowaniu PDF/ZPL program podstawi datę o 30 dni późniejszą od dzisiejszej, np. `03.08.2026`. +W polu tekstowym (albo w danych kodu kreskowego) wpisz `{{date+30d}}`. Podczas generowania PDF-a lub ZPL program wstawi datę 30 dni późniejszą niż dzisiejsza, np. `03.08.2026`. -### Jakie przesunięcia mogę używać? -`d` = dni, `m` = miesiące, `y` = lata, z plusem lub minusem: `{{date+14d}}`, `{{date-7d}}`, `{{date+3m}}`, `{{date+1y}}`. Samo `{{date}}` to dzisiejsza data. +### Jakie przesunięcia dat mogę używać? +`d` = dni, `m` = miesiące, `y` = lata — z plusem albo minusem: `{{date+14d}}`, `{{date-7d}}`, `{{date+3m}}`, `{{date+1y}}`. Samo `{{date}}` to dzisiejsza data. -### Jak zmienić format daty? -Dodaj format po dwukropku, z klocków DD/MM/YY/YYYY: `{{date+14d:DD/MM/YY}}` → `18/07/26`, `{{date:YYYY-MM-DD}}` → `2026-07-04`. Bez formatu dostajesz `DD.MM.YYYY`. +### Jak zmienić format wyświetlanej daty? +Dodaj format po dwukropku, z klocków DD/MM/YY/YYYY: `{{date+14d:DD/MM/YY}}` → `18/07/26`, `{{date:YYYY-MM-DD}}` → `2026-07-04`. Bez podanego formatu dostajesz `DD.MM.YYYY`. ### Kiedy dokładnie liczy się data? -W momencie **generowania** (PDF lub ZPL), według daty serwera — nie w momencie pisania szablonu. Zielony chip w edytorze to tylko podgląd na dziś. +W momencie **generowania** (PDF-a albo ZPL), według zegara serwera — nie w momencie, gdy piszesz szablon. Zielony chip w edytorze to tylko podgląd na dziś, żebyś od razu widziała/widział, jak to będzie wyglądać. -### Co jeśli 31 stycznia dodam 1 miesiąc? -Dostaniesz 28 (lub 29) lutego — program nie tworzy nieistniejących dat. +### Co się stanie, jeśli do 31 stycznia dodam 1 miesiąc? +Dostaniesz 28 (albo 29) lutego — program nigdy nie tworzy dat, które nie istnieją. -### Mam w arkuszu kolumnę o nazwie `date`. Co wygra? -Dla gołego `{{date}}` wygrywa **kolumna z arkusza** (jak dotychczas). Formy z przesunięciem lub formatem (`{{date+14d}}`, `{{date:YYYY-MM-DD}}`) zawsze liczą się automatycznie. +### Mam w arkuszu kolumnę o nazwie `date`. Która wersja wygrywa? +Dla gołego `{{date}}` wygrywa **kolumna z arkusza** (tak jak wcześniej). Formy z przesunięciem lub formatem (`{{date+14d}}`, `{{date:YYYY-MM-DD}}`) zawsze liczą się automatycznie, niezależnie od kolumny. ### Czemu w kreatorze serii pole `{{date}}` nie wymaga mapowania? -Bo bez mapowania program podstawi dzisiejszą datę. Mapujesz tylko jeśli chcesz brać daty z kolumny arkusza. +Bo bez mapowania program sam podstawi dzisiejszą datę. Mapujesz je tylko wtedy, gdy chcesz brać daty z kolumny w arkuszu. --- -## ZPL / drukarki Zebra +## ZPL i TSPL / drukarki etykiet ### Co to jest ZPL i po co mi to? -ZPL to język drukarek etykiet (Zebra i zgodne). Jeśli drukujesz na takiej drukarce albo dostajesz gotowe etykiety w ZPL z innego systemu, program potrafi je **importować do edytora** i **eksportować twój projekt jako ZPL**. +**ZPL** to specjalny język, którym rozmawiają drukarki etykiet marki Zebra (i modele zgodne). Jeśli drukujesz na takiej drukarce, albo dostajesz gotowe etykiety w ZPL z innego systemu, program potrafi je **zaimportować do edytora** i **wyeksportować Twój projekt jako ZPL**. (Zobacz *Pomoc*, sekcja 7a.) ### Jak zaimportować etykietę ZPL? -Edytor → toolbar → **⤓ Importuj ZPL** → wklej kod → **Sprawdź** → **Importuj**. Uwaga: import zastępuje obecną zawartość canvasu. +Edytor → pasek narzędzi → **⤓ Importuj ZPL** → wklej kod → **Sprawdź** → **Importuj**. Uwaga: import zastępuje obecną zawartość canvasu. ### Nie znam DPI drukarki, z której pochodzi kod. -Zostaw w oknie importu opcję **Wykryj automatycznie** — program porówna wymiary z kodu (`^PW`/`^LL`) z rozmiarem twojej etykiety i dobierze 203 lub 300 dpi. +Zostaw w oknie importu opcję **Wykryj automatycznie**. **DPI** to gęstość wydruku — liczba kropek na milimetr, jaką drukuje drukarka; program porówna wymiary z kodu (`^PW`/`^LL`) z rozmiarem Twojej etykiety i sam dobierze 203 albo 300 dpi. ### Co się dzieje ze zmiennymi typu `{NAZWA}` w pojedynczych klamrach? -Przechodzą nietknięte w obie strony (import i eksport) — to zmienne drukarkowe twojego systemu. Podwójne klamry `{{...}}` to placeholdery tego programu. +Przechodzą nietknięte w obie strony (import i eksport) — to zmienne drukarkowe Twojego własnego systemu, nie mają nic wspólnego z placeholderami programu. Podwójne klamry `{{...}}` to placeholdery BarcodeLabelGen. ### Czym różni się eksport „Szablon (zmienne)" od „Wsad (dataset)"? -- **Szablon** — jeden kod ZPL; placeholdery kolumn zostają w kodzie, daty są od razu obliczone. Do wklejenia we własny system. -- **Wsad** — wybierasz wgrany plik danych i dostajesz jeden `.zpl` z etykietą na każdy wiersz (wszystko podmienione). +- **Szablon** — jeden kod ZPL; placeholdery kolumn zostają w kodzie (podmienisz je we własnym systemie), a daty są od razu obliczone. +- **Wsad** — wybierasz wcześniej wgrany plik danych i dostajesz jeden plik `.zpl` z osobną etykietą dla każdego wiersza (wszystko już podmienione). + +### Czy program obsługuje też drukarki TSC albo Toshiba? +Tak — przez **TSPL**, odpowiednik ZPL dla tych marek. W edytorze kliknij **⤒ TSPL**, wybierz DPI (203 lub 300) i pobierz albo skopiuj wygenerowany kod. To na razie prostsza funkcja niż ZPL: eksport działa tylko dla pojedynczej etykiety (bez trybu Wsad), nie ma jeszcze importu, a druk bezpośredni przez konektor obsługuje na razie tylko ZPL, nie TSPL. ### Czy mogę drukować bezpośrednio na drukarkę Zebra z programu? -Tak — przez **konektor** (`blg-connector`), mały program instalowany na komputerze w sieci z drukarkami. Skonfiguruj go raz (strona **Urządzenia** → token + plik `config.yaml`), a potem w edytorze klikasz **🖨 Drukuj**, wybierasz urządzenie i drukarkę — etykieta trafia do kolejki, agent odbiera ją i wysyła na drukarkę. Instrukcja: `connector/README.md` w repozytorium. +Tak — przez **konektor** (`blg-connector`), czyli mały program instalowany na komputerze w tej samej sieci co drukarki, który łączy aplikację z drukarką. Skonfiguruj go raz (strona **Urządzenia** → token, czyli unikalny kod dostępu, + plik `config.yaml`), a potem w edytorze klikasz **🖨 Drukuj**, wybierasz urządzenie i drukarkę — etykieta trafia do kolejki, agent ją odbiera i wysyła na drukarkę. Instrukcja instalacji: plik `connector/README.md` w repozytorium projektu. ### Przycisk Drukuj mówi, że urządzenie jest offline. -Agent na tym komputerze nie zgłosił się od ponad minuty — sprawdź, czy `blg-connector` działa i ma połączenie z serwerem. Zadanie możesz mimo to wysłać: poczeka w kolejce, aż agent wróci. +Agent (program konektora) na tym komputerze nie zgłosił się od ponad minuty — sprawdź, czy `blg-connector` działa i ma połączenie z serwerem. Zadanie druku możesz mimo to wysłać: poczeka w kolejce, aż agent wróci. -### Zadanie druku skończyło się błędem „printer unreachable". -Agent nie mógł połączyć się z drukarką po TCP (port 9100). Sprawdź IP drukarki w `config.yaml` agenta i czy drukarka jest włączona; potem wyślij zadanie ponownie. +### Zadanie druku skończyło się błędem „printer unreachable" (drukarka nieosiągalna). +Agent nie mógł połączyć się z drukarką przez sieć. Sprawdź adres IP drukarki w pliku `config.yaml` agenta i czy drukarka jest włączona, a potem wyślij zadanie ponownie. -### Jak przenieść etykietę ze starego programu (ERP/Word) do edytora? -Skonfiguruj **wirtualną drukarkę** konektora (sekcja `capture` w `config.yaml` + drukarka ZDesigner na porcie TCP/IP `127.0.0.1:9101` w Windows — krok po kroku w `connector/README.md`). Wydrukuj etykietę ze starego programu na tę drukarkę, a pojawi się w **Urządzenia → Inbox**, skąd otworzysz ją w edytorze. +### Jak przenieść etykietę ze starego programu (system magazynowy/Word) do edytora? +Skonfiguruj **wirtualną drukarkę** konektora — krok po kroku opisane w `connector/README.md`. Wydrukuj etykietę ze starego programu na tę wirtualną drukarkę, a pojawi się w **Urządzenia → Inbox**, skąd otworzysz ją w edytorze. ### Przechwycona etykieta nie ma loga/grafiki w edytorze. -Bitmapy ze sterownika (`^GF`) przechodzą jako nieedytowalny passthrough — wydrukują się poprawnie, ale edytor pokazuje tylko teksty, kody i kształty, które potrafi zamodelować. Grafika w trybie binarnym (`^GFB`) nie jest wspierana — zostaw w sterowniku tryb ASCII/hex. +Grafika ze sterownika drukarki przechodzi jako nieedytowalny element — wydrukuje się poprawnie, ale edytor pokaże tylko teksty, kody kreskowe i kształty, które potrafi rozpoznać i pozwolić Ci edytować. -### Coś wydrukowałem na wirtualną drukarkę i nic nie doszło. -Sprawdź log agenta. Najczęstsze powody: zadanie nie zawierało `^XA` (sterownik nie generuje ZPL — użyj ZDesigner), zadanie przekroczyło 2 MB, albo serwer był niedostępny — wtedy zadanie czeka w lokalnym spoolu agenta i zostanie wysłane automatycznie do 30 s po powrocie łączności. +### Coś wydrukowałam/em na wirtualną drukarkę i nic nie doszło. +Sprawdź log (dziennik zdarzeń) agenta na komputerze z konektorem. Najczęstsze przyczyny: zadanie nie zawierało poprawnego kodu drukarki, było za duże, albo serwer był chwilowo niedostępny — w tym ostatnim przypadku zadanie czeka lokalnie i wysyła się samo, gdy połączenie wróci. --- ## Generowanie serii (SQLite) ### Jak wgrać bazę SQLite? -Krok 1 wizardu Generuj Serię — wybierz plik z rozszerzeniem `.db`, `.sqlite` lub `.sqlite3`. Limit **50 MB**. +**SQLite** to plik z całą bazą danych w jednym pliku. Krok 1 kreatora Generuj Serię — wybierz plik z rozszerzeniem `.db`, `.sqlite` lub `.sqlite3`. Limit rozmiaru: **50 MB**. ### Co zobaczę po wgraniu? -Listę tabel z bazy, posortowanych: najwięcej wierszy na górze. Każda pozycja pokazuje liczbę kolumn i wierszy. +Listę tabel z bazy, posortowaną tak, że tabele z największą liczbą wierszy są na górze. Każda pozycja pokazuje liczbę kolumn i wierszy. -### Czemu pierwszy raz wybrałem tabelę i dostałem `table 'X' returned 0 rows`? -Wybrałeś tabelę bez danych (np. `basket_contents` z 0 wierszy). W tabeli musi być co najmniej 1 wiersz, żeby było co generować. Wybierz inną — sortowanie powinno wypchnąć tabele z danymi na górę listy. +### Wybrałam/em tabelę i dostałam/em komunikat, że ma 0 wierszy. +Wybrałaś/eś pustą tabelę. Musi w niej być co najmniej 1 wiersz danych, żeby było co generować — wybierz inną, sortowanie powinno wypychać tabele z danymi na górę listy. -### Jak napisać własne zapytanie SELECT? +### Jak napisać własne zapytanie do bazy? Pod listą tabel rozwiń **Pokaż zaawansowane: własne zapytanie SQL** i wpisz np.: ```sql SELECT sku, UPPER(name) AS name, price FROM products WHERE price > 10 ``` -Klik **Użyj tego źródła**. +Kliknij **Użyj tego źródła**. ### Jakie zapytania są dozwolone? -Tylko **pojedynczy SELECT** (opcjonalnie poprzedzony `WITH ... AS (...)`). Połączenie jest read-only. Blokowane są: `INSERT`, `UPDATE`, `DELETE`, `DROP`, `ALTER`, `CREATE`, `ATTACH`, `DETACH`, `PRAGMA`, `VACUUM`, `REINDEX`, transakcje. +Tylko **pojedyncze zapytanie odczytujące dane (SELECT)**. Połączenie z bazą jest tylko do odczytu — żadne polecenie, które mogłoby coś zmienić lub skasować, nie zostanie wykonane, nawet jeśli spróbujesz je wpisać. -### Dostałem `result exceeds 1000-row limit`. Co teraz? -Wynik twojego SELECT ma więcej niż 1000 wierszy (limit MVP). Dodaj `WHERE`, żeby zawęzić, albo `LIMIT 1000` na końcu. +### Dostałam/em komunikat, że wynik przekracza limit 1000 wierszy. Co teraz? +Twoje zapytanie zwróciło więcej niż 1000 wierszy. Dodaj warunek `WHERE`, żeby zawęzić wynik, albo dopisz `LIMIT 1000` na końcu. -### Czy mogę używać JOIN? -Tak — JOIN jest standardowym SELECT. Przykład: +### Czy mogę łączyć dane z dwóch tabel naraz (JOIN)? +Tak, to standardowa funkcja zapytań SQL. Przykład: ```sql SELECT p.sku, p.name, c.category_description FROM products p JOIN categories c ON p.category_id = c.category_id ``` -### Czy zmiany w bazie są zapisywane? -**Nie.** Połączenie jest read-only — żadne polecenie modyfikujące się nie wykona, nawet jeśli przeszłoby walidator. +### Czy zmiany, które próbuję zrobić w bazie, są zapisywane? +**Nie.** Połączenie jest tylko do odczytu — żadne polecenie modyfikujące się nie wykona. -### Mój plik .db pokazuje "file is not a valid SQLite database". -Plik prawdopodobnie nie jest SQLite (np. ma rozszerzenie `.db` ale to inny format). Sprawdź źródło pliku. +### Mój plik .db pokazuje błąd „to nie jest poprawna baza SQLite". +Plik prawdopodobnie nie jest w formacie SQLite (np. ma rozszerzenie `.db`, ale w środku jest coś innego). Sprawdź, skąd pochodzi ten plik. --- ## Import / eksport szablonów ### Po co eksportować szablon do pliku? -Trzy główne powody: **backup** (zachowujesz plik przed dużą zmianą), **klonowanie** (eksport + import z nadpisanym rozmiarem to gotowy szablon na inny format), **przenoszenie** (między instancjami / między userami). +Trzy główne powody: **backup** (zachowujesz plik na wypadek, gdyby coś poszło nie tak), **klonowanie** (eksport + import z nadpisanym rozmiarem daje gotowy szablon na inny format etykiety), **przenoszenie** (między instalacjami programu albo między użytkownikami). ### Gdzie jest przycisk eksportu? -- W liście szablonów (kafelek) — ikona **⬇** w prawym dolnym rogu (pojawia się po najechaniu). -- W edytorze toolbar — przycisk **⬇ Eksportuj** obok *Pobierz PDF*. +- Na liście szablonów: najedź na kafelek, kliknij ikonę **⬇** w prawym dolnym rogu. +- W edytorze: pasek narzędzi → przycisk **⬇ Eksportuj**, obok *Pobierz PDF*. ### Co dokładnie jest w pliku .blg-template.json? -Rozmiar etykiety, każdy obiekt (tekst, kod, prostokąt, linia, obraz) z dokładną pozycją i wszystkimi ustawieniami, oraz **wszystkie obrazki** zakodowane base64 w samym pliku. Plik jest samowystarczalny — nie potrzebujesz nic poza tym jednym JSON-em. +Rozmiar etykiety, każdy obiekt (tekst, kod, prostokąt, linia, obraz) z dokładną pozycją i wszystkimi ustawieniami, oraz wszystkie obrazki zakodowane wprost w pliku. Plik jest samowystarczalny — nie potrzebujesz nic więcej poza tym jednym plikiem. -### Czy mogę zaimportować szablon z innej instancji BarcodeLabelGen? -Tak. Format pliku (`$schema: "blg-template/v1"`) jest stabilny. Jeśli docelowa instancja nie ma takiego samego formatu etykiety jak źródłowa, użytkownik zostanie ostrzeżony i program podstawi format „Custom". +### Czy mogę zaimportować szablon z innej instalacji BarcodeLabelGen? +Tak, format pliku jest stabilny między wersjami. Jeśli docelowa instalacja nie zna dokładnie takiego samego formatu etykiety jak ta, z której eksportowałaś/eś, dostaniesz ostrzeżenie i program użyje formatu „Własny". -### Czy mogę zaimportować tylko część obiektów? -Tak — w drugim kroku okna importu jest **czeklista**. Domyślnie wszystko jest zaznaczone; odznaczasz to, czego nie chcesz. Pominięte obiekty `image` nie tworzą zbędnych obrazków u Ciebie w bibliotece. +### Czy mogę zaimportować tylko część obiektów z pliku? +Tak — w drugim kroku okna importu jest lista z checkboxami. Domyślnie wszystko jest zaznaczone; odznacz to, czego nie chcesz. Pominięte obrazki nie tworzą zbędnych plików w Twojej bibliotece. -### Co się dzieje gdy w pliku jest obrazek, który już mam? -Program sprawdza duplikat po hashu SHA-256 i pyta Cię: **Użyj istniejącego** (FK pokazuje istniejący obrazek, zero duplikatów na dysku) albo **Utwórz nową kopię** (świeży wpis z tą samą zawartością — przydatne gdy chcesz mieć osobną edytowalną kopię). +### Co się dzieje, gdy w pliku jest obrazek, który już mam? +Program sprawdza, czy to ten sam plik (porównując jego cyfrowy „odcisk palca") i pyta Cię: **Użyj istniejącego** (zero duplikatów na dysku) albo **Utwórz nową kopię** (przydatne, gdy chcesz mieć osobną, niezależnie edytowalną kopię). -### Czy mogę zaimportować szablon zmieniając jego rozmiar? -Tak — w drugim kroku są dwa pola **Szerokość/Wysokość**. Zostaw puste żeby zachować oryginał, albo wpisz nowe wartości. Obiekty zachowują swoje pozycje w mm, więc szablon zachowuje układ ale na innym formacie. +### Czy mogę zaimportować szablon, zmieniając jednocześnie jego rozmiar? +Tak — w drugim kroku są pola Szerokość/Wysokość. Zostaw je puste, żeby zachować oryginalny rozmiar, albo wpisz nowe wartości. Obiekty zachowują swoje pozycje w mm, więc układ zostaje ten sam, tylko na innym formacie. -### Dostaję "Couldn't read the file" -Plik nie jest poprawnym JSON-em (np. uszkodzony, otwarty w edytorze i zapisany z błędem). Spróbuj ponownie wyeksportować źródłowy szablon. +### Dostaję komunikat „Nie udało się odczytać pliku". +Plik nie jest poprawnym plikiem JSON — być może jest uszkodzony albo został zmieniony ręcznie i zapisany z błędem. Spróbuj ponownie wyeksportować szablon źródłowy. -### Dostaję "sha256 mismatch" -Treść base64 w pliku nie zgadza się z zadeklarowanym hashem — plik został zmodyfikowany ręcznie. Program odrzuca takie pliki świadomie (mogłoby to ukrywać podmieniony obrazek). Re-eksportuj ze źródła. +### Dostaję komunikat o niezgodności sumy kontrolnej (sha256). +Zawartość obrazka w pliku nie zgadza się z zapisaną w nim sumą kontrolną — cyfrowym „odciskiem palca", który potwierdza, że plik nie został zmieniony. Oznacza to, że plik był ręcznie modyfikowany. Program celowo odrzuca takie pliki — mogłoby to ukrywać podmieniony obrazek. Wyeksportuj plik ponownie ze źródła. -### Limity? -Plik ≤ 20 MB, szablon ≤ 50 obiektów, ≤ 20 obrazków, każdy obrazek ≤ 5 MB. +### Jakie są limity? +Plik do 20 MB, szablon do 50 obiektów, do 20 obrazków, każdy obrazek do 5 MB. ## Konta i bezpieczeństwo -### Jak admin dodaje nowego użytkownika? -**Administracja → Użytkownicy → Utwórz konto**. Podaj email, hasło tymczasowe (min. 10 znaków) i rolę. Po utworzeniu hasło wyświetla się **raz** — skopiuj i przekaż użytkownikowi. +### Jak administrator dodaje nowego użytkownika? +**Administracja → Użytkownicy → Utwórz konto**. Podaje email, hasło tymczasowe (minimum 10 znaków) i rolę. Po utworzeniu konta hasło wyświetla się **tylko raz** — trzeba je od razu skopiować i przekazać użytkownikowi. ### Jakie są role i co mogą robić? -- **Administrator** — wszystko + zarządzanie kontami. -- **Edytor** — tworzy i edytuje własne szablony i dataset'y, generuje PDF. +- **Administrator** — wszystko, w tym zarządzanie kontami. +- **Edytor** — tworzy i edytuje własne szablony i zestawy danych, generuje PDF-y. - **Tylko podgląd** — może oglądać, ale nie zapisuje zmian. -### Zapomniałem hasła. -Poproś admina o reset (Administracja → Użytkownicy → **Resetuj hasło**). Dostaniesz nowe hasło tymczasowe — przy logowaniu program zmusi cię do ustawienia własnego. - -### Czemu nie mogę dezaktywować swojego własnego konta? -Bo zostałbyś zablokowany na zewnątrz aplikacji bez możliwości naprawy. Drugiego administratora może dezaktywować inny administrator. +### Zapomniałam/em hasła. +Poproś administratora o reset hasła (**Administracja → Użytkownicy → Resetuj hasło**). Dostaniesz nowe hasło tymczasowe — przy logowaniu program poprosi Cię o ustawienie własnego. ---- +### Czemu nie mogę dezaktywować własnego konta? +Bo zostałabyś/zostałbyś zablokowana/y poza aplikacją bez możliwości naprawy tego samodzielnie. Drugi administrator może dezaktywować kogoś innego, ale nie samego siebie. ### Gdzie znajdę wcześniej wygenerowane PDF-y? -Menu → **Historia**. Trzymamy tam wszystkie wygenerowane pliki (pojedyncze i serie, PDF oraz wsadowy ZPL) przez 30 dni — kliknij **Pobierz**, aby ściągnąć ponownie bez generowania od nowa. +Menu → **Historia**. Program przechowuje tam wszystkie wygenerowane pliki (pojedyncze etykiety i całe serie, PDF-y i wsadowe pliki ZPL) przez 30 dni — kliknij **Pobierz**, żeby ściągnąć plik ponownie bez generowania go od nowa. ## Problemy techniczne -### "Sesja wygasła — odśwież stronę" -Token CSRF wygasł (zwykle po długiej nieaktywności). Odśwież F5 i zaloguj się ponownie. +### „Sesja wygasła — odśwież stronę" +Token bezpieczeństwa (mały, tymczasowy kod, który chroni Twoją sesję) wygasł — zwykle po dłuższej nieaktywności. Odśwież stronę (klawisz F5) i zaloguj się ponownie. -### Edytor pokazuje "Nie udało się wczytać szablonu" -Szablon mógł zostać usunięty, albo nie masz do niego dostępu. Wróć na **Szablony** i sprawdź listę. +### Edytor pokazuje „Nie udało się wczytać szablonu" +Szablon mógł zostać usunięty, albo nie masz do niego dostępu. Wróć na stronę **Szablony** i sprawdź listę. -### Pobieram PDF i dostaję błąd "pdf_render_failed" -Coś poszło nie tak po stronie serwera (zwykle nieprawidłowe dane w obiekcie). Sprawdź czy nie masz placeholdera kolumny `{{...}}` w pojedynczej etykiecie (kolumny działają tylko w generowaniu serii, w pojedynczym PDF zostają jako tekst; placeholdery daty liczą się wszędzie). +### Pobieram PDF i dostaję błąd „pdf_render_failed" +Coś poszło nie tak po stronie serwera — zwykle chodzi o nieprawidłowe dane w jednym z obiektów. Sprawdź, czy nie masz placeholdera kolumny `{{...}}` w pojedynczej etykiecie — kolumny działają tylko przy generowaniu serii, w pojedynczym PDF-ie zostają jako zwykły tekst (placeholdery dat liczą się wszędzie). -### Generuję serię i widzę `no_rows: filter matched no rows` -Filtr w Kroku 3 nie złapał żadnego wiersza. Wróć i poluzuj filtr lub go wyłącz. +### Generuję serię i widzę komunikat, że filtr nie złapał żadnego wiersza. +Filtr w Kroku 3 był zbyt restrykcyjny. Wróć i poluzuj go albo go wyłącz. -### Autozapis się zatrzymał na "Niezapisane zmiany" i nie idzie dalej. -Prawdopodobnie sieć padła. Sprawdź połączenie i kliknij ręcznie **Zapisz**. +### Autozapis utknął na „Niezapisane zmiany" i nic się nie dzieje. +Prawdopodobnie padło połączenie z siecią. Sprawdź internet i kliknij ręcznie **Zapisz**. -### W generowanym PDF brakuje jednego obiektu, choć w edytorze go widzę. -Sprawdź czy w prawym panelu nie ma odznaczonego **🖨 Drukuj w PDF** — wtedy obiekt jest tylko podglądowy. +### W wygenerowanym PDF brakuje jednego obiektu, choć w edytorze go widzę. +Sprawdź, czy w prawym panelu tego obiektu nie jest odznaczone **🖨 Drukuj w PDF** — wtedy jest on tylko podglądowy. ### Tekst w bloku jest przycięty. -Po wygenerowaniu PDF zobaczysz **N ostrzeżeń**. Dwie opcje: -1. Powiększ ramkę bloku. -2. Włącz **Auto-skalowanie** w prawym panelu i ustaw min. font. +Po wygenerowaniu PDF-a zobaczysz chip **„N ostrzeżeń"**. Masz dwie opcje: +1. Powiększ ramkę bloku tekstu. +2. Włącz **Auto-skalowanie** w prawym panelu i ustaw minimalny rozmiar fontu. -### Strona programu jest po angielsku, a chcę po polsku. -Przełącznik języka **PL/EN** jest w prawym górnym rogu nagłówka (także na stronie logowania). +### Program jest po angielsku, a chcę po polsku. +Przełącznik języka **PL/EN** jest w prawym górnym rogu nagłówka (dostępny też na stronie logowania). -### Na etykiecie wyszła data wczorajsza/jutrzejsza zamiast dzisiejszej. -Data liczy się według zegara **serwera**. Jeśli rozjazd się powtarza, poproś administratora o sprawdzenie strefy czasowej serwera (zmienna `TZ` w konfiguracji). +### Na etykiecie wyszła data wczorajsza albo jutrzejsza zamiast dzisiejszej. +Data liczy się według zegara **serwera**, na którym działa program. Jeśli ten rozjazd się powtarza, poproś administratora o sprawdzenie ustawień strefy czasowej serwera. --- ## Pytania, których nie ma na liście -Napisz do **dev@attv.uk** — opisz co próbujesz zrobić i co zobaczyłeś. Zrzut ekranu mile widziany. +Napisz do **dev@attv.uk** — opisz, co próbowałaś/eś zrobić i co zobaczyłaś/eś. Zrzut ekranu bardzo się przyda. diff --git a/docs/HELP.en.md b/docs/HELP.en.md index 6201465..fa2356f 100644 --- a/docs/HELP.en.md +++ b/docs/HELP.en.md @@ -1,6 +1,8 @@ # BarcodeLabelGen — Help -A short guide to the app. Read top-to-bottom for a complete tour, or jump straight to the feature you need. +This guide walks you through the app step by step — even if you've never used a similar tool before. Read it top to bottom, or jump straight to the section you need. + +If something's missing here, check the [FAQ](FAQ.en.md) or write to **dev@attv.uk**. --- @@ -8,26 +10,41 @@ A short guide to the app. Read top-to-bottom for a complete tour, or jump straig ### Signing in -1. Open the app URL in your browser. +1. Open the app's web address in your browser (your administrator gives you this). 2. Enter the email and password your administrator gave you. -3. On your first login, the app will ask you to set your own password (minimum 10 characters). This happens once — afterwards you go straight to the dashboard. +3. Click **Log in**. ![Login screen](screenshots/help/en/login.png) *frame: the Email + Password form with the "Log in" button and the PL/EN language switcher in the top-right corner.* -### The dashboard +4. If this is your **first** login, the app will ask you to set your own password (minimum 10 characters). This only happens once — on future logins you'll go straight to your panel. + +![the forced password-change screen shown on first login — two fields (new password / confirm password), the "minimum 10 characters" hint, and a "Set password" button.](screenshots/help/en/set-new-password.png) + +### The dashboard — your starting screen + +After signing in you land on the **Dashboard**. It's just a welcome screen — there's nothing to set up here. To start working, click **Templates** in the menu on the left. -After signing in you land on the **Dashboard**. It's just a welcome screen — to start working, click **Templates** in the left menu. +![the Dashboard right after login, with the left sidebar visible; the "Templates" menu item highlighted with an arrow to show where to click next.](screenshots/help/en/dashboard-empty.png) ### Creating your first template -1. **Templates** → **New template**. -2. Enter a name (e.g. "Product price tags"). -3. Pick a label format: - - **Predefined** — ready-made sizes (A4, Zebra 2×1″, etc.). - - **Custom size** — type width and height in mm and pick orientation. -4. Click **Create** — the editor opens. +A **template** is your label design — you build it once, then reuse it as many times as you like (e.g. to print 200 different products in one go). + +1. Click **Templates** in the left menu. +2. Click the **New template** button in the top-right corner. + +![the Templates page with the "New template" button in the top-right corner clearly highlighted.](screenshots/help/en/new-template-button.png) + +3. Type a name for the template, e.g. "Product price tags". +4. Pick a label format: + - **Predefined** — ready-made, common sizes (A4, Zebra 2×1″, etc.). Pick this if you're not sure exactly what size you need. + - **Custom size** — type width and height in millimetres and pick an orientation (portrait/landscape). Pick this if your labels are a non-standard size. + +![the "New template" dialog with the name field filled in and the choice between "Predefined" and "Custom size" visible; with "Custom size" selected, the width/height (mm) fields are shown.](screenshots/help/en/new-template-dialog.png) + +5. Click **Create**. The editor opens — you can start designing your label right away. --- @@ -38,13 +55,13 @@ After signing in you land on the **Dashboard**. It's just a welcome screen — t | Item | What's there | |---|---| | **Dashboard** | Welcome screen. | -| **Templates** | Your templates organised in folders + the *New template* and *Import* buttons. | -| **Library** | Ready-made starter projects + templates shared by other users (section 2a). | -| **Devices** | Print connectors and the Inbox of captured labels (section 7a). | +| **Templates** | Your templates organised in folders, plus the *New template* and *Import* buttons. | +| **Library** | Ready-made starter projects plus templates shared by other users (section 2a). | +| **Devices** | Print connectors and the inbox of captured labels (section 7a). | | **Help** | This guide + the FAQ, without leaving the app. | | **Administration → Users** | (admin only) account management. | -In the header, on the right: your email, the **PL/EN** language switcher and the **Log out** button. +In the header, on the right, you'll find: your email, the **PL/EN** language switcher, and the **Log out** button. ![Templates list](screenshots/help/en/templates.png) @@ -52,26 +69,28 @@ In the header, on the right: your email, the **PL/EN** language switcher and the ### The editor — screen layout -When you open a template you'll see: +When you open a template you'll see five areas of the screen: -- **Toolbar (top)** — Save, Undo/Redo, autosave indicator, **Generate series**, **⬇ Export** (template file), **📐 label size**, **⤓ Import ZPL**, **⤒ ZPL** (export), **Download PDF**. -- **Left panel (Add)** — buttons for inserting objects onto the label. -- **Canvas (center)** — your label at 1:1 scale (millimetres). -- **Alignment bar (above canvas)** — alignment and z-order controls. -- **Right panel (Properties)** — settings for the selected object. +- **Toolbar (top)** — Save, Undo/Redo, an autosave indicator, **Generate series**, **⬇ Export** (template file), **📐 label size**, **⤓ Import ZPL**, **⤒ ZPL** and **⤒ TSPL** (export for label printers — more in section 7a), **Download PDF**. +- **Left panel ("Add")** — buttons for inserting objects onto the label (text, barcode, image, etc.). +- **Canvas (center)** — your label at 1:1 scale, in millimetres — what you see corresponds exactly to the printed size. +- **Alignment bar (above canvas)** — aligning objects and changing their stacking order. +- **Right panel ("Properties")** — settings for whichever object you've just selected. ![The editor — overview](screenshots/help/en/editor-overview.png) *frame: the whole editor with a template open; arrows labelling the toolbar, the Add panel, the canvas, the alignment bar and the Properties panel.* -### Alignment bar — what each group does +### Alignment bar — what each button group does + +- **Page** — aligns the selected object to a page edge or the page center. +- **Selection** — aligns objects relative to each other (needs at least 2 selected). +- **Layer** — changes which object sits in front and which sits behind (more in section 4). +- **Distribute** — equal spacing between objects, horizontally or vertically (needs at least 3 selected). -- **Page** — aligns objects to a page edge or the page center. -- **Selection** — aligns objects relative to each other (needs ≥2 selected). -- **Layer** — moves selected objects forward/back in the z-stack. -- **Distribute** (3+ objects) — equal spacing horizontally/vertically. +![close-up of the alignment bar above the canvas with the four button groups — Page, Selection, Layer, Distribute — each labelled with an arrow.](screenshots/help/en/alignment-bar-groups.png) -Hover any icon for a tooltip. +Every icon has a tooltip — hover over it with your mouse to see what it does. --- @@ -79,72 +98,133 @@ Hover any icon for a tooltip. ### Folders — keeping your own templates tidy -On the **Templates** page there's a folder rail on the left: **All**, your folders (with counters) and **No folder**. Folders are **private** — every user has their own. +On the **Templates** page, on the left, there's a folder rail: **All**, your folders (with a count of templates in each), and **No folder**. Folders are **private** — every user only sees their own. + +![the folder rail on the left side of the Templates page — "All", two example folders with coloured dots and counters, "No folder", and the "New folder" button at the bottom.](screenshots/help/en/folder-rail.png) + +1. To create a new folder, click **New folder** at the bottom of the rail. +2. To move a template into a folder: hover over its card, click the **⚙** icon, pick a folder from the list, and click **Save**. + +![a template card with its ⚙ menu open, showing the list of folders to choose from and a "Save" button.](screenshots/help/en/folder-menu.png) -- **New folder** — button at the bottom of the rail. -- **Moving templates**: hover a template card → **⚙** → pick a folder → Save. -- **Edit folder (✎)** — rename and pick a **folder color** (8-color palette): a colored dot appears next to the folder in the rail and on its template cards. -- **Delete (✕)** — **does not delete the templates**, they go back to "No folder". +3. To rename a folder or change its colour, click the **✎** icon next to it. You can pick from 8 colours — a coloured dot then appears next to the folder in the rail and on its template cards. + +![the folder edit (✎) dialog with a name field and an 8-colour swatch picker.](screenshots/help/en/folder-edit.png) + +4. Deleting a folder (the **✕** icon) **does not delete its templates** — they simply move back to "No folder". ### The Library — ready-made projects and templates from others The **Library** menu item has two sections: -- **Ready-made projects** — built-in starters (product label with EAN and date, shipping address, shelf price, best-before date, warehouse label with QR, asset sticker). -- **From users** — templates that others have shared (you can see the author). +- **Ready-made projects** — built-in starters: a product label with an EAN code and a date, a shipping address, a shelf price, a best-before date, a warehouse label with a QR code, an asset sticker. +- **From users** — templates that other users have shared (you can see who the author is). -The **"Use"** button always creates **your own copy** and opens it in the editor — you can't break the original. +![the Library page with two sections — "Ready-made projects" at the top and "From users" below — each tile with a "Use" button.](screenshots/help/en/library-page.png) + +The **"Use"** button always creates **your own copy** and opens it in the editor right away — you can't accidentally break the original. ### Sharing your own template -Templates → hover a card → **⚙** → tick **"Share in the Library"**. In the same dialog you can also upload a **featured image** — a preview picture shown on the list card and in the Library. From then on every logged-in user can see it in the Library and clone it; **only you can edit it**. A shared template shows a 📚 icon in the list. Untick to withdraw it from the Library. +Want colleagues to be able to reuse your template? Share it in the Library: + +1. On the **Templates** page, hover over the template's card and click **⚙**. +2. Tick **"Share in the Library"**. +3. Optionally upload a **featured image** — a preview picture that will show up on the list card and in the Library. + +![the ⚙ menu of a template card with the "Share in the Library" checkbox ticked and the featured-image upload control visible.](screenshots/help/en/share-template.png) + +From that moment every logged-in user can see the template in the Library and clone it — but **only you can edit the original**. A shared template shows a 📚 icon in the list. Untick the box to withdraw it from the Library. --- ## 3. Building a label — object guide -Everything below lives in the **left panel**, *Add* section. +Everything below lives in the **left panel**, in the *Add* section. Each button inserts a different type of **object** — an element you can freely move and edit on the label. ### T — Text -**What it does:** A single line of fixed-size text. -**When to use:** Headers, short labels, fixed strings. -**How:** Click **T Text**, then select it on the canvas and edit content in the right panel. +**What it does:** Inserts a single line of fixed-size text — it does not wrap if the text is too long. +**When to use it:** Headers, short labels, fixed information. +**How:** + +1. Click **T Text** in the left panel. +2. Select the new object on the canvas. +3. Type the content into the field in the right panel. + +![canvas with a Text object selected; right panel showing the Content field with sample text.](screenshots/help/en/object-text.png) ### ¶ — Text block -**What it does:** Multi-line text that wraps inside a frame; optional *auto-fit* scales the font up/down to fit. -**When to use:** Variable-length product descriptions (perfect for `{{description}}` from a spreadsheet). -**How:** Click **¶ Text block**. In the right panel tick **Auto-fit font** and set min/max size. +**What it does:** Inserts multi-line text that automatically wraps inside a frame. You can also turn on **auto-fit** — the app will shrink or grow the font on its own so the text fits the frame. +**When to use it:** Product descriptions whose length varies — great together with `{{description}}` pulled from a spreadsheet (see section 6). +**How:** + +1. Click **¶ Text block**. +2. In the right panel, tick **Auto-fit** and set a minimum and maximum font size. + +![canvas with a Text block object selected; right panel showing the Auto-fit checkbox ticked and the min/max font size fields.](screenshots/help/en/object-textblock.png) ### ▭ — Rectangle, ╱ — Line -**What it does:** Helper geometry (frames, separators). -**How:** Click → drag on the canvas to size; set fill/stroke in the right panel. +**What it does:** Adds simple shapes — frames, separators, dividing lines. +**How:** + +1. Click ▭ or ╱. +2. Drag on the canvas to set its size. +3. In the right panel, set the fill and stroke colour. + +![canvas with a drawn rectangle and a line; right panel showing fill and stroke colour pickers.](screenshots/help/en/object-shapes.png) ### ▤ — Barcode -**What it does:** Renders a barcode from the value you give it. -**When to use:** Any product catalog with codes. -**How:** Click **▤ Barcode**, in the right panel pick the type (EAN-13, Code128, etc.) and enter the data. You can use `{{sku}}` to pull the value from a spreadsheet column. +**What it does:** Generates a **barcode** — a scannable pattern that represents a value, such as a product number — from the data you provide. +**When to use it:** Any product catalog that has codes. +**How:** + +1. Click **▤ Barcode**. +2. In the right panel, pick the barcode type (EAN-13, Code128, etc.). +3. Enter the data — or type `{{sku}}` to have the value pulled automatically from a spreadsheet (see section 6). + +![canvas with a Barcode object selected; right panel showing the barcode type list (EAN-13, Code128) and a Data field with an example value.](screenshots/help/en/object-barcode.png) ### ▦ — Table -**What it does:** A rows×columns grid with text in each cell — for property–value labels, nutrition facts, a mini list of items. -**How:** Click **▦ Table**. In the right panel set the number of rows/columns, type the cell contents (you can use `{{column}}` and `{{date+x}}` — chips appear below the grid), set the column widths in mm, the font and the border. Tick **Bold header** to emphasise the first row. -**Printing:** the table renders natively in the PDF and is emitted as native ZPL (`^GB` frame + cell text). Note: table rotation is not supported in ZPL (it exports without rotation). +**What it does:** Inserts a grid of rows and columns with text in each cell — useful for property–value labels, nutrition-fact tables, or a short list of items. +**How:** + +1. Click **▦ Table**. +2. In the right panel, set the number of rows and columns. +3. Type the content of each cell — you can use `{{column}}` placeholders and `{{date+x}}` dates (see sections 6 and 7); coloured chips previewing them appear below the grid. +4. Set the column widths (in mm), the font, and the border. +5. Tick **Bold header** if you want to emphasise the first row. + +![canvas with a Table object selected; right panel showing row/column count fields, a cell being edited that contains `{{column}}`, and the Bold header checkbox ticked.](screenshots/help/en/object-table.png) + +**Good to know about printing:** the table prints correctly both in the PDF and in a ZPL export (see section 7a). One limitation: a rotated table is not supported in ZPL — it exports without rotation. ### 🖼 — Image -**What it does:** Uploads a PNG/JPG/SVG and places it on the canvas. Prints in the PDF. -**When to use:** Logos, illustrations, icons, product photos. -**How:** Click **🖼 Image** → pick a file. Up to 5 MB. +**What it does:** Uploads an image file (PNG, JPG or SVG) and places it on the canvas. It prints normally in the PDF. +**When to use it:** A company logo, icons, product photos. +**How:** + +1. Click **🖼 Image**. +2. Pick a file from your computer (up to 5 MB). + +![canvas with an uploaded logo as an Image object; right panel showing basic file info.](screenshots/help/en/object-image.png) ### 🌄 — Background (reference) -**What it does:** Uploads an image as a **locked, full-canvas background** that's **visible in the editor only and is NOT printed in the PDF**. -**When to use:** Your labels arrived from the print shop with a logo already pre-printed. You scan a sample, upload it as background, position the new text against it, and generate the PDF — the printer overlays only the new text, the logo isn't double-printed. -**How:** Click **🌄 Background**, pick a file. The background drops to the bottom of the stack, locked (no handles). To change: select it, then in the right panel uncheck **Lock position** or check **Print in PDF**. +**What it does:** Uploads an image as a **locked, full-canvas background** — visible in the editor only, as a visual guide. The background **does not print** in the PDF. +**When to use it:** Your labels came from the print shop with a logo already pre-printed. You scan a sample of that label, upload it as the background, position your new text exactly where it should go, and generate the PDF — the printer overlays only the new text, and the logo doesn't get printed twice. +**How:** + +1. Click **🌄 Background**. +2. Pick a file. The background drops to the very bottom of the object stack and is locked — it has no handles for moving. +3. To change it: select it, then in the right panel uncheck **Lock position**, or check **Print in PDF** if you actually want it to print after all. + +![canvas with a Background image filling the entire label, looking locked/dimmed; right panel showing the Lock position and Print in PDF checkboxes near the top.](screenshots/help/en/object-background.png) --- @@ -152,123 +232,167 @@ Everything below lives in the **left panel**, *Add* section. ### Selecting -- Single click = select one. -- **Shift + click** = add to selection (multi-select). -- **Ctrl/Cmd + A** = select everything. +- Single click = select one object. +- **Shift + click** = add another object to the selection (multi-select — selecting several at once). +- **Ctrl/Cmd + A** = select every object on the label. + +![canvas with three objects selected at the same time (blue selection outlines), demonstrating multi-select via Shift+click.](screenshots/help/en/multiselect.png) ### Moving and resizing -- Drag the selected object with the mouse. -- Corner handles = resize; the handle above the object = rotate. -- A **locked** object has no handles — but you can still click it to unlock from the right panel. +- Drag the selected object with the mouse to move it. +- The corner handles resize it; the handle above the object rotates it. +- A **locked** object has no handles — but you can still select it and unlock it from the right panel. + +![a selected object on the canvas with visible corner resize handles and a rotate handle above it.](screenshots/help/en/resize-handles.png) -### Undo / redo +### Undoing changes -- **Ctrl/Cmd + Z** = undo. -- **Ctrl/Cmd + Shift + Z** or **Ctrl/Cmd + Y** = redo. +- **Ctrl/Cmd + Z** = undo the last change. +- **Ctrl/Cmd + Shift + Z** or **Ctrl/Cmd + Y** = redo an undone change. -One operation = one history step (e.g. aligning 5 objects undoes in a single Ctrl+Z). +One operation is one history step — e.g. aligning 5 objects at once undoes with a single Ctrl+Z. -### Duplicating +![close-up of the Undo and Redo buttons in the toolbar.](screenshots/help/en/undo-redo-buttons.png) -Two quick ways to clone the selected object (or a whole multi-selection): +### Duplicating (making copies) -- **Alt + drag** — hold **Alt** (or **Option** on Mac) and drag a selected object. The original stays put; a clone lands wherever you release. With multi-select the clones keep their relative positions — drag one of three selected items and you get three clones offset together at the drop point. -- **Ctrl/Cmd + D** — duplicate in place with a small offset (+5 mm right and down). Selection jumps to the clones, so a follow-up Ctrl+D stacks neatly down-right. +Two quick ways to copy the selected object (or a whole multi-selection): -The clone inherits everything: font, colour, rotation, the *Lock* / *Print in PDF* flags. Images share the same Asset (one binary → many objects). A multi-select duplication is one Ctrl+Z away. +- **Alt + drag** — hold **Alt** (on Mac: **Option**) and drag a selected object. The original stays in place; the copy lands wherever you release the mouse. +- **Ctrl/Cmd + D** — creates a copy "in place", offset by 5 mm right and down. The selection jumps to the new copy right away, so a follow-up Ctrl+D builds a staircase of copies. -### Layer order (z-order) +![canvas mid-drag with Alt held down — the original object visible at its starting position and a copy being created under the cursor.](screenshots/help/en/duplicate-altdrag.png) + +The copy inherits everything: font, colour, rotation, the *Lock* and *Print in PDF* settings. Images share the same source file, so they don't take up extra space. + +### Layer order (what's on top) In the **alignment bar**, **Layer** group: -- ⤓ **Send to back** — push selected under everything else. -- ↓ **Send backward** — move down by one neighbour. -- ↑ **Bring forward** — move up by one neighbour. -- ⤒ **Bring to front** — over everything. +- ⤓ **Send to back** — pushes the selected object under everything else. +- ↓ **Send backward** — moves it down by one position. +- ↑ **Bring forward** — moves it up by one position. +- ⤒ **Bring to front** — puts the object above everything else. + +![close-up of the Layer group in the alignment bar with the four icons each labelled by an arrow.](screenshots/help/en/layer-buttons.png) -Multi-select keeps the relative order of the selected items. +### Lock and printing (right panel) -### Lock + Print in PDF (right panel) +At the very top of the right panel, every object has two checkboxes: -Every object's right panel has two checkboxes at the top: +- **🔒 Lock position** — disables dragging and resizing (you can still edit its font, colour, etc.). +- **🖨 Print in PDF** — checked by default. If you uncheck it, the object stays visible only in the editor and won't appear in the generated PDF. Such objects appear faded on the canvas, so you can spot at a glance that they won't print. -- **🔒 Lock position** — disables drag and resize (you can still edit font, colour, etc.). -- **🖨 Print in PDF** — checked by default. Unchecked = visible only in the editor; the renderer skips it in the PDF. Non-printable objects appear at 50% opacity so you spot them at a glance. +![top of the right panel showing the 🔒 Lock position and 🖨 Print in PDF checkboxes; Print in PDF unchecked, with the matching object faded on the canvas.](screenshots/help/en/lock-print-checkboxes.png) ### Autosave -The editor saves every few seconds on its own. Status sits in the toolbar: +The editor saves your work on its own every few seconds. The status shows in the toolbar: + - **Unsaved changes** — something is pending. -- **Autosaving…** — sending now. +- **Autosaving…** — currently sending. - **Autosaved at 12:34** — last successful save. -You can also click **Save** manually. +![close-up of the autosave status area in the toolbar, showing the three states in sequence: "Unsaved changes", "Autosaving…", "Autosaved at 12:34".](screenshots/help/en/autosave-status.png) + +You can also click **Save** manually at any time. ### Version history -Every **manual** save (the **Save** button or **Ctrl+S**) creates a template version. Autosave overwrites the current state and does **not** clutter the history. The **🕘 History** toolbar button lists the versions (number, date, author) — click **Restore** to go back to the one you want. Restoring saves the current state as a new version ("restored from vN"), so it is reversible. The app keeps the last 30 versions per template. +Every **manual** save (the **Save** button or **Ctrl+S**) creates a new **version** of the template — a snapshot of how it looked at that moment. Autosave overwrites the current state and does not create extra versions, which keeps the list short and easy to read. + +1. Click **🕘 History** in the toolbar. +2. You'll see a list of versions: number, date and author. +3. Click **Restore** next to the one you want to go back to. + +![the History panel open, listing a few versions (number, date, author) with a Restore button next to one of them.](screenshots/help/en/version-history.png) + +Restoring saves the current state as a new version, so nothing is ever lost for good — the app keeps the last 30 versions per template. ### Changing the label size -The size you picked when creating the template **can be changed at any time**: click the **📐 {width}×{height}** button in the toolbar. +The size you picked when creating the template **can be changed at any time**. -- Type a new width and height in mm (1–1000), or click one of the ready-made presets (40×100, 50×30, 100×150, 105×148, 210×297). -- Objects are **not rescaled** — they keep their positions in mm. If you shrink the label, just drag any elements that ended up past the edge back in. +1. Click the **📐 {width}×{height}** button in the toolbar. +2. Type a new width and height in mm (1 to 1000), or click one of the ready-made presets (40×100, 50×30, 100×150, 105×148, 210×297). +3. Click **Apply**. ![The "Label size" dialog](screenshots/help/en/label-size.png) *frame: the modal with the Width/Height fields and the row of preset chips; cursor hovering the "Apply" button.* +Objects are **not rescaled** — they keep their positions in millimetres. If you shrink the label, just drag back in any elements that ended up past the new edge. + --- ## 5. Downloading a PDF — single label -Want to see the result first? Click **👁 Preview** — the PDF appears embedded in the app with a **Download PDF** button. Or click **Download PDF** in the editor toolbar directly. Rendering is synchronous (a few seconds), then the PDF downloads automatically. +Want to see the result first? Click **👁 Preview** — the PDF shows up embedded in the app together with a **Download PDF** button. -If any text didn't fit its block, you'll see an **N warnings** chip — hover it for details. +![the in-app PDF preview window with the rendered label and a Download PDF button underneath.](screenshots/help/en/preview-pdf.png) -Column placeholders (`{{name}}`) stay as literal text in a single PDF — data only gets substituted during series generation. **Date placeholders** (`{{date+14d}}`, see section 7), on the other hand, are calculated here too. +Or click **Download PDF** in the toolbar directly — the file starts downloading after a few seconds. + +If any text didn't fit its block, you'll see an **"N warnings"** chip in the toolbar. Hover over it to see the details. + +![toolbar showing the "N warnings" chip with an open tooltip listing the details of the clipped text.](screenshots/help/en/warnings-chip.png) + +Note: column placeholders (`{{name}}` — a placeholder, meaning a spot where the app will automatically insert data from your spreadsheet) stay as plain text in a single PDF — the real data is only substituted during **series generation** (section 6). Date placeholders (`{{date+14d}}`, section 7), on the other hand, are calculated right away, here too. --- ## 6. Generating a series — many labels from one template -This is the app's flagship feature. It lets you generate, say, 200 labels from one template, where each gets different data from a spreadsheet or database. +This is the app's **flagship feature**. It lets you generate, say, 200 labels from one template, where each one gets different data — a different product name and a different barcode, for instance — pulled from a spreadsheet or a database. ### Step 0 — prep the template -In a Text or Barcode object, insert a placeholder shaped like `{{column_name}}`, e.g.: +In a Text or Barcode object, insert a **placeholder** — a spot where the app will automatically insert data from your spreadsheet — shaped like `{{column_name}}`, e.g.: + - Text: `{{name}}` - Barcode data: `{{sku}}` -Each occurrence will be replaced with the value from the matching column. +Every occurrence gets replaced with the value from the matching column. ![Detected dynamic fields](screenshots/help/en/dynamic-fields.png) *frame: the right Properties panel with a text field containing `{{name}}` and `{{date+14d}}`; below it two chips — a purple `{{name}}` and a green `{{date+14d}} → 18.07.2026`.* -### Step 1 — Upload data +### Step 1 — upload your data + +1. Click **Generate Series** in the toolbar. +2. In Step 1, pick the file with your data. -Toolbar → **Generate Series** → Step 1 (Upload data). +![Step 1 of the Generate Series wizard with a file picker/drop area and a table of accepted formats.](screenshots/help/en/series-step1-upload.png) Accepted formats: -| Format | Max size | Max rows | +| Format | Max file size | Max rows | |---|---|---| | `.csv` | 10 MB | 1,000 | | `.xls` / `.xlsx` | 10 MB | 1,000 | | `.db` / `.sqlite` / `.sqlite3` | 50 MB | 1,000 (per query) | -#### CSV / Excel +#### If you upload CSV or Excel + +The file uploads to the server and is read right away. You'll see the detected columns and the row count. -The file uploads and is parsed immediately. You'll see the columns and row count. Click **Next**. +![preview after uploading a CSV file — the detected columns shown as a small table, the row count, and a "Next" button.](screenshots/help/en/series-csv-preview.png) -#### SQLite +Click **Next**. -After upload the app shows a **table list** (sorted with most-rows-first). Pick a table that has data and click **Use this source**. +#### If you upload SQLite (a database file) -If you need SQL-level filtering (e.g. only products in a specific category, or a JOIN across tables), expand **Show advanced** and write a SELECT, e.g.: +**SQLite** is a file that stores a database — if someone at work exports data from a warehouse system into a file like this, you can use it directly, without converting it to CSV first. + +1. After the upload, the app shows a **list of tables** in the database, sorted so the tables with the most rows appear at the top. +2. Pick the table that has the data you need. +3. Click **Use this source**. + +![the list of tables after uploading a SQLite file, sorted by row count, with a "Use this source" button on one of the rows.](screenshots/help/en/series-sqlite-tables.png) + +If you need a narrower selection of data (e.g. only products in one category), expand **Show advanced** and write a SELECT query — a database-language command that tells the app exactly what data to fetch, e.g.: ```sql SELECT sku, name, price @@ -276,33 +400,46 @@ FROM products WHERE category = 'labels' AND price > 0 ``` -**Security:** The connection is read-only. Only a single SELECT is accepted — `INSERT`, `UPDATE`, `DELETE`, `DROP`, `ATTACH`, `PRAGMA` are blocked. Up to 1,000 result rows — anything larger is rejected with a request to add `WHERE`/`LIMIT`. - -### Step 2 — Map fields +![the expanded "Show advanced" panel with a SQL query typed into a text box and a "Use this source" button.](screenshots/help/en/series-sqlite-sql.png) -The app detects every `{{...}}` placeholder in the template. If the placeholder name matches a column name exactly, it auto-maps. Otherwise, pick the column manually. +**Data safety:** the database connection is read-only. The app only accepts data-reading commands (SELECT) — nothing that could change or delete anything will ever run. The result can contain at most 1,000 rows. -### Step 3 — Filter (optional) +### Step 2 — map the fields -Drop rows before generating, e.g. *price > 10* or *category contains "tea"*. Click **Test filter** to see how many rows match. Skip this step to keep all rows. +The app automatically detects every `{{...}}` placeholder in your template. If a placeholder's name matches a column name exactly, the mapping is set automatically. If the names differ, pick the column manually from the list. ![Series wizard — mapping](screenshots/help/en/series-map.png) *frame: step 2 of the wizard with the placeholder list on the left and column selects on the right; next to `{{date}}` a green hint reading "Optional — today's date is used when unmapped".* -### Step 4 — Generate PDF +### Step 3 — filter (optional) + +If you don't want to print every row from the spreadsheet, you can filter them out — e.g. only products priced above 10, or only ones whose name contains "tea". + +1. Pick a column, a condition (e.g. "greater than"), and a value. +2. Click **Test filter** to see how many rows match. + +![Step 3 Filter screen with a column, condition and value selected (e.g. price > 10) and the result after clicking "Test filter" showing the number of matching rows.](screenshots/help/en/series-filter.png) -Click **Generate PDF**. A background job starts; a progress bar updates live. When it's done, the PDF downloads automatically. +You can also skip this step if you want to generate labels for every row. -If any labels had text overflowing their blocks, you'll see a warning list (which row, which object) — the PDF is still produced. +### Step 4 — generate the PDF + +Click **Generate PDF**. The app starts working in the background, and a progress bar shows how much is left. Once it's done, the PDF starts downloading automatically. + +![Step 4 with a progress bar mid-generation and a status message.](screenshots/help/en/series-progress.png) + +If some labels had text that didn't fit its block, you'll see a list of warnings — which rows, which objects. The PDF is still generated for every label regardless. + +![the warnings list shown after series generation, listing specific rows and objects with clipped text.](screenshots/help/en/series-warnings-list.png) --- ## 7. Date placeholders — `{{date+…}}` -Besides spreadsheet columns you can insert **dates calculated automatically at generation time** — perfect for best-before ("use by") and production dates. They work everywhere: in a single PDF, in a series and in the ZPL export. +Besides spreadsheet columns, you can insert **dates that are calculated automatically at the moment the label is generated** — perfect for best-before ("use by") dates and production dates. They work everywhere: in a single PDF, in a series, and in the ZPL export. -### Syntax +### How to write them | You type | You get (when generating on 04.07.2026) | |---|---| @@ -314,13 +451,13 @@ Besides spreadsheet columns you can insert **dates calculated automatically at g | `{{date+14d:DD/MM/YY}}` | 18/07/26 (custom format) | | `{{date+3m:YYYY-MM-DD}}` | 2026-10-04 | -- Offset units: **d** = days, **m** = months, **y** = years; both `+` and `-` work. -- The format (optional, after the colon) is built from the **DD**, **MM**, **YY**, **YYYY** building blocks — separators (dots, slashes, dashes, spaces) pass through unchanged. Without a format you get `DD.MM.YYYY`. -- Month ends are safe: 31 January + 1 month = 28/29 February (never "31 February"). +- Offset units: **d** = days, **m** = months, **y** = years. Both `+` and `-` work. +- The date format (optional, after a colon) is built from the **DD**, **MM**, **YY**, **YYYY** building blocks — separators (dots, slashes, dashes, spaces) pass through unchanged. Without a format you get `DD.MM.YYYY`. +- Month ends are handled safely: 31 January + 1 month gives 28 or 29 February — the app never invents a date that doesn't exist, like "31 February". ### How do you know it will work? -Once you type the placeholder, a **green chip previewing the calculated date** appears in the right panel (purple chips are regular spreadsheet columns). Hover the chip — a tooltip reminds you that the final value is calculated at generation time. +Once you type the placeholder, a **green chip previewing the calculated date** appears in the right panel (purple chips are regular spreadsheet columns). Hover over the chip — a tooltip reminds you the final value is calculated only at generation time. ![Green date chip](screenshots/help/en/date-chip.png) @@ -328,63 +465,121 @@ Once you type the placeholder, a **green chip previewing the calculated date** a ### Good to know -- A **column named `date`** in your spreadsheet wins for a bare `{{date}}` — forms with an offset (`{{date+14d}}`) are always calculated automatically. -- The date is calculated **at PDF/ZPL generation time**, using the server clock — not when you save the template. -- In the series wizard, date fields **don't need to be mapped** to a column. +- If your spreadsheet has a **column named `date`**, it wins for a bare `{{date}}`. Forms with an offset (`{{date+14d}}`) are always calculated automatically, regardless of any spreadsheet column. +- The date is calculated **at generation time** for the PDF or ZPL, using the server's clock — not when you save the template. +- In the series wizard, date fields **don't need to be mapped** to any column. --- -## 7a. ZPL — Zebra label printers +## 7a. Printing on label printers (ZPL and TSPL) -**ZPL** is the language of label printers (Zebra and compatibles). The app works in both directions: it can import an existing ZPL label into the editor and export your design as ZPL. +Labels printed on specialised label printers (like Zebra, TSC, Toshiba) don't use an ordinary PDF — they speak their own command language. The app can both **read** that language (import) and **write** it (export), so you don't need to learn it yourself. -### Importing ZPL +### ZPL — Zebra and compatible printers -Toolbar → **⤓ Import ZPL**. +**ZPL** is a special language that Zebra-brand label printers (and compatible models) speak. The app can import an existing label written in ZPL into the editor, and export your design as ZPL code. -1. Paste the ZPL code (e.g. from another system or from your label supplier). -2. Pick the **Printer DPI** — if you don't know it, leave **Auto-detect** (the app compares the dimensions in the code with your label size). -3. Click **Analyze** — you'll see the number of recognised objects and the detected DPI; if the label in the code is bigger than yours, you get a hint. -4. Click **Import** — the objects land on the canvas. **Careful:** the import replaces the current label content. +#### Importing ZPL -Printer variables in single braces (e.g. `{NAZWA}`) pass through untouched, and commands the editor doesn't model are preserved and come back on export. +1. Click **⤓ Import ZPL** in the toolbar. +2. Paste the ZPL code — e.g. one you received from a label supplier or another system. +3. Pick the **Printer DPI** — this is the print density, i.e. how many dots per millimetre the printer produces. If you don't know your printer's DPI, leave **Auto-detect** on — the app compares the dimensions in the code with your label size and works it out for you. +4. Click **Analyze** — you'll see the number of recognised objects and the detected DPI. If the label in the code is larger than yours, you'll get a hint. +5. Click **Import** — the objects land on the canvas. ![ZPL import dialog](screenshots/help/en/zpl-import.png) *frame: the modal with ZPL code pasted in, the DPI select set to "Auto-detect" and the analysis result "12 objects · 203 dpi".* -### Exporting ZPL +**Careful:** the import replaces the label's current content — if you've already designed something, make a copy first (section 8a). -Toolbar → **⤒ ZPL**. Two modes: +#### Exporting ZPL -- **Template (variables)** — one ZPL code built from your design; column placeholders `{{...}}` stay in the code (you substitute them in your own system), while **date placeholders are calculated right away**. **Copy** and **Download .zpl** buttons. -- **Batch (dataset)** — pick a previously uploaded data file and the app generates one `.zpl` file with a label for every row (columns and dates both substituted). +Click **⤒ ZPL** in the toolbar. You get two modes to choose from: -Pick the DPI that matches your printer (203 or 300). +- **Template (variables)** — one ZPL code built from your design. Column placeholders `{{...}}` stay in the code as text (you substitute them in your own system), while date placeholders are calculated right away. You get **Copy** and **Download .zpl** buttons. +- **Batch (dataset)** — pick a previously uploaded data file and the app generates one `.zpl` file with a separate label for every row (both columns and dates substituted). ![ZPL export dialog](screenshots/help/en/zpl-export.png) *frame: the modal in "Template (variables)" mode with a preview of the generated code and the Copy / Download .zpl buttons.* -### Direct printing — the connector +Pick the DPI that matches your printer (usually 203 or 300). + +### TSPL — TSC and Toshiba printers + +**TSPL** is the equivalent of ZPL for TSC and Toshiba-brand printers — a different dialect of the same idea: a command language the label printer understands. + +1. Click **⤒ TSPL** in the toolbar. +2. Pick the printer's DPI (203 or 300). +3. You'll see a live preview of the generated code. +4. Click **Copy** or **Download .txt**. + +![the "Export TSPL" dialog with a DPI choice (203/300), a live preview of the generated TSPL code, and Copy / Download .txt buttons.](screenshots/help/en/tspl-export.png) + +TSPL export only works for a single label (there's no Batch mode yet) and there isn't an import in the other direction yet — this feature is at an earlier stage than ZPL. + +### Easiest: the "Connect a printer" wizard + +Don't want to create a settings file or paste codes by hand? The app has a **wizard** that walks you through everything step by step — it downloads the right program for you, prepares a ready-made settings file, and gives you one command to copy. Go to **Devices** and click **🖨 Connect a printer**. + +1. **Pick your computer.** The wizard detects your system (Mac / Windows / Linux) — confirm with one click. + +![wizard step 1 — the question "Which computer is the printer connected to?" with Mac, Windows, Linux and Linux (ARM) tiles.](screenshots/help/en/connect-wizard-os.png) + +2. **Name this computer** (e.g. "Office computer") — it's just a label so you recognise it in the list. + +![wizard step 2 — the "Name this computer" field with an example name.](screenshots/help/en/connect-wizard-name.png) + +3. **Download two files** — the connector program and a ready-made settings file (the server address and your code are already filled in; nothing to edit). + +![wizard step 3 — two download buttons: the program and the settings file, with a note about keeping the key private.](screenshots/help/en/connect-wizard-download.png) + +4. **Run the program** — copy one ready command and paste it into Terminal (on a Mac the wizard hides the system-block removal inside it). Leave that window open. -Instead of downloading a `.zpl` file, you can print **straight from the editor** with the **🖨 Print** button: +![wizard step 4 — a box with the command to copy, a Copy button and a reminder to "leave the window open".](screenshots/help/en/connect-wizard-run.png) -1. On a computer on the same network as your printers, install the **blg-connector** agent (binary in the Assets of every GitHub release; configuration: `connector/README.md`). -2. In the app: **Devices → Add device** → copy the token into the agent's `config.yaml`. The device switches to **Online** and reports its list of printers. -3. In the editor: **🖨 Print** → pick the device, printer, number of copies and DPI → **Print**. The dialog shows the progress: *queued → agent picked it up → printed* (or an error with the reason). +5. **Wait for the connection** — the wizard detects when your computer checks in and shows "Connected". -**Fast path:** if the connector runs **on the same computer** as your browser, the dialog detects it automatically and shows a preselected **⚡ This computer — instant print** option — the label then goes straight to the printer, skipping the server round-trip. +![wizard step 5 — the message "Waiting for your computer to check in…" with a waiting indicator.](screenshots/help/en/connect-wizard-waiting.png) + +6. **Point to your printer (optional)** — enter the printer's IP address, or leave test mode (prints saved to a file) to check everything first. + +![wizard step 6 — "Where is your printer?" with an IP address field and a test-mode option.](screenshots/help/en/connect-wizard-printer.png) + +Once the computer shows as **Online**, you print from the editor exactly as described below. + +### Advanced: manual connector setup + +If you'd rather do it by hand (or you're automating many stations), you can print **straight from the editor** (this applies to ZPL — not TSPL) thanks to the **connector** — a small program installed on a computer on the same network as your printers, which links the app to the printer. + +1. Install the **blg-connector** agent on a computer connected to the network your printers are on (the download is in the Assets section of every GitHub release; setup instructions: `connector/README.md`). +2. In the app, go to **Devices → Add device**. + +![the "Add device" dialog with a generated token (access code) to copy into the agent's config.yaml file.](screenshots/help/en/connector-add-device.png) + +3. Copy the generated **token** (a unique access code) into the agent's `config.yaml` file. The device switches to **Online** and reports its list of connected printers. +4. In the editor, click **🖨 Print**, pick the device, the printer, the number of copies and the DPI, then click **Print**. + +![the print dialog in the editor with device, printer, copies and DPI selections, a "Print" button, and a visible progress bar (queued → picked up by agent → printed).](screenshots/help/en/connector-print-dialog.png) + +The dialog shows live progress: *queued → picked up by the agent → printed* (or an error with the reason). + +**Fast path:** if the connector is running **on the same computer** where your browser is open, the app detects it automatically and offers a **⚡ This computer — instant print** option. The label then goes straight to the printer, skipping the round trip through the server. + +![the print dialog with the "⚡ This computer — instant print" option pre-selected by default.](screenshots/help/en/connector-fastpath.png) Date placeholders are calculated at print time; column placeholders stay in the code (this prints a single label, not a series). ### Virtual printer — capture labels from other programs -The connector can also work **the other way round**: it pretends to be a network printer, and anything other applications (an ERP, Word, a legacy warehouse program) print to it lands in the **Inbox** on the **Devices** page. +The connector can also work **the other way round**: it pretends to be an ordinary network printer, and anything other applications (a warehouse system, Word, a legacy program) print to it lands in the **Inbox** on the **Devices** page. -1. In the agent's `config.yaml`, enable the `capture` section (step-by-step instructions, including the Windows printer setup: `connector/README.md`). +1. In the agent's `config.yaml`, enable the `capture` section (step-by-step instructions, together with the Windows printer setup, are in `connector/README.md`). 2. Print something from any application to that virtual printer. -3. **Devices → Inbox** → **Open in editor** — the label becomes a regular template: size detected from the code, texts and barcodes editable. +3. Go to **Devices → Inbox** and click **Open in editor** — the label becomes a regular template: size detected from the code, texts and barcodes editable right away. + +![the Devices → Inbox page with a list of a few captured labels (thumbnails, timestamps) and an "Open in editor" button.](screenshots/help/en/devices-inbox.png) From the Inbox you can also copy the raw ZPL code or delete an entry. The app keeps at most the 200 most recent captures per device. @@ -392,7 +587,11 @@ From the Inbox you can also copy the raw ZPL code or delete an entry. The app ke ## 7c. Generated-file history -Menu → **History**. Every generation — a single label (**Download PDF**) and a series (PDF or batch ZPL) — is added to a list: template name, type, label count, size, date. Click **Download** to fetch the file again, or **Delete** to remove the entry. Files stay available for **30 days**, after which they are deleted automatically. +Click **History** in the menu. Every generation — a single label (**Download PDF**) and a whole series (PDF or batch ZPL) — is added to a list: template name, type, label count, size, date. + +![the History page with a list of generated files — columns for template name, type, label count, size, date — and Download and Delete buttons on each row.](screenshots/help/en/generated-history.png) + +Click **Download** to fetch the file again without regenerating it, or **Delete** to remove the entry. Files stay available for **30 days**, after which they are deleted automatically. --- @@ -407,60 +606,78 @@ Left menu → **Administration → Users**. ### Creating a user 1. Click **Create account**. -2. Enter email + temporary password (minimum 10 characters; you can generate a random one). +2. Enter an email and a temporary password (minimum 10 characters; you can also generate a random one). 3. Pick a role: - - **Administrator** — full access plus user management. - - **Editor** — creates/edits their own templates and datasets. - - **Viewer** — can open and view, but doesn't save. -4. After clicking *Create*, the temporary password is shown **once** — pass it to the user. + - **Administrator** — full access, including user management. + - **Editor** — creates and edits their own templates and datasets. + - **Viewer** — can open and view, but doesn't save changes. + +![the "Create account" dialog with Email, temporary password (with a "Generate" button), and a Role dropdown (Administrator/Editor/Viewer).](screenshots/help/en/admin-create-user.png) + +4. After clicking **Create**, the app shows the temporary password **once** — pass it to the new user right away. ### Resetting a password -Click **Reset password** next to the account → generate a new temporary one → hand it over. The user is forced to change it on next login. +1. Click **Reset password** next to the user's account. +2. The app generates a new temporary password — hand it over to the user. + +![a user row with "Reset password" clicked, and a dialog showing the newly generated temporary password, displayed only once.](screenshots/help/en/admin-reset-password.png) + +The user will be asked to set their own password at their next login. -### Activating / deactivating +### Activating / deactivating an account -Toggle **Active** on the row. You cannot deactivate your own account (safety check). +The **Active** toggle in a user's row switches account access on or off. You can't deactivate your own account — a safeguard against accidentally locking yourself out. + +![close-up of the "Active" toggle in a user row, switched on (green); for the current user's own row the toggle is greyed out/disabled.](screenshots/help/en/admin-active-toggle.png) --- ## 8a. Importing / exporting templates -You can save any template as a single `.blg-template.json` file (label size + every object's position + content + bundled images). The file is portable: archive it, mail it, or import it into another BarcodeLabelGen instance. +You can save any template as a single `.blg-template.json` file — it contains the label size, every object's position and content, and images encoded right inside the file. A file like this is portable: archive it, mail it, or import it into another BarcodeLabelGen installation. ### Export -Two entry points: -- **Templates** → hover a template tile → **⬇** icon in the bottom-right corner. -- Editor toolbar → **⬇ Export** button (next to *Download PDF*). +You have two options: + +- On the **Templates** page: hover over a template's card and click the **⬇** icon in the bottom-right corner. +- In the editor: click **⬇ Export** in the toolbar (next to *Download PDF*). + +![close-up of the ⬇ Export button in the editor toolbar, next to the Download PDF button.](screenshots/help/en/editor-export-button.png) -You get a `.blg-template.json` — keep it somewhere safe as a backup. +This downloads a `.blg-template.json` file — keep it in a backup folder for safety. ### Import -**Templates** → **⬆ Import** opens a 2-step modal: +On the **Templates** page, click **⬆ Import** — a 2-step dialog opens: + +1. **Pick a file** — choose your `.blg-template.json` file. The app validates it and shows a preview. + +![Step 1 of the import dialog with a .blg-template.json file selected, a validity checkmark, and a content preview.](screenshots/help/en/import-step1.png) + +2. **Configure** — here you can: + - change the new template's name (default is the name from the file; on a naming clash the app adds a "(copy)" suffix), + - override the label size (leave blank to keep the original), + - uncheck objects you don't want to import (a checklist with a type icon and a content preview for each one), + - for every duplicate image, choose: **Reuse existing** (saves space) or **Create new copy**. -1. **Pick a file** — choose your `.blg-template.json`. The app validates it and shows a preview. -2. **Configure** — you can: - - change the **name** of the new template (default is the name from the file; on collision a "(kopia)" suffix is appended automatically), - - **override the size** (leave blank = keep original), - - **uncheck objects** you don't want to bring in (checklist with type icons + short content preview), - - for every **duplicate image** decide: *Reuse existing* (save space) or *Create new copy*. +![Step 2 of the import dialog with a name field, width/height override fields, an object checklist with checkboxes, and the Reuse existing / Create new copy choice for a duplicate image.](screenshots/help/en/import-step2.png) -Click **Import** → a new template is created and opens in the editor. +3. Click **Import** — the app creates a new template and opens it in the editor. -### Typical workflows +### Typical situations where this comes in handy -- **Backup before a big change** — export, archive the file, edit freely. Something broke → re-import. -- **Clone a layout to a different size** — export, import with size override (e.g. same label for A6 and 100×50 mm). -- **Move a template between instances** (dev → prod) — export on one side, import on the other. -- **Partial import** — take the barcode block + a couple of fields from a finished template and uncheck the rest. +- **Backup before a big change** — export it, keep the file archived, edit freely. If something goes wrong, import it back. +- **Cloning a layout to a different size** — export, then import with a size override (e.g. the same label design for A6 and for 100×50 mm). +- **Moving a template between installations** (e.g. test → production) — export on one side, import on the other. +- **Partial import** — take just the barcode layout and 2-3 fields from a finished template and uncheck the rest. ### Limits and safety -- Max 20 MB file, 50 objects, 20 images (5 MB each). -- Images are integrity-checked: sha256 must match the base64 payload. Tampered files are rejected. -- The new template always belongs to your account, regardless of who exported the file. +- Up to 20 MB per file, 50 objects, 20 images (5 MB each). +- Images are checked with a special checksum (**SHA-256** — a digital "fingerprint" of the file that confirms it hasn't been altered). Files someone has manually modified are rejected. +- The new template always lands in your own account, no matter who exported the file. ## 9. Keyboard shortcuts diff --git a/docs/HELP.pl.md b/docs/HELP.pl.md index e8a9907..ee394aa 100644 --- a/docs/HELP.pl.md +++ b/docs/HELP.pl.md @@ -1,6 +1,8 @@ # BarcodeLabelGen — Pomoc -Krótki przewodnik po programie. Czytaj sekcjami, w kolejności, albo skacz od razu do interesującego cię feature'u. +Ten przewodnik pomoże Ci poznać program krok po kroku — nawet jeśli nigdy wcześniej nie używałaś/eś podobnego narzędzia. Możesz czytać po kolei od góry albo od razu przejść do sekcji, która Cię interesuje. + +Jeśli czegoś tu zabraknie, zajrzyj też do [FAQ](FAQ.pl.md) albo napisz na **dev@attv.uk**. --- @@ -8,26 +10,41 @@ Krótki przewodnik po programie. Czytaj sekcjami, w kolejności, albo skacz od r ### Logowanie -1. Otwórz adres aplikacji w przeglądarce. -2. Wpisz email i hasło, które dał ci administrator. -3. Przy pierwszym logowaniu program poprosi cię o ustawienie własnego hasła (min. 10 znaków). To jednorazowe — kolejne logowania od razu pokazują panel. +1. Otwórz adres aplikacji w przeglądarce (dostajesz go od administratora). +2. Wpisz swój email i hasło, które dał Ci administrator. +3. Kliknij **Zaloguj**. ![Ekran logowania](screenshots/help/pl/login.png) *kadr: formularz Email + Hasło z przyciskiem „Zaloguj" i przełącznikiem języka PL/EN w prawym górnym rogu.* -### Pulpit +4. Jeśli logujesz się **pierwszy raz**, program poprosi Cię o ustawienie własnego hasła (minimum 10 znaków). To zdarza się tylko raz — przy kolejnych logowaniach od razu zobaczysz swój panel. + +![ekran wymuszonej zmiany hasła po pierwszym logowaniu — dwa pola (nowe hasło / powtórz hasło), informacja „minimum 10 znaków" i przycisk „Ustaw hasło".](screenshots/help/pl/set-new-password.png) + +### Pulpit — ekran startowy + +Po zalogowaniu widzisz **Pulpit**. To tylko ekran powitalny — nie musisz tu nic ustawiać. Żeby zacząć pracę, kliknij **Szablony** w menu po lewej stronie. -Po zalogowaniu trafiasz na **Pulpit**. To tylko ekran powitalny — żeby zacząć pracę kliknij **Szablony** w lewym menu. +![Pulpit tuż po zalogowaniu z lewym menu bocznym; pozycja „Szablony" w menu wyróżniona strzałką jako miejsce, w które trzeba kliknąć.](screenshots/help/pl/dashboard-empty.png) ### Tworzenie pierwszego szablonu -1. **Szablony** → **Nowy szablon**. -2. Wpisz nazwę (np. "Cennik produktów"). -3. Wybierz format etykiety: - - **Predefiniowane** — gotowe rozmiary (A4, Zebra 2×1″ itd.). - - **Własny rozmiar** — wpisz szerokość i wysokość w mm i wybierz orientację. -4. Klik **Utwórz** — otwiera się edytor. +**Szablon** to Twój projekt etykiety — robisz go raz, a potem możesz go używać wielokrotnie (np. za jednym razem wydrukować 200 różnych produktów). + +1. Kliknij **Szablony** w lewym menu. +2. Kliknij przycisk **Nowy szablon** w prawym górnym rogu. + +![strona Szablony z przyciskiem „Nowy szablon" w prawym górnym rogu, wyraźnie podświetlonym/zaznaczonym.](screenshots/help/pl/new-template-button.png) + +3. Wpisz nazwę szablonu, np. „Cennik produktów". +4. Wybierz format etykiety: + - **Predefiniowane** — gotowe, popularne rozmiary (A4, Zebra 2×1″ itd.). Wybierz to, jeśli nie masz pewności, jakiego rozmiaru potrzebujesz. + - **Własny rozmiar** — wpisz szerokość i wysokość w milimetrach i wybierz orientację (pionowa/pozioma). Wybierz to, jeśli Twoje etykiety mają niestandardowy rozmiar. + +![okno „Nowy szablon" z wypełnioną nazwą i widocznym wyborem między „Predefiniowane" a „Własny rozmiar"; przy zaznaczonej opcji „Własny rozmiar" widoczne pola szerokość/wysokość w mm.](screenshots/help/pl/new-template-dialog.png) + +5. Kliknij **Utwórz**. Otworzy się edytor — możesz od razu zacząć projektować etykietę. --- @@ -38,13 +55,13 @@ Po zalogowaniu trafiasz na **Pulpit**. To tylko ekran powitalny — żeby zaczą | Pozycja | Co tu znajdziesz | |---|---| | **Pulpit** | Ekran startowy. | -| **Szablony** | Twoje szablony w folderach + przyciski *Nowy szablon* i *Importuj*. | -| **Biblioteka** | Gotowe projekty na start + szablony udostępnione przez innych (sekcja 2a). | -| **Urządzenia** | Konektory druku i Inbox przechwyconych etykiet (sekcja 7a). | -| **Pomoc** | Ten przewodnik + FAQ, bez wychodzenia z programu. | -| **Administracja → Użytkownicy** | (tylko admin) zarządzanie kontami. | +| **Szablony** | Twoje szablony, poukładane w folderach, plus przyciski *Nowy szablon* i *Importuj*. | +| **Biblioteka** | Gotowe projekty na start oraz szablony udostępnione przez innych (sekcja 2a). | +| **Urządzenia** | Konektory druku i skrzynka przechwyconych etykiet (sekcja 7a). | +| **Pomoc** | Ten przewodnik + FAQ — bez wychodzenia z programu. | +| **Administracja → Użytkownicy** | (tylko dla administratora) zarządzanie kontami. | -W nagłówku po prawej: twój email, przełącznik języka **PL/EN** i przycisk **Wyloguj**. +W nagłówku po prawej stronie znajdziesz: swój email, przełącznik języka **PL/EN** i przycisk **Wyloguj**. ![Lista szablonów](screenshots/help/pl/templates.png) @@ -52,26 +69,28 @@ W nagłówku po prawej: twój email, przełącznik języka **PL/EN** i przycisk ### Edytor — układ ekranu -Po otwarciu szablonu widzisz: +Po otwarciu szablonu zobaczysz pięć obszarów ekranu: -- **Toolbar (góra)** — Zapisz, Cofnij/Ponów, autozapis, **Generuj serię**, **⬇ Eksportuj** (plik szablonu), **📐 rozmiar etykiety**, **⤓ Importuj ZPL**, **⤒ ZPL** (eksport), **Pobierz PDF**. -- **Lewy panel (Dodaj)** — przyciski wstawiania obiektów na etykietę. -- **Canvas (środek)** — twoja etykieta w skali 1:1 (mm). -- **Pasek wyrównania (nad canvasem)** — wyrównywanie i kolejność warstw. -- **Prawy panel (Właściwości)** — ustawienia zaznaczonego obiektu. +- **Pasek narzędzi (góra)** — Zapisz, Cofnij/Ponów, informacja o autozapisie, **Generuj serię**, **⬇ Eksportuj** (plik szablonu), **📐 rozmiar etykiety**, **⤓ Importuj ZPL**, **⤒ ZPL** i **⤒ TSPL** (eksport dla drukarek etykiet — więcej w sekcji 7a), **Pobierz PDF**. +- **Lewy panel („Dodaj")** — przyciski do wstawiania obiektów na etykietę (tekst, kod kreskowy, obraz itd.). +- **Canvas (środek)** — Twoja etykieta w skali 1:1, w milimetrach — to, co widzisz, odpowiada rzeczywistemu rozmiarowi wydruku. +- **Pasek wyrównania (nad canvasem)** — wyrównywanie obiektów i zmiana ich kolejności. +- **Prawy panel („Właściwości")** — ustawienia obiektu, który akurat zaznaczyłaś/eś. ![Edytor — widok ogólny](screenshots/help/pl/editor-overview.png) -*kadr: cały edytor z otwartym szablonem; podpisane strzałkami: toolbar, panel Dodaj, canvas, pasek wyrównania, panel Właściwości.* +*kadr: cały edytor z otwartym szablonem; podpisane strzałkami: pasek narzędzi, panel Dodaj, canvas, pasek wyrównania, panel Właściwości.* + +### Pasek wyrównania — co robi która grupa przycisków -### Pasek wyrównania — co która grupa robi +- **Strona** — wyrównuje zaznaczony obiekt do krawędzi lub środka strony. +- **Zaznaczenie** — wyrównuje obiekty względem siebie (potrzeba co najmniej 2 zaznaczonych). +- **Warstwa** — zmienia, który obiekt jest z przodu, a który z tyłu (więcej w sekcji 4). +- **Rozłóż** — równe odstępy między obiektami w poziomie lub w pionie (potrzeba co najmniej 3 zaznaczonych). -- **Strona** — wyrównuje obiekt do krawędzi/środka strony. -- **Zaznaczenie** — wyrównuje obiekty względem siebie (potrzebne ≥2 zaznaczone). -- **Warstwa** — zmienia kolejność (przód/tył) zaznaczonych obiektów. -- **Rozłóż** (3+ obiektów) — równe odstępy w poziomie/pionie. +![zbliżenie na pasek wyrównania nad canvasem z czterema grupami przycisków — Strona, Zaznaczenie, Warstwa, Rozłóż — każda podpisana strzałką.](screenshots/help/pl/alignment-bar-groups.png) -Każda ikona ma podpowiedź (najedź myszką). +Każda ikona ma podpowiedź — najedź na nią myszką, a zobaczysz, co robi. --- @@ -79,72 +98,133 @@ Każda ikona ma podpowiedź (najedź myszką). ### Foldery — porządek we własnych szablonach -Na stronie **Szablony** po lewej masz pasek folderów: **Wszystkie**, Twoje foldery (z licznikami) i **Bez folderu**. Foldery są **prywatne** — każdy użytkownik ma swoje. +Na stronie **Szablony**, po lewej stronie, masz pasek folderów: **Wszystkie**, Twoje foldery (z liczbą szablonów w każdym) i **Bez folderu**. Foldery są **prywatne** — każdy użytkownik widzi tylko swoje. + +![pasek folderów po lewej stronie strony Szablony — pozycje „Wszystkie", dwa przykładowe foldery z kolorowymi kropkami i licznikami, „Bez folderu" oraz przycisk „Nowy folder" na dole.](screenshots/help/pl/folder-rail.png) + +1. Aby założyć nowy folder, kliknij **Nowy folder** na dole paska. +2. Aby przenieść szablon do folderu: najedź myszką na jego kafelek, kliknij ikonę **⚙**, wybierz folder z listy i kliknij **Zapisz**. + +![kafelek szablonu z otwartym menu ⚙, widoczna lista folderów do wyboru i przycisk „Zapisz".](screenshots/help/pl/folder-menu.png) -- **Nowy folder** — przycisk na dole paska. -- **Przenoszenie**: najedź na kafelek szablonu → **⚙** → wybierz folder → Zapisz. -- **Edycja (✎)** — zmiana nazwy i **kolor folderu** (paleta 8 kolorów): kolorowa kropka pojawia się przy folderze na pasku i na kafelkach jego szablonów. -- **Usunięcie (✕)** — **nie kasuje szablonów**, wracają do „Bez folderu". +3. Aby zmienić nazwę lub kolor folderu, kliknij ikonę **✎** przy folderze. Do wyboru masz 8 kolorów — kolorowa kropka pojawi się przy folderze na pasku i na kafelkach jego szablonów. + +![okno edycji folderu (✎) z polem nazwy i paletą 8 kolorowych kropek do wyboru.](screenshots/help/pl/folder-edit.png) + +4. Usunięcie folderu (ikona **✕**) **nie kasuje szablonów** — po prostu wracają do „Bez folderu". ### Biblioteka — gotowe projekty i szablony od innych Pozycja **Biblioteka** w menu ma dwie sekcje: -- **Gotowe projekty** — wbudowane startery (etykieta produktu z EAN i datą, adres wysyłki, cena półkowa, termin przydatności, etykieta magazynowa z QR, naklejka inwentarzowa). -- **Od użytkowników** — szablony, które inni udostępnili (widać autora). +- **Gotowe projekty** — przygotowane wzory na start: etykieta produktu z kodem EAN i datą, adres wysyłki, cena półkowa, termin przydatności, etykieta magazynowa z kodem QR, naklejka inwentarzowa. +- **Od użytkowników** — szablony, które udostępnili inni użytkownicy (widzisz, kto jest autorem). -Przycisk **„Użyj"** zawsze tworzy **Twoją własną kopię** i otwiera ją w edytorze — oryginału nie da się zepsuć. +![strona Biblioteka z dwiema sekcjami — „Gotowe projekty" u góry i „Od użytkowników" niżej — każda pozycja z przyciskiem „Użyj".](screenshots/help/pl/library-page.png) + +Przycisk **„Użyj"** zawsze tworzy **Twoją własną kopię** i od razu otwiera ją w edytorze — nie da się przypadkiem zepsuć oryginału. ### Udostępnianie własnego szablonu -Szablony → najedź na kafelek → **⚙** → zaznacz **„Udostępnij w Bibliotece"**. W tym samym oknie możesz też wgrać **grafikę wyróżniającą** — obrazek podglądowy pokazywany na kafelku listy i w Bibliotece. Od tej chwili wszyscy zalogowani widzą go w Bibliotece i mogą sklonować; **edytujesz tylko Ty**. Udostępniony szablon ma na liście ikonę 📚. Odznacz, aby wycofać z Biblioteki. +Chcesz, żeby inni w firmie mogli skorzystać z Twojego szablonu? Udostępnij go w Bibliotece: + +1. Na stronie **Szablony** najedź myszką na kafelek szablonu i kliknij **⚙**. +2. Zaznacz **„Udostępnij w Bibliotece"**. +3. Opcjonalnie wgraj **grafikę wyróżniającą** — to obrazek podglądowy, który będzie widoczny na kafelku listy i w Bibliotece. + +![menu ⚙ kafelka szablonu z zaznaczonym polem „Udostępnij w Bibliotece" i widocznym polem do wgrania grafiki wyróżniającej.](screenshots/help/pl/share-template.png) + +Od tej chwili wszyscy zalogowani użytkownicy widzą szablon w Bibliotece i mogą go sklonować — ale **edytować oryginał możesz tylko Ty**. Udostępniony szablon ma na liście ikonę 📚. Odznacz pole, aby wycofać go z Biblioteki. --- ## 3. Tworzenie etykiety — przewodnik po obiektach -Wszystkie poniższe są w **lewym panelu**, sekcja *Dodaj*. +Wszystko poniżej znajdziesz w **lewym panelu**, w sekcji *Dodaj*. Każdy przycisk wstawia inny typ **obiektu** — czyli elementu, który możesz dowolnie przesuwać i edytować na etykiecie. ### T — Tekst -**Co robi:** Pojedyncza linia tekstu o stałym rozmiarze. -**Kiedy:** Etykiety / nagłówki / krótkie napisy. -**Jak:** Klik **T Tekst**, potem zaznacz na canvasie i edytuj treść w prawym panelu. +**Co robi:** Wstawia pojedynczą linię tekstu o stałym rozmiarze — nie zawija się, jeśli tekst jest za długi. +**Kiedy używać:** Nagłówki, krótkie napisy, stałe informacje. +**Jak:** + +1. Kliknij **T Tekst** w lewym panelu. +2. Zaznacz nowy obiekt na canvasie. +3. W prawym panelu wpisz treść w polu tekstowym. + +![canvas z zaznaczonym obiektem Tekst; prawy panel z polem Treść i przykładowym tekstem.](screenshots/help/pl/object-text.png) ### ¶ — Blok tekstu -**Co robi:** Wieloliniowy tekst, który automatycznie zawija się w ramce; opcjonalnie *auto-skalowanie* zmniejsza/zwiększa font żeby się zmieścił. -**Kiedy:** Opisy produktu o zmiennej długości (idealne do `{{description}}` ze spreadsheetu). -**Jak:** Klik **¶ Blok tekstu**. W prawym panelu zaznacz **Auto-skalowanie** i ustaw min/max font. +**Co robi:** Wstawia wieloliniowy tekst, który automatycznie zawija się w ramce. Możesz też włączyć **auto-skalowanie** — program sam zmniejszy lub zwiększy font, żeby tekst zmieścił się w ramce. +**Kiedy używać:** Opisy produktów o zmiennej długości — świetnie sprawdza się z `{{description}}` pobieranym z arkusza (patrz sekcja 6). +**Jak:** + +1. Kliknij **¶ Blok tekstu**. +2. W prawym panelu zaznacz **Auto-skalowanie** i ustaw minimalny oraz maksymalny rozmiar fontu. + +![canvas z zaznaczonym Blokiem tekstu; prawy panel z zaznaczonym checkboxem Auto-skalowanie oraz polami min/max font.](screenshots/help/pl/object-textblock.png) ### ▭ — Prostokąt, ╱ — Linia -**Co robi:** Geometria pomocnicza (ramki, separatory). -**Jak:** Klik → przeciągnij w canvasie żeby zmienić rozmiar; ustaw kolor/obrys w prawym panelu. +**Co robi:** Dodaje prostą geometrię — ramki, separatory, podziałki. +**Jak:** + +1. Kliknij ▭ lub ╱. +2. Przeciągnij na canvasie, żeby ustawić rozmiar. +3. W prawym panelu ustaw kolor wypełnienia i obrysu. + +![canvas z narysowanym prostokątem i linią; prawy panel z wyborem koloru wypełnienia i obrysu.](screenshots/help/pl/object-shapes.png) ### ▤ — Kod kreskowy -**Co robi:** Generuje kod kreskowy z podanej wartości. -**Kiedy:** Każdy katalog produktów z kodem. -**Jak:** Klik **▤ Kod kreskowy**, w prawym panelu wybierz typ (EAN-13, Code128 itd.) i wpisz dane. Możesz wpisać `{{sku}}` żeby wartość pobrać z arkusza. +**Co robi:** Generuje **kod kreskowy** (czytelny dla skanera kod graficzny reprezentujący np. numer produktu) na podstawie podanej wartości. +**Kiedy używać:** Każdy katalog produktów, który ma swoje kody. +**Jak:** + +1. Kliknij **▤ Kod kreskowy**. +2. W prawym panelu wybierz typ kodu (EAN-13, Code128 itd.). +3. Wpisz dane — możesz też wpisać `{{sku}}`, żeby wartość była pobierana automatycznie z arkusza (patrz sekcja 6). + +![canvas z zaznaczonym obiektem Kod kreskowy; prawy panel z listą typów kodu (EAN-13, Code128) i polem Dane z przykładową wartością.](screenshots/help/pl/object-barcode.png) ### ▦ — Tabela -**Co robi:** Siatka wierszy×kolumn z tekstem w komórkach — do etykiet typu cecha–wartość, wartości odżywcze, mini-lista pozycji. -**Jak:** Klik **▦ Tabela**. W prawym panelu ustaw liczbę wierszy/kolumn, wpisz treść komórek (możesz używać `{{kolumna}}` i `{{date+x}}` — pod siatką pojawiają się chipy), ustaw szerokości kolumn w mm, font i ramkę. Zaznacz **Pogrubiony nagłówek**, aby wyróżnić pierwszy wiersz. -**Druk:** tabela renderuje się natywnie w PDF i jest emitowana jako natywny ZPL (ramka `^GB` + tekst komórek). Uwaga: obrót tabeli nie jest wspierany w ZPL (eksportuje się bez obrotu). +**Co robi:** Wstawia siatkę wierszy i kolumn z tekstem w komórkach — przydatna do etykiet typu cecha–wartość, tabelek wartości odżywczych albo krótkiej listy pozycji. +**Jak:** + +1. Kliknij **▦ Tabela**. +2. W prawym panelu ustaw liczbę wierszy i kolumn. +3. Wpisz treść komórek — możesz używać placeholderów `{{kolumna}}` i dat `{{date+x}}` (patrz sekcje 6 i 7); pod siatką pojawią się kolorowe chipy z podglądem. +4. Ustaw szerokości kolumn (w mm), font i ramkę. +5. Zaznacz **Pogrubiony nagłówek**, jeśli chcesz wyróżnić pierwszy wiersz. + +![canvas z zaznaczoną Tabelą; prawy panel z polami liczby wierszy/kolumn, edytowaną komórką zawierającą `{{kolumna}}` i zaznaczonym checkboxem Pogrubiony nagłówek.](screenshots/help/pl/object-table.png) + +**Warto wiedzieć o druku:** tabela drukuje się poprawnie zarówno w PDF, jak i przy eksporcie do ZPL (patrz sekcja 7a). Jedno ograniczenie: obrócona tabela nie jest wspierana w ZPL — przy eksporcie wraca do pozycji bez obrotu. ### 🖼 — Obraz -**Co robi:** Wgrywa PNG/JPG/SVG i wstawia na canvas. Drukuje się w PDF. -**Kiedy:** Logo, ilustracje, ikony, zdjęcia produktu. -**Jak:** Klik **🖼 Obraz** → wybierz plik. Maks 5 MB. +**Co robi:** Wgrywa plik graficzny (PNG, JPG lub SVG) i wstawia go na canvas. Drukuje się normalnie w PDF. +**Kiedy używać:** Logo firmy, ikony, zdjęcia produktu. +**Jak:** + +1. Kliknij **🖼 Obraz**. +2. Wybierz plik z komputera (maksymalnie 5 MB). + +![canvas z wgranym logo jako obiekt Obraz; prawy panel z podstawowymi informacjami o pliku.](screenshots/help/pl/object-image.png) ### 🌄 — Tło (referencja) -**Co robi:** Wgrywa obraz jako **zablokowane tło na cały rozmiar etykiety**, które jest **widoczne tylko w edytorze, ale NIE drukuje się w PDF**. -**Kiedy:** Etykiety przyszły z drukarni z już wydrukowanym logo. Skanujesz wzór, wgrywasz jako tło, ustawiasz tekst pasujący do logo, generujesz PDF — drukarka dodrukowuje tylko nowy tekst, logo się nie dubluje. -**Jak:** Klik **🌄 Tło**, wybierz plik. Tło ląduje na samym dole stosu, zablokowane (bez uchwytów). Żeby zmienić: zaznacz, w prawym panelu odznacz **Zablokuj pozycję** lub zaznacz **Drukuj w PDF**. +**Co robi:** Wgrywa obraz jako **zablokowane tło na cały rozmiar etykiety** — widoczne tylko w edytorze, jako pomoc wizualna. Tło **nie drukuje się** w PDF. +**Kiedy używać:** Twoje etykiety przyszły z drukarni z już wydrukowanym logo. Skanujesz taki wzór, wgrywasz go jako tło, ustawiasz nowy tekst dokładnie tam, gdzie powinien być, generujesz PDF — drukarka dodrukowuje tylko nowy tekst, a logo nie dubluje się na wydruku. +**Jak:** + +1. Kliknij **🌄 Tło**. +2. Wybierz plik. Tło ląduje na samym dole stosu obiektów i jest zablokowane — nie ma uchwytów do przesuwania. +3. Żeby je zmienić: zaznacz je, potem w prawym panelu odznacz **Zablokuj pozycję** albo zaznacz **Drukuj w PDF**, jeśli jednak chcesz, żeby się wydrukowało. + +![canvas z wgranym Tłem wypełniającym całą etykietę, wyglądającym na zablokowane/przygaszone; prawy panel z checkboxami Zablokuj pozycję i Drukuj w PDF blisko góry.](screenshots/help/pl/object-background.png) --- @@ -152,123 +232,167 @@ Wszystkie poniższe są w **lewym panelu**, sekcja *Dodaj*. ### Zaznaczanie -- Pojedynczy klik = zaznacz jeden. -- **Shift + klik** = dodaj do zaznaczenia (multi-select). -- **Ctrl/Cmd + A** = zaznacz wszystko. +- Pojedynczy klik = zaznacz jeden obiekt. +- **Shift + klik** = dodaj kolejny obiekt do zaznaczenia (tzw. multi-select — zaznaczenie kilku naraz). +- **Ctrl/Cmd + A** = zaznacz wszystkie obiekty na etykiecie. + +![canvas z trzema zaznaczonymi obiektami naraz (niebieskie obwódki zaznaczenia), pokazujący zaznaczenie wielokrotne przez Shift+klik.](screenshots/help/pl/multiselect.png) ### Przesuwanie i skalowanie -- Przeciągaj zaznaczony obiekt myszką. -- Uchwyty na rogach = skalowanie; uchwyt nad obiektem = obrót. -- Obiekt **zablokowany** nie ma uchwytów — ale dalej można go zaznaczyć żeby odblokować w prawym panelu. +- Przeciągaj zaznaczony obiekt myszką, żeby go przesunąć. +- Uchwyty na rogach służą do zmiany rozmiaru; uchwyt nad obiektem służy do obrotu. +- Obiekt **zablokowany** nie ma uchwytów — ale wciąż możesz go zaznaczyć i odblokować w prawym panelu. + +![zaznaczony obiekt na canvasie z widocznymi uchwytami do zmiany rozmiaru na rogach i uchwytem obrotu nad obiektem.](screenshots/help/pl/resize-handles.png) + +### Cofanie zmian + +- **Ctrl/Cmd + Z** = cofnij ostatnią zmianę. +- **Ctrl/Cmd + Shift + Z** lub **Ctrl/Cmd + Y** = przywróć cofniętą zmianę. + +Jedna operacja to jeden krok historii — np. wyrównanie 5 obiektów naraz cofniesz jednym Ctrl+Z. -### Cofanie +![zbliżenie na przyciski Cofnij i Ponów w pasku narzędzi.](screenshots/help/pl/undo-redo-buttons.png) -- **Ctrl/Cmd + Z** = cofnij. -- **Ctrl/Cmd + Shift + Z** lub **Ctrl/Cmd + Y** = ponów. +### Duplikowanie (robienie kopii) -Jedna operacja = jeden krok historii (np. wyrównanie 5 obiektów cofa się jednym Ctrl+Z). +Dwa szybkie sposoby na skopiowanie zaznaczonego obiektu (albo całego zaznaczenia wielu obiektów): -### Duplikowanie +- **Alt + przeciąganie** — przytrzymaj **Alt** (na Mac: **Option**) i przeciągnij zaznaczony obiekt. Oryginał zostaje na miejscu, a kopia ląduje tam, gdzie puścisz myszkę. +- **Ctrl/Cmd + D** — tworzy kopię „w miejscu", przesuniętą o 5 mm w prawo i w dół. Zaznaczenie od razu przeskakuje na nową kopię, więc kolejne Ctrl+D buduje schodek kopii. -Dwa szybkie sposoby zrobienia kopii zaznaczonego obiektu (lub całego multi-selectu): +![canvas w trakcie przeciągania z wciśniętym Alt — widoczny oryginalny obiekt w miejscu startowym i tworzona kopia pod kursorem.](screenshots/help/pl/duplicate-altdrag.png) -- **Alt + przeciąganie** — przytrzymaj **Alt** (lub **Option** na Mac) i przeciągnij zaznaczony obiekt. Oryginał zostaje w miejscu, klon ląduje pod kursorem w momencie puszczenia myszki. Multi-select zachowuje względne pozycje — przeciągnij jeden z 3 zaznaczonych, dostaniesz 3 klony w nowej lokalizacji. -- **Ctrl/Cmd + D** — duplikuje zaznaczone "w miejscu" z drobnym przesunięciem (+5 mm w prawo i w dół). Selekcja od razu skacze na klony, więc kolejne Ctrl+D buduje schodek kopii w prawo-w-dół. +Kopia dziedziczy wszystko: font, kolor, obrót, ustawienia *Zablokuj* i *Drukuj w PDF*. Obrazy współdzielą ten sam plik źródłowy, więc nie zajmują dodatkowego miejsca. -Klon dziedziczy wszystko: font, kolor, rotację, flagi *Zablokuj* / *Drukuj w PDF*, a obrazy współdzielą ten sam Asset (jedna binarka → wiele obiektów). Pełne duplikowanie multi-selectu cofniesz jednym Ctrl+Z. +### Kolejność warstw (co jest na wierzchu) -### Kolejność warstw (z-order) +W **pasku wyrównania**, w grupie **Warstwa**: -W **pasku wyrównania**, grupa **Warstwa**: +- ⤓ **Na sam dół** — chowa zaznaczony obiekt pod resztę. +- ↓ **Niżej** — przesuwa o jedną pozycję w dół. +- ↑ **Wyżej** — przesuwa o jedną pozycję w górę. +- ⤒ **Na sam wierzch** — stawia obiekt nad wszystkimi innymi. -- ⤓ **Na sam dół** — wsadź zaznaczone pod resztę. -- ↓ **Niżej** — przesuń o jedno pod sąsiada. -- ↑ **Wyżej** — przesuń o jedno nad sąsiada. -- ⤒ **Na sam wierzch** — nad wszystko. +![zbliżenie na grupę Warstwa w pasku wyrównania z czterema ikonami podpisanymi strzałkami.](screenshots/help/pl/layer-buttons.png) -Multi-select zachowuje względną kolejność zaznaczonych. +### Blokada i drukowanie (prawy panel) -### Lock + Drukuj w PDF (prawy panel) +Na samej górze prawego panelu każdy obiekt ma dwa pola wyboru: -Każdy obiekt ma na górze prawego panelu dwa checkboxy: +- **🔒 Zablokuj pozycję** — wyłącza przesuwanie i zmianę rozmiaru (nadal można edytować font, kolor itd.). +- **🖨 Drukuj w PDF** — domyślnie zaznaczone. Jeśli je odznaczysz, obiekt będzie widoczny tylko w edytorze i nie pojawi się w wygenerowanym PDF. Takie obiekty są wyblakłe na canvasie, żebyś od razu widziała/widział, że nie wydrukują się. -- **🔒 Zablokuj pozycję** — wyłącza przesuwanie i skalowanie (ale dalej można edytować font, kolor itd.). -- **🖨 Drukuj w PDF** — domyślnie zaznaczone. Odznaczone = obiekt widać tylko w edytorze, w PDF się nie pojawi (renderer go pomija). Obiekty nie-drukowane są wyblakłe (50% przezroczystości) żebyś od razu widział. +![góra prawego panelu z checkboxami 🔒 Zablokuj pozycję i 🖨 Drukuj w PDF; Drukuj w PDF odznaczone, a odpowiadający obiekt na canvasie wyblakły.](screenshots/help/pl/lock-print-checkboxes.png) ### Autozapis -Edytor sam zapisuje co kilka sekund. Status w toolbarze: -- **Niezapisane zmiany** — coś jest do zapisania. -- **Autozapis…** — w trakcie wysyłania. -- **Autozapisano 12:34** — ostatni zapis. +Edytor sam zapisuje Twoją pracę co kilka sekund. Status widzisz w pasku narzędzi: -Możesz też ręcznie kliknąć **Zapisz**. +- **Niezapisane zmiany** — jest coś do zapisania. +- **Autozapis…** — trwa wysyłanie. +- **Autozapisano 12:34** — ostatni udany zapis. + +![zbliżenie na obszar statusu autozapisu w pasku narzędzi, pokazujące kolejno trzy stany: „Niezapisane zmiany", „Autozapis…", „Autozapisano 12:34".](screenshots/help/pl/autosave-status.png) + +Możesz też w każdej chwili kliknąć **Zapisz** ręcznie. ### Historia wersji -Każde **ręczne** zapisanie (przycisk **Zapisz** lub **Ctrl+S**) tworzy wersję szablonu. Autozapis nadpisuje bieżący stan i **nie** zaśmieca historii. Przycisk **🕘 Historia** w toolbarze pokazuje listę wersji (numer, data, autor) — kliknij **Przywróć**, aby wrócić do wybranej. Przywrócenie zapisuje bieżący stan jako nową wersję („restored from vN"), więc jest odwracalne. Program trzyma 30 ostatnich wersji na szablon. +Każdy **ręczny** zapis (przycisk **Zapisz** albo **Ctrl+S**) tworzy nową **wersję** szablonu — czyli migawkę tego, jak wyglądał w danym momencie. Autozapis nadpisuje bieżący stan i nie tworzy dodatkowych wersji, dzięki czemu lista pozostaje krótka i czytelna. + +1. Kliknij **🕘 Historia** w pasku narzędzi. +2. Zobaczysz listę wersji: numer, datę i autora. +3. Kliknij **Przywróć** przy tej, do której chcesz wrócić. + +![panel Historia otwarty, z listą kilku wersji (numer, data, autor) i przyciskiem Przywróć przy jednej z nich.](screenshots/help/pl/version-history.png) + +Przywrócenie zapisuje bieżący stan jako nową wersję, więc niczego nie tracisz bezpowrotnie — program trzyma 30 ostatnich wersji na szablon. ### Zmiana rozmiaru etykiety -Rozmiar ustawiony przy tworzeniu szablonu **można zmienić w każdej chwili**: w toolbarze kliknij przycisk **📐 {szerokość}×{wysokość}**. +Rozmiar wybrany przy tworzeniu szablonu **można zmienić w każdej chwili**. -- Wpisz nową szerokość i wysokość w mm (1–1000), albo kliknij jeden z gotowych presetów (40×100, 50×30, 100×150, 105×148, 210×297). -- Obiekty **nie są przeskalowywane** — zachowują pozycje w mm. Po zmniejszeniu etykiety elementy poza krawędzią po prostu przesuwasz z powrotem. +1. W pasku narzędzi kliknij przycisk **📐 {szerokość}×{wysokość}**. +2. Wpisz nową szerokość i wysokość w mm (od 1 do 1000) albo kliknij jeden z gotowych presetów (40×100, 50×30, 100×150, 105×148, 210×297). +3. Kliknij **Zastosuj**. ![Okno „Rozmiar etykiety"](screenshots/help/pl/label-size.png) *kadr: modal z polami Szerokość/Wysokość i rzędem presetów-chipów; kursor nad przyciskiem „Zastosuj".* +Obiekty **nie są przeskalowywane** — zachowują swoje pozycje w milimetrach. Jeśli zmniejszysz etykietę, po prostu przeciągnij z powrotem elementy, które wystają poza nową krawędź. + --- ## 5. Pobieranie PDF — pojedyncza etykieta -Chcesz najpierw zobaczyć wynik? Kliknij **👁 Podgląd** — PDF pokaże się w oknie aplikacji z przyciskiem **Pobierz PDF**. Albo od razu **Pobierz PDF** w toolbarze. Renderowanie jest synchroniczne (kilka sekund), plik PDF zaczyna się ściągać. +Chcesz najpierw zobaczyć wynik? Kliknij **👁 Podgląd** — PDF pojawi się w oknie aplikacji razem z przyciskiem **Pobierz PDF**. -Jeśli któryś tekst nie zmieścił się w bloku, zobaczysz chip **N ostrzeżeń** — najedź żeby zobaczyć szczegóły. +![okno podglądu PDF w aplikacji z wyrenderowaną etykietą i przyciskiem Pobierz PDF pod spodem.](screenshots/help/pl/preview-pdf.png) -Placeholdery kolumn (`{{name}}`) w pojedynczym PDF zostają jako tekst — dane podmienia dopiero generowanie serii. **Placeholdery daty** (`{{date+14d}}`, patrz sekcja 7) są natomiast obliczane także tutaj. +Możesz też od razu kliknąć **Pobierz PDF** w pasku narzędzi — plik zacznie się ściągać po kilku sekundach. + +Jeśli jakiś tekst nie zmieścił się w swoim bloku, zobaczysz w pasku narzędzi chip **„N ostrzeżeń"**. Najedź na niego myszką, żeby zobaczyć szczegóły. + +![pasek narzędzi z widocznym chipem „N ostrzeżeń" i otwartym dymkiem podpowiedzi ze szczegółami przycięcia tekstu.](screenshots/help/pl/warnings-chip.png) + +Uwaga: placeholdery kolumn (`{{name}}` — miejsce, w które program sam wstawi dane z arkusza) w pojedynczym PDF zostają jako zwykły tekst — prawdziwe dane podstawia dopiero **generowanie serii** (sekcja 6). Placeholdery daty (`{{date+14d}}`, sekcja 7) są natomiast obliczane od razu, także tutaj. --- ## 6. Generowanie serii — wiele etykiet z jednego szablonu -To główny feature programu. Pozwala wygenerować np. 200 etykiet z jednego szablonu, gdzie każda dostaje inne dane z arkusza/bazy. +To jest **główna funkcja programu**. Pozwala wygenerować np. 200 etykiet z jednego szablonu, gdzie każda dostaje inne dane — np. inną nazwę produktu i inny kod kreskowy — pobrane z arkusza albo z bazy danych. -### Krok 0 — przygotowanie szablonu +### Krok 0 — przygotuj szablon -Wstaw w Text lub Barcode placeholder w postaci `{{nazwa_kolumny}}`, np.: -- Text: `{{name}}` -- Barcode data: `{{sku}}` +W obiekcie Tekst albo Kod kreskowy wstaw **placeholder** — czyli miejsce, w które program sam wstawi dane z Twojego arkusza — w postaci `{{nazwa_kolumny}}`, np.: -Każde wystąpienie zostanie podmienione wartością z odpowiedniej kolumny. +- Tekst: `{{name}}` +- Dane kodu kreskowego: `{{sku}}` + +Każde takie wystąpienie zostanie podmienione wartością z odpowiedniej kolumny. ![Wykryte pola dynamiczne](screenshots/help/pl/dynamic-fields.png) *kadr: prawy panel Właściwości z polem tekstowym zawierającym `{{name}}` i `{{date+14d}}`; poniżej dwa chipy — fioletowy `{{name}}` i zielony `{{date+14d}} → 18.07.2026`.* -### Krok 1 — Wgraj dane +### Krok 1 — wgraj dane + +1. W pasku narzędzi kliknij **Generuj serię**. +2. W Kroku 1 wybierz plik z danymi. -Toolbar → **Generuj serię** → Krok 1 (Wgraj dane). +![Krok 1 kreatora Generuj serię z polem do wyboru/przeciągnięcia pliku i tabelką dopuszczalnych formatów.](screenshots/help/pl/series-step1-upload.png) Akceptowane formaty: -| Format | Maks rozmiar | Maks wierszy | +| Format | Maksymalny rozmiar pliku | Maksymalna liczba wierszy | |---|---|---| | `.csv` | 10 MB | 1000 | | `.xls` / `.xlsx` | 10 MB | 1000 | | `.db` / `.sqlite` / `.sqlite3` | 50 MB | 1000 (na zapytanie) | -#### CSV / Excel +#### Jeśli wgrywasz CSV lub Excel + +Plik trafia na serwer i od razu jest odczytywany. Zobaczysz listę wykrytych kolumn i liczbę wierszy. -Plik trafia na serwer i od razu jest parsowany. Widzisz kolumny i liczbę wierszy. Klik **Dalej**. +![podgląd po wgraniu pliku CSV — lista wykrytych kolumn jako mała tabelka, liczba wierszy i przycisk „Dalej".](screenshots/help/pl/series-csv-preview.png) -#### SQLite +Kliknij **Dalej**. -Po uploadzie program pokazuje **listę tabel** (posortowane: najpierw te z największą liczbą wierszy). Wybierz tabelę z danymi i kliknij **Użyj tego źródła**. +#### Jeśli wgrywasz SQLite (bazę danych) -Jeśli potrzebujesz filtrowania na poziomie SQL (np. tylko produkty z konkretnej kategorii, lub JOIN dwóch tabel), rozwiń **Pokaż zaawansowane** i wpisz zapytanie SELECT, np.: +**SQLite** to plik z bazą danych — jeśli ktoś w firmie eksportuje dane z systemu magazynowego do takiego pliku, możesz go użyć bezpośrednio, bez konwersji na CSV. + +1. Po wgraniu pliku program pokaże **listę tabel** w bazie, posortowaną tak, że tabele z największą liczbą wierszy są na górze. +2. Wybierz tabelę, która zawiera dane, których potrzebujesz. +3. Kliknij **Użyj tego źródła**. + +![lista tabel po wgraniu pliku SQLite, posortowana według liczby wierszy, z przyciskiem „Użyj tego źródła" przy jednej z pozycji.](screenshots/help/pl/series-sqlite-tables.png) + +Jeśli potrzebujesz węższego wyboru danych (np. tylko produkty z jednej kategorii), rozwiń **Pokaż zaawansowane** i wpisz zapytanie SELECT — to polecenie języka baz danych, które mówi programowi dokładnie, jakie dane pobrać, np.: ```sql SELECT sku, name, price @@ -276,33 +400,46 @@ FROM products WHERE category = 'labels' AND price > 0 ``` -**Bezpieczeństwo:** Połączenie jest read-only. Akceptowany jest tylko pojedynczy SELECT — `INSERT`, `UPDATE`, `DELETE`, `DROP`, `ATTACH`, `PRAGMA` są blokowane. Maksymalnie 1000 wierszy w wyniku — większe odrzucone z prośbą o `WHERE`/`LIMIT`. - -### Krok 2 — Mapowanie pól +![rozwinięty panel „Pokaż zaawansowane" z wpisanym zapytaniem SQL w polu tekstowym i przyciskiem „Użyj tego źródła".](screenshots/help/pl/series-sqlite-sql.png) -Program wykrywa wszystkie placeholdery `{{...}}` z szablonu. Jeśli nazwa placeholdera == nazwa kolumny, mapowanie wstawia się automatycznie. Jeśli różne — wybierz ręcznie z listy. +**Bezpieczeństwo danych:** połączenie z bazą jest tylko do odczytu. Program przyjmuje wyłącznie polecenia odczytujące dane (SELECT) — żadne polecenie mogące coś zmienić lub skasować nie zostanie wykonane. Wynik może mieć maksymalnie 1000 wierszy. -### Krok 3 — Filtr (opcjonalny) +### Krok 2 — dopasuj pola (mapowanie) -Możesz odsiać wiersze przed generowaniem, np. *price > 10* albo *category contains "tea"*. Klik **Sprawdź filtr** pokazuje ile wierszy się załapie. Pomiń ten krok jeśli chcesz wszystkie. +Program sam wykrywa wszystkie placeholdery `{{...}}` z Twojego szablonu. Jeśli nazwa placeholdera pasuje dokładnie do nazwy kolumny w danych, dopasowanie ustawia się samo. Jeśli nazwy się różnią, wybierz kolumnę ręcznie z listy. ![Kreator serii — mapowanie](screenshots/help/pl/series-map.png) *kadr: krok 2 kreatora z listą placeholderów po lewej i selectami kolumn po prawej; przy `{{date}}` widoczna zielona podpowiedź „Opcjonalne — bez mapowania użyta zostanie dzisiejsza data".* -### Krok 4 — Generuj PDF +### Krok 3 — filtr (opcjonalnie) + +Jeśli nie chcesz drukować wszystkich wierszy z arkusza, możesz je odsiać, np. tylko produkty droższe niż 10 zł albo takie, których nazwa zawiera słowo „herbata". + +1. Wybierz kolumnę, warunek (np. „większe niż") i wartość. +2. Kliknij **Sprawdź filtr**, żeby zobaczyć, ile wierszy się załapie. + +![Krok 3 Filtr z wybraną kolumną, warunkiem i wartością (np. price > 10) oraz wynikiem po kliknięciu „Sprawdź filtr" pokazującym liczbę pasujących wierszy.](screenshots/help/pl/series-filter.png) -Klik **Generuj PDF**. Powstaje zadanie w tle, pasek pokazuje postęp. Po zakończeniu PDF ściąga się automatycznie. +Możesz też pominąć ten krok, jeśli chcesz wygenerować etykiety dla wszystkich wierszy. -Jeśli któreś etykiety mają teksty nie mieszczące się w blokach, zobaczysz listę ostrzeżeń (które wiersze, które obiekty) — PDF i tak powstaje. +### Krok 4 — wygeneruj PDF + +Kliknij **Generuj PDF**. Program zaczyna pracować w tle, a pasek postępu pokazuje, ile zostało. Po zakończeniu PDF zaczyna się pobierać automatycznie. + +![Krok 4 z paskiem postępu generowania w trakcie pracy i statusem tekstowym.](screenshots/help/pl/series-progress.png) + +Jeśli w niektórych etykietach tekst nie zmieścił się w swoim bloku, zobaczysz listę ostrzeżeń — które wiersze, które obiekty. PDF i tak zostanie wygenerowany dla wszystkich etykiet. + +![lista ostrzeżeń po wygenerowaniu serii, z wyszczególnieniem konkretnych wierszy i obiektów z przyciętym tekstem.](screenshots/help/pl/series-warnings-list.png) --- ## 7. Placeholdery daty — `{{date+…}}` -Oprócz kolumn z arkusza możesz wstawiać **daty liczone automatycznie w momencie generowania** — idealne do terminów przydatności („zużyć do") i dat produkcji. Działają wszędzie: w pojedynczym PDF, w serii i w eksporcie ZPL. +Oprócz kolumn z arkusza możesz wstawić **daty liczone automatycznie w momencie generowania etykiety** — idealne do terminów przydatności („zużyć do") i dat produkcji. Działają wszędzie: w pojedynczym PDF, w serii i w eksporcie ZPL. -### Składnia +### Jak to zapisać | Wpisujesz | Dostajesz (przy generowaniu 04.07.2026) | |---|---| @@ -314,13 +451,13 @@ Oprócz kolumn z arkusza możesz wstawiać **daty liczone automatycznie w momenc | `{{date+14d:DD/MM/YY}}` | 18/07/26 (własny format) | | `{{date+3m:YYYY-MM-DD}}` | 2026-10-04 | -- Jednostki przesunięcia: **d** = dni, **m** = miesiące, **y** = lata; działa `+` i `-`. -- Format (opcjonalnie, po dwukropku) składasz z klocków **DD**, **MM**, **YY**, **YYYY** — separatory (kropki, ukośniki, myślniki, spacje) przechodzą bez zmian. Bez formatu dostajesz `DD.MM.YYYY`. -- Koniec miesiąca jest bezpieczny: 31 stycznia + 1 miesiąc = 28/29 lutego (nie „31 lutego"). +- Jednostki przesunięcia: **d** = dni, **m** = miesiące, **y** = lata. Działa zarówno `+`, jak i `-`. +- Format zapisu daty (opcjonalny, po dwukropku) budujesz z klocków **DD**, **MM**, **YY**, **YYYY** — separatory (kropki, ukośniki, myślniki, spacje) zostają bez zmian. Bez podania formatu dostajesz `DD.MM.YYYY`. +- Koniec miesiąca jest bezpieczny: 31 stycznia + 1 miesiąc da 28 lub 29 lutego — program nigdy nie stworzy nieistniejącej daty typu „31 lutego". -### Skąd wiesz, że zadziała? +### Skąd wiesz, że to zadziała -Po wpisaniu placeholdera w prawym panelu pojawia się **zielony chip z podglądem obliczonej daty** (fioletowe chipy to zwykłe kolumny z arkusza). Najedź na chip — tooltip przypomina, że finalna wartość liczy się przy generowaniu. +Po wpisaniu placeholdera w prawym panelu pojawia się **zielony chip z podglądem obliczonej daty** (fioletowe chipy to zwykłe kolumny z arkusza). Najedź na chip myszką — podpowiedź przypomni, że ostateczna wartość liczy się dopiero w momencie generowania. ![Zielony chip daty](screenshots/help/pl/date-chip.png) @@ -328,63 +465,121 @@ Po wpisaniu placeholdera w prawym panelu pojawia się **zielony chip z podgląde ### Dobrze wiedzieć -- **Kolumna o nazwie `date`** w arkuszu ma pierwszeństwo dla gołego `{{date}}` — formy z przesunięciem (`{{date+14d}}`) zawsze liczą się automatycznie. -- Data liczy się **w momencie generowania PDF/ZPL**, według czasu serwera — nie w momencie zapisania szablonu. -- W kreatorze serii pola datowe **nie wymagają mapowania** na kolumnę. +- Jeśli w arkuszu masz **kolumnę o nazwie `date`**, to ona ma pierwszeństwo dla gołego `{{date}}`. Formy z przesunięciem (`{{date+14d}}`) zawsze liczą się automatycznie, niezależnie od kolumn w arkuszu. +- Data liczy się **w momencie generowania** PDF lub ZPL, według zegara serwera — nie w momencie, gdy zapisujesz szablon. +- W kreatorze serii pola datowe **nie wymagają mapowania** na żadną kolumnę. --- -## 7a. ZPL — drukarki etykiet Zebra +## 7a. Drukowanie na drukarkach etykiet (ZPL i TSPL) -**ZPL** to język drukarek etykiet (Zebra i zgodne). Program potrafi w obie strony: zaimportować istniejącą etykietę ZPL do edytora i wyeksportować twój projekt jako ZPL. +Etykiety drukowane na specjalnych drukarkach etykiet (np. Zebra, TSC, Toshiba) nie używają zwykłego PDF-a — mówią własnym językiem poleceń. Program potrafi ten język zarówno **czytać** (import), jak i **pisać** (eksport), więc nie musisz znać się na nim samodzielnie. -### Import ZPL +### ZPL — drukarki Zebra i zgodne -Toolbar → **⤓ Importuj ZPL**. +**ZPL** to specjalny język, którym rozmawiają drukarki etykiet marki Zebra (i modele zgodne z nią). Program potrafi zaimportować istniejącą etykietę zapisaną w ZPL do edytora oraz wyeksportować Twój projekt jako kod ZPL. -1. Wklej kod ZPL (np. z innego systemu albo od dostawcy etykiet). -2. Wybierz **DPI drukarki** — jeśli nie wiesz, zostaw **Wykryj automatycznie** (program porówna wymiary z kodu z rozmiarem twojej etykiety). -3. Klik **Sprawdź** — zobaczysz liczbę rozpoznanych obiektów i wykryte DPI; jeśli etykieta z kodu jest większa niż twoja, dostaniesz podpowiedź. -4. Klik **Importuj** — obiekty lądują na canvasie. **Uwaga:** import zastępuje obecną zawartość etykiety. +#### Import ZPL -Zmienne drukarkowe w pojedynczych klamrach (np. `{NAZWA}`) przechodzą bez zmian, a polecenia, których edytor nie modeluje, są zachowywane i wracają przy eksporcie. +1. W pasku narzędzi kliknij **⤓ Importuj ZPL**. +2. Wklej kod ZPL — np. otrzymany od dostawcy etykiet albo z innego systemu. +3. Wybierz **DPI drukarki** — to gęstość wydruku, czyli liczba kropek na milimetr, jaką drukuje drukarka. Jeśli nie wiesz, jakie DPI ma Twoja drukarka, zostaw **Wykryj automatycznie** — program porówna wymiary z kodu z rozmiarem Twojej etykiety i sam je odgadnie. +4. Kliknij **Sprawdź** — zobaczysz liczbę rozpoznanych obiektów i wykryte DPI. Jeśli etykieta z kodu jest większa niż Twoja, dostaniesz podpowiedź. +5. Kliknij **Importuj** — obiekty lądują na canvasie. ![Okno importu ZPL](screenshots/help/pl/zpl-import.png) *kadr: modal z wklejonym kodem ZPL, selektem DPI ustawionym na „Wykryj automatycznie" i wynikiem analizy „12 obiektów · 203 dpi".* -### Eksport ZPL +**Uwaga:** import zastępuje obecną zawartość etykiety — jeśli coś już zaprojektowałaś/eś, zrób najpierw kopię (sekcja 8a). -Toolbar → **⤒ ZPL**. Dwa tryby: +#### Eksport ZPL -- **Szablon (zmienne)** — jeden kod ZPL z twojego projektu; placeholdery kolumn `{{...}}` zostają w kodzie (podmienisz je we własnym systemie), a **placeholdery daty są od razu obliczone**. Przyciski **Kopiuj** i **Pobierz .zpl**. -- **Wsad (dataset)** — wybierz wgrany wcześniej plik danych, a program wygeneruje jeden plik `.zpl` z etykietą dla każdego wiersza (podmienione i kolumny, i daty). +W pasku narzędzi kliknij **⤒ ZPL**. Do wyboru masz dwa tryby: -Wybierz DPI zgodne z twoją drukarką (203 lub 300). +- **Szablon (zmienne)** — jeden kod ZPL z Twojego projektu. Placeholdery kolumn `{{...}}` zostają w kodzie jako tekst (podmienisz je we własnym systemie), a placeholdery daty są od razu obliczone. Do dyspozycji masz przyciski **Kopiuj** i **Pobierz .zpl**. +- **Wsad (dataset)** — wybierz wcześniej wgrany plik danych, a program wygeneruje jeden plik `.zpl` z osobną etykietą dla każdego wiersza (podmienione i kolumny, i daty). ![Okno eksportu ZPL](screenshots/help/pl/zpl-export.png) *kadr: modal w trybie „Szablon (zmienne)" z podglądem wygenerowanego kodu i przyciskami Kopiuj / Pobierz .zpl.* -### Druk bezpośredni — konektor +Wybierz DPI zgodne z Twoją drukarką (zwykle 203 lub 300). + +### TSPL — drukarki TSC i Toshiba + +**TSPL** to odpowiednik ZPL dla drukarek marki TSC i Toshiba — inny dialekt tego samego pomysłu: język poleceń zrozumiały dla drukarki etykiet. + +1. W pasku narzędzi kliknij **⤒ TSPL**. +2. Wybierz DPI drukarki (203 lub 300). +3. Zobaczysz na bieżąco podgląd wygenerowanego kodu. +4. Kliknij **Kopiuj** albo **Pobierz .txt**. + +![okno „Export TSPL" z wyborem DPI (203/300), podglądem wygenerowanego kodu TSPL i przyciskami Kopiuj / Pobierz .txt.](screenshots/help/pl/tspl-export.png) + +Eksport TSPL działa tylko dla pojedynczej etykiety (bez trybu Wsad) i nie ma jeszcze importu w drugą stronę — to funkcja na wcześniejszym etapie rozwoju niż ZPL. + +### Najprościej: kreator „Podłącz drukarkę" + +Nie chcesz ręcznie tworzyć pliku ustawień ani przeklejać kodów? Aplikacja ma **kreator**, który przeprowadzi Cię przez wszystko krok po kroku — sam pobierze właściwy program, przygotuje gotowy plik ustawień i poda jedną komendę do skopiowania. Wejdź w **Urządzenia** i kliknij **🖨 Podłącz drukarkę**. + +1. **Wybierz swój komputer.** Kreator sam wykrywa system (Mac / Windows / Linux) — potwierdź jednym kliknięciem. + +![krok 1 kreatora — pytanie „Na jakim komputerze podłączasz drukarkę?" z kafelkami Mac, Windows, Linux i Linux (ARM).](screenshots/help/pl/connect-wizard-os.png) + +2. **Nazwij ten komputer** (np. „Komputer w biurze") — to tylko etykieta, żebyś rozpoznał go na liście. + +![krok 2 kreatora — pole „Nazwij ten komputer" z przykładową nazwą.](screenshots/help/pl/connect-wizard-name.png) + +3. **Pobierz dwa pliki** — program łączący oraz gotowy plik ustawień (adres serwera i Twój kod są już w nim wpisane; nic nie trzeba edytować). + +![krok 3 kreatora — dwa przyciski pobierania: program i plik ustawień, z notką o prywatności klucza.](screenshots/help/pl/connect-wizard-download.png) + +4. **Uruchom program** — skopiuj jedną gotową komendę i wklej ją w Terminalu (na Macu kreator chowa w niej zdjęcie systemowej blokady). Zostaw to okno otwarte. -Zamiast pobierać plik `.zpl`, możesz drukować **prosto z edytora** przyciskiem **🖨 Drukuj**: +![krok 4 kreatora — pole z komendą do skopiowania, przycisk Kopiuj i przypomnienie „zostaw okno otwarte".](screenshots/help/pl/connect-wizard-run.png) -1. Na komputerze w sieci z drukarkami zainstaluj agenta **blg-connector** (binarka w Assets każdego wydania na GitHubie; konfiguracja: `connector/README.md`). -2. W aplikacji: **Urządzenia → Dodaj urządzenie** → skopiuj token do `config.yaml` agenta. Urządzenie przejdzie na **Online** i zgłosi listę drukarek. -3. W edytorze: **🖨 Drukuj** → wybierz urządzenie, drukarkę, liczbę kopii i DPI → **Drukuj**. Okno pokaże postęp: *w kolejce → agent odebrał → wydrukowano* (lub błąd z powodem). +5. **Poczekaj na połączenie** — kreator sam wykryje, gdy komputer się zgłosi, i pokaże „Połączono". -**Szybka ścieżka:** jeśli konektor działa **na tym samym komputerze**, na którym otwarta jest przeglądarka, dialog wykryje go automatycznie i pokaże opcję **⚡ Ten komputer — druk natychmiastowy** (domyślnie wybraną) — etykieta idzie wtedy prosto na drukarkę, bez rundy przez serwer. +![krok 5 kreatora — komunikat „Czekam, aż Twój komputer się zgłosi…" ze wskaźnikiem oczekiwania.](screenshots/help/pl/connect-wizard-waiting.png) -Placeholdery daty są obliczane w momencie druku; placeholdery kolumn zostają w kodzie (druk pojedynczej etykiety, nie serii). +6. **Wskaż drukarkę (opcjonalnie)** — podaj adres IP drukarki albo zostaw tryb testowy (wydruki zapisywane do pliku), żeby najpierw wszystko sprawdzić. + +![krok 6 kreatora — „Gdzie jest Twoja drukarka?" z polem na adres IP i opcją trybu testowego.](screenshots/help/pl/connect-wizard-printer.png) + +Gdy komputer pokaże się jako **Online**, drukujesz z edytora dokładnie tak, jak opisano niżej. + +### Sposób zaawansowany: ręczna konfiguracja konektora + +Jeśli wolisz zrobić to ręcznie (albo automatyzujesz wiele stanowisk), możesz drukować **prosto z edytora** (dotyczy ZPL — nie TSPL) dzięki **konektorowi** — małemu programowi instalowanemu na komputerze w tej samej sieci co drukarki, który łączy aplikację z drukarką. + +1. Zainstaluj agenta **blg-connector** na komputerze podłączonym do sieci z drukarkami (plik do pobrania w sekcji Assets każdego wydania na GitHubie; instrukcja konfiguracji: `connector/README.md`). +2. W aplikacji przejdź do **Urządzenia → Dodaj urządzenie**. + +![okno „Dodaj urządzenie" z wygenerowanym tokenem (kodem) do skopiowania do pliku config.yaml agenta.](screenshots/help/pl/connector-add-device.png) + +3. Skopiuj wygenerowany **token** (unikalny kod dostępu) do pliku `config.yaml` agenta. Urządzenie przejdzie w stan **Online** i zgłosi listę podłączonych drukarek. +4. W edytorze kliknij **🖨 Drukuj**, wybierz urządzenie, drukarkę, liczbę kopii i DPI, a potem kliknij **Drukuj**. + +![okno druku w edytorze z wyborem urządzenia, drukarki, liczby kopii i DPI, przyciskiem „Drukuj" oraz widocznym paskiem postępu (w kolejce → agent odebrał → wydrukowano).](screenshots/help/pl/connector-print-dialog.png) + +Okno pokaże na żywo postęp: *w kolejce → agent odebrał → wydrukowano* (albo błąd wraz z powodem). + +**Szybka ścieżka:** jeśli konektor działa **na tym samym komputerze**, na którym masz otwartą przeglądarkę, program wykryje go automatycznie i zaproponuje opcję **⚡ Ten komputer — druk natychmiastowy**. Etykieta idzie wtedy prosto na drukarkę, bez rundy przez serwer. + +![okno druku z zaznaczoną domyślnie opcją „⚡ Ten komputer — druk natychmiastowy".](screenshots/help/pl/connector-fastpath.png) + +Placeholdery daty są obliczane w momencie druku; placeholdery kolumn zostają w kodzie (bo to druk pojedynczej etykiety, nie serii). ### Wirtualna drukarka — przejmij etykiety z innych programów -Konektor potrafi też działać **w drugą stronę**: udaje drukarkę sieciową, a wszystko, co inne aplikacje (ERP, Word, stary program magazynowy) na nią wydrukują, trafia do **Inboxa** na stronie **Urządzenia**. +Konektor potrafi działać też **w drugą stronę**: udaje zwykłą drukarkę sieciową, a wszystko, co inne aplikacje (system magazynowy, Word, stary program) na nią wydrukują, trafia do **Inboxa** (skrzynki odbiorczej) na stronie **Urządzenia**. -1. W `config.yaml` agenta włącz sekcję `capture` (instrukcja krok po kroku, razem z konfiguracją drukarki w Windows: `connector/README.md`). +1. W pliku `config.yaml` agenta włącz sekcję `capture` (instrukcja krok po kroku razem z konfiguracją drukarki w Windows znajduje się w `connector/README.md`). 2. Wydrukuj coś z dowolnej aplikacji na tę wirtualną drukarkę. -3. **Urządzenia → Inbox** → **Otwórz w edytorze** — etykieta staje się normalnym szablonem: rozmiar wykryty z kodu, teksty i kody kreskowe edytowalne. +3. Przejdź do **Urządzenia → Inbox** i kliknij **Otwórz w edytorze** — etykieta staje się zwykłym szablonem: rozmiar wykryty z kodu, teksty i kody kreskowe od razu edytowalne. + +![strona Urządzenia → Inbox z listą kilku przechwyconych etykiet (miniatury, znaczniki czasu) i przyciskiem „Otwórz w edytorze".](screenshots/help/pl/devices-inbox.png) Z Inboxa możesz też skopiować surowy kod ZPL albo usunąć wpis. Program przechowuje maksymalnie 200 ostatnich przechwyceń na urządzenie. @@ -392,11 +587,15 @@ Z Inboxa możesz też skopiować surowy kod ZPL albo usunąć wpis. Program prze ## 7c. Historia wygenerowanych plików -Menu → **Historia**. Każde wygenerowanie — pojedyncza etykieta (**Pobierz PDF**) i seria (PDF lub wsadowy ZPL) — trafia na listę: nazwa szablonu, typ, liczba etykiet, rozmiar, data. Kliknij **Pobierz**, aby pobrać plik ponownie, albo **Usuń**, aby skasować wpis. Pliki są dostępne przez **30 dni**, potem są automatycznie usuwane. +W menu kliknij **Historia**. Każde wygenerowanie — pojedyncza etykieta (**Pobierz PDF**) i cała seria (PDF albo wsadowy ZPL) — trafia na listę: nazwa szablonu, typ, liczba etykiet, rozmiar, data. + +![strona Historia z listą wygenerowanych plików — kolumny nazwa szablonu, typ, liczba etykiet, rozmiar, data — i przyciskami Pobierz oraz Usuń w wierszu.](screenshots/help/pl/generated-history.png) + +Kliknij **Pobierz**, aby pobrać plik ponownie bez generowania go od nowa, albo **Usuń**, aby skasować wpis. Pliki są dostępne przez **30 dni**, potem są automatycznie usuwane. --- -## 8. Administracja (tylko admin) +## 8. Administracja (tylko administrator) Lewe menu → **Administracja → Użytkownicy**. @@ -406,61 +605,79 @@ Lewe menu → **Administracja → Użytkownicy**. ### Tworzenie użytkownika -1. Klik **Utwórz konto**. -2. Wpisz email + hasło tymczasowe (min. 10 znaków, możesz wygenerować losowe). +1. Kliknij **Utwórz konto**. +2. Wpisz email i hasło tymczasowe (minimum 10 znaków; możesz też wygenerować losowe). 3. Wybierz rolę: - - **Administrator** — pełny dostęp + zarządzanie użytkownikami. - - **Edytor** — tworzy/edytuje swoje szablony i dataset'y. - - **Tylko podgląd** — może otwierać i podglądać, ale nie zapisuje. -4. Po kliknięciu *Utwórz* program pokaże hasło tymczasowe **tylko raz** — przekaż je użytkownikowi. + - **Administrator** — pełny dostęp, w tym zarządzanie użytkownikami. + - **Edytor** — tworzy i edytuje własne szablony oraz zestawy danych. + - **Tylko podgląd** — może otwierać i oglądać, ale nie zapisuje zmian. + +![okno „Utwórz konto" z polami Email, hasło tymczasowe (z przyciskiem „Generuj") i rozwijaną listą Rola (Administrator/Edytor/Tylko podgląd).](screenshots/help/pl/admin-create-user.png) + +4. Po kliknięciu **Utwórz** program pokaże hasło tymczasowe **tylko raz** — od razu przekaż je nowemu użytkownikowi. ### Reset hasła -Klik **Resetuj hasło** przy koncie → wygeneruj nowe tymczasowe → przekaż użytkownikowi. Przy następnym logowaniu zostanie zmuszony je zmienić. +1. Przy koncie użytkownika kliknij **Resetuj hasło**. +2. Program wygeneruje nowe hasło tymczasowe — przekaż je użytkownikowi. + +![wiersz użytkownika z kliknietym przyciskiem „Resetuj hasło" i oknem pokazującym nowo wygenerowane hasło tymczasowe, wyświetlone tylko raz.](screenshots/help/pl/admin-reset-password.png) + +Przy następnym logowaniu użytkownik zostanie poproszony o ustawienie własnego hasła. -### Aktywacja / dezaktywacja +### Aktywacja / dezaktywacja konta -Toggle **Aktywne** w wierszu użytkownika. Nie możesz dezaktywować własnego konta (zabezpieczenie). +Przełącznik **Aktywne** w wierszu użytkownika włącza lub wyłącza dostęp do konta. Nie możesz dezaktywować własnego konta — to zabezpieczenie przed przypadkowym zablokowaniem samej/samego siebie. + +![zbliżenie na przełącznik „Aktywne" w wierszu użytkownika, włączony (zielony); dla wiersza własnego konta przełącznik wyszarzony/nieaktywny.](screenshots/help/pl/admin-active-toggle.png) --- ## 8a. Import / eksport szablonów -Każdy szablon możesz zapisać jako jeden plik `.blg-template.json` (rozmiar etykiety + pozycje wszystkich obiektów + ich treść + obrazki zakodowane w pliku). Plik jest przenośny: zarchiwizujesz go, wyślesz mailem albo zaimportujesz na drugiej instancji BarcodeLabelGen. +Każdy szablon możesz zapisać jako jeden plik `.blg-template.json` — zawiera rozmiar etykiety, pozycje wszystkich obiektów, ich treść oraz obrazki zakodowane wprost w pliku. Taki plik jest przenośny: możesz go zarchiwizować, wysłać mailem albo zaimportować na innej instalacji BarcodeLabelGen. ### Eksport -Dwa miejsca: -- **Szablony** → najedź na kafelek szablonu → ikona **⬇** w prawym dolnym rogu. -- W edytorze → toolbar → przycisk **⬇ Eksportuj** (obok *Pobierz PDF*). +Masz dwie możliwości: + +- Na stronie **Szablony**: najedź myszką na kafelek szablonu i kliknij ikonę **⬇** w prawym dolnym rogu. +- W edytorze: w pasku narzędzi kliknij **⬇ Eksportuj** (obok *Pobierz PDF*). + +![zbliżenie na przycisk ⬇ Eksportuj w pasku narzędzi edytora, obok przycisku Pobierz PDF.](screenshots/help/pl/editor-export-button.png) -Pobiera się plik `.blg-template.json` — najlepiej trzymaj go w katalogu backupów. +Pobierze się plik `.blg-template.json` — najlepiej trzymaj go w folderze z backupami. ### Import -**Szablony** → **⬆ Importuj** otwiera 2-krokowe okno: +Na stronie **Szablony** kliknij **⬆ Importuj** — otworzy się okno w dwóch krokach: + +1. **Wybór pliku** — wskaż plik `.blg-template.json`. Program sprawdzi jego poprawność i pokaże podgląd. + +![Krok 1 okna importu z wybranym plikiem .blg-template.json, znacznikiem poprawności i podglądem zawartości.](screenshots/help/pl/import-step1.png) + +2. **Konfiguracja** — możesz tu: + - zmienić nazwę nowego szablonu (domyślnie ta z pliku; przy powtórzeniu nazwy program doda dopisek „(kopia)"), + - nadpisać rozmiar etykiety (puste pole = zostaw oryginalny), + - odznaczyć obiekty, których nie chcesz importować (lista z ikoną typu i podglądem treści przy każdym), + - dla każdego powtarzającego się obrazka wybrać: **Użyj istniejącego** (oszczędza miejsce) albo **Utwórz nową kopię**. -1. **Wybór pliku** — podaj `.blg-template.json`. Program sprawdza poprawność i pokazuje podgląd. -2. **Konfiguracja** — możesz: - - zmienić **nazwę** nowego szablonu (domyślnie nazwa z pliku; jeśli kolizja → automatyczny suffix „(kopia)"), - - **nadpisać rozmiar** (puste = oryginalny), - - **odznaczyć obiekty** które nie mają zostać zaimportowane (czeklista — każdy obiekt z ikoną typu i preview treści), - - dla każdego **duplikatu obrazka** wybrać: *Użyj istniejącego* (oszczędność miejsca) lub *Utwórz nową kopię*. +![Krok 2 okna importu z polem nazwy, polami nadpisania szerokości/wysokości, listą obiektów z checkboxami i wyborem Użyj istniejącego / Utwórz nową kopię przy zdublowanym obrazku.](screenshots/help/pl/import-step2.png) -Klik **Importuj** → tworzy nowy szablon i otwiera go w edytorze. +3. Kliknij **Importuj** — program tworzy nowy szablon i otwiera go w edytorze. -### Typowe przypadki użycia +### Typowe sytuacje, w których się to przydaje -- **Backup przed dużą zmianą** — eksportuj, zostaw plik w archiwum, edytuj swobodnie. Coś poszło źle → reimportuj. -- **Klonowanie układu na inny rozmiar** — eksport, import z nadpisanym rozmiarem (np. ta sama etykieta dla A6 i 100×50 mm). -- **Przeniesienie szablonu między instancjami** (dev → prod) — eksport po jednej stronie, import po drugiej. -- **Wybiórczy import** — bierzesz układ kodu kreskowego + 2-3 pola z gotowego szablonu, resztę odznaczasz. +- **Backup przed dużą zmianą** — wyeksportuj, zostaw plik w archiwum, edytuj bez obaw. Jeśli coś pójdzie nie tak — zaimportuj z powrotem. +- **Kopiowanie układu na inny rozmiar** — wyeksportuj, zaimportuj z nadpisanym rozmiarem (np. ta sama etykieta w wersji na A6 i na 100×50 mm). +- **Przeniesienie szablonu między instalacjami** (np. testowa → produkcyjna) — wyeksportuj po jednej stronie, zaimportuj po drugiej. +- **Wybiórczy import** — bierzesz np. tylko układ kodu kreskowego i 2-3 pola z gotowego szablonu, resztę odznaczasz. ### Limity i bezpieczeństwo -- Maks. 20 MB plik, 50 obiektów, 20 obrazków (5 MB każdy). -- Obrazki są weryfikowane: sha256 musi się zgadzać z zawartością base64. Pliki manipulowane są odrzucane. -- Nowy szablon zawsze trafia do twojego konta (niezależnie kto wyeksportował plik). +- Maksymalnie 20 MB plik, 50 obiektów, 20 obrazków (po 5 MB każdy). +- Obrazki są weryfikowane specjalną sumą kontrolną (**sha256** — cyfrowy „odcisk palca" pliku, który potwierdza, że nie został zmieniony). Pliki, które ktoś ręcznie zmodyfikował, są odrzucane. +- Nowy szablon zawsze trafia na Twoje konto, niezależnie od tego, kto wyeksportował plik. ## 9. Skróty klawiaturowe @@ -470,7 +687,7 @@ Klik **Importuj** → tworzy nowy szablon i otwiera go w edytorze. | Ctrl/Cmd + Z | Cofnij | | Ctrl/Cmd + Shift + Z | Ponów | | Ctrl/Cmd + A | Zaznacz wszystko (w canvasie) | -| Ctrl/Cmd + D | Duplikuj zaznaczone (offset +5 mm) | +| Ctrl/Cmd + D | Duplikuj zaznaczone (przesunięcie +5 mm) | | Alt + przeciąganie | Duplikuj zaznaczone pod kursorem | | Delete / Backspace | Usuń zaznaczone | | Shift + klik | Dodaj do zaznaczenia | @@ -479,6 +696,6 @@ Klik **Importuj** → tworzy nowy szablon i otwiera go w edytorze. ## 10. Wsparcie -Programem zarządza **Tomasz "Amigo" Lewandowski** — kontakt: dev@attv.uk · www.attv.uk. +Programem zarządza **Tomasz „Amigo" Lewandowski** — kontakt: dev@attv.uk · www.attv.uk. Kod źródłowy: github.com/AmigoUK/BarcodeLabelGen diff --git a/docs/screenshots/help/en/admin-active-toggle.png b/docs/screenshots/help/en/admin-active-toggle.png new file mode 100644 index 0000000..bf1729b Binary files /dev/null and b/docs/screenshots/help/en/admin-active-toggle.png differ diff --git a/docs/screenshots/help/en/admin-create-user.png b/docs/screenshots/help/en/admin-create-user.png new file mode 100644 index 0000000..568f169 Binary files /dev/null and b/docs/screenshots/help/en/admin-create-user.png differ diff --git a/docs/screenshots/help/en/admin-reset-password.png b/docs/screenshots/help/en/admin-reset-password.png new file mode 100644 index 0000000..1102403 Binary files /dev/null and b/docs/screenshots/help/en/admin-reset-password.png differ diff --git a/docs/screenshots/help/en/alignment-bar-groups.png b/docs/screenshots/help/en/alignment-bar-groups.png new file mode 100644 index 0000000..8337136 Binary files /dev/null and b/docs/screenshots/help/en/alignment-bar-groups.png differ diff --git a/docs/screenshots/help/en/autosave-status.png b/docs/screenshots/help/en/autosave-status.png new file mode 100644 index 0000000..db1497c Binary files /dev/null and b/docs/screenshots/help/en/autosave-status.png differ diff --git a/docs/screenshots/help/en/connect-wizard-download.png b/docs/screenshots/help/en/connect-wizard-download.png new file mode 100644 index 0000000..bcdf839 Binary files /dev/null and b/docs/screenshots/help/en/connect-wizard-download.png differ diff --git a/docs/screenshots/help/en/connect-wizard-name.png b/docs/screenshots/help/en/connect-wizard-name.png new file mode 100644 index 0000000..598b05a Binary files /dev/null and b/docs/screenshots/help/en/connect-wizard-name.png differ diff --git a/docs/screenshots/help/en/connect-wizard-os.png b/docs/screenshots/help/en/connect-wizard-os.png new file mode 100644 index 0000000..cc2ccf3 Binary files /dev/null and b/docs/screenshots/help/en/connect-wizard-os.png differ diff --git a/docs/screenshots/help/en/connect-wizard-printer.png b/docs/screenshots/help/en/connect-wizard-printer.png new file mode 100644 index 0000000..078f7aa Binary files /dev/null and b/docs/screenshots/help/en/connect-wizard-printer.png differ diff --git a/docs/screenshots/help/en/connect-wizard-run.png b/docs/screenshots/help/en/connect-wizard-run.png new file mode 100644 index 0000000..de7e795 Binary files /dev/null and b/docs/screenshots/help/en/connect-wizard-run.png differ diff --git a/docs/screenshots/help/en/connect-wizard-waiting.png b/docs/screenshots/help/en/connect-wizard-waiting.png new file mode 100644 index 0000000..7d49f9f Binary files /dev/null and b/docs/screenshots/help/en/connect-wizard-waiting.png differ diff --git a/docs/screenshots/help/en/connector-add-device.png b/docs/screenshots/help/en/connector-add-device.png new file mode 100644 index 0000000..e7dbe2f Binary files /dev/null and b/docs/screenshots/help/en/connector-add-device.png differ diff --git a/docs/screenshots/help/en/connector-fastpath.png b/docs/screenshots/help/en/connector-fastpath.png new file mode 100644 index 0000000..9f61902 Binary files /dev/null and b/docs/screenshots/help/en/connector-fastpath.png differ diff --git a/docs/screenshots/help/en/connector-print-dialog.png b/docs/screenshots/help/en/connector-print-dialog.png new file mode 100644 index 0000000..ac5611f Binary files /dev/null and b/docs/screenshots/help/en/connector-print-dialog.png differ diff --git a/docs/screenshots/help/en/dashboard-empty.png b/docs/screenshots/help/en/dashboard-empty.png new file mode 100644 index 0000000..fa40e98 Binary files /dev/null and b/docs/screenshots/help/en/dashboard-empty.png differ diff --git a/docs/screenshots/help/en/date-chip.png b/docs/screenshots/help/en/date-chip.png index 6ec8aa8..2978cd9 100644 Binary files a/docs/screenshots/help/en/date-chip.png and b/docs/screenshots/help/en/date-chip.png differ diff --git a/docs/screenshots/help/en/devices-inbox.png b/docs/screenshots/help/en/devices-inbox.png new file mode 100644 index 0000000..8f18c28 Binary files /dev/null and b/docs/screenshots/help/en/devices-inbox.png differ diff --git a/docs/screenshots/help/en/duplicate-altdrag.png b/docs/screenshots/help/en/duplicate-altdrag.png new file mode 100644 index 0000000..48fecb4 Binary files /dev/null and b/docs/screenshots/help/en/duplicate-altdrag.png differ diff --git a/docs/screenshots/help/en/dynamic-fields.png b/docs/screenshots/help/en/dynamic-fields.png index ad444ba..c8cf5e3 100644 Binary files a/docs/screenshots/help/en/dynamic-fields.png and b/docs/screenshots/help/en/dynamic-fields.png differ diff --git a/docs/screenshots/help/en/editor-export-button.png b/docs/screenshots/help/en/editor-export-button.png new file mode 100644 index 0000000..7afb8c6 Binary files /dev/null and b/docs/screenshots/help/en/editor-export-button.png differ diff --git a/docs/screenshots/help/en/editor-overview.png b/docs/screenshots/help/en/editor-overview.png index 54d008e..7efd0d3 100644 Binary files a/docs/screenshots/help/en/editor-overview.png and b/docs/screenshots/help/en/editor-overview.png differ diff --git a/docs/screenshots/help/en/folder-edit.png b/docs/screenshots/help/en/folder-edit.png new file mode 100644 index 0000000..a68c46e Binary files /dev/null and b/docs/screenshots/help/en/folder-edit.png differ diff --git a/docs/screenshots/help/en/folder-menu.png b/docs/screenshots/help/en/folder-menu.png new file mode 100644 index 0000000..173bf2e Binary files /dev/null and b/docs/screenshots/help/en/folder-menu.png differ diff --git a/docs/screenshots/help/en/folder-rail.png b/docs/screenshots/help/en/folder-rail.png new file mode 100644 index 0000000..68570cc Binary files /dev/null and b/docs/screenshots/help/en/folder-rail.png differ diff --git a/docs/screenshots/help/en/generated-history.png b/docs/screenshots/help/en/generated-history.png new file mode 100644 index 0000000..896338e Binary files /dev/null and b/docs/screenshots/help/en/generated-history.png differ diff --git a/docs/screenshots/help/en/import-step1.png b/docs/screenshots/help/en/import-step1.png new file mode 100644 index 0000000..6d5988a Binary files /dev/null and b/docs/screenshots/help/en/import-step1.png differ diff --git a/docs/screenshots/help/en/import-step2.png b/docs/screenshots/help/en/import-step2.png new file mode 100644 index 0000000..e3902db Binary files /dev/null and b/docs/screenshots/help/en/import-step2.png differ diff --git a/docs/screenshots/help/en/label-size.png b/docs/screenshots/help/en/label-size.png index 0962f41..b8f13d9 100644 Binary files a/docs/screenshots/help/en/label-size.png and b/docs/screenshots/help/en/label-size.png differ diff --git a/docs/screenshots/help/en/layer-buttons.png b/docs/screenshots/help/en/layer-buttons.png new file mode 100644 index 0000000..081c005 Binary files /dev/null and b/docs/screenshots/help/en/layer-buttons.png differ diff --git a/docs/screenshots/help/en/library-page.png b/docs/screenshots/help/en/library-page.png new file mode 100644 index 0000000..8b96b79 Binary files /dev/null and b/docs/screenshots/help/en/library-page.png differ diff --git a/docs/screenshots/help/en/lock-print-checkboxes.png b/docs/screenshots/help/en/lock-print-checkboxes.png new file mode 100644 index 0000000..026640e Binary files /dev/null and b/docs/screenshots/help/en/lock-print-checkboxes.png differ diff --git a/docs/screenshots/help/en/multiselect.png b/docs/screenshots/help/en/multiselect.png new file mode 100644 index 0000000..713b76f Binary files /dev/null and b/docs/screenshots/help/en/multiselect.png differ diff --git a/docs/screenshots/help/en/new-template-button.png b/docs/screenshots/help/en/new-template-button.png new file mode 100644 index 0000000..3a39154 Binary files /dev/null and b/docs/screenshots/help/en/new-template-button.png differ diff --git a/docs/screenshots/help/en/new-template-dialog.png b/docs/screenshots/help/en/new-template-dialog.png new file mode 100644 index 0000000..5ebf3d8 Binary files /dev/null and b/docs/screenshots/help/en/new-template-dialog.png differ diff --git a/docs/screenshots/help/en/object-background.png b/docs/screenshots/help/en/object-background.png new file mode 100644 index 0000000..30d4395 Binary files /dev/null and b/docs/screenshots/help/en/object-background.png differ diff --git a/docs/screenshots/help/en/object-barcode.png b/docs/screenshots/help/en/object-barcode.png new file mode 100644 index 0000000..e15c7a7 Binary files /dev/null and b/docs/screenshots/help/en/object-barcode.png differ diff --git a/docs/screenshots/help/en/object-image.png b/docs/screenshots/help/en/object-image.png new file mode 100644 index 0000000..ed2f18f Binary files /dev/null and b/docs/screenshots/help/en/object-image.png differ diff --git a/docs/screenshots/help/en/object-shapes.png b/docs/screenshots/help/en/object-shapes.png new file mode 100644 index 0000000..accc76b Binary files /dev/null and b/docs/screenshots/help/en/object-shapes.png differ diff --git a/docs/screenshots/help/en/object-table.png b/docs/screenshots/help/en/object-table.png new file mode 100644 index 0000000..49e7849 Binary files /dev/null and b/docs/screenshots/help/en/object-table.png differ diff --git a/docs/screenshots/help/en/object-text.png b/docs/screenshots/help/en/object-text.png new file mode 100644 index 0000000..9643bb6 Binary files /dev/null and b/docs/screenshots/help/en/object-text.png differ diff --git a/docs/screenshots/help/en/object-textblock.png b/docs/screenshots/help/en/object-textblock.png new file mode 100644 index 0000000..2b98a5e Binary files /dev/null and b/docs/screenshots/help/en/object-textblock.png differ diff --git a/docs/screenshots/help/en/preview-pdf.png b/docs/screenshots/help/en/preview-pdf.png new file mode 100644 index 0000000..61fa4af Binary files /dev/null and b/docs/screenshots/help/en/preview-pdf.png differ diff --git a/docs/screenshots/help/en/resize-handles.png b/docs/screenshots/help/en/resize-handles.png new file mode 100644 index 0000000..77060fd Binary files /dev/null and b/docs/screenshots/help/en/resize-handles.png differ diff --git a/docs/screenshots/help/en/series-csv-preview.png b/docs/screenshots/help/en/series-csv-preview.png new file mode 100644 index 0000000..e302b63 Binary files /dev/null and b/docs/screenshots/help/en/series-csv-preview.png differ diff --git a/docs/screenshots/help/en/series-filter.png b/docs/screenshots/help/en/series-filter.png new file mode 100644 index 0000000..bcd4a2f Binary files /dev/null and b/docs/screenshots/help/en/series-filter.png differ diff --git a/docs/screenshots/help/en/series-map.png b/docs/screenshots/help/en/series-map.png index 3104c02..80b37d4 100644 Binary files a/docs/screenshots/help/en/series-map.png and b/docs/screenshots/help/en/series-map.png differ diff --git a/docs/screenshots/help/en/series-progress.png b/docs/screenshots/help/en/series-progress.png new file mode 100644 index 0000000..8b0b001 Binary files /dev/null and b/docs/screenshots/help/en/series-progress.png differ diff --git a/docs/screenshots/help/en/series-sqlite-sql.png b/docs/screenshots/help/en/series-sqlite-sql.png new file mode 100644 index 0000000..553be5d Binary files /dev/null and b/docs/screenshots/help/en/series-sqlite-sql.png differ diff --git a/docs/screenshots/help/en/series-sqlite-tables.png b/docs/screenshots/help/en/series-sqlite-tables.png new file mode 100644 index 0000000..4922613 Binary files /dev/null and b/docs/screenshots/help/en/series-sqlite-tables.png differ diff --git a/docs/screenshots/help/en/series-step1-upload.png b/docs/screenshots/help/en/series-step1-upload.png new file mode 100644 index 0000000..110a2d9 Binary files /dev/null and b/docs/screenshots/help/en/series-step1-upload.png differ diff --git a/docs/screenshots/help/en/series-warnings-list.png b/docs/screenshots/help/en/series-warnings-list.png new file mode 100644 index 0000000..a59ee4f Binary files /dev/null and b/docs/screenshots/help/en/series-warnings-list.png differ diff --git a/docs/screenshots/help/en/set-new-password.png b/docs/screenshots/help/en/set-new-password.png new file mode 100644 index 0000000..a3ca474 Binary files /dev/null and b/docs/screenshots/help/en/set-new-password.png differ diff --git a/docs/screenshots/help/en/share-template.png b/docs/screenshots/help/en/share-template.png new file mode 100644 index 0000000..173bf2e Binary files /dev/null and b/docs/screenshots/help/en/share-template.png differ diff --git a/docs/screenshots/help/en/templates.png b/docs/screenshots/help/en/templates.png index b24e404..39dd0a9 100644 Binary files a/docs/screenshots/help/en/templates.png and b/docs/screenshots/help/en/templates.png differ diff --git a/docs/screenshots/help/en/tspl-export.png b/docs/screenshots/help/en/tspl-export.png new file mode 100644 index 0000000..d1f90c6 Binary files /dev/null and b/docs/screenshots/help/en/tspl-export.png differ diff --git a/docs/screenshots/help/en/undo-redo-buttons.png b/docs/screenshots/help/en/undo-redo-buttons.png new file mode 100644 index 0000000..6305532 Binary files /dev/null and b/docs/screenshots/help/en/undo-redo-buttons.png differ diff --git a/docs/screenshots/help/en/users-admin.png b/docs/screenshots/help/en/users-admin.png index 89d74ed..d4d9563 100644 Binary files a/docs/screenshots/help/en/users-admin.png and b/docs/screenshots/help/en/users-admin.png differ diff --git a/docs/screenshots/help/en/version-history.png b/docs/screenshots/help/en/version-history.png new file mode 100644 index 0000000..42bf4e7 Binary files /dev/null and b/docs/screenshots/help/en/version-history.png differ diff --git a/docs/screenshots/help/en/warnings-chip.png b/docs/screenshots/help/en/warnings-chip.png new file mode 100644 index 0000000..60a8212 Binary files /dev/null and b/docs/screenshots/help/en/warnings-chip.png differ diff --git a/docs/screenshots/help/en/zpl-export.png b/docs/screenshots/help/en/zpl-export.png index e59f418..9e8fda1 100644 Binary files a/docs/screenshots/help/en/zpl-export.png and b/docs/screenshots/help/en/zpl-export.png differ diff --git a/docs/screenshots/help/en/zpl-import.png b/docs/screenshots/help/en/zpl-import.png index 827374d..d48a023 100644 Binary files a/docs/screenshots/help/en/zpl-import.png and b/docs/screenshots/help/en/zpl-import.png differ diff --git a/docs/screenshots/help/pl/admin-active-toggle.png b/docs/screenshots/help/pl/admin-active-toggle.png new file mode 100644 index 0000000..18af277 Binary files /dev/null and b/docs/screenshots/help/pl/admin-active-toggle.png differ diff --git a/docs/screenshots/help/pl/admin-create-user.png b/docs/screenshots/help/pl/admin-create-user.png new file mode 100644 index 0000000..2f0e665 Binary files /dev/null and b/docs/screenshots/help/pl/admin-create-user.png differ diff --git a/docs/screenshots/help/pl/admin-reset-password.png b/docs/screenshots/help/pl/admin-reset-password.png new file mode 100644 index 0000000..d940a20 Binary files /dev/null and b/docs/screenshots/help/pl/admin-reset-password.png differ diff --git a/docs/screenshots/help/pl/alignment-bar-groups.png b/docs/screenshots/help/pl/alignment-bar-groups.png new file mode 100644 index 0000000..f57d71d Binary files /dev/null and b/docs/screenshots/help/pl/alignment-bar-groups.png differ diff --git a/docs/screenshots/help/pl/autosave-status.png b/docs/screenshots/help/pl/autosave-status.png new file mode 100644 index 0000000..0c5a209 Binary files /dev/null and b/docs/screenshots/help/pl/autosave-status.png differ diff --git a/docs/screenshots/help/pl/connect-wizard-download.png b/docs/screenshots/help/pl/connect-wizard-download.png new file mode 100644 index 0000000..e7dbfe6 Binary files /dev/null and b/docs/screenshots/help/pl/connect-wizard-download.png differ diff --git a/docs/screenshots/help/pl/connect-wizard-name.png b/docs/screenshots/help/pl/connect-wizard-name.png new file mode 100644 index 0000000..79348ea Binary files /dev/null and b/docs/screenshots/help/pl/connect-wizard-name.png differ diff --git a/docs/screenshots/help/pl/connect-wizard-os.png b/docs/screenshots/help/pl/connect-wizard-os.png new file mode 100644 index 0000000..e9517e8 Binary files /dev/null and b/docs/screenshots/help/pl/connect-wizard-os.png differ diff --git a/docs/screenshots/help/pl/connect-wizard-printer.png b/docs/screenshots/help/pl/connect-wizard-printer.png new file mode 100644 index 0000000..298fda3 Binary files /dev/null and b/docs/screenshots/help/pl/connect-wizard-printer.png differ diff --git a/docs/screenshots/help/pl/connect-wizard-run.png b/docs/screenshots/help/pl/connect-wizard-run.png new file mode 100644 index 0000000..7f7fa65 Binary files /dev/null and b/docs/screenshots/help/pl/connect-wizard-run.png differ diff --git a/docs/screenshots/help/pl/connect-wizard-waiting.png b/docs/screenshots/help/pl/connect-wizard-waiting.png new file mode 100644 index 0000000..1e0037f Binary files /dev/null and b/docs/screenshots/help/pl/connect-wizard-waiting.png differ diff --git a/docs/screenshots/help/pl/connector-add-device.png b/docs/screenshots/help/pl/connector-add-device.png new file mode 100644 index 0000000..f8e0497 Binary files /dev/null and b/docs/screenshots/help/pl/connector-add-device.png differ diff --git a/docs/screenshots/help/pl/connector-fastpath.png b/docs/screenshots/help/pl/connector-fastpath.png new file mode 100644 index 0000000..35fd154 Binary files /dev/null and b/docs/screenshots/help/pl/connector-fastpath.png differ diff --git a/docs/screenshots/help/pl/connector-print-dialog.png b/docs/screenshots/help/pl/connector-print-dialog.png new file mode 100644 index 0000000..481d90f Binary files /dev/null and b/docs/screenshots/help/pl/connector-print-dialog.png differ diff --git a/docs/screenshots/help/pl/dashboard-empty.png b/docs/screenshots/help/pl/dashboard-empty.png new file mode 100644 index 0000000..93a2941 Binary files /dev/null and b/docs/screenshots/help/pl/dashboard-empty.png differ diff --git a/docs/screenshots/help/pl/date-chip.png b/docs/screenshots/help/pl/date-chip.png index 02bc1a6..83106d9 100644 Binary files a/docs/screenshots/help/pl/date-chip.png and b/docs/screenshots/help/pl/date-chip.png differ diff --git a/docs/screenshots/help/pl/devices-inbox.png b/docs/screenshots/help/pl/devices-inbox.png new file mode 100644 index 0000000..ac4938a Binary files /dev/null and b/docs/screenshots/help/pl/devices-inbox.png differ diff --git a/docs/screenshots/help/pl/duplicate-altdrag.png b/docs/screenshots/help/pl/duplicate-altdrag.png new file mode 100644 index 0000000..e90704c Binary files /dev/null and b/docs/screenshots/help/pl/duplicate-altdrag.png differ diff --git a/docs/screenshots/help/pl/dynamic-fields.png b/docs/screenshots/help/pl/dynamic-fields.png index f4e2f33..fde40e6 100644 Binary files a/docs/screenshots/help/pl/dynamic-fields.png and b/docs/screenshots/help/pl/dynamic-fields.png differ diff --git a/docs/screenshots/help/pl/editor-export-button.png b/docs/screenshots/help/pl/editor-export-button.png new file mode 100644 index 0000000..4bee765 Binary files /dev/null and b/docs/screenshots/help/pl/editor-export-button.png differ diff --git a/docs/screenshots/help/pl/editor-overview.png b/docs/screenshots/help/pl/editor-overview.png index 46eed4f..0b66dd5 100644 Binary files a/docs/screenshots/help/pl/editor-overview.png and b/docs/screenshots/help/pl/editor-overview.png differ diff --git a/docs/screenshots/help/pl/folder-edit.png b/docs/screenshots/help/pl/folder-edit.png new file mode 100644 index 0000000..67aab4e Binary files /dev/null and b/docs/screenshots/help/pl/folder-edit.png differ diff --git a/docs/screenshots/help/pl/folder-menu.png b/docs/screenshots/help/pl/folder-menu.png new file mode 100644 index 0000000..a8eccec Binary files /dev/null and b/docs/screenshots/help/pl/folder-menu.png differ diff --git a/docs/screenshots/help/pl/folder-rail.png b/docs/screenshots/help/pl/folder-rail.png new file mode 100644 index 0000000..83bb60b Binary files /dev/null and b/docs/screenshots/help/pl/folder-rail.png differ diff --git a/docs/screenshots/help/pl/generated-history.png b/docs/screenshots/help/pl/generated-history.png new file mode 100644 index 0000000..9a94002 Binary files /dev/null and b/docs/screenshots/help/pl/generated-history.png differ diff --git a/docs/screenshots/help/pl/import-step1.png b/docs/screenshots/help/pl/import-step1.png new file mode 100644 index 0000000..7b2f755 Binary files /dev/null and b/docs/screenshots/help/pl/import-step1.png differ diff --git a/docs/screenshots/help/pl/import-step2.png b/docs/screenshots/help/pl/import-step2.png new file mode 100644 index 0000000..b62d750 Binary files /dev/null and b/docs/screenshots/help/pl/import-step2.png differ diff --git a/docs/screenshots/help/pl/label-size.png b/docs/screenshots/help/pl/label-size.png index 0d3cf6d..f761d75 100644 Binary files a/docs/screenshots/help/pl/label-size.png and b/docs/screenshots/help/pl/label-size.png differ diff --git a/docs/screenshots/help/pl/layer-buttons.png b/docs/screenshots/help/pl/layer-buttons.png new file mode 100644 index 0000000..b6c4be6 Binary files /dev/null and b/docs/screenshots/help/pl/layer-buttons.png differ diff --git a/docs/screenshots/help/pl/library-page.png b/docs/screenshots/help/pl/library-page.png new file mode 100644 index 0000000..ea4ae56 Binary files /dev/null and b/docs/screenshots/help/pl/library-page.png differ diff --git a/docs/screenshots/help/pl/lock-print-checkboxes.png b/docs/screenshots/help/pl/lock-print-checkboxes.png new file mode 100644 index 0000000..ae299bd Binary files /dev/null and b/docs/screenshots/help/pl/lock-print-checkboxes.png differ diff --git a/docs/screenshots/help/pl/multiselect.png b/docs/screenshots/help/pl/multiselect.png new file mode 100644 index 0000000..827354c Binary files /dev/null and b/docs/screenshots/help/pl/multiselect.png differ diff --git a/docs/screenshots/help/pl/new-template-button.png b/docs/screenshots/help/pl/new-template-button.png new file mode 100644 index 0000000..4de69af Binary files /dev/null and b/docs/screenshots/help/pl/new-template-button.png differ diff --git a/docs/screenshots/help/pl/new-template-dialog.png b/docs/screenshots/help/pl/new-template-dialog.png new file mode 100644 index 0000000..8ab74e2 Binary files /dev/null and b/docs/screenshots/help/pl/new-template-dialog.png differ diff --git a/docs/screenshots/help/pl/object-background.png b/docs/screenshots/help/pl/object-background.png new file mode 100644 index 0000000..cf0820f Binary files /dev/null and b/docs/screenshots/help/pl/object-background.png differ diff --git a/docs/screenshots/help/pl/object-barcode.png b/docs/screenshots/help/pl/object-barcode.png new file mode 100644 index 0000000..807ba22 Binary files /dev/null and b/docs/screenshots/help/pl/object-barcode.png differ diff --git a/docs/screenshots/help/pl/object-image.png b/docs/screenshots/help/pl/object-image.png new file mode 100644 index 0000000..e73f5d4 Binary files /dev/null and b/docs/screenshots/help/pl/object-image.png differ diff --git a/docs/screenshots/help/pl/object-shapes.png b/docs/screenshots/help/pl/object-shapes.png new file mode 100644 index 0000000..852744f Binary files /dev/null and b/docs/screenshots/help/pl/object-shapes.png differ diff --git a/docs/screenshots/help/pl/object-table.png b/docs/screenshots/help/pl/object-table.png new file mode 100644 index 0000000..f4c4eec Binary files /dev/null and b/docs/screenshots/help/pl/object-table.png differ diff --git a/docs/screenshots/help/pl/object-text.png b/docs/screenshots/help/pl/object-text.png new file mode 100644 index 0000000..99a90e3 Binary files /dev/null and b/docs/screenshots/help/pl/object-text.png differ diff --git a/docs/screenshots/help/pl/object-textblock.png b/docs/screenshots/help/pl/object-textblock.png new file mode 100644 index 0000000..37b01da Binary files /dev/null and b/docs/screenshots/help/pl/object-textblock.png differ diff --git a/docs/screenshots/help/pl/preview-pdf.png b/docs/screenshots/help/pl/preview-pdf.png new file mode 100644 index 0000000..0f264ac Binary files /dev/null and b/docs/screenshots/help/pl/preview-pdf.png differ diff --git a/docs/screenshots/help/pl/resize-handles.png b/docs/screenshots/help/pl/resize-handles.png new file mode 100644 index 0000000..77060fd Binary files /dev/null and b/docs/screenshots/help/pl/resize-handles.png differ diff --git a/docs/screenshots/help/pl/series-csv-preview.png b/docs/screenshots/help/pl/series-csv-preview.png new file mode 100644 index 0000000..966d80e Binary files /dev/null and b/docs/screenshots/help/pl/series-csv-preview.png differ diff --git a/docs/screenshots/help/pl/series-filter.png b/docs/screenshots/help/pl/series-filter.png new file mode 100644 index 0000000..96f1152 Binary files /dev/null and b/docs/screenshots/help/pl/series-filter.png differ diff --git a/docs/screenshots/help/pl/series-map.png b/docs/screenshots/help/pl/series-map.png index 11cfa5a..1d258e9 100644 Binary files a/docs/screenshots/help/pl/series-map.png and b/docs/screenshots/help/pl/series-map.png differ diff --git a/docs/screenshots/help/pl/series-progress.png b/docs/screenshots/help/pl/series-progress.png new file mode 100644 index 0000000..18ca204 Binary files /dev/null and b/docs/screenshots/help/pl/series-progress.png differ diff --git a/docs/screenshots/help/pl/series-sqlite-sql.png b/docs/screenshots/help/pl/series-sqlite-sql.png new file mode 100644 index 0000000..bd7cbe0 Binary files /dev/null and b/docs/screenshots/help/pl/series-sqlite-sql.png differ diff --git a/docs/screenshots/help/pl/series-sqlite-tables.png b/docs/screenshots/help/pl/series-sqlite-tables.png new file mode 100644 index 0000000..5d6ae43 Binary files /dev/null and b/docs/screenshots/help/pl/series-sqlite-tables.png differ diff --git a/docs/screenshots/help/pl/series-step1-upload.png b/docs/screenshots/help/pl/series-step1-upload.png new file mode 100644 index 0000000..0b8fe17 Binary files /dev/null and b/docs/screenshots/help/pl/series-step1-upload.png differ diff --git a/docs/screenshots/help/pl/series-warnings-list.png b/docs/screenshots/help/pl/series-warnings-list.png new file mode 100644 index 0000000..31cb8c0 Binary files /dev/null and b/docs/screenshots/help/pl/series-warnings-list.png differ diff --git a/docs/screenshots/help/pl/set-new-password.png b/docs/screenshots/help/pl/set-new-password.png new file mode 100644 index 0000000..96fa6ae Binary files /dev/null and b/docs/screenshots/help/pl/set-new-password.png differ diff --git a/docs/screenshots/help/pl/share-template.png b/docs/screenshots/help/pl/share-template.png new file mode 100644 index 0000000..a8eccec Binary files /dev/null and b/docs/screenshots/help/pl/share-template.png differ diff --git a/docs/screenshots/help/pl/templates.png b/docs/screenshots/help/pl/templates.png index 6b8d648..29d5592 100644 Binary files a/docs/screenshots/help/pl/templates.png and b/docs/screenshots/help/pl/templates.png differ diff --git a/docs/screenshots/help/pl/tspl-export.png b/docs/screenshots/help/pl/tspl-export.png new file mode 100644 index 0000000..40f8567 Binary files /dev/null and b/docs/screenshots/help/pl/tspl-export.png differ diff --git a/docs/screenshots/help/pl/undo-redo-buttons.png b/docs/screenshots/help/pl/undo-redo-buttons.png new file mode 100644 index 0000000..2d8128b Binary files /dev/null and b/docs/screenshots/help/pl/undo-redo-buttons.png differ diff --git a/docs/screenshots/help/pl/users-admin.png b/docs/screenshots/help/pl/users-admin.png index 6f54d65..c85c8be 100644 Binary files a/docs/screenshots/help/pl/users-admin.png and b/docs/screenshots/help/pl/users-admin.png differ diff --git a/docs/screenshots/help/pl/version-history.png b/docs/screenshots/help/pl/version-history.png new file mode 100644 index 0000000..5d60a54 Binary files /dev/null and b/docs/screenshots/help/pl/version-history.png differ diff --git a/docs/screenshots/help/pl/warnings-chip.png b/docs/screenshots/help/pl/warnings-chip.png new file mode 100644 index 0000000..d03eb5b Binary files /dev/null and b/docs/screenshots/help/pl/warnings-chip.png differ diff --git a/docs/screenshots/help/pl/zpl-export.png b/docs/screenshots/help/pl/zpl-export.png index 712a014..05b243b 100644 Binary files a/docs/screenshots/help/pl/zpl-export.png and b/docs/screenshots/help/pl/zpl-export.png differ diff --git a/docs/screenshots/help/pl/zpl-import.png b/docs/screenshots/help/pl/zpl-import.png index 7c06930..bb4453b 100644 Binary files a/docs/screenshots/help/pl/zpl-import.png and b/docs/screenshots/help/pl/zpl-import.png differ diff --git a/tools/capture-help-screenshots.py b/tools/capture-help-screenshots.py index a0b2507..36807f5 100644 --- a/tools/capture-help-screenshots.py +++ b/tools/capture-help-screenshots.py @@ -1,50 +1,411 @@ #!/usr/bin/env python3 -"""Zrzuty ekranu do docs/HELP.{pl,en}.md — headless Chromium + sesja z API.""" -# -# Regeneruje zrzuty do docs/HELP.{pl,en}.md. -# Wymagania: pip install playwright && playwright install chromium --with-deps; -# zalogowane sesje curl w /tmp/blg.txt (admin) i /tmp/blg-demo.txt (demo@blg.local) -# oraz szablon demo (id przekazywany argumentem) — patrz docs/PROJECT.md. +"""Zrzuty ekranu do docs/HELP.{pl,en}.md — headless Chromium + sesja z API. +Regeneruje wszystkie zrzuty w docs/screenshots/help/{pl,en}/ opisane w +docs/superpowers (F37 help guide). Wymagania: + pip install playwright && playwright install chromium + zalogowane sesje curl w /tmp/blg.txt (admin) i /tmp/blg-demo.txt (demo@blg.local) + demo dataset CSV w /tmp/demo-data.csv (kolumny name,sku,cena) + +Jednorazowe dane pomocnicze (foldery, "galeria obiektów", udostępniony +szablon admina, urządzenie z lokalnym agentem, przechwycone etykiety) są +tworzone automatycznie (idempotentnie) przez ensure_setup() poniżej — patrz +funkcja dla pełnej listy encji i identyfikatorów. + +Uruchomienie: python3 capture-help-screenshots.py +""" + +from __future__ import annotations + +import json import re +import ssl +import subprocess import sys +import time +import urllib.error +import urllib.request from pathlib import Path -from playwright.sync_api import sync_playwright +from playwright.sync_api import Page, sync_playwright BASE = "https://linuxserv1.tailc29352.ts.net:18003" DOMAIN = "linuxserv1.tailc29352.ts.net" OUT = Path("/var/www/html/BarcodeLabelGen/docs/screenshots/help") -TPL_ID = sys.argv[1] if len(sys.argv) > 1 else "97" +SCRATCH = Path( + "/tmp/claude-0/-var-www-html-BarcodeLabelGen/2144656e-8c4a-44ed-868c-8a7c89da30d7/scratchpad" +) + +DEMO_JAR = "/tmp/blg-demo.txt" +ADMIN_JAR = "/tmp/blg.txt" +DEMO_EMAIL = "demo@blg.local" +DEMO_PASSWORD = "demo-docs-password-456!" +ADMIN_EMAIL = "shots-admin@blg.local" +ADMIN_PASSWORD = "shots-admin-pass-789!" +# The account is intentionally toggled back to "must change password" (via +# a self-targeted admin reset-password call) at the end of every admin +# capture block, so each run of this script reproduces the forced +# password-change screen honestly rather than assuming it from a fixture. +ADMIN_FINAL_PASSWORD = "shots-admin-final-2026!" + +# Internal dev/test tooling only, never used against production: the target +# is a Tailscale-only staging host serving a self-signed cert (same pattern +# as the `curl -sk` / Playwright `ignore_https_errors=True` used throughout +# this dossier's other capture scripts), so certificate verification is +# intentionally relaxed here. +SSL_CTX = ssl.create_default_context() +SSL_CTX.check_hostname = False +SSL_CTX.verify_mode = ssl.CERT_NONE + +RESULTS: dict[str, dict[str, str]] = {} + + +def mark(name: str, lang: str, status: str) -> None: + RESULTS.setdefault(name, {})[lang] = status + print(f"[{lang}] {name}: {status}") + + +# -------------------------------------------------------------------------- +# Tiny cookie-jar based HTTP client (no `requests` dependency available). +# -------------------------------------------------------------------------- + + +class Session: + def __init__(self, jar_path: str, email: str, password: str): + self.jar_path = jar_path + self.email = email + self.password = password + self.cookies: dict[str, str] = {} + self._load_jar() + + def _load_jar(self) -> None: + p = Path(self.jar_path) + if not p.exists(): + return + for line in p.read_text().splitlines(): + raw = line + if raw.startswith("#HttpOnly_"): + raw = raw[len("#HttpOnly_"):] + elif raw.startswith("#") or not raw.strip(): + continue + parts = raw.split("\t") + if len(parts) >= 7: + self.cookies[parts[5]] = parts[6] + + def _cookie_header(self) -> str: + return "; ".join(f"{k}={v}" for k, v in self.cookies.items()) + + def request(self, method: str, path: str, body: dict | None = None, auth: str | None = None): + url = f"{BASE}{path}" + data = json.dumps(body).encode() if body is not None else None + headers = {"Content-Type": "application/json"} + if auth: + headers["Authorization"] = auth + else: + headers["Cookie"] = self._cookie_header() + csrf = self.cookies.get("csrf_token") + if csrf and method in ("POST", "PUT", "PATCH", "DELETE"): + headers["X-CSRF-Token"] = csrf + req = urllib.request.Request(url, data=data, headers=headers, method=method) + try: + with urllib.request.urlopen(req, context=SSL_CTX, timeout=20) as resp: + self._capture_cookies(resp) + raw = resp.read() + return resp.status, (json.loads(raw) if raw else None) + except urllib.error.HTTPError as e: + self._capture_cookies(e) + raw = e.read() + try: + return e.code, json.loads(raw) + except Exception: + return e.code, None + + def _capture_cookies(self, resp) -> None: + for header in resp.headers.get_all("Set-Cookie") or []: + kv = header.split(";", 1)[0] + if "=" in kv: + k, v = kv.split("=", 1) + self.cookies[k.strip()] = v.strip() + + def ensure_login(self, password_fallbacks: tuple[str, ...] = ()) -> None: + status, body = self.request("GET", "/api/me") + if status == 200: + return + # refresh csrf then log in — try the configured password first, then + # any fallbacks (e.g. the admin account's password after this same + # script completed its one-time forced change on a previous run). + self.request("GET", "/api/me") + for pw in (self.password, *password_fallbacks): + status, body = self.request( + "POST", "/api/auth/login", {"email": self.email, "password": pw} + ) + if status == 200: + self.password = pw + return + raise RuntimeError(f"login failed for {self.email}: {status} {body}") + + +def api_get(sess: Session, path: str): + return sess.request("GET", path) + + +def api_post(sess: Session, path: str, body: dict | None = None): + return sess.request("POST", path, body) + + +def api_put(sess: Session, path: str, body: dict | None = None): + return sess.request("PUT", path, body) + + +# -------------------------------------------------------------------------- +# One-time scratch-data setup (idempotent). Populates fixed IDs used below. +# -------------------------------------------------------------------------- + +IDS = { + "tpl_main": 97, # "Etykieta produktu 50×30" — demo's main shared template + "rich": None, # "Zrzuty — obiekty" gallery template (all object types) + "overflow": None, # "Zrzuty — przepełnienie" ({{opis}} placeholder — series overflow warnings) + "warnings_demo": None, # "Zrzuty — ostrzeżenia PDF" (static long text — single-PDF warning chip) + "device": None, # device with a real local connector for the fast path +} + + +def ensure_setup(demo: Session, admin: Session) -> None: + print("=== ensure_setup ===") + + # Folders (Produkcja/red already holds tpl 97 from earlier manual setup; + # Kosmetyki/blue holds a second template so the folder rail shows 2+ + # folders with counts). + _, folders = api_get(demo, "/api/folders") + names = {f["name"] for f in folders.get("folders", [])} + if "Kosmetyki" not in names: + api_post(demo, "/api/folders", {"name": "Kosmetyki", "color": "#3b82f6"}) + if "Produkcja" not in names: + api_post(demo, "/api/folders", {"name": "Produkcja", "color": "#ef4444"}) + + _, folders = api_get(demo, "/api/folders") + by_name = {f["name"]: f["id"] for f in folders.get("folders", [])} + _, templates = api_get(demo, "/api/templates") + tpl_by_name = {t["name"]: t for t in templates.get("templates", [])} + + if "Kosmetyki" in by_name: + for t in templates.get("templates", []): + if t["id"] != IDS["tpl_main"] and t.get("folder_id") is None: + api_put(demo, f"/api/templates/{t['id']}", {"folder_id": by_name["Kosmetyki"]}) + break + + # "Zrzuty — obiekty" gallery template (created + populated once via a + # dedicated helper — see setup_rich.py in this dossier's scratchpad for + # the interactive population step). Re-used verbatim across runs. + if "Zrzuty — obiekty" in tpl_by_name: + IDS["rich"] = tpl_by_name["Zrzuty — obiekty"]["id"] + else: + _, created = api_post( + demo, + "/api/templates", + { + "name": "Zrzuty — obiekty", + "format_id": 8, + "width_mm": 120, + "height_mm": 90, + "canvas_data": {"objects": [], "stage": {"width_mm": 120, "height_mm": 90}}, + }, + ) + IDS["rich"] = created["id"] + print(f"NOTE: created empty rich template id={IDS['rich']} — populate via setup_rich.py") + + # Overflow-demo template: one text + one autoFit textBlock, used to force + # PDF/series text-overflow warnings on demand. + if "Zrzuty — przepełnienie" in tpl_by_name: + IDS["overflow"] = tpl_by_name["Zrzuty — przepełnienie"]["id"] + else: + _, created = api_post( + demo, + "/api/templates", + { + "name": "Zrzuty — przepełnienie", + "format_id": 8, + "width_mm": 40, + "height_mm": 30, + "canvas_data": { + "stage": {"width_mm": 40, "height_mm": 30}, + "objects": [ + { + "id": "ov1", "type": "text", "x": 2, "y": 2, "text": "{{name}}", + "fontSize": 3, "fontFamily": "Inter, sans-serif", "fill": "#0f172a", + }, + { + "id": "ov2", "type": "text", "x": 2, "y": 8, "width": 36, "height": 18, + "text": "{{opis}}", "fontSize": 3, "fontFamily": "Inter, sans-serif", + "fill": "#0f172a", "autoFit": True, "minFontSize": 2, "maxFontSize": 6, + }, + ], + }, + }, + ) + IDS["overflow"] = created["id"] + + # Static-text overflow template: a single-PDF generation (no series + # mapping involved) needs literal long text, not an unresolved + # `{{placeholder}}` — those render as their raw (short) syntax outside + # a series job and never overflow. + if "Zrzuty — ostrzeżenia PDF" in tpl_by_name: + IDS["warnings_demo"] = tpl_by_name["Zrzuty — ostrzeżenia PDF"]["id"] + else: + long_text = ( + "To jest bardzo dlugi tekst ktory na pewno nie zmiesci sie w tym malutkim " + "bloku niezaleznie od najmniejszej czcionki bo jest zbyt dlugi zdecydowanie " + "za dlugi naprawde bardzo dlugi tekst" + ) + _, created = api_post( + demo, + "/api/templates", + { + "name": "Zrzuty — ostrzeżenia PDF", + "format_id": 8, + "width_mm": 40, + "height_mm": 30, + "canvas_data": { + "stage": {"width_mm": 40, "height_mm": 30}, + "objects": [ + { + "id": "w1", "type": "text", "x": 2, "y": 2, "width": 36, "height": 24, + "text": long_text, "fontSize": 3, "fontFamily": "Inter, sans-serif", + "fill": "#0f172a", "autoFit": True, "minFontSize": 2, "maxFontSize": 6, + }, + ], + }, + }, + ) + IDS["warnings_demo"] = created["id"] + + # Admin-owned shared template so demo's Library "From users" section has + # a real "Use" button (own shared templates only ever show "Your + # template"). + _, admin_templates = api_get(admin, "/api/templates") + if not any(t["name"] == "Etykieta magazynowa (admin)" for t in admin_templates.get("templates", [])): + _, created = api_post( + admin, + "/api/templates", + { + "name": "Etykieta magazynowa (admin)", + "format_id": 7, + "canvas_data": { + "objects": [ + {"id": "a1", "type": "text", "x": 4, "y": 4, "text": "{{name}}", + "fontSize": 5, "fontFamily": "Inter, sans-serif", "fill": "#0f172a"}, + {"id": "a2", "type": "barcode", "x": 4, "y": 12, "width": 30, "height": 12, + "barcodeType": "code128", "data": "{{sku}}"}, + ] + }, + }, + ) + api_put(admin, f"/api/templates/{created['id']}", {"is_shared": True}) + + # Device + real local connector for the print-modal fast path (F21). + _, devices = api_get(demo, "/api/devices") + existing = next((d for d in devices.get("devices", []) if d["name"] == "Ten komputer (test)"), None) + if existing: + IDS["device"] = existing["id"] + else: + _, created = api_post(demo, "/api/devices", {"name": "Ten komputer (test)"}) + IDS["device"] = created["device"]["id"] + token = created["token"] + cfg = SCRATCH / "fastpath-config.yaml" + cfg.write_text( + f'server_url: "{BASE}"\n' + f'token: "{token}"\n' + "poll_interval_seconds: 5\n" + "heartbeat_interval_seconds: 10\n" + 'listen: "127.0.0.1:9110"\n' + "printers:\n" + ' - name: "test-plik"\n' + f' host: "file://{SCRATCH}/blg-wydruki"\n' + " port: 9100\n" + ) + (SCRATCH / "blg-wydruki").mkdir(exist_ok=True) + subprocess.Popen( + [str(SCRATCH / "blg-connector-linux-amd64"), "-config", str(cfg)], + stdout=open(SCRATCH / "connector.log", "a"), + stderr=subprocess.STDOUT, + ) + time.sleep(3) + + # A few captured labels for the Devices → Inbox table. + _, caps = api_get(demo, "/api/captures") + if len(caps.get("captures", [])) == 0 and IDS["device"]: + _, devs = api_get(demo, "/api/devices") + token_dev = next((d for d in devs.get("devices", []) if d["id"] == IDS["device"]), None) + # Token isn't retrievable after creation; only reachable on first + # creation above. If captures are already missing and no fresh token + # is available, skip — non-fatal (best-effort in the report). + print("NOTE: no captures and no fresh device token available — skip seeding captures") + + print("IDS:", IDS) -def jar_cookies(path): + +def is_local_agent_up() -> bool: + try: + req = urllib.request.Request("http://127.0.0.1:9110/status") + with urllib.request.urlopen(req, timeout=2) as resp: + return resp.status == 200 + except Exception: + return False + + +# -------------------------------------------------------------------------- +# Playwright helpers +# -------------------------------------------------------------------------- + + +def jar_cookies(path: str) -> list[dict]: out = [] for line in Path(path).read_text().splitlines(): - if line.startswith("#HttpOnly_"): - line = line[len("#HttpOnly_"):] - elif line.startswith("#") or not line.strip(): + raw = line + if raw.startswith("#HttpOnly_"): + raw = raw[len("#HttpOnly_"):] + elif raw.startswith("#") or not raw.strip(): continue - parts = line.split("\t") + parts = raw.split("\t") if len(parts) >= 7: name, value = parts[5], parts[6] - out.append({ - "name": name, "value": value, "domain": DOMAIN, - "path": "/", "secure": True, - "httpOnly": name == "session", "sameSite": "Lax", - }) + out.append( + { + "name": name, "value": value, "domain": DOMAIN, "path": "/", + "secure": True, "httpOnly": name == "session", "sameSite": "Lax", + } + ) return out -DEMO = jar_cookies("/tmp/blg-demo.txt") -ADMIN = jar_cookies("/tmp/blg.txt") L = { "pl": { - "series": "Generuj serię", "next": "Dalej", "analyze": "Sprawdź", - "import_zpl": "Importuj ZPL", "export_zpl": "⤒ ZPL", + "series": "Generuj serię", "next": "Dalej", "back": "Wstecz", "analyze": "Sprawdź", + "import_zpl": "Importuj ZPL", "export_zpl": "⤒ ZPL", "export_tspl": "⤒ TSPL", + "new_template": "Nowy szablon", "import_tpl": "Importuj", "settings_title": "Folder i udostępnianie", + "save": "Zapisz", "close": "Zamknij", "cancel": "Anuluj", "create": "Utwórz", + "new_folder": "Nowy folder", "folder_name_ph": "Nazwa folderu", "edit_folder": "Edytuj folder", + "share_toggle": "Udostępnij w Bibliotece", "history_btn": "Historia", "preview_btn": "Podgląd", + "label_size_btn": "📐", "print_btn": "Drukuj", "device_label": "Urządzenie (konektor)", + "printer_label": "Drukarka", "connect_btn": "Podłącz drukarkę", "create_advanced": "Zaawansowane: utwórz token", + "device_name_ph": "np. Komputer w magazynie", "use_source": "Użyj tego źródła", + "show_advanced": "Pokaż zaawansowane", "test_filter": "Sprawdź filtr", "start_series": "Generuj PDF", + "download_pdf": "Pobierz PDF", "export_btn": "Eksportuj", "choose_file": "Wybierz plik…", + "create_account": "Utwórz konto", "reset_password": "Resetuj hasło", "used": "Użyj", }, "en": { - "series": "Generate series", "next": "Next", "analyze": "Analyze", - "import_zpl": "Import ZPL", "export_zpl": "⤒ ZPL", + "series": "Generate series", "next": "Next", "back": "Back", "analyze": "Analyze", + "import_zpl": "Import ZPL", "export_zpl": "⤒ ZPL", "export_tspl": "⤒ TSPL", + "new_template": "New template", "import_tpl": "Import", "settings_title": "Folder & sharing", + "save": "Save", "close": "Close", "cancel": "Cancel", "create": "Create", + "new_folder": "New folder", "folder_name_ph": "Folder name", "edit_folder": "Edit folder", + "share_toggle": "Share in the Library", "history_btn": "History", "preview_btn": "Preview", + "label_size_btn": "📐", "print_btn": "Print", "device_label": "Device (connector)", + "printer_label": "Printer", "connect_btn": "Connect a printer", "create_advanced": "Advanced: create a token", + "device_name_ph": "e.g. Warehouse PC", "use_source": "Use this source", + "show_advanced": "Show advanced", "test_filter": "Test filter", "start_series": "Generate PDF", + "download_pdf": "Download PDF", "export_btn": "Export", "choose_file": "Choose file…", + "create_account": "Create account", "reset_password": "Reset password", "used": "Use", }, } @@ -57,128 +418,892 @@ def jar_cookies(path): ^XZ""" -def shoot(page, path, clip=None): - page.screenshot(path=str(path), clip=clip) - print("saved", path) +def shoot(page: Page, path: Path, clip=None, full_page=False, name: str | None = None, lang: str = "") -> bool: + try: + page.screenshot(path=str(path), clip=clip, full_page=full_page) + except Exception as e: + if name: + mark(name, lang, f"FAIL (screenshot error: {e})") + return False + size = path.stat().st_size if path.exists() else 0 + # Tight close-up crops (a few toolbar icons on a flat background) + # legitimately compress to a couple KB — 1200B is a better floor for + # "not literally blank" than a flat 5KB rule. + ok = size > 1200 + if name: + mark(name, lang, "ok" if ok else f"FAIL (tiny file {size}B)") + return ok + + +def clip_of(locator, pad=8): + box = locator.bounding_box() + if not box: + return None + return { + "x": max(0, box["x"] - pad), "y": max(0, box["y"] - pad), + "width": box["width"] + 2 * pad, "height": box["height"] + 2 * pad, + } + + +def highlight(page: Page, selector_js: str, color: str = "#6366f1") -> None: + """Draw an arrow + ring around the first element matched by a JS + querySelector-style function (passed as a JS expression string).""" + page.evaluate( + f"""() => {{ + const el = ({selector_js}); + if (!el) return; + const r = el.getBoundingClientRect(); + el.style.outline = '3px solid {color}'; + el.style.outlineOffset = '3px'; + el.style.boxShadow = '0 0 0 6px rgba(99,102,241,0.35)'; + const arrow = document.createElement('div'); + // Prefer pointing in from the left; if there isn't ~50px of room + // there (e.g. a sidebar item flush against the viewport edge), + // point down from above instead so the arrow is never clipped. + const hasLeftRoom = r.left >= 50; + arrow.textContent = hasLeftRoom ? '→' : '↓'; + arrow.style.position = 'fixed'; + if (hasLeftRoom) {{ + arrow.style.left = (r.left - 46) + 'px'; + arrow.style.top = (r.top + r.height/2 - 18) + 'px'; + }} else {{ + arrow.style.left = (r.left + r.width/2 - 12) + 'px'; + arrow.style.top = (Math.max(0, r.top - 40)) + 'px'; + }} + arrow.style.fontSize = '34px'; + arrow.style.color = '{color}'; + arrow.style.fontWeight = '900'; + arrow.style.zIndex = 99999; + arrow.setAttribute('data-shot-arrow', '1'); + document.body.appendChild(arrow); + }}""" + ) + + +def clear_highlights(page: Page) -> None: + page.evaluate( + "() => document.querySelectorAll('[data-shot-arrow]').forEach(e => e.remove())" + ) + + +def mask_emails(page: Page) -> None: + page.evaluate( + """() => { + const repl = {'tomasz@mazowszebakery.co.uk': 'jan.kowalski@example.com'}; + const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT); + let n; while ((n = walker.nextNode())) { + for (const [a, b] of Object.entries(repl)) + if (n.textContent.includes(a)) n.textContent = n.textContent.replaceAll(a, b); + } + }""" + ) + + +# -------------------------------------------------------------------------- +# Object gallery (template "Zrzuty — obiekty") — mm-space object map, used to +# click precisely on the canvas by converting mm -> px via the stage scale. +# -------------------------------------------------------------------------- + +GALLERY_OBJECTS = { + "text": (20, 7), + "textblock": (29, 27), + "rect": (20, 55), + "line": (20, 75), + "barcode": (90, 25), + "table": (85, 52), + "image": (92, 77), + "background_gap": (50, 45), # empty strip between the two columns +} +GALLERY_WIDTH_MM = 120 + + +def canvas_scale(page: Page) -> tuple[float, float, float]: + box = page.locator(".konvajs-content").first.bounding_box() + scale = box["width"] / GALLERY_WIDTH_MM + return scale, box["x"], box["y"] -def run_lang(browser, lang): +def click_mm(page: Page, x_mm: float, y_mm: float, shift=False, alt=False) -> None: + scale, ox, oy = canvas_scale(page) + x, y = ox + x_mm * scale, oy + y_mm * scale + if shift: + page.keyboard.down("Shift") + if alt: + page.keyboard.down("Alt") + try: + page.mouse.click(x, y) + finally: + if shift: + page.keyboard.up("Shift") + if alt: + page.keyboard.up("Alt") + + +def run_lang(browser, lang: str) -> None: t = L[lang] out = OUT / lang out.mkdir(parents=True, exist_ok=True) + # Unique-ish suffix for anything this run creates server-side (devices, + # …) so re-running the script never collides with a leftover from a + # previous pass (e.g. "device_name_taken"). + run_tag = f"{lang}-{int(time.time())}" def new_ctx(cookies): ctx = browser.new_context( viewport={"width": 1440, "height": 900}, device_scale_factor=2, - locale="pl-PL" if lang == "pl" else "en-GB", + locale="pl-PL" if lang == "pl" else "en-GB", ignore_https_errors=True, + accept_downloads=True, ) ctx.add_init_script(f"localStorage.setItem('i18nextLng','{lang}')") if cookies: ctx.add_cookies(cookies) return ctx - # 1. login (bez sesji) + DEMO = jar_cookies(DEMO_JAR) + ADMIN = jar_cookies(ADMIN_JAR) + + # ---- 1. login (no session) -------------------------------------------- ctx = new_ctx(None) page = ctx.new_page() page.goto(BASE + "/login", wait_until="networkidle") - shoot(page, out / "login.png") + shoot(page, out / "login.png", name="login.png", lang=lang) ctx.close() - # sesja demo + # ================= demo session : main flows =========================== ctx = new_ctx(DEMO) page = ctx.new_page() + page.on("dialog", lambda d: d.accept()) + + # ---- 3. dashboard (highlight "Templates" nav item) --------------------- + page.goto(BASE + "/", wait_until="networkidle") + page.wait_for_timeout(300) + highlight(page, "[...document.querySelectorAll('nav a')].find(a => a.getAttribute('href') === '/templates')") + shoot(page, out / "TODO-dashboard-empty.png", name="TODO-dashboard-empty.png", lang=lang) + clear_highlights(page) + + # ---- 4/5. Templates page: new-template button + dialog ----------------- + page.goto(BASE + "/templates", wait_until="networkidle") + page.wait_for_timeout(400) + highlight(page, f"[...document.querySelectorAll('button')].find(b => b.textContent.includes('{t['new_template']}'))") + shoot(page, out / "TODO-new-template-button.png", name="TODO-new-template-button.png", lang=lang) + clear_highlights(page) - # 2. lista szablonów + page.locator("button", has_text=t["new_template"]).first.click() + page.wait_for_timeout(300) + modal = page.locator("div.fixed.inset-0 > div").first + modal.locator("input").first.fill("Etykieta testowa") + # pick the custom-size option (last 's first