Commit Graph

4214 Commits

Author SHA1 Message Date
Anas Rashid
bf96002fa1 Bookmark icon: our own rounded design (soft corners and notch), like common app icon packs
Owner reference: a Flaticon bookmark pack (similar, not the same). Drawn here, so no third-party
licence or attribution; grey outline when not set, filled brand colour when set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 00:04:58 +02:00
Anas Rashid
cd404710f0 Bookmark icon like Android apps: grey outline when not set, filled brand colour when set
Material Design 'bookmark' / 'bookmark_border' (Apache-2.0) drawn as a CSS mask so it follows the
theme; replaces the 🔖 emoji on bookmark buttons, couplet bookmarks, bookmarked phrases, contents
lists and cards, and the نشانات tab.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 00:03:02 +02:00
2b558ea494 Merge pull request 'Bookmarks (🔖) for poets, books/chapters and poems; word book on its own page' (#47) from feature/bookmarks-and-word-book into main
Reviewed-on: #47
2026-10-08 22:00:43 +00:00
Anas Rashid
98585962e2 Bookmarks with 🔖 for poets, books/chapters and poems; word book on its own page
Owner direction: the word collection is separate from bookmarks, and bookmarks use the bookmark
glyph, not hearts.
- 🔖 نشان buttons on poet, book/chapter (new) and poem pages; contents lists and cards show 🔖.
- /library (نشانات): bookmarked poets, books and chapters, poems, couplets, phrases.
- /words (ذخیرۂ الفاظ): the word book on its own page; tabs between the two; header links.
- Word button: 'ذخیرۂ الفاظ میں ڈالیں' (+), 'ذخیرۂ الفاظ میں ہے' (✓) once added.
- API: library kind 'category' (books and chapters) in toggle, state, marks and the list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:58:59 +02:00
4b3d228f49 Merge pull request 'Word book: + instead of a heart for words' (#46) from feature/word-book into main
Reviewed-on: #46
2026-10-08 21:58:16 +00:00
Anas Rashid
d4eaab769c Word book: + to add words (ذخیرۂ الفاظ), ✓ when added; hearts only for poets and works
Owner request: words are not favourites. The sidebar button reads '+ ذخیرۂ الفاظ میں' and turns
into '✓' once added; the selection menu has '+ لفظ'; the library section is ذخیرۂ الفاظ.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:52:41 +02:00
c05ad94b53 Merge pull request 'Personal library: saved poets, works, couplets and words' (#45) from feature/library into main
Reviewed-on: #45
2026-10-08 21:51:04 +00:00
Anas Rashid
55cc602815 Library: bookmarked phrases, and glyphs where the reader's marks are
- Select any part of a couplet or paragraph and press نشان (selection menu): the phrase is
  bookmarked and shown underlined with a small 🔖, also across the two misras; clicking it removes
  the bookmark (with a confirmation). Listed in the library as نشان زدہ عبارتیں.
- Contents lists show ♥ on favourite works and 🔖 on works holding bookmarks; book/section cards and
  poet cards (home) show 🔖 when they hold any. API: GET /api/library/marks; phrases in state and
  library; phrase kind validated against the couplet's text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:48:42 +02:00
531d4e9f69 Merge pull request 'Profile: full name and bio' (#43) from feature/profile into main
Reviewed-on: #43
2026-10-08 21:45:19 +00:00
Anas Rashid
8f4a71530d Personal library and word collection (#24, #25)
- Save (♡) poets and works, bookmark (🔖) couplets and paragraphs, save words from the word sidebar
  or the selection menu (with the couplet they came from); notes on items.
- /library (میری لائبریری): the reader's name and bio, then favourite poets, works, bookmarked
  couplets and saved words, each with its full path (poet » book » section » work) and a link to
  the couplet (#cN anchors on poem pages); saved words open the word sidebar.
- API (api/src/library.ts), JSON + Bearer tokens so mobile apps (#44) can use it; deleting an
  account deletes its library. Signed-out readers are sent to sign-in.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:35:34 +02:00
Anas Rashid
be0238521f Profile: full name and bio (#42)
Readers write their full name and a short bio (Urdu or any text) on /account; the name heads the
account page and the header shows the first name. API: POST /api/auth/profile (trimmed, control
characters dropped, 100 and 1,000 characters).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:31:19 +02:00
264a42c996 Merge pull request 'Permissions (IAM): moderators and scoped grants' (#40) from feature/permissions into main
Reviewed-on: #40
2026-10-08 21:22:26 +00:00
Anas Rashid
d72c14f8bd Permissions (IAM): L2/L1 moderators and scoped grants (#29)
- Roles: reader, mod-l2, mod-l1, admin. Grants: scope (all, poet, book/section with everything in
  it, one work) x content (poets, books, works, dictionary) x actions (create, edit, delete,
  arrange); dictionary grants are site-wide. can() in api/src/permissions.ts is the one check.
- Admin API and pages: role dropdown on /admin; /admin/user/:id lists a moderator's grants, adds
  them (target by poet id or page link) and revokes them; demoting a moderator clears their grants;
  grant and revoke go to the audit log.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:13:06 +02:00
48b106857d Merge pull request 'Admin panel: users, password resets, roles, audit log' (#39) from feature/admin-panel into main
Reviewed-on: #39
2026-10-08 21:10:04 +00:00
Anas Rashid
acf7767495 Admin panel: users, password reset on request, disable, roles, delete, audit log (#27)
- API (api/src/admin.ts, admins only): user list and search; password reset generates a temporary
  password shown once and ends the user's sessions; disable/enable (ends sessions, blocks sign-in);
  roles reader/admin; delete; an admin cannot disable, demote or delete themself. Every action is
  written to audit_log (kept when users are deleted).
- First admin from the server: npm run make-admin -- <email> (after signing up).
- Site: /admin (users) and /admin/audit; 'ایڈمن' link in the header for admins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 23:08:19 +02:00
6610ed0f71 Merge pull request 'Accounts: email and password' (#37) from feature/accounts into main
Reviewed-on: #37
2026-10-08 21:05:05 +00:00
Anas Rashid
aa264aa6c3 Merge main (selection menu) into feature/accounts 2026-10-08 22:58:08 +02:00
c177b45561 Merge pull request 'Text selection menu like Ganjoor's' (#26) from feature/selection-menu into main
Reviewed-on: #26
2026-10-08 20:57:38 +00:00
Anas Rashid
e12f6eeb2a Selection menu: remove the قرآن option (owner request)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 22:56:46 +02:00
Anas Rashid
e409adc668 Accounts: email and password sign-up, sign-in/out, change password, delete account (#18)
- API (api/src/auth.ts): scrypt password hashes; random session tokens stored only as SHA-256;
  30-day sliding sessions; rate limits on sign-in (per IP and per email) and sign-up; changing the
  password signs out other devices; deleting the account removes its data.
- Site: /signup, /signin (returns to the page the reader came from), /account; header link; plain
  forms, no JavaScript needed. Session in an HTTP-only, SameSite=Lax cookie (Secure over HTTPS).
- CSRF: Astro's origin check, with the site's hostnames listed (SITE_HOSTS) so its own form posts
  pass and other sites' are refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 22:53:51 +02:00
Anas Rashid
c558960ddc Text selection menu like Ganjoor's (#23)
Selecting reading text shows a menu by the selection: معنی (word sidebar, single words),
تلاش (search Divan, phrases quoted), کاپی (text with poet, title and link), شیئر (link to the
page with the selection highlighted via ?hl=; native share sheet where available), قرآن
(tanzil.net), گوگل. Escape or clearing the selection closes it; no layout shift.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 22:36:52 +02:00
Anas Rashid
3201a1c478 Word dictionary in full: all senses with usage examples, every IPA with dialect labels, every recording, full etymologies, synonyms, derived and related words
Panel folds the extras (more meanings, examples, long etymologies, long word lists). 175 MB in PostgreSQL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 22:27:37 +02:00
Anas Rashid
19b68425f6 Word sidebar: readings by vowels, vowel variants, similar words; Persian/Arabic explained in Urdu
- readings: one spelling, different short vowels (ملک: مَلَک، مَلِک، مِلک، مُلک), each with form,
  transliteration, IPA, audio and meanings
- always: same consonants with other long vowels (consonant-skeleton regex)
- not found: inflection stems (آنکھوں -> آنکھ, جاتے -> جانا), prefix regex, trigram similarity
- Persian/Arabic words with no Urdu entry: Urdu translations via English first, then English, then
  their own language; filler glosses (especially, usually…) dropped
- punctuation, dashes and spaces dropped from selected words and lookup keys; clean titles

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 22:07:10 +02:00
Anas Rashid
7fcc0ee7fa Word dictionary: full Wiktionary data for Urdu, Persian and Arabic in PostgreSQL, kept in sync
- en.wiktionary entries via kaikki.org (meanings, IPA, transliteration, audio, etymology, synonyms)
- ur./fa./ar.wiktionary dumps for meanings in each language (incl. more Urdu entry formats)
- English->Urdu pivot table from Urdu entries' glosses and ur.wiktionary's English entries
- dict-sync.ts: re-imports sources when upstream publishes new files, applies daily recent changes;
  run from deploy/sync.sh
- lookups now read the database (2-7 ms) instead of calling Wikimedia live

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:48:56 +02:00
Anas Rashid
ef618094d5 Word sidebar: select a word for its meanings and pronunciation from Wiktionary
Urdu, Persian and Arabic Wiktionaries in that order, each labelled; English meanings, IPA,
transliteration and audio from en.wiktionary; Urdu equivalents pivoted through English when Urdu
Wiktionary has no entry. Cached in PostgreSQL for 30 days. Left panel on wide screens, bottom
sheet on phones.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:38:10 +02:00
Anas Rashid
efb7aacde3 Search: highlighted matches, matching couplet or prose excerpt, exact phrase first; opened pages keep the highlight (?hl=)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:30:42 +02:00
Anas Rashid
9a2db49eab Radif per ghazal, matla/maqta labels, pen name set apart; contents grouped by radif letter
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:27:43 +02:00
Anas Rashid
4c7e0559a7 Search: separate Roman-to-Urdu toggle and on-screen keyboard toggle; keys hidden until asked
The header's flex rule overrode [hidden], so the keys showed on every page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:17:14 +02:00
Anas Rashid
647b9fa369 Keyboard in the Gboard/Windows Urdu layout with a shift layer; physical keys by position
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:15:58 +02:00
Anas Rashid
3e0e3a625d Keyboard rows stacked and centred (not the header's flex row)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:14:40 +02:00
Anas Rashid
4ef7de256c Keyboard toggle as an icon inside the search box
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:13:50 +02:00
Anas Rashid
f82df122b9 Urdu on-screen keyboard and phonetic typing in search
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:11:57 +02:00
Anas Rashid
87cea30f28 Self-host Naskh and Nastaliq fonts (fixes font switch in Brave); ?font= URL option
- @fontsource/noto-naskh-arabic and noto-nastaliq-urdu bundled with the site; no Google
  Fonts request (Brave Shields blocked it, so the switch had nothing to switch to)
- ?font=nastaliq|naskh sets the reading font (shareable links)
Validated: both fonts render from local files in headless Chromium; no googleapis requests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:09:26 +02:00
Anas Rashid
13610569cc deploy/sync.sh: daily Wikisource -> divan-data -> PostgreSQL sync for the server
- updates a divan-data checkout, fetches new/edited works (incremental), rebuilds, upserts
  into Postgres; optional commit/push of data (DIVAN_DATA_PUSH=1); flock against overlap
- import prints progress only on a terminal
Validated locally: full run 1m46s, 382 poets / 11,087 poems imported.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 21:00:15 +02:00
Anas Rashid
17200fc9aa Show Gregorian (عیسوی) years alongside Hijri for poets
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 20:53:53 +02:00
Anas Rashid
74f3973191 Header controls keep Naskh at normal height in Nastaliq mode
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 20:52:41 +02:00
Anas Rashid
d70e0a1fed Pinned poets on the home page (مقبول شعرا): Iqbal, Ghalib, Mir Taqi Mir, Mir Dard, Dagh
- poets.pin_order; editorial list in db/featured.sql, applied after every import
- home page shows the pinned row first, like Ganjoor
- Faiz is not included: his work is under copyright until 2035, so not in divan-data

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 20:42:33 +02:00
Anas Rashid
6b414c3d2d v2 UI: Ganjoor's look (palette, gold pills, centred couplets with ✤), leaner CSS
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 20:32:06 +02:00
Anas Rashid
2f4344dcdf Remove the .NET stack; Divan is now Node.js + TypeScript + PostgreSQL
- delete RMuseum (API), DivanRazor (site), solution, Dockerfile/compose, SQL Server
  deploy/backup scripts, run-local.sh, archived upstream docs
- the .NET code stays available at tag dotnet-final
- README rewritten for v2; .gitignore for Node

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 20:30:03 +02:00
Anas Rashid
60f0791405 Divan v2: Astro site with Divan's own design (proof of concept)
- web/: server-rendered Astro over the Node API: home (poets by Hijri century), poet
  (intro + sections), category (numbered contents), poem (couplets, prev/next, source),
  search (paged); Naskh default with Nastaliq toggle, light/dark, Urdu digits, RTL, mobile
- api: section counts include nested categories
- README: how to run v2

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 20:26:54 +02:00
Anas Rashid
c450ec010a Divan v2: Node.js API + PostgreSQL (proof of concept)
- db/schema.sql: poets, categories, poems, verses; normalised search_text with pg_trgm index
- api/: Fastify + pg, Node native TypeScript (no build step)
  - import.ts: loads divan-data (folder or CDN) with upserts; full import in ~36 s
  - server.ts: /api/poets, /api/page?url=, /api/search (Urdu-normalised ILIKE), /health
  - urdu.ts (+ test): normaliser ported from the .NET version

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 20:24:20 +02:00
Anas Rashid
4a1d2e0642 Poet/category tables of contents after import; Divan wordmark instead of Ganjoor logo
- public data import ran with empty HtmlText for poet/category pages (upstream fills it
  with a separate admin TOC job), so poet pages showed no works; the import now
  regenerates tables of contents for the importing user
- replace Ganjoor's logo image (gdap/gdapk) with the Divan wordmark on all pages; delete it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 20:17:24 +02:00
Anas Rashid
5f2b779e89 Public home page, Naskh default font, no poet photos
- home page is public: no redirect to login/admin import; flat poet list until century
  groups exist (upstream needed a pinned 'popular poets' group, which Divan never has, so
  the home page always redirected); public 'being prepared' notice when there are no poets
- Naskh is the default font, Nastaliq the option (site and static reader)
- poet/writer photos hidden everywhere (CSS on /poet/image/), poet page photos tab removed

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 19:59:20 +02:00
Anas Rashid
b186ed3336 run-local.sh: default data path ../divan-data
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 19:47:44 +02:00
Anas Rashid
37fe04ac79 Rename to Divan (Roman script) throughout
Projects (DivanRazor, DivanService.sln), files, folders, classes, settings (Divan:),
API routes (/api/divan), DB name and tables (divan, Divan*), local container/paths,
repo links (anas-rashid/divan, divan-data). Urdu دیوان unchanged.
Migrations renamed consistently (no pending model changes); requires a fresh database.
Stop tracking the generated RMuseum/RMuseum.xml (was upstream's stale doc).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 19:46:27 +02:00
Anas Rashid
a5f0f1b74d Fix poet image placeholder: null RImageId cast threw 500 (#4); run-local waits via HTTP
- GetPoetImageIdByUrl: missing portrait -> Guid.Empty (placeholder) instead of a null cast
- run-local.sh: wait for SQL Server with a query and for API/site via HTTP
  (pipefail+grep -q never matched; API log level hides 'Now listening' outside Development)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 01:03:44 +02:00
Anas Rashid
3df43e4979 Backups, restore and rollback runbook (#10, #12)
- deploy/backup.sh / deploy/restore.sh (compose service or local container; side-by-side test restore)
- restore: chown copied backup for sqlservr, fail loudly on unreadable file list
- README: update, backup, test-restore and rollback steps

Validated locally: backup 194 MB, restored into a test DB with identical counts, test DB dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:27:31 +02:00
Anas Rashid
e78fc9bd99 Security pass (#11): no default JWT secret, CORS allow-list, Divan system emails
- appsettings: drop upstream's public JWT secret; API refuses to start outside Development without one
- Cors:AllowedOrigins (compose: https://SITE_DOMAIN); unset = any origin for local dev
- default admin/system/bot emails @divan.local instead of @ganjoor.net

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:26:01 +02:00
Anas Rashid
e8cedaab8f Museum subsystem: remove public UI, keep backend dormant (#5)
- poem page: drop manuscripts/images tab and Pinterest/museum image suggestion form
- remove image recommendation (Pin) page and route
- museum API, services and tables stay dormant (no schema change)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:24:30 +02:00
Anas Rashid
c97bb18c3a Urdu public pages, batch 3 (#2): browser JS messages and labels
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 00:23:00 +02:00