Skip to content

Links: Find in Folder… looks for missing linked files in a folder and its subfolders - #1287

Merged
bflatastic merged 3 commits into
storytold:mainfrom
dotCooCoo:feat/link-search
Oct 12, 2026
Merged

bflatastic merged 3 commits into
storytold:mainfrom
dotCooCoo:feat/link-search

Conversation

@dotCooCoo

Copy link
Copy Markdown
Contributor

What and why

When a document opens without its linked files, Linked File Not Found asks about each one. Replace with Apply to All
looks for the other files by name in the folder of the file picked, and the Links panel's Relink to Folder… looks in a
folder the user picks. Files kept in subfolders, or spread over several folders, had to be relinked one folder at a
time. The reference app's Search for Missing Links in this folder option relinks the other missing files it finds in the
folder of the file picked
(described by an Adobe community manager in May 2024).

Find in Folder…, in Linked File Not Found and in the Links panel menu, looks in a folder the user picks and in its
subfolders for files with the names of the missing linked files, in any letter case. In the Links panel it looks for
the selected linked files when there are any. It uses the folder search of the Missing Fonts dialog (#792). Find Linked
Files lists each file it looks for with the files found for it. A file with the size and contents saved with the link
comes first (Same file). A file of the saved size comes next when the link saved no hash or the file is larger than
1 GB (Same size), and any other file of the name comes last. Within each group, a file named in the same letter case
comes first. Then each file's folder names are compared with the saved path's, from the innermost outward, up to the
first one that differs, and a file with more matching names comes before one with fewer.

  • A file is chosen when it is the only file found, or the only Same file. The user can choose another file or Don't
    Relink. Relink relinks each file chosen, and each relink is one undo step.
  • Copy into the document's folder, off by default and shown for a saved document, copies each image's file next to the
    document first. It never replaces a file: the file's name, then Name 2.png, Name 3.png and so on, are tried in turn
    until one is free, which gets the copy, or is a file with the same contents, which is used. A placed document is
    relinked where its file is.
  • When Find Linked Files opens from Linked File Not Found, the files left without a choice and any that fail to relink
    go back to that dialog. The status line names the first file that failed.
  • Agents use links.findFiles, links.relink {copy} and ui.findLinksInFolder, which docs/mcp.md and
    docs/control-protocol.md describe. On the web, Find in Folder… is disabled in the Links panel menu, and there is no
    folder search.
  • text.addFontFiles and links.relink {copy} make their copies with fileio::copy_new, which never replaces a file.
    This change moves the loop that picks a numbered name (Name 2 to Name 99) from fontfiles.rs into
    fileio::copy_new. The text.addFontFiles params text and docs/mcp.md say which names are tried for a copy.

How it was tested

The change adds 8 engine tests (crates/engine/src/tests_linkfiles.rs), 1 in crates/doc/src/links.rs and 9 UI tests
(crates/ui-egui/src/dialogs/find_links.rs). I broke parts of the change a few at a time, and for each broken part at
least one test that covers it failed. No test checks the 1 GB hashing limit, the limit of 1,000 linked files a search
looks for, or the Stop button in Find Linked Files. Two Add Fonts tests in tests_fontfiles.rs,
copying_never_replaces_a_file and suitcase_fonts_are_found_and_copied_with_their_fork, fail when the shared copy
never keeps a file.

On macOS 27.0.1 (Apple Silicon) with rustc 1.99.0, on top of main at 2ac3ba2:

  • CRAFT_FONTS_DIR=<craft-fonts checkout> cargo xtask ci: fmt, clippy, test, assets, brands, layers and wasm pass.
    The test step ran 121 test binaries and doc-test runs: 5,399 passed, 4 ignored.

I ran the tests on macOS only.

git merge-tree reports no conflicts with main at 2ac3ba2. #1223 (shortcut sets) already conflicts with that main in
crates/ui-egui/src/dialogs/mod.rs and docs/roadmap.md. The only conflict this change adds to #1223 is in the read
root list of docs/mcp.md, where both add an entry; keeping both gives "file.newFromTemplate, preset imports,
shortcut sets (shortcutSet.read), the folders text.findFontFiles and links.findFiles search."

Checklist

  • cargo xtask ci passes (fmt, clippy, tests, assets, brands, layers, wasm).
  • No panics in shipped code: no unwrap(), expect(), panic!, unreachable!, todo! or
    unimplemented! outside tests, and no unchecked indexing on data from files, parameters or
    messages. Errors are returned as Result and shown to the user
    (why and how).
  • Clean-room: nothing copied from Adobe products or GPL/AGPL code (AGENTS.md).
  • Every new asset has a row in ASSETS.md.

dotCooCoo and others added 3 commits October 12, 2026 08:16
… its subfolders

Find in Folder… in Linked File Not Found and in the Links panel menu
looks in a folder the user picks, and in its subfolders, for the files
with the names of missing linked files, in any letter case. In the
Links panel menu it looks for the selected linked files when there are
any. It uses the folder search of the Missing Fonts dialog. Find
Linked Files lists each file it looks for with the files found for it.
A file with the size and contents saved with the link comes first. A
file of the saved size comes next when the link saved no hash or the
file is larger than 1 GB, and any other file of the name comes last.
Within each group, a file named in the same letter case comes first.
Then each file's folder names are compared with the saved path's, from
the innermost outward, up to the first one that differs, and a file
with more matching names comes before one with fewer. A file is chosen
when it is the only file found, or the only one with the saved
contents, and the user can choose another file or Don't Relink.

Relink relinks each file chosen, and each relink is one undo step.
With Copy into the document's folder, which shows for a saved
document, each image's file is copied next to the document first. The
file's name, then Name 2.png, Name 3.png and so on, are tried in turn
until one is free, which gets the copy, or is a file with the same
contents, which is used. A placed document is relinked where its file
is. The status line names the first file that fails to relink, and the
other files still relink. When Find Linked Files opens from Linked
File Not Found, the missing files left without a choice, and any that
fail to relink, go back to that dialog. On the web, Find in Folder… is
disabled in the Links panel menu, and links.findFiles can't start a
search.

- links.findFiles searches on separate threads and returns at once;
  links.findFiles {} reports the search, and {stop: true} stops it. It
  looks for at most 1,000 linked files at once: the active document's
  missing ones, or those of the objects it is given. It hashes a file
  only when a link of its name saved the file's size and a hash, and
  only when the file is 1 GB or smaller. It ends once every linked
  file has its same file, and it searches only the folders
  text.findFontFiles may search, with the same maxSeconds, depth and
  count limits.
- links.relink {copy} copies the file it is given into the saved
  document's folder and links images to the copy; a call that includes
  a placed document fails. Each name it writes is checked against the
  automation write root. text.addFontFiles and links.relink {copy}
  make their copies with fileio::copy_new, which never replaces a
  file. The text.addFontFiles params text and docs/mcp.md say which
  names are tried for a copy.
- vectorcraft-doc adds links::hash_reader, which hashes what a reader
  reads as hash_bytes hashes bytes.
- ui.findLinksInFolder is the Links panel's Find in Folder… for agents;
  docs/control-protocol.md lists the dialog's fields.

Docs: docs/mcp.md, docs/control-protocol.md, docs/target-app-parity.md.
@bflatastic
bflatastic merged commit 0586ee8 into storytold:main Oct 12, 2026
1 check passed
@bflatastic

Copy link
Copy Markdown
Member

Thanks @dotCooCoo! Find in Folder… and Find Linked Files are a big help for documents whose links moved.

I read the folder handling closely, since it walks the file system: it reuses the Missing Fonts walker, which follows no links, reads only regular files, checks the folder against the automation roots, and caps depth, folders, files and time. The copy into the document's folder only ever creates new files there (Name 2.png and so on), never replacing one.

One fix from me, in a separate commit: on Windows, 7 tests built expected paths with / (root.join("doc/photo.png")), while the app reports them with \. The tests now build those paths with the platform separator. No change to the shipped code.

Checked: the full local gate on the combined branch, and live on Windows. Over MCP, links.findFiles rejects a relative folder, finds photo.PNG in a subfolder as the same file, and links.relink {copy} copies it next to the document, then reuses that copy the second time. In the app (in Hungarian, with the new rows), Linked File Not Found → Find in Folder… opened Find Linked Files with the same file chosen, Relink relinked it, and the file with no match went back to Linked File Not Found.

@bflatastic bflatastic mentioned this pull request Oct 12, 2026
4 tasks done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants