Explorer — file operations
See how the Explorer creates, moves, and inspects files. The examples cover new items, drag and drop, progress and name conflicts, the Properties popup, the preview panel, and search.
This page uses a saved copy of the design system, last changed on . It may differ from the current design source.
New folder · new file · where file commands live
Toolbar — create group and view group (wide · narrow · remote · read-only)
Two small icon groups sit between the PathField and the SegToggle: create (New folder · New file) and, after a 1px separator, view (Find · Preview panel, both toggles with the IconButton active state). Icon-only, sm, tooltip = the command name. Delete, Rename and the clipboard stay in the context menu and on keys; the toolbar holds only what has no selection target. When the cell is narrower than --tasty-explorer-toolbar-compact-below (440) both groups fold into one More IconButton whose menu lists the same four rows; the PathField keeps flex 1 and the nav group never folds. A remote (mirror) explorer hides the create group (Find and Preview stay). A folder the user can't write to keeps the buttons disabled with the tooltip “Can't write to this folder”.
New glyphs: icons/folderPlus.svg, icons/filePlus.svg (and info for Properties). Keybinding actions the app may add: explorer.new_folder, explorer.new_file, explorer.find, explorer.toggle_preview, explorer.properties — the design names actions only, never keys.
Context menu — create rows (empty area · folder) and Properties
The empty-area shape (target = current folder) opens with a new first group: New folder · New file. A folder row (list or sidebar tree) gets the same two rows inside its file-ops group; they create inside that folder. Every shape ends with Properties (info glyph) in the system group. Remote explorers hide the create rows like the other write rows. The F2 / Del hints stay representative; the app shows a hint only when a binding exists.
Name input — inline, at the top of the list (Detail · List · Grid)
A new item is named inline, not in a dialog: an editing row appears at the top of the current listing (not at its sorted place, so it never jumps while typing), selected, with the shared Input in the name slot. This is the same component as the inline rename the grid spec describes; the Rename dialog stays until the app builds it. Defaults: folder “New folder” fully selected, file “untitled.txt” with the stem selected; if the name is taken the default becomes “New folder 2”. ↵ confirms, Esc cancels and nothing is written. Losing focus confirms when the name is valid and cancels when it is empty or invalid. After a confirm the item moves to its sorted place and stays selected — only if the user is still on that folder in that explorer; focus never moves to another surface.
Name errors — empty · invalid character · already exists
Errors show at the input, never as a toast, and the input stays open: the Input takes its invalid border and a small message box hangs under the field, over the next rows (the list does not shift). The message box is the menu surface with a 1px --tasty-explorer-name-error-fg edge and text-primary caption copy, so it reads at 4.5:1 in both themes. Empty and invalid are checked as the user types; “already exists” is checked on ↵ (the app never overwrites).
i18n: explorer.new.folder_default “New folder” · explorer.new.file_default “untitled.txt” · explorer.name.empty · explorer.name.invalid_char · explorer.name.reserved · explorer.name.exists. The default names are translated; the extension of the file default is not.
Drag to move or copy · drop indication
Drag chip — one item · many items · move / copy / refused
While dragging, a chip follows the pointer (12 right, 12 below). Line 1 is what is dragged: the item's glyph and name, or a stack glyph and “3 items”. Line 2 says what the drop will do before release, in the op colour: Move to {folder} (accent-primary), Copy to {folder} (accent-success) or Can't drop — {reason} (accent-danger). Default: move on the same disk, copy across disks; holding the modifier from Keybindings flips it and the line updates at once. The dragged rows themselves keep their selection look (no dim; dim stays the meaning of Cut).
Drop targets — folder row · grid cell · tree node · favorite · current folder
Every target takes the same mark: a 1px inset accent ring plus the accent tint fill (--tasty-explorer-drop-target-bg = accent-primary at tint-fill-alpha). Nothing moves or grows. A folder row or cell is a target; a file row is not — over a file, the target falls back to the folder being shown, and the whole list body takes the ring. Sidebar tree nodes and favorite rows are targets too. Hovering a closed tree folder for 800 ms expands it. A refused target gets no ring; the chip says why.
Files from the OS — copy into the explorer, not open
Files dragged in from the OS and dropped on an explorer cell are now copied into the target folder (folder row, or the folder being shown). Over an explorer cell the window drop overlay is not drawn; the explorer shows its own ring and a chip that always reads Copy to (an OS drag is never a move). Everywhere else in the window the existing overlay and “open with handler” stay as they are. A remote explorer refuses the OS drop and says so in the chip. Dragging items out of the explorer (for example into a terminal to paste a path) is left for a later pass.
i18n: explorer.drag.items “{n} items” · explorer.drag.move_to “Move to {folder}” · explorer.drag.copy_to “Copy to {folder}” · explorer.drag.refused “Can't drop — {reason}” · reasons: into_itself “a folder can't go inside itself” · same_folder “already in this folder” · remote “remote folders are read-only” · no_write “no write access”.
Progress · conflicts · cancel · results · undo
Running — on the explorer's own status line · queue
Work shows in the status line of the explorer that started it, so two explorers in different cells each show only their own. A 2px bar runs along the top edge of the status line (determinate by bytes; no animation), the left side reads “Copying 12 of 40 · {current file}”, the right side has the byte count, a “+n queued” Tag when more work waits in this explorer, and a Cancel ×. Clicking the left side or the Tag opens the queue popover above the status line: the running job (Cancel) and queued jobs (remove). One job runs at a time per app; a job queued in another explorer shows only there. Nothing takes keyboard focus.
Name conflict — surface-scoped prompt · apply to all · waiting
A conflict pauses the job and asks with a popup scoped to that explorer cell (dims only the cell, like the markdown large-file confirm). It opens only while the explorer has focus; if the user is typing in another surface the status line turns warning-toned — “Waiting for your answer · 1 name conflict” with a Show button — and the popup opens when they come back. Answers: Keep both (primary, the default on ↵; the app names the copy), Skip, Replace (files only, never the default) and Cancel the rest (ghost, left; Esc). With more conflicts left, a checkbox applies the answer to all of them. A folder colliding with a folder (or a file with a folder) offers only Skip / Keep both — no merge, no replace.
Results — done · cancelled · partial + retry · failed · trash unavailable · undo
Results appear as a Toast anchored inside the explorer cell, bottom-right, 8 above the status line (the cell's toasts stack upward). Done and cancelled leave after the standard toast time; anything with a failure stays until dismissed. A partial or failed result lists up to 3 failed paths (path ellipsised at the front so the name stays, reason under it) and then “and n more”; Copy paths copies every failed path, Retry runs only the failed and skipped items. A move across disks whose source delete failed is its own line (“copied; original not removed”). Trash never falls back to permanent delete. Undo sits on the done toast for as long as it is visible; its own result reports what could not be undone.
i18n: explorer.op.copying / moving / trashing “{verb} {i} of {n} · {file}” · explorer.op.queued “+{n} queued” · explorer.op.waiting “Waiting for your answer · {n} name conflict(s)” · explorer.conflict.* (title, in, existing, incoming, no_merge, apply_all, keep_both, skip, replace, cancel_rest) · explorer.result.* (done, cancelled, partial, failed, source_not_removed, more, retry, copy_paths, undo, undo_partial) · explorer.trash.unavailable.
Properties · preview panel · Grid thumbnails
Properties — popup for one item · folder · several · remote
Opened from the Properties context-menu row or the app's properties action, as a popup scoped to the explorer cell (same family as Rename; no scrim needed, Esc or × closes). It shows the item it was opened for and does not follow later selection changes. Fields are a two-column list: caption label (96) and value; paths and permissions are mono and wrap anywhere, with a Copy button on Location and Link target. Sizes that take time (folders, several items) show a Spinner and fill in when the background count ends; the popup never blocks the list. A remote item shows only what the host lists (name, kind, size, modified, location) and says so.
Preview panel — text · image · other · loading · too large · unreadable
The preview panel comes back as a toggle (toolbar view group). It docks on the right of the listing at --tasty-explorer-preview-width (288), resizable by a splitter between 200 and 460; the width is remembered per explorer. A 40 header shows the name and “kind · size” (image: pixel size). Text files (UTF-8, by extension or content) show their first 64 KB in mono; images fit the panel; anything else, folders included, shows “No preview for this file type” and leaves the facts to the header. A new selection clears the old preview at once and shows Loading preview… until the read ends. Over 1 MB or unreadable shows a state, never content. Remote explorers preview text and images the host returns.
Grid thumbnails — 40 slot for every cell
Thumbnails are Grid only (List and Detail keep their glyphs). Every Grid cell's icon slot becomes --tasty-explorer-grid-thumb-size (40), so rows stay uniform whether or not a folder holds images; folders and other files keep the 16 glyph centred in it. An image file shows its glyph in accent-info while the thumbnail loads, then the picture fit into 40 × 40 with a 1px separator edge and radius-sm. Thumbnails are made in the background, cached, and skipped over the app's size limit (glyph stays). Remote explorers keep glyphs. Cell width stays 80; the cell grows by 24 in height (16 → 40 slot).
“img” stands in for the decoded picture. Real side: grid_cell() reserves the 40 slot for every entry; the thumbnail cache is keyed by path + mtime.
Filter this folder · search subfolders
Find bar — filter the current folder
The Find action (toolbar search toggle, or the app's find action while the explorer has focus) opens a bar docked under the toolbar, the full width of the cell, 36 high. Typing filters the folder being shown at once: rows that don't match are hidden, the matched part of each name takes --tasty-explorer-match-fg, and the bar shows “{shown} of {total}”. Type-ahead is off while the field has focus; on the list it jumps among the visible rows. Esc in the field clears the text, a second Esc closes the bar; × closes it; Back / Up / the address bar close it too. Remote explorers get the filter.
Subfolders — recursive search · searching · no results · errors · stopped
Ticking Subfolders turns the same bar into a recursive search from the folder being shown. It runs in the background and fills the list as it goes: Searching… {n} found with a Spinner and a Stop button. Results replace the listing; Detail adds a Folder column (path relative to the start folder, mono, “.” for the start folder itself) and drops Type. List and Grid keep their look and show the folder in the tooltip and on the status line for the selected result. Every command on a result (open, copy, cut, rename, trash) uses its real path. Folders that can't be read are skipped and counted in accent-warning; nothing found is the centred empty state. Remote explorers don't offer Subfolders.
i18n: explorer.find.filter_placeholder · explorer.find.search_placeholder “Search in {folder} and subfolders” · explorer.find.subfolders · explorer.find.count “{shown} of {total}” · explorer.find.searching · explorer.find.stop · explorer.find.stopped · explorer.find.skipped · explorer.find.none “No matches in {folder}” · explorer.find.failed.
Follow-ups — find · names · results · properties (2026-10-09 batch 10)
Find — every view highlights · `..` while filtering · no matches · skipped · cap · More rows
The matched part takes --tasty-explorer-match-fg in all three views (Detail, List, Grid), filter and search alike. While filtering the .. row stays at the top: it is navigation, never matched, never counted in “{shown} of {total}”; recursive results drop it (they replace the listing). A filter with no matches keeps the bar (“0 of 6”) and shows the state screen with a filter line — “No names match “{query}””, no sub-line. One unreadable folder reads “1 folder skipped”. A recursive search stops at 5,000 results: the bar reads “5,000+ found · stopped”, the results stay, and a muted line asks for a longer query. Native menus have no check mark, so the More rows say the action: Find / Close find, Show preview / Hide preview — and Preview folds into More with the rest of the view group under 440.
i18n (new): explorer.find.none_filter “No names match “{query}”” · explorer.find.skipped_one “1 folder skipped” · explorer.find.capped “{n}+ found · stopped” · explorer.find.capped_hint “Showing the first {n}. Type more to narrow the search.” · explorer.more.find_close “Close find” · explorer.more.preview_show “Show preview” · explorer.more.preview_hide “Hide preview”.
Names — leading / trailing spaces are refused
A new or renamed name that starts or ends with a space is refused at the field, never trimmed silently (the user may have meant it, and a silent change breaks what they typed). Spaces only = the existing empty error. On Windows a trailing period keeps the reserved-name message, since the OS would change the name the same way. The message box is capped by the new --tasty-explorer-name-error-max-width.
Results — originals left after a cross-disk move · Undo kept a changed copy
A cross-disk move is copy, then delete. When the delete stops partway the copy is complete but the original may be whole or partly left. That result counts as a failure: warning tone, stays until dismissed, no Undo (the copy may be the only whole one). Undo of a copy never trashes a copy that changed after the job; it keeps it and lists it with its own reason.
en / ko / ja — source_left_move “Moved {done} of {total} · {n} originals not fully removed” / “{total}개 중 {done}개 이동 · 원본 {n}개가 다 지워지지 않음” / “{total} 件中 {done} 件を移動 · 元の {n} 件を削 除しきれませんでした” · source_not_removed “Copied; the original is still there, in whole or in part ({reason})” / “복사됨. 원본이 전부 또는 일부 남아 있음 ({reason})” / “コピー済み。元の項目の全部または一部が残っています ({reason})” · changed_kept “Changed after the copy, so it was kept” / “복사 뒤 바뀌어서 남겨 둠” / “コピー後に変更されたため残しました”.
Properties · preview — can't read · several selected · pixel limit · link glyph
Properties that can't be read keep the popup frame: the item's own glyph and name in the title, then an error line (alertTriangle 16 in explorer-error-fg + “Can't read properties”), the OS reason in mono, and a Retry. The title is regular weight — the same rule as the plugin name: the theme carries no semibold UI face, so popups use regular at 14 and the size carries the title. The preview with nothing selected keeps “Select a file”; with several it says how many and asks for one. A picture over the pixel limit uses the same “Too large to preview” screen as the byte limit, with its own sub-line and the real size on the reason line. Symlinks take the new link glyph. Kind reads as a word: “PNG image”, “Folder”, “Symbolic link”, unknown type “{EXT} file”, no extension “File”.
i18n (new): explorer.props.unreadable “Can't read properties” · explorer.preview.multi “{n} items selected” · explorer.preview.multi_sub “Select one file to preview it.” · explorer.preview.too_large_pixels_sub “Over {px} px on a side, or needs more than {mem} to decode.” · explorer.kind.image “{type} image” · explorer.kind.ext_file “{ext} file”.
Follow-ups — kind words · refused requests · Retry of originals · undo titles (2026-10-10 batch 11)
Kind words — one table for Type, Kind and the preview header · links say what they point to
A short format-name table turns an extension into a word. The same word fills the Detail Type column, Properties Kind and the preview header, so the three never disagree. Anything not in the table falls back to “{EXT} file”; no extension is “File”. A symlink reads “Link to {kind}” in Type and in the preview header, and the size is the target's (the preview shows the target). Properties keeps the lstat answer, “Symbolic link”, with the target path. A broken link reads “Broken link”. The byte limit now says “Over 1 MiB.” — every binary limit says MiB.
i18n (new): explorer.kind.markdown · .text · .pdf · .archive · .shell · .link_to “Link to {kind}” · .broken_link “Broken link”. HTML / JSON / TOML / YAML / CSV are the same in all three languages and need no key.
Requests refused — queue full · request too large
A file operation the app can't take is never dropped silently. Both cases show a Warning toast in the explorer's own cell (Surface scope, standard time): the user's action did not happen, so it is more than Info, but nothing was lost — clipboard and selection are unchanged. Queue full carries Show queue, which opens the existing “+n queued” popover above the status line. Too large has no action. The limits (8 waiting, 1 MiB of paths) are implementation details and stay out of the copy.
Copy confirmed as proposed — queue_full en “Too many file operations are waiting. Try again when one finishes.” / ko “기다리는 파일 작업이 너무 많습니다. 작업이 하나 끝난 뒤 다시 시도하세요.” / ja “待機中のファイル操作が多すぎます。操作が一つ終わってからもう一度お試しください。” · too_large en “Too many items for one file operation. Select fewer items and try again.” / ko “한 번의 파일 작업에 담기에는 항목이 너무 많습니다. 항목을 줄여 다시 시도하세요.” / ja “一度のファイル操作には項目が多すぎます。項目を減らしてもう一度お試しください。” · show_queue “Show queue” / “대기열 보기” / “キューを表示”.
Retry of originals — its own progress words · one result card · leftover reasons
Retry on an “originals not fully removed” card compares each original with its copy, then deletes the original. It gets its own words: “Checking” while comparing, “Removing originals” while deleting, and the success card “Removed {n} originals”. When one Retry press runs both a delete of originals and a re-move of failed items, the user sees one card for the press: it appears when both jobs end and its title follows the worse outcome. An original that is kept goes back on the warning card (source_left_move, counting only what is left) with its reason, and Retry n returns. A cancelled delete lands there too, not on an info card.
en / ko / ja — copy_missing “The copy is missing or changed type, so the original was kept” / “사본이 없거나 종류가 바뀌어 원본을 남겨 둠” / “コピーが見つからないか種類が変わったため、元の項目を残しました” · kept_not_in_copy “Kept {n} items that aren't in the copy” (_one “Kept 1 item that isn't in the copy”) / “사본에 없는 항목 {n}개를 남겨 둠” / “コピーにない {n} 件を残しました” · remove_cancelled “Cancelled; the original is still there, in whole or in part” / “취소함. 원본이 전부 또는 일부 남아 있음” / “取り消したため、元の項目の全部または一部が残っています” · same_as_copy “Kept: the copy is the same file as the original” / “사본이 원본과 같은 파일이라 남겨 둠” / “コピーが元と同じファイルのため残しました” · checking “확인 중 {n}개 중 {i}개 · {name}” / “確認中 {i}/{n} 件 · {name}” · removing_originals “원본 지우는 중 {n}개 중 {i}개 · {name}” / “元の項目を削除中 {i}/{n} 件 · {name}” · remove_originals “원본 {n}개 지우기” / “元の {n} 件を削除” · removed_originals “원본 {n}개를 지움” / “元の {n} 件を削除しました”.
Undo results — kept-only title · mixed · singular rule
An Undo of a copy that only kept changed copies says so in its title. When some items couldn't be removed as well, the failure wins the title (it needs action) and the kept items are still listed with their own line. Singular rule, app-wide: every English count string gets a _one variant used when n = 1 (“1 item”, “1 original”); ko and ja have no plural and use the same string. This covers partial_*, failed_*, undo_* and the new keys here.
undo_kept_copy ko “복사 되돌림 · 바뀐 항목 {n}개 남겨 둠” / ja “コピーを元に戻しました · 変更された {n} 件を残しました” · undo_partial_copy_one “Undid copy · 1 item couldn't be removed” · undo_partial_move_one “Undid move · 1 item couldn't be put back”.
Remaining states — links · keyboard item · drag-select · hidden files · show in folder · redo · create in folder (b11)
Links in the listing · broken link
A symlink keeps its target's glyph (folder, file, image) and gets a trailing link glyph 12 after the name in --tasty-explorer-link-glyph, in all three views (Grid: after the label's last line). Type reads “Link to {kind}”. A broken link has no target glyph to borrow: the link glyph takes the item slot in --tasty-explorer-link-broken-fg (warning), the name stays normal ink (no strike-through — nothing was deleted), Type reads “Broken link”, and the tooltip names the missing target.
i18n: explorer.link.target_missing “Target not found: {path}” / “대상을 찾을 수 없음: {path}” / “リンク先が見つかりません: {path}”.
Keyboard current item · drag-select rectangle
Arrow keys, Home / End and PageUp / Down move a current item separate from the selection. It is a 1px inset ring in --tasty-explorer-cursor-ring (border-focus) and is drawn only after keyboard movement; a mouse click hides it until the next key. On a selected row it sits on top of the selection fill. Drag-select from empty space draws a rectangle with --tasty-explorer-marquee-bg and a 1px --tasty-explorer-marquee-border (the drop-target tint pair). Near the top or bottom edge, inside --tasty-explorer-autoscroll-zone 24, the list scrolls faster the closer the pointer gets; nothing extra is drawn.
Hidden files · show in enclosing folder · undo / redo
Hidden files toggle from a More row that says the action (“Show hidden files” / “Hide hidden files”, no check mark) and the shortcut Ctrl+Shift+. (macOS ⌘⇧.). Off by default. When shown, hidden items use --tasty-explorer-hidden-fg for name and glyph — muted ink, not opacity, so they never look like cut items. While they are hidden the status line counts them: “5 items · 3 hidden”. Show in enclosing folder (search results and links) opens the folder that really holds the item, selects it, puts the current item on it and scrolls it into view; no flash. Redo lives beside Undo in the empty-area context menu and on Ctrl+Shift+Z / Ctrl+Y (macOS ⌘⇧Z). Each explorer keeps up to 10 steps; no history list is shown. A step that can no longer be undone leaves the row disabled with the reason in its tooltip.
i18n: explorer.more.hidden_show “Show hidden files” / “숨김 파일 보기” / “隠しファイルを表示” · .hidden_hide “Hide hidden files” / “숨김 파일 숨기기” / “隠しファイルを非表示” · explorer.status.hidden “{n} hidden” / “숨김 {n}개” / “非表示 {n}” · explorer.menu.show_in_folder “Show in enclosing folder” / “들어 있는 폴더에서 보기” / “含まれているフォルダで表示” · explorer.menu.redo “Redo {op}” / “다시 실행: {op}” / “やり直す: {op}” · explorer.menu.undo_stale “Can't undo: {reason}” / “되돌릴 수 없음: {reason}” / “元に戻せません: {reason}”.
Create inside a folder — input under the target row
“New file / New folder” from a folder row's menu puts the input row directly under that folder, indented by --tasty-explorer-create-indent 16, with a muted “in {folder}” after the field. The target folder takes the drop-target ring while the field is open, so the destination is visible in every view. In Grid the new cell appears right after the folder cell. Enter creates the item inside the folder (the listing does not navigate); Esc cancels.
i18n: explorer.create.in_folder “in {folder}” / “{folder} 안에” / “{folder} 内”.
Follow-ups — address · hidden toggle · create field · auto-scroll · Undo / Redo menu (2026-10-10 batch 12)
Address field — folders and files · remote check pending
The address field now takes a folder or a file; a file opens its folder and selects it. The placeholder and the not-found toast say path, not folder. Remote explorers must ask the remote whether the path is a file, so on Enter the field keeps the typed path (it does not snap back to the current folder), stays in its focus border and, if the answer has not come after --tasty-explorer-address-pending-delay 200 ms, shows a Spinner in the trailing slot. The list and the status line do not change: nothing has moved yet. The answer navigates; after 8 s without one the explorer goes to the typed path, as today. Esc during the check cancels it and restores the current path. Local explorers answer at once and never show the pending state.
i18n — explorer.address.not_found “Path not found: {}” / “경로를 찾을 수 없습니다: {}” / “パスが見つかりません: {}” · explorer.address.placeholder “Go to folder or file…” / “폴더나 파일로 이동…” / “フォルダーま たはファイルへ移動…” · spinner tooltip explorer.address.checking “Checking the remote path…” / “원격 경로 확인 중…” / “リモートのパスを確認しています…”.
Hidden files on a wide toolbar · create field width
A wide cell gets a hidden files toggle at the end of the view group (after Find and Preview); it folds into More with the rest of the group under --tasty-explorer-toolbar-compact-below 440. It is an IconButton with the eye glyph that shows pressed while hidden files are shown; its tooltip carries the action words and the shortcut, so the native More menu does not need a shortcut column. The create-in-folder field stops at --tasty-explorer-create-field-max-width 240 and the muted “in {folder}” follows it at space-sm, in Detail and List alike. Grid shows no caption (the target cell's drop-target ring already says where). The target ring uses the drag-and-drop ring — radius, not radius-sm — so the two never differ.
Tooltip strings reuse explorer.more.hidden_show / .hidden_hide with the bound shortcut appended in parentheses (the shortcut is formatted, not translated). The b11 kit sample with radius-sm is superseded.
Edge auto-scroll · dragging items · tree expand
One rule for every drag that can reach a list edge: drag-select, dragging items in the listing, and dragging over the sidebar tree (each in its own scroll area). Inside the --tasty-explorer-autoscroll-zone 24 band the list scrolls at speed = 20 rows/s × t², t = 0 at the band's inner edge and 1 at the list edge, in rows of the current view (Detail row, List row, Grid cell row) so every view moves the same number of items. Past the edge the pointer keeps the full speed; it does not grow with distance. Nothing is drawn for the band. Hovering a closed tree folder during an item drag expands it after --tasty-explorer-drag-expand-delay 800 ms; the drop-target ring is the waiting cue, no extra mark.
Undo / Redo rows — copy · empty history · Windows · shortcuts
The Undo / Redo group sits where it is now: in the empty-area menu between the create / paste group and Properties. It is always there: with no step a row is a disabled bare “Undo” / “Redo”. A row names the step with a gerund (“Undo copying 3 items”), with an en _one form. Rows bound to a command show their first binding in the native shortcut column — Undo and Redo as well as Rename and Trash; one rule for the whole menu. A step that can no longer be undone is disabled with the reason in the tooltip on Linux and macOS; Windows popup menus have no item tooltips, so there the row label itself becomes the reason sentence. The reason is a full sentence (“A newer file is there now”), so the lead-in ends with a period, not a colon.
i18n — explorer.menu.undo “Undo {op}” / “실행 취소: {op}” / “元に戻す: {op}” · explorer.menu.undo_none “Undo” / “실행 취소” / “元に戻す” · explorer.menu.redo_none “Redo” / “다시 실행” / “やり直す” · explorer.menu.op_copy “copying {n} items” (_one “copying 1 item”) / “{n}개 복사” / “{n} 件のコピー” · explorer.menu.op_move “moving {n} items” (_one “moving 1 item”) / “{n}개 이동” / “{n} 件の移動” · explorer.menu.undo_stale changes to “Can't undo. {reason}” / “되돌릴 수 없습니다. {reason}” / “元に戻せません。{reason}” (reason strings stay as they are) · settings labels confirmed: settings.keybindings.explorer_undo_label “Undo:” / “실행 취소:” / “元に戻す:” · explorer_redo_label “Redo:” / “다시 실행:” / “やり直す:”.
Remaining UI — counting stage · Properties rows · low preview (b12)
Progress — counting stage · zero-byte work
A copy or move first counts what it will touch. That stage now has its own words — “Counting items to copy · {n} found” — and no bar (there is nothing to measure against yet); the count refreshes at most 4 times a second. Once counting ends, the line counts files found, not top-level items, and names the file in work: “Copying 1,204 of 200,000 · a/b/file.txt” shows only the name. The bar follows bytes; when the byte total is 0 or unknown it follows the file count, so a tree of empty files still moves.
i18n — explorer.op.counting_copy “Counting items to copy · {n} found” / “복사할 항목 세는 중 · {n}개 찾음” / “コピーする項目を数えています · {n} 件” · explorer.op.counting_move “Counting items to move · {n} found” / “이동할 항목 세는 중 · {n}개 찾음” / “移動する項目を数えています · {n} 件”. Numbers use the locale grouping.
Properties — own path · folder permissions · links · partial size
For one item, a Path row (the item's own absolute path, mono, Copy) replaces Location; several items keep Location (their common folder). Folders get the same Permissions row as files. A link shows what the user cares about — its target: Kind “Symbolic link”, Link target, then the target's kind, size and modified time with “Target” labels; the link's own lstat values are not shown. A broken link is Kind “Broken link” (the word the Type column uses) with the warning link glyph in the header and a “Target not found” line under Link target. When a folder size count skipped folders it could not read, a warning line under Size says so.
i18n — explorer.props.path “Path” / “경로” / “パス” · .target_kind “Target kind” / “대상 종류” / “リンク先の種類” · .target_size “Target size” / “대상 크기” / “リンク先のサイズ” · .target_modified “Target modified” / “대상 수정 시각” / “リンク先の更新日時” · .target_missing “Target not found” / “대상을 찾을 수 없음” / “リンク先が見つかりません” · .partial “Some folders couldn't be read” / “일부 폴더를 읽지 못함” / “一部のフォルダーを読み取れませんでした” · Kind for a broken link reuses explorer.kind.broken_link.
Preview panel in a low cell — header only
When the body under the preview header would be shorter than --tasty-explorer-preview-body-min-height 120, the body is hidden and only the header stays (name + kind · size), without its bottom rule. A 30 px thumbnail says nothing; the header still tells the user what is selected. The panel keeps its width, and the width rules (hide under preview-min + list-min) are unchanged.
Conditional items B1–B7 (no design until decided).
Remote explorer — writes that differ from local (2026-10-12)
D1 · A remote that can't write — rows shown disabled, reason in the tooltip and toast
A remote running a new Tasty shows the write rows exactly like local. A remote that can't write (older Tasty, or a headless server) now shows the rows disabled (option b) instead of hiding them. Reason: the rows tell the user the command exists and where it went; hiding them made the menu look broken next to a writable remote in the next cell. The native menu can only grey a row and add a tooltip (Linux, macOS); Windows shows no tooltip, so the shortcut path keeps the toast, and the toast now names the reason. Two reasons, two strings. The toolbar create group follows the same rule (disabled, tooltip = reason), the same way a local folder without write access does.
i18n: explorer.remote.write_needs_update “Update Tasty on {host} to change remote files.” / “{host}의 Tasty를 업데이트하면 원격 파일을 바꿀 수 있습니다.” / “{host} の Tasty を更新すると、リモートのファイルを変更できます。” · explorer.remote.write_headless “{host} can't change files (server without a window).” / “{host}는 파일을 바꿀 수 없습니다(화면 없는 서버).” / “{host} はファイルを変更できません(画面のないサーバー)。”. The old single “remote write not supported” toast key goes. Tooltip and toast share the key.
D2 · Waiting for the remote — the input row stays, read-only, with a spinner
Local creates the item at once; a remote needs a round trip. After ↵ the input row stays where it is, the field turns read-only (text kept, so the user sees what was sent) and a Spinner takes the trailing slot. Nothing moves, so a fast reply looks like the local flow and a slow one still shows that work is in progress. On success the row becomes the real item at its sorted place, selected. On failure the field becomes editable again with the text kept, and the error toast shows as today. Esc does nothing while waiting: the request is already on the remote. The Rename popup works the same way: it stays open, field read-only, a “Renaming on {host}…” line with a Spinner, both buttons disabled; it closes on success.
i18n: explorer.remote.waiting “Waiting for {host}” / “{host} 응답 기다리는 중” / “{host} の応答を待っています” · explorer.remote.renaming “Renaming on {host}…” / “{host}에서 이름 바꾸는 중…” / “{host} で名前を変更中…”.
Rename popup — the read-only field against the popup ground (2026-10-14)
The rename popup (workspace · tab · category · explorer item) is a dialog: its ground is --tasty-dialog-bg → bg-panel, the same as every frame on Overlays › Dialogs. It is not the popover ground surface-raised. On bg-panel the read-only field keeps its normal neutral box (state-disabled-fill → surface-raised, state-disabled-border → border-default) and its edge reads. The waiting state changes only the ink and the buttons, never the box. Inputs that really sit on surface-raised (menus, banners, popovers) pass ground="raised": every state steps the box down to bg-panel with a border-strong edge.
The editable field keeps input-bg (surface-raised) like every dialog Input. The product's current crust fill for the editable rename field is not a token value; align it to input-bg when the ground moves to dialog-bg.
D3 · Connection lost — result unknown
When the link drops mid-job or while a conflict question is open, the remote stops at the item it was on and this side never gets the report. The result card says exactly that: warning tone, it stays until dismissed, and it does not claim that nothing changed. There is no Retry and no Undo (the current state is unknown, so neither can be offered honestly); the only control is ×. A second line says the folder will be read again on reconnect, because that is when the user can see what happened. An open conflict popup for that job closes with it.
i18n: explorer.remote.lost_title “Connection to {host} lost · result unknown” / “{host} 연결이 끊겨 결과를 알 수 없음” / “{host} との接続が切れたため結果が不明です” · explorer.remote.lost_body “The {op} stopped at the item it was on. Some items may already have changed. The folder reloads when the connection is back.” / “{op}은(는) 하던 항목에서 멈췄습니다. 일부 항목은 이미 바뀌었을 수 있습니다. 다시 연결되면 폴더를 다시 읽습니다.” / “{op}は処理中の項目で止まりました。一部の項目はすでに変更されている可能性があります。再接続するとフォルダーを再読み込みします。”
D4 · Remote progress line and queue — host shown
A remote job uses the same status line, bar and Cancel. The host is shown at the start of the line (remote glyph + host, --tasty-workspace-mirror-fg), because remote work waits in its own queue and runs one job at a time per connection; without the mark a slow job looks stuck behind nothing. “+n queued” counts only this cell's remote queue. The queue popover gets a header line “On {host} · one at a time” and otherwise the same rows.
i18n: explorer.remote.queue_title “On {host} · one at a time” / “{host} · 한 번에 하나씩” / “{host} · 一度に 1 件”. The host mark has no string of its own (it is the host name).
D5 · Drag chip — new refusal reasons · conflict popup unchanged
Drops inside one remote now work. Two refusals get their own line-2 reason: between this computer and a remote, or between two remotes, says different computer; a remote that can't write no longer says “read-only” (writable remotes exist now) but “this remote can't change files”. The name-conflict popup is used as is for remote jobs.
i18n: explorer.drag.reason.other_computer “different computer” / “다른 컴퓨터” / “別のコンピューター” · explorer.drag.reason.remote changes from “remote folders are read-only” to “this remote can't change files” / “이 원격은 파일을 바꿀 수 없음” / “このリモートはファイルを変更できません”.
Delete permanently — confirm and results (2026-10-12)
Context menu — “Delete permanently…” right after Move to Trash
One new row, directly under Move to Trash in the same file-ops group, so the two ways to delete sit together and the safe one comes first. Danger tone like Trash, label ends in “…” because it always opens a confirm. The shortcut column shows the first configured binding (defaults: Shift+Delete on Windows / Linux, ⌘⌥⌫ on macOS); with no binding it shows nothing. The row is hidden where there is nothing to delete (empty-area menu) and on remote explorers (no remote delete in this scope). The key on a remote shows an info toast; with no selection it does nothing. No toolbar button.
Confirm — window-scope centred dialog · file · folder · link · many · long paths
The confirm uses the existing Window-scope centred confirm (scrim over the window, the same as the category and script confirms): the act is destructive and the dialog must hold the keyboard. It does not borrow the 380 category width; it reuses the info-modal frame (--tasty-info-modal-width 440, max --tasty-info-modal-max-height 360) because it carries a list. Structure: title row (danger trash glyph, “Delete permanently?”) · a summary sentence that names one item or counts the top-level items · the target list (glyph, name, parent path in mono, ellipsised at the front so the end of the path stays) that scrolls past --tasty-explorer-delete-list-max-height · the line “This can't be undone.” · footer Cancel (ghost) and Delete permanently (danger). A folder row says “contents included”; a link says the link is deleted and its target is kept. Same-named files from different folders are told apart by their parent path.
Confirm — Latte · ko · ja · narrow window · keyboard focus
Default focus is Cancel. Tab / Shift+Tab move between Cancel and Delete permanently (the list is not a stop; it scrolls with the wheel). ↵ and Space press the focused button, so an Enter left over from the menu or the key that opened the dialog presses Cancel. Auto-repeat of the opening key is ignored. Esc, the close action and a click on the scrim all cancel: nothing is deleted. The danger button fires once and is disabled right after the press, so a double click or repeat can't queue two jobs. On a window narrower than the frame, the frame takes the window width minus the scrim margin and the footer wraps.
i18n (new): explorer.delete.menu “Delete permanently…” / “영구 삭제…” / “完全に削除…” · explorer.delete.title “Delete permanently?” / “영구 삭제할까요?” / “完全に削除しますか?” · explorer.delete.one_file · explorer.delete.one_folder · explorer.delete.one_link · explorer.delete.many + explorer.delete.many_one (“1 item will be deleted…”) · explorer.delete.many_folders + _one (“including everything inside {k} folders” / “1 folder”) · explorer.delete.irreversible “This can't be undone.” / “되돌릴 수 없습니다.” / “元に戻せません。” · explorer.delete.confirm “Delete permanently” / “영구 삭제” / “完全に削除” · explorer.delete.tag_folder / tag_link / tag_broken_link · explorer.delete.remote_unavailable. Full strings in the frame source gallery/explorer-delete-frame.jsx XD_COPY.
Cancel and invalid targets — nothing is deleted
The confirm keeps a snapshot of the paths and of the explorer that opened it. Changing the selection or folder behind the scrim does not change the targets. If that explorer goes away (pane closed, surface replaced) the confirm closes and an info toast in the window says nothing was deleted. If a target disappeared or was replaced between the confirm and the job reaching it, that item is skipped, never the new file; it is reported in the result as “changed since you confirmed”.
Progress — “Deleting”, items only, stop at the next item
After the press the job goes into the existing bounded queue and shows on the explorer's status line. The verb is Deleting (never “Moving to Trash” or “Copying”). Total bytes are unknown, so the line shows top-level items only and the bar is the track with no fill (the existing unknown-size rule); no percent. Cancel × reads “Stop after this item”: stopping happens at item boundaries; a folder that is being deleted finishes or fails on its own. Queue rows read “Delete {n} items permanently”.
i18n: explorer.op.deleting “Deleting {i} of {n} · {file}” / “삭제 중 {i}/{n} · {file}” / “削除中 {i}/{n} · {file}” · explorer.op.stop_after_item “Stop after this item” / “이 항목까지만 하고 멈춤” / “この項目の後で停止” · explorer.queue.delete “Delete {n} items permanently” + _one.
Results — done · stopped · partial · folder partly deleted · failed · retry
The existing result card, with no Undo anywhere and nothing added to the Undo / Redo history. A partial result says what was deleted and lists what is left (≤ 3 paths + “and n more”, Copy paths). A folder that failed part-way is listed as left, with the reason “Folder kept; some of its contents were deleted”, so the card never says nothing was deleted when something was. Retry appears when items are left; it opens the same confirm again for those items only, with the same warning. Stopped tells how many are gone and that they can't be brought back.
i18n: explorer.result.deleted “Deleted {n} items permanently” / “항목 {n}개를 영구 삭제함” / “{n} 個の項目を完全に削除しました” + _one · explorer.result.delete_stopped “Stopped · {i} of {n} deleted. Deleted items can't be restored.” / “멈춤 · {n}개 중 {i}개 삭제됨. 삭제된 항목은 되돌릴 수 없습니다.” / “停止しました · {n} 個中 {i} 個を削除。削除した項目は元に戻せません。” · explorer.result.delete_partial “Deleted {i} of {n} · {k} left” / “{n}개 중 {i}개 삭제 · {k}개 남음” / “{n} 個中 {i} 個を削除 · {k} 個が残っています” · explorer.result.delete_failed “Couldn't delete {n} items” + _one / “항목 {n}개를 삭제하지 못함” / “{n} 個の項目を削除できませんでした” · explorer.result.nothing_deleted · explorer.result.folder_partly_deleted “Folder kept; some of its contents were deleted” / “폴더는 남았지만 안쪽 일부는 삭제됨” / “フォルダーは残りましたが、中身の一部は削除されました” · explorer.result.changed_skipped “Changed since you confirmed — skipped” / “확인한 뒤 바뀌어 건너뜀” / “確認後に変更されたためスキップ” · explorer.result.source_closed.
Temporary folder left behind (2026-10-12)
Result card — one extra line, kind raised to at least Warning
Copy, cross-disk move and their undo stage through a hidden .tasty-copy-* folder. When it can't be removed at the end, the card keeps the items' own title (the items did finish as reported) but its kind is raised to at least Warning: a leftover folder on disk is something to act on, and a green Success card next to a failure line reads as a contradiction. Danger stays Danger. The folder gets the existing ResultLine shape — path, then a fixed reason with the OS error in parentheses — placed after any item lines. Actions are unchanged: Copy paths (includes the folder path), Undo when the job allows it, no Retry. The card stays until closed, like every Warning card.
New key explorer.result.staging_left: en “Temporary folder couldn't be removed ({error})” · ko “임시 폴더를 지우지 못했습니다 ({error})” · ja “一時フォルダーを削除できませんでした({error})”. {error} is the OS error text as shown on other result lines.
Properties — time format · Open · permissions (2026-10-21)
Time format setting — Settings › General › General
One Select row under Language, because it is a display preference like the language. Three choices, all in the computer's local time zone: Minutes (default, today's format), Seconds, Seconds and UTC offset. The offset is numeric (+09:00): zone abbreviations differ by OS and are ambiguous (CST), and a UTC choice would contradict “local time”. The caption under the row names the fields it changes and shows the chosen format with a fixed example date; the option labels stay short words. Saving applies to open Properties popups on their next draw. The Detail list date column is not affected.
i18n (new): settings.general.props_time_format “Time format in Properties” / “속성 창 시각 형식” / “プロパティの日時形式” · .props_time_format.caption (above) · .props_time_format.example “Example:” / “예:” / “例:” · options .minutes “Minutes” / “분까지” / “分まで” · .seconds “Seconds” / “초까지” / “秒まで” · .seconds_offset “Seconds and UTC offset” / “초 + UTC 오프셋” / “秒+UTCオフセット”. Config value: minutes | seconds | seconds_offset.
Time fields in each format — wrapping in the value column
Time values are mono caption in the ~264 value column. The longest format (26 characters) fits on one line at the 360 popup width. When the popup is narrower (cell smaller than 360), a time value wraps only between its parts — date · time · offset — so the offset moves whole to line 2 and digits are never split. Paths keep wrap-anywhere.
Open — footer button, shown where Enter would open
A footer under the fields, right-aligned Open (secondary sm, no icon), above a separator rule. It does what Enter / double-click does in the list for the item the popup was opened for: a file follows the file-handler settings, a folder is entered in that Explorer, a link opens its target. The popup stays open. It is shown for one item, local or remote (remote follows the remote list's Enter; where that list's Enter does nothing, the footer is not drawn). A broken link shows Open disabled with the tooltip “Target not found”. Several items and can't read have no footer. Tab order: field buttons (Copy, Edit) then Open then ×.
i18n (new): explorer.props.open “Open” / “열기” / “開く” · disabled tooltip reuses explorer.props.target_missing.
Permissions — Edit opens an editor under the row, Apply writes
The Permissions row keeps its text value and gets an Edit button (ghost sm) at its end. Edit opens a box under the row, spanning the content width — from the label column's left edge to the right inset (330 at the 360 popup), not inside the 226 value column, which is narrower than the 242 the grid needs — with a 3 × 3 checkbox grid — Owner · Group · Others × Read · Write · Execute — and the resulting mode as text + octal, so the change is readable before it runs. Nothing is written until Apply (primary sm, disabled while nothing changed); Cancel closes the box. Apply goes through the file-operation queue: the box shows Applying… with a Spinner and locks its controls; on success it closes and the row shows the new value; on failure it stays open with the reason (OS error) and the grid re-reads the real mode. Esc closes the popup and never undoes an applied change. Folders say the change is not recursive; setuid / setgid / sticky are kept and said so when present. When the user is neither the owner nor root, Edit is not drawn and a muted line says only the owner can change permissions. No Edit on links, several items, remote items or can't read.
i18n (new): explorer.props.perm.edit “Edit” / “편집” / “編集” · .perm.read / .write / .execute · .perm.owner / .group / .others · .perm.apply “Apply” / “적용” / “適用” · .perm.applying “Applying…” / “적용 중…” / “適用中…” · .perm.folder_only “Changes this folder only, not the items in it.” · .perm.special_kept “Special bits (setuid, setgid, sticky) are kept.” · .perm.locked “Only the owner can change permissions.” · .perm.failed “Couldn't change permissions” · .perm.read_only “Read-only” · .perm.windows_hint “Windows file attribute. Other access rules aren't changed here.” — ko / ja as in the PA table in gallery/explorer-ops-props-actions.jsx. Cancel reuses common.cancel.
Permissions — narrow Explorer cell: the box re-lays itself, never clips (2026-10-22)
The popup shrinks with its cell and the editor box now does too: it is max-width 100% of the content and chooses its layout from its own width. Grid (box ≥ explorer-props-perm-grid-min-width 242) is the batch 8 layout, unchanged. Stack (box ≥ explorer-props-perm-stack-min-width 186): the role column goes away; column heads sit over the three 56 cells and each role name is a line of its own above its three checkboxes, so the grid stays aligned and needs 3 × 56 + 18. List (box < 186): no head row; each role name, then three labelled checkboxes (Read · Write · Execute) one per line. List has no lower bound: labels and role names wrap anywhere, nothing is cut, only the box grows taller (the popup grows with it, as today). Cell width 56 is never changed. In every layout the readout wraps only at “ · ”, Cancel / Apply wrap onto a second line (still right-aligned, Apply last) when they don't fit side by side, and the box's right edge, padding and the footer Open keep their inset. No horizontal scroll.
Boundaries in cell width (popup = cell − 2 × space-sm; box = popup − 2 × 14 − 2): grid at cell ≥ 288, stack 232 – 287, list below 232. These follow from the tokens; the implementation compares the box width with the two tokens, never the cell width. Shared function tasty_ui_widgets::perm_box takes the available width and returns the layout.
Already built — context menu OS rows · link note · partial read (check)
Confirmed with two changes. Order: Move to Trash · separator · Open in system · Show in Finder / File Explorer / File Manager (one item only) · separator · Properties — the OS rows get their own group so Properties stays the last row on its own. Label: “Open in system” in sentence case, as the existing menu row already reads (“Show in Finder” keeps the product name). Icons: external for Open in system, folderOpen for Show in …. Remote cells hide both rows and the group's separator. The link note wording and place (muted note under the fields, same as the remote note) are confirmed. Some items couldn't be read under Kinds and — for a remote Total size with folders are confirmed as built.
Strings unchanged except the label case: explorer.menu.open_in_system “Open in system” / “시스템에서 열기” / “システムで開く” · .reveal_macos “Show in Finder” / “Finder에서 보기” / “Finder で表示” · .reveal_windows “Show in File Explorer” / “파일 탐색기에서 보기” / “エクスプローラーで表示” · .reveal_linux “Show in File Manager” / “파일 관리자에서 보기” / “ファイルマネージャーで表示” · explorer.props.link_note as requested.