Skip to content

Carry what the files state for OCS, polylines, text, viewports and layouts (0.3 port, 1/3) - #1

Closed
iujunhyung wants to merge 25 commits into
mainfrom
integrate/0.3
Closed

iujunhyung wants to merge 25 commits into
mainfrom
integrate/0.3

Conversation

@iujunhyung

@iujunhyung iujunhyung commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Handoff. This branch is the port of the 0.3 "readable" work (tag
archive/feat-0.3-readable) onto the three-repository split, handed over to you
on 2026-09-23: the iujunhyung side has stopped working on it. It is yours to
review, change, merge or drop. Nothing has been merged into any main and
nothing has been published.

The three pull requests depend on each other: uncad-model, then
iron-render-cad, then uncad
. Each branch already contains its repository's
current main (a fast-forward). To build them together before any release, see
"Local setup" in
docs/PORT_STATUS.md,
which also lists what remains (CLI options not yet ported, export parity to check
item by item, 18 unclassified corpus differences, a 17-41 % slowdown, no
adversarial review yet).

Merge order: 1. #1 2. iyulab/iron-render-cad#1 3. iyulab/uncad#1

What this adds. Fields the parser now reads and the renderer and the LLM/VLM
export consume. Every addition follows the model's principles: Option for what a
file may leave unstated, Ref for every reference by name or handle, a doc sentence
per field, and serde(default) so a 0.1.0 document still loads, except a 0.1.0
polyline. That last case is pinned by a tripwire test.

  • Extrusion on TEXT, ATTRIB, ATTDEF and INSERT, alongside the extrusion upstream
    added for CIRCLE, ARC, polylines, SOLID and TRACE. Affine2::from_insert applies
    an INSERT's extrusion.
  • Per-vertex start_width and end_width on upstream's PolylineVertex, and
    const_width on the entity.
  • Text placement: horizontal and vertical justification, the alignment point,
    width factor, oblique angle and the style name. Attribute flags.
  • MTEXT: rect_width, the extents and the style name.
  • VIEWPORT: the stated view, on, viewport_id and the frozen layers.
  • DIMENSION: the ordinate axis. MLINE: its scale. The new variant Entity::PolylineMesh.
  • LayerRecord: off, frozen, locked, plot, lineweight and linetype.
  • DimStyleRecord: the rest of the display variables, as Option.
  • Tables.layouts: a LayoutRecord with its plot settings.
  • Golden cases g11 to g16 pin all of the above.

Needs your decision. These are "propose first" under the principles.

  • The CadDatabase doc said the model was deliberately without linetypes,
    lineweights and styles. Layers now carry state, lineweight and a linetype name,
    and texts name their style. The linetype and style definitions, dictionaries and
    header variables are still left out.
  • A 0.1.0 polyline document no longer loads, because of your PolylineVertex
    change combined with ours. This is a breaking 0.2.0.

Tests. 59 pass. The fields were checked against a DXF twin of every drawing
where one exists.

🤖 Generated with Claude Code

iujunhyung and others added 25 commits September 23, 2026 13:38
tests/data/model-0.1.0.json is what the published 0.1.0 serializes for a
drawing with one entity of every kind it knew, and the tables. A field
added since is absent from it, and each such field documents what such a
document reads as; the test holds them to it, so a consumer with an
archive of 0.1.0 output keeps working as the model grows.

SPLINE and LEADER are left out of the document: their shape changed after
0.1.0 (degree and knots; the annotation as a three-state reference), and a
0.1.0 document carrying either no longer loads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A polygon mesh is an M by N grid of vertices. It used to fall through to
Entity::Unknown, and a polygon-mesh DXF did not read at all -- REVSURF,
RULESURF, EDGESURF and most terrain tools write exactly this entity. The
new variant carries it the way POLYLINE_PFACE already is: a Solid3DEntity
whose wireframe is the grid lines. The edge order is stated on the
variant, so two readers of one mesh agree edge for edge.

A new Entity variant is a breaking change by design (the enum is
exhaustive so that consumers meet it as a compile error).

Ported-from: efd43cc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CIRCLE, ARC, LWPOLYLINE and POLYLINE_2D, TEXT, ATTRIB, ATTDEF, INSERT,
SOLID and TRACE state their coordinates in an object coordinate system
(OCS) whose normal is DXF 210. Without it the coordinates do not say where
the entity is: a mirrored entity (normal (0, 0, -1)) stated at x = 10 lies
at x = -10, and a reader that drops the normal puts every mirrored
circle, polyline and block reference on the wrong side of the y axis.

Each of them now carries `extrusion`, following the ELLIPSE precedent:
the coordinates stay as the file states them, and taking them to the
world is the consumer's step. Where the model's point type is 2D -- the
polyline vertices, the text anchor, the solid's corners -- the OCS z the
file states is carried as `elevation` (DXF 38 on an LWPOLYLINE, the z of
group 10 otherwise), so a consumer has all it needs for a tilted normal
too. Both default for a document written before them: (0, 0, 1) and 0,
the reference's own defaults.

The module doc now says which coordinates are OCS ones; the fields that
were already OCS without saying so (centres, anchors, corners, vertices)
say so too. Their meaning does not change.

Ported-from: fae3b14, 89d77c5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Affine2::from_insert ignored the INSERT's OCS: a block mirrored with its
reference (normal (0, 0, -1)) came out unmirrored and on the wrong side of
the y axis. On AutoCADSamples7.dwg that is 132 block references whose
contents land in the wrong place. The placement now takes the INSERT's
OCS to the world's (x, y) by the DXF reference's arbitrary axis
algorithm, which is where the normal and the insertion point's OCS z come
in. For the usual normal (0, 0, 1) the result is bit for bit what it was.

Proposal for upstream (the change of an existing method's result for
data it could not see before): the model already owns this one piece of
arithmetic so that every consumer of a block reference agrees where its
contents are drawn. With `InsertEntity::extrusion` in the model, leaving
the normal out of `from_insert` would make each consumer apply it on its
own -- the disagreement Affine2 exists to prevent. Nothing else in the
model takes OCS coordinates to the world; for CIRCLE, ARC, polylines,
text and solids that stays the consumer's step. If upstream prefers to
expose the OCS map itself (so consumers stop re-deriving the arbitrary
axis algorithm for those), `ocs_to_world` here is the candidate.

Ported-from: fae3b14
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A polyline's segment is an arc when its vertex states a bulge (DXF 42),
and it has a width when the vertex states one (40/41) or the polyline
does (43). Without them a fillet, a slot, a rounded corner or a revision
cloud scallop is its chord, a DONUT (two vertices, bulges of 1) is a line
back and forth, and a wide polyline is a hairline.

`bulges` and `widths` are one entry per vertex, or empty when they would
say nothing -- every segment straight, every segment as wide as
`const_width` -- so that two spellings of one polyline (all zeros stated,
nothing stated) compare equal. The bulge is carried as the file states
it, in the OCS like the vertices. POLYLINE_2D shares the struct, takes
its bulges and widths from its VERTEX records and has no constant width
of its own. Length and area, which follow from them, are a consumer's to
compute.

This replaces the sentence 531c5e7 added to `vertices` ("a vertex's bulge
is not carried"), which is no longer true.

Ported-from: fae3b14
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… styled

A justified text is placed by its alignment point (DXF 11), not by its
start point (10): the same 10 is the left end of a left-justified text
and a point the application computed from the font for a centred one.
On one sample drawing 541 of 861 texts were centre or middle justified
and landed in the wrong place without it.

TEXT, ATTRIB and ATTDEF gain `horizontal_justification` (72) and
`vertical_justification` (73, and 74 on the attribute types), both
enums with the format's own defaults; `alignment_point` (11, None for
left/baseline text, which states no such point); `width_factor` (41,
default 1) and `oblique_angle` (51, radians, default 0); and
`style_name` (7), a three-state reference like the others. The model has
no STYLE table; the reference resolves by name the way every other
reference does, and an absent group means the style STANDARD, which a
reader applies when the drawing has one.

Ported-from: 96abb6d
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DXF 70 on ATTRIB and ATTDEF is a set of four flags. Bit 1 hides the
attribute's value while the drawing keeps it -- a title block's
invisible attributes are data nobody sees on the sheet -- and it is a
different flag from the DXF 60 every entity can carry. Bit 2 makes an
ATTDEF constant: its value is the one every reference shows, and no
ATTRIB carries it. `flags: AttributeFlags` carries all four bits by the
names the reference gives them; a document written before it reads as
no flag set.

Ported-from: 96abb6d
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The model had MTEXT's insertion point, attachment and rotation but not
how wide its block is. DXF 41 is the width the text wraps at (0: not
wrapped), and 42/43 the block's size as the writing application laid it
out -- the one statement of how far the text reaches that does not need
the font, which is what bounds, crops and hit tests of a drawing's text
need. `extents_width`/`extents_height` are None when the file does not
state them. `style_name` (DXF 7) is the text style, as on TEXT.

The baseline direction (DXF 11) stays folded into `rotation`, as
upstream already carries it.

Ported-from: 96abb6d
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A paper-space VIEWPORT was its frame alone, so nothing said what part of
the model a sheet shows or at what scale. It now carries the view the
format states for it (`view: Option<ViewportView>`: centre in display
coordinates, 12; the model height shown, 45; target, 17; direction, 16;
twist, 51, radians; lens length, 42), `None` when the record does not
carry one (files older than R2000 keep it in extended data); whether it
is on (68, or the off bit of 90); its number in the layout (69, 1 being
the sheet's own overall viewport, `None` from the binary format, which
stores none); and the layers frozen in it alone (341/331), each a
three-state reference.

Scale, the model window and the model-to-paper mapping follow from these
and stay with consumers. MULTILEADER's doc no longer names VIEWPORT as
another frame-only entity.

Ported-from: 1f08658
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Whether a layer's entities are shown is layer state the model did not
carry, except as the sign of a DXF layer's colour -- and the binary
format keeps an off layer's colour positive and says "off" with a flag
of its own, so every off layer of a DWG read as on. LayerRecord now says
it for every file (`off`), with the other two flags that decide what is
shown or touched (`frozen`, `locked`, DXF 70 bits 1 and 4), the plot
flag (290, None when the file does not state it or the reader cannot
tell a stated 0 from an absent group), the lineweight (370, hundredths
of a millimetre or -3 for the default; None likewise) and the linetype
(6, a three-state reference).

Proposal for upstream: CadDatabase's doc listed linetypes, lineweights
and styles among what the model deliberately leaves out. A layer now
carries its lineweight and names its linetype, and texts name their
style (the previous commits), so the sentence now says what is still out:
the linetype and text style definitions, dictionaries and header
variables. This revises a documented scope statement, which the
principles ask to propose first; the fields themselves are additions.
Which entity is hidden, and why, stays a consumer's rule.

Ported-from: c045d6a
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A drawing's paper space is a set of sheets, each a LAYOUT object: a tab
with a name and a place in the tab order, the block it shows, its
limits, and the plot settings that say what paper it is set up for.
Without them a reader of the model sees `*Paper_Space` blocks but not
which tab each is, how big the sheet is or which way up.

Tables gains `layouts` (name -> LayoutRecord), empty for a file that
has none. LayoutRecord carries the name (DXF 1), the tab order (71), the
shown block as a three-state reference (330), the limits (10/11) and
PlotSettings: the paper's name and size (4, 44/45), its margins
(40-43), the plot origin (46/47), the paper units (72) and rotation
(73) as enums, and the custom scale's two sides (142/143) as stated.
Where a sheet lies in paper space, and the viewports' model-to-paper
mapping, follow from these and stay with consumers.

The module and CadDatabase docs now list DIMSTYLE among the tables, which
they had not since it was added.

Ported-from: 1f08658, 7ff5c70
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An ordinate dimension measures one coordinate of its feature from a
datum, and bit 64 of DXF 70 says which: set for X, clear for Y. Without
it the model has the feature (13), the datum (10) and a measurement, and
no way to say which distance between them the measurement is.
`ordinate_axis: Option<OrdinateAxis>` is that bit on an ordinate
dimension and None on every other kind.

The doc of `definition_point` said group 10 is an ordinate's feature. It
is the datum the ordinate measures from; the feature is group 13, which
`points.extension1` carries. A reader filling the field from group 10
already puts the datum there -- only the sentence was wrong.

Ported-from: 74bb835, a7f9ea5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DimStyleRecord carried ten of the variables that turn a measurement into
the text a dimension shows. The text also depends on how the style
writes a length (DIMLUNIT: decimal, architectural, fractional, ...), which
zeros it leaves out (DIMZIN), what it rounds to (DIMRND), how it writes
an angle and with how many places (DIMAUNIT, DIMADEC) and how it stacks
a fraction (DIMFRAC). Without them "1'-3 1/2\"" and "15.50" are the same
style. Each is added the way the others are: Option, None when the style
does not state it; the three formats as enums, None too for a value
outside the format's own.

The arrow size (DIMASZ) joins the text height as the second size the
style is drawn at; together they tell a style that states its sizes from
one a file names but leaves empty. The struct's doc now says so.

Formatting the text, and which value stands in for one the style does
not state, stay the consumer's.

Ported-from: 74bb835
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An MLINE's style gives its lines' offsets in the style's own units, and
the MLINE's DXF 40 is the factor they are drawn at: a 200-unit wall in
the STANDARD style (offsets 0.5 and -0.5) states 200. Without it every
multi-line is drawn one unit wide, whatever the drawing says.

`scale` is Option: the format requires the group, so a reader that has
it always gives it, and `None` is a model that was not given one -- a
document written before this field existed -- rather than a guessed 1.

Ported-from: 00414f4
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The spec, the writer and the oracle learn the shapes the model gained,
so cases can pin them:

- a spec can mirror an OCS entity (`EntitySpec::Mirrored`: normal
  (0, 0, -1), at an elevation), give an LWPOLYLINE bulges, per-vertex
  widths and a constant width, justify a TEXT (72/73/11 with width
  factor, obliquing and a style), and write SOLIDs, polygon meshes,
  ordinate dimensions and paper-space VIEWPORTs;
- it declares text styles (a STYLE table), layers in any state (off as
  a negative colour, frozen/locked bits, 290, 370), dimension styles
  with every variable the model carries, invisible attributes, entities
  of paper space (DXF 67), and LAYOUT objects with their plot settings
  (an OBJECTS section, written only when there are layouts);
- the oracle states what a reader owes back: coordinates as the file
  states them with their normal, polyline bulges and widths folded to
  the empty list where they say nothing, the text style STANDARD for a
  text that names none, a mesh's grid lines in the model's order, paper
  space after the model and in its own block.

Every existing case writes the same bytes as before and expects the same
model; the property tests now generate the new shapes too. The named
cases that use them follow, one per commit.

Ported-from: fae3b14, 96abb6d, c045d6a, 1f08658, 7ff5c70, efd43cc, 74bb835, a7f9ea5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A circle, an arc, a bulged polyline at an elevation, a text, a solid and
a block reference, each written in the OCS whose normal is (0, 0, -1) --
what AutoCAD's MIRROR leaves behind -- beside one circle in the world's
own axes. The oracle keeps every coordinate and bulge as the file states
it, with the normal; the test checks that the block reference's
placement takes it across the y axis, the one OCS step the model owns.

The feature branch's mirrored_ocs_r2000.dxf and mirrored_bulge_r2000.dxf
fixtures pinned the same drawing in world coordinates, which is what the
model no longer does.

Ported-from: fae3b14, 89d77c5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A slot whose ends are half circles, a tapered arrow drawn with
per-vertex widths, a constant-width polyline with a quarter-circle
corner, a DONUT (two vertices, both bulges 1), and a polyline whose file
states zero bulges and widths equal to its constant width. The last one
pins the model's rule that such a spelling reads as the empty lists, the
same model as a polyline that states nothing.

Ported-from: fae3b14
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Texts in each justification the reference defines, placed by their
alignment point (11), with a width factor, an oblique angle and a named
style; a text naming no style in a file that declares STANDARD, which is
what the reference reads the absence as; a text naming a style the file
never declares, which must come back unresolved and keeping that name;
and a title block with one visible attribute and one invisible one
(DXF 70, bit 1).

A reader going through LibreDWG's DXF importer will see STANDARD where
the file names the undeclared style: the importer drops the name and
applies its own default, the same loss G5 and G10 already pin for an
undeclared dimension style and an undefined block.

Ported-from: 96abb6d
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Seven layers, one per state the model carries (off, frozen, locked, not
plotted, plotted with a lineweight, the default lineweight), each with a
line on it; a paper space with a border, a title, the sheet's own
overall viewport, a detail viewport at half scale turned 30 degrees with
one layer frozen in it alone, and a viewport that is off; and three
layouts: the model tab, an A3 sheet stated portrait and turned a
quarter, and an empty letter sheet in inches on a second paper space.

The plot flag is stated as 0 on one layer. A reader that cannot tell a
stated 0 from an absent group reports None there, which the model's
field allows and a parser's golden test pins as a known deviation.

Ported-from: c045d6a, 1f08658, 7ff5c70
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A three-by-four polygon mesh, closed in N and open in M, with varying
heights. The oracle is its grid lines in the order the model states for
POLYLINE_MESH, 8 between the rows and 12 along them.

Its vertices carry the subclass marker AcDbPolygonMeshVertex, which an
unpatched LibreDWG DXF importer does not know and rejects the whole file
over (a critical error); the vendored copy with the feature branch's
in_dxf.c/dynapi.c patch reads it, and its grid matches this oracle edge
for edge.

Ported-from: efd43cc, 598fc09
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A plate dimensioned from its lower-left corner by three X-type and two
Y-type ordinate dimensions (DXF 70 bit 64 set and clear), all measuring
from the datum at group 10, their features at 13 and their leaders
ending at 14; one states its measurement, one a literal text. The ORD
style states every variable the model carries, the ARCH style only its
two unit formats, so the oracle pins both the stated values and the
unstated ones as None.

Ported-from: 74bb835, a7f9ea5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The JSON module named four kinds of reference field; the model now has
more (text, tolerance and leader style names, a layer's linetype, a
viewport's frozen layers, a layout's block, a leader's annotation), all
serialized the same three-state way. The list now names them all.

Ref::Unresolved linked to EntityCommon::handle, which became
source_handle when entities gained reference IDs; the link is repaired.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tables::layouts said a file older than R2000 has none. Four R13 and R14
DWGs of the LibreDWG corpus that a later AutoCAD saved in those formats
(example_r13.dwg, example_r14.dwg, r14/Constraints.dwg, r14/Leader.dwg)
keep their LAYOUT objects, and a reader reads them like any other: three
tabs each (their DXF twins carry none). R13 and R14 have no layouts of
their own; the sentence now says that, and that such a save keeps them.

Found while porting the layout reader onto uncad; the field, its shape
and the rest of its documentation are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…E edges

Upstream (bcd39d6) added several things this branch had added too. Where
both describe the same thing, upstream's shape (names, types, JSON keys,
serde defaults, docs) is kept and ours is dropped; what upstream does not
carry is kept on top of it, in upstream's conventions.

Overlaps and how each was resolved:

- Polyline bulge -- adopted upstream. A vertex is upstream's
  `PolylineVertex { point, bulge }` on LWPOLYLINE, POLYLINE_2D and HATCH
  polyline paths; our `LwPolylineEntity::bulges: Vec<f64>` (empty when
  all zero) is removed.
- Polyline widths -- combined. Upstream carries none; ours were a
  parallel `widths: Vec<SegmentWidth>` on the entity. Since the bulge now
  sits on the vertex, the widths do too: `PolylineVertex::start_width` /
  `end_width` (DXF 40/41, serde default 0; 0 on a HATCH boundary).
  `SegmentWidth` is removed. `const_width` (DXF 43) stays on the entity
  and keeps its rule: a polyline that states it again on every vertex is
  the same model as one that states it once (vertices carry 0).
- Polyline elevation and extrusion -- adopted upstream. Same names, types
  and defaults as ours; upstream's docs replace ours.
- CIRCLE/ARC extrusion -- adopted upstream (same field; upstream's docs).
- SOLID/TRACE elevation and extrusion -- adopted upstream (same fields;
  upstream's docs, and the corners' doc back to upstream's wording).
- 3DFACE invisible edges -- adopted upstream (ours had none).
- HATCH polyline path as vertices -- adopted upstream.
- Principles: strings carry the text's own codes -- adopted upstream.
- TEXT/ATTRIB/ATTDEF/INSERT extrusion and elevation -- kept ours (upstream
  has none); already upstream's type (`Point3D`, serde default (0, 0, 1);
  `f64`, default 0), docs reworded in upstream's terms.
  `Affine2::from_insert` reads that same `Point3D` and is unchanged.
- Golden spec vertex -- combined. Upstream's `spec::Vertex { at, bulge }`
  is kept and gains `start_width` / `end_width` (`Vertex::wide`); our
  spec `LwPolyline { bulges, widths }` lists are removed, `const_width`
  stays and `elevation` (DXF 38) is added.
- Golden mirroring -- adopted upstream, extended. Upstream's
  `mirrored: bool` on Circle, Arc and LwPolyline replaces our
  `EntitySpec::Mirrored { elevation, entity }` wrapper; Text, Solid and
  Insert gain the same field for G11. Only the polyline keeps an
  elevation, the one G11 states.
- Golden writer -- adopted upstream's `extrusion(mirrored)` and its rule
  of writing 42 only for an arc segment, in place of our writer state and
  our all-or-nothing 42; 40/41 are written on every vertex of a polyline
  that has widths, 43 and 38 when not 0.
- Golden expected model -- adopted upstream's `extrusion(mirrored)` in
  place of our `Ocs` threading; polylines map vertices with their widths.
- Golden cases -- upstream's G7 additions kept as written; our G11 and
  G12 re-expressed in the merged shapes (G12's last polyline no longer
  states zero bulges: an absent 42 is the same straight segment).
- Golden properties -- upstream's mirrored circle and arc strategies
  kept; our polyline strategy (bulges, widths, constant width) kept on
  upstream's vertices; our mirrored-wrapper strategy replaced by a solid
  with its own flag and a mirrored polyline at an elevation.
- JSON tests -- upstream's values kept, widths moved onto the vertices.
- JSON compatibility -- a 0.1.0 polyline (bare points) no longer loads
  under upstream's vertex shape, as a SPLINE and a LEADER already did not:
  the 0.1.0 document drops its LWPOLYLINE, its POLYLINE_2D and its HATCH's
  polyline path (re-serialized by uncad-model 0.1.0 itself, so it is still
  that version's output), and a test pins that such a polyline does not
  load.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@birdnamoo

Copy link
Copy Markdown
Contributor

Merged into main as 218f969 (absorbed with follow-up adjustments). Closing this PR; the integrate/0.3 branch is kept for now. Thanks!

@birdnamoo birdnamoo closed this Sep 24, 2026
@birdnamoo
birdnamoo deleted the integrate/0.3 branch September 26, 2026 12:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants