Help us improve
Share bugs, ideas, or general feedback.
Connects a Meta Display Glasses webapp to REST APIs or WebSockets with loading/error states and caching. Use when fetching data, adding real-time updates, or handling offline fallback.
npx claudepluginhub facebookincubator/meta-wearables-webapp --plugin meta-wearables-webappHow this skill is triggered — by the user, by Claude, or both
Slash command
/meta-wearables-webapp:connect-api [api-name-or-url][api-name-or-url]The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Before generating or modifying any code, read both:
Creates complete webapps for Meta Display Glasses with D-pad navigation, EMG input, and 600x600 dark-theme display. Useful when starting a new smart glasses project or scaffolding a glasses app.
Sets up display capability for Meta Ray-Ban Display glasses: device selection, UI DSL, icons, buttons, images, and video playback. Use with getting-started and permissions-registration for a full app.
Builds UI for Even Hub G2 glasses displays using text containers, lists, images, page lifecycle, and layout patterns on 576x288 greyscale canvas. For creating or updating glasses app content.
Share bugs, ideas, or general feedback.
Before generating or modifying any code, read both:
${CLAUDE_PLUGIN_ROOT}/references/display-guidelines.md${CLAUDE_PLUGIN_ROOT}/references/performance-guidelines.mdThese define the non-negotiable display physics, input model, and performance budgets for Meta Display Glasses webapps. Do not skip — generated UI that ignores these will fail on-device.
Add real API connections to an existing webapp. The base app.js template already includes apiGet(), setLoading(), setError(), showToast(), and connectWebSocket() helpers — this skill shows how to wire them up.
/create-webapp (which includes the API/UI helpers in app.js)Ask the user:
If not specified, recommend from these free, no-key-required APIs:
| Category | API | Base URL |
|---|---|---|
| Weather | Open-Meteo | api.open-meteo.com/v1/forecast |
| Trivia | Open Trivia DB | opentdb.com/api.php |
| Crypto | CoinGecko | api.coingecko.com/api/v3 |
| Earthquakes | USGS | earthquake.usgs.gov/earthquakes/feed/v1.0 |
| Wikipedia | Wikipedia REST | en.wikipedia.org/api/rest_v1 |
| Recipes | TheMealDB | themealdb.com/api/json/v1/1 |
| Countries | REST Countries | restcountries.com/v3.1 |
| Jokes | Official Joke API | official-joke-api.appspot.com |
| Pokemon | PokeAPI | pokeapi.co/api/v2 |
| IP Location | ipapi | ipapi.co/json/ |
Add these elements inside the relevant screen in index.html (the CSS is already in styles.css from /create-webapp):
<div id="loading" class="loading-container hidden">
<div class="loading-spinner"></div>
<div class="loading-text">Loading...</div>
</div>
<div id="error" class="error-container hidden">
<div class="error-icon">⚠</div>
<div class="error-message">Something went wrong</div>
<button class="nav-item focusable" data-action="refresh">Try Again</button>
</div>
Add a refresh button to the nav bar:
<button class="nav-item focusable" data-action="refresh">↻ Refresh</button>
In app.js, update the CONFIG object:
api: {
baseUrl: 'https://api.example.com',
cacheDuration: 5 * 60 * 1000,
},
Use the existing apiGet() helper in onScreenEnter():
function onScreenEnter(screenId) {
switch (screenId) {
case 'home':
loadData();
break;
}
}
function loadData() {
apiGet(CONFIG.api.baseUrl + '/endpoint', { cacheKey: 'my-data' })
.then(function(data) {
renderData(data);
})
.catch(function() {
// Error already shown by apiGet
});
}
In handleAppAction():
case 'refresh':
state.cache = {};
onScreenEnter(state.currentScreen);
break;
Use the existing connectWebSocket() helper:
var ws = connectWebSocket('wss://stream.example.com/data', {
onOpen: function() { showToast('Connected', 'success'); },
onMessage: function(data) { updateDisplay(data); },
onClose: function() { showToast('Reconnecting...', 'warning'); },
onError: function() { showToast('Connection error', 'error'); },
});
For periodic updates:
var refreshInterval = null;
function startAutoRefresh(intervalMs) {
stopAutoRefresh();
refreshInterval = setInterval(function() {
onScreenEnter(state.currentScreen);
}, intervalMs || 60000);
}
function stopAutoRefresh() {
if (refreshInterval) {
clearInterval(refreshInterval);
refreshInterval = null;
}
}
Start in init(): startAutoRefresh(60000);
function loadWeather() {
var url = 'https://api.open-meteo.com/v1/forecast' +
'?latitude=40.71&longitude=-74.01' +
'¤t_weather=true' +
'&daily=temperature_2m_max,temperature_2m_min,weathercode' +
'&timezone=auto&forecast_days=5';
apiGet(url, { cacheKey: 'weather' })
.then(function(data) {
var w = data.current_weather;
document.getElementById('temp').textContent = Math.round(w.temperature) + '\u00B0';
document.getElementById('condition').textContent = getWeatherLabel(w.weathercode);
document.getElementById('wind').textContent = w.windspeed + ' km/h';
renderForecast(data.daily);
});
}
function loadPrices() {
var url = 'https://api.coingecko.com/api/v3/simple/price' +
'?ids=bitcoin,ethereum,solana' +
'&vs_currencies=usd&include_24hr_change=true';
apiGet(url, { cacheKey: 'crypto', cacheDuration: 30000 })
.then(function(data) {
Object.keys(data).forEach(function(coin) {
var price = data[coin].usd;
var change = data[coin].usd_24h_change;
document.getElementById(coin + '-price').textContent = '$' + price.toLocaleString();
var changeEl = document.getElementById(coin + '-change');
changeEl.textContent = (change >= 0 ? '+' : '') + change.toFixed(2) + '%';
changeEl.className = 'badge ' + (change >= 0 ? 'badge-success' : 'badge-danger');
});
});
}
.focusable class for D-pad navigation/add-ui — Add screens, buttons, and components for API data