🌳
pt0/peatsite/GalleryIndexAI.jsx
1import React from 'react'
9const styles = baseStyles + markdownStyles + galleryStyles
11const EpTypeLabel = ({epType, epTypeHref}) => {
12 if (!epType) return null
13 const label = `# ${epType}`
14 return epTypeHref
15 ? <a href={epTypeHref} className="ep-type" onClick={e => e.stopPropagation()}>{label}</a>
16 : <span className="ep-type">{label}</span>
19const CardHeader = ({command, epType, epTypeHref, helpHref, locStats, cardIdx}) => (
20 command && (
21 <div className="card-header">
22 <div className="card-command">
23 <span>
24 {helpHref ? <a href={helpHref} className="command-link">{command}</a> : <span>{command}</span>}
25 {locStats && <span className="app-loc" data-loc-idx={cardIdx}> # {locStats.appLocStr}</span>}
26 </span>
27 <EpTypeLabel epType={epType} epTypeHref={epTypeHref} />
28 </div>
29 {locStats?.epTypeLocStr && <div className="eptype-loc" data-loc-idx={cardIdx} data-loc-type="eptype">{locStats.epTypeLocStr}</div>}
30 </div>
31 )
34const PkgStats = ({locStats}) => {
35 if (!locStats?.pkgsLine && !locStats?.dpkgsLine) return null
36 return (
37 <div className="pkg-stats">
38 {locStats.pkgsLine && <div className="pkgs-line">{locStats.pkgsLine}</div>}
39 {locStats.dpkgsLine && <div className="dpkgs-line">{locStats.dpkgsLine}</div>}
40 </div>
41 )
44const Description = ({description}) => (
45 description && <div className="card-description" dangerouslySetInnerHTML={{__html: description}} />
48const TileCard = ({cardId, path, href, command, epType, epTypeHref, description, helpHref, locStats, cardIdx}) => (
49 <div id={cardId} className="card tile-card">
50 <CardHeader command={command} epType={epType} epTypeHref={epTypeHref} helpHref={helpHref} locStats={locStats} cardIdx={cardIdx} />
51 <div className="card-body">
52 <div id={`tile-${path}`} className="tile-container" data-href={href}>
53 <button className="music-toggle" id="music-toggle">&#9835;</button>
54 </div>
55 <Description description={description} />
56 </div>
57 <PkgStats locStats={locStats} />
58 </div>
61const ColorCard = ({cardId, href, bgStyle, command, epType, epTypeHref, description, helpHref, locStats, cardIdx}) => (
62 <div id={cardId} className="card">
63 <CardHeader command={command} epType={epType} epTypeHref={epTypeHref} helpHref={helpHref} locStats={locStats} cardIdx={cardIdx} />
64 <div className="card-body">
65 <a href={href}><div className="color-tile" style={{background: bgStyle}} /></a>
66 <Description description={description} />
67 </div>
68 <PkgStats locStats={locStats} />
69 </div>
72const LeanCard = ({cardId, command, epType, epTypeHref, description, helpHref, locStats, cardIdx}) => (
73 <div id={cardId} className="card">
74 <CardHeader command={command} epType={epType} epTypeHref={epTypeHref} helpHref={helpHref} locStats={locStats} cardIdx={cardIdx} />
75 <div className="card-body-inline">
76 <Description description={description} />
77 <PkgStats locStats={locStats} />
78 </div>
79 </div>
82const ImageCard = ({cardId, href, imageUrl, imageAlign, caption, rounds, command, epType, epTypeHref, description, helpHref, locStats, cardIdx}) => (
83 <div id={cardId} className="card">
84 <CardHeader command={command} epType={epType} epTypeHref={epTypeHref} helpHref={helpHref} locStats={locStats} cardIdx={cardIdx} />
85 <div className="card-body">
86 <div className="image-wrapper">
87 {rounds ? rounds.map((r, i) => (
88 <div key={i} className="round-item">
89 {r.label && <div className="image-caption">{r.label}</div>}
90 {r.image && <a href={href}><img src={r.image} className="image-tile" alt="" /></a>}
91 </div>
92 )) : <>
93 {caption && <div className="image-caption">{caption}</div>}
94 <a href={href}><img src={imageUrl} className="image-tile" alt="" style={imageAlign ? {objectPosition: imageAlign} : undefined} /></a>
95 </>}
96 </div>
97 <Description description={description} />
98 </div>
99 <PkgStats locStats={locStats} />
100 </div>
103const MarkdownSection = ({html}) => html ? (
104 <div className="markdown-content" dangerouslySetInnerHTML={{__html: html}} />
105) : null
107const tileScriptContent = `
108import { createTile } from './threerulecycle/tileAI.js'
109const container = document.getElementById('tile-threerulecycle')
110const musicBtn = document.getElementById('music-toggle')
111if (container) {
112 const tile = await createTile(container, {
113 size: 180,
114 audioBasePath: 'threerulecycle/',
115 precomputedUrl: 'threerulecycle/precomputed-states.json',
116 onActivate: () => { musicBtn.classList.add('playing') }
117 })
118 container.addEventListener('click', (e) => {
119 if (e.target.closest('.music-toggle')) return
120 if (e.metaKey || e.ctrlKey) {
121 e.stopPropagation()
122 window.open(container.dataset.href, '_blank')
123 return
124 }
125 if (tile.isPlaying()) {
126 e.stopPropagation()
127 window.location.href = container.dataset.href
128 }
129 }, true)
130 musicBtn.addEventListener('click', () => {
131 tile.toggle()
132 musicBtn.classList.toggle('playing', tile.isPlaying())
133 })
137const locModalScriptContent = `
138const {locDataA = [], epNamesA = [], epPathsA = [], epScopeLegendA = [], srcBaseUrl: srcBase = '', sectionLocH = {}} = window.__locModalData || {}
139const clrHexH = {cyan: '#5fd7d7', magenta: '#d787d7', green: '#5fd7af', gray: '#888', yellow: '#d7af5f'}
140const mkLegendHtml = (epType) => {
141 const scopes = epType && epType !== 'eptNextjsApp' ? epScopeLegendA.filter(s => s.scope !== 'pubfejs') : epScopeLegendA
142 return scopes.map(s => \`<span style="color:\${clrHexH[s.clr]}">\${s.clr}</span>=\${s.scope}\`).join(' ')
144const fmtLoc = (n) => n >= 1000 ? \`# \${(n / 1000).toFixed(1)}k loc\` : \`# \${n}loc\`
145const sumLoc = (filesA) => filesA.reduce((s, f) => s + f.loc, 0)
146const sectionHdr = (label, filesA) => \`<div class="loc-section-header"><span>\${label}</span><span class="loc-section-total"># total \${fmtLoc(sumLoc(filesA)).slice(2)}</span></div>\`
147const renderFile = (f) => \`<div class="loc-modal-file clr-\${f.clr || 'gray'}"><a href="\${srcBase}\${f.path}">\${f.path}</a><span class="loc-cnt">\${fmtLoc(f.loc)}</span></div>\`
148const renderGroupedFiles = (filesA, thisLabel, namesA) => {
149 namesA = namesA || epNamesA
150 const thisF = filesA.filter(f => f.scope === 'this')
151 const someF = filesA.filter(f => f.scope === 'some')
152 const allF = filesA.filter(f => f.scope === 'all')
153 let html = ''
154 if (thisF.length) {
155 html += sectionHdr(thisLabel || 'ep-specific', thisF) + thisF.map(renderFile).join('')
156 }
157 if (someF.length) {
158 const groups = new Map()
159 for (const f of someF) {
160 const key = (f.globalEpIndices || f.epIndices).join(',')
161 if (!groups.has(key)) groups.set(key, [])
162 groups.get(key).push(f)
163 }
164 const sorted = [...groups.entries()].sort((a, b) => {
165 const aIdx = a[1][0].globalEpIndices || a[1][0].epIndices
166 const bIdx = b[1][0].globalEpIndices || b[1][0].epIndices
167 return aIdx.length - bIdx.length
168 })
169 for (const [, files] of sorted) {
170 const indices = files[0].globalEpIndices || files[0].epIndices
171 const names = indices.map(i => namesA[i]).filter(Boolean)
172 const label = names.length <= 5 ? names.join(', ') : \`shared by \${names.length} eps\`
173 html += sectionHdr(label, files) + files.map(renderFile).join('')
174 }
175 }
176 if (allF.length) html += sectionHdr('used-basically-everywhere', allF) + allF.map(renderFile).join('')
177 return html
179document.body.addEventListener('click', (e) => {
180 const locEl = e.target.closest('[data-loc-key], .app-loc, .eptype-loc')
181 if (!locEl) return
182 e.preventDefault()
183 e.stopPropagation()
184 let filesA, thisLabel, namesA, epType
185 const sectionKey = locEl.dataset.locKey
186 if (sectionKey) {
187 const section = sectionLocH[sectionKey]
188 if (!section) return
189 filesA = section.filesA
190 namesA = section.namesA
191 epType = '__section__'
192 } else if (locEl.dataset.locIdx) {
193 const idx = parseInt(locEl.dataset.locIdx, 10)
194 const isEptype = locEl.dataset.locType === 'eptype'
195 const data = locDataA[idx]
196 filesA = isEptype ? data?.epTypeFilesA : data?.appFilesA
197 thisLabel = isEptype
198 ? \`\${data.epTypeName} (\${data.epTypeDefPath})\`
199 : \`\${epNamesA[idx]} (\${epPathsA[idx]})\`
200 epType = data?.epTypeName
201 } else return
202 if (!filesA?.length) return
203 document.querySelector('.loc-modal-overlay')?.remove()
204 document.body.classList.add('modal-open')
205 const presentClrs = new Set(filesA.map(f => f.clr))
206 const legendHtml = epType === '__section__'
207 ? epScopeLegendA.filter(s => presentClrs.has(s.clr)).map(s => \`<span style="color:\${clrHexH[s.clr]}">\${s.clr}</span>=\${s.scope}\`).join(' ')
208 : mkLegendHtml(epType)
209 const epFileObjs = epType === '__section__' ? (sectionLocH[sectionKey]?.epFileObjs || []) : []
210 const epListHtml = epFileObjs.length ? sectionHdr('entrypoints', epFileObjs) + epFileObjs.map(renderFile).join('') : ''
211 const overlay = document.createElement('div')
212 overlay.className = 'loc-modal-overlay'
213 overlay.innerHTML = \`
214 <div class="loc-modal">
215 <div class="loc-modal-header">
216 <span class="loc-legend">\${legendHtml}</span>
217 <button class="loc-modal-close">&times;</button>
218 </div>
219 <div class="loc-modal-body">\${epListHtml}\${renderGroupedFiles(filesA, thisLabel, namesA)}</div>
220 </div>
221 \`
222 document.body.appendChild(overlay)
223 const esc = (e) => { if (e.key === 'Escape') close() }
224 const close = () => { overlay.remove(); document.body.classList.remove('modal-open'); document.removeEventListener('keydown', esc) }
225 overlay.querySelector('.loc-modal-close').onclick = close
226 overlay.onclick = (e) => { if (e.target === overlay) close() }
227 document.addEventListener('keydown', esc)
228})
231export const GalleryIndex = ({cards, hasTile, markdownBeforeHtml, markdownAfterHtml, srcBaseUrl, totalLocStr, allAppFilesA, globalEpNamesA, epNamesA, epPathsA, epScopeLegendA, sectionLocH}) => {
232 const locDataA = cards.map(c => c.locStats ? {appFilesA: c.locStats.appFilesA, epTypeFilesA: c.locStats.epTypeFilesA, epTypeName: c.locStats.epTypeName, epTypeDefPath: c.locStats.epTypeDefPath} : null)
233 return (
234 <html>
235 <head>
236 <meta charSet="UTF-8" />
237 <meta name="viewport" content={viewportContent} />
238 <title>peatSymposium</title>
239 <style dangerouslySetInnerHTML={{__html: styles}} />
240 </head>
241 <body>
242 <div dangerouslySetInnerHTML={{__html: hostBannerHtml}} />
243 <a href="/" className="home-link">🌳</a>
244 <div className="page-content">
245 <MarkdownSection html={markdownBeforeHtml} />
246 <h2>demos / made w peat:</h2>
247 <div className="grid">
248 {(() => {
249 const renderCard = (card, i) =>
250 card.noTile ? <LeanCard key={i} {...card} cardIdx={i} />
251 : card.isTile ? <TileCard key={i} {...card} cardIdx={i} />
252 : card.isImage ? <ImageCard key={i} {...card} cardIdx={i} />
253 : <ColorCard key={i} {...card} cardIdx={i} />
254 const out = []
255 for (let i = 0; i < cards.length; i++) {
256 const grp = cards[i].collapsedGroup
257 if (grp) {
258 const start = i
259 while (i < cards.length && cards[i].collapsedGroup === grp) i++
260 const grpCards = []
261 for (let j = start; j < i; j++) grpCards.push({card: cards[j], idx: j})
262 out.push(
263 <details key={`grp-${start}`} className="gallery-group">
264 <summary>{grp} <span className="gallery-group-cnt">({grpCards.length})</span></summary>
265 <div className="gallery-group-body">
266 {grpCards.map(({card, idx}) => renderCard(card, idx))}
267 </div>
268 </details>
269 )
270 i--
271 } else {
272 out.push(renderCard(cards[i], i))
273 }
274 }
275 return out
276 })()}
277 </div>
278 <MarkdownSection html={markdownAfterHtml} />
279 </div>
280 <script dangerouslySetInnerHTML={{__html: `window.__locModalData = ${JSON.stringify({locDataA, epNamesA, epPathsA, epScopeLegendA, srcBaseUrl, sectionLocH})}`}} />
281 <script dangerouslySetInnerHTML={{__html: locModalScriptContent}} />
282 {hasTile && <script type="module" dangerouslySetInnerHTML={{__html: tileScriptContent}} />}
283 <script type="module" src="./cheetaNekoAI.js" />
284 <script type="module" dangerouslySetInnerHTML={{__html: beHereNowShadowScript}} />
285 <script dangerouslySetInnerHTML={{__html: hamsaFlutterScript}} />
286 </body>
287 </html>
288 )