Skip to content

Commit 448892f

Browse files
authored
fix(spaces): keep Space emoji/colors stable on artifact re-deploy (#2378)
* fix(spaces): keep Space emoji/colors stable on artifact re-deploy Re-deploying an existing artifact re-uploaded README.md, which buildReadme regenerates with a random emoji and gradient each time, so the Space's emoji/colors changed on every update. Only push index.html on update; the README (emoji, colors, title) is written once at creation. A genuine recreate (Space deleted on the Hub) still writes a fresh README. * fix(spaces): seed Space emoji/colors deterministically instead of skipping README Re-deploys keep a stable emoji/gradient because the README is seeded per artifact (conversationId:artifactIdentifier) rather than randomized, so re-uploading it on every deploy never reshuffles the Space. This also keeps index.html + README on both create and update paths, so a Space recovered from a failed first upload (the self-heal path) still gets its README/config. Verified against the Hub: a no-change re-upload is accepted as an empty commit, not an error.
1 parent 9ee91dd commit 448892f

1 file changed

Lines changed: 23 additions & 7 deletions

File tree

src/routes/api/v2/spaces/deploy/+server.ts

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,19 @@ const bodySchema = z.object({
3030
const SPACE_EMOJIS = ["🚀", "✨", "🎨", "🤗", "💡", "🧩", "🌈", "⚡️", "🪄", "🔮", "🛸", "🎯"];
3131
const SPACE_COLORS = ["red", "yellow", "green", "blue", "indigo", "purple", "pink", "gray"];
3232

33-
function pick<T>(arr: readonly T[]): T {
34-
return arr[Math.floor(Math.random() * arr.length)];
33+
// Deterministic pick, seeded per artifact, so a Space keeps the same emoji and
34+
// gradient across re-deploys. A random pick would reshuffle them on every update
35+
// (the README is re-uploaded each deploy).
36+
function hashString(s: string): number {
37+
let h = 0;
38+
for (let i = 0; i < s.length; i++) {
39+
h = (Math.imul(h, 31) + s.charCodeAt(i)) | 0;
40+
}
41+
return Math.abs(h);
42+
}
43+
44+
function pick<T>(arr: readonly T[], seed: string): T {
45+
return arr[hashString(seed) % arr.length];
3546
}
3647

3748
function slugify(title: string): string {
@@ -45,7 +56,7 @@ function slugify(title: string): string {
4556
return slug || "huggingchat-artifact";
4657
}
4758

48-
function buildReadme(title: string): string {
59+
function buildReadme(title: string, seed: string): string {
4960
const trimmed = title.slice(0, 200);
5061
// YAML is a superset of JSON, so a JSON double-quoted string is a valid YAML
5162
// double-quoted scalar with quotes, backslashes, control chars, and unicode
@@ -54,9 +65,9 @@ function buildReadme(title: string): string {
5465
const headingTitle = trimmed.replace(/[\r\n]+/g, " ");
5566
return `---
5667
title: ${yamlTitle}
57-
emoji: ${pick(SPACE_EMOJIS)}
58-
colorFrom: ${pick(SPACE_COLORS)}
59-
colorTo: ${pick(SPACE_COLORS)}
68+
emoji: ${pick(SPACE_EMOJIS, `${seed}:emoji`)}
69+
colorFrom: ${pick(SPACE_COLORS, `${seed}:from`)}
70+
colorTo: ${pick(SPACE_COLORS, `${seed}:to`)}
6071
sdk: static
6172
pinned: false
6273
tags:
@@ -124,14 +135,19 @@ export const POST: RequestHandler = async ({ locals, request }) => {
124135
error(404, "Conversation not found");
125136
}
126137

138+
// README emoji/colors are seeded per artifact so they stay identical across
139+
// re-deploys; index.html + README are uploaded on every deploy (create and
140+
// update), so a Space recovered from a failed first upload still gets its
141+
// README/config, and re-deploying never reshuffles the Space's appearance.
142+
const readmeSeed = `${body.conversationId}:${body.artifactIdentifier}`;
127143
const files = [
128144
{
129145
path: "index.html",
130146
content: new Blob([buildDeployableHtml(body.kind, body.content)], { type: "text/html" }),
131147
},
132148
{
133149
path: "README.md",
134-
content: new Blob([buildReadme(body.title)], { type: "text/markdown" }),
150+
content: new Blob([buildReadme(body.title, readmeSeed)], { type: "text/markdown" }),
135151
},
136152
];
137153

0 commit comments

Comments
 (0)