Commit ·
41c845f
1
Parent(s): 1337168
fix(game): give stage real height, center dialogue, brighten scene
Browse files- app.py +2 -1
- render.py +5 -5
- styles.css +65 -38
- tests/test_render.py +6 -6
app.py
CHANGED
|
@@ -1089,7 +1089,8 @@ with gr.Blocks(title="Hollow") as demo:
|
|
| 1089 |
label="", show_label=False, height=210, buttons=[],
|
| 1090 |
autoscroll=True,
|
| 1091 |
)
|
| 1092 |
-
bond_panel = gr.HTML(value=_render_bond(20, "Hollow")
|
|
|
|
| 1093 |
with gr.Row(elem_id="game-inputrow"):
|
| 1094 |
msg_input = gr.Textbox(
|
| 1095 |
placeholder="tell it something you remember...",
|
|
|
|
| 1089 |
label="", show_label=False, height=210, buttons=[],
|
| 1090 |
autoscroll=True,
|
| 1091 |
)
|
| 1092 |
+
bond_panel = gr.HTML(value=_render_bond(20, "Hollow"),
|
| 1093 |
+
elem_classes="bond-panel")
|
| 1094 |
with gr.Row(elem_id="game-inputrow"):
|
| 1095 |
msg_input = gr.Textbox(
|
| 1096 |
placeholder="tell it something you remember...",
|
render.py
CHANGED
|
@@ -91,17 +91,17 @@ def render_entity(affinity: int, mode: str = "idle", seq: int = 0,
|
|
| 91 |
t = min(a, 45) / 45 # fully materialized by bond 45
|
| 92 |
# veil the SHARP child in shadow instead of blurring it (blur reads as
|
| 93 |
# a broken/loading image): dark and fog-shrouded at low bond, emerging
|
| 94 |
-
brightness = round(0.
|
| 95 |
-
opacity = round(0.
|
| 96 |
almost_on = a >= 76
|
| 97 |
flash_key = f"flash-{seq % 2}"
|
| 98 |
tone_class = _tone_class(tone)
|
| 99 |
|
| 100 |
-
# a
|
| 101 |
look = (
|
| 102 |
f"filter:brightness({brightness}) "
|
| 103 |
-
f"drop-shadow(0 0
|
| 104 |
-
f"drop-shadow(0 0
|
| 105 |
f"opacity:{opacity};"
|
| 106 |
)
|
| 107 |
|
|
|
|
| 91 |
t = min(a, 45) / 45 # fully materialized by bond 45
|
| 92 |
# veil the SHARP child in shadow instead of blurring it (blur reads as
|
| 93 |
# a broken/loading image): dark and fog-shrouded at low bond, emerging
|
| 94 |
+
brightness = round(0.72 + 0.28 * t, 2)
|
| 95 |
+
opacity = round(0.82 + 0.18 * t, 2)
|
| 96 |
almost_on = a >= 76
|
| 97 |
flash_key = f"flash-{seq % 2}"
|
| 98 |
tone_class = _tone_class(tone)
|
| 99 |
|
| 100 |
+
# a stronger inner/outer glow so the silhouette reads against the fog
|
| 101 |
look = (
|
| 102 |
f"filter:brightness({brightness}) "
|
| 103 |
+
f"drop-shadow(0 0 20px rgba(140,120,170,{round(0.25 + 0.25*t,2)})) "
|
| 104 |
+
f"drop-shadow(0 0 50px rgba(100,80,140,{round(0.15 + 0.20*t,2)}));"
|
| 105 |
f"opacity:{opacity};"
|
| 106 |
)
|
| 107 |
|
styles.css
CHANGED
|
@@ -1331,19 +1331,25 @@ footer { display: none !important; }
|
|
| 1331 |
overflow: hidden;
|
| 1332 |
background: #04040a;
|
| 1333 |
z-index: 50;
|
| 1334 |
-
display: flex;
|
| 1335 |
-
align-items: center;
|
| 1336 |
-
justify-content: center;
|
| 1337 |
}
|
| 1338 |
#game-view.hide, #game-view.hidden { display: none !important; }
|
| 1339 |
|
| 1340 |
-
/* the letterboxed stage —
|
| 1341 |
#game-stage {
|
| 1342 |
-
position:
|
| 1343 |
-
|
| 1344 |
-
|
| 1345 |
-
|
| 1346 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1347 |
}
|
| 1348 |
|
| 1349 |
/* Gradio wrappers inside the stage must not create their own boxes */
|
|
@@ -1367,7 +1373,7 @@ footer { display: none !important; }
|
|
| 1367 |
inset: -10%;
|
| 1368 |
background-size: cover;
|
| 1369 |
background-position: center 38%;
|
| 1370 |
-
filter: grayscale(1) blur(
|
| 1371 |
transform: scale(1.08);
|
| 1372 |
z-index: 0;
|
| 1373 |
}
|
|
@@ -1379,8 +1385,8 @@ footer { display: none !important; }
|
|
| 1379 |
pointer-events: none;
|
| 1380 |
z-index: 1;
|
| 1381 |
background:
|
| 1382 |
-
radial-gradient(ellipse at 50%
|
| 1383 |
-
linear-gradient(to bottom, rgba(6,4,12,0.
|
| 1384 |
}
|
| 1385 |
|
| 1386 |
/* a subtle inner frame edge so the scene feels contained like the intro */
|
|
@@ -1389,13 +1395,13 @@ footer { display: none !important; }
|
|
| 1389 |
inset: 0;
|
| 1390 |
pointer-events: none;
|
| 1391 |
z-index: 2;
|
| 1392 |
-
box-shadow: inset 0 0
|
| 1393 |
}
|
| 1394 |
|
| 1395 |
/* top bar: title + controls — sits just above the stage */
|
| 1396 |
#game-topbar {
|
| 1397 |
position: absolute;
|
| 1398 |
-
top: -
|
| 1399 |
left: 0;
|
| 1400 |
right: 0;
|
| 1401 |
z-index: 10;
|
|
@@ -1429,7 +1435,7 @@ footer { display: none !important; }
|
|
| 1429 |
z-index: 8;
|
| 1430 |
width: min(260px, 22vw);
|
| 1431 |
min-width: 180px;
|
| 1432 |
-
max-height:
|
| 1433 |
transition: max-height 0.45s ease, opacity 0.35s ease;
|
| 1434 |
overflow: hidden;
|
| 1435 |
opacity: 0.88;
|
|
@@ -1439,7 +1445,7 @@ footer { display: none !important; }
|
|
| 1439 |
padding: 8px 10px;
|
| 1440 |
}
|
| 1441 |
.game-drawer:hover { opacity: 1; }
|
| 1442 |
-
.game-drawer.open { max-height:
|
| 1443 |
.game-drawer:not(.open) {
|
| 1444 |
max-height: 38px;
|
| 1445 |
cursor: pointer;
|
|
@@ -1474,7 +1480,7 @@ footer { display: none !important; }
|
|
| 1474 |
z-index: 4;
|
| 1475 |
width: min(360px, 26vw);
|
| 1476 |
min-width: 220px;
|
| 1477 |
-
height: min(520px,
|
| 1478 |
pointer-events: none;
|
| 1479 |
display: flex;
|
| 1480 |
align-items: flex-end;
|
|
@@ -1513,27 +1519,30 @@ footer { display: none !important; }
|
|
| 1513 |
position: absolute;
|
| 1514 |
inset: 5% -10% 0 -10%;
|
| 1515 |
background: radial-gradient(ellipse 55% 70% at 50% 82%,
|
| 1516 |
-
rgba(170,150,200,0.
|
| 1517 |
pointer-events: none;
|
| 1518 |
-
opacity: 0.
|
| 1519 |
mix-blend-mode: screen;
|
| 1520 |
}
|
| 1521 |
|
| 1522 |
/* bottom subtitle / dialogue panel */
|
| 1523 |
#game-dialogue {
|
| 1524 |
position: absolute;
|
| 1525 |
-
left:
|
|
|
|
| 1526 |
bottom: 10px;
|
| 1527 |
-
transform: translateX(-50%);
|
| 1528 |
z-index: 9;
|
| 1529 |
-
width:
|
| 1530 |
display: flex;
|
| 1531 |
flex-direction: column;
|
|
|
|
| 1532 |
gap: 6px;
|
| 1533 |
}
|
| 1534 |
|
| 1535 |
#game-dialogue .chatbot {
|
| 1536 |
-
|
|
|
|
|
|
|
| 1537 |
background: rgba(12,9,18,0.62) !important;
|
| 1538 |
border: 10px solid transparent !important;
|
| 1539 |
border-image: var(--stone-frame) 30 !important;
|
|
@@ -1542,6 +1551,7 @@ footer { display: none !important; }
|
|
| 1542 |
backdrop-filter: blur(3px);
|
| 1543 |
-webkit-backdrop-filter: blur(3px);
|
| 1544 |
padding: 12px 16px !important;
|
|
|
|
| 1545 |
}
|
| 1546 |
|
| 1547 |
#game-dialogue .bot,
|
|
@@ -1549,7 +1559,7 @@ footer { display: none !important; }
|
|
| 1549 |
#game-dialogue .bot .message,
|
| 1550 |
#game-dialogue .bot p {
|
| 1551 |
font-style: italic !important;
|
| 1552 |
-
font-size: 1.
|
| 1553 |
color: #d8d2e6 !important;
|
| 1554 |
text-shadow: 0 0 16px rgba(120,70,160,0.22);
|
| 1555 |
}
|
|
@@ -1559,7 +1569,7 @@ footer { display: none !important; }
|
|
| 1559 |
#game-dialogue .user .message,
|
| 1560 |
#game-dialogue .user p {
|
| 1561 |
font-style: normal !important;
|
| 1562 |
-
font-size: 1.
|
| 1563 |
color: #e6e0f0 !important;
|
| 1564 |
}
|
| 1565 |
|
|
@@ -1571,6 +1581,8 @@ footer { display: none !important; }
|
|
| 1571 |
display: flex;
|
| 1572 |
gap: 8px;
|
| 1573 |
align-items: flex-end;
|
|
|
|
|
|
|
| 1574 |
}
|
| 1575 |
|
| 1576 |
#game-inputrow textarea,
|
|
@@ -1580,35 +1592,50 @@ footer { display: none !important; }
|
|
| 1580 |
padding: 8px 10px !important;
|
| 1581 |
}
|
| 1582 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1583 |
/* tone classes — the world blooms or rots */
|
| 1584 |
-
.tone-warm #game-bg { filter: grayscale(1) blur(
|
| 1585 |
.tone-warm #game-vig {
|
| 1586 |
background:
|
| 1587 |
-
radial-gradient(ellipse at 50%
|
| 1588 |
-
linear-gradient(to bottom, rgba(6,4,12,0.
|
| 1589 |
}
|
| 1590 |
-
.tone-warm .entity-img { filter: brightness(1.
|
| 1591 |
|
| 1592 |
-
.tone-neutral #game-bg { filter: grayscale(1) blur(
|
| 1593 |
|
| 1594 |
-
.tone-wounded #game-bg { filter: grayscale(1) blur(
|
| 1595 |
.tone-wounded #game-vig {
|
| 1596 |
background:
|
| 1597 |
-
radial-gradient(ellipse at 50%
|
| 1598 |
-
linear-gradient(to bottom, rgba(6,4,12,0.
|
| 1599 |
}
|
| 1600 |
-
.tone-wounded .entity-img { opacity: 0.
|
| 1601 |
|
| 1602 |
.tone-hostile #game-bg {
|
| 1603 |
-
filter: grayscale(1) blur(
|
| 1604 |
}
|
| 1605 |
.tone-hostile #game-vig {
|
| 1606 |
background:
|
| 1607 |
-
radial-gradient(ellipse at 50%
|
| 1608 |
-
linear-gradient(to bottom, rgba(40,6,12,0.
|
| 1609 |
}
|
| 1610 |
.tone-hostile .entity-img {
|
| 1611 |
-
filter: sepia(0.22) saturate(1.35) brightness(0.
|
| 1612 |
}
|
| 1613 |
|
| 1614 |
/* recall migration — the claimed memory drifts toward the child */
|
|
|
|
| 1331 |
overflow: hidden;
|
| 1332 |
background: #04040a;
|
| 1333 |
z-index: 50;
|
|
|
|
|
|
|
|
|
|
| 1334 |
}
|
| 1335 |
#game-view.hide, #game-view.hidden { display: none !important; }
|
| 1336 |
|
| 1337 |
+
/* the letterboxed stage — absolute, centered, real height so children work */
|
| 1338 |
#game-stage {
|
| 1339 |
+
position: absolute !important;
|
| 1340 |
+
left: 50% !important;
|
| 1341 |
+
top: 50% !important;
|
| 1342 |
+
transform: translate(-50%, -50%) !important;
|
| 1343 |
+
width: min(1280px, 95vw) !important;
|
| 1344 |
+
height: min(calc(95vw * 0.625), 82vh) !important;
|
| 1345 |
+
max-height: 82vh !important;
|
| 1346 |
+
overflow: visible !important;
|
| 1347 |
+
background: transparent !important;
|
| 1348 |
+
border: none !important;
|
| 1349 |
+
box-shadow: none !important;
|
| 1350 |
+
padding: 0 !important;
|
| 1351 |
+
margin: 0 !important;
|
| 1352 |
+
gap: 0 !important;
|
| 1353 |
}
|
| 1354 |
|
| 1355 |
/* Gradio wrappers inside the stage must not create their own boxes */
|
|
|
|
| 1373 |
inset: -10%;
|
| 1374 |
background-size: cover;
|
| 1375 |
background-position: center 38%;
|
| 1376 |
+
filter: grayscale(1) blur(38px) brightness(0.52);
|
| 1377 |
transform: scale(1.08);
|
| 1378 |
z-index: 0;
|
| 1379 |
}
|
|
|
|
| 1385 |
pointer-events: none;
|
| 1386 |
z-index: 1;
|
| 1387 |
background:
|
| 1388 |
+
radial-gradient(ellipse at 50% 42%, transparent 46%, rgba(4,3,8,0.62) 100%),
|
| 1389 |
+
linear-gradient(to bottom, rgba(6,4,12,0.45) 0%, transparent 13%);
|
| 1390 |
}
|
| 1391 |
|
| 1392 |
/* a subtle inner frame edge so the scene feels contained like the intro */
|
|
|
|
| 1395 |
inset: 0;
|
| 1396 |
pointer-events: none;
|
| 1397 |
z-index: 2;
|
| 1398 |
+
box-shadow: inset 0 0 70px rgba(0,0,0,0.42);
|
| 1399 |
}
|
| 1400 |
|
| 1401 |
/* top bar: title + controls — sits just above the stage */
|
| 1402 |
#game-topbar {
|
| 1403 |
position: absolute;
|
| 1404 |
+
top: -44px;
|
| 1405 |
left: 0;
|
| 1406 |
right: 0;
|
| 1407 |
z-index: 10;
|
|
|
|
| 1435 |
z-index: 8;
|
| 1436 |
width: min(260px, 22vw);
|
| 1437 |
min-width: 180px;
|
| 1438 |
+
max-height: 55vh;
|
| 1439 |
transition: max-height 0.45s ease, opacity 0.35s ease;
|
| 1440 |
overflow: hidden;
|
| 1441 |
opacity: 0.88;
|
|
|
|
| 1445 |
padding: 8px 10px;
|
| 1446 |
}
|
| 1447 |
.game-drawer:hover { opacity: 1; }
|
| 1448 |
+
.game-drawer.open { max-height: 55vh; }
|
| 1449 |
.game-drawer:not(.open) {
|
| 1450 |
max-height: 38px;
|
| 1451 |
cursor: pointer;
|
|
|
|
| 1480 |
z-index: 4;
|
| 1481 |
width: min(360px, 26vw);
|
| 1482 |
min-width: 220px;
|
| 1483 |
+
height: min(520px, 55vh);
|
| 1484 |
pointer-events: none;
|
| 1485 |
display: flex;
|
| 1486 |
align-items: flex-end;
|
|
|
|
| 1519 |
position: absolute;
|
| 1520 |
inset: 5% -10% 0 -10%;
|
| 1521 |
background: radial-gradient(ellipse 55% 70% at 50% 82%,
|
| 1522 |
+
rgba(170,150,200,0.35) 0%, transparent 72%);
|
| 1523 |
pointer-events: none;
|
| 1524 |
+
opacity: 0.95;
|
| 1525 |
mix-blend-mode: screen;
|
| 1526 |
}
|
| 1527 |
|
| 1528 |
/* bottom subtitle / dialogue panel */
|
| 1529 |
#game-dialogue {
|
| 1530 |
position: absolute;
|
| 1531 |
+
left: 0;
|
| 1532 |
+
right: 0;
|
| 1533 |
bottom: 10px;
|
|
|
|
| 1534 |
z-index: 9;
|
| 1535 |
+
width: 100%;
|
| 1536 |
display: flex;
|
| 1537 |
flex-direction: column;
|
| 1538 |
+
align-items: center;
|
| 1539 |
gap: 6px;
|
| 1540 |
}
|
| 1541 |
|
| 1542 |
#game-dialogue .chatbot {
|
| 1543 |
+
width: min(780px, 74vw) !important;
|
| 1544 |
+
height: 200px !important;
|
| 1545 |
+
margin: 0 auto !important;
|
| 1546 |
background: rgba(12,9,18,0.62) !important;
|
| 1547 |
border: 10px solid transparent !important;
|
| 1548 |
border-image: var(--stone-frame) 30 !important;
|
|
|
|
| 1551 |
backdrop-filter: blur(3px);
|
| 1552 |
-webkit-backdrop-filter: blur(3px);
|
| 1553 |
padding: 12px 16px !important;
|
| 1554 |
+
box-sizing: border-box !important;
|
| 1555 |
}
|
| 1556 |
|
| 1557 |
#game-dialogue .bot,
|
|
|
|
| 1559 |
#game-dialogue .bot .message,
|
| 1560 |
#game-dialogue .bot p {
|
| 1561 |
font-style: italic !important;
|
| 1562 |
+
font-size: 1.12rem !important;
|
| 1563 |
color: #d8d2e6 !important;
|
| 1564 |
text-shadow: 0 0 16px rgba(120,70,160,0.22);
|
| 1565 |
}
|
|
|
|
| 1569 |
#game-dialogue .user .message,
|
| 1570 |
#game-dialogue .user p {
|
| 1571 |
font-style: normal !important;
|
| 1572 |
+
font-size: 1.0rem !important;
|
| 1573 |
color: #e6e0f0 !important;
|
| 1574 |
}
|
| 1575 |
|
|
|
|
| 1581 |
display: flex;
|
| 1582 |
gap: 8px;
|
| 1583 |
align-items: flex-end;
|
| 1584 |
+
width: min(780px, 74vw);
|
| 1585 |
+
margin: 0 auto;
|
| 1586 |
}
|
| 1587 |
|
| 1588 |
#game-inputrow textarea,
|
|
|
|
| 1592 |
padding: 8px 10px !important;
|
| 1593 |
}
|
| 1594 |
|
| 1595 |
+
#game-dialogue .bond-panel {
|
| 1596 |
+
width: min(780px, 74vw) !important;
|
| 1597 |
+
margin: 0 auto !important;
|
| 1598 |
+
}
|
| 1599 |
+
|
| 1600 |
+
#game-dialogue .voice-channel,
|
| 1601 |
+
#game-dialogue .voice-channel * {
|
| 1602 |
+
height: 0 !important;
|
| 1603 |
+
min-height: 0 !important;
|
| 1604 |
+
padding: 0 !important;
|
| 1605 |
+
margin: 0 !important;
|
| 1606 |
+
border: none !important;
|
| 1607 |
+
overflow: hidden !important;
|
| 1608 |
+
}
|
| 1609 |
+
|
| 1610 |
/* tone classes — the world blooms or rots */
|
| 1611 |
+
.tone-warm #game-bg { filter: grayscale(1) blur(38px) brightness(0.62); }
|
| 1612 |
.tone-warm #game-vig {
|
| 1613 |
background:
|
| 1614 |
+
radial-gradient(ellipse at 50% 42%, transparent 52%, rgba(4,3,8,0.45) 100%),
|
| 1615 |
+
linear-gradient(to bottom, rgba(6,4,12,0.28) 0%, transparent 13%);
|
| 1616 |
}
|
| 1617 |
+
.tone-warm .entity-img { filter: brightness(1.12) drop-shadow(0 0 18px rgba(190,170,220,0.30)) !important; }
|
| 1618 |
|
| 1619 |
+
.tone-neutral #game-bg { filter: grayscale(1) blur(38px) brightness(0.52); }
|
| 1620 |
|
| 1621 |
+
.tone-wounded #game-bg { filter: grayscale(1) blur(38px) brightness(0.38); }
|
| 1622 |
.tone-wounded #game-vig {
|
| 1623 |
background:
|
| 1624 |
+
radial-gradient(ellipse at 50% 42%, transparent 38%, rgba(4,3,8,0.78) 100%),
|
| 1625 |
+
linear-gradient(to bottom, rgba(6,4,12,0.65) 0%, transparent 13%);
|
| 1626 |
}
|
| 1627 |
+
.tone-wounded .entity-img { opacity: 0.85 !important; }
|
| 1628 |
|
| 1629 |
.tone-hostile #game-bg {
|
| 1630 |
+
filter: grayscale(1) blur(38px) brightness(0.30);
|
| 1631 |
}
|
| 1632 |
.tone-hostile #game-vig {
|
| 1633 |
background:
|
| 1634 |
+
radial-gradient(ellipse at 50% 42%, transparent 32%, rgba(30,4,8,0.80) 100%),
|
| 1635 |
+
linear-gradient(to bottom, rgba(40,6,12,0.52) 0%, transparent 20%);
|
| 1636 |
}
|
| 1637 |
.tone-hostile .entity-img {
|
| 1638 |
+
filter: sepia(0.22) saturate(1.35) brightness(0.95) drop-shadow(0 0 18px rgba(120,40,40,0.35)) !important;
|
| 1639 |
}
|
| 1640 |
|
| 1641 |
/* recall migration — the claimed memory drifts toward the child */
|
tests/test_render.py
CHANGED
|
@@ -69,8 +69,8 @@ class TestRenderEntity:
|
|
| 69 |
|
| 70 |
def test_low_affinity_is_dark_and_faint(self):
|
| 71 |
out = render_entity(0)
|
| 72 |
-
assert "brightness(0.
|
| 73 |
-
assert "opacity:0.
|
| 74 |
assert "scale(" not in out # size never changes — frame always fits
|
| 75 |
|
| 76 |
def test_full_affinity_is_bright_full(self):
|
|
@@ -137,10 +137,10 @@ class TestRenderEntityUX:
|
|
| 137 |
def test_recognizable_silhouette_at_start(self):
|
| 138 |
# bond 20 (session start) must already read as a child, not a smudge
|
| 139 |
out = render_entity(20)
|
| 140 |
-
# t = 20/45 ≈ 0.444; brightness = 0.
|
| 141 |
-
assert "brightness(0.
|
| 142 |
-
# opacity = 0.
|
| 143 |
-
assert "opacity:0.
|
| 144 |
|
| 145 |
def test_scene_tone_class_present(self):
|
| 146 |
assert 'class="entity-scene tone-warm"' in render_entity(50, tone=20)
|
|
|
|
| 69 |
|
| 70 |
def test_low_affinity_is_dark_and_faint(self):
|
| 71 |
out = render_entity(0)
|
| 72 |
+
assert "brightness(0.72)" in out
|
| 73 |
+
assert "opacity:0.82" in out
|
| 74 |
assert "scale(" not in out # size never changes — frame always fits
|
| 75 |
|
| 76 |
def test_full_affinity_is_bright_full(self):
|
|
|
|
| 137 |
def test_recognizable_silhouette_at_start(self):
|
| 138 |
# bond 20 (session start) must already read as a child, not a smudge
|
| 139 |
out = render_entity(20)
|
| 140 |
+
# t = 20/45 ≈ 0.444; brightness = 0.72 + 0.28*0.444 ≈ 0.84
|
| 141 |
+
assert "brightness(0.84)" in out
|
| 142 |
+
# opacity = 0.82 + 0.18*0.444 ≈ 0.90
|
| 143 |
+
assert "opacity:0.9" in out
|
| 144 |
|
| 145 |
def test_scene_tone_class_present(self):
|
| 146 |
assert 'class="entity-scene tone-warm"' in render_entity(50, tone=20)
|