{"id":49557574,"url":"https://github.com/ashmod/panels","last_synced_at":"2026-05-03T05:53:17.324Z","repository":{"id":338524267,"uuid":"1157832199","full_name":"ashmod/panels","owner":"ashmod","description":"Your online Sunday funnies hub: pick your favourite comics, discover new ones, and enjoy random strips anytime.","archived":false,"fork":false,"pushed_at":"2026-04-23T20:23:16.000Z","size":16006,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-03T05:53:15.076Z","etag":null,"topics":["api","comic-strips","comics","entertainment","recommendation-engine","recommendations","rust","sunday-funnies","web-app"],"latest_commit_sha":null,"homepage":"https://panels.ashmod.dev/","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ashmod.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-14T11:18:39.000Z","updated_at":"2026-04-23T20:23:39.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ashmod/panels","commit_stats":null,"previous_names":["ashmod/panels"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ashmod/panels","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ashmod%2Fpanels","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ashmod%2Fpanels/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ashmod%2Fpanels/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ashmod%2Fpanels/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ashmod","download_url":"https://codeload.github.com/ashmod/panels/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ashmod%2Fpanels/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32559716,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-03T03:21:47.309Z","status":"ssl_error","status_checked_at":"2026-05-03T03:21:43.884Z","response_time":103,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","comic-strips","comics","entertainment","recommendation-engine","recommendations","rust","sunday-funnies","web-app"],"created_at":"2026-05-03T05:53:16.692Z","updated_at":"2026-05-03T05:53:17.318Z","avatar_url":"https://github.com/ashmod.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cpicture\u003e\n  \u003csource srcset=\"assets/panels_white.svg\" media=\"(prefers-color-scheme: dark)\"\u003e\n  \u003cimg src=\"assets/panels.svg\" alt=\"Panels logo\"\u003e\n\u003c/picture\u003e\n\n\u003cbr\u003e\u003cbr\u003e\n\n\u003cdiv align=\"center\"\u003e\n\n[![CI](https://img.shields.io/github/actions/workflow/status/ashmod/panels/ci.yml?branch=main\u0026label=CI\u0026logo=github\u0026style=flat-square)](https://github.com/ashmod/panels/actions/workflows/ci.yml)\n[![Tests](https://img.shields.io/github/actions/workflow/status/ashmod/panels/tests.yml?branch=main\u0026label=Tests\u0026logo=github\u0026style=flat-square)](https://github.com/ashmod/panels/actions/workflows/tests.yml)\n[![Deployment](https://img.shields.io/website?url=https%3A%2F%2Fpanels.ashmod.dev%2Fapi%2Fhealth\u0026label=Deployment\u0026logo=heroku\u0026style=flat-square)](https://panels.ashmod.dev)\n\n\u003c/div\u003e\n\nPanels is a comic strip browser built with Rust. Pick your favorite strips, fetch a random panel instantly, doomscroll your favourites, and get recommendations based on what you already like.\n\nPanels works best when you run it on your own machine or on a host you control, for complete access to all sources like GoComics.\n\n\u003e [!NOTE]\nPanels is a personal project and is not affiliated with any comic publishers. All comics are sourced from publicly available data and are intended for personal use and enjoyment. All comics are property of their respective creators and publishers.\n\n## Live Demo\n\nA live demo is available at **[panels.ashmod.dev](https://panels.ashmod.dev)**.\n\nThe demo runs a lightweight deployment without the GoComics browser integration, so it includes full collections for a subset of comics sourced through alternative providers:\n\n- **Calvin and Hobbes**\n- **Peanuts**\n- **Garfield**\n- **xkcd**\n- **Dilbert**\n- **Piled Higher and Deeper**\n\nA handful of other series have their **most recent** strips available via RSS.\u003cbr /\u003e\nTo access the full catalog, run Panels locally or self-host it.\n\n## Quick Start\n\nThe fastest way to try Panels locally is with Docker. If you'd rather build from source, skip to the Rust instructions below.\n\n### Run with Docker\n\nPrerequisites: [Docker](https://docs.docker.com/get-docker/) (and Docker Compose, bundled with Docker Desktop).\n\n```bash\ndocker compose up --build\n```\n\nThen open `http://localhost:3000`. The image is built in two stages: a Rust builder compiles the binary, and a Playwright runtime image provides Node and Firefox for the GoComics fallback — no local Rust or Node toolchain required.\n\nTo use a different host port, override it at the compose level:\n\n```bash\nPANELS_PORT=4000 docker compose up --build -d\n```\n\n### Run from source\n\n### 1. Prerequisites\n\n- Rust stable toolchain\n- `cargo`\n- `node`\n- `npm`\n\n### 2. Install browser helper dependencies\n\nPanels uses Playwright for GoComics pages, so install the Node dependency once:\n\n```bash\nnpm install\n```\n\nThis runs the package `postinstall` step, which downloads the Playwright browser used by the GoComics fallback.\n\n### 3. Start the app\n\n```bash\ncargo run\n```\n\nPanels starts on `http://localhost:3000` by default.\n\n### 4. Open it\n\nVisit `http://localhost:3000` in your browser.\n\nIf you just want to confirm the backend is up:\n\n```bash\ncurl http://localhost:3000/api/health\n```\n\nExpected response:\n\n```json\n{\"status\":\"ok\"}\n```\n\nYou can also specify the port with:\n\n```bash\ncargo run -- --port PORT_NUM\n```\n\n## Configuration\n\nPanels uses CLI flags and environment variables:\n\n| Flag | Env | Default | Description |\n|---|---|---|---|\n| `--port` | `PANELS_PORT` | `3000` | HTTP server port |\n| `--data-dir` | `PANELS_DATA_DIR` | `data` | Path containing `comics.json`, `tags.json`, and `badges/` |\n| `--strip-cache-max` | `PANELS_STRIP_CACHE_MAX` | `500` | Max strip cache entries |\n| `--strip-cache-ttl` | `PANELS_STRIP_CACHE_TTL` | `1800` | Strip cache TTL in seconds |\n| n/a | `PANELS_GOCOMICS_BROWSER` | unset | Optional browser executable path for the GoComics Playwright fallback |\n\nExample:\n\n```bash\nPANELS_PORT=4000 PANELS_DATA_DIR=./data cargo run\n```\n\nIf Playwright should use a specific browser binary:\n\n```bash\nPANELS_GOCOMICS_BROWSER=/path/to/browser cargo run\n```\n\n## API Overview\n\n### `GET /api/health`\n\nBasic liveness endpoint.\n\n### `GET /api/comics`\n\nReturns comics with tag metadata.\n\nQuery params:\n- `search` (optional): matches `title` or `endpoint`\n- `tag` (optional): exact tag filter (case-insensitive)\n\nExample:\n\n```bash\ncurl \"http://localhost:3000/api/comics?search=garfield\u0026tag=humor\"\n```\n\n### `GET /api/recommendations`\n\nReturns scored recommendations from selected comic endpoints.\n\nQuery params:\n- `selected` (required for non-empty results): comma-separated endpoints\n- `limit` (optional): max results, default `10`\n\nExample:\n\n```bash\ncurl \"http://localhost:3000/api/recommendations?selected=garfield,peanuts\u0026limit=8\"\n```\n\n### `GET /api/comics/{endpoint}/{date}`\n\nReturns one strip as JSON.\n\n`{date}` supports:\n- `YYYY-MM-DD`\n- `latest`\n- `random`\n\nExamples:\n\n```bash\ncurl \"http://localhost:3000/api/comics/garfield/latest\"\ncurl \"http://localhost:3000/api/comics/garfield/random\"\ncurl \"http://localhost:3000/api/comics/garfield/2025-02-14\"\n```\n\n### `GET /api/comics/{endpoint}/{date}/image`\n\nProxies the strip image bytes and content type.\n\nCaching behavior:\n- `date=random`: `Cache-Control: no-store`\n- any non-random request: `Cache-Control: public, max-age=86400, s-maxage=604800`\n\nExample:\n\n```bash\ncurl -I \"http://localhost:3000/api/comics/garfield/random/image\"\n```\n\n## Development\n\nTypical local workflow:\n\n```bash\nnpm install\ncargo run\n```\n\nBefore opening a PR, run:\n\n```bash\ncargo fmt\ncargo clippy --all-targets -- -D warnings\ncargo test --all-targets\n```\n\nCI (`.github/workflows/ci.yml`) runs:\n- `cargo check --all-targets`\n- `cargo test --all-targets`\n\nTests workflow (`.github/workflows/tests.yml`) runs:\n- `cargo test --all-targets`\n\n## Contributing\n\nContributions are welcome. Open an issue or send a PR if a comic is missing, a source breaks, or you have an improvement in mind.\n\n### Issue reporting\n\nWhen reporting an issue, please include:\n- A clear description of the problem.\n- Steps to reproduce the issue.\n- Expected vs actual behavior.\n- Any relevant logs or error messages.  \n \n### Pull request guidelines\n\n1. Fork the repo and create a feature branch.\n2. Make focused changes with clear commit messages.\n3. Run the local quality checks.\n4. Open the PR with context and testing notes.\n\n### Local quality checks\n\n```bash\ncargo fmt\ncargo clippy --all-targets -- -D warnings\ncargo test --all-targets\n```\n\n### Pull request checklist\n\n- Scope is limited to one feature or fix.\n- API behavior changes are documented in `README.md`.\n- New behavior is covered by tests in `tests/` or module tests.\n- Clippy and tests pass locally.\n\n## License\n\nMIT. See [`LICENSE`](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fashmod%2Fpanels","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fashmod%2Fpanels","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fashmod%2Fpanels/lists"}