@khanglvm/relay 0.10.2 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -0
- package/docs/AGENT.md +66 -1
- package/package.json +6 -2
- package/skills/relay/SKILL.md +14 -0
- package/src/cli.js +97 -2
- package/src/mcp-ui/board.js +854 -0
- package/src/mcp-ui/index.html +36 -0
- package/src/mcp.js +451 -0
- package/src/spec.js +65 -3
- package/src/ui/app.js +33 -0
- package/src/ui/blocks.css +25 -0
- package/src/ui/blocks.js +63 -0
- package/src/ui/style.css +11 -0
package/src/ui/blocks.css
CHANGED
|
@@ -545,3 +545,28 @@ body.blk-full-open { overflow: hidden; }
|
|
|
545
545
|
/* code blocks reuse the viewer toolbar (comment + full-screen); the pre keeps
|
|
546
546
|
its own card, the wrapper only hosts the toolbar strip */
|
|
547
547
|
.blk-codewrap { position: relative; }
|
|
548
|
+
|
|
549
|
+
/* ---------- palette block (swatch cards; hover reveals hex, click copies) ---------- */
|
|
550
|
+
.blk-palette .pal-title { font-size: 11px; font-weight: 600; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); margin: 2px 0 10px; }
|
|
551
|
+
.blk-palette .pal-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 14px; }
|
|
552
|
+
.pal-card { border: 1px solid var(--border); border-radius: 12px; overflow: hidden; background: var(--card); }
|
|
553
|
+
.pal-card.pal-spotlight { margin-bottom: 16px; }
|
|
554
|
+
.pal-swatches { display: flex; height: 72px; }
|
|
555
|
+
.pal-spotlight .pal-swatches { height: 104px; }
|
|
556
|
+
.pal-sw { flex: 1; position: relative; cursor: pointer; border: 0; padding: 0; transition: flex .2s var(--ease); }
|
|
557
|
+
.pal-sw:hover { flex: 1.6; }
|
|
558
|
+
.pal-hex { position: absolute; bottom: 7px; left: 50%; transform: translateX(-50%); font: 600 10px/1 var(--mono, ui-monospace, "SF Mono", monospace); background: rgba(0,0,0,.42); color: #fff; padding: 2px 6px; border-radius: 4px; white-space: nowrap; opacity: 0; transition: opacity .15s; pointer-events: none; }
|
|
559
|
+
.pal-sw:hover .pal-hex, .pal-sw.copied .pal-hex { opacity: 1; }
|
|
560
|
+
.pal-sw.copied::after { content: "Copied"; position: absolute; top: 6px; left: 50%; transform: translateX(-50%); font: 600 9px/1 var(--sans); background: var(--ok); color: #fff; padding: 2px 7px; border-radius: 4px; }
|
|
561
|
+
.pal-info { padding: 10px 14px; display: flex; justify-content: space-between; align-items: center; gap: 10px; }
|
|
562
|
+
.pal-spotlight .pal-info { padding: 12px 16px; }
|
|
563
|
+
.pal-name { font-size: 14px; font-weight: 600; color: var(--fg); }
|
|
564
|
+
.pal-spotlight .pal-name { font-size: 15px; }
|
|
565
|
+
.pal-sub { font-size: 12px; color: var(--muted); margin-top: 2px; }
|
|
566
|
+
.pal-tag { flex: none; font-size: 11px; font-weight: 600; padding: 3px 11px; border-radius: 20px; background: var(--bg-sunken); color: var(--fg-2); }
|
|
567
|
+
.pal-tag.tone-warm { background: #FAECE7; color: #993C1D; }
|
|
568
|
+
.pal-tag.tone-cool { background: #E6F1FB; color: #185FA5; }
|
|
569
|
+
.pal-tag.tone-neutral { background: #F1EFE8; color: #5F5E5A; }
|
|
570
|
+
.pal-tag.tone-nature { background: #EAF3DE; color: #3B6D11; }
|
|
571
|
+
.pal-tag.tone-bold { background: #FBEAF0; color: #72243E; }
|
|
572
|
+
.pal-tag.tone-digital { background: #EEEDFE; color: #3C3489; }
|
package/src/ui/blocks.js
CHANGED
|
@@ -1426,6 +1426,65 @@
|
|
|
1426
1426
|
return container;
|
|
1427
1427
|
}
|
|
1428
1428
|
|
|
1429
|
+
// ---------- palette ----------
|
|
1430
|
+
// Color palettes as swatch cards: hover a swatch to reveal its hex, click to
|
|
1431
|
+
// copy it. One palette may be {featured:true} → a larger spotlight row. Lets
|
|
1432
|
+
// an agent present a curated palette with one structured block instead of
|
|
1433
|
+
// hand-writing HTML. Swatches and the whole block are commentable.
|
|
1434
|
+
function copyColor(text, btn) {
|
|
1435
|
+
const done = () => {
|
|
1436
|
+
btn.classList.add('copied');
|
|
1437
|
+
setTimeout(() => btn.classList.remove('copied'), 900);
|
|
1438
|
+
};
|
|
1439
|
+
try {
|
|
1440
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
1441
|
+
navigator.clipboard.writeText(text).then(done, () => {});
|
|
1442
|
+
return;
|
|
1443
|
+
}
|
|
1444
|
+
} catch { /* fall through */ }
|
|
1445
|
+
done(); // no clipboard (sandbox) — still flash feedback
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
function paletteCard(p, big, ctx, blockId) {
|
|
1449
|
+
const card = el('div', { class: 'pal-card' + (big ? ' pal-spotlight' : '') });
|
|
1450
|
+
const row = el('div', { class: 'pal-swatches' });
|
|
1451
|
+
for (const c of p.colors) {
|
|
1452
|
+
const sw = el('button', { class: 'pal-sw', type: 'button', style: 'background:' + c, title: 'Copy ' + c });
|
|
1453
|
+
sw.append(el('span', { class: 'pal-hex' }, c));
|
|
1454
|
+
sw.addEventListener('click', () => copyColor(c, sw));
|
|
1455
|
+
if (ctx.annotate) {
|
|
1456
|
+
ctx.annotate.register(sw, {
|
|
1457
|
+
blockId,
|
|
1458
|
+
questionId: ctx.questionId,
|
|
1459
|
+
target: { kind: 'swatch', label: (p.name ? p.name + ' · ' : '') + c },
|
|
1460
|
+
});
|
|
1461
|
+
}
|
|
1462
|
+
row.append(sw);
|
|
1463
|
+
}
|
|
1464
|
+
card.append(row);
|
|
1465
|
+
const info = el('div', { class: 'pal-info' });
|
|
1466
|
+
const meta = el('div', { class: 'pal-meta' });
|
|
1467
|
+
if (p.name) meta.append(el('div', { class: 'pal-name' }, p.name));
|
|
1468
|
+
if (p.sub) meta.append(el('div', { class: 'pal-sub' }, p.sub));
|
|
1469
|
+
info.append(meta);
|
|
1470
|
+
if (p.tag) info.append(el('span', { class: 'pal-tag' + (p.tagTone ? ' tone-' + p.tagTone : '') }, p.tag));
|
|
1471
|
+
if (p.name || p.sub || p.tag) card.append(info);
|
|
1472
|
+
return card;
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
function renderPalette(block, ctx, blockId) {
|
|
1476
|
+
const wrap = el('div', { class: 'blk-palette' });
|
|
1477
|
+
const palettes = Array.isArray(block.palettes) ? block.palettes : [];
|
|
1478
|
+
if (block.title) wrap.append(el('div', { class: 'pal-title' }, block.title));
|
|
1479
|
+
const grid = el('div', { class: 'pal-grid' });
|
|
1480
|
+
for (const p of palettes) {
|
|
1481
|
+
if (p.featured) wrap.append(paletteCard(p, true, ctx, blockId));
|
|
1482
|
+
else grid.append(paletteCard(p, false, ctx, blockId));
|
|
1483
|
+
}
|
|
1484
|
+
if (grid.childNodes.length) wrap.append(grid);
|
|
1485
|
+
return wrap;
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1429
1488
|
// ---------- viewer controls (zoom / fit / full-screen) ----------
|
|
1430
1489
|
// Large diagrams get squeezed to the column width; these controls let the
|
|
1431
1490
|
// user zoom (buttons or cmd/ctrl+wheel) and expand any visual block into a
|
|
@@ -1753,6 +1812,10 @@
|
|
|
1753
1812
|
inner = renderImage(block, ctx, blockId);
|
|
1754
1813
|
wrapper.append(inner);
|
|
1755
1814
|
break;
|
|
1815
|
+
case 'palette':
|
|
1816
|
+
inner = renderPalette(block, ctx, blockId);
|
|
1817
|
+
wrapper.append(inner);
|
|
1818
|
+
break;
|
|
1756
1819
|
default:
|
|
1757
1820
|
wrapper.append(el('div', { class: 'blk-error' }, 'Unknown block type: ' + esc(String(block.type))));
|
|
1758
1821
|
}
|
package/src/ui/style.css
CHANGED
|
@@ -311,3 +311,14 @@ textarea { min-height: 90px; resize: vertical; }
|
|
|
311
311
|
@media (prefers-reduced-motion: reduce) {
|
|
312
312
|
* { transition: none !important; animation: none !important; }
|
|
313
313
|
}
|
|
314
|
+
|
|
315
|
+
/* ---------- color picker question (native input + hex + presets) ---------- */
|
|
316
|
+
.colorpick { display: flex; flex-direction: column; gap: 10px; }
|
|
317
|
+
.colorrow { display: flex; align-items: center; gap: 10px; }
|
|
318
|
+
.colorswatch { width: 48px; height: 38px; padding: 0; border: 1px solid var(--border-strong); border-radius: 10px; background: var(--card); cursor: pointer; }
|
|
319
|
+
.colorswatch::-webkit-color-swatch-wrapper { padding: 3px; }
|
|
320
|
+
.colorswatch::-webkit-color-swatch { border: 0; border-radius: 7px; }
|
|
321
|
+
.colorhex { width: 150px; font-family: var(--mono, ui-monospace, "SF Mono", monospace); text-transform: lowercase; }
|
|
322
|
+
.colorpresets { display: flex; flex-wrap: wrap; gap: 8px; }
|
|
323
|
+
.colorpreset { width: 26px; height: 26px; border-radius: 7px; border: 1px solid rgba(0,0,0,.14); cursor: pointer; padding: 0; transition: transform .12s var(--ease); }
|
|
324
|
+
.colorpreset:hover { transform: scale(1.12); }
|