flagon-io/g1t

public

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t/apps/docs/src/components/PageTitle.astro

176 lines4,507 bytesCodeBlame
1---
2// A page's title, what it is about beneath it, and a way to take the page
3// to an AI assistant: copied as Markdown, read as Markdown, or opened in
4// Claude or ChatGPT.
5import { ChevronDown, Copy } from '@lucide/astro';
6
7const { entry, id } = Astro.locals.starlightRoute;
8const { title, description } = entry.data;
9const markdown = `/${id === '' || id === 'index' ? 'index' : id}.md`;
10const absolute = new URL(markdown, Astro.site).href;
11const ask = encodeURIComponent(`Read ${absolute} so I can ask questions about it.`);
12---
13
14<div class="g1t-page-title">
15 <div class="g1t-page-title-row">
16 <h1 id="_top">{title}</h1>
17 <div class="g1t-copy">
18 <button type="button" class="g1t-copy-main" data-markdown={markdown}>
19 <Copy />
20 <span>Copy page</span>
21 </button>
22 <details class="g1t-copy-more">
23 <summary aria-label="More ways to use this page">
24 <ChevronDown />
25 </summary>
26 <div class="g1t-copy-menu">
27 <a href={markdown} target="_blank" rel="noopener">
28 <strong>View as Markdown</strong>
29 <span>This page as plain text</span>
30 </a>
31 <a href={`https://claude.ai/new?q=${ask}`} target="_blank" rel="noopener">
32 <strong>Open in Claude</strong>
33 <span>Ask questions about this page</span>
34 </a>
35 <a href={`https://chatgpt.com/?hints=search&q=${ask}`} target="_blank" rel="noopener">
36 <strong>Open in ChatGPT</strong>
37 <span>Ask questions about this page</span>
38 </a>
39 </div>
40 </details>
41 </div>
42 </div>
43 {description && <p class="g1t-description">{description}</p>}
44</div>
45
46<script>
47 document.querySelectorAll<HTMLButtonElement>('.g1t-copy-main').forEach((button) => {
48 button.addEventListener('click', async () => {
49 const label = button.querySelector('span')!;
50 try {
51 const text = await (await fetch(button.dataset.markdown!)).text();
52 await navigator.clipboard.writeText(text);
53 label.textContent = 'Copied';
54 } catch {
55 label.textContent = 'Could not copy';
56 }
57 setTimeout(() => (label.textContent = 'Copy page'), 1600);
58 });
59 });
60 // A menu closes when you click elsewhere.
61 document.addEventListener('click', (event) => {
62 document.querySelectorAll<HTMLDetailsElement>('.g1t-copy-more[open]').forEach((menu) => {
63 if (!menu.contains(event.target as Node)) menu.open = false;
64 });
65 });
66</script>
67
68<style>
69 .g1t-page-title {
70 margin-top: 0.75rem;
71 }
72 .g1t-page-title-row {
73 display: flex;
74 align-items: flex-start;
75 justify-content: space-between;
76 gap: 1.5rem;
77 }
78 h1 {
79 margin: 0;
80 font-family: var(--g1t-font-display);
81 font-optical-sizing: auto;
82 font-size: 2rem;
83 font-weight: 600;
84 letter-spacing: -0.015em;
85 line-height: 1.2;
86 color: var(--g1t-fg);
87 }
88 .g1t-description {
89 margin-top: 0.6rem;
90 max-width: 42rem;
91 font-size: 1.125rem;
92 line-height: 1.55;
93 color: var(--g1t-muted);
94 }
95 .g1t-copy {
96 position: relative;
97 display: flex;
98 flex-shrink: 0;
99 margin-top: 0.35rem;
100 border-radius: 0.5rem;
101 box-shadow: inset 0 0 0 1px var(--g1t-line);
102 }
103 .g1t-copy :global(svg) {
104 width: 0.9rem;
105 height: 0.9rem;
106 }
107 .g1t-copy-main,
108 .g1t-copy-more summary {
109 display: flex;
110 align-items: center;
111 gap: 0.4rem;
112 border: 0;
113 background: transparent;
114 padding: 0.3rem 0.6rem;
115 font: inherit;
116 font-size: 0.8125rem;
117 color: var(--g1t-muted);
118 cursor: pointer;
119 transition: color 0.15s, background 0.15s;
120 }
121 .g1t-copy-main {
122 border-radius: 0.5rem 0 0 0.5rem;
123 }
124 .g1t-copy-more summary {
125 list-style: none;
126 border-left: 1px solid var(--g1t-line);
127 border-radius: 0 0.5rem 0.5rem 0;
128 padding-inline: 0.45rem;
129 }
130 .g1t-copy-more summary::-webkit-details-marker {
131 display: none;
132 }
133 .g1t-copy-main:hover,
134 .g1t-copy-more summary:hover {
135 background: var(--g1t-surface);
136 color: var(--g1t-fg);
137 }
138 .g1t-copy-menu {
139 position: absolute;
140 top: calc(100% + 0.4rem);
141 right: 0;
142 z-index: 10;
143 width: 15rem;
144 border-radius: 0.75rem;
145 padding: 0.35rem;
146 background: var(--g1t-raised);
147 box-shadow:
148 inset 0 0 0 1px var(--g1t-line-strong),
149 0 12px 32px rgb(0 0 0 / 0.45);
150 }
151 .g1t-copy-menu a {
152 display: block;
153 border-radius: 0.5rem;
154 padding: 0.45rem 0.6rem;
155 text-decoration: none;
156 }
157 .g1t-copy-menu a:hover {
158 background: var(--g1t-surface);
159 }
160 .g1t-copy-menu strong {
161 display: block;
162 font-size: 0.8125rem;
163 font-weight: 500;
164 color: var(--g1t-fg);
165 }
166 .g1t-copy-menu span {
167 display: block;
168 font-size: 0.75rem;
169 color: var(--g1t-faint);
170 }
171 @media (max-width: 40rem) {
172 .g1t-copy {
173 display: none;
174 }
175 }
176</style>