diff --git a/README.md b/README.md index 975fc66..e7cf29e 100644 --- a/README.md +++ b/README.md @@ -13,21 +13,24 @@ Morocco (Ministry of Habous), matching AlAdhan `method=21`: - Fajr 19°, Isha 17° (twilight angles) - Standard Asr (shadow factor 1× — Maliki default in Morocco) - Dhuhr = solar noon + 5 min; Maghrib = sunset + 5 min (AlAdhan's baked offsets) -- Location hardcoded: Casablanca 33.5731, −7.5898 · TZ = UTC+1 year-round - (Morocco dropped DST in 2018) +- Location hardcoded: Casablanca 33.5731, −7.5898 · fixed TZ = UTC+1. + Ramadan clock changes are not handled by this version. All computation happens **on the watch** — no GPS, no network. Pure solar math (same Meeus approach as the sundial) lives in `src/common/prayer.js`. -## Screen +## Screen (v0.3) -- Header: CASABLANCA + current clock -- Purple banner: next prayer name + countdown (`1h 55m`, `+1d …` wraps past midnight) -- Six chord-tapered rows: widths [380, 412, 424, 406, 368, 304] px computed from - the circle chord at each row's position (fixed-width rows clipped off the disc - in the first layout — asserted numerically in tests now) -- Passed prayers dimmed (opacity 0.45); next row purple-highlighted -- 15s refresh +- Pure #000000 AMOLED background, including the list and prayer rows +- Larger 42px clock, 28px prayer names and 36px times +- Native vertically swipe-scrollable list: 80px rows in a 320×240px viewport +- Top/bottom spacers let the first and last prayers scroll to the center +- Fixed next-prayer countdown; lavender outline/text highlight the next prayer +- Normal system back button exits; no crown support assumed +- 15s refresh; no changes to prayer calculations + +Build and automated tests pass; v0.3 scrolling/rendering needs an on-watch check. +`preview-0.3.png` is a static layout mock with illustrative times, not a watch screenshot. ## Project layout @@ -52,6 +55,7 @@ npm ci npm run release node tests/prayer.cjs node tests/page.cjs +node tests/amoled.cjs ``` ## Install diff --git a/preview-0.3.png b/preview-0.3.png new file mode 100644 index 0000000..bfe839d Binary files /dev/null and b/preview-0.3.png differ diff --git a/src/manifest.json b/src/manifest.json index 47c3a11..2b1e3a7 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -2,8 +2,8 @@ "package": "com.ilias.salah", "name": "SALAH", "icon": "/common/icon.png", - "versionName": "0.2", - "versionCode": 2, + "versionName": "0.3", + "versionCode": 3, "minAPILevel": 1, "deviceTypeList": [ "watch" @@ -22,7 +22,7 @@ } }, "display": { - "backgroundColor": "#0a0d18" + "backgroundColor": "#000000" }, "features": [ { diff --git a/src/pages/index/index.ux b/src/pages/index/index.ux index 278d240..ac22aee 100644 --- a/src/pages/index/index.ux +++ b/src/pages/index/index.ux @@ -1,19 +1,20 @@ diff --git a/tests/amoled.cjs b/tests/amoled.cjs new file mode 100644 index 0000000..4e6d946 --- /dev/null +++ b/tests/amoled.cjs @@ -0,0 +1,25 @@ +const assert = require('node:assert/strict') +const fs = require('node:fs') +const src = fs.readFileSync('src/pages/index/index.ux', 'utf8') +const manifest = JSON.parse(fs.readFileSync('src/manifest.json')) +function css(name, prop) { + const block = src.match(new RegExp('\\.' + name + '\\s*\\{([^}]+)'))[1] + return block.match(new RegExp(prop + ':\\s*([^;]+)'))[1].trim() +} +assert.equal(css('page', 'background-color'), '#000000', 'page must be AMOLED black') +assert.equal(manifest.display.backgroundColor, '#000000') +assert.match(src, /]*class="prayers"/, 'native swipe-scrollable list') +assert.match(src, /]*type="prayer"[^>]*for="\{\{ row in rows \}\}"/) +assert.ok(parseFloat(css('rowName', 'font-size')) >= 28) +assert.ok(parseFloat(css('rowTime', 'font-size')) >= 36) +const top = parseFloat(css('prayers', 'top')) +const height = parseFloat(css('prayers', 'height')) +const width = parseFloat(css('prayers', 'width')) +const dy = Math.max(Math.abs(top - 233), Math.abs(top + height - 233)) +assert.ok(width <= 2 * Math.sqrt(233 ** 2 - dy ** 2) - 20, 'whole scroll viewport fits circle') +const rowH = parseFloat(css('row', 'height')) +const pad = parseFloat(css('spacer', 'height')) +assert.equal(pad, (height - rowH) / 2, 'first/last rows can reach viewport center') +assert.ok(6 * rowH + 2 * pad > height, 'content actually scrolls') +assert.ok(!src.includes('onBackPress'), 'system back still exits') +console.log('AMOLED, large text, scroll wiring and circular viewport checks passed') diff --git a/tests/page.cjs b/tests/page.cjs index f763ea7..9ed462c 100644 --- a/tests/page.cjs +++ b/tests/page.cjs @@ -148,18 +148,10 @@ test('update() rows: six entries, key+label+time wired', () => { } }) -test('row widths taper with the chord and fit the 466 circle at every row position', () => { - // rows: 6 x 40px + 5 x 4px margins, block starts after header+banner (top=130) - const ROW_WIDTHS = spec.ROW_WIDTHS || sandbox.ROW_WIDTHS - assert.equal(ROW_WIDTHS.length, 6) - const H = 40, MARGIN = 4, BLOCK_TOP = 130, R = 233, INSET = 10 - for (let n = 0; n < 6; n++) { - const top = BLOCK_TOP + n * (H + MARGIN) - const dy = Math.max(Math.abs(233 - top), Math.abs(233 - (top + H))) - const chord = 2 * Math.sqrt(Math.max(0, R * R - dy * dy)) - const safe = chord - 2 * INSET - assert.ok(ROW_WIDTHS[n] <= safe, `row ${n} width ${ROW_WIDTHS[n]} exceeds safe chord ${safe.toFixed(0)} at top ${top}`) - } +test('all rows fit the scroll viewport', () => { + const widths = sandbox.ROW_WIDTHS + assert.equal(widths.length, 6) + widths.forEach(width => assert.equal(width, 320)) }) test('bound rowStyle width equals ROW_WIDTHS[i]', () => {