Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,8 @@ Do not batch updates. The moment you finish editing a file, append the entry to
- What was deleted and why — `file.js`, `file.css`, i18n keys

## i18n
- Added: `key1`, `key2` — 10 locales
- Removed: `oldKey1`, `oldKey2` — 10 locales
- Added: `key1`, `key2` — 11 locales
- Removed: `oldKey1`, `oldKey2` — 11 locales
```

Rules:
Expand Down Expand Up @@ -649,7 +649,7 @@ Google real name (`user.displayName` from Firebase Auth) is saved to Firestore a
---

## i18n — workflow & key reference
All **10 locales** (en/fr/de/es/it/zh/pt/pt-pt/pl/ru) share the same key set. **Don't re-read the locale JSON files** — the full key list is in `docs/i18n-keys.md` (pointer below). The workflow (add keys, consistency check) stays here.
All **11 locales** (en/fr/de/es/it/zh/pt/pt-pt/pl/ru/nl) share the same key set. **Don't re-read the locale JSON files** — the full key list is in `docs/i18n-keys.md` (pointer below). The workflow (add keys, consistency check) stays here.

### Adding new keys — use the helper script
**Never edit the 10 locale files by hand.** Use `npm run i18n:add` instead — it writes every locale in one shot, validates JSON, and falls back to the EN value when a translation is missing.
Expand Down Expand Up @@ -690,7 +690,7 @@ a doc — or a file it describes — is staged. Run it by hand with `npm run doc
| `llms.txt` "~N lines" | `renderer/inventory.js` | 15 % |
| `llms.txt` CSS file count | `renderer/css/*.css` | exact |
| `llms.txt` brand count **+ protocol table rows** | `renderer/printers/*/index.js` | exact |
| `llms.txt` "N keys × 10 locales" | `renderer/locales/en.json` | 2 % |
| `llms.txt` "N keys × 11 locales" | `renderer/locales/en.json` | 2 % |
| `FEATURES.md` "current as of vX.Y.Z" | `package.json` | current, or previous patch |
| Paths in README / llms.txt / CLAUDE.md / AGENT.md | the filesystem | must exist (templates + globs skipped) |
| `llms.txt` Firestore map | `.collection("…")` calls in `renderer/inventory.js` | every collection written must be documented |
Expand All @@ -705,7 +705,7 @@ A pre-commit hook runs `npm run i18n:check` automatically — the commit is bloc

```bash
npm run i18n:check
# → "OK — 10 locales × N keys, all consistent." (exit 0)
# → "OK — 11 locales × N keys, all consistent." (exit 0)
# or a per-file list of missing/extra/empty/type-mismatch issues (exit 1)
```

Expand All @@ -727,7 +727,7 @@ The full per-category key list (App/status, Settings, Account, Login, Credential
- **UI/UX — be autonomous, apply these by default (don't wait to be told each tweak):** (1) **Declutter** — cut redundant chrome (a `»` tab AND an ✕; titles repeating visible context; obvious subtitles). (2) **Context over labels** — if a state is already visible (selected item highlighted), don't repeat it. (3) **Consistency** — matching components look/size/behave identically (same size collapsed vs expanded; side-cards stack + get `»` tabs like group/material; collapse animation mirrors expand). (4) **Smooth motion, never jumpy** — elements stay put and the container resizes around them (no `justify-content:center` snap); slide don't pop; on a swap the incoming card covers before the outgoing closes; animate labels (max-width/opacity), not `display:none`. (5) **Real icons sized with presence** — masked SVGs over dots/glyphs, consistent sizes, centred on both axes (mind flex `min-width:auto` + top-heavy SVGs). (6) **Show the active/selected state.** (7) **Help/secondary actions in context + light.** (8) **Tighten space** — kill dead gaps, stacked paddings, useless separators. (9) Prefer **shared/generic** CSS-JS over per-file repetition. Propose refinements proactively with a one-line rationale. *(Full version: memory `feedback-ui-ux-autonomy`.)*
- **Language**: Conversation with the user is in French. All project content — code comments, commit messages, documentation, instructions — must be written in English.
- **Brand voice (all user-facing copy)**: every string a user reads — UI labels, buttons, empty states, prompts, nudges, error messages, modals, AND the "What's New" notes (`data/whatsnew.json`) — uses a **playful, deadpan, second-degree voice, Discord-style** (witty, self-aware, talks to you like a mate) — **NOT** Burger-King-style (no aggressive/provocative/troll edge). Lead with the **user benefit** in plain words; **no technical jargon** (avoid "modal", "toggle", "side panel", "header", class names, "TestFlight"…); **never describe what doesn't exist** or our process ("no emoji", "we removed X", "fixed bug Y"). Keep the tone natural in **every locale**. This playful voice is **register 3** — for **in-app copy + What's New (`data/whatsnew.json`) ONLY**. It is one of **three distinct copy registers** (see [*Three copy registers*](#️-three-copy-registers--changelog--release-note--whats-new)): the **release note** (`data/release-notes/`, register 2) is BambuLab-style factual, and the **`CHANGELOG.md`** (register 1) is exhaustive & technical. Same change, three voices — never blur them, and never ship the technical changelog wording where the playful or the Bambu one belongs. *(Same rule applies to the mobile app — see its CLAUDE.md.)*
- **i18n**: always add all **10** translations (en/fr/de/es/it/zh/pt/pt-pt/pl/ru) in the same edit batch. **Use `npm run i18n:add` — do NOT hand-edit the locale JSON files.** See the *Adding new keys* section above for syntax.
- **i18n**: always add all **11** translations (en/fr/de/es/it/zh/pt/pt-pt/pl/ru/nl) in the same edit batch. **Use `npm run i18n:add` — do NOT hand-edit the locale JSON files.** See the *Adding new keys* section above for syntax.
- **Commits**: no `Co-Authored-By` line. **Never commit without explicit user instruction** — make the change, then stop and wait for the order to commit.
- **Naming — clear human semantics first**: every name you introduce (Firestore collection/field, function, variable, CSS class, IPC channel, config key) must read plainly to a human and say what it *is*, so the meaning is obvious without chasing the code. Prefer a self-describing name over a clever/encoded one — e.g. a collection `rfidList` over `rfidChips`, a boolean `rfidListed`/`rfidBackup` over a multi-state `rfidChip: 1|2`. **Avoid encoding several states into one cryptic value** (magic numbers, packed flags): split into separate, well-named booleans/fields. **Don't store what you can derive** — drop redundant fields when an existing one already implies the answer (e.g. no `startState`/type field when the presence of a `backup` already means "TigerTag+"). When a name turns out unclear, rename it (and its doc/schema) rather than letting it stand. Clear semantics now prevent compounding complexity later.
- **JS**: all logic lives in `inventory.js`. Do not inline JS in `inventory.html`.
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ A QR code to download the mobile app is always accessible in the sidebar.

### 🌍 Accounts & i18n
- Multi-account — switch between multiple TigerTag accounts
- **10 locales** — EN · FR · DE · ES · IT · PL · PT (Brasil) · PT (Portugal) · 中文 · RU
- **11 locales** — EN · FR · DE · ES · IT · PL · PT (Brasil) · PT (Portugal) · 中文 · RU · NL
- Per-account language preference synced with Firestore
- Google sign-in via loopback OAuth (RFC 8252 + PKCE) — Touch ID / passkey native support

Expand Down Expand Up @@ -331,7 +331,7 @@ Built artifacts go to `dist/` (git-ignored).
UI strings live in `renderer/locales/<lang>.json`. Never edit the 10 locale files by hand — use the helper instead:

```bash
# Add a new key across all 10 locales
# Add a new key across all 11 locales
npm run i18n:add -- myKey en="Hello" fr="Bonjour" de="Hallo" \
es="Hola" it="Ciao" zh="你好" pt="Olá" pt-pt="Olá" pl="Cześć" ru="Привет"

Expand Down Expand Up @@ -383,7 +383,7 @@ TigerTag-Studio-Manager/
│ ├── CODEMAP.md # Line-range index for inventory.js (read first, grep last)
│ ├── firebase.js # Firebase init (public config)
│ ├── css/ # 10 themed files, loaded in order (00-base → 10-settings → … → 70-detail-misc)
│ ├── locales/ # i18n JSON — en fr de es it zh pt pt-pt pl ru (10 locales, edit via npm run i18n:add)
│ ├── locales/ # i18n JSON — en fr de es it zh pt pt-pt pl ru nl (11 locales, edit via npm run i18n:add)
│ ├── IoT/ # Extracted device modules (own CSS inside each)
│ │ ├── tigerscale/ # TigerScale — Firestore subscription, panel, health tick
│ │ └── td1s/ # TD1S color/TD sensor engine + TD/Color edit modals
Expand Down Expand Up @@ -423,7 +423,7 @@ TigerTag-Studio-Manager/
4. **Run**: `npm start` to test locally
5. **Open a Pull Request**

Guidelines: keep the renderer vanilla (no React/Vue), add i18n strings with `npm run i18n:add` (all 10 locales), don't commit `node_modules/` or `dist/`.
Guidelines: keep the renderer vanilla (no React/Vue), add i18n strings with `npm run i18n:add` (all 11 locales), don't commit `node_modules/` or `dist/`.

**Reporting issues** — use [GitHub Issues](https://github.com/TigerTag-Project/TigerTag-Studio-Manager/issues). Use **Settings → Debug → Report a problem** in the app to copy a self-contained diagnostic report (version, platform, last 50 errors) to paste into your issue.

Expand Down
4 changes: 2 additions & 2 deletions docs/i18n-keys.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# i18n keys — complete reference (TigerTag Studio)

> Extracted from `CLAUDE.md` to keep the always-loaded instructions lean. **Read this on demand instead of re-reading the locale JSON files.** All **10 locales** (en/fr/de/es/it/zh/pt/pt-pt/pl/ru) have every key below. To add/change keys use `npm run i18n:add` (never hand-edit the locale files) — see CLAUDE.md → *i18n — workflow*.
> Extracted from `CLAUDE.md` to keep the always-loaded instructions lean. **Read this on demand instead of re-reading the locale JSON files.** All **11 locales** (en/fr/de/es/it/zh/pt/pt-pt/pl/ru/nl) have every key below. To add/change keys use `npm run i18n:add` (never hand-edit the locale files) — see CLAUDE.md → *i18n — workflow*.

### App / status
| Key | Purpose |
Expand Down Expand Up @@ -243,4 +243,4 @@
| `notifPaxxTitle` | Paxx firmware release notice title (Snapmaker owners) |
| `notifPaxxText {{version}}` | Paxx release notice body — click opens the .bin download |

> ⚠️ This table is a snapshot and can drift as keys are added. The authoritative key set is the locale files themselves; `npm run i18n:check` is the source of truth for "do all 10 locales agree". When you add keys via `npm run i18n:add`, append them to the relevant section here too.
> ⚠️ This table is a snapshot and can drift as keys are added. The authoritative key set is the locale files themselves; `npm run i18n:check` is the source of truth for "do all 11 locales agree". When you add keys via `npm run i18n:add`, append them to the relevant section here too.
6 changes: 3 additions & 3 deletions llms.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Current version: **2.27.3** — macOS (Intel + Apple Silicon, signed & notarized
- **Printer integrations**: `renderer/printers/<brand>/index.js` — one file per brand, loaded dynamically
- **IoT sensors**: `renderer/IoT/td1s/` (TD1S color sensor), `renderer/IoT/tigerscale/` (TigerScale ESP32)
- **CSS**: `renderer/css/00-base.css` → `70-detail-misc.css` (10 files, loaded in numeric order via `<link>` tags)
- **i18n**: `renderer/locales/` — 10 JSON files (en/fr/de/es/it/zh/pt/pt-pt/pl/ru), managed via `npm run i18n:add`
- **i18n**: `renderer/locales/` — 11 JSON files (en/fr/de/es/it/zh/pt/pt-pt/pl/ru/nl), managed via `npm run i18n:add`
- **TigerTag SDK**: `@tigertag/sdk` npm package — parse, verify, encode TigerTag NTAG chips (pages 4–39, 144 bytes, blockSize=4)

## Key source files
Expand Down Expand Up @@ -251,7 +251,7 @@ All brands: printer discovery (mDNS + port scan + manual IP), filament edit per
npm run i18n:add -- myKey --after existingKey en="..." fr="..." de="..." es="..." it="..." zh="..." pt="..." pt-pt="..." pl="..." ru="..."
npm run i18n:check # validates consistency (also runs as pre-commit hook)
```
1523 keys × 10 locales as of v2.27.2.
1523 keys × 11 locales as of v2.27.4.

## CSS conventions

Expand Down Expand Up @@ -298,7 +298,7 @@ npm run i18n:check # validates consistency (also runs as pre-commit hook)

```bash
npm start # dev (no bundler)
npm run i18n:check # validate all 10 locales
npm run i18n:check # validate all 11 locales
git tag v2.13.0 && git push --tags # triggers CI: parallel build macOS/Windows/Linux → GitHub Release
```

Expand Down
1 change: 1 addition & 0 deletions renderer/inventory.html
Original file line number Diff line number Diff line change
Expand Up @@ -2027,6 +2027,7 @@ <h3 class="eac-group-title" data-i18n="eacGroupRegion">Language &amp; region</h3
<option value="pt-pt">Português (PT)</option>
<option value="zh">中文</option>
<option value="ru">Русский</option>
<option value="nl">Nederlands</option>
</select>
</div>

Expand Down
4 changes: 2 additions & 2 deletions renderer/inventory.js
Original file line number Diff line number Diff line change
Expand Up @@ -1445,7 +1445,7 @@ import { elgFanStep } from './printers/elegoo/widget_control.js';

/* ── lookups ── */
async function loadLocales() {
await Promise.all(["en", "fr", "de", "es", "it", "zh", "pt", "pt-pt", "pl", "ru"].map(async lang => {
await Promise.all(["en", "fr", "de", "es", "it", "zh", "pt", "pt-pt", "pl", "ru", "nl"].map(async lang => {
try {
const r = await fetch(`locales/${lang}.json`);
if (r.ok) state.i18n[lang] = await r.json();
Expand Down Expand Up @@ -33929,7 +33929,7 @@ import { elgFanStep } from './printers/elegoo/widget_control.js';
["en", "English"], ["fr", "Français"], ["de", "Deutsch"],
["es", "Español"], ["it", "Italiano"], ["zh", "中文"],
["pt", "Português (Brasil)"], ["pt-pt", "Português (Portugal)"], ["pl", "Polski"],
["ru", "Русский"],
["ru", "Русский"], ["nl", "Nederlands"],
];
// An imported photo ALWAYS wins as the avatar; the generated colour is only
// the fallback shown when there is no photo. So shuffling a colour never hides
Expand Down
Loading