${o(e.title)}
-${o(e.engine)} \xB7 ${o(e.license)}
+${s(e.title)}
+${s(e.engine)} \xB7 ${s(e.license)}
${o(e.description||"No description yet.")}
+${s(e.description||"No description yet.")}
diff --git a/_config.yml b/_config.yml index 44973e0..ecb55ec 100644 --- a/_config.yml +++ b/_config.yml @@ -1,9 +1,9 @@ title: "GeneratedArt" description: >- - Community-run generative art platform and NFT marketplace for code-based - generative art (p5.js, three.js, WebGL, GLSL). A spiritual successor to - fxhash and Art Blocks, with a physical-digital bridge through the Geneva - gallery. + Community-run platform for AI Art Creative Coding and NFT marketplace — + train custom AI art models with disclosed provenance, or hand-code p5.js, + three.js, WebGL, and GLSL sketches. A spiritual successor to fxhash and + Art Blocks, with a physical-digital bridge through the Geneva gallery. url: "https://generatedart.com" baseurl: "" permalink: "/:title" diff --git a/_data/navigation.yml b/_data/navigation.yml index 002b705..88fc91a 100644 --- a/_data/navigation.yml +++ b/_data/navigation.yml @@ -1,426 +1,25 @@ main_menu: - - text: "Home" - url: "/" - menu_type: "megamenu" - submenu: - - text: "Digital Startup" - url: "/" - - text: "Saas Landing" - url: "/home-pages/index-2" - - text: "SEO Agency" - url: "/home-pages/index-3" - - text: "Modern Business" - url: "/home-pages/index-4" - - text: "Design Agency" - url: "/home-pages/index-5" - - text: "App Presentation" - url: "/home-pages/index-6" - - text: "Creative Landing" - url: "/home-pages/index-7" - - text: "Marketing Startup" - url: "/home-pages/index-8" - - text: "Digital Agency" - url: "/home-pages/index-9" - - text: "Creative Business" - url: "/home-pages/index-10" - - text: "Software Landing" - url: "/home-pages/index-11" - - text: "App Landing" - url: "/home-pages/index-12" - - text: "Creative Agency" - url: "/home-pages/index-13" - - text: "Wedding Photography" - url: "/home-pages/index-14" - - text: "Food Photography" - url: "/home-pages/index-15" - - text: "Creative Onepage" - url: "/home-pages/index-16" - - text: "Marketing Company" - url: "/home-pages/index-17" - - text: "Startup Business" - url: "/home-pages/index-18" - - text: "Creative Resume" - url: "/home-pages/index-19" - - text: "Digital Studio" - url: "/home-pages/index-20" - - text: "Travel Blog" - url: "/home-pages/index-21" - - text: "Minimal Shop" - url: "/home-pages/index-22" - - text: "Minimal Portfolio" - url: "/home-pages/index-23" - - text: "Coming Soon" - url: "/home-pages/index-24" - - text: "Personal Creative" - url: "/home-pages/index-25" - - text: "Minimal Photography" - url: "/home-pages/index-26" - - text: "Maintenance" - url: "/home-pages/index-27" - - text: "Features" - url: "#" - menu_type: "dropdown" - level_two: - - sub_menu_text: "Navigation" - sub_menu: - - text: "Classic Navigation" - url: "/features/navigation1" - - text: "Classic Light Navigation" - url: "/features/navigation2" - - text: "Classic Dark Navigation" - url: "/features/navigation3" - - text: "Topbar Navigation" - url: "/features/navigation4" - - text: "Wide Navigation" - url: "/features/navigation5" - - text: "Extended Navigation" - url: "/features/navigation6" - - text: "Fancy Light Navigation" - url: "/features/navigation7" - - text: "Fancy Dark Navigation" - url: "/features/navigation8" - - text: "Centered Navigation" - url: "/features/navigation9" - - text: "Shop Navigation" - url: "/features/navigation10" - - text: "Classic Navigation for Light Background" - url: "/features/navigation11" - - sub_menu_text: "Page Header" - sub_menu: - - text: "Header with Image" - url: "/features/header1" - - text: "Header with Breadcrumb" - url: "/features/header2" - - text: "Header with no Image" - url: "/features/header3" - - text: "Header with Gray Wrapper" - url: "/features/header4" - - text: "Header with Dark Wrapper" - url: "/features/header5" - - text: "Header with Colored Background" - url: "/features/header6" - - text: "Header with Gradient" - url: "/features/header7" - - text: "Header with Video Wrapper" - url: "/features/header8" - - text: "Header with Section Wrapper" - url: "/features/header9" - - text: "Header with Page Title" - url: "/features/header10" - - sub_menu_text: "Footer" - sub_menu: - - text: "Multi Column Footer V1" - url: "/features/footer1" - - text: "Centered Footer" - url: "/features/footer2" - - text: "Minimal Footer" - url: "/features/footer3" - - text: "Blog Footer" - url: "/features/footer4" - - text: "Multi Column Footer V2" - url: "/features/footer5" - - text: "Footer with Overflow" - url: "/features/footer6" - - text: "Footer with Background Image" - url: "/features/footer7" - - text: "Blog Footer with Background" - url: "/features/footer8" - level_one: - - text: "Modal" - url: "/features/modal" - - text: "FAQ" - url: "/features/faq" - - text: "Page Loading " - url: "/features/loading" - - text: "Page Scrolling" - url: "/features/scroll" - - - text: "Pages" - url: "#" - menu_type: "dropdown" - level_two: - - sub_menu_text: "Services" - sub_menu: - - text: "Service 01" - url: "/services/index" + - text: "Models" + url: "/models/" + menu_type: "simple" - - text: "Service 02" - url: "/services/index-2" + - text: "Explore" + url: "/explore/" + menu_type: "simple" - - text: "Service 03" - url: "/services/index-3" + - text: "Galleries" + url: "/galleries/" + menu_type: "simple" - - text: "Service 04" - url: "/services/index-4" + - text: "Briefs" + url: "/briefs/" + menu_type: "simple" - - text: "Service 05" - url: "/services/index-5" + - text: "About" + url: "/about/" + menu_type: "simple" - - sub_menu_text: "About Us" - sub_menu: - - text: "About 01" - url: "/about/about" - - - text: "About 02" - url: "/about/about-2" - - - text: "About 03" - url: "/about/about-3" - - - text: "About 04" - url: "/about/about-4" - - - sub_menu_text: "Shop" - sub_menu: - - text: "Shop 01" - url: "/shop/index" - - - text: "Shop 02" - url: "/shop/index-2" - - - text: "Product Page" - url: "/shop/brand-box" - - - text: "Featurd Product Page" - url: "/shop/featured/featured_product1" - - - text: "Cart Page" - url: "/shop/cart" - - - text: "Checkout Page" - url: "/shop/checkout" - - - sub_menu_text: "Contact" - sub_menu: - - text: "Contact 01" - url: "/contact/contact-1" - - - text: "Contact 02" - url: "/contact/contact-2" - - - text: "Contact 03" - url: "/contact/contact-3" - - - text: "Contact 04" - url: "/contact/contact-4" - - - - - text: "Elements" - url: "#" - menu_type: "megamenu" - submenu: - - - text: "Accordion" - url: "/elements/accordion" - - - text: "Dividers" - url: "/elements/dividers" - - - text: "Lightbox" - url: "/elements/lightbox" - - - text: "Tabs" - url: "/elements/tabs" - - - text: "Alerts" - url: "/elements/alerts" - - - text: "Dropcap" - url: "/elements/dropcap" - - - text: "Lists" - url: "/elements/lists" - - - text: "Team" - url: "/elements/team" - - - text: "Animated Text" - url: "/elements/animated_text" - - - text: "Forms" - url: "/elements/forms" - - - text: "Media Player" - url: "/elements/media_player" - - - text: "Testimonials" - url: "/elements/testimonials" - - - text: "Blockquote" - url: "/elements/blockquote" - - - text: "Buttons & Badges" - url: "/elements/buttons_badges" - - - text: "Call to Action" - url: "/elements/call_to_action" - - - text: "Carousel" - url: "/elements/carousel" - - - text: "Clients" - url: "/elements/clients" - - - text: "Countdown" - url: "/elements/countdown" - - - text: "Counter" - url: "/elements/counter" - - - text: "Icons - Concept" - url: "/elements/icons_concept" - - - text: "Icons - Jam" - url: "/elements/icons_jam" - - - text: "Icons - Icofont" - url: "/elements/icons_icofont" - - - text: "Icons - Lineal" - url: "/elements/icons_lineal" - - - text: "Icon Box" - url: "/elements/icon_box" - - - text: "Image Hover" - url: "/elements/image_hover" - - - text: "Image Mask" - url: "/elements/icons_image_mask" - - - text: "Modal" - url: "/elements/modal" - - - text: "Pricing Tables" - url: "/elements/pricing" - - - text: "Process Steps" - url: "/elements/process_steps" - - - text: "Progress Bar" - url: "/elements/progress_bar" - - - text: "Scroll Animations" - url: "/elements/scroll_animations" - - - text: "Social Icons" - url: "/elements/social_icons" - - - text: "Tables" - url: "/elements/tables" - - - text: "Titles" - url: "/elements/titles" - - - text: "Toggle" - url: "/elements/toggle" - - - text: "Tooltip & Popover" - url: "/elements/tooltip_popover" - - - text: "Typography" - url: "/elements/typography" - - - text: "Video Popup" - url: "/elements/video_popup" - - - text: "Waves" - url: "/elements/waves" - - - text: "Wrappers" - url: "/elements/wrappers" - - - text: "Portfolio" - url: "#" - level_one: - - text: "Grid Portfolio" - url: "/portfolios/grid-portfolio" - - - text: "Grid Portfolio With Details" - url: "/portfolios/grid-portfolio-details" - - - text: "Masonry Portfolio" - url: "/portfolios/masonry-portfolio" - - - text: "Masonry Portfolio With Details" - url: "/portfolios/masonry-portfolio-details" - - - text: "Mosaic Portfolio" - url: "/portfolios/mosaic-portfolio" - - - text: "Full Screen Grid Portfolio" - url: "/portfolios/fullscreen-grid" - - - text: "Portfolio With Item Details" - url: "/portfolios/portfolio-item-details" - - - text: "Box Portfolio With Item Details" - url: "/portfolios/box-item-details" - level_two: - - sub_menu_text: "Portfolio Posts" - sub_menu: - - text: "Post with multiple images" - url: "/portfolio/portfolio-post-multi-images" - - text: "Post with slider" - url: "/portfolio/portfolio-post-slider" - - text: "Post with masonry gallery" - url: "/portfolio/portfolio-post-masonry" - - text: "Post with carousel" - url: "/portfolio/portfolio-post-carousel" - - text: "Post with columns" - url: "/portfolio/portfolio-post-columns" - - text: "Post with video" - url: "/portfolio/portfolio-post-video" - - - text: "Blog" - url: "#" - menu_type: "dropdown" - level_one: - - text: "Classic Grid" - url: "/blogs/classic-grid" - - - text: "Classic Grid With Sidebar" - url: "/blogs/classic-grid-sidebar" - - - text: "Box Grid Blog" - url: "/blogs/boxed-grid" - - - text: "Box Grid Blog With Sidebar" - url: "/blogs/boxed-grid-sidebar" - - - text: "Classic Blog" - url: "/blogs/classic" - - - text: "Classic Blog with Sidebar" - url: "/blogs/classic-sidebar" - - - text: "Boxed Classic Blog" - url: "/blogs/boxed-classic" - - - text: "Boxed Classic Blog With Sidebar" - url: "/blogs/boxed-classic-sidebar" - level_two: - - sub_menu_text: "Blog Posts" - sub_menu: - - text: "Post with sidebar" - url: "/posts/simple-post-with-sidebar" - - text: "Post with no sidebar" - url: "/posts/simple-post-with-no-sidebar" - - text: "Box Post with no sidebar" - url: "/posts/post-boxed" - - text: "Box Post with sidebar" - url: "/posts/post-boxed-sidebar" - - text: "Post with code highlight" - url: "/posts/post-with-code-highlight" - - text: "Post with video" - url: "/posts/post-with-video" - - text: "Post with image" - url: "/posts/post-with-image" - - text: "Post with carousel" - url: "/posts/post-with-carousel" - - text: "Post with Multiple Carousel" - url: "/posts/multi-carousel-post" - - text: "Post with gallery" - url: "/posts/post-with-gallery" + - text: "Dashboard" + url: "/dashboard/projects/" + menu_type: "simple" diff --git a/about/index.html b/about/index.html index 3c19adb..5a9eca4 100644 --- a/about/index.html +++ b/about/index.html @@ -1,12 +1,14 @@ --- layout: default title: "About GeneratedArt" -sub_title: "An open marketplace for code-based generative art, owned by the people who make it." +sub_title: "Where AI Creative Coders build the models that make the art — owned by the people who train them." header_image: "/assets/images/art/bg1.webp" description: >- - GeneratedArt is a community-run platform for code-based generative art - (p5.js, three.js, WebGL, GLSL). The repo is the artwork. The artist is the - owner. The platform is shared infrastructure. + GeneratedArt is a community-run platform for AI Art Creative Coding — + training custom AI art models, and hand-coded p5.js, three.js, WebGL, and + GLSL sketches. The code is the artwork, whether it's a sketch or the + model that produced it. The artist is the owner. The platform is shared + infrastructure. --- {% include layouts/nav/nav-2.html default_logo="black" drop_search=true %} {% include layouts/header/page-title9.html %} @@ -17,18 +19,23 @@
- GeneratedArt is a marketplace and home for code-based generative art. - Everything that ships here is a sketch — a JavaScript or shader program that - renders a unique output for every collector who mints it. The repo is the - artwork. The artist owns the contract. The platform is shared - infrastructure, not a landlord. + GeneratedArt is a marketplace and home for AI Art Creative Coding — + the modern practice where an artist's creative act is training the model + that makes the art, in the same lineage as dataset-trained generative + systems like Refik Anadol's work, without claiming any affiliation. Hand-coded + p5.js, three.js, WebGL, and GLSL sketches remain a fully respected second + path. Either way, code is the medium: a model's architecture, training + config, and inference pipeline are code, exactly as a sketch is. The artist + owns the contract. The platform is shared infrastructure, not a landlord.
We started this because the existing options each gave up something we weren't willing to give up. Centralised marketplaces hold the keys, dictate - royalties, and de-platform without warning. Pure on-chain venues are - beautiful but inaccessible to most artists. The middle ground — open, - curatable, transparent, with a real physical-world endpoint — was empty. + royalties, and de-platform without warning. Prompt-only AI art tools rent + you an aesthetic with no reproducibility and no way to say what a model was + actually trained on. Pure on-chain venues are beautiful but inaccessible to + most artists. The middle ground — open, curatable, transparent, provenance + disclosed by construction, with a real physical-world endpoint — was empty.
- A sketch is its source. Mints reference an immutable commit, not a - JPEG. If the platform disappears, the work still runs — pull the repo, - open the file, render in any browser. + A sketch is its source; a trained model is its base model, training + method, and dataset. Mints reference an immutable commit or a locked + CID, not a JPEG. If the platform disappears, the work still runs — + pull the repo or the weights, render in any browser.
+ Every AI art model publishes its base model, training method, and a + dataset note in the creator's own words — publicly, alongside the + model — the same trust mechanic frozen-CID provenance already gives + a code sketch. +
+@@ -87,7 +104,8 @@
Static front-end on GitHub Pages. Hono on Cloudflare Workers for the - API. D1 + KV + R2 + Queues at the edge. ERC-721 contracts on Base. + API. D1 + KV + R2 + Queues at the edge. Claude for code generation, + fal.ai for custom-trained models. ERC-721 contracts on Base. Wallets via WalletConnect. Pinning via IPFS. No central database, no auth provider, no platform that can pull the rug.
diff --git a/assets/js/ga-dashboard.js b/assets/js/ga-dashboard.js index 15a5c91..11083d8 100644 --- a/assets/js/ga-dashboard.js +++ b/assets/js/ga-dashboard.js @@ -1,19 +1,19 @@ -var u=new Set,f=10,g=!1,c=null;function v(e){g||(g=!0,c=e,window.addEventListener("error",t=>{let r=t.error&&t.error.message||t.message||"unknown_error",n=t.error&&t.error.stack||`${t.filename??""}:${t.lineno??0}:${t.colno??0}`;h(r,n)}),window.addEventListener("unhandledrejection",t=>{let r=t.reason,n=r instanceof Error?r.message:typeof r=="string"?r:"unhandled_rejection",a=r instanceof Error?r.stack??"":"";h(n,a)}))}function E(e){try{let t=new URL(e,location.origin);return`${t.origin}${t.pathname}`.slice(0,200)}catch{return e.split("?")[0].split("#")[0].slice(0,200)}}async function h(e,t){if(!c||u.size>=f)return;let r=`${e}::${t.slice(0,80)}`;if(!u.has(r)){u.add(r);try{await fetch(`${c.apiBase}/v1/internal/client-error`,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({message:String(e).slice(0,500),stack:String(t).slice(0,4e3),page:c.page,url:E(location.href),ts:Date.now()}),keepalive:!0})}catch{}}}var y={draft:"Draft",published:"Published",minted:"Minted",archived:"Archived"};function j(e){return`${y[e]||e}`}function o(e){return e.replace(/[&<>"']/g,t=>{switch(t){case"&":return"&";case"<":return"<";case">":return">";case'"':return""";default:return"'"}})}function w(e){let t=e.status==="archived",r=e.repo_url?`View repo \u2192`:"",n=t?"":``;return` +var u=new Set,f=10,g=!1,d=null;function v(e){g||(g=!0,d=e,window.addEventListener("error",t=>{let n=t.error&&t.error.message||t.message||"unknown_error",r=t.error&&t.error.stack||`${t.filename??""}:${t.lineno??0}:${t.colno??0}`;h(n,r)}),window.addEventListener("unhandledrejection",t=>{let n=t.reason,r=n instanceof Error?n.message:typeof n=="string"?n:"unhandled_rejection",a=n instanceof Error?n.stack??"":"";h(r,a)}))}function E(e){try{let t=new URL(e,location.origin);return`${t.origin}${t.pathname}`.slice(0,200)}catch{return e.split("?")[0].split("#")[0].slice(0,200)}}async function h(e,t){if(!d||u.size>=f)return;let n=`${e}::${t.slice(0,80)}`;if(!u.has(n)){u.add(n);try{await fetch(`${d.apiBase}/v1/internal/client-error`,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({message:String(e).slice(0,500),stack:String(t).slice(0,4e3),page:d.page,url:E(location.href),ts:Date.now()}),keepalive:!0})}catch{}}}var y={draft:"Draft",published:"Published",minted:"Minted",archived:"Archived"};function w(e){return`${y[e]||e}`}function s(e){return e.replace(/[&<>"']/g,t=>{switch(t){case"&":return"&";case"<":return"<";case">":return">";case'"':return""";default:return"'"}})}function C(e){let t=e.status==="archived",n=e.repo_url?`View repo \u2192`:"",r=t?"":``;return`${o(e.engine)} \xB7 ${o(e.license)}
+${s(e.engine)} \xB7 ${s(e.license)}
${o(e.description||"No description yet.")}
+${s(e.description||"No description yet.")}
Click "New project" to create your first generative-art repo.
@@ -66,5 +66,5 @@ var u=new Set,f=10,g=!1,c=null;function v(e){g||(g=!0,c=e,window.addEventListene${escapeHtml(p.engine)} \u00B7 ${escapeHtml(p.license)}
\n${escapeHtml(p.description || \"No description yet.\")}
\n \nClick \"New project\" to create your first generative-art repo.
\n${escapeHtml(p.engine)} \u00B7 ${escapeHtml(p.license)}
\n${escapeHtml(p.description || \"No description yet.\")}
\n \nClick \"New project\" to create your first generative-art repo.
\n${n(s.description||"No description yet.")}
+ +No datasets yet. Your source material stays private \u2014 only the models you publish are public.
Bad URL \u2014 dataset pages look like /datasets/detail/?slug=your-dataset.
';return}try{let a=await l(`${t.apiBase}/v1/datasets/${encodeURIComponent(this.slug)}`);this.dataset=a.dataset}catch(a){if((a.message.startsWith("404")?404:0)===404){t.onUnauthenticated?.();return}t.rootEl.innerHTML=`Failed to load dataset: ${n(String(a))}
`;return}this.renderHeader(),this.populateBaseModelOptions(),await this.loadItems(),await this.loadTrainingJobs(),this.attachEvents()}renderHeader(){let t=this.dataset,e=this.cfg.rootEl;e.querySelector("#ga-dataset-title").textContent=t.title,e.querySelector("#ga-dataset-desc").textContent=t.description||"No curatorial statement yet.",e.querySelector("#ga-dataset-meta").textContent=`${t.item_count} item${t.item_count===1?"":"s"} \xB7 ${E[t.rights_declaration]||t.rights_declaration} \xB7 ${t.visibility}`;let a=e.querySelector("#ga-train-submit");a&&(a.disabled=t.item_count<5);let i=e.querySelector("#ga-train-min-hint");i&&i.classList.toggle("d-none",t.item_count>=5)}populateBaseModelOptions(){let t=this.cfg.rootEl.querySelector("#ga-train-base-model");t&&(t.innerHTML=Object.entries(g).map(([e,a])=>``).join(""))}async loadItems(){let t=this.cfg.rootEl.querySelector("#ga-dataset-items");if(t)try{let e=await l(`${this.cfg.apiBase}/v1/datasets/${encodeURIComponent(this.slug)}/items?limit=100`);t.innerHTML=e.items.length?e.items.map(a=>this.renderItem(a)).join(""):'No items yet \u2014 drop images or video below.
'}catch(e){t.innerHTML=`${n(t.flag_reason||"flagged")}
`:""} + +No training runs yet.
';let a=e.jobs.some(i=>i.status==="queued"||i.status==="training");a&&!this.pollTimer?this.pollTimer=setInterval(()=>void this.loadTrainingJobs(),4e3):!a&&this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null)}catch(e){t.innerHTML=`${n(a)} \xB7 ${i}
+${escapeHtml(d.description || \"No description yet.\")}
\n \nNo datasets yet. Your source material stays private \u2014 only the models you publish are public.
Bad URL \u2014 dataset pages look like /datasets/detail/?slug=your-dataset.
`;\n return;\n }\n try {\n const data = await fetchJson<{ dataset: Dataset }>(`${cfg.apiBase}/v1/datasets/${encodeURIComponent(this.slug)}`);\n this.dataset = data.dataset;\n } catch (err) {\n const status = (err as Error).message.startsWith(\"404\") ? 404 : 0;\n if (status === 404) {\n cfg.onUnauthenticated?.();\n return;\n }\n cfg.rootEl.innerHTML = `Failed to load dataset: ${escapeHtml(String(err))}
`;\n return;\n }\n\n this.renderHeader();\n this.populateBaseModelOptions();\n await this.loadItems();\n await this.loadTrainingJobs();\n this.attachEvents();\n }\n\n private renderHeader(): void {\n const d = this.dataset!;\n const root = this.cfg.rootEl;\n root.querySelectorNo items yet \u2014 drop images or video below.
`;\n } catch (err) {\n grid.innerHTML = `${escapeHtml(i.flag_reason || \"flagged\")}
` : \"\"}\n \nNo training runs yet.
`;\n // Poll while any job is still in flight \u2014 training is genuinely\n // async at the provider (minutes to hours in production; instant\n // under TRAINING_MOCK).\n const inFlight = data.jobs.some((j) => j.status === \"queued\" || j.status === \"training\");\n if (inFlight && !this.pollTimer) {\n this.pollTimer = setInterval(() => void this.loadTrainingJobs(), 4000);\n } else if (!inFlight && this.pollTimer) {\n clearInterval(this.pollTimer);\n this.pollTimer = null;\n }\n } catch (err) {\n el.innerHTML = `${escapeHtml(when)} \u00B7 ${statusLine}
\n${s(n.description||"No description yet.")}
+${s(t)} \xB7 ${n.run_count} run${n.run_count===1?"":"s"}
+ ${e} +No models yet.
Bad URL \u2014 model pages look like /models/detail/?slug=your-model.
';return}let r=null;try{r=await c(`${e.apiBase}/v1/me`)}catch{r=null}try{let o=await c(`${e.apiBase}/v1/models/${encodeURIComponent(this.slug)}`);this.model=o.model,this.versions=o.versions}catch(o){e.rootEl.innerHTML=`Model not found: ${s(String(o))}
`;return}this.isOwner=!!r&&!!this.model.owner&&r.user.handle===this.model.owner.handle,this.render(),this.attachEvents()}render(){let e=this.model,t=this.cfg.rootEl;t.querySelector("#ga-model-title").textContent=e.title,t.querySelector("#ga-model-desc").textContent=e.description||"No description yet.",t.querySelector("#ga-model-meta").textContent=`${L[e.kind]||e.kind} \xB7 ${_[e.provider]||e.provider} \xB7 ${e.visibility}`;let r=t.querySelector("#ga-model-versions");r&&(r.innerHTML=this.versions.length?this.versions.map(i=>` +${s(i.provider_model_id)}
+ ${H(i)} + ${i.dataset_note?`${s(i.dataset_note)}
`:""} +seed ${s(e.seed)}
`;if(e.output_kind==="code"&&e.output_text)t.innerHTML=`${r}${s(e.output_text)}`;else if(e.output_kind==="image"&&e.output_key){let o=`${this.cfg.apiBase}/v1/captures/${e.output_key}`;t.innerHTML=`${r}Render succeeded with no returned output.
`}attachEvents(){let e=this.cfg.rootEl;e.querySelector("#ga-publish-version-form")?.addEventListener("submit",t=>{t.preventDefault(),this.publishVersion()}),e.querySelector("#ga-render-btn")?.addEventListener("click",t=>{this.render_(t.currentTarget)})}},S={async mount(n){y({apiBase:n.apiBase,page:`models-${n.mode}`}),n.mode==="catalogue"?await new p().mount(n):await new h().mount(n)}};window.GAModels=S;var x=S;export{x as default}; +//# sourceMappingURL=ga-models.js.map diff --git a/assets/js/ga-models.js.map b/assets/js/ga-models.js.map new file mode 100644 index 0000000..3958018 --- /dev/null +++ b/assets/js/ga-models.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../workers/api/client/lib/clientErrors.ts", "../../workers/api/client/models.ts"], + "sourcesContent": ["// Window onerror / unhandledrejection \u2192 POST /v1/internal/client-error.\n// Per-page de-dupe and 10-report cap; the Worker rate-limits per IP\n// and forwards to Sentry via SENTRY_DSN_PUBLIC server-side.\n//\n// Why not @sentry/browser? Bundle size is the deciding factor:\n// @sentry/browser min+gz is ~25KB, which is larger than every\n// per-page bundle this app ships (galleries 18KB, studio 100KB,\n// dashboard 7KB). The proxy pattern below has equivalent delivery\n// (the worker uses ctx.waitUntil for the forward), preserves stack\n// fidelity (we POST the raw `Error.stack` string verbatim), and\n// keeps the public DSN out of the client where it would otherwise\n// be visible to scrapers. The trade-off is that we don't get the\n// SDK's auto-capture of breadcrumbs / fetch/console hooks; for v1\n// observability that's an acceptable gap given the size budget.\n\nconst SEEN = new Set${escapeHtml(m.description || \"No description yet.\")}
\n${escapeHtml(price)} \u00B7 ${m.run_count} run${m.run_count === 1 ? \"\" : \"s\"}
\n ${owner}\nNo models yet.
Bad URL \u2014 model pages look like /models/detail/?slug=your-model.
`;\n return;\n }\n\n let me: { user: { handle: string } } | null = null;\n try {\n me = await fetchJson(`${cfg.apiBase}/v1/me`);\n } catch {\n me = null;\n }\n\n try {\n const data = await fetchJson<{ model: ModelSummary; versions: ModelVersion[] }>(\n `${cfg.apiBase}/v1/models/${encodeURIComponent(this.slug)}`,\n );\n this.model = data.model;\n this.versions = data.versions;\n } catch (err) {\n cfg.rootEl.innerHTML = `Model not found: ${escapeHtml(String(err))}
`;\n return;\n }\n\n this.isOwner = !!me && !!this.model.owner && me.user.handle === this.model.owner.handle;\n this.render();\n this.attachEvents();\n }\n\n private render(): void {\n const m = this.model!;\n const root = this.cfg.rootEl;\n root.querySelector${escapeHtml(v.provider_model_id)}
\n ${provenanceTag(v)}\n ${v.dataset_note ? `${escapeHtml(v.dataset_note)}
` : \"\"}\nseed ${escapeHtml(job.seed)}
`;\n if (job.output_kind === \"code\" && job.output_text) {\n el.innerHTML = `${seedLine}${escapeHtml(job.output_text)}`;\n } else if (job.output_kind === \"image\" && job.output_key) {\n const src = `${this.cfg.apiBase}/v1/captures/${job.output_key}`;\n el.innerHTML = `${seedLine}Render succeeded with no returned output.
`;\n }\n }\n\n private attachEvents(): void {\n const root = this.cfg.rootEl;\n root.querySelectore;){let c=!0,f=!1;if(!h||a>r[h-1].to){let O=j[a-1];O!=l&&(c=!1,f=O==16)}let u=!c&&l==1?[]:null,d=c?i:i+1,p=a;e:for(;;)if(h&&p==r[h-1].to){if(f)break e;let O=r[--h];if(!c)for(let g=O.from,y=h;;){if(g==e)break e;if(y&&r[y-1].to==g)g=r[--y].from;else{if(j[g-1]==l)break e;break}}if(u)u.push(O);else{O.toj.length;)j[j.length]=256;let i=[],s=e==Vt?0:1;return ho(n,s,s,t,0,n.length,i),i}function cc(n){return[new Ie(0,n,0)]}var fc="";function Tp(n,e,t,i,s){var r;let o=i.head-n.from,l=Ie.find(e,o,(r=i.bidiLevel)!==null&&r!==void 0?r:-1,i.assoc),a=e[l],h=a.side(s,t);if(o==h){let u=l+=s?1:-1;if(u<0||u>=e.length)return null;a=e[l=u],o=a.side(!s,t),h=a.side(s,t)}let c=te(n.text,o,a.forward(s,t));(c