Commit Graph

56 Commits

Author SHA1 Message Date
David Montero Crespo d4a42a4e75 feat(ui): the Part Inspector - right-click dialog merged with the picker card
The right-click dialog was a 250px column: every pin as a text row (an ESP32
means 30 rows of scrolling), a few editors, Rotate/Delete. The picker's info
card (datasheet, brand, Buy) was a separate surface you could only reach by
hovering the catalogue. One dialog now serves both:

- ~560px, two panes: live preview + actions on the left, Properties/Datasheet
  tabs on the right. The Datasheet tab IS the picker card's body -
  ComponentInfoPanel was split into an embeddable ComponentInfoBody (+
  useComponentDoc, and its .cip-* styles moved to their own stylesheet) so
  both surfaces render one implementation.
- "Show pins" (off by default): the preview is the REAL web component,
  mounted and scaled, with pin markers at their true pinInfo positions and
  labels flanking the side each pin lives on. pinInspectorLayout is pure
  geometry - nearest-edge classification, per-side label stacking with a
  minimum spacing and leader lines when nudged, interior pins as
  dot+tooltip - driven only by pinInfo plus the measured element size, so
  every part and board, present or future, works with no per-part data.
  Dots are tinted by the pin's declared signal (i2c/spi/power/...).
- Clicking a pin keeps the old contract (onPinSelect -> handlePinClick), so
  wires start with the same undo, colors and running-state gating.
- The two open gestures finally agree on coordinates: right-click always
  passed viewport coords while touch passed canvas-local ones; the dialog is
  now portaled to body in viewport space and the touch site converts.
- Keybind capture, the SD Card panel, the two-step delete, Escape and
  click-outside all migrate unchanged. Mobile keeps the full-sheet layout.

Geometry covered by pin-inspector-layout.test.ts with coordinate tables
copied verbatim from the shipped parts (ESP32 two-column board, LED, an
interior-pin case).
2026-08-03 21:44:46 +02:00
David Montero Crespo 697a9de4f4 fix(i18n): deep-merge locale files — shallow spread was eating sections
The common namespace was assembled with a shallow spread, so any
top-level section present in two locale files had the later file clobber
the earlier one WHOLESALE. Two live casualties:

- header: adding header.shareProject to common2 (yesterday's raw-string
  cleanup) erased common.json's whole header — header.auth.* and
  header.nav.* — so the account menu rendered "header.auth.signOut" as
  its label. My regression, caught verifying the account-menu fixes.
- editor.share.*: common2's editor section has been clobbering
  common.json's since forever — the Share modal's seven visibility
  labels were silently lost in every locale. Pre-existing.

Locale files merge by key now (deepMerge, both the synchronous en init
and the dynamic per-locale loader), which ends the failure class instead
of the instance; shareProject also moves to common.json's header where
it belonged. Verified: all header.auth/nav and editor.share leaves
resolve again in all nine locales.
2026-07-31 00:30:34 +02:00
David Montero Crespo 155513a493 fix(editor): account footer is the account alone; language rides elsewhere
The bottom-left block read wrong: a globe crowding the account button for
no gain — language already lives in the menubar's Language menu for
everyone, and (for signed-in users) inside the account menu. The footer
now holds only the account button.

Width-aware, as requested: when the explorer is dragged narrow (footer
under 150px, via container query) the username hides and the avatar
alone identifies the account; with the explorer collapsed, the floating
fallback box shows just the circle.

Adds the OSS half of the crash fix: LocaleSync listens for
'velxio-locale-switch' window events and performs the locale change as a
normal SPA navigation. The pro account menu is injected into its own
React root OUTSIDE the Router, so it cannot navigate itself — its
language rows dispatch this event instead.
2026-07-30 22:56:19 +02:00
David Montero Crespo e17f6fd6cf i18n(editor): translate the remaining raw UI strings
The explorer toggle tooltips (Hide/Show file explorer) and the header's
Share-project title were raw English — now keyed and translated in all
nine locales, same common2.json home as the menubar strings.
2026-07-30 21:18:37 +02:00
David Montero Crespo db632832b4 i18n(editor): translate the new menubar in all nine locales
Every menu we added lived on t() English fallbacks — the keys did not
exist in ANY locale file, so a Spanish or Chinese user got an English
menubar. 29 keys (editor.menu.* + the toolbar labels the File menu
reuses) now exist in en/es/pt-br/it/fr/zh-cn/de/ja/ru.

Two entries needed their own leaf keys: editor.toolbar.compile and .run
are nested OBJECTS (title/options/... sub-keys), so t() on them cannot
resolve to a string — the menu now uses editor.menu.compile / .run,
which also avoids double shortcuts ("Compile (Ctrl+B)" plus the menu's
own Ctrl+B column).

Verified mechanically: all 38 keys the menubar references resolve to
strings in all nine locales.
2026-07-30 21:12:28 +02:00
David Montero 5218f7314b feat(simulator): map pushbuttons to keyboard keys
Any pushbutton (pushbutton / pushbutton-6mm) can now be driven from the
keyboard. Assign a key from the component property dialog — a keycap
control captures the next keypress (Escape cancels, modifiers alone are
rejected) — and a keycap badge next to the component label shows the
mapping on the canvas. Several buttons may share one key on purpose;
the dialog shows a hint when that happens.

At runtime a global bridge translates keydown/keyup into the same
button-press / button-release DOM events the mouse fires on the wokwi
element, so every simulation path (avr8js pin logic, SPICE-driven
inputs, the QEMU GPIO bridge, the pressed visual) behaves identically
to a mouse click. Guards: ignored while typing in inputs or the code
editor, ignored with Ctrl/Alt/Meta held, auto-repeat collapses into one
long press, and window blur releases everything so no button sticks
after Alt-Tab.

The binding is stored as the component's 'key' property, so it
round-trips through project saves and .vlx exports and is undoable like
any other property edit. Strings added to all 9 locales.
2026-07-18 04:02:01 +02:00
David Montero Crespo e72413a13f feat(ui): replace native confirm() dialogs with reusable modal
Convert the remaining window.confirm() call sites to the in-app
MessageDialogHost, extended with a new confirm mode (Cancel + Confirm
buttons, optional danger styling) via showConfirmDialog().

Sites converted:
- New workspace (EditorPage)
- Load project / delete file (FileExplorer)
- Overwrite SPIFFS file (BoardOptionsModal)
- Delete VFS node (VirtualFileSystem)

All dialog strings are internationalized across the 9 supported locales
(en, es, pt-br, it, fr, zh-cn, de, ja, ru); the two previously
English-only modals now pull from i18n too.
2026-07-18 01:59:18 +02:00
David Montero 1ad669fae9 feat(editor): multi-board primary Run runs all boards (split-menu for active-only)
In a multi-board project the wired boards are one system, so running just
the active board almost never matches intent (a cross-wired UART pair only
comes alive when both run). The primary Run button now runs ALL boards when
there is more than one, with a split caret-menu to still run only the active
board. Single-board and board-less behaviour is unchanged, and the separate
Run-All double-triangle button is kept only for board+chip / chips-only
projects where the primary Run is not already a run-all.
2026-07-03 01:22:17 +02:00
David Montero 03339a132a feat(pi): gpiozero core + boot feedback, terminal and upload UX
- boot_images manifest: bump arm64 rootfs (gpiozero/colorzero baked in,
  hostname applied at boot, reworded MOTD)
- RaspberryPi3Bridge: onBooted shell-ready detector + sendAndWaitForPrompt
  flow control (resets on disconnect)
- RaspberryPiWorkspace: distinct Booting overlay + piBooted-driven status,
  inline SVG icons replacing emoji glyphs
- SerialMonitor: strip CSI/DSR escapes so the dumb console no longer shows
  a literal [6n next to the prompt
- VirtualFileSystem: upload auto-starts the Pi and waits for the shell, then
  flow-controls each command (no more dropped lines on large files)
- i18n: bootingTitle/bootingNote + reworded offlineNote2 across 9 locales
2026-06-22 20:21:06 +02:00
David Montero e51d26ce33 feat(editor): follow-up GitHub star prompt for past dismissers
The star banner used a single localStorage flag (velxio_star_prompted)
set identically whether the user clicked through to the repo or just
closed it, so once dismissed it never showed again and clickers and
closers were indistinguishable.

Now track three flags:
  velxio_star_prompted     - dismissed the first ask
  velxio_star_prompted_v2  - dismissed the follow-up (stop forever)
  velxio_star_clicked      - clicked through to the repo (stop forever)

Anyone who dismissed the first ask without clicking through gets ONE
follow-up (round 2) with a stronger message; clicking the repo link at
any time opts them out permanently. Capped at two asks total.

Adds starBanner.title2/body2 copy in all 9 locales.
2026-06-15 22:15:14 +02:00
David Montero Crespo fae9208fae Merge branch 'master' of https://github.com/davidmonterocrespo24/velxio 2026-06-15 11:57:59 -03:00
David Montero Crespo cf6a609671 Raspberri pi 4 y 5 2026-06-15 11:57:10 -03:00
David Montero b820c372cc feat(seo): add /v3 release landing page + refresh About for 3.0
- New Velxio3Page (/v3): retro CPUs (Z80/8080/4004/4040/8086), MicroSD,
  ePaper, multi-board interconnect, ngspice WASM migration, undo/redo,
  -88% bundle, 100+ examples. JSON-LD (SoftwareApplication 3.0.0, FAQ,
  breadcrumb). Mirrors the v2.5 page; reuses SEOPage.css/Velxio2Page.css.
- Wire-up: App.tsx route, entry-server.tsx prerender map, seoRoutes.ts
  entry (priority 0.95, prerendered to dist/v3/index.html). v2-5 demoted
  to 0.9.
- AboutPage: Velxio 3.0 is now the 'latest' release card (v2.5 + v2 kept);
  stats refreshed (boards 17 -> 19+, CPU architectures 6 -> 10+ for the
  new retro ISAs).
- i18n: new v3 block in releases.json + about.releases.v3* in common2.json,
  auto-translated to all 9 locales via translate-i18n.mjs.
2026-06-15 08:52:09 +02:00
David Montero Crespo f9ebdc240e fix(i18n): load + resolve non-English locales on direct navigation
Every non-English page fell back to English when loaded directly (deep link,
refresh, SEO-indexed URL). Two causes:

1. index.ts seeded lng with the URL locale at init, but only the English
   bundle is inlined. LocaleSync only fetches a locale bundle when
   i18n.language !== target, which was already false on a direct non-default
   load, so the bundle was never loaded. Init at DEFAULT_LOCALE and let
   LocaleSync drive the locale from the URL (a real changeLanguage that
   re-renders).

2. The region-coded locales (zh-cn, pt-br) never resolved their own bundle:
   i18next's default code formatting rewrote them to zh-CN / pt-BR, which
   failed the lowercase supportedLngs check and were dropped from the resolve
   hierarchy. Add lowerCaseLng: true.

Verified in a production build: /zh-cn/* and /pt-br/* render translated;
toResolveHierarchy now returns ['zh-cn','en'] / ['pt-br','en'].
2026-06-14 08:17:05 +02:00
David Montero Crespo 9fc0655612 i18n: fill 19 missing keys in de/fr/it/ja/ru
quotaModal.* (10), editor.share.updateFailed + visibility labels/hints (7),
editor.toolbar.exportBom + exportScreenshot (2) were present in en but absent
in de/fr/it/ja/ru. es/pt-br/zh-cn were already complete. Translated via
DeepSeek; interpolation tokens and JSON shape preserved, existing values
untouched.
2026-06-13 08:01:21 +02:00
David Montero 083823d945 chore(pricing): free tier shows 20 daily AI credits (up to 600/month)
Landing pricing card copy updated across all 9 locales: free was advertised
as '100 daily AI credits (up to 1,500/month)'; lowered to 20/day, 600/month
to match the backend quota (see velxio-prod quota.py).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 05:37:57 +02:00
David Montero 5bbf2427ec feat(pricing): surface STM32 & Raspberry Pi 3/4/5; refresh board count
The board roster grew to 30+ (8 STM32 variants + Raspberry Pi 3/4/5), so
the home pricing cards and SEO FAQ were stale at '19 boards'.

- Home pricing (9 locales): free bullet '19 boards' -> '30+ boards';
  the Maker bullet that just repeated the board count now states the real
  paid differentiator — unlimited ESP32 / STM32 / Raspberry Pi simulation
  time (free is time-capped on these server-side QEMU boards).
- SEO FAQ: roster updated to 30+ boards across 6 CPU architectures,
  adding ARM Cortex-M (STM32) and Raspberry Pi 3/4/5.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 07:29:54 +02:00
David Montero 44f12f0e53 feat(seo+nav+community): public-project indexing + classroom nav + examples grid
Search-engine indexing of public projects
- Update robots.txt to also list /sitemap-projects.xml so Googlebot /
  Bingbot discover every public project's canonical /:username/:slug URL.
- Add /docs/github-sync + /classroom entries to seoRoutes.ts so the
  build-time sitemap.xml picks them up.

Navigation polish
- AppHeader gains a "For schools" link between Pricing and Download.
- LandingPage's pricing section gets a slim banner under the cards
  pointing institutional visitors to /classroom (visible discovery path,
  not just a footer link).
- Localised header.nav.classroom + landing.pricing.classroomBanner +
  landing.pricing.classroomCta across all 9 maintained locales (en/es/
  pt-br/fr/de/it/ja/ru/zh-cn).

Community examples
- New CommunityProjectsGrid component lives next to ExamplesGallery on
  /examples.  Fetches /api/projects/featured (Pro-overlay-only endpoint)
  and renders the top public projects ranked by run_count.  Quietly
  hides itself when the endpoint returns nothing or fails, so the OSS
  build still ships cleanly.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 18:11:28 +02:00
David Montero 1d847d19cc fix(landing): pricing card prices, names + features for the shipped tiers
The home page's pricing section was still showing the dropped Phase 0
shape (Free / Pro $15 / Pro Max $35) instead of what /pricing and the
billing backend actually serve (Free / Maker $7 / Pro $19).

  - Replace the middle card from "Pro $15" → "Maker $7" (CTA "Start
    Maker") + the right card from "Pro Max $35" → "Pro $19" (CTA
    "Subscribe to Pro").  "Most popular" badge moves to the now-Pro
    card (still the upsell sweet spot).
  - i18n keys renamed in lockstep: tiers.pro → tiers.maker, tiers.pro_max
    → tiers.pro.  Updated in all 9 locales (en/es/pt-br/fr/de/it/ja/ru/
    zh-cn) with translated copy that mentions the actually-shipped Pro
    perks (private projects, GitHub Sync, BOM CSV, schematic PNG,
    watermark-free embed).  The Spanish line about "Maker" is left as
    the loanword so it stays consistent with /pricing.

No backend changes — quota.py PLANS was already correct.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 17:00:02 +02:00
David Montero 9a40de78c0 feat(share-modal): D1.4 — 3-level visibility picker (public/unlisted/private)
Phase 1 D1.4 — replaces the binary public/private toggle in ShareModal
with three radio-button-styled options. Optimistic UI: every option
renders for every user; the backend's 403 (with structured
visibility_not_allowed detail) redirects to /pricing?from=visibility_X
so the pricing page can lead the right pitch.

Why optimistic-then-redirect instead of hiding/locking options:

  1. Discovery — Free / Maker users SEE Pro unlocks Private. That's the
     exact conversion signal the pricing page is trying to surface.
  2. Discovery without surprise — the locked click goes to /pricing
     with a hint, not a dead modal.
  3. Less plan-coupling — this upstream component doesn't need to know
     about the pro overlay's plan store. Backend is the only source of
     truth for what's allowed.

Touched:
  - ShareModal.tsx: full rewrite as a 3-option picker with badges
    (Maker / Pro) on the gated options.
  - projectService.ts: ProjectResponse / ProjectSaveData now declare
    `visibility?: 'public' | 'unlisted' | 'private'`. is_public stays
    declared for backward compat with old callers.
  - useProjectStore.ts: CurrentProject gains `visibility?`; setVisibility
    accepts EITHER the legacy boolean OR the new enum and keeps both
    fields coherent.
  - common.json (4 locales): new editor.share.visibility.{publicLabel,
    publicHint, unlistedLabel, unlistedHint, privateLabel, privateHint}
    + editor.share.updateFailed.

Backend gating + DB migration are in the velxio-prod pro overlay
(commit referencing this submodule pointer).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 05:49:03 +02:00
David Montero c957767e2e feat(editor): add Export-Screenshot button (Phase 3 D3.2)
Front-end half of the schematic image export. New camera-icon button in
the editor toolbar between BOM and Upload-Firmware. Handler:

  1. POSTs to /api/pro/projects/{id}/screenshot.png (server renders the
     canvas with headless chromium, returns a PNG).
  2. 402 → /pricing?from=screenshot_export
  3. 401 → /login with redirect-back
  4. 422 → friendly "add at least one component" toast
  5. 200 → blob download with Content-Disposition filename
  6. The "rendering..." toast surfaces during the 5-10 s of headless
     chromium time so users know to wait, not click again.

i18n key editor.toolbar.exportScreenshot added in en/es/pt-br/zh-cn.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 04:18:43 +02:00
David Montero b5a80a65b9 feat(editor): add BOM-export button + handleExportBom flow
Phase 3 D3.1 — front-end half of the BOM export. The toolbar gains a
new spreadsheet-icon button next to the existing project-export button.
On click:

  1. POST is NOT used — the backend endpoint is GET-based and streams a
     CSV. We just open the URL.
  2. 402 (Pro-required) routes the user to /pricing?from=bom_export
     so the page can show the right upgrade narrative.
  3. 401 routes to /login with redirect-back.
  4. 200 triggers a Blob download with Content-Disposition filename.

i18n key editor.toolbar.exportBom added in en/es/pt-br/zh-cn — the
" — Pro" suffix on the tooltip hints at the gating without forcing the
user to discover it only on click.

The button is shown to everyone, not hidden by plan. Free/Maker users
clicking it gets the 402 route to /pricing, which is intentional — that
is the upgrade-discovery funnel we want, not a silent locked icon.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 03:12:52 +02:00
David Montero 6df53a0d32 i18n: refresh landing.hero subtitle + trustLine with AI/offline/AGPLv3 USPs
Phase 1 D1.8 — the home-page hero leads with three defensible
differentiators Velxio has that the dominant alternative doesn't:

  - AI agent integrated (Wokwi has none)
  - Works offline as a desktop app (Wokwi is cloud-only)
  - AGPLv3 open source (Wokwi is proprietary)

Subtitle and trustLine rewritten across all four shipped locales
(en, es, pt-br, zh-cn). No layout change — the LandingPage.tsx
component renders both strings already.

The competitor name isn't mentioned anywhere — the user comparing
side-by-side does the math themselves. Anchoring on the USPs makes
the eventual /pricing visit ("Maker $7 = AI included") land in
context instead of cold.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 22:57:23 +02:00
David Montero 0beee4af3d i18n: add quotaModal block in en/es/pt-br/zh-cn (10 keys × 4 langs)
The quota-exhausted modal (rendered by the velxio.dev pro overlay when
a free user hits the daily AI cap) was hardcoded English. The modal is
seen by users from CN/BR/MX/CO/AR/PE/IN — the audiences most likely to
bounce on English-only UX. This adds the four key languages.

Keys:
  titleFree    — "You've hit today's free limit"
  titlePaid    — "You've reached your daily limit"
  bodyFree     — explainer + Pro upgrade pitch (interpolates cap/proCap/multiplier)
  bodyPaid     — explainer for paid users who hit their own tier's cap
  today / thisMonth / resets — stats labels
  ctaUpgrade   — primary CTA ("Upgrade to Pro — $15/mo")
  ctaSeePlans  — fallback CTA for non-free users
  ctaWait      — secondary "Wait until reset"

Upstream-only change — the velxio-prod overlay's AgentChatPanel.tsx is
wired to consume these via useTranslation in a separate commit.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 20:57:29 +02:00
David Montero 8437f70770 feat(header): add Download nav link → /account/desktop-install
Inserts a Download entry between Pricing and Blog in the main nav.
Routes to the existing DesktopInstallPage in the velxio-prod pro
overlay (auth gate + platform-detect + signed-licence download flow).

i18n: header.nav.download added across all 9 shipped locales
(en/es/ja/it/de/ru/pt-br/zh-cn/fr) with native translations.

Self-hosted OSS image: the route doesn't exist there, so the link
lands on the upstream router's 404 — same fallback behaviour as
/pricing already has for self-hosters. Acceptable until the OSS
side gets its own placeholder.
2026-05-27 08:34:14 +02:00
David Montero 737ec5c6eb feat(import): unify project import — accept .vlx and .zip in both entry points
Velxio had two parallel import paths that confused users (reported on
Discord by AgUn / dmontero):

  * Toolbar "Import a project from a .zip file" → Wokwi .zip only
  * File-explorer "Open .vlx file"               → Velxio .vlx only

If you exported a Velxio project as .vlx and tried to bring it back via
the toolbar Import button, you bounced off "wrong format" with no hint
that the .vlx loader was hiding behind the file-explorer save-bar.

Fix: introduce `utils/importProject.ts` as the single dispatcher.  It
sniffs the extension and routes:

  *.vlx  → importVlxFile      (writes directly to stores)
  *.zip  → importFromWokwiZip (returns a payload the caller applies,
                               so the toolbar can still trigger the
                               install-libraries modal afterwards)

Both UI entry points now go through the dispatcher with the same
`accept=".vlx,.zip,application/json,application/zip"` filter:

  * Toolbar "Import project (.vlx Velxio or .zip Wokwi)"
  * File-explorer "Open project (.vlx Velxio or .zip Wokwi)"

The toolbar tooltip is i18n-driven — updated EN + 8 other locales
(es, fr, de, it, pt-br, ja, ru, zh-cn) so every user sees the same
clarification.

Wokwi compatibility kept intact — the .zip path still resolves to
`importFromWokwiZip` and the same library-install modal pops if the
imported project lists libraries we don't have locally.
2026-05-22 20:55:06 +02:00
davidmonterocrespo24 0dcb504c1b feat(landing): "Try Simulator Free Online" + download CTA slot below
Two coordinated copy/layout changes on the landing hero:

  - Primary CTA label gets "Online" added across the 9 supported
    locales — "Try Simulator Free Online →" / "Probar el simulador
    online gratis →" / etc. Reason: with the Velxio Desktop
    download path now live, users should immediately understand
    that the green button is the BROWSER version, and there's a
    local install option for people who want it faster offline.

  - The pro-overlay slot for the desktop download CTA was
    `landing-hero-primary-cta` and sat ABOVE the hero CTAs. Renamed
    to `landing-hero-download-cta` and moved BELOW so the funnel
    reads "try online (primary) → or install locally (secondary)"
    instead of "install locally (primary) → try online (secondary)".

OSS layout is unchanged — the slot is still empty in pure builds.

Also picks up the auto-regenerated sitemap.xml lastmod dates from a
recent deploy (every URL bumped 2026-05-19 → 2026-05-21).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 03:33:21 +02:00
davidmonterocrespo24 f0953fcf45 i18n: admin.users keys for "Reset agent usage" button
New keys across all 9 locales (en/es/fr/de/it/pt-br/ja/ru/zh-cn):
  - admin.users.actions.resetAgentUsage           — button label
  - admin.users.actions.resetAgentUsageTooltip    — hover hint
  - admin.users.confirmResetAgentUsage            — confirm dialog
  - admin.users.resetAgentUsageDone               — success toast
  - admin.users.resetAgentUsageFailed             — error toast

Consumed by the velxio-prod overlay's AdminPage Users tab, which adds
a "Reset agent" button per row that hits
POST /api/admin/users/{user_id}/reset-agent-usage and clears today's
pro_agent_usage_events for the user.  Live agent quota recomputes
from the events table, so the user can keep using the agent
immediately after the button click.
2026-05-14 23:29:21 +02:00
davidmonterocrespo24 aaebfedd23 feat(landing): bump pricing display to 100/500/2000 daily credits
Mirrors the velxio-prod backend quota bump (PLANS dict in
pro/backend/app/pro/services/quota.py). With 9router serving the bulk
of agent traffic for free, the cost-of-LLM ceiling is much lower than
when the limits were originally tuned, so we can be significantly more
generous and let casual users actually evaluate the agent.

  Free     20  /day, 300 /mo   →  100  /day, 1500 /mo
  Pro      400 /day, 12k /mo   →  500  /day, 15k  /mo
  Pro Max  1000/day, 30k /mo   →  2000 /day, 60k  /mo

Updates the landing.pricing.tiers.{free|pro|pro_max}.f1 string in all
9 locales (de, en, es, fr, it, ja, pt-br, ru, zh-cn) with each
locale's native thousands separator (',' / '.' / ' ' depending on
convention).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 08:10:25 +02:00
davidmonterocrespo24 888ce03cc3 docs: BUILD-QEMU.md + 'Build QEMU from source' docs section
Adds a transparent self-hosting path for users who would rather not
run Velxio's prebuilt libqemu binaries. The prebuilts have always
been a convenience under the AGPLv3 license; this just documents
how to skip them.

- docs/BUILD-QEMU.md as the canonical step-by-step (dependencies per
  Debian/Arch/macOS, ESP32 xtensa + ESP32-C3 riscv32 configure-and-
  ninja, drop-in instructions, troubleshooting, license notes on the
  QEMU/Velxio GPL-vs-AGPL boundary).
- DocsPage gets a new 'build-qemu' section between Setup and Roadmap
  in the sidebar. Content is hardcoded English (technical reference,
  not marketing copy) and ends with a link to the .md on GitHub.
- nav + SEO meta keys added to all 9 locales (de en es fr it ja
  pt-br ru zh-cn). Body remains English in every locale; technical
  content doesn't need translation for the audience that follows it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 05:44:10 +02:00
davidmonterocrespo24 fedb197be0 feat(header): add Pricing link to top nav + landing footer
The /pricing page exists (PricingPlaceholder upstream, real PricingPage
portal-mounted by the private overlay) but had no entry in the top nav.
Adds 'pricing' to header.nav in all 9 locales (de, en, es, fr, it, ja,
pt-br, ru, zh-cn), wires the Link in AppHeader between About and Blog,
and mirrors the link in the landing-page footer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 05:28:59 +02:00
davidmonterocrespo24 cd4050c499 feat(landing+theme): multiplier pricing copy + softer canvas
Two small fixes that compound:

1. Drop LLM model names from the landing AI section. The agent
   auto-routes between several providers (9router combo, direct
   DeepSeek, direct Gemini, future-others) and naming any of them on
   the homepage misleads visitors. Replace "DeepSeek-V4-Flash and
   Gemini 2.5 under the hood" with "frontier LLMs auto-routed for
   cost and reliability" so the marketing line stays accurate as the
   provider mix changes.

2. Pricing copy switches from absolute message counts (300/day,
   700/day — small, intimidating, hard to anchor) to comparative
   multipliers (Pro = 20×, Pro Max = 50×). Visitors instinctively
   read these as "much more" without needing to count usage. The
   multipliers reflect the new backend quotas (400/day, 1000/day,
   committed separately in velxio-prod's pro overlay).

3. --color-bg-canvas moves from gray-1000 (#000000) to gray-950
   (#0a0a0c). Pure black collided with the slightly-lighter card
   surface (gray-900 #141416) and produced a harsh transition wherever
   a `min-height: 100vh` page wrapper grew taller than its content —
   visible on docs and user-profile pages with sparse content. The
   2-luminance-step shift removes the jarring while keeping the dark
   palette feel intact. gray-1000 stays in the scale for intentional
   black uses.

All 9 locales updated for (1) and (2).
2026-05-13 21:02:48 +02:00
davidmonterocrespo24 b7797b1eea feat(landing): AI agent + pricing sections
Two new sections on the landing page, between Features and Support:

1. "Powered by AI agents" — three cards explaining the in-editor agent
   (place & wire parts, generate code, diagnose circuits). Calls out
   DeepSeek-V4-Flash + Gemini 2.5 as the LLM backbone so visitors know
   the simulator does more than draw boxes.

2. "Pricing" — three cards summarising Free / Pro / Pro Max with the
   actual monthly cost, the daily AI-message quota, and a CTA per
   tier. Pro is highlighted as Most Popular. Free CTA opens the editor,
   the two paid CTAs link to /pricing where the PayPal subscription
   flow lives.

The simulator itself stays free — only the AI-agent quota changes per
tier — that copy is repeated in the section subtitle so visitors don't
worry about the boards/components becoming paywalled.

All 9 locales translated.
2026-05-13 20:24:40 +02:00
davidmonterocrespo24 5d40408718 feat(landing): licensing section — AGPLv3 + commercial option
Adds a two-card section at the foot of the landing page (before the
brand footer) that surfaces Velxio's licensing model: AGPLv3 for the
public release, commercial license for teams that need to ship
Velxio inside closed-source products. Mirrors the existing
.feature-card visual language so it slots into the page without a
new design system.

Commercial CTA opens a mailto:info@velxio.dev. Open-source CTA links
to GitHub via the existing trackVisitGitHub handler so the analytics
event still fires.

All 9 locales translated.
2026-05-13 14:27:49 +02:00
David Montero Crespo 8de51da5a5 feat(simulator): wire color palette from PR #170 + Copilot suggestions
- Add color picker button to SelectionActionBar for wire selections
- Toggle palette using WIRE_KEY_COLORS swatches
- Pass currentColor and onColorChange from SimulatorCanvas
- Reset showPalette on kind/onColorChange change (Copilot suggestion)
- Use t('editor.selectionBar.changeColor') for title/aria-label (Copilot suggestion)
- Add changeColor i18n key to all 9 locale files

Co-authored-by: naweiss <naweiss@users.noreply.github.com>
2026-05-11 12:14:36 -03:00
David Montero Crespo a7e796161e fix(i18n): split common.json (editor + about) so all 8 locales translate
The common bundle ballooned to 30KB after Block 15 added the AboutPage
prose, putting Russian translations past DeepSeek's 8192-token output
cap. The editor + about sub-trees (the two heaviest, ~15KB combined)
move to a new common2.json file. Both files now sit at 12-18KB and
translate cleanly.

i18n bootstrap merges common2 into the same common namespace at
load time and lazy-loads it per locale, so every existing t('editor.*')
and t('about.*') call keeps resolving without source changes.

All 9 locales regenerated via DeepSeek. Closes the gap left by the
Blocks 15+16 commit where only zh-cn/common had been refreshed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 18:50:21 -03:00
David Montero Crespo 3428ab130a feat(i18n): translate AboutPage prose + 15 SEO landing pages (blocks 15+16)
AboutPage: ~28 prose blocks across Story / How It Works / Open Source /
Creator / Releases / Quote / Community sections; <Trans> for paragraphs
with inline <strong>, <em>, <a> markup.

15 SEO landing pages now use t() for all user-facing copy under
seo.<page>.* keys: CircuitSimulatorPage, SpiceSimulatorPage,
ElectronicsSimulatorPage, CustomChipSimulatorPage, Attiny85SimulatorPage,
ArduinoSimulatorPage, ArduinoEmulatorPage, AtmegaSimulatorPage,
ArduinoMegaSimulatorPage, Esp32SimulatorPage, Esp32S3SimulatorPage,
Esp32C3SimulatorPage, RaspberryPiPicoSimulatorPage,
RaspberryPiSimulatorPage. Code blocks, FQBNs, JSON-LD schema strings
intentionally stay in English.

The seo bundle (67KB English source) is split into 4 balanced files
(seo.json + seo2.json + seo3.json + seo4.json, ~17KB each) so each
DeepSeek translation request stays inside the 8192-token output cap.
i18n bootstrap merges all 4 halves under the seo.* keyspace.

Translations: 8 locales × 4 seo bundles all regenerated via DeepSeek.
common.json (now 30KB after about additions) only has zh-cn refreshed
so far — the remaining 7 locales' common.json need a follow-up pass
(the bundle is at the edge of DeepSeek's output limit).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 18:38:51 -03:00
David Montero Crespo 7f437a753e feat(i18n): translate DocsPage prose (block 14)
Wire useTranslation() + Trans into DocsPage.tsx. ~330 user-facing
strings across 13 sections (intro, getting-started, emulator, riscv,
esp32, rp2040, rpi3, components, roadmap, architecture, third-party,
mcp, setup) plus sidebar nav + page chrome are now keyed under docs.*.

Strings with inline <a>, <code>, <strong>, <em> use the <Trans/>
component with mapped slots; bare prose uses t().

Code blocks, FQBNs, hex addresses, library names visible as link text,
and JSON-LD schema strings stay in English on purpose.

Internal Link to=... wrapped with localize() so /es/docs/... etc.
keep their locale prefix.

The English docs bundle is split in half (docs.json ~22KB +
docs2.json ~22KB) so each fits inside DeepSeek's 8192-token output
window. The i18n bootstrap merges both halves into the docs.* keyspace
under the default common namespace.

All 9 locales regenerated via DeepSeek (parallel run for the two
namespaces).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 17:47:19 -03:00
David Montero Crespo 0ff76a0f9d feat(i18n): translate Velxio 2.0 + 2.5 release pages (block 13)
Wire useTranslation() into Velxio2Page and Velxio25Page: hero badge,
accent, subtitle, CTAs, board/example/outcome cards, OSS section, and
footer links all keyed under landing.v2.* and landing.v25.*.

Split en/common.json (34KB) into common.json (25KB) + releases.json
(9KB) so each translation request stays inside DeepSeek's 8192-token
output cap. i18n bootstrap merges both bundles into the default common
namespace at load time, lazy loader fetches both per locale.

translate-i18n.mjs: set max_tokens=8192 + response_format json_object
on the DeepSeek call so future bundles closer to the cap don't get
silently truncated.

All 9 locales regenerated via DeepSeek (fr/de/es/it/pt-br/zh-cn/ja/ru).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 17:19:16 -03:00
David Montero Crespo aae3ab1c5c feat(i18n): translate small editor remates + admin internals (Blocks 11+12)
Block 11 — small editor surfaces
- PinPickerDialog: close, filter pins, no-match, rotate / delete
  action buttons.
- RaspberryPiWorkspace: connection status (Connected / Starting…
  / Offline), Start Pi button + tooltip, Connect / Disconnect
  buttons + tooltips, Terminal tab label, file-tab close, the
  full offline overlay (title / subtitle / start CTA / two-line
  note about the staging area), terminal-loading + no-file-
  selected fallbacks.
- GitHubStarBanner: aria-label, title, body copy, Star CTA,
  dismiss button.
- ExampleLoaderPage: "Loading example…" status, "Example {{id}}
  not found." 404 line, "Browse all examples" recovery link.
  /editor and /examples links wrapped in localize().

Block 12 — admin-only internals (only admins ever see these)
- AdminBoardsTab: section headings ("By board family" / "By
  exact FQBN"), full table column headers (Family / FQBN /
  Compiles / Errors / Success rate / Runs / Distinct users /
  Distinct projects), range selector label, no-data fallback,
  load-failed error.
- AdminDashboardTab: 8 KPI cards (Total users / Total projects
  / Compiles / Runs / DAU / WAU / MAU / Public-Private), all
  chart titles + subtitles (Activity over time, Compiles by
  board family, Board diversity for pricing signal, Top FQBNs,
  Top countries with Cloudflare disclaimer, Top users / Top
  projects), table column headers, no-data fallbacks, load
  errors. Pluralised "{{count}} board(s)" via i18next plurals
  in the diversity pie chart.
- UserActivityModal: title with username interpolation, subtitle,
  range selector label, table column headers (Date / Project /
  Compiles / Errors / Runs / Saves), pluralised
  "{{count}} project(s)" line, deleted-project / no-project
  placeholder strings, no-activity empty state, load-failed.

All 8 non-English locales auto-translated via the
`scripts/translate-i18n.mjs` DeepSeek pipeline (one --force run,
~7 min). sameShape() validation passed on every output.
2026-05-09 16:22:16 -03:00
David Montero Crespo 6bc9fe80ce feat(i18n): translate AdminPage + UserProfile + Pricing + EditorPage shell (Editor block 10 — final cleanup)
Closes the Phase 2 i18n rollout. Every visitor- and user-facing
surface velxio renders in normal use now reads from t().

AdminPage (admin-only)
- Header (panel title, logout) and the four tabs (Dashboard /
  Users / Projects / Boards).
- Setup screen for first-admin creation (title, body, password
  fields + mismatch error + create-admin button).
- Not-admin gate page.
- EditUserModal (title, four labels, admin/active toggles,
  cancel/save).
- UsersTab: search placeholder, count pluralisation, all 12
  table columns, Activity / Edit / Delete actions, empty state,
  delete-confirm prompt with username interpolation.
- ProjectsTab: search placeholder, count pluralisation, all 9
  table columns, public/private badge labels, delete action +
  confirm with project-name interpolation, empty state.
- All error messages (load failed / save failed / delete failed)
  fall back through t().

UserProfilePage
- "New project" CTA, loading + empty + not-found states,
  "Private" project badge, "Copy shareable link" tooltip.
- The /editor link uses localize() so /es/<username>'s "New
  project" button stays in Spanish.

PricingPlaceholder
- Title + the two paragraphs (self-hosted note + hosted Pro
  tier note + GitHub source note). Inline links wrapped via
  the Trans component so the link surface stays clickable in
  every locale without each translation having to re-write the
  HTML.

EditorPage shell
- Mobile bottom-tab labels (Code / Circuit), file-explorer
  toggle (Show / Hide), View mode aria-label, view-mode
  segmented control labels (Code / Both / Circuit), and the
  three "Drag to resize" handle tooltips on the panel splitters.

Translations
- en.json hand-curated for the new keys.
- All 8 non-English locales auto-translated via the existing
  `npm run translate:i18n` pipeline (DeepSeek, ~5 min for the
  whole bundle, sameShape() validates each output before write).

This closes Phase 2 of i18n. Phase 3 (DocsPage prose, AboutPage
long-form paragraphs, the 15 SEO landing pages) is deliberately
deferred — Docs/About are best handled by extracting the prose
into JSON keys and running the same script, while the SEO pages
are intentionally optimised for English keyword targeting and
should not be machine-translated en masse.
2026-05-09 12:49:13 -03:00
David Montero Crespo 99156c9c9d feat(editor): translate Oscilloscope + property dialog + selection bar + console + custom chips + sensor + board picker (Editor block 9)
This commit closes the cluster of small editor surfaces that touch
the active simulation experience. Every visible control on these
panels now reads from t() keys.

Translated:
- Oscilloscope panel (title, Add Channel button + tooltip, Time/div
  label, Run / Pause toggle copy + tooltips, Clear, empty-state copy
  + hint, per-channel remove tooltip).
- ComponentPropertyDialog (close, pin-roles header with two
  wire-mode variants, Arduino Pin label, rotate / delete buttons +
  the inline confirm-delete prompt with name interpolation).
- SelectionActionBar (toolbar aria-label, Rotate / Delete / Deselect
  with kind-aware delete labels for wire / component / board).
- CompilationConsole (Output title, error / warning badge counts
  with i18next pluralisation, filter dropdown, autoscroll label,
  Clear + Close icon tooltips, empty-state).
- CustomChipDialog (header with chipName interpolation, Examples /
  Editor tabs, Attributes panel header, compile status messages
  including the "✓ Compiled — N KB" success line, footer
  Cancel / Save & Place / Compile first buttons).
- SensorControlPanel (close button).
- BoardPickerModal (Add Board heading).

Translation pipeline
- en.json gets the new keys hand-curated.
- The 8 non-English locales were auto-translated via DeepSeek
  using the existing scripts/translate-i18n.mjs pipeline (one
  --force run, ~1 min total). Output validated with sameShape()
  before write so any LLM-introduced key drift would have failed
  loudly.

Quality note
- DeepSeek's translations now cover the entire bundle, including
  earlier hand-translated content. Tone may differ slightly from
  the prior hand passes but the meaning is consistent and brand /
  technical terms (Velxio, ngspice-WASM, ATmega328P, ESP32-C3,
  etc.) are preserved unchanged in every locale per the prompt
  invariants.
2026-05-09 12:25:30 -03:00
David Montero Crespo ea290d0c22 feat(editor): translate InstallLibrariesModal + SerialMonitor (Editor block 8)
InstallLibrariesModal — auto-install prompt that fires when an
example needs libraries:
- Title, subtitle (with the "Installing X of Y" progress
  interpolation, the all-done success state, and the singular /
  plural prompt explaining the requirement count).
- Per-row status badges (pending / installing… / installed /
  error) plus the Wokwi-hosted-library tooltip.
- Footer buttons: Close / Skip / "Install All ({{count}})" with
  loading variant.

SerialMonitor — multi-board tabbed serial console:
- Empty-state when no board is on the canvas.
- Right-side tab controls: Autoscroll checkbox, Clear button +
  tooltip.
- The "(Open IoT Gateway)" inline link rendered next to detected
  AP IP addresses.
- Output-area placeholders for the running-but-no-data and
  before-start states.
- Send button + input placeholder (different copy for MicroPython
  REPL vs raw Serial input).
- The line-ending dropdown options (None / Newline / Carriage
  return / Both).

Hand-translated for all 9 locales. Hotkeys (Ctrl+C) and dropdown
values stay untranslated (constants the firmware reads).

Pending in Phase 3:
- Oscilloscope panel, custom-chip dialog, sensor control panel.
- ComponentPropertyDialog (per-component property forms).
- Admin / Profile / Project pages.
- Long-form docs prose (DocsPage 2715 lines, AboutPage long
  paragraphs).
- 15 SEO landing pages (intentionally English for keyword targeting).
2026-05-09 11:35:22 -03:00
David Montero Crespo c1b0398c0d feat(editor): translate ComponentPicker + LibraryManager modals (Editor block 7)
ComponentPickerModal — the "Add Component" dialog:
- Header (title + close button), search input placeholder + clear,
  category tabs (All Components / Boards), loading + empty-state
  copy, "Clear filters" button.

LibraryManagerModal — the Arduino library browser:
- Window title, Search / Installed tabs, filter input placeholder.
- Search-tab states: searching-for-query, generic loading, no
  results (with optional query interpolation).
- Per-library row: "by {{author}}" caption, Install / Installing /
  Uninstall / Uninstalling button labels.
- Installed-tab empty-state with the prompt to use the Search tab.

Brand and product names left untouched ("LIBRARY MANAGER" stays
all-caps in English; the localised variants follow each language's
convention for product UI titles). Hand-translated for all 9
locales.

InstallLibrariesModal still pending — it's the auto-install
prompt that fires when an example needs libraries; smaller scope
but lives in the same area.
2026-05-09 11:29:07 -03:00
David Montero Crespo 4df81a3eda feat(examples): translate ExamplesPage + ExamplesGallery to 9 locales
The /examples gallery is fully localised:
- Header (heading + subtitle).
- Search input placeholder + aria-label + the clear button.
- Match-count tag with i18next pluralisation (handles _one /
  _other and Russian's _few / _many).
- Category and Difficulty filter labels + their button labels
  (basics / sensors / displays / communication / games / robotics
  / circuits; beginner / intermediate / advanced).
- Per-card "Copy shareable link" tooltip.
- Empty-state copy with two variants (with-search / without-
  search) interpolating the search query.
- Reset-filters button.
- The library-install progress overlay copy from
  ExamplesPage.tsx ("Installing libraries (N/M)") with done/total
  interpolation.

Internal /editor link uses localize() so a Spanish reader who
clicks an example lands on /es/editor.

Hand-translated for all 8 non-English locales. Per-example titles
+ descriptions are NOT i18n yet — they live in the
src/data/examples* tables and would need a separate pipeline.
DocsPage (2715 lines of prose) deferred too — best handled by
running scripts/translate-i18n.mjs once the keys are extracted.
2026-05-09 11:24:53 -03:00
David Montero Crespo 6e09725727 feat(about): translate AboutPage chrome + CTA + footer (partial)
The visible chrome of /about now reads from i18n in all 9 locales:
- Hero title + subtitle
- 7 section headings (Story / How It Works / Open Source Philosophy /
  Creator / Recent releases / Community & Press / CTA)
- Final CTA card (title, subtitle, "Open Editor" button)
- Footer copy switched to t('footer.about') so it shows the AGPLv3
  About-Velxio paragraph instead of the stale MIT/avr8js credit
- Footer + CTA Links wrapped in localize() so /es/about's "Open Editor"
  routes to /es/editor

Long-form prose (Story body paragraphs, Open Source Philosophy
paragraphs, Creator bio, Releases blurbs, Personal-story quote, Press
section) deliberately stays in English in this commit. Each is a
multi-paragraph chunk that benefits from a curated translation pass
rather than an inline machine pass — slate it for a follow-up.

Tech-stack tags (Java, Python, React, Docker, etc.) and the creator's
name + role + GitHub/LinkedIn/Medium link captions stay untranslated:
all proper nouns / brand identifiers.
2026-05-09 03:27:40 -03:00
David Montero Crespo 20b22b1398 feat(auth): translate LoginPage + RegisterPage to 9 locales
Both auth forms now go through t('auth.login.*') and
t('auth.register.*'):
- Title + subtitle, email / password / username labels with
  username placeholder and password-min-length placeholder.
- Submit button toggles between idle and loading states.
- "or" divider, "Continue with Google" button, and the
  switch-to-other-form footer link.
- Inline validation errors: reserved username, username regex,
  password length, plus the generic catch-all from the API.

Internal /editor and /login / /register links wrapped in
localize() so a Spanish user who registers stays at /es/editor
after success.

AboutPage (519 lines) deferred to its own commit — too dense for
the same change.
2026-05-09 03:23:09 -03:00
David Montero Crespo ecc35f72cb feat(editor): translate SimulatorCanvas header + remove dialog (Editor block 4)
The canvas header (the bar above the simulation area) and the
"Remove board?" confirmation dialog now read from i18n.

Translated:
- Status dot tooltip (Running / Stopped).
- Active board selector tooltip + "No board" placeholder + the
  hint that prompts the user to add a board.
- Undo / Redo buttons: aria-label, dynamic title with the action
  description and the empty-state fallback. Action descriptions
  themselves stay untranslated (they come from the editor history
  store as English literals — translating them would mean reaching
  into a different store; deferred).
- Serial Monitor and Oscilloscope toggles (button title + label).
- Zoom in / out / reset-view buttons.
- Component count tooltip + Add Component button.
- The error-banner Dismiss button.
- "Remove board" item in the right-click menu, with a localised
  "(N wires)" parenthetical via i18next pluralisation.
- The full removal confirmation dialog: title with board label
  interpolation, body copy with optional connected-wires sentence,
  Cancel + Remove buttons.

Pluralisation uses i18next's _one / _other (and _few / _many for
Russian) suffixes so wire counts read naturally per language.

Hand-translated for all 8 non-English locales. Untouched (deferred):
the property dialog, custom-chip dialog, sensor control panel, and
the various inline tooltips on board pins and wire endpoints —
those are denser and benefit from a separate pass.
2026-05-09 03:20:36 -03:00
David Montero Crespo fa4f3d6e80 feat(editor): translate Save / Share / LoginPrompt modals (Editor block 3)
The three modal dialogs that fire during the editor's routine save +
share + auth-required flows are now fully localised across all 9
locales.

LoginPromptModal
- Title, body, and the three buttons (Sign in / Create account /
  Cancel). Sign in / Sign up Links use localize() so a Spanish
  reader prompted to log in lands at /es/login rather than dropping
  back to English.

SaveProjectModal
- Title (toggles between Save / Update), name + description fields
  with placeholders, save button (toggles between Save / Update /
  Saving…), Cancel button.
- Visibility toggle: Public / Private label + hint copy under the
  icon.
- All four error paths now go through t() with a {{status}}
  interpolation for the generic HTTP failure message.

ShareModal
- Title, public/private label + hint pair, "Make private" /
  "Make public" toggle, Copy button, the warning shown when the
  project is private, and the Close button.

Hand-curated translations for all 8 non-English locales. Status
codes (403) and shortcut markers preserved.
2026-05-09 03:12:52 -03:00
David Montero Crespo 7f0ac2a74c feat(editor): translate FileExplorer + FileTabs to 9 locales (Editor block 2)
FileExplorer (sidebar)
- Workspace header label and the new-workspace / save-project icon
  buttons now read from t('editor.fileExplorer.*').
- Per-board section: collapse / expand toggle, status dot tooltip
  (Running / Compiled / Idle), per-board "new file" button, and the
  composite "<board name> — click to edit" hover title (the board
  name itself stays untranslated — it's a product noun like
  "Arduino Uno").
- File rows: hover title with optional "(unsaved)" suffix,
  unsaved-dot tooltip, and the right-click context menu's Rename /
  Delete commands.
- Empty-state placeholder when no boards are on the canvas.
- The window.confirm() shown before deleting a file now reads from
  t() too, so non-English users see the prompt in their language.

FileTabs (open-tabs strip above the editor)
- Per-tab close button title and the unsaved-changes dot tooltip.
- Inline confirm dialog when closing a modified file: prompt copy,
  "Close anyway" and "Cancel" buttons.

Hand-translated for all 9 locales. Hotkey hints (Ctrl+S, Strg+S)
localised per German convention; other locales keep "Ctrl+S" as the
universally-recognised label.
2026-05-09 03:08:51 -03:00