Function bodies 456 total
toggleLightMode function · javascript · L177-L183 (7 LOC)client/src/pages/StringSheets.jsx
function toggleLightMode() {
setLightMode(prev => {
const next = !prev
try { localStorage.setItem('ss_light_mode', next ? '1' : '0') } catch {}
return next
})
}flash function · javascript · L185-L188 (4 LOC)client/src/pages/StringSheets.jsx
function flash(msg, type = 'ok') {
setFeedback({ msg, type })
setTimeout(() => setFeedback(null), 2500)
}handleChange function · javascript · L190-L202 (13 LOC)client/src/pages/StringSheets.jsx
function handleChange(entry, value) {
const id = `${entry.cat}.${entry.key}`
setOverrides(prev => {
const next = { ...prev }
if (value === entry.default) {
delete next[id]
} else {
next[id] = value
}
saveOverrides(next)
return next
})
}handleReset function · javascript · L204-L213 (10 LOC)client/src/pages/StringSheets.jsx
function handleReset() {
if (!confirm('Reset all values and tag edits to defaults? This clears localStorage.')) return
localStorage.removeItem(LS_KEY)
localStorage.removeItem(LS_TAGS_KEY)
localStorage.removeItem(LS_TOKEN_KEY)
setOverrides({})
setTagOverrides({})
setTokenExamples({})
flash('Reset to defaults')
}handleExportCsv function · javascript · L215-L219 (5 LOC)client/src/pages/StringSheets.jsx
function handleExportCsv() {
const csv = buildCsv(STRING_CATALOG, overrides)
downloadFile(csv, 'game-strings.csv', 'text/csv;charset=utf-8;')
flash('CSV downloaded')
}handleDownloadJs function · javascript · L221-L225 (5 LOC)client/src/pages/StringSheets.jsx
function handleDownloadJs() {
const js = buildJsFile(STRING_CATALOG, overrides, tagOverrides)
downloadFile(js, 'gameStrings.js', 'text/javascript')
flash('gameStrings.js downloaded — replace the file in client/src/strings/')
}handleCopyJs function · javascript · L227-L230 (4 LOC)client/src/pages/StringSheets.jsx
function handleCopyJs() {
const js = buildJsFile(STRING_CATALOG, overrides, tagOverrides)
navigator.clipboard.writeText(js).then(() => flash('Copied to clipboard'))
}Repobility analyzer · published findings · https://repobility.com
handleApplyToSource function · javascript · L232-L253 (22 LOC)client/src/pages/StringSheets.jsx
async function handleApplyToSource() {
const js = buildJsFile(STRING_CATALOG, overrides, tagOverrides)
try {
const res = await fetch('/dev/write-file', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ file: '../shared/strings/gameStrings.js', content: js }),
})
const data = await res.json()
if (data.ok) {
localStorage.removeItem(LS_KEY)
localStorage.removeItem(LS_TAGS_KEY)
setOverrides({})
setTagOverrides({})
flash('Applied — gameStrings.js updated')
} else {
flash(data.error || 'Write failed', 'err')
}
} catch {
flash('Could not reach dev server', 'err')
}
}handleImportClick function · javascript · L255-L257 (3 LOC)client/src/pages/StringSheets.jsx
function handleImportClick() {
importRef.current?.click()
}handleImportFile function · javascript · L259-L292 (34 LOC)client/src/pages/StringSheets.jsx
function handleImportFile(e) {
const file = e.target.files?.[0]
if (!file) return
const reader = new FileReader()
reader.onload = (ev) => {
try {
const rows = parseCsv(ev.target.result)
const next = { ...overrides }
let count = 0
for (const row of rows) {
const cat = row.category?.trim()
const key = row.key?.trim()
const value = row.value?.trim()
if (!cat || !key || value === undefined) continue
const entry = STRING_CATALOG.find(e => e.cat === cat && e.key === key)
if (!entry) continue
const id = `${cat}.${key}`
if (value === entry.default) {
delete next[id]
} else {
next[id] = value
count++
}
}
saveOverrides(next)
setOverrides(next)
flash(`Imported — ${count} override${count !== 1 ? 's' : ''} applied`)
} catch {
flash('Import failed — check CSV formatgetAutoTagId function · javascript · L297-L301 (5 LOC)client/src/pages/StringSheets.jsx
function getAutoTagId(entry) {
const autoGroup = TAG_GROUPS.find(g => g.id === entry.cat)
const prefix = entry.key.split('.')[0]
return autoGroup?.tags.find(t => t.id === prefix)?.id ?? null
}getEntryTags function · javascript · L303-L307 (5 LOC)client/src/pages/StringSheets.jsx
function getEntryTags(entry) {
const explicit = getEffectiveTags(entry, tagOverrides)
const autoTagId = getAutoTagId(entry)
return autoTagId ? [autoTagId, ...explicit] : explicit
}handleAddTag function · javascript · L309-L318 (10 LOC)client/src/pages/StringSheets.jsx
function handleAddTag(entry, tag) {
const id = `${entry.cat}.${entry.key}`
setTagOverrides(prev => {
const current = prev[id] ?? entry.tags ?? []
if (current.includes(tag)) return prev
const next = { ...prev, [id]: [...current, tag] }
saveTagOverrides(next)
return next
})
}handleRemoveTag function · javascript · L320-L336 (17 LOC)client/src/pages/StringSheets.jsx
function handleRemoveTag(entry, tag) {
const id = `${entry.cat}.${entry.key}`
setTagOverrides(prev => {
const current = prev[id] ?? entry.tags ?? []
const updated = current.filter(t => t !== tag)
const next = { ...prev }
// If the updated list equals the catalog default, remove the override
const defaultTags = entry.tags ?? []
if (updated.length === defaultTags.length && updated.every((t, i) => t === defaultTags[i])) {
delete next[id]
} else {
next[id] = updated
}
saveTagOverrides(next)
return next
})
}handleTokenExample function · javascript · L338-L356 (19 LOC)client/src/pages/StringSheets.jsx
function handleTokenExample(entry, token, value) {
const id = `${entry.cat}.${entry.key}`
setTokenExamples(prev => {
const entryTokens = { ...(prev[id] || {}) }
if (value === '') {
delete entryTokens[token]
} else {
entryTokens[token] = value
}
const next = { ...prev }
if (Object.keys(entryTokens).length === 0) {
delete next[id]
} else {
next[id] = entryTokens
}
saveTokenExamples(next)
return next
})
}Open data scored by Repobility · https://repobility.com
getStr function · javascript · L12-L20 (9 LOC)client/src/strings/index.js
export function getStr(cat, key) {
try {
const raw = localStorage.getItem(LS_KEY)
const overrides = raw ? JSON.parse(raw) : {}
return overrides[`${cat}.${key}`] ?? defaults[`${cat}.${key}`] ?? key
} catch {
return defaults[`${cat}.${key}`] ?? key
}
}devWriteFilePlugin function · javascript · L8-L45 (38 LOC)client/vite.config.js
function devWriteFilePlugin() {
return {
name: 'dev-write-file',
configureServer(server) {
server.middlewares.use('/dev/write-file', (req, res) => {
if (req.method !== 'POST') {
res.statusCode = 405;
res.end('Method Not Allowed');
return;
}
let body = '';
req.on('data', chunk => { body += chunk; });
req.on('end', () => {
try {
const { file, content } = JSON.parse(body);
// Resolve from client/ dir; allow writes to src/ or ../shared/strings/
const target = path.resolve(__dirname, file);
const allowed = [
path.resolve(__dirname, 'src'),
path.resolve(__dirname, '../shared/strings'),
];
if (!allowed.some(root => target.startsWith(root))) {
res.statusCode = 403;
res.end(JSON.stringify({ error: 'Path not in allowed write roots' }));
return;
}
displayInit function · cpp · L40-L50 (11 LOC)esp32-terminal/src/display.cpp
void displayInit() {
// Initialize SPI with ESP32-S3 pins
SPI.begin(PIN_OLED_CLK, -1, PIN_OLED_MOSI, PIN_OLED_CS);
// Initialize U8g2
u8g2.begin();
u8g2.setContrast(255); // Max contrast for amber OLED
u8g2.clearBuffer();
u8g2.sendBuffer();
}displayClear function · cpp · L51-L55 (5 LOC)esp32-terminal/src/display.cpp
void displayClear() {
u8g2.clearBuffer();
u8g2.sendBuffer();
}getStyleColor function · cpp · L59-L71 (13 LOC)esp32-terminal/src/display.cpp
uint8_t getStyleColor(DisplayStyle style) {
switch (style) {
case DisplayStyle::LOCKED:
case DisplayStyle::CRITICAL:
return 255; // Bright
case DisplayStyle::ABSTAINED:
return 128; // Dim
case DisplayStyle::WAITING:
return 200; // Slightly dim
default:
return 255; // Normal = bright
}
}drawIconXBM function · cpp · L74-L78 (5 LOC)esp32-terminal/src/display.cpp
static void drawIconXBM(const uint8_t* icon, int x, int y) {
if (icon == nullptr) return;
u8g2.setDrawColor(1);
u8g2.drawXBMP(x, y, ICON_SIZE, ICON_SIZE, icon);
}drawSelectionBar function · cpp · L81-L85 (5 LOC)esp32-terminal/src/display.cpp
static void drawSelectionBar(int activeIndex) {
if (activeIndex < 0 || activeIndex > 2) return;
u8g2.setDrawColor(1);
u8g2.drawBox(BAR_X, SLOT_Y[activeIndex], BAR_W, ICON_SLOT_H);
}_renderOperator function · cpp · L90-L179 (90 LOC)esp32-terminal/src/display.cpp
static void _renderOperator(const DisplayState& state) {
static const int OP_Y[] = {14, 34, 54}; // baselines for 3 evenly-spaced lines
static const int MAX_CHARS = 37; // max chars per line at 6px each
// Build combined text
String sentence = state.line1.left;
String preview = state.line2.text;
String combined;
if (sentence.length() > 0 && preview.length() > 0)
combined = sentence + " " + preview;
else if (sentence.length() > 0)
combined = sentence;
else
combined = preview;
// Word-wrap into 3 rows
String rows[3] = {"", "", ""};
int rowIdx = 0;
int pos = 0;
while (pos < (int)combined.length() && rowIdx < 3) {
int sp = combined.indexOf(' ', pos);
String word = (sp == -1) ? combined.substring(pos) : combined.substring(pos, sp);
pos = (sp == -1) ? combined.length() : sp + 1;
if (word.length() == 0) continue;
String candidate = rows[rowIdx].length() Source: Repobility analyzer · https://repobility.com
_renderBuffer function · cpp · L182-L255 (74 LOC)esp32-terminal/src/display.cpp
static void _renderBuffer(const DisplayState& state) {
u8g2.clearBuffer();
// Operator sentence mode uses completely different layout
if (state.line2.style == DisplayStyle::OPERATOR) {
_renderOperator(state);
u8g2.sendBuffer();
return;
}
// === ICON COLUMN ===
for (int i = 0; i < 3; i++) {
const uint8_t* bitmap = getIconBitmap(state.icons[i].id);
drawIconXBM(bitmap, ICON_COL_X, ICON_Y[i]);
}
// Draw selection bar next to the active icon slot (only if 2+ icons visible)
int visibleIcons = 0;
for (int i = 0; i < 3; i++) {
if (state.icons[i].id != "empty") visibleIcons++;
}
if (visibleIcons >= 2) {
drawSelectionBar(state.idleScrollIndex);
}
// === LINE 1: Context (small, left and right aligned) ===
u8g2.setFont(FONT_SMALL);
u8g2.setDrawColor(1);
u8g2.drawStr(MARGIN_X, LINE1_Y, state.line1.left.c_str());
if (state.line1.right.length() > 0) {
int rightWdisplayRender function · cpp · L256-L283 (28 LOC)esp32-terminal/src/display.cpp
void displayRender(const DisplayState& state) {
// Operator mode renders once (inverted box provides the visual, no blink animation)
if (state.line2.style == DisplayStyle::OPERATOR) {
_renderBuffer(state);
return;
}
// Critical content only flashes on first display — track seen text so
// scrolling away and back does not re-trigger the blink animation.
static String lastCriticalText = "";
bool isCritical = state.line2.style == DisplayStyle::CRITICAL;
bool isNewCritical = isCritical && state.line2.text != lastCriticalText;
if (isCritical) lastCriticalText = state.line2.text;
int blinkCycles = isNewCritical ? 3 : 1;
for (int blink = 0; blink < blinkCycles; blink++) {
if (blink > 0) {
// Blank frame between blinks
delay(150);
u8g2.clearBuffer();
u8g2.sendBuffer();
delay(150);
}
_renderBuffer(state);
}
}displayMessage function · cpp · L284-L293 (10 LOC)esp32-terminal/src/display.cpp
void displayMessage(const char* line1, const char* line2, const char* line3) {
DisplayState state;
state.line1.left = line1;
state.line1.right = "";
state.line2.text = line2;
state.line2.style = DisplayStyle::NORMAL;
state.line3.text = line3;
displayRender(state);
}displayPlayerSelect function · cpp · L294-L328 (35 LOC)esp32-terminal/src/display.cpp
void displayPlayerSelect(uint8_t selectedPlayer) {
u8g2.clearBuffer();
// === LINE 1: Title ===
u8g2.setFont(FONT_SMALL);
u8g2.setDrawColor(1);
char selectTitle[32];
snprintf(selectTitle, sizeof(selectTitle), "%s > SELECT TERMINAL", FIRMWARE_VERSION);
u8g2.drawStr(MARGIN_X, LINE1_Y, selectTitle);
// === LINE 2: Selected player/operator (large, centered) ===
u8g2.setFont(FONT_LARGE);
char playerText[16];
if (selectedPlayer == 0) {
snprintf(playerText, sizeof(playerText), "OPERATOR");
} else {
snprintf(playerText, sizeof(playerText), "PLAYER %d", selectedPlayer);
}
int textWidth = u8g2.getStrWidth(playerText);
int textX = (DISPLAY_WIDTH - textWidth) / 2;
// Draw selection box
u8g2.drawFrame(textX - 6, LINE2_Y - 18, textWidth + 12, 24);
u8g2.drawStr(textX, LINE2_Y, playerText);
// === LINE 3: Instructions ===
u8g2.setFont(FONT_SMALL);
const char* instructions = "DIAL select - YES confidisplayConnectionStatus function · cpp · L329-L390 (62 LOC)esp32-terminal/src/display.cpp
void displayConnectionStatus(ConnectionState connState, const char* detail) {
const char* line1 = "";
const char* line2 = "";
const char* line3 = "";
switch (connState) {
case ConnectionState::BOOT:
line1 = "MURDERHOUSE";
line2 = "BOOTING";
line3 = "v" FIRMWARE_VERSION;
break;
case ConnectionState::PLAYER_SELECT:
// Handled separately by displayPlayerSelect()
return;
case ConnectionState::WIFI_CONNECTING:
line1 = "CONNECTING...";
line2 = "WIFI";
line3 = detail ? detail : "Searching for network...";
break;
case ConnectionState::DISCOVERING:
line1 = "CONNECTING...";
line2 = "SCANNING";
line3 = detail ? detail : "Looking for server...";
break;
case ConnectionState::WS_CONNECTING:
line1 = "CONNECTING...";
line2 = "SERVER";
line3heartrateInit function · cpp · L39-L52 (14 LOC)esp32-terminal/src/heartrate.cpp
void heartrateInit() {
// AD8232 shutdown control — start in shutdown (HIGH = off)
pinMode(PIN_AD8232_SDN, OUTPUT);
digitalWrite(PIN_AD8232_SDN, HIGH);
hrPowered = false;
hrEnabled = false;
// Red heartbeat LED
pinMode(PIN_LED_HEARTBEAT, OUTPUT);
digitalWrite(PIN_LED_HEARTBEAT, LOW);
Serial.println("[HR] AD8232 initialized, SDN HIGH (shutdown)");
}heartratePowerOn function · cpp · L53-L60 (8 LOC)esp32-terminal/src/heartrate.cpp
void heartratePowerOn() {
if (!hrPowered) {
digitalWrite(PIN_AD8232_SDN, LOW);
hrPowered = true;
Serial.println("[HR] AD8232 powered on (warm-up)");
}
}heartratePowerOff function · cpp · L61-L71 (11 LOC)esp32-terminal/src/heartrate.cpp
void heartratePowerOff() {
if (hrPowered) {
digitalWrite(PIN_AD8232_SDN, HIGH);
digitalWrite(PIN_LED_HEARTBEAT, LOW);
beatLedOn = false;
hrPowered = false;
hrEnabled = false;
Serial.println("[HR] AD8232 powered off");
}
}Same scanner, your repo: https://repobility.com — Repobility
heartrateEnable function · cpp · L72-L79 (8 LOC)esp32-terminal/src/heartrate.cpp
void heartrateEnable() {
if (!hrEnabled) {
heartratePowerOn(); // Ensure powered on
hrEnabled = true;
Serial.println("[HR] Reporting enabled");
}
}heartrateDisable function · cpp · L80-L88 (9 LOC)esp32-terminal/src/heartrate.cpp
void heartrateDisable() {
if (hrEnabled) {
digitalWrite(PIN_LED_HEARTBEAT, LOW);
beatLedOn = false;
hrEnabled = false;
Serial.println("[HR] Reporting disabled");
}
}heartrateUpdate function · cpp · L89-L154 (66 LOC)esp32-terminal/src/heartrate.cpp
void heartrateUpdate() {
if (!hrPowered) return;
unsigned long now = millis();
// Turn off beat LED after flash duration
if (beatLedOn && (now - beatLedOnTime >= AD8232_BEAT_FLASH_MS)) {
digitalWrite(PIN_LED_HEARTBEAT, LOW);
beatLedOn = false;
}
// Sample at ~250 Hz
if (now - lastSampleTime < AD8232_SAMPLE_MS) return;
lastSampleTime = now;
// Read analog signal
int sample = analogRead(PIN_AD8232_OUT);
// Update rolling min/max window
if (now - windowStart > WINDOW_MS) {
// Decay toward current sample to avoid stale extremes
rollingMin = sample;
rollingMax = sample;
windowStart = now;
} else {
if (sample < rollingMin) rollingMin = sample;
if (sample > rollingMax) rollingMax = sample;
}
int range = rollingMax - rollingMin;
if (range < MIN_RANGE) return; // Signal too weak or just noise, skip detection
// Dynamic threshold
int threshold = rollinheartrateGetBPM function · cpp · L155-L170 (16 LOC)esp32-terminal/src/heartrate.cpp
uint8_t heartrateGetBPM() {
if (beatIntervalCount == 0) return 0;
unsigned long sum = 0;
int count = min(beatIntervalCount, BPM_BUFFER_SIZE);
for (int i = 0; i < count; i++) {
sum += beatIntervals[i];
}
unsigned long avgInterval = sum / count;
if (avgInterval == 0) return 0;
unsigned long bpm = 60000 / avgInterval;
if (bpm > 220) bpm = 220;
return (uint8_t)bpm;
}heartrateIsActive function · cpp · L171-L174 (4 LOC)esp32-terminal/src/heartrate.cpp
bool heartrateIsActive() {
return (lastBeatTime > 0) && (millis() - lastBeatTime < ACTIVE_TIMEOUT_MS);
}getIconBitmap function · c · L499-L523 (25 LOC)esp32-terminal/src/icons.h
inline const uint8_t* getIconBitmap(const String& id) {
if (id == "nobody") return ICON_NOBODY;
if (id == "alpha") return ICON_ALPHA;
if (id == "sleeper") return ICON_SLEEPER;
if (id == "seeker") return ICON_SEEKER;
if (id == "medic") return ICON_MEDIC;
if (id == "hunter") return ICON_HUNTER;
if (id == "vigilante") return ICON_VIGILANTE;
if (id == "judge") return ICON_JUDGE;
if (id == "cupid") return ICON_CUPID;
if (id == "handler") return ICON_HANDLER;
if (id == "fixer") return ICON_FIXER;
if (id == "marked") return ICON_MARKED;
if (id == "prospect") return ICON_PROSPECT;
if (id == "chemist") return ICON_CHEMIST;
if (id == "pistol") return ICON_PISTOL;
if (id == "gavel") return ICON_GAVEL;
if (id == "clue") return ICON_CRYSTAL_BALL;
if (id == "coward") return ICON_COWARD;
if (id == "hardened") return ICON_HARDENED;
if (id == "skull") return ICON_SKULL;
if (id == "empty") return ICON_EMPTY;
if (id =inputInit function · cpp · L27-L43 (17 LOC)esp32-terminal/src/input.cpp
void inputInit() {
// Configure button pins with internal pullup
pinMode(PIN_BTN_YES, INPUT_PULLUP);
pinMode(PIN_BTN_NO, INPUT_PULLUP);
pinMode(PIN_ENCODER_SW, INPUT_PULLUP);
// Initialize rotary encoder
ESP32Encoder::useInternalWeakPullResistors = puType::up;
encoder.attachFullQuad(PIN_ENCODER_A, PIN_ENCODER_B);
encoder.clearCount();
lastEncoderCount = 0;
// Initialize button states
lastYesState = digitalRead(PIN_BTN_YES);
lastNoState = digitalRead(PIN_BTN_NO);
}inputPoll function · cpp · L44-L123 (80 LOC)esp32-terminal/src/input.cpp
InputEvent inputPoll() {
unsigned long now = millis();
// === Check YES button ===
bool yesState = digitalRead(PIN_BTN_YES);
if (yesState != lastYesState) {
if (now - lastYesChange > DEBOUNCE_MS) {
lastYesChange = now;
lastYesState = yesState;
if (yesState == LOW) {
// Button pressed — start long press timer
yesPressing = true;
yesPressStart = now;
yesLongFired = false;
} else {
// Button released — fire normal press if long press didn't already fire
if (yesPressing && !yesLongFired) {
yesPressing = false;
return InputEvent::YES;
}
yesPressing = false;
}
}
}
// Check YES long press threshold while button is held
if (yesPressing && !yesLongFired && (now - yesPressStart >= LONG_PRESS_MS)) {
yesLongFired = truRepobility analyzer · published findings · https://repobility.com
inputGetRotaryPosition function · cpp · L124-L129 (6 LOC)esp32-terminal/src/input.cpp
uint8_t inputGetRotaryPosition() {
int32_t count = encoder.getCount() / ENCODER_PULSES_PER_DETENT;
int pos = ((count % 8) + 8) % 8 + 1;
return (uint8_t)pos;
}getPulseBrightness function · cpp · L22-L25 (4 LOC)esp32-terminal/src/leds.cpp
static float getPulseBrightness() {
// Sine wave from 0.2 to 1.0
return 0.2f + 0.8f * (0.5f + 0.5f * sin(pulsePhase));
}applyLedState function · cpp · L28-L49 (22 LOC)esp32-terminal/src/leds.cpp
static void applyLedState(uint8_t channel, LedState state) {
uint8_t bright = (channel == PWM_CHANNEL_NO) ? LED_BRIGHT_NO : LED_BRIGHT;
uint8_t brightness = LED_OFF;
switch (state) {
case LedState::OFF:
brightness = LED_OFF;
break;
case LedState::DIM:
brightness = LED_DIM;
break;
case LedState::BRIGHT:
brightness = bright;
break;
case LedState::PULSE:
// Treat pulse same as bright for button LEDs
brightness = bright;
break;
}
ledcWrite(channel, brightness);
}ledsInit function · cpp · L50-L71 (22 LOC)esp32-terminal/src/leds.cpp
void ledsInit() {
// Initialize PWM for button LEDs
ledcSetup(PWM_CHANNEL_YES, PWM_FREQ, PWM_RESOLUTION);
ledcSetup(PWM_CHANNEL_NO, PWM_FREQ, PWM_RESOLUTION);
ledcSetup(PWM_CHANNEL_POWER, PWM_FREQ, PWM_RESOLUTION);
ledcAttachPin(PIN_LED_YES, PWM_CHANNEL_YES);
ledcAttachPin(PIN_LED_NO, PWM_CHANNEL_NO);
ledcAttachPin(PIN_LED_POWER, PWM_CHANNEL_POWER);
// Start with LEDs off (power LED on at low brightness)
ledcWrite(PWM_CHANNEL_YES, 0);
ledcWrite(PWM_CHANNEL_NO, 0);
ledcWrite(PWM_CHANNEL_POWER, LED_POWER_BRIGHT);
// Initialize Neopixel
neopixel.begin();
neopixel.setBrightness(25); // 25/255 brightness
neopixel.clear();
neopixel.show();
}ledsUpdate function · cpp · L72-L97 (26 LOC)esp32-terminal/src/leds.cpp
void ledsUpdate() {
unsigned long now = millis();
// Update pulse animation (~60 fps)
if (now - lastPulseUpdate > 16) {
lastPulseUpdate = now;
// Advance pulse phase (complete cycle in LED_PULSE_MS)
pulsePhase += (2.0f * PI * 16.0f) / LED_PULSE_MS;
if (pulsePhase > 2.0f * PI) {
pulsePhase -= 2.0f * PI;
}
// Update neopixel if pulsing
if (statusPulse) {
float brightness = getPulseBrightness();
neopixel.setPixelColor(0, neopixel.Color(
(uint8_t)(statusR * brightness),
(uint8_t)(statusG * brightness),
(uint8_t)(statusB * brightness)
));
neopixel.show();
}
}
}ledsSetYes function · cpp · L98-L102 (5 LOC)esp32-terminal/src/leds.cpp
void ledsSetYes(LedState state) {
yesLedState = state;
applyLedState(PWM_CHANNEL_YES, state);
}ledsSetNo function · cpp · L103-L107 (5 LOC)esp32-terminal/src/leds.cpp
void ledsSetNo(LedState state) {
noLedState = state;
applyLedState(PWM_CHANNEL_NO, state);
}ledsSetFromDisplay function · cpp · L108-L112 (5 LOC)esp32-terminal/src/leds.cpp
void ledsSetFromDisplay(const DisplayState& state) {
ledsSetYes(state.leds.yes);
ledsSetNo(state.leds.no);
}Open data scored by Repobility · https://repobility.com
ledsSetStatusColor function · cpp · L113-L122 (10 LOC)esp32-terminal/src/leds.cpp
void ledsSetStatusColor(uint8_t r, uint8_t g, uint8_t b) {
statusR = r;
statusG = g;
statusB = b;
statusPulse = false;
neopixel.setPixelColor(0, neopixel.Color(r, g, b));
neopixel.show();
}ledsSetStatus function · cpp · L123-L179 (57 LOC)esp32-terminal/src/leds.cpp
void ledsSetStatus(ConnectionState state) {
switch (state) {
case ConnectionState::BOOT:
// White - initializing
statusR = 100; statusG = 100; statusB = 100;
statusPulse = false;
break;
case ConnectionState::PLAYER_SELECT:
// Purple - selecting player
statusR = 150; statusG = 0; statusB = 255;
statusPulse = true;
break;
case ConnectionState::WIFI_CONNECTING:
// Blue - connecting to WiFi
statusR = 0; statusG = 0; statusB = 255;
statusPulse = true;
break;
case ConnectionState::WS_CONNECTING:
// Yellow - connecting to WebSocket
statusR = 255; statusG = 200; statusB = 0;
statusPulse = true;
break;
case ConnectionState::JOINING:
// Cyan - joining game
statusR = 0; statusG = 255; statusB = 255;
statusPulse = false;
ledsSetGameState function · cpp · L180-L232 (53 LOC)esp32-terminal/src/leds.cpp
void ledsSetGameState(GameLedState state) {
switch (state) {
case GameLedState::NONE:
// No game state - don't change neopixel
return;
case GameLedState::LOBBY:
statusR = 100; statusG = 100; statusB = 100;
statusPulse = false;
break;
case GameLedState::DAY:
statusR = 0; statusG = 255; statusB = 0;
statusPulse = false;
break;
case GameLedState::NIGHT:
statusR = 0; statusG = 0; statusB = 255;
statusPulse = false;
break;
case GameLedState::VOTING:
statusR = 255; statusG = 200; statusB = 0;
statusPulse = true;
break;
case GameLedState::LOCKED:
statusR = 0; statusG = 255; statusB = 0;
statusPulse = false;
break;
case GameLedState::ABSTAINED:
statusR = 60; statusG = 60; statusB = 60;
statusPulse = fals