diff --git a/CHANGELOG.md b/CHANGELOG.md index 0769724..519b4ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## Documentation update — 2026-09-06 + +- Add four actual-app screenshots with labelled demo data, capture details and exact-byte publication checks. +- Replace the README overview with a feature table and clarify local tutoring, storage, pack revisions and release limits. + ## 0.2.0 — 2026-09-06 - Add an optional engineering pack: 31 lessons, six tracks and 96 exercise/material files. The original starter pack and its record identity stay unchanged. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6dbe012..111b3f1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -11,6 +11,8 @@ Contribute a bounded improvement that helps a learner begin, understand, practis 5. Check changed flows at desktop and narrow widths, with keyboard and reduced motion. Use `?qa` so test records stay separate. 6. Review files, Git history and the static asset list. Use the GitHub noreply address shown in your account's email settings. Keep secrets, personal exports, local settings, logs and screenshots of real records out of commits. +For README images, follow the [screenshot capture and review procedure](docs/screenshots/README.md). Only reviewed demo captures belong in documentation. + Keep the three-lesson starter stable unless a deliberate content revision is intended. New engineering material belongs in its own pack revision. A pack fingerprint selects a separate notebook; document that consequence instead of silently migrating progress. AI connection changes need explicit local opt-in, a provider with no executable tools, no browser credentials, bounded requests, failure recovery and independent review. Do not add a public proxy or shared API key as a convenience. GitHub Actions uses synthetic tests and must never require a contributor's Codex auth. diff --git a/README.md b/README.md index 1e50898..faf1487 100644 --- a/README.md +++ b/README.md @@ -1,51 +1,73 @@ # Padipps -**Make time for understanding.** A local-first study notebook, deliberate practice loop and Pomodoro bookshelf. Bring a study pack, work through one small model, explain it in your own words, make something, and keep a record of what happened. +Padipps is a study app for practising concepts, keeping lesson notes and running Pomodoro sessions. Choose a lesson, explain the idea in your own words, do the independent exercise, and record what needs another attempt. -Padipps is a playful name inspired by colloquial Tamil around studying. It is a small, extensible web app, with no Padipps account, database, analytics or shared AI key. Optional local tutoring uses each person's own Codex sign-in. +Your work stays in your browser. Optional tutoring connects to your own Codex account on your computer. The name is inspired by colloquial Tamil around studying. -[Open Padipps in your browser](https://siddath.github.io/padipps/) · [Pack format](PACKS.md) +[Try Padipps](https://siddath.github.io/padipps/) · [Study tracks](docs/STUDY_TRACKS.md) · [Create a pack](PACKS.md) · [Codex setup](docs/CODEX.md) -## Run +![Padipps practice library in the Ink theme, with 31 lessons and six track filters](docs/screenshots/practice.jpg) -Requires Node.js 20 or newer. There are no package-install or build dependencies. +*The running app with the engineering pack selected. Screenshots use an isolated demo session; no personal study records or account details appear. [Capture details](docs/screenshots/README.md).* -```sh -git clone https://github.com/siddath/padipps.git -cd padipps -npm start -``` +## Features + +| Feature | What you can do | +| --- | --- | +| Guided practice | Recognise the problem, work through a small model and trace, explain it back, then attempt an independent task. Record an outcome, evidence and a review date. | +| Study library | Start with three lessons or select the optional engineering pack: 31 lessons across six overlapping tracks, with 96 exercise/material files. Filter by track and search within it. | +| Notebook | Link notes to lessons, search them, mark them Open, Revisit or Resolved, and inspect up to 20 previous versions. Drafts save as you type. | +| Focus timer | Configure focus, short-break and long-break intervals. Pause, resume or reload without losing the timer. Start each interval yourself. | +| Study shelf | Reveal a book after a finished focus timer. Choose philosophy, art or spirituality ideas with source links and reflection questions, or turn ideas off. | +| Appearance | Choose Ink, Sand, Midnight, Violet, Rose or Graphite; collapse the sidebar; use GSAP transitions with system or manual reduced motion. | +| Lesson conversations | Use Explain, Quiz me or Check my understanding. Each lesson has saved messages and drafts, Stop/Retry, and Markdown/JSON conversation archives. In-app answers require the optional local Codex connection. | +| Portable study packs | Preview and import data-only JSON packs for your own subjects. Each pack revision keeps its own notebook, focus and conversation history. | +| Backups | Export notebook Markdown for an editor or Obsidian, restore notebook JSON, and export/restore focus records through their separate backup controls. | + +Practice outcomes are self-reported. A completed timer records elapsed time; it does not establish attention or understanding. The app does not run learner code or grade mastery. Book ideas are editorial interpretations, with sources, rather than quotations. + +
+More screenshots: notebook, focus timer and book reveal + +**Notebook, Violet theme.** A sample note marked Revisit, with zero practice attempts recorded. + +![Padipps notebook with a labelled demo note, Revisit status and Markdown export](docs/screenshots/notebook.jpg) -Open http://127.0.0.1:4177. To use another port: `PORT=4178 npm start`. +**Focus room, Sand theme.** A short break ready to start after a five-second QA timer. -For a static host, run `npm run build` and serve `dist/` over HTTP(S). The build copies an explicit public asset list and excludes the local chat backend. Hash routes work without rewrite rules. This app does not yet have a service worker: a first load and reload require the local server or static host to be reachable. Ordinary practice, notes and focus need no model or remote API. The hosted demo cannot connect to a contributor's Codex or to your computer. +![Padipps focus room showing a five-minute short break and book illustration](docs/screenshots/focus.jpg) -## What it does +**Study shelf, Sand theme.** The QA book opens a reflection on Epictetus with a source link. This is a test reward, not a learner achievement. -- **Practice:** recognition → small model and trace → explain back → independent work → evidence and a review date. Track filters come from the active pack. -- **Notebook:** session-linked notes, Open / Revisit / Resolved status, up to 20 previous versions, Markdown export and validated JSON restore. -- **Focus room:** configurable focus, short break and long break intervals. Pause and reload preserve your timer. A running countdown continues across page changes and reloads. Every interval starts explicitly. -- **Study shelf:** completed focus timers reveal books. Each new book can carry an idea about philosophy, art or spirituality, with a primary-text or museum source and a question for your break. Choose your interests or turn ideas off. Ideas are editorial interpretations, not quotations. -- **Appearance:** Ink, Sand, Midnight, Violet, Rose and Graphite; a collapsible sidebar; GSAP feedback and transitions with system and manual reduced-motion support. -- **Study conversation:** Explain, Quiz me and Check my understanding, with separate lesson threads, saved drafts, stop/retry and archival exports. Optional local Codex answers use only the selected lesson context and recent messages. A copyable prompt remains available without a connection. +![Padipps study shelf showing a five-second QA book and a source-linked reflection](docs/screenshots/shelf.jpg) -A book records elapsed timer time. It cannot verify attention or understanding. Practice outcomes are self-reported; reviewer feedback is an additional record, not an automatic verification. The app does not run learner code or grade mastery. +
-## Bring your own subjects +## Run locally -Open **Study packs → Preview engineering pack → Use this pack** for 31 original lessons across DSA, backend engineering, distributed systems, AI application engineering, FDE/customer delivery and product design. The [track guide](docs/STUDY_TRACKS.md) links the [independent exercise materials](materials/engineering/), including a fictional customer-delivery capstone. No prior experience, completed work or personal curriculum is assumed. +Requires Node.js 20 or newer. The study app needs no package installation. -The original three-lesson starter remains available and unchanged. Pack selection is explicit: your starter notebook is not silently migrated or reset. To create another subject, download the current pack as a template, edit its JSON and import it. Preview before choosing **Use this pack**. Import itself stays in the browser; pressing Send in a connected tutor shares only the previewed lesson context and recent chat messages with the selected person's Codex account. +```sh +git clone https://github.com/siddath/padipps.git +cd padipps +npm start +``` -See [PACKS.md](PACKS.md) for the data contract. A pack can teach another discipline: change the tracks, prompts, traces, tasks, rubrics and references. Pack files contain data only. HTML is displayed as text and source links are restricted to HTTP(S). Do not treat imported teaching material as verified merely because it passes structural validation. +Open http://127.0.0.1:4177. Use `PORT=4178 npm start` if that port is busy. Chat is disabled by default. -Each pack ID and content revision has its own notebook and focus history. Backups include a SHA-256 fingerprint of the teaching pack and can only be restored against that exact revision. Even editorial edits create a new content fingerprint; retain the original pack to reopen its history. Use a new ID when changing lesson identities, gates or meaning. Switching packs or revisions does not merge learner records. Keep copies of imported pack files so you can reopen their associated notebooks later. +The [hosted app](https://siddath.github.io/padipps/) supports practice, notes, focus and a copyable tutor prompt. It has no chat backend and cannot connect to your computer. To host your own copy, run `npm run build` and serve `dist/` over HTTP(S). Hash routes need no rewrite rules. There is no service worker; loading or reloading requires a reachable server. -Place unpublished curricula in `private-packs/` or `local-packs/`, both ignored by Git. Backups belong in ignored `backups/`. Imported material stays in your browser unless you explicitly send selected context to the optional tutor, export it or commit a file yourself. The static build never includes those folders. +## Choose your study material + +Open **Study packs → Preview engineering pack → Use this pack**. The included tracks cover DSA, backend engineering, distributed systems, AI application engineering, forward deployed engineering/customer delivery and product design. Some lessons belong to more than one track. See the [track guide](docs/STUDY_TRACKS.md) for counts, prerequisites and the fictional customer-delivery capstone, and [exercise materials](materials/engineering/) for unsolved starters. + +The original three-lesson starter stays available. Switching packs preserves its records. To create another subject, download the current pack as a template, edit its JSON, then import and preview it before choosing **Use this pack**. [PACKS.md](PACKS.md) documents lesson fields, validation and extension rules. + +Keep copies of the exact pack files you use. Backups belong to a pack ID and SHA-256 content fingerprint; even an editorial change creates a separate revision. Switching revisions does not merge histories. Import validation checks structure and safe links, not teaching accuracy. ## Connect your own Codex -Chat is off by default. There is no shared account, hosted model proxy or API key bundled with Padipps. On your own computer, install and sign in to the supported Codex CLI, then explicitly enable the local adapter: +On your computer, install the supported CLI and sign in, then enable the local adapter: ```sh npm install -g @openai/codex@0.147.0 @@ -53,25 +75,25 @@ codex login npm run chat ``` -Open the local URL printed in the terminal and choose **Ask Padipps Study**. Your existing Codex account supplies the model connection and its usage limits. Never enter credentials into a pack, notebook, GitHub issue or this browser interface. See [Codex setup and limitations](docs/CODEX.md). +Open the printed local URL and choose **Ask Padipps Study**. Review the lesson context before sending. Your account supplies the connection and usage limits; Padipps includes no shared account or API key. Never enter credentials into the browser interface, a pack or an issue. -The adapter currently accepts only CLI **0.147.0** and rechecks before each turn. It runs in an isolated temporary home/workspace, with executable tools, project instructions, memories and connectors disabled. A different version becomes unavailable until revalidated. macOS is live-verified; Linux has automated boundary checks but no live-model verification in this release. Windows local chat is disabled. Normal study features and the manual copyable prompt remain available on static hosts and other platforms. +The adapter accepts CLI **0.147.0** and uses **GPT-5.6 Sol**. It isolates startup context and disables executable tools, global instructions, memories and connectors. macOS has live-model verification; Linux has synthetic boundary tests but no live-model verification in this release. Windows local chat is disabled. A separate supported CLI installation can avoid changing the version used by another project. See [setup, data flow and troubleshooting](docs/CODEX.md). -This is a local text conversation, not a synchronized ChatGPT or Codex app task. It cannot grade mastery, complete an exercise, inspect your editor or change learning progress. If you use help during practice, record that honestly as assisted. +This conversation is local to Padipps; it does not sync with ChatGPT or Codex app tasks. Model answers can be wrong and do not update practice progress. Record assisted work as assisted. ## Keep your work -Browser storage is local to an origin and unencrypted. Clearing browser data, changing host/port, or using another browser does not transfer your work. Other scripts on the same origin or someone with access to your browser profile can access its storage. Host on an origin you trust. +Browser storage is unencrypted and belongs to the current host, port and browser profile. Changing any of these does not transfer your records. Clearing browser data can remove them. Use an origin and browser profile you trust; there is no cloud sync or Padipps account. -Use **Backup** for notebook JSON and Markdown. The **Focus room → Focus record & backup** has a separate focus JSON export. Notebook imports are validated and merged by stable ID, preserving current conflicting entries. Export both versions before reconciling conflicting edits. The app preserves unreadable originals for recovery and reports failed saves. There is no cloud sync. +Use **Backup** for notebook JSON and Markdown, and **Focus room → Focus record & backup** for focus JSON. Notebook restore preserves conflicting current entries. Export both versions before reconciling conflicting edits. JSON restores app state; Markdown fits a folder or Obsidian vault. -Chat keeps the latest 80 messages per lesson and sends at most the latest 12 whole messages within its context limit. Its Markdown and JSON exports are separate, explicitly nonrestorable archives. A conversation is never learning evidence by itself. [Privacy and data flow](PRIVACY.md) explains storage, optional model egress and publication exclusions. +Chat retains the latest 80 messages per lesson and sends at most 12 recent whole messages within its context limit. Conversation exports are separate archives with no restore path. Only the selected lesson context and recent messages go to the optional tutor when you press Send. Other notes and files are not attached. -For a long-lived personal notebook, the Markdown export fits an ordinary folder or Obsidian vault. JSON is the round-trip backup that restores app state. +Keep private packs in the Git-ignored `private-packs/` or `local-packs/` folders and exports in `backups/`. The static build excludes those folders and the chat backend. Read [privacy and data flow](PRIVACY.md) before importing sensitive material or enabling tutoring. -## Extend and verify +## Contribute -Vanilla browser ES modules and Node’s built-in test runner keep the code small. The pure study, focus, pack and reflection modules are also available through the package’s `./study`, `./focus`, `./packs` and `./reflections` exports. `private: true` prevents accidental npm publication; it does not restrict use of this public repository. +The app uses browser ES modules, vendored GSAP and Node's built-in test runner. The package exports the pure study, focus, pack and reflection modules through `./study`, `./focus`, `./packs` and `./reflections`. Its `private: true` setting prevents accidental npm publication; the repository is public and MIT-licensed. ```sh npm run check @@ -80,16 +102,12 @@ npm run audit:publication -- --history npm run build ``` -Run a disposable UI sandbox with `?qa#today`. QA uses separate browser keys and exposes a five-second focus interval. Never use synthetic QA entries as learning evidence. - -Useful next contributions: keyboard-tested pack authoring, accessible sound options, and a user-controlled sync adapter. Add features only when they help someone start a block, learn from it, or return to a gap. - -Read [contribution guidance](CONTRIBUTING.md), [architecture](docs/ARCHITECTURE.md), [testing](docs/TESTING.md) and [security reporting](SECURITY.md). GitHub Actions tests the repository and publishes only `dist/` from the default branch; no contributor credential is used by that workflow. +Use a disposable port and `?qa#today` for interface checks. QA has separate record storage and a five-second timer; its entries are test data. GitHub Actions runs the checks and publishes the static asset allowlist from the default branch without model credentials. -## Contact +Read [contribution guidance](CONTRIBUTING.md), [architecture](docs/ARCHITECTURE.md), [testing](docs/TESTING.md) and [security reporting](SECURITY.md). Pack authoring tools, accessible sound controls and opt-in sync are possible contributions, not features in this release. -For general questions or collaboration, contact [Siddath](mailto:siddath.raghavan@gmail.com). This address is intentionally public. Report vulnerabilities through the private channel in [SECURITY.md](SECURITY.md). +## Contact and license -## License +Contact [Siddath](mailto:siddath.raghavan@gmail.com) for general questions or collaboration. This address is intentionally public. Use the private channel in [SECURITY.md](SECURITY.md) for vulnerabilities. -Padipps code and original educational writing use the [MIT license](LICENSE). GSAP is distributed under its separate Standard License. See [third-party notices](THIRD_PARTY_LICENSES.md) for animation, generated artwork and linked source provenance. +Code and original educational writing use the [MIT license](LICENSE). GSAP has its own Standard License. [Third-party notices](THIRD_PARTY_LICENSES.md) cover animation, generated artwork and linked sources. diff --git a/docs/screenshots/README.md b/docs/screenshots/README.md new file mode 100644 index 0000000..ed67704 --- /dev/null +++ b/docs/screenshots/README.md @@ -0,0 +1,20 @@ +# Application screenshots + +These are unedited browser captures of Padipps 0.2.0, app revision `5b45173988a447e082a812a02e7a0257af0d47fa`, taken on 6 September 2026. They show the public application running on a separate local port with chat disabled. The browser's default viewport was 1280 × 720; no device emulation or screenshot compositing was used. + +| Image | Visible state | +| --- | --- | +| [Practice library](practice.jpg) | Engineering pack selected; 31 lessons, six overlapping track filters; Ink theme. | +| [Notebook](notebook.jpg) | One sample note titled “Demo note: plan the next study block”, marked Revisit; zero recorded attempts; Violet theme. | +| [Focus room](focus.jpg) | Short break ready to start following a five-second QA timer; Sand theme. | +| [Study shelf](shelf.jpg) | One book labelled “QA timer check”, its five-second duration and a source-linked editorial reflection; Sand theme. | + +The notebook entry and timer receipt are synthetic demonstration data created through the interface in a `?qa` session. They are not personal notes, study evidence or claims of learning. No model conversation, account details, private connections or credentials were used in these captures. The book illustration is part of the shipped app; its provenance is in [third-party notices](../../THIRD_PARTY_LICENSES.md). + +## Updating the images + +Use the public app on a disposable port with `?qa`. Interact through the visible interface and label sample records. Capture the page itself, excluding desktop windows, account menus and browser chrome. Keep screenshots faithful to the shipped UI; do not generate a mockup or fill in model answers. + +Before committing, inspect every image for private text and check metadata. These JPEGs contain a standard JFIF header and image segments, with no EXIF, XMP, ICC or comment segments. Their exact SHA-256 hashes are recorded in [reviewed-images.json](reviewed-images.json). The publication guard admits only matching bytes at the recorded screenshot paths. A changed image needs another manual review and a new hash; retain reviewed older hashes when they remain in Git history. Hash approval records the review, but cannot inspect pixels or prove privacy by itself. + +Run the publication audit and verify that the images render in GitHub's README. Screenshots are repository documentation and are excluded from the static app build. diff --git a/docs/screenshots/focus.jpg b/docs/screenshots/focus.jpg new file mode 100644 index 0000000..f53ca0f Binary files /dev/null and b/docs/screenshots/focus.jpg differ diff --git a/docs/screenshots/notebook.jpg b/docs/screenshots/notebook.jpg new file mode 100644 index 0000000..77681e1 Binary files /dev/null and b/docs/screenshots/notebook.jpg differ diff --git a/docs/screenshots/practice.jpg b/docs/screenshots/practice.jpg new file mode 100644 index 0000000..29abfa8 Binary files /dev/null and b/docs/screenshots/practice.jpg differ diff --git a/docs/screenshots/reviewed-images.json b/docs/screenshots/reviewed-images.json new file mode 100644 index 0000000..102bdc6 --- /dev/null +++ b/docs/screenshots/reviewed-images.json @@ -0,0 +1,14 @@ +{ + "docs/screenshots/focus.jpg": [ + "6a16d0e11e209864295206198531240d6af1b709f402a1189a51aac88cb78a54" + ], + "docs/screenshots/notebook.jpg": [ + "c5a88b859fda3329f4de8ae375245cce6eea35886b86278a6c4c471caf415343" + ], + "docs/screenshots/practice.jpg": [ + "8a8a32fef0bd64ef1ea5465840448b74cf49c65ef2bd6ad6d443c7ea4d45efe5" + ], + "docs/screenshots/shelf.jpg": [ + "ed8c5884835b2af060282f7b9d0b5b1fe5eda474be4f517f1d4d9b0bdc6a692f" + ] +} diff --git a/docs/screenshots/shelf.jpg b/docs/screenshots/shelf.jpg new file mode 100644 index 0000000..feb468c Binary files /dev/null and b/docs/screenshots/shelf.jpg differ diff --git a/scripts/audit-publication.mjs b/scripts/audit-publication.mjs index 6e73bd1..770dc2f 100644 --- a/scripts/audit-publication.mjs +++ b/scripts/audit-publication.mjs @@ -1,5 +1,6 @@ /** Redacted pre-publication checks. These are guardrails, not a secret-free certification. */ import {execFileSync} from 'node:child_process'; +import {createHash} from 'node:crypto'; import {readFile, lstat} from 'node:fs/promises'; import {fileURLToPath} from 'node:url'; import path from 'node:path'; @@ -37,6 +38,14 @@ export function publicCommitEmail(email, publicContactEmails=[]){ || publicContactEmails.includes(email); } +// Hashes record a manual pixel and metadata review, not an automated privacy guarantee. +export function reviewedScreenshot(file, buffer, manifest={}){ + return /^docs\/screenshots\/[a-z0-9-]+\.jpg$/.test(file) + && buffer[0]===0xff && buffer[1]===0xd8 && buffer[2]===0xff + && Array.isArray(manifest[file]) + && manifest[file].includes(createHash('sha256').update(buffer).digest('hex')); +} + function publicContacts(policy){ if(!policy||Object.keys(policy).length!==1||!Array.isArray(policy.publicContactEmails) ||policy.publicContactEmails.some(email=>typeof email!=='string'||!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email))) @@ -46,10 +55,12 @@ function publicContacts(policy){ async function main(){ const publicContactEmails=publicContacts(JSON.parse(await readFile(path.join(root,'publication-policy.json'),'utf8'))); + const screenshots=JSON.parse(await readFile(path.join(root,'docs/screenshots/reviewed-images.json'),'utf8')); const findings=[],seen=new Set();let files=0,blobs=0,commits=0; const inspect=(file,buffer,revision)=>{ files++; for(const item of inspectPath(file))findings.push({file,...item,...(revision?{revision}:{})}); + if(reviewedScreenshot(file,buffer,screenshots))return; if(buffer.includes(0)){ if(!file.endsWith('.webp'))findings.push({file,rule:'unreviewed-binary',line:0}); else if(['EXIF','XMP ','ICCP'].some(tag=>buffer.includes(Buffer.from(tag))))findings.push({file,rule:'image-metadata-needs-review',line:0}); diff --git a/tests/publication.test.mjs b/tests/publication.test.mjs index 682f22e..cfb0c7b 100644 --- a/tests/publication.test.mjs +++ b/tests/publication.test.mjs @@ -1,6 +1,7 @@ import test from 'node:test'; +import {readFileSync} from 'node:fs'; import assert from 'node:assert/strict'; -import {inspectText,inspectPath,publicCommitEmail} from '../scripts/audit-publication.mjs'; +import {inspectText,inspectPath,publicCommitEmail,reviewedScreenshot} from '../scripts/audit-publication.mjs'; import {BROWSER_ASSETS,STATIC_FILES} from '../public-assets.mjs'; test('publication guard detects secrets without returning their values',()=>{ @@ -27,3 +28,17 @@ test('publication boundary excludes local state, auth, archives and backend from assert.ok(BROWSER_ASSETS.includes('packs/engineering.json'));assert.ok(BROWSER_ASSETS.includes('chat-ui.js')); for(const file of STATIC_FILES){assert.equal(inspectPath(file).length,0,file);assert.equal(file.endsWith('.mjs'),false,file);assert.equal(file.startsWith('tests/'),false,file);assert.equal(file.includes('..'),false,file);} }); + +test('only exact manually reviewed screenshot bytes and paths are admitted',()=>{ + const manifest=JSON.parse(readFileSync(new URL('../docs/screenshots/reviewed-images.json',import.meta.url))); + for(const file of Object.keys(manifest)){ + const bytes=readFileSync(new URL('../'+file,import.meta.url)); + assert.equal(reviewedScreenshot(file,bytes,manifest),true,file); + const changed=Buffer.from(bytes);changed[changed.length-1]^=1; + assert.equal(reviewedScreenshot(file,changed,manifest),false); + assert.equal(reviewedScreenshot(file,Buffer.concat([bytes,Buffer.from('private metadata')]),manifest),false); + assert.equal(reviewedScreenshot('assets/renamed.jpg',bytes,manifest),false); + assert.equal(reviewedScreenshot(file,bytes,{}),false); + assert.equal(reviewedScreenshot('docs/screenshots/../private.jpg',bytes,manifest),false); + } +});