Spaces:
Sleeping
Sleeping
Add poll edit/delete for members; preserve votes for unchanged options
Browse files- README.md +1 -1
- app.py +105 -10
- frontend/.astro/content-assets.mjs +1 -0
- frontend/.astro/content-modules.mjs +1 -0
- frontend/.astro/content.d.ts +199 -0
- frontend/.astro/types.d.ts +2 -0
- frontend/dist/_astro/{PollApp.BI8OLAG4.js → PollApp.LRn70eey.js} +5 -5
- frontend/dist/_astro/{index.D5zPhbGI.css → index.CWh57_AP.css} +1 -1
- frontend/dist/index.html +1 -1
- frontend/src/components/PollApp.tsx +85 -17
- frontend/src/styles/global.css +5 -0
README.md
CHANGED
|
@@ -8,7 +8,7 @@ sdk_version: 6.25.0
|
|
| 8 |
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
license: mit
|
| 11 |
-
short_description: Create polls and vote
|
| 12 |
hf_oauth: true
|
| 13 |
# NOTE: Uncomment the line below to hard-gate the entire Space at the
|
| 14 |
# platform level so only slmconsortium members can even open it.
|
|
|
|
| 8 |
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
license: mit
|
| 11 |
+
short_description: Create polls and vote — members only
|
| 12 |
hf_oauth: true
|
| 13 |
# NOTE: Uncomment the line below to hard-gate the entire Space at the
|
| 14 |
# platform level so only slmconsortium members can even open it.
|
app.py
CHANGED
|
@@ -226,6 +226,21 @@ def api_vote(
|
|
| 226 |
return {"ok": True, "message": msg, "poll": poll_to_dict(polls[poll_id], username)}
|
| 227 |
|
| 228 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 229 |
def api_create(
|
| 230 |
question: str,
|
| 231 |
options_raw: str,
|
|
@@ -236,16 +251,9 @@ def api_create(
|
|
| 236 |
if not allowed:
|
| 237 |
return {"ok": False, "message": status, "poll": None}
|
| 238 |
|
| 239 |
-
question = (question
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
return {"ok": False, "message": "Give your poll a question (min 3 characters).", "poll": None}
|
| 243 |
-
if len(options) < 2:
|
| 244 |
-
return {"ok": False, "message": "Provide at least 2 options (one per line).", "poll": None}
|
| 245 |
-
if len(options) > 20:
|
| 246 |
-
return {"ok": False, "message": "Max 20 options per poll.", "poll": None}
|
| 247 |
-
if len(set(o.lower() for o in options)) != len(options):
|
| 248 |
-
return {"ok": False, "message": "Options must be unique.", "poll": None}
|
| 249 |
|
| 250 |
with _lock:
|
| 251 |
polls = load_polls()
|
|
@@ -266,6 +274,71 @@ def api_create(
|
|
| 266 |
}
|
| 267 |
|
| 268 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 269 |
# ------------------------------------------------------- classic UI views
|
| 270 |
|
| 271 |
def poll_label(poll: Dict) -> str:
|
|
@@ -505,6 +578,28 @@ with gr.Blocks(title="SLM Consortium Polls") as demo:
|
|
| 505 |
api_name="create_poll",
|
| 506 |
)
|
| 507 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 508 |
|
| 509 |
# ------------------------------------------------- app: static + gradio
|
| 510 |
|
|
|
|
| 226 |
return {"ok": True, "message": msg, "poll": poll_to_dict(polls[poll_id], username)}
|
| 227 |
|
| 228 |
|
| 229 |
+
def _validate_poll(question: str, options_raw: str) -> Tuple[str, List[str], Optional[str]]:
|
| 230 |
+
"""Return (question, options, error_message_or_None)."""
|
| 231 |
+
question = (question or "").strip()
|
| 232 |
+
options = parse_options(options_raw or "")
|
| 233 |
+
if len(question) < 3:
|
| 234 |
+
return question, options, "Give your poll a question (min 3 characters)."
|
| 235 |
+
if len(options) < 2:
|
| 236 |
+
return question, options, "Provide at least 2 options (one per line)."
|
| 237 |
+
if len(options) > 20:
|
| 238 |
+
return question, options, "Max 20 options per poll."
|
| 239 |
+
if len(set(o.lower() for o in options)) != len(options):
|
| 240 |
+
return question, options, "Options must be unique."
|
| 241 |
+
return question, options, None
|
| 242 |
+
|
| 243 |
+
|
| 244 |
def api_create(
|
| 245 |
question: str,
|
| 246 |
options_raw: str,
|
|
|
|
| 251 |
if not allowed:
|
| 252 |
return {"ok": False, "message": status, "poll": None}
|
| 253 |
|
| 254 |
+
question, options, err = _validate_poll(question, options_raw)
|
| 255 |
+
if err:
|
| 256 |
+
return {"ok": False, "message": err, "poll": None}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 257 |
|
| 258 |
with _lock:
|
| 259 |
polls = load_polls()
|
|
|
|
| 274 |
}
|
| 275 |
|
| 276 |
|
| 277 |
+
def api_edit(
|
| 278 |
+
poll_id: str,
|
| 279 |
+
question: str,
|
| 280 |
+
options_raw: str,
|
| 281 |
+
profile: gr.OAuthProfile | None,
|
| 282 |
+
token: gr.OAuthToken | None,
|
| 283 |
+
) -> Dict:
|
| 284 |
+
allowed, username, status = check_membership(profile, token)
|
| 285 |
+
if not allowed:
|
| 286 |
+
return {"ok": False, "message": status, "poll": None}
|
| 287 |
+
|
| 288 |
+
question, options, err = _validate_poll(question, options_raw)
|
| 289 |
+
if err:
|
| 290 |
+
return {"ok": False, "message": err, "poll": None}
|
| 291 |
+
|
| 292 |
+
with _lock:
|
| 293 |
+
polls = load_polls()
|
| 294 |
+
poll = polls.get(poll_id or "")
|
| 295 |
+
if poll is None:
|
| 296 |
+
return {"ok": False, "message": "That poll no longer exists — refresh the list.", "poll": None}
|
| 297 |
+
# Keep votes for choices whose text is unchanged; drop votes for
|
| 298 |
+
# removed/edited options (indices may shift, so remap by text).
|
| 299 |
+
old_opts = poll["options"]
|
| 300 |
+
remap: Dict[int, int] = {}
|
| 301 |
+
for old_idx, opt in enumerate(old_opts):
|
| 302 |
+
if opt in options:
|
| 303 |
+
remap[old_idx] = options.index(opt)
|
| 304 |
+
votes_reset = old_opts != options
|
| 305 |
+
kept = 0
|
| 306 |
+
for u, idx in list(poll.get("voters", {}).items()):
|
| 307 |
+
new_idx = remap.get(idx)
|
| 308 |
+
if new_idx is None:
|
| 309 |
+
poll["voters"].pop(u, None)
|
| 310 |
+
else:
|
| 311 |
+
poll["voters"][u] = new_idx
|
| 312 |
+
kept += 1
|
| 313 |
+
poll["question"] = question
|
| 314 |
+
poll["options"] = options
|
| 315 |
+
save_polls(polls)
|
| 316 |
+
updated = poll_to_dict(poll, username)
|
| 317 |
+
|
| 318 |
+
msg = f"Poll updated by @{username}."
|
| 319 |
+
if votes_reset:
|
| 320 |
+
msg += f" Kept {kept} vote(s) for unchanged options; votes for edited/removed options were dropped."
|
| 321 |
+
return {"ok": True, "message": msg, "poll": updated}
|
| 322 |
+
|
| 323 |
+
|
| 324 |
+
def api_delete(
|
| 325 |
+
poll_id: str,
|
| 326 |
+
profile: gr.OAuthProfile | None,
|
| 327 |
+
token: gr.OAuthToken | None,
|
| 328 |
+
) -> Dict:
|
| 329 |
+
allowed, username, status = check_membership(profile, token)
|
| 330 |
+
if not allowed:
|
| 331 |
+
return {"ok": False, "message": status}
|
| 332 |
+
|
| 333 |
+
with _lock:
|
| 334 |
+
polls = load_polls()
|
| 335 |
+
poll = polls.pop(poll_id or "", None)
|
| 336 |
+
if poll is None:
|
| 337 |
+
return {"ok": False, "message": "That poll no longer exists — refresh the list."}
|
| 338 |
+
save_polls(polls)
|
| 339 |
+
return {"ok": True, "message": f"Poll “{poll['question']}” deleted by @{username}."}
|
| 340 |
+
|
| 341 |
+
|
| 342 |
# ------------------------------------------------------- classic UI views
|
| 343 |
|
| 344 |
def poll_label(poll: Dict) -> str:
|
|
|
|
| 578 |
api_name="create_poll",
|
| 579 |
)
|
| 580 |
|
| 581 |
+
api_edit_pid = gr.Textbox(visible=False)
|
| 582 |
+
api_edit_q = gr.Textbox(visible=False)
|
| 583 |
+
api_edit_opts = gr.Textbox(visible=False)
|
| 584 |
+
api_edit_out = gr.JSON(visible=False)
|
| 585 |
+
api_edit_btn = gr.Button(visible=False)
|
| 586 |
+
api_edit_btn.click(
|
| 587 |
+
api_edit,
|
| 588 |
+
inputs=[api_edit_pid, api_edit_q, api_edit_opts],
|
| 589 |
+
outputs=api_edit_out,
|
| 590 |
+
api_name="edit_poll",
|
| 591 |
+
)
|
| 592 |
+
|
| 593 |
+
api_del_pid = gr.Textbox(visible=False)
|
| 594 |
+
api_del_out = gr.JSON(visible=False)
|
| 595 |
+
api_del_btn = gr.Button(visible=False)
|
| 596 |
+
api_del_btn.click(
|
| 597 |
+
api_delete,
|
| 598 |
+
inputs=[api_del_pid],
|
| 599 |
+
outputs=api_del_out,
|
| 600 |
+
api_name="delete_poll",
|
| 601 |
+
)
|
| 602 |
+
|
| 603 |
|
| 604 |
# ------------------------------------------------- app: static + gradio
|
| 605 |
|
frontend/.astro/content-assets.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
export default new Map();
|
frontend/.astro/content-modules.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
export default new Map();
|
frontend/.astro/content.d.ts
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
declare module 'astro:content' {
|
| 2 |
+
export interface RenderResult {
|
| 3 |
+
Content: import('astro/runtime/server/index.js').AstroComponentFactory;
|
| 4 |
+
headings: import('astro').MarkdownHeading[];
|
| 5 |
+
remarkPluginFrontmatter: Record<string, any>;
|
| 6 |
+
}
|
| 7 |
+
interface Render {
|
| 8 |
+
'.md': Promise<RenderResult>;
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
export interface RenderedContent {
|
| 12 |
+
html: string;
|
| 13 |
+
metadata?: {
|
| 14 |
+
imagePaths: Array<string>;
|
| 15 |
+
[key: string]: unknown;
|
| 16 |
+
};
|
| 17 |
+
}
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
declare module 'astro:content' {
|
| 21 |
+
type Flatten<T> = T extends { [K: string]: infer U } ? U : never;
|
| 22 |
+
|
| 23 |
+
export type CollectionKey = keyof AnyEntryMap;
|
| 24 |
+
export type CollectionEntry<C extends CollectionKey> = Flatten<AnyEntryMap[C]>;
|
| 25 |
+
|
| 26 |
+
export type ContentCollectionKey = keyof ContentEntryMap;
|
| 27 |
+
export type DataCollectionKey = keyof DataEntryMap;
|
| 28 |
+
|
| 29 |
+
type AllValuesOf<T> = T extends any ? T[keyof T] : never;
|
| 30 |
+
type ValidContentEntrySlug<C extends keyof ContentEntryMap> = AllValuesOf<
|
| 31 |
+
ContentEntryMap[C]
|
| 32 |
+
>['slug'];
|
| 33 |
+
|
| 34 |
+
export type ReferenceDataEntry<
|
| 35 |
+
C extends CollectionKey,
|
| 36 |
+
E extends keyof DataEntryMap[C] = string,
|
| 37 |
+
> = {
|
| 38 |
+
collection: C;
|
| 39 |
+
id: E;
|
| 40 |
+
};
|
| 41 |
+
export type ReferenceContentEntry<
|
| 42 |
+
C extends keyof ContentEntryMap,
|
| 43 |
+
E extends ValidContentEntrySlug<C> | (string & {}) = string,
|
| 44 |
+
> = {
|
| 45 |
+
collection: C;
|
| 46 |
+
slug: E;
|
| 47 |
+
};
|
| 48 |
+
export type ReferenceLiveEntry<C extends keyof LiveContentConfig['collections']> = {
|
| 49 |
+
collection: C;
|
| 50 |
+
id: string;
|
| 51 |
+
};
|
| 52 |
+
|
| 53 |
+
/** @deprecated Use `getEntry` instead. */
|
| 54 |
+
export function getEntryBySlug<
|
| 55 |
+
C extends keyof ContentEntryMap,
|
| 56 |
+
E extends ValidContentEntrySlug<C> | (string & {}),
|
| 57 |
+
>(
|
| 58 |
+
collection: C,
|
| 59 |
+
// Note that this has to accept a regular string too, for SSR
|
| 60 |
+
entrySlug: E,
|
| 61 |
+
): E extends ValidContentEntrySlug<C>
|
| 62 |
+
? Promise<CollectionEntry<C>>
|
| 63 |
+
: Promise<CollectionEntry<C> | undefined>;
|
| 64 |
+
|
| 65 |
+
/** @deprecated Use `getEntry` instead. */
|
| 66 |
+
export function getDataEntryById<C extends keyof DataEntryMap, E extends keyof DataEntryMap[C]>(
|
| 67 |
+
collection: C,
|
| 68 |
+
entryId: E,
|
| 69 |
+
): Promise<CollectionEntry<C>>;
|
| 70 |
+
|
| 71 |
+
export function getCollection<C extends keyof AnyEntryMap, E extends CollectionEntry<C>>(
|
| 72 |
+
collection: C,
|
| 73 |
+
filter?: (entry: CollectionEntry<C>) => entry is E,
|
| 74 |
+
): Promise<E[]>;
|
| 75 |
+
export function getCollection<C extends keyof AnyEntryMap>(
|
| 76 |
+
collection: C,
|
| 77 |
+
filter?: (entry: CollectionEntry<C>) => unknown,
|
| 78 |
+
): Promise<CollectionEntry<C>[]>;
|
| 79 |
+
|
| 80 |
+
export function getLiveCollection<C extends keyof LiveContentConfig['collections']>(
|
| 81 |
+
collection: C,
|
| 82 |
+
filter?: LiveLoaderCollectionFilterType<C>,
|
| 83 |
+
): Promise<
|
| 84 |
+
import('astro').LiveDataCollectionResult<LiveLoaderDataType<C>, LiveLoaderErrorType<C>>
|
| 85 |
+
>;
|
| 86 |
+
|
| 87 |
+
export function getEntry<
|
| 88 |
+
C extends keyof ContentEntryMap,
|
| 89 |
+
E extends ValidContentEntrySlug<C> | (string & {}),
|
| 90 |
+
>(
|
| 91 |
+
entry: ReferenceContentEntry<C, E>,
|
| 92 |
+
): E extends ValidContentEntrySlug<C>
|
| 93 |
+
? Promise<CollectionEntry<C>>
|
| 94 |
+
: Promise<CollectionEntry<C> | undefined>;
|
| 95 |
+
export function getEntry<
|
| 96 |
+
C extends keyof DataEntryMap,
|
| 97 |
+
E extends keyof DataEntryMap[C] | (string & {}),
|
| 98 |
+
>(
|
| 99 |
+
entry: ReferenceDataEntry<C, E>,
|
| 100 |
+
): E extends keyof DataEntryMap[C]
|
| 101 |
+
? Promise<DataEntryMap[C][E]>
|
| 102 |
+
: Promise<CollectionEntry<C> | undefined>;
|
| 103 |
+
export function getEntry<
|
| 104 |
+
C extends keyof ContentEntryMap,
|
| 105 |
+
E extends ValidContentEntrySlug<C> | (string & {}),
|
| 106 |
+
>(
|
| 107 |
+
collection: C,
|
| 108 |
+
slug: E,
|
| 109 |
+
): E extends ValidContentEntrySlug<C>
|
| 110 |
+
? Promise<CollectionEntry<C>>
|
| 111 |
+
: Promise<CollectionEntry<C> | undefined>;
|
| 112 |
+
export function getEntry<
|
| 113 |
+
C extends keyof DataEntryMap,
|
| 114 |
+
E extends keyof DataEntryMap[C] | (string & {}),
|
| 115 |
+
>(
|
| 116 |
+
collection: C,
|
| 117 |
+
id: E,
|
| 118 |
+
): E extends keyof DataEntryMap[C]
|
| 119 |
+
? string extends keyof DataEntryMap[C]
|
| 120 |
+
? Promise<DataEntryMap[C][E]> | undefined
|
| 121 |
+
: Promise<DataEntryMap[C][E]>
|
| 122 |
+
: Promise<CollectionEntry<C> | undefined>;
|
| 123 |
+
export function getLiveEntry<C extends keyof LiveContentConfig['collections']>(
|
| 124 |
+
collection: C,
|
| 125 |
+
filter: string | LiveLoaderEntryFilterType<C>,
|
| 126 |
+
): Promise<import('astro').LiveDataEntryResult<LiveLoaderDataType<C>, LiveLoaderErrorType<C>>>;
|
| 127 |
+
|
| 128 |
+
/** Resolve an array of entry references from the same collection */
|
| 129 |
+
export function getEntries<C extends keyof ContentEntryMap>(
|
| 130 |
+
entries: ReferenceContentEntry<C, ValidContentEntrySlug<C>>[],
|
| 131 |
+
): Promise<CollectionEntry<C>[]>;
|
| 132 |
+
export function getEntries<C extends keyof DataEntryMap>(
|
| 133 |
+
entries: ReferenceDataEntry<C, keyof DataEntryMap[C]>[],
|
| 134 |
+
): Promise<CollectionEntry<C>[]>;
|
| 135 |
+
|
| 136 |
+
export function render<C extends keyof AnyEntryMap>(
|
| 137 |
+
entry: AnyEntryMap[C][string],
|
| 138 |
+
): Promise<RenderResult>;
|
| 139 |
+
|
| 140 |
+
export function reference<C extends keyof AnyEntryMap>(
|
| 141 |
+
collection: C,
|
| 142 |
+
): import('astro/zod').ZodEffects<
|
| 143 |
+
import('astro/zod').ZodString,
|
| 144 |
+
C extends keyof ContentEntryMap
|
| 145 |
+
? ReferenceContentEntry<C, ValidContentEntrySlug<C>>
|
| 146 |
+
: ReferenceDataEntry<C, keyof DataEntryMap[C]>
|
| 147 |
+
>;
|
| 148 |
+
// Allow generic `string` to avoid excessive type errors in the config
|
| 149 |
+
// if `dev` is not running to update as you edit.
|
| 150 |
+
// Invalid collection names will be caught at build time.
|
| 151 |
+
export function reference<C extends string>(
|
| 152 |
+
collection: C,
|
| 153 |
+
): import('astro/zod').ZodEffects<import('astro/zod').ZodString, never>;
|
| 154 |
+
|
| 155 |
+
type ReturnTypeOrOriginal<T> = T extends (...args: any[]) => infer R ? R : T;
|
| 156 |
+
type InferEntrySchema<C extends keyof AnyEntryMap> = import('astro/zod').infer<
|
| 157 |
+
ReturnTypeOrOriginal<Required<ContentConfig['collections'][C]>['schema']>
|
| 158 |
+
>;
|
| 159 |
+
|
| 160 |
+
type ContentEntryMap = {
|
| 161 |
+
|
| 162 |
+
};
|
| 163 |
+
|
| 164 |
+
type DataEntryMap = {
|
| 165 |
+
|
| 166 |
+
};
|
| 167 |
+
|
| 168 |
+
type AnyEntryMap = ContentEntryMap & DataEntryMap;
|
| 169 |
+
|
| 170 |
+
type ExtractLoaderTypes<T> = T extends import('astro/loaders').LiveLoader<
|
| 171 |
+
infer TData,
|
| 172 |
+
infer TEntryFilter,
|
| 173 |
+
infer TCollectionFilter,
|
| 174 |
+
infer TError
|
| 175 |
+
>
|
| 176 |
+
? { data: TData; entryFilter: TEntryFilter; collectionFilter: TCollectionFilter; error: TError }
|
| 177 |
+
: { data: never; entryFilter: never; collectionFilter: never; error: never };
|
| 178 |
+
type ExtractDataType<T> = ExtractLoaderTypes<T>['data'];
|
| 179 |
+
type ExtractEntryFilterType<T> = ExtractLoaderTypes<T>['entryFilter'];
|
| 180 |
+
type ExtractCollectionFilterType<T> = ExtractLoaderTypes<T>['collectionFilter'];
|
| 181 |
+
type ExtractErrorType<T> = ExtractLoaderTypes<T>['error'];
|
| 182 |
+
|
| 183 |
+
type LiveLoaderDataType<C extends keyof LiveContentConfig['collections']> =
|
| 184 |
+
LiveContentConfig['collections'][C]['schema'] extends undefined
|
| 185 |
+
? ExtractDataType<LiveContentConfig['collections'][C]['loader']>
|
| 186 |
+
: import('astro/zod').infer<
|
| 187 |
+
Exclude<LiveContentConfig['collections'][C]['schema'], undefined>
|
| 188 |
+
>;
|
| 189 |
+
type LiveLoaderEntryFilterType<C extends keyof LiveContentConfig['collections']> =
|
| 190 |
+
ExtractEntryFilterType<LiveContentConfig['collections'][C]['loader']>;
|
| 191 |
+
type LiveLoaderCollectionFilterType<C extends keyof LiveContentConfig['collections']> =
|
| 192 |
+
ExtractCollectionFilterType<LiveContentConfig['collections'][C]['loader']>;
|
| 193 |
+
type LiveLoaderErrorType<C extends keyof LiveContentConfig['collections']> = ExtractErrorType<
|
| 194 |
+
LiveContentConfig['collections'][C]['loader']
|
| 195 |
+
>;
|
| 196 |
+
|
| 197 |
+
export type ContentConfig = typeof import("../src/content.config.mjs");
|
| 198 |
+
export type LiveContentConfig = never;
|
| 199 |
+
}
|
frontend/.astro/types.d.ts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/// <reference types="astro/client" />
|
| 2 |
+
/// <reference path="content.d.ts" />
|
frontend/dist/_astro/{PollApp.BI8OLAG4.js → PollApp.LRn70eey.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
import{r as L}from"./index.qNTDzdXh.js";var
|
| 2 |
* @license React
|
| 3 |
* react-jsx-runtime.production.js
|
| 4 |
*
|
|
@@ -6,7 +6,7 @@ import{r as L}from"./index.qNTDzdXh.js";var he={exports:{}},ne={};/**
|
|
| 6 |
*
|
| 7 |
* This source code is licensed under the MIT license found in the
|
| 8 |
* LICENSE file in the root directory of this source tree.
|
| 9 |
-
*/var Ne;function nt(){if(Ne)return ne;Ne=1;var e=Symbol.for("react.transitional.element"),s=Symbol.for("react.fragment");function t(n,i,a){var o=null;if(a!==void 0&&(o=""+a),i.key!==void 0&&(o=""+i.key),"key"in i){a={};for(var r in i)r!=="key"&&(a[r]=i[r])}else a=i;return i=a.ref,{$$typeof:e,type:n,key:o,ref:i!==void 0?i:null,props:a}}return ne.Fragment=s,ne.jsx=t,ne.jsxs=t,ne}var Ce;function it(){return Ce||(Ce=1,he.exports=nt()),he.exports}var p=it();const at="modulepreload",ot=function(e){return"/"+e},Oe={},Pe=function(s,t,n){let i=Promise.resolve();if(t&&t.length>0){let o=function(h){return Promise.all(h.map(g=>Promise.resolve(g).then(u=>({status:"fulfilled",value:u}),u=>({status:"rejected",reason:u}))))};document.getElementsByTagName("link");const r=document.querySelector("meta[property=csp-nonce]"),d=r?.nonce||r?.getAttribute("nonce");i=o(t.map(h=>{if(h=ot(h),h in Oe)return;Oe[h]=!0;const g=h.endsWith(".css"),u=g?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${h}"]${u}`))return;const _=document.createElement("link");if(_.rel=g?"stylesheet":at,g||(_.as="script"),_.crossOrigin="",_.href=h,d&&_.setAttribute("nonce",d),document.head.appendChild(_),g)return new Promise(($,R)=>{_.addEventListener("load",$),_.addEventListener("error",()=>R(new Error(`Unable to preload CSS for ${h}`)))})}))}function a(o){const r=new Event("vite:preloadError",{cancelable:!0});if(r.payload=o,window.dispatchEvent(r),!r.defaultPrevented)throw o}return i.then(o=>{for(const r of o||[])r.status==="rejected"&&a(r.reason);return s().catch(a)})};var rt=Object.defineProperty,Ue=e=>{throw TypeError(e)},ct=(e,s,t)=>s in e?rt(e,s,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[s]=t,m=(e,s,t)=>ct(e,typeof s!="symbol"?s+"":s,t),Be=(e,s,t)=>s.has(e)||Ue("Cannot "+t),ie=(e,s,t)=>(Be(e,s,"read from private field"),s.get(e)),lt=(e,s,t)=>s.has(e)?Ue("Cannot add the same private member more than once"):s instanceof WeakSet?s.add(e):s.set(e,t),ut=(e,s,t,n)=>(Be(e,s,"write to private field"),s.set(e,t),t),fe=new Intl.Collator(0,{numeric:1}).compare;function Ie(e,s,t){return e=e.split("."),s=s.split("."),fe(e[0],s[0])||fe(e[1],s[1])||(s[2]=s.slice(2).join("."),t=/[.-]/.test(e[2]=e.slice(2).join(".")),t==/[.-]/.test(s[2])?fe(e[2],s[2]):t?-1:1)}const dt="host",Fe="queue/data",pt="queue/join",qe="upload",ht="login",ce="config",ft="info",mt="runtime",_t="sleeptime",gt="heartbeat",wt="component_server",yt="reset",vt="cancel",bt="app_id",xt="https://gradio-space-api-fetcher-v2.hf.space/api",Je="This application is currently busy. Please try again. ",Q="Connection errored out. ",G="Could not resolve app config. ",kt="Could not get space status. ",Et="Could not get API info. ",be="Space metadata could not be loaded. ",$t="Invalid URL. A full URL path is required.",jt="Not authorized to access this space. ",We="Invalid credentials. Could not login. ",St="Login credentials are required to access this space.",Nt="File system access is only available in Node.js environments",Me="Root URL not found in client config",Ct="Error uploading file";function Ot(e,s,t){return s.startsWith("http://")||s.startsWith("https://")?t?e:s:e+s}async function Te(e,s,t){try{return(await(await fetch(`https://huggingface.co/api/spaces/${e}/jwt`,{headers:{Authorization:`Bearer ${s}`,...t?{Cookie:t}:{}}})).json()).token||!1}catch{return!1}}function Pt(e){let s={};return e.forEach(({api_name:t,id:n})=>{t&&(s[t]=n)}),s}async function qt(e){const s=this.options.hf_token?{Authorization:`Bearer ${this.options.hf_token}`}:{};if(s["Content-Type"]="application/json",typeof window<"u"&&window.gradio_config&&location.origin!=="http://localhost:9876"){if(window.gradio_config.current_page&&(e=e.substring(0,e.lastIndexOf("/"))),window.gradio_config.dev_mode){let t=we(e,this.deep_link?ce+"?deep_link="+this.deep_link:ce);const n=await this.fetch(t,{headers:s,credentials:"include"}),i=await Ae(n,e,!!this.options.auth);window.gradio_config={...i,current_page:window.gradio_config.current_page}}return window.gradio_config.root=e,{...window.gradio_config}}else if(e){let t=we(e,this.deep_link?ce+"?deep_link="+this.deep_link:ce);const n=await this.fetch(t,{headers:s,credentials:"include"});return Ae(n,e,!!this.options.auth)}throw new Error(G)}async function Ae(e,s,t){var n,i;if(e?.status===401&&!t){const a=await e.json(),o=(n=a?.detail)==null?void 0:n.auth_message;throw new Error(o||St)}else if(e?.status===401&&t)throw new Error(We);if(e?.status===200){let a=await e.json();return a.root=s,(i=a.dependencies)==null||i.forEach((o,r)=>{o.id===void 0&&(o.id=r)}),a}else if(e?.status===401)throw new Error(jt);throw new Error(G)}async function Tt(){const{http_protocol:e,host:s}=await le(this.app_reference,this.options.hf_token);try{if(this.options.auth){const t=await Ge(e,s,this.options.auth,this.fetch,this.options.hf_token);t&&this.set_cookies(t)}}catch(t){throw Error(t.message)}}async function Ge(e,s,t,n,i){const a=new FormData;a.append("username",t?.[0]),a.append("password",t?.[1]);let o={};i&&(o.Authorization=`Bearer ${i}`);const r=await n(`${e}//${s}/${ht}`,{headers:o,method:"POST",body:a,credentials:"include"});if(r.status===200)return r.headers.get("set-cookie");throw r.status===401?new Error(We):new Error(be)}function me(e){if(e.startsWith("http")){const{protocol:s,host:t,pathname:n}=new URL(e);return{ws_protocol:s==="https:"?"wss":"ws",http_protocol:s,host:t+(n!=="/"?n:"")}}return{ws_protocol:"wss",http_protocol:"https:",host:new URL(e).host}}const He=e=>{let s=[];return e.split(/,(?=\s*[^\s=;]+=[^\s=;]+)/).forEach(t=>{const[n,i]=t.split(";")[0].split("=");n&&i&&s.push(`${n.trim()}=${i.trim()}`)}),s},xe=/^[a-zA-Z0-9_\-\.]+\/[a-zA-Z0-9_\-\.]+$/,At=/.*hf\.space\/{0,1}.*$/;async function le(e,s){const t={};s&&(t.Authorization=`Bearer ${s}`);const n=e.trim().replace(/\/$/,"");if(xe.test(n))try{const i=(await(await fetch(`https://huggingface.co/api/spaces/${n}/${dt}`,{headers:t})).json()).host;return{space_id:e,...me(i)}}catch{throw new Error(be)}if(At.test(n)){const{ws_protocol:i,http_protocol:a,host:o}=me(n);return{space_id:o.split("/")[0].replace(".hf.space",""),ws_protocol:i,http_protocol:a,host:o}}return{space_id:!1,...me(n)}}const we=(...e)=>{try{return e.reduce((s,t)=>(s=s.replace(/\/+$/,""),t=t.replace(/^\/+/,""),new URL(t,s+"/").toString()))}catch{throw new Error($t)}};function Dt(e,s,t){const n={named_endpoints:{},unnamed_endpoints:{}};return Object.keys(e).forEach(i=>{(i==="named_endpoints"||i==="unnamed_endpoints")&&(n[i]={},Object.entries(e[i]).forEach(([a,{parameters:o,returns:r}])=>{var d,h,g,u;const _=((d=s.dependencies.find(c=>c.api_name===a||c.api_name===a.replace("/","")))==null?void 0:d.id)||t[a.replace("/","")]||-1,$=_!==-1?(h=s.dependencies.find(c=>c.id==_))==null?void 0:h.types:{generator:!1,cancel:!1};if(_!==-1&&((u=(g=s.dependencies.find(c=>c.id==_))==null?void 0:g.inputs)==null?void 0:u.length)!==o.length){const c=s.dependencies.find(w=>w.id==_).inputs.map(w=>{var N;return(N=s.components.find(C=>C.id===w))==null?void 0:N.type});try{c.forEach((w,N)=>{if(w==="state"){const C={component:"state",example:null,parameter_default:null,parameter_has_default:!0,parameter_name:null,hidden:!0};o.splice(N,0,C)}})}catch(w){console.error(w)}}const R=(c,w,N,C)=>({...c,description:zt(c?.type,N),type:Rt(c?.type,w,N,C)||""});n[i][a]={parameters:o.map(c=>R(c,c?.component,c?.serializer,"parameter")),returns:r.map(c=>R(c,c?.component,c?.serializer,"return")),type:$}}))}),n}function Rt(e,s,t,n){if(s==="Api")return e.type;switch(e?.type){case"string":return"string";case"boolean":return"boolean";case"number":return"number"}if(t==="JSONSerializable"||t==="StringSerializable")return"any";if(t==="ListStringSerializable")return"string[]";if(s==="Image")return n==="parameter"?"Blob | File | Buffer":"string";if(t==="FileSerializable")return e?.type==="array"?n==="parameter"?"(Blob | File | Buffer)[]":"{ name: string; data: string; size?: number; is_file?: boolean; orig_name?: string}[]":n==="parameter"?"Blob | File | Buffer":"{ name: string; data: string; size?: number; is_file?: boolean; orig_name?: string}";if(t==="GallerySerializable")return n==="parameter"?"[(Blob | File | Buffer), (string | null)][]":"[{ name: string; data: string; size?: number; is_file?: boolean; orig_name?: string}, (string | null))][]"}function zt(e,s){return s==="GallerySerializable"?"array of [file, label] tuples":s==="ListStringSerializable"?"array of strings":s==="FileSerializable"?"array of files or single file":e?.description}function _e(e,s){switch(e.msg){case"send_data":return{type:"data"};case"send_hash":return{type:"hash"};case"queue_full":return{type:"update",status:{queue:!0,message:Je,stage:"error",code:e.code,success:e.success}};case"heartbeat":return{type:"heartbeat"};case"unexpected_error":return{type:"unexpected_error",status:{queue:!0,message:e.message,session_not_found:e.session_not_found,stage:"error",success:!1}};case"broken_connection":return{type:"broken_connection",status:{queue:!0,message:e.message,stage:"error",success:!1}};case"estimation":return{type:"update",status:{queue:!0,stage:s||"pending",code:e.code,size:e.queue_size,position:e.rank,eta:e.rank_eta,success:e.success}};case"progress":return{type:"update",status:{queue:!0,stage:"pending",code:e.code,progress_data:e.progress_data,success:e.success}};case"log":return{type:"log",data:e};case"process_generating":return{type:"generating",status:{queue:!0,message:e.success?null:e.output.error,stage:e.success?"generating":"error",code:e.code,progress_data:e.progress_data,eta:e.average_duration,changed_state_ids:e.success?e.output.changed_state_ids:void 0},data:e.success?e.output:null};case"process_streaming":return{type:"streaming",status:{queue:!0,message:e.output.error,stage:"streaming",time_limit:e.time_limit,code:e.code,progress_data:e.progress_data,eta:e.eta},data:e.output};case"process_completed":return"error"in e.output?{type:"update",status:{queue:!0,title:e.output.title,message:e.output.error,visible:e.output.visible,duration:e.output.duration,stage:"error",code:e.code,success:e.success}}:{type:"complete",status:{queue:!0,message:e.success?void 0:e.output.error,stage:e.success?"complete":"error",code:e.code,progress_data:e.progress_data,changed_state_ids:e.success?e.output.changed_state_ids:void 0},data:e.success?e.output:null};case"process_starts":return{type:"update",status:{queue:!0,stage:"pending",code:e.code,size:e.rank,position:0,success:e.success,eta:e.eta},original_msg:"process_starts"}}return{type:"none",status:{stage:"error",queue:!0}}}const Lt=(e=[],s)=>{const t=s?s.parameters:[];if(Array.isArray(e))return s&&t.length>0&&e.length>t.length&&console.warn("Too many arguments provided for the endpoint."),e;const n=[],i=Object.keys(e);return t.forEach((a,o)=>{if(e.hasOwnProperty(a.parameter_name))n[o]=e[a.parameter_name];else if(a.parameter_has_default)n[o]=a.parameter_default;else throw new Error(`No value provided for required parameter: ${a.parameter_name}`)}),i.forEach(a=>{if(!t.some(o=>o.parameter_name===a))throw new Error(`Parameter \`${a}\` is not a valid keyword argument. Please refer to the API for usage.`)}),n.forEach((a,o)=>{if(a===void 0&&!t[o].parameter_has_default)throw new Error(`No value provided for required parameter: ${t[o].parameter_name}`)}),n};async function Ut(){if(this.api_info)return this.api_info;const{hf_token:e}=this.options,{config:s}=this,t={"Content-Type":"application/json"};if(e&&(t.Authorization=`Bearer ${e}`),!!s)try{let n,i;if(typeof window<"u"&&window.gradio_api_info)i=window.gradio_api_info;else{if(Ie(s?.version||"2.0.0","3.30")<0)n=await this.fetch(xt,{method:"POST",body:JSON.stringify({serialize:!1,config:JSON.stringify(s)}),headers:t,credentials:"include"});else{const a=we(s.root,this.api_prefix,ft);n=await this.fetch(a,{headers:t,credentials:"include"})}if(!n.ok)throw new Error(Q);i=await n.json()}return"api"in i&&(i=i.api),i.named_endpoints["/predict"]&&!i.unnamed_endpoints[0]&&(i.unnamed_endpoints[0]=i.named_endpoints["/predict"]),Dt(i,s,this.api_map)}catch(n){throw new Error("Could not get API info. "+n.message)}}async function Bt(e,s,t){var n;const i={};(n=this==null?void 0:this.options)!=null&&n.hf_token&&(i.Authorization=`Bearer ${this.options.hf_token}`);const a=1e3,o=[];let r;for(let d=0;d<s.length;d+=a){const h=s.slice(d,d+a),g=new FormData;h.forEach(_=>{g.append("files",_)});try{const _=t?`${e}${this.api_prefix}/${qe}?upload_id=${t}`:`${e}${this.api_prefix}/${qe}`;r=await this.fetch(_,{method:"POST",body:g,headers:i,credentials:"include"})}catch(_){throw new Error(Q+_.message)}if(!r.ok){const _=await r.text();return{error:`HTTP ${r.status}: ${_}`}}const u=await r.json();u&&o.push(...u)}return{files:o}}async function It(e,s,t,n){let i=(Array.isArray(e)?e:[e]).map(o=>o.blob);const a=i.filter(o=>o.size>(n??1/0));if(a.length)throw new Error(`File size exceeds the maximum allowed size of ${n} bytes: ${a.map(o=>o.name).join(", ")}`);return await Promise.all(await this.upload_files(s,i,t).then(async o=>{if(o.error)throw new Error(o.error);return o.files?o.files.map((r,d)=>new ke({...e[d],path:r,url:`${s}${this.api_prefix}/file=${r}`})):[]}))}class ke{constructor({path:s,url:t,orig_name:n,size:i,blob:a,is_stream:o,mime_type:r,alt_text:d,b64:h}){m(this,"path"),m(this,"url"),m(this,"orig_name"),m(this,"size"),m(this,"blob"),m(this,"is_stream"),m(this,"mime_type"),m(this,"alt_text"),m(this,"b64"),m(this,"meta",{_type:"gradio.FileData"}),this.path=s,this.url=t,this.orig_name=n,this.size=i,this.blob=t?void 0:a,this.is_stream=o,this.mime_type=r,this.alt_text=d,this.b64=h}}class Ft{constructor(s,t){m(this,"type"),m(this,"command"),m(this,"meta"),m(this,"fileData"),this.type="command",this.command=s,this.meta=t}}typeof process<"u"&&process.versions&&process.versions.node;function De(e,s,t){for(;t.length>1;){const i=t.shift();if(typeof i=="string"||typeof i=="number")e=e[i];else throw new Error("Invalid key type")}const n=t.shift();if(typeof n=="string"||typeof n=="number")e[n]=s;else throw new Error("Invalid key type")}async function ye(e,s=void 0,t=[],n=!1,i=void 0){if(Array.isArray(e)){let a=[];return await Promise.all(e.map(async(o,r)=>{var d;let h=t.slice();h.push(String(r));const g=await ye(e[r],n?((d=i?.parameters[r])==null?void 0:d.component)||void 0:s,h,!1,i);a=a.concat(g)})),a}else{if(globalThis.Buffer&&e instanceof globalThis.Buffer||e instanceof Blob)return[{path:t,blob:new Blob([e]),type:s}];if(typeof e=="object"&&e!==null){let a=[];for(const o of Object.keys(e)){const r=[...t,o],d=e[o];a=a.concat(await ye(d,void 0,r,!1,i))}return a}}return[]}function Jt(e,s){var t,n;let i=(n=(t=s?.dependencies)==null?void 0:t.find(a=>a.id==e))==null?void 0:n.queue;return i!=null?!i:!s.enable_queue}function Wt(e,s){return new Promise((t,n)=>{const i=new MessageChannel;i.port1.onmessage=({data:a})=>{i.port1.close(),t(a)},window.parent.postMessage(e,s,[i.port2])})}function ae(e,s,t,n,i=!1){if(n==="input"&&!i)throw new Error("Invalid code path. Cannot skip state inputs for input.");if(n==="output"&&i)return e;let a=[],o=0;const r=n==="input"?s.inputs:s.outputs;for(let d=0;d<r.length;d++){const h=r[d],g=t.find(u=>u.id===h);if(g?.type==="state"){if(i)if(e.length===r.length){const u=e[o];a.push(u),o++}else a.push(null);else{o++;continue}continue}else{const u=e[o];a.push(u),o++}}return a}async function Mt(e,s,t){const n=this;await Gt(n,s);const i=await ye(s,void 0,[],!0,t);return(await Promise.all(i.map(async({path:a,blob:o,type:r})=>{if(!o)return{path:a,type:r};const d=await n.upload_files(e,[o]),h=d.files&&d.files[0];return{path:a,file_url:h,type:r,name:typeof File<"u"&&o instanceof File?o?.name:void 0}}))).forEach(({path:a,file_url:o,type:r,name:d})=>{if(r==="Gallery")De(s,o,a);else if(o){const h=new ke({path:o,orig_name:d});De(s,h,a)}}),s}async function Gt(e,s){var t,n;if(!((t=e.config)!=null&&t.root||(n=e.config)!=null&&n.root_url))throw new Error(Me);await Ve(e,s)}async function Ve(e,s,t=[]){for(const n in s)s[n]instanceof Ft?await Ht(e,s,n):typeof s[n]=="object"&&s[n]!==null&&await Ve(e,s[n],[...t,n])}async function Ht(e,s,t){var n,i;let a=s[t];const o=((n=e.config)==null?void 0:n.root)||((i=e.config)==null?void 0:i.root_url);if(!o)throw new Error(Me);try{let r,d;if(typeof process<"u"&&process.versions&&process.versions.node){const _=await Pe(()=>import("./__vite-browser-external-DYxpcVy9.BIHI7g3E.js"),[]);d=(await Pe(async()=>{const{resolve:$}=await import("./__vite-browser-external-DYxpcVy9.BIHI7g3E.js");return{resolve:$}},[])).resolve(process.cwd(),a.meta.path),r=await _.readFile(d)}else throw new Error(Nt);const h=new Blob([r],{type:"application/octet-stream"}),g=await e.upload_files(o,[h]),u=g.files&&g.files[0];if(u){const _=new ke({path:u,orig_name:a.meta.name||""});s[t]=_}}catch(r){console.error(Ct,r)}}async function Vt(e,s,t){const n={"Content-Type":"application/json"};this.options.hf_token&&(n.Authorization=`Bearer ${this.options.hf_token}`);try{var i=await this.fetch(e,{method:"POST",body:JSON.stringify(s),headers:{...n,...t},credentials:"include"})}catch{return[{error:Q},500]}let a,o;try{a=await i.json(),o=i.status}catch(r){a={error:`Could not parse server response: ${r}`},o=500}return[a,o]}async function Yt(e,s={}){let t=!1,n=!1;if(!this.config)throw new Error("Could not resolve app config");if(typeof e=="number")this.config.dependencies.find(i=>i.id==e);else{const i=e.replace(/^\//,"");this.config.dependencies.find(a=>a.id==this.api_map[i])}return new Promise(async(i,a)=>{const o=this.submit(e,s,null,null,!0);let r;for await(const d of o)d.type==="data"&&(n&&i(r),t=!0,r=d),d.type==="status"&&(d.stage==="error"&&a(d),d.stage==="complete"&&(n=!0,t&&i(r)))})}async function oe(e,s,t){let n=s==="subdomain"?`https://huggingface.co/api/spaces/by-subdomain/${e}`:`https://huggingface.co/api/spaces/${e}`,i,a;try{if(i=await fetch(n),a=i.status,a!==200)throw new Error;i=await i.json()}catch{t({status:"error",load_status:"error",message:kt,detail:"NOT_FOUND"});return}if(!i||a!==200)return;const{runtime:{stage:o},id:r}=i;switch(o){case"STOPPED":case"SLEEPING":t({status:"sleeping",load_status:"pending",message:"Space is asleep. Waking it up...",detail:o}),setTimeout(()=>{oe(e,s,t)},1e3);break;case"PAUSED":t({status:"paused",load_status:"error",message:"This space has been paused by the author. If you would like to try this demo, consider duplicating the space.",detail:o,discussions_enabled:await Re(r)});break;case"RUNNING":case"RUNNING_BUILDING":t({status:"running",load_status:"complete",message:"Space is running.",detail:o});break;case"BUILDING":t({status:"building",load_status:"pending",message:"Space is building...",detail:o}),setTimeout(()=>{oe(e,s,t)},1e3);break;case"APP_STARTING":t({status:"starting",load_status:"pending",message:"Space is starting...",detail:o}),setTimeout(()=>{oe(e,s,t)},1e3);break;default:t({status:"space_error",load_status:"error",message:"This space is experiencing an issue.",detail:o,discussions_enabled:await Re(r)});break}}const Ye=async(e,s)=>{let t=0;const n=12,i=5e3;return new Promise(a=>{oe(e,xe.test(e)?"space_name":"subdomain",o=>{s(o),o.status==="running"||o.status==="error"||o.status==="paused"||o.status==="space_error"?a():(o.status==="sleeping"||o.status==="building")&&(t<n?(t++,setTimeout(()=>{Ye(e,s).then(a)},i)):a())})})},Qt=/^(?=[^]*\b[dD]iscussions{0,1}\b)(?=[^]*\b[dD]isabled\b)[^]*$/;async function Re(e){try{const s=await fetch(`https://huggingface.co/api/spaces/${e}/discussions`,{method:"HEAD"}),t=s.headers.get("x-error-message");return!(!s.ok||t&&Qt.test(t))}catch{return!1}}async function Zt(e,s){const t={};s&&(t.Authorization=`Bearer ${s}`);try{const n=await fetch(`https://huggingface.co/api/spaces/${e}/${mt}`,{headers:t});if(n.status!==200)throw new Error("Space hardware could not be obtained.");const{hardware:i}=await n.json();return i.current}catch(n){throw new Error(n.message)}}async function Xt(e,s,t){const n={};t&&(n.Authorization=`Bearer ${t}`);const i={seconds:s};try{const a=await fetch(`https://huggingface.co/api/spaces/${e}/${_t}`,{method:"POST",headers:{"Content-Type":"application/json",...n},body:JSON.stringify(i)});if(a.status!==200)throw new Error("Could not set sleep timeout on duplicated Space. Please visit *ADD HF LINK TO SETTINGS* to set a timeout manually to reduce billing charges.");return await a.json()}catch(a){throw new Error(a.message)}}const ze=["cpu-basic","cpu-upgrade","cpu-xl","t4-small","t4-medium","a10g-small","a10g-large","a10g-largex2","a10g-largex4","a100-large","zero-a10g","h100","h100x8"];async function Kt(e,s){const{hf_token:t,private:n,hardware:i,timeout:a,auth:o}=s;if(i&&!ze.includes(i))throw new Error(`Invalid hardware type provided. Valid types are: ${ze.map(w=>`"${w}"`).join(",")}.`);const{http_protocol:r,host:d}=await le(e,t);let h=null;if(o){const w=await Ge(r,d,o,fetch);w&&(h=He(w))}const g={Authorization:`Bearer ${t}`,"Content-Type":"application/json",...h?{Cookie:h.join("; ")}:{}},u=(await(await fetch("https://huggingface.co/api/whoami-v2",{headers:g})).json()).name,_=e.split("/")[1],$={repository:`${u}/${_}`};n&&($.private=!0);let R;try{i||(R=await Zt(e,t))}catch(w){throw Error(be+w.message)}const c=i||R||"cpu-basic";$.hardware=c;try{const w=await fetch(`https://huggingface.co/api/spaces/${e}/duplicate`,{method:"POST",headers:g,body:JSON.stringify($)});if(w.status===409)try{return await ve.connect(`${u}/${_}`,s)}catch(C){throw console.error("Failed to connect Client instance:",C),C}else if(w.status!==200)throw new Error(w.statusText);const N=await w.json();return await Xt(`${u}/${_}`,a||300,t),await ve.connect(es(N.url),s)}catch(w){throw new Error(w)}}function es(e){const s=/https:\/\/huggingface.co\/spaces\/([^/]+\/[^/]+)/,t=e.match(s);if(t)return t[1]}var M;class ts extends TransformStream{constructor(s={allowCR:!1}){super({transform:(t,n)=>{for(t=ie(this,M)+t;;){const i=t.indexOf(`
|
| 10 |
-
`),a=s.allowCR?t.indexOf("\r"):-1;if(a!==-1&&a!==t.length-1&&(i===-1||i-1>a)){n.enqueue(t.slice(0,a)),t=t.slice(a+1);continue}if(i===-1)break;const o=t[i-1]==="\r"?i-1:i;n.enqueue(t.slice(0,o)),t=t.slice(i+1)}
|
| 11 |
-
`+r:r):o==="event"?(a||(a={}),a[o]=r):o==="id"?(a||(a={}),a[o]=+r||r):o==="retry"&&(a||(a={}),a[o]=+r||void 0))}}async function as(e,s){let t=new Request(e,s);Le(t.headers,"Accept","text/event-stream"),Le(t.headers,"Content-Type","application/json");let n=await fetch(t);if(!n.ok)throw n;return is(n,t.signal)}async function os(){let{event_callbacks:e,unclosed_events:s,pending_stream_messages:t,stream_status:n,config:i,jwt:a}=this;const o=this;if(!i)throw new Error("Could not resolve app config");n.open=!0;let r=null,d=new URLSearchParams({session_hash:this.session_hash}).toString(),h=new URL(`${i.root}${this.api_prefix}/${Fe}?${d}`);if(a&&h.searchParams.set("__sign",a),r=this.stream(h),!r){console.warn("Cannot connect to SSE endpoint: "+h.toString());return}r.onmessage=async function(g){let u=JSON.parse(g.data);if(u.msg==="close_stream"){Ee(n,o.abort_controller);return}const _=u.event_id;if(!_)await Promise.all(Object.keys(e).map($=>e[$](u)));else if(e[_]&&i){u.msg==="process_completed"&&["sse","sse_v1","sse_v2","sse_v2.1","sse_v3"].includes(i.protocol)&&s.delete(_);let $=e[_];typeof window<"u"&&typeof document<"u"?setTimeout($,0,u):$(u)}else t[_]||(t[_]=[]),t[_].push(u)},r.onerror=async function(g){console.error(g),await Promise.all(Object.keys(e).map(u=>e[u]({msg:"broken_connection",message:Q})))}}function Ee(e,s){e&&(e.open=!1,s?.abort())}function rs(e,s,t){e[s]?t.data.forEach((n,i)=>{let a=cs(e[s][i],n);e[s][i]=a,t.data[i]=a}):(e[s]=[],t.data.forEach((n,i)=>{e[s][i]=n}))}function cs(e,s){return s.forEach(([t,n,i])=>{e=ls(e,n,t,i)}),e}function ls(e,s,t,n){if(s.length===0){if(t==="replace")return n;if(t==="append")return e+n;throw new Error(`Unsupported action: ${t}`)}let i=e;for(let o=0;o<s.length-1;o++)i=i[s[o]];const a=s[s.length-1];switch(t){case"replace":i[a]=n;break;case"append":i[a]+=n;break;case"add":Array.isArray(i)?i.splice(Number(a),0,n):i[a]=n;break;case"delete":Array.isArray(i)?i.splice(Number(a),1):delete i[a];break;default:throw new Error(`Unknown action: ${t}`)}return e}function us(e,s={}){const t={close:()=>{console.warn("Method not implemented.")},onerror:null,onmessage:null,onopen:null,readyState:0,url:e.toString(),withCredentials:!1,CONNECTING:0,OPEN:1,CLOSED:2,addEventListener:()=>{throw new Error("Method not implemented.")},dispatchEvent:()=>{throw new Error("Method not implemented.")},removeEventListener:()=>{throw new Error("Method not implemented.")}};return as(e,s).then(async n=>{t.readyState=t.OPEN;try{for await(const i of n)t.onmessage&&t.onmessage(i);t.readyState=t.CLOSED}catch(i){t.onerror&&t.onerror(i),t.readyState=t.CLOSED}}).catch(n=>{console.error(n),t.onerror&&t.onerror(n),t.readyState=t.CLOSED}),t}function ds(e,s={},t,n,i){var a;try{let o=function(v){(i||Ze[v.type])&&g(v)},r=function(){for(et=!0;se.length>0;)se.shift()({value:void 0,done:!0})},d=function(v){se.length>0?se.shift()(v):de.push(v)},h=function(v){d(ps(v)),r()},g=function(v){d({value:v,done:!1})},u=function(){return de.length>0?Promise.resolve(de.shift()):new Promise(v=>se.push(v))};const{hf_token:_}=this.options,{fetch:$,app_reference:R,config:c,session_hash:w,api_info:N,api_map:C,stream_status:H,pending_stream_messages:Z,pending_diff_streams:X,event_callbacks:K,unclosed_events:V,post_data:ee,options:W,api_prefix:f}=this,x=this;if(!N)throw new Error("No API found");if(!c)throw new Error("Could not resolve app config");let{fn_index:l,endpoint_info:U,dependency:q}=hs(N,e,C,c),re=Lt(s,U),k,B,J=c.protocol??"ws",$e="",Qe=()=>$e;const y=typeof e=="number"?"/predict":e;let te,O=null,z=!1,ue={},Y=typeof window<"u"&&typeof document<"u"?new URLSearchParams(window.location.search).toString():"";const Ze=((a=W?.events)==null?void 0:a.reduce((v,I)=>(v[I]=!0,v),{}))||{};async function Xe(){let v={},I={};J==="ws"?(k&&k.readyState===0?k.addEventListener("open",()=>{k.close()}):k.close(),v={fn_index:l,session_hash:w}):(v={event_id:O},I={event_id:O,session_hash:w,fn_index:l});try{if(!c)throw new Error("Could not resolve app config");"event_id"in I&&await $(`${c.root}${f}/${vt}`,{headers:{"Content-Type":"application/json"},method:"POST",body:JSON.stringify(I)}),await $(`${c.root}${f}/${yt}`,{headers:{"Content-Type":"application/json"},method:"POST",body:JSON.stringify(v)})}catch{console.warn("The `/reset` endpoint could not be called. Subsequent endpoint results may be unreliable.")}}const Ke=async v=>{await this._resolve_heartbeat(v)};async function je(v){if(!c)return;let I=v.render_id;c.components=[...c.components.filter(P=>P.props.rendered_in!==I),...v.components],c.dependencies=[...c.dependencies.filter(P=>P.rendered_in!==I),...v.dependencies];const pe=c.components.some(P=>P.type==="state"),j=c.dependencies.some(P=>P.targets.some(T=>T[1]==="unload"));c.connect_heartbeat=pe||j,await Ke(c),o({type:"render",data:v,endpoint:y,fn_index:l})}this.handle_blob(c.root,re,U).then(async v=>{var I;if(te={data:ae(v,q,c.components,"input",!0)||[],event_data:t,fn_index:l,trigger_id:n},Jt(l,c))o({type:"status",endpoint:y,stage:"pending",queue:!1,fn_index:l,time:new Date}),ee(`${c.root}${f}/run${y.startsWith("/")?y:`/${y}`}${Y?"?"+Y:""}`,{...te,session_hash:w}).then(([j,P])=>{const T=j.data;P==200?(o({type:"data",endpoint:y,fn_index:l,data:ae(T,q,c.components,"output",W.with_null_state),time:new Date,event_data:t,trigger_id:n}),j.render_config&&je(j.render_config),o({type:"status",endpoint:y,fn_index:l,stage:"complete",eta:j.average_duration,queue:!1,time:new Date})):o({type:"status",stage:"error",endpoint:y,fn_index:l,message:j.error,queue:!1,time:new Date})}).catch(j=>{o({type:"status",stage:"error",message:j.message,endpoint:y,fn_index:l,queue:!1,time:new Date})});else if(J=="ws"){const{ws_protocol:j,host:P}=await le(R,_);o({type:"status",stage:"pending",queue:!0,endpoint:y,fn_index:l,time:new Date});let T=new URL(`${j}://${Ot(P,c.root,!0)}/queue/join${Y?"?"+Y:""}`);this.jwt&&T.searchParams.set("__sign",this.jwt),k=new WebSocket(T),k.onclose=A=>{A.wasClean||o({type:"status",stage:"error",broken:!0,message:Q,queue:!0,endpoint:y,fn_index:l,time:new Date})},k.onmessage=function(A){const D=JSON.parse(A.data),{type:S,status:E,data:b}=_e(D,ue[l]);if(S==="update"&&E&&!z)o({type:"status",endpoint:y,fn_index:l,time:new Date,...E}),E.stage==="error"&&k.close();else if(S==="hash"){k.send(JSON.stringify({fn_index:l,session_hash:w}));return}else S==="data"?k.send(JSON.stringify({...te,session_hash:w})):S==="complete"?z=E:S==="log"?o({type:"log",title:b.title,log:b.log,level:b.level,endpoint:y,duration:b.duration,visible:b.visible,fn_index:l}):S==="generating"&&o({type:"status",time:new Date,...E,stage:E?.stage,queue:!0,endpoint:y,fn_index:l});b&&(o({type:"data",time:new Date,data:ae(b.data,q,c.components,"output",W.with_null_state),endpoint:y,fn_index:l,event_data:t,trigger_id:n}),z&&(o({type:"status",time:new Date,...z,stage:E?.stage,queue:!0,endpoint:y,fn_index:l}),k.close()))},Ie(c.version||"2.0.0","3.6")<0&&addEventListener("open",()=>k.send(JSON.stringify({hash:w})))}else if(J=="sse"){o({type:"status",stage:"pending",queue:!0,endpoint:y,fn_index:l,time:new Date});var pe=new URLSearchParams({fn_index:l.toString(),session_hash:w}).toString();let j=new URL(`${c.root}${f}/${Fe}?${Y?Y+"&":""}${pe}`);if(this.jwt&&j.searchParams.set("__sign",this.jwt),B=this.stream(j),!B)return Promise.reject(new Error("Cannot connect to SSE endpoint: "+j.toString()));B.onmessage=async function(P){const T=JSON.parse(P.data),{type:A,status:D,data:S}=_e(T,ue[l]);if(A==="update"&&D&&!z)o({type:"status",endpoint:y,fn_index:l,time:new Date,...D}),D.stage==="error"&&(B?.close(),r());else if(A==="data"){let[E,b]=await ee(`${c.root}${f}/queue/data`,{...te,session_hash:w,event_id:O});b!==200&&(o({type:"status",stage:"error",message:Q,queue:!0,endpoint:y,fn_index:l,time:new Date}),B?.close(),r())}else A==="complete"?z=D:A==="log"?o({type:"log",title:S.title,log:S.log,level:S.level,endpoint:y,duration:S.duration,visible:S.visible,fn_index:l}):(A==="generating"||A==="streaming")&&o({type:"status",time:new Date,...D,stage:D?.stage,queue:!0,endpoint:y,fn_index:l});S&&(o({type:"data",time:new Date,data:ae(S.data,q,c.components,"output",W.with_null_state),endpoint:y,fn_index:l,event_data:t,trigger_id:n}),z&&(o({type:"status",time:new Date,...z,stage:D?.stage,queue:!0,endpoint:y,fn_index:l}),B?.close(),r()))}}else if(J=="sse_v1"||J=="sse_v2"||J=="sse_v2.1"||J=="sse_v3"){o({type:"status",stage:"pending",queue:!0,endpoint:y,fn_index:l,time:new Date});let j="";typeof window<"u"&&typeof document<"u"&&(j=(I=window?.location)==null?void 0:I.hostname);const P=j.includes(".dev.")?`https://moon-${j.split(".")[1]}.dev.spaces.huggingface.tech`:"https://huggingface.co";(typeof window<"u"&&typeof document<"u"&&window.parent!=window&&window.supports_zerogpu_headers?Wt("zerogpu-headers",P):Promise.resolve(null)).then(T=>ee(`${c.root}${f}/${pt}?${Y}`,{...te,session_hash:w},T)).then(async([T,A])=>{if(A===503)o({type:"status",stage:"error",message:Je,queue:!0,endpoint:y,fn_index:l,time:new Date});else if(A===422)o({type:"status",stage:"error",message:T.detail,queue:!0,endpoint:y,fn_index:l,code:"validation_error",time:new Date}),r();else if(A!==200)o({type:"status",stage:"error",broken:!1,message:T.detail,queue:!0,endpoint:y,fn_index:l,time:new Date});else{O=T.event_id,$e=O;let D=async function(S){try{const{type:E,status:b,data:F,original_msg:tt}=_e(S,ue[l]);if(E=="heartbeat")return;if(E==="update"&&b&&!z)o({type:"status",endpoint:y,fn_index:l,time:new Date,original_msg:tt,...b});else if(E==="complete")z=b;else if(E=="unexpected_error"||E=="broken_connection"){console.error("Unexpected error",b?.message);const st=E==="broken_connection";o({type:"status",stage:"error",message:b?.message||"An Unexpected Error Occurred!",queue:!0,endpoint:y,broken:st,session_not_found:b?.session_not_found,fn_index:l,time:new Date})}else if(E==="log"){o({type:"log",title:F.title,log:F.log,level:F.level,endpoint:y,duration:F.duration,visible:F.visible,fn_index:l});return}else(E==="generating"||E==="streaming")&&(o({type:"status",time:new Date,...b,stage:b?.stage,queue:!0,endpoint:y,fn_index:l}),F&&q.connection!=="stream"&&["sse_v2","sse_v2.1","sse_v3"].includes(J)&&rs(X,O,F));F&&(o({type:"data",time:new Date,data:ae(F.data,q,c.components,"output",W.with_null_state),endpoint:y,fn_index:l}),F.render_config&&await je(F.render_config),z&&(o({type:"status",time:new Date,...z,stage:b?.stage,queue:!0,endpoint:y,fn_index:l}),r())),(b?.stage==="complete"||b?.stage==="error")&&(K[O]&&delete K[O],O in X&&delete X[O])}catch(E){console.error("Unexpected client exception",E),o({type:"status",stage:"error",message:"An Unexpected Error Occurred!",queue:!0,endpoint:y,fn_index:l,time:new Date}),["sse_v2","sse_v2.1","sse_v3"].includes(J)&&(Ee(H,x.abort_controller),H.open=!1,r())}};O in Z&&(Z[O].forEach(S=>D(S)),delete Z[O]),K[O]=D,V.add(O),H.open||await this.open_stream()}})}});let et=!1;const de=[],se=[],Se={[Symbol.asyncIterator]:()=>Se,next:u,throw:async v=>(h(v),u()),return:async()=>(r(),u()),cancel:Xe,event_id:Qe};return Se}catch(o){throw console.error("Submit function encountered an error:",o),o}}function ps(e){return{then:(s,t)=>t(e)}}function hs(e,s,t,n){let i,a,o;if(typeof s=="number")i=s,a=e.unnamed_endpoints[i],o=n.dependencies.find(r=>r.id==s);else{const r=s.replace(/^\//,"");i=t[r],a=e.named_endpoints[s.trim()],o=n.dependencies.find(d=>d.id==t[r])}if(typeof i!="number")throw new Error("There is no endpoint matching that name of fn_index matching that number.");return{fn_index:i,endpoint_info:a,dependency:o}}class ve{constructor(s,t={events:["data"]}){m(this,"app_reference"),m(this,"options"),m(this,"deep_link",null),m(this,"config"),m(this,"api_prefix",""),m(this,"api_info"),m(this,"api_map",{}),m(this,"session_hash",Math.random().toString(36).substring(2)),m(this,"jwt",!1),m(this,"last_status",{}),m(this,"cookies",null),m(this,"stream_status",{open:!1}),m(this,"closed",!1),m(this,"pending_stream_messages",{}),m(this,"pending_diff_streams",{}),m(this,"event_callbacks",{}),m(this,"unclosed_events",new Set),m(this,"heartbeat_event",null),m(this,"abort_controller",null),m(this,"stream_instance",null),m(this,"current_payload"),m(this,"ws_map",{}),m(this,"view_api"),m(this,"upload_files"),m(this,"upload"),m(this,"handle_blob"),m(this,"post_data"),m(this,"submit"),m(this,"predict"),m(this,"open_stream"),m(this,"resolve_config"),m(this,"resolve_cookies");var n;this.app_reference=s,this.deep_link=((n=t.query_params)==null?void 0:n.deep_link)||null,t.events||(t.events=["data"]),this.options=t,this.current_payload={},this.view_api=Ut.bind(this),this.upload_files=Bt.bind(this),this.handle_blob=Mt.bind(this),this.post_data=Vt.bind(this),this.submit=ds.bind(this),this.predict=Yt.bind(this),this.open_stream=os.bind(this),this.resolve_config=qt.bind(this),this.resolve_cookies=Tt.bind(this),this.upload=It.bind(this),this.fetch=this.fetch.bind(this),this.handle_space_success=this.handle_space_success.bind(this),this.stream=this.stream.bind(this)}get_url_config(s=null){if(!this.config)throw new Error(G);s===null&&(s=window.location.href);const t=o=>o.replace(/^\/+|\/+$/g,"");let n=t(new URL(this.config.root).pathname),i=t(new URL(s).pathname),a;return i.startsWith(n)?a=t(i.substring(n.length)):a="",this.get_page_config(a)}get_page_config(s){if(!this.config)throw new Error(G);let t=this.config;return s in t.page||(s=""),{...t,current_page:s,layout:t.page[s].layout,components:t.components.filter(n=>t.page[s].components.includes(n.id)),dependencies:this.config.dependencies.filter(n=>t.page[s].dependencies.includes(n.id))}}fetch(s,t){const n=new Headers(t?.headers||{});if(this&&this.cookies&&n.append("Cookie",this.cookies),this&&this.options.headers)for(const i in this.options.headers)n.append(i,this.options.headers[i]);return fetch(s,{...t,headers:n})}stream(s){const t=new Headers;if(this&&this.cookies&&t.append("Cookie",this.cookies),this&&this.options.headers)for(const n in this.options.headers)t.append(n,this.options.headers[n]);return this&&this.options.hf_token&&t.append("Authorization",`Bearer ${this.options.hf_token}`),this.abort_controller=new AbortController,this.stream_instance=us(s.toString(),{credentials:"include",headers:t,signal:this.abort_controller.signal}),this.stream_instance}async init(){var s;this.options.auth&&await this.resolve_cookies(),await this._resolve_config().then(({config:t})=>this._resolve_heartbeat(t)),this.api_info=await this.view_api(),this.api_map=Pt(((s=this.config)==null?void 0:s.dependencies)||[])}async _resolve_heartbeat(s){if(s&&(this.config=s,this.api_prefix=s.api_prefix||"",this.config&&this.config.connect_heartbeat&&this.config.space_id&&this.options.hf_token&&(this.jwt=await Te(this.config.space_id,this.options.hf_token,this.cookies))),s.space_id&&this.options.hf_token&&(this.jwt=await Te(s.space_id,this.options.hf_token)),this.config&&this.config.connect_heartbeat){const t=new URL(`${this.config.root}${this.api_prefix}/${gt}/${this.session_hash}`);this.jwt&&t.searchParams.set("__sign",this.jwt),this.heartbeat_event||(this.heartbeat_event=this.stream(t))}}static async connect(s,t={events:["data"]}){const n=new this(s,t);return t.session_hash&&(n.session_hash=t.session_hash),await n.init(),n}async reconnect(){const s=new URL(`${this.config.root}${this.api_prefix}/${bt}`);let t;try{const n=await this.fetch(s);if(!n.ok)throw new Error;t=(await n.json()).app_id}catch{return"broken"}return t!==this.config.app_id?"changed":"connected"}close(){this.closed=!0,Ee(this.stream_status,this.abort_controller)}set_current_payload(s){this.current_payload=s}static async duplicate(s,t={events:["data"]}){return Kt(s,t)}async _resolve_config(){const{http_protocol:s,host:t,space_id:n}=await le(this.app_reference,this.options.hf_token),{status_callback:i}=this.options;n&&i&&await Ye(n,i);let a;try{let o=`${s}//${t}`;if(a=await this.resolve_config(o),!a)throw new Error(G);return this.config_success(a)}catch(o){if(n&&i)oe(n,xe.test(n)?"space_name":"subdomain",this.handle_space_success);else throw i&&i({status:"error",message:"Could not load this space.",load_status:"error",detail:"NOT_FOUND"}),Error(o)}}async config_success(s){if(this.config=s,this.api_prefix=s.api_prefix||"",this.config.auth_required)return this.prepare_return_obj();try{this.api_info=await this.view_api()}catch(t){console.error(Et+t.message)}return this.prepare_return_obj()}async handle_space_success(s){var t;if(!this)throw new Error(G);const{status_callback:n}=this.options;if(n&&n(s),s.status==="running")try{if(this.config=await this._resolve_config(),this.api_prefix=((t=this==null?void 0:this.config)==null?void 0:t.api_prefix)||"",!this.config)throw new Error(G);return await this.config_success(this.config)}catch(i){throw n&&n({status:"error",message:"Could not load this space.",load_status:"error",detail:"NOT_FOUND"}),i}}async component_server(s,t,n){var i;if(!this.config)throw new Error(G);const a={},{hf_token:o}=this.options,{session_hash:r}=this;o&&(a.Authorization=`Bearer ${this.options.hf_token}`);let d,h=this.config.components.find(u=>u.id===s);(i=h?.props)!=null&&i.root_url?d=h.props.root_url:d=this.config.root;let g;if("binary"in n){g=new FormData;for(const u in n.data)u!=="binary"&&g.append(u,n.data[u]);g.set("component_id",s.toString()),g.set("fn_name",t),g.set("session_hash",r)}else g=JSON.stringify({data:n,component_id:s,fn_name:t,session_hash:r}),a["Content-Type"]="application/json";o&&(a.Authorization=`Bearer ${o}`);try{const u=await this.fetch(`${d}${this.api_prefix}/${wt}/`,{method:"POST",body:g,headers:a,credentials:"include"});if(!u.ok)throw new Error("Could not connect to component server: "+u.statusText);return await u.json()}catch(u){console.warn(u)}}set_cookies(s){this.cookies=He(s).join("; ")}prepare_return_obj(){return{config:this.config,predict:this.predict,submit:this.submit,view_api:this.view_api,component_server:this.component_server}}async connect_ws(s){return new Promise((t,n)=>{let i;try{i=new WebSocket(s)}catch{this.ws_map[s]="failed";return}this.ws_map[s]="pending",i.onopen=()=>{this.ws_map[s]=i,t()},i.onerror=a=>{console.error("WebSocket error:",a),this.close_ws(s),this.ws_map[s]="failed",t()},i.onclose=()=>{this.ws_map[s]="closed"},i.onmessage=a=>{}})}async send_ws_message(s,t){if(!(s in this.ws_map))await this.connect_ws(s);else if(this.ws_map[s]==="pending"||this.ws_map[s]==="closed"||this.ws_map[s]==="failed")return;const n=this.ws_map[s];n instanceof WebSocket?n.send(JSON.stringify(t)):this.post_data(s,t)}async close_ws(s){if(s in this.ws_map){const t=this.ws_map[s];t instanceof WebSocket&&(t.close(),delete this.ws_map[s])}}}async function ge(e,s,t){return(await e.predict(s,t)).data[0]}const ms=()=>{const e=L.useRef(null),[s,t]=L.useState(!1),[n,i]=L.useState(null),[a,o]=L.useState({username:null,member:!1}),[r,d]=L.useState([]),[h,g]=L.useState(!1),[u,_]=L.useState("polls"),[$,R]=L.useState(""),[c,w]=L.useState(["",""]),[N,C]=L.useState(null),H=20,Z=(f,x)=>{w(l=>l.map((U,q)=>q===f?x:U))},X=()=>{w(f=>f.length>=H?f:[...f,""])},K=f=>{w(x=>x.length<=1?x:x.filter((l,U)=>U!==f))},V=L.useCallback(async()=>{const f=e.current;if(f)try{const x=await ge(f,"/polls",[]);o(x.me),d(x.polls),i(null)}catch{i("Cannot reach the voting backend.")}},[]);L.useEffect(()=>{let f=!1;return(async()=>{try{e.current=await ve.connect(window.location.origin+"/gradio"),f||(t(!0),await V())}catch{f||i("Cannot reach the voting backend.")}})(),()=>{f=!0}},[V]);const ee=async(f,x)=>{if(!(h||!e.current)){g(!0);try{const l=await ge(e.current,"/vote",[f,x]);C({kind:l.ok?"ok":"err",text:l.message}),l.ok&&l.poll?d(U=>U.map(q=>q.id===l.poll.id?l.poll:q)):await V()}catch{C({kind:"err",text:"Vote failed — please retry."})}finally{g(!1)}}},W=async()=>{if(!(h||!e.current)){g(!0);try{const f=await ge(e.current,"/create_poll",[$,c.filter(x=>x.trim()).join(`
|
| 12 |
-
`)]);
|
|
|
|
| 1 |
+
import{r as L}from"./index.qNTDzdXh.js";var ge={exports:{}},se={};/**
|
| 2 |
* @license React
|
| 3 |
* react-jsx-runtime.production.js
|
| 4 |
*
|
|
|
|
| 6 |
*
|
| 7 |
* This source code is licensed under the MIT license found in the
|
| 8 |
* LICENSE file in the root directory of this source tree.
|
| 9 |
+
*/var Ce;function nt(){if(Ce)return se;Ce=1;var e=Symbol.for("react.transitional.element"),s=Symbol.for("react.fragment");function t(n,i,a){var o=null;if(a!==void 0&&(o=""+a),i.key!==void 0&&(o=""+i.key),"key"in i){a={};for(var r in i)r!=="key"&&(a[r]=i[r])}else a=i;return i=a.ref,{$$typeof:e,type:n,key:o,ref:i!==void 0?i:null,props:a}}return se.Fragment=s,se.jsx=t,se.jsxs=t,se}var qe;function it(){return qe||(qe=1,ge.exports=nt()),ge.exports}var l=it();const at="modulepreload",ot=function(e){return"/"+e},Oe={},Pe=function(s,t,n){let i=Promise.resolve();if(t&&t.length>0){let o=function(h){return Promise.all(h.map(g=>Promise.resolve(g).then(d=>({status:"fulfilled",value:d}),d=>({status:"rejected",reason:d}))))};document.getElementsByTagName("link");const r=document.querySelector("meta[property=csp-nonce]"),u=r?.nonce||r?.getAttribute("nonce");i=o(t.map(h=>{if(h=ot(h),h in Oe)return;Oe[h]=!0;const g=h.endsWith(".css"),d=g?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${h}"]${d}`))return;const m=document.createElement("link");if(m.rel=g?"stylesheet":at,g||(m.as="script"),m.crossOrigin="",m.href=h,u&&m.setAttribute("nonce",u),document.head.appendChild(m),g)return new Promise((E,R)=>{m.addEventListener("load",E),m.addEventListener("error",()=>R(new Error(`Unable to preload CSS for ${h}`)))})}))}function a(o){const r=new Event("vite:preloadError",{cancelable:!0});if(r.payload=o,window.dispatchEvent(r),!r.defaultPrevented)throw o}return i.then(o=>{for(const r of o||[])r.status==="rejected"&&a(r.reason);return s().catch(a)})};var rt=Object.defineProperty,Be=e=>{throw TypeError(e)},ct=(e,s,t)=>s in e?rt(e,s,{enumerable:!0,configurable:!0,writable:!0,value:t}):e[s]=t,f=(e,s,t)=>ct(e,typeof s!="symbol"?s+"":s,t),Ie=(e,s,t)=>s.has(e)||Be("Cannot "+t),ne=(e,s,t)=>(Ie(e,s,"read from private field"),s.get(e)),lt=(e,s,t)=>s.has(e)?Be("Cannot add the same private member more than once"):s instanceof WeakSet?s.add(e):s.set(e,t),dt=(e,s,t,n)=>(Ie(e,s,"write to private field"),s.set(e,t),t),_e=new Intl.Collator(0,{numeric:1}).compare;function Fe(e,s,t){return e=e.split("."),s=s.split("."),_e(e[0],s[0])||_e(e[1],s[1])||(s[2]=s.slice(2).join("."),t=/[.-]/.test(e[2]=e.slice(2).join(".")),t==/[.-]/.test(s[2])?_e(e[2],s[2]):t?-1:1)}const ut="host",Je="queue/data",pt="queue/join",Te="upload",ht="login",ce="config",ft="info",mt="runtime",gt="sleeptime",_t="heartbeat",wt="component_server",yt="reset",bt="cancel",vt="app_id",xt="https://gradio-space-api-fetcher-v2.hf.space/api",We="This application is currently busy. Please try again. ",X="Connection errored out. ",H="Could not resolve app config. ",kt="Could not get space status. ",Et="Could not get API info. ",ke="Space metadata could not be loaded. ",jt="Invalid URL. A full URL path is required.",$t="Not authorized to access this space. ",Me="Invalid credentials. Could not login. ",St="Login credentials are required to access this space.",Nt="File system access is only available in Node.js environments",Ge="Root URL not found in client config",Ct="Error uploading file";function qt(e,s,t){return s.startsWith("http://")||s.startsWith("https://")?t?e:s:e+s}async function De(e,s,t){try{return(await(await fetch(`https://huggingface.co/api/spaces/${e}/jwt`,{headers:{Authorization:`Bearer ${s}`,...t?{Cookie:t}:{}}})).json()).token||!1}catch{return!1}}function Ot(e){let s={};return e.forEach(({api_name:t,id:n})=>{t&&(s[t]=n)}),s}async function Pt(e){const s=this.options.hf_token?{Authorization:`Bearer ${this.options.hf_token}`}:{};if(s["Content-Type"]="application/json",typeof window<"u"&&window.gradio_config&&location.origin!=="http://localhost:9876"){if(window.gradio_config.current_page&&(e=e.substring(0,e.lastIndexOf("/"))),window.gradio_config.dev_mode){let t=be(e,this.deep_link?ce+"?deep_link="+this.deep_link:ce);const n=await this.fetch(t,{headers:s,credentials:"include"}),i=await Ae(n,e,!!this.options.auth);window.gradio_config={...i,current_page:window.gradio_config.current_page}}return window.gradio_config.root=e,{...window.gradio_config}}else if(e){let t=be(e,this.deep_link?ce+"?deep_link="+this.deep_link:ce);const n=await this.fetch(t,{headers:s,credentials:"include"});return Ae(n,e,!!this.options.auth)}throw new Error(H)}async function Ae(e,s,t){var n,i;if(e?.status===401&&!t){const a=await e.json(),o=(n=a?.detail)==null?void 0:n.auth_message;throw new Error(o||St)}else if(e?.status===401&&t)throw new Error(Me);if(e?.status===200){let a=await e.json();return a.root=s,(i=a.dependencies)==null||i.forEach((o,r)=>{o.id===void 0&&(o.id=r)}),a}else if(e?.status===401)throw new Error($t);throw new Error(H)}async function Tt(){const{http_protocol:e,host:s}=await de(this.app_reference,this.options.hf_token);try{if(this.options.auth){const t=await He(e,s,this.options.auth,this.fetch,this.options.hf_token);t&&this.set_cookies(t)}}catch(t){throw Error(t.message)}}async function He(e,s,t,n,i){const a=new FormData;a.append("username",t?.[0]),a.append("password",t?.[1]);let o={};i&&(o.Authorization=`Bearer ${i}`);const r=await n(`${e}//${s}/${ht}`,{headers:o,method:"POST",body:a,credentials:"include"});if(r.status===200)return r.headers.get("set-cookie");throw r.status===401?new Error(Me):new Error(ke)}function we(e){if(e.startsWith("http")){const{protocol:s,host:t,pathname:n}=new URL(e);return{ws_protocol:s==="https:"?"wss":"ws",http_protocol:s,host:t+(n!=="/"?n:"")}}return{ws_protocol:"wss",http_protocol:"https:",host:new URL(e).host}}const Ve=e=>{let s=[];return e.split(/,(?=\s*[^\s=;]+=[^\s=;]+)/).forEach(t=>{const[n,i]=t.split(";")[0].split("=");n&&i&&s.push(`${n.trim()}=${i.trim()}`)}),s},Ee=/^[a-zA-Z0-9_\-\.]+\/[a-zA-Z0-9_\-\.]+$/,Dt=/.*hf\.space\/{0,1}.*$/;async function de(e,s){const t={};s&&(t.Authorization=`Bearer ${s}`);const n=e.trim().replace(/\/$/,"");if(Ee.test(n))try{const i=(await(await fetch(`https://huggingface.co/api/spaces/${n}/${ut}`,{headers:t})).json()).host;return{space_id:e,...we(i)}}catch{throw new Error(ke)}if(Dt.test(n)){const{ws_protocol:i,http_protocol:a,host:o}=we(n);return{space_id:o.split("/")[0].replace(".hf.space",""),ws_protocol:i,http_protocol:a,host:o}}return{space_id:!1,...we(n)}}const be=(...e)=>{try{return e.reduce((s,t)=>(s=s.replace(/\/+$/,""),t=t.replace(/^\/+/,""),new URL(t,s+"/").toString()))}catch{throw new Error(jt)}};function At(e,s,t){const n={named_endpoints:{},unnamed_endpoints:{}};return Object.keys(e).forEach(i=>{(i==="named_endpoints"||i==="unnamed_endpoints")&&(n[i]={},Object.entries(e[i]).forEach(([a,{parameters:o,returns:r}])=>{var u,h,g,d;const m=((u=s.dependencies.find(c=>c.api_name===a||c.api_name===a.replace("/","")))==null?void 0:u.id)||t[a.replace("/","")]||-1,E=m!==-1?(h=s.dependencies.find(c=>c.id==m))==null?void 0:h.types:{generator:!1,cancel:!1};if(m!==-1&&((d=(g=s.dependencies.find(c=>c.id==m))==null?void 0:g.inputs)==null?void 0:d.length)!==o.length){const c=s.dependencies.find(b=>b.id==m).inputs.map(b=>{var S;return(S=s.components.find(q=>q.id===b))==null?void 0:S.type});try{c.forEach((b,S)=>{if(b==="state"){const q={component:"state",example:null,parameter_default:null,parameter_has_default:!0,parameter_name:null,hidden:!0};o.splice(S,0,q)}})}catch(b){console.error(b)}}const R=(c,b,S,q)=>({...c,description:zt(c?.type,S),type:Rt(c?.type,b,S,q)||""});n[i][a]={parameters:o.map(c=>R(c,c?.component,c?.serializer,"parameter")),returns:r.map(c=>R(c,c?.component,c?.serializer,"return")),type:E}}))}),n}function Rt(e,s,t,n){if(s==="Api")return e.type;switch(e?.type){case"string":return"string";case"boolean":return"boolean";case"number":return"number"}if(t==="JSONSerializable"||t==="StringSerializable")return"any";if(t==="ListStringSerializable")return"string[]";if(s==="Image")return n==="parameter"?"Blob | File | Buffer":"string";if(t==="FileSerializable")return e?.type==="array"?n==="parameter"?"(Blob | File | Buffer)[]":"{ name: string; data: string; size?: number; is_file?: boolean; orig_name?: string}[]":n==="parameter"?"Blob | File | Buffer":"{ name: string; data: string; size?: number; is_file?: boolean; orig_name?: string}";if(t==="GallerySerializable")return n==="parameter"?"[(Blob | File | Buffer), (string | null)][]":"[{ name: string; data: string; size?: number; is_file?: boolean; orig_name?: string}, (string | null))][]"}function zt(e,s){return s==="GallerySerializable"?"array of [file, label] tuples":s==="ListStringSerializable"?"array of strings":s==="FileSerializable"?"array of files or single file":e?.description}function ye(e,s){switch(e.msg){case"send_data":return{type:"data"};case"send_hash":return{type:"hash"};case"queue_full":return{type:"update",status:{queue:!0,message:We,stage:"error",code:e.code,success:e.success}};case"heartbeat":return{type:"heartbeat"};case"unexpected_error":return{type:"unexpected_error",status:{queue:!0,message:e.message,session_not_found:e.session_not_found,stage:"error",success:!1}};case"broken_connection":return{type:"broken_connection",status:{queue:!0,message:e.message,stage:"error",success:!1}};case"estimation":return{type:"update",status:{queue:!0,stage:s||"pending",code:e.code,size:e.queue_size,position:e.rank,eta:e.rank_eta,success:e.success}};case"progress":return{type:"update",status:{queue:!0,stage:"pending",code:e.code,progress_data:e.progress_data,success:e.success}};case"log":return{type:"log",data:e};case"process_generating":return{type:"generating",status:{queue:!0,message:e.success?null:e.output.error,stage:e.success?"generating":"error",code:e.code,progress_data:e.progress_data,eta:e.average_duration,changed_state_ids:e.success?e.output.changed_state_ids:void 0},data:e.success?e.output:null};case"process_streaming":return{type:"streaming",status:{queue:!0,message:e.output.error,stage:"streaming",time_limit:e.time_limit,code:e.code,progress_data:e.progress_data,eta:e.eta},data:e.output};case"process_completed":return"error"in e.output?{type:"update",status:{queue:!0,title:e.output.title,message:e.output.error,visible:e.output.visible,duration:e.output.duration,stage:"error",code:e.code,success:e.success}}:{type:"complete",status:{queue:!0,message:e.success?void 0:e.output.error,stage:e.success?"complete":"error",code:e.code,progress_data:e.progress_data,changed_state_ids:e.success?e.output.changed_state_ids:void 0},data:e.success?e.output:null};case"process_starts":return{type:"update",status:{queue:!0,stage:"pending",code:e.code,size:e.rank,position:0,success:e.success,eta:e.eta},original_msg:"process_starts"}}return{type:"none",status:{stage:"error",queue:!0}}}const Lt=(e=[],s)=>{const t=s?s.parameters:[];if(Array.isArray(e))return s&&t.length>0&&e.length>t.length&&console.warn("Too many arguments provided for the endpoint."),e;const n=[],i=Object.keys(e);return t.forEach((a,o)=>{if(e.hasOwnProperty(a.parameter_name))n[o]=e[a.parameter_name];else if(a.parameter_has_default)n[o]=a.parameter_default;else throw new Error(`No value provided for required parameter: ${a.parameter_name}`)}),i.forEach(a=>{if(!t.some(o=>o.parameter_name===a))throw new Error(`Parameter \`${a}\` is not a valid keyword argument. Please refer to the API for usage.`)}),n.forEach((a,o)=>{if(a===void 0&&!t[o].parameter_has_default)throw new Error(`No value provided for required parameter: ${t[o].parameter_name}`)}),n};async function Ut(){if(this.api_info)return this.api_info;const{hf_token:e}=this.options,{config:s}=this,t={"Content-Type":"application/json"};if(e&&(t.Authorization=`Bearer ${e}`),!!s)try{let n,i;if(typeof window<"u"&&window.gradio_api_info)i=window.gradio_api_info;else{if(Fe(s?.version||"2.0.0","3.30")<0)n=await this.fetch(xt,{method:"POST",body:JSON.stringify({serialize:!1,config:JSON.stringify(s)}),headers:t,credentials:"include"});else{const a=be(s.root,this.api_prefix,ft);n=await this.fetch(a,{headers:t,credentials:"include"})}if(!n.ok)throw new Error(X);i=await n.json()}return"api"in i&&(i=i.api),i.named_endpoints["/predict"]&&!i.unnamed_endpoints[0]&&(i.unnamed_endpoints[0]=i.named_endpoints["/predict"]),At(i,s,this.api_map)}catch(n){throw new Error("Could not get API info. "+n.message)}}async function Bt(e,s,t){var n;const i={};(n=this==null?void 0:this.options)!=null&&n.hf_token&&(i.Authorization=`Bearer ${this.options.hf_token}`);const a=1e3,o=[];let r;for(let u=0;u<s.length;u+=a){const h=s.slice(u,u+a),g=new FormData;h.forEach(m=>{g.append("files",m)});try{const m=t?`${e}${this.api_prefix}/${Te}?upload_id=${t}`:`${e}${this.api_prefix}/${Te}`;r=await this.fetch(m,{method:"POST",body:g,headers:i,credentials:"include"})}catch(m){throw new Error(X+m.message)}if(!r.ok){const m=await r.text();return{error:`HTTP ${r.status}: ${m}`}}const d=await r.json();d&&o.push(...d)}return{files:o}}async function It(e,s,t,n){let i=(Array.isArray(e)?e:[e]).map(o=>o.blob);const a=i.filter(o=>o.size>(n??1/0));if(a.length)throw new Error(`File size exceeds the maximum allowed size of ${n} bytes: ${a.map(o=>o.name).join(", ")}`);return await Promise.all(await this.upload_files(s,i,t).then(async o=>{if(o.error)throw new Error(o.error);return o.files?o.files.map((r,u)=>new je({...e[u],path:r,url:`${s}${this.api_prefix}/file=${r}`})):[]}))}class je{constructor({path:s,url:t,orig_name:n,size:i,blob:a,is_stream:o,mime_type:r,alt_text:u,b64:h}){f(this,"path"),f(this,"url"),f(this,"orig_name"),f(this,"size"),f(this,"blob"),f(this,"is_stream"),f(this,"mime_type"),f(this,"alt_text"),f(this,"b64"),f(this,"meta",{_type:"gradio.FileData"}),this.path=s,this.url=t,this.orig_name=n,this.size=i,this.blob=t?void 0:a,this.is_stream=o,this.mime_type=r,this.alt_text=u,this.b64=h}}class Ft{constructor(s,t){f(this,"type"),f(this,"command"),f(this,"meta"),f(this,"fileData"),this.type="command",this.command=s,this.meta=t}}typeof process<"u"&&process.versions&&process.versions.node;function Re(e,s,t){for(;t.length>1;){const i=t.shift();if(typeof i=="string"||typeof i=="number")e=e[i];else throw new Error("Invalid key type")}const n=t.shift();if(typeof n=="string"||typeof n=="number")e[n]=s;else throw new Error("Invalid key type")}async function ve(e,s=void 0,t=[],n=!1,i=void 0){if(Array.isArray(e)){let a=[];return await Promise.all(e.map(async(o,r)=>{var u;let h=t.slice();h.push(String(r));const g=await ve(e[r],n?((u=i?.parameters[r])==null?void 0:u.component)||void 0:s,h,!1,i);a=a.concat(g)})),a}else{if(globalThis.Buffer&&e instanceof globalThis.Buffer||e instanceof Blob)return[{path:t,blob:new Blob([e]),type:s}];if(typeof e=="object"&&e!==null){let a=[];for(const o of Object.keys(e)){const r=[...t,o],u=e[o];a=a.concat(await ve(u,void 0,r,!1,i))}return a}}return[]}function Jt(e,s){var t,n;let i=(n=(t=s?.dependencies)==null?void 0:t.find(a=>a.id==e))==null?void 0:n.queue;return i!=null?!i:!s.enable_queue}function Wt(e,s){return new Promise((t,n)=>{const i=new MessageChannel;i.port1.onmessage=({data:a})=>{i.port1.close(),t(a)},window.parent.postMessage(e,s,[i.port2])})}function ie(e,s,t,n,i=!1){if(n==="input"&&!i)throw new Error("Invalid code path. Cannot skip state inputs for input.");if(n==="output"&&i)return e;let a=[],o=0;const r=n==="input"?s.inputs:s.outputs;for(let u=0;u<r.length;u++){const h=r[u],g=t.find(d=>d.id===h);if(g?.type==="state"){if(i)if(e.length===r.length){const d=e[o];a.push(d),o++}else a.push(null);else{o++;continue}continue}else{const d=e[o];a.push(d),o++}}return a}async function Mt(e,s,t){const n=this;await Gt(n,s);const i=await ve(s,void 0,[],!0,t);return(await Promise.all(i.map(async({path:a,blob:o,type:r})=>{if(!o)return{path:a,type:r};const u=await n.upload_files(e,[o]),h=u.files&&u.files[0];return{path:a,file_url:h,type:r,name:typeof File<"u"&&o instanceof File?o?.name:void 0}}))).forEach(({path:a,file_url:o,type:r,name:u})=>{if(r==="Gallery")Re(s,o,a);else if(o){const h=new je({path:o,orig_name:u});Re(s,h,a)}}),s}async function Gt(e,s){var t,n;if(!((t=e.config)!=null&&t.root||(n=e.config)!=null&&n.root_url))throw new Error(Ge);await Ye(e,s)}async function Ye(e,s,t=[]){for(const n in s)s[n]instanceof Ft?await Ht(e,s,n):typeof s[n]=="object"&&s[n]!==null&&await Ye(e,s[n],[...t,n])}async function Ht(e,s,t){var n,i;let a=s[t];const o=((n=e.config)==null?void 0:n.root)||((i=e.config)==null?void 0:i.root_url);if(!o)throw new Error(Ge);try{let r,u;if(typeof process<"u"&&process.versions&&process.versions.node){const m=await Pe(()=>import("./__vite-browser-external-DYxpcVy9.BIHI7g3E.js"),[]);u=(await Pe(async()=>{const{resolve:E}=await import("./__vite-browser-external-DYxpcVy9.BIHI7g3E.js");return{resolve:E}},[])).resolve(process.cwd(),a.meta.path),r=await m.readFile(u)}else throw new Error(Nt);const h=new Blob([r],{type:"application/octet-stream"}),g=await e.upload_files(o,[h]),d=g.files&&g.files[0];if(d){const m=new je({path:d,orig_name:a.meta.name||""});s[t]=m}}catch(r){console.error(Ct,r)}}async function Vt(e,s,t){const n={"Content-Type":"application/json"};this.options.hf_token&&(n.Authorization=`Bearer ${this.options.hf_token}`);try{var i=await this.fetch(e,{method:"POST",body:JSON.stringify(s),headers:{...n,...t},credentials:"include"})}catch{return[{error:X},500]}let a,o;try{a=await i.json(),o=i.status}catch(r){a={error:`Could not parse server response: ${r}`},o=500}return[a,o]}async function Yt(e,s={}){let t=!1,n=!1;if(!this.config)throw new Error("Could not resolve app config");if(typeof e=="number")this.config.dependencies.find(i=>i.id==e);else{const i=e.replace(/^\//,"");this.config.dependencies.find(a=>a.id==this.api_map[i])}return new Promise(async(i,a)=>{const o=this.submit(e,s,null,null,!0);let r;for await(const u of o)u.type==="data"&&(n&&i(r),t=!0,r=u),u.type==="status"&&(u.stage==="error"&&a(u),u.stage==="complete"&&(n=!0,t&&i(r)))})}async function ae(e,s,t){let n=s==="subdomain"?`https://huggingface.co/api/spaces/by-subdomain/${e}`:`https://huggingface.co/api/spaces/${e}`,i,a;try{if(i=await fetch(n),a=i.status,a!==200)throw new Error;i=await i.json()}catch{t({status:"error",load_status:"error",message:kt,detail:"NOT_FOUND"});return}if(!i||a!==200)return;const{runtime:{stage:o},id:r}=i;switch(o){case"STOPPED":case"SLEEPING":t({status:"sleeping",load_status:"pending",message:"Space is asleep. Waking it up...",detail:o}),setTimeout(()=>{ae(e,s,t)},1e3);break;case"PAUSED":t({status:"paused",load_status:"error",message:"This space has been paused by the author. If you would like to try this demo, consider duplicating the space.",detail:o,discussions_enabled:await ze(r)});break;case"RUNNING":case"RUNNING_BUILDING":t({status:"running",load_status:"complete",message:"Space is running.",detail:o});break;case"BUILDING":t({status:"building",load_status:"pending",message:"Space is building...",detail:o}),setTimeout(()=>{ae(e,s,t)},1e3);break;case"APP_STARTING":t({status:"starting",load_status:"pending",message:"Space is starting...",detail:o}),setTimeout(()=>{ae(e,s,t)},1e3);break;default:t({status:"space_error",load_status:"error",message:"This space is experiencing an issue.",detail:o,discussions_enabled:await ze(r)});break}}const Qe=async(e,s)=>{let t=0;const n=12,i=5e3;return new Promise(a=>{ae(e,Ee.test(e)?"space_name":"subdomain",o=>{s(o),o.status==="running"||o.status==="error"||o.status==="paused"||o.status==="space_error"?a():(o.status==="sleeping"||o.status==="building")&&(t<n?(t++,setTimeout(()=>{Qe(e,s).then(a)},i)):a())})})},Qt=/^(?=[^]*\b[dD]iscussions{0,1}\b)(?=[^]*\b[dD]isabled\b)[^]*$/;async function ze(e){try{const s=await fetch(`https://huggingface.co/api/spaces/${e}/discussions`,{method:"HEAD"}),t=s.headers.get("x-error-message");return!(!s.ok||t&&Qt.test(t))}catch{return!1}}async function Zt(e,s){const t={};s&&(t.Authorization=`Bearer ${s}`);try{const n=await fetch(`https://huggingface.co/api/spaces/${e}/${mt}`,{headers:t});if(n.status!==200)throw new Error("Space hardware could not be obtained.");const{hardware:i}=await n.json();return i.current}catch(n){throw new Error(n.message)}}async function Xt(e,s,t){const n={};t&&(n.Authorization=`Bearer ${t}`);const i={seconds:s};try{const a=await fetch(`https://huggingface.co/api/spaces/${e}/${gt}`,{method:"POST",headers:{"Content-Type":"application/json",...n},body:JSON.stringify(i)});if(a.status!==200)throw new Error("Could not set sleep timeout on duplicated Space. Please visit *ADD HF LINK TO SETTINGS* to set a timeout manually to reduce billing charges.");return await a.json()}catch(a){throw new Error(a.message)}}const Le=["cpu-basic","cpu-upgrade","cpu-xl","t4-small","t4-medium","a10g-small","a10g-large","a10g-largex2","a10g-largex4","a100-large","zero-a10g","h100","h100x8"];async function Kt(e,s){const{hf_token:t,private:n,hardware:i,timeout:a,auth:o}=s;if(i&&!Le.includes(i))throw new Error(`Invalid hardware type provided. Valid types are: ${Le.map(b=>`"${b}"`).join(",")}.`);const{http_protocol:r,host:u}=await de(e,t);let h=null;if(o){const b=await He(r,u,o,fetch);b&&(h=Ve(b))}const g={Authorization:`Bearer ${t}`,"Content-Type":"application/json",...h?{Cookie:h.join("; ")}:{}},d=(await(await fetch("https://huggingface.co/api/whoami-v2",{headers:g})).json()).name,m=e.split("/")[1],E={repository:`${d}/${m}`};n&&(E.private=!0);let R;try{i||(R=await Zt(e,t))}catch(b){throw Error(ke+b.message)}const c=i||R||"cpu-basic";E.hardware=c;try{const b=await fetch(`https://huggingface.co/api/spaces/${e}/duplicate`,{method:"POST",headers:g,body:JSON.stringify(E)});if(b.status===409)try{return await xe.connect(`${d}/${m}`,s)}catch(q){throw console.error("Failed to connect Client instance:",q),q}else if(b.status!==200)throw new Error(b.statusText);const S=await b.json();return await Xt(`${d}/${m}`,a||300,t),await xe.connect(es(S.url),s)}catch(b){throw new Error(b)}}function es(e){const s=/https:\/\/huggingface.co\/spaces\/([^/]+\/[^/]+)/,t=e.match(s);if(t)return t[1]}var G;class ts extends TransformStream{constructor(s={allowCR:!1}){super({transform:(t,n)=>{for(t=ne(this,G)+t;;){const i=t.indexOf(`
|
| 10 |
+
`),a=s.allowCR?t.indexOf("\r"):-1;if(a!==-1&&a!==t.length-1&&(i===-1||i-1>a)){n.enqueue(t.slice(0,a)),t=t.slice(a+1);continue}if(i===-1)break;const o=t[i-1]==="\r"?i-1:i;n.enqueue(t.slice(0,o)),t=t.slice(i+1)}dt(this,G,t)},flush:t=>{if(ne(this,G)==="")return;const n=s.allowCR&&ne(this,G).endsWith("\r")?ne(this,G).slice(0,-1):ne(this,G);t.enqueue(n)}}),lt(this,G,"")}}G=new WeakMap;function ss(e){let s=new TextDecoderStream,t=new ts({allowCR:!0});return e.pipeThrough(s).pipeThrough(t)}function ns(e){let s=/[:]\s*/.exec(e),t=s&&s.index;if(t)return[e.substring(0,t),e.substring(t+s[0].length)]}function Ue(e,s,t){e.get(s)||e.set(s,t)}async function*is(e,s){if(!e.body)return;let t=ss(e.body),n,i=t.getReader(),a;for(;;){if(s&&s.aborted)return i.cancel();if(n=await i.read(),n.done)return;if(!n.value){a&&(yield a),a=void 0;continue}let[o,r]=ns(n.value)||[];o&&(o==="data"?(a||(a={}),a[o]=a[o]?a[o]+`
|
| 11 |
+
`+r:r):o==="event"?(a||(a={}),a[o]=r):o==="id"?(a||(a={}),a[o]=+r||r):o==="retry"&&(a||(a={}),a[o]=+r||void 0))}}async function as(e,s){let t=new Request(e,s);Ue(t.headers,"Accept","text/event-stream"),Ue(t.headers,"Content-Type","application/json");let n=await fetch(t);if(!n.ok)throw n;return is(n,t.signal)}async function os(){let{event_callbacks:e,unclosed_events:s,pending_stream_messages:t,stream_status:n,config:i,jwt:a}=this;const o=this;if(!i)throw new Error("Could not resolve app config");n.open=!0;let r=null,u=new URLSearchParams({session_hash:this.session_hash}).toString(),h=new URL(`${i.root}${this.api_prefix}/${Je}?${u}`);if(a&&h.searchParams.set("__sign",a),r=this.stream(h),!r){console.warn("Cannot connect to SSE endpoint: "+h.toString());return}r.onmessage=async function(g){let d=JSON.parse(g.data);if(d.msg==="close_stream"){$e(n,o.abort_controller);return}const m=d.event_id;if(!m)await Promise.all(Object.keys(e).map(E=>e[E](d)));else if(e[m]&&i){d.msg==="process_completed"&&["sse","sse_v1","sse_v2","sse_v2.1","sse_v3"].includes(i.protocol)&&s.delete(m);let E=e[m];typeof window<"u"&&typeof document<"u"?setTimeout(E,0,d):E(d)}else t[m]||(t[m]=[]),t[m].push(d)},r.onerror=async function(g){console.error(g),await Promise.all(Object.keys(e).map(d=>e[d]({msg:"broken_connection",message:X})))}}function $e(e,s){e&&(e.open=!1,s?.abort())}function rs(e,s,t){e[s]?t.data.forEach((n,i)=>{let a=cs(e[s][i],n);e[s][i]=a,t.data[i]=a}):(e[s]=[],t.data.forEach((n,i)=>{e[s][i]=n}))}function cs(e,s){return s.forEach(([t,n,i])=>{e=ls(e,n,t,i)}),e}function ls(e,s,t,n){if(s.length===0){if(t==="replace")return n;if(t==="append")return e+n;throw new Error(`Unsupported action: ${t}`)}let i=e;for(let o=0;o<s.length-1;o++)i=i[s[o]];const a=s[s.length-1];switch(t){case"replace":i[a]=n;break;case"append":i[a]+=n;break;case"add":Array.isArray(i)?i.splice(Number(a),0,n):i[a]=n;break;case"delete":Array.isArray(i)?i.splice(Number(a),1):delete i[a];break;default:throw new Error(`Unknown action: ${t}`)}return e}function ds(e,s={}){const t={close:()=>{console.warn("Method not implemented.")},onerror:null,onmessage:null,onopen:null,readyState:0,url:e.toString(),withCredentials:!1,CONNECTING:0,OPEN:1,CLOSED:2,addEventListener:()=>{throw new Error("Method not implemented.")},dispatchEvent:()=>{throw new Error("Method not implemented.")},removeEventListener:()=>{throw new Error("Method not implemented.")}};return as(e,s).then(async n=>{t.readyState=t.OPEN;try{for await(const i of n)t.onmessage&&t.onmessage(i);t.readyState=t.CLOSED}catch(i){t.onerror&&t.onerror(i),t.readyState=t.CLOSED}}).catch(n=>{console.error(n),t.onerror&&t.onerror(n),t.readyState=t.CLOSED}),t}function us(e,s={},t,n,i){var a;try{let o=function(x){(i||Ze[x.type])&&g(x)},r=function(){for(et=!0;te.length>0;)te.shift()({value:void 0,done:!0})},u=function(x){te.length>0?te.shift()(x):fe.push(x)},h=function(x){u(ps(x)),r()},g=function(x){u({value:x,done:!1})},d=function(){return fe.length>0?Promise.resolve(fe.shift()):new Promise(x=>te.push(x))};const{hf_token:m}=this.options,{fetch:E,app_reference:R,config:c,session_hash:b,api_info:S,api_map:q,stream_status:V,pending_stream_messages:U,pending_diff_streams:Y,event_callbacks:K,unclosed_events:ue,post_data:ee,options:z,api_prefix:W}=this,pe=this;if(!S)throw new Error("No API found");if(!c)throw new Error("Could not resolve app config");let{fn_index:w,endpoint_info:oe,dependency:M}=hs(S,e,q,c),p=Lt(s,oe),y,v,j=c.protocol??"ws",J="",re=()=>J;const _=typeof e=="number"?"/predict":e;let Q,O=null,B=!1,he={},Z=typeof window<"u"&&typeof document<"u"?new URLSearchParams(window.location.search).toString():"";const Ze=((a=z?.events)==null?void 0:a.reduce((x,I)=>(x[I]=!0,x),{}))||{};async function Xe(){let x={},I={};j==="ws"?(y&&y.readyState===0?y.addEventListener("open",()=>{y.close()}):y.close(),x={fn_index:w,session_hash:b}):(x={event_id:O},I={event_id:O,session_hash:b,fn_index:w});try{if(!c)throw new Error("Could not resolve app config");"event_id"in I&&await E(`${c.root}${W}/${bt}`,{headers:{"Content-Type":"application/json"},method:"POST",body:JSON.stringify(I)}),await E(`${c.root}${W}/${yt}`,{headers:{"Content-Type":"application/json"},method:"POST",body:JSON.stringify(x)})}catch{console.warn("The `/reset` endpoint could not be called. Subsequent endpoint results may be unreliable.")}}const Ke=async x=>{await this._resolve_heartbeat(x)};async function Se(x){if(!c)return;let I=x.render_id;c.components=[...c.components.filter(P=>P.props.rendered_in!==I),...x.components],c.dependencies=[...c.dependencies.filter(P=>P.rendered_in!==I),...x.dependencies];const me=c.components.some(P=>P.type==="state"),N=c.dependencies.some(P=>P.targets.some(T=>T[1]==="unload"));c.connect_heartbeat=me||N,await Ke(c),o({type:"render",data:x,endpoint:_,fn_index:w})}this.handle_blob(c.root,p,oe).then(async x=>{var I;if(Q={data:ie(x,M,c.components,"input",!0)||[],event_data:t,fn_index:w,trigger_id:n},Jt(w,c))o({type:"status",endpoint:_,stage:"pending",queue:!1,fn_index:w,time:new Date}),ee(`${c.root}${W}/run${_.startsWith("/")?_:`/${_}`}${Z?"?"+Z:""}`,{...Q,session_hash:b}).then(([N,P])=>{const T=N.data;P==200?(o({type:"data",endpoint:_,fn_index:w,data:ie(T,M,c.components,"output",z.with_null_state),time:new Date,event_data:t,trigger_id:n}),N.render_config&&Se(N.render_config),o({type:"status",endpoint:_,fn_index:w,stage:"complete",eta:N.average_duration,queue:!1,time:new Date})):o({type:"status",stage:"error",endpoint:_,fn_index:w,message:N.error,queue:!1,time:new Date})}).catch(N=>{o({type:"status",stage:"error",message:N.message,endpoint:_,fn_index:w,queue:!1,time:new Date})});else if(j=="ws"){const{ws_protocol:N,host:P}=await de(R,m);o({type:"status",stage:"pending",queue:!0,endpoint:_,fn_index:w,time:new Date});let T=new URL(`${N}://${qt(P,c.root,!0)}/queue/join${Z?"?"+Z:""}`);this.jwt&&T.searchParams.set("__sign",this.jwt),y=new WebSocket(T),y.onclose=D=>{D.wasClean||o({type:"status",stage:"error",broken:!0,message:X,queue:!0,endpoint:_,fn_index:w,time:new Date})},y.onmessage=function(D){const A=JSON.parse(D.data),{type:C,status:$,data:k}=ye(A,he[w]);if(C==="update"&&$&&!B)o({type:"status",endpoint:_,fn_index:w,time:new Date,...$}),$.stage==="error"&&y.close();else if(C==="hash"){y.send(JSON.stringify({fn_index:w,session_hash:b}));return}else C==="data"?y.send(JSON.stringify({...Q,session_hash:b})):C==="complete"?B=$:C==="log"?o({type:"log",title:k.title,log:k.log,level:k.level,endpoint:_,duration:k.duration,visible:k.visible,fn_index:w}):C==="generating"&&o({type:"status",time:new Date,...$,stage:$?.stage,queue:!0,endpoint:_,fn_index:w});k&&(o({type:"data",time:new Date,data:ie(k.data,M,c.components,"output",z.with_null_state),endpoint:_,fn_index:w,event_data:t,trigger_id:n}),B&&(o({type:"status",time:new Date,...B,stage:$?.stage,queue:!0,endpoint:_,fn_index:w}),y.close()))},Fe(c.version||"2.0.0","3.6")<0&&addEventListener("open",()=>y.send(JSON.stringify({hash:b})))}else if(j=="sse"){o({type:"status",stage:"pending",queue:!0,endpoint:_,fn_index:w,time:new Date});var me=new URLSearchParams({fn_index:w.toString(),session_hash:b}).toString();let N=new URL(`${c.root}${W}/${Je}?${Z?Z+"&":""}${me}`);if(this.jwt&&N.searchParams.set("__sign",this.jwt),v=this.stream(N),!v)return Promise.reject(new Error("Cannot connect to SSE endpoint: "+N.toString()));v.onmessage=async function(P){const T=JSON.parse(P.data),{type:D,status:A,data:C}=ye(T,he[w]);if(D==="update"&&A&&!B)o({type:"status",endpoint:_,fn_index:w,time:new Date,...A}),A.stage==="error"&&(v?.close(),r());else if(D==="data"){let[$,k]=await ee(`${c.root}${W}/queue/data`,{...Q,session_hash:b,event_id:O});k!==200&&(o({type:"status",stage:"error",message:X,queue:!0,endpoint:_,fn_index:w,time:new Date}),v?.close(),r())}else D==="complete"?B=A:D==="log"?o({type:"log",title:C.title,log:C.log,level:C.level,endpoint:_,duration:C.duration,visible:C.visible,fn_index:w}):(D==="generating"||D==="streaming")&&o({type:"status",time:new Date,...A,stage:A?.stage,queue:!0,endpoint:_,fn_index:w});C&&(o({type:"data",time:new Date,data:ie(C.data,M,c.components,"output",z.with_null_state),endpoint:_,fn_index:w,event_data:t,trigger_id:n}),B&&(o({type:"status",time:new Date,...B,stage:A?.stage,queue:!0,endpoint:_,fn_index:w}),v?.close(),r()))}}else if(j=="sse_v1"||j=="sse_v2"||j=="sse_v2.1"||j=="sse_v3"){o({type:"status",stage:"pending",queue:!0,endpoint:_,fn_index:w,time:new Date});let N="";typeof window<"u"&&typeof document<"u"&&(N=(I=window?.location)==null?void 0:I.hostname);const P=N.includes(".dev.")?`https://moon-${N.split(".")[1]}.dev.spaces.huggingface.tech`:"https://huggingface.co";(typeof window<"u"&&typeof document<"u"&&window.parent!=window&&window.supports_zerogpu_headers?Wt("zerogpu-headers",P):Promise.resolve(null)).then(T=>ee(`${c.root}${W}/${pt}?${Z}`,{...Q,session_hash:b},T)).then(async([T,D])=>{if(D===503)o({type:"status",stage:"error",message:We,queue:!0,endpoint:_,fn_index:w,time:new Date});else if(D===422)o({type:"status",stage:"error",message:T.detail,queue:!0,endpoint:_,fn_index:w,code:"validation_error",time:new Date}),r();else if(D!==200)o({type:"status",stage:"error",broken:!1,message:T.detail,queue:!0,endpoint:_,fn_index:w,time:new Date});else{O=T.event_id,J=O;let A=async function(C){try{const{type:$,status:k,data:F,original_msg:tt}=ye(C,he[w]);if($=="heartbeat")return;if($==="update"&&k&&!B)o({type:"status",endpoint:_,fn_index:w,time:new Date,original_msg:tt,...k});else if($==="complete")B=k;else if($=="unexpected_error"||$=="broken_connection"){console.error("Unexpected error",k?.message);const st=$==="broken_connection";o({type:"status",stage:"error",message:k?.message||"An Unexpected Error Occurred!",queue:!0,endpoint:_,broken:st,session_not_found:k?.session_not_found,fn_index:w,time:new Date})}else if($==="log"){o({type:"log",title:F.title,log:F.log,level:F.level,endpoint:_,duration:F.duration,visible:F.visible,fn_index:w});return}else($==="generating"||$==="streaming")&&(o({type:"status",time:new Date,...k,stage:k?.stage,queue:!0,endpoint:_,fn_index:w}),F&&M.connection!=="stream"&&["sse_v2","sse_v2.1","sse_v3"].includes(j)&&rs(Y,O,F));F&&(o({type:"data",time:new Date,data:ie(F.data,M,c.components,"output",z.with_null_state),endpoint:_,fn_index:w}),F.render_config&&await Se(F.render_config),B&&(o({type:"status",time:new Date,...B,stage:k?.stage,queue:!0,endpoint:_,fn_index:w}),r())),(k?.stage==="complete"||k?.stage==="error")&&(K[O]&&delete K[O],O in Y&&delete Y[O])}catch($){console.error("Unexpected client exception",$),o({type:"status",stage:"error",message:"An Unexpected Error Occurred!",queue:!0,endpoint:_,fn_index:w,time:new Date}),["sse_v2","sse_v2.1","sse_v3"].includes(j)&&($e(V,pe.abort_controller),V.open=!1,r())}};O in U&&(U[O].forEach(C=>A(C)),delete U[O]),K[O]=A,ue.add(O),V.open||await this.open_stream()}})}});let et=!1;const fe=[],te=[],Ne={[Symbol.asyncIterator]:()=>Ne,next:d,throw:async x=>(h(x),d()),return:async()=>(r(),d()),cancel:Xe,event_id:re};return Ne}catch(o){throw console.error("Submit function encountered an error:",o),o}}function ps(e){return{then:(s,t)=>t(e)}}function hs(e,s,t,n){let i,a,o;if(typeof s=="number")i=s,a=e.unnamed_endpoints[i],o=n.dependencies.find(r=>r.id==s);else{const r=s.replace(/^\//,"");i=t[r],a=e.named_endpoints[s.trim()],o=n.dependencies.find(u=>u.id==t[r])}if(typeof i!="number")throw new Error("There is no endpoint matching that name of fn_index matching that number.");return{fn_index:i,endpoint_info:a,dependency:o}}class xe{constructor(s,t={events:["data"]}){f(this,"app_reference"),f(this,"options"),f(this,"deep_link",null),f(this,"config"),f(this,"api_prefix",""),f(this,"api_info"),f(this,"api_map",{}),f(this,"session_hash",Math.random().toString(36).substring(2)),f(this,"jwt",!1),f(this,"last_status",{}),f(this,"cookies",null),f(this,"stream_status",{open:!1}),f(this,"closed",!1),f(this,"pending_stream_messages",{}),f(this,"pending_diff_streams",{}),f(this,"event_callbacks",{}),f(this,"unclosed_events",new Set),f(this,"heartbeat_event",null),f(this,"abort_controller",null),f(this,"stream_instance",null),f(this,"current_payload"),f(this,"ws_map",{}),f(this,"view_api"),f(this,"upload_files"),f(this,"upload"),f(this,"handle_blob"),f(this,"post_data"),f(this,"submit"),f(this,"predict"),f(this,"open_stream"),f(this,"resolve_config"),f(this,"resolve_cookies");var n;this.app_reference=s,this.deep_link=((n=t.query_params)==null?void 0:n.deep_link)||null,t.events||(t.events=["data"]),this.options=t,this.current_payload={},this.view_api=Ut.bind(this),this.upload_files=Bt.bind(this),this.handle_blob=Mt.bind(this),this.post_data=Vt.bind(this),this.submit=us.bind(this),this.predict=Yt.bind(this),this.open_stream=os.bind(this),this.resolve_config=Pt.bind(this),this.resolve_cookies=Tt.bind(this),this.upload=It.bind(this),this.fetch=this.fetch.bind(this),this.handle_space_success=this.handle_space_success.bind(this),this.stream=this.stream.bind(this)}get_url_config(s=null){if(!this.config)throw new Error(H);s===null&&(s=window.location.href);const t=o=>o.replace(/^\/+|\/+$/g,"");let n=t(new URL(this.config.root).pathname),i=t(new URL(s).pathname),a;return i.startsWith(n)?a=t(i.substring(n.length)):a="",this.get_page_config(a)}get_page_config(s){if(!this.config)throw new Error(H);let t=this.config;return s in t.page||(s=""),{...t,current_page:s,layout:t.page[s].layout,components:t.components.filter(n=>t.page[s].components.includes(n.id)),dependencies:this.config.dependencies.filter(n=>t.page[s].dependencies.includes(n.id))}}fetch(s,t){const n=new Headers(t?.headers||{});if(this&&this.cookies&&n.append("Cookie",this.cookies),this&&this.options.headers)for(const i in this.options.headers)n.append(i,this.options.headers[i]);return fetch(s,{...t,headers:n})}stream(s){const t=new Headers;if(this&&this.cookies&&t.append("Cookie",this.cookies),this&&this.options.headers)for(const n in this.options.headers)t.append(n,this.options.headers[n]);return this&&this.options.hf_token&&t.append("Authorization",`Bearer ${this.options.hf_token}`),this.abort_controller=new AbortController,this.stream_instance=ds(s.toString(),{credentials:"include",headers:t,signal:this.abort_controller.signal}),this.stream_instance}async init(){var s;this.options.auth&&await this.resolve_cookies(),await this._resolve_config().then(({config:t})=>this._resolve_heartbeat(t)),this.api_info=await this.view_api(),this.api_map=Ot(((s=this.config)==null?void 0:s.dependencies)||[])}async _resolve_heartbeat(s){if(s&&(this.config=s,this.api_prefix=s.api_prefix||"",this.config&&this.config.connect_heartbeat&&this.config.space_id&&this.options.hf_token&&(this.jwt=await De(this.config.space_id,this.options.hf_token,this.cookies))),s.space_id&&this.options.hf_token&&(this.jwt=await De(s.space_id,this.options.hf_token)),this.config&&this.config.connect_heartbeat){const t=new URL(`${this.config.root}${this.api_prefix}/${_t}/${this.session_hash}`);this.jwt&&t.searchParams.set("__sign",this.jwt),this.heartbeat_event||(this.heartbeat_event=this.stream(t))}}static async connect(s,t={events:["data"]}){const n=new this(s,t);return t.session_hash&&(n.session_hash=t.session_hash),await n.init(),n}async reconnect(){const s=new URL(`${this.config.root}${this.api_prefix}/${vt}`);let t;try{const n=await this.fetch(s);if(!n.ok)throw new Error;t=(await n.json()).app_id}catch{return"broken"}return t!==this.config.app_id?"changed":"connected"}close(){this.closed=!0,$e(this.stream_status,this.abort_controller)}set_current_payload(s){this.current_payload=s}static async duplicate(s,t={events:["data"]}){return Kt(s,t)}async _resolve_config(){const{http_protocol:s,host:t,space_id:n}=await de(this.app_reference,this.options.hf_token),{status_callback:i}=this.options;n&&i&&await Qe(n,i);let a;try{let o=`${s}//${t}`;if(a=await this.resolve_config(o),!a)throw new Error(H);return this.config_success(a)}catch(o){if(n&&i)ae(n,Ee.test(n)?"space_name":"subdomain",this.handle_space_success);else throw i&&i({status:"error",message:"Could not load this space.",load_status:"error",detail:"NOT_FOUND"}),Error(o)}}async config_success(s){if(this.config=s,this.api_prefix=s.api_prefix||"",this.config.auth_required)return this.prepare_return_obj();try{this.api_info=await this.view_api()}catch(t){console.error(Et+t.message)}return this.prepare_return_obj()}async handle_space_success(s){var t;if(!this)throw new Error(H);const{status_callback:n}=this.options;if(n&&n(s),s.status==="running")try{if(this.config=await this._resolve_config(),this.api_prefix=((t=this==null?void 0:this.config)==null?void 0:t.api_prefix)||"",!this.config)throw new Error(H);return await this.config_success(this.config)}catch(i){throw n&&n({status:"error",message:"Could not load this space.",load_status:"error",detail:"NOT_FOUND"}),i}}async component_server(s,t,n){var i;if(!this.config)throw new Error(H);const a={},{hf_token:o}=this.options,{session_hash:r}=this;o&&(a.Authorization=`Bearer ${this.options.hf_token}`);let u,h=this.config.components.find(d=>d.id===s);(i=h?.props)!=null&&i.root_url?u=h.props.root_url:u=this.config.root;let g;if("binary"in n){g=new FormData;for(const d in n.data)d!=="binary"&&g.append(d,n.data[d]);g.set("component_id",s.toString()),g.set("fn_name",t),g.set("session_hash",r)}else g=JSON.stringify({data:n,component_id:s,fn_name:t,session_hash:r}),a["Content-Type"]="application/json";o&&(a.Authorization=`Bearer ${o}`);try{const d=await this.fetch(`${u}${this.api_prefix}/${wt}/`,{method:"POST",body:g,headers:a,credentials:"include"});if(!d.ok)throw new Error("Could not connect to component server: "+d.statusText);return await d.json()}catch(d){console.warn(d)}}set_cookies(s){this.cookies=Ve(s).join("; ")}prepare_return_obj(){return{config:this.config,predict:this.predict,submit:this.submit,view_api:this.view_api,component_server:this.component_server}}async connect_ws(s){return new Promise((t,n)=>{let i;try{i=new WebSocket(s)}catch{this.ws_map[s]="failed";return}this.ws_map[s]="pending",i.onopen=()=>{this.ws_map[s]=i,t()},i.onerror=a=>{console.error("WebSocket error:",a),this.close_ws(s),this.ws_map[s]="failed",t()},i.onclose=()=>{this.ws_map[s]="closed"},i.onmessage=a=>{}})}async send_ws_message(s,t){if(!(s in this.ws_map))await this.connect_ws(s);else if(this.ws_map[s]==="pending"||this.ws_map[s]==="closed"||this.ws_map[s]==="failed")return;const n=this.ws_map[s];n instanceof WebSocket?n.send(JSON.stringify(t)):this.post_data(s,t)}async close_ws(s){if(s in this.ws_map){const t=this.ws_map[s];t instanceof WebSocket&&(t.close(),delete this.ws_map[s])}}}async function le(e,s,t){return(await e.predict(s,t)).data[0]}const ms=()=>{const e=L.useRef(null),[s,t]=L.useState(!1),[n,i]=L.useState(null),[a,o]=L.useState({username:null,member:!1}),[r,u]=L.useState([]),[h,g]=L.useState(!1),[d,m]=L.useState("polls"),[E,R]=L.useState(null),[c,b]=L.useState(""),[S,q]=L.useState(["",""]),[V,U]=L.useState(null),Y=20,K=(p,y)=>{q(v=>v.map((j,J)=>J===p?y:j))},ue=()=>{q(p=>p.length>=Y?p:[...p,""])},ee=p=>{q(y=>y.length<=1?y:y.filter((v,j)=>j!==p))},z=L.useCallback(async()=>{const p=e.current;if(p)try{const y=await le(p,"/polls",[]);o(y.me),u(y.polls),i(null)}catch{i("Cannot reach the voting backend.")}},[]);L.useEffect(()=>{let p=!1;return(async()=>{try{e.current=await xe.connect(window.location.origin+"/gradio"),p||(t(!0),await z())}catch{p||i("Cannot reach the voting backend.")}})(),()=>{p=!0}},[z]);const W=async(p,y)=>{if(!(h||!e.current)){g(!0);try{const v=await le(e.current,"/vote",[p,y]);U({kind:v.ok?"ok":"err",text:v.message}),v.ok&&v.poll?u(j=>j.map(J=>J.id===v.poll.id?v.poll:J)):await z()}catch{U({kind:"err",text:"Vote failed — please retry."})}finally{g(!1)}}},pe=p=>{R(p.id),b(p.question),q(p.options.length>0?[...p.options]:["",""]),U(null),m("create")},w=()=>{R(null),b(""),q(["",""]),U(null)},oe=async()=>{if(h||!e.current)return;g(!0);const p=S.filter(j=>j.trim()).join(`
|
| 12 |
+
`),y=E?"/edit_poll":"/create_poll",v=E?[E,c,p]:[c,p];try{const j=await le(e.current,y,v);U({kind:j.ok?"ok":"err",text:j.message}),j.ok&&(R(null),b(""),q(["",""]),await z(),m("polls"))}catch{U({kind:"err",text:"Could not save poll — please retry."})}finally{g(!1)}},M=async p=>{if(!(h||!e.current)&&window.confirm(`Delete poll “${p.question}” and its ${p.total} vote(s)?`)){g(!0);try{const y=await le(e.current,"/delete_poll",[p.id]);U({kind:y.ok?"ok":"err",text:y.message}),await z()}catch{U({kind:"err",text:"Could not delete poll — please retry."})}finally{g(!1)}}};return l.jsxs("div",{className:"page",children:[l.jsxs("div",{className:"bg","aria-hidden":"true",children:[l.jsx("div",{className:"bg-orb bg-orb-a"}),l.jsx("div",{className:"bg-orb bg-orb-b"}),l.jsx("div",{className:"bg-orb bg-orb-c"}),l.jsx("div",{className:"bg-grid"})]}),l.jsxs("div",{className:"shell",children:[l.jsxs("header",{className:"top",children:[l.jsxs("h1",{children:[l.jsx("span",{className:"logo","aria-hidden":"true",children:"🗳️"})," SLM Consortium Polls"]}),l.jsxs("div",{className:"auth",children:[a.username?l.jsxs("span",{className:`chip ${a.member?"chip-ok":"chip-warn"}`,children:["@",a.username,a.member?"":" · not a member"]}):l.jsx("a",{className:"btn",href:"/gradio/login/huggingface?_target_url=/",children:"Sign in with Hugging Face"}),l.jsx("button",{className:"btn quiet",onClick:()=>void z(),children:"Refresh"})]})]}),l.jsxs("p",{className:"lede",children:["Hugging Face sign-in required. Only ",l.jsx("code",{children:"slmconsortium"})," members can vote or create polls. Click to vote, and click another option to change your vote."]}),l.jsxs("nav",{className:"tabs",role:"tablist","aria-label":"Sections",children:[l.jsx("button",{role:"tab","aria-selected":d==="polls",className:d==="polls"?"active":"",onClick:()=>m("polls"),children:"🗳️ Polls"}),l.jsx("button",{role:"tab","aria-selected":d==="create",className:d==="create"?"active":"",onClick:()=>m("create"),children:"➕ Create poll"})]}),n&&l.jsx("p",{className:"notice err",children:n}),V&&l.jsx("p",{className:`notice ${V.kind}`,children:V.text}),!s&&!n&&l.jsx("p",{className:"muted",children:"Loading…"}),d==="polls"&&l.jsxs("div",{className:"tabpanel",role:"tabpanel",children:[s&&r.length===0&&l.jsx("p",{className:"muted",children:"No polls yet."}),r.map(p=>l.jsxs("section",{className:"card",children:[l.jsx("h2",{children:p.question}),l.jsxs("p",{className:"meta",children:[p.total," vote",p.total===1?"":"s"," · by @",p.created_by,a.member&&l.jsxs("span",{className:"card-actions",children:[l.jsx("button",{className:"icon-btn",disabled:h,title:"Edit poll (votes for edited/removed options are dropped)","aria-label":`Edit poll ${p.question}`,onClick:()=>pe(p),children:"✏️"}),l.jsx("button",{className:"icon-btn danger",disabled:h,title:"Delete poll","aria-label":`Delete poll ${p.question}`,onClick:()=>void M(p),children:"🗑️"})]})]}),l.jsx("ul",{className:"opts",children:p.options.map((y,v)=>{const j=p.total?p.counts[v]/p.total*100:0,J=p.my_vote===v,re=Object.entries(p.voters??{}).filter(([,_])=>_===v).map(([_])=>_).sort((_,Q)=>_.toLowerCase().localeCompare(Q.toLowerCase()));return l.jsx("li",{children:l.jsxs("button",{className:J?"mine":"",disabled:!a.member||h,title:a.username?a.member?`Vote for ${y}`:"Only slmconsortium members can vote":"Sign in to vote",onClick:()=>void W(p.id,v),children:[l.jsxs("span",{className:"opt-top",children:[l.jsxs("span",{children:[y,J?" ✓":""]}),l.jsxs("span",{className:"num",children:[p.counts[v]," · ",j.toFixed(0),"%"]})]}),l.jsx("span",{className:"bar",children:l.jsx("i",{style:{width:`${j}%`}})}),re.length>0&&l.jsx("span",{className:"voters",children:re.map(_=>l.jsxs("span",{className:`voter ${_===a.username?"me":""}`,children:["@",_]},_))})]})},v)})})]},p.id))]}),d==="create"&&l.jsx("div",{className:"tabpanel",role:"tabpanel",children:l.jsxs("section",{className:"card",children:[l.jsx("h2",{children:E?"Edit poll":"New poll"}),!a.member&&l.jsx("p",{className:"muted",children:a.username?"Only slmconsortium members can create polls.":"Sign in first."}),l.jsxs("label",{children:["Question",l.jsx("input",{value:c,onChange:p=>b(p.target.value),placeholder:"Which meeting time works best?",disabled:!a.member})]}),l.jsxs("div",{className:"field",children:[l.jsxs("span",{className:"field-label",children:["Options",l.jsxs("span",{className:"field-hint",children:[S.length,"/",Y]})]}),l.jsx("ul",{className:"opt-editor",children:S.map((p,y)=>l.jsxs("li",{children:[l.jsx("input",{value:p,onChange:v=>K(y,v.target.value),placeholder:`Option ${y+1}`,disabled:!a.member,"aria-label":`Option ${y+1}`}),l.jsx("button",{type:"button",className:"icon-btn",onClick:()=>ee(y),disabled:!a.member||S.length<=1,"aria-label":`Remove option ${y+1}`,title:S.length<=1?"Need at least one option row":"Remove option",children:"✕"})]},y))}),l.jsx("button",{type:"button",className:"btn quiet add-opt",onClick:ue,disabled:!a.member||S.length>=Y,children:"+ Add option"})]}),l.jsxs("div",{className:"form-actions",children:[l.jsx("button",{className:"btn",disabled:!a.member||h||c.trim().length<3||S.filter(p=>p.trim()).length<2,onClick:()=>void oe(),children:h?"Working…":E?"Save changes":"Create poll"}),E&&l.jsx("button",{className:"btn quiet",onClick:w,disabled:h,children:"Cancel"})]})]})}),l.jsx("footer",{className:"muted",children:l.jsx("a",{href:"/gradio/",children:"Classic UI"})})]})]})};export{ms as default};
|
frontend/dist/_astro/{index.D5zPhbGI.css → index.CWh57_AP.css}
RENAMED
|
@@ -1 +1 @@
|
|
| 1 |
-
:root{--bg: #06040f;--panel: rgba(12, 9, 26, .72);--panel-solid: #0c091a;--border: rgba(140, 120, 255, .22);--border-strong: rgba(160, 130, 255, .45);--text: #eceaf6;--muted: #a49fc2;--accent: #7b5cff;--accent-soft: rgba(123, 92, 255, .18);--ok: #5ee6a8;--err: #ff7a90}*{box-sizing:border-box}html,body{margin:0;padding:0}body{background:var(--bg);color:var(--text);font-family:ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,sans-serif;line-height:1.5;min-height:100vh}.bg{position:fixed;inset:0;z-index:0;overflow:hidden;background:radial-gradient(ellipse 120% 80% at 50% -20%,#161033 0%,transparent 60%),var(--bg)}.bg-orb{position:absolute;border-radius:50%;filter:blur(90px);opacity:.55;will-change:transform}.bg-orb-a{width:55vmax;height:55vmax;left:-15vmax;top:-18vmax;background:radial-gradient(circle,rgba(82,38,255,.5) 0%,transparent 70%);animation:drift-a 26s ease-in-out infinite alternate}.bg-orb-b{width:45vmax;height:45vmax;right:-18vmax;top:10vmax;background:radial-gradient(circle,rgba(140,60,255,.35) 0%,transparent 70%);animation:drift-b 32s ease-in-out infinite alternate}.bg-orb-c{width:50vmax;height:50vmax;left:15vmax;bottom:-25vmax;background:radial-gradient(circle,rgba(40,90,255,.3) 0%,transparent 70%);animation:drift-c 38s ease-in-out infinite alternate}@keyframes drift-a{0%{transform:translateZ(0) scale(1)}to{transform:translate3d(8vmax,6vmax,0) scale(1.15)}}@keyframes drift-b{0%{transform:translateZ(0) scale(1.1)}to{transform:translate3d(-7vmax,9vmax,0) scale(.95)}}@keyframes drift-c{0%{transform:translateZ(0) scale(1)}to{transform:translate3d(-6vmax,-8vmax,0) scale(1.2)}}.bg-grid{position:absolute;inset:0;background-image:linear-gradient(rgba(150,130,255,.05) 1px,transparent 1px),linear-gradient(90deg,rgba(150,130,255,.05) 1px,transparent 1px);background-size:44px 44px;mask-image:radial-gradient(ellipse 90% 70% at 50% 35%,black 0%,transparent 75%);-webkit-mask-image:radial-gradient(ellipse 90% 70% at 50% 35%,black 0%,transparent 75%)}@media(prefers-reduced-motion:reduce){.bg-orb{animation:none}}.shell{position:relative;z-index:1;max-width:660px;margin:0 auto;padding:40px 20px 80px}.top{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;border-bottom:1px solid var(--border);padding-bottom:16px;margin-bottom:12px}.top h1{font-size:20px;margin:0;font-weight:650;letter-spacing:-.01em}.logo{margin-right:4px}.auth{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.chip{display:inline-block;font-size:13px;padding:5px 12px;border-radius:999px;border:1px solid var(--border-strong);background:var(--accent-soft)}.chip-ok{color:var(--ok);border-color:#5ee6a866;background:#5ee6a81a}.chip-warn{color:#ffcf7a;border-color:#ffcf7a66;background:#ffcf7a14}.lede{color:var(--muted);font-size:14px}.lede code,.meta code{font-size:13px;background:var(--accent-soft);border:1px solid var(--border);border-radius:4px;padding:1px 5px}.muted{color:var(--muted);font-size:14px}a{color:var(--text)}.btn{appearance:none;background:var(--accent);color:#fff;border:1px solid transparent;border-radius:8px;padding:8px 16px;font-size:14px;font-weight:500;cursor:pointer;text-decoration:none;display:inline-block;font-family:inherit;box-shadow:0 4px 20px #7b5cff59;transition:background .15s ease,transform .1s ease}.btn:hover:not(:disabled){background:#8e73ff;transform:translateY(-1px)}.btn:disabled{opacity:.4;cursor:not-allowed}.btn.quiet{background:#ffffff0f;color:var(--text);border-color:var(--border);box-shadow:none}.btn.quiet:hover:not(:disabled){background:#ffffff1f;transform:none}.tabs{display:flex;gap:4px;margin:20px 0 16px;padding:4px;border:1px solid var(--border);border-radius:12px;background:var(--panel);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px)}.tabs button{flex:1;appearance:none;border:1px solid transparent;border-radius:9px;background:transparent;color:var(--muted);font-size:14px;font-weight:500;font-family:inherit;padding:9px 12px;cursor:pointer;transition:color .15s ease,background .15s ease}.tabs button:hover{color:var(--text)}.tabs button.active{color:#fff;background:var(--accent-soft);border-color:var(--border-strong)}.notice{font-size:14px;padding:10px 14px;margin:0 0 14px;border-radius:8px;border:1px solid}.notice.err{color:var(--err);border-color:#ff7a9059;background:#ff7a9014}.notice.ok{color:var(--ok);border-color:#5ee6a859;background:#5ee6a814}.card{border:1px solid var(--border);border-radius:14px;padding:20px;margin-bottom:16px;background:var(--panel);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);box-shadow:0 8px 32px #00000059}.card h2{font-size:17px;margin:0 0 2px;font-weight:600}.meta{color:var(--muted);font-size:13px;margin:0 0 12px}.opts{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}.opts button{width:100%;text-align:left;background:#ffffff0a;color:var(--text);border:1px solid var(--border);border-radius:10px;padding:11px 14px;font-size:14px;cursor:pointer;font-family:inherit;transition:border-color .15s ease,background .15s ease}.opts button:hover:not(:disabled){border-color:var(--border-strong);background:#ffffff12}.opts button:disabled{cursor:default}.opts button.mine{border-color:var(--accent);background:var(--accent-soft);box-shadow:0 0 0 1px var(--accent) inset}.opt-top{display:flex;justify-content:space-between;gap:12px}.num{color:var(--muted);font-variant-numeric:tabular-nums;white-space:nowrap}.bar{display:block;height:4px;background:#ffffff14;border-radius:99px;margin-top:9px;overflow:hidden}.bar i{display:block;height:100%;background:linear-gradient(90deg,#5226ff,#9a7bff);border-radius:99px;transition:width .4s ease}.voters{display:flex;flex-wrap:wrap;gap:4px;margin-top:9px}.voter{font-size:11.5px;line-height:1;padding:4px 8px;border-radius:999px;border:1px solid var(--border);background:#ffffff0f;color:var(--muted);font-weight:500}.voter.me{color:#fff;border-color:#ffffff8c;background:#7b5cff73}label{display:block;font-size:13px;color:var(--muted);margin:14px 0}input,textarea{display:block;width:100%;margin-top:5px;border:1px solid var(--border);border-radius:10px;padding:10px 13px;font-size:14px;font-family:inherit;color:var(--text);background:#ffffff0d;transition:border-color .15s ease}input::placeholder,textarea::placeholder{color:#a49fc28c}input:focus,textarea:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px #7b5cff33}input:disabled,textarea:disabled{background:#ffffff08;color:var(--muted);cursor:not-allowed}textarea{resize:vertical}.field{display:block;font-size:13px;color:var(--muted);margin:14px 0}.field-label{display:flex;justify-content:space-between;align-items:baseline}.field-hint{font-size:12px;color:var(--muted);opacity:.7;font-variant-numeric:tabular-nums}.opt-editor{list-style:none;margin:5px 0 10px;padding:0;display:flex;flex-direction:column;gap:8px}.opt-editor li{display:flex;gap:8px;align-items:center}.opt-editor input{flex:1;margin-top:0}.icon-btn{appearance:none;flex:0 0 auto;width:38px;height:38px;border-radius:10px;border:1px solid var(--border);background:#ffffff0a;color:var(--muted);font-size:15px;line-height:1;cursor:pointer;font-family:inherit;transition:border-color .15s ease,color .15s ease,background .15s ease}.icon-btn:hover:not(:disabled){border-color:#ff7a9080;color:var(--err);background:#ff7a9014}.icon-btn:disabled{opacity:.35;cursor:not-allowed}.add-opt{margin-top:2px}footer{border-top:1px solid var(--border);margin-top:16px;padding-top:16px;font-size:13px}footer a{color:var(--muted)}footer a:hover{color:var(--text)}
|
|
|
|
| 1 |
+
:root{--bg: #06040f;--panel: rgba(12, 9, 26, .72);--panel-solid: #0c091a;--border: rgba(140, 120, 255, .22);--border-strong: rgba(160, 130, 255, .45);--text: #eceaf6;--muted: #a49fc2;--accent: #7b5cff;--accent-soft: rgba(123, 92, 255, .18);--ok: #5ee6a8;--err: #ff7a90}*{box-sizing:border-box}html,body{margin:0;padding:0}body{background:var(--bg);color:var(--text);font-family:ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,sans-serif;line-height:1.5;min-height:100vh}.bg{position:fixed;inset:0;z-index:0;overflow:hidden;background:radial-gradient(ellipse 120% 80% at 50% -20%,#161033 0%,transparent 60%),var(--bg)}.bg-orb{position:absolute;border-radius:50%;filter:blur(90px);opacity:.55;will-change:transform}.bg-orb-a{width:55vmax;height:55vmax;left:-15vmax;top:-18vmax;background:radial-gradient(circle,rgba(82,38,255,.5) 0%,transparent 70%);animation:drift-a 26s ease-in-out infinite alternate}.bg-orb-b{width:45vmax;height:45vmax;right:-18vmax;top:10vmax;background:radial-gradient(circle,rgba(140,60,255,.35) 0%,transparent 70%);animation:drift-b 32s ease-in-out infinite alternate}.bg-orb-c{width:50vmax;height:50vmax;left:15vmax;bottom:-25vmax;background:radial-gradient(circle,rgba(40,90,255,.3) 0%,transparent 70%);animation:drift-c 38s ease-in-out infinite alternate}@keyframes drift-a{0%{transform:translateZ(0) scale(1)}to{transform:translate3d(8vmax,6vmax,0) scale(1.15)}}@keyframes drift-b{0%{transform:translateZ(0) scale(1.1)}to{transform:translate3d(-7vmax,9vmax,0) scale(.95)}}@keyframes drift-c{0%{transform:translateZ(0) scale(1)}to{transform:translate3d(-6vmax,-8vmax,0) scale(1.2)}}.bg-grid{position:absolute;inset:0;background-image:linear-gradient(rgba(150,130,255,.05) 1px,transparent 1px),linear-gradient(90deg,rgba(150,130,255,.05) 1px,transparent 1px);background-size:44px 44px;mask-image:radial-gradient(ellipse 90% 70% at 50% 35%,black 0%,transparent 75%);-webkit-mask-image:radial-gradient(ellipse 90% 70% at 50% 35%,black 0%,transparent 75%)}@media(prefers-reduced-motion:reduce){.bg-orb{animation:none}}.shell{position:relative;z-index:1;max-width:660px;margin:0 auto;padding:40px 20px 80px}.top{display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap;border-bottom:1px solid var(--border);padding-bottom:16px;margin-bottom:12px}.top h1{font-size:20px;margin:0;font-weight:650;letter-spacing:-.01em}.logo{margin-right:4px}.auth{display:flex;gap:8px;align-items:center;flex-wrap:wrap}.chip{display:inline-block;font-size:13px;padding:5px 12px;border-radius:999px;border:1px solid var(--border-strong);background:var(--accent-soft)}.chip-ok{color:var(--ok);border-color:#5ee6a866;background:#5ee6a81a}.chip-warn{color:#ffcf7a;border-color:#ffcf7a66;background:#ffcf7a14}.lede{color:var(--muted);font-size:14px}.lede code,.meta code{font-size:13px;background:var(--accent-soft);border:1px solid var(--border);border-radius:4px;padding:1px 5px}.muted{color:var(--muted);font-size:14px}a{color:var(--text)}.btn{appearance:none;background:var(--accent);color:#fff;border:1px solid transparent;border-radius:8px;padding:8px 16px;font-size:14px;font-weight:500;cursor:pointer;text-decoration:none;display:inline-block;font-family:inherit;box-shadow:0 4px 20px #7b5cff59;transition:background .15s ease,transform .1s ease}.btn:hover:not(:disabled){background:#8e73ff;transform:translateY(-1px)}.btn:disabled{opacity:.4;cursor:not-allowed}.btn.quiet{background:#ffffff0f;color:var(--text);border-color:var(--border);box-shadow:none}.btn.quiet:hover:not(:disabled){background:#ffffff1f;transform:none}.tabs{display:flex;gap:4px;margin:20px 0 16px;padding:4px;border:1px solid var(--border);border-radius:12px;background:var(--panel);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px)}.tabs button{flex:1;appearance:none;border:1px solid transparent;border-radius:9px;background:transparent;color:var(--muted);font-size:14px;font-weight:500;font-family:inherit;padding:9px 12px;cursor:pointer;transition:color .15s ease,background .15s ease}.tabs button:hover{color:var(--text)}.tabs button.active{color:#fff;background:var(--accent-soft);border-color:var(--border-strong)}.notice{font-size:14px;padding:10px 14px;margin:0 0 14px;border-radius:8px;border:1px solid}.notice.err{color:var(--err);border-color:#ff7a9059;background:#ff7a9014}.notice.ok{color:var(--ok);border-color:#5ee6a859;background:#5ee6a814}.card{border:1px solid var(--border);border-radius:14px;padding:20px;margin-bottom:16px;background:var(--panel);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);box-shadow:0 8px 32px #00000059}.card h2{font-size:17px;margin:0 0 2px;font-weight:600}.meta{color:var(--muted);font-size:13px;margin:0 0 12px}.opts{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:8px}.opts button{width:100%;text-align:left;background:#ffffff0a;color:var(--text);border:1px solid var(--border);border-radius:10px;padding:11px 14px;font-size:14px;cursor:pointer;font-family:inherit;transition:border-color .15s ease,background .15s ease}.opts button:hover:not(:disabled){border-color:var(--border-strong);background:#ffffff12}.opts button:disabled{cursor:default}.opts button.mine{border-color:var(--accent);background:var(--accent-soft);box-shadow:0 0 0 1px var(--accent) inset}.opt-top{display:flex;justify-content:space-between;gap:12px}.num{color:var(--muted);font-variant-numeric:tabular-nums;white-space:nowrap}.bar{display:block;height:4px;background:#ffffff14;border-radius:99px;margin-top:9px;overflow:hidden}.bar i{display:block;height:100%;background:linear-gradient(90deg,#5226ff,#9a7bff);border-radius:99px;transition:width .4s ease}.voters{display:flex;flex-wrap:wrap;gap:4px;margin-top:9px}.voter{font-size:11.5px;line-height:1;padding:4px 8px;border-radius:999px;border:1px solid var(--border);background:#ffffff0f;color:var(--muted);font-weight:500}.voter.me{color:#fff;border-color:#ffffff8c;background:#7b5cff73}label{display:block;font-size:13px;color:var(--muted);margin:14px 0}input,textarea{display:block;width:100%;margin-top:5px;border:1px solid var(--border);border-radius:10px;padding:10px 13px;font-size:14px;font-family:inherit;color:var(--text);background:#ffffff0d;transition:border-color .15s ease}input::placeholder,textarea::placeholder{color:#a49fc28c}input:focus,textarea:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px #7b5cff33}input:disabled,textarea:disabled{background:#ffffff08;color:var(--muted);cursor:not-allowed}textarea{resize:vertical}.field{display:block;font-size:13px;color:var(--muted);margin:14px 0}.field-label{display:flex;justify-content:space-between;align-items:baseline}.field-hint{font-size:12px;color:var(--muted);opacity:.7;font-variant-numeric:tabular-nums}.opt-editor{list-style:none;margin:5px 0 10px;padding:0;display:flex;flex-direction:column;gap:8px}.opt-editor li{display:flex;gap:8px;align-items:center}.opt-editor input{flex:1;margin-top:0}.icon-btn{appearance:none;flex:0 0 auto;width:38px;height:38px;border-radius:10px;border:1px solid var(--border);background:#ffffff0a;color:var(--muted);font-size:15px;line-height:1;cursor:pointer;font-family:inherit;transition:border-color .15s ease,color .15s ease,background .15s ease}.icon-btn:hover:not(:disabled){border-color:#ff7a9080;color:var(--err);background:#ff7a9014}.icon-btn:disabled{opacity:.35;cursor:not-allowed}.add-opt{margin-top:2px}.card-actions{display:inline-flex;gap:6px;margin-left:auto}.card-actions .icon-btn{width:28px;height:28px;border-radius:8px;font-size:13px;vertical-align:middle}.card-actions .icon-btn:hover:not(:disabled){border-color:#8e73ff80;color:#8e73ff;background:#8e73ff14}.form-actions{display:flex;gap:10px;margin-top:4px}footer{border-top:1px solid var(--border);margin-top:16px;padding-top:16px;font-size:13px}footer a{color:var(--muted)}footer a:hover{color:var(--text)}
|
frontend/dist/index.html
CHANGED
|
@@ -1 +1 @@
|
|
| 1 |
-
<!DOCTYPE html><html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>SLM Consortium Polls</title><meta name="description" content="Create polls and vote."><link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🗳️</text></svg>"><link rel="stylesheet" href="/_astro/index.
|
|
|
|
| 1 |
+
<!DOCTYPE html><html lang="en"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>SLM Consortium Polls</title><meta name="description" content="Create polls and vote."><link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🗳️</text></svg>"><link rel="stylesheet" href="/_astro/index.CWh57_AP.css"></head> <body> <style>astro-island,astro-slot,astro-static-slot{display:contents}</style><script>(()=>{var e=async t=>{await(await t())()};(self.Astro||(self.Astro={})).only=e;window.dispatchEvent(new Event("astro:only"));})();</script><script>(()=>{var A=Object.defineProperty;var g=(i,o,a)=>o in i?A(i,o,{enumerable:!0,configurable:!0,writable:!0,value:a}):i[o]=a;var d=(i,o,a)=>g(i,typeof o!="symbol"?o+"":o,a);{let i={0:t=>m(t),1:t=>a(t),2:t=>new RegExp(t),3:t=>new Date(t),4:t=>new Map(a(t)),5:t=>new Set(a(t)),6:t=>BigInt(t),7:t=>new URL(t),8:t=>new Uint8Array(t),9:t=>new Uint16Array(t),10:t=>new Uint32Array(t),11:t=>1/0*t},o=t=>{let[l,e]=t;return l in i?i[l](e):void 0},a=t=>t.map(o),m=t=>typeof t!="object"||t===null?t:Object.fromEntries(Object.entries(t).map(([l,e])=>[l,o(e)]));class y extends HTMLElement{constructor(){super(...arguments);d(this,"Component");d(this,"hydrator");d(this,"hydrate",async()=>{var b;if(!this.hydrator||!this.isConnected)return;let e=(b=this.parentElement)==null?void 0:b.closest("astro-island[ssr]");if(e){e.addEventListener("astro:hydrate",this.hydrate,{once:!0});return}let c=this.querySelectorAll("astro-slot"),n={},h=this.querySelectorAll("template[data-astro-template]");for(let r of h){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("data-astro-template")||"default"]=r.innerHTML,r.remove())}for(let r of c){let s=r.closest(this.tagName);s!=null&&s.isSameNode(this)&&(n[r.getAttribute("name")||"default"]=r.innerHTML)}let p;try{p=this.hasAttribute("props")?m(JSON.parse(this.getAttribute("props"))):{}}catch(r){let s=this.getAttribute("component-url")||"<unknown>",v=this.getAttribute("component-export");throw v&&(s+=` (export ${v})`),console.error(`[hydrate] Error parsing props for component ${s}`,this.getAttribute("props"),r),r}let u;await this.hydrator(this)(this.Component,p,n,{client:this.getAttribute("client")}),this.removeAttribute("ssr"),this.dispatchEvent(new CustomEvent("astro:hydrate"))});d(this,"unmount",()=>{this.isConnected||this.dispatchEvent(new CustomEvent("astro:unmount"))})}disconnectedCallback(){document.removeEventListener("astro:after-swap",this.unmount),document.addEventListener("astro:after-swap",this.unmount,{once:!0})}connectedCallback(){if(!this.hasAttribute("await-children")||document.readyState==="interactive"||document.readyState==="complete")this.childrenConnectedCallback();else{let e=()=>{document.removeEventListener("DOMContentLoaded",e),c.disconnect(),this.childrenConnectedCallback()},c=new MutationObserver(()=>{var n;((n=this.lastChild)==null?void 0:n.nodeType)===Node.COMMENT_NODE&&this.lastChild.nodeValue==="astro:end"&&(this.lastChild.remove(),e())});c.observe(this,{childList:!0}),document.addEventListener("DOMContentLoaded",e)}}async childrenConnectedCallback(){let e=this.getAttribute("before-hydration-url");e&&await import(e),this.start()}async start(){let e=JSON.parse(this.getAttribute("opts")),c=this.getAttribute("client");if(Astro[c]===void 0){window.addEventListener(`astro:${c}`,()=>this.start(),{once:!0});return}try{await Astro[c](async()=>{let n=this.getAttribute("renderer-url"),[h,{default:p}]=await Promise.all([import(this.getAttribute("component-url")),n?import(n):()=>()=>{}]),u=this.getAttribute("component-export")||"default";if(!u.includes("."))this.Component=h[u];else{this.Component=h;for(let f of u.split("."))this.Component=this.Component[f]}return this.hydrator=p,this.hydrate},e,this)}catch(n){console.error(`[astro-island] Error hydrating ${this.getAttribute("component-url")}`,n)}}attributeChangedCallback(){this.hydrate()}}d(y,"observedAttributes",["props"]),customElements.get("astro-island")||customElements.define("astro-island",y)}})();</script><astro-island uid="TlKYT" component-url="/_astro/PollApp.LRn70eey.js" component-export="default" renderer-url="/_astro/client.BlZe1zq3.js" props="{}" ssr client="only" opts="{"name":"PollApp","value":"react"}"></astro-island> </body></html>
|
frontend/src/components/PollApp.tsx
CHANGED
|
@@ -35,6 +35,7 @@ const PollApp: React.FC = () => {
|
|
| 35 |
const [polls, setPolls] = useState<Poll[]>([]);
|
| 36 |
const [busy, setBusy] = useState(false);
|
| 37 |
const [tab, setTab] = useState<Tab>('polls');
|
|
|
|
| 38 |
const [question, setQuestion] = useState('');
|
| 39 |
const [options, setOptions] = useState<string[]>(['', '']);
|
| 40 |
const [notice, setNotice] = useState<Notice>(null);
|
|
@@ -104,24 +105,62 @@ const PollApp: React.FC = () => {
|
|
| 104 |
}
|
| 105 |
};
|
| 106 |
|
| 107 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
if (busy || !clientRef.current) return;
|
| 109 |
setBusy(true);
|
|
|
|
|
|
|
|
|
|
| 110 |
try {
|
| 111 |
const data = await callApi<{ ok: boolean; message: string; poll: Poll | null }>(
|
| 112 |
clientRef.current,
|
| 113 |
-
|
| 114 |
-
|
| 115 |
);
|
| 116 |
setNotice({ kind: data.ok ? 'ok' : 'err', text: data.message });
|
| 117 |
if (data.ok) {
|
|
|
|
| 118 |
setQuestion('');
|
| 119 |
setOptions(['', '']);
|
| 120 |
await refresh();
|
| 121 |
setTab('polls');
|
| 122 |
}
|
| 123 |
} catch {
|
| 124 |
-
setNotice({ kind: 'err', text: 'Could not
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
} finally {
|
| 126 |
setBusy(false);
|
| 127 |
}
|
|
@@ -197,6 +236,28 @@ const PollApp: React.FC = () => {
|
|
| 197 |
<h2>{poll.question}</h2>
|
| 198 |
<p className="meta">
|
| 199 |
{poll.total} vote{poll.total === 1 ? '' : 's'} · by @{poll.created_by}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 200 |
</p>
|
| 201 |
<ul className="opts">
|
| 202 |
{poll.options.map((opt, idx) => {
|
|
@@ -254,7 +315,7 @@ const PollApp: React.FC = () => {
|
|
| 254 |
{tab === 'create' && (
|
| 255 |
<div className="tabpanel" role="tabpanel">
|
| 256 |
<section className="card">
|
| 257 |
-
<h2>New poll</h2>
|
| 258 |
{!me.member && (
|
| 259 |
<p className="muted">
|
| 260 |
{!me.username ? 'Sign in first.' : 'Only slmconsortium members can create polls.'}
|
|
@@ -308,18 +369,25 @@ const PollApp: React.FC = () => {
|
|
| 308 |
+ Add option
|
| 309 |
</button>
|
| 310 |
</div>
|
| 311 |
-
<
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 323 |
</section>
|
| 324 |
</div>
|
| 325 |
)}
|
|
|
|
| 35 |
const [polls, setPolls] = useState<Poll[]>([]);
|
| 36 |
const [busy, setBusy] = useState(false);
|
| 37 |
const [tab, setTab] = useState<Tab>('polls');
|
| 38 |
+
const [editingId, setEditingId] = useState<string | null>(null);
|
| 39 |
const [question, setQuestion] = useState('');
|
| 40 |
const [options, setOptions] = useState<string[]>(['', '']);
|
| 41 |
const [notice, setNotice] = useState<Notice>(null);
|
|
|
|
| 105 |
}
|
| 106 |
};
|
| 107 |
|
| 108 |
+
const startEdit = (poll: Poll) => {
|
| 109 |
+
setEditingId(poll.id);
|
| 110 |
+
setQuestion(poll.question);
|
| 111 |
+
setOptions(poll.options.length > 0 ? [...poll.options] : ['', '']);
|
| 112 |
+
setNotice(null);
|
| 113 |
+
setTab('create');
|
| 114 |
+
};
|
| 115 |
+
|
| 116 |
+
const cancelEdit = () => {
|
| 117 |
+
setEditingId(null);
|
| 118 |
+
setQuestion('');
|
| 119 |
+
setOptions(['', '']);
|
| 120 |
+
setNotice(null);
|
| 121 |
+
};
|
| 122 |
+
|
| 123 |
+
const doSubmit = async () => {
|
| 124 |
if (busy || !clientRef.current) return;
|
| 125 |
setBusy(true);
|
| 126 |
+
const optsArg = options.filter((o) => o.trim()).join('\n');
|
| 127 |
+
const api = editingId ? '/edit_poll' : '/create_poll';
|
| 128 |
+
const args = editingId ? [editingId, question, optsArg] : [question, optsArg];
|
| 129 |
try {
|
| 130 |
const data = await callApi<{ ok: boolean; message: string; poll: Poll | null }>(
|
| 131 |
clientRef.current,
|
| 132 |
+
api,
|
| 133 |
+
args
|
| 134 |
);
|
| 135 |
setNotice({ kind: data.ok ? 'ok' : 'err', text: data.message });
|
| 136 |
if (data.ok) {
|
| 137 |
+
setEditingId(null);
|
| 138 |
setQuestion('');
|
| 139 |
setOptions(['', '']);
|
| 140 |
await refresh();
|
| 141 |
setTab('polls');
|
| 142 |
}
|
| 143 |
} catch {
|
| 144 |
+
setNotice({ kind: 'err', text: 'Could not save poll — please retry.' });
|
| 145 |
+
} finally {
|
| 146 |
+
setBusy(false);
|
| 147 |
+
}
|
| 148 |
+
};
|
| 149 |
+
|
| 150 |
+
const doDelete = async (poll: Poll) => {
|
| 151 |
+
if (busy || !clientRef.current) return;
|
| 152 |
+
if (!window.confirm(`Delete poll “${poll.question}” and its ${poll.total} vote(s)?`)) return;
|
| 153 |
+
setBusy(true);
|
| 154 |
+
try {
|
| 155 |
+
const data = await callApi<{ ok: boolean; message: string }>(
|
| 156 |
+
clientRef.current,
|
| 157 |
+
'/delete_poll',
|
| 158 |
+
[poll.id]
|
| 159 |
+
);
|
| 160 |
+
setNotice({ kind: data.ok ? 'ok' : 'err', text: data.message });
|
| 161 |
+
await refresh();
|
| 162 |
+
} catch {
|
| 163 |
+
setNotice({ kind: 'err', text: 'Could not delete poll — please retry.' });
|
| 164 |
} finally {
|
| 165 |
setBusy(false);
|
| 166 |
}
|
|
|
|
| 236 |
<h2>{poll.question}</h2>
|
| 237 |
<p className="meta">
|
| 238 |
{poll.total} vote{poll.total === 1 ? '' : 's'} · by @{poll.created_by}
|
| 239 |
+
{me.member && (
|
| 240 |
+
<span className="card-actions">
|
| 241 |
+
<button
|
| 242 |
+
className="icon-btn"
|
| 243 |
+
disabled={busy}
|
| 244 |
+
title="Edit poll (votes for edited/removed options are dropped)"
|
| 245 |
+
aria-label={`Edit poll ${poll.question}`}
|
| 246 |
+
onClick={() => startEdit(poll)}
|
| 247 |
+
>
|
| 248 |
+
✏️
|
| 249 |
+
</button>
|
| 250 |
+
<button
|
| 251 |
+
className="icon-btn danger"
|
| 252 |
+
disabled={busy}
|
| 253 |
+
title="Delete poll"
|
| 254 |
+
aria-label={`Delete poll ${poll.question}`}
|
| 255 |
+
onClick={() => void doDelete(poll)}
|
| 256 |
+
>
|
| 257 |
+
🗑️
|
| 258 |
+
</button>
|
| 259 |
+
</span>
|
| 260 |
+
)}
|
| 261 |
</p>
|
| 262 |
<ul className="opts">
|
| 263 |
{poll.options.map((opt, idx) => {
|
|
|
|
| 315 |
{tab === 'create' && (
|
| 316 |
<div className="tabpanel" role="tabpanel">
|
| 317 |
<section className="card">
|
| 318 |
+
<h2>{editingId ? 'Edit poll' : 'New poll'}</h2>
|
| 319 |
{!me.member && (
|
| 320 |
<p className="muted">
|
| 321 |
{!me.username ? 'Sign in first.' : 'Only slmconsortium members can create polls.'}
|
|
|
|
| 369 |
+ Add option
|
| 370 |
</button>
|
| 371 |
</div>
|
| 372 |
+
<div className="form-actions">
|
| 373 |
+
<button
|
| 374 |
+
className="btn"
|
| 375 |
+
disabled={
|
| 376 |
+
!me.member ||
|
| 377 |
+
busy ||
|
| 378 |
+
question.trim().length < 3 ||
|
| 379 |
+
options.filter((o) => o.trim()).length < 2
|
| 380 |
+
}
|
| 381 |
+
onClick={() => void doSubmit()}
|
| 382 |
+
>
|
| 383 |
+
{busy ? 'Working…' : editingId ? 'Save changes' : 'Create poll'}
|
| 384 |
+
</button>
|
| 385 |
+
{editingId && (
|
| 386 |
+
<button className="btn quiet" onClick={cancelEdit} disabled={busy}>
|
| 387 |
+
Cancel
|
| 388 |
+
</button>
|
| 389 |
+
)}
|
| 390 |
+
</div>
|
| 391 |
</section>
|
| 392 |
</div>
|
| 393 |
)}
|
frontend/src/styles/global.css
CHANGED
|
@@ -315,6 +315,11 @@ textarea { resize: vertical; }
|
|
| 315 |
.icon-btn:disabled { opacity: 0.35; cursor: not-allowed; }
|
| 316 |
.add-opt { margin-top: 2px; }
|
| 317 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 318 |
footer {
|
| 319 |
border-top: 1px solid var(--border);
|
| 320 |
margin-top: 16px;
|
|
|
|
| 315 |
.icon-btn:disabled { opacity: 0.35; cursor: not-allowed; }
|
| 316 |
.add-opt { margin-top: 2px; }
|
| 317 |
|
| 318 |
+
.card-actions { display: inline-flex; gap: 6px; margin-left: auto; }
|
| 319 |
+
.card-actions .icon-btn { width: 28px; height: 28px; border-radius: 8px; font-size: 13px; vertical-align: middle; }
|
| 320 |
+
.card-actions .icon-btn:hover:not(:disabled) { border-color: rgba(142, 115, 255, 0.5); color: #8e73ff; background: rgba(142, 115, 255, 0.08); }
|
| 321 |
+
.form-actions { display: flex; gap: 10px; margin-top: 4px; }
|
| 322 |
+
|
| 323 |
footer {
|
| 324 |
border-top: 1px solid var(--border);
|
| 325 |
margin-top: 16px;
|