diff --git a/docs/THIRD_PARTY.md b/docs/THIRD_PARTY.md
index c77c7eba..3c239e03 100644
--- a/docs/THIRD_PARTY.md
+++ b/docs/THIRD_PARTY.md
@@ -357,6 +357,25 @@ npx tsx ../scripts/generate-component-metadata.ts
Verify that the component has simulation logic registered in `PartSimulationRegistry` (files `BasicParts.ts` or `ComplexParts.ts`). Only the 16 registered components have interactive behavior.
+## Fritzing parts library (CC-BY-SA)
+
+Velxio ships a few Fritzing-drawn breadboard SVGs as static assets for
+parts that wokwi-elements does not include. The original SVGs live
+under `third-party/fritzing-parts/` (vendored upstream copy from
+https://github.com/fritzing/fritzing-parts) and are licensed
+**Creative Commons Attribution-ShareAlike** (CC-BY-SA), which requires
+attribution and that derivatives stay under the same licence.
+
+| Velxio asset | Fritzing source | Notes |
+|---|---|---|
+| `frontend/public/component-svgs/bmp280.svg` | `svg/core/breadboard/bmp180_breadboard.svg` | Adafruit BMP180 breakout art reused for the BMP280 (visually identical Bosch predecessor). |
+| `frontend/public/component-svgs/attiny85.svg` | `svg/core/breadboard/ATtiny85_breadboard.svg` | DIP-8 ATtiny85, used by `Attiny85Element.ts`. |
+
+Each Web Component that mounts one of these assets carries an inline
+attribution comment pointing back to its source. Any new asset copied
+out of `third-party/fritzing-parts/` MUST be added to this table and
+to the corresponding component's header comment.
+
## References
- [Wokwi Elements Documentation](https://elements.wokwi.com/)
@@ -364,3 +383,5 @@ Verify that the component has simulation logic registered in `PartSimulationRegi
- [Wokwi Simulator](https://wokwi.com)
- [Lit Documentation](https://lit.dev/) — Framework used by wokwi-elements
- [Web Components Guide](https://developer.mozilla.org/en-US/docs/Web/Web_Components)
+- [Fritzing parts repository](https://github.com/fritzing/fritzing-parts)
+- [CC-BY-SA 4.0 licence text](https://creativecommons.org/licenses/by-sa/4.0/)
diff --git a/frontend/public/component-svgs/attiny85.svg b/frontend/public/component-svgs/attiny85.svg
new file mode 100644
index 00000000..a2ad627b
--- /dev/null
+++ b/frontend/public/component-svgs/attiny85.svg
@@ -0,0 +1,97 @@
+
+
+
+
diff --git a/frontend/public/component-svgs/bmp280.svg b/frontend/public/component-svgs/bmp280.svg
new file mode 100644
index 00000000..bfa4ff49
--- /dev/null
+++ b/frontend/public/component-svgs/bmp280.svg
@@ -0,0 +1,69 @@
+
+
+
+
diff --git a/frontend/public/components-metadata.json b/frontend/public/components-metadata.json
index e34223f0..1826aa78 100644
--- a/frontend/public/components-metadata.json
+++ b/frontend/public/components-metadata.json
@@ -3980,7 +3980,7 @@
"category": "passive"
},
{
- "thumbnail": "",
+ "thumbnail": "",
"tags": [
"sensor",
"i2c",
diff --git a/frontend/src/components/simulator/BoardOnCanvas.tsx b/frontend/src/components/simulator/BoardOnCanvas.tsx
index 0fe8c20a..d9dfab39 100644
--- a/frontend/src/components/simulator/BoardOnCanvas.tsx
+++ b/frontend/src/components/simulator/BoardOnCanvas.tsx
@@ -30,7 +30,7 @@ const BOARD_SIZE: Record = {
'arduino-nano-esp32': { w: 217, h: 90 },
'xiao-esp32-c3': { w: 91, h: 117 },
'aitewinrobot-esp32c3-supermini': { w: 90, h: 123 },
- attiny85: { w: 160, h: 100 },
+ attiny85: { w: 160, h: 132 },
};
interface BoardOnCanvasProps {
diff --git a/frontend/src/components/velxio-components/Attiny85Element.ts b/frontend/src/components/velxio-components/Attiny85Element.ts
index 4d2ab8d9..40d1121b 100644
--- a/frontend/src/components/velxio-components/Attiny85Element.ts
+++ b/frontend/src/components/velxio-components/Attiny85Element.ts
@@ -1,50 +1,52 @@
/**
- * ATtiny85 — Web Component (DIP-8 package, Digispark-style)
+ * Attiny85Element.ts — DIP-8 ATtiny85 Web Component
*
- * Exposes the standard `pinInfo` array used by the wire system to position
- * wire endpoints on real pins instead of the component corner.
+ * Visual: Fritzing-drawn ATtiny85 DIP package loaded as a static asset
+ * from /component-svgs/attiny85.svg. Original copied verbatim from
+ * third-party/fritzing-parts/svg/core/breadboard/ATtiny85_breadboard.svg
+ * (CC-BY-SA, see THIRDPARTY_LICENSES.md).
*
- * Pin layout (DIP-8):
- * Left side (top to bottom): PB5 (RST), PB3, PB4, GND
- * Right side (top to bottom): VCC, PB2, PB1, PB0
+ * Geometry note (different from the original hand-drawn version):
+ * The Fritzing layout puts the chip HORIZONTAL with 4 pins on the TOP
+ * edge and 4 pins on the BOTTOM edge — not vertical with pins on
+ * left/right like the older art. Existing wired examples will rewire
+ * automatically (the wire system reads coords by pin name from
+ * pinInfo) but external components positioned to the right of the
+ * chip may need to be moved manually for clean routing.
*
- * The visual layout matches the older React component
- * (`Attiny85.tsx` → `Attiny85`), but here we own the DOM so wires can read
- * `element.pinInfo`. The `led1` attribute mirrors the PB1 (Digispark LED)
- * pin state.
+ * viewBox 28.801 × 23.768 mm scaled uniformly at 5.555 px/mm → 160 × 132 px.
+ * Pin terminal centres in the source SVG (mm), all 8 connectors:
+ * row x y pin label
+ * top 3.601 1.084 VCC (connector7)
+ * top 10.801 1.084 PB2 (connector6)
+ * top 18.000 1.084 PB1 (connector5)
+ * top 25.201 1.084 PB0 (connector4)
+ * bottom 3.601 22.687 PB5 (connector0, RST)
+ * bottom 10.801 22.687 PB3 (connector1)
+ * bottom 18.000 22.687 PB4 (connector2)
+ * bottom 25.201 22.687 GND (connector3)
+ *
+ * Built-in LED on PB1 (Digispark convention) is overlaid as a circle in
+ * the outer SVG — drives off the `led1` attribute / property like before.
*/
-// DIP-8 dimensions (must match BoardOnCanvas BOARD_SIZE = 160 × 100)
const W = 160;
-const H = 100;
-const BX = 30; // chip body left
-const BY = 10; // chip body top
-const BW = 100; // chip body width
-const BH = 80; // chip body height
-const PIN_W = 28; // pin stub length
+const H = 132;
+const TOP_Y = 6; // CSS px — y of all four top-edge pin tips
+const BOT_Y = 126; // CSS px — y of all four bottom-edge pin tips
+const PIN_X = [20, 60, 100, 140] as const;
-// Vertical pin centres
-const PIN_STARTS_Y = [BY + 10, BY + 30, BY + 50, BY + 70];
-
-const PIN_LABELS_LEFT = ['PB5', 'PB3', 'PB4', 'GND'];
-const PIN_LABELS_RIGHT = ['VCC', 'PB2', 'PB1', 'PB0'];
-
-/**
- * Pin tip coordinates (where wires must attach), in SVG pixels relative to
- * the element's top-left corner. The wire system reads these via the
- * `pinInfo` property on the rendered DOM element.
- */
const PIN_INFO: ReadonlyArray<{ name: string; x: number; y: number; description?: string }> = [
- // Left column — pin tip = BX - PIN_W
- { name: 'PB5', x: BX - PIN_W, y: PIN_STARTS_Y[0], description: 'PB5 / RST' },
- { name: 'PB3', x: BX - PIN_W, y: PIN_STARTS_Y[1] },
- { name: 'PB4', x: BX - PIN_W, y: PIN_STARTS_Y[2] },
- { name: 'GND', x: BX - PIN_W, y: PIN_STARTS_Y[3] },
- // Right column — pin tip = BX + BW + PIN_W
- { name: 'VCC', x: BX + BW + PIN_W, y: PIN_STARTS_Y[0] },
- { name: 'PB2', x: BX + BW + PIN_W, y: PIN_STARTS_Y[1] },
- { name: 'PB1', x: BX + BW + PIN_W, y: PIN_STARTS_Y[2], description: 'PB1 / built-in LED' },
- { name: 'PB0', x: BX + BW + PIN_W, y: PIN_STARTS_Y[3] },
+ // Top row — left to right
+ { name: 'VCC', x: PIN_X[0], y: TOP_Y, description: 'VCC (Pin 8)' },
+ { name: 'PB2', x: PIN_X[1], y: TOP_Y, description: 'PB2 / SCK / SCL / ADC1 (Pin 7)' },
+ { name: 'PB1', x: PIN_X[2], y: TOP_Y, description: 'PB1 / MISO / built-in LED (Pin 6)' },
+ { name: 'PB0', x: PIN_X[3], y: TOP_Y, description: 'PB0 / MOSI / SDA / OC0A (Pin 5)' },
+ // Bottom row — left to right
+ { name: 'PB5', x: PIN_X[0], y: BOT_Y, description: 'PB5 / RESET (Pin 1)' },
+ { name: 'PB3', x: PIN_X[1], y: BOT_Y, description: 'PB3 / XTAL1 / ADC3 (Pin 2)' },
+ { name: 'PB4', x: PIN_X[2], y: BOT_Y, description: 'PB4 / XTAL2 / ADC2 (Pin 3)' },
+ { name: 'GND', x: PIN_X[3], y: BOT_Y, description: 'GND (Pin 4)' },
];
class Attiny85Element extends HTMLElement {
@@ -75,7 +77,6 @@ class Attiny85Element extends HTMLElement {
return PIN_INFO;
}
- /** Property setter so React's `useEffect` path can drive the LED. */
set led1(v: boolean) {
this._led1 = !!v;
this.updateLed();
@@ -101,69 +102,49 @@ class Attiny85Element extends HTMLElement {
private render() {
if (!this.shadowRoot) return;
- // Build pin stubs + labels
- const leftStubs = PIN_STARTS_Y.map(
- (py) => ``,
- ).join('');
- const rightStubs = PIN_STARTS_Y.map(
- (py) => ``,
- ).join('');
- const leftLabels = PIN_LABELS_LEFT.map(
- (label, i) =>
- `${label === 'PB5' ? 'PB5/RST' : label}`,
- ).join('');
- const rightLabels = PIN_LABELS_RIGHT.map(
- (label, i) =>
- `${label}`,
- ).join('');
-
- // Built-in LED on PB1 (right side, 3rd from top → PIN_STARTS_Y[2])
const ledFill = this._led1 ? '#ffee44' : '#333';
const ledStroke = this._led1 ? '#ffcc00' : '#555';
const ledFilter = this._led1 ? 'drop-shadow(0 0 4px #ffcc00)' : 'none';
+ // Place the LED indicator just ABOVE the PB1 top-pin (PIN_X[2], TOP_Y),
+ // outside the chip body so it stays readable.
+ const ledX = PIN_X[2];
+ const ledY = TOP_Y - 12;
+
this.shadowRoot.innerHTML = `
`;
}
diff --git a/frontend/src/components/velxio-components/Bmp280Element.ts b/frontend/src/components/velxio-components/Bmp280Element.ts
index 732fd335..0e530195 100644
--- a/frontend/src/components/velxio-components/Bmp280Element.ts
+++ b/frontend/src/components/velxio-components/Bmp280Element.ts
@@ -1,59 +1,64 @@
/**
- * Bmp280Element.ts — Custom Web Component for the BMP280 barometric sensor.
+ * Bmp280Element.ts — BMP280 barometric pressure + temperature sensor
*
- * wokwi-elements does not include a BMP280 element, so we define one here.
- * Visual: a small I2C breakout board (82 × 61 px) styled after the MPU6050 module.
+ * wokwi-elements does not include a BMP280, so we ship our own. The visual
+ * is the Fritzing-drawn BMP180 breakout (mechanically identical Bosch
+ * predecessor — same I2C interface, same 4-pin pinout) loaded as a static
+ * asset from /component-svgs/bmp280.svg. Original SVG copied verbatim from
+ * third-party/fritzing-parts/svg/core/breadboard/bmp180_breadboard.svg
+ * (CC-BY-SA, see THIRDPARTY_LICENSES.md).
*
- * Pins:
- * VCC (left, top) — 3.3 V power
- * GND (left, 2nd) — Ground
- * SDA (left, 3rd) — I2C data
- * SCL (left, bottom)— I2C clock
+ * Geometry (matches the Fritzing source):
+ * viewBox 28.35 × 35.43 mm, scaled uniformly at 2.822 px/mm → 80 × 100 px.
+ * Connector centres in the source SVG (mm): all at y=4.821, with
+ * SDA → x=3.375 GND → x=17.773
+ * SCL → x=10.574 VCC → x=24.975
+ * Maps to CSS px (x ≈ mm × 2.822):
+ * SDA(9.5, 13.6) GND(50.2, 13.6)
+ * SCL(29.8, 13.6) VCC(70.5, 13.6)
+ *
+ * Pin order/names taken from third-party/fritzing-parts/core/bmp180_breakout.fzp
+ * (Adafruit BMP180 breakout, variant 3).
*/
+const W = 80; // CSS px
+const H = 100;
+const PIN_Y = 13.6; // CSS px — y of all four pin tips
+
+const PIN_INFO = [
+ { name: 'SDA', x: 9.5, y: PIN_Y, number: 1, signals: [] as Array },
+ { name: 'SCL', x: 29.8, y: PIN_Y, number: 2, signals: [] as Array },
+ { name: 'GND', x: 50.2, y: PIN_Y, number: 3, signals: [{ type: 'power', signal: 'GND' }] },
+ { name: 'VCC', x: 70.5, y: PIN_Y, number: 4, signals: [{ type: 'power', signal: 'VCC' }] },
+];
+
class Bmp280Element extends HTMLElement {
- readonly pinInfo = [
- { name: 'VCC', x: 0, y: 10, number: 1, signals: [{ type: 'power', signal: 'VCC' }] },
- { name: 'GND', x: 0, y: 22, number: 2, signals: [{ type: 'power', signal: 'GND' }] },
- { name: 'SDA', x: 0, y: 34, number: 3, signals: [] },
- { name: 'SCL', x: 0, y: 46, number: 4, signals: [] },
- ];
+ readonly pinInfo = PIN_INFO;
constructor() {
super();
this.attachShadow({ mode: 'open' }).innerHTML = `
-
-