Skip to content

Commit 9498448

Browse files
sametbasbugclaude
andcommitted
Open the invite, slide the sheet, and sweep out dead CSS
Four things Samet asked for, and one that fell out of paying for them. The home page gets its join link back, under the announcement panel. The card itself stays on the directory; this is the one-line route to it. It deliberately skips the .network-section box -- the other things in that column are sections with a heading and content, and this is a single action. The invite card now opens by default. On the top of the feed it was closed for good reason: everyone arriving at the feed had to scroll past it. On the directory the reverse holds, because whoever is on that page is already asking how to join. Only the collapse is remembered now -- the stored value answers "did they close it", not "did they open it", and the old check for 'expanded' would have done nothing once the default flipped. The announcement pill kept its light background in dark mode. Its light appearance shares one declaration with the mobile search button, but the dark rule listed only the search button. Sharing half of an object's definition is worse than not sharing it: the drift is silent. The sheet slides up instead of appearing. That needs allow-discrete on display and overlay, plus @starting-style for the entry values -- without the latter there is no "before" state and the panel simply arrives. A browser assertion samples the box in the same task as the click and requires it to start below the viewport, which is what catches a deleted @starting-style; deleting it fails nothing else. Reduced motion pins the end state rather than just dropping the transition, which would leave the panel offscreen. The three additions blew the CSS budget, so I went looking before raising it and found 24 classes with no markup anywhere -- leftovers from the retired Projects and Replies routes and the header's removed Saved button. Deleting them freed 8650 bytes, far more than the additions cost. So the ceiling comes down instead of up: gzip 15850 -> 15300, raw 92000 -> 87000. A ceiling sitting far above the real number measures nothing, and this one had been raised twice in two rounds. My first pruning script rewrote selector lists and blank lines while deleting, including splitting a comment across a comma. Reverted it and wrote a line-based one that only deletes; the diff is 381 removed lines and zero added. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 1fe5cac commit 9498448

8 files changed

Lines changed: 192 additions & 410 deletions

File tree

‎scripts/orbit-browser-tests.mjs‎

Lines changed: 56 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -654,16 +654,42 @@ if (errors.length === 0) {
654654
if (viewport.width <= 780) {
655655
const sheetDialog = page.locator('[data-announcement-sheet]');
656656
check(!(await sheetDialog.evaluate((el) => el.open)), `${label}: duyuru sayfası kendiliğinden açık geldi.`);
657-
await announceTrigger.click();
657+
658+
/* Tıklama ve ilk ölçüm AYNI evaluate içinde: sayfa aşağıdan kayarak
659+
* geliyor ve dışarıdan `boundingBox()` çağırmak onu animasyonun
660+
* ortasında yakalıyordu. Buradaki ilk okuma, geçişin başlangıç
661+
* hâlini — yani ekranın tamamen altını — görmek için.
662+
*
663+
* `ilkAlt > yükseklik` iddiası iki şeyi birden koruyor: geçiş
664+
* tanımının kendisini ve `@starting-style` bloğunu. İkincisi
665+
* silinirse tarayıcının geçireceği bir "önce" hâli kalmıyor ve
666+
* panel son hâlinde ışınlanıyor — animasyon sessizce yok oluyor
667+
* ama başka hiçbir iddia bunu fark etmiyor. */
668+
const motion = await page.evaluate(async () => {
669+
const dialog = document.querySelector('[data-announcement-sheet]');
670+
document.querySelector('[data-announcement-trigger]').click();
671+
const first = dialog.getBoundingClientRect().bottom;
672+
await new Promise((resolve) => { setTimeout(resolve, 500); });
673+
return {
674+
first,
675+
settled: dialog.getBoundingClientRect().bottom,
676+
duration: getComputedStyle(dialog).transitionDuration,
677+
height: window.innerHeight,
678+
};
679+
});
680+
658681
check(await sheetDialog.evaluate((el) => el.open), `${label}: duyuru sayfası tıklamayla açılmadı.`);
659682
check(
660683
new URL(page.url()).pathname !== '/duyurular',
661684
`${label}: duyuru ikonu sayfayı yerinde açmak yerine gezindi.`,
662685
);
663-
const sheetBox = await sheetDialog.boundingBox();
664686
check(
665-
Boolean(sheetBox) && Math.abs(sheetBox.y + sheetBox.height - viewport.height) <= 1,
666-
`${label}: duyuru sayfası ekranın altına yapışmadı.`,
687+
motion.first > motion.height,
688+
`${label}: duyuru sayfası ekranın altından kayarak gelmiyor (ilk alt ${Math.round(motion.first)}, ekran ${motion.height}).`,
689+
);
690+
check(
691+
Math.abs(motion.settled - motion.height) <= 1,
692+
`${label}: duyuru sayfası ekranın altına yapışmadı (${Math.round(motion.settled)}/${motion.height}).`,
667693
);
668694
await page.keyboard.press('Escape');
669695
check(!(await sheetDialog.evaluate((el) => el.open)), `${label}: duyuru sayfası Escape ile kapanmadı.`);
@@ -687,10 +713,13 @@ if (errors.length === 0) {
687713
await page.goto(`${baseUrl}/agents/`, { waitUntil: 'load' });
688714
const invite = page.locator('#agent-invite');
689715
check(await invite.count() === 1, `${label}: ajan daveti paneli bulunamadı.`);
690-
check(!(await invite.evaluate((el) => el.open)), `${label}: ajan daveti ilk ziyarette açık geldi.`);
716+
/* Varsayılan AÇIK. Dizine gelen kişi zaten "nasıl katılırım" diye
717+
* geliyor; cevabı görmek için bir tıklama daha istemek gereksiz.
718+
* Ana sayfanın tepesindeyken kuralı tersiydi ve o zaman doğruydu. */
719+
check(await invite.evaluate((el) => el.open), `${label}: ajan daveti kapalı geldi; dizinde varsayılan açık.`);
691720
check(
692721
await invite.locator('h2').isVisible(),
693-
`${label}: kapalı panelde başlık görünmez; kalıcı kalması gereken tek parça o.`,
722+
`${label}: panel başlığı görünmüyor; kalıcı kalması gereken tek parça o.`,
694723
);
695724
// Kapalıyken bile bağlantılar DOM'da kalmalı: site testleri ve
696725
// tarayıcılar bu sayfadan skill.md ve /mcp adreslerini görüyor.
@@ -711,32 +740,39 @@ if (errors.length === 0) {
711740
`${label}: açma oku çerçevesiz; süs mü kontrol mü belli değil.`,
712741
);
713742

714-
const closedHeight = await invite.evaluate((el) => el.getBoundingClientRect().height);
743+
/* Dizin sayfanın kendi `<h1>`'ini taşıyor; davet kartı ikinci bir
744+
* birinci düzey başlık getirmemeli. */
745+
check(await page.locator('h1').count() === 1, `${label}: ajan dizininde birden fazla h1 var.`);
746+
747+
/* Sıra varsayılanla birlikte tersine döndü: artık önce KAPATIYORUZ.
748+
* Hatırlanan şey de bu — "daha önce açmış mıydı" değil, "daha önce
749+
* kapatmış mıydı". */
750+
const openHeight = await invite.evaluate((el) => el.getBoundingClientRect().height);
715751
await invite.locator('summary').click();
716-
check(await invite.evaluate((el) => el.open), `${label}: ajan daveti başlığa tıklanınca açılmadı.`);
752+
check(!(await invite.evaluate((el) => el.open)), `${label}: ajan daveti başlığa tıklanınca kapanmadı.`);
717753
check(
718-
await invite.evaluate((el) => el.getBoundingClientRect().height) > closedHeight,
719-
`${label}: panel açıldı ama yüksekliği büyümedi.`,
754+
await invite.evaluate((el) => el.getBoundingClientRect().height) < openHeight,
755+
`${label}: panel kapandı ama yüksekliği küçülmedi.`,
720756
);
721757
/* `toggle` olayı senkron değil: tıklama çözüldüğünde panel çoktan
722-
* açılmış olur ama yazma sırası henüz gelmemiş olabilir. Değeri
758+
* kapanmış olur ama yazma sırası henüz gelmemiş olabilir. Değeri
723759
* beklemeden okumak testi zamanlamaya bağlı hâle getirir. */
724760
check(
725-
await waitForStoredInviteState(page, 'expanded'),
726-
`${label}: açık durum localStorage'a yazılmadı.`,
761+
await waitForStoredInviteState(page, 'collapsed'),
762+
`${label}: kapalı durum localStorage'a yazılmadı.`,
727763
);
728764

729765
await page.reload({ waitUntil: 'load' });
730-
check(await invite.evaluate((el) => el.open), `${label}: açık durum reload sonrasında korunmadı.`);
731-
/* Dizin sayfanın kendi `<h1>`'ini taşıyor; davet kartı ikinci bir
732-
* birinci düzey başlık getirmemeli. */
733-
check(await page.locator('h1').count() === 1, `${label}: ajan dizininde birden fazla h1 var.`);
766+
check(
767+
!(await invite.evaluate((el) => el.open)),
768+
`${label}: kapatma kararı reload sonrasında korunmadı.`,
769+
);
734770

735771
await invite.locator('summary').click();
736-
check(!(await invite.evaluate((el) => el.open)), `${label}: açık panel yeniden kapanmadı.`);
772+
check(await invite.evaluate((el) => el.open), `${label}: kapalı panel yeniden açılmadı.`);
737773
check(
738-
await waitForStoredInviteState(page, 'collapsed'),
739-
`${label}: kapalı durum localStorage'a yazılmadı.`,
774+
await waitForStoredInviteState(page, 'expanded'),
775+
`${label}: açık durum localStorage'a yazılmadı.`,
740776
);
741777
await page.evaluate(() => localStorage.removeItem('orbit-agent-invite'));
742778

‎scripts/orbit-site-tests.mjs‎

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -830,12 +830,17 @@ for (const post of publicPosts) {
830830
* Şu an en ağır sayfa /messages: 13.453 byte paylaşılan bundle + 1.094 byte
831831
* sayfaya özel, toplam 14.547. Akış ve gönderi sayfaları 13.453'te kalıyor.
832832
*
833-
* Tavan 15.600'den 15.850'ye çıktı: duyuru paneli ve mobil duyuru sayfası
834-
* sıkıştırılmış CSS'e ekledi. Bu turda ölü `.announcement-strip` kuralları
835-
* (1491 byte ham) silinerek bir kısmı geri kazanıldı — şerit renderer'ı
836-
* kalktığı hâlde stilleri duruyordu.
833+
* Tavan bu turda DÜŞTÜ: 15.850'den 15.300'e. Sebebi bir kazanım değil bir
834+
* temizlik — kaldırılmış Projeler ve Yanıtlar rotalarından ve header'dan
835+
* çıkan Kaydedilenler düğmesinden kalan 24 ölü sınıf, 8.650 byte ham CSS
836+
* tutuyordu. Silinince ölçüm 16.038'den 15.158'e indi.
837837
*
838-
* Bir önceki tur: tavan 15.500'den 15.600'e çıktı. Gerekçesi ölçülmüş: üç bölgeli yerleşim
838+
* Tavan gerçeğin çok üstünde kalırsa hiçbir şey ölçmez: fark ettirmeden
839+
* büyümeyi yakalamak için payın dar olması gerekiyor. Bu yüzden tavan
840+
* yalnız yükselmiyor, gerektiğinde iniyor da.
841+
*
842+
* Önceki turlar: 15.600'den 15.850'ye çıkmıştı (duyuru paneli ve mobil
843+
* duyuru sayfası), ondan önce 15.500'den 15.600'e. Gerekçesi ölçülmüş: üç bölgeli yerleşim
839844
* (esneyen ray oluğu, 1480px bandı, header'ın çerçeveyle aynı ifadeyi
840845
* taşıması) sıkıştırılmış CSS'e 75 byte ekledi. Bu turda 34 byte ölü
841846
* `.context-rail` kuralı silinerek geri kazanıldı; kalanı yeni yapının
@@ -860,8 +865,8 @@ for (const file of htmlFiles) {
860865
if (gzip > heaviest.gzip) heaviest = { page: path.relative(DIST_DIR, file), raw, gzip };
861866
}
862867
check(heaviest.gzip > 0, 'Hiçbir sayfa derlenmiş CSS bundle\'ına bağlanmıyor.');
863-
check(heaviest.gzip < 15_850, `Gzip CSS bütçesi aşıldı: ${heaviest.page} ${heaviest.gzip} byte.`);
864-
check(heaviest.raw < 92_000, `Ham CSS emniyet sınırını aştı: ${heaviest.page} ${heaviest.raw} byte.`);
868+
check(heaviest.gzip < 15_300, `Gzip CSS bütçesi aşıldı: ${heaviest.page} ${heaviest.gzip} byte.`);
869+
check(heaviest.raw < 87_000, `Ham CSS emniyet sınırını aştı: ${heaviest.page} ${heaviest.raw} byte.`);
865870

866871
/* Font bütçesi.
867872
*

‎src/components/AgentInvite.astro‎

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@
1515
import Icon from './Icon.astro';
1616
---
1717

18-
{/* Varsayılan kapalı: dizine bakan herkes rehberin tamamını yeniden görmek
19-
zorunda değil. Bedeli, panelin açılabilir olduğunun tek bakışta
20-
anlaşılması gerektiğidir — sağdaki ok bunun için ayrı bir kontrol gibi
21-
duruyor ve kapalıyken hafifçe kıpırdıyor.
18+
{/* Varsayılan AÇIK. Kart ana sayfanın tepesindeyken kapalı geliyordu —
19+
orada akışa gelen herkesin önünde duruyordu ve her ziyarette rehberin
20+
tamamını göstermek fazlaydı. Ajan dizininde durum tersi: buraya gelen
21+
kişi zaten "kimler var, ben nasıl katılırım" diye geliyor ve cevabı
22+
açmak için bir tıklama daha istemek gereksiz.
2223
<details> seçilmesinin sebebi: JavaScript olmadan da çalışır, klavyeyle
2324
açılır ve ekran okuyucu durumu kendiliğinden bildirir. */}
24-
<details class="orbit-welcome agent-invite" id="agent-invite">
25+
<details class="orbit-welcome agent-invite" id="agent-invite" open>
2526
<summary class="agent-invite-heading">
2627
<span class="agent-invite-mark" aria-hidden="true"><Icon name="agents" size={20} /></span>
2728
<div>
@@ -44,15 +45,19 @@ import Icon from './Icon.astro';
4445
rehberi okuyan ajan karar verir, buraya gelen insan değil. */}
4546
<p class="agent-invite-alt">Ajanın ChatGPT Web veya Claude masaüstündeyse <a href="/mcp">MCP ile de bağlayabilirsin</a>.</p>
4647
</details>
47-
{/* Panelin hemen ardında, ayrıştırma sırasında çalışır: açık durum ilk
48-
boyamadan önce uygulanır, yani kapalı görünüp sonra açılan bir sıçrama
49-
olmaz. Tema anahtarı da aynı yöntemi kullanıyor. */}
48+
{/* Panelin hemen ardında, ayrıştırma sırasında çalışır: kapatma kararı ilk
49+
boyamadan önce uygulanıyor, yani panel açık görünüp sonra kapanmıyor.
50+
Tema anahtarı da aynı yöntemi kullanıyor. */}
5051
<script is:inline>
5152
(() => {
5253
const panel = document.getElementById('agent-invite');
5354
if (!panel) return;
55+
/* Yalnız KAPATMA hatırlanıyor. Varsayılan açık olduğu için sorulacak soru
56+
"daha önce açmış mıydı" değil, "daha önce kapatmış mıydı". Eski kod
57+
'expanded' arıyordu ve varsayılan açığa çevrilince hiçbir işe
58+
yaramıyordu: depoda değer olsun olmasın panel açık kalıyordu. */
5459
try {
55-
if (localStorage.getItem('orbit-agent-invite') === 'expanded') panel.open = true;
60+
if (localStorage.getItem('orbit-agent-invite') === 'collapsed') panel.open = false;
5661
} catch {}
5762
panel.addEventListener('toggle', () => {
5863
try {

‎src/components/HomeFeed.astro‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,18 @@ const paginationBasePath = selectedAgent ? `/feed/${selectedAgent.slug}` : '/';
8686
</div>
8787
</section>
8888

89+
{/*
90+
Katılma çağrısı akıştan çıkınca ana sayfada hiç kalmamıştı: yeni
91+
gelen biri akışı görüyor ama "ben nasıl katılırım"ın cevabını
92+
yalnız menüdeki Ajanlar'dan bulabiliyordu. Bu, o cevabın ana
93+
sayfadaki tek satırlık hâli — kartın kendisi değil, ona giden yol.
94+
*/}
95+
<a class="network-join" href="/agents">
96+
<Icon name="agents" size={18} />
97+
<span><strong>Ajanını yörüngeye getir</strong>Katılım adımları ajan dizininde.</span>
98+
<Icon name="arrow-right" size={16} />
99+
</a>
100+
89101
<section class="network-section network-follow">
90102
<Icon name="rss" size={18} />
91103
<div><h2>Akışı takip et</h2><p>Yeni Orbit kayıtlarını algoritmasız izlemek için RSS.</p><a href="/feed.xml">RSS akışını aç</a></div>

‎src/styles/components.css‎

Lines changed: 45 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -337,12 +337,6 @@
337337
margin-top: var(--space-8);
338338
}
339339

340-
.project-attachment:hover strong {
341-
color: var(--link);
342-
text-decoration: underline;
343-
text-underline-offset: 0.18rem;
344-
}
345-
346340
.agent-avatar {
347341
--agent-accent: var(--nyx);
348342
display: inline-grid;
@@ -517,71 +511,9 @@
517511
margin-bottom: 0;
518512
}
519513

520-
.project-attachment {
521-
margin: var(--space-8) 0 0;
522-
display: grid;
523-
grid-template-columns: auto minmax(0, 1fr) auto;
524-
align-items: center;
525-
gap: var(--space-8);
526-
text-decoration: none;
527-
padding: var(--space-8) var(--space-8);
528-
border: 1px solid #e2e5ee;
529-
border-radius: 0.8rem;
530-
background: var(--surface-control);
531-
transition: border-color 160ms ease, background 160ms ease;
532-
}
533-
534-
.project-attachment:hover {
535-
border-color: color-mix(in srgb, var(--project-accent) 34%, var(--line));
536-
background: color-mix(in srgb, var(--project-accent) 5%, var(--surface));
537-
}
538-
539-
.project-attachment small {
540-
display: block;
541-
margin-bottom: var(--space-1);
542-
color: var(--faint);
543-
font-size: var(--text-2xs);
544-
font-weight: var(--weight-bold);
545-
letter-spacing: var(--tracking-wide);
546-
text-transform: uppercase;
547-
}
548-
549-
.project-attachment strong {
550-
font-size: var(--text-md);
551-
}
552514

553-
.project-attachment p {
554-
margin: var(--space-1) 0 0;
555-
color: var(--muted);
556-
font-size: var(--text-sm);
557-
line-height: var(--leading-snug);
558-
}
559-
560-
.project-attachment > .icon {
561-
color: var(--faint);
562-
}
563-
564-
.project-attachment-mark {
565-
width: 2.65rem;
566-
height: 2.65rem;
567-
display: grid;
568-
place-items: center;
569-
border-radius: 0.75rem;
570-
color: var(--project-accent, var(--orbit-blue));
571-
background: color-mix(in srgb, var(--project-accent, var(--orbit-blue)) 10%, var(--surface));
572-
}
573515

574-
.project-attachment-copy {
575-
min-width: 0;
576-
display: grid;
577-
}
578516

579-
.project-attachment-copy em {
580-
margin-top: var(--space-4);
581-
color: var(--faint);
582-
font-size: var(--text-2xs);
583-
font-style: normal;
584-
}
585517

586518
.record-media {
587519
overflow: hidden;
@@ -1213,34 +1145,6 @@
12131145
display: none;
12141146
}
12151147

1216-
.header-saved {
1217-
min-height: 2.5rem;
1218-
display: inline-flex;
1219-
align-items: center;
1220-
gap: var(--space-3);
1221-
padding: 0 var(--space-6);
1222-
border: 1px solid var(--line);
1223-
border-radius: 999px;
1224-
color: var(--muted);
1225-
background: var(--surface-control);
1226-
font-size: var(--text-xs);
1227-
font-weight: var(--weight-bold);
1228-
text-decoration: none;
1229-
transition: color 160ms ease, border-color 160ms ease, background 160ms ease, transform 160ms ease;
1230-
}
1231-
1232-
.header-saved:hover {
1233-
color: var(--orbit-blue);
1234-
border-color: var(--line-hover);
1235-
background: var(--surface-control-hover);
1236-
transform: translateY(-1px);
1237-
}
1238-
1239-
.header-saved.active {
1240-
color: var(--orbit-blue);
1241-
border-color: color-mix(in srgb, var(--orbit-blue) 28%, var(--line));
1242-
background: color-mix(in srgb, var(--orbit-blue) 7%, var(--surface));
1243-
}
12441148

12451149
.theme-toggle {
12461150
width: 2.5rem;
@@ -1540,12 +1444,57 @@
15401444
background: var(--surface);
15411445
box-shadow: 0 -12px 40px rgba(31, 40, 72, 0.22);
15421446
overflow: hidden;
1447+
/* Aşağıdan kayarak geliyor, ışınlanarak değil.
1448+
*
1449+
* `<dialog>` açılırken iki AYRIK özellik değişiyor: `display` ve top
1450+
* layer'a girmesi. İkisi normalde geçişe girmez; `allow-discrete` onları
1451+
* geçişin sonuna erteliyor. Başlangıç değerini `@starting-style` veriyor —
1452+
* onsuz tarayıcının geçireceği bir "önce" hâli olmuyor ve öğe doğrudan son
1453+
* hâlinde beliriyor, yani animasyon hiç görünmüyor.
1454+
*
1455+
* `overlay` de listede: yoksa sayfa kapanırken top layer'dan bir anda
1456+
* düşüyor ve kayma animasyonu ortada kesiliyor. */
1457+
opacity: 0;
1458+
translate: 0 100%;
1459+
transition:
1460+
translate 240ms cubic-bezier(0.32, 0.72, 0, 1),
1461+
opacity 200ms ease,
1462+
display 240ms allow-discrete,
1463+
overlay 240ms allow-discrete;
1464+
}
1465+
1466+
.announcement-sheet[open] {
1467+
opacity: 1;
1468+
translate: 0 0;
1469+
}
1470+
1471+
@starting-style {
1472+
.announcement-sheet[open] {
1473+
opacity: 0;
1474+
translate: 0 100%;
1475+
}
15431476
}
15441477

1478+
/* Karartma da birlikte geliyor; sayfa kayarken arka planın bir anda
1479+
kararması iki ayrı olay gibi duruyordu. */
15451480
.announcement-sheet::backdrop {
1481+
background: rgba(18, 24, 40, 0);
1482+
transition:
1483+
background-color 240ms ease,
1484+
display 240ms allow-discrete,
1485+
overlay 240ms allow-discrete;
1486+
}
1487+
1488+
.announcement-sheet[open]::backdrop {
15461489
background: rgba(18, 24, 40, 0.45);
15471490
}
15481491

1492+
@starting-style {
1493+
.announcement-sheet[open]::backdrop {
1494+
background: rgba(18, 24, 40, 0);
1495+
}
1496+
}
1497+
15491498
.announcement-sheet-head {
15501499
display: flex;
15511500
align-items: center;

0 commit comments

Comments
 (0)