Commit graph

87 commits

Author SHA1 Message Date
Erik
936628065c feat(frontend): replace inventory pagination with one-shot load + incremental scroll render
Sort/filter always operate on the full result set server-side; the whole set
(up to 10k) loads in one request and rows render progressively on scroll.
Over-cap searches show a "narrow your filters" notice.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 21:52:50 +02:00
Erik
2395bd6267 feat(frontend): render weapon OD as integer (0-10 loot-profile scale)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 19:10:03 +02:00
Erik
29f8bec046 fix(frontend): only show weapon-type chips when item type is Weapons
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 08:48:27 +02:00
Erik
d6fa0fa9b0 feat(frontend): multi-select weapon-type-by-skill filter + auto OD column
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 08:44:00 +02:00
Erik
1dd95819c7 feat(frontend): weapon OD rating filter, column, and detail row
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 08:22:54 +02:00
Erik
71f235c8b1 feat(frontend): inventory item-state filter — Any/Equipped/Not equipped radios, moved under Characters
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 06:59:22 +02:00
Erik
edbf7086a6 fix(frontend): harden inventory search — q= validation, error boundary, stable selection, URL throttle
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 06:41:54 +02:00
Erik
66ee4bd82b feat(frontend)!: replace inventory.html with /?view=inventory (set analysis dropped)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 06:30:59 +02:00
Erik
559fd6596a feat(frontend): inventory item detail panel
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 06:26:55 +02:00
Erik
4735aa72e9 feat(frontend): inventory chips row + results table (sort, columns, paging, keyboard)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 06:26:38 +02:00
Erik
0533e2b9ee feat(frontend): inventory filter sidebar (characters, type, slots, cantrips, ratings, sets, state, reqs)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 06:22:10 +02:00
Erik
9a9b33a528 feat(frontend): /?view=inventory route + inventory page scaffold + dark CSS
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 06:16:12 +02:00
Erik
6fd87a3751 feat(frontend): useInventorySearch hook (debounced abortable search, URL state)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 06:09:47 +02:00
Erik
5ddcecaece feat(frontend): inventory search types + filter/column constants
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-15 06:09:22 +02:00
Erik
6a0bb9fe80 feat(sidebar): restore the rickroll title-click easter egg
Holiday's over — revert the Sma Grodorna frog-hop title gag back to the
original /rick.mp4 fullscreen rickroll + shake/spin it replaced.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-24 08:15:21 +02:00
Erik
cc686da532 feat(midsummer): retire the theme out of season (holiday over)
Flip the seasonal master switch (SEASON_ACTIVE=false in useMidsummer) so the
Sma Grodorna theme is fully dormant — no rain/frogs/maypole/banner/palette,
regardless of any stored toggle preference — and remove the 🐸 toggle from the
sidebar. All theme code is kept; to bring it back next Midsummer, flip
SEASON_ACTIVE to true and re-add <FrogToggle /> in SidebarWindowButtons.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-24 08:12:00 +02:00
Erik
d86bc48862 feat(midsummer): rain of flowers/frogs/Swedish flags, dots become frogs, drop jingle
Per request: remove the WebAudio jingle (+ its 🔊 toggle and sound state);
replace the one-shot confetti with a continuous rain of 🌼🌸🐸🇸🇪🌿 over the
screen (MidsummerRain, gated by the theme, reduced-motion aware, leak-free);
and replace player-dot markers with frogs themselves (override the inline
dot color/border) instead of a flower-crown on top. Still toggled by the
🐸 Midsommar switch. Includes rebuilt static bundle.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-19 09:47:39 +02:00
Erik
1f86e7cc86 polish(midsummer): guard frog-hop against rapid re-click stacking
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-19 09:33:51 +02:00
Erik
3cd2165c15 feat(midsummer): WebAudio Sma grodorna jingle, plays once on first gesture 2026-06-19 09:30:00 +02:00
Erik
e896ef1f21 feat(midsummer): frog-hop easter egg replaces the rickroll 2026-06-19 09:29:07 +02:00
Erik
c4dd1b7ae7 feat(midsummer): glad midsommar banner + one-shot confetti 2026-06-19 09:28:34 +02:00
Erik
e7b0f11bb1 feat(midsummer): flower-crown dots, frog on selected 2026-06-19 09:27:40 +02:00
Erik
da0cc79def feat(midsummer): dancing maypole pinned to map centre 2026-06-19 09:27:26 +02:00
Erik
2fb6fd2f3e feat(midsummer): sidebar frog toggle + jingle toggle (sound stubbed) 2026-06-19 09:26:45 +02:00
Erik
580fd6fbc5 feat(midsummer): pond-green palette overlay for sidebar and map 2026-06-19 09:26:12 +02:00
Erik
568992d0f9 feat(midsummer): theme state provider + data-midsummer attribute 2026-06-19 09:25:54 +02:00
Erik
87e4f2ff62 fix(dashboard): table width:auto so Character column sizes to content only
With width:100%, the table stretched to fill the container — and the
Character column (the only one with stretchable text) absorbed all the
extra space, looking much wider than the longest name needed.

width:auto lets each column size to its content. Table now fits its
data; container still scrolls horizontally if content ever exceeds the
viewport.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-23 19:37:49 +02:00
Erik
5f43ddce93 feat(dashboard): click-to-highlight rows + character column auto-sizes
Two small UX improvements to the Player Dashboard table (works in both
the new-tab fullscreen page and the deprecated in-app window since they
share PlayerDashboardContent):

1. Row highlight: click anywhere on a row to highlight it (blue tint +
   thin outline). Click again to unhighlight. Single selection — useful
   for tracking one character down a long sorted list.

2. Character column no longer truncates: removed maxWidth/overflow/
   textOverflow on the name cell. Column now sizes to the longest
   character name (still no wrapping; container scrolls horizontally
   if names are extreme).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-23 19:35:09 +02:00
Erik
5bda2b64f4 feat(dashboard): open Player Dashboard in a new tab
The 👥 Dashboard button used to open the player table as a draggable
in-app window, which competed for screen space with the map. It now
opens in a separate browser tab as a fullscreen page so users can put
the dashboard on a second monitor.

How:
- App.tsx branches on ?view=dashboard → renders PlayerDashboardFullPage
  (new file in components/) instead of the default MapLayout.
- SidebarWindowButtons.tsx: 👥 Dashboard onClick now does
  window.open('/?view=dashboard', '_blank', 'noopener'). Label shows
  '↗' so users know it's an external open.
- PlayerDashboardWindow.tsx refactored: extracted the sortable table
  body into a reusable PlayerDashboardContent component. The old window
  shell stays registered in WindowRenderer for backward compat — just
  no longer reachable from the default sidebar.
- map-layout.css: new .ml-dashboard-page rules for fullscreen layout.

Each tab gets its own useLiveData + WebSocket connection (server
already handles multiple browser clients). The new tab inherits the
session cookie from the original tab — no re-login.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-23 19:31:26 +02:00
Erik
1c1c43d28b feat(dashboard): logout button + admin user-management window
Logout: new sidebar link 'Log out (username)' that POSTs /api/logout
(clears session cookie) and navigates to /login. Visible to everyone.
Replaces 'no logout functionality' state where users could only get
out by deleting cookies manually.

Admin window: new 'Admin · Users' window (only shown when current
user.is_admin) lists all users in a table with:
  - Add user (username + password + admin checkbox)
  - Reset password inline per row
  - Toggle admin per row
  - Delete user per row (blocked for self)
Wraps the existing /api-admin/users CRUD endpoints in main.py.

Plumbing: useCurrentUser hook fetches /me on mount; apiPatch+apiDelete
helpers added to api/client.ts; new endpoint wrappers exported from
api/endpoints.ts; AdminUsersWindow.tsx registered in WindowRenderer
under id prefix 'adminusers'; CSS for admin table/form/buttons and
the muted-red logout link.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-15 20:10:10 +02:00
Erik
79cf88d3f7 feat(agent): Phase 1 — chat-window AI assistant via Claude Code subprocess
Adds an in-dashboard AI assistant that answers questions about live game
state. Designed reactively (no background loops) — every user message in
the chat window or via /api/agent/ask runs one `claude -p` invocation.

Architecture:
- New host-side FastAPI service (agent/) on 127.0.0.1:8767, OUTSIDE the
  dereth-tracker Docker container because `claude` and ~/.claude
  credentials live on the host.
- nginx routes /api/agent/* to the host service.
- The same browser session cookie the tracker issues authenticates
  agent requests (shared SECRET_KEY).
- The agent shells out to `claude -p --session-id <uuid>` with
  cwd=/home/erik/MosswartOverlord. Sessions persist as JSONL on disk
  via Claude Code's built-in machinery.
- An MCP stdio server (agent/mcp_overlord.py) exposes tools to Claude:
  get_live_players, get_recent_rares, query_telemetry_db (read-only,
  parsed by sqlglot to reject DML/DDL), get_player_state, get_inventory,
  get_inventory_search, get_combat_stats, get_equipment_cantrips,
  get_quest_status, get_server_health, suitbuilder_search.
- Read-only PG role (overlord_agent_ro) is the second line of defense
  on the SQL tool — even a parser bypass can't mutate.

Frontend:
- AgentWindow.tsx — draggable chat window with localStorage-pinned
  session UUID, "New Chat" button, on-mount rehydration from
  /agent/sessions/{id}/history (parses Claude Code's JSONL).
- Wired into WindowRenderer + Sidebar (🤖 Assistant button).

Operational:
- systemd unit (overlord-agent.service) + install.sh.
- agent/README.md documents env vars, deploy flow, smoke tests.
- nginx/overlord.conf gets a new /api/agent/ location with 180s timeout.
- CLAUDE.md gets an "Overlord Assistant Mode" section briefing the
  agent on which tools to use and how to behave.

NOT YET DEPLOYED — server still needs:
1. Apply agent/sql/0001_overlord_agent_ro.sql + ALTER ROLE password
2. Add AGENT_DB_DSN to /home/erik/MosswartOverlord/.env
3. bash agent/install.sh (creates venv, installs unit, starts service)
4. sudo cp /home/erik/MosswartOverlord/nginx/overlord.conf to nginx + reload

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 20:43:59 +02:00
Erik
f7f04d6a84 Revert floating badge, remove debug logs
The floating version badge scrolled awkwardly and wasn't necessary
now that the bind-mount/deploy issue is fixed. The existing ml-version
inside the Sidebar is sufficient.

Also removed the temporary [INV_DEBUG] console logs from useLiveData
and InventoryWindow — the inventory live-update bug is confirmed fixed.
Kept the per-character inventoryVersions fix and the cache-buster on
the refetch URL.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:20:24 +02:00
Erik
a5ff228d4f Add floating version badge in top-left corner
Small yellow badge fixed at position (4, 4) showing the running build
version. Helps visually confirm which bundle a browser is loading when
diagnosing cache issues.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:14:43 +02:00
Erik
0ff396cd0e Add debug logging for inventory live-update tracing + cache-bust fetch
Temporary instrumentation to diagnose why InventoryWindow doesn't refresh
on inventory_delta. Three log points:
- useLiveData: logs when inventory_delta arrives and version bump
- InventoryWindow effect: logs every run with state
- InventoryWindow fetch: logs when debounce fires and result count

Also added cache-buster (_t=timestamp) to the refetch URL in case HTTP
caching is masking fresh data.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:05:51 +02:00
Erik
d26f1f725c Fix inventory window never refreshing live (per-character version)
The inventoryVersion counter in useLiveData was a single global value that
bumped on every inventory_delta for any character. With 60+ active chars
all generating deltas, the global counter advanced multiple times per
second.

InventoryWindow's debounce effect watched this global counter, so every
bump reset its 2-second fetch timer. Since bumps arrived faster than 2s,
the fetch never fired — the window appeared frozen until the user closed
and reopened it (which triggered the initial-fetch effect).

Fix: make inventoryVersions a Map<string, number> keyed by character name.
Each inventory_delta now only bumps its own character's counter, so an
open window's debounce correctly fires 2s after its character's last
delta, ignoring unrelated traffic.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 18:57:55 +02:00
Erik
475c7aba03 feat: harder shake then spin 🌀🎵
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 23:25:13 +02:00
Erik
30c4067c99 feat: easter egg 🎵
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 23:22:44 +02:00
Erik
adb9d5feab feat: major cleanup + death alerts + idle detection + Discord webhooks
Cleanup:
- Removed 109 stale asset files from static/assets/ (was 122, now 13)
- Removed static/v2/ entirely (was duplicate of root assets)
- Removed dead dashboard code: DashboardView, Layout, GlobalStats,
  CharacterCard, CharacterGrid, VitalBar, TabContainer, CombatTab,
  RaresTab, MapTab, InventoryTab, global.css, MapTransformContext
- Removed recharts dependency (425KB chunk eliminated)
- CSS reduced from 17KB to 10KB
- Added deploy-frontend.sh script for one-command build+deploy
- Updated CLAUDE.md with combat_stats, share_*, dungeon_map events
  and React frontend architecture

Death alerts (frontend + backend):
- Frontend: DeathNotification component with red banner + sawtooth
  sound when vitae goes from 0 to >0
- Backend: detects vitae transition in vitals handler, sends Discord
  webhook to #aclog with "☠️ CHARACTER died! (vitae: X%)"
- Rate-limited: max 1 Discord alert per character per 5 minutes

Idle detection (backend):
- Background task runs every 60 seconds
- Detects: vt_state "default"/"idle" OR kph=0 while in combat/hunt
- Sends Discord webhook: "⚠️ CHARACTER appears idle (state: X, KPH: 0)"
- Auto-clears alert when character becomes active again
- No duplicate alerts for same idle period

Discord integration:
- DISCORD_ACLOG_WEBHOOK env var for webhook URL
- Used by both death alerts and idle detection
- Graceful fallback when not configured

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 16:32:14 +02:00
Erik
d2c30b610b fix(v2): character window now updates live from WebSocket
The CharacterWindow only fetched once from API on mount and never
updated. Now:
- character_stats WS messages are tracked in useLiveData via ref
- Passed through WindowRenderer to CharacterWindow as liveStats prop
- Window uses live WS data when available, falls back to API fetch
- Attributes, skills, vitals base values, properties (augmentations,
  ratings, etc.), allegiance all update in real-time

Also: vitals bars in the character window use live WS vitals data
(health_percentage etc.) for real-time HP/Stamina/Mana display.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 16:02:49 +02:00
Erik
a5bd659876 feat(v2): remove old dashboard, add vitae + resizable windows
- Removed old Recharts dashboard view entirely (no more viewMode
  toggle, DashboardView lazy import, Ctrl+D shortcut)
- Recharts chunk eliminated from build — bundle size reduced
- Player Dashboard window: added Vitae column (red when > 0%)
- ALL windows now resizable: drag bottom-right corner handle
  (min 300×200px). Subtle diagonal line grip indicator.
- Sidebar: removed 📊 Dashboard toggle link, removed broken
  /quest-status.html external link (replaced by 📜 Quests window)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 15:33:07 +02:00
Erik
938421999a feat(v2): Quest Status + Player Dashboard as React windows
Quest Status window (📜 Quests in sidebar):
- Fetches GET /quest-status API (polls every 30s)
- Grid: characters as rows × all unique quests as columns
- "READY" shown in green, countdowns in yellow, missing as dash
- Quest names shortened (removes "Timer", "Pickup" suffixes)
- Sticky header row, scrollable body
- Replaces broken quest-status.html link

Player Dashboard window (👥 Dashboard in sidebar):
- Sortable table of all online characters
- Columns: Character, State, KPH, Session kills, Total kills,
  Rares (total + session), Deaths, Uptime, HP%, Tapers
- Click column headers to sort (ascending/descending toggle)
- State badges: green=combat/hunt, red=other, gray=idle
- KPH in green, rares in gold, deaths in red (if > 0)
- HP% color-coded: green >80%, yellow >40%, red below

Sidebar changes:
- Removed broken /quest-status.html external link
- Added 👥 Dashboard + 📜 Quests as window opener buttons
- Both lazy-loaded (only fetched when first opened)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 14:02:00 +02:00
Erik
27caa21a56 style(v2): hide sidebar scrollbar between player column and map
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 13:58:09 +02:00
Erik
1a7300df37 style(v2): hide scrollbar on dashboard main area
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 13:56:25 +02:00
Erik
666af817a2 fix: add missing useRef import to InventoryWindow
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 13:54:51 +02:00
Erik
4638e60043 fix(v2): inventory no longer flickers — debounced re-fetch, no loading flash
inventory_delta WS messages were triggering immediate full re-fetch
with setLoading(true), causing content to flash blank. Now:
- Initial load shows loading state (once)
- Subsequent deltas debounced to 2s (batches rapid changes)
- Re-fetch runs silently without clearing existing items

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 13:54:06 +02:00
Erik
0112c59514 feat(v2): 13 improvements — functional, visual, UX, backend
Functional:
1. Chat: "▼ New messages below" indicator when scrolled up, click to jump
2. Combat stats: "Clear Session" button (red, with confirm dialog)
3. Inventory: live updates via inventory_delta WS (re-fetches on change)
4. Inventory: real mana time from equipment_cantrip_state WS (live
   countdown with state dot: green=active, red=inactive, yellow=unknown)

Visual:
5. Thin separator line between tool links and sort buttons
6. Selected player row highlighted with darker background (#2a3344)
7. Scroll-to-top button (▲) appears when scrolled past 200px in player list

UX:
8. Double-click player dot on map opens their chat window
9. Right-click player dot shows context menu (Chat/Stats/Inv/Char/Combat/Radar)
10. Ctrl+D keyboard shortcut toggles between map and dashboard views
11. Sound notification on rare drops (880Hz sine beep via Web Audio API)

Backend:
12. Deep-merge lifetime offense/defense per element — accumulates
    total_attacks, failed_attacks, crits, damage per AttackType×Element
    instead of overwriting with latest session data
13. Startup cleanup: deletes stale combat_stats records from before
    the lifetime fix (pre-2026-04-14T09:00Z)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 13:49:40 +02:00
Erik
0b64c6ccff feat(v2): chat command history + smart auto-scroll
Command history:
- Up/Down arrow keys browse sent command history (like bash/console)
- 50 commands stored per character in localStorage
- Persists across page reloads and browser sessions
- Current input preserved when browsing (restored on Down past end)
- Duplicates kept (matches user preference)

Smart auto-scroll:
- New messages only auto-scroll if user is already at the bottom
- If user has scrolled up to read history, it stays put
- Sending a message snaps back to bottom
- 30px threshold for "at bottom" detection

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 12:51:45 +02:00
Erik
8a2d0e1a72 style(v2): amber/yellow meta states now show red instead
Non-active, non-idle VTank states (nav, turn_in_quests, etc.) now
display in red instead of amber/yellow in both:
- Map sidebar: .ml-meta-pill.other (red background + text)
- Dashboard cards: .badge-other (red background + text)

Green = combat/hunt, Red = nav/other states, Gray = idle/default

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 12:36:23 +02:00
Erik
9f7686681b feat: v2 React frontend is now primary at /
- v1 vanilla JS frontend moved to /classic (static/classic/)
- v2 React app now serves at / (root)
- Vite base changed from /v2/ to /
- Assets at /assets/, service worker at /sw.js
- /classic still works — all v1 files preserved with relative paths
- /v2 still works as before (build output unchanged)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 12:17:23 +02:00
Erik
69678a9426 perf(v2): 8 optimizations — 24% smaller bundle, fewer re-renders
1. React.memo on WindowRenderer — prevents re-renders when parent
   state changes but no windows are affected

2. Coordinate display via direct DOM ref — no React state updates
   on mouse move (was triggering re-renders on every pixel)

3. useDeferredValue for sidebar vitals + player list — React
   prioritizes map interactions over stat text updates

4. Chat messages in ref — stores in useRef instead of useState,
   only bumps a version counter for re-render. Eliminates a
   new Map() allocation on every chat message.

5. Lazy-load 8 window components — InventoryWindow, CharacterWindow,
   RadarWindow, CombatStatsWindow, IssuesWindow, VitalSharingWindow,
   StatsWindow, CombatPickerWindow all loaded on first open.
   Main bundle dropped from 278KB to 211KB (24% reduction).

6. Preload critical assets — dereth.png, backpack icon, dungeon_tiles.json
   via <link rel="preload"> in index.html for instant map render.

7. Bundle splitting — React runtime extracted to separate 12KB chunk
   (cached independently). Window components split into 8 chunks.
   Total: 13 chunks vs previous 2.

8. Service worker — caches map images, icon sprites, and dungeon tiles.
   Icon images cached on first fetch. Repeat page loads serve from
   cache instantly. Auto-cleans old cache versions.

Net result:
- Initial load: 211KB main + 17KB CSS (was 278KB + 17KB)
- React cached separately: 12KB
- Windows load on demand: 1-15KB each
- Dashboard with Recharts: 425KB (unchanged, still lazy)
- Map images/icons: cached by service worker after first load

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 12:11:08 +02:00