Commit Graph
235 Commits
Author SHA1 Message Date
Hakan SevenandClaude Opus 5.5 0c1676e612 fix(underlay): bound how far DWF packages and DGN streams inflate
The DWF package reader, the DGN stream inflater and the new DGN
ExtendedColors scan each read a zlib/zip payload to its end, so a
crafted or corrupt file could inflate until memory ran out. Cap them:
1 GiB for a whole DWF package, 512 MiB per DGN stream, 4 MiB per
ExtendedColors record candidate - far above real files.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 17:17:38 +03:00
Hakan Seven d9899be35e Merge pull request #1514 from ramox81/codex/dwf-dgn-underlays
Attach Underlay ribbon menu, DWF/DGN attach dialog, DGN true colours
2026-09-28 17:17:17 +03:00
ramox81andClaude Opus 5.5 5b6cbff5b2 Attach Underlay ribbon menu, DWF/DGN attach dialog, DGN true colours
- Insert > Reference: the PDF Attach button becomes an Attach Underlay menu
  (PDF / DWF / DGN); its face runs the last one chosen.
- DWFATTACH, DGNATTACH and ATTACH on a .dwf/.dwfx/.dgn open the Attach
  dialog for that kind: sheets or models with thumbnails (one chosen),
  placement, path type, file details with the sheet / model size, and for
  DGN the conversion units (sub units offer 1 / sub units per master as the
  scale).
- DGN V8 true colours come from the ExtendedColors record packed in the
  non-model attributes stream; the colour word's high part numbers them.
- A DGN model's extent includes each text's box (its length along the
  baseline and 1.5 x its height), as the reference sizes a model.
- DWFx text uses the family named in its embedded (deobfuscated) font.
- DWF contrast pivots at 0.73 once colours are turned over for a dark
  background.
- Hairline strokes are drawn solid one pixel wide, and underlay rasters are
  made at or below their size on screen so those lines stay unbroken.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 17:08:06 +03:00
Hakan SevenandClaude Opus 5.5 e26e0352b7 fix(underlay): malformed DWF and DGN files no longer crash or hang the reader
- W2D: a stream cut off right after an extended opcode's '(' sliced its
  body backwards and panicked.
- DGN V8: the compound-file header was indexed before its length was
  known (a short file with the signature panicked), the sector shift was
  taken on trust (0 or 1 underflowed `sector / 4 - 1` into an endless
  loop), and the DIFAT chain count was unbounded. The header must now be
  complete, the shift 9 or 12, and the chain no longer than the file.

Found fuzzing the #1514 readers with truncated and random input.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 17:06:14 +03:00
ramox81 fecbcd8138 Merge remote-tracking branch 'origin/main' into codex/dwf-dgn-underlays
# Conflicts:
#	Cargo.lock
2026-09-28 16:13:14 +03:00
ramox81andClaude Opus 5.5 56ecf786f1 DWF/DGN underlays: clipping, layers, text, 3D DGN, colours and zoom extents
- PDFOSNAP, DWFOSNAP and DGNOSNAP are separate; UOSNAP sets them all and
  reads 2 while they differ. Enable Snap switches the selected kind's.
- DWFCLIP / DGNCLIP clip their own kind with the kind's prompts and message;
  CLIP routes DWF and DGN underlays there; Enter at a corner asks again.
- The right-edge underlay tools show for DWF and DGN too (import is PDF
  only).
- Underlay layers: DGN levels (V8 level table, "Default" listed as "0"; V7
  used levels named from the level name table), hidden through the layer
  override data with the DGN placeholder string first; a plotted DWF has
  none.
- V8 colour read from its colour word (by level, index, or a true colour
  drawn as its nearest index); 3D V8 elements drawn in plan (lines, line
  strings, ellipses and arcs by their orientation, text); a model's extent
  includes its text.
- DWF 6 TrueType text (font, height, rotation from the font opcode) and DWFx
  glyph runs are drawn; the default W2D colour map is the measured one.
- Contrast pulls lightness towards a per-kind pivot as measured (PDF 0.5,
  DWF 0.243, DGN 0.65).
- Zoom extents includes raster images and underlays whose frames are hidden.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 16:10:01 +03:00
Hakan Seven 7694c506aa Merge pull request #1512 from ramox81/codex/frame-parity
Frames: follow the drawing state, DWF/DGN frames, image frame profile, frame-only picking

The codec [patch] to ramox81/cadcodec@ea5edaa is dropped: that commit landed as opencadcodec#63 and main already pins ad16215, which contains it.
2026-09-28 13:52:06 +03:00
ramox81andClaude Opus 5.5 1cbe89ef62 Underlays are rasterised for the view's scale; DGN V7 strokes one pixel wide
A page shown small was drawn from its full-size raster and thin lines broke
up into dots. Once a zoom settles on another quarter-octave step, PDF, DWF
and DGN underlays are rasterised again near their size on screen (never
above the full-size raster), frames are requested until that happens, and
the adjusted-pixel memo is kept per raster size. V7 element weights no longer
widen strokes, matching V8 models.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 13:49:33 +03:00
ramox81andClaude Opus 5.5 cb7860e50e DWF and DGN underlays: attach, display, snaps, frames and references
DWFATTACH / -DWFATTACH and DGNATTACH / -DGNATTACH attach a DWF / DWFx sheet
or a DGN (V7 or V8) model with the reference's prompts; ATTACH routes the
file types to them. The sheet or model is read into vector paths, drawn as a
transparent underlay raster (contrast, fade, monochrome and background
adjustment apply) and offers its geometry to object snaps. Definitions are
kept in ACAD_DWFDEFINITIONS / ACAD_DGNDEFINITIONS, the External References
palette lists and reloads them, and image textures get line-keeping mip
levels so thin underlay lines stay visible when zoomed out.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 12:53:25 +03:00
ramox81andClaude Opus 5.5 5ca7becd97 Raster images: transparency off draws pixels opaque; TRANSPARENCY command
An image's transparency flag decides whether transparent pixels show what
is behind the image or draw in their stored colour: the image pass and PDF
plots now follow it (it was ignored, so every PNG alpha showed through).
TRANSPARENCY selects images and asks for the mode, defaulting to the first
image's current one.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 11:23:19 +03:00
ramox81andClaude Opus 5.5 7f8f8457c7 Frames: state-following ribbon list, DWF/DGN frames, image frame encoding and profile, frame-only picking
- The Frames list shows the drawing's frame state (hide / display and plot /
  display but not plot / frames vary), follows commands, undo and document
  switches, and lists the mixed state as a disabled item.
- DWFFRAME and DGNFRAME are frame variables of their own, kept in the header,
  and FRAME sets them with the others.
- Out-of-range frame values say "Requires an integer between 0 and 2." and
  ask again; a set value reports "Regenerating model.".
- IMAGEFRAME is stored in the image variables as bits (2 is written as 3) and
  a drawing without them uses the profile value, which setting IMAGEFRAME
  updates and the settings file keeps.
- Raster images, PDF underlays and wipeouts are picked on their frame only;
  underlay snap geometry is never picked.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-28 10:53:53 +03:00
Hakan Seven 22a5a8d16c Merge pull request #1485 from ramox81/codex/clip-followups
CLIP on images and viewports, IMAGECLIP, VPCLIP, linked clip boundaries and clip depth
2026-09-25 15:17:53 +03:00
Hakan SevenandClaude Opus 5.5 7e90d4d1a1 fix(pdf): per-piece snap bounds, linear image layering, snap op checks
Review of #1484:
- The underlay's snap geometry became one hidden wire per segment, but
  every piece took the whole page's bounding box, so each cursor move
  visited every piece. Each piece now gets its own box.
- Assigning unlayered images to the last layer compared Debug strings of
  every layered path and text per drawn item (quadratic, formatted anew
  each time); the owners are keyed once in a map.
- The `snap` automation op used (0, 0, 0) for a missing point; it now
  answers invalid_point. It also left the live snapper's base point set
  to the query's; the base point is restored.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-25 15:17:53 +03:00
Hakan Seven e2e2c36b6a Merge pull request #1484 from ramox81/codex/pdf-import-followups
PDF import and underlay follow-ups
2026-09-25 15:15:49 +03:00
ramox81andClaude Opus 5.5 46fa08a041 Raster images show square pixels when magnified
Raster image textures use nearest magnification; PDF pages and OLE
pictures keep bilinear filtering.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-25 12:22:45 +03:00
ramox81andClaude Opus 5.5 bd17e7e0d4 CLIP on raster images and viewports, IMAGECLIP, VPCLIP, clip depth rules
- CLIP continues as IMAGECLIP on a raster image and as VPCLIP on a layout
  viewport, with the reference prompts and stored data.
- Image clip boundaries sit on pixel centres, OFF only clears the display
  flag, and an inverted boundary cuts the image.
- VPCLIP polygon (Arc/Length/Close/Undo), clipping object and Delete; the
  viewport shrinks to the boundary's exact extents, view kept.
- XCLIP Clipdepth: Remove clears only the plane asked, a front plane behind
  the back one is rejected with the reference message.
- Attached and imported images carry the reference display flags.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-25 10:33:49 +03:00
ramox81andClaude Opus 5.5 edbbc9d135 PDF follow-ups: import layers, arcs and joins, names, background, page placement, snaps
- Unlayered raster images go with the PDF layer drawn last before them.
- Circular Bezier arcs import as arcs: bulged polyline segments with Join,
  ARC entities without it; straight chains still join.
- Imported PNGs go to "PDF Images" next to the PDF; import blocks are
  numbered imp31, imp32, ...
- Background darkness is its HSL lightness below one half.
- All chosen pages attach at the same insertion point.
- PDF geometry snaps as separate pieces: centre from the rim, intersections
  and perpendiculars.
- Automation: snap query with an optional base point.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-25 10:33:48 +03:00
Hakan SevenandClaude Opus 5.5 f537aff75c feat: node graph overlay; move to opencadcodec/opencadkernel/opencadgraph
The codec and kernel repositories and crates are renamed opencadcodec and
opencadkernel. Inside OCS they are depended on under short keys, so code
reads `codec::`, `kernel::` and the new `graph::` (opencadgraph, the node
graph engine). File-format and protocol spellings stay as they were: the
ACIS product id `acadrust`, the ACADRUST_* switches and the plugin
version keys `acadrust_version` / `acadrust_source`.

A button left of MCP on the command line opens a translucent node graph
over the viewport, with a palette of object, input, math, logic, list,
text and geometry nodes added by click or drag. An object node owns
drawing entities and its rows are the Properties panel's own rows for
them: the single-object page is extracted from refresh_properties as
entity_property_sections, values use the control protocol's
property_json, and writes go through set_property_value with a property
target override, so the panel, MCP and the graph share one read/write
path. A list reaching an object row makes one entity per element. Each
graph action is one undo step; the canvas pans and zooms.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-25 07:34:23 +03:00
ramox81andClaude Opus 5.5 cfa4b186c5 PDF underlays: adjusting for a dark background turns every colour's lightness over
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-24 20:04:53 +03:00
ramox81 c1ecb0edaf Merge remote-tracking branch 'origin/main' into codex/pdf-attach-parity
# Conflicts:
#	locales/ar-SA/opencadstudio.ftl
#	locales/bg-BG/opencadstudio.ftl
#	locales/cs-CZ/opencadstudio.ftl
#	locales/de-DE/opencadstudio.ftl
#	locales/el-GR/opencadstudio.ftl
#	locales/en-US/opencadstudio.ftl
#	locales/es-ES/opencadstudio.ftl
#	locales/fi-FI/opencadstudio.ftl
#	locales/fr-FR/opencadstudio.ftl
#	locales/hi-IN/opencadstudio.ftl
#	locales/hu-HU/opencadstudio.ftl
#	locales/it-IT/opencadstudio.ftl
#	locales/ja-JP/opencadstudio.ftl
#	locales/ko-KR/opencadstudio.ftl
#	locales/nl-NL/opencadstudio.ftl
#	locales/pl-PL/opencadstudio.ftl
#	locales/pt-BR/opencadstudio.ftl
#	locales/ru-RU/opencadstudio.ftl
#	locales/tr-TR/opencadstudio.ftl
#	locales/zh-CN/opencadstudio.ftl
#	locales/zh-TW/opencadstudio.ftl
#	src/app/control/actions.rs
#	src/app/mod.rs
#	src/app/update/mod.rs
#	src/app/view/modal.rs
#	src/io/xref.rs
#	src/ui/window/mod.rs
2026-09-24 19:38:48 +03:00
ramox81andClaude Opus 5.5 3f6bdf8c59 PDF underlays: attach dialog with pages, underlay tab, layers, import settings and file import, unload on the definition
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-24 18:31:40 +03:00
ramox81andClaude Opus 5.5 0c50013ca7 PDF underlays: attach prompts and definitions, display adjustments and clipping, Properties, snapping, PDFCLIP and PDFIMPORT
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-24 16:00:12 +03:00
ramox81andClaude Opus 5.5 498a97df0f Xref attach: placement preview, relative paths on the first save, no-match messages
The reference follows the cursor while its insertion point, scale or
rotation is asked (preview-only PScale/PX/PY/PZ/PRotate included); a
relative path chosen before the drawing had a file becomes relative on the
first save, and relative paths start from "./" at or below the drawing's
folder; -XREF name options report "No matching xref names found.".

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-09-23 15:34:06 +03:00
Hakan Seven 377f551b48 Merge pull request #1358 from HakanSeven12/plot/b4-windows-printer-routing
fix(print): send Windows plots to the chosen printer, name the default, report failures

# Conflicts:
#	locales/ar-SA/opencadstudio.ftl
#	locales/bg-BG/opencadstudio.ftl
#	locales/cs-CZ/opencadstudio.ftl
#	locales/de-DE/opencadstudio.ftl
#	locales/el-GR/opencadstudio.ftl
#	locales/en-US/opencadstudio.ftl
#	locales/es-ES/opencadstudio.ftl
#	locales/fi-FI/opencadstudio.ftl
#	locales/fr-FR/opencadstudio.ftl
#	locales/hi-IN/opencadstudio.ftl
#	locales/hu-HU/opencadstudio.ftl
#	locales/it-IT/opencadstudio.ftl
#	locales/ja-JP/opencadstudio.ftl
#	locales/ko-KR/opencadstudio.ftl
#	locales/nl-NL/opencadstudio.ftl
#	locales/pl-PL/opencadstudio.ftl
#	locales/pt-BR/opencadstudio.ftl
#	locales/ru-RU/opencadstudio.ftl
#	locales/tr-TR/opencadstudio.ftl
#	locales/zh-CN/opencadstudio.ftl
#	locales/zh-TW/opencadstudio.ftl
#	src/locale_catalog.rs
2026-09-19 23:00:18 +03:00
PanosandClaude Opus 5 c36ae08fd0 fix(print): send Windows plots to the chosen printer, name the default, report failures
On Windows a plot with no printer chosen went to the PDF application's
bare "print" verb — whatever that application considered its default —
and a viewer without a "printto" verb (Edge, store readers) silently
dropped the printer the dialog had chosen. The dialog also never named the
system default, and a failed printer enumeration left only "Default" and
"PDF" with the reason written to a console nobody sees.

- The default printer is looked up by name (GetDefaultPrinterW) both for
  the dialog's "System default printer (…)" entry and before every job.
- A pure route decision (plan_print_route) resolves "Default" to that
  name and uses the PDF application only when it registered a "printto"
  verb for .pdf (AssocQueryStringW); otherwise the job goes straight to
  the spooler through the existing GDI path. The report says which:
  "Sent to printer: NAME (direct print | via the PDF application)".
- list_printers reports the system's own error; the plot dialog shows it
  under the printer row and the command line warns once. A desktop without
  CUPS is an empty list, not an error.
- PRINTERS command: default printer, listed printers (or the error) and,
  on Windows, the print route — a report to paste into an issue.
- Properties… on the Default entry opens that printer's preferences sheet.
- GDI path: the driver's dmSize is kept (its private data lives behind
  it), the print raster is no longer memoised for the session, and a
  spooled temporary PDF is removed.

Tests: route planning (named / blank / no default / printto or not), the
PRINTERS command; the two Windows-only GDI tests pass the resolved name.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-18 15:49:27 +03:00
ramox81 6a1c649016 Fix Properties hyperlink collection editor 2026-09-17 18:17:25 +03:00
Hakan Seven 03f4e7cb86 Merge pull request #1309 from ghost/embed-print-fonts 2026-09-17 06:57:42 +03:00
Karim Jerbi ca0ee37d26 feat(snap): independent 3D object-snap system with six modes
Grid snap locks to SNAPUNIT instead of magnetizing; snapped 3D points keep elevation instead of flattening to z=0. New SnapTypes Vertex/EdgeMidpoint/FaceCenter/Knot/FacePerpendicular/NearestFace with their own F4 master toggle and mode set, tiers, and markers, fully independent of the 2D master. Solid B-rep corners/edge-midpoints/face-centers from mesh edges and kernel faces, spline knot points, and live perpendicular/nearest-to-face on mesh triangles merged priority-aware in move/click paths. Drafting Settings 3D Object Snap tab is fully live (was dead placeholders); Nearest-to-face off by default. TDD: behavioral LINE-to-solid tests plus engine/scene unit tests.
2026-09-16 16:43:37 +01:00
huanld 28efbffd57 feat: embed images (OLE2FRAME), print pipeline fixes, plot prefs, community fonts
- IMAGEEMBED: pack raster images into OLE2FRAME entities so a .dwg is
  self-contained; 'Embed Image' entry in the XREF attach menu; control/MCP
  op embed_image for external tools; dxf_name for OLE2FRAME queries.
- Scene: populate_images_from_document now rebuilds OLE2FRAME and PDF
  underlay models too (they used to vanish after any XREF op).
- Printing: add PRINTER_ENUM_NETWORK pass when listing printers; GDI
  spooler fallback (hayro raster at 300 DPI + StretchDIBits) for hosts
  whose PDF handler registers no print verb; DEVMODE orientation with a
  90-degree raster rotation fallback for v4 drivers; fix the fit-scale
  precedence bug that printed blank pages.
- PDF export dialogs on Windows use the async file dialog so the native
  overwrite confirmation no longer deadlocks the UI thread.
- Plot dialog: persist the chosen printer (saved on selection) and the
  picked plot window across sessions; page setups with no device no
  longer reset the output device.
- Community fonts: detect unresolvable .shx references on open and offer
  a download from the repository's fonts/ folder (contribution guide in
  fonts/README.md); custom font source URL setting for intranet font
  servers; downloaded fonts resolve from the per-user fonts directory.
2026-09-16 16:42:21 +07:00
Hakan Seven dfe8b77cca Merge PR #1280 and expose constraint value visibility 2026-09-15 11:55:05 +03:00
Hakan Seven 06fcf09ae3 Integrate standard parametric PR fixes 2026-09-15 10:43:00 +03:00
Hakan Seven d738ebfcc6 Standardize parametric constraints and interoperability 2026-09-15 03:19:52 +03:00
Felix RiestraandClaude Sonnet 5 ed2e0fbc4a Wire up the Properties panel's Parameters section
PropValue::ParamRow/ParamAddRow, the PropParamInput/Commit/Delete/AddNew
messages, their command_driver handlers (on_prop_param_commit/_delete/
_add_new, resolve_named_parameter_edit), and their render functions all
already existed — but nothing ever constructed the "Parameters" section
that would route data to them, so the whole inline add/rename/redefine/
delete workflow was unreachable from the UI. Adds that missing section
to the no-selection page in refresh_properties, alongside a new
PropValue::ParamsVisibilityToggle header row (mirrors the existing
show_constraint_values setting) for toggling whether constraint pills
show their driven value/parameter name.

Users can now manage named parameters inline in the Properties panel
(live resolved values, add/rename/redefine/delete) as an alternative to
the separate PARAMETERS modal, which continues to work unchanged.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-15 01:26:48 +02:00
Hakan Seven 2d20bdd51c Merge pull request #1251 2026-09-14 19:51:57 +03:00
Hakan Seven cba64631b8 fix: render PDF attachments on every platform
Fixes #1243
2026-09-14 12:53:06 +03:00
Marcel 0ea220f120 Add parallel offset grip editing for polylines 2026-09-13 19:19:19 +02:00
Hakan Seven 07acd1284c Render procedural checker materials correctly 2026-09-13 01:50:42 +03:00
Hakan Seven 7c5a388f19 Merge PR #1215: add rectangle grip dimensions 2026-09-12 14:53:37 +03:00
Marcel 913705106e Support dynamic rectangle resizing via grip editing 2026-09-11 20:52:33 +02:00
Marcel f30b490831 Add rectangle width and height grip editing 2026-09-11 20:15:36 +02:00
ramox81 61e5271055 Keep wipeout frames continuous 2026-09-11 17:27:34 +03:00
ramox81 21a232ef8d Fix XLine and wipeout properties 2026-09-11 17:11:52 +03:00
Hakan Seven ace75b93ca Merge pull request #1200 from Ramox81/codex/parity-boundary-object-type-20260911 2026-09-11 15:47:23 +03:00
Hakan Seven 753fa17b68 Merge pull request #1184 from Ramox81/codex/hatchedit-command-parity 2026-09-11 15:14:43 +03:00
ramox81 6272722baa Carry hatch origin through creation and pattern editing 2026-09-11 13:40:40 +03:00
ramox81 1039f51abf Associate hatch boundaries from internal points and selected sources 2026-09-11 11:18:01 +03:00
ramox81 6e74cc48d8 Recreate hatch boundaries as exact regions 2026-09-11 10:57:32 +03:00
ramox81 4deaffeb0a Add boundary region output selection 2026-09-11 10:38:50 +03:00
Felix Riestra ad718def3f Merge remote-tracking branch 'origin/main' into merge-test/main-into-constraints
# Conflicts:
#	src/app/command_driver.rs
#	src/command.rs
#	src/ui/ribbon/mod.rs
#	src/ui/window/options.rs
2026-09-10 23:34:50 +02:00
Felix RiestraandClaude Sonnet 5 bd9a2d08f7 Add Properties-panel Constraints/Parameters sections, a constraint-pill
value toggle, and redesigned constraint-ribbon icons

Properties panel:
- New "Parameters" section (shown with no selection) listing every named
  parameter with inline name/formula editing, add, and delete, backed by
  new PropValue::ParamRow/ParamAddRow and command_driver handlers
  (resolve_named_parameter_edit, on_prop_param_commit/_delete/_add_new).
  A fresh parameter's formula now defaults to "1", not "0" — a 0-length
  seed value is a genuine solver singularity for a Distance constraint
  driven by that parameter, with no defined direction to grow back out of.
- New "Constraints" section (shown for a single selected entity) listing
  every constraint touching it as a clickable PropValue::EntityLink that
  selects the linked entities in the viewport, danger-tinted when the
  constraint conflicts.

Constraint pills:
- New Options > General toggle, "Show values and parameter names on
  constraint markers" (on by default, persisted). Off, every pill shows
  just its bare glyph instead of glyph+driven-value/parameter-name — the
  glyph alone is enough to see a constraint is present, without the value
  text covering nearby geometry on a dense sketch.

Constraint-ribbon icons:
- Replaced all 18 unicode/letter glyph icons (including the plain "F",
  "L", "M", "S" letters) with proper SVGs under assets/icons/constrain/,
  matching the Modify section's two-color style (#B4B6B9 white / #6DB7ED
  blue, consistent stroke width). Tangent reuses the Draw section's
  white-arc-plus-blue-dots motif.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
2026-09-10 23:16:11 +02:00