@a-company/paradigm 3.24.1 → 3.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{accept-orchestration-AAYFKS74.js → accept-orchestration-6EM5EHXA.js} +4 -4
- package/dist/{auto-B22FVSQI.js → auto-24ICVUH4.js} +1 -1
- package/dist/{chunk-EZ6XW6FB.js → chunk-7IJ5JVKT.js} +1 -1
- package/dist/{chunk-6EQRU7WC.js → chunk-J26YQVAK.js} +1 -1
- package/dist/{chunk-VHSTF72C.js → chunk-N6RNYCZD.js} +1 -1
- package/dist/{chunk-XKI55IFI.js → chunk-SCC77UUP.js} +82 -62
- package/dist/chunk-UPLDI7CN.js +1334 -0
- package/dist/{chunk-GC6X3YM7.js → chunk-ZOH24ZPF.js} +5 -5
- package/dist/conductor-CAIY5LJA.js +74 -0
- package/dist/{diff-QC7PWIPF.js → diff-AH7L4PRQ.js} +4 -4
- package/dist/discipline-5F5OVTXB.js +24 -0
- package/dist/{doctor-RVODPMHJ.js → doctor-INBOLZC7.js} +1 -1
- package/dist/index.js +57 -25
- package/dist/{list-CAL7KS7B.js → list-QTFWN35D.js} +3 -2
- package/dist/mcp.js +285 -23
- package/dist/{orchestrate-NNNWNELP.js → orchestrate-HMSQ2CED.js} +4 -4
- package/dist/{providers-NKGY36QF.js → providers-YW3FG6DA.js} +1 -1
- package/dist/{reindex-CMZARW5K.js → reindex-YG3KIXAK.js} +1 -1
- package/dist/{shift-R6TQ6MBP.js → shift-DRF5M3G6.js} +23 -17
- package/dist/{spawn-52PASJJL.js → spawn-DIY7T4QW.js} +2 -2
- package/dist/{team-JZHIH7H5.js → team-YOGT2Q2X.js} +5 -5
- package/dist/{timeline-B6TMGWRU.js → timeline-RKXNRMKF.js} +4 -3
- package/dist/university-content/courses/.purpose +4 -4
- package/dist/university-content/courses/para-101.json +1 -1
- package/dist/university-content/courses/para-201.json +18 -2
- package/dist/university-content/plsat/.purpose +18 -0
- package/dist/university-content/plsat/v3.0.json +105 -0
- package/dist/university-ui/assets/{index-BPzqnvrg.js → index-TcsCEBMo.js} +2 -2
- package/dist/university-ui/assets/{index-BPzqnvrg.js.map → index-TcsCEBMo.js.map} +1 -1
- package/dist/university-ui/index.html +1 -1
- package/package.json +1 -1
- package/templates/paradigm/config.yaml +1 -0
- package/dist/chunk-CHSHON3O.js +0 -669
- package/dist/{chunk-7WTOOH23.js → chunk-5SXMV4SP.js} +0 -0
- package/dist/{chunk-4UC6AQOC.js → chunk-C5ZE6WEX.js} +0 -0
- package/dist/{flow-KZKMMXJC.js → flow-UFMPVOEM.js} +1 -1
|
@@ -495,7 +495,7 @@
|
|
|
495
495
|
{
|
|
496
496
|
"id": "first-steps",
|
|
497
497
|
"title": "Your First Steps",
|
|
498
|
-
"content": "## Getting Started with Paradigm\n\nYou have learned the concepts — now it is time to put them into practice. This lesson walks through the concrete steps to initialize Paradigm in a new project and set up the foundational files.\n\n## Step 1: Initialize the Project\n\nRun `paradigm shift` in your project root. This creates the `.paradigm/` directory with a starter `config.yaml`:\n\n```bash\nparadigm shift\n```\n\nThe init command will prompt you for:\n- **Project name** — Used in config.yaml and as a display name\n- **Discipline** — `web`, `backend`, `fullstack`, `mobile`, `cli`, etc
|
|
498
|
+
"content": "## Getting Started with Paradigm\n\nYou have learned the concepts — now it is time to put them into practice. This lesson walks through the concrete steps to initialize Paradigm in a new project and set up the foundational files.\n\n## Step 1: Initialize the Project\n\nRun `paradigm shift` in your project root. This creates the `.paradigm/` directory with a starter `config.yaml`:\n\n```bash\nparadigm shift\n```\n\nThe init command will prompt you for:\n- **Project name** — Used in config.yaml and as a display name\n- **Discipline** — `web`, `backend`, `fullstack`, `mobile`, `cli`, etc. (auto-detected from your project)\n- **Agent provider** — Which AI tool you use (`claude-code`, `cursor-cli`, etc.)\n\nParadigm automatically detects your discipline from project markers (`package.json`, `Cargo.toml`, `go.mod`, etc.) and your **stack preset** from framework dependencies. For example, a project with Next.js in its dependencies will be detected as `discipline: fullstack` with `stack: nextjs`. The stack preset provides framework-specific scan patterns, purpose-required paths, and refined symbol mappings.\n\nYou can also specify a stack explicitly: `paradigm init --stack fastapi`.\n\nAfter init, you will have:\n```\n.paradigm/\n config.yaml # Includes detected discipline and stack\n tags.yaml\n agents.yaml\n```\n\n## Step 2: Create Your First Purpose File\n\nPick a source directory that contains meaningful code — perhaps your main feature module or your API routes. Create a `.purpose` file:\n\n```yaml\nname: User Authentication\ndescription: Handles user login, registration, and session management\ncontext:\n - Uses bcrypt for password hashing\n - Sessions stored in Redis with 24h TTL\n - Rate limited to 5 login attempts per minute\n\ncomponents:\n #auth-handler:\n description: POST /auth/login and POST /auth/register endpoints\n file: auth.ts\n tags: [feature, auth]\n signals: [\"!login-success\", \"!login-failed\"]\n gates: [\"^authenticated\"]\n\n #session-manager:\n description: Creates and validates user sessions in Redis\n file: session.ts\n tags: [state, auth]\n```\n\nStart small. You do not need to document every file on day one. Begin with the most important module and expand over time.\n\n## Step 3: Set Up portal.yaml (If Needed)\n\nIf your application has any protected endpoints, create `portal.yaml` at the project root:\n\n```yaml\nversion: \"1.0\"\n\ngates:\n ^authenticated:\n description: User must have a valid session\n check: req.session.userId != null\n type: auth\n effects: []\n\nroutes:\n \"POST /auth/login\": []\n \"POST /auth/register\": []\n \"GET /api/profile\": [^authenticated]\n \"PUT /api/profile\": [^authenticated]\n```\n\nNote that public routes like login and register have empty gate arrays `[]` — they are listed to document that they are intentionally unprotected.\n\n## Step 4: Run Your First Scan\n\nGenerate the navigator map so AI agents can find symbols quickly:\n\n```bash\nparadigm scan\n```\n\nThis reads all `.purpose` files and `portal.yaml`, builds a symbol index, and writes `navigator.yaml`.\n\n## Step 5: The Orientation Protocol\n\nWhen starting a new AI session (or when an AI agent first encounters your project), the agent should follow this protocol:\n\n1. **Call `paradigm_status`** — Gets a project overview: symbol counts, health, available features.\n2. **Read `config.yaml`** — Understands the discipline, conventions, and preferences.\n3. **Check `portal.yaml`** — Knows about security gates if they exist.\n4. **Use `paradigm_navigate`** — Finds the relevant code area for the current task.\n\nThis four-step orientation takes ~500 tokens total and gives the agent everything it needs to work effectively.\n\n## Step 6: Iterate\n\nParadigm grows with your project. As you add features:\n- Create `.purpose` files for new directories\n- Add gates to `portal.yaml` for new protected routes\n- Record team decisions in `.paradigm/wisdom/decisions.yaml`\n- Log antipatterns in `.paradigm/wisdom/antipatterns.yaml`\n- Run `paradigm scan` periodically to rebuild the navigator\n\n## Common Pitfalls\n\n- **Do not document everything on day one.** Start with the most critical module and expand.\n- **Do not skip portal.yaml.** If you have any gates or preconditions, you need it.\n- **Do not forget to re-scan.** After adding new `.purpose` files, run `paradigm scan` to update the navigator.\n- **Do not put .purpose files in .paradigm/.** They live alongside source code.\n- **Do not use raw console.log.** Use the Paradigm logger from the start to build good habits.",
|
|
499
499
|
"keyConcepts": [
|
|
500
500
|
"paradigm shift creates the .paradigm/ directory",
|
|
501
501
|
"Start with one .purpose file in your most important module",
|
|
@@ -382,14 +382,17 @@
|
|
|
382
382
|
{
|
|
383
383
|
"id": "disciplines",
|
|
384
384
|
"title": "Disciplines",
|
|
385
|
-
"content": "## How Symbols Map Across Domains\n\nA Paradigm `discipline` defines how directory patterns and code structures map to symbol types in a specific development domain. A web frontend project organizes code differently from a backend API, which differs from a CLI tool. Disciplines capture these differences so that tooling — the navigator, the logging conventions, gate recommendations, and auto-scan — works correctly regardless of your tech stack.\n\n## Auto-Detection\n\nWhen you run `paradigm shift` or `paradigm init`, Paradigm automatically detects the discipline from your project structure. It examines `package.json`, `Cargo.toml`, `go.mod`, `pyproject.toml`, and other project markers to infer the best match. The detected discipline is written to `.paradigm/config.yaml`:\n\n```yaml\ndiscipline: fullstack # auto-detected from Next.js in package.json\n```\n\nDetection heuristics include: monorepo markers (workspaces), framework deps (Next.js → fullstack, React alone → web, Express alone → api), Python ML deps (PyTorch → ml), Rust crate deps (clap → cli, axum → api, bevy → game), and more. You can always override the detected value.\n\n## The 14 Disciplines\n\nParadigm supports 14 disciplines, each with tailored symbol mappings, purpose-required paths, and scan patterns:\n\n| Discipline | When to Use | Key Directories |\n|------------|-------------|------------------|\n| `web` | Frontend-only (React, Vue, Svelte) | `components/`, `pages/`, `hooks/`, `stores/` |\n| `backend` | General backend (fallback) | `services/`, `routes/`, `models/` |\n| `fullstack` | SSR or combined frontend+backend (Next.js, Django) | `components/`, `pages/`, `api/`, `services/` |\n| `api` | API-only (Express, FastAPI, Gin) | `routes/`, `endpoints/`, `controllers/` |\n| `cli` | CLI tools (Node bin, Click, clap) | `commands/`, `cmd/` |\n| `ml` | Machine learning (PyTorch, TF, scikit-learn) | `models/`, `experiments/`, `pipelines/` |\n| `mobile` | Mobile apps (React Native, Flutter) | `screens/`, `widgets/`, `navigation/` |\n| `game` | Game dev (Bevy, Godot, Unity) | `gameplay/`, `entities/`, `systems/` |\n| `embedded` | Embedded/IoT (embedded-hal, PlatformIO) | `drivers/`, `hal/`, `protocols/` |\n| `devops` | Infrastructure (Terraform, Ansible) | `modules/`, `pipelines/`, `scripts/` |\n| `data` | Data engineering (dbt, Airflow, Spark) | `models/`, `dags/`, `transforms/` |\n| `library` | Reusable packages (npm, PyPI, crates) | `src/`, `lib/` |\n| `monorepo` | Multi-package repos (workspaces, Nx) | `packages/`, `apps/`, `libs/` |\n| `custom` | User-defined mappings | Whatever you configure |\n\n## Web Discipline\n\nIn a web project, the primary units are routes, components, and pages:\n\n| Directory | Symbol | Rationale |\n|-----------|--------|-----------|\n| `routes/`, `pages/`, `views/` | `#` component | User-facing entry points |\n| `components/` | `#` component | Reusable UI elements |\n| `hooks/` | `#` component | Shared logic (hooks are code units, not signals) |\n| `stores/`, `state/` | `#` component (tag: `[state]`) | Client-side state |\n| `middleware/` | `^` gate | Route guards and auth checks |\n| `api/` | `#` component | API client wrappers |\n\nAn important distinction: **hooks are components, not signals**. A frontend hook like `useAuth` encapsulates logic — it is `#useAuth`, a component. The `!` signal prefix is reserved for events that trigger decoupled side effects.\n\n## Backend / API Discipline\n\nIn a backend or API project, the primary units are services, controllers, and models:\n\n| Directory | Symbol | Rationale |\n|-----------|--------|-----------|\n| `services/` | `#` component | Business logic |\n| `controllers/`, `handlers/` | `#` component | Request handlers |\n| `models/`, `entities/` | `#` component (tag: `[state]`) | Data models |\n| `middleware/`, `guards/` | `^` gate | Auth and validation |\n| `events/`, `listeners/` | `!` signal | Event emitters and handlers |\n| `jobs/`, `workers/` | `#` component | Background processing |\n| `integrations/`, `clients/` | `#` component (tag: `[integration]`) | External service wrappers |\n\nThe `api` discipline is like `backend` but focused on HTTP endpoints (adds `endpoints/`, `controllers/`, `webhooks/`).\n\n## Fullstack Discipline\n\nThe fullstack discipline combines both mappings. Paradigm determines which mapping to use based on the directory path:\n\n```\nsrc/\n client/ → Web discipline mappings apply\n server/ → Backend discipline mappings apply\n shared/ → Common mappings (# for all code units)\n```\n\nAuto-detected for SSR frameworks like Next.js, Nuxt, SvelteKit, or when both React and Express are present.\n\n## Domain-Specific Disciplines\n\n**ML**: Scans `models/`, `experiments/`, `notebooks/`. Pipelines map to `$` flows. Training events map to `!` signals.\n\n**Data**: Scans `dbt/`, `dags/`, `transforms/`. ETL pipelines are `$` flows. Data quality checks are `!` signals.\n\n**Game**: Scans `gameplay/`, `entities/`, `systems/`. Game loops are `$` flows. Game events are `!` signals.\n\n**Embedded**: Scans `drivers/`, `hal/`, `protocols/`. State machines are `$` flows. Interrupts are `!` signals.\n\n## Why Disciplines Matter\n\nDisciplines affect four things:\n\n1. **Symbol mappings** — Each discipline populates the `logging.symbol-mapping` section in config.yaml with directory-to-symbol mappings appropriate for your domain.\n2. **Navigator generation** — `paradigm scan` uses the discipline to categorize directories and suggest symbol types for undocumented code.\n3. **Gate recommendations** — `paradigm_gates_for_route` uses the discipline to understand which routes exist and what patterns apply.\n4. **Auto-scan patterns** — `paradigm scan --auto` adds discipline-specific file patterns (e.g., ML scans `.ipynb` notebooks, game scans `.gd` scripts).\n\nWith auto-detection, most projects get the right discipline without any manual configuration.\n\n## Custom Mappings\n\nIf your project does not fit a standard discipline, you can override mappings in `config.yaml`:\n\n```yaml\ndiscipline: backend\ncustom-mappings:\n \"workers/\": \"#\" # Override default if needed\n \"policies/\": \"^\" # Treat policies as gates\n \"sagas/\": \"$\" # Treat sagas as flows\n```\n\nCustom mappings extend (not replace) the discipline defaults. Or set `discipline: custom` and define everything yourself.",
|
|
385
|
+
"content": "## How Symbols Map Across Domains\n\nA Paradigm `discipline` defines how directory patterns and code structures map to symbol types in a specific development domain. A web frontend project organizes code differently from a backend API, which differs from a CLI tool. Disciplines capture these differences so that tooling — the navigator, the logging conventions, gate recommendations, and auto-scan — works correctly regardless of your tech stack.\n\n## Auto-Detection\n\nWhen you run `paradigm shift` or `paradigm init`, Paradigm automatically detects the discipline from your project structure. It examines `package.json`, `Cargo.toml`, `go.mod`, `pyproject.toml`, and other project markers to infer the best match. The detected discipline is written to `.paradigm/config.yaml`:\n\n```yaml\ndiscipline: fullstack # auto-detected from Next.js in package.json\n```\n\nDetection heuristics include: monorepo markers (workspaces), framework deps (Next.js → fullstack, React alone → web, Express alone → api), Python ML deps (PyTorch → ml), Rust crate deps (clap → cli, axum → api, bevy → game), and more. You can always override the detected value.\n\n## The 14 Disciplines\n\nParadigm supports 14 disciplines, each with tailored symbol mappings, purpose-required paths, and scan patterns:\n\n| Discipline | When to Use | Key Directories |\n|------------|-------------|------------------|\n| `web` | Frontend-only (React, Vue, Svelte) | `components/`, `pages/`, `hooks/`, `stores/` |\n| `backend` | General backend (fallback) | `services/`, `routes/`, `models/` |\n| `fullstack` | SSR or combined frontend+backend (Next.js, Django) | `components/`, `pages/`, `api/`, `services/` |\n| `api` | API-only (Express, FastAPI, Gin) | `routes/`, `endpoints/`, `controllers/` |\n| `cli` | CLI tools (Node bin, Click, clap) | `commands/`, `cmd/` |\n| `ml` | Machine learning (PyTorch, TF, scikit-learn) | `models/`, `experiments/`, `pipelines/` |\n| `mobile` | Mobile apps (React Native, Flutter) | `screens/`, `widgets/`, `navigation/` |\n| `game` | Game dev (Bevy, Godot, Unity) | `gameplay/`, `entities/`, `systems/` |\n| `embedded` | Embedded/IoT (embedded-hal, PlatformIO) | `drivers/`, `hal/`, `protocols/` |\n| `devops` | Infrastructure (Terraform, Ansible) | `modules/`, `pipelines/`, `scripts/` |\n| `data` | Data engineering (dbt, Airflow, Spark) | `models/`, `dags/`, `transforms/` |\n| `library` | Reusable packages (npm, PyPI, crates) | `src/`, `lib/` |\n| `monorepo` | Multi-package repos (workspaces, Nx) | `packages/`, `apps/`, `libs/` |\n| `custom` | User-defined mappings | Whatever you configure |\n\n## Web Discipline\n\nIn a web project, the primary units are routes, components, and pages:\n\n| Directory | Symbol | Rationale |\n|-----------|--------|-----------|\n| `routes/`, `pages/`, `views/` | `#` component | User-facing entry points |\n| `components/` | `#` component | Reusable UI elements |\n| `hooks/` | `#` component | Shared logic (hooks are code units, not signals) |\n| `stores/`, `state/` | `#` component (tag: `[state]`) | Client-side state |\n| `middleware/` | `^` gate | Route guards and auth checks |\n| `api/` | `#` component | API client wrappers |\n\nAn important distinction: **hooks are components, not signals**. A frontend hook like `useAuth` encapsulates logic — it is `#useAuth`, a component. The `!` signal prefix is reserved for events that trigger decoupled side effects.\n\n## Backend / API Discipline\n\nIn a backend or API project, the primary units are services, controllers, and models:\n\n| Directory | Symbol | Rationale |\n|-----------|--------|-----------|\n| `services/` | `#` component | Business logic |\n| `controllers/`, `handlers/` | `#` component | Request handlers |\n| `models/`, `entities/` | `#` component (tag: `[state]`) | Data models |\n| `middleware/`, `guards/` | `^` gate | Auth and validation |\n| `events/`, `listeners/` | `!` signal | Event emitters and handlers |\n| `jobs/`, `workers/` | `#` component | Background processing |\n| `integrations/`, `clients/` | `#` component (tag: `[integration]`) | External service wrappers |\n\nThe `api` discipline is like `backend` but focused on HTTP endpoints (adds `endpoints/`, `controllers/`, `webhooks/`).\n\n## Fullstack Discipline\n\nThe fullstack discipline combines both mappings. Paradigm determines which mapping to use based on the directory path:\n\n```\nsrc/\n client/ → Web discipline mappings apply\n server/ → Backend discipline mappings apply\n shared/ → Common mappings (# for all code units)\n```\n\nAuto-detected for SSR frameworks like Next.js, Nuxt, SvelteKit, or when both React and Express are present.\n\n## Domain-Specific Disciplines\n\n**ML**: Scans `models/`, `experiments/`, `notebooks/`. Pipelines map to `$` flows. Training events map to `!` signals.\n\n**Data**: Scans `dbt/`, `dags/`, `transforms/`. ETL pipelines are `$` flows. Data quality checks are `!` signals.\n\n**Game**: Scans `gameplay/`, `entities/`, `systems/`. Game loops are `$` flows. Game events are `!` signals.\n\n**Embedded**: Scans `drivers/`, `hal/`, `protocols/`. State machines are `$` flows. Interrupts are `!` signals.\n\n## Why Disciplines Matter\n\nDisciplines affect four things:\n\n1. **Symbol mappings** — Each discipline populates the `logging.symbol-mapping` section in config.yaml with directory-to-symbol mappings appropriate for your domain.\n2. **Navigator generation** — `paradigm scan` uses the discipline to categorize directories and suggest symbol types for undocumented code.\n3. **Gate recommendations** — `paradigm_gates_for_route` uses the discipline to understand which routes exist and what patterns apply.\n4. **Auto-scan patterns** — `paradigm scan --auto` adds discipline-specific file patterns (e.g., ML scans `.ipynb` notebooks, game scans `.gd` scripts).\n\nWith auto-detection, most projects get the right discipline without any manual configuration.\n\n## Custom Mappings\n\nIf your project does not fit a standard discipline, you can override mappings in `config.yaml`:\n\n```yaml\ndiscipline: backend\ncustom-mappings:\n \"workers/\": \"#\" # Override default if needed\n \"policies/\": \"^\" # Treat policies as gates\n \"sagas/\": \"$\" # Treat sagas as flows\n```\n\nCustom mappings extend (not replace) the discipline defaults. Or set `discipline: custom` and define everything yourself.\n\n## Stack Presets\n\nDisciplines tell Paradigm *what kind* of project you have (web, backend, mobile). Stack presets go one level deeper — they tell Paradigm *which framework* you are using. A stack preset layers framework-specific configuration on top of the discipline.\n\nParadigm ships 16 stack presets:\n\n| Preset | Discipline | What It Adds |\n|--------|------------|-------------|\n| `nextjs` | fullstack | `app/` routes, server actions, RSC patterns |\n| `remix` | fullstack | loader/action patterns, nested routes |\n| `sveltekit` | fullstack | `+page.svelte`, `+server.ts` patterns |\n| `nuxt` | fullstack | `composables/`, auto-imports |\n| `react-spa` | web | CRA/Vite SPA patterns, `hooks/`, `contexts/` |\n| `vue-spa` | web | Composition API, Pinia stores |\n| `express` | api | `app.get/post`, middleware chains |\n| `fastapi` | api | `@app.get`, Pydantic models, dependency injection |\n| `django` | fullstack | `views.py`, `models.py`, `urls.py` |\n| `flask` | api | `@app.route`, blueprints |\n| `gin-go` | api | `r.GET`, handler groups |\n| `axum-rs` | api | Axum extractors, tower middleware |\n| `swift-ios` | mobile | SwiftUI views, `@Observable`, navigation |\n| `kotlin-android` | mobile | Jetpack Compose, ViewModels, Hilt |\n| `react-native` | mobile | Expo/bare RN, navigation, native modules |\n| `flutter` | mobile | Widgets, BLoC/Riverpod, platform channels |\n\nStack presets are auto-detected during `paradigm init` from your dependencies and project files. You can also specify one explicitly:\n\n```bash\nparadigm init --stack nextjs\n```\n\nThe detected stack is written to config.yaml:\n\n```yaml\ndiscipline: fullstack\nstack: nextjs\n```\n\nStack presets add three things on top of the discipline:\n1. **Refined symbol mappings** — Framework-specific directories (e.g., `app/api/` for Next.js route handlers)\n2. **Purpose-required paths** — Directories that should have `.purpose` files for the framework to work well with Paradigm\n3. **Scan hints** — Framework-specific patterns for component detection, route patterns, auth patterns, and state management\n\nTo see all available presets:\n\n```bash\nparadigm presets\nparadigm presets --discipline mobile # Filter by discipline\n```\n\nStack presets solve the cold-start problem: when you run `paradigm init` on an existing Next.js project, the preset knows to look for `app/` routes, server components, and API handlers — producing meaningful `.purpose` scaffolding instead of generic stubs.",
|
|
386
386
|
"keyConcepts": [
|
|
387
387
|
"Disciplines define how directories map to symbol types per domain",
|
|
388
388
|
"14 disciplines: web, backend, fullstack, api, cli, ml, mobile, game, embedded, devops, data, library, monorepo, custom",
|
|
389
389
|
"Auto-detection at init time from project markers (package.json, Cargo.toml, etc.)",
|
|
390
390
|
"Hooks are components (#), not signals (!)",
|
|
391
391
|
"Disciplines affect symbol mappings, navigator, gate recommendations, and auto-scan patterns",
|
|
392
|
-
"Custom mappings can extend discipline defaults"
|
|
392
|
+
"Custom mappings can extend discipline defaults",
|
|
393
|
+
"16 stack presets layer framework-specific config on top of disciplines",
|
|
394
|
+
"Stack presets add refined symbol mappings, purpose-required paths, and scan hints",
|
|
395
|
+
"paradigm presets lists available presets, filterable by discipline"
|
|
393
396
|
],
|
|
394
397
|
"quiz": [
|
|
395
398
|
{
|
|
@@ -443,6 +446,19 @@
|
|
|
443
446
|
},
|
|
444
447
|
"correct": "C",
|
|
445
448
|
"explanation": "Custom mappings in config.yaml extend the discipline defaults. Adding \"policies/\": \"^\" tells Paradigm to treat files in the policies/ directory as gates, without changing the overall discipline or renaming directories."
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
"id": "q5",
|
|
452
|
+
"question": "A Next.js project runs `paradigm init`. The output shows `discipline: fullstack` and `stack: nextjs`. What does the stack preset add that the discipline alone does not provide?",
|
|
453
|
+
"choices": {
|
|
454
|
+
"A": "A completely different set of symbol mappings that replaces the discipline",
|
|
455
|
+
"B": "Framework-specific scan hints, refined symbol mappings for Next.js patterns like app/ routes, and purpose-required paths",
|
|
456
|
+
"C": "Automatic code generation for Next.js boilerplate",
|
|
457
|
+
"D": "A Next.js-specific version of portal.yaml with pre-defined gates",
|
|
458
|
+
"E": "Nothing — stack presets and disciplines are the same thing"
|
|
459
|
+
},
|
|
460
|
+
"correct": "B",
|
|
461
|
+
"explanation": "Stack presets layer framework-specific configuration on top of disciplines. For Next.js, the preset adds knowledge about app/ directory routing, server components, API route handlers, and other Next.js-specific patterns. It refines the generic fullstack discipline with framework-aware scan hints and purpose-required paths. Presets extend disciplines — they do not replace them."
|
|
446
462
|
}
|
|
447
463
|
]
|
|
448
464
|
},
|
|
@@ -129,3 +129,21 @@ components:
|
|
|
129
129
|
file: v3.0.json
|
|
130
130
|
tags: [plsat, para-301]
|
|
131
131
|
references: ["#paradigm-sync"]
|
|
132
|
+
|
|
133
|
+
plsat-questions-stack-presets:
|
|
134
|
+
description: "PLSAT questions testing stack presets and discipline layering (para-201)"
|
|
135
|
+
file: v3.0.json
|
|
136
|
+
tags: [plsat, para-201]
|
|
137
|
+
references: ["#stack-presets", "#disciplines"]
|
|
138
|
+
|
|
139
|
+
plsat-questions-auto-scan:
|
|
140
|
+
description: "PLSAT questions testing paradigm scan auto and cold-start bootstrap (para-301, para-101)"
|
|
141
|
+
file: v3.0.json
|
|
142
|
+
tags: [plsat, para-301, para-101]
|
|
143
|
+
references: ["#scan-index", "#stack-presets"]
|
|
144
|
+
|
|
145
|
+
plsat-questions-cold-start:
|
|
146
|
+
description: "PLSAT questions testing incremental adoption on existing projects (para-101)"
|
|
147
|
+
file: v3.0.json
|
|
148
|
+
tags: [plsat, para-101]
|
|
149
|
+
references: ["#cold-start", "#disciplines"]
|
|
@@ -2384,6 +2384,111 @@
|
|
|
2384
2384
|
"explanation": "A 'broken' status means one or more files referenced by the protocol (targets, exemplars, or template_from) no longer exist. This is more severe than 'stale' (where files exist but have been modified). A broken protocol cannot be reliably followed until its references are updated to point to existing files via paradigm_protocol_update."
|
|
2385
2385
|
}
|
|
2386
2386
|
]
|
|
2387
|
+
},
|
|
2388
|
+
{
|
|
2389
|
+
"type": "standalone",
|
|
2390
|
+
"slot": "slot-097",
|
|
2391
|
+
"course": "para-201",
|
|
2392
|
+
"variants": [
|
|
2393
|
+
{
|
|
2394
|
+
"id": "plsat-097",
|
|
2395
|
+
"scenario": "You run `paradigm init` on an existing Next.js project. The output shows `discipline: fullstack` and `stack: nextjs`. Your colleague asks what the difference is between a discipline and a stack preset.",
|
|
2396
|
+
"question": "Which statement BEST describes the relationship between disciplines and stack presets?",
|
|
2397
|
+
"choices": {
|
|
2398
|
+
"A": "They are the same thing — 'discipline' is the old name and 'stack preset' is the new name",
|
|
2399
|
+
"B": "Disciplines define domain-level symbol mappings (web, backend, mobile), while stack presets layer framework-specific configuration (Next.js, FastAPI, SwiftUI) on top of the discipline",
|
|
2400
|
+
"C": "Stack presets replace disciplines entirely — once a preset is detected, the discipline is ignored",
|
|
2401
|
+
"D": "Disciplines are for code organization and stack presets are for deployment configuration",
|
|
2402
|
+
"E": "Stack presets are only used for auto-scan patterns, while disciplines control everything else"
|
|
2403
|
+
},
|
|
2404
|
+
"correct": "B",
|
|
2405
|
+
"explanation": "Disciplines and stack presets are a two-layer system. The discipline (e.g., 'fullstack') defines broad symbol mappings for the development domain. The stack preset (e.g., 'nextjs') adds framework-specific refinements: scan hints for Next.js patterns like app/ routes and server components, purpose-required paths, and additional symbol mappings. The preset extends the discipline — it does not replace it."
|
|
2406
|
+
},
|
|
2407
|
+
{
|
|
2408
|
+
"id": "plsat-097b",
|
|
2409
|
+
"scenario": "A team is setting up Paradigm on a Flutter mobile app. They run `paradigm init` and see `discipline: mobile` with `stack: flutter`. They want to see what other stack presets are available for mobile projects.",
|
|
2410
|
+
"question": "Which command shows available stack presets filtered by discipline?",
|
|
2411
|
+
"choices": {
|
|
2412
|
+
"A": "`paradigm disciplines --list`",
|
|
2413
|
+
"B": "`paradigm presets --discipline mobile`",
|
|
2414
|
+
"C": "`paradigm init --show-stacks`",
|
|
2415
|
+
"D": "`paradigm config --list-presets`",
|
|
2416
|
+
"E": "`paradigm scan --detect-stack`"
|
|
2417
|
+
},
|
|
2418
|
+
"correct": "B",
|
|
2419
|
+
"explanation": "The `paradigm presets` command lists all available stack presets. The `--discipline` flag filters to show only presets for a specific discipline. For mobile, this would show flutter, swift-ios, kotlin-android, and react-native presets."
|
|
2420
|
+
}
|
|
2421
|
+
]
|
|
2422
|
+
},
|
|
2423
|
+
{
|
|
2424
|
+
"type": "standalone",
|
|
2425
|
+
"slot": "slot-098",
|
|
2426
|
+
"course": "para-301",
|
|
2427
|
+
"variants": [
|
|
2428
|
+
{
|
|
2429
|
+
"id": "plsat-098",
|
|
2430
|
+
"scenario": "You join a large existing project with 200+ source files and no Paradigm setup. Running `paradigm init` creates the `.paradigm/` directory, but no `.purpose` files exist yet. A colleague suggests running `paradigm scan auto` to bootstrap the project.",
|
|
2431
|
+
"question": "What does `paradigm scan auto` do?",
|
|
2432
|
+
"choices": {
|
|
2433
|
+
"A": "It reads all source files line-by-line and creates comprehensive documentation for every function",
|
|
2434
|
+
"B": "It uses regex-based heuristics to detect components, routes, auth patterns, and signals in your codebase, then generates draft `.purpose` files with detected symbols",
|
|
2435
|
+
"C": "It copies `.purpose` templates from a global registry and places them in every directory",
|
|
2436
|
+
"D": "It connects to an AI service to analyze your code and generate documentation",
|
|
2437
|
+
"E": "It deletes all existing `.purpose` files and starts fresh"
|
|
2438
|
+
},
|
|
2439
|
+
"correct": "B",
|
|
2440
|
+
"explanation": "paradigm scan auto uses pattern-based detection to find components (exported classes/functions), routes (HTTP method patterns like app.get/router.post), auth patterns (JWT, session checks, middleware), and signals (event emitters). It produces draft .purpose files with detected symbols and confidence levels. The detection is local and regex-based — it does not call external services or read every line. Stack presets enhance the scan with framework-specific patterns."
|
|
2441
|
+
},
|
|
2442
|
+
{
|
|
2443
|
+
"id": "plsat-098b",
|
|
2444
|
+
"scenario": "After running `paradigm scan auto` on a FastAPI project, the auto-scan detects several route handlers in `src/routes/users.py` and marks them with `confidence: high`. It also finds some utility functions and marks them `confidence: medium`.",
|
|
2445
|
+
"question": "What do the confidence levels on auto-detected symbols indicate?",
|
|
2446
|
+
"choices": {
|
|
2447
|
+
"A": "How important the symbol is to the project — high means critical, medium means optional",
|
|
2448
|
+
"B": "How certain the scanner is that the detected pattern actually represents that symbol type — high means strong pattern match, medium means heuristic match",
|
|
2449
|
+
"C": "How many lines of code the detected symbol contains — more lines means higher confidence",
|
|
2450
|
+
"D": "How recently the file was modified — recently modified files get higher confidence",
|
|
2451
|
+
"E": "How many other symbols reference this one — more references means higher confidence"
|
|
2452
|
+
},
|
|
2453
|
+
"correct": "B",
|
|
2454
|
+
"explanation": "Confidence levels reflect the scanner's certainty about the detection. A route handler matching `@app.get('/users')` is a strong, unambiguous pattern match (high confidence). A utility function detected from an exported function that doesn't match any specific pattern is a heuristic match (medium confidence). Low confidence detections are more speculative. Users should review auto-generated .purpose files, especially medium and low confidence entries."
|
|
2455
|
+
}
|
|
2456
|
+
]
|
|
2457
|
+
},
|
|
2458
|
+
{
|
|
2459
|
+
"type": "standalone",
|
|
2460
|
+
"slot": "slot-099",
|
|
2461
|
+
"course": "para-101",
|
|
2462
|
+
"variants": [
|
|
2463
|
+
{
|
|
2464
|
+
"id": "plsat-099",
|
|
2465
|
+
"scenario": "A developer is tasked with adding Paradigm to a mature React Native project that has been in development for two years. The project has 150+ components, navigation stacks, Redux state management, and several API integration files. The developer is worried about the setup effort.",
|
|
2466
|
+
"question": "What is the recommended approach for adding Paradigm to a large existing project?",
|
|
2467
|
+
"choices": {
|
|
2468
|
+
"A": "Document every single component on day one — comprehensive coverage is required before Paradigm is useful",
|
|
2469
|
+
"B": "Start with `paradigm init` (which auto-detects discipline and stack), then create one `.purpose` file for the most critical module, and expand incrementally",
|
|
2470
|
+
"C": "Rewrite the project structure to match Paradigm's expected directory layout",
|
|
2471
|
+
"D": "Wait until starting a new project — Paradigm cannot be added to existing codebases",
|
|
2472
|
+
"E": "Run `paradigm scan auto` and commit all generated files without review"
|
|
2473
|
+
},
|
|
2474
|
+
"correct": "B",
|
|
2475
|
+
"explanation": "Paradigm is designed for incremental adoption. Start with `paradigm init` which auto-detects your discipline (mobile) and stack preset (react-native), configuring symbol mappings and scan patterns for your framework. Then create one .purpose file for your most important module. You can optionally run `paradigm scan auto` to bootstrap additional .purpose files, but always review auto-generated content. A common pitfall is trying to document everything on day one — start small and expand as the project grows."
|
|
2476
|
+
},
|
|
2477
|
+
{
|
|
2478
|
+
"id": "plsat-099b",
|
|
2479
|
+
"scenario": "A team runs `paradigm init` on their Django project. The init command auto-detects `discipline: fullstack` and `stack: django`. The team then runs `paradigm scan auto` which generates draft `.purpose` files for `views/`, `models/`, and `urls/` directories.",
|
|
2480
|
+
"question": "Why did the auto-scan know to look in `views/`, `models/`, and `urls/` directories?",
|
|
2481
|
+
"choices": {
|
|
2482
|
+
"A": "These are hardcoded directories that every Paradigm scan checks regardless of discipline",
|
|
2483
|
+
"B": "The django stack preset provides scan hints with Django-specific component patterns, route patterns, and directory structures",
|
|
2484
|
+
"C": "The scan randomly explores all directories and happened to find code there",
|
|
2485
|
+
"D": "The team manually configured these directories in config.yaml before scanning",
|
|
2486
|
+
"E": "Django is a special case with its own dedicated scanner module in Paradigm"
|
|
2487
|
+
},
|
|
2488
|
+
"correct": "B",
|
|
2489
|
+
"explanation": "Stack presets include scan hints — framework-specific patterns that tell the auto-scanner where to look and what patterns to match. The django preset knows that views.py files contain route handlers, models.py files contain data models, and urls.py files define URL routing. This is why stack presets solve the cold-start problem: they bring framework knowledge that makes auto-scanning productive for existing projects."
|
|
2490
|
+
}
|
|
2491
|
+
]
|
|
2387
2492
|
}
|
|
2388
2493
|
]
|
|
2389
2494
|
}
|
|
@@ -83,5 +83,5 @@ Error generating stack: `+i.message+`
|
|
|
83
83
|
*/var Il=S,fm=cm;function dm(e,t){return e===t&&(e!==0||1/e===1/t)||e!==e&&t!==t}var pm=typeof Object.is=="function"?Object.is:dm,hm=fm.useSyncExternalStore,mm=Il.useRef,vm=Il.useEffect,gm=Il.useMemo,ym=Il.useDebugValue;hf.useSyncExternalStoreWithSelector=function(e,t,n,r,l){var i=mm(null);if(i.current===null){var o={hasValue:!1,value:null};i.current=o}else o=i.current;i=gm(function(){function u(x){if(!c){if(c=!0,h=x,x=r(x),l!==void 0&&o.hasValue){var y=o.value;if(l(y,x))return m=y}return m=x}if(y=m,pm(h,x))return y;var w=r(x);return l!==void 0&&l(y,w)?(h=x,y):(h=x,m=w)}var c=!1,h,m,v=n===void 0?null:n;return[function(){return u(t())},v===null?void 0:function(){return u(v())}]},[t,n,r,l]);var s=hm(e,i[0],i[1]);return vm(function(){o.hasValue=!0,o.value=s},[s]),ym(s),s};pf.exports=hf;var xm=pf.exports;const wm=Uu(xm),gf={},{useDebugValue:Sm}=vo,{useSyncExternalStoreWithSelector:km}=wm;let Iu=!1;const Cm=e=>e;function Em(e,t=Cm,n){(gf?"production":void 0)!=="production"&&n&&!Iu&&(console.warn("[DEPRECATED] Use `createWithEqualityFn` instead of `create` or use `useStoreWithEqualityFn` instead of `useStore`. They can be imported from 'zustand/traditional'. https://github.com/pmndrs/zustand/discussions/1937"),Iu=!0);const r=km(e.subscribe,e.getState,e.getServerState||e.getInitialState,t,n);return Sm(r),r}const Ou=e=>{(gf?"production":void 0)!=="production"&&typeof e!="function"&&console.warn("[DEPRECATED] Passing a vanilla store will be unsupported in a future version. Instead use `import { useStore } from 'zustand'`.");const t=typeof e=="function"?em(e):e,n=(r,l)=>Em(t,r,l);return Object.assign(n,t),n},cs=e=>e?Ou(e):Ou,Ol=cs((e,t)=>({courses:[],courseCache:{},isLoading:!1,error:null,loadCourses:async()=>{e({isLoading:!0,error:null});try{const n=await fetch("/api/courses");if(!n.ok)throw new Error("Failed to load courses");const r=await n.json();e({courses:r.courses,isLoading:!1})}catch(n){e({error:n.message,isLoading:!1})}},loadCourse:async n=>{const r=t().courseCache[n];if(r)return r;try{const l=await fetch(`/api/courses/${n}`);if(!l.ok)return null;const i=await l.json();return e(o=>({courseCache:{...o.courseCache,[n]:i}})),i}catch{return null}}})),yf="paradigm-university-progress";function jm(){try{const e=localStorage.getItem(yf);return e?JSON.parse(e):{}}catch{return{}}}function pi(e){try{localStorage.setItem(yf,JSON.stringify(e))}catch{}}const Ml=cs((e,t)=>({progress:jm(),completeLesson:(n,r)=>{e(l=>{const i=l.progress[n]||{courseId:n,completedLessons:[],quizResults:{}};if(i.completedLessons.includes(r))return l;const o={...l.progress,[n]:{...i,completedLessons:[...i.completedLessons,r]}};return pi(o),{progress:o}})},recordQuiz:n=>{e(r=>{const l=r.progress[n.courseId]||{courseId:n.courseId,completedLessons:[],quizResults:{}},i={...r.progress,[n.courseId]:{...l,quizResults:{...l.quizResults,[n.lessonId]:n}}};return pi(i),{progress:i}})},getCourseProgress:n=>t().progress[n]||{courseId:n,completedLessons:[],quizResults:{}},isLessonCompleted:(n,r)=>{const l=t().progress[n];return l?l.completedLessons.includes(r):!1},getCoursePercentage:(n,r)=>{if(r===0)return 0;const l=t().progress[n];return l?Math.round(l.completedLessons.length/r*100):0},resetProgress:()=>{pi({}),e({progress:{}})}})),xf="paradigm-university-plsat";function Nm(){try{const e=localStorage.getItem(xf);return e?JSON.parse(e):{certificates:[],studentName:""}}catch{return{certificates:[],studentName:""}}}function Mu(e){try{localStorage.setItem(xf,JSON.stringify(e))}catch{}}const fs=cs((e,t)=>{const n=Nm();return{certificates:n.certificates,studentName:n.studentName,setStudentName:r=>{e({studentName:r});const l=t();Mu({certificates:l.certificates,studentName:r})},addCertificate:r=>{e(l=>{const i=[...l.certificates,r];return Mu({certificates:i,studentName:l.studentName}),{certificates:i}})},getLatestCertificate:()=>{const r=t().certificates;return r.length===0?null:r.reduce((l,i)=>new Date(i.date)>new Date(l.date)?i:l)},getCertificateForVersion:r=>t().certificates.find(l=>l.plsatVersion===r&&l.passed)||null,hasPassed:()=>t().certificates.some(r=>r.passed)}});function wf({percentage:e,size:t=48,strokeWidth:n=4}){const r=(t-n)/2,l=2*Math.PI*r,i=l-e/100*l;return a.jsxs("div",{className:"progress-ring",style:{width:t,height:t},children:[a.jsxs("svg",{width:t,height:t,children:[a.jsx("circle",{className:"ring-bg",cx:t/2,cy:t/2,r,fill:"none",strokeWidth:n}),a.jsx("circle",{className:"ring-fill",cx:t/2,cy:t/2,r,fill:"none",strokeWidth:n,strokeLinecap:"round",strokeDasharray:l,strokeDashoffset:i})]}),a.jsxs("span",{className:"ring-label",children:[e,"%"]})]})}function Pm(){const{courses:e,isLoading:t,loadCourses:n}=Ol(),r=Ml(i=>i.getCoursePercentage),l=fs(i=>i.hasPassed);return S.useEffect(()=>{n()},[n]),t?a.jsx("div",{className:"loading",children:"Opening the campus gates..."}):a.jsxs("div",{className:"home",children:[a.jsxs("div",{className:"home-hero",children:[a.jsx(pn,{size:140}),a.jsx("h1",{children:"Paradigm University"}),a.jsx("p",{className:"motto",children:"Universitas Paradigmatica — Lux in Codice"}),a.jsx("p",{className:"description",children:"Master the Paradigm framework through structured courses, hands-on quizzes, and the legendary PLSAT certification exam."})]}),a.jsx("div",{className:"gold-divider"}),a.jsxs("section",{className:"course-catalog",children:[a.jsx("h2",{children:"Course Catalog"}),e.map(i=>{const o=r(i.id,i.lessonCount);return a.jsxs(q,{to:`/course/${i.id}`,className:"course-card",children:[a.jsxs("div",{className:"course-card-header",children:[a.jsxs("div",{className:"course-card-title",children:[a.jsx("span",{className:"course-number",children:i.id.replace("para-","PARA ")}),a.jsx("h3",{children:i.title.replace(/^PARA \d+: /,"")})]}),a.jsx(wf,{percentage:o})]}),a.jsx("p",{className:"course-description",children:i.description}),a.jsx("div",{className:"course-topics",children:i.lessons.map(s=>a.jsx("span",{className:"course-topic-tag",children:s.title},s.id))}),a.jsxs("div",{className:"course-meta",children:[a.jsxs("span",{children:[i.lessonCount," lessons"]}),a.jsx("span",{className:"course-meta-cta",children:"Start course →"})]})]},i.id)})]}),a.jsx("div",{className:"gold-divider"}),a.jsxs("section",{children:[a.jsx("h2",{className:"mb-lg",children:"Quick Links"}),a.jsxs("div",{className:"quick-links",children:[a.jsx(q,{to:"/plsat",className:"quick-link",children:l()?"Retake the PLSAT":"Take the PLSAT"}),a.jsx(q,{to:"/reference",className:"quick-link",children:"Reference Library"}),a.jsx(q,{to:"/certificate",className:"quick-link",children:"View Certificates"}),a.jsx(q,{to:"/course/para-101",className:"quick-link",children:"Start Learning"})]})]})]})}function _m(){const{courses:e,isLoading:t,loadCourses:n}=Ol(),r=Ml(l=>l.getCoursePercentage);return S.useEffect(()=>{n()},[n]),t?a.jsx("div",{className:"loading",children:"Loading courses..."}):a.jsx("div",{className:"home",children:a.jsxs("section",{className:"course-catalog",children:[a.jsx("h2",{children:"Course Catalog"}),e.map(l=>{const i=r(l.id,l.lessonCount);return a.jsxs(q,{to:`/course/${l.id}`,className:"course-card",children:[a.jsx("span",{className:"course-number",children:l.id.replace("para-","PARA ")}),a.jsx("h3",{children:l.title.replace(/^PARA \d+: /,"")}),a.jsx("p",{className:"course-description",children:l.description}),a.jsxs("div",{className:"course-meta",children:[a.jsxs("span",{children:[l.lessonCount," lessons"]}),a.jsx(wf,{percentage:i})]})]},l.id)})]})})}function Du(e){return e.replace(/`([^`]+)`/g,"<code>$1</code>").replace(/\*\*([^*]+)\*\*/g,"<strong>$1</strong>").replace(/\*([^*]+)\*/g,"<em>$1</em>")}function Lm(e){const t=e.trim().split(`
|
|
84
84
|
`);if(t.length<2)return e;const n=o=>o.split("|").map(s=>s.trim()).filter(s=>s.length>0),r=n(t[0]),l=t.slice(2).map(n);let i="<table><thead><tr>";for(const o of r)i+=`<th>${Du(o)}</th>`;i+="</tr></thead><tbody>";for(const o of l){i+="<tr>";for(const s of o)i+=`<td>${Du(s)}</td>`;i+="</tr>"}return i+="</tbody></table>",i}function Ut(e){const t=[];let n=e.replace(/```(\w*)\n([\s\S]*?)```/g,(r,l,i)=>{const o=t.length;return t.push(`<pre><code>${i}</code></pre>`),`\0BLOCK${o}\0`});return n=n.replace(/((?:^\|.+\|\n?)+)/gm,r=>{const l=r.trim().split(`
|
|
85
85
|
`);if(l.length>=3&&/^[\s-:|]+$/.test(l[1])){const i=t.length;return t.push(Lm(r)),`\0BLOCK${i}\0`}return r}),n=n.replace(/`([^`]+)`/g,"<code>$1</code>").replace(/^#### (.+)$/gm,"<h4>$1</h4>").replace(/^### (.+)$/gm,"<h3>$1</h3>").replace(/^## (.+)$/gm,"<h2>$1</h2>").replace(/\*\*([^*]+)\*\*/g,"<strong>$1</strong>").replace(/\*([^*]+)\*/g,"<em>$1</em>").replace(/^> (.+)$/gm,"<blockquote>$1</blockquote>").replace(/^\d+\.\s+(.+)$/gm,"<oli>$1</oli>").replace(/((?:<oli>.*<\/oli>\n?)+)/g,r=>"<ol>"+r.replace(/<\/?oli>/g,l=>l.replace("oli","li"))+"</ol>").replace(/^- (.+)$/gm,"<li>$1</li>").replace(/((?:<li>.*<\/li>\n?)+)/g,"<ul>$1</ul>").replace(/^(?!<(?:h[1-6]|ul|ol|li|p|blockquote|pre|table|thead|tbody|tr|td|th|\x00)).+$/gm,"<p>$&</p>").replace(/\n{2,}/g,`
|
|
86
|
-
`),n=n.replace(/\x00BLOCK(\d+)\x00/g,(r,l)=>t[Number(l)]),n}function Au(){const{courseId:e,lessonId:t}=uf(),n=sf(),r=Ol(d=>d.loadCourse),[l,i]=S.useState(null),[o,s]=S.useState(null),[u,c]=S.useState(!0),{isLessonCompleted:h,completeLesson:m}=Ml();if(S.useEffect(()=>{e&&(c(!0),r(e).then(d=>{if(i(d),d&&d.lessons.length>0){const f=t?d.lessons.find(p=>p.id===t):null;f?s(f):(s(d.lessons[0]),n(`/course/${e}/${d.lessons[0].id}`,{replace:!0}))}c(!1)}))},[e,t,r,n]),u)return a.jsx("div",{className:"loading",children:"Opening the textbook..."});if(!l)return a.jsxs("div",{className:"empty-state",children:[a.jsx("h3",{children:"Course not found"}),a.jsx("p",{children:"The requested course does not exist."}),a.jsx(q,{to:"/",className:"btn btn-primary mt-lg",children:"Return to Campus"})]});const v=o?l.lessons.findIndex(d=>d.id===o.id):0,x=()=>{o&&e&&m(e,o.id)},y=(d,f=!1)=>{s(d),n(`/course/${e}/${d.id}`),f&&window.scrollTo(0,0)},w=()=>{v<l.lessons.length-1&&y(l.lessons[v+1],!0)},N=()=>{v>0&&y(l.lessons[v-1],!0)};return a.jsxs("div",{className:"course-layout",children:[a.jsxs("aside",{className:"course-sidebar",children:[a.jsx("h2",{children:l.title}),a.jsx("nav",{className:"lesson-nav",children:l.lessons.map(d=>{const f=e?h(e,d.id):!1,p=(o==null?void 0:o.id)===d.id;let g="lesson-nav-item";return p&&(g+=" active"),f&&!p&&(g+=" completed"),a.jsx("button",{className:g,onClick:()=>y(d),children:d.title},d.id)})})]}),a.jsx("div",{className:"course-content",children:o&&a.jsxs(a.Fragment,{children:[a.jsx("h1",{children:o.title}),o.keyConcepts.length>0&&a.jsx("div",{className:"key-concepts",children:o.keyConcepts.map(d=>a.jsx("span",{className:"concept-tag",children:d},d))}),a.jsx("div",{className:"lesson-content",dangerouslySetInnerHTML:{__html:Ut(o.content)}}),a.jsxs("div",{className:"lesson-actions",children:[a.jsx("div",{children:v>0&&a.jsx("button",{className:"btn btn-secondary",onClick:N,children:"Previous"})}),a.jsxs("div",{style:{display:"flex",gap:"0.5rem"},children:[e&&!h(e,o.id)&&a.jsx("button",{className:"btn btn-secondary",onClick:x,children:"Mark Complete"}),o.quiz.length>0&&e&&a.jsx(q,{to:`/course/${e}/quiz/${o.id}`,className:"btn btn-gold",children:"Take Quiz"}),v<l.lessons.length-1&&a.jsx("button",{className:"btn btn-primary",onClick:w,children:"Next Lesson"})]})]})]})})]})}function co({number:e,question:t,scenario:n,choices:r,correct:l,explanation:i,selectedAnswer:o,onSelect:s,showResult:u,onAnswered:c,splitLayout:h}){const[m,v]=S.useState(null),[x,y]=S.useState(!1),w=s!==void 0,N=w?o||null:m,d=w?u:x,f=j=>{d&&!w||(w?s==null||s(j):(v(j),y(!0),c==null||c(j)))},p=N===l,g=Object.keys(r).sort(),E=a.jsxs(a.Fragment,{children:[a.jsxs("div",{className:"question-number",children:["Question ",e]}),n&&a.jsx("div",{className:"scenario",dangerouslySetInnerHTML:{__html:Ut(n)}}),a.jsx("div",{className:"question-text",dangerouslySetInnerHTML:{__html:Ut(t)}})]}),k=a.jsx("div",{className:"choices",children:g.map(j=>{let P="choice-btn";return N===j&&(P+=" selected"),d&&j===l&&(P+=" correct"),d&&N===j&&j!==l&&(P+=" incorrect"),a.jsxs("button",{className:P,onClick:()=>f(j),disabled:d&&!w,children:[a.jsxs("span",{className:"choice-letter",children:[j,"."]}),a.jsx("span",{dangerouslySetInnerHTML:{__html:Ut(r[j])}})]},j)})});return h?a.jsxs("div",{className:"question-card",children:[a.jsxs("div",{className:"question-split-layout",children:[a.jsx("div",{className:"question-content",children:E}),a.jsx("div",{className:"answer-choices",children:k})]}),d&&a.jsxs("div",{className:`explanation ${p?"":"wrong"}`,children:[a.jsx("strong",{children:p?"Correct!":`Incorrect. The answer is ${l}.`}),a.jsx("br",{}),a.jsx("span",{dangerouslySetInnerHTML:{__html:Ut(i)}})]})]}):a.jsxs("div",{className:"question-card",children:[E,k,d&&a.jsxs("div",{className:`explanation ${p?"":"wrong"}`,children:[a.jsx("strong",{children:p?"Correct!":`Incorrect. The answer is ${l}.`}),a.jsx("br",{}),a.jsx("span",{dangerouslySetInnerHTML:{__html:Ut(i)}})]})]})}function Tm(){const{courseId:e,lessonId:t}=uf(),n=Ol(g=>g.loadCourse),{recordQuiz:r,completeLesson:l,getCourseProgress:i}=Ml(),[o,s]=S.useState(null),[u,c]=S.useState(null),[h,m]=S.useState(!0),[v,x]=S.useState(!1),[y,w]=S.useState(0),N=e&&t?i(e).quizResults[t]:void 0;S.useEffect(()=>{e&&(m(!0),n(e).then(g=>{if(g&&t){const E=g.lessons.findIndex(k=>k.id===t);s(E>=0?g.lessons[E]:null),E>=0&&E<g.lessons.length-1&&c(g.lessons[E+1].id)}m(!1)}))},[e,t,n]);const[d,f]=S.useState({}),p=(g,E)=>{if(f(k=>({...k,[g]:E})),o){const k={...d,[g]:E};if(Object.keys(k).length===o.quiz.length){const j=o.quiz.filter(P=>k[P.id]===P.correct).length;if(w(j),x(!0),e&&t){const P={courseId:e,lessonId:t,score:j,total:o.quiz.length,answers:k,date:new Date().toISOString()};r(P),l(e,t)}}}};return h?a.jsx("div",{className:"loading",children:"Preparing your examination..."}):!o||o.quiz.length===0?a.jsxs("div",{className:"empty-state",children:[a.jsx("h3",{children:"No quiz available"}),a.jsx("p",{children:"This lesson does not have a quiz."}),a.jsx(q,{to:`/course/${e}`,className:"btn btn-primary mt-lg",children:"Back to Course"})]}):a.jsxs("div",{className:"quiz-container",children:[a.jsxs("div",{className:"quiz-header",children:[a.jsxs("h1",{children:[o.title," — Quiz"]}),a.jsx("p",{className:"quiz-progress",children:v?`Score: ${y}/${o.quiz.length} (${Math.round(y/o.quiz.length*100)}%)`:`${Object.keys(d).length}/${o.quiz.length} answered`}),N&&!v&&a.jsxs("p",{className:"text-muted mt-sm",children:["Previous best: ",N.score,"/",N.total]})]}),o.quiz.map((g,E)=>a.jsx(co,{number:E+1,question:g.question,choices:g.choices,correct:g.correct,explanation:g.explanation,onAnswered:k=>p(g.id,k)},g.id)),v&&a.jsxs("div",{className:"text-center mt-xl",children:[a.jsx("p",{className:"mb-lg",style:{fontSize:"1.25rem",fontFamily:"var(--font-serif)"},children:y===o.quiz.length?"Perfect score! Exemplary scholarship.":y>=o.quiz.length*.8?"Well done, scholar. You have demonstrated understanding.":"Review the material and try again. Persistence is the path to mastery."}),u?a.jsx(q,{to:`/course/${e}/${u}`,className:"btn btn-primary",children:"Next Lesson"}):a.jsx(q,{to:`/course/${e}`,className:"btn btn-primary",children:"Return to Course"})]})]})}function zm({totalSeconds:e,onTimeUp:t,running:n}){const[r,l]=S.useState(e),i=S.useCallback(()=>{t()},[t]);S.useEffect(()=>{if(!n)return;const h=setInterval(()=>{l(m=>m<=1?(clearInterval(h),i(),0):m-1)},1e3);return()=>clearInterval(h)},[n,i]);const o=Math.floor(r/60),s=r%60,u=r/e*100;let c="timer-display";return u<20?c+=" critical":u<40&&(c+=" warning"),a.jsxs("span",{className:c,children:[String(o).padStart(2,"0"),":",String(s).padStart(2,"0")]})}function Fu({text:e}){const t=e.split(/(```[\s\S]*?```)/g);return a.jsx("div",{className:"passage-block",children:a.jsx("div",{className:"passage-content",children:t.map((n,r)=>{if(n.startsWith("```")){const l=n.match(/^```(\w*)\n?([\s\S]*?)```$/),i=l?l[2]:n.slice(3,-3);return a.jsx("pre",{children:a.jsx("code",{children:i})},r)}return n.split(/\n\n+/).map((l,i)=>a.jsx("p",{children:l},`${r}-${i}`))})})})}function $u(e,t,n){if(!t)return null;const r=e[n];return r.passageId?t[r.passageId]??null:null}function Rm(){const[e,t]=S.useState(null),[n,r]=S.useState("intro"),[l,i]=S.useState({}),[o,s]=S.useState(0),[u,c]=S.useState(!0),[h,m]=S.useState(null),{studentName:v,setStudentName:x,addCertificate:y}=fs(),[w,N]=S.useState(v);S.useEffect(()=>{fetch("/api/plsat/3.0").then(k=>k.json()).then(k=>{t(k),c(!1)}).catch(()=>c(!1))},[]);const d=S.useCallback(()=>{if(!e)return;const k=e.questions.filter(de=>l[de.id]===de.correct).length,j=e.questions.length,P=Math.round(k/j*100),M=P>=e.passThreshold*100,z={name:w||"Anonymous Scholar",score:k,total:j,percentage:P,passed:M,plsatVersion:e.version,frameworkVersion:e.frameworkVersion,date:new Date().toISOString()};m(z),y(z),w&&x(w),r("results")},[e,l,w,y,x]),f=S.useCallback(()=>{d()},[d]),p=()=>{d()},g=()=>{i({}),s(0),r("exam")},E=S.useMemo(()=>!e||n!=="exam"?null:$u(e.questions,e.passages,o),[e,n,o]);if(u)return a.jsx("div",{className:"loading",children:"The examination board is convening..."});if(!e)return a.jsxs("div",{className:"empty-state",children:[a.jsx("h3",{children:"PLSAT Unavailable"}),a.jsx("p",{children:"Could not load the examination. Please try again."})]});if(n==="intro")return a.jsxs("div",{className:"plsat-container",children:[a.jsxs("div",{className:"plsat-intro",children:[a.jsx(pn,{size:100}),a.jsx("h1",{children:"The PLSAT"}),a.jsx("p",{className:"plsat-subtitle",children:"Paradigm Licensure Standardized Assessment Test"}),a.jsxs("p",{className:"text-muted",children:["Version ",e.version]})]}),a.jsxs("div",{className:"plsat-rules",children:[a.jsx("h3",{children:"Examination Rules"}),a.jsxs("ul",{children:[a.jsxs("li",{children:[a.jsxs("strong",{children:[e.questions.length," questions"]})," covering all aspects of the Paradigm framework"]}),a.jsxs("li",{children:[a.jsxs("strong",{children:[Math.floor(e.timeLimit/60)," minutes"]})," to complete the examination"]}),a.jsxs("li",{children:[a.jsxs("strong",{children:[e.passThreshold*100,"%"]})," required to pass and receive certification"]}),a.jsx("li",{children:"All questions are multiple choice (A through E)"}),a.jsx("li",{children:"Some questions reference a shared passage — read it carefully"}),a.jsx("li",{children:"You may navigate between questions freely"}),a.jsx("li",{children:"There is no penalty for guessing — answer every question"}),a.jsx("li",{children:"Your certificate will display the PLSAT version for posterity"})]})]}),a.jsxs("div",{className:"text-center",children:[a.jsx("div",{className:"mb-lg",children:a.jsx("input",{type:"text",className:"name-input",placeholder:"Enter your name, scholar",value:w,onChange:k=>N(k.target.value)})}),a.jsx("button",{className:"btn btn-primary btn-lg",onClick:g,children:"Begin Examination"})]})]});if(n==="exam"){const k=e.questions[o],j=Object.keys(l).length;return a.jsxs("div",{className:"plsat-container",children:[a.jsxs("div",{className:"plsat-timer",children:[a.jsx(zm,{totalSeconds:e.timeLimit,onTimeUp:f,running:!0}),a.jsxs("span",{className:"plsat-progress-text",children:["Question ",o+1," of ",e.questions.length," | ",j," answered"]})]}),a.jsxs("div",{style:{marginTop:"var(--space-lg)"},children:[E&&a.jsx(Fu,{text:E}),a.jsx(co,{number:o+1,question:k.question,scenario:k.scenario,choices:k.choices,correct:k.correct,explanation:k.explanation,selectedAnswer:l[k.id],onSelect:P=>i(M=>({...M,[k.id]:P})),showResult:!1,splitLayout:!0})]}),a.jsxs("div",{style:{display:"flex",justifyContent:"space-between",marginTop:"var(--space-lg)"},children:[a.jsx("button",{className:"btn btn-secondary",disabled:o===0,onClick:()=>s(P=>P-1),children:"Previous"}),a.jsxs("div",{style:{display:"flex",gap:"0.5rem"},children:[j===e.questions.length&&a.jsx("button",{className:"btn btn-gold",onClick:p,children:"Submit Examination"}),a.jsx("button",{className:"btn btn-primary",disabled:o===e.questions.length-1,onClick:()=>s(P=>P+1),children:"Next"})]})]}),a.jsx("div",{style:{display:"flex",flexWrap:"wrap",gap:"4px",marginTop:"var(--space-xl)",justifyContent:"center"},children:e.questions.map((P,M)=>a.jsx("button",{onClick:()=>s(M),style:{width:28,height:28,borderRadius:"50%",border:M===o?"2px solid var(--burgundy)":"1px solid var(--parchment-dark)",background:l[P.id]?"var(--gold-bg)":"var(--cream)",color:"var(--ink)",fontSize:"0.6875rem",cursor:"pointer",fontWeight:M===o?600:400},children:M+1},P.id))})]})}return n==="results"&&h?a.jsx("div",{className:"plsat-container",children:a.jsxs("div",{className:"plsat-results",children:[a.jsx(pn,{size:80}),a.jsxs("div",{className:`score-display ${h.passed?"passed":"failed"}`,children:[h.percentage,"%"]}),a.jsx("p",{className:"verdict",children:h.passed?"Congratulations! You have passed the PLSAT.":"The examination board regrets to inform you that you did not pass."}),a.jsxs("p",{className:"text-muted mb-lg",children:["Score: ",h.score,"/",h.total," | PLSAT v",h.plsatVersion," | ",new Date(h.date).toLocaleDateString()]}),a.jsxs("div",{style:{display:"flex",gap:"1rem",justifyContent:"center"},children:[h.passed&&a.jsx(q,{to:"/certificate",className:"btn btn-gold btn-lg",children:"View Certificate"}),a.jsx("button",{className:"btn btn-secondary",onClick:()=>r("review"),children:"Review Answers"}),a.jsx("button",{className:"btn btn-primary",onClick:()=>r("intro"),children:h.passed?"Retake":"Try Again"})]})]})}):n==="review"?a.jsxs("div",{className:"plsat-container",children:[a.jsxs("div",{className:"quiz-header",children:[a.jsx("h1",{children:"PLSAT Review"}),a.jsx("p",{className:"quiz-progress",children:h?`Score: ${h.score}/${h.total} (${h.percentage}%)`:""})]}),e.questions.map((k,j)=>{const P=$u(e.questions,e.passages,j);return a.jsxs("div",{children:[P&&a.jsx(Fu,{text:P}),a.jsx(co,{number:j+1,question:k.question,scenario:k.scenario,choices:k.choices,correct:k.correct,explanation:k.explanation,selectedAnswer:l[k.id],onSelect:()=>{},showResult:!0,splitLayout:!0})]},k.id)}),a.jsx("div",{className:"text-center mt-xl",children:a.jsx("button",{className:"btn btn-primary",onClick:()=>r("results"),children:"Back to Results"})})]}):null}function Im(){const[e,t]=S.useState(null),[n,r]=S.useState(!0);return S.useEffect(()=>{fetch("/api/reference").then(l=>l.json()).then(l=>{t(l),r(!1)}).catch(()=>r(!1))},[]),n?a.jsx("div",{className:"loading",children:"Opening the reference library..."}):e?a.jsxs("div",{className:"reference-container",children:[a.jsx("h1",{className:"mb-lg",children:"Reference Library"}),e.sections.map(l=>a.jsxs("section",{className:"reference-section",children:[a.jsx("h2",{children:l.title}),a.jsx("div",{className:"reference-grid",children:l.cards.map(i=>a.jsxs("div",{className:"ref-card",children:[i.symbol&&a.jsx("div",{className:"ref-symbol",children:i.symbol}),a.jsx("h4",{children:i.name}),a.jsx("p",{children:i.description}),i.examples&&i.examples.length>0&&a.jsx("div",{className:"ref-examples",children:i.examples.map(o=>a.jsx("span",{className:"ref-example",children:o},o))}),i.logger&&a.jsx("p",{style:{marginTop:"0.5rem"},children:a.jsx("code",{children:i.logger})}),i.when&&a.jsx("p",{className:"text-muted",style:{fontSize:"0.8125rem",marginTop:"0.25rem"},children:a.jsx("em",{children:i.when})}),i.command&&a.jsx("p",{style:{marginTop:"0.5rem"},children:a.jsx("code",{children:i.command})}),i.steps&&i.steps.length>0&&a.jsx("ol",{style:{fontSize:"0.875rem",paddingLeft:"1.25rem",marginTop:"0.5rem"},children:i.steps.map((o,s)=>a.jsx("li",{style:{marginBottom:"0.25rem"},children:o},s))})]},i.id))})]},l.id))]}):a.jsxs("div",{className:"empty-state",children:[a.jsx("h3",{children:"Reference library unavailable"}),a.jsx("p",{children:"Could not load reference data."})]})}function Om(){const{certificates:e}=fs(),t=e.filter(r=>r.passed),n=t.length>0?t.reduce((r,l)=>new Date(l.date)>new Date(r.date)?l:r):null;return n?a.jsxs("div",{className:"certificate-container",children:[a.jsx("div",{className:"no-print text-center mb-lg",children:a.jsx("button",{className:"btn btn-gold",onClick:()=>window.print(),children:"Print Certificate"})}),a.jsxs("div",{className:"certificate",children:[a.jsx(pn,{size:100,className:"cert-seal"}),a.jsx("h1",{children:"Paradigm University"}),a.jsx("p",{className:"cert-title",children:"Universitas Paradigmatica — Lux in Codice"}),a.jsx("div",{className:"gold-divider"}),a.jsx("p",{style:{fontSize:"0.875rem",color:"var(--ink-muted)",marginTop:"var(--space-lg)"},children:"This is to certify that"}),a.jsx("div",{className:"cert-name",children:n.name}),a.jsxs("p",{className:"cert-body",children:["has successfully completed the",a.jsx("br",{}),a.jsx("strong",{children:"Paradigm Licensure Standardized Assessment Test"}),a.jsx("br",{}),"and is hereby recognized as a certified Paradigm practitioner."]}),a.jsxs("p",{className:"cert-score",children:["Score: ",n.score,"/",n.total," (",n.percentage,"%)"]}),a.jsx("div",{className:"gold-divider"}),a.jsxs("dl",{className:"cert-meta",children:[a.jsxs("div",{children:[a.jsx("dt",{children:"PLSAT Version"}),a.jsxs("dd",{children:["v",n.plsatVersion]})]}),a.jsxs("div",{children:[a.jsx("dt",{children:"Framework Version"}),a.jsxs("dd",{children:["v",n.frameworkVersion]})]}),a.jsxs("div",{children:[a.jsx("dt",{children:"Date Issued"}),a.jsx("dd",{children:new Date(n.date).toLocaleDateString("en-US",{year:"numeric",month:"long",day:"numeric"})})]})]})]}),e.length>1&&a.jsxs("div",{className:"no-print mt-xl",children:[a.jsx("h3",{className:"mb-md",children:"All Attempts"}),[...e].reverse().map((r,l)=>a.jsx("div",{className:"ref-card",style:{marginBottom:"0.5rem"},children:a.jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center"},children:[a.jsxs("span",{children:["PLSAT v",r.plsatVersion," — ",r.score,"/",r.total," (",r.percentage,"%)",r.passed?" ✓":""]}),a.jsx("span",{className:"text-muted",children:new Date(r.date).toLocaleDateString()})]})},l))]})]}):a.jsxs("div",{className:"certificate-container",children:[a.jsxs("div",{className:"empty-state",children:[a.jsx(pn,{size:80}),a.jsx("h3",{className:"mt-lg",children:"No Certificates Yet"}),a.jsx("p",{children:"Pass the PLSAT examination to earn your Paradigm certification."}),a.jsx(q,{to:"/plsat",className:"btn btn-primary mt-lg",children:"Take the PLSAT"})]}),e.length>0&&a.jsxs("div",{className:"mt-xl",children:[a.jsx("h3",{className:"mb-md",children:"Previous Attempts"}),e.map((r,l)=>a.jsx("div",{className:"ref-card",style:{marginBottom:"0.5rem"},children:a.jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center"},children:[a.jsxs("span",{children:["PLSAT v",r.plsatVersion," — ",r.score,"/",r.total," (",r.percentage,"%)"]}),a.jsx("span",{className:"text-muted",children:new Date(r.date).toLocaleDateString()})]})},l))]})]})}function Mm(){return a.jsxs("div",{className:"app",children:[a.jsx(Zh,{version:"3.10.5"}),a.jsx("main",{className:"main-content",children:a.jsxs(Bh,{children:[a.jsx(We,{path:"/",element:a.jsx(Pm,{})}),a.jsx(We,{path:"/courses",element:a.jsx(_m,{})}),a.jsx(We,{path:"/course/:courseId",element:a.jsx(Au,{})}),a.jsx(We,{path:"/course/:courseId/:lessonId",element:a.jsx(Au,{})}),a.jsx(We,{path:"/course/:courseId/quiz/:lessonId",element:a.jsx(Tm,{})}),a.jsx(We,{path:"/plsat",element:a.jsx(Rm,{})}),a.jsx(We,{path:"/reference",element:a.jsx(Im,{})}),a.jsx(We,{path:"/certificate",element:a.jsx(Om,{})})]})})]})}hi.createRoot(document.getElementById("root")).render(a.jsx(vo.StrictMode,{children:a.jsx(Gh,{children:a.jsx(Mm,{})})}));
|
|
87
|
-
//# sourceMappingURL=index-
|
|
86
|
+
`),n=n.replace(/\x00BLOCK(\d+)\x00/g,(r,l)=>t[Number(l)]),n}function Au(){const{courseId:e,lessonId:t}=uf(),n=sf(),r=Ol(d=>d.loadCourse),[l,i]=S.useState(null),[o,s]=S.useState(null),[u,c]=S.useState(!0),{isLessonCompleted:h,completeLesson:m}=Ml();if(S.useEffect(()=>{e&&(c(!0),r(e).then(d=>{if(i(d),d&&d.lessons.length>0){const f=t?d.lessons.find(p=>p.id===t):null;f?s(f):(s(d.lessons[0]),n(`/course/${e}/${d.lessons[0].id}`,{replace:!0}))}c(!1)}))},[e,t,r,n]),u)return a.jsx("div",{className:"loading",children:"Opening the textbook..."});if(!l)return a.jsxs("div",{className:"empty-state",children:[a.jsx("h3",{children:"Course not found"}),a.jsx("p",{children:"The requested course does not exist."}),a.jsx(q,{to:"/",className:"btn btn-primary mt-lg",children:"Return to Campus"})]});const v=o?l.lessons.findIndex(d=>d.id===o.id):0,x=()=>{o&&e&&m(e,o.id)},y=(d,f=!1)=>{s(d),n(`/course/${e}/${d.id}`),f&&window.scrollTo(0,0)},w=()=>{v<l.lessons.length-1&&y(l.lessons[v+1],!0)},N=()=>{v>0&&y(l.lessons[v-1],!0)};return a.jsxs("div",{className:"course-layout",children:[a.jsxs("aside",{className:"course-sidebar",children:[a.jsx("h2",{children:l.title}),a.jsx("nav",{className:"lesson-nav",children:l.lessons.map(d=>{const f=e?h(e,d.id):!1,p=(o==null?void 0:o.id)===d.id;let g="lesson-nav-item";return p&&(g+=" active"),f&&!p&&(g+=" completed"),a.jsx("button",{className:g,onClick:()=>y(d),children:d.title},d.id)})})]}),a.jsx("div",{className:"course-content",children:o&&a.jsxs(a.Fragment,{children:[a.jsx("h1",{children:o.title}),o.keyConcepts.length>0&&a.jsx("div",{className:"key-concepts",children:o.keyConcepts.map(d=>a.jsx("span",{className:"concept-tag",children:d},d))}),a.jsx("div",{className:"lesson-content",dangerouslySetInnerHTML:{__html:Ut(o.content)}}),a.jsxs("div",{className:"lesson-actions",children:[a.jsx("div",{children:v>0&&a.jsx("button",{className:"btn btn-secondary",onClick:N,children:"Previous"})}),a.jsxs("div",{style:{display:"flex",gap:"0.5rem"},children:[e&&!h(e,o.id)&&a.jsx("button",{className:"btn btn-secondary",onClick:x,children:"Mark Complete"}),o.quiz.length>0&&e&&a.jsx(q,{to:`/course/${e}/quiz/${o.id}`,className:"btn btn-gold",children:"Take Quiz"}),v<l.lessons.length-1&&a.jsx("button",{className:"btn btn-primary",onClick:w,children:"Next Lesson"})]})]})]})})]})}function co({number:e,question:t,scenario:n,choices:r,correct:l,explanation:i,selectedAnswer:o,onSelect:s,showResult:u,onAnswered:c,splitLayout:h}){const[m,v]=S.useState(null),[x,y]=S.useState(!1),w=s!==void 0,N=w?o||null:m,d=w?u:x,f=j=>{d&&!w||(w?s==null||s(j):(v(j),y(!0),c==null||c(j)))},p=N===l,g=Object.keys(r).sort(),E=a.jsxs(a.Fragment,{children:[a.jsxs("div",{className:"question-number",children:["Question ",e]}),n&&a.jsx("div",{className:"scenario",dangerouslySetInnerHTML:{__html:Ut(n)}}),a.jsx("div",{className:"question-text",dangerouslySetInnerHTML:{__html:Ut(t)}})]}),k=a.jsx("div",{className:"choices",children:g.map(j=>{let P="choice-btn";return N===j&&(P+=" selected"),d&&j===l&&(P+=" correct"),d&&N===j&&j!==l&&(P+=" incorrect"),a.jsxs("button",{className:P,onClick:()=>f(j),disabled:d&&!w,children:[a.jsxs("span",{className:"choice-letter",children:[j,"."]}),a.jsx("span",{dangerouslySetInnerHTML:{__html:Ut(r[j])}})]},j)})});return h?a.jsxs("div",{className:"question-card",children:[a.jsxs("div",{className:"question-split-layout",children:[a.jsx("div",{className:"question-content",children:E}),a.jsx("div",{className:"answer-choices",children:k})]}),d&&a.jsxs("div",{className:`explanation ${p?"":"wrong"}`,children:[a.jsx("strong",{children:p?"Correct!":`Incorrect. The answer is ${l}.`}),a.jsx("br",{}),a.jsx("span",{dangerouslySetInnerHTML:{__html:Ut(i)}})]})]}):a.jsxs("div",{className:"question-card",children:[E,k,d&&a.jsxs("div",{className:`explanation ${p?"":"wrong"}`,children:[a.jsx("strong",{children:p?"Correct!":`Incorrect. The answer is ${l}.`}),a.jsx("br",{}),a.jsx("span",{dangerouslySetInnerHTML:{__html:Ut(i)}})]})]})}function Tm(){const{courseId:e,lessonId:t}=uf(),n=Ol(g=>g.loadCourse),{recordQuiz:r,completeLesson:l,getCourseProgress:i}=Ml(),[o,s]=S.useState(null),[u,c]=S.useState(null),[h,m]=S.useState(!0),[v,x]=S.useState(!1),[y,w]=S.useState(0),N=e&&t?i(e).quizResults[t]:void 0;S.useEffect(()=>{e&&(m(!0),n(e).then(g=>{if(g&&t){const E=g.lessons.findIndex(k=>k.id===t);s(E>=0?g.lessons[E]:null),E>=0&&E<g.lessons.length-1&&c(g.lessons[E+1].id)}m(!1)}))},[e,t,n]);const[d,f]=S.useState({}),p=(g,E)=>{if(f(k=>({...k,[g]:E})),o){const k={...d,[g]:E};if(Object.keys(k).length===o.quiz.length){const j=o.quiz.filter(P=>k[P.id]===P.correct).length;if(w(j),x(!0),e&&t){const P={courseId:e,lessonId:t,score:j,total:o.quiz.length,answers:k,date:new Date().toISOString()};r(P),l(e,t)}}}};return h?a.jsx("div",{className:"loading",children:"Preparing your examination..."}):!o||o.quiz.length===0?a.jsxs("div",{className:"empty-state",children:[a.jsx("h3",{children:"No quiz available"}),a.jsx("p",{children:"This lesson does not have a quiz."}),a.jsx(q,{to:`/course/${e}`,className:"btn btn-primary mt-lg",children:"Back to Course"})]}):a.jsxs("div",{className:"quiz-container",children:[a.jsxs("div",{className:"quiz-header",children:[a.jsxs("h1",{children:[o.title," — Quiz"]}),a.jsx("p",{className:"quiz-progress",children:v?`Score: ${y}/${o.quiz.length} (${Math.round(y/o.quiz.length*100)}%)`:`${Object.keys(d).length}/${o.quiz.length} answered`}),N&&!v&&a.jsxs("p",{className:"text-muted mt-sm",children:["Previous best: ",N.score,"/",N.total]})]}),o.quiz.map((g,E)=>a.jsx(co,{number:E+1,question:g.question,choices:g.choices,correct:g.correct,explanation:g.explanation,onAnswered:k=>p(g.id,k)},g.id)),v&&a.jsxs("div",{className:"text-center mt-xl",children:[a.jsx("p",{className:"mb-lg",style:{fontSize:"1.25rem",fontFamily:"var(--font-serif)"},children:y===o.quiz.length?"Perfect score! Exemplary scholarship.":y>=o.quiz.length*.8?"Well done, scholar. You have demonstrated understanding.":"Review the material and try again. Persistence is the path to mastery."}),u?a.jsx(q,{to:`/course/${e}/${u}`,className:"btn btn-primary",children:"Next Lesson"}):a.jsx(q,{to:`/course/${e}`,className:"btn btn-primary",children:"Return to Course"})]})]})}function zm({totalSeconds:e,onTimeUp:t,running:n}){const[r,l]=S.useState(e),i=S.useCallback(()=>{t()},[t]);S.useEffect(()=>{if(!n)return;const h=setInterval(()=>{l(m=>m<=1?(clearInterval(h),i(),0):m-1)},1e3);return()=>clearInterval(h)},[n,i]);const o=Math.floor(r/60),s=r%60,u=r/e*100;let c="timer-display";return u<20?c+=" critical":u<40&&(c+=" warning"),a.jsxs("span",{className:c,children:[String(o).padStart(2,"0"),":",String(s).padStart(2,"0")]})}function Fu({text:e}){const t=e.split(/(```[\s\S]*?```)/g);return a.jsx("div",{className:"passage-block",children:a.jsx("div",{className:"passage-content",children:t.map((n,r)=>{if(n.startsWith("```")){const l=n.match(/^```(\w*)\n?([\s\S]*?)```$/),i=l?l[2]:n.slice(3,-3);return a.jsx("pre",{children:a.jsx("code",{children:i})},r)}return n.split(/\n\n+/).map((l,i)=>a.jsx("p",{children:l},`${r}-${i}`))})})})}function $u(e,t,n){if(!t)return null;const r=e[n];return r.passageId?t[r.passageId]??null:null}function Rm(){const[e,t]=S.useState(null),[n,r]=S.useState("intro"),[l,i]=S.useState({}),[o,s]=S.useState(0),[u,c]=S.useState(!0),[h,m]=S.useState(null),{studentName:v,setStudentName:x,addCertificate:y}=fs(),[w,N]=S.useState(v);S.useEffect(()=>{fetch("/api/plsat/3.0").then(k=>k.json()).then(k=>{t(k),c(!1)}).catch(()=>c(!1))},[]);const d=S.useCallback(()=>{if(!e)return;const k=e.questions.filter(de=>l[de.id]===de.correct).length,j=e.questions.length,P=Math.round(k/j*100),M=P>=e.passThreshold*100,z={name:w||"Anonymous Scholar",score:k,total:j,percentage:P,passed:M,plsatVersion:e.version,frameworkVersion:e.frameworkVersion,date:new Date().toISOString()};m(z),y(z),w&&x(w),r("results")},[e,l,w,y,x]),f=S.useCallback(()=>{d()},[d]),p=()=>{d()},g=()=>{i({}),s(0),r("exam")},E=S.useMemo(()=>!e||n!=="exam"?null:$u(e.questions,e.passages,o),[e,n,o]);if(u)return a.jsx("div",{className:"loading",children:"The examination board is convening..."});if(!e)return a.jsxs("div",{className:"empty-state",children:[a.jsx("h3",{children:"PLSAT Unavailable"}),a.jsx("p",{children:"Could not load the examination. Please try again."})]});if(n==="intro")return a.jsxs("div",{className:"plsat-container",children:[a.jsxs("div",{className:"plsat-intro",children:[a.jsx(pn,{size:100}),a.jsx("h1",{children:"The PLSAT"}),a.jsx("p",{className:"plsat-subtitle",children:"Paradigm Licensure Standardized Assessment Test"}),a.jsxs("p",{className:"text-muted",children:["Version ",e.version]})]}),a.jsxs("div",{className:"plsat-rules",children:[a.jsx("h3",{children:"Examination Rules"}),a.jsxs("ul",{children:[a.jsxs("li",{children:[a.jsxs("strong",{children:[e.questions.length," questions"]})," covering all aspects of the Paradigm framework"]}),a.jsxs("li",{children:[a.jsxs("strong",{children:[Math.floor(e.timeLimit/60)," minutes"]})," to complete the examination"]}),a.jsxs("li",{children:[a.jsxs("strong",{children:[e.passThreshold*100,"%"]})," required to pass and receive certification"]}),a.jsx("li",{children:"All questions are multiple choice (A through E)"}),a.jsx("li",{children:"Some questions reference a shared passage — read it carefully"}),a.jsx("li",{children:"You may navigate between questions freely"}),a.jsx("li",{children:"There is no penalty for guessing — answer every question"}),a.jsx("li",{children:"Your certificate will display the PLSAT version for posterity"})]})]}),a.jsxs("div",{className:"text-center",children:[a.jsx("div",{className:"mb-lg",children:a.jsx("input",{type:"text",className:"name-input",placeholder:"Enter your name, scholar",value:w,onChange:k=>N(k.target.value)})}),a.jsx("button",{className:"btn btn-primary btn-lg",onClick:g,children:"Begin Examination"})]})]});if(n==="exam"){const k=e.questions[o],j=Object.keys(l).length;return a.jsxs("div",{className:"plsat-container",children:[a.jsxs("div",{className:"plsat-timer",children:[a.jsx(zm,{totalSeconds:e.timeLimit,onTimeUp:f,running:!0}),a.jsxs("span",{className:"plsat-progress-text",children:["Question ",o+1," of ",e.questions.length," | ",j," answered"]})]}),a.jsxs("div",{style:{marginTop:"var(--space-lg)"},children:[E&&a.jsx(Fu,{text:E}),a.jsx(co,{number:o+1,question:k.question,scenario:k.scenario,choices:k.choices,correct:k.correct,explanation:k.explanation,selectedAnswer:l[k.id],onSelect:P=>i(M=>({...M,[k.id]:P})),showResult:!1,splitLayout:!0})]}),a.jsxs("div",{style:{display:"flex",justifyContent:"space-between",marginTop:"var(--space-lg)"},children:[a.jsx("button",{className:"btn btn-secondary",disabled:o===0,onClick:()=>s(P=>P-1),children:"Previous"}),a.jsxs("div",{style:{display:"flex",gap:"0.5rem"},children:[j===e.questions.length&&a.jsx("button",{className:"btn btn-gold",onClick:p,children:"Submit Examination"}),a.jsx("button",{className:"btn btn-primary",disabled:o===e.questions.length-1,onClick:()=>s(P=>P+1),children:"Next"})]})]}),a.jsx("div",{style:{display:"flex",flexWrap:"wrap",gap:"4px",marginTop:"var(--space-xl)",justifyContent:"center"},children:e.questions.map((P,M)=>a.jsx("button",{onClick:()=>s(M),style:{width:28,height:28,borderRadius:"50%",border:M===o?"2px solid var(--burgundy)":"1px solid var(--parchment-dark)",background:l[P.id]?"var(--gold-bg)":"var(--cream)",color:"var(--ink)",fontSize:"0.6875rem",cursor:"pointer",fontWeight:M===o?600:400},children:M+1},P.id))})]})}return n==="results"&&h?a.jsx("div",{className:"plsat-container",children:a.jsxs("div",{className:"plsat-results",children:[a.jsx(pn,{size:80}),a.jsxs("div",{className:`score-display ${h.passed?"passed":"failed"}`,children:[h.percentage,"%"]}),a.jsx("p",{className:"verdict",children:h.passed?"Congratulations! You have passed the PLSAT.":"The examination board regrets to inform you that you did not pass."}),a.jsxs("p",{className:"text-muted mb-lg",children:["Score: ",h.score,"/",h.total," | PLSAT v",h.plsatVersion," | ",new Date(h.date).toLocaleDateString()]}),a.jsxs("div",{style:{display:"flex",gap:"1rem",justifyContent:"center"},children:[h.passed&&a.jsx(q,{to:"/certificate",className:"btn btn-gold btn-lg",children:"View Certificate"}),a.jsx("button",{className:"btn btn-secondary",onClick:()=>r("review"),children:"Review Answers"}),a.jsx("button",{className:"btn btn-primary",onClick:()=>r("intro"),children:h.passed?"Retake":"Try Again"})]})]})}):n==="review"?a.jsxs("div",{className:"plsat-container",children:[a.jsxs("div",{className:"quiz-header",children:[a.jsx("h1",{children:"PLSAT Review"}),a.jsx("p",{className:"quiz-progress",children:h?`Score: ${h.score}/${h.total} (${h.percentage}%)`:""})]}),e.questions.map((k,j)=>{const P=$u(e.questions,e.passages,j);return a.jsxs("div",{children:[P&&a.jsx(Fu,{text:P}),a.jsx(co,{number:j+1,question:k.question,scenario:k.scenario,choices:k.choices,correct:k.correct,explanation:k.explanation,selectedAnswer:l[k.id],onSelect:()=>{},showResult:!0,splitLayout:!0})]},k.id)}),a.jsx("div",{className:"text-center mt-xl",children:a.jsx("button",{className:"btn btn-primary",onClick:()=>r("results"),children:"Back to Results"})})]}):null}function Im(){const[e,t]=S.useState(null),[n,r]=S.useState(!0);return S.useEffect(()=>{fetch("/api/reference").then(l=>l.json()).then(l=>{t(l),r(!1)}).catch(()=>r(!1))},[]),n?a.jsx("div",{className:"loading",children:"Opening the reference library..."}):e?a.jsxs("div",{className:"reference-container",children:[a.jsx("h1",{className:"mb-lg",children:"Reference Library"}),e.sections.map(l=>a.jsxs("section",{className:"reference-section",children:[a.jsx("h2",{children:l.title}),a.jsx("div",{className:"reference-grid",children:l.cards.map(i=>a.jsxs("div",{className:"ref-card",children:[i.symbol&&a.jsx("div",{className:"ref-symbol",children:i.symbol}),a.jsx("h4",{children:i.name}),a.jsx("p",{children:i.description}),i.examples&&i.examples.length>0&&a.jsx("div",{className:"ref-examples",children:i.examples.map(o=>a.jsx("span",{className:"ref-example",children:o},o))}),i.logger&&a.jsx("p",{style:{marginTop:"0.5rem"},children:a.jsx("code",{children:i.logger})}),i.when&&a.jsx("p",{className:"text-muted",style:{fontSize:"0.8125rem",marginTop:"0.25rem"},children:a.jsx("em",{children:i.when})}),i.command&&a.jsx("p",{style:{marginTop:"0.5rem"},children:a.jsx("code",{children:i.command})}),i.steps&&i.steps.length>0&&a.jsx("ol",{style:{fontSize:"0.875rem",paddingLeft:"1.25rem",marginTop:"0.5rem"},children:i.steps.map((o,s)=>a.jsx("li",{style:{marginBottom:"0.25rem"},children:o},s))})]},i.id))})]},l.id))]}):a.jsxs("div",{className:"empty-state",children:[a.jsx("h3",{children:"Reference library unavailable"}),a.jsx("p",{children:"Could not load reference data."})]})}function Om(){const{certificates:e}=fs(),t=e.filter(r=>r.passed),n=t.length>0?t.reduce((r,l)=>new Date(l.date)>new Date(r.date)?l:r):null;return n?a.jsxs("div",{className:"certificate-container",children:[a.jsx("div",{className:"no-print text-center mb-lg",children:a.jsx("button",{className:"btn btn-gold",onClick:()=>window.print(),children:"Print Certificate"})}),a.jsxs("div",{className:"certificate",children:[a.jsx(pn,{size:100,className:"cert-seal"}),a.jsx("h1",{children:"Paradigm University"}),a.jsx("p",{className:"cert-title",children:"Universitas Paradigmatica — Lux in Codice"}),a.jsx("div",{className:"gold-divider"}),a.jsx("p",{style:{fontSize:"0.875rem",color:"var(--ink-muted)",marginTop:"var(--space-lg)"},children:"This is to certify that"}),a.jsx("div",{className:"cert-name",children:n.name}),a.jsxs("p",{className:"cert-body",children:["has successfully completed the",a.jsx("br",{}),a.jsx("strong",{children:"Paradigm Licensure Standardized Assessment Test"}),a.jsx("br",{}),"and is hereby recognized as a certified Paradigm practitioner."]}),a.jsxs("p",{className:"cert-score",children:["Score: ",n.score,"/",n.total," (",n.percentage,"%)"]}),a.jsx("div",{className:"gold-divider"}),a.jsxs("dl",{className:"cert-meta",children:[a.jsxs("div",{children:[a.jsx("dt",{children:"PLSAT Version"}),a.jsxs("dd",{children:["v",n.plsatVersion]})]}),a.jsxs("div",{children:[a.jsx("dt",{children:"Framework Version"}),a.jsxs("dd",{children:["v",n.frameworkVersion]})]}),a.jsxs("div",{children:[a.jsx("dt",{children:"Date Issued"}),a.jsx("dd",{children:new Date(n.date).toLocaleDateString("en-US",{year:"numeric",month:"long",day:"numeric"})})]})]})]}),e.length>1&&a.jsxs("div",{className:"no-print mt-xl",children:[a.jsx("h3",{className:"mb-md",children:"All Attempts"}),[...e].reverse().map((r,l)=>a.jsx("div",{className:"ref-card",style:{marginBottom:"0.5rem"},children:a.jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center"},children:[a.jsxs("span",{children:["PLSAT v",r.plsatVersion," — ",r.score,"/",r.total," (",r.percentage,"%)",r.passed?" ✓":""]}),a.jsx("span",{className:"text-muted",children:new Date(r.date).toLocaleDateString()})]})},l))]})]}):a.jsxs("div",{className:"certificate-container",children:[a.jsxs("div",{className:"empty-state",children:[a.jsx(pn,{size:80}),a.jsx("h3",{className:"mt-lg",children:"No Certificates Yet"}),a.jsx("p",{children:"Pass the PLSAT examination to earn your Paradigm certification."}),a.jsx(q,{to:"/plsat",className:"btn btn-primary mt-lg",children:"Take the PLSAT"})]}),e.length>0&&a.jsxs("div",{className:"mt-xl",children:[a.jsx("h3",{className:"mb-md",children:"Previous Attempts"}),e.map((r,l)=>a.jsx("div",{className:"ref-card",style:{marginBottom:"0.5rem"},children:a.jsxs("div",{style:{display:"flex",justifyContent:"space-between",alignItems:"center"},children:[a.jsxs("span",{children:["PLSAT v",r.plsatVersion," — ",r.score,"/",r.total," (",r.percentage,"%)"]}),a.jsx("span",{className:"text-muted",children:new Date(r.date).toLocaleDateString()})]})},l))]})]})}function Mm(){return a.jsxs("div",{className:"app",children:[a.jsx(Zh,{version:"3.10.6"}),a.jsx("main",{className:"main-content",children:a.jsxs(Bh,{children:[a.jsx(We,{path:"/",element:a.jsx(Pm,{})}),a.jsx(We,{path:"/courses",element:a.jsx(_m,{})}),a.jsx(We,{path:"/course/:courseId",element:a.jsx(Au,{})}),a.jsx(We,{path:"/course/:courseId/:lessonId",element:a.jsx(Au,{})}),a.jsx(We,{path:"/course/:courseId/quiz/:lessonId",element:a.jsx(Tm,{})}),a.jsx(We,{path:"/plsat",element:a.jsx(Rm,{})}),a.jsx(We,{path:"/reference",element:a.jsx(Im,{})}),a.jsx(We,{path:"/certificate",element:a.jsx(Om,{})})]})})]})}hi.createRoot(document.getElementById("root")).render(a.jsx(vo.StrictMode,{children:a.jsx(Gh,{children:a.jsx(Mm,{})})}));
|
|
87
|
+
//# sourceMappingURL=index-TcsCEBMo.js.map
|