Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history

daily-brief.tsx

Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.

daily-brief.tsxBlame634 lines · 1 contributor
11c3ab6ccanty labs1import type { FC } from "hono/jsx";
2
3export interface NeedsYouItem {
4 title: string;
5 detail: string;
6 sessionHref?: string;
7 diffHref?: string;
8}
9
10export interface ShippedItem {
11 time: string;
12 title: string;
13 detail: string;
14 meta: string;
15}
16
17export interface HandledItem {
18 what: string;
19 detail: string;
20}
21
22export interface DailyBriefProps {
23 needsYou?: NeedsYouItem[];
24 shipped?: ShippedItem[];
25 handled?: HandledItem[];
26 savedHrs?: number;
27 user?: { name?: string; initials?: string };
28}
29
30const DEFAULT_NEEDS_YOU: NeedsYouItem[] = [
31 {
32 title: "Approve the repair on PR №218 — auto-repair × sandbox unification",
33 detail:
34 "Claude fixed the failing tests inside the PR sandbox overnight. Gates are green; Performance flagged an unbounded retry which Claude then capped. All that's left is your approval — or take the branch over manually if you'd rather work the diff yourself.",
35 sessionHref: "#",
36 diffHref: "#",
37 },
38];
39
40const DEFAULT_SHIPPED: ShippedItem[] = [
41 {
42 time: "02:14",
43 title: "Dependency bumps merged across 3 repos",
44 detail:
45 "drizzle-orm 0.44 → 0.45, hono patch, bun types. Fleet changeset, one review, all gates green.",
46 meta: "gluecron · gluecron-cli · editor-extensions — reverted in one click if needed",
47 },
48 {
49 time: "03:40",
50 title: "Flaky test quarantined, then fixed",
51 detail:
52 "auto-repair bisected sandbox-boot.test.ts to a race on port assignment, patched the fixture, and un-quarantined it after 40 green runs.",
53 meta: "PR №216 · merged by policy · 0 human minutes spent",
54 },
55 {
56 time: "05:52",
57 title: "Security advisory patched before you knew it existed",
58 detail:
59 "CVE in a transitive dep of the registry middleware. Patched, gated, deployed. Exposure window: 19 minutes.",
60 meta: "PR №217 · deploy 82321e8 · advisory GHSA-4q7x",
61 },
62];
63
64const DEFAULT_HANDLED: HandledItem[] = [
65 {
66 what: "4 PRs continuously rebased",
67 detail: "no merge conflicts reached any developer this week",
68 },
69 {
70 what: "EU semantic index backfill",
71 detail: "completed 04:10, helvetia-fintech ticket auto-resolved",
72 },
73 {
74 what: "2 stale branches archived",
75 detail: "per repo policy, both had merged upstream",
76 },
77];
78
79export const DailyBrief: FC<DailyBriefProps> = (props) => {
80 const needsYou = props.needsYou ?? DEFAULT_NEEDS_YOU;
81 const shipped = props.shipped ?? DEFAULT_SHIPPED;
82 const handled = props.handled ?? DEFAULT_HANDLED;
83 const savedHrs = props.savedHrs ?? 3.1;
84 const user = props.user ?? {};
85 const initials = user.initials ?? "C";
86
87 const today = new Date();
88 const dateLabel = today.toLocaleDateString("en-US", {
89 weekday: "long",
90 month: "long",
91 day: "numeric",
92 });
93
94 return (
95 <>
96 <style dangerouslySetInnerHTML={{ __html: css }} />
97 <div class="db-root">
98 <header class="db-header">
99 <a href="/" class="db-logo">
100 <span class="db-logo-mark" />
101 gluecron
102 </a>
103 <span class="db-header-sep">/</span>
104 <span class="db-header-crumb">Brief</span>
105 <div class="db-header-right">
106 <span class="db-header-date">{dateLabel}</span>
107 <span class="db-avatar">{initials}</span>
108 </div>
109 </header>
110
111 <main class="db-main">
112 <div class="db-eyebrow">Your morning brief · 7:00 am</div>
113 <h1 class="db-headline">
114 While you were away, the platform shipped three things and held one
115 back for you.
116 </h1>
117 <p class="db-subtitle">
118 Two-minute read. One decision needed. Everything below is linked,
119 audited, and reversible.
120 </p>
121
122 {/* Needs You section */}
123 <section class="db-section db-needs">
124 <h2 class="db-section-label db-label-amber">
125 <span class="db-pulse-dot db-pulse-amber" />
126 Needs you · {needsYou.length}
127 </h2>
128 {needsYou.map((item) => (
129 <div class="db-action-card">
130 <div class="db-action-title">{item.title}</div>
131 <p class="db-action-detail">{item.detail}</p>
132 <div class="db-action-buttons">
133 <a
134 href={item.sessionHref ?? "#"}
135 class="db-btn db-btn-primary"
136 >
137 Open session
138 </a>
139 <a href={item.diffHref ?? "#"} class="db-btn db-btn-secondary">
140 Review diff manually
141 </a>
142 </div>
143 </div>
144 ))}
145 </section>
146
147 {/* Shipped overnight section */}
148 <section class="db-section db-shipped">
149 <h2 class="db-section-label db-label-green">
150 Shipped overnight · {shipped.length}
151 </h2>
152 <div class="db-timeline">
153 {shipped.map((item) => (
154 <div class="db-timeline-row">
155 <span class="db-timeline-time">{item.time}</span>
156 <div class="db-timeline-body">
157 <div class="db-timeline-title">{item.title}</div>
158 <div class="db-timeline-detail">{item.detail}</div>
159 <div class="db-timeline-meta">{item.meta}</div>
160 </div>
161 </div>
162 ))}
163 </div>
164 </section>
165
166 {/* Handled quietly section */}
167 <section class="db-section db-handled">
168 <h2 class="db-section-label db-label-muted">Handled quietly</h2>
169 <div class="db-handled-list">
170 {handled.map((item) => (
171 <div class="db-handled-row">
172 <span class="db-handled-dot" />
173 <span class="db-handled-text">
174 <strong class="db-handled-what">{item.what}</strong>
175 {" — "}
176 {item.detail}
177 </span>
178 </div>
179 ))}
180 </div>
181 </section>
182
183 {/* Footer */}
184 <div class="db-footer">
185 <p class="db-footer-note">
186 Your team's standup, replaced. Every item links to its full audit
187 trail.
188 </p>
189 <span class="db-footer-saved">
190 saved you ≈ {savedHrs.toFixed(1)} hrs overnight
191 </span>
192 </div>
193 </main>
194 </div>
195 <script
196 dangerouslySetInnerHTML={{
197 __html: clientJs,
198 }}
199 />
200 </>
201 );
202};
203
204export default DailyBrief;
205
206const css = `
207@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;450;500;600;700&family=Inter+Tight:wght@500;600;700;800&family=JetBrains+Mono:wght@400;500&display=swap');
208
209*, *::before, *::after {
210 box-sizing: border-box;
211 margin: 0;
212 padding: 0;
213}
214
215@keyframes gcPulse {
216 0%, 100% { opacity: 1; }
217 50% { opacity: 0.35; }
218}
219
220.db-root {
221 font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
222 font-size: 14px;
223 line-height: 1.55;
224 letter-spacing: -0.008em;
225 color: #16181d;
226 background: #fcfcfd;
227 min-height: 100vh;
228 display: flex;
229 flex-direction: column;
230 -webkit-font-smoothing: antialiased;
231 text-rendering: optimizeLegibility;
232}
233
234/* ── Header ── */
235.db-header {
236 height: 56px;
237 border-bottom: 1px solid rgba(22, 24, 29, 0.07);
238 background: #fcfcfd;
239 display: flex;
240 align-items: center;
241 padding: 0 28px;
242 gap: 20px;
243 position: sticky;
244 top: 0;
245 z-index: 50;
246}
247
248.db-logo {
249 display: inline-flex;
250 align-items: center;
251 gap: 9px;
252 font-family: 'Inter Tight', sans-serif;
253 font-weight: 600;
254 font-size: 15px;
255 letter-spacing: -0.02em;
256 color: #16181d;
257 text-decoration: none;
258}
259
260.db-logo-mark {
261 width: 16px;
262 height: 16px;
263 border-radius: 5px;
264 background: #4353c9;
265 display: inline-block;
266 flex-shrink: 0;
267}
268
269.db-header-sep {
270 color: #c4c6cf;
271}
272
273.db-header-crumb {
274 font-size: 13px;
275 color: #16181d;
276 font-weight: 500;
277}
278
279.db-header-right {
280 margin-left: auto;
281 display: flex;
282 align-items: center;
283 gap: 14px;
284}
285
286.db-header-date {
287 font-size: 12.5px;
288 color: #6b7080;
289}
290
291.db-avatar {
292 width: 28px;
293 height: 28px;
294 border-radius: 50%;
295 background: #16181d;
296 color: #ffffff;
297 font-size: 11px;
298 font-weight: 600;
299 display: inline-flex;
300 align-items: center;
301 justify-content: center;
302 font-family: 'Inter', sans-serif;
303 flex-shrink: 0;
304}
305
306/* ── Main layout ── */
307.db-main {
308 flex: 1;
309 max-width: 760px;
310 width: 100%;
311 margin: 0 auto;
312 padding: 56px 32px 88px;
313}
314
315/* ── Eyebrow + headline ── */
316.db-eyebrow {
317 font-family: 'JetBrains Mono', monospace;
318 font-size: 11px;
319 letter-spacing: 0.12em;
320 text-transform: uppercase;
321 color: #8a8d99;
322 margin-bottom: 12px;
323}
324
325.db-headline {
326 font-family: 'Inter Tight', sans-serif;
327 font-size: 32px;
328 font-weight: 600;
329 letter-spacing: -0.028em;
330 line-height: 1.12;
331 color: #111318;
332 margin: 0 0 12px;
333}
334
335.db-subtitle {
336 font-size: 15px;
337 color: #6b7080;
338 margin: 0 0 44px;
339 line-height: 1.6;
340 max-width: 58ch;
341}
342
343/* ── Sections ── */
344.db-section {
345 margin-bottom: 48px;
346}
347
348.db-section-label {
349 font-family: 'JetBrains Mono', monospace;
350 font-size: 11px;
351 letter-spacing: 0.12em;
352 text-transform: uppercase;
353 font-weight: 500;
354 margin: 0 0 14px;
355 display: flex;
356 align-items: center;
357 gap: 8px;
358}
359
360.db-label-amber {
361 color: #b45309;
362}
363
364.db-label-green {
365 color: #1e7f5c;
366}
367
368.db-label-muted {
369 color: #8a8d99;
370}
371
372/* ── Pulse dot ── */
373.db-pulse-dot {
374 width: 7px;
375 height: 7px;
376 border-radius: 50%;
377 flex-shrink: 0;
378}
379
380.db-pulse-amber {
381 background: #b45309;
382 animation: gcPulse 1.4s ease-in-out infinite;
383}
384
385/* ── Action card (Needs You) ── */
386.db-action-card {
387 border: 1px solid rgba(22, 24, 29, 0.10);
388 border-radius: 14px;
389 background: #ffffff;
390 padding: 24px 26px;
391}
392
393.db-action-title {
394 font-size: 15px;
395 font-weight: 600;
396 color: #16181d;
397 margin-bottom: 6px;
398 line-height: 1.4;
399}
400
401.db-action-detail {
402 font-size: 13.5px;
403 color: #6b7080;
404 margin: 0 0 16px;
405 line-height: 1.6;
406}
407
408.db-action-buttons {
409 display: flex;
410 gap: 10px;
411 flex-wrap: wrap;
412}
413
414/* ── Buttons ── */
415.db-btn {
416 padding: 8px 16px;
417 border-radius: 8px;
418 font-size: 13px;
419 font-weight: 500;
420 text-decoration: none;
421 white-space: nowrap;
422 display: inline-block;
423 line-height: 1.5;
424 transition: border-color 0.12s ease, background 0.12s ease;
425 font-family: 'Inter', sans-serif;
426}
427
428.db-btn-primary {
429 background: #16181d;
430 color: #ffffff;
431 border: 1px solid transparent;
432}
433
434.db-btn-primary:hover {
435 background: #2a2d36;
436}
437
438.db-btn-secondary {
439 background: #ffffff;
440 color: #16181d;
441 border: 1px solid rgba(22, 24, 29, 0.12);
442}
443
444.db-btn-secondary:hover {
445 border-color: rgba(22, 24, 29, 0.22);
446}
447
448/* ── Timeline (Shipped) ── */
449.db-timeline {
450 display: flex;
451 flex-direction: column;
452}
453
454.db-timeline-row {
455 display: grid;
456 grid-template-columns: 64px minmax(0, 1fr);
457 gap: 18px;
458 padding: 16px 4px;
459 border-bottom: 1px solid rgba(22, 24, 29, 0.06);
460}
461
462.db-timeline-row:last-child {
463 border-bottom: none;
464}
465
466.db-timeline-time {
467 font-family: 'JetBrains Mono', monospace;
468 font-size: 12px;
469 color: #8a8d99;
470 padding-top: 2px;
471 flex-shrink: 0;
472}
473
474.db-timeline-body {
475 min-width: 0;
476}
477
478.db-timeline-title {
479 font-size: 14px;
480 font-weight: 500;
481 color: #16181d;
482 line-height: 1.45;
483}
484
485.db-timeline-detail {
486 font-size: 13px;
487 color: #6b7080;
488 margin-top: 2px;
489 line-height: 1.55;
490}
491
492.db-timeline-meta {
493 font-family: 'JetBrains Mono', monospace;
494 font-size: 11.5px;
495 color: #8a8d99;
496 margin-top: 6px;
497}
498
499/* ── Handled quietly ── */
500.db-handled-list {
501 display: flex;
502 flex-direction: column;
503 gap: 10px;
504}
505
506.db-handled-row {
507 display: flex;
508 gap: 12px;
509 align-items: baseline;
510 font-size: 13.5px;
511 color: #6b7080;
512}
513
514.db-handled-dot {
515 width: 6px;
516 height: 6px;
517 border-radius: 50%;
518 background: #c4c6cf;
519 flex-shrink: 0;
520 position: relative;
521 top: -2px;
522}
523
524.db-handled-text {
525 line-height: 1.55;
526}
527
528.db-handled-what {
529 color: #16181d;
530 font-weight: 500;
531}
532
533/* ── Footer ── */
534.db-footer {
535 border-top: 1px solid rgba(22, 24, 29, 0.07);
536 padding-top: 20px;
537 display: flex;
538 justify-content: space-between;
539 align-items: baseline;
540 gap: 16px;
541 flex-wrap: wrap;
542}
543
544.db-footer-note {
545 font-size: 12.5px;
546 color: #8a8d99;
547 margin: 0;
548 line-height: 1.55;
549}
550
551.db-footer-saved {
552 font-family: 'JetBrains Mono', monospace;
553 font-size: 11.5px;
554 color: #8a8d99;
555 white-space: nowrap;
556}
557
558/* ── Responsive ── */
559@media (max-width: 600px) {
560 .db-main {
561 padding: 40px 20px 64px;
562 }
563
564 .db-headline {
565 font-size: 26px;
566 }
567
568 .db-timeline-row {
569 grid-template-columns: 48px minmax(0, 1fr);
570 gap: 12px;
571 }
572
573 .db-footer {
574 flex-direction: column;
575 gap: 8px;
576 }
577
578 .db-header {
579 padding: 0 16px;
580 }
581}
582`;
583
584const clientJs = `
585(function () {
586 'use strict';
587
588 // Hover state for secondary button — pure CSS handles most, but ensure
589 // any programmatic focus rings stay subtle.
590 var secondaryBtns = document.querySelectorAll('.db-btn-secondary');
591 secondaryBtns.forEach(function (btn) {
592 btn.addEventListener('mouseenter', function () {
593 btn.style.borderColor = 'rgba(22,24,29,0.22)';
594 });
595 btn.addEventListener('mouseleave', function () {
596 btn.style.borderColor = 'rgba(22,24,29,0.12)';
597 });
598 });
599
600 // Animate timeline rows in on load
601 var rows = document.querySelectorAll('.db-timeline-row');
602 rows.forEach(function (row, i) {
603 row.style.opacity = '0';
604 row.style.transform = 'translateY(8px)';
605 row.style.transition = 'opacity 0.28s ease, transform 0.28s ease';
606 setTimeout(function () {
607 row.style.opacity = '1';
608 row.style.transform = 'translateY(0)';
609 }, 120 + i * 80);
610 });
611
612 // Animate handled rows
613 var handledRows = document.querySelectorAll('.db-handled-row');
614 handledRows.forEach(function (row, i) {
615 row.style.opacity = '0';
616 row.style.transition = 'opacity 0.24s ease';
617 setTimeout(function () {
618 row.style.opacity = '1';
619 }, 400 + i * 60);
620 });
621
622 // Action card entrance
623 var actionCard = document.querySelector('.db-action-card');
624 if (actionCard) {
625 actionCard.style.opacity = '0';
626 actionCard.style.transform = 'translateY(6px)';
627 actionCard.style.transition = 'opacity 0.3s ease, transform 0.3s ease';
628 setTimeout(function () {
629 actionCard.style.opacity = '1';
630 actionCard.style.transform = 'translateY(0)';
631 }, 60);
632 }
633})();
634`;