polats commited on
Commit
d951b25
·
verified ·
1 Parent(s): be2c1ca

Add Karate Wiener title intro overlay

Browse files
.gitattributes CHANGED
@@ -206,3 +206,4 @@ pose_vocab_sheet.png filter=lfs diff=lfs merge=lfs -text
206
  wiener_proto_card.png filter=lfs diff=lfs merge=lfs -text
207
  wiener_session_crab.png filter=lfs diff=lfs merge=lfs -text
208
  wiener_session_scorpion.png filter=lfs diff=lfs merge=lfs -text
 
 
206
  wiener_proto_card.png filter=lfs diff=lfs merge=lfs -text
207
  wiener_session_crab.png filter=lfs diff=lfs merge=lfs -text
208
  wiener_session_scorpion.png filter=lfs diff=lfs merge=lfs -text
209
+ assets/karate_wiener_title_overlay.wav filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -1,49 +1,47 @@
1
- ---
2
- title: Karate Wiener
3
- emoji: 🥋
4
- colorFrom: green
5
- colorTo: gray
6
- sdk: gradio
7
- sdk_version: 6.9.0
8
- app_file: app.py
9
- python_version: "3.10"
10
- suggested_hardware: zero-a10g
11
- pinned: true
12
- license: apache-2.0
13
- hf_oauth: true
14
- short_description: Create your own karate moves with a sausage man
15
- models:
16
- - nvidia/Kimodo-SMPLX-RP-v1
17
- - McGill-NLP/LLM2Vec-Meta-Llama-3-8B-Instruct-mntp
18
- - McGill-NLP/LLM2Vec-Meta-Llama-3-8B-Instruct-mntp-supervised
19
- ---
20
-
21
-
22
- # Kimodo ZeroGPU
23
-
24
- Viewer-first ZeroGPU Space for Kimodo text-to-motion generation.
25
-
26
- Generated animations are saved into the configured Hugging Face Dataset store so
27
- they survive Space rebuilds and can be reused by the kata maker. Animation IDs
28
- are deterministic from the normalized prompt, model, seconds, denoising steps,
29
- seed, and generation schema; regenerating with the same settings returns the
30
- cached animation instead of spending GPU time again.
31
-
32
- ## Generation model
33
-
34
- The Space generates on the **SMPL-X 22-joint** skeleton via `kimodo-smplx-rp`
35
- (set `KIMODO_MODEL` to override). This matches the skeleton used by the kimodo
36
- web/kata viewers, so generated clips can be retargeted directly onto skinned
37
- display characters with no joint remapping.
38
-
39
- > Note: `nvidia/Kimodo-SMPLX-RP-v1` is distributed under NVIDIA's research/R&D
40
- > model license, which is more restrictive than the NVIDIA Open Model license
41
- > used by the SOMA models. Review it before public/commercial use.
42
-
43
- ## Display characters
44
-
45
- The 3D viewer can play a clip as the procedural skeleton or retarget it onto a
46
- skinned rig (an s&box Citizen ships in `assets/`). The picker in the viewer HUD
47
- switches between them; retargeting is rest-pose alignment driven by each clip's
48
- `global_quats_xyzw`. Additional rigs can be added to `_CHARACTER_CATALOG` in
49
- `app.py` as `{ id, label, mapping, glb_b64 }`.
 
1
+ ---
2
+ title: Kimodo ZeroGPU
3
+ emoji: 🥋
4
+ colorFrom: green
5
+ colorTo: gray
6
+ sdk: gradio
7
+ sdk_version: 6.9.0
8
+ app_file: app.py
9
+ python_version: "3.10"
10
+ suggested_hardware: zero-a10g
11
+ pinned: false
12
+ license: apache-2.0
13
+ hf_oauth: true
14
+ models:
15
+ - nvidia/Kimodo-SMPLX-RP-v1
16
+ - McGill-NLP/LLM2Vec-Meta-Llama-3-8B-Instruct-mntp
17
+ - McGill-NLP/LLM2Vec-Meta-Llama-3-8B-Instruct-mntp-supervised
18
+ ---
19
+
20
+ # Kimodo ZeroGPU
21
+
22
+ Viewer-first ZeroGPU Space for Kimodo text-to-motion generation.
23
+
24
+ Generated animations are saved into the configured Hugging Face Dataset store so
25
+ they survive Space rebuilds and can be reused by the kata maker. Animation IDs
26
+ are deterministic from the normalized prompt, model, seconds, denoising steps,
27
+ seed, and generation schema; regenerating with the same settings returns the
28
+ cached animation instead of spending GPU time again.
29
+
30
+ ## Generation model
31
+
32
+ The Space generates on the **SMPL-X 22-joint** skeleton via `kimodo-smplx-rp`
33
+ (set `KIMODO_MODEL` to override). This matches the skeleton used by the kimodo
34
+ web/kata viewers, so generated clips can be retargeted directly onto skinned
35
+ display characters with no joint remapping.
36
+
37
+ > Note: `nvidia/Kimodo-SMPLX-RP-v1` is distributed under NVIDIA's research/R&D
38
+ > model license, which is more restrictive than the NVIDIA Open Model license
39
+ > used by the SOMA models. Review it before public/commercial use.
40
+
41
+ ## Display characters
42
+
43
+ The 3D viewer can play a clip as the procedural skeleton or retarget it onto a
44
+ skinned rig (an s&box Citizen ships in `assets/`). The picker in the viewer HUD
45
+ switches between them; retargeting is rest-pose alignment driven by each clip's
46
+ `global_quats_xyzw`. Additional rigs can be added to `_CHARACTER_CATALOG` in
47
+ `app.py` as `{ id, label, mapping, glb_b64 }`.
 
 
app.py CHANGED
@@ -129,8 +129,9 @@ CLOTHING_CATALOG = [
129
  # it's still in the catalog so it can be added from the wardrobe.)
130
  DEFAULT_CLOTHING = {"face": "nerdy_glasses",
131
  "torso_over": "kimono", "legs": "kimono_trousers"}
132
- _DEFAULT_SCENE_PATH = Path(__file__).parent / "assets" / "default_scene.webp"
133
- DIT360_SPACE = os.environ.get("KIMODO_DIT360_SPACE", "Insta360-Research/DiT360").strip()
 
134
  KLEIN_SPACE = os.environ.get("KIMODO_KLEIN_SPACE", "polats/tiny-army-klein-zerogpu").strip()
135
  TRIPOSPLAT_SPACE = os.environ.get("KIMODO_TRIPOSPLAT_SPACE", "polats/kimodo-triposplat-zerogpu").strip()
136
  # tiny-aya (Cohere Tiny Aya) text model — used to suggest/improve dojo scene prompts.
@@ -155,10 +156,18 @@ TINY_AYA_SPACE = os.environ.get("KIMODO_TINY_AYA_SPACE", "polats/tiny-army-tiny-
155
  # the "NEW-TAB BACKEND EXTENSION POINT" marker near the compose wiring.
156
  # See tabs/README.md + tabs/templates/ for a complete copy-paste example.
157
  _TABS_DIR = Path(__file__).parent / "tabs"
158
- def _read_tabs(pattern: str) -> str:
159
- if not _TABS_DIR.is_dir():
160
- return ""
161
- return "".join(p.read_text(encoding="utf-8") for p in sorted(_TABS_DIR.glob(pattern)))
 
 
 
 
 
 
 
 
162
 
163
  APP_CSS = """
164
  #main-preview {
@@ -1792,7 +1801,7 @@ def _render_preview_html(
1792
  fill.position.set(-2, 3, 5);
1793
  scene.add(fill);
1794
 
1795
- const floor = new THREE.GridHelper(1, 20, 0x4c5663, 0x2b3138);
1796
  floor.position.y = dojoFloorSurfaceY;
1797
  floor.scale.set(dojoRoomSize, 1, dojoRoomSize);
1798
  scene.add(floor);
@@ -5963,8 +5972,8 @@ _ACCT_JS = r"""
5963
  # Pluggable side-tab behavior, concatenated into the drawer-JS scope (see _read_tabs).
5964
  _EXTRA_TABS_JS = _read_tabs("*.tab.js")
5965
 
5966
- DRAWER_JS = f"""
5967
- (() => {{
5968
  // Lift the SSR boot splash (the body::before/::after overlay in _BLOCKS_CSS) by
5969
  // tagging <body>. Called on the viewer's first preview-ready / viewer-error, plus a
5970
  // safety timeout so a slow/blocked CDN never traps the user behind the spinner.
@@ -6254,9 +6263,335 @@ DRAWER_JS = f"""
6254
  }}
6255
  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot);
6256
  else boot();
6257
- }})();
6258
- """
6259
- APP_HEAD = (
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6260
  # Warm the CDNs the viewer iframe pulls three.js (+ lazy spark) from. (head= is
6261
  # injected at hydration, so this is a best-effort warm-up; the boot splash that
6262
  # hides the pre-hydration flash lives in the SSR'd css= block instead — see
@@ -6264,10 +6599,11 @@ APP_HEAD = (
6264
  '<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>'
6265
  '<link rel="dns-prefetch" href="https://cdn.jsdelivr.net">'
6266
  '<link rel="preconnect" href="https://unpkg.com" crossorigin>'
6267
- '<script src="https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js"></script>'
6268
- # APP_CSS + any pluggable-tab styles (tabs/<name>.tab.css), then the drawer JS.
6269
- f"<style>{APP_CSS}{_read_tabs('*.tab.css')}</style><script>{DRAWER_JS}</script>"
6270
- )
 
6271
 
6272
 
6273
  def generate(
 
129
  # it's still in the catalog so it can be added from the wardrobe.)
130
  DEFAULT_CLOTHING = {"face": "nerdy_glasses",
131
  "torso_over": "kimono", "legs": "kimono_trousers"}
132
+ _DEFAULT_SCENE_PATH = Path(__file__).parent / "assets" / "default_scene.webp"
133
+ _TITLE_OVERLAY_AUDIO_PATH = Path(__file__).parent / "assets" / "karate_wiener_title_overlay.wav"
134
+ DIT360_SPACE = os.environ.get("KIMODO_DIT360_SPACE", "Insta360-Research/DiT360").strip()
135
  KLEIN_SPACE = os.environ.get("KIMODO_KLEIN_SPACE", "polats/tiny-army-klein-zerogpu").strip()
136
  TRIPOSPLAT_SPACE = os.environ.get("KIMODO_TRIPOSPLAT_SPACE", "polats/kimodo-triposplat-zerogpu").strip()
137
  # tiny-aya (Cohere Tiny Aya) text model — used to suggest/improve dojo scene prompts.
 
156
  # the "NEW-TAB BACKEND EXTENSION POINT" marker near the compose wiring.
157
  # See tabs/README.md + tabs/templates/ for a complete copy-paste example.
158
  _TABS_DIR = Path(__file__).parent / "tabs"
159
+ def _read_tabs(pattern: str) -> str:
160
+ if not _TABS_DIR.is_dir():
161
+ return ""
162
+ return "".join(p.read_text(encoding="utf-8") for p in sorted(_TABS_DIR.glob(pattern)))
163
+
164
+
165
+ def _load_b64(path: Path) -> str:
166
+ try:
167
+ return base64.b64encode(path.read_bytes()).decode("ascii")
168
+ except Exception as exc: # pragma: no cover - optional startup flourish
169
+ print(f"Could not load {path}: {exc}")
170
+ return ""
171
 
172
  APP_CSS = """
173
  #main-preview {
 
1801
  fill.position.set(-2, 3, 5);
1802
  scene.add(fill);
1803
 
1804
+ const floor = new THREE.GridHelper(1, 20, 0x42f6ff, 0x1aa6b8);
1805
  floor.position.y = dojoFloorSurfaceY;
1806
  floor.scale.set(dojoRoomSize, 1, dojoRoomSize);
1807
  scene.add(floor);
 
5972
  # Pluggable side-tab behavior, concatenated into the drawer-JS scope (see _read_tabs).
5973
  _EXTRA_TABS_JS = _read_tabs("*.tab.js")
5974
 
5975
+ DRAWER_JS = f"""
5976
+ (() => {{
5977
  // Lift the SSR boot splash (the body::before/::after overlay in _BLOCKS_CSS) by
5978
  // tagging <body>. Called on the viewer's first preview-ready / viewer-error, plus a
5979
  // safety timeout so a slow/blocked CDN never traps the user behind the spinner.
 
6263
  }}
6264
  if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', boot);
6265
  else boot();
6266
+ }})();
6267
+ """
6268
+
6269
+ TITLE_OVERLAY_CSS = """
6270
+ #kw-title-overlay {
6271
+ position: fixed; inset: 0; z-index: 100020; overflow: hidden;
6272
+ display: grid; place-items: center; background: transparent;
6273
+ font-family: Impact, Haettenschweiler, "Arial Black", system-ui, sans-serif;
6274
+ pointer-events: none;
6275
+ }
6276
+ #kw-title-overlay.kw-ready { pointer-events: auto; }
6277
+ #kw-title-overlay.kw-done { pointer-events: none; }
6278
+ #kw-title-overlay::before {
6279
+ content: ""; position: absolute; inset: 0; pointer-events: none;
6280
+ background:
6281
+ radial-gradient(circle at 50% 50%, transparent 0 24%, rgba(0,0,0,.08) 62%, rgba(0,0,0,.22) 100%),
6282
+ linear-gradient(90deg, rgba(255,42,163,.08), transparent 25% 75%, rgba(66,246,255,.08));
6283
+ }
6284
+ .kw-sun {
6285
+ position: absolute; z-index: 3; left: 50%; top: 45%; width: min(42vw, 460px);
6286
+ aspect-ratio: 1; border-radius: 50%; opacity: 0; pointer-events: none;
6287
+ transform: translate(-50%, -58%) scale(.72);
6288
+ background:
6289
+ repeating-linear-gradient(0deg, transparent 0 18px, rgba(7,2,15,.78) 19px 26px),
6290
+ linear-gradient(180deg, #ffe96c, #ff8a26 46%, #ef2a7a 100%);
6291
+ }
6292
+ #kw-title-overlay.kw-exploded .kw-sun { animation: kwSunReveal .55s ease-out forwards; }
6293
+ #kw-title-overlay.kw-vanish .kw-sun { animation: kwSunVanish .48s ease-in forwards; }
6294
+ .kw-card {
6295
+ position: relative; z-index: 6; width: min(94vw, 1080px); text-align: center;
6296
+ transform: translateY(-1vh); overflow: visible;
6297
+ }
6298
+ .kw-logo {
6299
+ position: relative; transform: translateX(-145vw) rotate(-1080deg) scale(.18);
6300
+ opacity: 0; transform-origin: 50% 52%; overflow: visible;
6301
+ }
6302
+ #kw-title-overlay.kw-running .kw-logo { animation: kwLogoSpinIn .95s linear forwards; }
6303
+ #kw-title-overlay.kw-vanish .kw-logo { animation: kwLogoVanish .48s cubic-bezier(.62,0,.9,.42) forwards; }
6304
+ .kw-title {
6305
+ margin: 0; display: flex; flex-direction: column; align-items: center;
6306
+ text-transform: uppercase; line-height: .78; letter-spacing: 0;
6307
+ font-size: clamp(72px, 15vw, 184px); color: #ffd84a;
6308
+ -webkit-text-stroke: clamp(2px, .45vw, 6px) #2a032a;
6309
+ text-shadow:
6310
+ 5px 5px 0 #8f143f,
6311
+ 10px 10px 0 #2a032a,
6312
+ 0 0 18px rgba(255,216,74,.65),
6313
+ 0 0 44px rgba(255,42,163,.85);
6314
+ transform: rotate(-7deg) scale(1);
6315
+ }
6316
+ .kw-title span:last-child {
6317
+ color: #ff5d2c;
6318
+ text-shadow:
6319
+ 5px 5px 0 #7021a8,
6320
+ 10px 10px 0 #22022d,
6321
+ 0 0 18px rgba(255,107,25,.75),
6322
+ 0 0 46px rgba(66,246,255,.46);
6323
+ }
6324
+ #kw-title-overlay.kw-exploded .kw-title { animation: kwTitleImpact .82s ease-out forwards; }
6325
+ .kw-blast { position: absolute; inset: 0; z-index: 4; width: 100%; height: 100%; pointer-events: none; }
6326
+ .kw-grain, .kw-scanlines { position: absolute; inset: 0; z-index: 7; pointer-events: none; }
6327
+ .kw-play {
6328
+ position: absolute; z-index: 6; left: 50%; top: 50%;
6329
+ transform: translate(-50%, -50%); display: none; align-items: center; justify-content: center;
6330
+ width: clamp(78px, 19vw, 110px); aspect-ratio: 1; padding: 0; border: 0;
6331
+ border-radius: 999px;
6332
+ background:
6333
+ linear-gradient(rgba(7,2,15,.14), rgba(7,2,15,.14)) padding-box,
6334
+ linear-gradient(180deg, rgba(255,233,108,.58) 0%, rgba(255,155,47,.52) 43%, rgba(239,42,122,.5) 100%) border-box;
6335
+ border: 4px solid transparent; backdrop-filter: blur(1px);
6336
+ box-shadow: 0 0 0 1px rgba(42,3,42,.38), 0 0 18px rgba(239,42,122,.18), inset 0 0 20px rgba(255,155,47,.06);
6337
+ cursor: pointer;
6338
+ }
6339
+ .kw-play::before {
6340
+ content: ""; width: 0; height: 0; margin-left: 7%;
6341
+ border-top: clamp(13px, 3.2vw, 18px) solid transparent;
6342
+ border-bottom: clamp(13px, 3.2vw, 18px) solid transparent;
6343
+ border-left: clamp(20px, 5vw, 29px) solid rgba(255,138,38,.58);
6344
+ filter: drop-shadow(0 0 6px rgba(255,138,38,.22)) drop-shadow(0 0 12px rgba(239,42,122,.16));
6345
+ }
6346
+ .kw-play:active { transform: translate(-50%, -50%) scale(.94); filter: brightness(1.18); }
6347
+ #kw-title-overlay.kw-ready .kw-play { display: inline-flex; animation: kwPlayIdle 1.45s ease-in-out infinite; }
6348
+ #kw-title-overlay.kw-pressed .kw-play { display: inline-flex; animation: kwPlayPressed .22s ease-in forwards; }
6349
+ #kw-title-overlay.kw-running .kw-play,
6350
+ #kw-title-overlay.kw-vanish .kw-play,
6351
+ #kw-title-overlay.kw-done .kw-play { display: none; }
6352
+ .kw-grain {
6353
+ opacity: .12;
6354
+ background-image:
6355
+ radial-gradient(circle at 15% 35%, #fff 0 1px, transparent 1px),
6356
+ radial-gradient(circle at 68% 22%, #fff 0 1px, transparent 1px),
6357
+ radial-gradient(circle at 45% 71%, #fff 0 1px, transparent 1px);
6358
+ background-size: 37px 31px, 53px 47px, 71px 61px;
6359
+ animation: kwGrain .3s steps(2,end) infinite;
6360
+ }
6361
+ .kw-scanlines { background: repeating-linear-gradient(0deg, rgba(255,255,255,.035) 0 1px, transparent 1px 4px); mix-blend-mode: screen; }
6362
+ #kw-title-overlay.kw-filters-out .kw-grain,
6363
+ #kw-title-overlay.kw-filters-out .kw-scanlines { opacity: 0; transition: opacity 1.8s ease; }
6364
+ #kw-title-overlay.kw-flash { animation: kwScreenFlash .82s ease-out; }
6365
+ @keyframes kwLogoSpinIn {
6366
+ 0% { opacity: 0; transform: translateX(-145vw) rotate(-1260deg) scale(.16); }
6367
+ 66% { opacity: 1; transform: translateX(-26vw) rotate(-520deg) scale(.82); }
6368
+ 91% { opacity: 1; transform: translateX(0) rotate(-94deg) scale(1.18); }
6369
+ 97% { opacity: 1; transform: translateX(0) rotate(-24deg) scale(1.24); }
6370
+ 100% { opacity: 1; transform: translateX(0) rotate(0deg) scale(1.18); }
6371
+ }
6372
+ @keyframes kwLogoVanish {
6373
+ 0% { opacity: 1; transform: translateX(0) rotate(0deg) scale(1.18); filter: brightness(1.15); }
6374
+ 100% { opacity: 0; transform: translateX(0) rotate(0deg) scale(.03); filter: brightness(2.4) blur(8px); }
6375
+ }
6376
+ @keyframes kwSunReveal {
6377
+ 0% { opacity: 0; transform: translate(-50%, -58%) scale(.72); }
6378
+ 22% { opacity: .95; transform: translate(-50%, -58%) scale(1.08); }
6379
+ 100% { opacity: .88; transform: translate(-50%, -58%) scale(1); }
6380
+ }
6381
+ @keyframes kwSunVanish {
6382
+ from { opacity: .88; transform: translate(-50%, -58%) scale(1); }
6383
+ to { opacity: 0; transform: translate(-50%, -58%) scale(.25); }
6384
+ }
6385
+ @keyframes kwTitleImpact {
6386
+ 0% { transform: rotate(-7deg) scale(1); filter: brightness(1) saturate(1.4); }
6387
+ 18% { transform: rotate(-4deg) scale(1.1); filter: brightness(3.2) saturate(2.2); }
6388
+ 34% { transform: rotate(-10deg) scale(1.03); filter: brightness(1.55) saturate(1.7); }
6389
+ 100% { transform: rotate(-7deg) scale(1); filter: brightness(1.16) saturate(1.48); }
6390
+ }
6391
+ @keyframes kwScreenFlash {
6392
+ 0% { box-shadow: inset 0 0 0 100vmax rgba(255,247,196,.82); }
6393
+ 18% { box-shadow: inset 0 0 0 100vmax rgba(255,88,24,.42); }
6394
+ 100% { box-shadow: inset 0 0 0 100vmax rgba(255,88,24,0); }
6395
+ }
6396
+ @keyframes kwPlayIdle {
6397
+ 0%, 100% { opacity: .54; transform: translate(-50%, -50%) scale(1); filter: saturate(1.02) brightness(.96); }
6398
+ 50% { opacity: .7; transform: translate(-50%, -50%) scale(1.06); filter: saturate(1.12) brightness(1.06); }
6399
+ }
6400
+ @keyframes kwPlayPressed {
6401
+ 0% { opacity: 1; transform: translate(-50%, -50%) scale(1); filter: brightness(1); }
6402
+ 42% { opacity: .96; transform: translate(-50%, -50%) scale(1.18); filter: brightness(1.45); }
6403
+ 100% { opacity: 0; transform: translate(-50%, -50%) scale(.08); filter: brightness(2) blur(3px); }
6404
+ }
6405
+ @keyframes kwGrain { from { transform: translate(0,0); } to { transform: translate(-18px,11px); } }
6406
+ @media (max-width: 620px) {
6407
+ .kw-title { font-size: clamp(58px, 21vw, 112px); }
6408
+ .kw-sun { width: 76vw; }
6409
+ }
6410
+ """
6411
+
6412
+ _TITLE_OVERLAY_AUDIO_B64 = _load_b64(_TITLE_OVERLAY_AUDIO_PATH)
6413
+ _TITLE_OVERLAY_AUDIO_DATA = (
6414
+ "data:audio/wav;base64," + _TITLE_OVERLAY_AUDIO_B64
6415
+ if _TITLE_OVERLAY_AUDIO_B64 else ""
6416
+ )
6417
+
6418
+ TITLE_OVERLAY_JS = f"""
6419
+ (() => {{
6420
+ const AUDIO_SRC = {json.dumps(_TITLE_OVERLAY_AUDIO_DATA)};
6421
+ if (!AUDIO_SRC || window.__kwTitleOverlayInstalled) return;
6422
+ window.__kwTitleOverlayInstalled = true;
6423
+
6424
+ function install() {{
6425
+ if (document.getElementById('kw-title-overlay')) return;
6426
+ const root = document.createElement('div');
6427
+ root.id = 'kw-title-overlay';
6428
+ root.innerHTML = `
6429
+ <canvas class="kw-blast" aria-hidden="true"></canvas>
6430
+ <div class="kw-sun" aria-hidden="true"></div>
6431
+ <section class="kw-card" aria-label="Karate Wiener title card">
6432
+ <div class="kw-logo">
6433
+ <h1 class="kw-title"><span>Karate</span><span>Wiener</span></h1>
6434
+ </div>
6435
+ </section>
6436
+ <button class="kw-play" type="button" aria-label="Start Karate Wiener intro"></button>
6437
+ <div class="kw-grain" aria-hidden="true"></div>
6438
+ <div class="kw-scanlines" aria-hidden="true"></div>
6439
+ <audio class="kw-audio" preload="auto"></audio>
6440
+ `;
6441
+ document.body.appendChild(root);
6442
+
6443
+ const audio = root.querySelector('.kw-audio');
6444
+ const canvas = root.querySelector('.kw-blast');
6445
+ const ctx = canvas.getContext('2d');
6446
+ const mobile = window.matchMedia('(max-width: 700px), (pointer: coarse)').matches;
6447
+ let particles = [];
6448
+ let sparks = [];
6449
+ let raf = 0;
6450
+ let active = false;
6451
+ let starting = false;
6452
+ audio.src = AUDIO_SRC;
6453
+
6454
+ function resize() {{
6455
+ const dpr = Math.min(mobile ? 1.35 : 2, window.devicePixelRatio || 1);
6456
+ canvas.width = Math.floor(window.innerWidth * dpr);
6457
+ canvas.height = Math.floor(window.innerHeight * dpr);
6458
+ canvas.style.width = `${{window.innerWidth}}px`;
6459
+ canvas.style.height = `${{window.innerHeight}}px`;
6460
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
6461
+ }}
6462
+ function rand(min, max) {{ return min + Math.random() * (max - min); }}
6463
+ function makeExplosion() {{
6464
+ const cx = window.innerWidth / 2;
6465
+ const cy = window.innerHeight * 0.45;
6466
+ particles = [];
6467
+ sparks = [];
6468
+ const pc = mobile ? 58 : 96;
6469
+ const sc = mobile ? 96 : 170;
6470
+ for (let i = 0; i < pc; i += 1) {{
6471
+ const a = rand(0, Math.PI * 2);
6472
+ const speed = rand(5, 24);
6473
+ particles.push({{
6474
+ x: cx + Math.cos(a) * rand(0, 56),
6475
+ y: cy + Math.sin(a) * rand(0, 38),
6476
+ vx: Math.cos(a) * speed,
6477
+ vy: Math.sin(a) * speed - rand(0, 5),
6478
+ r: rand(10, 54),
6479
+ life: rand(.85, 1.55),
6480
+ max: 1,
6481
+ hue: rand(22, 58),
6482
+ }});
6483
+ }}
6484
+ for (let i = 0; i < sc; i += 1) {{
6485
+ const a = rand(-Math.PI * .95, Math.PI * 1.95);
6486
+ const speed = rand(7, 38);
6487
+ sparks.push({{
6488
+ x: cx, y: cy,
6489
+ vx: Math.cos(a) * speed,
6490
+ vy: Math.sin(a) * speed,
6491
+ life: rand(.4, 1.1),
6492
+ len: rand(8, 30),
6493
+ hue: rand(35, 62),
6494
+ }});
6495
+ }}
6496
+ root.classList.add('kw-flash', 'kw-exploded');
6497
+ window.setTimeout(() => root.classList.remove('kw-flash'), 900);
6498
+ animateExplosion();
6499
+ }}
6500
+ function drawExplosion() {{
6501
+ ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
6502
+ ctx.globalCompositeOperation = 'lighter';
6503
+ particles.forEach((p) => {{
6504
+ const alpha = Math.max(0, p.life / p.max);
6505
+ const grad = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, p.r);
6506
+ grad.addColorStop(0, `hsla(${{p.hue}},100%,72%,${{.8 * alpha}})`);
6507
+ grad.addColorStop(.38, `hsla(${{p.hue - 18}},100%,50%,${{.45 * alpha}})`);
6508
+ grad.addColorStop(1, 'hsla(3,94%,30%,0)');
6509
+ ctx.fillStyle = grad;
6510
+ ctx.beginPath();
6511
+ ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
6512
+ ctx.fill();
6513
+ }});
6514
+ ctx.lineCap = 'round';
6515
+ sparks.forEach((s) => {{
6516
+ const alpha = Math.max(0, s.life);
6517
+ ctx.strokeStyle = `hsla(${{s.hue}},100%,70%,${{alpha}})`;
6518
+ ctx.lineWidth = 2.4 * alpha;
6519
+ ctx.beginPath();
6520
+ ctx.moveTo(s.x, s.y);
6521
+ ctx.lineTo(s.x - s.vx * .7, s.y - s.vy * .7 + s.len);
6522
+ ctx.stroke();
6523
+ }});
6524
+ ctx.globalCompositeOperation = 'source-over';
6525
+ }}
6526
+ function animateExplosion() {{
6527
+ window.cancelAnimationFrame(raf);
6528
+ let last = performance.now();
6529
+ function tick(now) {{
6530
+ const dt = Math.min(.033, (now - last) / 1000);
6531
+ last = now;
6532
+ particles.forEach((p) => {{
6533
+ p.x += p.vx; p.y += p.vy;
6534
+ p.vx *= .964; p.vy = p.vy * .96 + .22;
6535
+ p.r *= 1.012; p.life -= dt;
6536
+ }});
6537
+ sparks.forEach((s) => {{
6538
+ s.x += s.vx; s.y += s.vy;
6539
+ s.vx *= .982; s.vy = s.vy * .98 + .5;
6540
+ s.life -= dt * 1.18;
6541
+ }});
6542
+ particles = particles.filter((p) => p.life > 0);
6543
+ sparks = sparks.filter((s) => s.life > 0);
6544
+ drawExplosion();
6545
+ if (particles.length || sparks.length) raf = window.requestAnimationFrame(tick);
6546
+ else ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
6547
+ }}
6548
+ raf = window.requestAnimationFrame(tick);
6549
+ }}
6550
+ async function run() {{
6551
+ if (active) return;
6552
+ active = true;
6553
+ root.classList.remove('kw-ready', 'kw-pressed', 'kw-exploded', 'kw-flash', 'kw-vanish', 'kw-filters-out', 'kw-done');
6554
+ void root.offsetWidth;
6555
+ root.classList.add('kw-running');
6556
+ window.setTimeout(async () => {{
6557
+ makeExplosion();
6558
+ try {{
6559
+ audio.muted = false;
6560
+ audio.volume = 1;
6561
+ audio.currentTime = 0;
6562
+ await audio.play();
6563
+ }} catch (error) {{
6564
+ console.warn('Karate Wiener audio blocked:', error);
6565
+ }}
6566
+ }}, 950);
6567
+ window.setTimeout(() => root.classList.add('kw-vanish'), 3450);
6568
+ window.setTimeout(() => root.classList.add('kw-filters-out'), 3950);
6569
+ window.setTimeout(() => {{
6570
+ root.classList.add('kw-done');
6571
+ root.remove();
6572
+ }}, 5600);
6573
+ }}
6574
+ resize();
6575
+ window.addEventListener('resize', resize, {{ passive: true }});
6576
+ root.addEventListener('click', async (event) => {{
6577
+ if (!root.classList.contains('kw-ready') || starting) return;
6578
+ starting = true;
6579
+ event.preventDefault();
6580
+ root.classList.add('kw-pressed');
6581
+ window.setTimeout(run, 180);
6582
+ }});
6583
+ if (mobile) {{
6584
+ root.classList.add('kw-ready');
6585
+ }} else {{
6586
+ window.setTimeout(run, 350);
6587
+ }}
6588
+ }}
6589
+ if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', install);
6590
+ else install();
6591
+ }})();
6592
+ """
6593
+
6594
+ APP_HEAD = (
6595
  # Warm the CDNs the viewer iframe pulls three.js (+ lazy spark) from. (head= is
6596
  # injected at hydration, so this is a best-effort warm-up; the boot splash that
6597
  # hides the pre-hydration flash lives in the SSR'd css= block instead — see
 
6599
  '<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>'
6600
  '<link rel="dns-prefetch" href="https://cdn.jsdelivr.net">'
6601
  '<link rel="preconnect" href="https://unpkg.com" crossorigin>'
6602
+ '<script src="https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js"></script>'
6603
+ # APP_CSS + any pluggable-tab styles (tabs/<name>.tab.css), then the drawer JS.
6604
+ f"<style>{APP_CSS}{_read_tabs('*.tab.css')}{TITLE_OVERLAY_CSS}</style>"
6605
+ f"<script>{DRAWER_JS}</script><script>{TITLE_OVERLAY_JS}</script>"
6606
+ )
6607
 
6608
 
6609
  def generate(
assets/karate_wiener_title_overlay.wav ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:dcd9c57314a80c1e1ee4f95f95823a7171abdcac03508e96edfc4d765d474046
3
+ size 783404
deploy_out2.txt CHANGED
@@ -802,3 +802,7 @@ variables + HF_TOKEN secret set
802
 
803
  ...ace\comic_koma_sample.png: 100%|##########| 365kB / 365kB
804
 
805
  ...raw_lora-minimal_1234.png: 100%|##########| 490kB / 490kB
806
 
807
  [+ 167 files] : 100%|##########| 285MB / 285MB
 
 
 
 
 
802
 
803
  ...ace\comic_koma_sample.png: 100%|##########| 365kB / 365kB
804
 
805
  ...raw_lora-minimal_1234.png: 100%|##########| 490kB / 490kB
806
 
807
  [+ 167 files] : 100%|##########| 285MB / 285MB
808
+ No files have been modified since last commit. Skipping to prevent empty commit.
809
+ Uploaded: https://huggingface.co/spaces/build-small-hackathon/karate-wiener/commit/be2c1ca83c0c7fd01bc453c629c1bface7121a4f
810
+ Space URL: https://huggingface.co/spaces/build-small-hackathon/karate-wiener
811
+ restarting local gradio -> http://127.0.0.1:7860 (log: C:\Users\paulg\AppData\Local\Temp\kimodo_local_gradio.log)
deploy_space.py CHANGED
@@ -43,6 +43,21 @@ def main() -> None:
43
  ".kimodo-animations/**",
44
  # Local headless probes / checks (never part of the Space).
45
  "_*.py",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  ],
47
  )
48
  runtime = api.request_space_hardware(repo_id=args.repo_id, hardware=SpaceHardware.ZERO_A10G)
 
43
  ".kimodo-animations/**",
44
  # Local headless probes / checks (never part of the Space).
45
  "_*.py",
46
+ # Experiment line + LAN-only lab/prototype servers (never deployed).
47
+ "experiments/**",
48
+ "kimodo_lab.py",
49
+ "flux_lab.py",
50
+ "wiener_dojo.py",
51
+ "wiener_dojo_web.py",
52
+ ".comic-cache/**",
53
+ "comic_klein_out/**",
54
+ "weiner_dataset_raw/**",
55
+ "weiner_lora_train/**",
56
+ "weiner_lora_train.zip",
57
+ "*.task",
58
+ "*.obj",
59
+ "dojo_*",
60
+ ".gitignore",
61
  ],
62
  )
63
  runtime = api.request_space_hardware(repo_id=args.repo_id, hardware=SpaceHardware.ZERO_A10G)