_attachment_display.html iterates a context variable named
`attachments`, so every caller binds it before the include.
_post_card.html was the one caller that did not: it guarded on
item.attachments but included the partial with nothing bound, so the
gallery looped over whatever `attachments` happened to be in the
surrounding page context and rendered empty. Every post with an image
looked image-less on the feed and on profiles, and on a project page -
where project_detail.html sets `attachments` at template scope for the
project's own files - a devlog card would have rendered the project's
files as its own.
With the image actually reaching the card, render a lone one properly:
a gallery holding exactly one item gets a `single` class and takes the
full content column (max-height 480px, object-fit contain, no hover
scale), matching the original DevPlace. That branch serves the stored
original rather than thumbnail_url, because a thumbnail is 200px on its
longest side and stretching it to the column width is visibly blurry.
Animated GIFs needed no change and now have a test proving it: they
never had a thumbnail to flatten, so they already took the original-file
path and simply render larger.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Pressing Ctrl+V with a screenshot on the clipboard now attaches it
immediately instead of requiring a trip through the file picker.
The clipboard reader lives in dp-upload behind a new opt-in `paste`
boolean attribute: with it set, the component binds one paste listener
on its closest form and routes the clipboard image files through the
same handleFiles path as the picker and the drop target, so validation,
limits, the terms gate and the hidden attachment_uids field are shared.
A paste carrying plain text is never swallowed.
It is opt-in rather than a form-wide default because a form may hold
several upload buttons - projects.html has cover and logo beside the
attachment one - and a default would attach one pasted screenshot to
all of them.
Set on _attachment_form.html, so every form including it inherits the
behaviour (post composer, post edit, gists, projects, issues,
screenshots), plus _comment_form.html, messages.html and the embed-mode
skeleton AppChat builds.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
DeviiTerminal bound [data-devii-open] click listeners only after the
async /devii/session fetch resolved, silently dropping early clicks.
Switch to a single delegated document listener bound in the
constructor, matching the ModalManager/dp-lightbox pattern.
The steal-confirm and comment-vote e2e tests asserted DOM state right
after a click with no wait for the triggering POST to land, racing the
server under CI load. Wrap those clicks in page.expect_response.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Add `cmd_isslop_prune`, `cmd_isslop_clear`, `cmd_isslop_analyze` CLI commands for AI usage analysis job management
- Register `/game` router with `index` and `farm` endpoints for Code Farm idle game
- Add `politics` to allowed TOPICS constant replacing `signals`
- Introduce `ISSLOP_DIR`, `ISSLOP_WORKSPACES_DIR`, `ISSLOP_RUNS_DIR`, `ISSLOP_MEDIA_DIR` config paths
- Add `clear_user_stars` and `clear_user_projects_cache` calls on vote and project create/delete
- Update `make prod` to use `nproc` workers via `DEVPLACE_WEB_WORKERS` env var
- Convert `database.py` and `utils.py` to packages for modular structure
- Add `devplace apikey` and `devplace token` CLI subcommands for API key and access token management
- Invalidate `_hot_settings_value` and `_hot_settings_at` in `_patch_limits` so stale
cached limits from prior tests do not persist after monkey-patching `get_int_setting`.
- Add a polling retry loop (up to 5 seconds) in `test_maintenance_mode_blocks_guests`
to handle eventual consistency of the maintenance-mode flag propagation.
- Replace brittle hardcoded `@bob_test` assertion in mention test with dynamic
`data-username` attribute extraction from the active dropdown item.
- Replace `asyncio.run` calls in bot unit tests with shared `run_async` helper from
`tests.conftest` to avoid event-loop conflicts in the test suite.
- Change `insert` to `upsert` with `["uid"]` conflict target in Gitea test fixture
to prevent duplicate-key errors on repeated test runs.
- Replace `asyncio.new_event_loop().run_until_complete` in Telegram worker tests
with the same shared `run_async` helper for consistency.
- Add `/block`, `/mute` endpoints with block/unblock and mute/unmute functionality in `routers/relations.py`, hiding blocked users' content everywhere except their own profile while muting only suppresses notifications
- Introduce `devplace emoji-sync` CLI command to regenerate `static/js/emoji-shortcodes.js` from the emoji library, documented in `CLAUDE.md` and wired in `cli.py`
- Create `get_blocked_uids()` database helper and apply it in `content.py` `load_detail()` to filter blocked users' posts from detail views
- Implement `_uid_index()` and `_drop_index()` helpers in `database.py` for unique uid indexes across tables, with `user_relations` added to `SOFT_DELETE_TABLES`
- Document new routes in `AGENTS.md` and `README.md`, including emoji shortcodes rendering behavior distinct from the emoji picker
- Add comprehensive documentation for backend content rendering in AGENTS.md, detailing the new `render_content` and `render_title` Jinja globals built on mistune with media processing, emoji shortcodes, and XSS protection
- Document the `X-Response-Time` header and bottom-left render time indicator in README.md
- Update bot token pricing documentation to clarify fallback vs gateway cost headers
- Add `email_accounts` to soft-delete tables and `idx_users_role` composite index in database schema
- Implement `telegram_pairings` and `telegram_links` table creation with column migration and indexes
- Add `/profile/{username}/telegram` endpoint to docs API with request/unpair actions
- Register `TelegramService` in main.py lifespan and add `response_timing` middleware emitting `X-Response-Time` header
- Introduce `TelegramPairForm` model and `guard_public_host_sync` synchronous host validation function
- Add `account_api_key` field to `BotState` and `_adopt_account_api_key()` method that fetches the bot's own `users.api_key` from its profile JSON after auth, switching `LLMClient` to use it instead of the shared bootstrap key, routing gateway spend through the bot's user identity
- Replace `interleave_by_author` with a simpler greedy algorithm that picks the first row whose owner differs from the last picked, removing the multi-queue priority logic
- Extend `_ai_quota` response with `unlimited`, `requests`, and `last_used` fields sourced from new `user_spend_24h()` analytics helper
- Pass `recent_post_titles` from `BotState` into `LLMClient.generate_post()` and add a `distinct_rule` prompt instructing the model to avoid repeating framing, examples, or opinions from the bot's last six posts
- Remove early-return dedup check in `ArticleRegistry.admit` that skipped articles already held by the same bot owner, allowing re-admission under a different category
- Render bot username as a clickable link with avatar in the admin bots table
Split the monolithic test directory into three tiers (unit, api, e2e) with a path-mirroring directory structure. Added corresponding Makefile targets (test-unit, test-api, test-e2e) and updated all documentation references (CLAUDE.md, README.md, testing-cicd.html, testing-framework.html, testing-make.html) to reflect the new layout and naming conventions.