Commit Graph

469 Commits

Author SHA1 Message Date
David Montero f09489f498 fix(editor): toolbar never overlaps on a narrow bar; layout switches live in the View menu
Two things went wrong when the window was small and the AI chat docked:

1. Libraries / Output painted over the board selector. The editor zone
   has min-width: max-content, but its child .editor-toolbar-wrapper is
   an inline-size query container, and size containment makes it
   contribute zero intrinsic width — so the floor resolved to nothing,
   flexbox squeezed the zone under its buttons and the bar never got to
   wrap. Containment is now off for the in-bar mount (the wrapper keeps
   it for its standalone mobile mount); the two label collapses that
   depended on it are re-keyed to the unified-toolbar container at the
   same thresholds the canvas controls use (1010 / 905px), so the row
   degrades in step and, when it truly cannot fit, wraps instead.

2. The explorer / Code / Both / Circuit segmented toggle now hides once
   the shared bar is under 760px (its inline display moved to CSS so a
   container query can do that). Every switch it had is a checkmarked
   row in the View menu: File Explorer, and Code / Both / Circuit as a
   radio group under a View mode label. explorerOpen moves from
   EditorPage state into useEditorStore so the menu can read it.
2026-08-18 00:17:38 +02:00
David Montero 0e107fafe6 feat(editor): M5Stack section (Cardputer ADV, Core) ahead of STM32 in the starter dialog
The New Project dialog now lists an M5Stack section — M5 Cardputer ADV first,
then M5Stack Core — between ESP32 and STM32. Both kinds are overlay-registered,
so the section only renders when the overlay mounted (an OSS build shows no
empty block). Selecting a card loads the board's on-LCD hello example
(cardputer-adv-hello / m5stack-core-m5-helloworld), which also provides the
card thumbnail.

The sections builder is extracted as buildStarterSections(defs) and covered
by a unit test that pins the display order.
2026-08-17 18:50:37 +02:00
David Montero Crespo 1b04e910ce fix(editor): quick suggestions stay alive inside snippet placeholders 2026-08-15 21:31:01 +02:00
David Montero aed1796a8f fix(wires): stop board-corner wire anchors — alias fallbacks for pin-name spellings
A wire endpoint naming a pin the board element does not expose silently
resolved to nothing and rendered anchored at the board corner. A full
audit (all gallery examples + every saved project on velxio.dev) found 15
examples and 28 user projects doing it — the same electrical pin spelled
differently per surface: GND.1 on the classic ESP32 (element exposes
GND/GND2), 3.3V vs 3V3, GND2 vs GND.2, D13 vs 13 vs GP13, A0-A2 vs
GP26-28 on the Pico, 36/39 vs VP/VN, bare 5V vs VBUS/VIN.

calculatePinPosition now tries those equivalences AFTER the exact match
and the two existing fallbacks miss (so no current resolution changes),
healing old saves, .vlx imports and agent-authored wires without a data
migration. The Pico element gains A0/A1/A2 alias pads on the ADC pins
(RX2/16 dual-entry precedent) and a GP25 pad at the onboard LED silk so
pico-blink-style wires have an anchor. The two examples that were
outright wrong (microsd-card-esp32 GND.1, pico-serial-echo TX) now use
the element's real pin names.

Netlist snapshots (166) untouched; verifier + builder suites green.
2026-08-15 20:08:47 +02:00
David Montero Crespo fe8ac7e4e3 fix(editor): modals above the agent side panel (z 8000 -> 9000)
With the agent panel open, the library/install/verification modals were
partially covered on the right, hiding the close button and actions.
2026-08-15 08:48:23 +02:00
David Montero Crespo cbd116ce19 fix(editor): fixedOverflowWidgets so marker hovers render above the canvas
A hover wider than the editor pane slid under the simulator canvas
(sibling stacking context) and could not be read.
2026-08-15 08:32:33 +02:00
David Montero 941df93cb3 fix(editor): reconcile active file-group pointers; stop persisting compiledProgram
deleteFileGroup left editorStore.activeGroupId dangling at the removed
group: Monaco (and agent writes) kept editing a group compile never reads,
so a stale same-named sketch.ino shipped instead of the code on screen
(the 'leftover template Blink built for 35 minutes' failure). The pointer
now re-targets a surviving group, and compile re-points the editor when
the two pointers diverge (with a visible warning log).

compiledProgram is no longer serialised into boards_json: the firmware
blob was 99.99% of a typical ESP32 save (a bare board already ~1.04 MB),
pushing every real project against the request-size ceiling, and a
restored binary is stale the moment the code differs. Loaders still
accept it from old rows.
2026-08-15 08:29:39 +02:00
David Montero d2648f5a66 fix(sim): programmatic property changes reach the running simulation
Three stacked defects made ANY non-knob property change invisible to a
running sim (values restored from saved projects, the agent's
set_component_property, the property dialog):
1. DynamicComponent assigned element properties without firing the events
   part simulators bind to — dispatch input/change + dispatchSensorUpdate
   on real value changes (applied-value guard breaks the echo loop).
2. Parts received the shared legacy AVR simulator regardless of board —
   route attachEvents to the simulator of the board the part is wired to
   (getBoardSimulator), so direct analog injection works on non-AVR boards.
3. The SPICE->ADC push stomped un-modeled parts: a net with no SPICE
   element gets an auto pull-down, solves to 0 V, and overwrote the
   part's own injected voltage every solve (the dead analog-joystick).
   Only sourced nets are pushed now.
Verified end-to-end: pot curve exact to the LSB (value 100/300/600 ->
ADC 400/1201/2401 on GPIO35), joystick idle at centre 2047, full-scale
4095 after a store write.
2026-08-15 08:29:39 +02:00
David Montero 44782a419a fix(spice): per-voltage supply rails — VIN/5V no longer clamped to the 3.3V logic rail
Board supply pins split into the main logic rail plus aux rails by real
voltage (ESP32/XIAO VIN,5V -> aux_rail_5 at 5 V; 5V-Arduino 3.3V pin ->
aux_rail_3v3; Pico VBUS/VSYS -> 5 V). An external 5 V supply wired to VIN
used to read 3.3 V (ideal vcc_rail source clamped the whole net — customer
isolated it with a voltmeter). Aux rails join positiveSourceNets and the
relay nominal-voltage check; the voltmeter and hover overlay read them.
Also: ratings entry for the 4-pin ssd1306-i2c-4pin (supply pin VCC) and a
VCC alias on the 8-pin entry, so its power check works at all.
2026-08-15 08:29:39 +02:00
David Montero Crespo c550b29f9e fix(editor): portal toolbar modals to body — container queries broke their overlays
The unified-toolbar responsiveness work gave .unified-toolbar
container-type: inline-size, which makes it the containing block for
position:fixed descendants. The three modals rendered inside the toolbar
(library manager, install-libraries, circuit verification) shrank to the
toolbar's 89px box: pinned to the top, header and close button clipped
offscreen. createPortal(document.body) makes them immune to ancestor
containment. The library manager also gets a fixed tall height
(min(86vh, 1000px)) instead of a bouncy 80vh cap.
2026-08-15 08:19:30 +02:00
David Montero Crespo 7c3b1c6e24 feat(editor): compile-diagnostics seam — publish raw build output for editor markers 2026-08-15 06:44:35 +02:00
David Montero Crespo 55bc03d281 feat(editor): intellisense registry seam for the pro completion engine 2026-08-15 06:13:25 +02:00
David Montero Crespo 8391cc7fa8 fix(gateway): keep in-browser WiFi boards reachable through the IoT gateway
The ESP32 examples now default to the in-browser JS engines, whose lwIP
serves the sketch's HTTP server from the simulation tab itself. Opening
the IoT gateway in a new tab backgrounds that tab, the browser throttles
its timers, and the in-chip server can't answer before the proxy's
timeout — the exact failure the Pico W iframe path already solved.

- WifiStatus grows an inBrowser flag (set by the overlay's JS bridges);
  the serial-monitor link and the WiFi badge now open the in-app iframe
  for any board that carries it, not just pi-pico-w.
- /api/gateway polls ~1s before answering 404: the net bridge dials on
  'got_ip', which races the sketch printing its URL, so a fast click
  used to land before the bridge registered.
2026-08-14 19:45:39 +02:00
David Montero Crespo df19778a3c chore(docs): retire the in-app /docs pages from routes and sitemap
The documentation moved to the Starlight portal served statically at
/docs/ (nginx shadows the old SPA routes and 301s the legacy slugs).
The header Documentation link becomes a full-page <a> so it reaches
nginx instead of client-routing into the removed pages.
2026-08-13 21:20:55 +02:00
David Montero Crespo aa887a1efe feat(components): runtime purchase-link override seam (registerBuyLink)
A private build can now register a per-component buy URL at boot
(fetched from its backend), superseding the datasheet front-matter.
loadDoc merges the override, so both Buy buttons, the UTM decoration
and the click tracking pick it up unchanged — and a component with no
authored datasheet gets a Buy button too (empty body, buy set).
2026-08-12 06:39:29 +02:00
David Montero Crespo e3a71e0958 feat(flash): MicroPython web-flash seam (firmware install + raw-REPL upload)
The proWebFlash seam gains an optional flashMicroPython(files) method
plus an 'uploading' progress phase. MicroPython boards enable the
Flash menu when the overlay implements it — no compile step needed,
the modal collects the workspace group's files (same path as the Run
flow) and explains the two-step install+upload. toFirmwareVariant is
exported so the flasher shares the emulator's per-chip offset table.
2026-08-11 22:55:47 +02:00
David Montero Crespo 820a0fa024 fix(flash): block web flashing for MicroPython boards
compiledProgram holds the 'micropython-loaded' sentinel in MicroPython
mode, not a flash image — atob() blew up with an encoding DOMException
before the port chooser even opened. The menu item now disables with an
explanatory tooltip and the modal shows the limitation instead of the
Connect button.
2026-08-11 22:37:15 +02:00
David Montero Crespo 8694a53d44 docs(flash): drop stale mount-time wording in the web-mode comment 2026-08-11 21:23:56 +02:00
David Montero Crespo def450f4f1 feat(flash): seam for browser Web Serial flashing (pro overlay hook)
The board context menu's Flash item and FlashModal gain a second
backend: a Web Serial flasher the pro overlay can install via
lib/proWebFlash.ts (installWebFlashImpl). Without an overlay the seam
is inert — web builds keep the desktop-only behavior. esptool-js is
declared here because the overlay has no package.json of its own; OSS
code never imports it (verified absent from the vite bundle).
2026-08-11 21:23:56 +02:00
David Montero 75efd68b85 feat(news): announcement opens as a modal before the starter dialog
The corner toast went unread. The announcement is a modal again — what
makes it viable this time is sequencing, not stacking: a tiny gate
(lib/newsGate.ts) lets EditorPage hold the pristine-visit starter
dialog until the news flow is done (nothing to show, or modal closed),
so only one startup overlay is ever on screen and news always comes
first. The pending decision is bounded at 2.5s so a dead feed cannot
hold the starter dialog hostage; reading time is unbounded.

The modal is deliberately a touch wider than the starter dialog
(880px vs 840px) and the news fetch delay drops 4s -> 250ms since the
starter now waits on it. Toast markup and styles removed; Help >
What's new keeps reopening the last delivered post.
2026-08-11 20:12:37 +02:00
David Montero Crespo fb8086c205 fix(canvas): collapse the Camera/Mic buttons to icon-only when the toolbar is cramped
With the AI chat docked, the canvas header ran out of room and the Camera
and Microphone buttons overlapped the icons before them (visible as
"…Camera" colliding on a XIAO-S3-Sense board). Serial and Scope already
collapse to icon-only via the `@container unified-toolbar` query
(font-size: 0 hides the label, the fixed-size SVG stays), but Camera/Mic
carried their layout in INLINE styles, and an inline font-size beats the
class rule so the query could never reach them.

Move the two toggles' static layout into a shared `.canvas-cam-mic-btn`
class (only state-driven styles — background, border, colour, the pulse
animation — stay inline) and add that class to the same collapse rules as
Serial, in both the container query and the mobile media query. Also drop
the header button gap from 8px to 4px once the toolbar is narrow so all
four (Serial/Camera/Mic/Scope) pack in without overlapping.

Side effect: the toggles now share Serial's 28px height, so the toolbar row
lines up (they were a touch shorter before).
2026-08-11 20:01:56 +02:00
David Montero 6e64027921 feat(news): interaction seam for What's New (impression/open/link_click/video_play)
New lib/newsEvents.ts registry (same pattern as newsSource.ts): OSS
default is a no-op sink; an extension overlay can register one to
forward the signals to its own analytics. NewsAnnouncer reports the
toast impression and modal opens (toast vs Help menu); NewsMarkdown
grows an optional onInteract callback fired on body link clicks and
YouTube preview plays — the admin authoring preview passes nothing, so
authoring never counts as engagement.
2026-08-11 18:37:23 +02:00
David Montero 8f7b878149 fix(canvas): guard the mount-time property loop against the persisted imageData string too
c201f5d9 guarded the property-sync effect, but the mount effect has its
own raw Object.entries(properties) loop that runs first and assigned the
saved imageData: '' onto the fresh element — so the firstUpdated
putImageData crash survived. Same guard, second call site.
2026-08-11 05:45:33 +02:00
David Montero 7ecc791187 fix(canvas): don't clobber a display's live ImageData with the persisted string
Saved projects carry imageData: '' in the SSD1306's properties (the
generated metadata lists it as a text prop). The property-sync effect
assigned that string onto the web component, replacing the element's
live ImageData and crashing wokwi-elements' firstUpdated with
'putImageData: parameter 1 is not of type ImageData' on every project
load. Only a real ImageData may pass through now.
2026-08-11 05:19:31 +02:00
David Montero Crespo ea1cdffb56 fix(serial): linkify bare emulated-subnet IPs, not only http:// URLs
The serial monitor turns printed device URLs into IoT-gateway links —
but only when the sketch printed a full http:// URL. That matched the
Arduino idiom and missed MicroPython's: `print("Open:",
wlan.ifconfig()[0])` emits a bare "192.168.4.15", which rendered as
plain text. The address is unreachable from the host by design (it only
exists inside the emulated network), so exactly the users who just
gained real WiFi were handed an IP with no way to open it.

The scheme is now optional; a negative lookahead keeps longer
dotted strings (192.168.4.15.99) from half-matching. ifconfig() tuples
linkify every same-subnet address they contain — all of them route to
the same gateway root, so the extra links are harmless.
2026-08-10 22:11:04 +02:00
David Montero 0653aad3d4 fix(picker): hide the Nano RP2040 Connect shell — art without a simulator
The metadata auto-scan sweeps every wokwi-elements board into the
registry, and boards Velxio does not emulate surface as component cards
that promise support we don't have. Filtered in the picker, not deleted
from the metadata: four saved projects already contain the element and
must keep rendering.
2026-08-10 21:45:05 +02:00
David Montero 2908726007 feat(editor): the starter picker offers the Nano family and the ESP32-CAM
The Arduino section listed only Uno and Mega — Nano and Nano ESP32
existed as first-class boards (kinds, FQBNs, examples) but could only be
reached through the gallery or the board dropdown. Same for the
ESP32-CAM, which now sits in the ESP32 section next to its DevKit
sibling. The camera board's starter is the webcam demo rather than a
blink: the board exists to show the sensor, and a bare LED sketch would
be a misleading first run.
2026-08-10 21:33:55 +02:00
David Montero Crespo 99471d0657 feat(news): announce with a toast, open the modal only on request
Entering the editor could stack two overlays: the starter-template
dialog opened on mount, then four seconds later the "What's new" modal
landed on top of it. Two dimmed backdrops over each other, and the
smaller, *less* important surface in front — the z-indexes said it
outright, news 10000 against the starter's 9000.

The delay made it worse rather than gentler. Arriving after the user has
started reading the templates is the most disruptive moment available;
appearing instantly would at least have let them clear it before
engaging.

The rule this restores: a modal is for what BLOCKS, and an announcement
never blocks. News now slides into the corner as a toast that dims
nothing, steals no focus (role="status", polite), and retires by itself.
The full post is one click behind "Read more" — a modal the user asked
for is a categorically different thing from one thrown at them — and
stays reachable from Help ▸ What's new once the toast is gone.

Worth noting the audience argument too: the starter dialog only fires on
a PRISTINE visit, i.e. for someone brand new, and "what's new" is
meaningless without a baseline to compare against. Returning users, the
ones it is actually written for, arrive with a project loaded and never
saw the collision.

Renamed NewsModal → NewsAnnouncer: it is not a modal by default any more,
and leaving the old name would have mislead the next reader.

Strings are new keys under `news.*`, filled in for all nine locales — the
previous modal had "What's new" and "Got it" hardcoded in English.

Toast sits at z-index 8000, deliberately BELOW the starter dialog, so if
the two ever coincide again the blocking task keeps the foreground.
2026-08-10 19:52:55 +02:00
David Montero Crespo 28eca48168 feat(editor): session entry points in the File and Account menus
The editor menubar's Account menu hosted only the shared `user-menu`
slot, so it had no way in or out of a session: the bottom-left dropdown
renders My projects / Sign in from its own markup, which this menubar
never sees. Signed-in users had no route to their saved work from the
editor, and anonymous ones had no way to sign in without leaving it.

Declares two account-scoped command ids and adds an `optional` item flag
that HIDES a row whose command is unregistered instead of the default
"render disabled". Absence here is not "temporarily unavailable" but
"does not apply": OSS has no accounts, and in pro exactly one of the two
is meaningful at a time — a greyed-out My projects would read as broken.

My projects sits third in File, at the head of the project group rather
than tacked onto the two "new …" rows: it opens saved work, which is
what Open/Save beneath it are for.

Labels reuse header.auth.{myProjects,signIn}, already translated in all
nine locales — no new strings.
2026-08-10 19:47:20 +02:00
David Montero Crespo 0b875d4440 Merge remote-tracking branch 'origin/master' into v3.2-worktree
# Conflicts:
#	backend/app/services/espidf_compiler.py
#	frontend/src/components/editor/EditorToolbar.tsx
#	frontend/src/utils/vlxFile.ts
2026-08-10 17:00:24 +02:00
David Montero Crespo 7a7c9f93f7 feat(simulator): tilt and battery controls for boards that carry an IMU or a gauge
The emulated BMI270 and battery gauge were faithful and inert: with nothing
driving them the IMU reported the board lying flat for the whole run and the
gauge reported the single voltage it was seeded with, so a sketch that reacts
to tilt or warns on low charge could not be exercised at all.

A Sensors popover on the canvas header supplies the missing half. Tilt is
entered as roll/pitch on a drag pad rather than three raw axes — what a user
means is "lean it left" — and gravity in the board's frame is that rotation
applied to (0, 0, 1) g, which is also why the vector stays at 1 g whatever the
attitude. Turning the board spins the gyro for as long as the drag lasts and
parks it back at zero shortly after, matching a real part: a held tilt reads no
rotation. Battery is a slider over the LiPo working range.

Gated per board by builtInImu / builtInBattery on the pro board def, the same
way the Mic toggle is gated, and each section renders only if its flag is set.

The microphone stream also learns to resample. The engine now tells the source
which rate the guest programmed, so a sketch recording at 8 kHz consumes two
captured samples per read instead of playing back at half speed.
2026-08-09 23:48:56 +02:00
David Montero Crespo d446031674 feat(news): bare image URLs render inline in post markdown
Authors paste screenshot URLs without knowing the ![](...) syntax; GFM
autolinks them into plain <a> tags, so videos previewed but images did
not. An autolinked href ending in an image extension now renders as the
image itself; explicit [label](...png) links stay links. All images get
loading=lazy.
2026-08-09 23:12:56 +02:00
David Montero Crespo c0d3543573 feat(metrics): compiles and runs remember which gallery example they came from
useProjectStore.currentExampleId — stamped by loadExample, dropped the
moment a real project (or a blank workspace) takes over. The compile
request and the run event carry it as example_id, analytics only, so
'which examples get compiled and run most' becomes answerable.
2026-08-09 22:34:26 +02:00
David Montero Crespo 8b2715affd feat(metrics): compiles and runs carry the editor BoardKind; ads learn to stay quiet
Several boards share one FQBN (the Pimoroni RP2350 trio all compile as
rpipico2), so FQBN-keyed metrics can't tell them apart. The compile
request and the run event now carry board_kind — analytics only, rides
into the metric hook's extra.

suppressOnlineOnlyAds(): overlay seam so the hosted build can silence
'available in the online editor' ad cards for items it manages — an
embargoed item must vanish, not advertise the site the user is on.
Pure OSS builds never call it; ads behave exactly as before.
2026-08-09 20:48:11 +02:00
David Montero Crespo f791536007 fix(wires): close both halves of the load-time endpoint race
The whenDefined recalc was necessary but not sufficient — measured on
staging, the example route loses BOTH ways:

- the element side: every settle timer can fire before the part is
  findable in the DOM. The pinInfo-ready poll in DynamicComponent now
  also re-derives wire endpoints the first time the part's pinInfo is
  actually readable, and waits up to 10s (the 2s cap was the corner-
  wire bug again on slow connections).
- the wire side: loadExample stores wires with zeroed endpoints BEHIND
  awaits (library installs), potentially after every recalc has come
  and gone. A canvas effect now spots endpoints still at (0,0) and
  runs one deferred recalc; real coordinates land, the condition
  clears, no loop.
2026-08-09 08:32:20 +02:00
David Montero Crespo 4b9fa3f0d0 feat(datasheet): Product-page links carry UTM attribution and report the click
Both Buy renderers (picker hover panel + Part Inspector) now append
utm_source=velxio&utm_medium=simulator&utm_campaign=datasheet with the
component id in utm_content, so vendors can attribute the referral.
trackProductPageClick fires the GA4 event and dispatches a
'velxio:product-page-click' CustomEvent — an overlay build can persist
the click server-side without this tree knowing about it.
2026-08-09 08:16:47 +02:00
David Montero Crespo 75842e3668 fix(wires): recalculate endpoints when a late-defined element upgrades
The canvas re-derives wire endpoints on load-settle timers (100/300/
500ms), but an overlay-defined custom element can upgrade after the
last one — its chunk is large — and calculatePinPosition then finds no
pinInfo, so every wire to the part falls back to its corner and floats
there until something moves it (field report: the Grove gesture module
with all four Grove wires dangling on bare PCB). A customElements
.whenDefined hook now recalculates when the tag actually defines,
however late that is.
2026-08-09 08:13:48 +02:00
David Montero Crespo 6d6d298130 fix(console): the final flush speaks in whole lines, from the real buffer
tailAfterStream sliced result.stdout at the live-buffer offset, but the
buffer interleaves stdout+stderr in arrival order while result.stdout is
stdout-only plus a joining newline — offsets never line up, so the flush
could start mid-line ('f/esptool_py && ...'). Instead, the poll loop now
emits one final onProgress(state:'done') with the completed buffer and
the existing delta logic flushes the tail perfectly aligned. tailAfterStream
is gone.
2026-08-09 08:05:43 +02:00
David Montero Crespo 23784568f6 fix(console): flush the stdout tail the last status poll never saw
With the success-path reprint gone, the lines that land between the
final running-poll and job completion (esptool image creation, the
binary-size summary) silently vanished. tailAfterStream() appends
result.stdout past the streamed offset, noise-filtered, before the
structured analysis — skipped once the live buffer hits its 256 KB
tail-trim cap, where offsets stop lining up.
2026-08-09 07:52:46 +02:00
David Montero Crespo 0a868cc254 fix(console): the compile log tells the story once, without cmake's diary
ESP-IDF builds sprayed the compilation console with configure chatter
(-- cmake status, git-metadata probes that 'fatal' on the synthesized
project tree, IDF component-manager NOTEs, Python env checks) and then
reprinted the entire stdout a second time when parseCompileResult ran
over the final result after the live stream had already shown it.

- isNoiseBuildLine(): pattern list for build-system chatter, applied to
  the live stream deltas and to the final-result parsing.
- parseCompileResult(streamedLive): skips the stdout reprint on success;
  on failure re-emits only error/warning lines so they still get the
  red/yellow markers the plain live stream lacked.

Compiler errors, FAILED blocks, ninja [N/M] progress, esptool output and
binary-size summaries all still show. The full log stays available
server-side.
2026-08-09 07:36:36 +02:00
David Montero Crespo 980c564f66 Revert "ux(editor): the pristine starter picker is a screen, not a modal"
This reverts commit c93d954169655d5f6f6a4cc1a68ad31b4cdd77e4.
2026-08-08 06:24:47 +02:00
David Montero Crespo fccb51f3f0 Revert "fix(starter): the takeover cedes the agent chat's docked width"
This reverts commit eeb4480dc37f3d62e94f704ebe11c37a706ef5e9.
2026-08-08 06:24:47 +02:00
David Montero Crespo 15654b664b fix(starter): the takeover cedes the agent chat's docked width
The chat reserves its width as padding-right on .app-container, but an
absolute inset-0 layer spans the padding box, so the picker's right
column and its scrollbar landed under the panel. The screen now gives
up the same width through the same CSS variable, with the same 0.12s
ease the padding uses, and falls back to zero in builds without the
overlay.
2026-08-08 05:39:23 +02:00
David Montero Crespo 3dd780503f ux(editor): the pristine starter picker is a screen, not a modal
A modal over a pristine /editor dims an interface that is empty anyway
and offers a Cancel whose only destination is a dead workspace. The
picker now takes over the content area (StarterScreen): no veil, no
Cancel, the grid gets the room the 840px dialog never had, and the
examples gallery link is the secondary path out. The modal stays for
"New workspace" over existing work, where the replace-everything
warning and Cancel mean something — both shells share one card grid.

The details that make the takeover behave like a page: it sits at
z-index 90 (above the canvas' floating zoom pill, below the header's
stacking context so the menu dropdowns stay clickable; the fixed
account corner box is render-gated instead), the panes underneath are
inert while it is up, focus lands on the heading on mount and parks on
the workspace container on dismissal, and any path that fills the
workspace meanwhile — the File-menu modal, Open project…, a dropped
.vlx — dismisses it rather than letting its cards silently wipe the
loaded work. Full editor.newProject i18n for all nine locales (German
in Sie-form), heading outline h1→h2 / h3→h4, token focus ring, and
reduced-motion respected.
2026-08-08 04:53:36 +02:00
David Montero Crespo 027a6055a4 ux(picker): maker-first ordering — passives, analog and logic gates sink to the end
The component grid and the category filter now follow a fixed
maker-first category order (sensors, outputs/LEDs, displays, input,
motors, communication, electromech, other… then passive, analog, logic):
Velxio's audience reaches for a DHT22 or an LED far more often than a
bare transistor or a 74HC gate. Stable sort — order within a category is
untouched, and search results group the same way.
2026-08-08 04:15:10 +02:00
David Montero Crespo 9d38beae0e ux(menubar): Account menu replaces the top-level Language menu
The account options that only lived in the bottom-left user button
(subscription, licenses, simulation history, replays, privacy) are now
reachable from the header too: a new Account menu hosts the pro items via
the SAME data-velxio-slot="user-menu" the corner dropdown uses — one
overlay renderer, two hosts, empty in OSS builds. Language moves from its
own top-level trigger into a section of that menu, so the menubar stays at
five triggers and frees header room for when the AI chat docks.
2026-08-08 04:02:32 +02:00
David Montero Crespo f9a9657b5e ux(canvas): zoom controls float on the canvas' bottom-right corner
The canvas header runs out of room when the AI chat panel docks on the
right — the board selector clipped and the zoom cluster was squeezed out
entirely. The −/%/+ cluster now floats on the circuit canvas' bottom-right
corner (always free), with stopPropagation so a click never starts a pan.
2026-08-07 23:53:48 +02:00
David Montero Crespo cd83813a9b fix(picker+inspector): dedupe Pi cards, wire Zero/1/2 as real boards, readable many-pin inspectors
Component picker: registry entries that ARE boards (the injected Raspberry
Pi family) no longer render as component cards — clicking one dropped a
dead 40-pin canvas prop that never boots (that is how "add a Pi 4" looked
like it placed a Pi 3). The boards row is the only path now, and it gains
Raspberry Pi Zero / 1 / 2 cards (Pi-3 illustration, PRO pill, same gate):
their QEMU profiles already existed in qemu_manager; the canvas renders
them through the Pi-3 element and BoardOnCanvas/store route them like any
Pi kind.

Part inspector: a board whose canvas node is NOT a registered custom
element (the Pi family's plain <img> wrapper) now clones the live node for
the preview instead of giving up — the Pi 3/4/5 pin map was completely
missing before. Wide boards with 40+ pins (Mega, Pi family) render
ROTATED 90° so their long pin rows become side columns with vertical room
(new PINS_ART_ROTATED_H box), and pinInspectorLayout splits a dual-line
header pair to OPPOSITE gutters the way printed pinout posters do — 40
interleaved labels never fit one gutter. Existing 18 layout tests pass.
2026-08-07 23:03:05 +02:00
David Montero Crespo e4e4331010 ux(canvas): component camera toggles say "Camera", like the board toggle
The header spoke two languages: the ESP32-CAM/Sense board toggle says
"Camera" while component-owned webcams printed their registry label
("HuskyLens cam", "Gesture cam") — which read as a different control.
The button now says "Camera" everywhere; the component identity stays in
the tooltip, where it disambiguates when several cameras coexist.
2026-08-07 22:46:00 +02:00
David Montero Crespo da4678bb60 ux(editor): smaller starter cards, bigger blue + on Blank, Arduino Mega card
Cards drop to a 178px grid (dialog 840px) with tighter type so more
starters fit per row; the Blank card's plus glyph goes accent-blue at
64px. Arduino Mega (mega-blink) joins the Arduino section next to the
Uno. Recaptured xiao-rp2040-blink.webp now that the board art is
silk-screened "XIAO-RP2040" (was "XIAO-ESP32-C3").
2026-08-07 22:11:43 +02:00