A clean-room Thai catalog covering every key the bundled catalogs translate, its LANGUAGES row (one plural form) and Thai month and weekday names in DATE_LANGUAGES.
Taiwan, Japan and China write dates year first with slashes (Windows'
zh-TW short date is yyyy/M/d), and neither the Fill & Sign presets nor
the header and footer date tokens offered it. Both now have yyyy/mm/dd
and yyyy/m/d. The header and footer list gains them at its end, since
its selection is an index.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RUNQSPVXx3dDBabRZHAnjz
Prepare a form ▸ Detect form fields places each field on a line or box the
page already draws, but every new widget got a grey /MK /BC and an opaque
white /MK /BG, so the fill hid the printed form underneath. Detected fields
now clear both in the same undoable step; fields drawn by hand keep the
defaults.
Refs #294, #2831.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add Background draws behind the page content, so on a page that first
fills itself with opaque white (browsers' Save as PDF, print drivers) the
background was hidden completely and the tool looked broken.
- pdfcraft-edit: new `backdrop` module. A strictly detected backdrop (one
axis-aligned `re`, opaque white in a device colour space, painted before
anything else and covering the crop box) is replaced by turning that one
fill into `n` in a per-page copy of its stream; the original stream is
left untouched for other pages. Remove background restores it, unless
the copy was edited since. Page-sized pictures (scans) are reported only.
- Background gains `replace_backdrop` (default on); the default colour is
a visible light yellow instead of near-white.
- Dialog: offers to replace the backdrop, warns about scanned pages,
checks pages a few per frame, preview draws text over the colour.
- Automation: `doc_background` takes `replace_backdrop`; new read-only
`doc_backdrops` lists pages with a backdrop.
Acrobat's behaviour on such pages is unobserved; recorded as our choice
in parity/.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A file edited after it was made PDF/A or PDF/X can keep claiming a
standard it no longer meets. organize::remove_standard_id removes the
claim: the pdfaid: XMP properties for PDF/A, or GTS_PDFXVersion and
GTS_PDFXConformance in the document information and as pdfxid: XMP
properties for PDF/X. Output intents and other claims (PDF/UA) stay.
The XMP is edited with the packet editor Document Properties already
uses (sync_info and the new remove_properties share its read and write),
so the rest of the packet stays byte for byte and unsafe packets are left
alone. It is one undoable edit, in Standards ▸ PDF/A as a button worded
like its undo label, and the standards_remove_id tool (pdfa or pdfx).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts: ATTRIBUTION.toml, crates/ui-egui/src/i18n/es.tsv, crates/ui-egui/src/i18n/vi.tsv. Catalogs keep upstream's rows, then this branch's rows upstream lacks; ATTRIBUTION.toml is upstream's with every sha256 recomputed; progress lines keep both sides, upstream's first.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(ui): validated signature display, snapshot tab reuse
Signature widgets with layered appearances baked the unverified state
into the page, so a valid signature kept reading 'Signature Not Verified'
while the banner and panel said valid. Validated verdicts are now drawn
into the icon and caption layers in the display copy only: the signed
bytes are untouched, saving keeps the original appearance, and files
without those layers or without a valid verdict render exactly as before.
Other valid signatures still get the verdict mark over their widget,
sharing one style with the Signatures panel, and hide it again once the
appearance itself shows the verdict.
View signed version and View revision opened a new tab on every click,
and every tab showed the document title, so repeats piled up identical
tabs. Repeats now reuse the snapshot tab, snapshot tabs read like their
source plus the suffix in the tab strip, window title and Combine picker,
and carry a notice bar naming the revision with a Close that asks about
saving first.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* Address review comments on validated appearances and trust refresh
Count every field when checking shared appearance streams, so a
caption shared with an unvalidated field is never rewritten for the
valid one alone.
Pick the icon slot by drawing instead of length, and only when a
single layer qualifies. Bare rectangles never count as the icon, and
ambiguous or missing slots fall back to the overlay badge.
Revalidate and invalidate together through one helper, so trusting a
certificate always drops rendered pages. Trust alone still marks
nothing dirty.
Tests: mixed-verdict sharing, longer decorative borders, competing
and doubled layers, capture-before preservation, trust invalidation
without zooming, and signed Session round trips with rendering,
saving and passwords.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* Prove the icon slot geometrically and cover real layered signatures
A lone line drawing is still not the icon: frames and borders span the
box while the status mark lives in a corner of it. The icon now has to
sit strictly inside the appearance frame and cover at most half of it;
anything else falls back to the overlay badge. Widgets without a
measurable frame veto the rewrite the same way.
Tests: parsed extents and inhabitant checks as units, both reported
border repros plus a frameless widget, and real layered signatures
built with cms::sign_detached through Session open, trust, rendering,
save and reopen, plain and password-protected.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* Identify the icon by shape instead of geometry
A lone line drawing is still not the icon, but size and position were
the wrong signals: the real status mark is large, and geometry blocked
it. Frames, borders and panels are rectangles by nature while the mark
is not, so a drawing made only of axis-aligned rectangles is decoration
however it is stroked. Singularity, shared-stream and template checks
are unchanged; anything ambiguous still falls back to the overlay badge.
Tests: rectangularity as a unit, nested line-drawn frames, and a
triangular fixture mark in place of the rectangular one.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* Budget the appearance walk and keep the marks with the bytes
The walk that finds a signature's icon and caption capped depth,
layers and fields, but not the work: a dictionary whose entries all
point at the next one is visited once per entry per level, so a small
hostile file never finishes opening. The walk now counts its visits
and gives up, and a field whose tree cannot be read to the end stops
the whole pass, because a layer we never read may be the one two
fields share and a rewritten shared layer puts one field's verdict on
another field's page. The sharing count is exact again with that: two
widgets of one field pointing at the same appearance claim it once,
or the field vetoes its own rewrite.
A mark used to outlive the bytes it described. A failed display write
left the page showing the file's own caption while the verdict still
claimed its state was drawn; a metadata edit dropped the mark while
the page kept drawing the state, so the badge landed on top of it. The
write fallback now drops the marks and logs the loss, and metadata
carries them over.
Tests: a self-referential appearance tree, which never finished before
the budget, two widgets sharing one appearance, a metadata edit that
keeps its mark, a save that goes through the writer instead of
short-circuiting on the opened file, and the snapshot bar's Close
button pressed for real.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* Cite the new regressions in parity, restore main's installer note
The appearance entry now cites the bounded walk and the save through
the writer, and says what a tree that cannot be read to the end does.
The installer entry carried main's older text, which still claims
signed installers are planned; main's newer note is taken verbatim.
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
* M9: preserve rounded decoration beside raster status glyphs
* M9: place appearance progress beside signature history
* M9: retain appearances with ambiguous curved layers
* M9: keep signature verdicts in viewer overlays
Defer generic appearance rewriting: caption text and a unique drawing do not establish an icon role. Preserve original artwork for saves, image and text exports, and both print modes while retaining snapshot reuse and trust refresh. Cover chamfered borders and mixed signer/caption streams through signed Session round trips.
---------
Signed-off-by: Chiranjeevi U <244287281+chiranjeevi-max@users.noreply.github.com>
Co-authored-by: skylar taylor-barrick <hi@skylar.cc>
A selected image gets a rotate knob (Shift: 15 degree steps); image_edit
gains rotate_by (degrees). The turn is one extra cm about the visible centre.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
add_text_replacement built the StrikeOut and the Caret from one Meta, and
add_annotation stamps /NM from meta.id, so the pair shared a single name. XFDF
names a reply by the /NM of what it replies to, so the exporter wrote the
caret's inreplyto as the caret's own name. Importing that file found a comment
replying to itself, and the Replace Text comment did not arrive: the reporter's
second document listed none. FDF was unaffected because it keeps the pair by
object structure rather than by name.
Take a second Meta for the caret. The ids stay supplied by the caller, as the
rest of the crate does so edits remain deterministic, and the one production
caller already has a generator and calls it twice. /IRT still points at the
strikeout, so the group still moves, lists and deletes as one.
Tests: the annotation test now asserts the two carry different names and that
the caret's /IRT resolves to the strikeout's. A new XFDF test exports a Replace
Text pair and imports it into another document, asserting both comments arrive
with their thread intact; it fails if the two are given one id again.
Register Catalan (`ca`) as an interface and date language with a
2,592-entry contributor-original catalog written from the meaning of the
English labels. Plurals use the one/other rule; every `ca-*` locale,
including Valencian (`ca_ES@valencia`), resolves to this catalog.
Tests cover registration and persistence, every command, menu, tool group
and section, every `tl!` literal in the UI source, and every key in the
other bundled catalogs. The Latin-script glyph test in tests/fonts.rs now
includes Catalan (the interpunct of l·l is covered by the bundled faces).
Catalan dates elide the preposition before a vowel (7 de març, 7 d'abril,
8 d'octubre), so the date language sets months_with_day, as Czech and Russian
do; a unit test pins it.
Update localization docs, the parity table and the roadmap log, and add the
ATTRIBUTION.toml entry (ATTRIBUTION.md regenerated). The contributor, a
native Catalan speaker, has reviewed the translation.
Validation: cargo fmt --check; clippy -D warnings and tests on
pdfcraft-ui-egui (i18n, fonts); pdfcraft-engine dates tests;
cargo xtask assets; cargo xtask layers.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
An expand button at the top-left of the All tools sidebar opens a view that
fills the workspace, like Acrobat's tools page: every tool as a tile, filed
under five categories (Create & Edit, Review & Compare, Forms & Signatures,
Protect & Standardize, Customize & Automate), with a search field that matches
tool names and the items inside each tool, in English or the UI language.
Choosing a tile opens that tool's panel; Collapse, Esc or another workspace
puts the view away. `--tools full` opens it from the command line and the
control channel.
The categories live in the engine catalogue (TOOL_CATEGORIES), with a test
that every tool group is in exactly one. Six new strings are translated in
all 22 catalogues.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* M1: edit finds the pages a damaged file's readers find (fixes#1656)
* M1: rebuild a lost page tree only when the reader shows the same pages, and refuse edits to signed rebuilt files (#1656)
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* M6: make list boxes directly selectable and scroll to selection (#1524)
- In-place selection: List boxes are rendered directly on the page and
selectable in place. Clicking an option row selects it immediately;
Ctrl/Cmd-clicking toggles items in multi-select lists, and Shift-clicking
selects ranges, without opening a popup menu.
- Appearance and scroll (/TI): When options are selected, /TI (top index)
is automatically adjusted so the selected items are scrolled into view
within the widget's visible rows, and option rows and selection highlights
are drawn into the appearance stream.
- Unit and UI tests: Added tests in pdfcraft-forms verifying /TI scrolling
and appearance generation for multi-select lists, and UI tests in
pdfcraft-ui-egui covering in-place click and Ctrl-click toggling.
Also ensured test harness in reload.rs explicitly sets language to en.
Fixes#1524
* List boxes: keep the engine's page appearance, consume arrow keys
Drop the overpaint of every fillable list box with an egui mock (it ignored the
PDF's fonts, colours and /MK /R; the engine redraws the appearance with the new
/TI), and consume ArrowUp/ArrowDown in the list overlay so they don't also
scroll the page.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* M7: grow text boxes and edit existing text
Text boxes now grow to the page edge and vertically as needed, retaining their size when saved. Clicking native or added text in either text tool edits the existing item instead of layering a duplicate over it.\n\nCo-authored-by: OpenAI gpt-6.1-sol <noreply@openai.com>
* #1539: build against main's Document::added() accessor and draft-aware prepare
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Print: 4 x 6 in label paper and a Label layout for shipping labels
Thermal label printers (e.g. VEVOR Y486) could not be used for eBay/carrier
labels: the paper list had no 4 x 6 in size, the chosen paper was never sent
to CUPS (the queue's default was used), and labels laid out sideways on a
Letter/A4 sheet printed small or rotated.
- PAPERS gains "4 x 6 in label (101.6 x 152.4 mm)"; cups_media() sends the
chosen sheet to the spooler as `-o media=` (Letter, Legal, Tabloid, A3-A5
and w288h432 for the label), so the driver feeds the size the PDF was
laid out on.
- New Layout::Label: each page is cut to its printed area (a low-resolution
render, as Fit Visible does), turned a quarter turn when it is sideways in
the direction that makes its text upright (text_reads_up reads the text
and form matrices), and fitted uniformly with a small margin. Vector
content is kept, so barcodes stay sharp and undistorted. "Turn upside
down" flips it 180 degrees. One label per sheet.
- Choosing the label paper selects the Label layout, and vice versa.
- Strings translated in every bundled catalog.
Tests: label geometry (crop, quarter turns, no stretching, margins),
cups_media, text direction; print, ui-egui and automation unit tests pass.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GFXSksdMaJ3YnheY7Q6sTx
* Refresh catalog hashes after merging main
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Alessio Boschi <alessio.boschi@dinova.one>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* M12: find untagged accessibility content
* i18n: translate untagged content checker (rebased)
* fix(i18n): refresh untagged content translation hashes
* fix(tests): restore full test modules for find-untagged content
* fix(i18n): preserve existing translations after rebase
* ci: rerun shared timeout test
* chore(deps): refresh rust for the advisory gate
Use the current lockfile, including the rustls security update, so the dependency check can run on this branch.
* Refresh catalog hashes after merging main
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* M12: tag unstructured AcroForm widgets
* i18n: translate form tag command
* i18n: repair form-field label merges
* fix(i18n): refresh translation catalog hashes
* fix(i18n): preserve main translations after rebase
* Merge main; tag a widget listed on several pages once, docs say existing trees are extended
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Merge main; add the Vietnamese catalog entry for Tag form fields
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts: the language catalogs (both sides' rows kept), ATTRIBUTION.toml's checksums
(recomputed from the merged files), and tests both sides appended at the same place.
Upstream's new Vietnamese catalog gets this change's labels.
* Defer secondary PDF indexes until first use
* Merge main into #1434; read Document::added through the new method in content_ui
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Keep selected form values and page content through later edits (#1319, #1305, #1324, #1306)
An XFA radio item whose text contains a space was written back as an appearance-state name, so the next layout selected nothing. Replace Pages filled the gap between a split selection. Marking a figure decorative replaced every content stream on the page. Control clicks and drags accepted coordinates that overflow to infinity.
* Move the decorative-figure regression out of pdfcraft-edit
edit and a11y are both layer 4, so a dev-dependency from edit to a11y fails the layer check. The same regression now runs in the engine, which already depends on both.
* Drop pdfcraft-a11y from the edit crate's lockfile entry
* Run a tool after a background open of the file it asked for
A tool chosen with no document opened the picked file on the worker, but only ran when that open finished inside the picker callback. Remember the command on the pending open, and let the command test wait for the worker.
* Wait for background opens in the command tests
Opening a picked file or a recent file finishes on the worker, so a fixed handful of frames can assert before the tab exists.
* Compare batch copies to the PDF that was written
A new text PDF stamps the current second, so a later pdf() call is a different file. The numbered copy and the source must match the bytes that were saved.
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* M3: add selected-text Copy to page and Edit menus (#1225)
Register selected page-text Copy with the existing document permission gate and expose it in Edit and the ordinary page context menu. Route floating Copy through the same gate while preserving focused-input and Organize clipboard ownership.
Four regressions fail behaviorally on the exact base; five candidate regressions pass. All eight cargo xtask ci stages pass: 2575 tests passed, 12 ignored, and 31 WASM crate checks. Native synthetic checks verify Unicode clipboard output, disabled no-selection/restricted actions, and permissions-password unlock using presented Xvfb/Vulkan software-rendered frames. Physical display and hardware GPU coverage are not claimed; the optional cargo-deny audit was unavailable.
Generated with Codex for FredApps.
* Tests: scope GoToR case mutation to Unix for Windows Clippy
Keep the common directory/non-PDF cases immutable. Shadow them in the Unix-only block so FIFO coverage can append its case without an unused-mut warning on Windows. No assertion or platform coverage is removed.
This repairs the exact hosted Windows Clippy failure inherited from upstream. Source review and whitespace checks passed; no local build, test or formatter was run during the pause. Automatically triggered hosted CI will validate this head separately.
Generated with Codex for FredApps.
* Tests: scope document link target mutation to Unix for Windows Clippy
Preserve directory coverage on all native platforms and device coverage on Unix. Static diff, whitespace and conflict checks passed; no local build, test or formatter was run during the pause.
Generated with Codex for FredApps.
* Tests: escape GoToR fixture file paths as PDF literal strings
Windows absolute paths were interpreted as PDF escape sequences, introducing control characters and failing the hosted local GoToR root test. Escape backslashes and parentheses in the fixture helper without changing product behavior or assertions. Static diff and whitespace checks passed; hosted CI will validate the repair. No local builds, tests or formatters ran.
Generated with Codex for FredApps.
* Tests: use ordinary absolute paths in the GoToR fixture
Build the absolute target from the original sandbox base rather than its canonicalized Windows verbatim path. Assert absolute-path form and canonical identity with the sandbox target. Preserve production UNC/device/verbatim rejection and existing containment and destination assertions. Static diff and whitespace checks passed; no local build, test or formatter ran.
Generated with Codex for FredApps.
---------
Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
bookmark_list and link_list add a destination: its type (XYZ, Fit, FitH,
FitV, FitR) and its position in the same top-left points as every rect.
A link through a named destination now gives its page (to_page, named)
instead of only the name.
Co-authored-by: Brandon Thomas <echelon@gmail.com>
On wasm32-unknown-unknown the engine has neither a clock (`now()` is empty) nor a time zone
(`local_utc_offset()` is 0), so in a browser Fill & Sign stamps 1 Jan 1970 and `/ModDate` is never
written. The one way to inject a clock, `with_clock`, is a test shortcut: it takes the injected
instant AS local time and zeroes the zone, so a host that injects only that gets either the local
dates or the UTC ones wrong by its offset (21:30 in São Paulo, UTC-3, is already the next day
in UTC).
`Session::with_wall_clock(clock, utc_offset)` keeps the two separate: `clock` is the UTC instant
(what the file records) and `utc_offset` is asked on every use, because the zone changes with
daylight saving. `Automation::with_wall_clock` forwards it. Nothing changes for a native build, or
for `with_clock`.
The test pins the contrast: at 21:30 in São Paulo the stamped day is the 14th, `signing_date`
carries `-03'00'`, the file's instant stays 00:30Z on the 15th, and the plain test clock still
answers the 15th.
Co-authored-by: Paulo Junior <195025778+bonfim6P@users.noreply.github.com>
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
New files from Combine, Extract, Split and Create, signed, timestamped and
LTV-embedded files and the OCR batch output reopen with their pages before
anyone writes them, as saves already did. The desktop app, the automation
tools (with or without --root) and the CLI's edit, combine, extract and
split write through pdfcraft_platform::staging::write_atomically, so a
failed write or --out naming the input never leaves a half-written file.
Special files such as /dev/stdout are still written as given.
Claude-Session: https://claude.ai/code/session_01RUNQSPVXx3dDBabRZHAnjz
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* M6: sort multi-select choice indices
Write ascending /I option indices without reordering /V selections.
Add forms and engine regressions for reverse selection and save/reopen.
Co-authored-by: Codex <codex@openai.com>
* M0: update rustls to 0.23.46
Update only the resolved rustls version and registry checksum.
Keep the existing choice-index feature changes and advisory policy.
Co-authored-by: Codex <codex@openai.com>
---------
Co-authored-by: sanyamsingh001 <314051937+sanyamsingh001@users.noreply.github.com>
Co-authored-by: Codex <codex@openai.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* M4: set page transitions and display times (/Trans, /Dur) through the engine and the tools
organize::set_transition writes or removes a page's /Trans dictionary
(only the entries the style reads and that differ from the defaults) and
/Dur, with every value checked before any page changes; Edit::SetTransition
is one undo step ("Set page transitions", translated in every catalogue);
page_transition_set is the tool. The counterpart of reading them (#2074);
playing them and the Organize dialog stay planned.
Refs #379.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RUNQSPVXx3dDBabRZHAnjz
* Refresh catalog hashes after merging main
---------
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Edit: crop an existing page image with its handles
ImageChange::Crop(rect) keeps the part of a page image inside rect (user space)
with a clipping path placed just inside the scope that holds the image (or a
new q … Q around its Do); a clipping path already at the scope's start, the
picture's earlier crop, is replaced, so a crop can grow back. The pixels are
never scaled, and a later move or resize wraps the scope, so the crop moves
with the picture. ImageEdit::Crop (undo label "Crop image"), image_edit
action crop. In the window, right-click ▸ Crop Image puts the selected
picture in crop mode: eight handles trim (the inside pans), the part that goes
is dimmed, Enter or Done applies, Esc or Cancel leaves; Remove Crop shows the
whole picture again. Rows in every language, their checksums in
ATTRIBUTION.toml, parity edit.image-crop shipped.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Image crop: Bulgarian hint without a character the monospace UI font lacks
JetBrains Mono has no 'ѝ', so primary_ui_fonts_cover_latin_and_cyrillic_catalogs
failed; the hint is reworded ("with its handles" -> "with the handles").
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Refresh catalog hashes after merging main
---------
Co-authored-by: Osvaldo Duran <338149330+regendorf-ux@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
* Comments: font size for text boxes in Comment Properties (#1908)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
* Refresh catalog hashes after merging main
---------
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>