Commit Graph
2291 Commits
Author SHA1 Message Date
PiMinusMax d9b13ab702 fix: Cmd/Ctrl + the + key zooms the canvas, not the whole window
On layouts with a dedicated + key (German) or Shift+= (US), Zoom In's
Cmd+= arrives as Key::Plus. No shortcut matched it, so egui's built-in
keyboard zoom scaled the whole UI, and the UI-scale preference reset it
on the next frame. Plus now counts as = for shortcuts, and egui's
keyboard zoom is off since the UI scale is a preference.
2026-10-13 15:09:26 +02:00
aiongg 578818466e Merge craft-cms: Colour management on craft-cms 2026-10-13 13:08:18 +00:00
aiongg fc31f7f14d Merge craft-cms-adoption: colour management on craft-cms (next's image path) 2026-10-13 13:07:32 +00:00
aiongg 73af5d7f63 Merge script-dollar: GREP replacements like $1 stay literal in scripts (#749)
# Conflicts:
#	ROADMAP.md
2026-10-13 13:01:31 +00:00
aiongg d227ee84e9 fix(script): only a well-formed $N reference is replaced; $$ is a literal $ (#749) 2026-10-13 13:00:34 +00:00
aiongg 18991a8fc2 Colour: effects goldens with Coated FOGRA39 swatch colours 2026-10-13 13:00:24 +00:00
sanyamsingh001andCodex 3ac4d2e913 Preserve endnote and editorial payloads in text slices
Co-Authored-By: Codex <codex@openai.com>
2026-10-13 22:59:29 +10:00
haranishi 027c986b65 test: Linksパネルのデバッグ用書き出しを除去 (#703) 2026-10-13 21:54:37 +09:00
Losminek 9ea463eb70 Merge remote-tracking branch 'upstream/main' into cs-pr 2026-10-13 14:53:24 +02:00
moppeeerandClaude Opus 5.5 61ea089149 feat(ui): Text Frame Options laid out as InDesign's, with its General options
InDesign's Text Frame Options has a section list (General, Baseline
Options, Column Rules, ...). General holds Columns (Fixed Number / Fixed
Width with Number, Width, Gutter), Balance Columns, Inset Spacing as a
2x2 grid with a link button, Vertical Justification and Ignore Text
Wrap; Baseline Options holds First Baseline Offset and Min. DesignCraft
showed one flat list without the columns type, width, balance, link,
ignore-wrap or baseline settings, although the model keeps them.

- dialogs: a section list (General, Baseline Options, Column Rules);
  General as above (Width shows what Fixed Number columns get, editable
  for Fixed Width; a side typed while linked sets all four insets);
  Baseline Options; Column Rules as before. Enum captions translated.
- OK sends the new fields only when edited, as before.
- engine: `object.textFrameOptions` takes `columnWidth` (Fixed Width)
  and `firstBaselineMin`, which compose already honours.

LYH Studio episode 14 sets two columns (9:45) and a 0.1875 in inset
(10:57) here.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 14:46:54 +02:00
aiongg 44e5a28833 Integration (movable-dialogs x #683): test that the New Document window drags mostly aside and keeps its preview 2026-10-13 12:44:54 +00:00
Philipp Krüger f8474fa1c8 Merge branch 'main' into feat/pptx-export 2026-10-13 14:44:29 +02:00
aiongg cce067f4e1 Merge movable-dialogs: drag a dialog by its title to move it 2026-10-13 12:38:09 +00:00
moppeeerandClaude Opus 5.5 a59919bd24 feat(ui): Text Wrap panel with four offsets, link and Invert
InDesign's Text Wrap panel has the five wrap buttons with Invert, then
Top, Left, Bottom and Right offsets around a link button that makes them
all the same. DesignCraft's panel had a single Offset field that always
showed 0 (the object's offset was never read), and Invert only in
Properties.

- SelInfo carries the wrap offsets; the panel shows each side's value
  in the document's units and sets one side, or all four while linked
  (linked by default when they're equal).
- Invert sits next to the wrap buttons; fields are disabled without a
  wrap.

LYH Studio episode 14 (11:13) opens Window › Text Wrap to give the quote
a Jump Object wrap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 14:36:40 +02:00
aiongg b6c63d89c2 Colour: craft-cms for ICC transforms; Coated FOGRA39 default CMYK, BPC display, FOGRA39 output intent
- designcraft-color: ICC profiles (built-in RGB working spaces, user .icc files) go through
  craft-cms from storytold/craft-libs. The default CMYK working space is craft-cms's Coated
  FOGRA39; CMYK swatches show relative colorimetric with the settings' black point
  compensation. Generic CMYK and Device CMYK stay selectable. A CMYK profile name resolves
  through Builtin::stand_in_for; a name with no stand-in is an UnknownProfile error.
  ColorSettings::with_known_profiles replaces unavailable saved names with the defaults; the UI
  uses it when it restores saved settings at startup. Lab, gamut error and proofing of ICC CMYK
  spaces use craft-cms transforms. The Generic CMYK output profile is encoded by craft-cms.
- designcraft-pdf: the PDF/X output intent is the working CMYK space's profile: the FOGRA39
  built-in with identifier FOGRA39, user profiles as loaded, Generic CMYK otherwise.
- moxcms is no longer a direct dependency of designcraft-color.
2026-10-13 12:35:17 +00:00
aiongg 98c4022bf5 Integration: format 2026-10-13 12:32:27 +00:00
aiongg afd2f45b69 Merge pr/673: fix(import): preserve XLSX numeric and cached text values (#673) 2026-10-13 12:31:43 +00:00
aiongg 28b6be1a2d Integration (#703): links test assets and graphics carry pdf_crop/pdf_box and clip/wrap 2026-10-13 12:31:24 +00:00
aiongg 3d5fe33f6f Integration (#703): drop the links fixture capture 2026-10-13 12:30:40 +00:00
aiongg bf2a5b81a1 Merge pr/703: fix: show source paths in the Links panel (#703) 2026-10-13 12:30:26 +00:00
aiongg 354d1248fa Integration (#702): grouped reading-order test assets and graphics carry pdf_crop/pdf_box and clip/wrap 2026-10-13 12:29:59 +00:00
aiongg cd9886116d Merge pr/702: fix: use group transforms for HTML and EPUB reading order (#702) 2026-10-13 12:29:48 +00:00
moppeeer a255dc99b5 feat(tools): resize a selected placed graphic by its own handles (#699)
With a placed graphic selected (Direct Selection, or the content
grabber), the graphic's own brown bounding box has eight handles:
dragging one resizes the image inside its frame (Shift keeps its
proportions, Alt from its centre) and leaves the frame as it is.

- tools: a content-handle hit test on the selected graphic's bounds
  (after the frame's anchors under Direct Selection), a ContentResize
  drag previewing `transform.set {content, bounds}`, the handle cursors,
  and the content ghost while dragging. The frame's handles don't resize
  the frame while its graphic is selected.
- engine: `transform.set` takes `bounds` for content: the graphic's new
  bounding box on the spread (normalized, never empty).
- canvas: the brown content box gets its handles; the frame's handles
  are hidden while the graphic is selected.

(cherry picked from commit 8e0e6c2f2e)
2026-10-13 12:29:32 +00:00
aiongg c9a09989c8 feat(ui): a dragged dialog may go partly off-screen 2026-10-13 12:28:23 +00:00
aiongg 1533665af8 Merge pr/688: Create Outlines makes one compound path per line, so Ungroup separates the lines (#688) 2026-10-13 12:28:22 +00:00
aiongg 3f489a1273 Merge pr/689: Ungroup leaves frames with pasted-in content whole; name them by their shape (#689) 2026-10-13 12:27:49 +00:00
aiongg 0eb1ecfe1b Merge pr/691: Copy a selected placed graphic on its own, so Paste Into puts the whole image in place (#691) 2026-10-13 12:27:29 +00:00
aiongg bcad0182e5 Merge pr/698: Effects apply to frames with pasted-in content (Inner Glow, Basic Feather, shadows…) (#698) 2026-10-13 12:27:17 +00:00
aiongg 23eb86988f Merge pr/704: fix(tables): keep each table anchor in its own paragraph (#704) 2026-10-13 12:26:51 +00:00
aiongg 5f2110e7e5 Merge pr/687: Bump quick-xml to 0.41 (#687) 2026-10-13 12:13:14 +00:00
aiongg e3ee342a4a feat(ui): drag a dialog by its title to move it 2026-10-13 12:12:16 +00:00
aiongg d234ede3a2 Merge craft-atomic: Save: delegate atomic writes to craft-atomic 2026-10-13 12:12:16 +00:00
aiongg 004e585e34 Merge upstream main e862e0a6 into next 2026-10-13 12:11:25 +00:00
aiongg e81a21e645 refactor(save): delegate atomic writes to craft-atomic
The engine's atomic module keeps its functions and signatures and delegates to craft-libs/craft-atomic (pinned to e9546e19), so every save and export caller is unchanged. write_atomic_with also turns a panicking writer into an error, so its temporary file is removed. Recovery checkpoints and package output keep their own writers, which replace links instead of following them.

On Unix, new files follow the umask and replaced files keep their permissions.
2026-10-13 11:56:42 +00:00
dotCooCoo 573e2da84a Swatches: dropping an .ase file on the window loads its swatches
open_dropped sent every dropped file that is not a document to
file.place, so a swatch exchange (.ase) file dropped on the desktop
window failed with a "Can't Place the File" alert: "invalid parameters
for `file.place`: unsupported or corrupt image".

open_dropped now sends .ase files to swatch.load with the dropped path,
as Load Swatches in the Swatches panel menu does with the picked path.
The web app already sent dropped .ase files to swatch.load. The
swatches are added to the active document as Load Swatches adds them,
and if the file can't be loaded, the status line shows the error.
2026-10-13 04:41:03 -07:00
haranishi 27302d8ea3 fix: Linksパネルにリンク元パスを表示 #569 2026-10-13 20:18:40 +09:00
haranishi 1b6a03e350 fix: グループ変換をHTML・EPUBの出力順に反映 #314 2026-10-13 20:10:10 +09:00
Philipp Krüger fc31170346 fix(pptx): refuse to embed fonts whose licence forbids outline embedding
eot() only refused fsType 0x0002 exactly, so 0x0003 (which
FontFace::restricted_licence and file.package already treat as
restricted) and Bitmap embedding only (bit 9, e.g. 0x0200, 0x0208) were
embedded with their glyf outlines. Reuse restricted_licence and also
refuse bit 9, matching VectorCraft's Package (storytold/vectorcraft#782).
2026-10-13 13:00:32 +02:00
moppeeerandClaude Opus 5.5 96e9f371ac fix(render): effects apply to frames with pasted-in content
A frame holding items pasted into it (Edit › Paste Into) was drawn by
its own branch in draw_item that skipped every effect: Basic Feather,
Inner Glow, shadows and the rest were stored but never drawn. LYH Studio
episode 14 (11:51) gives the outlined "GET MORE." title, with a photo
pasted into it, an Inner Glow and a Basic Feather.

Such a frame now goes the way of any other object: draw_body draws its
fill, its items clipped to the frame and its stroke, so draw_item_fx
wraps all of it. Its silhouette counts as solid for shadow spread.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 12:02:57 +02:00
haranishi 3b36c28694 test: macOSのMCP再接続テストのソケット設定を修正 2026-10-13 18:25:53 +09:00
moppeeerandClaude Opus 5.5 6c65a55905 fix(object): Ungroup leaves frames with pasted-in content whole
A frame holding items pasted into it isn't a group. Ungroup took it
apart like one, leaving its content loose and losing the clipping shape,
and the Properties panel called it "Group" with an Ungroup quick action.
As in InDesign, Ungroup now only takes real groups apart; other objects
stay as they are and stay selected (Ungroup used to clear them from the
selection). Such frames are labelled by their shape (<ellipse>, ...), so
Properties shows "Ellipse" with Convert Shape.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 11:01:30 +02:00
moppeeerandClaude Opus 5.5 f55b94f3ff fix(type): Create Outlines makes one compound path per line
As in InDesign, outlining a text frame gives a group with one compound
path per line (split further by text colour within a line), so Ungroup
leaves each line on its own: LYH Studio episode 14 ungroups the outlined
title at 4:56 to tighten the space between its lines before making one
compound path again. A single line in one colour stays a single compound
path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 10:21:39 +02:00
Noah Scholz 65bb4e3f66 fix(tables): keep each table anchor in its own paragraph
Backspace at the start of the second of two consecutive tables merged their
anchor paragraphs: Story::delete kept one table id, the other table was
dropped and the paragraph held two anchors. Pasting a table next to an anchor
replaced the existing table the same way, and pasting several paragraphs
right before an anchor left its table unreferenced. Saving succeeded;
reopening failed with "paragraph must hold exactly one anchor".

fix_tables now splits a table paragraph that holds further anchors, so every
anchor gets its own paragraph again. delete and insert_story tell it which
table each surviving anchor belongs to (fix_tables_with), and insert_story
fixes tables up once after the whole paste instead of after every run.

Regression tests in doc (Backspace between tables, deleting the middle of
three, paste before/after an anchor, paragraphs pasted before an anchor) and
engine (text.delete and edit.paste through commands, then a file round-trip);
all failed before the fix.

Claude-Session: https://claude.ai/code/session_01La6SDgsmdZfgfDDmx3wiGU
2026-10-13 08:17:51 +00:00
moppeeerandClaude Opus 5.5 39ec501351 fix(edit): copy a selected graphic on its own; delete leaves the frame
With a placed image selected via Direct Selection (InDesign's content
selection), Copy now copies the graphic itself: a frame of the whole
image's bounds (uncropped) at its spread position, without the frame's
fill, stroke or effects. Paste Into then puts the full picture in place
inside the target shape, as InDesign does (LYH Studio episode 14, 5:44).
Cut and Delete with the graphic selected remove only the graphic; the
frame stays, empty and selected.

Nested items copy in spread coordinates (parent transform applied).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 10:12:43 +02:00
natocool2andClaude Opus 5.5 5a5e97b0a6 Bump quick-xml to 0.41 (RUSTSEC-2026-0194, RUSTSEC-2026-0195)
The .docx/.xlsx text import iterates attributes with quick-xml's default duplicate check, so one crafted
start tag with N attributes cost O(N^2) (RUSTSEC-2026-0194, fixed in quick-xml 0.41). Bumping also clears
RUSTSEC-2026-0195 (NsReader namespace allocation), which this workspace does not reach (no NsReader).

unescape_value() is deprecated in 0.41 (clippy -D warnings); normalized_value(Implicit1_0) is what the
deprecated method now does. Regression test: an element with 100,000 attributes parses in < 5 s.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-13 09:07:47 +01:00
moppeeerandClaude Opus 5.5 7192c2e136 feat(eyedropper): A sampled colour goes to the stroke when the Stroke box is in front
As in InDesign, the Eyedropper applies a colour sampled from a placed image
to the fill or the stroke, whichever is in front in the toolbox: with the
Stroke box in front a selected line takes it on its stroke (it has no
fill to show it). The toolbox's state reaches tools through
ViewInfo/ToolContext::stroke_in_front.

Depends on #572.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 09:56:25 +02:00
moppeeerandClaude Opus 5.5 812df5b3d3 fix(tools): Fit along the frame's own axes; skip locked frames; fit text by its layout
Side handles map to the frame's local axis through spread and frame
rotation. Graphic frames honour the axis. Locked frames and frames on
locked layers are left alone. Text frames fit by searching the frame
height with its own columns and vertical justification, so two columns
stay two columns and Bottom/Center/Justify don't inflate it. A mouse
double-click on a handle fits (main's canvas counts clicks on the press);
a UI test covers it with one undo step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 09:42:26 +02:00
moppeeerandClaude Opus 5.5 45d803c6a4 feat(tools): Double-click a frame handle to fit the frame to its content
In InDesign double-clicking a selection handle fits the frame to its
content: a corner fits both sides, a top or bottom handle the height, a
side handle the width. LYH Studio's tutorial uses it at 9:30 to fit the
INDESIGN text frame before moving it to the corner. In DesignCraft the
double-click switched to the Type tool.

- Selection tool: a double-click on a handle runs object.fit
  {mode: fitFrameToContent, axis: both|height|width}.
- object.fit fitFrameToContent fits text frames to their composed text:
  the bottom to the last line's descent, the right side to the longest
  line (one column), plus the insets, keeping the top-left. Overset text
  grows the frame first. Before, it fitted graphic frames only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 09:39:42 +02:00
moppeeer d68b59e538 Integrate Windows window controls into the menu bar 2026-10-13 09:33:23 +02:00
moppeeerandClaude Opus 5.5 c05a2e922f fix(ui): Keep Enter and Escape in the Save Document Preset popup; saved presets in the engine
Review follow-up. Enter in the Save Document Preset popup created the
document and Escape closed the whole dialog: the dialog footer took
both keys while a popup was open. The dialog now leaves Enter and
Escape to an open popup (read before it draws, as egui closes a popup
on Escape that frame): Enter in the preset name saves the preset,
Escape closes only the popup. Clicking the name field no longer closes
the popup either (menus close on any click; this one only outside), and
the field takes focus when the popup opens.

Saved presets lived only in the UI state, so `file.new {preset: name}`
refused them. They are now engine preferences (saved with prefs.json):
`file.savePreset {name, …the file.new settings}` replaces a preset of
that name and refuses built-in names, `file.deletePreset {name}` removes
one, `file.presets` lists them after the built-in ones (`saved: true`),
and `file.new {preset}` makes documents from them by name. The Saved tab
reads and writes them through those commands.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 09:33:20 +02:00