/** * ATtiny85 — Web Component (DIP-8 package, Digispark-style) * * Exposes the standard `pinInfo` array used by the wire system to position * wire endpoints on real pins instead of the component corner. * * Pin layout (DIP-8): * Left side (top to bottom): PB5 (RST), PB3, PB4, GND * Right side (top to bottom): VCC, PB2, PB1, PB0 * * 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. */ // 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 // 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] }, ]; class Attiny85Element extends HTMLElement { static get observedAttributes(): string[] { return ['led1']; } private _led1 = false; constructor() { super(); this.attachShadow({ mode: 'open' }); } connectedCallback() { this.render(); } attributeChangedCallback(name: string, _old: string | null, value: string | null) { if (name === 'led1') { this._led1 = value !== null && value !== 'false'; this.updateLed(); } } /** Wires use this to find pin coordinates. */ get pinInfo() { return PIN_INFO; } /** Property setter so React's `useEffect` path can drive the LED. */ set led1(v: boolean) { this._led1 = !!v; this.updateLed(); } get led1(): boolean { return this._led1; } private updateLed() { const led = this.shadowRoot?.getElementById('attiny-led1') as unknown as SVGElement | null; if (!led) return; if (this._led1) { led.setAttribute('fill', '#ffee44'); led.setAttribute('stroke', '#ffcc00'); led.style.filter = 'drop-shadow(0 0 4px #ffcc00)'; } else { led.setAttribute('fill', '#333'); led.setAttribute('stroke', '#555'); led.style.filter = 'none'; } } 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'; this.shadowRoot.innerHTML = ` ${leftStubs} ${rightStubs} ATtiny85 8-bit AVR ${leftLabels} ${rightLabels} `; } } if (!customElements.get('velxio-attiny85')) { customElements.define('velxio-attiny85', Attiny85Element); } export {};