Skip to content

Commit 7b1f66d

Browse files
sametbasbugclaude
andcommitted
Open the agent detail inside its own row
The detail used to live in a separate container far below the list. You clicked an agent at the top of the card and the answer appeared hundreds of pixels down, past everything else — so which agent you were looking at was easy to lose. It also reprinted the handle and the state badge that the list row three lines above had just shown, which is how "Aktif" came to appear three times on one screen. - The list is one column now and the selected row expands in place. Two columns could not keep an expanded row aligned with its neighbour. - Messages and the following feed were each a full-width card holding a single link. They are two buttons in the detail now, and the privacy distinction they carried survives as one sentence: the feed is private, who the agent follows is not. - The media policy number input was 715px wide and unlabelled — the screen just said "10". It has a label and a sane width. The panel had no browser coverage at all, which is why the split and the repetition went unnoticed. Both mutations — reprinting the badge, moving the detail back out of the row — now fail the suite; I checked by making them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent ceaf171 commit 7b1f66d

3 files changed

Lines changed: 244 additions & 56 deletions

File tree

‎scripts/orbit-browser-tests.mjs‎

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,24 @@ const browserAgents = ['nyx', 'hemera', 'selene', 'asteria'].map((handle, index)
8989
accent: ['#7c6cf2', '#5267d9', '#d86f86', '#4c9c88'][index],
9090
founder: true,
9191
}));
92+
/* Paneldeki iki ajan. Biri yazmış ve incelemede bekleyeni var, diğeri hiç
93+
* yazmamış — panelin iki uç hâli de aynı testte görünüyor. */
94+
const browserSponsorAgents = [
95+
{
96+
id: 'agent-nyx', handle: 'nyx', bio: 'nyx Orbit ajanı', role: '',
97+
avatarAsset: '/avatars/nyx.webp', accent: '#7c6cf2',
98+
status: 'active', onboardingState: 'active',
99+
stats: { postCount: 5, replyCount: 2, latestActivityAt: Date.UTC(2026, 7, 1) },
100+
reviewCounts: { pending: 2, pendingReview: 1 },
101+
},
102+
{
103+
id: 'agent-metis', handle: 'metis', bio: 'metis Orbit ajanı', role: '',
104+
avatarAsset: '', accent: '#4c9c88',
105+
status: 'active', onboardingState: 'active',
106+
stats: { postCount: 0, replyCount: 0, latestActivityAt: null },
107+
reviewCounts: { pending: 0, pendingReview: 0 },
108+
},
109+
];
92110
const browserFeedRecords = [{
93111
id: 'dynamic-record-selene',
94112
kind: 'post',
@@ -186,6 +204,40 @@ if (errors.length === 0) {
186204
response.end(JSON.stringify({ records: browserFeedRecords }));
187205
return;
188206
}
207+
/* Sponsor paneli. Owner çerezinden ayrı bir çerez kullanıyor: owner
208+
* rolü platform araçlarını da yüklüyor ve onların uçları burada
209+
* mocklanmadığı için panel hata durumuna düşerdi. Ölçtüğümüz şey
210+
* ajan listesi, o yüzden sade hesap yeterli. */
211+
if (request.headers.cookie?.includes('orbit-sponsor-test=1')) {
212+
const sponsorJson = (body) => {
213+
response.writeHead(200, { 'cache-control': 'no-store', 'content-type': 'application/json; charset=utf-8' });
214+
response.end(JSON.stringify(body));
215+
};
216+
if (pathname === '/v1/me') {
217+
sponsorJson({
218+
account: {
219+
roles: [], agentQuota: 3, displayName: 'Orbit Sponsor',
220+
handle: 'orbit-sponsor', avatarUrl: null, announcementEmails: true,
221+
},
222+
session: { id: 'session-sponsor' },
223+
agentQuota: { limit: 3, used: 2, remaining: 1, canCreate: true },
224+
sponsoredAgents: browserSponsorAgents,
225+
});
226+
return;
227+
}
228+
if (pathname === '/v1/sessions') { sponsorJson({ sessions: [] }); return; }
229+
if (pathname === '/v1/mcp/authorizations') { sponsorJson({ authorizations: [] }); return; }
230+
if (pathname === '/v1/me/connected-sites') { sponsorJson({ connectedSites: [] }); return; }
231+
const manage = /^\/v1\/agents\/([^/]+)\/manage$/u.exec(pathname);
232+
if (manage) {
233+
const agent = browserSponsorAgents.find((item) => item.id === manage[1]);
234+
sponsorJson({
235+
agent: { ...agent, activeCredential: { id: 'cred-1', lastUsedAt: Date.UTC(2026, 7, 1) } },
236+
mediaPolicy: { mediaEnabled: true, dailyImageLimit: 10 },
237+
});
238+
return;
239+
}
240+
}
189241
if (pathname === '/v1/me') {
190242
const owner = request.headers.cookie?.includes('orbit-owner-test=1');
191243
response.writeHead(owner ? 200 : 401, {
@@ -757,6 +809,93 @@ if (errors.length === 0) {
757809
await context.close();
758810
}));
759811

812+
/* Sponsor paneli: ajan listesi ve seçilen ajanın detayı.
813+
*
814+
* Bu yüzey uzun süre hiç ölçülmedi. Detay bir dönem listenin çok
815+
* altında ayrı bir kapta duruyordu; hangi ajana bakıldığı kaybolur,
816+
* "Aktif" aynı ekranda üç kez yazardı. */
817+
for (const [label, viewport] of [
818+
['masaüstü', { width: 1440, height: 900 }],
819+
['dar ekran', { width: 390, height: 844 }],
820+
]) {
821+
const context = await browser.newContext({ viewport, colorScheme: 'light' });
822+
await context.addCookies([{ name: 'orbit-sponsor-test', value: '1', url: baseUrl }]);
823+
const page = await context.newPage();
824+
const pageErrors = [];
825+
page.on('pageerror', (error) => pageErrors.push(error.message));
826+
await page.goto(`${baseUrl}/dashboard`, { waitUntil: 'load' });
827+
await page.waitForSelector('.agent-row .agent-detail');
828+
829+
const panel = await page.evaluate(() => {
830+
const rows = [...document.querySelectorAll('.agent-row')];
831+
const selected = document.querySelector('.agent-row.selected');
832+
return {
833+
rows: rows.length,
834+
details: document.querySelectorAll('.agent-detail').length,
835+
detailInsideSelected: selected?.querySelector('.agent-detail') !== null,
836+
selectedHandle: selected?.querySelector('strong')?.textContent,
837+
expanded: selected?.querySelector('.agent-list-item')?.getAttribute('aria-expanded'),
838+
statesInSelected: selected?.querySelectorAll('.agent-state').length,
839+
activityLines: rows.map((row) => row.querySelector('small')?.textContent),
840+
waitingBadges: rows.map((row) => row.querySelector('.agent-waiting')?.textContent ?? null),
841+
summary: document.querySelector('#activity-summary')?.textContent,
842+
quota: document.querySelector('#quota-note')?.textContent,
843+
detailLinks: [...(selected?.querySelectorAll('.agent-detail a[href]') ?? [])].map((a) => a.getAttribute('href')),
844+
deadCards: document.querySelectorAll('#messages-card, #following-card, #agent-detail').length,
845+
};
846+
});
847+
848+
check(panel.rows === 2, `${label}: panel iki ajan satırı çizmedi (${panel.rows}).`);
849+
check(panel.details === 1, `${label}: aynı anda birden fazla ajan detayı açık (${panel.details}).`);
850+
check(panel.detailInsideSelected, `${label}: detay seçilen satırın içinde değil.`);
851+
check(panel.selectedHandle === '@nyx', `${label}: ilk ajan seçili gelmedi (${panel.selectedHandle}).`);
852+
check(panel.expanded === 'true', `${label}: seçili satır aria-expanded taşımıyor.`);
853+
/* Durum rozeti satır başına bir tane. Detay kendi rozetini bir daha
854+
* basarsa bu sayı ikiye çıkar — üçlü "Aktif" tekrarı böyle doğmuştu. */
855+
check(panel.statesInSelected === 1, `${label}: seçili satırda durum rozeti tekrarlanıyor (${panel.statesInSelected}).`);
856+
check(
857+
panel.activityLines[0]?.startsWith('5 gönderi · 2 yanıt'),
858+
`${label}: ajan satırı kayıt sayılarını yazmıyor (${panel.activityLines[0]}).`,
859+
);
860+
check(
861+
panel.activityLines[1] === 'Henüz kayıt yok',
862+
`${label}: hiç yazmamış ajan için boş hâl yazılmıyor (${panel.activityLines[1]}).`,
863+
);
864+
check(panel.waitingBadges[0] === '3 incelemede', `${label}: bekleyen inceleme sayısı yanlış (${panel.waitingBadges[0]}).`);
865+
check(panel.waitingBadges[1] === null, `${label}: bekleyeni olmayan ajana rozet basılmış.`);
866+
check(panel.summary?.includes('2 ajan'), `${label}: özet satırı ajan sayısını yazmıyor (${panel.summary}).`);
867+
check(panel.summary?.includes('7 kayıt'), `${label}: özet satırı toplam kaydı yanlış topluyor (${panel.summary}).`);
868+
check(panel.summary?.includes('3 kayıt incelemede'), `${label}: özet satırı bekleyeni yazmıyor (${panel.summary}).`);
869+
check(panel.quota?.includes('1/3'), `${label}: kalan ajan hakkı butonun yanında yazmıyor (${panel.quota}).`);
870+
check(
871+
panel.detailLinks.includes('/messages') && panel.detailLinks.includes('/following'),
872+
`${label}: mesaj ve takip bağlantıları detayda değil (${panel.detailLinks.join(', ')}).`,
873+
);
874+
check(panel.deadCards === 0, `${label}: kaldırılan mesaj/takip kartları ya da eski detay kabı hâlâ basılıyor.`);
875+
876+
/* İkinci ajana geçiş: detay taşınmalı, çoğalmamalı. */
877+
await page.locator('.agent-row').nth(1).locator('.agent-list-item').click();
878+
await page.waitForFunction(() => (
879+
document.querySelector('.agent-row.selected strong')?.textContent === '@metis'
880+
&& document.querySelector('.agent-row.selected .agent-detail') !== null
881+
));
882+
const afterSwitch = await page.evaluate(() => ({
883+
details: document.querySelectorAll('.agent-detail').length,
884+
selectedHandle: document.querySelector('.agent-row.selected strong')?.textContent,
885+
selectedRows: document.querySelectorAll('.agent-row.selected').length,
886+
}));
887+
check(afterSwitch.details === 1, `${label}: ajan değişince detay çoğaldı (${afterSwitch.details}).`);
888+
check(afterSwitch.selectedRows === 1, `${label}: aynı anda iki satır seçili (${afterSwitch.selectedRows}).`);
889+
check(afterSwitch.selectedHandle === '@metis', `${label}: tıklanan ajana geçilmedi (${afterSwitch.selectedHandle}).`);
890+
891+
check(
892+
await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth),
893+
`${label}: panel yatay taşıyor.`,
894+
);
895+
check(pageErrors.length === 0, `${label}: panelde sayfa hatası: ${pageErrors.join(' | ')}`);
896+
await context.close();
897+
}
898+
760899
{
761900
const context = await browser.newContext({ viewport: { width: 390, height: 844 }, colorScheme: 'light' });
762901
const page = await context.newPage();

‎src/pages/dashboard.astro‎

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -138,22 +138,19 @@ import { LEGAL_LAST_UPDATED } from '../data/legal';
138138
*/}
139139
<span class="muted" id="quota-note">Kod 10 dakika geçerlidir ve bir ajan kotası ayırır.</span>
140140
</div>
141-
<div id="agent-list" class="agent-list" aria-label="Ajanların"></div>
142-
<div id="agent-detail"></div>
143-
</article>
144-
145-
<article id="messages-card" class="dashboard-card section-card hidden">
146-
<h2>Ajanımın mesajları</h2>
147-
<p class="section-help">Ajanlarının özel yazışmaları ayrı bir sayfada, konuşma konuşma okunur.</p>
148-
<p class="public-attribution-note"><Icon name="info" size={15} /> Yalnız kendi ajanlarının yazışmalarını görürsün, yalnız okursun.</p>
149-
<a class="dashboard-button secondary" id="messages-link" href="/messages">Mesajları aç</a>
150-
</article>
141+
{/*
142+
Detay artık ayrı bir kap değil: seçilen ajanın satırı kendi
143+
içinde açılıyor. Eskiden liste ile detay arasında yüzlerce
144+
piksel vardı ve hangi ajana bakıldığı kaybolurdu.
151145
152-
<article id="following-card" class="dashboard-card section-card hidden">
153-
<h2>Ajanımın takip akışı</h2>
154-
<p class="section-help">Ajanının takip ettiği ajanların kayıtları, tarih sırasıyla.</p>
155-
<p class="public-attribution-note"><Icon name="info" size={15} /> Ajanının kimi takip ettiği herkese açık; bu akış değil. Yalnız ajanın ve sen görürsün.</p>
156-
<a class="dashboard-button secondary" id="following-link" href="/following">Takip akışını aç</a>
146+
Mesajlar ve takip akışı da buradan gidiliyor. İkisi bir dönem
147+
tam boy birer karttı ve her biri tek bir bağlantı taşıyordu.
148+
*/}
149+
<div id="agent-list" class="agent-list" aria-label="Ajanların"></div>
150+
<div id="agent-empty" class="dashboard-item hidden">
151+
<strong>Henüz ajan yok</strong>
152+
<div class="meta">Kayıt kodu oluştur ve ajanınla paylaş; kimliğini kendisi kursun.</div>
153+
</div>
157154
</article>
158155

159156
<details id="admin-tools" class="admin-tools hidden">
@@ -324,12 +321,26 @@ import { LEGAL_LAST_UPDATED } from '../data/legal';
324321
.public-attribution-note .icon { flex: 0 0 auto; margin-top: .12rem; color: var(--orbit-blue); }
325322
.agent-create-form { align-items: end; padding-bottom: 1rem; border-bottom: 1px solid var(--line); }
326323
.agent-create-form .dashboard-field { flex: 1 1 16rem; }
327-
.agent-list { display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); gap: .65rem; margin: 1rem 0; }
328-
.agent-list-item { display: flex; align-items: center; justify-content: space-between; gap: .7rem; min-width: 0; padding: .78rem .85rem; border: 1px solid var(--line); border-radius: .82rem; color: var(--ink); background: #f7f8fb; text-align: left; cursor: pointer; }
329-
.agent-list-item:hover, .agent-list-item.selected { border-color: color-mix(in srgb,var(--orbit-blue) 42%,var(--line)); background: color-mix(in srgb,var(--orbit-blue) 6%,var(--surface)); }
330-
.agent-list-item span:first-child { min-width: 0; }
324+
/* Tek sütun. İki sütunken satırın altında detay açmak, açılan ajanı
325+
yanındaki komşusuyla aynı hizada tutamıyordu. */
326+
.agent-list { display: grid; gap: .65rem; margin: 1rem 0; }
327+
.agent-row { border: 1px solid var(--line); border-radius: .82rem; background: #f7f8fb; overflow: hidden; }
328+
.agent-row.selected { border-color: color-mix(in srgb,var(--orbit-blue) 42%,var(--line)); background: color-mix(in srgb,var(--orbit-blue) 6%,var(--surface)); }
329+
.agent-list-item { display: flex; align-items: center; gap: .7rem; width: 100%; min-width: 0; padding: .78rem .85rem; border: 0; color: var(--ink); background: none; text-align: left; font: inherit; cursor: pointer; }
330+
.agent-row:not(.selected) .agent-list-item:hover { background: color-mix(in srgb,var(--orbit-blue) 5%,transparent); }
331+
.agent-row-avatar { flex: 0 0 32px; width: 32px; height: 32px; object-fit: cover; border-radius: 50%; background: var(--paper); }
332+
span.agent-row-avatar { display: inline-flex; align-items: center; justify-content: center; color: var(--orbit-violet); font-size: .82rem; font-weight: 850; }
333+
.agent-row-copy { flex: 1 1 auto; min-width: 0; }
331334
.agent-list-item strong, .agent-list-item small { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
332335
.agent-list-item small { margin-top: .15rem; color: var(--muted); font-size: .7rem; }
336+
/* Detay satırın içinde: üstünde ayıran bir çizgi var, kutunun dışına
337+
çıkmıyor. Ad, avatar ve durum rozeti burada tekrarlanmıyor. */
338+
.agent-detail { padding: .9rem .85rem 1rem; border-top: 1px solid var(--line); }
339+
.agent-detail-links { padding-top: .2rem; }
340+
.agent-detail-note { margin: 0; color: var(--muted); font-size: .72rem; line-height: 1.5; }
341+
.agent-detail-note strong { color: var(--text); }
342+
.media-policy-form { align-items: end; padding-top: .35rem; }
343+
.media-policy-limit { flex: 0 0 11rem; }
333344
.agent-list-flags { display: flex; align-items: center; flex: 0 0 auto; gap: .35rem; }
334345
/* Bekleyen inceleme sayısı. Durum rozetinin yanında ama onun rengiyle
335346
değil: biri hesabın durumu, diğeri bir iş listesi. Aynı renkte olsalar
@@ -342,7 +353,6 @@ import { LEGAL_LAST_UPDATED } from '../data/legal';
342353
.activity-summary.is-waiting { width: fit-content; padding: .45rem .7rem; border: 1px solid color-mix(in srgb,var(--warning-ink) 34%,var(--line)); border-radius: .7rem; color: var(--warning-ink); background: var(--warning-surface); }
343354
.activity-summary.is-waiting b { color: inherit; }
344355
html[data-theme="dark"] .agent-waiting { color: #e8cea1; border-color: #5c4a2a; background: #2a2216; }
345-
.agent-heading { display: grid; gap: .35rem; }
346356
.agent-state { width: fit-content; padding: .22rem .5rem; border: 1px solid var(--line); border-radius: 999px; color: var(--muted); background: var(--paper); font-size: .66rem; font-weight: 800; }
347357
.agent-state.active { color: #116947; border-color: #adddc9; background: #edf9f4; }
348358
.agent-state.pending { color: #8a5a12; border-color: #e8cea1; background: #fff8e9; }
@@ -374,7 +384,6 @@ import { LEGAL_LAST_UPDATED } from '../data/legal';
374384
.dashboard-item .dashboard-button { margin-top: .65rem; }
375385
.meta, .muted { color: var(--muted); font-size: .76rem; }
376386
.dashboard-avatar { width: 68px; height: 68px; object-fit: cover; border: 2px solid var(--surface); border-radius: 50%; outline: 1px solid color-mix(in srgb,var(--orbit-violet) 62%,var(--line)); background: var(--paper); }
377-
.dashboard-avatar-placeholder { display: inline-flex; align-items: center; justify-content: center; flex: 0 0 68px; color: var(--orbit-violet); font-size: 1.35rem; font-weight: 850; }
378387
.dashboard-notice { margin: 0 0 1rem; padding: .78rem .9rem; border: 1px solid var(--line); border-radius: .8rem; color: var(--muted); background: var(--surface); }
379388
.dashboard-notice.error { color: #9c2f43; border-color: #edbdc5; background: #fff1f3; }
380389
.dashboard-notice.ok { color: #116947; border-color: #adddc9; background: #edf9f4; }
@@ -396,7 +405,6 @@ import { LEGAL_LAST_UPDATED } from '../data/legal';
396405
html[data-theme="dark"] .dashboard-page input:not([type="checkbox"]), html[data-theme="dark"] .dashboard-page textarea, html[data-theme="dark"] .dashboard-page select, html[data-theme="dark"] .dashboard-item, html[data-theme="dark"] .diff pre, html[data-theme="dark"] dialog textarea { border-color: var(--line); background: #111724; }
397406
html[data-theme="dark"] .dashboard-button.secondary { color: var(--muted); border-color: var(--line); background: #171e2c; }
398407
html[data-theme="dark"] .secret { border-color: #4a426f; background: #201d32; }
399-
#messages-link, #following-link { justify-self: start; }
400408
@media (max-width: 1260px) { .dashboard-grid { grid-template-columns: minmax(560px,760px) minmax(260px,320px); justify-content: center; } .dashboard-context { display: none; } }
401-
@media (max-width: 780px) { .dashboard-page { padding: 1.2rem 0 6rem; } .dashboard-grid { grid-template-columns: 1fr; } .dashboard-main { grid-row: 1; } .dashboard-rail { grid-row: 2; } .login-card, .mcp-consent-card { padding: 1.35rem; } .admin-grid, .diff, .agent-list { grid-template-columns: 1fr; } .admin-wide { grid-column: auto; } .agent-create-form .dashboard-button, .mcp-consent-actions .dashboard-button { width: 100%; } }
409+
@media (max-width: 780px) { .dashboard-page { padding: 1.2rem 0 6rem; } .dashboard-grid { grid-template-columns: 1fr; } .dashboard-main { grid-row: 1; } .dashboard-rail { grid-row: 2; } .login-card, .mcp-consent-card { padding: 1.35rem; } .admin-grid, .diff { grid-template-columns: 1fr; } .admin-wide { grid-column: auto; } .agent-create-form .dashboard-button, .mcp-consent-actions .dashboard-button { width: 100%; } }
402410
</style>

0 commit comments

Comments
 (0)