- web/src/lib/editor.ts (Tiptap 3 MIT core): verse units of misras (1 = مصرع, 2 = شعر, 3+ = بند, as in
Divan text) with مطلع/حسن مطلع/مقطع labels, prose paragraphs, chapter headings and sub-headings, a
dictionary-link mark, footnotes and variant readings; Enter adds a misra (or moves into the next
empty one), Enter on an empty misra starts a new unit.
- Revision page: editor and source tabs over the same text, the work's data points as rows, a
toolbar (couplet, paragraph, headings, labels, dictionary link, footnote, variant, undo/redo,
Roman typing, Urdu keyboard). Saving always sends Divan text.
- Divan text: inline segments keep where links, footnotes and variants sit; toText writes a
document back. All 11,087 works round-trip unchanged through toText and through the editor.
- The Urdu keyboard and Roman typing type into rich editors; the reader's word sidebar and
selection menu stay out of editors.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- api/src/git.ts: each published version is a commit in the divan-data checkout (queued, one at a
time): the moderator as author by public name with a placeholder email, the summary and version
in the message, Reviewed-by / Approved-by / Divan-Revision / Divan-Version trailers;
DIVAN_GIT_PUSH=1 pushes. The commit id is kept with the revision.
- Privacy fix: published files and commits carry public names (profile name, else 'موڈریٹر <id>'),
never email addresses (divan-data is public).
- Poem pages: 'دیوان کا ورژن …' with who edited, reviewed and published, the date, and a link to
the commit diff (DIVAN_DATA_COMMIT_URL).
- deploy/sync.sh: pull --rebase so publishing commits are kept.
- Admins are super moderators (documented).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- revisions (a work's Divan text, summary, status, author/reviewer/publisher, the text it started from)
and revision_events (who did what, with comments): the review thread and the moderation log.
- API (api/src/moderation.ts): what a moderator may do on a work, queue, drafts (one open draft per
person and work), save, submit, approve, return/reject (reason required), publish; history; log.
Rules come from the IAM grants: L2 drafts, an L1 covering the work reviews, an admin publishes; L1
drafts go to the admin, admin drafts publish directly. Publishing numbers the version, writes it
to divan-data (owned.ts) and updates the site; a draft based on an older version is refused (409).
- Line diff (api/src/diff.ts) against the text a draft started from.
- Site: ترمیم کریں / تاریخچہ on works, /mod (queue, activity log), /mod/rev/<id> (editor with the Urdu
keyboard and Roman typing, diff, preview, actions, timeline), /mod/work/<id> (versions).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- api/src/owned.ts: fromPoem (a work as Divan text, the start of an edit) and writeOwned (the .dtx
source and the generated .json into divan-data/divan/); a CLI for writing one by hand.
- Divan text: chapter headings and sub-headings with levels, as their own verse entries.
- All 11,087 works convert to Divan text and back unchanged (apart from repeated spaces, and raw
'== … ==' markers in prose becoming headings).
- docs/content-model.md: the content management model (what is identified, versions and who did
what (#51), tags and tag search (#52), storage).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Results in groups: شعرا و ادبا (by name or pen name, with years, work counts and a 'search in their
works' link), کتابیں اور ابواب (books/chapters by title), and کلام (content).
- Content pages: numbered pagination (first, last, two either side, gaps) and 'نتائج ۴۱–۶۰ از ۸۱۱۶'.
- Author filter: chips for the authors the results come from, with counts; tick one or several to
narrow; a name box finds any other poet/writer, with the Urdu keyboard and Roman-to-Urdu typing.
The keyboard and Roman typing now work on any input marked data-urdu, not only the search box.
- API: poet=1,2 (several), authors with counts, poets and books on page 1 (api/src/search.ts).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/content-model.md compares MediaWiki (+FlaggedRevs/Approved Revs, ProofreadPage, SMW, Wikibase),
Wiki.js, BookStack, Outline, Docmost, DokuWiki, wikiparser-node, Tiptap/ProseMirror/CodeMirror, TEI P5
and Ganjoor's correction model, and recommends: Divan text (a Wikisource-compatible markup subset)
as the published source in divan-data, one file per work; moderation workflow in PostgreSQL; Tiptap
editor with a source view; wikiparser-node for Wikisource sync; TEI export.
Prototype api/src/divantext.ts (parse, verse JSON, TEI) with tests on a real Ghalib ghazal and Sir
Syed chapter: identical verses to today's export, Wikisource markup reads as-is, TEI validates.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
- 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>
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>
- 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>
- 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>