rename natus → sky across the codebase — natal chart abstraction is now sky throughout, since chart inputs aren't birthday-gated
Mechanical rename: 5 files (sky-wheel.js, _sky.scss, _sky_overlay.html, SkyWheelSpec.js x2), 24 in-place edits across templates/views/urls/SCSS/JS/tests/CLAUDE.md. URL names epic:natus_save → epic:sky_save (epic namespaced, no clash w. dashboard:sky_save), JS module NatusWheel → SkyWheel, DOM ids id_natus_* → id_sky_*, BEM classes natus-* → sky-*, dashboard sky_natus_data/sky_natus_preview collapsed to sky_data/sky_preview_data. No DB migration needed (User.sky_chart_data + GameEvent.SKY_SAVED already used sky-prefix). 778 ITs + Jasmine green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -110,7 +110,7 @@ Drop → RESERVED → confirm/reject. `_gate_context()` builds slot state; `_exp
|
||||
`epic:room` view at `/gameboard/room/<uuid>/`. `gatekeeper` redirects there when `table_status` is set. Error redirects in `select_role`/`select_sig` use `epic:room` if `table_status` is set, else `epic:gatekeeper`.
|
||||
|
||||
## SCSS Import Order
|
||||
`core.scss`: `rootvars → applets → base → button-pad → dashboard → gameboard → palette-picker → room → card-deck → natus → tray → billboard → tooltips → game-kit → wallet-tokens`
|
||||
`core.scss`: `rootvars → applets → base → button-pad → dashboard → gameboard → palette-picker → room → card-deck → sky → tray → billboard → tooltips → game-kit → wallet-tokens`
|
||||
|
||||
## Critical Gotchas
|
||||
|
||||
|
||||
@@ -619,13 +619,13 @@ class SkySaveViewTest(TestCase):
|
||||
self.assertAlmostEqual(self.user.sky_chart_data["houses"]["asc"], 123.4)
|
||||
|
||||
|
||||
class SkyNatusDataViewTest(TestCase):
|
||||
class SkyDataViewTest(TestCase):
|
||||
def setUp(self):
|
||||
self.user = User.objects.create(email="disco@test.io")
|
||||
self.client.force_login(self.user)
|
||||
|
||||
def test_returns_stored_chart_with_asc_preserved(self):
|
||||
"""sky_natus_data returns sky_chart_data — asc must match what was saved."""
|
||||
"""sky_data returns sky_chart_data — asc must match what was saved."""
|
||||
stored = {
|
||||
"planets": {},
|
||||
"houses": {"cusps": [float(i * 30) for i in range(12)], "asc": 236.1, "mc": 159.1},
|
||||
|
||||
@@ -17,6 +17,6 @@ urlpatterns = [
|
||||
path('sky/', views.sky_view, name='sky'),
|
||||
path('sky/preview', views.sky_preview, name='sky_preview'),
|
||||
path('sky/save', views.sky_save, name='sky_save'),
|
||||
path('sky/data', views.sky_natus_data, name='sky_natus_data'),
|
||||
path('sky/data', views.sky_data, name='sky_data'),
|
||||
path('set-pronouns', views.set_pronouns, name='set_pronouns'),
|
||||
]
|
||||
|
||||
@@ -287,7 +287,7 @@ def save_payment_method(request):
|
||||
|
||||
# ── My Sky (personal natal chart) ────────────────────────────────────────────
|
||||
|
||||
def _sky_natus_preview(request):
|
||||
def _sky_preview_data(request):
|
||||
"""Shared preview logic — proxies to PySwiss, no DB writes."""
|
||||
date_str = request.GET.get('date')
|
||||
time_str = request.GET.get('time', '12:00')
|
||||
@@ -380,7 +380,7 @@ def sky_view(request):
|
||||
|
||||
@login_required(login_url="/")
|
||||
def sky_preview(request):
|
||||
return _sky_natus_preview(request)
|
||||
return _sky_preview_data(request)
|
||||
|
||||
|
||||
@login_required(login_url="/")
|
||||
@@ -440,7 +440,7 @@ def sky_save(request):
|
||||
|
||||
|
||||
@login_required(login_url="/")
|
||||
def sky_natus_data(request):
|
||||
def sky_data(request):
|
||||
user = request.user
|
||||
if not user.sky_chart_data:
|
||||
return HttpResponse(status=404)
|
||||
|
||||
@@ -653,7 +653,7 @@ class Character(models.Model):
|
||||
on_delete=models.SET_NULL, related_name='character_significators',
|
||||
)
|
||||
|
||||
# ── natus input (what the gamer entered) ─────────────────────────────
|
||||
# ── sky input (what the gamer entered) ─────────────────────────────
|
||||
birth_dt = models.DateTimeField(null=True, blank=True) # UTC
|
||||
birth_lat = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True)
|
||||
birth_lon = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True)
|
||||
@@ -662,7 +662,7 @@ class Character(models.Model):
|
||||
max_length=1, choices=HOUSE_SYSTEM_CHOICES, default=PORPHYRY,
|
||||
)
|
||||
|
||||
# ── computed natus snapshot (full PySwiss response) ───────────────────
|
||||
# ── computed sky snapshot (full PySwiss response) ───────────────────
|
||||
chart_data = models.JSONField(null=True, blank=True)
|
||||
|
||||
# ── celtic cross spread (added at PICK SEA) ───────────────────────────
|
||||
|
||||
@@ -1933,16 +1933,16 @@ class SelectSigViewTest(TestCase):
|
||||
self.assertEqual(response.status_code, 400)
|
||||
|
||||
|
||||
# ── natus_preview (epic) ──────────────────────────────────────────────────────
|
||||
# ── sky_preview (epic) ──────────────────────────────────────────────────────
|
||||
|
||||
class NatusPreviewViewTest(TestCase):
|
||||
class SkyPreviewViewTest(TestCase):
|
||||
def setUp(self):
|
||||
self.user = User.objects.create(email="pc@natus.io")
|
||||
self.user = User.objects.create(email="pc@sky.io")
|
||||
self.client.force_login(self.user)
|
||||
self.room, _ = _make_sig_room(self.user)
|
||||
self.room.table_status = Room.SKY_SELECT
|
||||
self.room.save()
|
||||
self.url = reverse("epic:natus_preview", kwargs={"room_id": self.room.id})
|
||||
self.url = reverse("epic:sky_preview", kwargs={"room_id": self.room.id})
|
||||
|
||||
def test_missing_params_returns_400(self):
|
||||
response = self.client.get(self.url, {"date": "1990-06-15"})
|
||||
@@ -2025,16 +2025,16 @@ class TarotDealViewTest(TestCase):
|
||||
)
|
||||
|
||||
|
||||
# ── natus_save (epic) ─────────────────────────────────────────────────────────
|
||||
# ── sky_save (epic) ─────────────────────────────────────────────────────────
|
||||
|
||||
class NatusSaveViewTest(TestCase):
|
||||
class SkySaveViewTest(TestCase):
|
||||
def setUp(self):
|
||||
self.user = User.objects.create(email="pc@natussave.io")
|
||||
self.user = User.objects.create(email="pc@skysave.io")
|
||||
self.client.force_login(self.user)
|
||||
self.room, _ = _make_sig_room(self.user)
|
||||
self.room.table_status = Room.SKY_SELECT
|
||||
self.room.save()
|
||||
self.url = reverse("epic:natus_save", kwargs={"room_id": self.room.id})
|
||||
self.url = reverse("epic:sky_save", kwargs={"room_id": self.room.id})
|
||||
|
||||
def _post(self, payload):
|
||||
import json as _json
|
||||
@@ -2134,7 +2134,7 @@ class NatusSaveViewTest(TestCase):
|
||||
|
||||
def test_confirm_with_dict_shaped_elements_extracts_count(self):
|
||||
"""Some chart payloads enrich each element to {count, contributors};
|
||||
natus_save should read .count rather than treating the dict as a value."""
|
||||
sky_save should read .count rather than treating the dict as a value."""
|
||||
from apps.drama.models import GameEvent
|
||||
chart = {
|
||||
"elements": {
|
||||
@@ -2156,7 +2156,7 @@ class NatusSaveViewTest(TestCase):
|
||||
self.assertEqual(event.data.get("top_capacitors"), ["Ardor"])
|
||||
|
||||
def test_confirm_copies_seat_significator_to_character(self):
|
||||
"""natus_save with action=confirm copies seat.significator onto Character."""
|
||||
"""sky_save with action=confirm copies seat.significator onto Character."""
|
||||
earthman, _ = DeckVariant.objects.get_or_create(
|
||||
slug="earthman", defaults={"name": "Earthman Deck", "card_count": 108}
|
||||
)
|
||||
|
||||
@@ -25,8 +25,8 @@ urlpatterns = [
|
||||
path('room/<uuid:room_id>/abandon', views.abandon_room, name='abandon_room'),
|
||||
path('room/<uuid:room_id>/tarot/', views.tarot_deck, name='tarot_deck'),
|
||||
path('room/<uuid:room_id>/tarot/deal', views.tarot_deal, name='tarot_deal'),
|
||||
path('room/<uuid:room_id>/natus/preview', views.natus_preview, name='natus_preview'),
|
||||
path('room/<uuid:room_id>/natus/save', views.natus_save, name='natus_save'),
|
||||
path('room/<uuid:room_id>/sky/preview', views.sky_preview, name='sky_preview'),
|
||||
path('room/<uuid:room_id>/sky/save', views.sky_save, name='sky_save'),
|
||||
path('room/<uuid:room_id>/sea/partial', views.sea_partial, name='sea_partial'),
|
||||
path('room/<uuid:room_id>/sea/deck', views.sea_deck, name='sea_deck'),
|
||||
]
|
||||
|
||||
@@ -25,7 +25,7 @@ def stack_reversal_probability(user=None, room=None):
|
||||
|
||||
|
||||
|
||||
# Element key → in-game capacitor name (mirrors ELEMENT_INFO in natus-wheel.js).
|
||||
# Element key → in-game capacitor name (mirrors ELEMENT_INFO in sky-wheel.js).
|
||||
# Used by the SKY_SAVED provenance event to render prose like
|
||||
# "yields them a unique Ardor capacity."
|
||||
ELEMENT_CAPACITOR_NAMES = {
|
||||
|
||||
@@ -975,10 +975,10 @@ def tarot_deal(request, room_id):
|
||||
})
|
||||
|
||||
|
||||
# ── Natus (natal chart) ───────────────────────────────────────────────────────
|
||||
# ── Sky (natal chart) ───────────────────────────────────────────────────────
|
||||
|
||||
@login_required
|
||||
def natus_preview(request, room_id):
|
||||
def sky_preview(request, room_id):
|
||||
"""Proxy GET to PySwiss /api/chart/ and augment with distinction counts.
|
||||
|
||||
Query params:
|
||||
@@ -1064,7 +1064,7 @@ def natus_preview(request, room_id):
|
||||
|
||||
|
||||
@login_required
|
||||
def natus_save(request, room_id):
|
||||
def sky_save(request, room_id):
|
||||
"""Create or update the draft Character for the requesting gamer's seat.
|
||||
|
||||
POST body (JSON):
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/**
|
||||
* natus-wheel.js — Self-contained D3 natal-chart module.
|
||||
* sky-wheel.js — Self-contained D3 natal-chart module.
|
||||
*
|
||||
* Public API:
|
||||
* NatusWheel.draw(svgEl, data) — first render
|
||||
* NatusWheel.redraw(data) — live update (same SVG)
|
||||
* NatusWheel.clear() — empty the SVG
|
||||
* SkyWheel.draw(svgEl, data) — first render
|
||||
* SkyWheel.redraw(data) — live update (same SVG)
|
||||
* SkyWheel.clear() — empty the SVG
|
||||
*
|
||||
* `data` shape — matches the /epic/natus/preview/ proxy response:
|
||||
* `data` shape — matches the /epic/sky/preview/ proxy response:
|
||||
* {
|
||||
* planets: { Sun: { sign, degree, speed, retrograde }, … },
|
||||
* houses: { cusps: [f×12], asc: f, mc: f },
|
||||
@@ -28,7 +28,7 @@
|
||||
* already defined in the page; falls back to neutral colours if absent.
|
||||
*/
|
||||
|
||||
const NatusWheel = (() => {
|
||||
const SkyWheel = (() => {
|
||||
'use strict';
|
||||
|
||||
// ── Constants ──────────────────────────────────────────────────────────────
|
||||
@@ -223,8 +223,8 @@ const NatusWheel = (() => {
|
||||
if (_staticBase) return _staticBase;
|
||||
const scripts = document.querySelectorAll('script[src]');
|
||||
for (const s of scripts) {
|
||||
if (s.src.includes('natus-wheel')) {
|
||||
_staticBase = s.src.replace(/natus-wheel\.js.*$/, '');
|
||||
if (s.src.includes('sky-wheel')) {
|
||||
_staticBase = s.src.replace(/sky-wheel\.js.*$/, '');
|
||||
return _staticBase;
|
||||
}
|
||||
}
|
||||
@@ -957,7 +957,7 @@ const NatusWheel = (() => {
|
||||
* Called on every draw() so a fresh innerHTML replaces any stale state.
|
||||
*/
|
||||
function _injectTooltipControls() {
|
||||
_tooltipEl = document.getElementById('id_natus_tooltip');
|
||||
_tooltipEl = document.getElementById('id_sky_tooltip');
|
||||
if (!_tooltipEl) return;
|
||||
_tooltipEl.innerHTML =
|
||||
`<button type="button" class="btn btn-equip nw-asp-don">DON</button>` +
|
||||
@@ -1384,8 +1384,8 @@ const NatusWheel = (() => {
|
||||
(() => {
|
||||
const scripts = document.querySelectorAll('script[src]');
|
||||
for (const s of scripts) {
|
||||
if (s.src.includes('natus-wheel')) {
|
||||
return s.src.replace(/natus-wheel\.js.*$/, 'icons/zodiac-signs/');
|
||||
if (s.src.includes('sky-wheel')) {
|
||||
return s.src.replace(/sky-wheel\.js.*$/, 'icons/zodiac-signs/');
|
||||
}
|
||||
}
|
||||
return '/static/apps/gameboard/icons/zodiac-signs/';
|
||||
@@ -1394,7 +1394,7 @@ const NatusWheel = (() => {
|
||||
await Promise.all(SIGNS.map(async sign => {
|
||||
const url = base + sign.name.toLowerCase() + '.svg';
|
||||
const resp = await window.fetch(url);
|
||||
if (!resp.ok) { console.warn(`NatusWheel: failed to load ${url}`); return; }
|
||||
if (!resp.ok) { console.warn(`SkyWheel: failed to load ${url}`); return; }
|
||||
const text = await resp.text();
|
||||
const doc = new DOMParser().parseFromString(text, 'image/svg+xml');
|
||||
const path = doc.querySelector('path');
|
||||
@@ -119,7 +119,7 @@ class StargazerNoteFromDashboardTest(FunctionalTest):
|
||||
self.browser.get(self.live_server_url)
|
||||
|
||||
confirm_btn = self.wait_for(
|
||||
lambda: self.browser.find_element(By.ID, "id_natus_confirm")
|
||||
lambda: self.browser.find_element(By.ID, "id_sky_confirm")
|
||||
)
|
||||
self.assertIsNotNone(confirm_btn.get_attribute("disabled"))
|
||||
self.assertFalse(self.browser.find_elements(By.CSS_SELECTOR, ".note-banner"))
|
||||
@@ -136,7 +136,7 @@ class StargazerNoteFromDashboardTest(FunctionalTest):
|
||||
self.browser.execute_script(_mock_preview_js(_CHART_FIXTURE))
|
||||
_fill_valid_sky_form(self.browser)
|
||||
|
||||
confirm_btn = self.browser.find_element(By.ID, "id_natus_confirm")
|
||||
confirm_btn = self.browser.find_element(By.ID, "id_sky_confirm")
|
||||
self.wait_for(lambda: self.assertIsNone(confirm_btn.get_attribute("disabled")))
|
||||
confirm_btn.click()
|
||||
|
||||
@@ -338,7 +338,7 @@ class StargazerNoteFromSkyPageTest(FunctionalTest):
|
||||
self.browser.get(self.sky_url)
|
||||
|
||||
confirm_btn = self.wait_for(
|
||||
lambda: self.browser.find_element(By.ID, "id_natus_confirm")
|
||||
lambda: self.browser.find_element(By.ID, "id_sky_confirm")
|
||||
)
|
||||
self.assertIsNotNone(confirm_btn.get_attribute("disabled"))
|
||||
self.assertFalse(self.browser.find_elements(By.CSS_SELECTOR, ".note-banner"))
|
||||
@@ -351,11 +351,11 @@ class StargazerNoteFromSkyPageTest(FunctionalTest):
|
||||
self.create_pre_authenticated_session("stargazer@test.io")
|
||||
self.browser.get(self.sky_url)
|
||||
|
||||
self.wait_for(lambda: self.browser.find_element(By.ID, "id_natus_confirm"))
|
||||
self.wait_for(lambda: self.browser.find_element(By.ID, "id_sky_confirm"))
|
||||
self.browser.execute_script(_mock_preview_js(_CHART_FIXTURE))
|
||||
_fill_valid_sky_form(self.browser)
|
||||
|
||||
confirm_btn = self.browser.find_element(By.ID, "id_natus_confirm")
|
||||
confirm_btn = self.browser.find_element(By.ID, "id_sky_confirm")
|
||||
self.wait_for(lambda: self.assertIsNone(confirm_btn.get_attribute("disabled")))
|
||||
confirm_btn.click()
|
||||
|
||||
@@ -386,11 +386,11 @@ class StargazerNoteFromSkyPageTest(FunctionalTest):
|
||||
self.create_pre_authenticated_session("stargazer@test.io")
|
||||
self.browser.get(self.sky_url)
|
||||
|
||||
self.wait_for(lambda: self.browser.find_element(By.ID, "id_natus_confirm"))
|
||||
self.wait_for(lambda: self.browser.find_element(By.ID, "id_sky_confirm"))
|
||||
self.browser.execute_script(_mock_preview_js(_CHART_FIXTURE))
|
||||
_fill_valid_sky_form(self.browser)
|
||||
|
||||
confirm_btn = self.browser.find_element(By.ID, "id_natus_confirm")
|
||||
confirm_btn = self.browser.find_element(By.ID, "id_sky_confirm")
|
||||
self.wait_for(lambda: self.assertIsNone(confirm_btn.get_attribute("disabled")))
|
||||
confirm_btn.click()
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Functional tests for the My Sky dashboard feature.
|
||||
|
||||
My Sky is a dashboard applet linking to /dashboard/sky/ — a full-page
|
||||
natus (natal chart) interface where the user can save their personal sky
|
||||
sky (natal chart) interface where the user can save their personal sky
|
||||
to their account (stored on the User model, independent of any game room).
|
||||
"""
|
||||
|
||||
@@ -61,7 +61,7 @@ class MySkyAppletTest(FunctionalTest):
|
||||
|
||||
def test_my_sky_applet_links_to_sky_page_with_form(self):
|
||||
"""Applet is visible on dashboard; link leads to /dashboard/sky/ with
|
||||
all natus form fields present."""
|
||||
all sky form fields present."""
|
||||
self.create_pre_authenticated_session("stargazer@test.io")
|
||||
self.browser.get(self.live_server_url)
|
||||
|
||||
@@ -80,14 +80,14 @@ class MySkyAppletTest(FunctionalTest):
|
||||
lambda: self.assertRegex(self.browser.current_url, r"/dashboard/sky/$")
|
||||
)
|
||||
|
||||
# 4. All natus form fields are present
|
||||
# 4. All sky form fields are present
|
||||
self.browser.find_element(By.ID, "id_nf_date")
|
||||
self.browser.find_element(By.ID, "id_nf_time")
|
||||
self.browser.find_element(By.ID, "id_nf_place")
|
||||
self.browser.find_element(By.ID, "id_nf_lat")
|
||||
self.browser.find_element(By.ID, "id_nf_lon")
|
||||
self.browser.find_element(By.ID, "id_nf_tz")
|
||||
self.browser.find_element(By.ID, "id_natus_confirm")
|
||||
self.browser.find_element(By.ID, "id_sky_confirm")
|
||||
|
||||
|
||||
class MySkyLocalStorageTest(FunctionalTest):
|
||||
@@ -171,7 +171,7 @@ class MySkyAppletWheelTest(FunctionalTest):
|
||||
def test_saved_sky_wheel_renders_with_element_tooltip_in_applet(self):
|
||||
"""When the user has saved sky data, the natal wheel appears in the My Sky
|
||||
applet and clicking an element-ring slice shows the tooltip.
|
||||
(Planet click tooltip is covered by NatusWheelSpec.js T3/T4/T5.)"""
|
||||
(Planet click tooltip is covered by SkyWheelSpec.js T3/T4/T5.)"""
|
||||
self.create_pre_authenticated_session("stargazer@test.io")
|
||||
self.browser.get(self.live_server_url)
|
||||
|
||||
@@ -192,14 +192,14 @@ class MySkyAppletWheelTest(FunctionalTest):
|
||||
slice_el,
|
||||
)
|
||||
self.wait_for(lambda: self.assertEqual(
|
||||
self.browser.find_element(By.ID, "id_natus_tooltip")
|
||||
self.browser.find_element(By.ID, "id_sky_tooltip")
|
||||
.value_of_css_property("display"),
|
||||
"block",
|
||||
))
|
||||
|
||||
|
||||
class MySkyAppletFormTest(FunctionalTest):
|
||||
"""My Sky applet shows natus entry form when no sky data is saved."""
|
||||
"""My Sky applet shows sky entry form when no sky data is saved."""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
@@ -212,7 +212,7 @@ class MySkyAppletFormTest(FunctionalTest):
|
||||
# ── T4 ───────────────────────────────────────────────────────────────────
|
||||
|
||||
def test_applet_shows_entry_form_when_no_sky_saved(self):
|
||||
"""When no sky data is saved the My Sky applet shows all natus form
|
||||
"""When no sky data is saved the My Sky applet shows all sky form
|
||||
fields and a disabled SAVE SKY button; no wheel is drawn yet."""
|
||||
self.create_pre_authenticated_session("stargazer@test.io")
|
||||
self.browser.get(self.live_server_url)
|
||||
@@ -227,7 +227,7 @@ class MySkyAppletFormTest(FunctionalTest):
|
||||
applet.find_element(By.ID, "id_nf_lat")
|
||||
applet.find_element(By.ID, "id_nf_lon")
|
||||
applet.find_element(By.ID, "id_nf_tz")
|
||||
applet.find_element(By.ID, "id_natus_confirm")
|
||||
applet.find_element(By.ID, "id_sky_confirm")
|
||||
|
||||
self.assertFalse(applet.find_elements(By.CSS_SELECTOR, ".nw-root"))
|
||||
|
||||
@@ -276,7 +276,7 @@ class MySkyAppletFormTest(FunctionalTest):
|
||||
""")
|
||||
|
||||
# Wait for confirm button to be enabled (preview resolved)
|
||||
confirm_btn = self.browser.find_element(By.ID, "id_natus_confirm")
|
||||
confirm_btn = self.browser.find_element(By.ID, "id_sky_confirm")
|
||||
self.wait_for(lambda: self.assertIsNone(
|
||||
confirm_btn.get_attribute("disabled")
|
||||
))
|
||||
@@ -331,4 +331,4 @@ class MySkyWheelConjunctionTest(FunctionalTest):
|
||||
))
|
||||
|
||||
# (T7 tick-extends-past-zodiac, T8 click-raises-to-front, and T9c/T9n/T9w
|
||||
# cycle navigation are covered by NatusWheelSpec.js.)
|
||||
# cycle navigation are covered by SkyWheelSpec.js.)
|
||||
|
||||
@@ -38,7 +38,7 @@ def _make_sky_confirmed_room(live_server_url, user, earthman):
|
||||
|
||||
@tag("channels")
|
||||
class PickSeaAsyncTransitionTest(ChannelsFunctionalTest):
|
||||
"""After sky confirm, the natus overlay closes and the room reloads to the
|
||||
"""After sky confirm, the sky overlay closes and the room reloads to the
|
||||
table hex w. the PICK SEA btn visible — the gamer must opt into the sea
|
||||
overlay rather than be auto-launched into it."""
|
||||
|
||||
@@ -66,19 +66,19 @@ class PickSeaAsyncTransitionTest(ChannelsFunctionalTest):
|
||||
self.room_url = self.live_server_url + reverse(
|
||||
"epic:room", kwargs={"room_id": self.room.id}
|
||||
)
|
||||
self.natus_save_url = self.live_server_url + reverse(
|
||||
"epic:natus_save", kwargs={"room_id": self.room.id}
|
||||
self.sky_save_url = self.live_server_url + reverse(
|
||||
"epic:sky_save", kwargs={"room_id": self.room.id}
|
||||
)
|
||||
|
||||
def _confirm_sky(self):
|
||||
"""POST to natus_save with action=confirm from browser JS (bypasses chart form)."""
|
||||
"""POST to sky_save with action=confirm from browser JS (bypasses chart form)."""
|
||||
# Wait for the room WS connection to be ready before triggering confirm
|
||||
self.wait_for(lambda: self.browser.execute_script(
|
||||
"return !!(window._roomSocket && window._roomSocket.readyState === 1);"
|
||||
))
|
||||
self.browser.execute_script(f"""
|
||||
const csrf = (document.cookie.match(/csrftoken=([^;]+)/) || ['',''])[1];
|
||||
fetch('{self.natus_save_url}', {{
|
||||
fetch('{self.sky_save_url}', {{
|
||||
method: 'POST',
|
||||
credentials: 'same-origin',
|
||||
headers: {{'Content-Type': 'application/json', 'X-CSRFToken': csrf}},
|
||||
@@ -113,8 +113,8 @@ class PickSeaAsyncTransitionTest(ChannelsFunctionalTest):
|
||||
)
|
||||
self.assertFalse(has_sea_open)
|
||||
|
||||
def test_natus_overlay_closed_after_sky_confirm(self):
|
||||
"""Natus overlay is gone (page reloaded) after sky confirm."""
|
||||
def test_sky_overlay_closed_after_sky_confirm(self):
|
||||
"""Sky overlay is gone (page reloaded) after sky confirm."""
|
||||
self.create_pre_authenticated_session("founder@test.io")
|
||||
self.browser.get(self.room_url)
|
||||
self.wait_for(lambda: self.browser.find_element(By.ID, "id_pick_sky_btn"))
|
||||
@@ -122,8 +122,8 @@ class PickSeaAsyncTransitionTest(ChannelsFunctionalTest):
|
||||
self._confirm_sky()
|
||||
|
||||
self.wait_for(lambda: self.browser.find_element(By.ID, "id_pick_sea_btn"))
|
||||
natus = self.browser.find_elements(By.ID, "id_natus_overlay")
|
||||
self.assertTrue(not natus or not natus[0].is_displayed())
|
||||
sky = self.browser.find_elements(By.ID, "id_sky_overlay")
|
||||
self.assertTrue(not sky or not sky[0].is_displayed())
|
||||
|
||||
def test_clicking_pick_sea_btn_opens_sea_overlay(self):
|
||||
"""The gamer's explicit click on PICK SEA is what opens the sea overlay."""
|
||||
|
||||
@@ -46,7 +46,7 @@ class PickSkyLocalStorageTest(FunctionalTest):
|
||||
)
|
||||
self.browser.execute_script("arguments[0].click()", btn)
|
||||
self.wait_for(
|
||||
lambda: self.browser.find_element(By.ID, "id_natus_overlay")
|
||||
lambda: self.browser.find_element(By.ID, "id_sky_overlay")
|
||||
)
|
||||
|
||||
def _fill_form(self):
|
||||
@@ -89,7 +89,7 @@ class PickSkyLocalStorageTest(FunctionalTest):
|
||||
self._fill_form()
|
||||
|
||||
# Close via NVM
|
||||
self.browser.find_element(By.ID, "id_natus_cancel").click()
|
||||
self.browser.find_element(By.ID, "id_sky_cancel").click()
|
||||
|
||||
# Reopen
|
||||
self._open_overlay()
|
||||
|
||||
@@ -27,7 +27,7 @@ from apps.lyric.models import User
|
||||
# - On page reload: tray always starts closed (JS in-memory only).
|
||||
#
|
||||
# Contents (populated in later sprints): Role card, Significator, Celtic Cross
|
||||
# draw, natus wheel, committed dice/cards for this table.
|
||||
# draw, sky wheel, committed dice/cards for this table.
|
||||
#
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
// ── NatusWheelSpec.js ─────────────────────────────────────────────────────────
|
||||
// ── SkyWheelSpec.js ─────────────────────────────────────────────────────────
|
||||
//
|
||||
// Unit specs for natus-wheel.js — planet/element click-to-lock tooltips.
|
||||
// Unit specs for sky-wheel.js — planet/element click-to-lock tooltips.
|
||||
//
|
||||
// DOM contract assumed:
|
||||
// <svg id="id_natus_svg"> — target for NatusWheel.draw()
|
||||
// <div id="id_natus_tooltip"> — tooltip portal (position:fixed on page)
|
||||
// <svg id="id_sky_svg"> — target for SkyWheel.draw()
|
||||
// <div id="id_sky_tooltip"> — tooltip portal (position:fixed on page)
|
||||
//
|
||||
// Click-lock contract:
|
||||
// click on [data-planet] group → adds .nw-planet--active class
|
||||
// raises group to DOM front
|
||||
// shows #id_natus_tooltip with
|
||||
// shows #id_sky_tooltip with
|
||||
// planet name, in-sign degree, sign name,
|
||||
// ℞ if retrograde, and "n / total" index
|
||||
// click same planet again → removes .nw-planet--active; hides tooltip
|
||||
@@ -40,7 +40,7 @@ const CONJUNCTION_CHART = {
|
||||
house_system: "O",
|
||||
};
|
||||
|
||||
describe("NatusWheel — planet click tooltips", () => {
|
||||
describe("SkyWheel — planet click tooltips", () => {
|
||||
|
||||
const SYNTHETIC_CHART = {
|
||||
planets: {
|
||||
@@ -67,7 +67,7 @@ describe("NatusWheel — planet click tooltips", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
svgEl = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
svgEl.setAttribute("id", "id_natus_svg");
|
||||
svgEl.setAttribute("id", "id_sky_svg");
|
||||
svgEl.setAttribute("width", "400");
|
||||
svgEl.setAttribute("height", "400");
|
||||
svgEl.style.width = "400px";
|
||||
@@ -75,16 +75,16 @@ describe("NatusWheel — planet click tooltips", () => {
|
||||
document.body.appendChild(svgEl);
|
||||
|
||||
tooltipEl = document.createElement("div");
|
||||
tooltipEl.id = "id_natus_tooltip";
|
||||
tooltipEl.id = "id_sky_tooltip";
|
||||
tooltipEl.className = "tt";
|
||||
tooltipEl.style.display = "none";
|
||||
document.body.appendChild(tooltipEl);
|
||||
|
||||
NatusWheel.draw(svgEl, SYNTHETIC_CHART);
|
||||
SkyWheel.draw(svgEl, SYNTHETIC_CHART);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
NatusWheel.clear();
|
||||
SkyWheel.clear();
|
||||
svgEl.remove();
|
||||
tooltipEl.remove();
|
||||
});
|
||||
@@ -145,13 +145,13 @@ describe("NatusWheel — planet click tooltips", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("NatusWheel — tick lines, raise, and cycle navigation", () => {
|
||||
describe("SkyWheel — tick lines, raise, and cycle navigation", () => {
|
||||
|
||||
let svgEl2, tooltipEl;
|
||||
|
||||
beforeEach(() => {
|
||||
svgEl2 = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
svgEl2.setAttribute("id", "id_natus_svg_conj");
|
||||
svgEl2.setAttribute("id", "id_sky_svg_conj");
|
||||
svgEl2.setAttribute("width", "400");
|
||||
svgEl2.setAttribute("height", "400");
|
||||
svgEl2.style.width = "400px";
|
||||
@@ -159,17 +159,17 @@ describe("NatusWheel — tick lines, raise, and cycle navigation", () => {
|
||||
document.body.appendChild(svgEl2);
|
||||
|
||||
tooltipEl = document.createElement("div");
|
||||
tooltipEl.id = "id_natus_tooltip";
|
||||
tooltipEl.id = "id_sky_tooltip";
|
||||
tooltipEl.className = "tt";
|
||||
tooltipEl.style.display = "none";
|
||||
tooltipEl.style.position = "fixed";
|
||||
document.body.appendChild(tooltipEl);
|
||||
|
||||
NatusWheel.draw(svgEl2, CONJUNCTION_CHART);
|
||||
SkyWheel.draw(svgEl2, CONJUNCTION_CHART);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
NatusWheel.clear();
|
||||
SkyWheel.clear();
|
||||
svgEl2.remove();
|
||||
tooltipEl.remove();
|
||||
});
|
||||
@@ -295,7 +295,7 @@ describe("NatusWheel — tick lines, raise, and cycle navigation", () => {
|
||||
// - DOFF clears lines; re-opening same planet finds DON active
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
describe("NatusWheel — DON/DOFF aspect line persistence", () => {
|
||||
describe("SkyWheel — DON/DOFF aspect line persistence", () => {
|
||||
|
||||
const ASPECT_CHART = {
|
||||
planets: {
|
||||
@@ -326,7 +326,7 @@ describe("NatusWheel — DON/DOFF aspect line persistence", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
svgEl = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
svgEl.setAttribute("id", "id_natus_svg");
|
||||
svgEl.setAttribute("id", "id_sky_svg");
|
||||
svgEl.setAttribute("width", "400");
|
||||
svgEl.setAttribute("height", "400");
|
||||
svgEl.style.width = "400px";
|
||||
@@ -334,16 +334,16 @@ describe("NatusWheel — DON/DOFF aspect line persistence", () => {
|
||||
document.body.appendChild(svgEl);
|
||||
|
||||
tooltipEl = document.createElement("div");
|
||||
tooltipEl.id = "id_natus_tooltip";
|
||||
tooltipEl.id = "id_sky_tooltip";
|
||||
tooltipEl.className = "tt";
|
||||
tooltipEl.style.display = "none";
|
||||
document.body.appendChild(tooltipEl);
|
||||
|
||||
NatusWheel.draw(svgEl, ASPECT_CHART);
|
||||
SkyWheel.draw(svgEl, ASPECT_CHART);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
NatusWheel.clear();
|
||||
SkyWheel.clear();
|
||||
svgEl.remove();
|
||||
tooltipEl.remove();
|
||||
});
|
||||
@@ -454,7 +454,7 @@ describe("NatusWheel — DON/DOFF aspect line persistence", () => {
|
||||
});
|
||||
});
|
||||
|
||||
xdescribe("NatusWheel — half-wheel tooltip positioning", () => {
|
||||
xdescribe("SkyWheel — half-wheel tooltip positioning", () => {
|
||||
|
||||
const HALF_CHART = {
|
||||
planets: {
|
||||
@@ -481,7 +481,7 @@ xdescribe("NatusWheel — half-wheel tooltip positioning", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
svgEl3 = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
svgEl3.setAttribute("id", "id_natus_svg_half");
|
||||
svgEl3.setAttribute("id", "id_sky_svg_half");
|
||||
svgEl3.setAttribute("width", "400");
|
||||
svgEl3.setAttribute("height", "400");
|
||||
svgEl3.style.width = "400px";
|
||||
@@ -489,7 +489,7 @@ xdescribe("NatusWheel — half-wheel tooltip positioning", () => {
|
||||
document.body.appendChild(svgEl3);
|
||||
|
||||
tooltipEl = document.createElement("div");
|
||||
tooltipEl.id = "id_natus_tooltip";
|
||||
tooltipEl.id = "id_sky_tooltip";
|
||||
tooltipEl.className = "tt";
|
||||
tooltipEl.style.display = "none";
|
||||
tooltipEl.style.position = "fixed";
|
||||
@@ -501,11 +501,11 @@ xdescribe("NatusWheel — half-wheel tooltip positioning", () => {
|
||||
{ left: 0, top: 0, width: 400, height: 400, right: 400, bottom: 400 }
|
||||
);
|
||||
|
||||
NatusWheel.draw(svgEl3, HALF_CHART);
|
||||
SkyWheel.draw(svgEl3, HALF_CHART);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
NatusWheel.clear();
|
||||
SkyWheel.clear();
|
||||
svgEl3.remove();
|
||||
tooltipEl.remove();
|
||||
});
|
||||
@@ -561,7 +561,7 @@ xdescribe("NatusWheel — half-wheel tooltip positioning", () => {
|
||||
// clicking a classic-element slice lists contributor planet names in the tooltip.
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
describe("NatusWheel — element tooltip contributor display", () => {
|
||||
describe("SkyWheel — element tooltip contributor display", () => {
|
||||
|
||||
const ENRICHED_CHART = {
|
||||
planets: {
|
||||
@@ -608,7 +608,7 @@ describe("NatusWheel — element tooltip contributor display", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
svgEl = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
svgEl.setAttribute("id", "id_natus_svg");
|
||||
svgEl.setAttribute("id", "id_sky_svg");
|
||||
svgEl.setAttribute("width", "400");
|
||||
svgEl.setAttribute("height", "400");
|
||||
svgEl.style.width = "400px";
|
||||
@@ -616,16 +616,16 @@ describe("NatusWheel — element tooltip contributor display", () => {
|
||||
document.body.appendChild(svgEl);
|
||||
|
||||
tooltipEl = document.createElement("div");
|
||||
tooltipEl.id = "id_natus_tooltip";
|
||||
tooltipEl.id = "id_sky_tooltip";
|
||||
tooltipEl.className = "tt";
|
||||
tooltipEl.style.display = "none";
|
||||
document.body.appendChild(tooltipEl);
|
||||
|
||||
NatusWheel.draw(svgEl, ENRICHED_CHART);
|
||||
SkyWheel.draw(svgEl, ENRICHED_CHART);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
NatusWheel.clear();
|
||||
SkyWheel.clear();
|
||||
svgEl.remove();
|
||||
tooltipEl.remove();
|
||||
});
|
||||
@@ -682,7 +682,7 @@ describe("NatusWheel — element tooltip contributor display", () => {
|
||||
// Clicking the same sign again closes the tooltip.
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
describe("NatusWheel — sign ring click tooltips", () => {
|
||||
describe("SkyWheel — sign ring click tooltips", () => {
|
||||
|
||||
const SIGN_CHART = {
|
||||
planets: {
|
||||
@@ -703,7 +703,7 @@ describe("NatusWheel — sign ring click tooltips", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
svgEl = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
svgEl.setAttribute("id", "id_natus_svg");
|
||||
svgEl.setAttribute("id", "id_sky_svg");
|
||||
svgEl.setAttribute("width", "400");
|
||||
svgEl.setAttribute("height", "400");
|
||||
svgEl.style.width = "400px";
|
||||
@@ -711,16 +711,16 @@ describe("NatusWheel — sign ring click tooltips", () => {
|
||||
document.body.appendChild(svgEl);
|
||||
|
||||
tooltipEl = document.createElement("div");
|
||||
tooltipEl.id = "id_natus_tooltip";
|
||||
tooltipEl.id = "id_sky_tooltip";
|
||||
tooltipEl.className = "tt";
|
||||
tooltipEl.style.display = "none";
|
||||
document.body.appendChild(tooltipEl);
|
||||
|
||||
NatusWheel.draw(svgEl, SIGN_CHART);
|
||||
SkyWheel.draw(svgEl, SIGN_CHART);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
NatusWheel.clear();
|
||||
SkyWheel.clear();
|
||||
svgEl.remove();
|
||||
tooltipEl.remove();
|
||||
});
|
||||
@@ -756,7 +756,7 @@ describe("NatusWheel — sign ring click tooltips", () => {
|
||||
// Clicking the same house again closes the tooltip.
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
describe("NatusWheel — house ring click tooltips", () => {
|
||||
describe("SkyWheel — house ring click tooltips", () => {
|
||||
|
||||
const HOUSE_CHART = {
|
||||
planets: {
|
||||
@@ -777,7 +777,7 @@ describe("NatusWheel — house ring click tooltips", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
svgEl = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
svgEl.setAttribute("id", "id_natus_svg");
|
||||
svgEl.setAttribute("id", "id_sky_svg");
|
||||
svgEl.setAttribute("width", "400");
|
||||
svgEl.setAttribute("height", "400");
|
||||
svgEl.style.width = "400px";
|
||||
@@ -785,16 +785,16 @@ describe("NatusWheel — house ring click tooltips", () => {
|
||||
document.body.appendChild(svgEl);
|
||||
|
||||
tooltipEl = document.createElement("div");
|
||||
tooltipEl.id = "id_natus_tooltip";
|
||||
tooltipEl.id = "id_sky_tooltip";
|
||||
tooltipEl.className = "tt";
|
||||
tooltipEl.style.display = "none";
|
||||
document.body.appendChild(tooltipEl);
|
||||
|
||||
NatusWheel.draw(svgEl, HOUSE_CHART);
|
||||
SkyWheel.draw(svgEl, HOUSE_CHART);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
NatusWheel.clear();
|
||||
SkyWheel.clear();
|
||||
svgEl.remove();
|
||||
tooltipEl.remove();
|
||||
});
|
||||
@@ -835,7 +835,7 @@ describe("NatusWheel — house ring click tooltips", () => {
|
||||
// Planet tooltips include angle aspects in their own aspect lists.
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
describe("NatusWheel — angle (ASC/MC) click tooltips", () => {
|
||||
describe("SkyWheel — angle (ASC/MC) click tooltips", () => {
|
||||
|
||||
// ASC=0°(Aries): Sun@8° → Conjunction orb 8° ✓; Mars@188° → Opposition orb 8° ✓
|
||||
// MC=90°(Cancer): Moon@97° → Conjunction orb 7° ✓
|
||||
@@ -863,7 +863,7 @@ describe("NatusWheel — angle (ASC/MC) click tooltips", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
svgEl = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
svgEl.setAttribute("id", "id_natus_svg");
|
||||
svgEl.setAttribute("id", "id_sky_svg");
|
||||
svgEl.setAttribute("width", "400");
|
||||
svgEl.setAttribute("height", "400");
|
||||
svgEl.style.width = "400px";
|
||||
@@ -871,16 +871,16 @@ describe("NatusWheel — angle (ASC/MC) click tooltips", () => {
|
||||
document.body.appendChild(svgEl);
|
||||
|
||||
tooltipEl = document.createElement("div");
|
||||
tooltipEl.id = "id_natus_tooltip";
|
||||
tooltipEl.id = "id_sky_tooltip";
|
||||
tooltipEl.className = "tt";
|
||||
tooltipEl.style.display = "none";
|
||||
document.body.appendChild(tooltipEl);
|
||||
|
||||
NatusWheel.draw(svgEl, ANGLE_CHART);
|
||||
SkyWheel.draw(svgEl, ANGLE_CHART);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
NatusWheel.clear();
|
||||
SkyWheel.clear();
|
||||
svgEl.remove();
|
||||
tooltipEl.remove();
|
||||
});
|
||||
@@ -25,7 +25,7 @@
|
||||
<script src="SigSelectSpec.js"></script>
|
||||
<script src="SeaDealSpec.js"></script>
|
||||
<script src="FanStageSpec.js"></script>
|
||||
<script src="NatusWheelSpec.js"></script>
|
||||
<script src="SkyWheelSpec.js"></script>
|
||||
<script src="NoteSpec.js"></script>
|
||||
<script src="NotePageSpec.js"></script>
|
||||
<!-- src files -->
|
||||
@@ -40,7 +40,7 @@
|
||||
<script src="/static/apps/epic/sea.js"></script>
|
||||
<script src="/static/apps/gameboard/game-kit.js"></script>
|
||||
<script src="/static/apps/gameboard/d3.min.js"></script>
|
||||
<script src="/static/apps/gameboard/natus-wheel.js"></script>
|
||||
<script src="/static/apps/gameboard/sky-wheel.js"></script>
|
||||
<!-- Jasmine env config (optional) -->
|
||||
<script src="lib/jasmine-6.0.1/boot1.js"></script>
|
||||
|
||||
|
||||
@@ -926,7 +926,7 @@ html:has(.sig-backdrop) {
|
||||
}
|
||||
|
||||
// ── PICK SEA overlay ─────────────────────────────────────────────────────────
|
||||
// Mirrors .natus-* structure but with columns reversed:
|
||||
// Mirrors .sky-* structure but with columns reversed:
|
||||
// left = transparent (Celtic Cross card positions)
|
||||
// right = rgba(--priUser) opaque (spread select)
|
||||
|
||||
@@ -1414,7 +1414,7 @@ $_glow-gravity: 0 0 0.8rem 0.15rem rgba(var(--quaUser), 0.6);
|
||||
|
||||
}
|
||||
|
||||
// NVM button — same positioning as .natus-modal-wrap > .btn-cancel
|
||||
// NVM button — same positioning as .sky-modal-wrap > .btn-cancel
|
||||
.sea-modal-wrap > .btn-cancel {
|
||||
position: absolute;
|
||||
top: -1rem;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
// ─── Natus (Pick Sky) overlay ────────────────────────────────────────────────
|
||||
// ─── Sky (Pick Sky) overlay ────────────────────────────────────────────────
|
||||
// Gaussian backdrop + centred modal, matching the gate/sig overlay pattern.
|
||||
// Open state: html.natus-open (added by JS on PICK SKY click).
|
||||
// Open state: html.sky-open (added by JS on PICK SKY click).
|
||||
//
|
||||
// Layout: header / two-column body (form | wheel) / footer
|
||||
// Collapses to stacked single-column below 600 px.
|
||||
|
||||
// ── Scroll-lock ───────────────────────────────────────────────────────────────
|
||||
|
||||
html.natus-open {
|
||||
html.sky-open {
|
||||
overflow: hidden;
|
||||
|
||||
#id_aperture_fill { opacity: 1; }
|
||||
@@ -15,7 +15,7 @@ html.natus-open {
|
||||
|
||||
// ── Backdrop ──────────────────────────────────────────────────────────────────
|
||||
|
||||
.natus-backdrop {
|
||||
.sky-backdrop {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.75);
|
||||
@@ -23,18 +23,18 @@ html.natus-open {
|
||||
z-index: 100;
|
||||
pointer-events: none;
|
||||
|
||||
// Hidden until html.natus-open
|
||||
// Hidden until html.sky-open
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s ease;
|
||||
}
|
||||
|
||||
html.natus-open .natus-backdrop {
|
||||
html.sky-open .sky-backdrop {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
// ── Overlay shell (positions + scrolls the modal) ─────────────────────────────
|
||||
|
||||
.natus-overlay {
|
||||
.sky-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
@@ -45,7 +45,7 @@ html.natus-open .natus-backdrop {
|
||||
overscroll-behavior: contain;
|
||||
pointer-events: none;
|
||||
|
||||
// Hidden until html.natus-open
|
||||
// Hidden until html.sky-open
|
||||
visibility: hidden;
|
||||
|
||||
@media (orientation: landscape) {
|
||||
@@ -55,7 +55,7 @@ html.natus-open .natus-backdrop {
|
||||
}
|
||||
}
|
||||
|
||||
html.natus-open .natus-overlay {
|
||||
html.sky-open .sky-overlay {
|
||||
visibility: visible;
|
||||
pointer-events: none; // modal itself is pointer-events: auto
|
||||
}
|
||||
@@ -64,7 +64,7 @@ html.natus-open .natus-overlay {
|
||||
|
||||
// Thin wrapper: position:relative so the NVM circle can sit on the corner
|
||||
// without being clipped by the modal's overflow:hidden.
|
||||
.natus-modal-wrap {
|
||||
.sky-modal-wrap {
|
||||
position: relative;
|
||||
pointer-events: none; // overlay handles pointer-events; children re-enable
|
||||
width: 92vw;
|
||||
@@ -76,16 +76,16 @@ html.natus-open .natus-overlay {
|
||||
transition: opacity 0.2s ease, transform 0.2s ease;
|
||||
}
|
||||
|
||||
html.natus-open .natus-modal-wrap {
|
||||
html.sky-open .sky-modal-wrap {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.natus-modal {
|
||||
.sky-modal {
|
||||
pointer-events: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%; // fills .natus-modal-wrap
|
||||
width: 100%; // fills .sky-modal-wrap
|
||||
max-height: 96vh;
|
||||
border: 0.1rem solid rgba(var(--terUser), 0.25);
|
||||
border-radius: 0.5rem;
|
||||
@@ -94,7 +94,7 @@ html.natus-open .natus-modal-wrap {
|
||||
|
||||
// ── Header ────────────────────────────────────────────────────────────────────
|
||||
|
||||
.natus-modal-header {
|
||||
.sky-modal-header {
|
||||
flex-shrink: 0;
|
||||
padding: 0.6rem 1rem;
|
||||
background: rgba(var(--priUser), 1);
|
||||
@@ -121,7 +121,7 @@ html.natus-open .natus-modal-wrap {
|
||||
|
||||
// ── Body: two columns ─────────────────────────────────────────────────────────
|
||||
|
||||
.natus-modal-body {
|
||||
.sky-modal-body {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
@@ -130,7 +130,7 @@ html.natus-open .natus-modal-wrap {
|
||||
}
|
||||
|
||||
// Form column — fixed width; form-main scrolls, confirm btn pinned at bottom
|
||||
.natus-form-col {
|
||||
.sky-form-col {
|
||||
flex: 0 0 240px;
|
||||
overflow: hidden;
|
||||
padding: 0.9rem 1rem;
|
||||
@@ -142,7 +142,7 @@ html.natus-open .natus-modal-wrap {
|
||||
}
|
||||
|
||||
// Scrollable inner container (form fields + status)
|
||||
.natus-form-main {
|
||||
.sky-form-main {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
@@ -152,12 +152,12 @@ html.natus-open .natus-modal-wrap {
|
||||
}
|
||||
|
||||
// Confirm btn inside form-col — full width, pinned at column bottom
|
||||
.natus-form-col > #id_natus_confirm {
|
||||
.sky-form-col > #id_sky_confirm {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
// Wheel column — fills remaining space
|
||||
.natus-wheel-col {
|
||||
.sky-wheel-col {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
@@ -168,7 +168,7 @@ html.natus-open .natus-modal-wrap {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.natus-svg {
|
||||
.sky-svg {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@@ -179,7 +179,7 @@ html.natus-open .natus-modal-wrap {
|
||||
|
||||
// ── Form fields ───────────────────────────────────────────────────────────────
|
||||
|
||||
.natus-field {
|
||||
.sky-field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
@@ -204,9 +204,9 @@ html.natus-open .natus-modal-wrap {
|
||||
}
|
||||
|
||||
// Place search field wrapper: text input + geo button inline
|
||||
.natus-place-field { position: relative; }
|
||||
.sky-place-field { position: relative; }
|
||||
|
||||
.natus-place-wrap {
|
||||
.sky-place-wrap {
|
||||
display: flex;
|
||||
gap: 0.4rem;
|
||||
align-items: center;
|
||||
@@ -216,7 +216,7 @@ html.natus-open .natus-modal-wrap {
|
||||
}
|
||||
|
||||
// Nominatim suggestion dropdown
|
||||
.natus-suggestions {
|
||||
.sky-suggestions {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
@@ -230,7 +230,7 @@ html.natus-open .natus-modal-wrap {
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.natus-suggestion-item {
|
||||
.sky-suggestion-item {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 0.4rem 0.6rem;
|
||||
@@ -253,7 +253,7 @@ html.natus-open .natus-modal-wrap {
|
||||
}
|
||||
|
||||
// Coords row: lat | lon (read-only, populated by place selection)
|
||||
.natus-coords {
|
||||
.sky-coords {
|
||||
flex-direction: row;
|
||||
align-items: flex-end;
|
||||
gap: 0.4rem;
|
||||
@@ -282,7 +282,7 @@ html.natus-open .natus-modal-wrap {
|
||||
|
||||
// ── Status line ───────────────────────────────────────────────────────────────
|
||||
|
||||
.natus-status {
|
||||
.sky-status {
|
||||
font-size: 0.65rem;
|
||||
opacity: 0.6;
|
||||
min-height: 1rem;
|
||||
@@ -295,11 +295,11 @@ html.natus-open .natus-modal-wrap {
|
||||
}
|
||||
|
||||
// ── NVM corner btn ────────────────────────────────────────────────────────────
|
||||
// Absolutely pinned to top-right corner of .natus-modal-wrap.
|
||||
// Absolutely pinned to top-right corner of .sky-modal-wrap.
|
||||
// transform: translate(50%,-50%) centres the circle on the corner point.
|
||||
// Lives outside .natus-modal so overflow:hidden doesn't clip it.
|
||||
// Lives outside .sky-modal so overflow:hidden doesn't clip it.
|
||||
|
||||
#id_natus_cancel {
|
||||
#id_sky_cancel {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
@@ -312,22 +312,22 @@ html.natus-open .natus-modal-wrap {
|
||||
// ── Narrow / portrait ─────────────────────────────────────────────────────────
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.natus-modal-wrap {
|
||||
.sky-modal-wrap {
|
||||
width: 92vw;
|
||||
}
|
||||
|
||||
.natus-modal {
|
||||
.sky-modal {
|
||||
max-height: 96vh;
|
||||
}
|
||||
|
||||
.natus-modal-body {
|
||||
.sky-modal-body {
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
// Form col stacks above wheel; internally becomes a flex-row so
|
||||
// form-main gets most of the width and confirm btn sits to its right.
|
||||
.natus-form-col {
|
||||
.sky-form-col {
|
||||
flex: 0 0 auto;
|
||||
flex-direction: row;
|
||||
align-items: flex-end;
|
||||
@@ -337,19 +337,19 @@ html.natus-open .natus-modal-wrap {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.natus-form-main {
|
||||
.sky-form-main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow-y: auto;
|
||||
max-height: 40vh;
|
||||
}
|
||||
|
||||
.natus-form-col > #id_natus_confirm {
|
||||
.sky-form-col > #id_sky_confirm {
|
||||
flex-shrink: 0;
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.natus-wheel-col {
|
||||
.sky-wheel-col {
|
||||
flex: 0 0 280px;
|
||||
}
|
||||
}
|
||||
@@ -512,10 +512,10 @@ body[class*="-light"] {
|
||||
|
||||
// ── Planet hover tooltip — must live outside any ancestor with transform or
|
||||
// container-type (both break position:fixed). Placed as a direct sibling of
|
||||
// .natus-overlay in room.html; alongside #id_tooltip_portal in home.html. ──
|
||||
// .sky-overlay in room.html; alongside #id_tooltip_portal in home.html. ──
|
||||
|
||||
#id_natus_tooltip,
|
||||
#id_natus_tooltip_2 {
|
||||
#id_sky_tooltip,
|
||||
#id_sky_tooltip_2 {
|
||||
position: fixed;
|
||||
z-index: 200;
|
||||
pointer-events: auto;
|
||||
@@ -787,8 +787,8 @@ body[class*="-light"] {
|
||||
}
|
||||
|
||||
// Element title colors — primary tier on dark palettes
|
||||
#id_natus_tooltip,
|
||||
#id_natus_tooltip_2 {
|
||||
#id_sky_tooltip,
|
||||
#id_sky_tooltip_2 {
|
||||
.tt-title--el-fire { color: rgba(var(--priRd), 1); }
|
||||
.tt-title--el-stone { color: rgba(var(--priFs), 1); }
|
||||
.tt-title--el-time { color: rgba(var(--priYl), 1); }
|
||||
@@ -798,8 +798,8 @@ body[class*="-light"] {
|
||||
}
|
||||
|
||||
// Sign tooltip title + sign icon SVG — element border colors (Stone/Air/Fire/Water schema)
|
||||
#id_natus_tooltip,
|
||||
#id_natus_tooltip_2 {
|
||||
#id_sky_tooltip,
|
||||
#id_sky_tooltip_2 {
|
||||
.tt-title--sign-fire { color: rgba(var(--priOr), 1); }
|
||||
.tt-title--sign-stone { color: rgba(var(--priMe), 1); }
|
||||
.tt-title--sign-air { color: rgba(var(--priBl), 1); }
|
||||
@@ -816,8 +816,8 @@ body[class*="-light"] {
|
||||
}
|
||||
|
||||
// On light palettes — switch to tertiary tier for legibility
|
||||
body[class*="-light"] #id_natus_tooltip,
|
||||
body[class*="-light"] #id_natus_tooltip_2 {
|
||||
body[class*="-light"] #id_sky_tooltip,
|
||||
body[class*="-light"] #id_sky_tooltip_2 {
|
||||
.tt-title--el-fire { color: rgba(var(--terRd), 1); }
|
||||
.tt-title--el-stone { color: rgba(var(--terFs), 1); }
|
||||
.tt-title--el-time { color: rgba(var(--terYl), 1); }
|
||||
@@ -827,8 +827,8 @@ body[class*="-light"] #id_natus_tooltip_2 {
|
||||
}
|
||||
|
||||
// On light palettes — switch to primary (darkest) tier for legibility
|
||||
body[class*="-light"] #id_natus_tooltip,
|
||||
body[class*="-light"] #id_natus_tooltip_2 {
|
||||
body[class*="-light"] #id_sky_tooltip,
|
||||
body[class*="-light"] #id_sky_tooltip_2 {
|
||||
.tt-title--au { color: rgba(var(--priAu), 1); }
|
||||
.tt-title--ag { color: rgba(var(--priAg), 1); }
|
||||
.tt-title--hg { color: rgba(var(--priHg), 1); }
|
||||
@@ -849,7 +849,7 @@ body[class*="-light"] #id_natus_tooltip_2 {
|
||||
|
||||
h2 { flex-shrink: 0; }
|
||||
|
||||
.natus-svg {
|
||||
.sky-svg {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
max-width: none;
|
||||
@@ -866,7 +866,7 @@ body[class*="-light"] #id_natus_tooltip_2 {
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
|
||||
#id_natus_confirm {
|
||||
#id_sky_confirm {
|
||||
margin-top: -1.5rem;
|
||||
align-self: center;
|
||||
position: relative;
|
||||
@@ -908,13 +908,13 @@ body.page-sky {
|
||||
}
|
||||
|
||||
// Stack wheel above form; allow body to grow past viewport (page scrolls, not body)
|
||||
.sky-page .natus-modal-body {
|
||||
.sky-page .sky-modal-body {
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
// Wheel takes its natural square size from its width — never shrinks for the form
|
||||
.sky-page .natus-wheel-col {
|
||||
.sky-page .sky-wheel-col {
|
||||
order: -1;
|
||||
flex: 0 0 auto;
|
||||
width: 100%;
|
||||
@@ -925,7 +925,7 @@ body.page-sky {
|
||||
}
|
||||
|
||||
// Form col runs horizontally below the wheel (same compact pattern as narrow-portrait modal)
|
||||
.sky-page .natus-form-col {
|
||||
.sky-page .sky-form-col {
|
||||
flex: 0 0 auto;
|
||||
flex-direction: row;
|
||||
align-items: flex-end;
|
||||
@@ -933,7 +933,7 @@ body.page-sky {
|
||||
border-top: 0.1rem solid rgba(var(--terUser), 0.12);
|
||||
}
|
||||
|
||||
.sky-page .natus-form-main {
|
||||
.sky-page .sky-form-main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
overflow-y: visible;
|
||||
@@ -942,8 +942,8 @@ body.page-sky {
|
||||
// ── Sidebar z-index sink (landscape sidebars must go below backdrop) ───────────
|
||||
|
||||
@media (orientation: landscape) {
|
||||
html.natus-open body .container .navbar,
|
||||
html.natus-open body #id_footer {
|
||||
html.sky-open body .container .navbar,
|
||||
html.sky-open body #id_footer {
|
||||
z-index: 90;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
// ── Tooltip base styles ───────────────────────────────────────────────────────
|
||||
// Shared by wallet tokens, game-kit kit bag, and natus wheel tooltips.
|
||||
// Portal tooltips (#id_tooltip_portal, #id_natus_tooltip) are position:fixed
|
||||
// Shared by wallet tokens, game-kit kit bag, and sky wheel tooltips.
|
||||
// Portal tooltips (#id_tooltip_portal, #id_sky_tooltip) are position:fixed
|
||||
// and override z-index; inline .tt cards use position:absolute within their
|
||||
// parent token container.
|
||||
|
||||
|
||||
@@ -258,7 +258,7 @@ $tray-bevel: 0.3rem; // inner bevel ring; grid must sit inside this
|
||||
position: relative;
|
||||
|
||||
// Whatever a cell holds (role-card img, sig stage card, future Celtic Cross
|
||||
// / natus wheel / dice) gets a soft drop shadow to lift it off the felt.
|
||||
// / sky wheel / dice) gets a soft drop shadow to lift it off the felt.
|
||||
// Applied to the child rather than the cell itself so the dotted grid
|
||||
// borders stay shadow-free.
|
||||
> * {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
@import 'palette-picker';
|
||||
@import 'room';
|
||||
@import 'card-deck';
|
||||
@import 'natus';
|
||||
@import 'sky';
|
||||
@import 'tray';
|
||||
@import 'billboard';
|
||||
@import 'note';
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
// ── NatusWheelSpec.js ─────────────────────────────────────────────────────────
|
||||
// ── SkyWheelSpec.js ─────────────────────────────────────────────────────────
|
||||
//
|
||||
// Unit specs for natus-wheel.js — planet/element click-to-lock tooltips.
|
||||
// Unit specs for sky-wheel.js — planet/element click-to-lock tooltips.
|
||||
//
|
||||
// DOM contract assumed:
|
||||
// <svg id="id_natus_svg"> — target for NatusWheel.draw()
|
||||
// <div id="id_natus_tooltip"> — tooltip portal (position:fixed on page)
|
||||
// <svg id="id_sky_svg"> — target for SkyWheel.draw()
|
||||
// <div id="id_sky_tooltip"> — tooltip portal (position:fixed on page)
|
||||
//
|
||||
// Click-lock contract:
|
||||
// click on [data-planet] group → adds .nw-planet--active class
|
||||
// raises group to DOM front
|
||||
// shows #id_natus_tooltip with
|
||||
// shows #id_sky_tooltip with
|
||||
// planet name, in-sign degree, sign name,
|
||||
// ℞ if retrograde, and "n / total" index
|
||||
// click same planet again → removes .nw-planet--active; hides tooltip
|
||||
@@ -40,7 +40,7 @@ const CONJUNCTION_CHART = {
|
||||
house_system: "O",
|
||||
};
|
||||
|
||||
describe("NatusWheel — planet click tooltips", () => {
|
||||
describe("SkyWheel — planet click tooltips", () => {
|
||||
|
||||
const SYNTHETIC_CHART = {
|
||||
planets: {
|
||||
@@ -67,7 +67,7 @@ describe("NatusWheel — planet click tooltips", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
svgEl = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
svgEl.setAttribute("id", "id_natus_svg");
|
||||
svgEl.setAttribute("id", "id_sky_svg");
|
||||
svgEl.setAttribute("width", "400");
|
||||
svgEl.setAttribute("height", "400");
|
||||
svgEl.style.width = "400px";
|
||||
@@ -75,16 +75,16 @@ describe("NatusWheel — planet click tooltips", () => {
|
||||
document.body.appendChild(svgEl);
|
||||
|
||||
tooltipEl = document.createElement("div");
|
||||
tooltipEl.id = "id_natus_tooltip";
|
||||
tooltipEl.id = "id_sky_tooltip";
|
||||
tooltipEl.className = "tt";
|
||||
tooltipEl.style.display = "none";
|
||||
document.body.appendChild(tooltipEl);
|
||||
|
||||
NatusWheel.draw(svgEl, SYNTHETIC_CHART);
|
||||
SkyWheel.draw(svgEl, SYNTHETIC_CHART);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
NatusWheel.clear();
|
||||
SkyWheel.clear();
|
||||
svgEl.remove();
|
||||
tooltipEl.remove();
|
||||
});
|
||||
@@ -145,13 +145,13 @@ describe("NatusWheel — planet click tooltips", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("NatusWheel — tick lines, raise, and cycle navigation", () => {
|
||||
describe("SkyWheel — tick lines, raise, and cycle navigation", () => {
|
||||
|
||||
let svgEl2, tooltipEl;
|
||||
|
||||
beforeEach(() => {
|
||||
svgEl2 = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
svgEl2.setAttribute("id", "id_natus_svg_conj");
|
||||
svgEl2.setAttribute("id", "id_sky_svg_conj");
|
||||
svgEl2.setAttribute("width", "400");
|
||||
svgEl2.setAttribute("height", "400");
|
||||
svgEl2.style.width = "400px";
|
||||
@@ -159,17 +159,17 @@ describe("NatusWheel — tick lines, raise, and cycle navigation", () => {
|
||||
document.body.appendChild(svgEl2);
|
||||
|
||||
tooltipEl = document.createElement("div");
|
||||
tooltipEl.id = "id_natus_tooltip";
|
||||
tooltipEl.id = "id_sky_tooltip";
|
||||
tooltipEl.className = "tt";
|
||||
tooltipEl.style.display = "none";
|
||||
tooltipEl.style.position = "fixed";
|
||||
document.body.appendChild(tooltipEl);
|
||||
|
||||
NatusWheel.draw(svgEl2, CONJUNCTION_CHART);
|
||||
SkyWheel.draw(svgEl2, CONJUNCTION_CHART);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
NatusWheel.clear();
|
||||
SkyWheel.clear();
|
||||
svgEl2.remove();
|
||||
tooltipEl.remove();
|
||||
});
|
||||
@@ -295,7 +295,7 @@ describe("NatusWheel — tick lines, raise, and cycle navigation", () => {
|
||||
// - DOFF clears lines; re-opening same planet finds DON active
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
describe("NatusWheel — DON/DOFF aspect line persistence", () => {
|
||||
describe("SkyWheel — DON/DOFF aspect line persistence", () => {
|
||||
|
||||
const ASPECT_CHART = {
|
||||
planets: {
|
||||
@@ -326,7 +326,7 @@ describe("NatusWheel — DON/DOFF aspect line persistence", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
svgEl = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
svgEl.setAttribute("id", "id_natus_svg");
|
||||
svgEl.setAttribute("id", "id_sky_svg");
|
||||
svgEl.setAttribute("width", "400");
|
||||
svgEl.setAttribute("height", "400");
|
||||
svgEl.style.width = "400px";
|
||||
@@ -334,16 +334,16 @@ describe("NatusWheel — DON/DOFF aspect line persistence", () => {
|
||||
document.body.appendChild(svgEl);
|
||||
|
||||
tooltipEl = document.createElement("div");
|
||||
tooltipEl.id = "id_natus_tooltip";
|
||||
tooltipEl.id = "id_sky_tooltip";
|
||||
tooltipEl.className = "tt";
|
||||
tooltipEl.style.display = "none";
|
||||
document.body.appendChild(tooltipEl);
|
||||
|
||||
NatusWheel.draw(svgEl, ASPECT_CHART);
|
||||
SkyWheel.draw(svgEl, ASPECT_CHART);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
NatusWheel.clear();
|
||||
SkyWheel.clear();
|
||||
svgEl.remove();
|
||||
tooltipEl.remove();
|
||||
});
|
||||
@@ -454,7 +454,7 @@ describe("NatusWheel — DON/DOFF aspect line persistence", () => {
|
||||
});
|
||||
});
|
||||
|
||||
xdescribe("NatusWheel — half-wheel tooltip positioning", () => {
|
||||
xdescribe("SkyWheel — half-wheel tooltip positioning", () => {
|
||||
|
||||
const HALF_CHART = {
|
||||
planets: {
|
||||
@@ -481,7 +481,7 @@ xdescribe("NatusWheel — half-wheel tooltip positioning", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
svgEl3 = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
svgEl3.setAttribute("id", "id_natus_svg_half");
|
||||
svgEl3.setAttribute("id", "id_sky_svg_half");
|
||||
svgEl3.setAttribute("width", "400");
|
||||
svgEl3.setAttribute("height", "400");
|
||||
svgEl3.style.width = "400px";
|
||||
@@ -489,7 +489,7 @@ xdescribe("NatusWheel — half-wheel tooltip positioning", () => {
|
||||
document.body.appendChild(svgEl3);
|
||||
|
||||
tooltipEl = document.createElement("div");
|
||||
tooltipEl.id = "id_natus_tooltip";
|
||||
tooltipEl.id = "id_sky_tooltip";
|
||||
tooltipEl.className = "tt";
|
||||
tooltipEl.style.display = "none";
|
||||
tooltipEl.style.position = "fixed";
|
||||
@@ -501,11 +501,11 @@ xdescribe("NatusWheel — half-wheel tooltip positioning", () => {
|
||||
{ left: 0, top: 0, width: 400, height: 400, right: 400, bottom: 400 }
|
||||
);
|
||||
|
||||
NatusWheel.draw(svgEl3, HALF_CHART);
|
||||
SkyWheel.draw(svgEl3, HALF_CHART);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
NatusWheel.clear();
|
||||
SkyWheel.clear();
|
||||
svgEl3.remove();
|
||||
tooltipEl.remove();
|
||||
});
|
||||
@@ -561,7 +561,7 @@ xdescribe("NatusWheel — half-wheel tooltip positioning", () => {
|
||||
// clicking a classic-element slice lists contributor planet names in the tooltip.
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
describe("NatusWheel — element tooltip contributor display", () => {
|
||||
describe("SkyWheel — element tooltip contributor display", () => {
|
||||
|
||||
const ENRICHED_CHART = {
|
||||
planets: {
|
||||
@@ -608,7 +608,7 @@ describe("NatusWheel — element tooltip contributor display", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
svgEl = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
svgEl.setAttribute("id", "id_natus_svg");
|
||||
svgEl.setAttribute("id", "id_sky_svg");
|
||||
svgEl.setAttribute("width", "400");
|
||||
svgEl.setAttribute("height", "400");
|
||||
svgEl.style.width = "400px";
|
||||
@@ -616,16 +616,16 @@ describe("NatusWheel — element tooltip contributor display", () => {
|
||||
document.body.appendChild(svgEl);
|
||||
|
||||
tooltipEl = document.createElement("div");
|
||||
tooltipEl.id = "id_natus_tooltip";
|
||||
tooltipEl.id = "id_sky_tooltip";
|
||||
tooltipEl.className = "tt";
|
||||
tooltipEl.style.display = "none";
|
||||
document.body.appendChild(tooltipEl);
|
||||
|
||||
NatusWheel.draw(svgEl, ENRICHED_CHART);
|
||||
SkyWheel.draw(svgEl, ENRICHED_CHART);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
NatusWheel.clear();
|
||||
SkyWheel.clear();
|
||||
svgEl.remove();
|
||||
tooltipEl.remove();
|
||||
});
|
||||
@@ -682,7 +682,7 @@ describe("NatusWheel — element tooltip contributor display", () => {
|
||||
// Clicking the same sign again closes the tooltip.
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
describe("NatusWheel — sign ring click tooltips", () => {
|
||||
describe("SkyWheel — sign ring click tooltips", () => {
|
||||
|
||||
const SIGN_CHART = {
|
||||
planets: {
|
||||
@@ -703,7 +703,7 @@ describe("NatusWheel — sign ring click tooltips", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
svgEl = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
svgEl.setAttribute("id", "id_natus_svg");
|
||||
svgEl.setAttribute("id", "id_sky_svg");
|
||||
svgEl.setAttribute("width", "400");
|
||||
svgEl.setAttribute("height", "400");
|
||||
svgEl.style.width = "400px";
|
||||
@@ -711,16 +711,16 @@ describe("NatusWheel — sign ring click tooltips", () => {
|
||||
document.body.appendChild(svgEl);
|
||||
|
||||
tooltipEl = document.createElement("div");
|
||||
tooltipEl.id = "id_natus_tooltip";
|
||||
tooltipEl.id = "id_sky_tooltip";
|
||||
tooltipEl.className = "tt";
|
||||
tooltipEl.style.display = "none";
|
||||
document.body.appendChild(tooltipEl);
|
||||
|
||||
NatusWheel.draw(svgEl, SIGN_CHART);
|
||||
SkyWheel.draw(svgEl, SIGN_CHART);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
NatusWheel.clear();
|
||||
SkyWheel.clear();
|
||||
svgEl.remove();
|
||||
tooltipEl.remove();
|
||||
});
|
||||
@@ -756,7 +756,7 @@ describe("NatusWheel — sign ring click tooltips", () => {
|
||||
// Clicking the same house again closes the tooltip.
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
describe("NatusWheel — house ring click tooltips", () => {
|
||||
describe("SkyWheel — house ring click tooltips", () => {
|
||||
|
||||
const HOUSE_CHART = {
|
||||
planets: {
|
||||
@@ -777,7 +777,7 @@ describe("NatusWheel — house ring click tooltips", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
svgEl = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
svgEl.setAttribute("id", "id_natus_svg");
|
||||
svgEl.setAttribute("id", "id_sky_svg");
|
||||
svgEl.setAttribute("width", "400");
|
||||
svgEl.setAttribute("height", "400");
|
||||
svgEl.style.width = "400px";
|
||||
@@ -785,16 +785,16 @@ describe("NatusWheel — house ring click tooltips", () => {
|
||||
document.body.appendChild(svgEl);
|
||||
|
||||
tooltipEl = document.createElement("div");
|
||||
tooltipEl.id = "id_natus_tooltip";
|
||||
tooltipEl.id = "id_sky_tooltip";
|
||||
tooltipEl.className = "tt";
|
||||
tooltipEl.style.display = "none";
|
||||
document.body.appendChild(tooltipEl);
|
||||
|
||||
NatusWheel.draw(svgEl, HOUSE_CHART);
|
||||
SkyWheel.draw(svgEl, HOUSE_CHART);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
NatusWheel.clear();
|
||||
SkyWheel.clear();
|
||||
svgEl.remove();
|
||||
tooltipEl.remove();
|
||||
});
|
||||
@@ -835,7 +835,7 @@ describe("NatusWheel — house ring click tooltips", () => {
|
||||
// Planet tooltips include angle aspects in their own aspect lists.
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
describe("NatusWheel — angle (ASC/MC) click tooltips", () => {
|
||||
describe("SkyWheel — angle (ASC/MC) click tooltips", () => {
|
||||
|
||||
// ASC=0°(Aries): Sun@8° → Conjunction orb 8° ✓; Mars@188° → Opposition orb 8° ✓
|
||||
// MC=90°(Cancer): Moon@97° → Conjunction orb 7° ✓
|
||||
@@ -863,7 +863,7 @@ describe("NatusWheel — angle (ASC/MC) click tooltips", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
svgEl = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
svgEl.setAttribute("id", "id_natus_svg");
|
||||
svgEl.setAttribute("id", "id_sky_svg");
|
||||
svgEl.setAttribute("width", "400");
|
||||
svgEl.setAttribute("height", "400");
|
||||
svgEl.style.width = "400px";
|
||||
@@ -871,16 +871,16 @@ describe("NatusWheel — angle (ASC/MC) click tooltips", () => {
|
||||
document.body.appendChild(svgEl);
|
||||
|
||||
tooltipEl = document.createElement("div");
|
||||
tooltipEl.id = "id_natus_tooltip";
|
||||
tooltipEl.id = "id_sky_tooltip";
|
||||
tooltipEl.className = "tt";
|
||||
tooltipEl.style.display = "none";
|
||||
document.body.appendChild(tooltipEl);
|
||||
|
||||
NatusWheel.draw(svgEl, ANGLE_CHART);
|
||||
SkyWheel.draw(svgEl, ANGLE_CHART);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
NatusWheel.clear();
|
||||
SkyWheel.clear();
|
||||
svgEl.remove();
|
||||
tooltipEl.remove();
|
||||
});
|
||||
@@ -25,7 +25,7 @@
|
||||
<script src="SigSelectSpec.js"></script>
|
||||
<script src="SeaDealSpec.js"></script>
|
||||
<script src="FanStageSpec.js"></script>
|
||||
<script src="NatusWheelSpec.js"></script>
|
||||
<script src="SkyWheelSpec.js"></script>
|
||||
<script src="NoteSpec.js"></script>
|
||||
<script src="NotePageSpec.js"></script>
|
||||
<!-- src files -->
|
||||
@@ -40,7 +40,7 @@
|
||||
<script src="/static/apps/epic/sea.js"></script>
|
||||
<script src="/static/apps/gameboard/game-kit.js"></script>
|
||||
<script src="/static/apps/gameboard/d3.min.js"></script>
|
||||
<script src="/static/apps/gameboard/natus-wheel.js"></script>
|
||||
<script src="/static/apps/gameboard/sky-wheel.js"></script>
|
||||
<!-- Jasmine env config (optional) -->
|
||||
<script src="lib/jasmine-6.0.1/boot1.js"></script>
|
||||
|
||||
|
||||
@@ -10,22 +10,22 @@
|
||||
|
||||
{% if not request.user.sky_chart_data %}
|
||||
<div id="id_applet_sky_form_wrap">
|
||||
<form id="id_natus_form" autocomplete="off">
|
||||
<form id="id_sky_form" autocomplete="off">
|
||||
|
||||
<div class="natus-field">
|
||||
<div class="sky-field">
|
||||
<label for="id_nf_date">Birth date</label>
|
||||
<input id="id_nf_date" name="date" type="date" required>
|
||||
</div>
|
||||
|
||||
<div class="natus-field">
|
||||
<div class="sky-field">
|
||||
<label for="id_nf_time">Birth time</label>
|
||||
<input id="id_nf_time" name="time" type="time" value="12:00">
|
||||
<small>Local time at birth place. Use 12:00 if unknown.</small>
|
||||
</div>
|
||||
|
||||
<div class="natus-field natus-place-field">
|
||||
<div class="sky-field sky-place-field">
|
||||
<label for="id_nf_place">Birth place</label>
|
||||
<div class="natus-place-wrap">
|
||||
<div class="sky-place-wrap">
|
||||
<input id="id_nf_place" name="place" type="text"
|
||||
placeholder="Start typing a city…"
|
||||
autocomplete="off">
|
||||
@@ -35,10 +35,10 @@
|
||||
<i class="fa-solid fa-location-crosshairs"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div id="id_nf_suggestions" class="natus-suggestions" hidden></div>
|
||||
<div id="id_nf_suggestions" class="sky-suggestions" hidden></div>
|
||||
</div>
|
||||
|
||||
<div class="natus-field natus-coords">
|
||||
<div class="sky-field sky-coords">
|
||||
<div>
|
||||
<label>Latitude</label>
|
||||
<input id="id_nf_lat" name="lat" type="text"
|
||||
@@ -51,7 +51,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="natus-field">
|
||||
<div class="sky-field">
|
||||
<label for="id_nf_tz">Timezone</label>
|
||||
<input id="id_nf_tz" name="tz" type="text"
|
||||
placeholder="auto-detected from location">
|
||||
@@ -60,15 +60,15 @@
|
||||
|
||||
</form>
|
||||
|
||||
<div id="id_natus_status" class="natus-status"></div>
|
||||
<div id="id_sky_status" class="sky-status"></div>
|
||||
|
||||
<button type="button" id="id_natus_confirm" class="btn btn-primary" disabled>
|
||||
<button type="button" id="id_sky_confirm" class="btn btn-primary" disabled>
|
||||
Save Sky
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<svg id="id_my_sky_svg" class="natus-svg"
|
||||
<svg id="id_my_sky_svg" class="sky-svg"
|
||||
{% if not request.user.sky_chart_data %}style="display:none;"{% endif %}></svg>
|
||||
{% if request.user.sky_chart_data %}
|
||||
{{ request.user.sky_chart_data|json_script:"id_my_sky_data" }}
|
||||
@@ -76,7 +76,7 @@
|
||||
</section>
|
||||
|
||||
<script src="{% static 'apps/gameboard/d3.min.js' %}"></script>
|
||||
<script src="{% static 'apps/gameboard/natus-wheel.js' %}"></script>
|
||||
<script src="{% static 'apps/gameboard/sky-wheel.js' %}"></script>
|
||||
<script>
|
||||
(function () {
|
||||
'use strict';
|
||||
@@ -87,24 +87,24 @@
|
||||
{% if request.user.sky_chart_data %}
|
||||
|
||||
// Sky already saved — fetch fresh enriched data from server then draw.
|
||||
fetch('{% url "sky_natus_data" %}')
|
||||
fetch('{% url "sky_data" %}')
|
||||
.then(function (r) { return r.ok ? r.json() : Promise.reject(r.status); })
|
||||
.then(function (data) {
|
||||
NatusWheel.preload().then(function () { NatusWheel.draw(svgEl, data); });
|
||||
SkyWheel.preload().then(function () { SkyWheel.draw(svgEl, data); });
|
||||
})
|
||||
.catch(function () {
|
||||
// Fallback: draw from inline stale data if endpoint fails.
|
||||
var stale = JSON.parse(document.getElementById('id_my_sky_data').textContent);
|
||||
NatusWheel.preload().then(function () { NatusWheel.draw(svgEl, stale); });
|
||||
SkyWheel.preload().then(function () { SkyWheel.draw(svgEl, stale); });
|
||||
});
|
||||
|
||||
{% else %}
|
||||
|
||||
// No sky saved yet — wire up the entry form.
|
||||
const formWrap = document.getElementById('id_applet_sky_form_wrap');
|
||||
const form = document.getElementById('id_natus_form');
|
||||
const statusEl = document.getElementById('id_natus_status');
|
||||
const confirmBtn = document.getElementById('id_natus_confirm');
|
||||
const form = document.getElementById('id_sky_form');
|
||||
const statusEl = document.getElementById('id_sky_status');
|
||||
const confirmBtn = document.getElementById('id_sky_confirm');
|
||||
const geoBtn = document.getElementById('id_nf_geolocate');
|
||||
const placeInput = document.getElementById('id_nf_place');
|
||||
const latInput = document.getElementById('id_nf_lat');
|
||||
@@ -117,7 +117,7 @@
|
||||
const SAVE_URL = section.dataset.saveUrl;
|
||||
const NOMINATIM = 'https://nominatim.openstreetmap.org/search';
|
||||
const USER_AGENT = 'EarthmanRPG/1.0 (https://earthmanrpg.me)';
|
||||
const LS_KEY = 'natus-form:dashboard:sky';
|
||||
const LS_KEY = 'sky-form:dashboard:sky';
|
||||
|
||||
let _lastChartData = null;
|
||||
let _placeDebounce = null;
|
||||
@@ -125,7 +125,7 @@
|
||||
const PLACE_DELAY = 400;
|
||||
const CHART_DELAY = 300;
|
||||
|
||||
NatusWheel.preload();
|
||||
SkyWheel.preload();
|
||||
|
||||
// ── localStorage persistence ──────────────────────────────────────────────
|
||||
|
||||
@@ -158,7 +158,7 @@
|
||||
|
||||
function setStatus(msg, type) {
|
||||
statusEl.textContent = msg;
|
||||
statusEl.className = 'natus-status' + (type ? ` natus-status--${type}` : '');
|
||||
statusEl.className = 'sky-status' + (type ? ` sky-status--${type}` : '');
|
||||
}
|
||||
|
||||
// ── Nominatim place search ────────────────────────────────────────────────
|
||||
@@ -197,7 +197,7 @@
|
||||
results.forEach(place => {
|
||||
const item = document.createElement('button');
|
||||
item.type = 'button';
|
||||
item.className = 'natus-suggestion-item';
|
||||
item.className = 'sky-suggestion-item';
|
||||
item.textContent = place.display_name;
|
||||
item.addEventListener('click', () => selectPlace(place));
|
||||
suggestions.appendChild(item);
|
||||
@@ -331,7 +331,7 @@
|
||||
.then(data => {
|
||||
formWrap.style.display = 'none';
|
||||
svgEl.style.display = '';
|
||||
NatusWheel.preload().then(() => NatusWheel.draw(svgEl, _lastChartData));
|
||||
SkyWheel.preload().then(() => SkyWheel.draw(svgEl, _lastChartData));
|
||||
Note.handleSaveResponse(data);
|
||||
})
|
||||
.catch(err => {
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
{% include "apps/applets/_partials/_gear.html" with menu_id="id_dash_applet_menu" %}
|
||||
</div>
|
||||
<div id="id_tooltip_portal" class="token-tooltip" style="display:none;"></div>
|
||||
<div id="id_natus_tooltip" class="tt" style="display:none;"></div>
|
||||
<div id="id_natus_tooltip_2" class="tt" style="display:none;"></div>
|
||||
<div id="id_sky_tooltip" class="tt" style="display:none;"></div>
|
||||
<div id="id_sky_tooltip_2" class="tt" style="display:none;"></div>
|
||||
{% endif %}
|
||||
{% endblock content %}
|
||||
|
||||
@@ -6,33 +6,33 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="sky-page"
|
||||
id="id_natus_overlay"
|
||||
id="id_sky_overlay"
|
||||
data-preview-url="{% url 'sky_preview' %}"
|
||||
data-save-url="{% url 'sky_save' %}">
|
||||
|
||||
<div class="natus-modal-body sky-body">
|
||||
<div class="sky-modal-body sky-body">
|
||||
|
||||
{# ── Form column ─────────────────────────────────────────────────── #}
|
||||
<div class="natus-form-col">
|
||||
<div class="natus-form-main">
|
||||
<form id="id_natus_form" autocomplete="off">
|
||||
<div class="sky-form-col">
|
||||
<div class="sky-form-main">
|
||||
<form id="id_sky_form" autocomplete="off">
|
||||
|
||||
<div class="natus-field">
|
||||
<div class="sky-field">
|
||||
<label for="id_nf_date">Birth date</label>
|
||||
<input id="id_nf_date" name="date" type="date" required
|
||||
{% if saved_birth_date %}value="{{ saved_birth_date }}"{% endif %}>
|
||||
</div>
|
||||
|
||||
<div class="natus-field">
|
||||
<div class="sky-field">
|
||||
<label for="id_nf_time">Birth time</label>
|
||||
<input id="id_nf_time" name="time" type="time"
|
||||
value="{{ saved_birth_time|default:'12:00' }}">
|
||||
<small>Local time at birth place. Use 12:00 if unknown.</small>
|
||||
</div>
|
||||
|
||||
<div class="natus-field natus-place-field">
|
||||
<div class="sky-field sky-place-field">
|
||||
<label for="id_nf_place">Birth place</label>
|
||||
<div class="natus-place-wrap">
|
||||
<div class="sky-place-wrap">
|
||||
<input id="id_nf_place" name="place" type="text"
|
||||
placeholder="Start typing a city…"
|
||||
autocomplete="off"
|
||||
@@ -43,10 +43,10 @@
|
||||
<i class="fa-solid fa-location-crosshairs"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div id="id_nf_suggestions" class="natus-suggestions" hidden></div>
|
||||
<div id="id_nf_suggestions" class="sky-suggestions" hidden></div>
|
||||
</div>
|
||||
|
||||
<div class="natus-field natus-coords">
|
||||
<div class="sky-field sky-coords">
|
||||
<div>
|
||||
<label>Latitude</label>
|
||||
<input id="id_nf_lat" name="lat" type="text"
|
||||
@@ -61,7 +61,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="natus-field">
|
||||
<div class="sky-field">
|
||||
<label for="id_nf_tz">Timezone</label>
|
||||
<input id="id_nf_tz" name="tz" type="text"
|
||||
placeholder="auto-detected from location"
|
||||
@@ -71,38 +71,38 @@
|
||||
|
||||
</form>
|
||||
|
||||
<div id="id_natus_status" class="natus-status"></div>
|
||||
</div>{# /.natus-form-main #}
|
||||
<div id="id_sky_status" class="sky-status"></div>
|
||||
</div>{# /.sky-form-main #}
|
||||
|
||||
<button type="button" id="id_natus_confirm" class="btn btn-primary" disabled>
|
||||
<button type="button" id="id_sky_confirm" class="btn btn-primary" disabled>
|
||||
Save Sky
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{# ── Wheel column ────────────────────────────────────────────────── #}
|
||||
<div class="natus-wheel-col">
|
||||
<svg id="id_natus_svg" class="natus-svg"></svg>
|
||||
<div class="sky-wheel-col">
|
||||
<svg id="id_sky_svg" class="sky-svg"></svg>
|
||||
</div>
|
||||
|
||||
</div>{# /.natus-modal-body #}
|
||||
</div>{# /.sky-modal-body #}
|
||||
</div>{# /.sky-page #}
|
||||
|
||||
{# Planet hover tooltip — position:fixed escapes any overflow:hidden ancestor #}
|
||||
<div id="id_natus_tooltip" class="tt" style="display:none;"></div>
|
||||
<div id="id_natus_tooltip_2" class="tt" style="display:none;"></div>
|
||||
<div id="id_sky_tooltip" class="tt" style="display:none;"></div>
|
||||
<div id="id_sky_tooltip_2" class="tt" style="display:none;"></div>
|
||||
|
||||
<script src="{% static 'apps/gameboard/d3.min.js' %}"></script>
|
||||
<script src="{% static 'apps/gameboard/natus-wheel.js' %}"></script>
|
||||
<script src="{% static 'apps/gameboard/sky-wheel.js' %}"></script>
|
||||
<script src="{% static 'apps/dashboard/note.js' %}"></script>
|
||||
<script>
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
const overlay = document.getElementById('id_natus_overlay');
|
||||
const form = document.getElementById('id_natus_form');
|
||||
const svgEl = document.getElementById('id_natus_svg');
|
||||
const statusEl = document.getElementById('id_natus_status');
|
||||
const confirmBtn = document.getElementById('id_natus_confirm');
|
||||
const overlay = document.getElementById('id_sky_overlay');
|
||||
const form = document.getElementById('id_sky_form');
|
||||
const svgEl = document.getElementById('id_sky_svg');
|
||||
const statusEl = document.getElementById('id_sky_status');
|
||||
const confirmBtn = document.getElementById('id_sky_confirm');
|
||||
const geoBtn = document.getElementById('id_nf_geolocate');
|
||||
const placeInput = document.getElementById('id_nf_place');
|
||||
const latInput = document.getElementById('id_nf_lat');
|
||||
@@ -122,13 +122,13 @@
|
||||
const PLACE_DELAY = 400;
|
||||
const CHART_DELAY = 300;
|
||||
|
||||
const _preloadReady = NatusWheel.preload();
|
||||
const _preloadReady = SkyWheel.preload();
|
||||
|
||||
// ── Status helper ───────────────────────────────────────────────────────
|
||||
|
||||
function setStatus(msg, type) {
|
||||
statusEl.textContent = msg;
|
||||
statusEl.className = 'natus-status' + (type ? ` natus-status--${type}` : '');
|
||||
statusEl.className = 'sky-status' + (type ? ` sky-status--${type}` : '');
|
||||
}
|
||||
|
||||
// ── Nominatim place search ──────────────────────────────────────────────
|
||||
@@ -167,7 +167,7 @@
|
||||
results.forEach(place => {
|
||||
const item = document.createElement('button');
|
||||
item.type = 'button';
|
||||
item.className = 'natus-suggestion-item';
|
||||
item.className = 'sky-suggestion-item';
|
||||
item.textContent = place.display_name;
|
||||
item.addEventListener('click', () => selectPlace(place));
|
||||
suggestions.appendChild(item);
|
||||
@@ -262,9 +262,9 @@
|
||||
setStatus('');
|
||||
confirmBtn.disabled = false;
|
||||
if (svgEl.querySelector('*')) {
|
||||
NatusWheel.redraw(data);
|
||||
SkyWheel.redraw(data);
|
||||
} else {
|
||||
NatusWheel.draw(svgEl, data);
|
||||
SkyWheel.draw(svgEl, data);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
@@ -321,7 +321,7 @@
|
||||
if (_savedSky) {
|
||||
_lastChartData = _savedSky;
|
||||
confirmBtn.disabled = false;
|
||||
NatusWheel.draw(svgEl, _savedSky);
|
||||
SkyWheel.draw(svgEl, _savedSky);
|
||||
} else if (_formReady()) {
|
||||
schedulePreview();
|
||||
}
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
{% load static %}
|
||||
{# PICK SKY overlay — natal chart entry + D3 wheel preview #}
|
||||
{# Included in room.html when table_status == "SKY_SELECT" #}
|
||||
{# Opens when user clicks #id_pick_sky_btn; html.natus-open controls #}
|
||||
{# Opens when user clicks #id_pick_sky_btn; html.sky-open controls #}
|
||||
{# visibility via CSS — backdrop-filter blur + centred modal. #}
|
||||
|
||||
<div class="natus-backdrop"></div>
|
||||
<div class="natus-overlay"
|
||||
id="id_natus_overlay"
|
||||
data-preview-url="{% url 'epic:natus_preview' room.id %}"
|
||||
data-save-url="{% url 'epic:natus_save' room.id %}"
|
||||
<div class="sky-backdrop"></div>
|
||||
<div class="sky-overlay"
|
||||
id="id_sky_overlay"
|
||||
data-preview-url="{% url 'epic:sky_preview' room.id %}"
|
||||
data-save-url="{% url 'epic:sky_save' room.id %}"
|
||||
data-sea-partial-url="{% url 'epic:sea_partial' room.id %}"
|
||||
data-user-seat-role="{{ user_seat_role }}">
|
||||
|
||||
<div class="natus-modal-wrap">
|
||||
<div class="natus-modal">
|
||||
<div class="sky-modal-wrap">
|
||||
<div class="sky-modal">
|
||||
|
||||
<header class="natus-modal-header">
|
||||
<header class="sky-modal-header">
|
||||
<h2>PICK <span>SKY</span></h2>
|
||||
<p>Enter your birth details to generate your natal chart.</p>
|
||||
</header>
|
||||
|
||||
<div class="natus-modal-body">
|
||||
<div class="sky-modal-body">
|
||||
|
||||
{# ── Form column ──────────────────────────────────────── #}
|
||||
<div class="natus-form-col">
|
||||
<div class="sky-form-col">
|
||||
|
||||
{# form-main scrolls independently; confirm btn stays pinned below it #}
|
||||
<div class="natus-form-main">
|
||||
<form id="id_natus_form" autocomplete="off">
|
||||
<div class="sky-form-main">
|
||||
<form id="id_sky_form" autocomplete="off">
|
||||
|
||||
<div class="natus-field">
|
||||
<div class="sky-field">
|
||||
<label for="id_nf_date">Birth date</label>
|
||||
<input id="id_nf_date" name="date" type="date" required>
|
||||
</div>
|
||||
|
||||
<div class="natus-field">
|
||||
<div class="sky-field">
|
||||
<label for="id_nf_time">Birth time</label>
|
||||
<input id="id_nf_time" name="time" type="time" value="12:00">
|
||||
<small>Local time at birth place. Use 12:00 if unknown.</small>
|
||||
</div>
|
||||
|
||||
<div class="natus-field natus-place-field">
|
||||
<div class="sky-field sky-place-field">
|
||||
<label for="id_nf_place">Birth place</label>
|
||||
<div class="natus-place-wrap">
|
||||
<div class="sky-place-wrap">
|
||||
<input id="id_nf_place" name="place" type="text"
|
||||
placeholder="Start typing a city…"
|
||||
autocomplete="off">
|
||||
@@ -52,10 +52,10 @@
|
||||
<i class="fa-solid fa-location-crosshairs"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div id="id_nf_suggestions" class="natus-suggestions" hidden></div>
|
||||
<div id="id_nf_suggestions" class="sky-suggestions" hidden></div>
|
||||
</div>
|
||||
|
||||
<div class="natus-field natus-coords">
|
||||
<div class="sky-field sky-coords">
|
||||
<div>
|
||||
<label>Latitude</label>
|
||||
<input id="id_nf_lat" name="lat" type="text"
|
||||
@@ -68,7 +68,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="natus-field">
|
||||
<div class="sky-field">
|
||||
<label for="id_nf_tz">Timezone</label>
|
||||
<input id="id_nf_tz" name="tz" type="text"
|
||||
placeholder="auto-detected from location">
|
||||
@@ -77,42 +77,42 @@
|
||||
|
||||
</form>
|
||||
|
||||
<div id="id_natus_status" class="natus-status"></div>
|
||||
</div>{# /.natus-form-main #}
|
||||
<div id="id_sky_status" class="sky-status"></div>
|
||||
</div>{# /.sky-form-main #}
|
||||
|
||||
<button type="button" id="id_natus_confirm" class="btn btn-primary" disabled>
|
||||
<button type="button" id="id_sky_confirm" class="btn btn-primary" disabled>
|
||||
Save Sky
|
||||
</button>
|
||||
|
||||
</div>
|
||||
|
||||
{# ── Wheel column ─────────────────────────────────────── #}
|
||||
<div class="natus-wheel-col">
|
||||
<svg id="id_natus_svg" class="natus-svg"></svg>
|
||||
<div class="sky-wheel-col">
|
||||
<svg id="id_sky_svg" class="sky-svg"></svg>
|
||||
</div>
|
||||
|
||||
</div>{# /.natus-modal-body #}
|
||||
</div>{# /.sky-modal-body #}
|
||||
|
||||
</div>{# /.natus-modal #}
|
||||
</div>{# /.sky-modal #}
|
||||
|
||||
{# NVM: circle btn centered on the top-right corner of the modal #}
|
||||
<button type="button" id="id_natus_cancel" class="btn btn-cancel btn-sm">NVM</button>
|
||||
<button type="button" id="id_sky_cancel" class="btn btn-cancel btn-sm">NVM</button>
|
||||
|
||||
</div>{# /.natus-modal-wrap #}
|
||||
</div>{# /.natus-overlay #}
|
||||
</div>{# /.sky-modal-wrap #}
|
||||
</div>{# /.sky-overlay #}
|
||||
|
||||
<script src="{% static 'apps/gameboard/d3.min.js' %}"></script>
|
||||
<script src="{% static 'apps/gameboard/natus-wheel.js' %}"></script>
|
||||
<script src="{% static 'apps/gameboard/sky-wheel.js' %}"></script>
|
||||
<script>
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
const overlay = document.getElementById('id_natus_overlay');
|
||||
const form = document.getElementById('id_natus_form');
|
||||
const svgEl = document.getElementById('id_natus_svg');
|
||||
const statusEl = document.getElementById('id_natus_status');
|
||||
const confirmBtn = document.getElementById('id_natus_confirm');
|
||||
const cancelBtn = document.getElementById('id_natus_cancel');
|
||||
const overlay = document.getElementById('id_sky_overlay');
|
||||
const form = document.getElementById('id_sky_form');
|
||||
const svgEl = document.getElementById('id_sky_svg');
|
||||
const statusEl = document.getElementById('id_sky_status');
|
||||
const confirmBtn = document.getElementById('id_sky_confirm');
|
||||
const cancelBtn = document.getElementById('id_sky_cancel');
|
||||
const geoBtn = document.getElementById('id_nf_geolocate');
|
||||
const placeInput = document.getElementById('id_nf_place');
|
||||
const latInput = document.getElementById('id_nf_lat');
|
||||
@@ -134,12 +134,12 @@
|
||||
|
||||
// Preload zodiac SVG icons eagerly — they'll be cached before any draw() call.
|
||||
// To swap an icon, replace the .svg file in zodiac-signs/ and hard-refresh.
|
||||
NatusWheel.preload();
|
||||
SkyWheel.preload();
|
||||
|
||||
// ── localStorage persistence ──────────────────────────────────────────────
|
||||
// Key scoped to room so multiple rooms don't clobber each other.
|
||||
|
||||
const LS_KEY = 'natus-form:' + SAVE_URL;
|
||||
const LS_KEY = 'sky-form:' + SAVE_URL;
|
||||
|
||||
function _saveForm() {
|
||||
const data = {
|
||||
@@ -167,8 +167,8 @@
|
||||
|
||||
// ── Open / Close ──────────────────────────────────────────────────────────
|
||||
|
||||
function openNatus() {
|
||||
document.documentElement.classList.add('natus-open');
|
||||
function openSky() {
|
||||
document.documentElement.classList.add('sky-open');
|
||||
// If the wheel is empty but the form has enough data (restored from
|
||||
// localStorage), kick off a fresh preview so the animation plays.
|
||||
if (!svgEl.querySelector('*') && _formReady()) {
|
||||
@@ -176,21 +176,21 @@
|
||||
}
|
||||
}
|
||||
|
||||
function closeNatus() {
|
||||
document.documentElement.classList.remove('natus-open');
|
||||
function closeSky() {
|
||||
document.documentElement.classList.remove('sky-open');
|
||||
hideSuggestions();
|
||||
}
|
||||
|
||||
const pickSkyBtn = document.getElementById('id_pick_sky_btn');
|
||||
if (pickSkyBtn) pickSkyBtn.addEventListener('click', openNatus);
|
||||
cancelBtn.addEventListener('click', closeNatus);
|
||||
overlay.addEventListener('click', (e) => { if (e.target === overlay) closeNatus(); });
|
||||
if (pickSkyBtn) pickSkyBtn.addEventListener('click', openSky);
|
||||
cancelBtn.addEventListener('click', closeSky);
|
||||
overlay.addEventListener('click', (e) => { if (e.target === overlay) closeSky(); });
|
||||
|
||||
// ── Status helper ─────────────────────────────────────────────────────────
|
||||
|
||||
function setStatus(msg, type) {
|
||||
statusEl.textContent = msg;
|
||||
statusEl.className = 'natus-status' + (type ? ` natus-status--${type}` : '');
|
||||
statusEl.className = 'sky-status' + (type ? ` sky-status--${type}` : '');
|
||||
}
|
||||
|
||||
// ── Nominatim place search ────────────────────────────────────────────────
|
||||
@@ -229,7 +229,7 @@
|
||||
results.forEach(place => {
|
||||
const item = document.createElement('button');
|
||||
item.type = 'button';
|
||||
item.className = 'natus-suggestion-item';
|
||||
item.className = 'sky-suggestion-item';
|
||||
item.textContent = place.display_name;
|
||||
item.addEventListener('click', () => selectPlace(place));
|
||||
suggestions.appendChild(item);
|
||||
@@ -333,9 +333,9 @@
|
||||
setStatus('');
|
||||
confirmBtn.disabled = false;
|
||||
if (svgEl.querySelector('*')) {
|
||||
NatusWheel.redraw(data);
|
||||
SkyWheel.redraw(data);
|
||||
} else {
|
||||
NatusWheel.draw(svgEl, data);
|
||||
SkyWheel.draw(svgEl, data);
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
@@ -383,7 +383,7 @@
|
||||
});
|
||||
});
|
||||
|
||||
// ── Sky confirmed → close natus & reload to land on hex w. PICK SEA ──────
|
||||
// ── Sky confirmed → close sky & reload to land on hex w. PICK SEA ──────
|
||||
//
|
||||
// The gamer should witness the table hex (now showing PICK SEA in place of
|
||||
// PICK SKY) before opting into the sea overlay. We reload the room page —
|
||||
@@ -391,7 +391,7 @@
|
||||
// hex's btn flips automatically, and the user clicks PICK SEA to continue.
|
||||
|
||||
function _onSkyConfirmed() {
|
||||
closeNatus();
|
||||
closeSky();
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
@@ -404,7 +404,7 @@
|
||||
|
||||
// ── Restore persisted form data ────────────────────────────────────────────
|
||||
// Called after all functions are defined. Wheel draw is deferred to
|
||||
// openNatus() so the animation plays when the modal opens, not silently
|
||||
// openSky() so the animation plays when the modal opens, not silently
|
||||
// in the background on page load.
|
||||
|
||||
// WS: server broadcasts sky_confirmed when any gamer confirms their sky.
|
||||
@@ -72,14 +72,14 @@
|
||||
{% include "apps/gameboard/_partials/_sig_select_overlay.html" %}
|
||||
{% endif %}
|
||||
|
||||
{# Natus (Pick Sky) overlay — natal chart entry #}
|
||||
{# Sky (Pick Sky) overlay — natal chart entry #}
|
||||
{% if room.table_status == "SKY_SELECT" and not sky_confirmed %}
|
||||
{% include "apps/gameboard/_partials/_natus_overlay.html" %}
|
||||
{% include "apps/gameboard/_partials/_sky_overlay.html" %}
|
||||
{% endif %}
|
||||
{# Natus tooltip: sibling of .natus-overlay, not inside .natus-modal-wrap (which has transform) #}
|
||||
{# Sky tooltip: sibling of .sky-overlay, not inside .sky-modal-wrap (which has transform) #}
|
||||
{% if room.table_status == "SKY_SELECT" and not sky_confirmed %}
|
||||
<div id="id_natus_tooltip" class="tt" style="display:none;"></div>
|
||||
<div id="id_natus_tooltip_2" class="tt" style="display:none;"></div>
|
||||
<div id="id_sky_tooltip" class="tt" style="display:none;"></div>
|
||||
<div id="id_sky_tooltip_2" class="tt" style="display:none;"></div>
|
||||
{% endif %}
|
||||
|
||||
{# Sea (Pick Sea) overlay — Celtic Cross spread entry #}
|
||||
|
||||
Reference in New Issue
Block a user