Fifteen Preferences options were stored but read by nothing. They now act,
through the existing plumbing (ToolContext for the tools, the prefs read by the
canvas for the view), and reach agents through prefs.set:
- General: Show Tool Tips (off: no widget's tool tip comes due), Double Click
To Isolate, Zoom with Mouse Wheel (the wheel zooms about the pointer,
Shift-wheel scrolls up and down, Cmd/Ctrl-wheel sideways), Use Precise
Cursors (Pen, Eyedropper, Slice and Blend pointers become a crosshair).
- Selection & Anchor Display: Tolerance (objects, anchors and handles, in
place of the fixed 3 and 4 px), Object Selection by Path Only (compound
paths too), Command Click to Select Objects Behind (cycles down the objects
under the pointer; not when Cmd only borrows the selection tool), Zoom to
Selection (Zoom In/Out centre the selection), anchor Size, Highlight anchors
on mouse over (Direct Selection).
- Type: Number of Recent Fonts (up to 15 Recent Fonts slots).
- Guides & Grid: guide and grid Color and Style (lines or dots), Grids In Back.
The canvas's context menu, panel drops and hover highlight pick objects like
the selection tools. The control channel gets ui.wheel (with modifiers held
like ui.click) so agents can turn the wheel. The rest of #394 stays open.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Under Wayland the window is native but arboard was built without its
data-control backend, so it read the X11 clipboard through XWayland,
which only holds what X11 apps copied: Paste found nothing and reported
an empty clipboard (egui's text paste into fields uses smithay-clipboard,
which is why that worked). arboard now has `wayland-data-control`, the
same as PhotoCraft; it falls back to X11 where the compositor lacks the
protocol (GNOME's Mutter).
The reporter's clipboard also held text/uri-list: with a file copied in a
file manager, Paste would have taken the path as text before the bitmap.
Copied files now paste first, as the first one that is art (SVG, PDF,
EMF/WMF or a bitmap), told from its first 4 KiB by the engine's format
detection (`clipboard::file_flavour`): text/uri-list through arboard on
Linux and macOS (a trailing CR from CRLF lists trimmed), CF_HDROP on
Windows, read with the clipboard closed again.
Dropping files on the window can't work under Wayland yet: winit 0.30,
which eframe 0.36 runs on, has no Wayland drag and drop (winit#1881,
added in 0.31), so no dropped_files arrive. docs/development.md documents
it and the workarounds (copy and paste the files, File > Place, or run
under XWayland with `WAYLAND_DISPLAY=`).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Find Font and the Glyphs panel were widgets::font_dropdown's last
callers; the font menu's own tests cover searching and Enter. ROADMAP:
the Glyphs panel and Find Font's Replace With use the font menu, without
its live preview.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
text.setRangeStyle takes charAlign, so the panel menu works on the
characters selected with the Type tool (it routes there while text is
edited, and charAlign alone was answered with "nothing to change").
One char_align_param parses it for both commands. The menu shows with
Preferences › Type › Show East Asian Options, like Mojikumi Set and
Top-to-Top Leading. Rows in the Simplified Chinese, Spanish and
Brazilian Portuguese catalogs; the Japanese Em Box Center names the em
box. ROADMAP: the gating, the range command, and ICF top/bottom still
missing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Characters of different sizes on one line all sat on the Roman baseline.
Japanese layout usually lines them up by their ideographic em boxes
instead (a 16 pt 第一回 next to 32 pt 雅楽 centred on it, not sitting on
its baseline). The Character panel menu now has Character Alignment:
- `CharStyle.char_align`: Roman Baseline (as before, the default), or the
em box Top/Right, Center or Bottom/Left: a character smaller than the
line's largest moves so that its em box top, centre or bottom lines up
with the largest's (the em box from the font's vertical metrics, else
0.12 em below the baseline to 0.88 em above it). Vertical type the
same across the column (top → right). Written only when not Roman
Baseline.
- `text.setFormat {charAlign: "romanBaseline"|"emBoxTop"|"emBoxCenter"|
"emBoxBottom"}`; the menu shows the selection's setting; catalog rows
in ja and zh-hant.
- Not yet: aligning on the ICF (average character face) top and bottom.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Fidelity slider names its ends, Accurate and Smooth, through a
shared form::slider_ends that the Color Guide Options' Less/More now use
too. The dialog's strings get Spanish rows (complete catalog), and
Accurate rows in every catalog. ROADMAP: the Drawing tools row lists
what the freehand Tool Options still lack (Keep selected, Alt to the
Smooth tool, Blob Brush Merge Only with Selection, brush angle and
roundness).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The five freehand tools keep options (Fidelity, fill, the two
tolerances, the brush size) that last across tool switches and are
saved with the preferences, but nothing in the window showed or changed
them: a double-click on the tool's button answered "the pencil tool has
no options", which the toolbar drops. It now opens a Tool Options dialog
with the options the tool has, and OK sets them with tool.setOption. The
dialog's strings are in the Japanese, Chinese (both scripts) and
Brazilian Portuguese catalogs.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Select All (Cmd/Ctrl+A) now follows the keyboard, as in Illustrator:
- macOS menu bar: the system menu takes Cmd+A/X/C/V before the window
sees them, so a focused panel, dialog or search field got the document
command. `menus::invoke_from_system_menu` (used by `NativeMenu::poll`)
hands Select All, Cut, Copy and Paste to a focused text field as the
events its keys would send; clicks on those menu items follow the same
rule. Other commands, and these with no field focused, run as before.
- Type tool: `select.all` itself selects all of the text being edited
while the Type tool edits (returns {editing, start, end}), so the menu,
the shortcut, the control channel and headless MCP (`press_key`) agree.
The UI's own Select All interception and the Type tool's hard-wired
Cmd+A are gone; Cmd+A reaches it through the Select All shortcut.
- Windows, Linux and the web already left Cmd+A to a focused field; now
covered by headless app-frame tests (field, Type tool, canvas).
Docs: docs/mcp.md (Select All while editing type), ROADMAP. Not yet:
Select All across threaded frames (one frame's text is selected).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SVG import kept a <text>'s nominal font-size and left the scale of the
transforms and viewBox above it in the type's transform, so the Character
panel showed one size and the canvas drew another (7 pt under scale(3)
drew at 21 pt).
The importer now moves that scale into the type: the scale across the
baseline (the mean scale for type on a path) multiplies the size,
leading, baseline shift, character stroke weights and dashes and the
text path, and the transform keeps only the rest (a stretch along the
baseline, a skew, a rotation or a reflection). The type draws exactly as
before. Paints resolve in the grown text space, non-scaling strokes and
the stroke under the characters are worked out before the fold, the
factor is rounded to 6 significant digits (usvg's transforms are single
precision) and never pushes sizes past the Character panel's
0.1–1296 pt (the rest stays in the transform).
Tests: a group's, the text's own and a viewBox scale; rotated and scaled
text equal to the same text written at twice the size; stretch and skew;
type on a scaled path; the size limits; gradients on scaled text; SVG
export → import of scaled type (stable over two round trips).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
When the browser dropped the page's WebGPU device or WebGL context, the
editor kept running but nothing it drew reached the screen: Chrome and
WebKit showed a frozen, stale canvas and Firefox a blank page, while
edits and Save went on.
- ui-egui: new `graphics` module. `GraphicsLoss` carries a loss report
from any thread (wgpu's device-lost callback, the canvas's
`webglcontextlost` event) to the UI thread. `VectorcraftApp::
graphics_lost` writes Data Recovery copies of the modified documents at
once. The app now notices when the host hands it a new egui context and
uploads everything again (fonts and theme, the canvas, place cursor
thumbnails); every per-thread texture cache (layer, symbol, swatch,
style, asset and glyph thumbnails, previews, font samples, Navigator,
Flattener Preview) is a `TexCache` that empties for a new context.
Without this, the moved app drew stale or wrong textures and egui
panicked on its missing font families.
- web: the shell watches both loss signals; on a loss it puts a fresh
canvas in place of the dead one and starts the same `WebRunner` again,
handing over the same app (documents, undo, panels), with services made
for the new context and a status-bar note. If no new graphics can be had
(or after three losses) it removes the stale canvas, lets go of the
recovery copies and says to reload.
- engine: `recovery::leave` lets go of a session's area but keeps its
copies, so the reloaded page offers them at once instead of after the
heartbeat goes stale.
- desktop: eframe can't give a window a new device, so a lost device
(driver reset) is logged, the copies are written and the status bar says
to save and restart; closing then skips the Save Changes question it
can't show.
- Tests: loss reporting across threads, `TexCache` emptying, the app
uploading the canvas, a cached preview, theme and fonts again in a new
context (fails without the fix), copies written on loss, and
`recovery::leave` offering copies at once with both lock and heartbeat
stores. docs/development.md (web section) and ROADMAP updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A live rectangle stored four corner radii but drew them all with the largest,
and the corner widgets always rounded all four corners, so one corner could not
be rounded on its own.
- geom: rectangle_with_corners draws each corner with its own radius and kind
(Round, Inverted Round, Chamfer); a sharp corner is one anchor, a cut one two.
Uniform corners keep the exact rounded-rectangle anchors.
- doc: LiveShape::Rectangle gains `kinds` (saved only when a corner isn't round;
older files read as round), plus helpers mapping anchors to corners.
- engine: object.setLiveShape takes `corners` and `kind`; without `corners` it
edits the Direct-Selected corners, else all four, and keeps the selected
corners selected as their anchors change.
- tools: with Direct Selection, only the selected corners show widgets and a
drag rounds just those; Alt-click cycles the corner kind; double-click opens
the new Corners dialog (also `ui.corners`).
- ui: Properties/Transform radius fields show and set the selected corners;
CSS export keeps per-corner radii and skips cut-in corners.
- docs: mcp.md (Live Corners), control-protocol.md (Corners dialog), ROADMAP.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An object Illustrator writes as a fill of its outline left open and then a
stroke of it closed imported as two objects: a filled path and a separate
stroke-only path. A fill and a stroke of the same outline (equal once every
subpath is closed) now become one path with the fill and a live stroke
(width, cap, join, miter limit, dash, paint), taking the stroke's outline.
Layers: a group whose view state is off (/Usage /View /ViewState /OFF, with or
without an /AS view event) comes in as a hidden layer with its art, as groups
off in the default configuration already did. The configuration's /Order
nesting (inline or as its own object, labels skipped) makes sublayers, which
take their top-level layer's colour; nesting depth, entries and groups are
capped. Splitting a page's art by group is no longer quadratic, so thousands
of layers import quickly.
The 6D art pen (rotation-aware brushes) is a separate feature, now on the
ROADMAP gap list.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Shape Builder only used the filled areas of the selected paths, so a
stroke-only line or open path across a shape did nothing. Like the
reference app's Shape Builder, open paths now act as cutting edges:
- pathops::shape_builder: with an open path in the selection, the
regions are the Live Paint planar map's faces (every path an edge), so a
line across a shape splits it and lines can enclose a region of their
own; it also returns each open path cut where other paths meet it. Closed
shapes alone, more than SHAPE_BUILDER_MAX_SEGMENTS segments, or a map
that can't be built fall back to the filled-area arrangement.
- Filled open paths count as closed (an invisible closing edge) where that
encloses an area; unfilled open paths and straight lines stay edges.
- shapeBuilder.merge: merging or deleting regions on both sides of a line
piece removes that piece (the line is split, its other pieces kept); an
area only lines enclose takes the current fill and the front-most
object's look. Alt (erase) within `tolerance` of a line piece deletes it.
Returns `lines` (pieces deleted).
- shapeBuilder.regions also lists the line pieces.
- The tool shares BuilderMap/Touched with the command, highlights the line
piece under the pointer while Alt is held and passes its tolerance.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A heartbeat alone can't tell a crashed tab from one whose timers stopped in
the background, so another tab could discard (or restore) the copies of a
still-live document once its heartbeat went stale.
- Recovery stores without locks can now announce an area for as long as the
app runs (RecoveryStore::announce/announced). The web shell implements it
with the browser's Web Locks (navigator.locks through js_sys::Reflect): a
tab holds a lock named after its area, which the browser releases only when
the tab is gone. Lock snapshots are refreshed every 10 s (once before the
first frame); a snapshot over a minute old counts every area as held.
- An area is gone only when no announcement holds it and its heartbeat is
stale. Where the store can't tell (no Web Locks), the owner repairs: each
heartbeat (also its first frame after a pause) and each recovery save write
again the copies missing from its area, and a heartbeat newer than the one
another tab wrote when taking the area over marks it running again.
- MemoryStore::announcing models Web Locks in tests; engine and UI tests cover
the issue's sequence. Native folder locks are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Japanese layout measures leading from the top of one line's ideographic
em box to the next (the right side, in vertical type), not from baseline
to baseline. The Paragraph panel menu now offers both:
- `ParaStyle.leading_model`: RomanBaseline (as before, the default) or
EmBoxTop. With EmBoxTop a line's leading is the space below it — the
next line's em box top is this line's plus its leading, and the next
baseline hangs from there by that line's tallest em box — and area
type's first line touches the top of the frame (its em box top on the
frame's top, at least Area Type Options' minimum). With baseline
leading a line's leading stays the space above it.
- Lines of one size are exactly as far apart either way; the models
differ for mixed sizes and for the first line of area type (fonts
whose ascent isn't the em box top: Hiragino's nearly is).
- The em box top comes from the font's vertical metrics (#343), else
0.88 em. Written only when EmBoxTop.
- `text.setFormat {leadingModel: "romanBaseline"|"emBoxTop"}`; menu items
Top-to-Top Leading / Bottom-to-Bottom Leading (checked as set); catalog
rows in ja and zh-hant.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Rotate, Scale, Reflect and Shear tools answer Enter with their
dialog, but the shortcut handler only applied what a tool asks of the
UI for the keys it claims, so the request was dropped and Enter did
nothing. It now applies it for Enter, and consumes the key when a
dialog opened, since a dialog takes Enter as its OK in the same frame.
The Selection, Direct Selection and Group Selection tools open the
Move dialog the same way, on Enter and on a double-click on their
toolbar button (`tool.options`), as Object > Transform > Move does.
With nothing selected neither opens anything.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Opening a PDF whose fonts aren't installed keeps the text live in the fallback font (as
Illustrator substitutes and highlights missing fonts), and opening said so, but every export
then wrote the fallback font silently ("warnings": []), so a headless convert re-set the type
without a word.
- Exports that draw type now say so in their warnings, naming the missing families and the
fallback font: PDF (outlined or real text, also .ai and Save As), EPS, EMF/WMF, raster images
and SVG with outlined or embedded fonts. Live SVG text, DXF, text and native files name the
font itself and don't warn. Replacing the font (Find Font) clears it.
- The fonts a document uses (text.fonts' scope, Package's font list) come from one helper.
- textAs: outlines already keeps the glyphs the PDF draws (its embedded font program, installed
or not): now covered by a test with an embedded serif font that isn't installed, checked
against the installed font's and the fallback's outlines.
- docs/mcp.md and ROADMAP describe the warning.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A PDF's DeviceCMYK image opened as RGB (the interpreter's pixels as a PNG),
so every export wrote it as DeviceRGB or ICC RGB, silently. Images now keep
their CMYK samples, as CMYK colours already did:
- Model: CMYK images are blobs holding CMYK samples, a four-component JPEG
(Adobe's inverted samples) or a Deflate CMYK TIFF. vectorcraft-doc's cmyk
module reads their ink amounts (ImageBlob::is_cmyk, ::cmyk, capped at 64
megapixels) and writes the TIFF (ImageBlob::cmyk_tiff).
- PDF import: DeviceCMYK, CalCMYK and four-component ICC images keep their
samples. A JPEG with Decode [1 0 1 0 1 0 1 0] is kept byte for byte; any
other is decoded (8 or 16 bits, its Decode array applied) and stored as a
CMYK TIFF. Masked CMYK images and 1/2/4-bit ones open in RGB with a warning.
Placing a CMYK TIFF keeps it CMYK too.
- PDF export: CMYK images are written in CMYK (DeviceCMYK, or ICC-based with
the CMYK profile when colours are tagged: PDF/X-3, PDF/X-4, PDF/A). A CMYK
JPEG neither resampled nor recompressed passes through unchanged; others are
resampled in CMYK and compressed as ZIP or as a CMYK JPEG (jpeg-encoder,
which also replaces the ZIP fallback and its warning for images converted to
CMYK). Output conversion treats them like CMYK colours: Preserve Numbers and
PDF/X-1a keep their numbers, Convert to Destination converts them to another
CMYK profile or to RGB through a 17^4 lookup table (cms::CmykLut, now also
behind the renderer's ink table). A CMYK image that can't be decoded is
reported in the warnings.
- Canvas: CMYK images show through the working CMYK profile, as CMYK fills
do, and paint their own inks in ink planes (CMYK raster exports, CMYK
blending). SVG export writes TIFF images as PNG.
Illustrator's Output › No Conversion keeps colour data as it is
(https://helpx.adobe.com/illustrator/using/pdf-options.html).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A PDF-compatible .ai paints the editor's page (an opaque white rectangle the
size of the page) before its first optional-content layer and outside every
layer. It came in as art: an extra "Page 1" layer with a page-sized white path
behind the artwork, which hid white art and gave SVG/PNG/PDF exports an
opaque background.
For pages with the editor's private data (the way the importer already tells
a .ai from a PDF), the first art outside every layer, painted before any
layer, is dropped when it is a plain fill-only rectangle in an opaque,
non-spot white, with normal blending, equal to the crop box (0.5 pt
tolerance). Plain PDFs keep such a rectangle as art (as the reference app
does when opening a PDF), and so does anything else: grey, smaller or larger
rectangles, strokes, translucency, white inks, fills over the layers.
The white-cover test the soft-mask reader used is now a shared helper.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds a Spanish catalog (crates/ui-egui/src/i18n/es.tsv) covering the whole
interface like Japanese and Traditional Chinese: every menu string, every
tl! literal, the hint-bar fragments, plurals and the runtime strings the
other complete catalogs carry. It is written from the meaning of the
English labels in neutral, international Spanish (Latin American usage)
with the vocabulary users of vector illustration software know (Mesa de
trabajo, Muestras, Trazo, Relleno, Trazado, Pluma, Selección directa,
Buscatrazos, Motivo, Preajuste); no other application's localisation was
copied. Like Japanese it keeps the product, OpenType, the built-in
workspace names and the perspective grid preset names in English.
The language is registered with complete_menus (so the completeness
tests enforce it) and the one/other plural rule; every es-* system locale
(es-ES, es-MX, es-AR, es-419 ...) resolves to it.
Tests: es locale tags, Spanish vocabulary and plurals, Spanish letters in
the bundled UI fonts, and a new check that catalogs written with spaces
keep the leading/trailing spaces of joined fragments. ROADMAP, README and
docs/development.md list the language.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Drop the workspace [patch.crates-io] and restore windows-link's crates.io
source and checksum in Cargo.lock, so every normal build keeps the
checksum-verified crate. windows7.ps1 now applies vendor/windows-link with
--config, checks that only windows-link's lock entry moved, builds and tests
with --locked, checks the patched crate is the one in use, and restores
Cargo.lock afterwards.
- windows7.yml runs on demand and weekly instead of on every PR, pins every
action by commit SHA, and leaves the dependency check and host tests to the
script (-Test) so the Cargo arguments live in one place.
- ROADMAP, README, NOTICE and docs: experimental, unsupported, not verified by
maintainers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Up/Down stepping of #357 now serves every numeric field through one
helper: num_field (lengths, in their unit) and plain/mixed fields
(degrees, percent, counts, at their precision: a count ignores the
Ctrl/Cmd tenth), as in Illustrator's panels and dialogs. After a step
the new value is selected, so typing replaces it. The select-all code
the dialog focus and focus/double-click already duplicated becomes one
helper, and the tests move to tests_numfields.rs with its field harness
(which can now run with the window unfocused). ROADMAP.md notes it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With Alt held, a press on a path's stroke selects the whole path and
an Alt-drag copies it, as before #363 and as Group Selection does in
Illustrator; only a plain press on a segment selects its anchors and
reshapes it. Records the segment drag in ROADMAP.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Finder, Open With, a drop on the Dock icon and `open -a` failed with "VectorCraft cannot open
files in the PDF document format":
- The release Info.plist declared no document types, so Launch Services refused the files before
the app saw them. Info.plist.in now declares every format File › Open reads (the engine's
OPEN_EXTS; a test keeps them in step): VectorCraft owns its own documents and templates and is an
alternate for the others, so it takes over no other app's files. `cargo xtask bundle` now fills
in the same template instead of its own, shorter plist.
- macOS delivers such files as open-document events, not arguments. winit 0.30's application
delegate has no `application:openURLs:`, so AppKit fell back to NSDocumentController, which
refuses every file of a non-NSDocument app (the error in both issues). The new
`open_documents` module adds that method to the delegate's class when the app is about to finish
launching (before AppKit delivers launch files), queues the paths and wakes the UI, which opens
them with `io::open_path` like command-line files; errors go to the status bar too.
New direct macOS dependencies `objc2-foundation` 0.3 and `block2` 0.6 were already in Cargo.lock
(through winit, muda and rfd). The module allows `unsafe_code` for the Objective-C runtime call.
Fixes#354
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The web reads dropped files asynchronously, and the place inbox only kept
the drop point, so a file dropped on document A was placed into whatever
document was active when its bytes arrived. A drop on a canvas now carries
place::DropAt (the document's uid, the point, embed), and drop_files makes
that document active again before placing; when it has closed meanwhile,
nothing is placed and the status bar says so. The native drop path is
synchronous and goes through the same code, so it is unchanged in effect.
Test: a drop pending while another document opens lands in the first one
at the drop point; one whose document closes is not placed anywhere.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Taiwan vocabulary left over from the zh-hant rows #303 started from is
replaced with the mainland's, about 560 rows: 档案 → 文件, a Taiwan 文件
(document) → 文档, 工作区域 → 画板, 笔画 → 描边, 色票 → 色板, 遮色片 → 蒙版,
渐层 → 渐变, 偏好设定 → 首选项, 快速键 → 快捷键, 按一下 → 单击, 描述档 → 配置文件,
品质 → 质量, 汇出/汇入 → 导出/导入, 连结 → 链接, 点阵 → 栅格, a row 列 → 行,
「」 → “”, half-width parentheses; tool names that collided or were wrong
are fixed (Selection Tool 选择工具, Scale Tool 比例缩放工具, Warp 变形,
Puppet Warp 操控变形, Divide 分割, Exclude 差集, the 直排 type tools).
- 42 rows for strings that landed after #303 (Layers panel, graphics
processor preference, panel collapse, the font menu's filters).
- A catalog header documenting the format and the vocabulary, and a test
that zh-hans has no Traditional characters or Taiwan terms.
rows were the better base (hand-adjusted mainland terms in the menus and
dialogs), so no rows were taken from #316.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Illustrator sets a paragraph's direction (Left-to-Right / Right-to-Left
Paragraph Direction in the Paragraph panel, with the Middle Eastern
options) and keeps its alignment buttons physical; it has no automatic
alignment toggle. So:
- ParaStyle.direction (None: from each paragraph's first strong
character, as before; written only when set), the Paragraph panel's two
direction buttons with Show Indic Options (original icons), and
text.setFormat {direction: "auto"|"leftToRight"|"rightToLeft"}. The
layout uses it as the base direction.
- The Automatic alignment check box is gone. Justify::Auto stays as the
start of the paragraph's direction, set on new type only (text.create,
the Type tools, with the mojikumi default in one new_type_para()):
TextObject::point is Left again, so importers (PDF, SVG, DXF, EMF) keep
their own alignment. The alignment buttons show what Auto resolves to.
- Bidi only where there is something right to left: plain paragraphs skip
the algorithm (no per-layout allocation), it runs once per paragraph
(shaping takes the paragraph's levels instead of recomputing them; path
type computed it four times), and the shaper keeps Han, Hiragana,
Katakana and Bopomofo in one segment (splitting at every change of
script cost Japanese text a shaper call per kana-kanji switch).
- Vertical type keeps its logical order, so its glyphs and lines are not
marked right to left (the caret no longer runs backwards down a column).
- PDF import puts Hebrew and Arabic, which PDFs (and VectorCraft's own
export) draw in visual order, back in logical order with the direction
that shows it as drawn; with bidi layout it came back mirrored.
- SVG import pins the direction SVG implies (left to right unless
direction: rtl); SVG export also outlines right-to-left paragraphs.
- Docs (development.md), ROADMAP (folded into the Type entry, Type core
row) and README updated; catalog rows for the new labels.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Unicode bidi (unicode-bidi, unicode-script, unicode-general-category):
each paragraph's direction from its first strong character, Hebrew and
Arabic shaped right to left, embedded Latin and numbers keep theirs,
lines wrapped in logical order and reordered per line by whole
clusters; source text, undo and style ranges stay logical.
- Caret, hit testing, selection and the arrow keys follow the visual
order.
- New text aligns automatically: right-to-left paragraphs right, others
left (Justify::Auto, text.setStyle {justify: "auto"}); the Paragraph
panel highlights the resolved alignment and has an Automatic alignment
toggle. Explicit alignments, and older documents', are kept.
- SVG export outlines bidirectional text (with a warning) until it can
be written as directional live text.
A file's saved selections are untrusted: on load at most 25 are kept, names
are trimmed, capped at 255 characters and unique, and ids the document
doesn't have are dropped (after fix_next_id such an id would select whatever
object is drawn next). Save and Edit Selection cap names the same way. The
limit, the name cleanup and the default "Selection N" live on
SavedSelection, shared by the engine, the Save Selection dialog and the
Select menu's slot table. docs/mcp.md documents the select.save /
savedList / recall / editSaved commands; ROADMAP counts 13 stubbed menu items
and names what's still missing (Update Selection, the Layers panel menu).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
normalize_panel walks DockTab::ALL and ICON_PANELS instead of repeating the
dock tab ids. vectorcraft://object/{id} turns only a missing object into
-32602; no document or an unreachable app stay backend errors, as before.
docs/mcp.md documents document.node's summary flag and the panel names
open_panel takes; control-protocol.md the window.panel names. The ROADMAP
states the measured speed-up (a leaf read on 2,000 objects: ~20 ms to ~0.06 ms).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- 28 rows for strings that landed on main after #317 was written (Layers
panel menu and options, Release to Layers, the Graphics Processor
preference), so the Japanese catalog stays complete.
- Fixes: Live Paint Bucket is ライブペイントツール (was the selection
tool's name), the File Info rating stars read {n} つ星 (were "rays"),
Flare Tool Options and Symbol Sprayer match their tool names, Rectangular
Grid matches its tool (長方形グリッド), recovery data is 復帰用データ
throughout, (今後対応予定) for every roadmap note.
- Consistent spelling: フォルダー, 切り替え, Web サイト, 英語 (米国), and
a half-width colon after every field label, as in the other 200 rows.
- tests: KEEPS_MENU_NAMES gets its own doc comment instead of taking
MENU_KEEP_AS_IS's.
- ROADMAP and docs/development.md: Japanese ships complete, Czech covers
the menus.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Paragraph panel's mojikumi control is one of the panel's options
(hidden with Hide Options) and appears when Preferences > Type > Show
East Asian Options is on, as Illustrator shows its Mojikumi and Kinsoku
sets; it is labelled Mojikumi Set. ROADMAP notes where it lives.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add Snapping to the Artboard Tool
Merged with the Artboard tool's Alt-drag duplicate (#348): a copy snaps too, and to the artboard
and art it leaves behind (a move skips them).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Text in fonts that aren't installed comes in as live type in the fallback font again, named as
before (Helvetica-Bold → Helvetica Bold), not outlines plus pink copies on a non-printing layer.
- The installed face that matches a PDF font is decided once per font from its first comparable
glyph, not per glyph (loading up to 64 faces each time, and switching style mid-line).
- Kangxi radicals are unified only for text from Adobe's CID tables, not ToUnicode maps.
- A line is laid out again only when tracking changed it.
- CidText::of is tested on data that isn't a CID-keyed CFF (embedded fonts are untrusted).
- NOTICE: Adobe CMap resources (BSD-3-Clause) bundled through hayro-cmap.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add the crates/ui-egui/src/i18n/pt-br.tsv catalog (Brazilian Portuguese):
every menu label, every tl! literal of the shell and the plural messages
(2533 strings + 13 plurals), written clean-room in ordinary Brazilian
Portuguese computing vocabulary, placeholders and trailing ellipses kept
in lockstep with the English source.
Register the language in i18n::LANGUAGES (code pt-br, native name
"Português (Brasil)", one/other plural rule) so the Language menu, the
preference and the system-locale match (pt_BR → pt-br) pick it up.
Extend the menu-label test to pt-br: it covers every menu label like cs
and ja, and keeps the MENU_KEEP_AS_IS strings (product name, language
names, workspace names, perspective grid presets) in English.
Update docs/development.md and ROADMAP.md accordingly.