Commit Graph

8 Commits

Author SHA1 Message Date
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 c9fe6f199d feat(editor): folders - create/delete folder trees in the file explorer
File names may now carry '/' paths; the explorer renders each board group
as a collapsible folder tree (folders first, files in group order, CSS-var
indentation because .fe-file-item pins padding with !important). New-folder
buttons on the board header and per-folder hover actions (new file here,
new subfolder, delete with confirm when files are inside; a folder holding
every file of the group cannot be deleted - groups keep >= 1 file).

Model: names are the source of truth; only EMPTY folders are tracked
(folderGroups per group) so they survive until a file lands in them, then
materialise into the name prefix. Round-trips through .vlx (optional
folderGroups field, laxly validated), the auth stash draft, and
loadProjectState. Examples gain boards[].files so a single board can ship
a full multi-file tree (first entry = main sketch).

Backend: the arduino-cli sketch writer now creates subdirectories for
'/'-pathed names and REJECTS names that resolve outside the sketch dir
('..' traversal) - previously it crashed on the former and wrote outside
the tmp dir on the latter.
2026-08-05 06:48:38 +02:00
David Montero cd67362312 feat(libraries): manifest auto-migration + name-affinity ranking + install-declares
Three-part fix for the library-contamination class of ESP32 compile
failures (a user's project kept pulling unrelated libraries because 79%
of projects compile with an empty manifest, forcing scan-all resolution
over ~1000 shared libraries):

1. Name-affinity ranking in _find_library_for_header: the provider whose
   name matches the header stem wins (exact normalized match > esp32
   arch declaration > contains-stem, alphabetical only as tie-break).
   Previously first-alphabetical won, so any stray library shipping a
   generic header (Servo.h has 6 providers) could hijack the build.

2. Manifest auto-migration: scan-all compiles now report the libraries
   the build really used (manifest_incomplete +
   manifest_suggested_libraries on the compile result); the editor folds
   single-candidate suggestions into the board's declared manifest after
   a green build, so the NEXT compile runs scoped. One green build = one
   migrated project; empty-manifest scan-all behaviour is untouched.

3. Install-declares: the Wokwi import library modal records successful
   installs in the active board's manifest, and .vlx export/import now
   round-trips board.libraries.

Tests: test_espidf_library_ranking.py (9) + library-manifest.test.ts (19).
2026-08-04 06:03:32 +02:00
David Montero Crespo cfa03aeaf9 feat(sd): boards with a built-in microSD slot take user uploads
A ProBoardDef can declare builtInSdCsPin, and the store already built a FAT
card from the project files for it - but there was no way to put YOUR files
on that card: the upload panel only existed for the microsd-card component
and for parts with metadata.sdSlot, and boards have no properties bag.

Boards now carry sdFiles (same shape the SD panel persists everywhere else):

- BoardInstance.sdFiles, consumed by the store's Run path (card component
  still wins when one is on the canvas);
- the board inspector shows the SD Card panel whenever the board kind
  declares builtInSdCsPin, and routes the panel's onChange to updateBoard;
- .vlx round-trips it (serialisableBoard + payload type), and gallery
  examples can pre-load the slot via ExampleBoard.sdFiles, applied by
  loadExample the way the panel would.

First user: the XIAO ESP32S3 Sense (CS=GPIO21), whose board def and example
land in the pro overlay.
2026-08-04 05:55:21 +02:00
David Montero 8e33088752 fix(editor): sync URL after New workspace + sever project identity on .vlx import
Three stale-project-identity fixes from reviewing the New-workspace flow:

- New workspace (web): handleNewClick cleared the workspace and the
  current project but left the browser on the old /user/slug URL — a
  refresh (or back-button pop) silently reloaded the OLD project over
  the fresh unsaved workspace. Now replaceState's to the localized
  /editor (replace, not push, so no back-entry points at the stale
  project route).
- New workspace (desktop menu): same URL fix for the newProject menu
  action, which cleared identity but never left the project route.
- .vlx import: importVlxFile mutated the stores WITHOUT clearing
  currentProject — with a saved project open, autosave saw the
  imported content as dirty edits on the old projectId and silently
  PUT the .vlx contents over the user's saved project (and pushed the
  clobber to GitHub on linked projects). Now severs identity first,
  same guard loadExample.ts already documents.
2026-07-18 08:24:06 +02:00
David Montero a68e7f8e94 feat(editor): rename boards & custom chips; show which target owns each file
Phase 2 of the run-system/UX work.

- BoardInstance gains an optional user ; boardDisplayName(board) resolver
  (name || kind label) routes every INSTANCE-label surface: file-explorer
  section header, compile console (EditorToolbar), canvas selector/tooltip/
  context-menu, Serial Monitor tabs, Oscilloscope board picker, Board Options
  subtitle. Board/component pickers keep the KIND label (they pick new boards).
- Inline rename on board AND chip section headers (double-click the name, or a
  hover pencil button). Board -> updateBoard(id,{name}); chip -> chipName in
  properties. Enter commits, Escape cancels (cancel-flag ref guards the
  unmount-fires-onBlur footgun), empty clears to the kind / 'Custom Chip'.
- FileTabs shows an owner badge naming the board/chip whose files are shown
  (resolved as a selector so it doesn't re-render on every sim pin toggle).
- CustomChipDialog no longer clobbers a user-given chipName: chip.json's name
  only seeds the blank defaults (My Chip / Custom Chip); loading an example
  relabels explicitly.
- Persistence: board name round-trips via projectPayload (+ dirty hash),
  vlxFile, ProjectByIdPage load + loadProjectState; chipName rides components_json.
- Drive-by: fixed a pre-existing rules-of-hooks violation in BoardOptionsModal
  (early return before a useCallback).

Reviewed by a 3-agent adversarial pass (completeness / persistence / correctness);
all major findings folded in.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 06:22:25 +02:00
David Montero 5a23e89eb5 feat(custom-chip): program lives in its own editor group, not the board sketch
A programmable custom-chip (a CPU emulator that runs a ROM/program, e.g. the
Z80 or 8080) now keeps its program (larson.s, chaser.c, ...) in a dedicated
editor file group — group-chip-<chipId> — rendered as its own collapsible
section in the file explorer, exactly like each board owns its sketch group.
Behaviour/driver chips and predefined chips carry no programFile and get no
group; they stay editable only in the chip designer.

Fixes two reported issues on the Z80 examples:
- /example/z80-larson-no-board: the board-less chip example now opens its
  program (larson.s) as the active group, editable on the left — previously
  the editor showed but no file appeared.
- /example/z80-led-chaser-c: the chip program (chaser.c) no longer shows as
  a sibling tab inside the Arduino sketch group; it sits in its own chip
  section instead. The board group shows only sketch.ino.

Details:
- useEditorStore: chipFileGroupId()/CHIP_GROUP_PREFIX helpers.
- loadExample: seedChipProgramGroups() routes each chip's programFile into its
  own group (seeded from the example files), sweeps stale chip groups, keeps
  the program OUT of the board group, and for a board-less chip example makes
  the chip group active so the program is the editable file shown.
- EditorToolbar.prepareCustomChips: resolves the program from the chip's own
  group (falls back to board files for older projects) before assembling ROM.
- FileExplorer: renders one collapsible section per programmable chip with an
  IC icon; clicking switches the editor to the chip group. Lazy-creates a
  group for chips dropped on the canvas.
- projectPayload + vlxFile: serialise chip groups alongside board groups and
  include them in the dirty-check hash, so chip-program edits persist on
  save / autosave / .vlx export and round-trip via replaceFileGroups on load.
- Regression tests for board-less + board+chip routing and stale-group sweep.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 22:07:56 +02:00
David Montero Crespo b4ab742456 feat(oss): portable .vlx project export/import for self-hosters
Phase 4 of the OSS / pro split. The OSS image has no auth and no
server-side persistence — without this commit, the user's workspace
was ephemeral (lost on tab refresh). `.vlx` is a single-file JSON
snapshot of the entire workspace (boards, file groups, components,
wires, active board id) that the user can save to disk and reload
later.

New: utils/vlxFile.ts
  - buildVlxPayload() / buildVlxBlob() — pure snapshot of the current
    editor + simulator stores.
  - triggerDownloadVlx({ name? }) — anchor-click download with a safe
    filename. Returns the filename actually used.
  - parseVlxFile(File) — async reader + validator. Checks
    format === "velxio-project", version <= 1, and the required
    arrays/objects are present. Throws VlxParseError with a human-
    readable message on any issue.
  - importVlxFile(File) — convenience wrapper that parses AND calls
    useSimulatorStore.loadProjectState() with the result.

Format intentionally mirrors the server's POST /api/projects body so
a Pro user can export-from-pro and import-into-OSS losslessly (and
vice-versa once Pro adds an Export button — out of scope here).

lib/proSaveAction.ts: the default (no-overlay) implementation now
calls triggerDownloadVlx() instead of console.info'ing about the
missing handler. The Pro overlay still wins via installSaveActionImpl()
— Save in Pro keeps opening SaveProjectModal. The Save button in OSS
now actually saves.

components/editor/FileExplorer.tsx: new "Open .vlx" button next to
New + Save. Opens a hidden file input; confirms with the user before
replacing the workspace (loadProjectState is destructive); surfaces
VlxParseError messages via window.alert.

Verified with both builds:
  - OSS-only: triggerSaveAction → download .vlx; FileExplorer shows
    3 buttons (New, Open, Save).
  - OSS + overlay: Pro's installSaveActionImpl overrides — Save opens
    SaveProjectModal as before. Open .vlx still works (independent
    button, not part of the save flow).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 16:33:00 -03:00