/**
* Client Lab v3.1 — full field kit + context + decorations.
*/
(async function () {
var decorateOn = true;
var ownMark = '★';
var otherMark = '·';
var runs = 0;
var decorateCount = 0;
var note = '';
var intensity = 40;
var accent = '#5865f2';
var ctxLabel = '—';
var journal = '';
try {
runs = parseInt((await ggbro.storage.get('runs')).value || '0', 10) || 0;
runs += 1;
await ggbro.storage.set('runs', String(runs));
decorateOn = ((await ggbro.storage.get('decorate')).value || '1') !== '0';
decorateCount = parseInt((await ggbro.storage.get('decorated')).value || '0', 10) || 0;
note = (await ggbro.storage.get('note')).value || '';
intensity = parseInt((await ggbro.storage.get('intensity')).value || '40', 10) || 40;
accent = (await ggbro.storage.get('accent')).value || '#5865f2';
journal = (await ggbro.storage.get('journal')).value || '';
} catch (e) {
ggbro.log('storage', e && e.message);
}
async function refreshContext() {
try {
var ctx = await ggbro.getContext();
ctxLabel = ctx.view
+ (ctx.channelId ? ' · ch ' + String(ctx.channelId).slice(0, 8) : '')
+ (ctx.serverId ? ' · srv ' + String(ctx.serverId).slice(0, 8) : '')
+ (ctx.dmUserId ? ' · dm ' + String(ctx.dmUserId).slice(0, 8) : '')
+ (ctx.groupId ? ' · grp ' + String(ctx.groupId).slice(0, 8) : '');
} catch (e) {
ctxLabel = 'context unavailable';
}
}
async function paint() {
await refreshContext();
await ggbro.ui.registerPanel({
id: 'main',
title: 'Client Lab',
schema: [
{
type: 'notice',
id: 'tip',
text: 'Kitchen sink — re-call registerPanel to refresh progress, labels, and values live.',
tone: 'info',
},
{ type: 'section', id: 's0', title: 'Stats' },
{ type: 'text', id: 'runs', label: 'Loads on this PC', value: String(runs) },
{ type: 'text', id: 'decoN', label: 'Decorate batches', value: String(decorateCount) },
{ type: 'text', id: 'ctx', label: 'Navigation context', value: ctxLabel },
{ type: 'progress', id: 'prog', label: 'Intensity meter', value: intensity, max: 100 },
{ type: 'section', id: 's1', title: 'Controls' },
{ type: 'toggle', id: 'decorate', label: 'Decorate visible messages', value: decorateOn },
{ type: 'input', id: 'note', label: 'Sticky note', value: note, placeholder: 'Type and Apply' },
{
type: 'textarea',
id: 'journal',
label: 'Lab journal',
value: journal,
placeholder: 'Longer notes…',
rows: 3,
},
{ type: 'slider', id: 'intensity', label: 'Intensity', value: intensity, min: 0, max: 100, step: 1 },
{ type: 'color', id: 'accent', label: 'Accent preview', value: accent },
{ type: 'button', id: 'ping', label: 'Toast ping' },
{ type: 'button', id: 'ding', label: 'Play boing' },
{ type: 'button', id: 'ctxBtn', label: 'Refresh context' },
{ type: 'button', id: 'clear', label: 'Clear decorations' },
{ type: 'link', id: 'docs', label: 'Plugin docs', href: 'https://ggbro.app/developers/plugins' },
],
});
}
await paint();
await ggbro.toast({ message: 'Client Lab ready (run #' + runs + ')', type: 'info' });
ggbro.playSound({ id: 'notification' }).catch(function () {});
ggbro.onEvent(function (event, payload) {
if (event === 'context') {
paint();
return;
}
if (event !== 'panelAction' || !payload) return;
if (payload.fieldId === 'ping') {
ggbro.toast({
message: note ? ('Note: ' + note) : 'Ping from Client Lab panel',
type: 'success',
});
return;
}
if (payload.fieldId === 'ding') {
ggbro.playSound({ id: 'boing' }).catch(function () {});
return;
}
if (payload.fieldId === 'ctxBtn') {
paint();
ggbro.toast({ message: 'Context refreshed', type: 'info' });
return;
}
if (payload.fieldId === 'note') {
note = String(payload.value || '').slice(0, 200);
ggbro.storage.set('note', note).catch(function () {});
paint();
ggbro.toast({ message: 'Note saved', type: 'success' });
return;
}
if (payload.fieldId === 'journal') {
journal = String(payload.value || '').slice(0, 1000);
ggbro.storage.set('journal', journal).catch(function () {});
paint();
ggbro.toast({ message: 'Journal saved', type: 'success' });
return;
}
if (payload.fieldId === 'intensity') {
intensity = Math.max(0, Math.min(100, Number(payload.value) || 0));
ggbro.storage.set('intensity', String(intensity)).catch(function () {});
paint();
return;
}
if (payload.fieldId === 'accent') {
accent = String(payload.value || '#5865f2');
ggbro.storage.set('accent', accent).catch(function () {});
paint();
return;
}
if (payload.fieldId === 'decorate') {
decorateOn = !!payload.value;
ggbro.storage.set('decorate', decorateOn ? '1' : '0').catch(function () {});
ggbro.toast({
message: decorateOn ? 'Decorations on' : 'Decorations off',
type: 'info',
});
return;
}
if (payload.fieldId === 'clear') {
decorateOn = false;
ggbro.storage.set('decorate', '0').catch(function () {});
paint();
ggbro.toast({
message: 'Decorate off — reload chat or wait for new messages to drop marks.',
type: 'warning',
});
}
});
ggbro.onMessages(function (messages) {
if (!decorateOn || !messages || !messages.length) return;
var patches = messages.slice(-12).map(function (m) {
return {
id: m.id,
prefixText: m.isOwn ? ownMark : otherMark,
};
});
ggbro.decorateMessages(patches).then(function () {
decorateCount += 1;
ggbro.storage.set('decorated', String(decorateCount)).catch(function () {});
}).catch(function () {});
});
})();