Commit Graph
12259 Commits
Author SHA1 Message Date
dotCooCoo 434cf3e692 Graphs: graphic styles and Default and Swap Fill and Stroke update a series' stored paint
Applying a graphic style, Redefine Graphic Style, Default Fill and Stroke
and Swap Fill and Stroke on a path in a graph series changed the path
but not the paint the graph stores for the series. When the graph was
drawn again, the series went back to its earlier paint.

These commands now store the mark's topmost visible fill and stroke on
the graph, in the same undo step as the command:

- graphicStyle.apply stores the paint, weight and opacity of both kinds
  of row. With `add` it stores only the kinds of row that are visible
  in the style.
- graphicStyle.redefine stores the paint, weight and opacity of the
  marks still linked to the style.
- paint.default stores the white fill and the 1 pt black stroke, both
  opaque.
- paint.swap stores the swapped paints and the stroke weight and keeps
  the stored opacity. It swaps the topmost rows even when they are
  hidden, and a hidden row it swaps is not stored.

The rule for each kind of mark is the one docs/mcp.md gives for the
Appearance commands. docs/mcp.md also says what the graph does not
store from a graphic style and that a series group given to a graphic
style command is not stored. The params text, docs/mcp.md and the
Graphs line in docs/target-app-parity.md list the commands.
2026-11-07 02:54:42 -08:00
Don Omondi 9c8eef4111 EPS import: a sampled function's sample reads count against the operation budget
Each evaluation of a sampled function copied its DataSource and read
every tap combination without counting that work: a shading evaluates
its function once per stop, so a long DataSource or many interpolated
inputs did far more work than the budget charged. Both now count. The
nearest-sample fallback is kept at the last sample, which a fractional
Size could put it past.
2026-11-07 05:24:15 -05:00
Taha-Mahmoodi 87978f8de2 Text: the Kufi joining test checks the drawn ink on the installed font, never skips when it is there, and covers ss01-ss05 2026-11-07 14:39:58 +04:30
Don Omondi 120bcf102e .ai import: an Art brush with neither form's count of numbers isn't read
An Art brush object whose last field had neither 15 numbers nor the
older form's 8 came in with default settings. It is now left out like
other brushes whose values can't be read. An older-form Art brush keeps
the Art brush's default colorization, as the note says.
2026-11-07 05:07:25 -05:00
Don Omondi 28f8f84fd5 EPS import: strips under a larger clip keep the clip
A run of gradient strips under a clip larger than the strips became the
clip path filled with the gradient, painting where the strips did not.
The clip path now takes the gradient only when every point of the clip
lies inside the run; otherwise the gradient stays clipped to the strips'
area.
2026-11-07 05:01:25 -05:00
ruchirguitar e6a899857f M2.1: split disconnected path pieces after deletion (#3380) 2026-11-07 15:20:14 +05:30
Taha-Mahmoodi 7e45947399 Glyphs: Alternates for Current Selection applies a feature value to one letter; Entire Font lists unencoded glyphs
The grid lists the alternates of the glyph the shaper makes for the selected character
in its word, and a double-click applies the feature that draws it (salt=2) to that
character alone in one undo step, so an Arabic word stays joined. A variant no feature
draws keeps the replacement-glyph fallback, with a tooltip. Entire Font adds glyphs no
character maps, with name or gid and the feature reaching them. i18n rows in every
catalog; parity docs updated (#5514).
2026-11-07 14:19:24 +04:30
Taha-Mahmoodi b563976b4d Text: OpenType feature values (salt=2), a GSUB reader and runs that differ only in features shaped as one piece
Features take a value (tag=N) in style commands, SVG and native files. Runs that
differ only in their feature lists are shaped in one buffer with ranged features, so
an alternate on one letter of an Arabic word keeps the word joined. FontFace reads
GSUB once (alternate sets, singles, the feature reaching a glyph), names unencoded
glyphs, and alternates_in_context finds a character's alternates in its word.
text.setRangeStyle takes glyphAlternate: null.
2026-11-07 14:13:48 +04:30
Taha-Mahmoodi 3a1421e95e docs: restore the status lines damaged by merges (main's line plus this PR's note) 2026-11-07 13:51:03 +04:30
Taha-Mahmoodi 6523828615 docs: restore the status lines damaged by merges (main's line plus this PR's note) 2026-11-07 13:51:03 +04:30
dotCooCoo 0bc150c51e UI: save the Help Bar setting to preference files that lack it
A preferences file from before the Help Bar setting gets `help_bar` from
`status_bar` when it is read. A save that changed only the status bar
left `help_bar` out of the file, so the next start hid the Help Bar with
the status bar. A save now writes `help_bar` whenever the file lacks it,
and keeps a value another window saved.

Docs: the UI evidence list, the UI checklist and the M14 row record
Window > Help Bar.
2026-11-07 00:18:45 -08:00
Taha-Mahmoodi c1e35047e4 Merge remote-tracking branch 'origin/main' into fix/import-document-title
# Conflicts:
#	docs/file-format-parity.md
#	docs/gaps.md
#	docs/target-app-parity.md
2026-11-07 12:40:04 +04:30
Taha-Mahmoodi 727bc8790c Merge remote-tracking branch 'origin/main' into fix/ruler-double-click-guide
# Conflicts:
#	crates/ui-egui/src/canvas/tests.rs
#	docs/gaps.md
#	docs/target-app-parity.md
#	docs/ui-parity.md
2026-11-07 12:40:04 +04:30
dotCooCoo 9fe7aec49c Graphs: Appearance commands and the Eyedropper update a series' stored paint
paint.setFill, paint.setStroke, paint.none, Apply Fill to Stroke and Apply
Stroke to Fill stored a series' paint on the graph. appearance.setItem, the
Eyedropper, appearance.transfer, Clear Appearance, Reduce to Basic Appearance
and the add, remove, move and duplicate row commands stored no paint or weight.
An Eyedropper pick of a blue fill at 40% onto a red column drew red at 40%
after the next redraw.

These commands and Show All Hidden Attributes now store the paint and weight
of the topmost visible fill and stroke of a path directly in a series group,
in the same undo step. A color, weight or opacity that a command writes to that
row replaces the stored value even when the mark's own value does not change. A
row that has no paint before and after the command does not change the stored
paint. These commands store no stroke for a series that draws Filled Lines
bands, and a band no longer takes its color from an empty stored stroke.

The stored opacity also comes from the topmost visible row: hiding a top row
stores the row under it, and an opacity set on a hidden top row is not stored.

Follows #5501.
2026-11-07 00:07:40 -08:00
dotCooCoo 73e216bd9d Docs: the parameter-key description names the export commands as an exception
The earlier paragraph said document.export takes any key when its description names none. The export commands refuse an unknown option key, so the paragraph now lists them as an exception and keeps document.save as the example that takes any key.
2026-11-07 00:01:56 -08:00
dotCooCoo 6ffcf800c8 Export: the export commands refuse an unknown option key
document.export, document.exportSelection, document.serialize, app.export
and file.export took {noSuchOption: true} and wrote the file with no
warning. They now refuse a key that no format lists in document.formats,
and name the nearest key when one is close. document.exportPdf,
document.pdfSettings and pdf.preset.save refuse a key PDF doesn't take,
document.exportEps one EPS doesn't take and document.exportDxf one DXF
doesn't take. Each refuses an unknown key inside an option object it takes,
such as marks.useDocumentBleed, and names its path.

A key that only another format reads, such as {format: pdf, quality: 80},
is still ignored: docs/mcp.md says one object can carry several formats'
options. The check runs for callers (the control channel, MCP, the CLI).
A journal or a recorded action isn't checked, and a saved PDF preset with
a key that is no longer read still loads.

document.formats now also lists PDF's thumbnails, fastWebView and
viewAfterSaving.
2026-11-06 23:09:15 -08:00
dotCooCoo 72af76d418 Artboards: a Cut or Copy with the Artboard tool replays on its artboards
edit.copy and edit.cut chose the artboard route by the tool chosen, which
the journal does not record. A replay in a new session acted on the objects
selected there, or stopped with "nothing selected" when there were none.

With the Artboard tool chosen, both now record selectedArtboards and art in
the journal entry. A given selectedArtboards runs the artboard route with
any tool and needs only a document. A recorded action leaves both values
out, so it follows the tool chosen when it plays. Cut and Copy of objects
record nothing new.
2026-11-06 21:35:41 -08:00
Don Omondi 5817cabb3a Merge remote-tracking branch 'upstream/main' into fix/eps-memory-cap
# Conflicts:
#	docs/file-format-parity.md
2026-11-07 00:12:50 -05:00
Don Omondi fbccefe17e Merge remote-tracking branch 'upstream/main' into fix/eps-sampled-function-shading
# Conflicts:
#	docs/file-format-parity.md
2026-11-07 00:12:44 -05:00
Don Omondi f906de86ff Merge remote-tracking branch 'upstream/main' into feat/ai-import-stacked-fills
# Conflicts:
#	crates/eps/src/import/ai/tests.rs
#	docs/file-format-parity.md
2026-11-07 00:12:40 -05:00
Don Omondi e6e8dde874 Docs: keep this branch's entries that the merge with main dropped 2026-11-07 00:12:00 -05:00
Don Omondi fc7e1bf0bd Docs: keep this branch's entries that the merge with main dropped 2026-11-07 00:11:56 -05:00
Don Omondi 21dc2bceff Merge remote-tracking branch 'upstream/main' into fix/ai-import-brushes
# Conflicts:
#	crates/eps/src/import/ai/brush.rs
#	docs/file-format-parity.md
#	docs/target-app-parity.md
2026-11-07 00:09:24 -05:00
Don Omondi d1b45aec9f Merge remote-tracking branch 'upstream/main' into fix/eps01-editing-overlay
# Conflicts:
#	docs/file-format-parity.md
#	docs/gaps.md
2026-11-07 00:09:19 -05:00
BFlat ff9deee46d Merge pull request #6015 from helenkwok/web/page-api
vectorcraft-web: window.vectorcraft takes its request functions from craft-web-api
2026-11-07 10:32:48 +08:00
BFlat 2b13421da5 Merge pull request #6012 from dotCooCoo/fix/ai-legacy-vertical-type
.ai import: legacy type keeps its writing direction, Tate-chu-yoko runs and upright Roman characters
2026-11-07 10:32:31 +08:00
BFlat c54679d4a7 Merge pull request #5985 from dotCooCoo/fix/text-story-current-thread
Type: story-wide panel changes follow the story's current thread
2026-11-07 10:32:10 +08:00
Helen Kwok f9bf685cbd vectorcraft-web: window.vectorcraft takes its request functions from craft-web-api
request, execute, commands and screenshot move onto the shared crate (storytold/craft-libs#413): a timeout on every call, the screenshot's PNG also in data, and docInspect and uiInspect beside them. A call the page gave up on gives up its request if the app has not started it, so the app skips it. openFile, captureDownloads, files, readFile and inspect stay VectorCraft's own, on the same object.
2026-11-07 09:18:00 +07:00
dotCooCoo fe9d62e05e .ai import: legacy type keeps its writing direction, Tate-chu-yoko runs and upright Roman characters
The legacy reader skipped `TV`, `Tv`, `Ty` and `TY` of section 15.3.4 of
the version 7 file format specification, so type set with `1 TV` opened
horizontal.

- `TV` sets a text object's writing direction. VectorCraft keeps one
  direction for each text object, the one in force at its first
  character, with a warning when a later character has another.
- `Ty` marks a Kumi block as a Tate-chu-yoko run, which is VectorCraft's
  own mapping, and `TY` does the same in horizontal type. A `TY` block in
  vertical type opens unmarked. Two blocks with nothing between them open
  as one block.
- `Tv` sets Standard Vertical Roman Alignment.

All four are modal, and an operand other than 0 or 1 leaves the value as
it was.

Docs: docs/file-format-parity.md.
2026-11-07 09:17:53 +07:00
dotCooCoo 1c2a8c7f7a Type: Justification after Select All covers a frame whose thread broke
After Select All in threaded text, the Justification dialog kept the edited
frame's selected range when that frame was no longer threaded at OK. A thread
broken after the frame moves the rest of the story into it, so the paragraphs
past the old range kept their values. The dialog now drops the range and
changes every paragraph in that frame.
2026-11-07 09:17:48 +07:00
dotCooCoo 24a42c5bb0 Type: Tabs indent markers apply to every paragraph when threaded text reflows
With mixed indents, the Tabs panel sends one text.setFormat per paragraph,
addressed by frame and byte offset. Threaded text reflows after each step,
so an indent that made earlier paragraphs wrap onto more lines moved text
into the next frame, and the later steps landed on the wrong paragraphs or
on none. The steps now run from the story's last paragraph to its first, so
each step's offset comes before every change already made.
2026-11-07 09:17:48 +07:00
dotCooCoo c592b31a1e Type: Tabs indent markers and Justification follow the whole story
After Select All in threaded text, the Tabs panel's indent markers read
the indents of the edited frame alone. When another frame's paragraphs
had a different left indent, a marker drag changed them by the edited
frame's values, or left them alone. The markers now take every
paragraph of every frame in the story, so each paragraph keeps its own
left indent or first-line position.

The Justification dialog kept the frames of the story from the moment
it opened. OK now acts on the frames in the edited frame's thread when
it is pressed, so a frame that left the thread while the dialog was
open is not changed, and a frame that joined it is.
2026-11-07 09:17:48 +07:00
dotCooCoo 40a46e539d Type: story-wide panel changes follow the story's current thread
After Select All in threaded text, the panels read the frames from the
list saved at Select All. A frame that had left the thread still took
Paragraph, Tabs, Justification and Text Styles changes, a frame added
later did not, and when the first saved frame had left, Character,
OpenType and font changes went to that frame alone.

text_story now reads the frames of the edited frame's thread from the
document. Locked and hidden frames stay in the story.
2026-11-07 09:17:46 +07:00
BFlat 845306373b Merge pull request #6017 from dotCooCoo/fix/ai-drawn-look-count
.ai import: drawn looks in pattern and brush art count in the note
2026-11-07 10:15:26 +08:00
BFlat 936f6034da Merge pull request #6014 from mockydevs/fix/ai-import-eid-heading
EPS/.ai editing data: a transparency set inside a clipping group stays after it
2026-11-07 10:15:10 +08:00
BFlat 7e3601d11c Merge pull request #6011 from mockydevs/fix/ai-import-blend-straight-spine
EPS/.ai editing data: blends with no spine written come in live
2026-11-07 10:14:54 +08:00
BFlat 7db6d41ec0 Merge pull request #5992 from mockydevs/fix/ai-symbols-before-art-styles
EPS/.ai editing data: symbols defined before the art styles take them
2026-11-07 10:14:38 +08:00
dotCooCoo 0d91749c74 .ai import: drawn looks in pattern and brush art count in the note
An object read as its drawn look counted in the import's "came in as
their drawn look" note when it was on the page or in a kept symbol, but
not when it was in a pattern's art or a brush's art. A file whose only
such objects were in a pattern or a brush opened without the note.

- A kept pattern's drawn looks count, once for each pattern name: a
  definition that a later one of the same name replaces no longer counts.
- A brush's art counts once a brush that is kept uses it; art no kept
  brush uses adds nothing.
2026-11-07 09:00:23 +07:00
Don Omondi ed00a8870e EPS/.ai editing data: a transparency set inside a clipping group stays after it
Q restores the painting state but not the transparency (Xy): the writer
sets Xy only when the next object's state differs from the last one it
wrote. Restored at Q, type after a clipping group following a Screen glow
drew in Screen and nearly vanished on a light background.
2026-11-07 09:00:20 +07:00
Don Omondi 95ac8d7cc7 EPS/.ai editing data: blends with no spine written come in live
A blend whose spine was never edited has only its keys in the editing
data, no spine part. Its spine is the straight lines between the keys'
centres in the record's order; read as such, the blend comes in live
instead of as its drawn steps.
2026-11-07 09:00:15 +07:00
Don Omondi 2626791d7d EPS/.ai editing data: symbols defined before the art styles take them
Illustrator 24 writes its symbol definitions ahead of %AI9_BeginArtStyles,
so a styled object inside a symbol found no style and came in as its drawn
look. The pre-pass that finds swatch names now also keeps the first
top-level art styles section, which the reader parses before the art.
2026-11-07 08:59:55 +07:00
Don Omondi 06179bbf19 Merge remote-tracking branch 'upstream/main' into fix/eps-memory-cap
# Conflicts:
#	docs/file-format-parity.md
2026-11-06 20:58:27 -05:00
Don Omondi 1b1af20685 Merge remote-tracking branch 'upstream/main' into fix/eps-sampled-function-shading
# Conflicts:
#	docs/file-format-parity.md
2026-11-06 20:58:27 -05:00
BFlat d2b82d6b32 Merge pull request #6005 from dotCooCoo/fix/prefs-set-folder-check
Preferences: prefs.set refuses a missing or unusable recovery or plug-ins folder
2026-11-07 09:57:12 +08:00
BFlat 4802247106 Merge pull request #6004 from dotCooCoo/fix/dialog-enter-repeat
Dialogs: a held Enter or Escape stops at the dialog it opened or closed
2026-11-07 09:56:52 +08:00
BFlat 0864d63ee2 Merge pull request #5988 from dotCooCoo/fix/offset-contour-check-index
Offset Path: the spurious-contour check tests only the source segments near each contour
2026-11-07 09:56:37 +08:00
BFlat 3637aebc5f Merge pull request #5983 from dotCooCoo/feat/artboard-context-menu
Artboards: the Artboard tool's canvas menu cuts, copies, pastes, renames, rearranges and exports artboards
2026-11-07 09:56:22 +08:00
dotCooCoo 9b9803aa0f Tests: the unusable-folder prefs test restores folder modes when it fails
set_refuses_a_recovery_or_plugins_folder_it_cannot_use takes write or
list permission away from two folders. A guard now gives each folder mode
0o755 when it is dropped, also when an assertion panics, so the test
folder is removed afterwards.
2026-11-07 08:38:20 +07:00
dotCooCoo 104d9562d4 Preferences: prefs.set refuses a missing or unusable recovery or plug-ins folder
prefs.set did not check the folder given as recoveryFolder or pluginsFolder,
beyond the automation roots check, so a control or MCP caller could save a
folder that does not exist, a file, or a folder the app cannot use. Only the
Preferences dialog checked the folder itself.

The dialog's folder checks move to the engine, and prefs.set runs them after the
automation roots check. A folder that fails returns the same error the dialog
shows, and no preference changes. An empty value, an omitted value and a folder
the preference already holds are not checked. The Wasm build skips the check, as
the dialog did. The dialog's OK now gets the check from prefs.set, and Choose…
calls the moved check directly.
2026-11-07 08:38:20 +07:00
dotCooCoo 20f35e4655 Dialogs: a held Enter or Escape stops at the dialog it opened or closed
Enter and Escape counted their key repeats as presses. Holding Enter with
a selection and a selection tool opened the Move dialog, confirmed it,
and opened it again at the key's repeat rate. After a drag, which gives
Move an offset, each round moved the object again.

The app now records what Enter or Escape went down on: the open dialog,
the command search, Edit Toolbar's drawer or Presentation Mode. When that
changes, the key's presses are removed from the input until it is let go.
The tool keys, the dialog's OK and the dialog's Escape also ignore
repeats, so holding Escape with the place cursor loaded discards one file.
A new press still acts, and Enter held in the Type tool still repeats line
breaks.

This also fixes these related faults:
- Ctrl+Enter or Cmd+Enter opened Move and confirmed it in the same frame.
- Holding the Enter that confirmed a dialog typed line breaks into the
  text being edited.
- A command bound to Enter reopened its dialog after the dialog's OK.
- Holding Enter on a value a field rejected confirmed the dialog.
- A command search that opened while Enter was down ran its first result.
- Holding Escape over a dialog's Tips closed the dialog as well.
- Holding Escape over a dialog went on to close Edit Toolbar's drawer,
  leave isolation mode or run a command bound to Escape.
2026-11-07 08:38:15 +07:00