From a4332a581600254242b0799869307d34dcdc0a02 Mon Sep 17 00:00:00 2001 From: umairliaqatali Date: Mon, 2 Mar 2026 14:21:52 +0500 Subject: [PATCH 01/12] Add initial docs structure and README navigation --- README.md | 12 ++++++++ docs/README.md | 19 ++++++++++++ docs/best-practices.md | 20 +++++++++++++ docs/contributing.md | 35 +++++++++++++++++++++++ docs/data-model.md | 39 +++++++++++++++++++++++++ docs/datasets.md | 25 ++++++++++++++++ docs/downloading-data.md | 51 +++++++++++++++++++++++++++++++++ docs/faq.md | 33 +++++++++++++++++++++ docs/getting-started.md | 62 ++++++++++++++++++++++++++++++++++++++++ docs/tutorials.md | 34 ++++++++++++++++++++++ 10 files changed, 330 insertions(+) create mode 100644 docs/README.md create mode 100644 docs/best-practices.md create mode 100644 docs/contributing.md create mode 100644 docs/data-model.md create mode 100644 docs/datasets.md create mode 100644 docs/downloading-data.md create mode 100644 docs/faq.md create mode 100644 docs/getting-started.md create mode 100644 docs/tutorials.md diff --git a/README.md b/README.md index 3cfb0bb8..a35b08d0 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,18 @@ QUL is implemented using Ruby on Rails, and Active Admin for implementing the ad - Import Data: Import data from different sources. - Quranic grammar and morphology: Manage Quranic grammar and morphology data. +## Documentation +Start with the docs index: [docs/README.md](docs/README.md) + +- Getting Started: [docs/getting-started.md](docs/getting-started.md) +- Downloading Data: [docs/downloading-data.md](docs/downloading-data.md) +- Data Model: [docs/data-model.md](docs/data-model.md) +- Datasets: [docs/datasets.md](docs/datasets.md) +- Tutorials: [docs/tutorials.md](docs/tutorials.md) +- Best Practices: [docs/best-practices.md](docs/best-practices.md) +- Contributing: [docs/contributing.md](docs/contributing.md) +- FAQ: [docs/faq.md](docs/faq.md) + ## Setup Guide This guide will help you set up the QUL project on your local machine. Follow the steps below to get started. diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..5001f066 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,19 @@ +# QUL Documentation + +This documentation is optimized for first-time users and contributors who want to run QUL locally and understand how data flows through the project. + +## Start Here + +1. [Getting Started](getting-started.md) +2. [Downloading Data](downloading-data.md) +3. [Data Model](data-model.md) +4. [Datasets](datasets.md) +5. [Tutorials](tutorials.md) +6. [Best Practices](best-practices.md) +7. [Contributing](contributing.md) +8. [FAQ](faq.md) + +## Audience + +- New users: set up quickly and run the app with the mini dump. +- Developers: understand DB boundaries, workflows, and contribution paths. diff --git a/docs/best-practices.md b/docs/best-practices.md new file mode 100644 index 00000000..3f12a0a6 --- /dev/null +++ b/docs/best-practices.md @@ -0,0 +1,20 @@ +# Best Practices + +## For New Users + +- Start with the mini dump, not custom data. +- Validate DB restore before debugging app code. +- Keep local setup notes (Ruby, PG port, env vars) for repeatability. + +## For Contributors + +- Use feature branches; avoid committing directly to `main`. +- Keep PRs focused (docs-only, migration-only, feature-only when possible). +- Write migration code to be idempotent when practical (`if_exists`, `if_not_exists`). +- If a migration touches `quran_dev`, verify state against existing dump schema. + +## For Documentation + +- Prefer explicit commands over abstract instructions. +- Include likely failure cases and exact fixes. +- Keep new-user path first; put advanced details after. diff --git a/docs/contributing.md b/docs/contributing.md new file mode 100644 index 00000000..451b4ba2 --- /dev/null +++ b/docs/contributing.md @@ -0,0 +1,35 @@ +# Contributing + +## Basic Flow + +1. Fork repository on GitHub. +2. Clone your fork. +3. Add upstream remote: + +```bash +git remote add upstream https://github.com/TarteelAI/quranic-universal-library.git +``` + +4. Create branch: + +```bash +git switch -c docs/your-change +``` + +5. Make changes, commit, push, open PR to `TarteelAI/quranic-universal-library`. + +## Keep Your Fork Updated + +```bash +git fetch upstream +git switch main +git merge upstream/main +git push origin main +``` + +## Documentation PR Checklist + +- Docs are clear for first-time users. +- All commands are copy-paste ready. +- Root `README.md` links to any new docs page. +- Spelling and link checks are done. diff --git a/docs/data-model.md b/docs/data-model.md new file mode 100644 index 00000000..e15af162 --- /dev/null +++ b/docs/data-model.md @@ -0,0 +1,39 @@ +# Data Model + +QUL is split across two PostgreSQL databases. + +## 1) `quran_dev` + +Purpose: Quranic content and linguistic data. + +Key characteristics: + +- Main schema: `quran` +- Large content tables: verses, words, translations, tafsirs, scripts, audio metadata +- Also includes grammar/morphology entities + +Representative tables: + +- `quran.verses` +- `quran.words` +- `quran.translations` +- `quran.tafsirs` +- `quran.resource_contents` + +## 2) `quran_community_tarteel` + +Purpose: app/community and editorial workflow. + +Representative tables: + +- `users` +- `admin_users` +- `draft_translations` +- `draft_tafsirs` +- `resource_permissions` +- `versions` (change history) + +## Why Two Databases? + +- Separates canonical Quran content from community/editorial workflows. +- Makes operational boundaries clearer for backup, migration, and governance. diff --git a/docs/datasets.md b/docs/datasets.md new file mode 100644 index 00000000..07fed2fd --- /dev/null +++ b/docs/datasets.md @@ -0,0 +1,25 @@ +# Datasets + +QUL development depends on the mini `quran_dev` dataset. + +## What It Includes + +- Core Quran structure (chapters, verses, words) +- Selected translations and tafsir metadata/content +- Script and mushaf-related data +- Audio-related metadata needed by the app +- Supporting lookup entities (languages, resources, tags, etc.) + +## What It Does Not Include + +- Full production dataset +- All historical or high-volume resources + +If your feature needs more data coverage, open an issue in the project and describe the missing requirement. + +## Where to Download + +- Mini SQL dump: `mini_quran_dev.sql.zip` +- Mini binary dump: `mini_quran_dev.dump.zip` + +Both are linked in [downloading-data.md](downloading-data.md). diff --git a/docs/downloading-data.md b/docs/downloading-data.md new file mode 100644 index 00000000..a399ccae --- /dev/null +++ b/docs/downloading-data.md @@ -0,0 +1,51 @@ +# Downloading Data + +QUL development uses a **mini dump** (not full production data). + +## Available Dumps + +- SQL dump zip: `https://static-cdn.tarteel.ai/qul/mini-dumps/mini_quran_dev.sql.zip` +- Binary dump zip: `https://static-cdn.tarteel.ai/qul/mini-dumps/mini_quran_dev.dump.zip` + +As of **July 28, 2025**: + +- SQL zip size: ~`202 MB` +- Binary zip size: ~`131 MB` + +## Option A: SQL Restore + +```bash +unzip mini_quran_dev.sql.zip +psql -d quran_dev -f mini_quran_dev.sql +``` + +## Option B: Binary Restore + +```bash +unzip mini_quran_dev.dump.zip +pg_restore --no-owner --no-privileges --no-tablespaces --no-acl --dbname quran_dev -v mini_quran_dev.dump +``` + +## Recommended Choice + +- Use **binary dump** first (typically faster restore). +- If binary restore fails due to format/client mismatch, use **SQL dump**. + +## Validation + +After restore: + +```bash +psql -d quran_dev -c "SELECT COUNT(*) FROM quran.verses;" +``` + +You should see a non-zero count. + +## Troubleshooting + +- `unsupported version ... in file header`: + - Use a newer `pg_restore` client (or use SQL dump). +- `role ... does not exist` during SQL restore: + - Create the missing role, or edit dump ownership statements if needed. +- `schema "quran" does not exist`: + - Ensure the restore completed successfully. diff --git a/docs/faq.md b/docs/faq.md new file mode 100644 index 00000000..2a74bd60 --- /dev/null +++ b/docs/faq.md @@ -0,0 +1,33 @@ +# FAQ + +## Do I need the full production database? + +No. Use the official **mini development dump**. Full production dump is not shared. + +## Should I use SQL or binary dump? + +Start with binary. If your `pg_restore` client cannot read it, use SQL dump. + +## Why do I see migration conflicts like duplicate columns/tables? + +The dump can already contain schema changes that also exist as migrations. Validate actual schema state before applying destructive fixes. + +## Which PostgreSQL version should I use? + +PostgreSQL `14.3+` is supported. If multiple versions are installed locally, set `PGPORT` (and optionally `PGHOST`) explicitly. + +## I run `bin/dev` and it fails with `foreman` missing. + +Install foreman in the active Ruby: + +```bash +gem install foreman +``` + +## App boots but some pages fail + +Check: + +1. Mini dump restore completed without errors. +2. `rails db:migrate` and `rails db:seed` finished. +3. Redis and PostgreSQL are running. diff --git a/docs/getting-started.md b/docs/getting-started.md new file mode 100644 index 00000000..a2540fae --- /dev/null +++ b/docs/getting-started.md @@ -0,0 +1,62 @@ +# Getting Started + +This guide gets QUL running on a fresh machine with the minimum required data. + +## Prerequisites + +- Ruby `3.3.3` (required by `.ruby-version`) +- Node `18.x` preferred (`.node-version` is `18.0.0`) +- PostgreSQL `14.3+` +- Redis `7+` + +## Quick Setup + +```bash +git clone https://github.com/TarteelAI/quranic-universal-library.git +cd quranic-universal-library +``` + +Install Ruby gems and JS dependencies: + +```bash +gem install bundler -v 2.5.15 +bundle _2.5.15_ install +npm install +``` + +Create required databases: + +```bash +createdb quran_community_tarteel +createdb quran_dev +``` + +Load the mini development dump into `quran_dev`: + +- SQL: `https://static-cdn.tarteel.ai/qul/mini-dumps/mini_quran_dev.sql.zip` +- Binary: `https://static-cdn.tarteel.ai/qul/mini-dumps/mini_quran_dev.dump.zip` + +Then run: + +```bash +bundle exec rails db:migrate +bundle exec rails db:seed +``` + +Start the app: + +```bash +bin/dev +``` + +Open: + +- App: `http://localhost:3000` +- Admin: `http://localhost:3000/admin` + +## Notes + +- QUL uses two DBs: + - `quran_dev`: Quran content data (schema: `quran`) + - `quran_community_tarteel`: users, permissions, moderation/workflow data +- If your PostgreSQL runs on a non-default port, export `PGPORT` before running Rails commands. diff --git a/docs/tutorials.md b/docs/tutorials.md new file mode 100644 index 00000000..88be88b3 --- /dev/null +++ b/docs/tutorials.md @@ -0,0 +1,34 @@ +# Tutorials + +These quick tutorials are meant for new contributors. + +## 1) Run QUL Locally + +1. Follow [getting-started.md](getting-started.md). +2. Confirm app opens at `http://localhost:3000`. +3. Confirm admin opens at `http://localhost:3000/admin`. + +## 2) Verify Both Databases Are Connected + +Run: + +```bash +bundle exec rails runner 'puts ActiveRecord::Base.connection.current_database; puts Verse.connection.current_database' +``` + +Expected output should include: + +- `quran_community_tarteel` +- `quran_dev` + +## 3) Make a Safe Documentation Change + +1. Create a branch: + +```bash +git switch -c docs/your-topic +``` + +2. Edit files in `docs/`. +3. Add links in `docs/README.md` (and root `README.md` if needed). +4. Open a PR with a short summary and screenshots/output when relevant. From 7e1b56fea18c6b47fd9ee2d154e9e723f41d9548 Mon Sep 17 00:00:00 2001 From: umairliaqatali Date: Thu, 5 Mar 2026 09:27:21 +0500 Subject: [PATCH 02/12] docs: checkpoint docs pages, tutorials, and playground updates --- Procfile.dev | 2 +- README.md | 144 +------ app/assets/config/manifest.js | 7 +- .../stylesheets/application.tailwind.css | 63 ++- app/controllers/community_controller.rb | 27 ++ .../controllers/docs_code_controller.js | 175 ++++++++ app/javascript/controllers/index.js | 3 + app/services/docs_page_service.rb | 266 ++++++++++++ app/views/community/_docs_markdown.html.erb | 5 + app/views/community/docs.html.erb | 12 +- app/views/community/docs_index.html.erb | 15 + app/views/landing/_header.html.erb | 2 + app/views/shared/_nav.html.erb | 3 + bin/dev | 24 +- bin/dev-env | 16 + config/routes.rb | 1 + config/tailwind.config.js | 11 + docs/README.md | 40 +- docs/best-practices.md | 40 +- docs/contributing.md | 51 ++- docs/data-model.md | 48 +-- docs/datasets.md | 98 ++++- docs/downloading-data.md | 95 +++-- docs/faq.md | 41 +- docs/getting-started.md | 97 +++-- docs/resource-ayah-theme.md | 85 ++++ docs/resource-ayah-topics.md | 86 ++++ docs/resource-fonts.md | 53 +++ docs/resource-guide-template.md | 63 +++ docs/resource-guides-index.md | 30 ++ docs/resource-morphology.md | 53 +++ docs/resource-mushaf-layouts.md | 158 +++++++ docs/resource-mutashabihat.md | 81 ++++ docs/resource-quran-metadata.md | 53 +++ docs/resource-quran-script.md | 160 ++++++++ docs/resource-recitations.md | 170 ++++++++ docs/resource-similar-ayah.md | 85 ++++ docs/resource-surah-information.md | 52 +++ docs/resource-tafsirs.md | 148 +++++++ docs/resource-topics-and-concepts.md | 14 + docs/resource-translations.md | 153 +++++++ docs/resource-transliteration.md | 52 +++ docs/tutorial-ayah-theme-end-to-end.md | 188 +++++++++ docs/tutorial-ayah-topics-end-to-end.md | 189 +++++++++ docs/tutorial-font-end-to-end.md | 190 +++++++++ docs/tutorial-morphology-end-to-end.md | 256 ++++++++++++ docs/tutorial-mushaf-layout-end-to-end.md | 271 ++++++++++++ docs/tutorial-mutashabihat-end-to-end.md | 327 +++++++++++++++ docs/tutorial-quran-metadata-end-to-end.md | 191 +++++++++ docs/tutorial-quran-script-end-to-end.md | 269 ++++++++++++ docs/tutorial-recitation-end-to-end.md | 207 ++++++++++ docs/tutorial-similar-ayah-end-to-end.md | 339 +++++++++++++++ docs/tutorial-surah-information-end-to-end.md | 285 +++++++++++++ docs/tutorial-tafsir-end-to-end.md | 250 +++++++++++ docs/tutorial-translation-end-to-end.md | 292 +++++++++++++ docs/tutorial-transliteration-end-to-end.md | 180 ++++++++ docs/tutorials.md | 50 +-- yarn.lock | 387 ++++++++++-------- 58 files changed, 6140 insertions(+), 513 deletions(-) create mode 100644 app/javascript/controllers/docs_code_controller.js create mode 100644 app/services/docs_page_service.rb create mode 100644 app/views/community/_docs_markdown.html.erb create mode 100644 app/views/community/docs_index.html.erb create mode 100755 bin/dev-env create mode 100644 docs/resource-ayah-theme.md create mode 100644 docs/resource-ayah-topics.md create mode 100644 docs/resource-fonts.md create mode 100644 docs/resource-guide-template.md create mode 100644 docs/resource-guides-index.md create mode 100644 docs/resource-morphology.md create mode 100644 docs/resource-mushaf-layouts.md create mode 100644 docs/resource-mutashabihat.md create mode 100644 docs/resource-quran-metadata.md create mode 100644 docs/resource-quran-script.md create mode 100644 docs/resource-recitations.md create mode 100644 docs/resource-similar-ayah.md create mode 100644 docs/resource-surah-information.md create mode 100644 docs/resource-tafsirs.md create mode 100644 docs/resource-topics-and-concepts.md create mode 100644 docs/resource-translations.md create mode 100644 docs/resource-transliteration.md create mode 100644 docs/tutorial-ayah-theme-end-to-end.md create mode 100644 docs/tutorial-ayah-topics-end-to-end.md create mode 100644 docs/tutorial-font-end-to-end.md create mode 100644 docs/tutorial-morphology-end-to-end.md create mode 100644 docs/tutorial-mushaf-layout-end-to-end.md create mode 100644 docs/tutorial-mutashabihat-end-to-end.md create mode 100644 docs/tutorial-quran-metadata-end-to-end.md create mode 100644 docs/tutorial-quran-script-end-to-end.md create mode 100644 docs/tutorial-recitation-end-to-end.md create mode 100644 docs/tutorial-similar-ayah-end-to-end.md create mode 100644 docs/tutorial-surah-information-end-to-end.md create mode 100644 docs/tutorial-tafsir-end-to-end.md create mode 100644 docs/tutorial-translation-end-to-end.md create mode 100644 docs/tutorial-transliteration-end-to-end.md diff --git a/Procfile.dev b/Procfile.dev index 1de67517..220167bb 100644 --- a/Procfile.dev +++ b/Procfile.dev @@ -1,3 +1,3 @@ -web: bin/rails server -p 3000 +web: bin/rails server -p ${PORT:-3001} js: yarn build --reload tailwind: bin/rails tailwindcss:watch diff --git a/README.md b/README.md index a35b08d0..34cc627f 100644 --- a/README.md +++ b/README.md @@ -41,144 +41,18 @@ QUL is implemented using Ruby on Rails, and Active Admin for implementing the ad ## Documentation Start with the docs index: [docs/README.md](docs/README.md) +Website docs index: [https://qul.tarteel.ai/docs](https://qul.tarteel.ai/docs) + +Primary path for resource users: + - Getting Started: [docs/getting-started.md](docs/getting-started.md) -- Downloading Data: [docs/downloading-data.md](docs/downloading-data.md) -- Data Model: [docs/data-model.md](docs/data-model.md) +- Downloading and Using Data: [docs/downloading-data.md](docs/downloading-data.md) +- Resource Guides Index: [docs/resource-guides-index.md](docs/resource-guides-index.md) - Datasets: [docs/datasets.md](docs/datasets.md) +- Data Model: [docs/data-model.md](docs/data-model.md) - Tutorials: [docs/tutorials.md](docs/tutorials.md) -- Best Practices: [docs/best-practices.md](docs/best-practices.md) -- Contributing: [docs/contributing.md](docs/contributing.md) - FAQ: [docs/faq.md](docs/faq.md) -## Setup Guide -This guide will help you set up the QUL project on your local machine. Follow the steps below to get started. - -### Prerequisites -- **Ruby**: Version 3.3.3 -- **Rails**: Version 7.0.3 -- **RVM or rbenv**: For managing Ruby versions -- **PostgreSQL**: 14.3 or higher -- **Redis**: 7.0.0 or higher - -#### 1. Clone the repository -```bash -git clone git@github.com:TarteelAI/quranic-universal-library.git -cd quranic-universal-library -``` - -#### 2. Install Ruby and setup environment -```bash -rvm install 3.3.3 -rvm use 3.3.3 -rvm gemset create qul -rvm gemset use qul -``` - -#### 3. Install PostgreSQL -- **Ubuntu/Debian** -```bash -sudo apt-get update -sudo apt-get install postgresql postgresql-contrib libpq-dev -``` -- **MacOS** -```bash -brew install postgresql -``` -- **Windows** -Download and install the latest version of PostgreSQL from the [official website](https://www.postgresql.org/download/windows/). - -#### 4. Install Dependencies -```bash -gem install bundler -bundle install -``` - -* **Tip:** To prevent Bundler from reinstalling due to a version mismatch, you can install the specific version listed under `BUNDLED_WITH` in `Gemfile.lock` using the command `gem install bundler -v [version]` - -#### 5. Database Configuration - -**QUL requires two databases:** - -1. **`quran_dev`**: This database holds all Quranic data, including translations, tafsirs, audio etc. It's accessed through `quran_api_db_dev` for the development environment. -2. **`quran_community_tarteel`**: This database hold data about user accounts, permissions, and user generated content and change log about translations. - -#### 6. Create Databases -Create the **`quran_community_tarteel`** database for managing user content. -```bash -rails db:create -``` - -For **`quran_dev`** you can create it manually or change the database name to `quran_dev` for `development` group in database.yml file and run `rails db:create` again. - -#### 7. Load the data for **`quran_dev`** database -The `quran_dev` database dump is available in both SQL and binary formats. Follow the appropriate instructions below to restore the database. - -> **Mini Database Dump for Development** -> -> The database backup provided below contains a limited subset of data, specifically selected for local development and testing. It is not a full production database and is intended solely for development and contribution purposes. -> -> We **do not provide or share** the full database backup. This mini dump contains all the essential data required to run QUL locally or contribute new features. If you're working on a feature that need more data, please open an issue. -> If you're looking for specific resource, please visit our [Resources page](https://qul.tarteel.ai/resources/) to download it. - - -#### Restoring from SQL Dump -7.1 **Restore using SQL Dump:** -Download the [SQL dump file](https://static-cdn.tarteel.ai/qul/mini-dumps/mini_quran_dev.sql.zip) and restore it using -```bash - psql quran_dev < "path to sql dump file" -``` -If it didn't work try running the following command: -```bash -psql -U postgres -d quran_dev -f path/to/quran_dev.sql -``` - -7.2 **Restore using binary dump:** -Download the [Binary dump file](https://static-cdn.tarteel.ai/qul/mini-dumps/mini_quran_dev.dump.zip) and restore it using -```bash -pg_restore --host localhost --port 5432 --no-owner --no-privileges --no-tablespaces --no-acl --dbname quran_dev -v "path to binary dump file" -``` - -### 8. Run the migrations for **`quran_community_tarteel`** database -```ruby -rails db:migrate -rails db:migrate -rails db:seed -``` - -#### 9. Run the Application -```bash -bin/dev -``` - -🌟Insha`Allah! Your application should be up and running at time point! 🌟 - -You can now visit [http://localhost:3000](http://localhost:3000) in your browser to explore the app. - -🔐 Head over to the admin panel at [http://localhost:3000/admin](http://localhost:3000/admin) - -### 10. Contributing to QUL -We welcome contributions to enhance the QUL project! If you'd like to contribute, please follow these steps: - -10.1 **Fork the Repository:** -Click on the "Fork" button at the top right of this page to create your own copy of the repository. - -10.2 **Clone Your Fork:** -```bash - git clone https://github.com/your_username/quranic-universal-library.git -``` - -10.3. **Create a new feature branch:** - ```bash - git checkout -b making-qul - ``` -10.4 **Make Your Changes:** - -10.5 **Push Your Changes:** -```bash -git add . -git commit -m "Add a brief description of your changes" -git push origin your-feature-branch -``` -10.6 **Create a Pull Request:** +## Contributing -May Allah reward your efforts and make them beneficial for the community! 🤲 +Use [docs/contributing.md](docs/contributing.md) for the complete contribution flow and PR checklist. diff --git a/app/assets/config/manifest.js b/app/assets/config/manifest.js index e7cf5ccb..334bb41c 100644 --- a/app/assets/config/manifest.js +++ b/app/assets/config/manifest.js @@ -1,4 +1,9 @@ //= link_tree ../images //= link_tree ../fonts //= link_tree ../builds - +//= link application.css +//= link active_admin.css +//= link landing.css +//= link export.css +//= link tinymce_custom.css +//= link pdf.css diff --git a/app/assets/stylesheets/application.tailwind.css b/app/assets/stylesheets/application.tailwind.css index f86ea66d..11237f36 100644 --- a/app/assets/stylesheets/application.tailwind.css +++ b/app/assets/stylesheets/application.tailwind.css @@ -72,10 +72,10 @@ } .tw-docs pre { - @apply tw-p-4 tw-rounded-md tw-overflow-x-auto; - background-color: #ecf0f1; - font-size: 1.1em; - color: #2c3e50; + @apply tw-p-4 tw-overflow-x-auto; + background-color: #0f172a; + color: #e2e8f0; + font-size: 0.95rem; } .tw-docs code { @@ -85,6 +85,59 @@ color: #2c3e50; } + .tw-docs pre code { + @apply tw-p-0 tw-bg-transparent tw-rounded-none; + color: inherit; + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + } + + .tw-docs-code-block, + .tw-docs-playground { + @apply tw-my-6 tw-overflow-hidden tw-rounded-xl tw-border tw-border-gray-200 tw-bg-white tw-shadow-sm; + } + + .tw-docs-code-header { + @apply tw-flex tw-items-center tw-justify-between tw-gap-2 tw-bg-gray-900 tw-px-3 tw-py-2 tw-text-gray-100; + } + + .tw-docs-code-language { + @apply tw-text-xs tw-font-semibold tw-uppercase tw-tracking-wide; + } + + .tw-docs-code-actions { + @apply tw-flex tw-items-center tw-gap-2; + } + + .tw-docs-code-button { + @apply tw-rounded tw-border-0 tw-bg-gray-700 tw-px-2 tw-py-1 tw-text-xs tw-font-medium tw-text-gray-100 hover:tw-bg-gray-600; + } + + .tw-docs-playground-grid { + @apply tw-grid xl:tw-grid-cols-2; + } + + .tw-docs-playground-pane { + @apply tw-flex tw-flex-col tw-bg-white; + } + + .tw-docs-playground-pane + .tw-docs-playground-pane { + @apply tw-border-t tw-border-gray-200 xl:tw-border-l xl:tw-border-t-0; + } + + .tw-docs-playground-label { + @apply tw-bg-gray-50 tw-px-4 tw-py-2 tw-text-xs tw-font-semibold tw-uppercase tw-tracking-wide tw-text-gray-500; + } + + .tw-docs-playground-editor { + @apply tw-min-h-[340px] lg:tw-min-h-[420px] tw-w-full tw-resize-y tw-border-0 tw-p-4 tw-font-mono tw-text-[13px] tw-leading-6 focus:tw-ring-2 focus:tw-ring-blue-500; + tab-size: 2; + background: linear-gradient(#ffffff, #ffffff) padding-box, linear-gradient(to bottom, #f8fafc, #f1f5f9) border-box; + } + + .tw-docs-playground-preview { + @apply tw-min-h-[340px] lg:tw-min-h-[420px] tw-w-full tw-border-0 tw-bg-white; + } + .tw-docs .example { @apply tw-p-4 tw-rounded-lg tw-mt-3 tw-mb-3; background-color: #eafaf1; @@ -163,4 +216,4 @@ .contributor-logo:hover .logo { transform: scale(1.05); -} \ No newline at end of file +} diff --git a/app/controllers/community_controller.rb b/app/controllers/community_controller.rb index 56243c8b..44cd3daa 100644 --- a/app/controllers/community_controller.rb +++ b/app/controllers/community_controller.rb @@ -24,6 +24,27 @@ def stt_validation end def docs + docs_page_service = DocsPageService.new + @docs_page = docs_page_service.find(params[:key]) + + if @docs_page.present? + @docs_source = :markdown + elsif (@docs_tag = DownloadableResourceTag.find_by(slug: params[:key])) + @docs_source = :tag + elsif docs_partial_exists?(params[:key]) + @docs_source = :partial + @docs_partial = params[:key] + else + raise ActionController::RoutingError, "Not Found" + end + + render layout: false if request.xhr? + end + + def docs_index + @docs_page = DocsPageService.new.readme + raise ActionController::RoutingError, "Not Found" unless @docs_page.present? + render layout: false if request.xhr? end @@ -82,4 +103,10 @@ def authorize_access! def init_presenter @presenter = CommunityPresenter.new(self) end + + def docs_partial_exists?(key) + return false unless key.to_s.match?(/\A[a-z0-9_-]+\z/) + + lookup_context.exists?("docs/#{key}", [], true) + end end diff --git a/app/javascript/controllers/docs_code_controller.js b/app/javascript/controllers/docs_code_controller.js new file mode 100644 index 00000000..711a0c1e --- /dev/null +++ b/app/javascript/controllers/docs_code_controller.js @@ -0,0 +1,175 @@ +import { Controller } from "@hotwired/stimulus"; + +export default class extends Controller { + static targets = ["code", "copyButton", "editor", "preview"]; + static values = { + playground: Boolean, + language: String + }; + + connect() { + this.initialCode = this.currentCode(); + this.applyEditorFontFallbacks(); + if (this.playgroundValue && this.hasEditorTarget && this.hasPreviewTarget) { + this.run(); + } + } + + copy(event) { + const sourceButton = event?.currentTarget || this.copyButtonTarget; + this.copyToClipboard(this.currentCode()) + .then(() => this.flashButton(sourceButton, "Copied")) + .catch(() => this.flashButton(sourceButton, "Copy failed")); + } + + run(event) { + event?.preventDefault(); + if (!this.playgroundValue || !this.hasEditorTarget || !this.hasPreviewTarget) return; + + const language = (this.languageValue || "javascript").toLowerCase(); + if (language !== "javascript") { + this.previewTarget.srcdoc = this.unsupportedPreview(language); + return; + } + + const safeCode = this.editorTarget.value.replace(/<\/script/gi, "<\\/script"); + this.previewTarget.srcdoc = this.javascriptPreview(safeCode, this.stylesheetLinksMarkup()); + } + + reset(event) { + event?.preventDefault(); + if (!this.playgroundValue || !this.hasEditorTarget) return; + + this.editorTarget.value = this.initialCode || ""; + this.run(); + } + + currentCode() { + if (this.hasEditorTarget) return this.editorTarget.value; + if (this.hasCodeTarget) return this.codeTarget.textContent || ""; + return ""; + } + + applyEditorFontFallbacks() { + if (!this.hasEditorTarget) return; + + // Keep monospace behavior, but add Arabic-capable fallbacks so Quranic glyphs don't render as boxes. + this.editorTarget.style.fontFamily = "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', 'Noto Naskh Arabic', 'Amiri Quran', 'Scheherazade New', 'Geeza Pro', 'Arial Unicode MS', monospace"; + this.editorTarget.style.fontFeatureSettings = "\"liga\" 1, \"calt\" 1"; + } + + flashButton(button, message) { + if (!button) return; + + const originalLabel = button.dataset.originalLabel || button.textContent; + button.dataset.originalLabel = originalLabel; + button.textContent = message; + + if (button._docsCodeTimeout) clearTimeout(button._docsCodeTimeout); + button._docsCodeTimeout = setTimeout(() => { + button.textContent = originalLabel; + }, 1200); + } + + copyToClipboard(text) { + if (navigator.clipboard?.writeText) { + return navigator.clipboard.writeText(text); + } + + return new Promise((resolve, reject) => { + const element = document.createElement("textarea"); + element.value = text; + element.style.position = "fixed"; + element.style.opacity = "0"; + document.body.appendChild(element); + element.select(); + + try { + document.execCommand("copy"); + resolve(); + } catch (error) { + reject(error); + } finally { + element.remove(); + } + }); + } + + javascriptPreview(code, stylesheetLinksMarkup = "") { + return ` + + + + ${stylesheetLinksMarkup} + + + +
+ + +`; + } + + stylesheetLinksMarkup() { + const links = Array.from(document.querySelectorAll('link[rel="stylesheet"][href]')) + .map((link) => link.getAttribute("href")) + .filter(Boolean) + .map((href) => ``) + .join(""); + + return links; + } + + unsupportedPreview(language) { + return `

Preview for "${language}" is not supported yet.

`; + } +} diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js index 9f8b55f0..2ba4b48d 100644 --- a/app/javascript/controllers/index.js +++ b/app/javascript/controllers/index.js @@ -221,3 +221,6 @@ application.register("offcanvas-reset", OffcanvasResetController); import OffcanvasController from "./offcanvas_controller.js"; application.register("offcanvas", OffcanvasController); + +import DocsCodeController from "./docs_code_controller.js"; +application.register("docs-code", DocsCodeController); diff --git a/app/services/docs_page_service.rb b/app/services/docs_page_service.rb new file mode 100644 index 00000000..167179f3 --- /dev/null +++ b/app/services/docs_page_service.rb @@ -0,0 +1,266 @@ +require "redcarpet" +require "uri" +require "nokogiri" + +class DocsPageService + Page = Struct.new(:slug, :title, :html, keyword_init: true) + + DOCS_DIR = Rails.root.join("docs").freeze + SLUG_PATTERN = /\A[a-z0-9][a-z0-9-]*\z/ + ALLOWED_TAGS = %w[ + h1 h2 h3 h4 h5 h6 p br hr ul ol li pre code blockquote strong em a table thead tbody tr th td + div span button textarea iframe + ].freeze + ALLOWED_ATTRIBUTES = %w[ + href title class rel target type aria-label sandbox + data-controller data-action data-docs-code-target data-docs-code-language-value data-docs-code-playground-value + ].freeze + PLAYGROUND_LANGUAGE = "playground-js".freeze + + def initialize + @markdown = Redcarpet::Markdown.new( + Redcarpet::Render::HTML.new(filter_html: true, hard_wrap: true), + autolink: true, + fenced_code_blocks: true, + no_intra_emphasis: true, + strikethrough: true, + tables: true + ) + @sanitizer = Rails::Html::SafeListSanitizer.new + end + + def readme + render_file("README.md", slug: "index") + end + + def find(slug) + return nil unless valid_slug?(slug) + + render_file("#{slug}.md", slug: slug) + end + + private + + def render_file(filename, slug:) + path = resolve_path(filename) + return nil unless path&.file? + + markdown = path.read + html = @markdown.render(markdown) + html = rewrite_internal_links(html) + html = decorate_code_blocks(html) + html = @sanitizer.sanitize(html, tags: ALLOWED_TAGS, attributes: ALLOWED_ATTRIBUTES) + + Page.new( + slug: slug, + title: extract_title(markdown, slug), + html: html + ) + rescue Errno::ENOENT + nil + end + + def resolve_path(filename) + docs_root = DOCS_DIR.expand_path + candidate = docs_root.join(filename).cleanpath + docs_root_prefix = "#{docs_root}/" + return nil unless candidate.extname == ".md" + return nil unless candidate.to_s.start_with?(docs_root_prefix) + + candidate + end + + def valid_slug?(slug) + slug.is_a?(String) && slug.match?(SLUG_PATTERN) + end + + def extract_title(markdown, slug) + heading = markdown.each_line.find { |line| line.start_with?("# ") } + return heading.sub("# ", "").strip if heading.present? + + slug.to_s.humanize + end + + def rewrite_internal_links(html) + fragment = Nokogiri::HTML.fragment(html) + fragment.css("a[href]").each do |link| + href = link["href"].to_s.strip + link["href"] = rewrite_href(href) + end + fragment.to_html + end + + def decorate_code_blocks(html) + fragment = Nokogiri::HTML.fragment(html) + fragment.css("pre > code").each do |code_node| + pre_node = code_node.parent + next unless pre_node + + language = extract_code_language(code_node["class"]) + if language == PLAYGROUND_LANGUAGE + pre_node.replace(build_playground_block(fragment, code_node.text)) + else + pre_node.replace(build_code_block(fragment, pre_node, language)) + end + end + fragment.to_html + end + + def build_code_block(fragment, pre_node, language) + normalized_language = normalize_code_language(language) + cloned_pre = pre_node.dup(1) + cloned_pre["data-docs-code-target"] = "code" + cloned_code = cloned_pre.at_css("code") + if cloned_code && normalized_language != "text" + cloned_code["class"] = "language-#{normalized_language}" + end + + wrapper = Nokogiri::XML::Node.new("div", fragment) + wrapper["class"] = "tw-docs-code-block" + wrapper["data-controller"] = "docs-code" + wrapper.add_child(build_code_header(fragment, display_language(normalized_language), copy_only: true)) + wrapper.add_child(cloned_pre) + wrapper + end + + def build_playground_block(fragment, code_text) + wrapper = Nokogiri::XML::Node.new("div", fragment) + wrapper["class"] = "tw-docs-playground" + wrapper["data-controller"] = "docs-code" + wrapper["data-docs-code-playground-value"] = "true" + wrapper["data-docs-code-language-value"] = "javascript" + wrapper.add_child(build_code_header(fragment, "JavaScript Playground", copy_only: false)) + + grid = Nokogiri::XML::Node.new("div", fragment) + grid["class"] = "tw-docs-playground-grid" + + editor_pane = Nokogiri::XML::Node.new("div", fragment) + editor_pane["class"] = "tw-docs-playground-pane" + editor_pane.add_child(build_playground_label(fragment, "Editor")) + + editor = Nokogiri::XML::Node.new("textarea", fragment) + editor["class"] = "tw-docs-playground-editor" + editor["data-docs-code-target"] = "editor" + editor.content = code_text + editor_pane.add_child(editor) + + preview_pane = Nokogiri::XML::Node.new("div", fragment) + preview_pane["class"] = "tw-docs-playground-pane" + preview_pane.add_child(build_playground_label(fragment, "Preview")) + + preview = Nokogiri::XML::Node.new("iframe", fragment) + preview["class"] = "tw-docs-playground-preview" + preview["title"] = "Code preview" + preview["sandbox"] = "allow-scripts" + preview["data-docs-code-target"] = "preview" + preview_pane.add_child(preview) + + grid.add_child(editor_pane) + grid.add_child(preview_pane) + wrapper.add_child(grid) + wrapper + end + + def build_code_header(fragment, language_label, copy_only:) + header = Nokogiri::XML::Node.new("div", fragment) + header["class"] = "tw-docs-code-header" + + language = Nokogiri::XML::Node.new("span", fragment) + language["class"] = "tw-docs-code-language" + language.content = language_label + header.add_child(language) + + actions = Nokogiri::XML::Node.new("div", fragment) + actions["class"] = "tw-docs-code-actions" + actions.add_child(build_code_button(fragment, "Copy", "docs-code#copy", "copyButton")) + + unless copy_only + actions.add_child(build_code_button(fragment, "Run", "docs-code#run")) + actions.add_child(build_code_button(fragment, "Reset", "docs-code#reset")) + end + + header.add_child(actions) + header + end + + def build_code_button(fragment, text, action, target = nil) + button = Nokogiri::XML::Node.new("button", fragment) + button["type"] = "button" + button["class"] = "tw-docs-code-button" + button["data-action"] = action + button["data-docs-code-target"] = target if target.present? + button.content = text + button + end + + def build_playground_label(fragment, text) + label = Nokogiri::XML::Node.new("div", fragment) + label["class"] = "tw-docs-playground-label" + label.content = text + label + end + + def extract_code_language(class_name) + return "text" if class_name.blank? + + tokens = class_name.to_s.split(/\s+/) + explicit = tokens.find { |token| token.start_with?("language-") } + language = explicit ? explicit.delete_prefix("language-") : tokens.first + language.to_s.downcase + end + + def normalize_code_language(language) + case language.to_s.downcase + when "", "plain", "plaintext" + "text" + when "js", "node" + "javascript" + when "shell", "sh", "zsh" + "bash" + else + language.to_s.downcase + end + end + + def display_language(language) + case language + when "javascript" + "JavaScript" + when "python" + "Python" + when "ruby" + "Ruby" + when "bash" + "Shell" + when "json" + "JSON" + when "sql" + "SQL" + else + language.to_s.titleize + end + end + + def rewrite_href(href) + return href if href.blank? || href.start_with?("#") + + begin + uri = URI.parse(href) + return href if uri.scheme.present? || uri.host.present? + rescue URI::InvalidURIError + return href + end + + path_part = href.split(/[?#]/, 2).first.to_s + suffix = href.delete_prefix(path_part) + normalized = path_part.sub(%r{\A\./}, "").sub(%r{\Adocs/}, "") + + return href unless normalized.end_with?(".md") + + slug = normalized.delete_suffix(".md") + return "/docs#{suffix}" if slug.casecmp("README").zero? || slug.blank? + return href unless valid_slug?(slug) + + "/docs/#{slug}#{suffix}" + end +end diff --git a/app/views/community/_docs_markdown.html.erb b/app/views/community/_docs_markdown.html.erb new file mode 100644 index 00000000..c5e2f1bc --- /dev/null +++ b/app/views/community/_docs_markdown.html.erb @@ -0,0 +1,5 @@ +
+
+ <%= safe_html docs_page.html %> +
+
diff --git a/app/views/community/docs.html.erb b/app/views/community/docs.html.erb index cdfd1dc2..cedf0604 100644 --- a/app/views/community/docs.html.erb +++ b/app/views/community/docs.html.erb @@ -18,9 +18,11 @@ <% end %> - <% if tag = DownloadableResourceTag.find_by(slug: params[:key]) %> - <%= render partial: "docs/tag", locals: { tag: tag } %> - <% else %> - <%= render partial: "docs/#{params[:key]}" %> + <% if @docs_source == :markdown %> + <%= render "community/docs_markdown", docs_page: @docs_page %> + <% elsif @docs_source == :tag %> + <%= render partial: "docs/tag", locals: { tag: @docs_tag } %> + <% elsif @docs_source == :partial %> + <%= render partial: "docs/#{@docs_partial}" %> <% end %> - \ No newline at end of file + diff --git a/app/views/community/docs_index.html.erb b/app/views/community/docs_index.html.erb new file mode 100644 index 00000000..29b32ba2 --- /dev/null +++ b/app/views/community/docs_index.html.erb @@ -0,0 +1,15 @@ +
+ <% unless request.xhr? %> +
+
+

Documentation

+

QUL Documentation

+

+ Start here to download QUL resources and integrate them in your app. Contributor setup docs are available as a secondary track. +

+
+
+ <% end %> + + <%= render "community/docs_markdown", docs_page: @docs_page %> +
diff --git a/app/views/landing/_header.html.erb b/app/views/landing/_header.html.erb index e15645b6..3674ddb3 100644 --- a/app/views/landing/_header.html.erb +++ b/app/views/landing/_header.html.erb @@ -19,6 +19,7 @@
<%= link_to 'Resources', resources_path, class: 'tw-text-black hover:tw-text-[#46ac7a] tw-transition-colors tw-hidden md:tw-flex' %> <%= link_to 'Tools', tools_path, class: 'tw-text-black hover:tw-text-[#46ac7a] tw-transition-colors tw-hidden md:tw-flex' %> + <%= link_to 'Docs', docs_index_path, class: 'tw-text-black hover:tw-text-[#46ac7a] tw-transition-colors tw-hidden md:tw-flex' %> <%= link_to 'Community', 'https://discord.gg/HAcGh8mfmj', target: '_blank', class: 'tw-text-black hover:tw-text-[#46ac7a] tw-transition-colors tw-hidden md:tw-flex' %> <%= link_to 'FAQ', faq_path, class: 'tw-text-black hover:tw-text-[#46ac7a] tw-transition-colors tw-hidden md:tw-flex' %> <%= link_to 'https://github.com/TarteelAI/quranic-universal-library', class: 'tw-text-black hover:tw-text-[#46ac7a] tw-transition-colors tw-hidden md:tw-flex tw-items-center', target: '_blank' do %> @@ -65,6 +66,7 @@
<%= link_to 'Resources', resources_path, class: 'tw-text-black hover:tw-text-[#46ac7a] tw-transition-colors tw-text-lg' %> <%= link_to 'Tools', tools_path, class: 'tw-text-black hover:tw-text-[#46ac7a] tw-transition-colors tw-text-lg' %> + <%= link_to 'Docs', docs_index_path, class: 'tw-text-black hover:tw-text-[#46ac7a] tw-transition-colors tw-text-lg' %> <%= link_to 'Credits', credits_path, class: 'tw-text-black hover:tw-text-[#46ac7a] tw-transition-colors tw-text-lg' %> <%= link_to 'Community', 'https://discord.gg/HAcGh8mfmj', target: '_blank', class: 'tw-text-black hover:tw-text-[#46ac7a] tw-transition-colors tw-text-lg' %> <%= link_to 'FAQ', faq_path, class: 'tw-text-black hover:tw-text-[#46ac7a] tw-transition-colors tw-text-lg' %> diff --git a/app/views/shared/_nav.html.erb b/app/views/shared/_nav.html.erb index 613f7815..0a4cccde 100644 --- a/app/views/shared/_nav.html.erb +++ b/app/views/shared/_nav.html.erb @@ -17,6 +17,9 @@
  • Resources
  • +
  • + <%= link_to 'Docs', docs_index_path, class: 'tw-text-gray-700 tw-font-medium tw-no-underline tw-px-3 tw-py-2 tw-rounded hover:tw-bg-gray-200 tw-block' %> +
  • diff --git a/bin/dev b/bin/dev index ad72c7d5..0ad4d2fd 100755 --- a/bin/dev +++ b/bin/dev @@ -1,12 +1,30 @@ #!/usr/bin/env sh -if ! gem list foreman -i --silent; then +set -eu + +APP_ROOT="$(cd "$(dirname "$0")/.." && pwd)" +cd "$APP_ROOT" + +. "$APP_ROOT/bin/dev-env" + +if [ -f ".ruby-version" ]; then + REQUIRED_RUBY="$(cat .ruby-version)" + CURRENT_RUBY="$(ruby -e 'print RUBY_VERSION' 2>/dev/null || true)" + + if [ "${CURRENT_RUBY}" != "${REQUIRED_RUBY}" ]; then + echo "Error: expected Ruby ${REQUIRED_RUBY} for this repo, but found ${CURRENT_RUBY:-unknown}." + echo "Run with rbenv enabled, then retry bin/dev." + exit 1 + fi +fi + +if ! command -v foreman >/dev/null 2>&1; then echo "Installing foreman..." gem install foreman fi -# Default to port 3000 if not specified -export PORT="${PORT:-3000}" +# Default to port 3001 if not specified +export PORT="${PORT:-3001}" # Let the debug gem allow remote connections, # but avoid loading until `debugger` is called diff --git a/bin/dev-env b/bin/dev-env new file mode 100755 index 00000000..47cb2965 --- /dev/null +++ b/bin/dev-env @@ -0,0 +1,16 @@ +#!/usr/bin/env sh + +# Ensure rbenv is available even when shell init files were not loaded. +if [ -d "${HOME}/.rbenv" ]; then + export PATH="${HOME}/.rbenv/bin:${PATH}" +fi + +if command -v rbenv >/dev/null 2>&1; then + # Avoid hard failure from rehash under strict shells; fallback for older rbenv. + eval "$(rbenv init - --no-rehash 2>/dev/null || rbenv init -)" +fi + +# Enforce this repository's pinned Ruby for all bin/dev subprocesses. +if [ -f ".ruby-version" ] && command -v rbenv >/dev/null 2>&1; then + export RBENV_VERSION="$(cat .ruby-version)" +fi diff --git a/config/routes.rb b/config/routes.rb index 34a38952..cd85ac1e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -54,6 +54,7 @@ get 'tools', to: 'community#tools', as: :tools get :quran_scripts_comparison, to: 'quran_scripts_comparison#compare_words', as: :compare_words_quran_scripts_comparison get 'ayah-boundaries', to: 'community#ayah_boundaries', as: :ayah_boundaries + get 'docs', to: 'community#docs_index', as: :docs_index get 'docs/:key', to: 'community#docs', as: :docs get 'tools/help/:key', to: 'community#tool_help', as: :tools_help get 'community/chars_info', as: :chars_info diff --git a/config/tailwind.config.js b/config/tailwind.config.js index ea6b0bb9..44ee1a0e 100644 --- a/config/tailwind.config.js +++ b/config/tailwind.config.js @@ -40,6 +40,17 @@ module.exports = { 'tw-link-button', 'tw-docs', 'tw-docs *', + 'tw-docs-code-block', + 'tw-docs-code-header', + 'tw-docs-code-language', + 'tw-docs-code-actions', + 'tw-docs-code-button', + 'tw-docs-playground', + 'tw-docs-playground-grid', + 'tw-docs-playground-pane', + 'tw-docs-playground-label', + 'tw-docs-playground-editor', + 'tw-docs-playground-preview', 'tw-btn', 'tw-btn-sm', 'tw-btn-info', diff --git a/docs/README.md b/docs/README.md index 5001f066..a21b1566 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,19 +1,37 @@ # QUL Documentation -This documentation is optimized for first-time users and contributors who want to run QUL locally and understand how data flows through the project. +Use this documentation to discover, download, and integrate QUL resources in your own app or research workflow. -## Start Here +You do not need to run the QUL CMS locally to use most QUL datasets. + +The pages here are published on the website at `/docs` and mirrored in the repository for versioned updates. + +## Audience + +- Resource users downloading datasets from QUL. +- Developers and researchers integrating QUL datasets. +- Contributors preparing documentation and code pull requests. + +## Resource Users (Primary) 1. [Getting Started](getting-started.md) -2. [Downloading Data](downloading-data.md) -3. [Data Model](data-model.md) +2. [Downloading and Using Data](downloading-data.md) +3. [Resource Guides Index](resource-guides-index.md) 4. [Datasets](datasets.md) -5. [Tutorials](tutorials.md) -6. [Best Practices](best-practices.md) -7. [Contributing](contributing.md) -8. [FAQ](faq.md) +5. [Data Model](data-model.md) +6. [Tutorials](tutorials.md) +7. [FAQ](faq.md) -## Audience +## Documentation Maintenance (Secondary) + +1. [Resource Guide Template](resource-guide-template.md) +2. [Best Practices](best-practices.md) +3. [Contributing](contributing.md) + +## Primary Links -- New users: set up quickly and run the app with the mini dump. -- Developers: understand DB boundaries, workflows, and contribution paths. +- Project home: [https://qul.tarteel.ai/](https://qul.tarteel.ai/) +- Resources directory: [https://qul.tarteel.ai/resources](https://qul.tarteel.ai/resources) +- Source code: [https://github.com/TarteelAI/quranic-universal-library](https://github.com/TarteelAI/quranic-universal-library) +- Issues: [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) +- Community Discord: [https://discord.gg/HAcGh8mfmj](https://discord.gg/HAcGh8mfmj) diff --git a/docs/best-practices.md b/docs/best-practices.md index 3f12a0a6..494efaf3 100644 --- a/docs/best-practices.md +++ b/docs/best-practices.md @@ -1,20 +1,34 @@ # Best Practices -## For New Users +## Data and Schema -- Start with the mini dump, not custom data. -- Validate DB restore before debugging app code. -- Keep local setup notes (Ruby, PG port, env vars) for repeatability. +- Keep `surah_id`, `ayah_number`, and `word_position` as integers. +- Add indexes for `surah_id + ayah_number` in verse-level tables. +- Keep translations and tafsir in separate tables to support multiple sources and languages. +- Avoid duplicating large text blobs when ID-based references are enough. -## For Contributors +## Performance -- Use feature branches; avoid committing directly to `main`. -- Keep PRs focused (docs-only, migration-only, feature-only when possible). -- Write migration code to be idempotent when practical (`if_exists`, `if_not_exists`). -- If a migration touches `quran_dev`, verify state against existing dump schema. +- Cache frequently requested verse and tafsir payloads. +- Use SQLite/PostgreSQL for large datasets instead of in-memory-only workflows. +- Stream large JSON files instead of loading them entirely. +- Benchmark expensive joins on morphology/topic workloads. -## For Documentation +## Data Quality and Validation -- Prefer explicit commands over abstract instructions. -- Include likely failure cases and exact fixes. -- Keep new-user path first; put advanced details after. +- Validate required keys (`surah_id`, `ayah_number`, `word_position`) early. +- Keep a small verification script to test joins across selected resource categories. +- Confirm UTF-8 handling in your runtime, storage, and API outputs. +- Track dataset source/version in your metadata for traceability. + +## Documentation Quality + +- Keep setup commands copy/paste ready. +- Put new-user steps before advanced implementation details. +- Include likely failure cases and exact remediation commands. +- Keep GitHub markdown files as canonical content for website docs. + +## Change Management + +- Keep pull requests focused by concern (docs, integrations, feature). +- Add concise release notes when docs change user-facing resource workflows. diff --git a/docs/contributing.md b/docs/contributing.md index 451b4ba2..614d5c7f 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -1,35 +1,46 @@ # Contributing -## Basic Flow +Thanks for helping improve QUL. -1. Fork repository on GitHub. +## Where to Contribute + +- Repository: [https://github.com/TarteelAI/quranic-universal-library](https://github.com/TarteelAI/quranic-universal-library) +- Issues: [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) + +## Standard Flow + +1. Fork the repository. 2. Clone your fork. -3. Add upstream remote: +3. Add upstream remote. +4. Create a feature branch. +5. Make focused changes. +6. Push branch and open PR. ```bash +git clone https://github.com/YOUR-USERNAME/quranic-universal-library.git +cd quranic-universal-library git remote add upstream https://github.com/TarteelAI/quranic-universal-library.git +git switch -c docs/your-topic ``` -4. Create branch: +## Documentation Contributions -```bash -git switch -c docs/your-change -``` +- Edit files in `docs/` first (source of truth). +- Ensure links are valid in both GitHub and website docs rendering. +- Update root `README.md` only when entrypoint links or quick-start instructions change. -5. Make changes, commit, push, open PR to `TarteelAI/quranic-universal-library`. +## Reporting Data Issues -## Keep Your Fork Updated +When opening an issue for dataset problems, include: -```bash -git fetch upstream -git switch main -git merge upstream/main -git push origin main -``` +- Dataset URL and format (JSON/SQLite) +- Exact identifiers (`surah_id`, `ayah_number`, `word_position` when relevant) +- Expected vs actual output +- Minimal reproducible snippet -## Documentation PR Checklist +## PR Checklist -- Docs are clear for first-time users. -- All commands are copy-paste ready. -- Root `README.md` links to any new docs page. -- Spelling and link checks are done. +- Scope is clear and focused. +- Commands in docs were re-validated. +- New user onboarding path remains intact. +- Backward-compatible website docs behavior is preserved. diff --git a/docs/data-model.md b/docs/data-model.md index e15af162..3269437c 100644 --- a/docs/data-model.md +++ b/docs/data-model.md @@ -1,39 +1,33 @@ # Data Model -QUL is split across two PostgreSQL databases. +This page explains how resource users should model and join downloaded QUL datasets. -## 1) `quran_dev` +## Quran Hierarchy -Purpose: Quranic content and linguistic data. +Quran -> Surah -> Ayah -> Word -Key characteristics: +Common fields by level: -- Main schema: `quran` -- Large content tables: verses, words, translations, tafsirs, scripts, audio metadata -- Also includes grammar/morphology entities +- Surah: `surah_id`, names, revelation type, ayah count +- Ayah: `surah_id`, `ayah_number`, text, juz/hizb/manzil context +- Word: `surah_id`, `ayah_number`, `word_position`, text, root/lemma/POS -Representative tables: +## Core Shared Identifiers -- `quran.verses` -- `quran.words` -- `quran.translations` -- `quran.tafsirs` -- `quran.resource_contents` +- `surah_id` (or `surah`) +- `ayah_number` (or `ayah`) +- `word_position` (or `position`) -## 2) `quran_community_tarteel` +## Recommended Join Patterns -Purpose: app/community and editorial workflow. +- Translations: `surah_id + ayah_number` +- Tafsir: `surah_id + ayah_number` +- Topics/themes: `surah_id + ayah_number` +- Morphology: `surah_id + ayah_number + word_position` -Representative tables: +## Practical Integration Notes -- `users` -- `admin_users` -- `draft_translations` -- `draft_tafsirs` -- `resource_permissions` -- `versions` (change history) - -## Why Two Databases? - -- Separates canonical Quran content from community/editorial workflows. -- Makes operational boundaries clearer for backup, migration, and governance. +- Keep numeric identity fields as integers for stable joins. +- Normalize by resource type (script, translation, tafsir, morphology) instead of duplicating large text fields. +- Add indexes on common join keys before shipping production workloads. +- Validate key consistency on a sample batch before full import. diff --git a/docs/datasets.md b/docs/datasets.md index 07fed2fd..e1be5730 100644 --- a/docs/datasets.md +++ b/docs/datasets.md @@ -1,25 +1,93 @@ # Datasets -QUL development depends on the mini `quran_dev` dataset. +Use this page to choose the right QUL category before integration. -## What It Includes +Resources directory: [https://qul.tarteel.ai/resources](https://qul.tarteel.ai/resources) -- Core Quran structure (chapters, verses, words) -- Selected translations and tafsir metadata/content -- Script and mushaf-related data -- Audio-related metadata needed by the app -- Supporting lookup entities (languages, resources, tags, etc.) +## Quran Script -## What It Does Not Include +Arabic Quran text in multiple scripts and related representations. -- Full production dataset -- All historical or high-volume resources +- Link: [https://qul.tarteel.ai/resources/quran-script](https://qul.tarteel.ai/resources/quran-script) +- Typical use: Quran readers, script rendering, typography work +- Guide: [resource-quran-script.md](resource-quran-script.md) -If your feature needs more data coverage, open an issue in the project and describe the missing requirement. +## Translations -## Where to Download +Ayah-level and word-level translations across languages. -- Mini SQL dump: `mini_quran_dev.sql.zip` -- Mini binary dump: `mini_quran_dev.dump.zip` +- Link: [https://qul.tarteel.ai/resources/translation](https://qul.tarteel.ai/resources/translation) +- Typical use: multilingual readers, search, localization +- Guide: [resource-translations.md](resource-translations.md) -Both are linked in [downloading-data.md](downloading-data.md). +## Tafsir + +Tafsir datasets in multiple languages and styles. + +- Link: [https://qul.tarteel.ai/resources/tafsir](https://qul.tarteel.ai/resources/tafsir) +- Typical use: commentary panels, study tools +- Guide: [resource-tafsirs.md](resource-tafsirs.md) + +## Recitations + +Audio recitation data and segment timing. + +- Link: [https://qul.tarteel.ai/resources/recitation](https://qul.tarteel.ai/resources/recitation) +- Typical use: memorization tools, synchronized playback +- Guide: [resource-recitations.md](resource-recitations.md) + +## Morphology and Grammar + +Word-level linguistic features such as roots, lemmas, and POS tags. + +- Link: [https://qul.tarteel.ai/resources/morphology](https://qul.tarteel.ai/resources/morphology) +- Typical use: NLP pipelines, grammar exploration, word study +- Guide: [resource-morphology.md](resource-morphology.md) + +## Topics and Concepts + +Theme/topic tagging for ayahs and semantic exploration. + +- Link: [https://qul.tarteel.ai/resources/ayah-topics](https://qul.tarteel.ai/resources/ayah-topics) +- Typical use: thematic search and discovery +- Guide: [resource-topics-and-concepts.md](resource-topics-and-concepts.md) + +## Surah Information + +Contextual and descriptive metadata about surahs. + +- Link: [https://qul.tarteel.ai/resources/surah-info](https://qul.tarteel.ai/resources/surah-info) +- Typical use: educational views and chapter overviews +- Guide: [resource-surah-information.md](resource-surah-information.md) + +## Structural Metadata + +Navigation-oriented entities like juz, hizb, rub, and manzil. + +- Link: [https://qul.tarteel.ai/resources/quran-metadata](https://qul.tarteel.ai/resources/quran-metadata) +- Typical use: navigation UIs and indexing +- Guide: [resource-quran-metadata.md](resource-quran-metadata.md) + +## Transliteration + +Non-Arabic script/phonetic representation for pronunciation support. + +- Link: [https://qul.tarteel.ai/resources/transliteration](https://qul.tarteel.ai/resources/transliteration) +- Typical use: pronunciation support and beginner-friendly reading +- Guide: [resource-transliteration.md](resource-transliteration.md) + +## Fonts + +Quran-focused fonts and related usage assets. + +- Link: [https://qul.tarteel.ai/resources/font](https://qul.tarteel.ai/resources/font) +- Typical use: script rendering and web/app typography +- Guide: [resource-fonts.md](resource-fonts.md) + +## Mushaf Layouts + +Page-oriented layout resources for mushaf-style viewing. + +- Link: [https://qul.tarteel.ai/resources/mushaf-layout](https://qul.tarteel.ai/resources/mushaf-layout) +- Typical use: page-by-page Quran navigation and rendering +- Guide: [resource-mushaf-layouts.md](resource-mushaf-layouts.md) diff --git a/docs/downloading-data.md b/docs/downloading-data.md index a399ccae..93330f88 100644 --- a/docs/downloading-data.md +++ b/docs/downloading-data.md @@ -1,51 +1,84 @@ -# Downloading Data +# Downloading and Using Data -QUL development uses a **mini dump** (not full production data). +Use this guide when consuming QUL data in your own application or research pipeline. -## Available Dumps +Resources directory: [https://qul.tarteel.ai/resources](https://qul.tarteel.ai/resources) -- SQL dump zip: `https://static-cdn.tarteel.ai/qul/mini-dumps/mini_quran_dev.sql.zip` -- Binary dump zip: `https://static-cdn.tarteel.ai/qul/mini-dumps/mini_quran_dev.dump.zip` +Many resources are available in: -As of **July 28, 2025**: +- JSON: simple integration, scripts, prototypes +- SQLite: better for larger datasets and query-heavy workflows -- SQL zip size: ~`202 MB` -- Binary zip size: ~`131 MB` +SQLite docs: [https://www.sqlite.org/docs.html](https://www.sqlite.org/docs.html) -## Option A: SQL Restore +## Typical Integration Workflow -```bash -unzip mini_quran_dev.sql.zip -psql -d quran_dev -f mini_quran_dev.sql -``` +1. Select a dataset from `/resources`. +2. Download JSON or SQLite. +3. Inspect keys (`surah_id`, `ayah_number`, `word_position`, etc.). +4. Load into your runtime. +5. Import into your database for production workloads. +6. Add indexes on high-traffic query keys. + +## Load Examples -## Option B: Binary Restore +### JavaScript -```bash -unzip mini_quran_dev.dump.zip -pg_restore --no-owner --no-privileges --no-tablespaces --no-acl --dbname quran_dev -v mini_quran_dev.dump +```javascript +const fs = require("fs") +const data = JSON.parse(fs.readFileSync("data.json", "utf8")) +console.log(data[0]) ``` -## Recommended Choice +### Python + +```python +import json -- Use **binary dump** first (typically faster restore). -- If binary restore fails due to format/client mismatch, use **SQL dump**. +with open("data.json", "r", encoding="utf-8") as f: + data = json.load(f) -## Validation +print(data[0]) +``` + +### Ruby -After restore: +```ruby +require "json" -```bash -psql -d quran_dev -c "SELECT COUNT(*) FROM quran.verses;" +data = JSON.parse(File.read("data.json")) +puts data.first ``` -You should see a non-zero count. +## Performance Tips + +- Index by `surah_id` + `ayah_number`. +- For large datasets, prefer SQLite or PostgreSQL over in-memory JSON processing. +- Cache frequently accessed verses/tafsir payloads. +- Stream very large JSON files instead of loading full files into memory. ## Troubleshooting -- `unsupported version ... in file header`: - - Use a newer `pg_restore` client (or use SQL dump). -- `role ... does not exist` during SQL restore: - - Create the missing role, or edit dump ownership statements if needed. -- `schema "quran" does not exist`: - - Ensure the restore completed successfully. +- Download is slow/fails: retry with a stable connection and verify file size after download. +- Schema mismatch in your app: inspect fields first and map keys explicitly. +- Encoding issues: ensure UTF-8 handling in runtime and database. +- Memory issues on large files: prefer SQLite or stream JSON. + +## When to Request Updates or Changes + +Open a GitHub issue when: + +- download links are broken +- files are missing expected records +- key mappings are inconsistent +- you need coverage not available in current packages + +Include: + +- resource URL +- chosen format (JSON/SQLite) +- identifiers involved (`surah_id`, `ayah_number`, `word_position` if relevant) +- expected vs actual behavior +- a minimal reproducible snippet + +Issue tracker: [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) diff --git a/docs/faq.md b/docs/faq.md index 2a74bd60..9a0ad9e8 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -1,33 +1,38 @@ # FAQ -## Do I need the full production database? +## Is there an API? -No. Use the official **mini development dump**. Full production dump is not shared. +QUL primarily provides downloadable datasets. Integrators usually package data into their own API or database layer. -## Should I use SQL or binary dump? +## Do I need to clone this repository to use QUL resources? -Start with binary. If your `pg_restore` client cannot read it, use SQL dump. +No. Most users can work directly from downloadable resources at [https://qul.tarteel.ai/resources](https://qul.tarteel.ai/resources). -## Why do I see migration conflicts like duplicate columns/tables? +## Should I choose JSON or SQLite? -The dump can already contain schema changes that also exist as migrations. Validate actual schema state before applying destructive fixes. +- JSON: best for quick scripts and prototypes. +- SQLite: best for larger datasets and query-heavy applications. -## Which PostgreSQL version should I use? +## How do I join different resources together? -PostgreSQL `14.3+` is supported. If multiple versions are installed locally, set `PGPORT` (and optionally `PGHOST`) explicitly. +Start with shared identifiers: -## I run `bin/dev` and it fails with `foreman` missing. +- `surah_id` +- `ayah_number` +- `word_position` (for word-level resources) -Install foreman in the active Ruby: +See [data-model.md](data-model.md) for join guidance. -```bash -gem install foreman -``` +## What should I do if data looks wrong or missing? -## App boots but some pages fail +Open an issue with: -Check: +- resource URL +- format (JSON/SQLite) +- exact identifiers involved +- expected vs actual output +- minimal reproducible snippet -1. Mini dump restore completed without errors. -2. `rails db:migrate` and `rails db:seed` finished. -3. Redis and PostgreSQL are running. +## Can I use QUL data commercially? + +Check repository license terms and dataset-specific licensing details before production use. diff --git a/docs/getting-started.md b/docs/getting-started.md index a2540fae..9cde4a1f 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -1,62 +1,79 @@ # Getting Started -This guide gets QUL running on a fresh machine with the minimum required data. +This guide is for users who want to download QUL resources and use them in their own applications. -## Prerequisites +You can start here without cloning the repository or running QUL locally. -- Ruby `3.3.3` (required by `.ruby-version`) -- Node `18.x` preferred (`.node-version` is `18.0.0`) -- PostgreSQL `14.3+` -- Redis `7+` +## Before You Start (Important Clarifications) -## Quick Setup +- You usually do **not** need to clone the QUL repository to use resources. +- QUL is primarily a downloadable resource platform (not a hosted public API service). +- Resource schemas are related but not always identical, so inspect fields before integration. +- Use JSON for speed of integration and SQLite for larger, query-heavy workloads. -```bash -git clone https://github.com/TarteelAI/quranic-universal-library.git -cd quranic-universal-library -``` +## What You Can Build -Install Ruby gems and JS dependencies: +- Quran reader apps (Arabic + translation) +- Verse/topic search experiences +- Tafsir exploration features +- Word-by-word learning tools (root, lemma, POS) +- NLP/AI pipelines using structured Quran datasets -```bash -gem install bundler -v 2.5.15 -bundle _2.5.15_ install -npm install -``` +## 5-Minute Quick Start (Resource Users) -Create required databases: +1. Open the resources directory: [https://qul.tarteel.ai/resources](https://qul.tarteel.ai/resources) +2. Pick a dataset category (start with Quran Script or Translation). +3. Choose format: + - JSON for quick integration and scripts + - SQLite for larger datasets and query-heavy usage +4. Download the file(s). +5. Load data in your app and render one ayah. -```bash -createdb quran_community_tarteel -createdb quran_dev -``` +## Common Dataset Categories -Load the mini development dump into `quran_dev`: +- Quran Script: [https://qul.tarteel.ai/resources/quran-script](https://qul.tarteel.ai/resources/quran-script) +- Translations: [https://qul.tarteel.ai/resources/translation](https://qul.tarteel.ai/resources/translation) +- Tafsir: [https://qul.tarteel.ai/resources/tafsir](https://qul.tarteel.ai/resources/tafsir) +- Recitations: [https://qul.tarteel.ai/resources/recitation](https://qul.tarteel.ai/resources/recitation) +- Morphology: [https://qul.tarteel.ai/resources/morphology](https://qul.tarteel.ai/resources/morphology) +- Topics: [https://qul.tarteel.ai/resources/ayah-topics](https://qul.tarteel.ai/resources/ayah-topics) +- Metadata: [https://qul.tarteel.ai/resources/quran-metadata](https://qul.tarteel.ai/resources/quran-metadata) -- SQL: `https://static-cdn.tarteel.ai/qul/mini-dumps/mini_quran_dev.sql.zip` -- Binary: `https://static-cdn.tarteel.ai/qul/mini-dumps/mini_quran_dev.dump.zip` +## Minimal Load Example -Then run: +### JavaScript -```bash -bundle exec rails db:migrate -bundle exec rails db:seed +```javascript +const fs = require("fs") +const data = JSON.parse(fs.readFileSync("data.json", "utf8")) +console.log(data[0]) ``` -Start the app: +### Python + +```python +import json -```bash -bin/dev +with open("data.json", "r", encoding="utf-8") as f: + data = json.load(f) + +print(data[0]) ``` -Open: +## Identifiers to Learn First + +Most datasets can be joined using: + +- `surah_id` +- `ayah_number` +- `word_position` (word-level resources) -- App: `http://localhost:3000` -- Admin: `http://localhost:3000/admin` +For details, see [data-model.md](data-model.md). -## Notes +## Next Pages -- QUL uses two DBs: - - `quran_dev`: Quran content data (schema: `quran`) - - `quran_community_tarteel`: users, permissions, moderation/workflow data -- If your PostgreSQL runs on a non-default port, export `PGPORT` before running Rails commands. +- Detailed download and format guidance: [downloading-data.md](downloading-data.md) +- Category-by-category overview: [datasets.md](datasets.md) +- Per-resource step-by-step guides: [resource-guides-index.md](resource-guides-index.md) +- Practical implementations: [tutorials.md](tutorials.md) +- Common questions: [faq.md](faq.md) diff --git a/docs/resource-ayah-theme.md b/docs/resource-ayah-theme.md new file mode 100644 index 00000000..321434f9 --- /dev/null +++ b/docs/resource-ayah-theme.md @@ -0,0 +1,85 @@ +# Ayah Theme Guide + +This guide is for resource users who want concise thematic summaries for ayah groups. + +Category URL: + +- [https://qul.tarteel.ai/resources/ayah-theme](https://qul.tarteel.ai/resources/ayah-theme) + +## What This Resource Is + +Ayah Theme resources provide short theme statements linked to ayah ranges. + +Typical fields include: + +- `theme` +- `surah_number` +- `ayah_from` / `ayah_to` +- keywords/tags + +## When to Use It + +Use ayah-theme data for: + +- Passage summary banners +- Theme-first reading aids +- Study tools with contextual cues + +## How to Get Your First Example Resource + +1. Open [https://qul.tarteel.ai/resources/ayah-theme](https://qul.tarteel.ai/resources/ayah-theme). +2. Keep default listing order. +3. Open first published card. +4. Verify `Theme Preview` and `Help`. +5. Download available format (commonly `sqlite`). + +## What the Preview and Help Tabs Show + +- `Theme Preview`: + - Theme for selected ayah + - Group/range coverage notes +- `Help`: + - Column descriptions for theme and ranges + +Integration implication: + +- Resolve themes by ayah range inclusion, not exact key match only. + +## Download and Integration Checklist + +1. Import theme rows. +2. Build range-based resolver. +3. For each ayah, find matching theme row. +4. Render theme + keywords above ayah block. + +## Real-World Usage Example + +Goal: + +- Show current passage theme while user reads. + +Expected outcome: + +- Theme updates correctly when ayah crosses into a new range. + +## Common Mistakes + +- Matching themes only to single ayah IDs. +- Ignoring ayah range boundaries. + +## When to Request Updates or Changes + +Open an issue when: + +- range values are incorrect +- theme text/keywords are missing +- download links are broken + +Issue link: + +- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) + +## Related Pages + +- [Tutorial 14: Ayah Theme End-to-End](tutorial-ayah-theme-end-to-end.md) +- [Ayah Topics Guide](resource-ayah-topics.md) diff --git a/docs/resource-ayah-topics.md b/docs/resource-ayah-topics.md new file mode 100644 index 00000000..08b79264 --- /dev/null +++ b/docs/resource-ayah-topics.md @@ -0,0 +1,86 @@ +# Ayah Topics Guide + +This guide is for resource users who want to map topics/concepts to related ayahs. + +Category URL: + +- [https://qul.tarteel.ai/resources/ayah-topics](https://qul.tarteel.ai/resources/ayah-topics) + +## What This Resource Is + +Ayah Topics resources provide topic entities and topic-to-ayah mappings. + +Typical data includes: + +- Topic metadata (`topic_id`, name, category) +- Mapping records from topic to `ayah_key` +- Topic counts/searchable labels + +## When to Use It + +Use ayah-topics data for: + +- Topic-first Quran exploration +- Educational thematic pathways +- Concept search interfaces + +## How to Get Your First Example Resource + +1. Open [https://qul.tarteel.ai/resources/ayah-topics](https://qul.tarteel.ai/resources/ayah-topics). +2. Keep default listing order. +3. Open the first published resource card. +4. Verify topics pane and `Help` tab are present. +5. Download available format (commonly `sqlite`). + +## What the Preview and Help Tabs Show + +- Topics pane: + - Topic list + search + topic links +- `Help` tab: + - Topic source/context notes + - Mapping usage guidance + +Integration implication: + +- Keep topic metadata and mapping rows as separate layers. + +## Download and Integration Checklist + +1. Import topic rows. +2. Import topic-to-ayah mappings. +3. Index by `topic_id` and `ayah_key`. +4. Build topic search + topic detail endpoints. +5. Join mapped ayah keys with script/translation for display. + +## Real-World Usage Example + +Goal: + +- User selects a topic and sees related ayahs with text preview. + +Expected outcome: + +- Stable mapping between selected topic and listed ayahs. + +## Common Mistakes + +- Mixing topic IDs from different datasets without normalization. +- Assuming one topic maps to a single ayah. + +## When to Request Updates or Changes + +Open an issue when: + +- Topic-to-ayah mapping appears wrong +- Topic labels/categories are inconsistent +- Download links are broken + +Issue link: + +- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) + +## Related Pages + +- [Tutorial 10: Ayah Topics End-to-End](tutorial-ayah-topics-end-to-end.md) +- [Quran Script Guide](resource-quran-script.md) +- [Translations Guide](resource-translations.md) diff --git a/docs/resource-fonts.md b/docs/resource-fonts.md new file mode 100644 index 00000000..f5477301 --- /dev/null +++ b/docs/resource-fonts.md @@ -0,0 +1,53 @@ +# Quran Fonts Guide + +## What This Resource Is + +Quran Fonts resources provide fonts used for Quranic rendering workflows and script-specific display needs. + +Category URL: [https://qul.tarteel.ai/resources/font](https://qul.tarteel.ai/resources/font) + +## When to Use It + +- Rendering Quran text with specific typographic styles +- Supporting script-sensitive display in web/apps +- Matching visual style requirements across platforms + +## How to Download or Access It + +1. Open the category URL above. +2. Select a font package. +3. Download available font formats (`ttf`, `woff`, `woff2`, or similar). +4. Load font files into your app/web assets. + +## Step-by-Step Integration + +1. Place font files in your asset path. +2. Register font with `@font-face`. +3. Apply font family to Quran text components. +4. Validate glyph support for your target script content. +5. Test rendering on desktop/mobile browsers. + +## Real-World Usage Example + +Goal: use a dedicated Quran font in a web reader. + +Flow: + +1. Download font package. +2. Add `@font-face` in CSS. +3. Apply class to ayah text block. +4. Validate readability and line-height. + +Expected outcome: + +- Quran text renders with intended typography across key screens. + +## When to Request Updates or Changes + +Open an issue when: + +- Font files are missing/broken +- Glyph support appears incomplete +- Font metadata/docs are unclear + +Issue link: [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) diff --git a/docs/resource-guide-template.md b/docs/resource-guide-template.md new file mode 100644 index 00000000..54febbae --- /dev/null +++ b/docs/resource-guide-template.md @@ -0,0 +1,63 @@ +# Resource Guide Template + +Use this template when writing or updating any QUL resource documentation page. + +## 1) What This Resource Is + +- Short definition of the dataset/resource. +- Scope of data it includes. +- Typical formats available (JSON, SQLite, other files). + +## 2) When to Use It + +- Practical use cases. +- Who benefits most (app developers, educators, researchers, etc.). +- When another resource category may be a better fit. + +## 3) How to Download or Access It + +1. Open the resource category URL. +2. Select a specific resource package/version. +3. Choose format (JSON/SQLite/etc.). +4. Download and validate file integrity. +5. Inspect fields before integration. + +Include: + +- Direct category URL. +- Notes on format tradeoffs. +- Any known prerequisites. + +## 4) Step-by-Step Integration + +1. Load the file. +2. Validate required keys. +3. Store/import in your app database. +4. Add indexes for common lookup fields. +5. Build one minimal feature end-to-end. + +## 5) Real-World Usage Example + +Include one realistic mini scenario with: + +- Goal +- Required resources +- Data flow +- Expected output + +## 6) When to Request Updates or Changes + +Open an issue when: + +- Data appears incorrect or missing. +- Links or downloadable files are broken. +- Required fields are absent/inconsistent. +- You need additional resource coverage. + +Issue report should include: + +- Resource URL +- Format +- Exact identifiers (`surah_id`, `ayah_number`, `word_position` if relevant) +- Expected vs actual behavior +- Minimal reproducible snippet diff --git a/docs/resource-guides-index.md b/docs/resource-guides-index.md new file mode 100644 index 00000000..4f58cd9c --- /dev/null +++ b/docs/resource-guides-index.md @@ -0,0 +1,30 @@ +# Resource Guides Index + +Use these guides when your priority is downloading QUL resources and building with them. + +Guide coverage is aligned with categories visible on the official resources directory. + +## Core Guides + +1. [Quran Script Guide](resource-quran-script.md) +2. [Translations Guide](resource-translations.md) +3. [Tafsirs Guide](resource-tafsirs.md) +4. [Recitations Guide](resource-recitations.md) +5. [Quran Metadata Guide](resource-quran-metadata.md) +6. [Quran Fonts Guide](resource-fonts.md) +7. [Transliteration Guide](resource-transliteration.md) +8. [Surah Information Guide](resource-surah-information.md) +9. [Ayah Topics Guide](resource-ayah-topics.md) +10. [Morphology Guide](resource-morphology.md) +11. [Mutashabihat Guide](resource-mutashabihat.md) +12. [Similar Ayah Guide](resource-similar-ayah.md) +13. [Ayah Theme Guide](resource-ayah-theme.md) +14. [Mushaf Layouts Guide](resource-mushaf-layouts.md) + +## Legacy / Umbrella + +- [Topics and Concepts Guide (legacy umbrella)](resource-topics-and-concepts.md) + +## Official Resource Directory + +- [https://qul.tarteel.ai/resources](https://qul.tarteel.ai/resources) diff --git a/docs/resource-morphology.md b/docs/resource-morphology.md new file mode 100644 index 00000000..fb23b832 --- /dev/null +++ b/docs/resource-morphology.md @@ -0,0 +1,53 @@ +# Morphology Guide + +## What This Resource Is + +Morphology resources provide word-level linguistic analysis such as roots, lemmas, grammatical tags, and related structures. + +Category URL: [https://qul.tarteel.ai/resources/morphology](https://qul.tarteel.ai/resources/morphology) + +## When to Use It + +- Word study tools +- Arabic NLP research workflows +- Educational grammar features + +## How to Download or Access It + +1. Open the category URL above. +2. Select morphology package. +3. Download JSON or SQLite. +4. Validate word-level keys before import. + +## Step-by-Step Integration + +1. Import morphology rows with word-level keys. +2. Join with Quran Script by `surah_id + ayah_number + word_position`. +3. Store root/lemma/POS fields in normalized tables/collections. +4. Add indexes on ayah identity + word position. +5. Build per-word inspection UI in your app. + +## Real-World Usage Example + +Goal: create a "tap word for grammar details" feature. + +Flow: + +1. Render ayah words in order. +2. User taps a word token. +3. App queries morphology entry for matching ayah + position. +4. App displays root, lemma, and grammatical metadata. + +Expected outcome: + +- Users can inspect linguistic details word-by-word. + +## When to Request Updates or Changes + +Open an issue when: + +- Word positions do not align with ayah text +- Root/lemma fields are missing or malformed +- Grammar tags are inconsistent across rows + +Issue link: [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) diff --git a/docs/resource-mushaf-layouts.md b/docs/resource-mushaf-layouts.md new file mode 100644 index 00000000..01849d2e --- /dev/null +++ b/docs/resource-mushaf-layouts.md @@ -0,0 +1,158 @@ +# Mushaf Layouts Guide + +This guide is for resource users who want to download and integrate Mushaf layout datasets from QUL. + +Category URL: + +- [https://qul.tarteel.ai/resources/mushaf-layout](https://qul.tarteel.ai/resources/mushaf-layout) + +## What This Resource Is + +Mushaf Layout resources provide page-structured data used to render Quran pages in a mushaf-like format. + +You can expect: + +- Page-by-page line layout +- Line type metadata (`ayah`, `surah_name`, `basmallah`) +- Word ID ranges per ayah line +- Alignment metadata (`is_centered`) + +## When to Use It + +Use mushaf layout data when building: + +- Page-faithful Quran readers +- Page navigation flows (prev/next page, jump to page) +- Learning tools that depend on printed page structure + +## How to Get Your First Example Resource + +Use this repeatable selection rule: + +1. Open [https://qul.tarteel.ai/resources/mushaf-layout](https://qul.tarteel.ai/resources/mushaf-layout). +2. Keep the default listing order. +3. Open the first published resource card. +4. Verify the detail page has: + - `Mushaf Page Preview` tab + - `Help` tab +5. Confirm available download formats shown on page (commonly `images`, `sqlite`, `docx`). + +This keeps onboarding concrete without hardcoding a resource ID. + +## What the Preview and Help Tabs Show + +On the mushaf layout detail page: + +- `Mushaf Page Preview` tab: + - `Jump to page` selector + - Previous/next page navigation + - Rendered page output with line/word structure +- `Help` tab: + - Required resources for rendering (layout + script + font + surah names) + - `pages` table fields and meaning + - `words` table field expectations + - Sample rendering code + +Integration implication: + +- Use `pages` table as your rendering skeleton. +- Use `first_word_id`/`last_word_id` to map ayah lines to word text. + +## Download and Integration Checklist + +1. Download the selected package. +2. Inspect fields in the layout data: + - `page_number` + - `line_number` + - `line_type` + - `is_centered` + - `first_word_id`, `last_word_id` + - `surah_number` +3. Load compatible word-by-word Quran script data. +4. Build index maps: + - `wordsByIndex[word_index] -> text` + - `surahNameByNumber[surah_number] -> surah name` +5. Render each line by `line_type`: + - `surah_name` => render surah heading + - `basmallah` => render basmallah line + - `ayah` => render words from `first_word_id..last_word_id` +6. Apply line alignment using `is_centered`. +7. Validate on multiple pages (start, middle, end). + +Starter integration snippet (JavaScript): + +```javascript +const linesForPage = (pagesRows, pageNumber) => + pagesRows + .filter((row) => row.page_number === pageNumber) + .sort((a, b) => a.line_number - b.line_number); + +const wordsInRange = (wordsByIndex, firstWordId, lastWordId) => { + const words = []; + for (let id = firstWordId; id <= lastWordId; id += 1) { + if (wordsByIndex[id]) words.push(wordsByIndex[id].text); + } + return words.join(" "); +}; + +const renderLine = (line, wordsByIndex, surahNameByNumber) => { + if (line.line_type === "surah_name") return surahNameByNumber[line.surah_number] || ""; + if (line.line_type === "basmallah") return "﷽"; + if (line.line_type === "ayah") return wordsInRange(wordsByIndex, line.first_word_id, line.last_word_id); + return ""; +}; +``` + +## Real-World Usage Example + +Goal: + +- Render one complete page in a mushaf-like reader. + +Required resources: + +- Mushaf Layout package +- Quran Script package (word-by-word) +- Surah names metadata + +Flow: + +1. User opens page 1. +2. App loads lines for page 1 from layout data. +3. For each ayah line, app resolves words by `first_word_id..last_word_id`. +4. UI renders lines in order with centered/justified alignment. +5. User moves to next page and flow repeats. + +Expected outcome: + +- Page structure is visually consistent with selected mushaf layout. +- Text ordering and line boundaries remain stable. + +## Common Mistakes + +- Treating all lines as ayah lines and ignoring `line_type`. +- Using wrong key for word range mapping. +- Ignoring `is_centered` and breaking page appearance. +- Testing only one page and missing edge cases. +- Mixing incompatible script/font with selected layout. + +## When to Request Updates or Changes + +Open an issue when you find: + +- Incorrect line-to-word mapping +- Missing lines/pages or broken page navigation +- Download package inconsistencies +- Metadata conflicts between preview and downloaded data + +Issue link: + +- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) + +## Related Pages + +- [Tutorials](tutorials.md) +- [Quran Script Guide](resource-quran-script.md) +- [Fonts Guide](resource-fonts.md) +- [Quran Metadata Guide](resource-quran-metadata.md) +- [Downloading and Using Data](downloading-data.md) diff --git a/docs/resource-mutashabihat.md b/docs/resource-mutashabihat.md new file mode 100644 index 00000000..265beb8b --- /dev/null +++ b/docs/resource-mutashabihat.md @@ -0,0 +1,81 @@ +# Mutashabihat Guide + +This guide is for resource users who want to work with phrase-level Quran similarity mappings. + +Category URL: + +- [https://qul.tarteel.ai/resources/mutashabihat](https://qul.tarteel.ai/resources/mutashabihat) + +## What This Resource Is + +Mutashabihat resources map shared/similar phrases across ayahs. + +Common files/structures include: + +- `phrases.json` +- `phrase_verses.json` + +## When to Use It + +Use mutashabihat data for: + +- Memorization revision tools +- Similar phrase comparison features + +## How to Get Your First Example Resource + +1. Open [https://qul.tarteel.ai/resources/mutashabihat](https://qul.tarteel.ai/resources/mutashabihat). +2. Keep default listing order. +3. Open first published card. +4. Verify `Mutashabihat Preview` and `Help`. +5. Download available format (commonly `json`). + +## What the Preview and Help Tabs Show + +- `Mutashabihat Preview`: + - Ayah-based phrase relationship exploration +- `Help`: + - File structure and phrase-lookup flow + +Integration implication: + +- Resolve phrase IDs first, then fetch phrase objects. + +## Download and Integration Checklist + +1. Load phrase mapping file (`phrase_verses`). +2. Load phrase dictionary file (`phrases`). +3. For each ayah, resolve phrase IDs to phrase entries. +4. Optionally join with script words for highlighting. + +## Real-World Usage Example + +Goal: + +- Show similar phrases for selected ayah in revision mode. + +Expected outcome: + +- Users can compare phrase overlaps across ayahs. + +## Common Mistakes + +- Treating phrase mapping as direct text data. +- Ignoring missing phrase IDs or orphan entries. + +## When to Request Updates or Changes + +Open an issue when: + +- phrase IDs point to missing phrase entries +- phrase mapping appears incorrect +- download links are broken + +Issue link: + +- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) + +## Related Pages + +- [Tutorial 12: Mutashabihat End-to-End](tutorial-mutashabihat-end-to-end.md) +- [Similar Ayah Guide](resource-similar-ayah.md) diff --git a/docs/resource-quran-metadata.md b/docs/resource-quran-metadata.md new file mode 100644 index 00000000..f07fbfe4 --- /dev/null +++ b/docs/resource-quran-metadata.md @@ -0,0 +1,53 @@ +# Quran Metadata Guide + +## What This Resource Is + +Quran Metadata resources provide structural navigation entities such as juz, hizb, rub, manzil, and related Quran organization data. + +Category URL: [https://qul.tarteel.ai/resources/quran-metadata](https://qul.tarteel.ai/resources/quran-metadata) + +## When to Use It + +- Building Quran navigation menus +- Filtering by juz/hizb/manzil +- Creating learning journeys based on Quran structure + +## How to Download or Access It + +1. Open the category URL above. +2. Select metadata package. +3. Download JSON or SQLite. +4. Validate structural keys and ayah references. + +## Step-by-Step Integration + +1. Import metadata tables/collections. +2. Map structural entries to ayah ranges. +3. Add indexed lookups by structure number (`juz`, `hizb`, etc.). +4. Connect metadata filters to your ayah list query. +5. Test navigation from structure -> ayah list -> ayah detail. + +## Real-World Usage Example + +Goal: add "Browse by Juz" in a mobile app. + +Flow: + +1. Load juz records from metadata. +2. User selects Juz 30. +3. App resolves ayah range for selected juz. +4. App renders ayahs and supports jump-to-next-juz. + +Expected outcome: + +- Users navigate Quran structurally without manual ayah lookup. + +## When to Request Updates or Changes + +Open an issue when: + +- Structural ranges do not map correctly to ayahs +- Metadata entries are missing/incomplete +- Downloads are broken or out-of-date + +Issue link: [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) diff --git a/docs/resource-quran-script.md b/docs/resource-quran-script.md new file mode 100644 index 00000000..0a442931 --- /dev/null +++ b/docs/resource-quran-script.md @@ -0,0 +1,160 @@ +# Quran Script Guide + +This guide is for resource users who want to download and integrate Quran Script datasets from QUL. + +Category URL: + +- [https://qul.tarteel.ai/resources/quran-script](https://qul.tarteel.ai/resources/quran-script) + +## What This Resource Is + +Quran Script resources provide Arabic Quran text in script-aware formats (word-by-word or ayah-by-ayah). + +Depending on the selected package, entries can include: + +- `verse_key` in `surah:ayah` +- Verse text (`text`) +- Rendering metadata (`script_type`, `font_family`) +- Word arrays (`words[].position`, `words[].text`, `words[].location`) +- Navigation metadata (`page_number`, `juz_number`, `hizb_number`) + +## When to Use It + +Use Quran Script data when building: + +- Arabic Quran readers +- Word-by-word study views +- Integrations with translation, tafsir, and recitation by shared ayah keys + +## How to Get Your First Example Resource + +Use this stable selection rule: + +1. Open [https://qul.tarteel.ai/resources/quran-script](https://qul.tarteel.ai/resources/quran-script). +2. Keep the default listing order. +3. Open the first published resource card. +4. Verify the detail page includes: + - `Preview` tab + - `Help` tab +5. Confirm available download links: + - `sqlite` + - `json` +6. Confirm whether package is `Word by word` or `Ayah by ayah`. + +This keeps onboarding concrete without hardcoded resource IDs. + +## What the Preview and Help Tabs Show + +On the script detail page: + +- `Preview` tab: + - `Jump to Ayah` + - Previous/next ayah navigation + - Rendered Arabic output (word blocks in word-by-word packages) +- `Help` tab: + - Sample JSON + - Field descriptions (`verse_key`, `text`, `script_type`, `font_family`, `words`) + - Usage examples for CSS and JavaScript rendering + +Integration implication: + +- You should carry `font_family` into UI rendering rules. +- You should use `verse_key` and `words[].location` as canonical join keys. + +## Download and Integration Checklist + +1. Download script package (`json` or `sqlite`). +2. Normalize keys: + - Ayah key: `surah:ayah` + - Word key: `surah:ayah:word` +3. Index verse rows by `verse_key`. +4. If word data exists, sort words by `position` before rendering. +5. Render with RTL + script-aware font fallback. +6. Join with translation/tafsir/recitation by ayah key. +7. Validate full-surah rendering and random ayah checks. + +Starter integration snippet (JavaScript): + +```javascript +const buildVerseIndex = (rows) => + rows.reduce((index, row) => { + index[row.verse_key] = { + text: row.text, + scriptType: row.script_type, + fontFamily: row.font_family, + words: Array.isArray(row.words) ? row.words : [] + }; + return index; + }, {}); + +const renderVerse = (container, verse) => { + container.dir = "rtl"; + container.style.textAlign = "right"; + container.style.fontFamily = `${verse.fontFamily || "serif"}, "Amiri Quran", "Noto Naskh Arabic", serif`; + container.textContent = verse.text; +}; + +const renderWords = (container, words) => { + container.innerHTML = ""; + words + .slice() + .sort((a, b) => a.position - b.position) + .forEach((word) => { + const chip = document.createElement("span"); + chip.textContent = word.text; + chip.title = word.location; + chip.style.margin = "4px"; + chip.style.padding = "6px 10px"; + chip.style.border = "1px solid #e2e8f0"; + chip.style.borderRadius = "8px"; + container.appendChild(chip); + }); +}; +``` + +## Real-World Usage Example + +Goal: + +- Render one selected ayah as full text plus word-by-word blocks. + +Flow: + +1. User selects ayah key. +2. App loads script row by `verse_key`. +3. App renders full verse text. +4. App renders `words[]` sorted by `position`. + +Expected outcome: + +- Arabic text is correct and readable. +- Word order is stable. +- Keys remain compatible with downstream joins. + +## Common Mistakes + +- Ignoring `font_family` and assuming script text itself is wrong. +- Joining with other datasets by row position instead of ayah key. +- Ignoring `words[].position` in word-by-word rendering. +- Mixing word-by-word assumptions into ayah-by-ayah resources. + +## When to Request Updates or Changes + +Open an issue when you find: + +- Missing or incorrect `verse_key` rows +- Broken json/sqlite links +- Incorrect word order or missing `words[].location` +- Inconsistent `script_type` / `font_family` metadata + +Issue link: + +- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) + +## Related Pages + +- [Tutorials](tutorials.md) +- [Tutorial 5: Quran Script End-to-End](tutorial-quran-script-end-to-end.md) +- [Translations Guide](resource-translations.md) +- [Tafsirs Guide](resource-tafsirs.md) +- [Downloading and Using Data](downloading-data.md) diff --git a/docs/resource-recitations.md b/docs/resource-recitations.md new file mode 100644 index 00000000..6f53f8a2 --- /dev/null +++ b/docs/resource-recitations.md @@ -0,0 +1,170 @@ +# Recitations Guide + +This guide is for resource users who want to download and integrate Quran recitation datasets from QUL. + +Category URL: + +- [https://qul.tarteel.ai/resources/recitation](https://qul.tarteel.ai/resources/recitation) + +## What This Resource Is + +Recitation resources provide Quran audio plus optional timing metadata that can be used for synchronized highlighting. + +From the resource preview/help flow, you should expect one of these patterns: + +- Surah-by-surah audio (one continuous file per surah) +- Ayah-by-ayah audio (one file per ayah) +- Segment arrays/timestamps for fine-grained sync + +## When to Use It + +Use recitation data when building: + +- Quran playback experiences +- Memorization and revision apps +- Interfaces that highlight ayah or words in sync with audio + +## How to Get Your First Example Resource + +Use this stable, repeatable selection rule: + +1. Open [https://qul.tarteel.ai/resources/recitation](https://qul.tarteel.ai/resources/recitation). +2. Keep the default listing order. +3. Open the first published resource card. +4. Verify the detail page has: + - `Recitation` preview tab + - `Help` tab with format samples +5. Download the available format (`JSON`, `SQLite`, or both). + +This keeps onboarding concrete without hardcoding a resource ID. + +## What the Preview and Help Tabs Show + +On the recitation detail page: + +- `Recitation` tab: + - Ayah picker (`Jump to Ayah`) + - Previous/next ayah navigation + - Audio player and highlight behavior driven by timing data +- `Help` tab: + - Surah-by-surah vs ayah-by-ayah recitation explanation + - Segment format examples and timestamp structures + +Integration implication: + +- If segment data exists, implement synchronized highlight mode. +- If segment data is absent, support audio playback without forced highlighting. + +## Download and Integration Checklist + +1. Download the package. +2. Inspect fields in the downloaded file: + - Ayah identity (`surah`, `ayah`, or `surah:ayah`) + - Audio pointer (`audio_url`, file path, or equivalent) + - Timing fields when present (`segments`, `timestamp_from`, `timestamp_to`, duration) +3. Normalize keys before joins: + - Recommended canonical key: `ayah_key = "#{surah}:#{ayah}"` +4. Load matching Quran text from [Quran Script Guide](resource-quran-script.md). +5. Join recitation rows and Quran text rows by ayah key. +6. Build a minimal playback sequence: + - Start audio + - Read current playback timestamp + - Resolve active ayah or segment + - Update highlight state +7. Validate on a full surah, not only one ayah. + +Starter integration snippet (JavaScript): + +```javascript +const normalizeAyahKey = (row) => row.ayah_key || `${row.surah}:${row.ayah}`; + +const buildTimingWindows = (recitationRows) => + recitationRows.map((row) => ({ + ayahKey: normalizeAyahKey(row), + from: Number(row.timestamp_from ?? 0), + to: Number(row.timestamp_to ?? 0), + segments: Array.isArray(row.segments) ? row.segments : [], + audioUrl: row.audio_url + })); + +const findActiveAyah = (timings, currentTime) => + timings.find((t) => currentTime >= t.from && currentTime < t.to) || null; + +const scriptRowByKey = new Map(scriptRows.map((row) => [`${row.surah}:${row.ayah}`, row])); +const timings = buildTimingWindows(recitationRows); + +audioElement.addEventListener("timeupdate", () => { + const active = findActiveAyah(timings, audioElement.currentTime); + if (!active) return; + + const ayahText = scriptRowByKey.get(active.ayahKey); + updateHighlightedAyah(active.ayahKey, ayahText); +}); +``` + +## Real-World Usage Example + +Goal: + +- Play a full surah while highlighting the currently recited ayah. + +Required resources: + +- Recitation package +- Quran Script package + +Flow: + +1. User chooses a surah. +2. App loads all ayahs for that surah from Quran Script. +3. App loads recitation timing map for the same surah. +4. During playback, app maps player time to the active ayah window. +5. UI highlights the active ayah and moves as playback progresses. + +Expected outcome: + +- Audio and text progression stay synchronized. +- Highlight transitions are stable and not delayed/jumping. + +Sample input/output for one ayah: + +```json +{ + "input": { + "recitation_row": { "surah": 1, "ayah": 2, "timestamp_from": 2.0, "timestamp_to": 5.0 }, + "script_row": { "surah": 1, "ayah": 2, "text": "Alhamdulillahi Rabbil Alamin" }, + "player_time": 3.4 + }, + "output": { + "active_ayah_key": "1:2", + "highlighted_text": "Alhamdulillahi Rabbil Alamin" + } +} +``` + +## Common Mistakes + +- Assuming every recitation is segmented. +- Joining by row order instead of ayah identity. +- Mixing key styles without normalization. +- Ignoring missing or null timing fields. +- Testing only one ayah and skipping full-surah validation. + +## When to Request Updates or Changes + +Open an issue when you find: + +- Broken audio links or inaccessible files +- Timestamp/segment windows that do not match audible recitation +- Missing ayah mappings in the exported package +- Reciter metadata inconsistencies + +Issue link: + +- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) + +## Related Pages + +- [Tutorials](tutorials.md) +- [Downloading and Using Data](downloading-data.md) +- [Data Model](data-model.md) diff --git a/docs/resource-similar-ayah.md b/docs/resource-similar-ayah.md new file mode 100644 index 00000000..c17a1a38 --- /dev/null +++ b/docs/resource-similar-ayah.md @@ -0,0 +1,85 @@ +# Similar Ayah Guide + +This guide is for resource users who want to integrate ayah-level similarity rankings. + +Category URL: + +- [https://qul.tarteel.ai/resources/similar-ayah](https://qul.tarteel.ai/resources/similar-ayah) + +## What This Resource Is + +Similar Ayah resources map each ayah to other ayahs with similarity metrics. + +Typical fields: + +- `verse_key` +- `matched_ayah_key` +- `matched_words` +- `coverage` +- `similarity_score` + +## When to Use It + +Use similar-ayah data for: + +- Compare ayah features +- Pattern discovery tools +- Memorization reinforcement + +## How to Get Your First Example Resource + +1. Open [https://qul.tarteel.ai/resources/similar-ayah](https://qul.tarteel.ai/resources/similar-ayah). +2. Keep default listing order. +3. Open first published card. +4. Verify `Similar Ayah Preview` and `Help`. +5. Download `json`/`sqlite`. + +## What the Preview and Help Tabs Show + +- `Similar Ayah Preview`: + - Similar ayah list for selected ayah +- `Help`: + - Similarity field definitions + +Integration implication: + +- Sort and interpret by score/coverage, not only raw match count. + +## Download and Integration Checklist + +1. Import similarity rows. +2. Group rows by `verse_key`. +3. Sort by `similarity_score`. +4. Join with script text for display context. + +## Real-World Usage Example + +Goal: + +- Show top 5 similar ayahs for selected ayah. + +Expected outcome: + +- Ranked matches with interpretable metrics. + +## Common Mistakes + +- Treating score as strict equivalence. +- Ignoring coverage and matched-word context. + +## When to Request Updates or Changes + +Open an issue when: + +- score/coverage values appear invalid +- key mapping looks incorrect +- downloads are broken + +Issue link: + +- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) + +## Related Pages + +- [Tutorial 13: Similar Ayah End-to-End](tutorial-similar-ayah-end-to-end.md) +- [Mutashabihat Guide](resource-mutashabihat.md) diff --git a/docs/resource-surah-information.md b/docs/resource-surah-information.md new file mode 100644 index 00000000..1bb78acb --- /dev/null +++ b/docs/resource-surah-information.md @@ -0,0 +1,52 @@ +# Surah Information Guide + +## What This Resource Is + +Surah Information resources provide chapter-level contextual data such as names, summaries, and descriptive metadata. + +Category URL: [https://qul.tarteel.ai/resources/surah-info](https://qul.tarteel.ai/resources/surah-info) + +## When to Use It + +- Surah overview screens +- Intro cards before reading a chapter +- Study features that explain chapter context + +## How to Download or Access It + +1. Open the category URL above. +2. Select a surah-info package. +3. Download JSON or SQLite. +4. Validate chapter identity fields and language/source metadata. + +## Step-by-Step Integration + +1. Import surah-level metadata. +2. Join on chapter identity (`surah_id` / surah number). +3. Store localized titles/descriptions where available. +4. Cache chapter metadata for fast loading. +5. Render surah header cards in your reader app. + +## Real-World Usage Example + +Goal: show a surah introduction before ayah list. + +Flow: + +1. User opens Surah 36. +2. App fetches surah-info for that surah. +3. App displays chapter title and summary before ayahs. + +Expected outcome: + +- Users get context before reading. + +## When to Request Updates or Changes + +Open an issue when: + +- Surah title or summary appears incorrect +- Language versions are missing or mismatched +- Surah identifiers are inconsistent + +Issue link: [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) diff --git a/docs/resource-tafsirs.md b/docs/resource-tafsirs.md new file mode 100644 index 00000000..97f51ffd --- /dev/null +++ b/docs/resource-tafsirs.md @@ -0,0 +1,148 @@ +# Tafsirs Guide + +This guide is for resource users who want to download and integrate tafsir datasets from QUL. + +Category URL: + +- [https://qul.tarteel.ai/resources/tafsir](https://qul.tarteel.ai/resources/tafsir) + +## What This Resource Is + +Tafsir resources provide ayah-linked commentary. A tafsir entry can apply to one ayah or a group of ayahs. + +Depending on the package, you may get: + +- JSON grouped tafsir mapping by ayah key +- SQLite rows with group reference fields +- Tafsir text that may include simple HTML formatting + +## When to Use It + +Use tafsir data when building: + +- Ayah detail views with commentary +- Study and reflection panels +- Comparative tafsir reading workflows + +## How to Get Your First Example Resource + +Use this stable selection rule: + +1. Open [https://qul.tarteel.ai/resources/tafsir](https://qul.tarteel.ai/resources/tafsir). +2. Keep the default listing order. +3. Open the first published resource card. +4. Verify the detail page includes: + - `Tafsir Preview` tab + - `Help` tab +5. Confirm available downloads: + - `json` + - `sqlite` + +This keeps onboarding concrete without hardcoding a resource ID. + +## What the Preview and Help Tabs Show + +On the tafsir detail page: + +- `Tafsir Preview` tab: + - `Jump to Ayah` + - Previous/next ayah navigation + - Arabic text + tafsir text +- `Help` tab: + - JSON grouped export example + - Explanation of object vs pointer values by ayah key + - SQLite columns including `group_ayah_key`, `from_ayah`, `to_ayah`, `ayah_keys` + +Integration implication: + +- Tafsir can be shared across ayah ranges. +- Your resolver must follow grouped references. + +## Download and Integration Checklist + +1. Download the tafsir package (`json` or `sqlite`). +2. Normalize ayah keys to one format (`surah:ayah`). +3. Implement grouped tafsir resolution: + - JSON object value = main tafsir text + - JSON string value = pointer to main tafsir ayah key +4. For SQLite exports: + - If `text` is empty, resolve via `group_ayah_key` +5. Join with Quran Script by ayah key. +6. Render tafsir as optional panel/expandable section. +7. Validate ayah-range/grouped entries with adjacent ayahs. + +Starter integration snippet (JavaScript): + +```javascript +const resolveTafsir = (tafsirByKey, ayahKey) => { + const value = tafsirByKey[ayahKey]; + if (!value) return null; + + if (typeof value === "object" && value.text) { + return { + groupAyahKey: ayahKey, + text: value.text, + ayahKeys: Array.isArray(value.ayah_keys) ? value.ayah_keys : [ayahKey] + }; + } + + if (typeof value === "string") { + const main = tafsirByKey[value]; + if (main && typeof main === "object" && main.text) { + return { + groupAyahKey: value, + text: main.text, + ayahKeys: Array.isArray(main.ayah_keys) ? main.ayah_keys : [value] + }; + } + } + + return null; +}; +``` + +## Real-World Usage Example + +Goal: + +- Show tafsir in an ayah detail panel, even when tafsir is grouped across multiple ayahs. + +Flow: + +1. User opens ayah key. +2. App looks up tafsir payload for that ayah key. +3. If record is grouped/pointer, app resolves main tafsir text. +4. App renders commentary and covered ayah range. + +Expected outcome: + +- No missing tafsir when the selected ayah belongs to a group. +- Commentary remains correctly mapped to its ayah coverage. + +## Common Mistakes + +- Treating every ayah key as standalone tafsir text. +- Ignoring pointer/group behavior in JSON exports. +- Ignoring `group_ayah_key` in SQLite rows. +- Rendering raw HTML tags without sanitization in production. + +## When to Request Updates or Changes + +Open an issue when you find: + +- Broken grouped references or missing target keys +- Tafsir text mapped to wrong ayah range +- Downloaded files missing expected grouped fields +- Broken json/sqlite download links + +Issue link: + +- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) + +## Related Pages + +- [Tutorials](tutorials.md) +- [Tutorial 4: Tafsir End-to-End](tutorial-tafsir-end-to-end.md) +- [Quran Script Guide](resource-quran-script.md) +- [Translations Guide](resource-translations.md) +- [Downloading and Using Data](downloading-data.md) diff --git a/docs/resource-topics-and-concepts.md b/docs/resource-topics-and-concepts.md new file mode 100644 index 00000000..1f0516fe --- /dev/null +++ b/docs/resource-topics-and-concepts.md @@ -0,0 +1,14 @@ +# Topics and Concepts Guide (Legacy Umbrella) + +This page is kept for compatibility and points to the split guides used by the current tutorials. + +Use these dedicated guides: + +1. [Ayah Topics Guide](resource-ayah-topics.md) +2. [Mutashabihat Guide](resource-mutashabihat.md) +3. [Similar Ayah Guide](resource-similar-ayah.md) +4. [Ayah Theme Guide](resource-ayah-theme.md) + +Official directory: + +- [https://qul.tarteel.ai/resources](https://qul.tarteel.ai/resources) diff --git a/docs/resource-translations.md b/docs/resource-translations.md new file mode 100644 index 00000000..174c5711 --- /dev/null +++ b/docs/resource-translations.md @@ -0,0 +1,153 @@ +# Translations Guide + +This guide is for resource users who want to download and integrate Quran translation datasets from QUL. + +Category URL: + +- [https://qul.tarteel.ai/resources/translation](https://qul.tarteel.ai/resources/translation) + +## What This Resource Is + +Translation resources provide translated text keyed by ayah, with multiple export structures. + +Depending on the selected package, you may get: + +- Simple translation text (`simple.json`, `simple.sqlite`) +- Translation with footnote tags +- Translation with inline footnotes +- Translation text chunks for structured rendering + +## When to Use It + +Use translation data when building: + +- Multilingual Quran readers +- Arabic + translation learning experiences +- Search and discovery features in non-Arabic languages + +## How to Get Your First Example Resource + +Use this stable selection rule: + +1. Open [https://qul.tarteel.ai/resources/translation](https://qul.tarteel.ai/resources/translation). +2. Keep the default listing order. +3. Open the first published resource card. +4. Verify the detail page has: + - `Translation Preview` tab + - `Help` tab +5. Confirm available download links (`simple.json`, `simple.sqlite`, and optional footnote/chunk variants). + +This keeps onboarding concrete without hardcoding a resource ID. + +## What the Preview and Help Tabs Show + +On the translation detail page: + +- `Translation Preview` tab: + - `Jump to Ayah` + - Previous/next ayah navigation + - Arabic ayah display + translation display +- `Help` tab: + - JSON/SQLite export overview + - Simple export structures (nested array, key-value) + - Footnote exports (tags, inline, chunks) + +Integration implication: + +- Simple format works for plain translation rendering. +- Footnote/chunk formats are better when annotation fidelity matters. + +## Download and Integration Checklist + +1. Download your selected translation package. +2. Identify the payload shape for each ayah: + - String (simple translation) + - Object (`t` + `f`) for tagged footnotes + - Chunk array +3. Normalize ayah keys to one format (recommended: `surah:ayah`). +4. Load Quran Script rows for Arabic text from [Quran Script Guide](resource-quran-script.md). +5. Join Arabic + translation by ayah key. +6. Render translation with format-aware logic. +7. Validate on consecutive ayahs, not just one sample ayah. + +Starter integration snippet (JavaScript): + +```javascript +const toAyahKey = (row) => row.ayah_key || `${row.surah}:${row.ayah}`; + +const buildTranslationIndex = (rows) => + rows.reduce((index, row) => { + index[toAyahKey(row)] = row.translation; + return index; + }, {}); + +const normalizeTranslation = (payload) => { + if (typeof payload === "string") return { text: payload, notes: [] }; + + if (payload && typeof payload === "object" && payload.t) { + const ids = []; + const text = payload.t.replace(/([^<]+)<\/sup>/g, (_, id, label) => { + ids.push(id); + return `[${label}]`; + }); + return { text, notes: ids.map((id) => ({ id, text: payload.f?.[id] || "" })) }; + } + + if (Array.isArray(payload)) { + const parts = []; + payload.forEach((chunk) => { + if (typeof chunk === "string") parts.push(chunk); + else if (chunk?.type === "i") parts.push(chunk.text); + else if (chunk?.type === "f") parts.push(`[${chunk.text}]`); + }); + return { text: parts.join(""), notes: [] }; + } + + return { text: "", notes: [] }; +}; +``` + +## Real-World Usage Example + +Goal: + +- Display one ayah with Arabic text and translation, including footnotes when available. + +Flow: + +1. User selects an ayah key. +2. App loads Arabic text from Quran Script by that ayah key. +3. App loads translation payload by the same ayah key. +4. App normalizes translation payload and renders text + notes. + +Expected outcome: + +- Arabic and translation remain correctly paired. +- Footnotes appear for resources that include them. + +## Common Mistakes + +- Joining Arabic and translation by row index instead of ayah key. +- Assuming all translation resources have identical payload structures. +- Rendering tagged-footnote HTML directly without sanitization in production. +- Ignoring missing footnote entries or missing ayah keys. + +## When to Request Updates or Changes + +Open an issue when you find: + +- Broken translation download links +- Translation text mapped to wrong ayah keys +- Footnote references without matching footnote text +- Missing language/source metadata + +Issue link: + +- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) + +## Related Pages + +- [Tutorials](tutorials.md) +- [Tutorial 3: Translation End-to-End](tutorial-translation-end-to-end.md) +- [Downloading and Using Data](downloading-data.md) +- [Data Model](data-model.md) diff --git a/docs/resource-transliteration.md b/docs/resource-transliteration.md new file mode 100644 index 00000000..91498786 --- /dev/null +++ b/docs/resource-transliteration.md @@ -0,0 +1,52 @@ +# Transliteration Guide + +## What This Resource Is + +Transliteration resources represent Quran text using non-Arabic scripts/phonetic mappings to support pronunciation-oriented reading. + +Category URL: [https://qul.tarteel.ai/resources/transliteration](https://qul.tarteel.ai/resources/transliteration) + +## When to Use It + +- Beginner-friendly reading modes +- Pronunciation assistance +- Transitional learning from transliteration to Arabic script + +## How to Download or Access It + +1. Open the category URL above. +2. Select transliteration package. +3. Download JSON or SQLite. +4. Validate ayah key compatibility with your script/translation tables. + +## Step-by-Step Integration + +1. Import transliteration rows by ayah identity. +2. Join with Quran Script and optional translation rows. +3. Add mode toggle: Arabic | Transliteration | Translation. +4. Ensure UI typography supports transliteration characters. +5. Validate that ayah ordering matches Quran Script. + +## Real-World Usage Example + +Goal: add transliteration toggle in ayah view. + +Flow: + +1. User opens ayah. +2. User turns on transliteration mode. +3. App renders transliteration text under Arabic line. + +Expected outcome: + +- Users can read pronunciation guidance alongside Quran text. + +## When to Request Updates or Changes + +Open an issue when: + +- Transliteration text is misaligned with ayahs +- Character mappings appear broken +- Missing ayah transliteration entries are found + +Issue link: [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) diff --git a/docs/tutorial-ayah-theme-end-to-end.md b/docs/tutorial-ayah-theme-end-to-end.md new file mode 100644 index 00000000..a1ddba86 --- /dev/null +++ b/docs/tutorial-ayah-theme-end-to-end.md @@ -0,0 +1,188 @@ +# Tutorial 14: Ayah Theme End-to-End + +This tutorial is for users who want to show concise thematic summaries for ayah groups. + +## 1) What This Resource Is + +Ayah Theme resources provide themes linked to one ayah or a range of ayahs. + +Typical fields include: + +- `theme` text +- range fields (`ayah_from`, `ayah_to` or equivalents) +- keywords/tags +- coverage counts + +Primary category: + +- [https://qul.tarteel.ai/resources/ayah-theme](https://qul.tarteel.ai/resources/ayah-theme) + +## 2) When to Use It + +Use ayah-theme data when building: + +- Passage summaries +- Theme-first study and reflection flows +- Quick contextual hints above ayah groups + +## 3) How to Get Your First Example Resource + +1. Open [https://qul.tarteel.ai/resources/ayah-theme](https://qul.tarteel.ai/resources/ayah-theme). +2. Keep default listing order and open the first published card. +3. Confirm the detail page includes: + - `Theme Preview` tab + - `Help` tab +4. Confirm available download format(s) (commonly `sqlite`). + +This keeps onboarding concrete without hardcoded IDs. + +## 4) What the Preview Shows (Website-Aligned) + +On ayah-theme detail pages: + +- `Theme Preview` tab: + - `Jump to Ayah` + - Theme summary for selected ayah/range + - Range coverage hints (for example multiple ayahs) +- `Help` tab: + - Theme field definitions + - Range logic (`ayah_from` to `ayah_to`) + +Practical meaning: + +- Theme entries can represent groups, not only single ayahs. +- You should resolve theme by range inclusion, not exact ayah equality only. + +## 5) Download and Use (Step-by-Step) + +1. Download ayah-theme package (commonly `sqlite`). +2. Import theme rows with range fields. +3. Build resolver to find matching theme for selected ayah. +4. Render theme text + keywords + covered range. +5. Join with script/translation display context. + +Starter integration snippet (JavaScript): + +```javascript +const findThemeForAyah = (themeRows, surah, ayah) => + themeRows.find((row) => + row.surah_number === surah && ayah >= row.ayah_from && ayah <= row.ayah_to + ) || null; +``` + +## 6) Real-World Example: Passage Theme Banner + +Goal: + +- User opens an ayah and sees the current passage theme. + +Inputs: + +- Ayah Theme package +- Quran Script package + +Processing: + +1. Resolve current ayah. +2. Find theme row where ayah falls in range. +3. Display theme banner above ayah text. + +Expected output: + +- Users get quick thematic context for current passage. + +Interactive preview (temporary sandbox): + +You can edit this code for testing. Edits are not saved and may not persist after refresh. + +```playground-js +// This sandbox demonstrates ayah-to-theme resolution using ayah ranges. + +const themeRows = [ + { + surah_number: 1, + ayah_from: 1, + ayah_to: 7, + theme: "Supplication to Allah for guidance taught by Allah Himself", + keywords: ["guidance", "worship", "mercy"] + }, + { + surah_number: 73, + ayah_from: 1, + ayah_to: 19, + theme: "Night prayer discipline and preparing for revelation", + keywords: ["qiyam", "recitation", "steadfastness"] + } +]; + +const findTheme = (surah, ayah) => + themeRows.find((r) => r.surah_number === surah && ayah >= r.ayah_from && ayah <= r.ayah_to) || null; + +const app = document.getElementById("app"); +app.innerHTML = ` +

    Ayah Theme Preview

    +

    Resolve thematic summary by ayah range

    +
    + +
    + + +
    +
    Pick an ayah to resolve its theme range.
    +
    +
    +`; + +const ayahSelect = app.querySelector("#ayah"); +const themeBox = app.querySelector("#theme"); + +const render = () => { + const [surahStr, ayahStr] = ayahSelect.value.split(":"); + const surah = Number(surahStr); + const ayah = Number(ayahStr); + const row = findTheme(surah, ayah); + + if (!row) { + themeBox.textContent = "No theme found for selected ayah."; + return; + } + + themeBox.innerHTML = ` +
    Theme: ${row.theme}
    +
    Range: ${row.surah_number}:${row.ayah_from} to ${row.surah_number}:${row.ayah_to}
    +
    Keywords: ${row.keywords.join(", ")}
    + `; +}; + +ayahSelect.addEventListener("change", render); +render(); +``` + +## 7) Common Mistakes to Avoid + +- Matching themes only by exact ayah instead of range. +- Ignoring multi-ayah group coverage. +- Showing stale theme when user navigates between ayahs. + +## 8) When to Request Updates or Changes + +Open an issue if you find: + +- Broken range mappings +- Missing theme text/keywords +- Broken download links + +Issue tracker: + +- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) + +## Related Docs + +- [Tutorials Index](tutorials.md) +- [Ayah Theme Guide](resource-ayah-theme.md) +- [Ayah Topics Guide](resource-ayah-topics.md) +- [Quran Script Guide](resource-quran-script.md) diff --git a/docs/tutorial-ayah-topics-end-to-end.md b/docs/tutorial-ayah-topics-end-to-end.md new file mode 100644 index 00000000..24f18788 --- /dev/null +++ b/docs/tutorial-ayah-topics-end-to-end.md @@ -0,0 +1,189 @@ +# Tutorial 10: Ayah Topics End-to-End + +This tutorial is for users who want to browse Quran content by topics and concept groups. + +## 1) What This Resource Is + +Ayah Topics resources map topics/concepts to related ayahs and often include topic taxonomy details. + +Typical content includes: + +- Topic entities (name, category, description) +- Topic-to-ayah mappings +- Topic counts and searchable labels + +Primary category: + +- [https://qul.tarteel.ai/resources/ayah-topics](https://qul.tarteel.ai/resources/ayah-topics) + +## 2) When to Use It + +Use ayah-topics data when building: + +- Topic-first discovery experiences +- Educational thematic study flows +- Search interfaces by concept/theme + +## 3) How to Get Your First Example Resource + +1. Open [https://qul.tarteel.ai/resources/ayah-topics](https://qul.tarteel.ai/resources/ayah-topics). +2. Keep default listing order and open the first published card. +3. Confirm page sections include: + - Topics listing/search area + - `Help` tab +4. Confirm available download formats (commonly `sqlite` on this resource type). + +This keeps onboarding concrete without hardcoded IDs. + +## 4) What the Preview Shows (Website-Aligned) + +On ayah-topics detail pages: + +- Topics pane: + - Search input for topic names/metadata + - Topic list with ayah counts + - Topic detail navigation +- `Help` tab: + - Explains topic families and usage + - Documents mapping behavior between topics and ayahs + +Practical meaning: + +- Topic rows and topic-ayah mapping rows should be treated as separate layers. +- Build topic indexes first, then resolve ayah lists for display. + +## 5) Download and Use (Step-by-Step) + +1. Download the package (commonly `sqlite`). +2. Import topic tables and mapping tables. +3. Normalize topic IDs and ayah keys. +4. Build searchable topic index. +5. On topic selection, fetch mapped ayah keys and join with script/translation. + +Starter integration snippet (JavaScript): + +```javascript +const buildTopicIndex = (topics) => + topics.reduce((index, topic) => { + index[topic.topic_id] = topic; + return index; + }, {}); + +const ayahKeysForTopic = (topicAyahMappings, topicId) => + topicAyahMappings + .filter((row) => row.topic_id === topicId) + .map((row) => row.ayah_key); +``` + +## 6) Real-World Example: Browse by Topic + +Goal: + +- User picks a topic and sees related ayahs. + +Inputs: + +- Ayah Topics package +- Quran Script package + +Processing: + +1. User searches/selects topic. +2. App loads mapped ayah keys. +3. App resolves ayah text and displays results. + +Expected output: + +- Topic-driven ayah browsing works with stable mapping. + +Interactive preview (temporary sandbox): + +You can edit this code for testing. Edits are not saved and may not persist after refresh. + +```playground-js +// This sandbox demonstrates topic search and topic->ayah mapping. + +const topics = [ + { topic_id: 101, name: "Patience", category: "General", ayahs_count: 3 }, + { topic_id: 102, name: "Prayer", category: "General", ayahs_count: 2 }, + { topic_id: 103, name: "Mercy", category: "Theme", ayahs_count: 2 } +]; + +const topicAyahMappings = [ + { topic_id: 101, ayah_key: "2:153" }, + { topic_id: 101, ayah_key: "3:200" }, + { topic_id: 101, ayah_key: "39:10" }, + { topic_id: 102, ayah_key: "2:43" }, + { topic_id: 102, ayah_key: "20:14" }, + { topic_id: 103, ayah_key: "7:156" }, + { topic_id: 103, ayah_key: "39:53" } +]; + +const app = document.getElementById("app"); +app.innerHTML = ` +

    Ayah Topics Preview

    +

    Search a topic and list mapped ayah keys

    + +
    +
    +`; + +const searchInput = app.querySelector("#search"); +const topicsBox = app.querySelector("#topics"); +const resultBox = app.querySelector("#result"); + +const renderTopics = () => { + const query = searchInput.value.trim().toLowerCase(); + const filtered = topics.filter((t) => t.name.toLowerCase().includes(query)); + + topicsBox.innerHTML = ""; + filtered.forEach((topic) => { + const btn = document.createElement("button"); + btn.type = "button"; + btn.textContent = `${topic.name} (${topic.ayahs_count})`; + btn.style.marginRight = "8px"; + btn.style.marginBottom = "8px"; + btn.style.padding = "6px 10px"; + btn.style.border = "1px solid #cbd5e1"; + btn.style.borderRadius = "8px"; + btn.style.background = "#fff"; + + btn.addEventListener("click", () => { + const ayahKeys = topicAyahMappings.filter((r) => r.topic_id === topic.topic_id).map((r) => r.ayah_key); + resultBox.innerHTML = `${topic.name}
    Ayah Keys: ${ayahKeys.join(", ") || "none"}`; + }); + + topicsBox.appendChild(btn); + }); + + if (filtered.length === 0) resultBox.textContent = "No topics found for this query."; +}; + +searchInput.addEventListener("input", renderTopics); +renderTopics(); +``` + +## 7) Common Mistakes to Avoid + +- Mixing topic metadata and topic-ayah mapping tables. +- Assuming topic IDs are globally stable across different resources. +- Rendering topic results without pagination for large topic sets. + +## 8) When to Request Updates or Changes + +Open an issue if you find: + +- Broken topic-to-ayah mappings +- Missing topic labels/descriptions +- Broken download links + +Issue tracker: + +- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) + +## Related Docs + +- [Tutorials Index](tutorials.md) +- [Ayah Topics Guide](resource-ayah-topics.md) +- [Quran Script Guide](resource-quran-script.md) +- [Translations Guide](resource-translations.md) diff --git a/docs/tutorial-font-end-to-end.md b/docs/tutorial-font-end-to-end.md new file mode 100644 index 00000000..b3df117c --- /dev/null +++ b/docs/tutorial-font-end-to-end.md @@ -0,0 +1,190 @@ +# Tutorial 6: Font End-to-End + +This tutorial is for users who want to download Quran font assets and apply them correctly in app/web rendering. + +## 1) What This Resource Is + +Font resources provide downloadable Quran-related font files used to render script-specific text and glyphs. + +Common file types include: + +- `woff` +- `woff2` +- `ttf` + +Primary category: + +- [https://qul.tarteel.ai/resources/font](https://qul.tarteel.ai/resources/font) + +## 2) When to Use It + +Use font resources when you are building: + +- Quran readers with script-specific display requirements +- Word-by-word/Glyph interfaces that depend on a specific font family +- Apps that must match a known Mushaf style + +## 3) How to Get Your First Example Resource + +1. Open [https://qul.tarteel.ai/resources/font](https://qul.tarteel.ai/resources/font). +2. Keep default listing order and open the first published card. +3. Confirm the detail page includes: + - `Glyph Preview` tab + - `Help` tab +4. Confirm available download files (`woff`, `woff2`, `ttf`). + +This keeps onboarding concrete without hardcoded resource IDs. + +## 4) What the Preview Shows (Website-Aligned) + +On the font detail page: + +- `Glyph Preview` tab: + - Shows font glyph behavior on sample Quran text + - Helps validate readability and shaping before download +- `Help` tab: + - Shows practical usage notes + - Includes live preview guidance for the selected font + +Practical meaning: + +- You should validate the chosen font against your target script content. +- You should always define fallbacks in case a user device cannot load the primary font. + +## 5) Download and Use (Step-by-Step) + +1. Download font files from the resource (`woff2` preferred for web, keep `ttf` for fallback). +2. Place font files in your static/public assets. +3. Register with `@font-face`. +4. Apply the font family to Quran text containers. +5. Validate on desktop + mobile and compare shaping. + +Starter integration snippet (CSS + JavaScript): + +```javascript +// 1) CSS (inject or place in stylesheet) +const css = ` +@font-face { + font-family: 'qpc-hafs'; + src: url('/fonts/qpc-hafs.woff2') format('woff2'), + url('/fonts/qpc-hafs.woff') format('woff'); + font-display: swap; +} + +.quran-script { + direction: rtl; + text-align: right; + font-family: 'qpc-hafs', 'Amiri Quran', 'Noto Naskh Arabic', serif; +} +`; + +// 2) Register style once +const style = document.createElement('style'); +style.textContent = css; +document.head.appendChild(style); + +// 3) Apply class to verse container +document.getElementById('verse').classList.add('quran-script'); +``` + +## 6) Real-World Example: Font-Safe Verse Rendering + +Goal: + +- Show one ayah with the selected Quran font and safe fallbacks. + +Inputs: + +- Font files (`woff2`, `woff`, optional `ttf`) +- Quran Script text + +Processing: + +1. Load font via `@font-face`. +2. Render ayah in RTL block with configured font stack. +3. Verify character shaping and spacing. + +Expected output: + +- Verse renders with intended visual style. +- Fallback still renders readable Arabic if primary font is unavailable. + +Interactive preview (temporary sandbox): + +You can edit this code for testing. Edits are not saved and may not persist after refresh. + +```playground-js +// This sandbox demonstrates font stack switching for Quran text rendering. + +const samples = { + "1:1": "بِسۡمِ ٱللَّهِ ٱلرَّحۡمَٰنِ ٱلرَّحِيمِ", + "73:4": "أَوۡ زِدۡ عَلَيۡهِ وَرَتِّلِ ٱلۡقُرۡءَانَ تَرۡتِيلًا" +}; + +const fontStacks = { + "qpc-hafs (with fallbacks)": "'KFGQPC Uthmanic Script HAFS','Amiri Quran','Noto Naskh Arabic','Scheherazade New',serif", + "Amiri Quran": "'Amiri Quran','Noto Naskh Arabic',serif", + "System fallback": "'Noto Naskh Arabic','Tahoma','Arial',serif" +}; + +const app = document.getElementById("app"); +app.innerHTML = ` +

    Font Preview (Quran Script)

    +

    Test verse rendering with different font stacks

    + + + + +
    +`; + +const ayahSelect = app.querySelector("#ayah"); +const stackSelect = app.querySelector("#stack"); +const verseBox = app.querySelector("#verse"); + +Object.keys(fontStacks).forEach((label) => { + const opt = document.createElement("option"); + opt.value = label; + opt.textContent = label; + stackSelect.appendChild(opt); +}); + +const render = () => { + verseBox.textContent = samples[ayahSelect.value] || ""; + verseBox.style.fontFamily = fontStacks[stackSelect.value]; +}; + +ayahSelect.addEventListener("change", render); +stackSelect.addEventListener("change", render); +stackSelect.value = "qpc-hafs (with fallbacks)"; +render(); +``` + +## 7) Common Mistakes to Avoid + +- Using a Quran script package without its intended font family. +- Defining only one font and no fallback stack. +- Skipping mobile rendering checks. +- Assuming glyph-style and Unicode-style fonts behave identically. + +## 8) When to Request Updates or Changes + +Open an issue if you find: + +- Broken font download links +- Missing glyph support for expected text +- Incorrect font metadata or unclear usage instructions + +Issue tracker: + +- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) + +## Related Docs + +- [Tutorials Index](tutorials.md) +- [Quran Fonts Guide](resource-fonts.md) +- [Quran Script Guide](resource-quran-script.md) +- [Mushaf Layouts Guide](resource-mushaf-layouts.md) diff --git a/docs/tutorial-morphology-end-to-end.md b/docs/tutorial-morphology-end-to-end.md new file mode 100644 index 00000000..6b053bd1 --- /dev/null +++ b/docs/tutorial-morphology-end-to-end.md @@ -0,0 +1,256 @@ +# Tutorial 11: Morphology End-to-End + +This tutorial is for users who want to integrate word-level Quran morphology (roots, lemmas, grammar tags) into reading/study tools. + +## 1) What This Resource Is + +Morphology resources provide word-level linguistic annotations for Quran words. + +Typical fields include: + +- Word location keys (for example `surah:ayah:word`) +- Root/lemma/stem fields +- Part-of-speech and grammar tags + +Primary category: + +- [https://qul.tarteel.ai/resources/morphology](https://qul.tarteel.ai/resources/morphology) + +## 2) When to Use It + +Use morphology data when building: + +- Tap-word grammar insights +- Root/lemma based search +- Arabic linguistic study tools + +## 3) How to Get Your First Example Resource + +1. Open [https://qul.tarteel.ai/resources/morphology](https://qul.tarteel.ai/resources/morphology). +2. Keep default listing order and open the first published card. +3. Confirm the detail page includes: + - `Preview` tab (resource-specific title such as `Word root Preview`) + - `Help` tab +4. Confirm available download formats (commonly `sqlite`). + +This keeps onboarding concrete without hardcoded IDs. + +## 4) What the Preview Shows (Website-Aligned) + +On morphology detail pages: + +- `Preview` tab: + - `Jump to Ayah` + - Word-level rows for selected ayah (`Word stem`, `Word root`, `Word lemma`) + - Ayah-level aggregates (`Ayah Stem`, `Ayah Root`, `Ayah Lemma`) +- `Help` tab: + - Field definitions (including word location key) + - Integration notes about joining with word-by-word script + +Practical meaning: + +- Morphology rows must be joined at word-level, not only ayah-level. +- Word order and location keys are mandatory for accurate overlays. + +## 5) Download and Use (Step-by-Step) + +1. Download morphology package (commonly `sqlite`). +2. Import rows with `word_location`/equivalent keys. +3. Join with Quran Script word data using the same key. +4. Index by root/lemma/POS for search features. +5. Render per-word analysis in UI. + +Starter integration snippet (JavaScript): + +```javascript +const buildMorphologyIndex = (rows) => + rows.reduce((index, row) => { + index[row.word_location] = row; + return index; + }, {}); + +const enrichWordsWithMorphology = (wordRows, morphologyIndex) => + wordRows.map((word) => ({ + ...word, + morphology: morphologyIndex[word.location] || null + })); +``` + +## 6) Real-World Example: Tap Word for Grammar + +Goal: + +- User taps a Quran word and sees stem/root/lemma details. + +Inputs: + +- Morphology package +- Quran Script word-by-word package + +Processing: + +1. Render words with location keys. +2. User taps one word. +3. App resolves morphology row by location key. +4. UI shows stem/root/lemma and ayah-level morphology context. + +Expected output: + +- Morphology panel reflects the correct tapped word and the ayah-level summary. + +Interactive preview (temporary sandbox): + +You can edit this code for testing. Edits are not saved and may not persist after refresh. +Tip: scroll down in the preview area to view Ayah Stem/Root/Lemma sections. + +```playground-js +// Source-aligned sample for Surah Al-Fatihah (1:1). +// Shows word stem/root/lemma and ayah stem/root/lemma in one preview. +const words = [ + { location: "1:1:1", text: "بِسۡمِ", stem: "سم", root: "س م و", lemma: "اسم" }, + { location: "1:1:2", text: "ٱللَّهِ", stem: "الله", root: "ا ل ه", lemma: "الله" }, + { location: "1:1:3", text: "ٱلرَّحۡمَٰنِ", stem: "رحمان", root: "ر ح م", lemma: "رحمان" }, + { location: "1:1:4", text: "ٱلرَّحِيمِ", stem: "رحيم", root: "ر ح م", lemma: "رحيم" } +]; + +const ayah = { + text_with_number: "بِسۡمِ ٱللَّهِ ٱلرَّحۡمَٰنِ ٱلرَّحِيمِ ١", + text_without_number: "بِسۡمِ ٱللَّهِ ٱلرَّحۡمَٰنِ ٱلرَّحِيمِ", + stem: "سْمِ اللَّهِ رَّحْمَٰنِ رَّحِيمِ", + root: "سمو اله رحم رحم", + lemma: "اسْم اللَّه رَّحْمَٰن رَّحِيم" +}; + +const app = document.getElementById("app"); +app.innerHTML = ` +

    Morphology Preview (Word Level)

    +

    Surah Al-Fatihah — Ayah 1 (word stem/root/lemma + ayah summary)

    +
    + Continue scrolling to see Ayah Stem, Ayah Root, and Ayah Lemma. +
    + + + +
    + + + + + + + + +
    WordValue
    +
    +
    +
    +`; + +const fieldSelect = app.querySelector("#field"); +const jumpAyahButton = app.querySelector("#jump-ayah"); +const rowsBox = app.querySelector("#rows"); +const infoBox = app.querySelector("#info"); +const ayahBox = app.querySelector("#ayah"); +let selectedLocation = "1:1:1"; + +const renderRows = () => { + const field = fieldSelect.value; + rowsBox.innerHTML = ""; + words.forEach((word) => { + const tr = document.createElement("tr"); + tr.innerHTML = ` + + + + ${word[field]} + `; + rowsBox.appendChild(tr); + }); + + rowsBox.querySelectorAll("button[data-location]").forEach((btn) => { + btn.addEventListener("click", () => { + selectedLocation = btn.getAttribute("data-location"); + renderInfo(); + }); + }); +}; + +const renderInfo = () => { + const word = words.find((item) => item.location === selectedLocation); + if (!word) { + infoBox.textContent = "No morphology found for selected word."; + return; + } + + infoBox.innerHTML = ` +
    Location: ${word.location}
    +
    Word: ${word.text}
    +
    Stem: ${word.stem}
    +
    Root: ${word.root}
    +
    Lemma: ${word.lemma}
    + `; +}; + +const renderAyah = () => { + ayahBox.innerHTML = ` +
    +
    Ayah Stem for Surah Al-Fatihah — Ayah 1
    +
    ${ayah.text_with_number}
    +
    Stem
    +
    ${ayah.stem}
    +
    +
    +
    Ayah Root for Surah Al-Fatihah — Ayah 1
    +
    ${ayah.text_with_number}
    +
    Root
    +
    ${ayah.root}
    +
    +
    +
    Ayah Lemma for Surah Al-Fatihah — Ayah 1
    +
    ${ayah.text_without_number}
    +
    Lemma
    +
    ${ayah.lemma}
    +
    + `; +}; + +fieldSelect.addEventListener("change", renderRows); +jumpAyahButton.addEventListener("click", () => { + ayahBox.scrollIntoView({ behavior: "smooth", block: "start" }); +}); +renderRows(); +renderInfo(); +renderAyah(); +``` + +## 7) Common Mistakes to Avoid + +- Joining morphology to verse-only keys instead of word location keys. +- Ignoring word order and position. +- Treating morphology labels as stable across different source datasets. + +## 8) When to Request Updates or Changes + +Open an issue if you find: + +- Incorrect location-key mappings +- Missing stem/root/lemma values +- Broken download links + +Issue tracker: + +- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) + +## Related Docs + +- [Tutorials Index](tutorials.md) +- [Morphology Guide](resource-morphology.md) +- [Quran Script Guide](resource-quran-script.md) diff --git a/docs/tutorial-mushaf-layout-end-to-end.md b/docs/tutorial-mushaf-layout-end-to-end.md new file mode 100644 index 00000000..f30ac623 --- /dev/null +++ b/docs/tutorial-mushaf-layout-end-to-end.md @@ -0,0 +1,271 @@ +# Tutorial 2: Mushaf Layout End-to-End + +This tutorial is for users who want to render page-accurate mushaf layouts from downloaded QUL data. + +## 1) What This Resource Is + +Mushaf Layout resources provide page-structured layout data for Quran pages. + +In practice, this includes page lines and word ranges you can use to render page-faithful mushaf views. + +Primary category: + +- [https://qul.tarteel.ai/resources/mushaf-layout](https://qul.tarteel.ai/resources/mushaf-layout) + +## 2) When to Use It + +Use mushaf layout data when you are building: + +- Page-by-page mushaf readers +- Navigation by page number (instead of only surah/ayah) +- Memorization or classroom tools that depend on printed page structure + +## Why Mushaf Layout Is Different (and Helpful) + +The Help sample code and preview on a detail page (for example [https://qul.tarteel.ai/resources/mushaf-layout/12](https://qul.tarteel.ai/resources/mushaf-layout/12)) show a key difference: + +- `mushaf-layout` is a rendering-structure resource, not only a content resource. +- It tells your app *how* to render a page (`line_number`, `line_type`, `is_centered`, `first_word_id`, `last_word_id`). +- Most other resources (recitation, translation, tafsir, etc.) mostly give content keyed by ayah/word, but not page geometry. + +Why this is useful: + +- You can recreate a printed mushaf page layout more accurately. +- You can support page-first navigation and memorization workflows. +- You can combine layout + script + font consistently, instead of hardcoding line templates in app code. + +## 3) How to Get Your First Example Resource + +1. Open [https://qul.tarteel.ai/resources/mushaf-layout](https://qul.tarteel.ai/resources/mushaf-layout). +2. Keep the default listing order and open the first published card. +3. Confirm the resource detail page includes: + - `Mushaf Page Preview` tab + - `Help` tab +4. Confirm available download formats shown on the page (commonly `images`, `sqlite`, `docx`). + +This keeps onboarding concrete without depending on a fixed resource ID. + +## 4) What the Preview Shows (Website-Aligned) + +On the mushaf layout detail page, preview helps you validate page rendering behavior before download: + +- `Mushaf Page Preview` tab: + - `Jump to page` selector + - Previous/next page navigation + - Rendered page output with line and word structure +- `Help` tab: + - Required data to render a page (layout + script + font) + - `pages` table schema (for line mapping) + - `words` table schema expectations (for word ranges) + - Sample rendering logic + +Practical meaning: + +- `pages` table controls line structure for each page. +- `first_word_id`/`last_word_id` ranges map into script words for line rendering. + +## 5) Download and Use (Step-by-Step) + +1. Download the selected mushaf layout package (for example `sqlite` and related assets). +2. Inspect layout records: + - `page_number` + - `line_number` + - `line_type` + - `is_centered` + - `first_word_id`, `last_word_id` + - `surah_number` (when relevant) +3. Load matching Quran script word data (word-by-word format). +4. For each page line: + - If `line_type` is `surah_name`, render surah heading line. + - If `line_type` is `ayah`, render words in `first_word_id..last_word_id`. + - If `line_type` is `basmallah`, render basmallah line. +5. Apply alignment using `is_centered`. +6. Validate with at least three pages (start, middle, end) to catch mapping issues. + +Starter integration snippet (JavaScript): + +```javascript +// Select all lines for a single page and keep display order stable. +const linesForPage = (pagesTableRows, pageNumber) => + pagesTableRows + .filter((row) => row.page_number === pageNumber) + .sort((a, b) => a.line_number - b.line_number); + +// Convert a word ID range into one display string. +const getWordsInRange = (wordsByIndex, firstWordId, lastWordId) => { + const result = []; + for (let id = firstWordId; id <= lastWordId; id += 1) { + if (wordsByIndex[id]) result.push(wordsByIndex[id].text); + } + return result.join(" "); +}; + +// Render line text by line type rules. +const renderLineText = (line, wordsByIndex, surahNameByNumber) => { + if (line.line_type === "surah_name") return surahNameByNumber[line.surah_number] || ""; + if (line.line_type === "basmallah") return "﷽"; + if (line.line_type === "ayah") return getWordsInRange(wordsByIndex, line.first_word_id, line.last_word_id); + return ""; +}; +``` + +## 6) Real-World Example: Render One Mushaf Page + +Goal: + +- User opens page 1 and sees line-accurate mushaf rendering. + +Inputs: + +- Mushaf Layout package (line mapping) +- Quran Script package (word text) +- Surah names metadata + +Processing: + +1. User selects page number. +2. App loads all lines for that page from layout data. +3. App resolves words for each ayah line using word ID ranges. +4. App applies centered/justified alignment based on `is_centered`. +5. UI renders all lines in page order. + +Expected output: + +- Page structure matches expected mushaf layout. +- Surah name and ayah lines render in correct order (with basmallah lines when present in layout data). +- Page navigation remains stable across adjacent pages. + +Interactive preview (temporary sandbox): + +You can edit this code for testing. Edits are not saved and may not persist after refresh. + +```playground-js +// This playground follows the Help section sample pattern from: +// https://qul.tarteel.ai/resources/mushaf-layout/12 + +// Surah labels used by line_type = "surah_name". +const SurahNames = { + 1: "الفاتحة" +}; + +// Simulated rows from the layout DB "pages" table for page 1. +const pageData = [ + { line_number: 1, line_type: "surah_name", is_centered: true, first_word_id: null, last_word_id: null, surah_number: 1 }, + { line_number: 2, line_type: "ayah", is_centered: true, first_word_id: 1, last_word_id: 5, surah_number: null }, + { line_number: 3, line_type: "ayah", is_centered: true, first_word_id: 6, last_word_id: 10, surah_number: null }, + { line_number: 4, line_type: "ayah", is_centered: true, first_word_id: 11, last_word_id: 17, surah_number: null }, + { line_number: 5, line_type: "ayah", is_centered: true, first_word_id: 18, last_word_id: 23, surah_number: null }, + { line_number: 6, line_type: "ayah", is_centered: true, first_word_id: 24, last_word_id: 29, surah_number: null }, + { line_number: 7, line_type: "ayah", is_centered: true, first_word_id: 30, last_word_id: 33, surah_number: null }, + { line_number: 8, line_type: "ayah", is_centered: true, first_word_id: 34, last_word_id: 36, surah_number: null } +]; + +// Simulated rows from Quran script DB "words" table keyed by word id. +const wordData = { + 1: "بِسۡمِ", 2: "ٱللَّهِ", 3: "ٱلرَّحۡمَٰنِ", 4: "ٱلرَّحِيمِ", 5: "١", + 6: "ٱلۡحَمۡدُ", 7: "لِلَّهِ", 8: "رَبِّ", 9: "ٱلۡعَٰلَمِينَ", 10: "٢", + 11: "ٱلرَّحۡمَٰنِ", 12: "ٱلرَّحِيمِ", 13: "٣", 14: "مَٰلِكِ", 15: "يَوۡمِ", + 16: "ٱلدِّينِ", 17: "٤", 18: "إِيَّاكَ", 19: "نَعۡبُدُ", 20: "وَإِيَّاكَ", + 21: "نَسۡتَعِينُ", 22: "٥", 23: "ٱهۡدِنَا", 24: "ٱلصِّرَٰطَ", 25: "ٱلۡمُسۡتَقِيمَ", + 26: "٦", 27: "صِرَٰطَ", 28: "ٱلَّذِينَ", 29: "أَنۡعَمۡتَ", 30: "عَلَيۡهِمۡ", + 31: "غَيۡرِ", 32: "ٱلۡمَغۡضُوبِ", 33: "عَلَيۡهِمۡ", 34: "وَلَا", 35: "ٱلضَّآلِّينَ", 36: "٧" +}; + +// Read words between first_word_id..last_word_id (inclusive), sorted by id. +const getWords = (firstWordId, lastWordId) => + Object.entries(wordData) + .map(([key, value]) => ({ id: Number(key), text: value })) + .sort((a, b) => a.id - b.id) + .filter((word) => word.id >= firstWordId && word.id <= lastWordId) + .map((word) => word.text) + .join(" "); + +const getSurahName = (number) => `سورۃ ${SurahNames[number] || ""}`; + +// Build sandbox preview container. +const app = document.getElementById("app"); +app.innerHTML = ` +

    Mushaf Page Preview (Simulated)

    +

    Aligned with Help sample logic: page lines + word ID ranges

    +
    +`; + +const page = app.querySelector("#page"); + +const renderPage = () => { + page.innerHTML = ""; + + // Render in the same stable order as page line numbers. + pageData + .slice() + .sort((a, b) => a.line_number - b.line_number) + .forEach((line) => { + const lineEl = document.createElement("div"); + lineEl.style.padding = "4px 0"; + lineEl.style.minHeight = "34px"; + lineEl.style.borderBottom = "1px dashed #f1f5f9"; + + // Respect is_centered from layout metadata. + if (line.is_centered) { + lineEl.style.textAlign = "center"; + lineEl.style.display = "flex"; + lineEl.style.justifyContent = "center"; + } else { + lineEl.style.textAlign = "justify"; + } + + // Choose content by line_type like the Help sample. + switch (line.line_type) { + case "surah_name": + lineEl.textContent = getSurahName(line.surah_number); + lineEl.style.fontWeight = "700"; + lineEl.style.fontSize = "1.25rem"; + break; + case "ayah": + lineEl.textContent = getWords(line.first_word_id, line.last_word_id); + lineEl.style.fontWeight = "500"; + lineEl.style.fontSize = "1.1rem"; + break; + case "basmallah": + lineEl.textContent = "﷽"; + lineEl.style.fontWeight = "700"; + break; + default: + lineEl.textContent = ""; + } + + page.appendChild(lineEl); + }); +}; + +renderPage(); +``` + +## 7) Common Mistakes to Avoid + +- Joining layout lines to words with wrong word ID field. +- Ignoring `line_type` and rendering every line as ayah text. +- Ignoring `is_centered`, which changes page appearance. +- Validating only page 1 and skipping middle/end page checks. +- Mixing incompatible script/font with selected layout package. + +## 8) When to Request Updates or Changes + +Open an issue if you find: + +- Incorrect line-to-word mapping on a page +- Missing lines or broken page navigation +- Download package inconsistencies (`images`, `sqlite`, `docx`, metadata) +- Layout metadata that conflicts with preview behavior + +Issue tracker: + +- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) + +## Related Docs + +- [Tutorials Index](tutorials.md) +- [Mushaf Layouts Guide](resource-mushaf-layouts.md) +- [Quran Script Guide](resource-quran-script.md) +- [Fonts Guide](resource-fonts.md) +- [Quran Metadata Guide](resource-quran-metadata.md) diff --git a/docs/tutorial-mutashabihat-end-to-end.md b/docs/tutorial-mutashabihat-end-to-end.md new file mode 100644 index 00000000..17af01f0 --- /dev/null +++ b/docs/tutorial-mutashabihat-end-to-end.md @@ -0,0 +1,327 @@ +# Tutorial 12: Mutashabihat End-to-End + +This tutorial is for users who want to integrate phrase-level similarity aids for memorization and comparison. + +## 1) What This Resource Is + +Mutashabihat resources provide phrase-level similarity mappings across ayahs. + +The help model typically includes files such as: + +- `phrases.json` (shared phrases) +- `phrase_verses.json` (ayah-to-phrase mappings) + +Primary category: + +- [https://qul.tarteel.ai/resources/mutashabihat](https://qul.tarteel.ai/resources/mutashabihat) + +## 2) When to Use It + +Use mutashabihat data when building: + +- Memorization revision tools +- Similar phrase comparison views +- Confusion-reduction aids for close ayah wording + +## 3) How to Get Your First Example Resource + +1. Open [https://qul.tarteel.ai/resources/mutashabihat](https://qul.tarteel.ai/resources/mutashabihat). +2. Keep default listing order and open the first published card. +3. Confirm the detail page includes: + - `Mutashabihat Preview` tab + - `Help` tab +4. Confirm available download file(s) (commonly `json`). + +This keeps onboarding concrete without hardcoded IDs. + +## 4) What the Preview Shows (Website-Aligned) + +On mutashabihat detail pages: + +- `Mutashabihat Preview` tab: + - `Jump to Ayah` + - Displays similar phrase relationships for selected ayah + - Shows phrase words and repeat statistics + - Lists phrase ayahs where the phrase appears +- `Help` tab: + - Explains `phrases.json` and `phrase_verses.json` + - Shows lookup flow to retrieve related phrases + +Practical meaning: + +- You should resolve phrase IDs first, then fetch phrase details. +- Mutashabihat is phrase-level guidance, not full tafsir/translation content. + +Concrete example (mutashabihat resource `73`): + +- Phrase words: + - بِسۡمِ + - ٱللَّهِ + - ٱلرَّحۡمَٰنِ + - ٱلرَّحِيمِ +- Summary: + - This phrase is repeated 2 times in 2 ayahs across 2 surahs. +- Phrase ayahs: + - `1:1`: بِسۡمِ ٱللَّهِ ٱلرَّحۡمَٰنِ ٱلرَّحِيمِ ١ + - `27:30`: إِنَّهُۥ مِن سُلَيۡمَٰنَ وَإِنَّهُۥ بِسۡمِ ٱللَّهِ ٱلرَّحۡمَٰنِ ٱلرَّحِيمِ ٣٠ + +## 5) Download and Use (Step-by-Step) + +1. Download mutashabihat dataset (`json`). +2. Load `phrase_verses.json` and `phrases.json`. +3. For selected ayah, fetch phrase IDs from phrase-verses mapping. +4. Resolve phrase IDs to phrase details. +5. Optionally join with script words for visual highlighting. + +Starter integration snippet (JavaScript): + +```javascript +// Required: load phrases.json, phrase_verses.json, and Quran words data. +const phraseIdsForAyah = (phraseVerses, ayahKey) => phraseVerses[ayahKey] || []; + +const phrasesForAyah = (phraseVerses, phrasesById, ayahKey) => + phraseIdsForAyah(phraseVerses, ayahKey) + .map((id) => phrasesById[id]) + .filter(Boolean); + +// In phrases.json, each phrase can include: +// - source: where the phrase is first sourced from +// - ayah: a mapping like { "2:23": [[from, to], ...] } for word ranges +const phraseRangesForAyah = (phrase, ayahKey) => + phrase?.ayah?.[ayahKey] || []; +``` + +## 6) Real-World Example: Similar Phrase Helper + +Goal: + +- User selects an ayah and sees phrase-level similar references. + +Inputs: + +- `phrases.json` +- `phrase_verses.json` + +Processing: + +1. Resolve phrase IDs for selected ayah. +2. Load matching phrase entries. +3. Render phrase and related ayah references. +4. On demand (`View all`), show all known ayahs where phrase appears. + +Expected output: + +- User can compare similar phrase patterns quickly. + +Interactive preview (temporary sandbox): + +You can edit this code for testing. Edits are not saved and may not persist after refresh. + +```playground-js +// This sandbox mirrors the Help flow: ayah -> phrase IDs -> phrase objects. +// It also mirrors Preview behavior: Jump to Ayah -> repeated phrase cards -> View all ayahs. + +const phraseVerses = { + "1:1": [7301], + "27:30": [7301], + "2:112": [7310, 7311, 7312] +}; + +const phrasesById = { + 7301: { + id: 7301, + words: ["بِسۡمِ", "ٱللَّهِ", "ٱلرَّحۡمَٰنِ", "ٱلرَّحِيمِ"], + phrase_text: "بِسۡمِ ٱللَّهِ ٱلرَّحۡمَٰنِ ٱلرَّحِيمِ", + repeated_count: 2, + ayah_count: 2, + surah_count: 2, + phrase_ayahs: [ + { ayah_key: "1:1", text: "بِسۡمِ ٱللَّهِ ٱلرَّحۡمَٰنِ ٱلرَّحِيمِ ١" }, + { ayah_key: "27:30", text: "إِنَّهُۥ مِن سُلَيۡمَٰنَ وَإِنَّهُۥ بِسۡمِ ٱللَّهِ ٱلرَّحۡمَٰنِ ٱلرَّحِيمِ ٣٠" } + ] + }, + 7310: { + id: 7310, + words: ["فَلَا", "خَوْفٌ", "عَلَيْهِمْ", "وَلَا"], + phrase_text: "فَلَا خَوْفٌ عَلَيْهِمْ وَلَا", + repeated_count: 13, + ayah_count: 13, + surah_count: 7, + phrase_ayahs: [ + { ayah_key: "2:112", text: "فَلَهُۥٓ أَجۡرُهُۥ عِندَ رَبِّهِۦ وَلَا خَوۡفٌ عَلَيۡهِمۡ وَلَا هُمۡ يَحۡزَنُونَ ١١٢" }, + { ayah_key: "2:262", text: "لَّهُمۡ أَجۡرُهُمۡ عِندَ رَبِّهِمۡ وَلَا خَوۡفٌ عَلَيۡهِمۡ وَلَا هُمۡ يَحۡزَنُونَ ٢٦٢" } + ] + }, + 7311: { + id: 7311, + words: ["مَنۡ", "أَسۡلَمَ", "وَجۡهَهُۥ", "لِلَّهِ", "وَهُوَ", "مُحۡسِنٞ"], + phrase_text: "مَنۡ أَسۡلَمَ وَجۡهَهُۥ لِلَّهِ وَهُوَ مُحۡسِنٞ", + repeated_count: 2, + ayah_count: 2, + surah_count: 2, + phrase_ayahs: [ + { ayah_key: "2:112", text: "مَنۡ أَسۡلَمَ وَجۡهَهُۥ لِلَّهِ وَهُوَ مُحۡسِنٞ" }, + { ayah_key: "31:22", text: "وَمَن يُسۡلِمۡ وَجۡهَهُۥٓ إِلَى ٱللَّهِ وَهُوَ مُحۡسِنٞ" } + ] + }, + 7312: { + id: 7312, + words: ["أَجْرُهُمْ", "عِندَ", "رَبِّهِمْ", "وَلَا", "خَوْفٌ", "عَلَيْهِمْ", "وَلَا", "هُمْ", "يَحْزَنُونَ"], + phrase_text: "أَجْرُهُمْ عِندَ رَبِّهِمْ وَلَا خَوْفٌ عَلَيْهِمْ وَلَا هُمْ يَحْزَنُونَ", + repeated_count: 5, + ayah_count: 5, + surah_count: 1, + phrase_ayahs: [ + { ayah_key: "2:112", text: "فَلَهُۥٓ أَجۡرُهُۥ عِندَ رَبِّهِۦ وَلَا خَوۡفٌ عَلَيۡهِمۡ وَلَا هُمۡ يَحۡزَنُونَ ١١٢" }, + { ayah_key: "2:277", text: "لَهُمۡ أَجۡرُهُمۡ عِندَ رَبِّهِمۡ وَلَا خَوۡفٌ عَلَيۡهِمۡ وَلَا هُمۡ يَحۡزَنُونَ ٢٧٧" } + ] + } +}; + +const ayahTextByKey = { + "1:1": "بِسۡمِ ٱللَّهِ ٱلرَّحۡمَٰنِ ٱلرَّحِيمِ ١", + "27:30": "إِنَّهُۥ مِن سُلَيۡمَٰنَ وَإِنَّهُۥ بِسۡمِ ٱللَّهِ ٱلرَّحۡمَٰنِ ٱلرَّحِيمِ ٣٠", + "2:112": "بَلَىٰۚ مَنۡ أَسۡلَمَ وَجۡهَهُۥ لِلَّهِ وَهُوَ مُحۡسِنٞ فَلَهُۥٓ أَجۡرُهُۥ عِندَ رَبِّهِۦ وَلَا خَوۡفٌ عَلَيۡهِمۡ وَلَا هُمۡ يَحۡزَنُونَ ١١٢" +}; + +const helpSample = { + phrase_verses: { "2:23": [50, 16379] }, + phrases: { + "50": { + surahs: 32, + ayahs: 70, + count: 71, + source: { key: "2:23", from: 15, to: 17 }, + ayah: { + "19:48": [[4, 6]], + "2:23": [[15, 17]] + } + } + } +}; + +const app = document.getElementById("app"); +app.innerHTML = ` +

    Mutashabihat Preview (Phrase-Level)

    +

    Preview behavior + Help data model on one screen

    +
    + Start with 1:1 (Bismillah). Then switch to 27:30 or 2:112 to see repeated phrase behavior. +
    + + +
    +
    +
    +
    +
    +
    Help sample: phrase_verses.json
    +
    
    +    
    Help sample: phrases.json
    +
    
    +  
    +`; + +const ayahSelect = app.querySelector("#ayah"); +const current = app.querySelector("#current"); +const ayahTextBox = app.querySelector("#ayah-text"); +const lookup = app.querySelector("#lookup"); +const result = app.querySelector("#result"); +const helpPhraseVersesBox = app.querySelector("#help-phrase-verses"); +const helpPhrasesBox = app.querySelector("#help-phrases"); + +helpPhraseVersesBox.textContent = JSON.stringify(helpSample.phrase_verses, null, 2); +helpPhrasesBox.textContent = JSON.stringify(helpSample.phrases, null, 2); + +const render = () => { + const key = ayahSelect.value; + const ids = phraseVerses[key] || []; + const rows = ids.map((id) => phrasesById[id]).filter(Boolean); + current.textContent = `Selected ayah: ${key}`; + ayahTextBox.textContent = ayahTextByKey[key] || ""; + lookup.innerHTML = ` +
    Lookup flow (from Help): ayah -> phrase IDs -> phrase objects
    +
    phrase_verses.json["${key}"]: [${ids.join(", ")}]
    + `; + + if (rows.length === 0) { + result.textContent = "No phrase relations found."; + return; + } + + result.innerHTML = rows + .map((r) => { + const words = r.words + .map( + (word) => + `${word}` + ) + .join(""); + + const ayahRows = r.phrase_ayahs + .map( + (item) => + `
  • + ${item.ayah_key} +
    ${item.text}
    +
  • ` + ) + .join(""); + + return ` +
    +
    ${words}
    +

    This phrase is repeated ${r.repeated_count} times in ${r.ayah_count} ayahs across ${r.surah_count} surahs.

    + + +
    + `; + }) + .join(""); + + result.querySelectorAll("button[data-phrase-id]").forEach((button) => { + button.addEventListener("click", () => { + const phraseId = button.getAttribute("data-phrase-id"); + const panel = result.querySelector(`#ayahs-${phraseId}`); + if (!panel) return; + const isHidden = panel.style.display === "none"; + panel.style.display = isHidden ? "block" : "none"; + button.textContent = isHidden ? "Hide ayahs" : "View all"; + }); + }); +}; + +ayahSelect.addEventListener("change", render); +render(); +``` + +## 7) Common Mistakes to Avoid + +- Treating mutashabihat as ayah-level one-to-one data. +- Ignoring phrase ID indirection. +- Not showing related ayah context alongside phrase matches. + +## 8) When to Request Updates or Changes + +Open an issue if you find: + +- Missing phrase IDs in mappings +- Broken references from phrase ID to phrase object +- Broken json download links + +Issue tracker: + +- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) + +## Related Docs + +- [Tutorials Index](tutorials.md) +- [Mutashabihat Guide](resource-mutashabihat.md) +- [Similar Ayah Guide](resource-similar-ayah.md) +- [Quran Script Guide](resource-quran-script.md) diff --git a/docs/tutorial-quran-metadata-end-to-end.md b/docs/tutorial-quran-metadata-end-to-end.md new file mode 100644 index 00000000..dc743cd2 --- /dev/null +++ b/docs/tutorial-quran-metadata-end-to-end.md @@ -0,0 +1,191 @@ +# Tutorial 7: Quran Metadata End-to-End + +This tutorial is for users who want to use Quran structural metadata (surah/juz/hizb/manzil and related entities) for navigation and filtering. + +## 1) What This Resource Is + +Quran Metadata resources provide structural reference data for Quran navigation. + +Typical metadata includes: + +- Surah records +- Juz/Hizb/Rub/Manzil structures +- Ayah-range references and numeric indexes + +Primary category: + +- [https://qul.tarteel.ai/resources/quran-metadata](https://qul.tarteel.ai/resources/quran-metadata) + +## 2) When to Use It + +Use metadata resources when you are building: + +- Browse-by-Juz/Hizb flows +- Structural navigation menus +- Section headers and contextual reading controls + +## 3) How to Get Your First Example Resource + +1. Open [https://qul.tarteel.ai/resources/quran-metadata](https://qul.tarteel.ai/resources/quran-metadata). +2. Keep default listing order and open the first published card. +3. Confirm the detail page includes: + - `Preview` tab (resource-specific title like `Surah names Preview`) + - `Help` tab +4. Confirm available downloads (`json`, `sqlite`). + +This keeps onboarding concrete without hardcoded IDs. + +## 4) What the Preview Shows (Website-Aligned) + +On metadata detail pages: + +- `Preview` tab: + - Displays current metadata item examples + - Provides navigation controls where relevant +- `Help` tab: + - Documents field definitions and enum values + - Clarifies how structural references map to Quran sections + +Practical meaning: + +- Metadata is the navigation layer; script/translation/tafsir are content layers. +- Build filters from metadata, then fetch ayah content via shared keys/ranges. + +## 5) Download and Use (Step-by-Step) + +1. Download metadata package (`json` or `sqlite`). +2. Import structural tables/records. +3. Normalize range fields to one format in your app. +4. Build indexes by structure number (e.g., `juz_number`, `hizb_number`). +5. Connect metadata filters to ayah queries. + +Starter integration snippet (JavaScript): + +```javascript +// Build lookup maps for fast structure-based navigation. +const buildMetadataIndexes = ({ surahs, juzRanges }) => { + const surahById = new Map(surahs.map((s) => [s.surah_id, s])); + const juzByNumber = new Map(juzRanges.map((j) => [j.juz_number, j])); + return { surahById, juzByNumber }; +}; + +// Resolve ayah range for selected juz. +const getJuzAyahRange = (juzByNumber, juzNumber) => { + const record = juzByNumber.get(juzNumber); + if (!record) return null; + return { + from: `${record.from_surah}:${record.from_ayah}`, + to: `${record.to_surah}:${record.to_ayah}` + }; +}; +``` + +## 6) Real-World Example: Browse by Juz + +Goal: + +- User selects a Juz and immediately sees its ayah range. + +Inputs: + +- Quran Metadata package +- Quran Script package + +Processing: + +1. User picks `Juz 30`. +2. App resolves range from metadata. +3. App queries and renders ayahs in that range. + +Expected output: + +- Structural navigation works without manual ayah lookups. + +Interactive preview (temporary sandbox): + +You can edit this code for testing. Edits are not saved and may not persist after refresh. + +```playground-js +// This sandbox shows how metadata powers structural browsing. + +const juzRanges = [ + { juz_number: 1, from_surah: 1, from_ayah: 1, to_surah: 2, to_ayah: 141 }, + { juz_number: 2, from_surah: 2, from_ayah: 142, to_surah: 2, to_ayah: 252 }, + { juz_number: 30, from_surah: 78, from_ayah: 1, to_surah: 114, to_ayah: 6 } +]; + +const surahs = [ + { surah_id: 1, name: "Al-Fatihah", revelation_place: "makkah" }, + { surah_id: 2, name: "Al-Baqarah", revelation_place: "madinah" }, + { surah_id: 78, name: "An-Naba", revelation_place: "makkah" }, + { surah_id: 114, name: "An-Nas", revelation_place: "makkah" } +]; + +const surahById = new Map(surahs.map((s) => [s.surah_id, s])); +const juzByNumber = new Map(juzRanges.map((j) => [j.juz_number, j])); + +const app = document.getElementById("app"); +app.innerHTML = ` +

    Quran Metadata Preview (Browse by Juz)

    +

    Resolve ayah ranges and boundary surahs from metadata

    + + +
    +`; + +const juzSelect = app.querySelector("#juz"); +const result = app.querySelector("#result"); + +[1, 2, 30].forEach((n) => { + const opt = document.createElement("option"); + opt.value = String(n); + opt.textContent = `Juz ${n}`; + juzSelect.appendChild(opt); +}); + +const render = () => { + const selected = Number(juzSelect.value); + const range = juzByNumber.get(selected); + if (!range) { + result.textContent = "Range not found"; + return; + } + + const fromSurah = surahById.get(range.from_surah)?.name || range.from_surah; + const toSurah = surahById.get(range.to_surah)?.name || range.to_surah; + + result.innerHTML = ` +
    From: ${range.from_surah}:${range.from_ayah} (${fromSurah})
    +
    To: ${range.to_surah}:${range.to_ayah} (${toSurah})
    + `; +}; + +juzSelect.addEventListener("change", render); +juzSelect.value = "30"; +render(); +``` + +## 7) Common Mistakes to Avoid + +- Treating metadata as text content instead of navigation data. +- Hardcoding ranges instead of using metadata package values. +- Ignoring enum fields (for example revelation place) during filtering. + +## 8) When to Request Updates or Changes + +Open an issue if you find: + +- Incorrect structural ranges +- Missing metadata records +- Broken json/sqlite links + +Issue tracker: + +- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) + +## Related Docs + +- [Tutorials Index](tutorials.md) +- [Quran Metadata Guide](resource-quran-metadata.md) +- [Quran Script Guide](resource-quran-script.md) +- [Surah Information Guide](resource-surah-information.md) diff --git a/docs/tutorial-quran-script-end-to-end.md b/docs/tutorial-quran-script-end-to-end.md new file mode 100644 index 00000000..264c41db --- /dev/null +++ b/docs/tutorial-quran-script-end-to-end.md @@ -0,0 +1,269 @@ +# Tutorial 5: Quran Script End-to-End + +This tutorial is for users who want to download Quran Script data and render Arabic text reliably in apps. + +## 1) What This Resource Is + +Quran Script resources provide Arabic Quran text in script-specific formats (for example: word-by-word and ayah-by-ayah variants). + +Depending on the selected package, script data can include: + +- Verse-level text (`verse_key`, `text`) +- Script metadata (`script_type`, `font_family`) +- Word-level arrays (`words[].position`, `words[].text`, `words[].location`) +- Navigation metadata (`page_number`, `juz_number`, `hizb_number`) + +Primary category: + +- [https://qul.tarteel.ai/resources/quran-script](https://qul.tarteel.ai/resources/quran-script) + +## 2) When to Use It + +Use Quran Script data when you are building: + +- Arabic Quran readers +- Word-by-word reading and study interfaces +- Features that require stable ayah or word keys for joins (translation, tafsir, audio sync) + +## 3) How to Get Your First Example Resource + +1. Open [https://qul.tarteel.ai/resources/quran-script](https://qul.tarteel.ai/resources/quran-script). +2. Keep the default listing order and open the first published card. +3. Confirm the detail page includes: + - `Preview` tab + - `Help` tab +4. Confirm available downloads: + - `sqlite` + - `json` +5. Confirm whether the package is marked `Word by word` or `Ayah by ayah`. + +This keeps onboarding concrete without hardcoding a resource ID. + +## 4) What the Preview Shows (Website-Aligned) + +On the script detail page: + +- `Preview` tab: + - `Jump to Ayah` selector + - Previous/next ayah navigation + - Rendered Arabic script output (often word-by-word blocks in word resources) +- `Help` tab: + - Sample JSON structure + - Field descriptions (`verse_key`, `text`, `script_type`, `font_family`, `words`) + - Rendering notes and usage examples (CSS + JS) + +Practical meaning: + +- You should treat `verse_key` and `words[].location` as canonical join keys. +- You should apply the provided `font_family` (or compatible fallback) for accurate rendering. + +## 5) Download and Use (Step-by-Step) + +1. Download selected script package (`json` or `sqlite`). +2. Inspect core fields: + - `verse_key` in `surah:ayah` + - `text` + - `script_type` + - `font_family` + - `words` (if word-by-word package) +3. Normalize keys in your app: + - Ayah key: `surah:ayah` + - Word key: `surah:ayah:word` +4. Store verse and word rows in indexed tables/maps. +5. Join with translation/tafsir/recitation by shared ayah keys. +6. Apply RTL direction + script font in UI. +7. Validate on one full surah and several random ayahs. + +Starter integration snippet (JavaScript): + +```javascript +// Build fast verse lookup by canonical ayah key. +const buildVerseIndex = (rows) => + rows.reduce((index, row) => { + index[row.verse_key] = { + text: row.text, + scriptType: row.script_type, + fontFamily: row.font_family, + words: Array.isArray(row.words) ? row.words : [] + }; + return index; + }, {}); + +// Render one verse with script-aware font + RTL settings. +const renderVerse = (container, verseRecord) => { + container.dir = "rtl"; + container.style.textAlign = "right"; + container.style.fontFamily = `${verseRecord.fontFamily || "serif"}, "Amiri Quran", "Noto Naskh Arabic", serif`; + container.textContent = verseRecord.text; +}; + +// Optional: render word-by-word blocks when word data exists. +const renderWordBlocks = (container, words) => { + container.innerHTML = ""; + words + .slice() + .sort((a, b) => a.position - b.position) + .forEach((word) => { + const chip = document.createElement("span"); + chip.textContent = word.text; + chip.title = word.location; // e.g., 1:1:2 + chip.style.display = "inline-block"; + chip.style.padding = "6px 10px"; + chip.style.margin = "4px"; + chip.style.border = "1px solid #e2e8f0"; + chip.style.borderRadius = "8px"; + container.appendChild(chip); + }); +}; +``` + +## 6) Real-World Example: Render One Ayah (Verse + Words) + +Goal: + +- User selects an ayah and sees both full Arabic verse text and word-by-word chips. + +Inputs: + +- Quran Script package (word-by-word variant) + +Processing: + +1. User selects ayah key (example: `73:4`). +2. App loads verse row by `verse_key`. +3. App renders full verse text using script-aware font. +4. App renders sorted `words[]` as chips. + +Expected output: + +- Correct Arabic script display. +- Word order remains stable by `position`. +- Keys remain compatible with translation/tafsir/audio joins. + +Interactive preview (temporary sandbox): + +You can edit this code for testing. Edits are not saved and may not persist after refresh. + +```playground-js +// This playground mirrors Quran Script help concepts: +// verse_key + text + script_type + font_family + words[] with location keys. + +const scriptRows = [ + { + verse_key: "73:4", + text: "أَوۡ زِدۡ عَلَيۡهِ وَرَتِّلِ ٱلۡقُرۡءَانَ تَرۡتِيلًا", + script_type: "text_qpc_hafs", + font_family: "qpc-hafs", + words: [ + { position: 1, text: "أَوۡ", location: "73:4:1" }, + { position: 2, text: "زِدۡ", location: "73:4:2" }, + { position: 3, text: "عَلَيۡهِ", location: "73:4:3" }, + { position: 4, text: "وَرَتِّلِ", location: "73:4:4" }, + { position: 5, text: "ٱلۡقُرۡءَانَ", location: "73:4:5" }, + { position: 6, text: "تَرۡتِيلًا", location: "73:4:6" } + ] + }, + { + verse_key: "1:1", + text: "بِسۡمِ ٱللَّهِ ٱلرَّحۡمَٰنِ ٱلرَّحِيمِ", + script_type: "text_qpc_hafs", + font_family: "qpc-hafs", + words: [ + { position: 1, text: "بِسۡمِ", location: "1:1:1" }, + { position: 2, text: "ٱللَّهِ", location: "1:1:2" }, + { position: 3, text: "ٱلرَّحۡمَٰنِ", location: "1:1:3" }, + { position: 4, text: "ٱلرَّحِيمِ", location: "1:1:4" } + ] + } +]; + +// Create verse lookup by verse_key. +const verseByKey = scriptRows.reduce((index, row) => { + index[row.verse_key] = row; + return index; +}, {}); + +const app = document.getElementById("app"); +app.innerHTML = ` +

    Quran Script Preview (Verse + Word by Word)

    +

    Shows how verse_key and words[].location map to rendering + integration keys

    + + +
    +
    +
    +`; + +const ayahSelect = app.querySelector("#ayah"); +const metaBox = app.querySelector("#meta"); +const verseBox = app.querySelector("#verse"); +const wordsBox = app.querySelector("#words"); + +const renderAyah = (ayahKey) => { + const verse = verseByKey[ayahKey]; + if (!verse) { + verseBox.textContent = "(Verse not found)"; + wordsBox.innerHTML = ""; + metaBox.textContent = ""; + return; + } + + // Show script metadata users should carry into app rendering rules. + metaBox.textContent = `verse_key: ${verse.verse_key} | script_type: ${verse.script_type} | font_family: ${verse.font_family}`; + + // Full verse rendering. + verseBox.textContent = verse.text; + + // Word-by-word rendering sorted by position. + wordsBox.innerHTML = ""; + verse.words + .slice() + .sort((a, b) => a.position - b.position) + .forEach((word) => { + const chip = document.createElement("span"); + chip.textContent = word.text; + chip.title = word.location; + chip.style.display = "inline-block"; + chip.style.padding = "6px 10px"; + chip.style.margin = "4px"; + chip.style.border = "1px solid #e2e8f0"; + chip.style.borderRadius = "8px"; + chip.style.fontFamily = "'KFGQPC Uthmanic Script HAFS','Amiri Quran','Noto Naskh Arabic','Scheherazade New',serif"; + wordsBox.appendChild(chip); + }); +}; + +ayahSelect.addEventListener("change", (event) => renderAyah(event.target.value)); +renderAyah(ayahSelect.value); +``` + +## 7) Common Mistakes to Avoid + +- Joining script data to other resources using row order instead of keys. +- Ignoring `font_family` and then assuming text is wrong when rendering is visually off. +- Treating word-by-word and ayah-by-ayah packages as interchangeable shapes. +- Ignoring `words[].position` and rendering words out of order. + +## 8) When to Request Updates or Changes + +Open an issue if you find: + +- Missing ayah rows or mismatched `verse_key` values +- Broken json/sqlite links for script resources +- Incorrect word order/location keys in word-by-word exports +- Metadata inconsistencies in `script_type` or `font_family` + +Issue tracker: + +- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) + +## Related Docs + +- [Tutorials Index](tutorials.md) +- [Quran Script Guide](resource-quran-script.md) +- [Translations Guide](resource-translations.md) +- [Tafsirs Guide](resource-tafsirs.md) +- [Downloading and Using Data](downloading-data.md) diff --git a/docs/tutorial-recitation-end-to-end.md b/docs/tutorial-recitation-end-to-end.md new file mode 100644 index 00000000..07b7ecce --- /dev/null +++ b/docs/tutorial-recitation-end-to-end.md @@ -0,0 +1,207 @@ +# Tutorial 1: Recitation End-to-End + +This tutorial is for users who want to download recitation data and build a minimal synced playback experience. + +## 1) What This Resource Is + +Recitation resources provide Quran audio and related timing metadata. + +Depending on the selected package, you can get: + +- Surah-by-surah audio (gapless playback) +- Ayah-by-ayah audio (gapped playback) +- Segment timing arrays used for synchronized word or ayah highlighting + +Primary category: + +- [https://qul.tarteel.ai/resources/recitation](https://qul.tarteel.ai/resources/recitation) + +## 2) When to Use It + +Use recitation data when you are building: + +- Quran audio players +- Memorization or revision tools +- Reading experiences that sync highlights to playback + +## 3) How to Get Your First Example Resource + +1. Open [https://qul.tarteel.ai/resources/recitation](https://qul.tarteel.ai/resources/recitation). +2. Open the first published card in the default page order. +3. Confirm the resource detail page includes: + - A `Recitation` preview tab + - A `Help` tab with segment format examples +4. Download the available file format (`JSON`, `SQLite`, or both). + +This avoids hardcoding a resource ID while keeping onboarding concrete. + +## 4) What the Preview Shows (Website-Aligned) + +On the recitation detail page, the preview helps you validate usability before download: + +- `Recitation` tab: + - Jump-to-ayah selector + - Previous/next ayah navigation + - Audio player with timing-driven highlighting behavior +- `Help` tab: + - Difference between surah-by-surah and ayah-by-ayah data + - Segment/timestamp sample formats for integration + +Practical meaning: + +- If segment arrays are present, you can build synchronized highlighting. +- If segment arrays are absent, treat the resource as audio-only playback. + +## 5) Download and Use (Step-by-Step) + +1. Download your selected recitation package. +2. Inspect fields before integration: + - Ayah identity fields (`surah`, `ayah`, or `surah:ayah`) + - Timing fields (`segments`, `timestamp_from`, `timestamp_to`) when provided + - Audio pointer field (`audio_url` or equivalent) +3. Normalize to a consistent key in your app. +4. Join recitation rows with Quran text rows using the same ayah key. +5. Build a minimal player loop. +6. Test with at least one full surah. + +Starter integration snippet (JavaScript): + +```javascript +// Convert mixed source fields into one stable key format (e.g., "2:255"). +const normalizeAyahKey = (row) => { + if (row.ayah_key) return row.ayah_key; + return `${row.surah}:${row.ayah}`; +}; + +// Build a quick lookup so playback can find timing/audio by ayah key. +const buildTimingIndex = (recitationRows) => { + return recitationRows.reduce((index, row) => { + const key = normalizeAyahKey(row); + index[key] = { + audioUrl: row.audio_url, + from: row.timestamp_from, + to: row.timestamp_to, + segments: Array.isArray(row.segments) ? row.segments : [] + }; + return index; + }, {}); +}; + +// Merge Quran text rows with recitation timing rows for display + sync. +const joinTextWithTiming = (scriptRows, timingIndex) => { + return scriptRows + .map((row) => { + const ayahKey = `${row.surah}:${row.ayah}`; + return { ...row, ayahKey, timing: timingIndex[ayahKey] || null }; + }) + // Keep only rows we can actually sync. + .filter((row) => row.timing); +}; +``` + +## 6) Real-World Example: Play One Surah with Live Ayah Highlighting + +Goal: + +- User plays a surah and sees the currently recited ayah highlighted in real time. + +Inputs: + +- Recitation resource (audio + timings when available) +- Quran Script resource (ayah text) + +Processing: + +1. User selects a surah. +2. App loads surah text from Quran Script. +3. App loads matching recitation timing map for the same surah. +4. On each player time update, app finds the active ayah time window. +5. UI updates highlight state for that ayah. + +Expected output: + +- Audio playback is smooth. +- Highlight transitions follow recitation timing. +- Ayah text and audio stay mapped by the same ayah key. + +Interactive preview (temporary sandbox): + +You can edit this code for testing. Edits are not saved and may not persist after refresh. + +```playground-js +const words = [ + { wordKey: "1:1:1", text: "بِسۡمِ", from: 0, to: 2 }, + { wordKey: "1:1:2", text: "ٱللَّهِ", from: 2, to: 4 }, + { wordKey: "1:1:3", text: "ٱلرَّحۡمَٰنِ", from: 4, to: 6 }, + { wordKey: "1:1:4", text: "ٱلرَّحِيمِ", from: 6, to: 8 } +]; + +const app = document.getElementById("app"); +app.innerHTML = ` +

    Word Playback Preview (Real Arabic Text)

    +

    Simulated timestamp-driven highlighting for Surah 1:1 words

    +
      +`; + +const list = app.querySelector("#word-list"); +words.forEach((word) => { + const li = document.createElement("li"); + li.dataset.wordKey = word.wordKey; + li.style.padding = "8px 10px"; + li.style.marginBottom = "6px"; + li.style.borderRadius = "8px"; + li.style.border = "1px solid #e2e8f0"; + li.style.transition = "all 120ms ease"; + li.style.textAlign = "right"; + li.style.fontSize = "1.2rem"; + li.textContent = word.text; + list.appendChild(li); +}); + +let currentSecond = 0; +const totalDuration = words[words.length - 1].to + 1; + +const setActiveWord = (second) => { + const active = words.find((word) => second >= word.from && second < word.to); + list.querySelectorAll("li").forEach((li) => { + const isActive = active && li.dataset.wordKey === active.wordKey; + li.style.background = isActive ? "#dcfce7" : "#ffffff"; + li.style.borderColor = isActive ? "#22c55e" : "#e2e8f0"; + li.style.fontWeight = isActive ? "700" : "400"; + }); +}; + +setActiveWord(currentSecond); +setInterval(() => { + currentSecond = (currentSecond + 1) % totalDuration; + setActiveWord(currentSecond); +}, 1000); +``` + +## 7) Common Mistakes to Avoid + +- Assuming all recitations include segments. +- Mixing key formats without normalization. +- Joining recitation to text using row order instead of ayah identity. +- Treating preview success for one ayah as proof that whole-surah mapping is correct. + +## 8) When to Request Updates or Changes + +Open an issue if you find: + +- Broken or inaccessible audio file links +- Segment/timestamp values that do not match audible recitation +- Missing ayah mappings in downloaded files +- Inconsistent reciter or package metadata + +Issue tracker: + +- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) + +## Related Docs + +- [Tutorials Index](tutorials.md) +- [Recitations Guide](resource-recitations.md) +- [Quran Script Guide](resource-quran-script.md) +- [Data Model](data-model.md) +- [Downloading and Using Data](downloading-data.md) diff --git a/docs/tutorial-similar-ayah-end-to-end.md b/docs/tutorial-similar-ayah-end-to-end.md new file mode 100644 index 00000000..a745f3f5 --- /dev/null +++ b/docs/tutorial-similar-ayah-end-to-end.md @@ -0,0 +1,339 @@ +# Tutorial 13: Similar Ayah End-to-End + +This tutorial is for users who want to show ayah-to-ayah similarity results (matched words, coverage, and score). + +## 1) What This Resource Is + +Similar Ayah resources provide ayah-level similarity records. + +Typical fields include: + +- `verse_key` +- `matched_ayah_key` +- `matched_words_count` +- `coverage` +- `score` +- `match_words_range` (word position range in matched ayah) + +Primary category: + +- [https://qul.tarteel.ai/resources/similar-ayah](https://qul.tarteel.ai/resources/similar-ayah) + +## 2) When to Use It + +Use similar-ayah data when building: + +- Compare-verses tools +- Pattern discovery across Quranic wording +- Memorization reinforcement features + +## 3) How to Get Your First Example Resource + +1. Open [https://qul.tarteel.ai/resources/similar-ayah](https://qul.tarteel.ai/resources/similar-ayah). +2. Keep default listing order and open the first published card. +3. Confirm the detail page includes: + - `Similar Ayah Preview` tab + - `Help` tab +4. Confirm available downloads (`json`, `sqlite`). + +This keeps onboarding concrete without hardcoded IDs. + +## 4) What the Preview Shows (Website-Aligned) + +On similar-ayah detail pages: + +- `Similar Ayah Preview` tab: + - `Jump to Ayah` + - Source ayah card for selected ayah + - "X has N similar ayahs" banner + - List of matching ayahs for selected ayah + - Highlighted matched words inside matched ayah text + - Match summaries ("This ayah matches N words with X% coverage and a similarity score of Y") +- `Help` tab: + - Field definitions (`verse_key`, `matched_ayah_key`, `matched_words_count`, `coverage`, `score`, `match_words_range`) + - Export format notes + +Practical meaning: + +- Similarity is scored data, not binary “same/different”. +- You should sort by score/coverage and let users inspect why matches appear. + +## 5) Download and Use (Step-by-Step) + +1. Download selected package (`json` or `sqlite`). +2. Import similarity rows. +3. Group rows by `verse_key`. +4. Sort matches by score and/or coverage. +5. Join with script text for readable display. +6. Use `match_words_range` to highlight matched words in the matched ayah. + +Starter integration snippet (JavaScript): + +```javascript +const matchesForAyah = (rows, ayahKey) => + rows + .filter((row) => row.verse_key === ayahKey) + .sort((a, b) => b.score - a.score || b.coverage - a.coverage); + +const summaryText = (row) => + `This ayah matches ${row.matched_words_count} words with ${row.coverage}% coverage and a similarity score of ${row.score}`; + +const isMatchedWordPosition = (wordIndexOneBased, range) => { + if (!Array.isArray(range) || range.length !== 2) return false; + const [from, to] = range; + return wordIndexOneBased >= from && wordIndexOneBased <= to; +}; +``` + +## 6) Real-World Example: Top Similar Ayahs Panel + +Goal: + +- User opens an ayah and sees top similar ayahs ranked by score. + +Inputs: + +- Similar Ayah package +- Quran Script package + +Processing: + +1. Filter rows by selected `verse_key`. +2. Sort by `score` (and `coverage` as tie-breaker). +3. Join matched ayah text and highlight words by `match_words_range`. +4. Display per-match summary sentence. + +Expected output: + +- Ranked similarity results that are interpretable, with visible highlighted overlap. + +Interactive preview (temporary sandbox): + +You can edit this code for testing. Edits are not saved and may not persist after refresh. + +```playground-js +// Preview-aligned sandbox: +// Jump to Ayah -> source ayah card -> similar ayah cards with highlighted matched words. +// Based on the behavior shown in similar-ayah resource 74. + +const sourceAyahTextByKey = { + "1:1": "بِسۡمِ ٱللَّهِ ٱلرَّحۡمَٰنِ ٱلرَّحِيمِ ١", + "1:3": "ٱلرَّحۡمَٰنِ ٱلرَّحِيمِ ٣" +}; + +const similarRows = [ + { + verse_key: "1:1", + matched_ayah_key: "27:30", + matched_words_count: 4, + coverage: 50, + score: 80, + match_words_range: [5, 8] + }, + { + verse_key: "1:1", + matched_ayah_key: "59:22", + matched_words_count: 2, + coverage: 15, + score: 56, + match_words_range: [12, 13] + }, + { + verse_key: "1:1", + matched_ayah_key: "1:3", + matched_words_count: 2, + coverage: 100, + score: 50, + match_words_range: [1, 2] + }, + { + verse_key: "1:1", + matched_ayah_key: "41:2", + matched_words_count: 2, + coverage: 50, + score: 50, + match_words_range: [3, 4] + }, + { + verse_key: "1:3", + matched_ayah_key: "1:1", + matched_words_count: 2, + coverage: 100, + score: 50, + match_words_range: [3, 4] + } +]; + +// Keep literals as UTF-8 Arabic text. +const matchedAyahTextByKey = { + "27:30": "إِنَّهُۥ مِن سُلَيۡمَٰنَ وَإِنَّهُۥ بِسۡمِ ٱللَّهِ ٱلرَّحۡمَٰنِ ٱلرَّحِيمِ ٣٠", + "59:22": "هُوَ ٱللَّهُ ٱلَّذِي لَآ إِلَٰهَ إِلَّا هُوَۖ عَٰلِمُ ٱلۡغَيۡبِ وَٱلشَّهَٰدَةِۖ هُوَ ٱلرَّحۡمَٰنُ ٱلرَّحِيمُ ٢٢", + "1:3": "ٱلرَّحۡمَٰنِ ٱلرَّحِيمِ ٣", + "41:2": "تَنزِيلٞ مِّنَ ٱلرَّحۡمَٰنِ ٱلرَّحِيمِ ٢", + "1:1": "بِسۡمِ ٱللَّهِ ٱلرَّحۡمَٰنِ ٱلرَّحِيمِ ١" +}; + +// Help-aligned schema and sample block (as shown on the resource Help tab). +const helpSchema = [ + { column: "verse_key", type: "TEXT", description: "The ayah key that similar ayahs belongs to (e.g., 1:1)." }, + { column: "matched_ayah_key", type: "TEXT", description: "Matching ayah key that shares similar wording." }, + { column: "matched_words_count", type: "INTEGER", description: "Number of matching words between the two ayahs." }, + { column: "coverage", type: "INTEGER", description: "Percentage of words in the source ayah that matched." }, + { column: "score", type: "INTEGER", description: "Similarity score between the source and matched ayah (0–100)." }, + { column: "match_words_range", type: "TEXT", description: "Word position range in matching ayah that matched with source ayah." } +]; + +const helpExample = { + verse_key: "1:1", + matched_ayah_key: "27:30", + matched_words_count: 4, + coverage: 100, + score: 100, + match_words_range: [5, 8] +}; + +const highlightMatchedWords = (ayahText, range) => + String(ayahText || "") + .split(/\s+/) + .filter(Boolean) + .map((word, index) => { + const pos = index + 1; + const inRange = + Array.isArray(range) && + range.length === 2 && + pos >= range[0] && + pos <= range[1]; + if (!inRange) return word; + return `${word}`; + }) + .join(" "); + +const app = document.getElementById("app"); +app.innerHTML = ` +

      Similar Ayah Preview

      +

      Preview + Help on one screen: interactive similar-ayah cards and schema/sample reference

      +
      + Top section mirrors Preview. Bottom section mirrors the Help field/schema explanation. +
      +
      + +
      + + +
      +
      Select the source ayah to compare similar matches.
      +
      +
      +
      +
      +
      +`; + +const ayahSelect = app.querySelector("#ayah"); +const source = app.querySelector("#source"); +const summary = app.querySelector("#summary"); +const result = app.querySelector("#result"); +const help = app.querySelector("#help"); + +const render = () => { + const key = ayahSelect.value; + source.innerHTML = ` +
      ${key}
      +
      ${sourceAyahTextByKey[key] || ""}
      + `; + + const rows = similarRows + .filter((r) => r.verse_key === key) + .sort((a, b) => b.score - a.score || b.coverage - a.coverage); + + summary.textContent = `${key} has ${rows.length} similar ayahs`; + + if (rows.length === 0) { + result.textContent = "No similar ayahs found."; + return; + } + + result.innerHTML = rows + .map((r) => { + const ayahText = matchedAyahTextByKey[r.matched_ayah_key] || ""; + return ` +
      +
      ${r.matched_ayah_key}
      +
      + ${highlightMatchedWords(ayahText, r.match_words_range)} +
      + + This ayah matches ${r.matched_words_count} words with ${r.coverage}% coverage and a similarity score of ${r.score} + +
      + `; + }) + .join(""); + + const schemaRows = helpSchema + .map( + (field) => ` + + ${field.column} + ${field.type} + ${field.description} + + ` + ) + .join(""); + + help.innerHTML = ` +

      Help Reference (Schema + Example)

      +

      QUL exports similar ayah data in JSON and SQLite. Core fields:

      +
      + + + + + + + + + ${schemaRows} +
      ColumnTypeDescription
      +
      +
      Help sample (if 1:1 matches 27:30):
      +
      ${JSON.stringify(helpExample, null, 2)}
      +
      + This ayah matches ${helpExample.matched_words_count} words, with ${helpExample.coverage}% coverage and a similarity score of ${helpExample.score}. + Word ${helpExample.match_words_range[0]} to ${helpExample.match_words_range[1]} matched with source ayah. +
      + `; +}; + +ayahSelect.addEventListener("change", render); +render(); +``` + +## 7) Common Mistakes to Avoid + +- Treating similarity score as strict equivalence. +- Ignoring coverage and matched-word context. +- Not sorting results (raw order can be misleading). + +## 8) When to Request Updates or Changes + +Open an issue if you find: + +- Mismatched `verse_key`/`matched_ayah_key` pairs +- Invalid score/coverage values +- Broken json/sqlite downloads + +Issue tracker: + +- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) + +## Related Docs + +- [Tutorials Index](tutorials.md) +- [Similar Ayah Guide](resource-similar-ayah.md) +- [Mutashabihat Guide](resource-mutashabihat.md) +- [Quran Script Guide](resource-quran-script.md) diff --git a/docs/tutorial-surah-information-end-to-end.md b/docs/tutorial-surah-information-end-to-end.md new file mode 100644 index 00000000..05820a10 --- /dev/null +++ b/docs/tutorial-surah-information-end-to-end.md @@ -0,0 +1,285 @@ +# Tutorial 9: Surah Information End-to-End + +This tutorial is for users who want to show chapter-level context (names, summaries, key notes) before ayah reading. + +## 1) What This Resource Is + +Surah Information resources provide chapter metadata and explanatory context. + +Typical fields include: + +- Surah identity (`surah_id` / number) +- Names/titles +- Short summary text +- Detailed long-form content (often with sections such as `Name`, `Period of Revelation`, `Theme`) +- Language/source metadata +- Revelation context and related descriptors + +Primary category: + +- [https://qul.tarteel.ai/resources/surah-info](https://qul.tarteel.ai/resources/surah-info) + +## 2) When to Use It + +Use surah-info data when building: + +- Surah intro cards before ayah list +- Chapter overview pages +- Learning experiences with chapter context + +## 3) How to Get Your First Example Resource + +1. Open [https://qul.tarteel.ai/resources/surah-info](https://qul.tarteel.ai/resources/surah-info). +2. Keep default listing order and open the first published card. +3. Confirm the detail page includes: + - `Surah Info Preview` tab + - `Help` tab +4. Confirm available downloads (`csv`, `json`, `sqlite`). + +This keeps onboarding concrete without hardcoded IDs. + +## 4) What the Preview Shows (Website-Aligned) + +On surah-info detail pages: + +- `Surah Info Preview` tab: + - `Jump to Surah` + - Next/previous surah navigation + - Short summary paragraph + - Detailed content blocks (for example `Name`, `Period of Revelation`, `Theme`) +- `Help` tab: + - Resource purpose and coverage (themes/topics/reasons for revelation/summaries) + - Format availability (`SQLite`, `CSV`, `JSON`) + - Note that some records include both short summary and detailed long-form text + - Note that detailed text may include HTML tags for formatting + +Practical meaning: + +- Surah-info is chapter-level context, not ayah-level text. +- Use it as a companion layer above script/translation views. + +Full Surah 1 example content (from a surah-info detail page, in the same structure users see): + +- Intro summary: + - This Surah is named Al-Fatihah because of its subject matter. Fatihah is that which opens a subject or a book or any other thing. In other words, Al-Fatihah is a sort of preface. +- Name: + - This Surah is named Al-Fatihah because of its subject matter. Fatihah is that which opens a subject or a book or any other thing. In other words, Al-Fatihah is a sort of preface. +- Period of Revelation: + - Surah Al-Fatihah is one of the very earliest Revelations to the Holy Prophet. As a matter of fact, we learn from authentic traditions that it was the first complete Surah that was revealed to Muhammad (Allah's peace be upon him). Before this, only a few miscellaneous verses were revealed which form parts of Alaq, Muzzammil, Muddaththir, etc. +- Theme: + - This Surah is in fact a prayer that Allah has taught to all those who want to make a study of His book. It has been placed at the very beginning of the Quran to teach this lesson to the reader: if you sincerely want to benefit from the Quran, you should offer this prayer to the Lord of the Universe. + - This preface is meant to create a strong desire in the heart of the reader to seek guidance from the Lord of the Universe Who alone can grant it. Thus Al-Fatihah indirectly teaches that the best thing for a man is to pray for guidance to the straight path, to study the Quran with the mental attitude of a seeker searching for the truth, and to recognize the fact that the Lord of the Universe is the source of all knowledge. He should, therefore, begin the study of the Quran with a prayer to Him for guidance. + - From this theme, it becomes clear that the real relation between Al-Fatihah and the Quran is not that of an introduction to a book but that of a prayer and its answer. Al-Fatihah is the prayer from the servant and the Quran is the answer from the Master to the servant's prayer. The servant prays to Allah to show him guidance and the Master places the whole of the Quran before him in answer to his prayer, as if to say, "This is the Guidance you begged from Me." + +## 5) Download and Use (Step-by-Step) + +1. Download selected package (`csv`, `json`, or `sqlite`). +2. Import surah records by surah number. +3. Normalize language/source fields if multiple sources are used. +4. Separate short summary and detailed content fields in your data model. +5. If rendering detailed HTML content, sanitize before output in production. +6. Cache chapter metadata for quick chapter loads. +7. Render intro card before ayah list. + +Starter integration snippet (JavaScript): + +```javascript +const buildSurahInfoIndex = (rows) => + rows.reduce((index, row) => { + index[row.surah_id] = row; + return index; + }, {}); + +// Use a proper HTML sanitizer in production; this is only a minimal placeholder. +const sanitizeHtml = (html) => + String(html || "") + .replace(/[\s\S]*?<\/script>/gi, "") + .replace(/on\w+="[^"]*"/gi, ""); + +const renderDetailedSections = (surahInfo) => { + if (Array.isArray(surahInfo.sections) && surahInfo.sections.length > 0) { + return surahInfo.sections + .map( + (section) => + `

      ${section.title}

      ${section.text}

      ` + ) + .join(""); + } + + // Some sources provide long-form chapter details as HTML. + return sanitizeHtml(surahInfo.detailed_html || ""); +}; + +const renderSurahHeader = (container, surahInfo) => { + if (!surahInfo) { + container.textContent = "Surah info not found"; + return; + } + + container.innerHTML = ` +

      ${surahInfo.name}

      +

      ${surahInfo.short_summary || ""}

      + Revelation: ${surahInfo.revelation_place || "unknown"} +
      ${renderDetailedSections(surahInfo)}
      + `; +}; +``` + +## 6) Real-World Example: Chapter Intro Card + +Goal: + +- Show surah context before rendering ayahs. + +Inputs: + +- Surah Information package +- Quran Script package + +Processing: + +1. User opens a surah. +2. App fetches chapter info by `surah_id`. +3. App renders intro card, then ayah list. + +Expected output: + +- Users get chapter context before reading ayah content. + +Interactive preview (temporary sandbox): + +You can edit this code for testing. Edits are not saved and may not persist after refresh. + +```playground-js +// This sandbox shows short summary + detailed sectioned content rendering. + +const surahInfoById = { + 1: { + name: "Al-Fatihah", + language: "English", + revelation_place: "makkah", + short_summary: + "This Surah is named Al-Fatihah because of its subject matter. Fatihah is that which opens a subject or a book or any other thing. In other words, Al-Fatihah is a sort of preface.", + sections: [ + { + title: "Name", + text: + "This Surah is named Al-Fatihah because of its subject matter. Fatihah is that which opens a subject or a book or any other thing. In other words, Al-Fatihah is a sort of preface." + }, + { + title: "Period of Revelation", + text: + "Surah Al-Fatihah is one of the very earliest Revelations to the Holy Prophet. As a matter of fact, we learn from authentic traditions that it was the first complete Surah that was revealed to Muhammad (Allah's peace be upon him). Before this, only a few miscellaneous verses were revealed which form parts of Alaq, Muzzammil, Muddaththir, etc." + }, + { + title: "Theme", + text: + "This Surah is in fact a prayer that Allah has taught to all those who want to make a study of His book. It has been placed at the very beginning of the Quran to teach this lesson to the reader: if you sincerely want to benefit from the Quran, you should offer this prayer to the Lord of the Universe." + }, + { + title: "Theme (continued)", + text: + "This preface is meant to create a strong desire in the heart of the reader to seek guidance from the Lord of the Universe Who alone can grant it. Thus Al-Fatihah indirectly teaches that the best thing for a man is to pray for guidance to the straight path, to study the Quran with the mental attitude of a seeker searching for the truth, and to recognize the fact that the Lord of the Universe is the source of all knowledge. He should, therefore, begin the study of the Quran with a prayer to Him for guidance." + }, + { + title: "Theme (prayer and answer)", + text: + "From this theme, it becomes clear that the real relation between Al-Fatihah and the Quran is not that of an introduction to a book but that of a prayer and its answer. Al-Fatihah is the prayer from the servant and the Quran is the answer from the Master to the servant's prayer. The servant prays to Allah to show him guidance and the Master places the whole of the Quran before him in answer to his prayer, as if to say, \"This is the Guidance you begged from Me.\"" + } + ] + }, + 36: { + name: "Ya-Sin", + language: "English", + revelation_place: "makkah", + short_summary: "Emphasizes resurrection, signs of Allah, and prophetic warning.", + sections: [ + { + title: "Name", + text: "Named after the disjointed letters Ya-Sin." + }, + { + title: "Period of Revelation", + text: "Makki surah focused on belief and accountability." + }, + { + title: "Theme", + text: "Calls to faith, reflection, and responsibility before Allah." + } + ] + } +}; + +const app = document.getElementById("app"); +app.innerHTML = ` +

      Surah Info Preview

      +

      Render short + detailed chapter context before ayah content

      + + +
      +
      +`; + +const surahSelect = app.querySelector("#surah"); +const meta = app.querySelector("#meta"); +const card = app.querySelector("#card"); + +const render = () => { + const surahId = Number(surahSelect.value); + const info = surahInfoById[surahId]; + if (!info) { + meta.textContent = ""; + card.textContent = "Surah info not found"; + return; + } + + // Render all detailed sections so users can see complete chapter context. + const detailedSections = (info.sections || []) + .map( + (section) => + `
      +

      ${section.title}

      +

      ${section.text}

      +
      ` + ) + .join(""); + + meta.textContent = `Language: ${info.language} | Revelation: ${info.revelation_place}`; + card.innerHTML = ` +

      ${info.name}

      +

      Short summary: ${info.short_summary}

      +
      ${detailedSections}
      + `; +}; + +surahSelect.addEventListener("change", render); +render(); +``` + +## 7) Common Mistakes to Avoid + +- Treating surah-info as ayah-level content. +- Not handling multiple language/source variants. +- Forgetting to cache chapter metadata for repeated navigations. + +## 8) When to Request Updates or Changes + +Open an issue if you find: + +- Missing/incorrect surah descriptions +- Mismatched surah identifiers +- Broken `csv/json/sqlite` downloads + +Issue tracker: + +- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) + +## Related Docs + +- [Tutorials Index](tutorials.md) +- [Surah Information Guide](resource-surah-information.md) +- [Quran Metadata Guide](resource-quran-metadata.md) +- [Quran Script Guide](resource-quran-script.md) diff --git a/docs/tutorial-tafsir-end-to-end.md b/docs/tutorial-tafsir-end-to-end.md new file mode 100644 index 00000000..76e0245b --- /dev/null +++ b/docs/tutorial-tafsir-end-to-end.md @@ -0,0 +1,250 @@ +# Tutorial 4: Tafsir End-to-End + +This tutorial is for users who want to download tafsir data and render ayah-linked commentary reliably. + +## 1) What This Resource Is + +Tafsir resources provide commentary linked to ayah keys, with support for grouped commentary that can cover multiple ayahs. + +Depending on the selected package, tafsir may include: + +- Ayah-linked tafsir text +- Grouped tafsir mapping (one main ayah key shared by multiple ayahs) +- Optional HTML formatting tags inside text (``, ``, etc.) + +Primary category: + +- [https://qul.tarteel.ai/resources/tafsir](https://qul.tarteel.ai/resources/tafsir) + +## 2) When to Use It + +Use tafsir data when you are building: + +- Ayah detail views with commentary +- Study panels below Arabic/translation text +- Research experiences that compare tafsir sources + +## 2A) Tafsir vs Translation + +- `Translation` provides direct meaning transfer of ayah text into another language. +- `Tafsir` provides explanation, interpretation, and context around ayah meaning. +- Translation is usually one ayah-to-text mapping, while tafsir can be grouped across multiple ayahs. +- In integration terms: translation is mostly direct key-based rendering; tafsir needs grouped/pointer resolution. + +## 3) How to Get Your First Example Resource + +1. Open [https://qul.tarteel.ai/resources/tafsir](https://qul.tarteel.ai/resources/tafsir). +2. Keep the default listing order and open the first published card. +3. Confirm the resource detail page includes: + - `Tafsir Preview` tab + - `Help` tab +4. Confirm available downloads on the detail page: + - `json` + - `sqlite` + +This keeps onboarding concrete without hardcoding a resource ID. + +## 4) What the Preview Shows (Website-Aligned) + +On the tafsir detail page: + +- `Tafsir Preview` tab: + - `Jump to Ayah` selector + - Previous/next ayah navigation + - Arabic ayah block + tafsir text block +- `Help` tab: + - JSON export model for grouped tafsir + - SQLite columns for grouped/shared commentary + - Notes about tafsir text including formatting tags + +Practical meaning: + +- Tafsir is not always one-ayah = one-unique-text. +- Your app must resolve grouped/shared tafsir references correctly. + +## 5) Download and Use (Step-by-Step) + +1. Download your selected tafsir package (`json` or `sqlite`). +2. Inspect payload shape: + - JSON keys are ayah keys like `2:3` + - Value may be an object (main tafsir group) or a string (pointer to group ayah key) +3. Normalize ayah keys in one format (`surah:ayah`). +4. Build a tafsir resolver: + - If current ayah maps to an object, use its `text`. + - If current ayah maps to a string, follow that pointer and use the target group text. +5. Join with Quran Script and optionally Translation by ayah key. +6. Render commentary safely (sanitize HTML if rendering tags in production). +7. Validate grouped cases across consecutive ayahs. + +Starter integration snippet (JavaScript): + +```javascript +// Resolve tafsir text for an ayah key. +// JSON model can be: +// - object: { text, ayah_keys } +// - string: pointer to another ayah key where main text is stored +const resolveTafsirText = (tafsirByAyahKey, ayahKey) => { + const value = tafsirByAyahKey[ayahKey]; + if (!value) return null; + + // Main group record. + if (typeof value === "object" && value.text) { + return { + groupAyahKey: ayahKey, + ayahKeys: Array.isArray(value.ayah_keys) ? value.ayah_keys : [ayahKey], + text: value.text + }; + } + + // Pointer record. Example: "2:4": "2:3" + if (typeof value === "string") { + const main = tafsirByAyahKey[value]; + if (main && typeof main === "object" && main.text) { + return { + groupAyahKey: value, + ayahKeys: Array.isArray(main.ayah_keys) ? main.ayah_keys : [value], + text: main.text + }; + } + } + + return null; +}; +``` + +## 6) Real-World Example: One Tafsir for Multiple Ayahs + +Goal: + +- User opens an ayah and sees the correct tafsir even when commentary is shared across a range. + +Inputs: + +- Tafsir package (`json` or `sqlite`) +- Quran Script package + +Processing: + +1. User selects ayah key (example: `2:4`). +2. App looks up tafsir by ayah key. +3. If value is pointer (`"2:3"`), app resolves to main group record. +4. App renders resolved tafsir text and shows covered ayah keys. + +Expected output: + +- Correct commentary appears for both main ayah and grouped ayahs. +- No blank tafsir for grouped/pointer ayahs. + +Interactive preview (temporary sandbox): + +You can edit this code for testing. Edits are not saved and may not persist after refresh. + +```playground-js +// This playground mirrors the Help model shown on tafsir detail pages: +// object = main tafsir group, string = pointer to main group ayah key. + +const arabicByAyah = { + "2:3": "ٱلَّذِينَ يُؤۡمِنُونَ بِٱلۡغَيۡبِ وَيُقِيمُونَ ٱلصَّلَوٰةَ وَمِمَّا رَزَقۡنَٰهُمۡ يُنفِقُونَ", + "2:4": "وَٱلَّذِينَ يُؤۡمِنُونَ بِمَآ أُنزِلَ إِلَيۡكَ وَمَآ أُنزِلَ مِن قَبۡلِكَ وَبِٱلۡأٓخِرَةِ هُمۡ يُوقِنُونَ", + "2:5": "أُولَٰئِكَ عَلَىٰ هُدًى مِنْ رَبِّهِمْ وَأُولَٰئِكَ هُمُ الْمُفْلِحُونَ" +}; + +const tafsirByAyahKey = { + // Main tafsir group record. + "2:3": { + text: "The muttaqeen are those who believe in the unseen and remain steadfast in worship.", + ayah_keys: ["2:3", "2:4"] + }, + // Pointer record: ayah 2:4 uses tafsir text stored at 2:3. + "2:4": "2:3", + // Independent tafsir record. + "2:5": { + text: "These are upon guidance from their Lord, and they are the successful.", + ayah_keys: ["2:5"] + } +}; + +// Resolve current ayah to a main tafsir record. +const resolveTafsir = (ayahKey) => { + const value = tafsirByAyahKey[ayahKey]; + if (!value) return null; + + if (typeof value === "object" && value.text) { + return { groupAyahKey: ayahKey, text: value.text, ayahKeys: value.ayah_keys || [ayahKey] }; + } + + if (typeof value === "string") { + const main = tafsirByAyahKey[value]; + if (main && typeof main === "object" && main.text) { + return { groupAyahKey: value, text: main.text, ayahKeys: main.ayah_keys || [value] }; + } + } + + return null; +}; + +const app = document.getElementById("app"); +app.innerHTML = ` +

      Tafsir Preview (Grouped Ayah Aware)

      +

      Demonstrates object + pointer tafsir mapping from the Help format

      + + +
      +
      +

      +`; + +const ayahSelect = app.querySelector("#ayah"); +const arabicBox = app.querySelector("#arabic"); +const tafsirBox = app.querySelector("#tafsir"); +const groupInfo = app.querySelector("#group"); + +const renderAyah = (ayahKey) => { + arabicBox.textContent = arabicByAyah[ayahKey] || "(Arabic not found)"; + + const resolved = resolveTafsir(ayahKey); + if (!resolved) { + tafsirBox.textContent = "(Tafsir not found)"; + groupInfo.textContent = ""; + return; + } + + tafsirBox.textContent = resolved.text; + groupInfo.textContent = `Group source: ${resolved.groupAyahKey} | Covers: ${resolved.ayahKeys.join(", ")}`; +}; + +ayahSelect.addEventListener("change", (event) => renderAyah(event.target.value)); +renderAyah(ayahSelect.value); +``` + +## 7) Common Mistakes to Avoid + +- Assuming every ayah key contains direct tafsir text. +- Ignoring string-pointer records in JSON grouped exports. +- Failing to resolve `group_ayah_key` in SQLite exports. +- Rendering tafsir HTML without sanitization in production apps. + +## 8) When to Request Updates or Changes + +Open an issue if you find: + +- Grouped ayah references pointing to missing source keys +- Tafsir text assigned to wrong ayah ranges +- Broken json/sqlite download links +- Missing or inconsistent source metadata + +Issue tracker: + +- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) + +## Related Docs + +- [Tutorials Index](tutorials.md) +- [Tafsirs Guide](resource-tafsirs.md) +- [Quran Script Guide](resource-quran-script.md) +- [Translations Guide](resource-translations.md) +- [Downloading and Using Data](downloading-data.md) diff --git a/docs/tutorial-translation-end-to-end.md b/docs/tutorial-translation-end-to-end.md new file mode 100644 index 00000000..e1bf3646 --- /dev/null +++ b/docs/tutorial-translation-end-to-end.md @@ -0,0 +1,292 @@ +# Tutorial 3: Translation End-to-End + +This tutorial is for users who want to download translation data and show Arabic + translated text in a reliable way. + +## 1) What This Resource Is + +Translation resources provide translated Quran text keyed by ayah, with multiple export formats. + +Depending on the selected package, you may get: + +- Simple translation text (`JSON` / `SQLite`) +- Footnote-tagged translation (``) +- Inline-footnote translation (`[[...]]`) +- Text chunk format for structured rendering + +Primary category: + +- [https://qul.tarteel.ai/resources/translation](https://qul.tarteel.ai/resources/translation) + +## 2) When to Use It + +Use translation data when you are building: + +- Multilingual Quran readers +- Arabic + translation views for learning apps +- Search and discovery experiences in non-Arabic languages + +## 3) How to Get Your First Example Resource + +1. Open [https://qul.tarteel.ai/resources/translation](https://qul.tarteel.ai/resources/translation). +2. Keep the default listing order and open the first published card. +3. Confirm the resource detail page includes: + - `Translation Preview` tab + - `Help` tab +4. Confirm available download formats shown on the page: + - `simple.json` + - `simple.sqlite` + - Optional footnote/chunk variants (when provided by that translation) + +This keeps onboarding concrete without hardcoding a resource ID. + +## 4) What the Preview Shows (Website-Aligned) + +On the translation detail page: + +- `Translation Preview` tab: + - `Jump to Ayah` selector + - Previous/next ayah navigation + - Arabic ayah block + translated text block +- `Help` tab: + - Export format examples + - Simple structures (nested array, key-value) + - Footnote structures (tags, inline notes, text chunks) + +Practical meaning: + +- If you only need plain text, use a simple format. +- If you need footnotes or formatting control, use footnote-tag/chunk formats. + +## 5) Download and Use (Step-by-Step) + +1. Download your selected translation package. +2. Inspect what format you received: + - Plain string by ayah key + - Object with translation text + footnotes (`t`, `f`) + - Chunk array with mixed text/objects +3. Normalize to one stable key format in app code (recommended: `surah:ayah`). +4. Load Quran Script data for Arabic text. +5. Join Arabic + translation rows by the same ayah key. +6. Render translations by format-aware rules. +7. Validate at least 5 consecutive ayahs so you catch format edge cases. + +Starter integration snippet (JavaScript): + +```javascript +// Convert source rows to one stable key like "73:4". +const ayahKeyFromRow = (row) => row.ayah_key || `${row.surah}:${row.ayah}`; + +// Build lookup map for fast joins with Arabic script rows. +const buildTranslationIndex = (rows) => + rows.reduce((index, row) => { + index[ayahKeyFromRow(row)] = row.translation; + return index; + }, {}); + +// Normalize the different translation payload shapes into one renderable object. +const normalizeTranslationPayload = (payload) => { + // Simple plain-text translation. + if (typeof payload === "string") return { text: payload, notes: [] }; + + // Footnote-tag format: { t: "...1...", f: { x: "note" } } + if (payload && typeof payload === "object" && payload.t) { + const noteIds = []; + const text = payload.t.replace(/([^<]+)<\/sup>/g, (_, id, label) => { + noteIds.push(id); + return `[${label}]`; + }); + const notes = noteIds.map((id) => ({ id, text: payload.f?.[id] || "" })); + return { text, notes }; + } + + // Text chunk format: ["plain", {type: "i", text: "italic"}, {type: "f", f: "12", text: "1"}] + if (Array.isArray(payload)) { + const textParts = []; + const notes = []; + payload.forEach((chunk) => { + if (typeof chunk === "string") textParts.push(chunk); + else if (chunk?.type === "i") textParts.push(chunk.text); + else if (chunk?.type === "f") { + textParts.push(`[${chunk.text}]`); + notes.push({ id: chunk.f, text: `Footnote ${chunk.f}` }); + } + }); + return { text: textParts.join(""), notes }; + } + + return { text: "", notes: [] }; +}; +``` + +## 6) Real-World Example: Arabic + Translation + Footnotes + +Goal: + +- User picks an ayah and sees Arabic text plus translation, with footnotes when available. + +Inputs: + +- Quran Script package (Arabic text) +- Translation package (simple or footnote/chunk format) + +Processing: + +1. User selects ayah key (example: `73:4`). +2. App loads Arabic text by ayah key. +3. App loads translation payload by same ayah key. +4. App normalizes payload to text + notes. +5. UI renders translation and optional footnote list. + +Expected output: + +- Arabic and translation stay correctly paired. +- Footnotes are visible when provided. +- Format differences do not break rendering. + +Interactive preview (temporary sandbox): + +You can edit this code for testing. Edits are not saved and may not persist after refresh. + +```playground-js +// This playground mirrors the Translation Preview + Help format ideas. +// It demonstrates simple text, footnote-tags, and chunk-based translation data. + +const arabicByAyah = { + "1:1": "بِسۡمِ ٱللَّهِ ٱلرَّحۡمَٰنِ ٱلرَّحِيمِ", + "1:2": "ٱلۡحَمۡدُ لِلَّهِ رَبِّ ٱلۡعَٰلَمِينَ", + "73:4": "أَوۡ زِدۡ عَلَيۡهِ وَرَتِّلِ ٱلۡقُرۡءَانَ تَرۡتِيلًا" +}; + +const translationByAyah = { + // 1) Simple text format. + "1:1": "In the name of Allah, the Most Compassionate, the Most Merciful.", + + // 2) Text chunks format. + "1:2": [ + { type: "i", text: "All praise" }, + " is for Allah, Lord of all worlds." + ], + + // 3) Footnote-tags format from Help concepts: { t, f }. + "73:4": { + t: "and recite the Qur'an in a measured way 1.", + f: { + "77646": "Measured recitation means deliberate and clear pacing." + } + } +}; + +// Convert tagged footnotes into readable inline markers + footnote list. +const parseTaggedTranslation = (payload) => { + const noteIds = []; + const text = payload.t.replace(/([^<]+)<\/sup>/g, (_, id, label) => { + noteIds.push(id); + return `[${label}]`; + }); + + const notes = noteIds.map((id) => ({ + id, + text: payload.f?.[id] || "(missing footnote text)" + })); + + return { text, notes }; +}; + +// Convert chunks format into display text + footnotes. +const parseChunksTranslation = (chunks) => { + const parts = []; + const notes = []; + + chunks.forEach((chunk) => { + if (typeof chunk === "string") { + parts.push(chunk); + return; + } + if (chunk?.type === "i") { + parts.push(chunk.text); + return; + } + if (chunk?.type === "f") { + parts.push(`[${chunk.text}]`); + notes.push({ id: chunk.f, text: `Footnote ${chunk.f}` }); + } + }); + + return { text: parts.join(""), notes }; +}; + +// Normalize all format types to a single output shape for rendering. +const normalizeTranslation = (payload) => { + if (typeof payload === "string") return { text: payload, notes: [] }; + if (Array.isArray(payload)) return parseChunksTranslation(payload); + if (payload?.t && payload?.f) return parseTaggedTranslation(payload); + return { text: "", notes: [] }; +}; + +const app = document.getElementById("app"); +app.innerHTML = ` +

      Translation Preview (Format-Aware)

      +

      Arabic + translation rendering with simple, chunk, and footnote-tag formats

      + + +
      +
      +
        +`; + +const ayahSelect = app.querySelector("#ayah"); +const arabicBox = app.querySelector("#arabic"); +const translationBox = app.querySelector("#translation"); +const notesList = app.querySelector("#notes"); + +const renderAyah = (ayahKey) => { + const arabic = arabicByAyah[ayahKey] || "(Arabic not found)"; + const rawTranslation = translationByAyah[ayahKey]; + const normalized = normalizeTranslation(rawTranslation); + + arabicBox.textContent = arabic; + translationBox.textContent = normalized.text || "(Translation not found)"; + + notesList.innerHTML = ""; + normalized.notes.forEach((note) => { + const li = document.createElement("li"); + li.textContent = `[${note.id}] ${note.text}`; + notesList.appendChild(li); + }); +}; + +ayahSelect.addEventListener("change", (event) => renderAyah(event.target.value)); +renderAyah(ayahSelect.value); +``` + +## 7) Common Mistakes to Avoid + +- Joining translation rows to Arabic by row order instead of ayah key. +- Assuming every translation package has the same structure. +- Rendering footnote-tag HTML directly without sanitization in production apps. +- Ignoring missing footnote entries or missing ayah keys. + +## 8) When to Request Updates or Changes + +Open an issue if you find: + +- Broken download links or missing format files +- Translation text mapped to the wrong ayah +- Footnote IDs without matching footnote text +- Inconsistent metadata for language/source + +Issue tracker: + +- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) + +## Related Docs + +- [Tutorials Index](tutorials.md) +- [Translations Guide](resource-translations.md) +- [Quran Script Guide](resource-quran-script.md) +- [Downloading and Using Data](downloading-data.md) +- [Data Model](data-model.md) diff --git a/docs/tutorial-transliteration-end-to-end.md b/docs/tutorial-transliteration-end-to-end.md new file mode 100644 index 00000000..eb99e8c4 --- /dev/null +++ b/docs/tutorial-transliteration-end-to-end.md @@ -0,0 +1,180 @@ +# Tutorial 8: Transliteration End-to-End + +This tutorial is for users who want to show pronunciation-friendly Quran text in non-Arabic script while preserving ayah mapping. + +## 1) What This Resource Is + +Transliteration resources provide Quran text transliterated into Latin/non-Arabic writing systems. + +Typical entries include: + +- `ayah_key` in `surah:ayah` +- Transliteration text for each ayah +- Optional variants by language/provider + +Primary category: + +- [https://qul.tarteel.ai/resources/transliteration](https://qul.tarteel.ai/resources/transliteration) + +## 2) When to Use It + +Use transliteration data when you are building: + +- Beginner-friendly reading modes +- Pronunciation assistance features +- Arabic + transliteration dual-display UI + +## 3) How to Get Your First Example Resource + +1. Open [https://qul.tarteel.ai/resources/transliteration](https://qul.tarteel.ai/resources/transliteration). +2. Keep default listing order and open the first published card. +3. Confirm the detail page includes: + - `Transliteration Preview` tab + - `Help` tab +4. Confirm downloads (`json`, `sqlite`). + +This keeps onboarding concrete without hardcoded IDs. + +## 4) What the Preview Shows (Website-Aligned) + +On transliteration detail pages: + +- `Transliteration Preview` tab: + - `Jump to Ayah` + - Previous/next ayah navigation + - Arabic line + transliteration line +- `Help` tab: + - Data shape keyed by `ayah_key` + - Field examples for app integration + +Practical meaning: + +- Transliteration must remain keyed exactly like script/translation for safe joins. +- It is a reading aid layer, not a replacement for script text. + +## 5) Download and Use (Step-by-Step) + +1. Download transliteration package (`json` or `sqlite`). +2. Normalize and index by `ayah_key`. +3. Join with Quran Script rows by ayah key. +4. Add display mode toggle (Arabic | Transliteration | Both). +5. Validate ayah order and alignment. + +Starter integration snippet (JavaScript): + +```javascript +const buildTransliterationIndex = (rows) => + rows.reduce((index, row) => { + index[row.ayah_key] = row.text; + return index; + }, {}); + +const joinScriptAndTransliteration = (scriptRows, transliterationIndex) => + scriptRows.map((row) => { + const ayahKey = `${row.surah}:${row.ayah}`; + return { + ayahKey, + arabic: row.text, + transliteration: transliterationIndex[ayahKey] || null + }; + }); +``` + +## 6) Real-World Example: Reading Mode Toggle + +Goal: + +- User toggles transliteration under Arabic text for selected ayah. + +Inputs: + +- Quran Script data +- Transliteration data + +Processing: + +1. App resolves selected ayah key. +2. App loads Arabic and transliteration lines by same key. +3. UI toggles transliteration visibility. + +Expected output: + +- Stable Arabic-transliteration pairing for every ayah. + +Interactive preview (temporary sandbox): + +You can edit this code for testing. Edits are not saved and may not persist after refresh. + +```playground-js +// This sandbox demonstrates ayah_key-based transliteration joins. + +const scriptByAyah = { + "1:1": "بِسۡمِ ٱللَّهِ ٱلرَّحۡمَٰنِ ٱلرَّحِيمِ", + "1:2": "ٱلۡحَمۡدُ لِلَّهِ رَبِّ ٱلۡعَٰلَمِينَ" +}; + +const transliterationByAyah = { + "1:1": "Bismillahi al-rahmani al-rahim", + "1:2": "Al-hamdu lillahi rabbil alamin" +}; + +const app = document.getElementById("app"); +app.innerHTML = ` +

        Transliteration Preview

        +

        Switch between Arabic-only and Arabic + transliteration

        + + + +
        +
        +`; + +const ayahSelect = app.querySelector("#ayah"); +const showToggle = app.querySelector("#show"); +const arabicBox = app.querySelector("#arabic"); +const translitBox = app.querySelector("#translit"); + +const render = () => { + const key = ayahSelect.value; + arabicBox.textContent = scriptByAyah[key] || "(Arabic not found)"; + + if (showToggle.checked) { + translitBox.style.display = "block"; + translitBox.textContent = transliterationByAyah[key] || "(Transliteration not found)"; + } else { + translitBox.style.display = "none"; + } +}; + +ayahSelect.addEventListener("change", render); +showToggle.addEventListener("change", render); +render(); +``` + +## 7) Common Mistakes to Avoid + +- Joining transliteration to script by row order instead of `ayah_key`. +- Showing transliteration without clear mode/toggle controls. +- Mixing ayah-by-ayah and word-by-word formats without normalization. + +## 8) When to Request Updates or Changes + +Open an issue if you find: + +- Missing transliteration rows for known ayahs +- Broken character mapping in transliteration text +- Broken json/sqlite links + +Issue tracker: + +- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) + +## Related Docs + +- [Tutorials Index](tutorials.md) +- [Transliteration Guide](resource-transliteration.md) +- [Quran Script Guide](resource-quran-script.md) +- [Translations Guide](resource-translations.md) diff --git a/docs/tutorials.md b/docs/tutorials.md index 88be88b3..c3fbac36 100644 --- a/docs/tutorials.md +++ b/docs/tutorials.md @@ -1,34 +1,36 @@ # Tutorials -These quick tutorials are meant for new contributors. +This page is the tutorials index for resource users. -## 1) Run QUL Locally +Each end-to-end tutorial is on its own page so you can focus on one resource type at a time. -1. Follow [getting-started.md](getting-started.md). -2. Confirm app opens at `http://localhost:3000`. -3. Confirm admin opens at `http://localhost:3000/admin`. +## Start Here: How to Pick Your First Resource -## 2) Verify Both Databases Are Connected +Use this repeatable rule for any resource type: -Run: +1. Open the resource category page. +2. Use the default listing order. +3. Open the first published resource card shown. +4. Check the preview and help tabs. +5. Download the format that fits your project. -```bash -bundle exec rails runner 'puts ActiveRecord::Base.connection.current_database; puts Verse.connection.current_database' -``` +## Tutorial Index -Expected output should include: +1. [Tutorial 1: Recitation End-to-End](tutorial-recitation-end-to-end.md) +2. [Tutorial 2: Mushaf Layout End-to-End](tutorial-mushaf-layout-end-to-end.md) +3. [Tutorial 3: Translation End-to-End](tutorial-translation-end-to-end.md) +4. [Tutorial 4: Tafsir End-to-End](tutorial-tafsir-end-to-end.md) +5. [Tutorial 5: Quran Script End-to-End](tutorial-quran-script-end-to-end.md) +6. [Tutorial 6: Font End-to-End](tutorial-font-end-to-end.md) +7. [Tutorial 7: Quran Metadata End-to-End](tutorial-quran-metadata-end-to-end.md) +8. [Tutorial 8: Transliteration End-to-End](tutorial-transliteration-end-to-end.md) +9. [Tutorial 9: Surah Information End-to-End](tutorial-surah-information-end-to-end.md) +10. [Tutorial 10: Ayah Topics End-to-End](tutorial-ayah-topics-end-to-end.md) +11. [Tutorial 11: Morphology End-to-End](tutorial-morphology-end-to-end.md) +12. [Tutorial 12: Mutashabihat End-to-End](tutorial-mutashabihat-end-to-end.md) +13. [Tutorial 13: Similar Ayah End-to-End](tutorial-similar-ayah-end-to-end.md) +14. [Tutorial 14: Ayah Theme End-to-End](tutorial-ayah-theme-end-to-end.md) -- `quran_community_tarteel` -- `quran_dev` +## Status -## 3) Make a Safe Documentation Change - -1. Create a branch: - -```bash -git switch -c docs/your-topic -``` - -2. Edit files in `docs/`. -3. Add links in `docs/README.md` (and root `README.md` if needed). -4. Open a PR with a short summary and screenshots/output when relevant. +All currently planned tutorials in this rollout are included. diff --git a/yarn.lock b/yarn.lock index cb7103a0..fc0411e4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -84,47 +84,47 @@ "@esbuild/aix-ppc64@0.21.2": version "0.21.2" - resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.2.tgz#7ccd2a552dc4eb740f094a46d18a1b1508b8d37c" + resolved "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.2.tgz" integrity sha512-/c7hocx0pm14bHQlqUVKmxwdT/e5/KkyoY1W8F9lk/8CkE037STDDz8PXUP/LE6faj2HqchvDs9GcShxFhI78Q== "@esbuild/aix-ppc64@0.21.5": version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f" + resolved "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz" integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ== -"@esbuild/android-arm64@0.21.2": - version "0.21.2" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.2.tgz#016abbee9f0c6f646b0c6b43b172a5053fe53aab" - integrity sha512-SGZKngoTWVUriO5bDjI4WDGsNx2VKZoXcds+ita/kVYB+8IkSCKDRDaK+5yu0b5S0eq6B3S7fpiEvpsa2ammlQ== - -"@esbuild/android-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052" - integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A== - "@esbuild/android-arm@0.21.2": version "0.21.2" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.2.tgz#99f3a3c90bf8ac37d1881af6b87d404a02007164" + resolved "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.2.tgz" integrity sha512-G1ve3b4FeyJeyCjB4MX1CiWyTaIJwT9wAYE+8+IRA53YoN/reC/Bf2GDRXAzDTnh69Fpl+1uIKg76DiB3U6vwQ== "@esbuild/android-arm@0.21.5": version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28" + resolved "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz" integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg== +"@esbuild/android-arm64@0.21.2": + version "0.21.2" + resolved "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.2.tgz" + integrity sha512-SGZKngoTWVUriO5bDjI4WDGsNx2VKZoXcds+ita/kVYB+8IkSCKDRDaK+5yu0b5S0eq6B3S7fpiEvpsa2ammlQ== + +"@esbuild/android-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz" + integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A== + "@esbuild/android-x64@0.21.2": version "0.21.2" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.2.tgz#5039e8d0b2ed03ca75d77e581ead591b1d87826f" + resolved "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.2.tgz" integrity sha512-1wzzNoj2QtNkAYwIcWJ66UTRA80+RTQ/kuPMtEuP0X6dp5Ar23Dn566q3aV61h4EYrrgGlOgl/HdcqN/2S/2vg== "@esbuild/android-x64@0.21.5": version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e" + resolved "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz" integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA== "@esbuild/darwin-arm64@0.21.2": version "0.21.2" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.2.tgz#6f55b81878d2295d7d4ecdbbb5ee418d379fb49a" + resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.2.tgz" integrity sha512-ZyMkPWc5eTROcLOA10lEqdDSTc6ds6nuh3DeHgKip/XJrYjZDfnkCVSty8svWdy+SC1f77ULtVeIqymTzaB6/Q== "@esbuild/darwin-arm64@0.21.5": @@ -144,177 +144,177 @@ "@esbuild/freebsd-arm64@0.21.2": version "0.21.2" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.2.tgz#f665703471824e67ff5f62e6c9ed298f3c363b1b" + resolved "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.2.tgz" integrity sha512-4kbOGdpA61CXqadD+Gb/Pw3YXamQGiz9mal/h93rFVSjr5cgMnmJd/gbfPRm+3BMifvnaOfS1gNWaIDxkE2A3A== "@esbuild/freebsd-arm64@0.21.5": version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e" + resolved "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz" integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g== "@esbuild/freebsd-x64@0.21.2": version "0.21.2" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.2.tgz#6493aa56760521125badd41f78369f18c49e367e" + resolved "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.2.tgz" integrity sha512-ShS+R09nuHzDBfPeMUliKZX27Wrmr8UFp93aFf/S8p+++x5BZ+D344CLKXxmY6qzgTL3mILSImPCNJOzD6+RRg== "@esbuild/freebsd-x64@0.21.5": version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261" + resolved "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz" integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ== -"@esbuild/linux-arm64@0.21.2": - version "0.21.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.2.tgz#beb96b83bfe32630d34eedc09b8e0722819f1a5b" - integrity sha512-Hdu8BL+AmO+eCDvvT6kz/fPQhvuHL8YK4ExKZfANWsNe1kFGOHw7VJvS/FKSLFqheXmB3rTF3xFQIgUWPYsGnA== - -"@esbuild/linux-arm64@0.21.5": - version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b" - integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q== - "@esbuild/linux-arm@0.21.2": version "0.21.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.2.tgz#b1c5176479397b34c36334218063e223b4e588dd" + resolved "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.2.tgz" integrity sha512-nnGXjOAv+7cM3LYRx4tJsYdgy8dGDGkAzF06oIDGppWbUkUKN9SmgQA8H0KukpU0Pjrj9XmgbWqMVSX/U7eeTA== "@esbuild/linux-arm@0.21.5": version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9" + resolved "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz" integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA== +"@esbuild/linux-arm64@0.21.2": + version "0.21.2" + resolved "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.2.tgz" + integrity sha512-Hdu8BL+AmO+eCDvvT6kz/fPQhvuHL8YK4ExKZfANWsNe1kFGOHw7VJvS/FKSLFqheXmB3rTF3xFQIgUWPYsGnA== + +"@esbuild/linux-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz" + integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q== + "@esbuild/linux-ia32@0.21.2": version "0.21.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.2.tgz#8ce387793eccdc28f5964e19f4dcbdb901099be4" + resolved "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.2.tgz" integrity sha512-m73BOCW2V9lcj7RtEMi+gBfHC6n3+VHpwQXP5offtQMPLDkpVolYn1YGXxOZ9hp4h3UPRKuezL7WkBsw+3EB3Q== "@esbuild/linux-ia32@0.21.5": version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2" + resolved "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz" integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg== "@esbuild/linux-loong64@0.14.54": version "0.14.54" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz#de2a4be678bd4d0d1ffbb86e6de779cde5999028" + resolved "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz" integrity sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw== "@esbuild/linux-loong64@0.21.2": version "0.21.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.2.tgz#c7360523a8e5e04e0b76b6e9a89a91ba573ac613" + resolved "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.2.tgz" integrity sha512-84eYHwwWHq3myIY/6ikALMcnwkf6Qo7NIq++xH0x+cJuUNpdwh8mlpUtRY+JiGUc60yu7ElWBbVHGWTABTclGw== "@esbuild/linux-loong64@0.21.5": version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df" + resolved "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz" integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg== "@esbuild/linux-mips64el@0.21.2": version "0.21.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.2.tgz#0adac2cc3451c25817b0c93bf160cd19008ed03a" + resolved "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.2.tgz" integrity sha512-9siSZngT0/ZKG+AH+/agwKF29LdCxw4ODi/PiE0F52B2rtLozlDP92umf8G2GPoVV611LN4pZ+nSTckebOscUA== "@esbuild/linux-mips64el@0.21.5": version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe" + resolved "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz" integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg== "@esbuild/linux-ppc64@0.21.2": version "0.21.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.2.tgz#d9e79563999288d367eeba2b8194874bef0e8a35" + resolved "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.2.tgz" integrity sha512-y0T4aV2CA+ic04ULya1A/8M2RDpDSK2ckgTj6jzHKFJvCq0jQg8afQQIn4EM0G8u2neyOiNHgSF9YKPfuqKOVw== "@esbuild/linux-ppc64@0.21.5": version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4" + resolved "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz" integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w== "@esbuild/linux-riscv64@0.21.2": version "0.21.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.2.tgz#34227910d843b399447a48180381425529eae7d6" + resolved "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.2.tgz" integrity sha512-x5ssCdXmZC86L2Li1qQPF/VaC4VP20u/Zm8jlAu9IiVOVi79YsSz6cpPDYZl1rfKSHYCJW9XBfFCo66S5gVPSA== "@esbuild/linux-riscv64@0.21.5": version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc" + resolved "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz" integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA== "@esbuild/linux-s390x@0.21.2": version "0.21.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.2.tgz#2835f5b9b4c961baf6d6f03a870ab2d5bc3fbfcc" + resolved "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.2.tgz" integrity sha512-NP7fTpGSFWdXyvp8iAFU04uFh9ARoplFVM/m+8lTRpaYG+2ytHPZWyscSsMM6cvObSIK2KoPHXiZD4l99WaxbQ== "@esbuild/linux-s390x@0.21.5": version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de" + resolved "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz" integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A== "@esbuild/linux-x64@0.21.2": version "0.21.2" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.2.tgz#756282185a936e752a3a80b227a950813fe62ee7" + resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.2.tgz" integrity sha512-giZ/uOxWDKda44ZuyfKbykeXznfuVNkTgXOUOPJIjbayJV6FRpQ4zxUy9JMBPLaK9IJcdWtaoeQrYBMh3Rr4vQ== "@esbuild/linux-x64@0.21.5": version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0" + resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz" integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ== "@esbuild/netbsd-x64@0.21.2": version "0.21.2" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.2.tgz#e1dde3694f5f8fbf2f7696d021c026e601579167" + resolved "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.2.tgz" integrity sha512-IeFMfGFSQfIj1d4XU+6lkbFzMR+mFELUUVYrZ+jvWzG4NGvs6o53ReEHLHpYkjRbdEjJy2W3lTekTxrFHW7YJg== "@esbuild/netbsd-x64@0.21.5": version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047" + resolved "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz" integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg== "@esbuild/openbsd-x64@0.21.2": version "0.21.2" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.2.tgz#b0a8c1ce0077a5b24c5e4cf1c4417128ae5b6489" + resolved "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.2.tgz" integrity sha512-48QhWD6WxcebNNaE4FCwgvQVUnAycuTd+BdvA/oZu+/MmbpU8pY2dMEYlYzj5uNHWIG5jvdDmFXu0naQeOWUoA== "@esbuild/openbsd-x64@0.21.5": version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70" + resolved "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz" integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow== "@esbuild/sunos-x64@0.21.2": version "0.21.2" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.2.tgz#fc7dd917ffcb2ebab4f22728a23ece3dd36c2979" + resolved "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.2.tgz" integrity sha512-90r3nTBLgdIgD4FCVV9+cR6Hq2Dzs319icVsln+NTmTVwffWcCqXGml8rAoocHuJ85kZK36DCteii96ba/PX8g== "@esbuild/sunos-x64@0.21.5": version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b" + resolved "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz" integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg== "@esbuild/win32-arm64@0.21.2": version "0.21.2" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.2.tgz#251e4cdafae688d54a43ac8544cb8c71e8fcdf15" + resolved "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.2.tgz" integrity sha512-sNndlsBT8OeE/MZDSGpRDJlWuhjuUz/dn80nH0EP4ZzDUYvMDVa7G87DVpweBrn4xdJYyXS/y4CQNrf7R2ODXg== "@esbuild/win32-arm64@0.21.5": version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d" + resolved "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz" integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A== "@esbuild/win32-ia32@0.21.2": version "0.21.2" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.2.tgz#1e3a818791b7e93ed353901c83d7cdc901ffcc8a" + resolved "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.2.tgz" integrity sha512-Ti2QChGNFzWhUNNVuU4w21YkYTErsNh3h+CzvlEhzgRbwsJ7TrWQqRzW3bllLKKvTppuF3DJ3XP1GEg11AfrEQ== "@esbuild/win32-ia32@0.21.5": version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b" + resolved "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz" integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA== "@esbuild/win32-x64@0.21.2": version "0.21.2" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.2.tgz#825b4e7c89b7e7ec64c450ed494a8af7e405a84d" + resolved "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.2.tgz" integrity sha512-VEfTCZicoZnZ6sGkjFPGRFFJuL2fZn2bLhsekZl1CJslflp2cJS/VoKs1jMk+3pDfsGW6CfQVUckP707HwbXeQ== "@esbuild/win32-x64@0.21.5": version "0.21.5" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz#acad351d582d157bb145535db2a6ff53dd514b5c" + resolved "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz" integrity sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw== "@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": @@ -451,7 +451,7 @@ "@nodelib/fs.stat" "2.0.5" run-parallel "^1.1.9" -"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": +"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": version "2.0.5" resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== @@ -710,7 +710,7 @@ acorn-jsx@^5.3.2: resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn@^8.9.0: +"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.9.0: version "8.12.1" resolved "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz" integrity sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg== @@ -839,7 +839,7 @@ asn1@~0.2.3: dependencies: safer-buffer "~2.1.0" -assert-plus@1.0.0, assert-plus@^1.0.0: +assert-plus@^1.0.0, assert-plus@1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz" integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== @@ -896,7 +896,7 @@ balanced-match@^1.0.0: resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -bare-events@^2.2.0, bare-events@^2.5.4: +bare-events@*, bare-events@^2.2.0, bare-events@^2.5.4: version "2.5.4" resolved "https://registry.npmjs.org/bare-events/-/bare-events-2.5.4.tgz" integrity sha512-+gFfDkR8pj4/TrWCGUGWmJIkBwuxPS5F+a5yWjOHQt2hHvNZd5YLzadjmDUtFmMM4y429bnKLa8bYBMHcYdnQA== @@ -1002,7 +1002,23 @@ buffer-crc32@~0.2.3: resolved "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz" integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== -buffer@^5.2.1, buffer@^5.5.0, buffer@^5.7.1: +buffer@^5.2.1: + version "5.7.1" + resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + +buffer@^5.7.1: version "5.7.1" resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== @@ -1091,15 +1107,16 @@ caseless@~0.12.0: resolved "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz" integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== -chalk@4.1.1, chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1: - version "4.1.1" - resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz" - integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== +chalk@^2.4.1: + version "2.4.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" -chalk@^2.4.1, chalk@^2.4.2: +chalk@^2.4.2: version "2.4.2" resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -1108,12 +1125,20 @@ chalk@^2.4.1, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1, chalk@4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz" + integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chardet@^0.7.0: version "0.7.0" resolved "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -chart.js@4, chart.js@^4.4.9: +chart.js@^4.4.9, chart.js@>=2.8.0, chart.js@4: version "4.5.0" resolved "https://registry.npmjs.org/chart.js/-/chart.js-4.5.0.tgz" integrity sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ== @@ -1139,7 +1164,7 @@ check-more-types@^2.24.0: resolved "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz" integrity sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA== -"chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.3, chokidar@^3.6.0: +chokidar@^3.5.3, chokidar@^3.6.0, "chokidar@>=3.0.0 <4.0.0": version "3.6.0" resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz" integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== @@ -1240,16 +1265,16 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" - integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== - color-name@~1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + colorette@^2.0.16: version "2.0.20" resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz" @@ -1289,6 +1314,16 @@ core-util-is@1.0.2: resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz" integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== +cosmiconfig@^9.0.0: + version "9.0.0" + resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz" + integrity sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg== + dependencies: + env-paths "^2.2.1" + import-fresh "^3.3.0" + js-yaml "^4.1.0" + parse-json "^5.2.0" + cosmiconfig@7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz" @@ -1300,16 +1335,6 @@ cosmiconfig@7.0.0: path-type "^4.0.0" yaml "^1.10.0" -cosmiconfig@^9.0.0: - version "9.0.0" - resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz" - integrity sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg== - dependencies: - env-paths "^2.2.1" - import-fresh "^3.3.0" - js-yaml "^4.1.0" - parse-json "^5.2.0" - cross-spawn@^6.0.5: version "6.0.6" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz" @@ -1401,7 +1426,7 @@ data-uri-to-buffer@^6.0.2: resolved "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz" integrity sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw== -date-fns@>=2: +date-fns@>=2, date-fns@>=2.0.0: version "4.1.0" resolved "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz" integrity sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg== @@ -1411,13 +1436,6 @@ dayjs@^1.10.4: resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz" integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg== -debug@4, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: - version "4.3.5" - resolved "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz" - integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== - dependencies: - ms "2.1.2" - debug@^3.1.0: version "3.2.7" resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" @@ -1425,6 +1443,13 @@ debug@^3.1.0: dependencies: ms "^2.1.1" +debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@4: + version "4.3.5" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz" + integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== + dependencies: + ms "2.1.2" + debug@^4.3.6: version "4.3.7" resolved "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz" @@ -1517,7 +1542,7 @@ delayed-stream@~1.0.0: resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -devtools-protocol@0.0.1330662: +devtools-protocol@*, devtools-protocol@0.0.1330662: version "0.0.1330662" resolved "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1330662.tgz" integrity sha512-pzh6YQ8zZfz3iKlCvgzVCu22NdpZ8hNmwU6WnQjNVquh0A9iVosPtNLWDwaWVGyrntQlltPFztTMK5Cg6lfCuw== @@ -1552,7 +1577,7 @@ dom-serializer@^1.0.1: dom-to-image-more@^3.7.2: version "3.7.2" - resolved "https://registry.yarnpkg.com/dom-to-image-more/-/dom-to-image-more-3.7.2.tgz#15afae48b771c63f0b066f7c0c48900506fb5a7e" + resolved "https://registry.npmjs.org/dom-to-image-more/-/dom-to-image-more-3.7.2.tgz" integrity sha512-uQf+pHv6eQhgfI8t2bFuinV0KsPyT8TZgCLwcSU8uBVgN9v6leb0mMpvp6HQAlAcplP3NCcGjxbdqef6pTzvmw== domelementtype@^2.0.1, domelementtype@^2.2.0: @@ -1612,7 +1637,7 @@ end-of-stream@^1.1.0: dependencies: once "^1.4.0" -enquirer@^2.3.6: +enquirer@^2.3.6, "enquirer@>= 2.3.0 < 3": version "2.4.1" resolved "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz" integrity sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ== @@ -1729,12 +1754,12 @@ es-to-primitive@^1.2.1: esbuild-android-64@0.14.54: version "0.14.54" - resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz#505f41832884313bbaffb27704b8bcaa2d8616be" + resolved "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz" integrity sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ== esbuild-android-arm64@0.14.54: version "0.14.54" - resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz#8ce69d7caba49646e009968fe5754a21a9871771" + resolved "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz" integrity sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg== esbuild-darwin-64@0.14.54: @@ -1744,67 +1769,67 @@ esbuild-darwin-64@0.14.54: esbuild-darwin-arm64@0.14.54: version "0.14.54" - resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz#3f7cdb78888ee05e488d250a2bdaab1fa671bf73" + resolved "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz" integrity sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw== esbuild-freebsd-64@0.14.54: version "0.14.54" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz#09250f997a56ed4650f3e1979c905ffc40bbe94d" + resolved "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz" integrity sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg== esbuild-freebsd-arm64@0.14.54: version "0.14.54" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz#bafb46ed04fc5f97cbdb016d86947a79579f8e48" + resolved "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz" integrity sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q== esbuild-linux-32@0.14.54: version "0.14.54" - resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz#e2a8c4a8efdc355405325033fcebeb941f781fe5" + resolved "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz" integrity sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw== esbuild-linux-64@0.14.54: version "0.14.54" - resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz#de5fdba1c95666cf72369f52b40b03be71226652" + resolved "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz" integrity sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg== -esbuild-linux-arm64@0.14.54: - version "0.14.54" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz#dae4cd42ae9787468b6a5c158da4c84e83b0ce8b" - integrity sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig== - esbuild-linux-arm@0.14.54: version "0.14.54" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz#a2c1dff6d0f21dbe8fc6998a122675533ddfcd59" + resolved "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz" integrity sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw== +esbuild-linux-arm64@0.14.54: + version "0.14.54" + resolved "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz" + integrity sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig== + esbuild-linux-mips64le@0.14.54: version "0.14.54" - resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz#d9918e9e4cb972f8d6dae8e8655bf9ee131eda34" + resolved "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz" integrity sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw== esbuild-linux-ppc64le@0.14.54: version "0.14.54" - resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz#3f9a0f6d41073fb1a640680845c7de52995f137e" + resolved "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz" integrity sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ== esbuild-linux-riscv64@0.14.54: version "0.14.54" - resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz#618853c028178a61837bc799d2013d4695e451c8" + resolved "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz" integrity sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg== esbuild-linux-s390x@0.14.54: version "0.14.54" - resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz#d1885c4c5a76bbb5a0fe182e2c8c60eb9e29f2a6" + resolved "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz" integrity sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA== esbuild-netbsd-64@0.14.54: version "0.14.54" - resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz#69ae917a2ff241b7df1dbf22baf04bd330349e81" + resolved "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz" integrity sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w== esbuild-openbsd-64@0.14.54: version "0.14.54" - resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz#db4c8495287a350a6790de22edea247a57c5d47b" + resolved "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz" integrity sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw== esbuild-plugin-vue3@^0.4.0: @@ -1824,7 +1849,7 @@ esbuild-rails@^1.0.7: esbuild-sunos-64@0.14.54: version "0.14.54" - resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz#54287ee3da73d3844b721c21bc80c1dc7e1bf7da" + resolved "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz" integrity sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw== esbuild-visualizer@^0.6.0: @@ -1838,20 +1863,20 @@ esbuild-visualizer@^0.6.0: esbuild-windows-32@0.14.54: version "0.14.54" - resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz#f8aaf9a5667630b40f0fb3aa37bf01bbd340ce31" + resolved "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz" integrity sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w== esbuild-windows-64@0.14.54: version "0.14.54" - resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz#bf54b51bd3e9b0f1886ffdb224a4176031ea0af4" + resolved "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz" integrity sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ== esbuild-windows-arm64@0.14.54: version "0.14.54" - resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz#937d15675a15e4b0e4fafdbaa3a01a776a2be982" + resolved "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz" integrity sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg== -esbuild@0.21.2: +esbuild@*, esbuild@0.21.2: version "0.21.2" resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.21.2.tgz" integrity sha512-LmHPAa5h4tSxz+g/D8IHY6wCjtIiFx8I7/Q0Aq+NmvtoYvyMnJU0KQJcqB6QH30X9x/W4CemgUtPgQDZFca5SA== @@ -1989,7 +2014,7 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4 resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint@^8.55.0: +"eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0", eslint@^8.55.0, eslint@>=6.0.0: version "8.57.0" resolved "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz" integrity sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ== @@ -2127,7 +2152,7 @@ external-editor@^3.0.3: iconv-lite "^0.4.24" tmp "^0.0.33" -extract-zip@2.0.1, extract-zip@^2.0.1: +extract-zip@^2.0.1, extract-zip@2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz" integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== @@ -2138,7 +2163,7 @@ extract-zip@2.0.1, extract-zip@^2.0.1: optionalDependencies: "@types/yauzl" "^2.9.1" -extsprintf@1.3.0, extsprintf@^1.2.0: +extsprintf@^1.2.0, extsprintf@1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz" integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== @@ -2459,7 +2484,19 @@ globby@^11.0.2: merge2 "^1.4.1" slash "^3.0.0" -globby@^14.0.0, globby@^14.0.1: +globby@^14.0.0: + version "14.0.2" + resolved "https://registry.npmjs.org/globby/-/globby-14.0.2.tgz" + integrity sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw== + dependencies: + "@sindresorhus/merge-streams" "^2.1.0" + fast-glob "^3.3.2" + ignore "^5.2.4" + path-type "^5.0.0" + slash "^5.1.0" + unicorn-magic "^0.1.0" + +globby@^14.0.1: version "14.0.2" resolved "https://registry.npmjs.org/globby/-/globby-14.0.2.tgz" integrity sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw== @@ -2647,21 +2684,21 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.3, inherits@^2.0.4: +inherits@^2.0.3, inherits@^2.0.4, inherits@2: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -ini@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz" - integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== - ini@^1.3.5: version "1.3.8" resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== +ini@2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz" + integrity sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA== + inquirer@^8.1.1: version "8.2.6" resolved "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz" @@ -2933,7 +2970,7 @@ jquery-ujs@^1.2.3: resolved "https://registry.npmjs.org/jquery-ujs/-/jquery-ujs-1.2.3.tgz" integrity sha512-59wvfx5vcCTHMeQT1/OwFiAj+UffLIwjRIoXdpO7Z7BCFGepzq9T9oLVeoItjTqjoXfUrHJvV7QU6pUR+UzOoA== -jquery@3.7.1, jquery@>=1.12.0, "jquery@>=1.8.0 <4.0.0": +jquery@>=1.12.0, jquery@>=1.8.0, "jquery@>=1.8.0 <4.0.0", jquery@3.7.1: version "3.7.1" resolved "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz" integrity sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg== @@ -2958,16 +2995,16 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" -jsbn@1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz" - integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A== - jsbn@~0.1.0: version "0.1.1" resolved "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz" integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== +jsbn@1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz" + integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A== + json-buffer@3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz" @@ -3276,7 +3313,7 @@ mitt@3.0.1: resolved "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz" integrity sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw== -ms@2.1.2, ms@^2.1.1: +ms@^2.1.1, ms@2.1.2: version "2.1.2" resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== @@ -3323,7 +3360,17 @@ nice-try@^1.0.4: resolved "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: +normalize-package-data@^2.3.2: + version "2.5.0" + resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== @@ -3692,16 +3739,16 @@ proxy-agent@^6.4.0: proxy-from-env "^1.1.0" socks-proxy-agent "^8.0.2" -proxy-from-env@1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz" - integrity sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A== - proxy-from-env@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== +proxy-from-env@1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz" + integrity sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A== + pump@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz" @@ -3938,7 +3985,7 @@ safe-regex-test@^1.0.0: get-intrinsic "^1.1.3" is-regex "^1.1.4" -"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: +safer-buffer@^2.0.2, safer-buffer@^2.1.0, "safer-buffer@>= 2.1.2 < 3", safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -3948,7 +3995,7 @@ sass-migrator@^1.7.3: resolved "https://registry.npmjs.org/sass-migrator/-/sass-migrator-1.8.1.tgz" integrity sha512-7F7+Dx2rzNN6vhqBjGo7Bl43NCnvPS5hP7YCMRCxW8FwRpcoM6rOsNFjwy8g329HH6ebKjdMnnYgtfRxtnz2kg== -sass@^1.44.0: +sass@^1.35.2, sass@^1.44.0: version "1.69.4" resolved "https://registry.npmjs.org/sass/-/sass-1.69.4.tgz" integrity sha512-+qEreVhqAy8o++aQfCJwp0sklr2xyEzkm9Pp/Igu9wNPoe7EZEQ8X/MBvvXggI2ql607cxKg/RKOwDj6pp2XDA== @@ -3967,7 +4014,7 @@ semver-compare@^1.0.0: resolved "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz" integrity sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow== -"semver@2 || 3 || 4 || 5", semver@^5.5.0: +semver@^5.5.0: version "5.7.2" resolved "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== @@ -3977,6 +4024,11 @@ semver@^7.3.4, semver@^7.3.6, semver@^7.5.3, semver@^7.6.0, semver@^7.6.3: resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== +"semver@2 || 3 || 4 || 5": + version "5.7.2" + resolved "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + set-function-length@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz" @@ -4096,7 +4148,7 @@ size-limit@^10.0.2: nanospinner "^1.1.0" picocolors "^1.0.0" -size-limit@^11.0.0: +size-limit@^11.0.0, size-limit@11.1.4: version "11.1.4" resolved "https://registry.npmjs.org/size-limit/-/size-limit-11.1.4.tgz" integrity sha512-V2JAI/Z7h8sEuxU3V+Ig3XKA5FcYbI4CZ7sh6s7wvuy+TUwDZYqw7sAqrHhQ4cgcNfPKIAHAaH8VaqOdbcwJDA== @@ -4171,7 +4223,7 @@ sortablejs@^1.15.2: resolved "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.2.tgz" integrity sha512-FJF5jgdfvoKn1MAKSdGs33bIqLi3LmsgVTliuX6iITj834F+JRQZN90Z93yql8h0K2t0RwDPBmxwlbZfDcxNZA== -"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.2.0, source-map-js@^1.2.1: +source-map-js@^1.2.0, source-map-js@^1.2.1, "source-map-js@>=0.6.2 <2.0.0": version "1.2.1" resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz" integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== @@ -4247,6 +4299,13 @@ streamx@^2.15.0, streamx@^2.21.0: optionalDependencies: bare-events "^2.2.0" +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" @@ -4292,13 +4351,6 @@ string.prototype.trimstart@^1.0.7: define-properties "^1.2.0" es-abstract "^1.22.1" -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" @@ -4401,7 +4453,7 @@ text-table@^0.2.0: resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== -three@^0.166.1: +three@^0.166.1, three@>=0.133.0: version "0.166.1" resolved "https://registry.npmjs.org/three/-/three-0.166.1.tgz" integrity sha512-LtuafkKHHzm61AQA1be2MAYIw1IjmhOUxhBa0prrLpEMWbV7ijvxCRHjSgHPGp2493wLBzwKV46tA9nivLEgKg== @@ -4481,7 +4533,7 @@ trim-newlines@^3.0.0: resolved "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz" integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== -trix@^2.1.13: +trix@^2.0.0, trix@^2.1.13: version "2.1.13" resolved "https://registry.npmjs.org/trix/-/trix-2.1.13.tgz" integrity sha512-LTwj6HPo/CDL6KSclIwn41J+EwcqXHiUE43BBbg8Q/whaZcoUaODypHYoGIDd3M32KVXw98M5vC6zNCod9KB2w== @@ -4581,7 +4633,7 @@ typed-query-selector@^2.12.0: resolved "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.0.tgz" integrity sha512-SbklCd1F0EiZOyPiW192rrHZzZ5sBijB6xM+cpmrwDqObvdtunOHHIk9fCGsoK5JVIYXoyEp4iEdE3upFH3PAg== -typescript@^4.7.4: +typescript@*, typescript@^4.7.4, typescript@>=4.9.5: version "4.9.5" resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz" integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== @@ -4702,7 +4754,7 @@ vue-eslint-parser@^9.4.3: lodash "^4.17.21" semver "^7.3.6" -vue@^3.2.24: +vue@^3.2.0, vue@^3.2.24, vue@^3.4.15, vue@3.5.13: version "3.5.13" resolved "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz" integrity sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ== @@ -4749,7 +4801,14 @@ which-typed-array@^1.1.11, which-typed-array@^1.1.13: gopd "^1.0.1" has-tostringtag "^1.0.0" -which@^1.2.9, which@^1.3.1: +which@^1.2.9: + version "1.3.1" + resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^1.3.1: version "1.3.1" resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== From c6fbc1e358cbdd0865b7e8efdd8f3186c0ee1755 Mon Sep 17 00:00:00 2001 From: umairliaqatali Date: Thu, 5 Mar 2026 09:36:07 +0500 Subject: [PATCH 03/12] docs: make tutorials canonical and deprecate duplicate resource guides --- docs/README.md | 5 +- docs/datasets.md | 26 +++-- docs/getting-started.md | 4 +- docs/resource-ayah-theme.md | 84 ++----------- docs/resource-ayah-topics.md | 85 ++------------ docs/resource-fonts.md | 52 ++------- docs/resource-guides-index.md | 27 +---- docs/resource-morphology.md | 52 ++------- docs/resource-mushaf-layouts.md | 157 ++----------------------- docs/resource-mutashabihat.md | 80 ++----------- docs/resource-quran-metadata.md | 52 ++------- docs/resource-quran-script.md | 159 ++----------------------- docs/resource-recitations.md | 169 ++------------------------- docs/resource-similar-ayah.md | 84 ++----------- docs/resource-surah-information.md | 51 ++------ docs/resource-tafsirs.md | 147 ++--------------------- docs/resource-topics-and-concepts.md | 16 +-- docs/resource-translations.md | 152 ++---------------------- docs/resource-transliteration.md | 51 ++------ docs/resource_tutorial_map.md | 22 ++++ docs/tutorials.md | 6 + 21 files changed, 160 insertions(+), 1321 deletions(-) create mode 100644 docs/resource_tutorial_map.md diff --git a/docs/README.md b/docs/README.md index a21b1566..d6fd8e58 100644 --- a/docs/README.md +++ b/docs/README.md @@ -16,10 +16,10 @@ The pages here are published on the website at `/docs` and mirrored in the repos 1. [Getting Started](getting-started.md) 2. [Downloading and Using Data](downloading-data.md) -3. [Resource Guides Index](resource-guides-index.md) +3. [Tutorials](tutorials.md) 4. [Datasets](datasets.md) 5. [Data Model](data-model.md) -6. [Tutorials](tutorials.md) +6. [Resource Guides Index (compatibility alias)](resource-guides-index.md) 7. [FAQ](faq.md) ## Documentation Maintenance (Secondary) @@ -27,6 +27,7 @@ The pages here are published on the website at `/docs` and mirrored in the repos 1. [Resource Guide Template](resource-guide-template.md) 2. [Best Practices](best-practices.md) 3. [Contributing](contributing.md) +4. [Resource to Tutorial Mapping](resource_tutorial_map.md) ## Primary Links diff --git a/docs/datasets.md b/docs/datasets.md index e1be5730..d96221e8 100644 --- a/docs/datasets.md +++ b/docs/datasets.md @@ -10,7 +10,7 @@ Arabic Quran text in multiple scripts and related representations. - Link: [https://qul.tarteel.ai/resources/quran-script](https://qul.tarteel.ai/resources/quran-script) - Typical use: Quran readers, script rendering, typography work -- Guide: [resource-quran-script.md](resource-quran-script.md) +- Tutorial: [tutorial-quran-script-end-to-end.md](tutorial-quran-script-end-to-end.md) ## Translations @@ -18,7 +18,7 @@ Ayah-level and word-level translations across languages. - Link: [https://qul.tarteel.ai/resources/translation](https://qul.tarteel.ai/resources/translation) - Typical use: multilingual readers, search, localization -- Guide: [resource-translations.md](resource-translations.md) +- Tutorial: [tutorial-translation-end-to-end.md](tutorial-translation-end-to-end.md) ## Tafsir @@ -26,7 +26,7 @@ Tafsir datasets in multiple languages and styles. - Link: [https://qul.tarteel.ai/resources/tafsir](https://qul.tarteel.ai/resources/tafsir) - Typical use: commentary panels, study tools -- Guide: [resource-tafsirs.md](resource-tafsirs.md) +- Tutorial: [tutorial-tafsir-end-to-end.md](tutorial-tafsir-end-to-end.md) ## Recitations @@ -34,7 +34,7 @@ Audio recitation data and segment timing. - Link: [https://qul.tarteel.ai/resources/recitation](https://qul.tarteel.ai/resources/recitation) - Typical use: memorization tools, synchronized playback -- Guide: [resource-recitations.md](resource-recitations.md) +- Tutorial: [tutorial-recitation-end-to-end.md](tutorial-recitation-end-to-end.md) ## Morphology and Grammar @@ -42,7 +42,7 @@ Word-level linguistic features such as roots, lemmas, and POS tags. - Link: [https://qul.tarteel.ai/resources/morphology](https://qul.tarteel.ai/resources/morphology) - Typical use: NLP pipelines, grammar exploration, word study -- Guide: [resource-morphology.md](resource-morphology.md) +- Tutorial: [tutorial-morphology-end-to-end.md](tutorial-morphology-end-to-end.md) ## Topics and Concepts @@ -50,7 +50,11 @@ Theme/topic tagging for ayahs and semantic exploration. - Link: [https://qul.tarteel.ai/resources/ayah-topics](https://qul.tarteel.ai/resources/ayah-topics) - Typical use: thematic search and discovery -- Guide: [resource-topics-and-concepts.md](resource-topics-and-concepts.md) +- Tutorials: + - [tutorial-ayah-topics-end-to-end.md](tutorial-ayah-topics-end-to-end.md) + - [tutorial-mutashabihat-end-to-end.md](tutorial-mutashabihat-end-to-end.md) + - [tutorial-similar-ayah-end-to-end.md](tutorial-similar-ayah-end-to-end.md) + - [tutorial-ayah-theme-end-to-end.md](tutorial-ayah-theme-end-to-end.md) ## Surah Information @@ -58,7 +62,7 @@ Contextual and descriptive metadata about surahs. - Link: [https://qul.tarteel.ai/resources/surah-info](https://qul.tarteel.ai/resources/surah-info) - Typical use: educational views and chapter overviews -- Guide: [resource-surah-information.md](resource-surah-information.md) +- Tutorial: [tutorial-surah-information-end-to-end.md](tutorial-surah-information-end-to-end.md) ## Structural Metadata @@ -66,7 +70,7 @@ Navigation-oriented entities like juz, hizb, rub, and manzil. - Link: [https://qul.tarteel.ai/resources/quran-metadata](https://qul.tarteel.ai/resources/quran-metadata) - Typical use: navigation UIs and indexing -- Guide: [resource-quran-metadata.md](resource-quran-metadata.md) +- Tutorial: [tutorial-quran-metadata-end-to-end.md](tutorial-quran-metadata-end-to-end.md) ## Transliteration @@ -74,7 +78,7 @@ Non-Arabic script/phonetic representation for pronunciation support. - Link: [https://qul.tarteel.ai/resources/transliteration](https://qul.tarteel.ai/resources/transliteration) - Typical use: pronunciation support and beginner-friendly reading -- Guide: [resource-transliteration.md](resource-transliteration.md) +- Tutorial: [tutorial-transliteration-end-to-end.md](tutorial-transliteration-end-to-end.md) ## Fonts @@ -82,7 +86,7 @@ Quran-focused fonts and related usage assets. - Link: [https://qul.tarteel.ai/resources/font](https://qul.tarteel.ai/resources/font) - Typical use: script rendering and web/app typography -- Guide: [resource-fonts.md](resource-fonts.md) +- Tutorial: [tutorial-font-end-to-end.md](tutorial-font-end-to-end.md) ## Mushaf Layouts @@ -90,4 +94,4 @@ Page-oriented layout resources for mushaf-style viewing. - Link: [https://qul.tarteel.ai/resources/mushaf-layout](https://qul.tarteel.ai/resources/mushaf-layout) - Typical use: page-by-page Quran navigation and rendering -- Guide: [resource-mushaf-layouts.md](resource-mushaf-layouts.md) +- Tutorial: [tutorial-mushaf-layout-end-to-end.md](tutorial-mushaf-layout-end-to-end.md) diff --git a/docs/getting-started.md b/docs/getting-started.md index 9cde4a1f..7eedfd88 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -74,6 +74,6 @@ For details, see [data-model.md](data-model.md). - Detailed download and format guidance: [downloading-data.md](downloading-data.md) - Category-by-category overview: [datasets.md](datasets.md) -- Per-resource step-by-step guides: [resource-guides-index.md](resource-guides-index.md) -- Practical implementations: [tutorials.md](tutorials.md) +- Canonical step-by-step implementations: [tutorials.md](tutorials.md) +- Resource guides compatibility index: [resource-guides-index.md](resource-guides-index.md) - Common questions: [faq.md](faq.md) diff --git a/docs/resource-ayah-theme.md b/docs/resource-ayah-theme.md index 321434f9..b4f94f5b 100644 --- a/docs/resource-ayah-theme.md +++ b/docs/resource-ayah-theme.md @@ -1,85 +1,15 @@ # Ayah Theme Guide -This guide is for resource users who want concise thematic summaries for ayah groups. +This page is kept for compatibility with older links. -Category URL: +Canonical implementation guide: -- [https://qul.tarteel.ai/resources/ayah-theme](https://qul.tarteel.ai/resources/ayah-theme) - -## What This Resource Is - -Ayah Theme resources provide short theme statements linked to ayah ranges. - -Typical fields include: - -- `theme` -- `surah_number` -- `ayah_from` / `ayah_to` -- keywords/tags - -## When to Use It - -Use ayah-theme data for: - -- Passage summary banners -- Theme-first reading aids -- Study tools with contextual cues - -## How to Get Your First Example Resource - -1. Open [https://qul.tarteel.ai/resources/ayah-theme](https://qul.tarteel.ai/resources/ayah-theme). -2. Keep default listing order. -3. Open first published card. -4. Verify `Theme Preview` and `Help`. -5. Download available format (commonly `sqlite`). - -## What the Preview and Help Tabs Show - -- `Theme Preview`: - - Theme for selected ayah - - Group/range coverage notes -- `Help`: - - Column descriptions for theme and ranges - -Integration implication: - -- Resolve themes by ayah range inclusion, not exact key match only. - -## Download and Integration Checklist - -1. Import theme rows. -2. Build range-based resolver. -3. For each ayah, find matching theme row. -4. Render theme + keywords above ayah block. - -## Real-World Usage Example - -Goal: - -- Show current passage theme while user reads. - -Expected outcome: - -- Theme updates correctly when ayah crosses into a new range. - -## Common Mistakes - -- Matching themes only to single ayah IDs. -- Ignoring ayah range boundaries. - -## When to Request Updates or Changes - -Open an issue when: - -- range values are incorrect -- theme text/keywords are missing -- download links are broken +- [Tutorial 14: Ayah Theme End-to-End](tutorial-ayah-theme-end-to-end.md) -Issue link: +Recommended starting point for new users: -- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) +- [Tutorials Index](tutorials.md) -## Related Pages +Resource directory entry: -- [Tutorial 14: Ayah Theme End-to-End](tutorial-ayah-theme-end-to-end.md) -- [Ayah Topics Guide](resource-ayah-topics.md) +- [https://qul.tarteel.ai/resources/ayah-theme](https://qul.tarteel.ai/resources/ayah-theme) diff --git a/docs/resource-ayah-topics.md b/docs/resource-ayah-topics.md index 08b79264..ddc64baa 100644 --- a/docs/resource-ayah-topics.md +++ b/docs/resource-ayah-topics.md @@ -1,86 +1,15 @@ # Ayah Topics Guide -This guide is for resource users who want to map topics/concepts to related ayahs. +This page is kept for compatibility with older links. -Category URL: +Canonical implementation guide: -- [https://qul.tarteel.ai/resources/ayah-topics](https://qul.tarteel.ai/resources/ayah-topics) - -## What This Resource Is - -Ayah Topics resources provide topic entities and topic-to-ayah mappings. - -Typical data includes: - -- Topic metadata (`topic_id`, name, category) -- Mapping records from topic to `ayah_key` -- Topic counts/searchable labels - -## When to Use It - -Use ayah-topics data for: - -- Topic-first Quran exploration -- Educational thematic pathways -- Concept search interfaces - -## How to Get Your First Example Resource - -1. Open [https://qul.tarteel.ai/resources/ayah-topics](https://qul.tarteel.ai/resources/ayah-topics). -2. Keep default listing order. -3. Open the first published resource card. -4. Verify topics pane and `Help` tab are present. -5. Download available format (commonly `sqlite`). - -## What the Preview and Help Tabs Show - -- Topics pane: - - Topic list + search + topic links -- `Help` tab: - - Topic source/context notes - - Mapping usage guidance - -Integration implication: - -- Keep topic metadata and mapping rows as separate layers. - -## Download and Integration Checklist - -1. Import topic rows. -2. Import topic-to-ayah mappings. -3. Index by `topic_id` and `ayah_key`. -4. Build topic search + topic detail endpoints. -5. Join mapped ayah keys with script/translation for display. - -## Real-World Usage Example - -Goal: - -- User selects a topic and sees related ayahs with text preview. - -Expected outcome: - -- Stable mapping between selected topic and listed ayahs. - -## Common Mistakes - -- Mixing topic IDs from different datasets without normalization. -- Assuming one topic maps to a single ayah. - -## When to Request Updates or Changes - -Open an issue when: - -- Topic-to-ayah mapping appears wrong -- Topic labels/categories are inconsistent -- Download links are broken +- [Tutorial 10: Ayah Topics End-to-End](tutorial-ayah-topics-end-to-end.md) -Issue link: +Recommended starting point for new users: -- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) +- [Tutorials Index](tutorials.md) -## Related Pages +Resource directory entry: -- [Tutorial 10: Ayah Topics End-to-End](tutorial-ayah-topics-end-to-end.md) -- [Quran Script Guide](resource-quran-script.md) -- [Translations Guide](resource-translations.md) +- [https://qul.tarteel.ai/resources/ayah-topics](https://qul.tarteel.ai/resources/ayah-topics) diff --git a/docs/resource-fonts.md b/docs/resource-fonts.md index f5477301..9355be03 100644 --- a/docs/resource-fonts.md +++ b/docs/resource-fonts.md @@ -1,53 +1,15 @@ # Quran Fonts Guide -## What This Resource Is +This page is kept for compatibility with older links. -Quran Fonts resources provide fonts used for Quranic rendering workflows and script-specific display needs. +Canonical implementation guide: -Category URL: [https://qul.tarteel.ai/resources/font](https://qul.tarteel.ai/resources/font) +- [Tutorial 6: Font End-to-End](tutorial-font-end-to-end.md) -## When to Use It +Recommended starting point for new users: -- Rendering Quran text with specific typographic styles -- Supporting script-sensitive display in web/apps -- Matching visual style requirements across platforms +- [Tutorials Index](tutorials.md) -## How to Download or Access It +Resource directory entry: -1. Open the category URL above. -2. Select a font package. -3. Download available font formats (`ttf`, `woff`, `woff2`, or similar). -4. Load font files into your app/web assets. - -## Step-by-Step Integration - -1. Place font files in your asset path. -2. Register font with `@font-face`. -3. Apply font family to Quran text components. -4. Validate glyph support for your target script content. -5. Test rendering on desktop/mobile browsers. - -## Real-World Usage Example - -Goal: use a dedicated Quran font in a web reader. - -Flow: - -1. Download font package. -2. Add `@font-face` in CSS. -3. Apply class to ayah text block. -4. Validate readability and line-height. - -Expected outcome: - -- Quran text renders with intended typography across key screens. - -## When to Request Updates or Changes - -Open an issue when: - -- Font files are missing/broken -- Glyph support appears incomplete -- Font metadata/docs are unclear - -Issue link: [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) +- [https://qul.tarteel.ai/resources/font](https://qul.tarteel.ai/resources/font) diff --git a/docs/resource-guides-index.md b/docs/resource-guides-index.md index 4f58cd9c..e4910793 100644 --- a/docs/resource-guides-index.md +++ b/docs/resource-guides-index.md @@ -1,30 +1,15 @@ -# Resource Guides Index +# Resource Guides Index (Compatibility Alias) -Use these guides when your priority is downloading QUL resources and building with them. +This page is kept for compatibility with older links. -Guide coverage is aligned with categories visible on the official resources directory. +Canonical step-by-step docs are now maintained in Tutorials. -## Core Guides +- [Tutorials Index](tutorials.md) -1. [Quran Script Guide](resource-quran-script.md) -2. [Translations Guide](resource-translations.md) -3. [Tafsirs Guide](resource-tafsirs.md) -4. [Recitations Guide](resource-recitations.md) -5. [Quran Metadata Guide](resource-quran-metadata.md) -6. [Quran Fonts Guide](resource-fonts.md) -7. [Transliteration Guide](resource-transliteration.md) -8. [Surah Information Guide](resource-surah-information.md) -9. [Ayah Topics Guide](resource-ayah-topics.md) -10. [Morphology Guide](resource-morphology.md) -11. [Mutashabihat Guide](resource-mutashabihat.md) -12. [Similar Ayah Guide](resource-similar-ayah.md) -13. [Ayah Theme Guide](resource-ayah-theme.md) -14. [Mushaf Layouts Guide](resource-mushaf-layouts.md) - -## Legacy / Umbrella +Legacy umbrella page: - [Topics and Concepts Guide (legacy umbrella)](resource-topics-and-concepts.md) -## Official Resource Directory +Official resource directory: - [https://qul.tarteel.ai/resources](https://qul.tarteel.ai/resources) diff --git a/docs/resource-morphology.md b/docs/resource-morphology.md index fb23b832..b1899cfb 100644 --- a/docs/resource-morphology.md +++ b/docs/resource-morphology.md @@ -1,53 +1,15 @@ # Morphology Guide -## What This Resource Is +This page is kept for compatibility with older links. -Morphology resources provide word-level linguistic analysis such as roots, lemmas, grammatical tags, and related structures. +Canonical implementation guide: -Category URL: [https://qul.tarteel.ai/resources/morphology](https://qul.tarteel.ai/resources/morphology) +- [Tutorial 11: Morphology End-to-End](tutorial-morphology-end-to-end.md) -## When to Use It +Recommended starting point for new users: -- Word study tools -- Arabic NLP research workflows -- Educational grammar features +- [Tutorials Index](tutorials.md) -## How to Download or Access It +Resource directory entry: -1. Open the category URL above. -2. Select morphology package. -3. Download JSON or SQLite. -4. Validate word-level keys before import. - -## Step-by-Step Integration - -1. Import morphology rows with word-level keys. -2. Join with Quran Script by `surah_id + ayah_number + word_position`. -3. Store root/lemma/POS fields in normalized tables/collections. -4. Add indexes on ayah identity + word position. -5. Build per-word inspection UI in your app. - -## Real-World Usage Example - -Goal: create a "tap word for grammar details" feature. - -Flow: - -1. Render ayah words in order. -2. User taps a word token. -3. App queries morphology entry for matching ayah + position. -4. App displays root, lemma, and grammatical metadata. - -Expected outcome: - -- Users can inspect linguistic details word-by-word. - -## When to Request Updates or Changes - -Open an issue when: - -- Word positions do not align with ayah text -- Root/lemma fields are missing or malformed -- Grammar tags are inconsistent across rows - -Issue link: [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) +- [https://qul.tarteel.ai/resources/morphology](https://qul.tarteel.ai/resources/morphology) diff --git a/docs/resource-mushaf-layouts.md b/docs/resource-mushaf-layouts.md index 01849d2e..91b12821 100644 --- a/docs/resource-mushaf-layouts.md +++ b/docs/resource-mushaf-layouts.md @@ -1,158 +1,15 @@ # Mushaf Layouts Guide -This guide is for resource users who want to download and integrate Mushaf layout datasets from QUL. +This page is kept for compatibility with older links. -Category URL: +Canonical implementation guide: -- [https://qul.tarteel.ai/resources/mushaf-layout](https://qul.tarteel.ai/resources/mushaf-layout) - -## What This Resource Is - -Mushaf Layout resources provide page-structured data used to render Quran pages in a mushaf-like format. - -You can expect: - -- Page-by-page line layout -- Line type metadata (`ayah`, `surah_name`, `basmallah`) -- Word ID ranges per ayah line -- Alignment metadata (`is_centered`) - -## When to Use It - -Use mushaf layout data when building: - -- Page-faithful Quran readers -- Page navigation flows (prev/next page, jump to page) -- Learning tools that depend on printed page structure - -## How to Get Your First Example Resource - -Use this repeatable selection rule: - -1. Open [https://qul.tarteel.ai/resources/mushaf-layout](https://qul.tarteel.ai/resources/mushaf-layout). -2. Keep the default listing order. -3. Open the first published resource card. -4. Verify the detail page has: - - `Mushaf Page Preview` tab - - `Help` tab -5. Confirm available download formats shown on page (commonly `images`, `sqlite`, `docx`). - -This keeps onboarding concrete without hardcoding a resource ID. - -## What the Preview and Help Tabs Show - -On the mushaf layout detail page: - -- `Mushaf Page Preview` tab: - - `Jump to page` selector - - Previous/next page navigation - - Rendered page output with line/word structure -- `Help` tab: - - Required resources for rendering (layout + script + font + surah names) - - `pages` table fields and meaning - - `words` table field expectations - - Sample rendering code - -Integration implication: - -- Use `pages` table as your rendering skeleton. -- Use `first_word_id`/`last_word_id` to map ayah lines to word text. - -## Download and Integration Checklist - -1. Download the selected package. -2. Inspect fields in the layout data: - - `page_number` - - `line_number` - - `line_type` - - `is_centered` - - `first_word_id`, `last_word_id` - - `surah_number` -3. Load compatible word-by-word Quran script data. -4. Build index maps: - - `wordsByIndex[word_index] -> text` - - `surahNameByNumber[surah_number] -> surah name` -5. Render each line by `line_type`: - - `surah_name` => render surah heading - - `basmallah` => render basmallah line - - `ayah` => render words from `first_word_id..last_word_id` -6. Apply line alignment using `is_centered`. -7. Validate on multiple pages (start, middle, end). +- [Tutorial 2: Mushaf Layout End-to-End](tutorial-mushaf-layout-end-to-end.md) -Starter integration snippet (JavaScript): +Recommended starting point for new users: -```javascript -const linesForPage = (pagesRows, pageNumber) => - pagesRows - .filter((row) => row.page_number === pageNumber) - .sort((a, b) => a.line_number - b.line_number); +- [Tutorials Index](tutorials.md) -const wordsInRange = (wordsByIndex, firstWordId, lastWordId) => { - const words = []; - for (let id = firstWordId; id <= lastWordId; id += 1) { - if (wordsByIndex[id]) words.push(wordsByIndex[id].text); - } - return words.join(" "); -}; +Resource directory entry: -const renderLine = (line, wordsByIndex, surahNameByNumber) => { - if (line.line_type === "surah_name") return surahNameByNumber[line.surah_number] || ""; - if (line.line_type === "basmallah") return "﷽"; - if (line.line_type === "ayah") return wordsInRange(wordsByIndex, line.first_word_id, line.last_word_id); - return ""; -}; -``` - -## Real-World Usage Example - -Goal: - -- Render one complete page in a mushaf-like reader. - -Required resources: - -- Mushaf Layout package -- Quran Script package (word-by-word) -- Surah names metadata - -Flow: - -1. User opens page 1. -2. App loads lines for page 1 from layout data. -3. For each ayah line, app resolves words by `first_word_id..last_word_id`. -4. UI renders lines in order with centered/justified alignment. -5. User moves to next page and flow repeats. - -Expected outcome: - -- Page structure is visually consistent with selected mushaf layout. -- Text ordering and line boundaries remain stable. - -## Common Mistakes - -- Treating all lines as ayah lines and ignoring `line_type`. -- Using wrong key for word range mapping. -- Ignoring `is_centered` and breaking page appearance. -- Testing only one page and missing edge cases. -- Mixing incompatible script/font with selected layout. - -## When to Request Updates or Changes - -Open an issue when you find: - -- Incorrect line-to-word mapping -- Missing lines/pages or broken page navigation -- Download package inconsistencies -- Metadata conflicts between preview and downloaded data - -Issue link: - -- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) - -## Related Pages - -- [Tutorials](tutorials.md) -- [Quran Script Guide](resource-quran-script.md) -- [Fonts Guide](resource-fonts.md) -- [Quran Metadata Guide](resource-quran-metadata.md) -- [Downloading and Using Data](downloading-data.md) +- [https://qul.tarteel.ai/resources/mushaf-layout](https://qul.tarteel.ai/resources/mushaf-layout) diff --git a/docs/resource-mutashabihat.md b/docs/resource-mutashabihat.md index 265beb8b..394f18b4 100644 --- a/docs/resource-mutashabihat.md +++ b/docs/resource-mutashabihat.md @@ -1,81 +1,15 @@ # Mutashabihat Guide -This guide is for resource users who want to work with phrase-level Quran similarity mappings. +This page is kept for compatibility with older links. -Category URL: +Canonical implementation guide: -- [https://qul.tarteel.ai/resources/mutashabihat](https://qul.tarteel.ai/resources/mutashabihat) - -## What This Resource Is - -Mutashabihat resources map shared/similar phrases across ayahs. - -Common files/structures include: - -- `phrases.json` -- `phrase_verses.json` - -## When to Use It - -Use mutashabihat data for: - -- Memorization revision tools -- Similar phrase comparison features - -## How to Get Your First Example Resource - -1. Open [https://qul.tarteel.ai/resources/mutashabihat](https://qul.tarteel.ai/resources/mutashabihat). -2. Keep default listing order. -3. Open first published card. -4. Verify `Mutashabihat Preview` and `Help`. -5. Download available format (commonly `json`). - -## What the Preview and Help Tabs Show - -- `Mutashabihat Preview`: - - Ayah-based phrase relationship exploration -- `Help`: - - File structure and phrase-lookup flow - -Integration implication: - -- Resolve phrase IDs first, then fetch phrase objects. - -## Download and Integration Checklist - -1. Load phrase mapping file (`phrase_verses`). -2. Load phrase dictionary file (`phrases`). -3. For each ayah, resolve phrase IDs to phrase entries. -4. Optionally join with script words for highlighting. - -## Real-World Usage Example - -Goal: - -- Show similar phrases for selected ayah in revision mode. - -Expected outcome: - -- Users can compare phrase overlaps across ayahs. - -## Common Mistakes - -- Treating phrase mapping as direct text data. -- Ignoring missing phrase IDs or orphan entries. - -## When to Request Updates or Changes - -Open an issue when: - -- phrase IDs point to missing phrase entries -- phrase mapping appears incorrect -- download links are broken +- [Tutorial 12: Mutashabihat End-to-End](tutorial-mutashabihat-end-to-end.md) -Issue link: +Recommended starting point for new users: -- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) +- [Tutorials Index](tutorials.md) -## Related Pages +Resource directory entry: -- [Tutorial 12: Mutashabihat End-to-End](tutorial-mutashabihat-end-to-end.md) -- [Similar Ayah Guide](resource-similar-ayah.md) +- [https://qul.tarteel.ai/resources/mutashabihat](https://qul.tarteel.ai/resources/mutashabihat) diff --git a/docs/resource-quran-metadata.md b/docs/resource-quran-metadata.md index f07fbfe4..06f728ea 100644 --- a/docs/resource-quran-metadata.md +++ b/docs/resource-quran-metadata.md @@ -1,53 +1,15 @@ # Quran Metadata Guide -## What This Resource Is +This page is kept for compatibility with older links. -Quran Metadata resources provide structural navigation entities such as juz, hizb, rub, manzil, and related Quran organization data. +Canonical implementation guide: -Category URL: [https://qul.tarteel.ai/resources/quran-metadata](https://qul.tarteel.ai/resources/quran-metadata) +- [Tutorial 7: Quran Metadata End-to-End](tutorial-quran-metadata-end-to-end.md) -## When to Use It +Recommended starting point for new users: -- Building Quran navigation menus -- Filtering by juz/hizb/manzil -- Creating learning journeys based on Quran structure +- [Tutorials Index](tutorials.md) -## How to Download or Access It +Resource directory entry: -1. Open the category URL above. -2. Select metadata package. -3. Download JSON or SQLite. -4. Validate structural keys and ayah references. - -## Step-by-Step Integration - -1. Import metadata tables/collections. -2. Map structural entries to ayah ranges. -3. Add indexed lookups by structure number (`juz`, `hizb`, etc.). -4. Connect metadata filters to your ayah list query. -5. Test navigation from structure -> ayah list -> ayah detail. - -## Real-World Usage Example - -Goal: add "Browse by Juz" in a mobile app. - -Flow: - -1. Load juz records from metadata. -2. User selects Juz 30. -3. App resolves ayah range for selected juz. -4. App renders ayahs and supports jump-to-next-juz. - -Expected outcome: - -- Users navigate Quran structurally without manual ayah lookup. - -## When to Request Updates or Changes - -Open an issue when: - -- Structural ranges do not map correctly to ayahs -- Metadata entries are missing/incomplete -- Downloads are broken or out-of-date - -Issue link: [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) +- [https://qul.tarteel.ai/resources/quran-metadata](https://qul.tarteel.ai/resources/quran-metadata) diff --git a/docs/resource-quran-script.md b/docs/resource-quran-script.md index 0a442931..27e97540 100644 --- a/docs/resource-quran-script.md +++ b/docs/resource-quran-script.md @@ -1,160 +1,15 @@ # Quran Script Guide -This guide is for resource users who want to download and integrate Quran Script datasets from QUL. +This page is kept for compatibility with older links. -Category URL: +Canonical implementation guide: -- [https://qul.tarteel.ai/resources/quran-script](https://qul.tarteel.ai/resources/quran-script) - -## What This Resource Is - -Quran Script resources provide Arabic Quran text in script-aware formats (word-by-word or ayah-by-ayah). - -Depending on the selected package, entries can include: - -- `verse_key` in `surah:ayah` -- Verse text (`text`) -- Rendering metadata (`script_type`, `font_family`) -- Word arrays (`words[].position`, `words[].text`, `words[].location`) -- Navigation metadata (`page_number`, `juz_number`, `hizb_number`) - -## When to Use It - -Use Quran Script data when building: - -- Arabic Quran readers -- Word-by-word study views -- Integrations with translation, tafsir, and recitation by shared ayah keys - -## How to Get Your First Example Resource - -Use this stable selection rule: - -1. Open [https://qul.tarteel.ai/resources/quran-script](https://qul.tarteel.ai/resources/quran-script). -2. Keep the default listing order. -3. Open the first published resource card. -4. Verify the detail page includes: - - `Preview` tab - - `Help` tab -5. Confirm available download links: - - `sqlite` - - `json` -6. Confirm whether package is `Word by word` or `Ayah by ayah`. - -This keeps onboarding concrete without hardcoded resource IDs. - -## What the Preview and Help Tabs Show - -On the script detail page: - -- `Preview` tab: - - `Jump to Ayah` - - Previous/next ayah navigation - - Rendered Arabic output (word blocks in word-by-word packages) -- `Help` tab: - - Sample JSON - - Field descriptions (`verse_key`, `text`, `script_type`, `font_family`, `words`) - - Usage examples for CSS and JavaScript rendering - -Integration implication: - -- You should carry `font_family` into UI rendering rules. -- You should use `verse_key` and `words[].location` as canonical join keys. - -## Download and Integration Checklist - -1. Download script package (`json` or `sqlite`). -2. Normalize keys: - - Ayah key: `surah:ayah` - - Word key: `surah:ayah:word` -3. Index verse rows by `verse_key`. -4. If word data exists, sort words by `position` before rendering. -5. Render with RTL + script-aware font fallback. -6. Join with translation/tafsir/recitation by ayah key. -7. Validate full-surah rendering and random ayah checks. - -Starter integration snippet (JavaScript): - -```javascript -const buildVerseIndex = (rows) => - rows.reduce((index, row) => { - index[row.verse_key] = { - text: row.text, - scriptType: row.script_type, - fontFamily: row.font_family, - words: Array.isArray(row.words) ? row.words : [] - }; - return index; - }, {}); - -const renderVerse = (container, verse) => { - container.dir = "rtl"; - container.style.textAlign = "right"; - container.style.fontFamily = `${verse.fontFamily || "serif"}, "Amiri Quran", "Noto Naskh Arabic", serif`; - container.textContent = verse.text; -}; - -const renderWords = (container, words) => { - container.innerHTML = ""; - words - .slice() - .sort((a, b) => a.position - b.position) - .forEach((word) => { - const chip = document.createElement("span"); - chip.textContent = word.text; - chip.title = word.location; - chip.style.margin = "4px"; - chip.style.padding = "6px 10px"; - chip.style.border = "1px solid #e2e8f0"; - chip.style.borderRadius = "8px"; - container.appendChild(chip); - }); -}; -``` - -## Real-World Usage Example - -Goal: - -- Render one selected ayah as full text plus word-by-word blocks. - -Flow: - -1. User selects ayah key. -2. App loads script row by `verse_key`. -3. App renders full verse text. -4. App renders `words[]` sorted by `position`. - -Expected outcome: - -- Arabic text is correct and readable. -- Word order is stable. -- Keys remain compatible with downstream joins. - -## Common Mistakes - -- Ignoring `font_family` and assuming script text itself is wrong. -- Joining with other datasets by row position instead of ayah key. -- Ignoring `words[].position` in word-by-word rendering. -- Mixing word-by-word assumptions into ayah-by-ayah resources. - -## When to Request Updates or Changes - -Open an issue when you find: - -- Missing or incorrect `verse_key` rows -- Broken json/sqlite links -- Incorrect word order or missing `words[].location` -- Inconsistent `script_type` / `font_family` metadata +- [Tutorial 5: Quran Script End-to-End](tutorial-quran-script-end-to-end.md) -Issue link: +Recommended starting point for new users: -- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) +- [Tutorials Index](tutorials.md) -## Related Pages +Resource directory entry: -- [Tutorials](tutorials.md) -- [Tutorial 5: Quran Script End-to-End](tutorial-quran-script-end-to-end.md) -- [Translations Guide](resource-translations.md) -- [Tafsirs Guide](resource-tafsirs.md) -- [Downloading and Using Data](downloading-data.md) +- [https://qul.tarteel.ai/resources/quran-script](https://qul.tarteel.ai/resources/quran-script) diff --git a/docs/resource-recitations.md b/docs/resource-recitations.md index 6f53f8a2..5ad14f69 100644 --- a/docs/resource-recitations.md +++ b/docs/resource-recitations.md @@ -1,170 +1,15 @@ # Recitations Guide -This guide is for resource users who want to download and integrate Quran recitation datasets from QUL. +This page is kept for compatibility with older links. -Category URL: +Canonical implementation guide: -- [https://qul.tarteel.ai/resources/recitation](https://qul.tarteel.ai/resources/recitation) - -## What This Resource Is - -Recitation resources provide Quran audio plus optional timing metadata that can be used for synchronized highlighting. - -From the resource preview/help flow, you should expect one of these patterns: - -- Surah-by-surah audio (one continuous file per surah) -- Ayah-by-ayah audio (one file per ayah) -- Segment arrays/timestamps for fine-grained sync - -## When to Use It - -Use recitation data when building: - -- Quran playback experiences -- Memorization and revision apps -- Interfaces that highlight ayah or words in sync with audio - -## How to Get Your First Example Resource - -Use this stable, repeatable selection rule: - -1. Open [https://qul.tarteel.ai/resources/recitation](https://qul.tarteel.ai/resources/recitation). -2. Keep the default listing order. -3. Open the first published resource card. -4. Verify the detail page has: - - `Recitation` preview tab - - `Help` tab with format samples -5. Download the available format (`JSON`, `SQLite`, or both). - -This keeps onboarding concrete without hardcoding a resource ID. - -## What the Preview and Help Tabs Show - -On the recitation detail page: - -- `Recitation` tab: - - Ayah picker (`Jump to Ayah`) - - Previous/next ayah navigation - - Audio player and highlight behavior driven by timing data -- `Help` tab: - - Surah-by-surah vs ayah-by-ayah recitation explanation - - Segment format examples and timestamp structures - -Integration implication: - -- If segment data exists, implement synchronized highlight mode. -- If segment data is absent, support audio playback without forced highlighting. - -## Download and Integration Checklist - -1. Download the package. -2. Inspect fields in the downloaded file: - - Ayah identity (`surah`, `ayah`, or `surah:ayah`) - - Audio pointer (`audio_url`, file path, or equivalent) - - Timing fields when present (`segments`, `timestamp_from`, `timestamp_to`, duration) -3. Normalize keys before joins: - - Recommended canonical key: `ayah_key = "#{surah}:#{ayah}"` -4. Load matching Quran text from [Quran Script Guide](resource-quran-script.md). -5. Join recitation rows and Quran text rows by ayah key. -6. Build a minimal playback sequence: - - Start audio - - Read current playback timestamp - - Resolve active ayah or segment - - Update highlight state -7. Validate on a full surah, not only one ayah. - -Starter integration snippet (JavaScript): - -```javascript -const normalizeAyahKey = (row) => row.ayah_key || `${row.surah}:${row.ayah}`; - -const buildTimingWindows = (recitationRows) => - recitationRows.map((row) => ({ - ayahKey: normalizeAyahKey(row), - from: Number(row.timestamp_from ?? 0), - to: Number(row.timestamp_to ?? 0), - segments: Array.isArray(row.segments) ? row.segments : [], - audioUrl: row.audio_url - })); +- [Tutorial 1: Recitation End-to-End](tutorial-recitation-end-to-end.md) -const findActiveAyah = (timings, currentTime) => - timings.find((t) => currentTime >= t.from && currentTime < t.to) || null; +Recommended starting point for new users: -const scriptRowByKey = new Map(scriptRows.map((row) => [`${row.surah}:${row.ayah}`, row])); -const timings = buildTimingWindows(recitationRows); +- [Tutorials Index](tutorials.md) -audioElement.addEventListener("timeupdate", () => { - const active = findActiveAyah(timings, audioElement.currentTime); - if (!active) return; +Resource directory entry: - const ayahText = scriptRowByKey.get(active.ayahKey); - updateHighlightedAyah(active.ayahKey, ayahText); -}); -``` - -## Real-World Usage Example - -Goal: - -- Play a full surah while highlighting the currently recited ayah. - -Required resources: - -- Recitation package -- Quran Script package - -Flow: - -1. User chooses a surah. -2. App loads all ayahs for that surah from Quran Script. -3. App loads recitation timing map for the same surah. -4. During playback, app maps player time to the active ayah window. -5. UI highlights the active ayah and moves as playback progresses. - -Expected outcome: - -- Audio and text progression stay synchronized. -- Highlight transitions are stable and not delayed/jumping. - -Sample input/output for one ayah: - -```json -{ - "input": { - "recitation_row": { "surah": 1, "ayah": 2, "timestamp_from": 2.0, "timestamp_to": 5.0 }, - "script_row": { "surah": 1, "ayah": 2, "text": "Alhamdulillahi Rabbil Alamin" }, - "player_time": 3.4 - }, - "output": { - "active_ayah_key": "1:2", - "highlighted_text": "Alhamdulillahi Rabbil Alamin" - } -} -``` - -## Common Mistakes - -- Assuming every recitation is segmented. -- Joining by row order instead of ayah identity. -- Mixing key styles without normalization. -- Ignoring missing or null timing fields. -- Testing only one ayah and skipping full-surah validation. - -## When to Request Updates or Changes - -Open an issue when you find: - -- Broken audio links or inaccessible files -- Timestamp/segment windows that do not match audible recitation -- Missing ayah mappings in the exported package -- Reciter metadata inconsistencies - -Issue link: - -- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) - -## Related Pages - -- [Tutorials](tutorials.md) -- [Downloading and Using Data](downloading-data.md) -- [Data Model](data-model.md) +- [https://qul.tarteel.ai/resources/recitation](https://qul.tarteel.ai/resources/recitation) diff --git a/docs/resource-similar-ayah.md b/docs/resource-similar-ayah.md index c17a1a38..b0a686b7 100644 --- a/docs/resource-similar-ayah.md +++ b/docs/resource-similar-ayah.md @@ -1,85 +1,15 @@ # Similar Ayah Guide -This guide is for resource users who want to integrate ayah-level similarity rankings. +This page is kept for compatibility with older links. -Category URL: +Canonical implementation guide: -- [https://qul.tarteel.ai/resources/similar-ayah](https://qul.tarteel.ai/resources/similar-ayah) - -## What This Resource Is - -Similar Ayah resources map each ayah to other ayahs with similarity metrics. - -Typical fields: - -- `verse_key` -- `matched_ayah_key` -- `matched_words` -- `coverage` -- `similarity_score` - -## When to Use It - -Use similar-ayah data for: - -- Compare ayah features -- Pattern discovery tools -- Memorization reinforcement - -## How to Get Your First Example Resource - -1. Open [https://qul.tarteel.ai/resources/similar-ayah](https://qul.tarteel.ai/resources/similar-ayah). -2. Keep default listing order. -3. Open first published card. -4. Verify `Similar Ayah Preview` and `Help`. -5. Download `json`/`sqlite`. - -## What the Preview and Help Tabs Show - -- `Similar Ayah Preview`: - - Similar ayah list for selected ayah -- `Help`: - - Similarity field definitions - -Integration implication: - -- Sort and interpret by score/coverage, not only raw match count. - -## Download and Integration Checklist - -1. Import similarity rows. -2. Group rows by `verse_key`. -3. Sort by `similarity_score`. -4. Join with script text for display context. - -## Real-World Usage Example - -Goal: - -- Show top 5 similar ayahs for selected ayah. - -Expected outcome: - -- Ranked matches with interpretable metrics. - -## Common Mistakes - -- Treating score as strict equivalence. -- Ignoring coverage and matched-word context. - -## When to Request Updates or Changes - -Open an issue when: - -- score/coverage values appear invalid -- key mapping looks incorrect -- downloads are broken +- [Tutorial 13: Similar Ayah End-to-End](tutorial-similar-ayah-end-to-end.md) -Issue link: +Recommended starting point for new users: -- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) +- [Tutorials Index](tutorials.md) -## Related Pages +Resource directory entry: -- [Tutorial 13: Similar Ayah End-to-End](tutorial-similar-ayah-end-to-end.md) -- [Mutashabihat Guide](resource-mutashabihat.md) +- [https://qul.tarteel.ai/resources/similar-ayah](https://qul.tarteel.ai/resources/similar-ayah) diff --git a/docs/resource-surah-information.md b/docs/resource-surah-information.md index 1bb78acb..76586e50 100644 --- a/docs/resource-surah-information.md +++ b/docs/resource-surah-information.md @@ -1,52 +1,15 @@ # Surah Information Guide -## What This Resource Is +This page is kept for compatibility with older links. -Surah Information resources provide chapter-level contextual data such as names, summaries, and descriptive metadata. +Canonical implementation guide: -Category URL: [https://qul.tarteel.ai/resources/surah-info](https://qul.tarteel.ai/resources/surah-info) +- [Tutorial 9: Surah Information End-to-End](tutorial-surah-information-end-to-end.md) -## When to Use It +Recommended starting point for new users: -- Surah overview screens -- Intro cards before reading a chapter -- Study features that explain chapter context +- [Tutorials Index](tutorials.md) -## How to Download or Access It +Resource directory entry: -1. Open the category URL above. -2. Select a surah-info package. -3. Download JSON or SQLite. -4. Validate chapter identity fields and language/source metadata. - -## Step-by-Step Integration - -1. Import surah-level metadata. -2. Join on chapter identity (`surah_id` / surah number). -3. Store localized titles/descriptions where available. -4. Cache chapter metadata for fast loading. -5. Render surah header cards in your reader app. - -## Real-World Usage Example - -Goal: show a surah introduction before ayah list. - -Flow: - -1. User opens Surah 36. -2. App fetches surah-info for that surah. -3. App displays chapter title and summary before ayahs. - -Expected outcome: - -- Users get context before reading. - -## When to Request Updates or Changes - -Open an issue when: - -- Surah title or summary appears incorrect -- Language versions are missing or mismatched -- Surah identifiers are inconsistent - -Issue link: [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) +- [https://qul.tarteel.ai/resources/surah-info](https://qul.tarteel.ai/resources/surah-info) diff --git a/docs/resource-tafsirs.md b/docs/resource-tafsirs.md index 97f51ffd..3029f9b6 100644 --- a/docs/resource-tafsirs.md +++ b/docs/resource-tafsirs.md @@ -1,148 +1,15 @@ # Tafsirs Guide -This guide is for resource users who want to download and integrate tafsir datasets from QUL. +This page is kept for compatibility with older links. -Category URL: +Canonical implementation guide: -- [https://qul.tarteel.ai/resources/tafsir](https://qul.tarteel.ai/resources/tafsir) - -## What This Resource Is - -Tafsir resources provide ayah-linked commentary. A tafsir entry can apply to one ayah or a group of ayahs. - -Depending on the package, you may get: - -- JSON grouped tafsir mapping by ayah key -- SQLite rows with group reference fields -- Tafsir text that may include simple HTML formatting - -## When to Use It - -Use tafsir data when building: - -- Ayah detail views with commentary -- Study and reflection panels -- Comparative tafsir reading workflows - -## How to Get Your First Example Resource - -Use this stable selection rule: - -1. Open [https://qul.tarteel.ai/resources/tafsir](https://qul.tarteel.ai/resources/tafsir). -2. Keep the default listing order. -3. Open the first published resource card. -4. Verify the detail page includes: - - `Tafsir Preview` tab - - `Help` tab -5. Confirm available downloads: - - `json` - - `sqlite` - -This keeps onboarding concrete without hardcoding a resource ID. - -## What the Preview and Help Tabs Show - -On the tafsir detail page: - -- `Tafsir Preview` tab: - - `Jump to Ayah` - - Previous/next ayah navigation - - Arabic text + tafsir text -- `Help` tab: - - JSON grouped export example - - Explanation of object vs pointer values by ayah key - - SQLite columns including `group_ayah_key`, `from_ayah`, `to_ayah`, `ayah_keys` - -Integration implication: - -- Tafsir can be shared across ayah ranges. -- Your resolver must follow grouped references. - -## Download and Integration Checklist - -1. Download the tafsir package (`json` or `sqlite`). -2. Normalize ayah keys to one format (`surah:ayah`). -3. Implement grouped tafsir resolution: - - JSON object value = main tafsir text - - JSON string value = pointer to main tafsir ayah key -4. For SQLite exports: - - If `text` is empty, resolve via `group_ayah_key` -5. Join with Quran Script by ayah key. -6. Render tafsir as optional panel/expandable section. -7. Validate ayah-range/grouped entries with adjacent ayahs. - -Starter integration snippet (JavaScript): - -```javascript -const resolveTafsir = (tafsirByKey, ayahKey) => { - const value = tafsirByKey[ayahKey]; - if (!value) return null; - - if (typeof value === "object" && value.text) { - return { - groupAyahKey: ayahKey, - text: value.text, - ayahKeys: Array.isArray(value.ayah_keys) ? value.ayah_keys : [ayahKey] - }; - } - - if (typeof value === "string") { - const main = tafsirByKey[value]; - if (main && typeof main === "object" && main.text) { - return { - groupAyahKey: value, - text: main.text, - ayahKeys: Array.isArray(main.ayah_keys) ? main.ayah_keys : [value] - }; - } - } - - return null; -}; -``` - -## Real-World Usage Example - -Goal: - -- Show tafsir in an ayah detail panel, even when tafsir is grouped across multiple ayahs. - -Flow: - -1. User opens ayah key. -2. App looks up tafsir payload for that ayah key. -3. If record is grouped/pointer, app resolves main tafsir text. -4. App renders commentary and covered ayah range. - -Expected outcome: - -- No missing tafsir when the selected ayah belongs to a group. -- Commentary remains correctly mapped to its ayah coverage. - -## Common Mistakes - -- Treating every ayah key as standalone tafsir text. -- Ignoring pointer/group behavior in JSON exports. -- Ignoring `group_ayah_key` in SQLite rows. -- Rendering raw HTML tags without sanitization in production. - -## When to Request Updates or Changes - -Open an issue when you find: - -- Broken grouped references or missing target keys -- Tafsir text mapped to wrong ayah range -- Downloaded files missing expected grouped fields -- Broken json/sqlite download links +- [Tutorial 4: Tafsir End-to-End](tutorial-tafsir-end-to-end.md) -Issue link: +Recommended starting point for new users: -- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) +- [Tutorials Index](tutorials.md) -## Related Pages +Resource directory entry: -- [Tutorials](tutorials.md) -- [Tutorial 4: Tafsir End-to-End](tutorial-tafsir-end-to-end.md) -- [Quran Script Guide](resource-quran-script.md) -- [Translations Guide](resource-translations.md) -- [Downloading and Using Data](downloading-data.md) +- [https://qul.tarteel.ai/resources/tafsir](https://qul.tarteel.ai/resources/tafsir) diff --git a/docs/resource-topics-and-concepts.md b/docs/resource-topics-and-concepts.md index 1f0516fe..034b84dc 100644 --- a/docs/resource-topics-and-concepts.md +++ b/docs/resource-topics-and-concepts.md @@ -1,14 +1,14 @@ # Topics and Concepts Guide (Legacy Umbrella) -This page is kept for compatibility and points to the split guides used by the current tutorials. +This page is kept for compatibility with older links. -Use these dedicated guides: +Canonical tutorials for this area: -1. [Ayah Topics Guide](resource-ayah-topics.md) -2. [Mutashabihat Guide](resource-mutashabihat.md) -3. [Similar Ayah Guide](resource-similar-ayah.md) -4. [Ayah Theme Guide](resource-ayah-theme.md) +1. [Tutorial 10: Ayah Topics End-to-End](tutorial-ayah-topics-end-to-end.md) +2. [Tutorial 12: Mutashabihat End-to-End](tutorial-mutashabihat-end-to-end.md) +3. [Tutorial 13: Similar Ayah End-to-End](tutorial-similar-ayah-end-to-end.md) +4. [Tutorial 14: Ayah Theme End-to-End](tutorial-ayah-theme-end-to-end.md) -Official directory: +Recommended starting point: -- [https://qul.tarteel.ai/resources](https://qul.tarteel.ai/resources) +- [Tutorials Index](tutorials.md) diff --git a/docs/resource-translations.md b/docs/resource-translations.md index 174c5711..7fe3add6 100644 --- a/docs/resource-translations.md +++ b/docs/resource-translations.md @@ -1,153 +1,15 @@ # Translations Guide -This guide is for resource users who want to download and integrate Quran translation datasets from QUL. +This page is kept for compatibility with older links. -Category URL: +Canonical implementation guide: -- [https://qul.tarteel.ai/resources/translation](https://qul.tarteel.ai/resources/translation) - -## What This Resource Is - -Translation resources provide translated text keyed by ayah, with multiple export structures. - -Depending on the selected package, you may get: - -- Simple translation text (`simple.json`, `simple.sqlite`) -- Translation with footnote tags -- Translation with inline footnotes -- Translation text chunks for structured rendering - -## When to Use It - -Use translation data when building: - -- Multilingual Quran readers -- Arabic + translation learning experiences -- Search and discovery features in non-Arabic languages - -## How to Get Your First Example Resource - -Use this stable selection rule: - -1. Open [https://qul.tarteel.ai/resources/translation](https://qul.tarteel.ai/resources/translation). -2. Keep the default listing order. -3. Open the first published resource card. -4. Verify the detail page has: - - `Translation Preview` tab - - `Help` tab -5. Confirm available download links (`simple.json`, `simple.sqlite`, and optional footnote/chunk variants). - -This keeps onboarding concrete without hardcoding a resource ID. - -## What the Preview and Help Tabs Show - -On the translation detail page: - -- `Translation Preview` tab: - - `Jump to Ayah` - - Previous/next ayah navigation - - Arabic ayah display + translation display -- `Help` tab: - - JSON/SQLite export overview - - Simple export structures (nested array, key-value) - - Footnote exports (tags, inline, chunks) - -Integration implication: - -- Simple format works for plain translation rendering. -- Footnote/chunk formats are better when annotation fidelity matters. - -## Download and Integration Checklist - -1. Download your selected translation package. -2. Identify the payload shape for each ayah: - - String (simple translation) - - Object (`t` + `f`) for tagged footnotes - - Chunk array -3. Normalize ayah keys to one format (recommended: `surah:ayah`). -4. Load Quran Script rows for Arabic text from [Quran Script Guide](resource-quran-script.md). -5. Join Arabic + translation by ayah key. -6. Render translation with format-aware logic. -7. Validate on consecutive ayahs, not just one sample ayah. - -Starter integration snippet (JavaScript): - -```javascript -const toAyahKey = (row) => row.ayah_key || `${row.surah}:${row.ayah}`; - -const buildTranslationIndex = (rows) => - rows.reduce((index, row) => { - index[toAyahKey(row)] = row.translation; - return index; - }, {}); - -const normalizeTranslation = (payload) => { - if (typeof payload === "string") return { text: payload, notes: [] }; - - if (payload && typeof payload === "object" && payload.t) { - const ids = []; - const text = payload.t.replace(/([^<]+)<\/sup>/g, (_, id, label) => { - ids.push(id); - return `[${label}]`; - }); - return { text, notes: ids.map((id) => ({ id, text: payload.f?.[id] || "" })) }; - } - - if (Array.isArray(payload)) { - const parts = []; - payload.forEach((chunk) => { - if (typeof chunk === "string") parts.push(chunk); - else if (chunk?.type === "i") parts.push(chunk.text); - else if (chunk?.type === "f") parts.push(`[${chunk.text}]`); - }); - return { text: parts.join(""), notes: [] }; - } - - return { text: "", notes: [] }; -}; -``` - -## Real-World Usage Example - -Goal: - -- Display one ayah with Arabic text and translation, including footnotes when available. - -Flow: - -1. User selects an ayah key. -2. App loads Arabic text from Quran Script by that ayah key. -3. App loads translation payload by the same ayah key. -4. App normalizes translation payload and renders text + notes. - -Expected outcome: - -- Arabic and translation remain correctly paired. -- Footnotes appear for resources that include them. - -## Common Mistakes - -- Joining Arabic and translation by row index instead of ayah key. -- Assuming all translation resources have identical payload structures. -- Rendering tagged-footnote HTML directly without sanitization in production. -- Ignoring missing footnote entries or missing ayah keys. - -## When to Request Updates or Changes - -Open an issue when you find: - -- Broken translation download links -- Translation text mapped to wrong ayah keys -- Footnote references without matching footnote text -- Missing language/source metadata +- [Tutorial 3: Translation End-to-End](tutorial-translation-end-to-end.md) -Issue link: +Recommended starting point for new users: -- [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) +- [Tutorials Index](tutorials.md) -## Related Pages +Resource directory entry: -- [Tutorials](tutorials.md) -- [Tutorial 3: Translation End-to-End](tutorial-translation-end-to-end.md) -- [Downloading and Using Data](downloading-data.md) -- [Data Model](data-model.md) +- [https://qul.tarteel.ai/resources/translation](https://qul.tarteel.ai/resources/translation) diff --git a/docs/resource-transliteration.md b/docs/resource-transliteration.md index 91498786..3f2d535c 100644 --- a/docs/resource-transliteration.md +++ b/docs/resource-transliteration.md @@ -1,52 +1,15 @@ # Transliteration Guide -## What This Resource Is +This page is kept for compatibility with older links. -Transliteration resources represent Quran text using non-Arabic scripts/phonetic mappings to support pronunciation-oriented reading. +Canonical implementation guide: -Category URL: [https://qul.tarteel.ai/resources/transliteration](https://qul.tarteel.ai/resources/transliteration) +- [Tutorial 8: Transliteration End-to-End](tutorial-transliteration-end-to-end.md) -## When to Use It +Recommended starting point for new users: -- Beginner-friendly reading modes -- Pronunciation assistance -- Transitional learning from transliteration to Arabic script +- [Tutorials Index](tutorials.md) -## How to Download or Access It +Resource directory entry: -1. Open the category URL above. -2. Select transliteration package. -3. Download JSON or SQLite. -4. Validate ayah key compatibility with your script/translation tables. - -## Step-by-Step Integration - -1. Import transliteration rows by ayah identity. -2. Join with Quran Script and optional translation rows. -3. Add mode toggle: Arabic | Transliteration | Translation. -4. Ensure UI typography supports transliteration characters. -5. Validate that ayah ordering matches Quran Script. - -## Real-World Usage Example - -Goal: add transliteration toggle in ayah view. - -Flow: - -1. User opens ayah. -2. User turns on transliteration mode. -3. App renders transliteration text under Arabic line. - -Expected outcome: - -- Users can read pronunciation guidance alongside Quran text. - -## When to Request Updates or Changes - -Open an issue when: - -- Transliteration text is misaligned with ayahs -- Character mappings appear broken -- Missing ayah transliteration entries are found - -Issue link: [https://github.com/TarteelAI/quranic-universal-library/issues](https://github.com/TarteelAI/quranic-universal-library/issues) +- [https://qul.tarteel.ai/resources/transliteration](https://qul.tarteel.ai/resources/transliteration) diff --git a/docs/resource_tutorial_map.md b/docs/resource_tutorial_map.md new file mode 100644 index 00000000..ecbe6363 --- /dev/null +++ b/docs/resource_tutorial_map.md @@ -0,0 +1,22 @@ +# Resource to Tutorial Mapping + +This file is the single mapping reference used to keep compatibility pages and canonical tutorial pages aligned. + +## Mapping + +| Compatibility Page | Canonical Tutorial | +|---|---| +| resource-recitations.md | tutorial-recitation-end-to-end.md | +| resource-mushaf-layouts.md | tutorial-mushaf-layout-end-to-end.md | +| resource-translations.md | tutorial-translation-end-to-end.md | +| resource-tafsirs.md | tutorial-tafsir-end-to-end.md | +| resource-quran-script.md | tutorial-quran-script-end-to-end.md | +| resource-fonts.md | tutorial-font-end-to-end.md | +| resource-quran-metadata.md | tutorial-quran-metadata-end-to-end.md | +| resource-transliteration.md | tutorial-transliteration-end-to-end.md | +| resource-surah-information.md | tutorial-surah-information-end-to-end.md | +| resource-ayah-topics.md | tutorial-ayah-topics-end-to-end.md | +| resource-morphology.md | tutorial-morphology-end-to-end.md | +| resource-mutashabihat.md | tutorial-mutashabihat-end-to-end.md | +| resource-similar-ayah.md | tutorial-similar-ayah-end-to-end.md | +| resource-ayah-theme.md | tutorial-ayah-theme-end-to-end.md | diff --git a/docs/tutorials.md b/docs/tutorials.md index c3fbac36..4b8e77e9 100644 --- a/docs/tutorials.md +++ b/docs/tutorials.md @@ -4,6 +4,12 @@ This page is the tutorials index for resource users. Each end-to-end tutorial is on its own page so you can focus on one resource type at a time. +This is the canonical implementation path for resource integration docs. + +Legacy compatibility index: + +- [Resource Guides Index (compatibility alias)](resource-guides-index.md) + ## Start Here: How to Pick Your First Resource Use this repeatable rule for any resource type: From d8f7d50211036a51dce8c097e21055486fa632ea Mon Sep 17 00:00:00 2001 From: umairliaqatali Date: Thu, 5 Mar 2026 12:37:27 +0500 Subject: [PATCH 04/12] docs: remove resource-guides index and fix mapping slug --- docs/README.md | 5 ++--- docs/getting-started.md | 1 - docs/resource-guides-index.md | 15 --------------- ...e_tutorial_map.md => resource-tutorial-map.md} | 0 docs/tutorials.md | 4 ---- 5 files changed, 2 insertions(+), 23 deletions(-) delete mode 100644 docs/resource-guides-index.md rename docs/{resource_tutorial_map.md => resource-tutorial-map.md} (100%) diff --git a/docs/README.md b/docs/README.md index d6fd8e58..52fd7b1d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -19,15 +19,14 @@ The pages here are published on the website at `/docs` and mirrored in the repos 3. [Tutorials](tutorials.md) 4. [Datasets](datasets.md) 5. [Data Model](data-model.md) -6. [Resource Guides Index (compatibility alias)](resource-guides-index.md) -7. [FAQ](faq.md) +6. [FAQ](faq.md) ## Documentation Maintenance (Secondary) 1. [Resource Guide Template](resource-guide-template.md) 2. [Best Practices](best-practices.md) 3. [Contributing](contributing.md) -4. [Resource to Tutorial Mapping](resource_tutorial_map.md) +4. [Resource to Tutorial Mapping](resource-tutorial-map.md) ## Primary Links diff --git a/docs/getting-started.md b/docs/getting-started.md index 7eedfd88..05983063 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -75,5 +75,4 @@ For details, see [data-model.md](data-model.md). - Detailed download and format guidance: [downloading-data.md](downloading-data.md) - Category-by-category overview: [datasets.md](datasets.md) - Canonical step-by-step implementations: [tutorials.md](tutorials.md) -- Resource guides compatibility index: [resource-guides-index.md](resource-guides-index.md) - Common questions: [faq.md](faq.md) diff --git a/docs/resource-guides-index.md b/docs/resource-guides-index.md deleted file mode 100644 index e4910793..00000000 --- a/docs/resource-guides-index.md +++ /dev/null @@ -1,15 +0,0 @@ -# Resource Guides Index (Compatibility Alias) - -This page is kept for compatibility with older links. - -Canonical step-by-step docs are now maintained in Tutorials. - -- [Tutorials Index](tutorials.md) - -Legacy umbrella page: - -- [Topics and Concepts Guide (legacy umbrella)](resource-topics-and-concepts.md) - -Official resource directory: - -- [https://qul.tarteel.ai/resources](https://qul.tarteel.ai/resources) diff --git a/docs/resource_tutorial_map.md b/docs/resource-tutorial-map.md similarity index 100% rename from docs/resource_tutorial_map.md rename to docs/resource-tutorial-map.md diff --git a/docs/tutorials.md b/docs/tutorials.md index 4b8e77e9..c95723cd 100644 --- a/docs/tutorials.md +++ b/docs/tutorials.md @@ -6,10 +6,6 @@ Each end-to-end tutorial is on its own page so you can focus on one resource typ This is the canonical implementation path for resource integration docs. -Legacy compatibility index: - -- [Resource Guides Index (compatibility alias)](resource-guides-index.md) - ## Start Here: How to Pick Your First Resource Use this repeatable rule for any resource type: From 0a9f10934ea7221adeeb283777eb6e4e05ba124b Mon Sep 17 00:00:00 2001 From: umairliaqatali Date: Thu, 5 Mar 2026 15:21:38 +0500 Subject: [PATCH 05/12] docs: align translation and mutashabihat dropdown styling --- docs/tutorial-mutashabihat-end-to-end.md | 16 ++++++++++++++-- docs/tutorial-translation-end-to-end.md | 14 +++++++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/docs/tutorial-mutashabihat-end-to-end.md b/docs/tutorial-mutashabihat-end-to-end.md index 17af01f0..1e95dd06 100644 --- a/docs/tutorial-mutashabihat-end-to-end.md +++ b/docs/tutorial-mutashabihat-end-to-end.md @@ -201,14 +201,26 @@ const helpSample = { }; const app = document.getElementById("app"); +const dropdownStyle = [ + "margin-bottom:12px", + "padding:8px", + "border:1px solid #cbd5e1", + "border-radius:8px", + "background:#fff", + "color:#0f172a", + "font-size:0.95rem", + "line-height:1.3", + "min-width:220px" +].join(";"); + app.innerHTML = `

        Mutashabihat Preview (Phrase-Level)

        Preview behavior + Help data model on one screen

        Start with 1:1 (Bismillah). Then switch to 27:30 or 2:112 to see repeated phrase behavior.
        - - diff --git a/docs/tutorial-translation-end-to-end.md b/docs/tutorial-translation-end-to-end.md index e1bf3646..3f36f872 100644 --- a/docs/tutorial-translation-end-to-end.md +++ b/docs/tutorial-translation-end-to-end.md @@ -224,11 +224,23 @@ const normalizeTranslation = (payload) => { }; const app = document.getElementById("app"); +const dropdownStyle = [ + "margin-bottom:12px", + "padding:8px", + "border:1px solid #cbd5e1", + "border-radius:8px", + "background:#fff", + "color:#0f172a", + "font-size:0.95rem", + "line-height:1.3", + "min-width:220px" +].join(";"); + app.innerHTML = `

        Translation Preview (Format-Aware)

        Arabic + translation rendering with simple, chunk, and footnote-tag formats

        - From 9cc5910eed34dac2c652042c0b59ee4f94d5ac72 Mon Sep 17 00:00:00 2001 From: umairliaqatali Date: Thu, 5 Mar 2026 15:32:02 +0500 Subject: [PATCH 06/12] docs: normalize tutorial playground dropdown styles --- docs/tutorial-ayah-theme-end-to-end.md | 27 ++++++++++++------- docs/tutorial-font-end-to-end.md | 20 +++++++++++--- docs/tutorial-morphology-end-to-end.md | 16 +++++++++-- docs/tutorial-quran-metadata-end-to-end.md | 16 +++++++++-- docs/tutorial-quran-script-end-to-end.md | 14 +++++++++- docs/tutorial-similar-ayah-end-to-end.md | 25 +++++++++++------ docs/tutorial-surah-information-end-to-end.md | 16 +++++++++-- docs/tutorial-tafsir-end-to-end.md | 14 +++++++++- docs/tutorial-transliteration-end-to-end.md | 16 +++++++++-- 9 files changed, 133 insertions(+), 31 deletions(-) diff --git a/docs/tutorial-ayah-theme-end-to-end.md b/docs/tutorial-ayah-theme-end-to-end.md index a1ddba86..9d718b81 100644 --- a/docs/tutorial-ayah-theme-end-to-end.md +++ b/docs/tutorial-ayah-theme-end-to-end.md @@ -119,19 +119,28 @@ const findTheme = (surah, ayah) => themeRows.find((r) => r.surah_number === surah && ayah >= r.ayah_from && ayah <= r.ayah_to) || null; const app = document.getElementById("app"); +const dropdownStyle = [ + "margin-bottom:12px", + "padding:8px", + "border:1px solid #cbd5e1", + "border-radius:8px", + "background:#fff", + "color:#0f172a", + "font-size:0.95rem", + "line-height:1.3", + "min-width:220px" +].join(";"); + app.innerHTML = `

        Ayah Theme Preview

        Resolve thematic summary by ayah range

        - -
        - - -
        + +
        Pick an ayah to resolve its theme range.
        diff --git a/docs/tutorial-font-end-to-end.md b/docs/tutorial-font-end-to-end.md index b3df117c..bc6f040d 100644 --- a/docs/tutorial-font-end-to-end.md +++ b/docs/tutorial-font-end-to-end.md @@ -128,16 +128,28 @@ const fontStacks = { }; const app = document.getElementById("app"); +const dropdownStyle = [ + "margin-bottom:12px", + "padding:8px", + "border:1px solid #cbd5e1", + "border-radius:8px", + "background:#fff", + "color:#0f172a", + "font-size:0.95rem", + "line-height:1.3", + "min-width:220px" +].join(";"); + app.innerHTML = `

        Font Preview (Quran Script)

        Test verse rendering with different font stacks

        - - - - + +
        `; diff --git a/docs/tutorial-morphology-end-to-end.md b/docs/tutorial-morphology-end-to-end.md index 6b053bd1..1e8da3c3 100644 --- a/docs/tutorial-morphology-end-to-end.md +++ b/docs/tutorial-morphology-end-to-end.md @@ -122,6 +122,18 @@ const ayah = { }; const app = document.getElementById("app"); +const dropdownStyle = [ + "margin-bottom:12px", + "padding:8px", + "border:1px solid #cbd5e1", + "border-radius:8px", + "background:#fff", + "color:#0f172a", + "font-size:0.95rem", + "line-height:1.3", + "min-width:220px" +].join(";"); + app.innerHTML = `

        Morphology Preview (Word Level)

        Surah Al-Fatihah — Ayah 1 (word stem/root/lemma + ayah summary)

        @@ -131,8 +143,8 @@ app.innerHTML = ` - - diff --git a/docs/tutorial-quran-metadata-end-to-end.md b/docs/tutorial-quran-metadata-end-to-end.md index dc743cd2..0de10226 100644 --- a/docs/tutorial-quran-metadata-end-to-end.md +++ b/docs/tutorial-quran-metadata-end-to-end.md @@ -125,11 +125,23 @@ const surahById = new Map(surahs.map((s) => [s.surah_id, s])); const juzByNumber = new Map(juzRanges.map((j) => [j.juz_number, j])); const app = document.getElementById("app"); +const dropdownStyle = [ + "margin-bottom:12px", + "padding:8px", + "border:1px solid #cbd5e1", + "border-radius:8px", + "background:#fff", + "color:#0f172a", + "font-size:0.95rem", + "line-height:1.3", + "min-width:220px" +].join(";"); + app.innerHTML = `

        Quran Metadata Preview (Browse by Juz)

        Resolve ayah ranges and boundary surahs from metadata

        - - + +
        `; diff --git a/docs/tutorial-quran-script-end-to-end.md b/docs/tutorial-quran-script-end-to-end.md index 264c41db..c681f04b 100644 --- a/docs/tutorial-quran-script-end-to-end.md +++ b/docs/tutorial-quran-script-end-to-end.md @@ -184,11 +184,23 @@ const verseByKey = scriptRows.reduce((index, row) => { }, {}); const app = document.getElementById("app"); +const dropdownStyle = [ + "margin-bottom:12px", + "padding:8px", + "border:1px solid #cbd5e1", + "border-radius:8px", + "background:#fff", + "color:#0f172a", + "font-size:0.95rem", + "line-height:1.3", + "min-width:220px" +].join(";"); + app.innerHTML = `

        Quran Script Preview (Verse + Word by Word)

        Shows how verse_key and words[].location map to rendering + integration keys

        - diff --git a/docs/tutorial-similar-ayah-end-to-end.md b/docs/tutorial-similar-ayah-end-to-end.md index a745f3f5..73184070 100644 --- a/docs/tutorial-similar-ayah-end-to-end.md +++ b/docs/tutorial-similar-ayah-end-to-end.md @@ -209,6 +209,18 @@ const highlightMatchedWords = (ayahText, range) => .join(" "); const app = document.getElementById("app"); +const dropdownStyle = [ + "margin-bottom:12px", + "padding:8px", + "border:1px solid #cbd5e1", + "border-radius:8px", + "background:#fff", + "color:#0f172a", + "font-size:0.95rem", + "line-height:1.3", + "min-width:220px" +].join(";"); + app.innerHTML = `

        Similar Ayah Preview

        Preview + Help on one screen: interactive similar-ayah cards and schema/sample reference

        @@ -216,14 +228,11 @@ app.innerHTML = ` Top section mirrors Preview. Bottom section mirrors the Help field/schema explanation.
        - -
        - - -
        + +
        Select the source ayah to compare similar matches.
        diff --git a/docs/tutorial-surah-information-end-to-end.md b/docs/tutorial-surah-information-end-to-end.md index 05820a10..0cc9c4eb 100644 --- a/docs/tutorial-surah-information-end-to-end.md +++ b/docs/tutorial-surah-information-end-to-end.md @@ -211,11 +211,23 @@ const surahInfoById = { }; const app = document.getElementById("app"); +const dropdownStyle = [ + "margin-bottom:12px", + "padding:8px", + "border:1px solid #cbd5e1", + "border-radius:8px", + "background:#fff", + "color:#0f172a", + "font-size:0.95rem", + "line-height:1.3", + "min-width:220px" +].join(";"); + app.innerHTML = `

        Surah Info Preview

        Render short + detailed chapter context before ayah content

        - - diff --git a/docs/tutorial-tafsir-end-to-end.md b/docs/tutorial-tafsir-end-to-end.md index 76e0245b..65ea8b25 100644 --- a/docs/tutorial-tafsir-end-to-end.md +++ b/docs/tutorial-tafsir-end-to-end.md @@ -184,11 +184,23 @@ const resolveTafsir = (ayahKey) => { }; const app = document.getElementById("app"); +const dropdownStyle = [ + "margin-bottom:12px", + "padding:8px", + "border:1px solid #cbd5e1", + "border-radius:8px", + "background:#fff", + "color:#0f172a", + "font-size:0.95rem", + "line-height:1.3", + "min-width:220px" +].join(";"); + app.innerHTML = `

        Tafsir Preview (Grouped Ayah Aware)

        Demonstrates object + pointer tafsir mapping from the Help format

        - diff --git a/docs/tutorial-transliteration-end-to-end.md b/docs/tutorial-transliteration-end-to-end.md index eb99e8c4..cfbd66ee 100644 --- a/docs/tutorial-transliteration-end-to-end.md +++ b/docs/tutorial-transliteration-end-to-end.md @@ -119,11 +119,23 @@ const transliterationByAyah = { }; const app = document.getElementById("app"); +const dropdownStyle = [ + "margin-bottom:12px", + "padding:8px", + "border:1px solid #cbd5e1", + "border-radius:8px", + "background:#fff", + "color:#0f172a", + "font-size:0.95rem", + "line-height:1.3", + "min-width:220px" +].join(";"); + app.innerHTML = `

        Transliteration Preview

        Switch between Arabic-only and Arabic + transliteration

        - - From 58ee5748a0607677f2ee2daebee0c0ce498a5649 Mon Sep 17 00:00:00 2001 From: umairliaqatali Date: Thu, 5 Mar 2026 15:34:42 +0500 Subject: [PATCH 07/12] docs: improve font playground feedback and preset visibility --- docs/tutorial-font-end-to-end.md | 52 ++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/docs/tutorial-font-end-to-end.md b/docs/tutorial-font-end-to-end.md index bc6f040d..14a73d24 100644 --- a/docs/tutorial-font-end-to-end.md +++ b/docs/tutorial-font-end-to-end.md @@ -121,10 +121,28 @@ const samples = { "73:4": "أَوۡ زِدۡ عَلَيۡهِ وَرَتِّلِ ٱلۡقُرۡءَانَ تَرۡتِيلًا" }; -const fontStacks = { - "qpc-hafs (with fallbacks)": "'KFGQPC Uthmanic Script HAFS','Amiri Quran','Noto Naskh Arabic','Scheherazade New',serif", - "Amiri Quran": "'Amiri Quran','Noto Naskh Arabic',serif", - "System fallback": "'Noto Naskh Arabic','Tahoma','Arial',serif" +const fontPresets = { + "qpc-hafs (with fallbacks)": { + family: "'KFGQPC Uthmanic Script HAFS','Amiri Quran','Noto Naskh Arabic','Scheherazade New',serif", + label: "QPC stack (if installed) + safe fallbacks", + accent: "#0f766e", + letterSpacing: "0px", + fontSize: "1.28rem" + }, + "Amiri Quran": { + family: "'Amiri Quran','Noto Naskh Arabic',serif", + label: "Amiri-first stack", + accent: "#1d4ed8", + letterSpacing: "0.1px", + fontSize: "1.24rem" + }, + "System fallback": { + family: "'Noto Naskh Arabic','Tahoma','Arial',serif", + label: "System-safe fallback stack", + accent: "#7c2d12", + letterSpacing: "0.25px", + fontSize: "1.2rem" + } }; const app = document.getElementById("app"); @@ -150,14 +168,16 @@ app.innerHTML = ` +
        `; const ayahSelect = app.querySelector("#ayah"); const stackSelect = app.querySelector("#stack"); +const statusBox = app.querySelector("#status"); const verseBox = app.querySelector("#verse"); -Object.keys(fontStacks).forEach((label) => { +Object.keys(fontPresets).forEach((label) => { const opt = document.createElement("option"); opt.value = label; opt.textContent = label; @@ -166,7 +186,27 @@ Object.keys(fontStacks).forEach((label) => { const render = () => { verseBox.textContent = samples[ayahSelect.value] || ""; - verseBox.style.fontFamily = fontStacks[stackSelect.value]; + const preset = fontPresets[stackSelect.value]; + if (!preset) return; + + verseBox.style.fontFamily = preset.family; + verseBox.style.fontSize = preset.fontSize; + verseBox.style.letterSpacing = preset.letterSpacing; + verseBox.style.borderColor = preset.accent; + verseBox.style.boxShadow = `inset 0 0 0 1px ${preset.accent}22`; + + const supportsFontCheck = typeof document.fonts?.check === "function"; + const qpcLoaded = supportsFontCheck ? document.fonts.check('16px "KFGQPC Uthmanic Script HAFS"') : false; + const amiriLoaded = supportsFontCheck ? document.fonts.check('16px "Amiri Quran"') : false; + const availability = supportsFontCheck + ? `Installed fonts detected: QPC HAFS = ${qpcLoaded ? "yes" : "no"}, Amiri Quran = ${amiriLoaded ? "yes" : "no"}` + : "Font availability check is not supported in this browser."; + + statusBox.innerHTML = ` + Active preset: ${stackSelect.value}
        + Applied: ${preset.label}
        + ${availability} + `; }; ayahSelect.addEventListener("change", render); From e38fa697a4bfea6088e38313a6931b1bbe6f3399 Mon Sep 17 00:00:00 2001 From: umairliaqatali Date: Thu, 5 Mar 2026 15:40:30 +0500 Subject: [PATCH 08/12] docs: clarify font fallback behavior in font tutorial playground --- docs/tutorial-font-end-to-end.md | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/docs/tutorial-font-end-to-end.md b/docs/tutorial-font-end-to-end.md index 14a73d24..4e43b1a3 100644 --- a/docs/tutorial-font-end-to-end.md +++ b/docs/tutorial-font-end-to-end.md @@ -124,6 +124,7 @@ const samples = { const fontPresets = { "qpc-hafs (with fallbacks)": { family: "'KFGQPC Uthmanic Script HAFS','Amiri Quran','Noto Naskh Arabic','Scheherazade New',serif", + candidates: ["KFGQPC Uthmanic Script HAFS", "Amiri Quran", "Noto Naskh Arabic", "Scheherazade New"], label: "QPC stack (if installed) + safe fallbacks", accent: "#0f766e", letterSpacing: "0px", @@ -131,13 +132,15 @@ const fontPresets = { }, "Amiri Quran": { family: "'Amiri Quran','Noto Naskh Arabic',serif", + candidates: ["Amiri Quran", "Noto Naskh Arabic"], label: "Amiri-first stack", accent: "#1d4ed8", letterSpacing: "0.1px", fontSize: "1.24rem" }, "System fallback": { - family: "'Noto Naskh Arabic','Tahoma','Arial',serif", + family: "'Noto Naskh Arabic','Geeza Pro','Tahoma','Arial',serif", + candidates: ["Noto Naskh Arabic", "Geeza Pro", "Tahoma", "Arial"], label: "System-safe fallback stack", accent: "#7c2d12", letterSpacing: "0.25px", @@ -168,12 +171,17 @@ app.innerHTML = ` +
        `; const ayahSelect = app.querySelector("#ayah"); const stackSelect = app.querySelector("#stack"); +const simulateMissingToggle = app.querySelector("#simulate-missing"); const statusBox = app.querySelector("#status"); const verseBox = app.querySelector("#verse"); @@ -189,13 +197,22 @@ const render = () => { const preset = fontPresets[stackSelect.value]; if (!preset) return; - verseBox.style.fontFamily = preset.family; + const simulateMissingPrimary = simulateMissingToggle.checked; + const requestedFamily = simulateMissingPrimary + ? `'QUL Missing Primary Demo',${preset.family}` + : preset.family; + + verseBox.style.fontFamily = requestedFamily; verseBox.style.fontSize = preset.fontSize; verseBox.style.letterSpacing = preset.letterSpacing; verseBox.style.borderColor = preset.accent; verseBox.style.boxShadow = `inset 0 0 0 1px ${preset.accent}22`; const supportsFontCheck = typeof document.fonts?.check === "function"; + const installedFonts = supportsFontCheck + ? preset.candidates.filter((fontName) => document.fonts.check(`16px "${fontName}"`)) + : []; + const likelyFont = installedFonts[0] || "browser default serif"; const qpcLoaded = supportsFontCheck ? document.fonts.check('16px "KFGQPC Uthmanic Script HAFS"') : false; const amiriLoaded = supportsFontCheck ? document.fonts.check('16px "Amiri Quran"') : false; const availability = supportsFontCheck @@ -205,12 +222,16 @@ const render = () => { statusBox.innerHTML = ` Active preset: ${stackSelect.value}
        Applied: ${preset.label}
        + Requested stack: ${preset.candidates.join(" -> ")} -> serif
        + Likely active font: ${likelyFont}
        + Simulated missing primary: ${simulateMissingPrimary ? "on" : "off"}
        ${availability} `; }; ayahSelect.addEventListener("change", render); stackSelect.addEventListener("change", render); +simulateMissingToggle.addEventListener("change", render); stackSelect.value = "qpc-hafs (with fallbacks)"; render(); ``` From a20e52159206cd8178be0f2498e051e6daf83d7b Mon Sep 17 00:00:00 2001 From: umairliaqatali Date: Thu, 5 Mar 2026 15:43:41 +0500 Subject: [PATCH 09/12] docs: auto-force fallback in system font preset --- docs/tutorial-font-end-to-end.md | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/docs/tutorial-font-end-to-end.md b/docs/tutorial-font-end-to-end.md index 4e43b1a3..b8581332 100644 --- a/docs/tutorial-font-end-to-end.md +++ b/docs/tutorial-font-end-to-end.md @@ -139,9 +139,10 @@ const fontPresets = { fontSize: "1.24rem" }, "System fallback": { - family: "'Noto Naskh Arabic','Geeza Pro','Tahoma','Arial',serif", - candidates: ["Noto Naskh Arabic", "Geeza Pro", "Tahoma", "Arial"], - label: "System-safe fallback stack", + // First font name is intentionally fake to force fallback behavior in demo. + family: "'QUL Missing Font Demo','Noto Naskh Arabic','Geeza Pro','Tahoma','Arial',serif", + candidates: ["QUL Missing Font Demo", "Noto Naskh Arabic", "Geeza Pro", "Tahoma", "Arial"], + label: "System-safe fallback stack (forced fallback demo)", accent: "#7c2d12", letterSpacing: "0.25px", fontSize: "1.2rem" @@ -171,17 +172,12 @@ app.innerHTML = ` -
        `; const ayahSelect = app.querySelector("#ayah"); const stackSelect = app.querySelector("#stack"); -const simulateMissingToggle = app.querySelector("#simulate-missing"); const statusBox = app.querySelector("#status"); const verseBox = app.querySelector("#verse"); @@ -197,12 +193,7 @@ const render = () => { const preset = fontPresets[stackSelect.value]; if (!preset) return; - const simulateMissingPrimary = simulateMissingToggle.checked; - const requestedFamily = simulateMissingPrimary - ? `'QUL Missing Primary Demo',${preset.family}` - : preset.family; - - verseBox.style.fontFamily = requestedFamily; + verseBox.style.fontFamily = preset.family; verseBox.style.fontSize = preset.fontSize; verseBox.style.letterSpacing = preset.letterSpacing; verseBox.style.borderColor = preset.accent; @@ -218,20 +209,23 @@ const render = () => { const availability = supportsFontCheck ? `Installed fonts detected: QPC HAFS = ${qpcLoaded ? "yes" : "no"}, Amiri Quran = ${amiriLoaded ? "yes" : "no"}` : "Font availability check is not supported in this browser."; + const forcedFallbackNote = + stackSelect.value === "System fallback" + ? "Yes (first requested font is intentionally missing)." + : "No."; statusBox.innerHTML = ` Active preset: ${stackSelect.value}
        Applied: ${preset.label}
        Requested stack: ${preset.candidates.join(" -> ")} -> serif
        Likely active font: ${likelyFont}
        - Simulated missing primary: ${simulateMissingPrimary ? "on" : "off"}
        + Forced fallback demo: ${forcedFallbackNote}
        ${availability} `; }; ayahSelect.addEventListener("change", render); stackSelect.addEventListener("change", render); -simulateMissingToggle.addEventListener("change", render); stackSelect.value = "qpc-hafs (with fallbacks)"; render(); ``` From 5b2fd37e7b1225cce771010e072f80c4fd6125db Mon Sep 17 00:00:00 2001 From: umairliaqatali Date: Thu, 5 Mar 2026 15:49:10 +0500 Subject: [PATCH 10/12] docs: fix font tutorial preview fallback behavior --- docs/tutorial-font-end-to-end.md | 47 +++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/docs/tutorial-font-end-to-end.md b/docs/tutorial-font-end-to-end.md index b8581332..85a28826 100644 --- a/docs/tutorial-font-end-to-end.md +++ b/docs/tutorial-font-end-to-end.md @@ -173,7 +173,7 @@ app.innerHTML = `
        -
        +
        `; const ayahSelect = app.querySelector("#ayah"); @@ -181,6 +181,28 @@ const stackSelect = app.querySelector("#stack"); const statusBox = app.querySelector("#status"); const verseBox = app.querySelector("#verse"); +// Canvas-based heuristic to detect whether a font appears available. +// Browsers do not provide a perfect "actual rendered font" API. +const fontSeemsAvailable = (fontName) => { + const canvas = document.createElement("canvas"); + const ctx = canvas.getContext("2d"); + if (!ctx) return false; + + const probe = "بِسۡمِ ٱللَّهِ"; + const size = "32px"; + ctx.font = `${size} monospace`; + const monoWidth = ctx.measureText(probe).width; + ctx.font = `${size} serif`; + const serifWidth = ctx.measureText(probe).width; + + ctx.font = `${size} "${fontName}", monospace`; + const withMono = ctx.measureText(probe).width; + ctx.font = `${size} "${fontName}", serif`; + const withSerif = ctx.measureText(probe).width; + + return withMono !== monoWidth || withSerif !== serifWidth; +}; + Object.keys(fontPresets).forEach((label) => { const opt = document.createElement("option"); opt.value = label; @@ -199,16 +221,14 @@ const render = () => { verseBox.style.borderColor = preset.accent; verseBox.style.boxShadow = `inset 0 0 0 1px ${preset.accent}22`; - const supportsFontCheck = typeof document.fonts?.check === "function"; - const installedFonts = supportsFontCheck - ? preset.candidates.filter((fontName) => document.fonts.check(`16px "${fontName}"`)) - : []; - const likelyFont = installedFonts[0] || "browser default serif"; - const qpcLoaded = supportsFontCheck ? document.fonts.check('16px "KFGQPC Uthmanic Script HAFS"') : false; - const amiriLoaded = supportsFontCheck ? document.fonts.check('16px "Amiri Quran"') : false; - const availability = supportsFontCheck - ? `Installed fonts detected: QPC HAFS = ${qpcLoaded ? "yes" : "no"}, Amiri Quran = ${amiriLoaded ? "yes" : "no"}` - : "Font availability check is not supported in this browser."; + const availableCandidates = preset.candidates.filter((fontName) => + fontName === "QUL Missing Font Demo" ? false : fontSeemsAvailable(fontName) + ); + const likelyFont = availableCandidates[0] || "browser default serif"; + const availability = + availableCandidates.length > 0 + ? availableCandidates.join(", ") + : "none (browser default serif likely)"; const forcedFallbackNote = stackSelect.value === "System fallback" ? "Yes (first requested font is intentionally missing)." @@ -218,9 +238,10 @@ const render = () => { Active preset: ${stackSelect.value}
        Applied: ${preset.label}
        Requested stack: ${preset.candidates.join(" -> ")} -> serif
        - Likely active font: ${likelyFont}
        + Likely active font (heuristic): ${likelyFont}
        + Detected available candidates (heuristic): ${availability}
        Forced fallback demo: ${forcedFallbackNote}
        - ${availability} + Browser computed font-family: ${window.getComputedStyle(verseBox).fontFamily} `; }; From 9824bcccbaf55f0888082de6ef31d23043e0a51b Mon Sep 17 00:00:00 2001 From: umairliaqatali Date: Thu, 5 Mar 2026 15:51:39 +0500 Subject: [PATCH 11/12] docs: remove font tutorial status details block --- docs/tutorial-font-end-to-end.md | 47 -------------------------------- 1 file changed, 47 deletions(-) diff --git a/docs/tutorial-font-end-to-end.md b/docs/tutorial-font-end-to-end.md index 85a28826..581aacee 100644 --- a/docs/tutorial-font-end-to-end.md +++ b/docs/tutorial-font-end-to-end.md @@ -172,37 +172,13 @@ app.innerHTML = ` -
        `; const ayahSelect = app.querySelector("#ayah"); const stackSelect = app.querySelector("#stack"); -const statusBox = app.querySelector("#status"); const verseBox = app.querySelector("#verse"); -// Canvas-based heuristic to detect whether a font appears available. -// Browsers do not provide a perfect "actual rendered font" API. -const fontSeemsAvailable = (fontName) => { - const canvas = document.createElement("canvas"); - const ctx = canvas.getContext("2d"); - if (!ctx) return false; - - const probe = "بِسۡمِ ٱللَّهِ"; - const size = "32px"; - ctx.font = `${size} monospace`; - const monoWidth = ctx.measureText(probe).width; - ctx.font = `${size} serif`; - const serifWidth = ctx.measureText(probe).width; - - ctx.font = `${size} "${fontName}", monospace`; - const withMono = ctx.measureText(probe).width; - ctx.font = `${size} "${fontName}", serif`; - const withSerif = ctx.measureText(probe).width; - - return withMono !== monoWidth || withSerif !== serifWidth; -}; - Object.keys(fontPresets).forEach((label) => { const opt = document.createElement("option"); opt.value = label; @@ -220,29 +196,6 @@ const render = () => { verseBox.style.letterSpacing = preset.letterSpacing; verseBox.style.borderColor = preset.accent; verseBox.style.boxShadow = `inset 0 0 0 1px ${preset.accent}22`; - - const availableCandidates = preset.candidates.filter((fontName) => - fontName === "QUL Missing Font Demo" ? false : fontSeemsAvailable(fontName) - ); - const likelyFont = availableCandidates[0] || "browser default serif"; - const availability = - availableCandidates.length > 0 - ? availableCandidates.join(", ") - : "none (browser default serif likely)"; - const forcedFallbackNote = - stackSelect.value === "System fallback" - ? "Yes (first requested font is intentionally missing)." - : "No."; - - statusBox.innerHTML = ` - Active preset: ${stackSelect.value}
        - Applied: ${preset.label}
        - Requested stack: ${preset.candidates.join(" -> ")} -> serif
        - Likely active font (heuristic): ${likelyFont}
        - Detected available candidates (heuristic): ${availability}
        - Forced fallback demo: ${forcedFallbackNote}
        - Browser computed font-family: ${window.getComputedStyle(verseBox).fontFamily} - `; }; ayahSelect.addEventListener("change", render); From 761f3afa07ca76d0aa7fd4a4ce07f16bea4d729f Mon Sep 17 00:00:00 2001 From: umairliaqatali Date: Thu, 5 Mar 2026 16:12:36 +0500 Subject: [PATCH 12/12] chore: drop local-only dev runner changes from docs branch --- Procfile.dev | 2 +- bin/dev | 24 +++--------------------- bin/dev-env | 16 ---------------- 3 files changed, 4 insertions(+), 38 deletions(-) delete mode 100755 bin/dev-env diff --git a/Procfile.dev b/Procfile.dev index 220167bb..1de67517 100644 --- a/Procfile.dev +++ b/Procfile.dev @@ -1,3 +1,3 @@ -web: bin/rails server -p ${PORT:-3001} +web: bin/rails server -p 3000 js: yarn build --reload tailwind: bin/rails tailwindcss:watch diff --git a/bin/dev b/bin/dev index 0ad4d2fd..ad72c7d5 100755 --- a/bin/dev +++ b/bin/dev @@ -1,30 +1,12 @@ #!/usr/bin/env sh -set -eu - -APP_ROOT="$(cd "$(dirname "$0")/.." && pwd)" -cd "$APP_ROOT" - -. "$APP_ROOT/bin/dev-env" - -if [ -f ".ruby-version" ]; then - REQUIRED_RUBY="$(cat .ruby-version)" - CURRENT_RUBY="$(ruby -e 'print RUBY_VERSION' 2>/dev/null || true)" - - if [ "${CURRENT_RUBY}" != "${REQUIRED_RUBY}" ]; then - echo "Error: expected Ruby ${REQUIRED_RUBY} for this repo, but found ${CURRENT_RUBY:-unknown}." - echo "Run with rbenv enabled, then retry bin/dev." - exit 1 - fi -fi - -if ! command -v foreman >/dev/null 2>&1; then +if ! gem list foreman -i --silent; then echo "Installing foreman..." gem install foreman fi -# Default to port 3001 if not specified -export PORT="${PORT:-3001}" +# Default to port 3000 if not specified +export PORT="${PORT:-3000}" # Let the debug gem allow remote connections, # but avoid loading until `debugger` is called diff --git a/bin/dev-env b/bin/dev-env deleted file mode 100755 index 47cb2965..00000000 --- a/bin/dev-env +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env sh - -# Ensure rbenv is available even when shell init files were not loaded. -if [ -d "${HOME}/.rbenv" ]; then - export PATH="${HOME}/.rbenv/bin:${PATH}" -fi - -if command -v rbenv >/dev/null 2>&1; then - # Avoid hard failure from rehash under strict shells; fallback for older rbenv. - eval "$(rbenv init - --no-rehash 2>/dev/null || rbenv init -)" -fi - -# Enforce this repository's pinned Ruby for all bin/dev subprocesses. -if [ -f ".ruby-version" ] && command -v rbenv >/dev/null 2>&1; then - export RBENV_VERSION="$(cat .ruby-version)" -fi