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>
- 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>
- 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>
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>
- 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>
- 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>
- 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>
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>
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>
- 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>
- 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>
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>
- @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>
- 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>
- 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>
- 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>
- 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>
- 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>
- 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>
- 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>
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>
- 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>
- 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>
- 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>
- 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>
- translate remaining public views and page-model strings (titles, messages, relation names)
- Urdu stopword list for word-frequency stats (replaces Persian list)
- error pages: drop list of alternative Ganjoor services
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- translate reading-path views (poet, category, poem, search, header, login, partials)
from Persian to Urdu (exact-run dictionary, 262 strings; AI-summary data marker kept)
- semantic search widget only when SemanticSearchAPIRoot is configured
- appsettings defaults: no Ganjoor tracking script, no Ganjoor semantic search,
API/site roots default to the local Divan stack instead of api.ganjoor.net
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- MakeTextSearchable: Urdu letter variants, Urdu full stop, Urdu diacritics, zwj/marks
- SearchLikePatterns: phrase or per-word LIKE patterns (escaped), normalised like stored text
- poem, similar-poem and comment search use LIKE (Linux SQL Server image has no full-text)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- poet ImageUrl always points at the API image endpoint
- endpoint serves a neutral SVG placeholder when a poet has no portrait
- public data export: poet image URLs use our API (WebServiceUrl), not ganjoor.net
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>