vividness pass: agent avatars + online pulse + getting-started empty state (0.27.0)
- generated gradient avatars w/ tag-based category emoji on agent cards - pulsing online-status indicator - actionable 3-step 'getting started' empty state on Browse (publish -> run -> use)
This commit is contained in:
parent
099fd7616f
commit
c6127747ed
1
.claude/scheduled_tasks.lock
Normal file
1
.claude/scheduled_tasks.lock
Normal file
@ -0,0 +1 @@
|
||||
{"sessionId":"17e654e8-7dd5-4856-85c5-0cfeb7ab736b","pid":3488121,"procStart":"252909119","acquiredAt":1781038486936}
|
||||
@ -7,7 +7,7 @@
|
||||
name="description"
|
||||
content="amerc is an agent mercenary layer for embedding, bringing, and hosting agents across vertical software boundaries."
|
||||
/>
|
||||
<meta name="version" content="0.26.0-mansion" />
|
||||
<meta name="version" content="0.27.0-vivid" />
|
||||
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "amerc-site",
|
||||
"version": "0.26.0-mansion",
|
||||
"version": "0.27.0-vivid",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
@ -2,6 +2,9 @@ import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { api } from './api.js';
|
||||
|
||||
const STATUS_COLOR = { online: '#36f0b0', pending: '#f2b85f', lost: '#ff9f5a', down: '#ff7a8c' };
|
||||
const TAG_EMOJI = { backend: '🛠️', model: '🧠', frontend: '🎨', ui: '🖌️', web: '🌐', data: '📊', devops: '⚙️', ml: '🤖', security: '🔐', api: '🔌', bot: '🤖', chat: '💬', test: '🧪', docs: '📄', design: '✏️', ops: '🚦', research: '🔬' };
|
||||
const classEmoji = (c) => { for (const t of (c.tags || [])) if (TAG_EMOJI[t]) return TAG_EMOJI[t]; return '🗡️'; };
|
||||
function avatarStyle(name) { let h = 0; for (const ch of String(name)) h = (h * 31 + ch.charCodeAt(0)) >>> 0; const a = h % 360, b = (a + 70 + h % 90) % 360; return { background: `linear-gradient(135deg, hsl(${a} 68% 46%), hsl(${b} 64% 32%))` }; }
|
||||
|
||||
// ---- Browse Agents: all public classes, filter by tag --------------------
|
||||
export function AgentBrowse({ auth, setRoute }) {
|
||||
@ -38,16 +41,29 @@ export function AgentBrowse({ auth, setRoute }) {
|
||||
<div className="ap-grid">
|
||||
{classes.map((c) => (
|
||||
<button key={c.id} className="ap-card" onClick={() => setOpen(c.id)}>
|
||||
<div className="ap-card-top"><strong>{c.name}</strong><span className="ap-kind">{c.kind}</span></div>
|
||||
<div className="ap-card-head2">
|
||||
<span className="ap-avatar" style={avatarStyle(c.name)}>{classEmoji(c)}</span>
|
||||
<div className="ap-card-titles"><strong>{c.name}</strong><span className="ap-kind">{c.kind}</span></div>
|
||||
</div>
|
||||
<p className="ap-desc">{c.description || 'No description.'}</p>
|
||||
<div className="ap-card-tags">{c.tags.map((t) => <span key={t}>{t}</span>)}</div>
|
||||
<div className="ap-card-foot">
|
||||
<span>by {c.owner}</span>
|
||||
<span className="ap-online"><i style={{ background: c.onlineCount ? STATUS_COLOR.online : '#555' }} />{c.onlineCount}/{c.instanceCount} online</span>
|
||||
<span className="ap-online"><i className={c.onlineCount ? 'pulse' : ''} style={{ background: c.onlineCount ? STATUS_COLOR.online : '#555' }} />{c.onlineCount}/{c.instanceCount} online</span>
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
{!classes.length && <p className="px-board-rows" style={{ opacity: 0.7 }}>No agent classes yet. Open <b>My Booth</b> to publish one.</p>}
|
||||
{!classes.length && (
|
||||
<div className="ap-empty-start">
|
||||
<h3>No agents here yet — be the first.</h3>
|
||||
<ol>
|
||||
<li><b>Publish a class</b> in <button className="ap-link" onClick={() => setRoute('booth')}>My Booth</button> — name it, tag it (e.g. <code>backend</code>, <code>model</code>).</li>
|
||||
<li><b>Run an instance</b> — your broker connects and it goes <span style={{ color: STATUS_COLOR.online }}>online</span>.</li>
|
||||
<li><b>Use it</b> — embed it as a chatbox or drive it through a web terminal.</li>
|
||||
</ol>
|
||||
<button className="px-action" onClick={() => setRoute('booth')}>Open My Booth →</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{open && <ClassModal id={open} auth={auth} onClose={() => { setOpen(null); load(); }} />}
|
||||
</div>
|
||||
|
||||
@ -6,7 +6,7 @@ import Mansion from './Mansion.jsx';
|
||||
import TavernGame from './TavernGame.jsx';
|
||||
import { api } from './api.js';
|
||||
|
||||
const RELEASE = '0.26.0-mansion';
|
||||
const RELEASE = '0.27.0-vivid';
|
||||
|
||||
const NAV = [
|
||||
{ id: 'home', label: 'Tavern' },
|
||||
|
||||
@ -1073,3 +1073,17 @@ button {
|
||||
.mn-created p { margin: 0 0 8px; font-size: 13px; color: #d8c4a0; } .mn-created code { background: #07060c; padding: 1px 5px; border-radius: 4px; color: #7fe9ff; }
|
||||
.mn-row { display: flex; align-items: center; gap: 10px; padding: 9px 0; border-bottom: 1px solid #241a36; font-size: 13px; }
|
||||
.mn-row a { color: #27d8ff; } .mn-row span { color: #8a7aa8; margin-left: auto; }
|
||||
|
||||
/* agent card avatars + online pulse + onboarding (0.27 vividness) */
|
||||
.ap-card-head2 { display: flex; align-items: center; gap: 11px; }
|
||||
.ap-avatar { width: 40px; height: 40px; border-radius: 11px; display: flex; align-items: center; justify-content: center; font-size: 20px; flex: none; box-shadow: 0 3px 10px rgba(0,0,0,0.45), inset 0 0 0 1px rgba(255,255,255,0.12); }
|
||||
.ap-card-titles { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
|
||||
.ap-card-titles strong { font-size: 15px; color: #fff; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
.ap-online i.pulse { box-shadow: 0 0 0 0 rgba(54,240,176,0.7); animation: ap-pulse 1.8s infinite; }
|
||||
@keyframes ap-pulse { 0% { box-shadow: 0 0 0 0 rgba(54,240,176,0.6); } 70% { box-shadow: 0 0 0 6px rgba(54,240,176,0); } 100% { box-shadow: 0 0 0 0 rgba(54,240,176,0); } }
|
||||
.ap-empty-start { grid-column: 1 / -1; max-width: 560px; margin: 10px auto; padding: 26px 28px; text-align: left; background: linear-gradient(160deg, #16102a, #0e0a18); border: 1px solid #2a1f3e; border-radius: 16px; box-shadow: 0 14px 40px rgba(0,0,0,0.5); }
|
||||
.ap-empty-start h3 { margin: 0 0 14px; color: #fff; font-size: 18px; font-family: Georgia, serif; }
|
||||
.ap-empty-start ol { margin: 0 0 18px; padding-left: 20px; display: flex; flex-direction: column; gap: 10px; }
|
||||
.ap-empty-start li { color: #c9b8e6; font-size: 13px; line-height: 1.6; }
|
||||
.ap-empty-start code { background: #07060c; color: #7fe9ff; padding: 1px 6px; border-radius: 4px; }
|
||||
.ap-link { background: none; color: #27d8ff; cursor: pointer; text-decoration: underline; font: inherit; padding: 0; }
|
||||
|
||||
Loading…
Reference in New Issue
Block a user