manikandanj commited on
Commit
8759d45
·
verified ·
1 Parent(s): b15e35f

Use direct Gradio Space app

Browse files
app.py CHANGED
@@ -28,20 +28,20 @@ def _load_local_env(path: Path, *, override: bool = False) -> None:
28
  _load_local_env(ROOT_ENV, override=False)
29
  _load_local_env(LOCAL_ENV, override=False)
30
 
31
- from time_machine.application.container import create_container
32
- from time_machine.ui.gradio_app import create_app
33
- from time_machine.ui.realtime import realtime_voice_socket
34
-
35
- import gradio as gr
36
  from fastapi import FastAPI, WebSocket
37
  from fastapi.responses import JSONResponse, RedirectResponse, Response
38
  from fastapi.staticfiles import StaticFiles
39
 
40
 
41
- container = create_container()
42
- demo = create_app(container)
43
- fastapi_app = FastAPI(title="AI Time Machine")
44
- fastapi_app.mount("/static", StaticFiles(directory=ROOT / "static"), name="static")
45
 
46
 
47
  @fastapi_app.websocket("/realtime/voice")
@@ -77,12 +77,16 @@ async def blank_redirect():
77
 
78
 
79
  head = getattr(demo, "time_machine_head", None)
80
- app = gr.mount_gradio_app(fastapi_app, demo, path="/", head=head)
81
-
82
-
83
- if __name__ == "__main__":
84
- import uvicorn
85
-
 
86
  host = os.getenv("GRADIO_SERVER_NAME", "0.0.0.0")
87
- port = int(os.getenv("GRADIO_SERVER_PORT", "7860"))
88
- uvicorn.run(app, host=host, port=port)
 
 
 
 
28
  _load_local_env(ROOT_ENV, override=False)
29
  _load_local_env(LOCAL_ENV, override=False)
30
 
31
+ from time_machine.application.container import create_container
32
+ from time_machine.ui.gradio_app import create_app
33
+ from time_machine.ui.realtime import realtime_voice_socket
34
+
35
+ import gradio as gr
36
  from fastapi import FastAPI, WebSocket
37
  from fastapi.responses import JSONResponse, RedirectResponse, Response
38
  from fastapi.staticfiles import StaticFiles
39
 
40
 
41
+ container = create_container()
42
+ demo = create_app(container)
43
+ fastapi_app = FastAPI(title="AI Time Machine")
44
+ fastapi_app.mount("/static", StaticFiles(directory=ROOT / "static"), name="static")
45
 
46
 
47
  @fastapi_app.websocket("/realtime/voice")
 
77
 
78
 
79
  head = getattr(demo, "time_machine_head", None)
80
+ fastapi_app_with_gradio = gr.mount_gradio_app(fastapi_app, demo, path="/app", head=head)
81
+ app = fastapi_app_with_gradio if os.getenv("TIME_MACHINE_FASTAPI_WRAPPER") else demo
82
+
83
+
84
+ if __name__ == "__main__":
85
+ import uvicorn
86
+
87
  host = os.getenv("GRADIO_SERVER_NAME", "0.0.0.0")
88
+ port = int(os.getenv("GRADIO_SERVER_PORT", "7860"))
89
+ if os.getenv("TIME_MACHINE_FASTAPI_WRAPPER"):
90
+ uvicorn.run(fastapi_app_with_gradio, host=host, port=port)
91
+ else:
92
+ demo.launch(server_name=host, server_port=port)
src/time_machine/adapters/fixtures/fixture_immersion.py CHANGED
@@ -133,14 +133,11 @@ def _palette(destination: Destination) -> tuple[str, str, str]:
133
 
134
  def _scene_svg(destination: Destination) -> str:
135
  primary, secondary, accent = _palette(destination)
136
- title = html.escape(f"{destination.place}, {destination.year}")
137
- atmosphere = html.escape(destination.atmosphere[:120])
138
- return f"""<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="768" viewBox="0 0 1280 768">
139
  <defs>
140
  <radialGradient id="portal" cx="50%" cy="38%" r="58%"><stop offset="0" stop-color="{accent}" stop-opacity=".9"/><stop offset=".42" stop-color="{primary}" stop-opacity=".46"/><stop offset="1" stop-color="#080503"/></radialGradient>
141
  <linearGradient id="ground" x1="0" x2="1"><stop offset="0" stop-color="{secondary}"/><stop offset=".5" stop-color="#080503"/><stop offset="1" stop-color="{primary}"/></linearGradient>
142
- <filter id="blur"><feGaussianBlur stdDeviation="14"/></filter>
143
- </defs>
144
  <rect width="1280" height="768" fill="#080503"/>
145
  <rect width="1280" height="768" fill="url(#portal)"/>
146
  <circle cx="640" cy="320" r="210" fill="none" stroke="{accent}" stroke-width="3" opacity=".65"/>
@@ -152,10 +149,8 @@ def _scene_svg(destination: Destination) -> str:
152
  <path d="M930 590 L980 285 L1050 590 Z" fill="#080503"/>
153
  <path d="M1110 585 L1160 350 L1210 585 Z" fill="#080503"/>
154
  </g>
155
- <g filter="url(#blur)" opacity=".45"><circle cx="290" cy="165" r="80" fill="{accent}"/><circle cx="955" cy="195" r="110" fill="{primary}"/></g>
156
- <text x="60" y="700" fill="#f4e7c7" font-family="Georgia, serif" font-size="34">{title}</text>
157
- <text x="64" y="734" fill="#dcb37b" font-family="Georgia, serif" font-size="20">{atmosphere}</text>
158
- </svg>"""
159
 
160
 
161
  def _portrait_svg(destination: Destination, persona: Persona) -> str:
 
133
 
134
  def _scene_svg(destination: Destination) -> str:
135
  primary, secondary, accent = _palette(destination)
136
+ return f"""<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="768" viewBox="0 0 1280 768">
 
 
137
  <defs>
138
  <radialGradient id="portal" cx="50%" cy="38%" r="58%"><stop offset="0" stop-color="{accent}" stop-opacity=".9"/><stop offset=".42" stop-color="{primary}" stop-opacity=".46"/><stop offset="1" stop-color="#080503"/></radialGradient>
139
  <linearGradient id="ground" x1="0" x2="1"><stop offset="0" stop-color="{secondary}"/><stop offset=".5" stop-color="#080503"/><stop offset="1" stop-color="{primary}"/></linearGradient>
140
+ </defs>
 
141
  <rect width="1280" height="768" fill="#080503"/>
142
  <rect width="1280" height="768" fill="url(#portal)"/>
143
  <circle cx="640" cy="320" r="210" fill="none" stroke="{accent}" stroke-width="3" opacity=".65"/>
 
149
  <path d="M930 590 L980 285 L1050 590 Z" fill="#080503"/>
150
  <path d="M1110 585 L1160 350 L1210 585 Z" fill="#080503"/>
151
  </g>
152
+ <g opacity=".28"><circle cx="290" cy="165" r="80" fill="{accent}"/><circle cx="955" cy="195" r="110" fill="{primary}"/></g>
153
+ </svg>"""
 
 
154
 
155
 
156
  def _portrait_svg(destination: Destination, persona: Persona) -> str:
src/time_machine/ui/assets/cockpit.css CHANGED
@@ -156,7 +156,7 @@ footer,
156
  .tte-reference-stage__image--warp {
157
  opacity: var(--tte-warp-opacity, 0);
158
  background-image: url("/static/img/travel/reference_warp_blend.png");
159
- filter: saturate(1.2) contrast(1.16) brightness(1.08);
160
  transform: scale(var(--tte-warp-scale, 1.04));
161
  }
162
 
@@ -180,7 +180,7 @@ footer,
180
  .tm-state-launching .tte-reference-stage__image--departure,
181
  .tm-state-traveling .tte-reference-stage__image--departure,
182
  .tm-state-braking .tte-reference-stage__image--departure {
183
- filter: saturate(1.04) contrast(1.08) blur(var(--tte-warp-blur, 0px));
184
  transform: scale(var(--tte-departure-scale, 1.02));
185
  }
186
 
@@ -450,33 +450,50 @@ footer,
450
  position: absolute;
451
  top: var(--sign-top, 42%);
452
  left: 104%;
453
- min-width: 190px;
454
- padding: 8px 14px 9px;
455
- border: 3px solid rgba(43, 28, 17, 0.8);
456
- border-radius: 6px;
457
  background:
458
- linear-gradient(180deg, #47624d, #26382c);
 
459
  color: #fff0cb;
460
- font: 900 14px/1.05 var(--tte-heading);
461
- letter-spacing: 0.08em;
462
  text-align: center;
463
  text-transform: uppercase;
464
  box-shadow:
465
- inset 0 0 0 2px rgba(246, 216, 145, 0.35),
466
- 0 15px 16px rgba(57, 33, 20, 0.3);
 
 
467
  animation: tte-sign-future var(--sign-speed, 1500ms) linear forwards;
468
  }
469
 
 
 
 
 
 
 
 
 
 
 
 
470
  .tte-era-sign::after {
471
  content: "";
472
  position: absolute;
473
  left: 50%;
474
  top: 100%;
475
- width: 7px;
476
- height: 72px;
477
  transform: translateX(-50%);
478
- border-radius: 999px;
479
- background: linear-gradient(90deg, #6e3d20, #d8aa52, #6e3d20);
 
 
 
480
  }
481
 
482
  .tte-era-sign.past {
@@ -510,11 +527,11 @@ footer,
510
 
511
  .tte-generated-world {
512
  position: absolute;
513
- inset: -5%;
514
  z-index: 22;
515
  opacity: 0;
516
  pointer-events: none;
517
- background-position: center;
518
  background-size: cover;
519
  filter: saturate(0.98) contrast(1.05);
520
  transform: scale(1.04);
@@ -539,18 +556,18 @@ footer,
539
 
540
  .tte-generated-person {
541
  position: absolute;
542
- right: clamp(6%, 10vw, 15%);
543
- bottom: 8%;
544
  z-index: 29;
545
  display: block;
546
- width: clamp(170px, 18vw, 300px);
547
- max-height: 78%;
548
  object-fit: contain;
549
  object-position: center bottom;
550
  opacity: 0;
551
  pointer-events: none;
552
  border-radius: 26px;
553
- transform: translateY(18px) scale(0.96);
554
  transform-origin: 50% 100%;
555
  filter: drop-shadow(0 24px 20px rgba(58, 31, 17, 0.28));
556
  transition: opacity 900ms ease, transform 1200ms cubic-bezier(0.2, 0.9, 0.2, 1);
@@ -559,7 +576,7 @@ footer,
559
  .tm-has-portrait.tm-state-arrived .tte-generated-person,
560
  .tm-has-portrait.tm-state-conversing .tte-generated-person {
561
  opacity: 1;
562
- transform: translateY(0) scale(1);
563
  }
564
 
565
  .tte-steam-curtain {
@@ -1213,6 +1230,7 @@ footer,
1213
  border: 3px solid rgba(54, 31, 18, 0.86);
1214
  border-radius: 8px;
1215
  background: rgba(255, 240, 203, 0.92);
 
1216
  color: var(--tte-readable-ink);
1217
  text-align: center;
1218
  box-shadow: 0 14px 22px rgba(34, 17, 9, 0.2);
@@ -1777,9 +1795,9 @@ footer,
1777
  }
1778
 
1779
  .tte-generated-person {
1780
- right: 3%;
1781
  bottom: 10%;
1782
- width: min(190px, 38vw);
1783
  }
1784
  }
1785
 
 
156
  .tte-reference-stage__image--warp {
157
  opacity: var(--tte-warp-opacity, 0);
158
  background-image: url("/static/img/travel/reference_warp_blend.png");
159
+ filter: saturate(1.06) contrast(1.06) brightness(1.03);
160
  transform: scale(var(--tte-warp-scale, 1.04));
161
  }
162
 
 
180
  .tm-state-launching .tte-reference-stage__image--departure,
181
  .tm-state-traveling .tte-reference-stage__image--departure,
182
  .tm-state-braking .tte-reference-stage__image--departure {
183
+ filter: saturate(1.04) contrast(1.08);
184
  transform: scale(var(--tte-departure-scale, 1.02));
185
  }
186
 
 
450
  position: absolute;
451
  top: var(--sign-top, 42%);
452
  left: 104%;
453
+ min-width: 204px;
454
+ padding: 9px 18px 10px;
455
+ border: 4px solid rgba(49, 30, 17, 0.9);
456
+ border-radius: 5px;
457
  background:
458
+ linear-gradient(90deg, rgba(255, 235, 166, 0.16), transparent 18% 82%, rgba(67, 37, 18, 0.18)),
459
+ linear-gradient(180deg, #6a7a55 0%, #3b5039 42%, #243325 100%);
460
  color: #fff0cb;
461
+ font: 900 14px/1.08 var(--tte-heading);
462
+ letter-spacing: 0.05em;
463
  text-align: center;
464
  text-transform: uppercase;
465
  box-shadow:
466
+ inset 0 0 0 2px rgba(246, 216, 145, 0.34),
467
+ inset 0 12px 12px rgba(255, 239, 184, 0.12),
468
+ inset 0 -13px 12px rgba(17, 9, 5, 0.28),
469
+ 0 17px 18px rgba(57, 33, 20, 0.34);
470
  animation: tte-sign-future var(--sign-speed, 1500ms) linear forwards;
471
  }
472
 
473
+ .tte-era-sign::before {
474
+ content: "";
475
+ position: absolute;
476
+ left: 10px;
477
+ right: 10px;
478
+ top: 7px;
479
+ height: 2px;
480
+ border-radius: 999px;
481
+ background: rgba(255, 241, 194, 0.42);
482
+ }
483
+
484
  .tte-era-sign::after {
485
  content: "";
486
  position: absolute;
487
  left: 50%;
488
  top: 100%;
489
+ width: 9px;
490
+ height: 76px;
491
  transform: translateX(-50%);
492
+ border: 2px solid rgba(45, 25, 14, 0.74);
493
+ border-top: 0;
494
+ background:
495
+ linear-gradient(90deg, #5a3019, #d6a14a 45%, #6e3d20);
496
+ box-shadow: 0 10px 10px rgba(46, 28, 18, 0.26);
497
  }
498
 
499
  .tte-era-sign.past {
 
527
 
528
  .tte-generated-world {
529
  position: absolute;
530
+ inset: -4%;
531
  z-index: 22;
532
  opacity: 0;
533
  pointer-events: none;
534
+ background-position: center 42%;
535
  background-size: cover;
536
  filter: saturate(0.98) contrast(1.05);
537
  transform: scale(1.04);
 
556
 
557
  .tte-generated-person {
558
  position: absolute;
559
+ left: 50%;
560
+ bottom: 7%;
561
  z-index: 29;
562
  display: block;
563
+ width: clamp(190px, 20vw, 330px);
564
+ max-height: 80%;
565
  object-fit: contain;
566
  object-position: center bottom;
567
  opacity: 0;
568
  pointer-events: none;
569
  border-radius: 26px;
570
+ transform: translateX(-50%) translateY(18px) scale(0.96);
571
  transform-origin: 50% 100%;
572
  filter: drop-shadow(0 24px 20px rgba(58, 31, 17, 0.28));
573
  transition: opacity 900ms ease, transform 1200ms cubic-bezier(0.2, 0.9, 0.2, 1);
 
576
  .tm-has-portrait.tm-state-arrived .tte-generated-person,
577
  .tm-has-portrait.tm-state-conversing .tte-generated-person {
578
  opacity: 1;
579
+ transform: translateX(-50%) translateY(0) scale(1);
580
  }
581
 
582
  .tte-steam-curtain {
 
1230
  border: 3px solid rgba(54, 31, 18, 0.86);
1231
  border-radius: 8px;
1232
  background: rgba(255, 240, 203, 0.92);
1233
+ color: #21130d;
1234
  color: var(--tte-readable-ink);
1235
  text-align: center;
1236
  box-shadow: 0 14px 22px rgba(34, 17, 9, 0.2);
 
1795
  }
1796
 
1797
  .tte-generated-person {
1798
+ left: 50%;
1799
  bottom: 10%;
1800
+ width: min(210px, 42vw);
1801
  }
1802
  }
1803
 
src/time_machine/ui/assets/cockpit.js CHANGED
@@ -694,9 +694,9 @@ function updateRideProgress() {
694
  const warpMix = Math.max(0, 1 - Math.abs(progress - 0.48) / 0.28);
695
  cockpit.style.setProperty("--tte-progress", progress.toFixed(3));
696
  cockpit.style.setProperty("--tte-arrival-opacity", arrivalMix.toFixed(3));
697
- cockpit.style.setProperty("--tte-warp-opacity", warpMix.toFixed(3));
698
  cockpit.style.setProperty("--tte-vortex-opacity", (warpMix * 0.82).toFixed(3));
699
- cockpit.style.setProperty("--tte-warp-blur", `${(warpMix * 2.5).toFixed(2)}px`);
700
  cockpit.style.setProperty("--tte-warp-scale", (1.04 + warpMix * 0.05).toFixed(3));
701
  cockpit.style.setProperty("--tte-departure-scale", (1.02 + warpMix * 0.035).toFixed(3));
702
  if (runtime.phase === "launching" && elapsed > 1050) {
@@ -793,9 +793,9 @@ function arriveAtDestination(destination, fallbackOnly) {
793
  setVisualPhase("braking");
794
  if (cockpit) {
795
  cockpit.style.setProperty("--tte-arrival-opacity", "1");
796
- cockpit.style.setProperty("--tte-warp-opacity", "0.18");
797
  cockpit.style.setProperty("--tte-vortex-opacity", "0.148");
798
- cockpit.style.setProperty("--tte-warp-blur", "0.45px");
799
  cockpit.style.setProperty("--tte-warp-scale", "1.049");
800
  cockpit.style.setProperty("--tte-departure-scale", "1.026");
801
  }
@@ -831,7 +831,7 @@ function imageSourceFromPayload(image) {
831
  if (!image || !image.image_b64 || !image.mime_type) {
832
  return "";
833
  }
834
- if (!/^image\/(png|jpe?g|webp)$/i.test(image.mime_type)) {
835
  return "";
836
  }
837
  return `data:${image.mime_type};base64,${image.image_b64}`;
 
694
  const warpMix = Math.max(0, 1 - Math.abs(progress - 0.48) / 0.28);
695
  cockpit.style.setProperty("--tte-progress", progress.toFixed(3));
696
  cockpit.style.setProperty("--tte-arrival-opacity", arrivalMix.toFixed(3));
697
+ cockpit.style.setProperty("--tte-warp-opacity", (warpMix * 0.32).toFixed(3));
698
  cockpit.style.setProperty("--tte-vortex-opacity", (warpMix * 0.82).toFixed(3));
699
+ cockpit.style.setProperty("--tte-warp-blur", "0px");
700
  cockpit.style.setProperty("--tte-warp-scale", (1.04 + warpMix * 0.05).toFixed(3));
701
  cockpit.style.setProperty("--tte-departure-scale", (1.02 + warpMix * 0.035).toFixed(3));
702
  if (runtime.phase === "launching" && elapsed > 1050) {
 
793
  setVisualPhase("braking");
794
  if (cockpit) {
795
  cockpit.style.setProperty("--tte-arrival-opacity", "1");
796
+ cockpit.style.setProperty("--tte-warp-opacity", "0.06");
797
  cockpit.style.setProperty("--tte-vortex-opacity", "0.148");
798
+ cockpit.style.setProperty("--tte-warp-blur", "0px");
799
  cockpit.style.setProperty("--tte-warp-scale", "1.049");
800
  cockpit.style.setProperty("--tte-departure-scale", "1.026");
801
  }
 
831
  if (!image || !image.image_b64 || !image.mime_type) {
832
  return "";
833
  }
834
+ if (!/^image\/(png|jpe?g|webp|svg\+xml)$/i.test(image.mime_type)) {
835
  return "";
836
  }
837
  return `data:${image.mime_type};base64,${image.image_b64}`;
src/time_machine/ui/gradio_app.py CHANGED
@@ -1,10 +1,12 @@
1
- from __future__ import annotations
2
-
3
- from pathlib import Path
4
- import base64
5
- import inspect
6
-
7
- import gradio as gr
 
 
8
 
9
  from time_machine.application.container import AppContainer
10
  from time_machine.ui.handlers import GradioHandlers
@@ -15,19 +17,20 @@ ASSET_DIR = Path(__file__).resolve().parent / "assets"
15
 
16
  def create_app(container: AppContainer) -> gr.Blocks:
17
  handlers = GradioHandlers(container)
18
- css = (
19
- (ASSET_DIR / "intro.css").read_text(encoding="utf-8")
20
- + "\n"
21
- + (ASSET_DIR / "cockpit.css").read_text(encoding="utf-8")
22
- )
23
-
24
- bg_path = ASSET_DIR / "bg.png"
25
- if bg_path.exists():
26
- bg_b64 = base64.b64encode(bg_path.read_bytes()).decode("utf-8")
27
- css = css.replace("url('bg.png')", f"url('data:image/png;base64,{bg_b64}')")
28
-
29
- cockpit_html = (ASSET_DIR / "cockpit.html").read_text(encoding="utf-8")
30
- intro_html = (ASSET_DIR / "intro.html").read_text(encoding="utf-8")
 
31
  intro_js = (ASSET_DIR / "intro.js").read_text(encoding="utf-8")
32
  cockpit_js = (ASSET_DIR / "cockpit.js").read_text(encoding="utf-8")
33
 
@@ -153,6 +156,20 @@ def create_app(container: AppContainer) -> gr.Blocks:
153
  outputs=[status],
154
  )
155
 
156
- if "head" not in blocks_kwargs:
157
- demo.time_machine_head = f"<script>{cockpit_js}</script><script>{intro_js}</script>"
158
- return demo.queue()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+ import base64
5
+ import inspect
6
+ import mimetypes
7
+ import re
8
+
9
+ import gradio as gr
10
 
11
  from time_machine.application.container import AppContainer
12
  from time_machine.ui.handlers import GradioHandlers
 
17
 
18
  def create_app(container: AppContainer) -> gr.Blocks:
19
  handlers = GradioHandlers(container)
20
+ css = (
21
+ (ASSET_DIR / "intro.css").read_text(encoding="utf-8")
22
+ + "\n"
23
+ + (ASSET_DIR / "cockpit.css").read_text(encoding="utf-8")
24
+ )
25
+
26
+ bg_path = ASSET_DIR / "bg.png"
27
+ if bg_path.exists():
28
+ bg_b64 = base64.b64encode(bg_path.read_bytes()).decode("utf-8")
29
+ css = css.replace("url('bg.png')", f"url('data:image/png;base64,{bg_b64}')")
30
+ css = _inline_static_urls(css)
31
+
32
+ cockpit_html = _inline_static_urls((ASSET_DIR / "cockpit.html").read_text(encoding="utf-8"))
33
+ intro_html = _inline_static_urls((ASSET_DIR / "intro.html").read_text(encoding="utf-8"))
34
  intro_js = (ASSET_DIR / "intro.js").read_text(encoding="utf-8")
35
  cockpit_js = (ASSET_DIR / "cockpit.js").read_text(encoding="utf-8")
36
 
 
156
  outputs=[status],
157
  )
158
 
159
+ if "head" not in blocks_kwargs:
160
+ demo.time_machine_head = f"<script>{cockpit_js}</script><script>{intro_js}</script>"
161
+ return demo.queue()
162
+
163
+
164
+ def _inline_static_urls(text: str) -> str:
165
+ return re.sub(r"(?P<quote>['\"])/static/(?P<path>[^'\")]+)(?P=quote)", _replace_static_url, text)
166
+
167
+
168
+ def _replace_static_url(match: re.Match[str]) -> str:
169
+ relative_path = match.group("path")
170
+ file_path = ASSET_DIR.parents[3] / "static" / relative_path
171
+ if not file_path.exists() or not file_path.is_file():
172
+ return match.group(0)
173
+ mime_type = mimetypes.guess_type(file_path.name)[0] or "application/octet-stream"
174
+ encoded = base64.b64encode(file_path.read_bytes()).decode("ascii")
175
+ return f"{match.group('quote')}data:{mime_type};base64,{encoded}{match.group('quote')}"
static/img/travel/reference_arrival.png CHANGED

Git LFS Details

  • SHA256: 1ce860b288059f5007bde165901684c26f5bf11d33ad94c5170e047328d2be1a
  • Pointer size: 132 Bytes
  • Size of remote file: 1.93 MB

Git LFS Details

  • SHA256: 2330dd3787de7427da9bfafa0927159380185457b3fd9760b2407a0e0b12944d
  • Pointer size: 132 Bytes
  • Size of remote file: 2.49 MB
static/img/travel/reference_departure.png CHANGED

Git LFS Details

  • SHA256: 79fd89f1f61f5818dd04036a0f67f874f88a6bc3ff8eb148025d8d8982d4eca1
  • Pointer size: 132 Bytes
  • Size of remote file: 2.14 MB

Git LFS Details

  • SHA256: 5335374c07e8a63d8592193de09a0fa420eef13d854dfdb091c05781971aec20
  • Pointer size: 132 Bytes
  • Size of remote file: 2.93 MB
static/img/travel/reference_warp_blend.png CHANGED

Git LFS Details

  • SHA256: b3a910f42cb0dfba4d985fa7ddebaa8b9290573acca3aab0259200a4ccdd19e5
  • Pointer size: 131 Bytes
  • Size of remote file: 981 kB

Git LFS Details

  • SHA256: 5335374c07e8a63d8592193de09a0fa420eef13d854dfdb091c05781971aec20
  • Pointer size: 132 Bytes
  • Size of remote file: 2.93 MB