tasty.gallery
Plugins · Explorer file ops
← Design
Specs
Theme

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”.

Layout spec
order
nav · PathField (flex 1) · create · | · view · SegToggle
create
folderPlus · filePlus · IconButton sm, icon-only
view
search · columns — toggles (active state)
separator
1px × 16 · separator · 4 each side
narrow
cell < --tasty-explorer-toolbar-compact-below 440 → one More (more glyph) · menu = same rows, toggles show the check
remote
create hidden · view kept
no write access
create disabled · tooltip “Can't write to this folder”
not in toolbar
Delete · Rename · Copy / Cut / Paste (menu + keys)
Tokens used
explorer-toolbar-compact-below— → size-440separator— group separatorstate-disabled-fg— disabled create

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.

Layout spec
empty area
create group first · then clipboard · then Properties
folder
create rows join the file-ops group · target = that folder
file / multi
no create rows · Properties last
remote
create rows hidden
target folder
fixed when the command starts — navigating away still creates there
Tokens used
menu-bg— menumenu-item-fg— rows

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.

Layout spec
placement
top of the listing while typing → sorted place after ↵
editor
Input (control-height 28) in the name slot · Detail row stays 28 · List row grows 22 → 28 while editing · Grid: 160 wide, centred under the glyph
defaults
“New folder” all selected · “untitled.txt” stem selected · taken → “… 2”
↵ / Esc
confirm / cancel (nothing on disk)
blur
valid → confirm · empty or invalid → cancel
after
sorted place, selected — only if the user still views that folder
write fails
error Toast + reload (unchanged)
Tokens used
surface-active— editing rowinput-border-focus— editor focustable-cell-height— row 28

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).

Layout spec
input
Input invalid (danger border)
message
under the field · menu-bg · 1px explorer-name-error-fg · caption 11 · text-primary · max explorer-name-error-max-width 240 · overlaps rows below
empty
“Enter a name.”
invalid
“A name can't contain “{char}”.” · also reserved names on Windows: “This name is reserved by the system.”
exists
“{name}” already exists in this folder. — checked on ↵
after an error
input stays open, text kept
Tokens used
explorer-name-error-fg— → accent-dangermenu-bg— message boxshadow-popover— message lift

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).

Layout spec
chip
surface-raised · 1px border-strong · radius · shadow-popover · max 240 · pointer +12 / +12
line 1
glyph + name (body 13, ellipsis) · many → layers glyph + “{n} items”
line 2
caption 11 · op glyph 12 · op colour · target folder semibold
default op
same disk → move · other disk → copy · modifier flips (key from Keybindings)
refused
onto itself / own subfolder · remote explorer · no write access
Esc
cancels the drag, nothing changes
Tokens used
explorer-drag-move-fg— → accent-primaryexplorer-drag-copy-fg— → accent-successexplorer-drag-refused-fg— → accent-dangerexplorer-drag-chip-max-width— → size-240

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.

Layout spec
mark
inset 1px ring + tint fill · radius of the row / cell · no layout change
folder row / cell
target
file row / empty space
target = folder being shown → ring on the whole list body
tree node · favorite
target
closed tree folder
expands after 800 ms hover
refused
no ring · chip line 2 in refused colour
drop
same operation as Paste → progress, conflicts and results below
Tokens used
explorer-drop-target-border— → accent-primaryexplorer-drop-target-bg— accent-primary × tint-fill-alphatint-fill-alpha— 0.12

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.

Layout spec
over an explorer
copy into · window drop overlay suppressed for that cell
elsewhere
window overlay + open with handler (unchanged)
op
always Copy to (no move from the OS)
remote
refused · “remote folders are read-only”
drag out
not in this pass
Tokens used
explorer-drop-target-border— body ringexplorer-drag-copy-fg— Copy tofile-drop-overlay-bg— window overlay fill · 0.12 (was alpha 31)file-drop-overlay-border— window overlay edge · 0.36 (was alpha 153)file-drop-overlay-fg— glyph + label · full ink

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.

Layout spec
where
status line of the explorer that started the job (never a window-wide toast)
bar
explorer-progress-height 2 · top edge of the status line · track / fill · width = bytes done
label
“{Copying|Moving|Moving to Trash} {i} of {n} · {file}” · caption · ellipsis
bytes
mono · “1.2 / 3.4 GB” · unknown size → item count only, bar full-width track with no fill
queued
Tag “+n queued” · opens the queue popover
queue
menu surface · 288 · above the status line · Spinner row = running (×) · layers row = queued (×)
cancel
× on the status line · stops items not yet done
Tokens used
explorer-progress-height— → size-2explorer-progress-track— → surface-raisedexplorer-progress-fill— → accent-primarystatus-bar-height— 24

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.

Layout spec
scope
popup clamped to the explorer cell · scrim-bg over the body · shadow-modal
width
explorer-conflict-width 400
title
14 semibold · “{name}” already exists · caption “in {folder}”
compare
Existing / Incoming · size · modified · mono caption
buttons
Cancel the rest (ghost, left) · Skip · Replace (files only) · Keep both (primary, ↵)
apply to all
Checkbox, only when more conflicts remain
focus
opens only when the explorer has focus · otherwise the status line waits (accent-warning) + Show
overwrite
offered for files from v1 · always an explicit choice
Tokens used
explorer-conflict-width— → size-400scrim-bg— cell dimaccent-warning— waiting line + bar

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.

Layout spec
placement
Toast inside the cell · bottom-right · status-bar-height + 8 · stack upward
done
success · “{Copied|Moved} {n} items to {folder}” · Undo (ghost) · standard time
cancelled
info · “{Copy} cancelled · {i} of {n} done” · standard time · partial file removed by the app
partial
warning · stays · ≤ 3 paths + “and n more” · Retry n · Copy paths
failed
danger · stays · same list and actions
path
mono caption · ellipsis at the front · reason under it (muted)
trash unavailable
danger · “Trash isn't available on this drive. Nothing was deleted.” · no fallback
undo
on the done toast while it shows · result toast lists what could not be undone
dismiss
× on every result
Tokens used
toast-accent-success— donetoast-accent-warning— partial / undo partialtoast-accent-danger— failed / trashtoast-max-width— 320

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.

Layout spec
open
context menu Properties (every shape) · keybinding action explorer.properties
popup
explorer-props-width 360 · scoped to the cell · shadow-modal · Esc / ×
title
glyph + name · 14 semibold · ellipsis
fields
label explorer-props-label-width 96 · caption muted · value body 13 (mono caption for paths / dates / mode) · wrap anywhere
local file
Kind · Size (with bytes) · Modified · Created · Location · Permissions
folder
Size counts in the background (Spinner) · item count
symlink
Link target + Copy
several
“{n} items” · Kinds · Total size · Location (common parent)
remote
Kind · Size · Modified · Location · muted note
selection change
popup keeps its item
Tokens used
explorer-props-width— → size-360explorer-props-label-width— → size-96shadow-modal— popupspinner-indicator— counting

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.

Layout spec
toggle
toolbar view group · columns glyph · keybinding action explorer.toggle_preview
panel
right · explorer-preview-width 288 · splitter 200 … 460 · 1px separator · remembered per explorer
header
40 · name (body, ellipsis) · kind · size (caption muted)
text
first 64 KB · mono caption · line-height-ui · on bg-sidebar · no wrap
image
fit, never upscaled · on bg-sidebar
other / folder
“No preview for this file type”
loading
Spinner · “Loading preview…” — the old preview is cleared first
too large
> 1 MB (app limit) · “Too large to preview”
unreadable
error tone · OS reason (mono)
narrow cell
panel hides itself below preview-min 200 + explorer-list-min-width 200 = 400; toggle stays on
Tokens used
explorer-preview-width— → size-288explorer-preview-min-width— → size-200explorer-preview-max-width— → size-460explorer-list-min-width— → size-200 (b10)explorer-preview-header-height— → size-40 (b10)bg-sidebar— preview bed

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).

Layout spec
scope
Grid only · local only
slot
explorer-grid-thumb-size 40 · every cell
thumbnail
fit 40 × 40 · 1px separator · radius-sm
loading / over limit
image glyph 16 · accent-info
cell
80 wide (unchanged) · +24 tall
Tokens used
explorer-grid-thumb-size— → size-40accent-info— image glyph fallbackseparator— thumb edge

“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.

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.

Layout spec
highlight
Detail · List · Grid — filter and search
.. row
kept while filtering (not counted) · dropped from recursive results
filter 0
bar “0 of {total}” · search glyph · “No names match “{query}”” · no sub-line
skipped
explorer.find.skipped_one “1 folder skipped” · skipped “{n} folders skipped”
cap
5,000 results · “{n}+ found · stopped” · status line hint · results kept
More rows
Find ↔ Close find · Show preview ↔ Hide preview · no check marks on any OS
narrow
< 440: New folder · New file · Find · Preview all fold into More
Tokens used
explorer-match-fg— all viewsexplorer-toolbar-compact-below— → 440accent-warning— skipped

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.

Layout spec
leading / trailing space
refused · explorer.name.edge_space
only spaces
explorer.name.empty
Windows trailing .
explorer.name.reserved (same copy)
box
explorer-name-error-max-width 240
Tokens used
explorer-name-error-max-width— → size-240 (new)explorer-name-error-fg— border + edge

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.

Layout spec
source left
warning · stays · no Undo · Retry n deletes the originals again · Copy paths
title
explorer.result.source_left_move
line
explorer.result.source_not_removed · reason in ( )
undo kept
explorer.result.changed_kept on the undo result line
Tokens used
toast-accent-warning— both

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”.

Layout spec
can't read
title = item glyph + name · alertTriangle + “Can't read properties” (explorer-error-fg) · reason mono · Retry (secondary sm)
title weight
regular (font-weight-normal) at 14 — rule for every popup title until a semibold UI face ships
none selected
“Select a file” (unchanged)
several
layers glyph · “{n} items selected” · “Select one file to preview it.”
pixel limit
same screen as > 1 MB · sub “Over {px} px on a side, or needs more than {mem} to decode.” · reason = real size
units
binary sizes say MiB (256 MiB here, 512 MiB in the image viewer)
Kind
“{TYPE} image” · Folder · Symbolic link · “{EXT} file” · File
list min
explorer-list-min-width 200
tokens
props-padding-x 14 · props-row-min-height 24 · props-row-line 20 · props-row-pad-top 2 · preview-header-height 40
Tokens used
explorer-props-padding-x— → size-14explorer-props-row-min-height— → size-24explorer-props-row-line— → size-20explorer-props-row-pad-top— → size-2explorer-preview-header-height— → size-40explorer-error-fg— can't read

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.

Layout spec
table (en / ko / ja)
md markdown → Markdown / Markdown / Markdown · txt → Text / 텍스트 / テキスト · html htm → HTML · json → JSON · toml → TOML · yaml yml → YAML · csv → CSV · pdf → PDF document / PDF 문서 / PDF 書類 · zip tar gz tgz bz2 xz zst 7z rar → Archive / 압축 파일 / アーカイブ · sh bash zsh fish ps1 → Shell script / 셸 스크립트 / シェルスクリプト · images → “{TYPE} image” / “{TYPE} 이미지” / “{TYPE} 画像”
fallback
“{EXT} file” / “{EXT} 파일” / “{EXT} ファイル” · no extension File / 파일 / ファイル · Folder / 폴더 / フォルダ
Type column
same word · ellipsis in the 92 column · full word in the tooltip
link
Type + preview header “Link to {kind}” / “{kind} 링크” / “{kind} へのリンク” · size = target · Properties Kind stays “Symbolic link” · broken “Broken link” / “끊긴 링크” / “リンク切れ”
byte limit
“Over 1 MiB.” / “1 MiB 초과” / “1 MiB を超えています。” (NBSP between number and unit)
popup title
regular weight AND text-primary — no accent colour (`strong`) on any popup title
Tokens used
text-muted— Type columnexplorer-preview-header-height— 40

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.

Layout spec
kind
Warning toast · Surface scope · standard time · not a result card
queue full
action Show queue → queue popover · explorer.request.queue_full + .show_queue
too large
no action · explorer.request.too_large
numbers
not in the copy
Tokens used
toast-accent-warning— both

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.

Layout spec
progress
explorer.progress.checking “Checking {i} of {n} · {name}” → .removing_originals “Removing originals {i} of {n} · {name}”
queue title
explorer.queue.remove_originals “Remove {n} originals”
success
explorer.result.removed_originals “Removed {n} originals” · _one “Removed 1 original” · standard time · no Undo
one press = one card
combined counts · tone of the worse outcome · shown when every job of the press ends
kept
back on source_left_move (warning · stays · Retry n · Copy paths)
changed after move
reuse changed_kept (path line = the original) — no new key
Tokens used
toast-accent-warning— kepttoast-accent-success— removed

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.

Layout spec
kept only
explorer.result.undo_kept_copy “Undid copy · kept {n} changed items” · _one “Undid copy · kept 1 changed item”
mixed
undo_partial_copy wins the title · kept lines stay listed
singular
en `_one` for every count key · ko/ja one string
Tokens used
toast-accent-warning— both

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.

Layout spec
link
target glyph · trailing link 12 (explorer-link-glyph-size) · gap space-xs · explorer-link-glyph
broken
link glyph in the item slot · explorer-link-broken-fg → accent-warning · name normal · Type “Broken link” · tooltip “Target not found: {path}”
views
Detail · List · Grid alike
Tokens used
explorer-link-glyph— → text-mutedexplorer-link-glyph-size— → icon-size-xs 12explorer-link-broken-fg— → accent-warning

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.

Layout spec
current item
1px inset ring · explorer-cursor-ring → border-focus · keyboard only · over selection
marquee
fill explorer-marquee-bg (tint-fill 0.12) · 1px explorer-marquee-border (tint-border 0.36) · no radius
auto-scroll
explorer-autoscroll-zone 24 at top / bottom · speed grows toward the edge · no indicator
samples
Mocha = keyboard ring on a selected row · Latte = drag-select
Tokens used
explorer-cursor-ring— → border-focusexplorer-cursor-ring-width— → border-width 1explorer-marquee-bg— fillexplorer-marquee-border— edgeexplorer-autoscroll-zone— → size-24

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.

Layout spec
hidden toggle
More row · action words · Ctrl+Shift+. / ⌘⇧. · default off · per explorer, remembered
hidden shown
name + glyph explorer-hidden-fg (→ text-muted) · no opacity
hidden off
status “{n} items · {h} hidden”
show in folder
opens the real parent · selects + current item · scrolls into view · no flash
search result · native
Open · Show in enclosing folder · separator · the item's usual file menu from Copy path on · no glyphs (OS menu) · Open = file → default handler, folder → navigate into it
redo
context menu beside Undo · Ctrl+Shift+Z · Ctrl+Y · ⌘⇧Z
history
10 steps per explorer · no list · stale step = disabled row + tooltip reason
Undo card
unchanged — one step, no Redo button
Tokens used
explorer-hidden-fg— → text-muted

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.

Layout spec
input
under the target row · indent explorer-create-indent 16 · caption “in {folder}” muted
target
drop-target ring + fill while the field is open
Grid
new cell right after the folder cell
keys
Enter creates inside · Esc cancels · listing stays
Tokens used
explorer-create-indent— → space-lg 16explorer-drop-target-border— target

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.

Layout spec
pending
remote only · field keeps the typed path + focus border · Spinner 12 in the trailing slot after 200 ms · list + status unchanged · Esc cancels
end
answer → navigate (file: open folder + select) · 8 s → go to the typed path
not found
error toast, explorer scope · path = the confirmed absolute path
placeholder
“Go to folder or file…”
Tokens used
explorer-address-pending-delay— → motion-ui-fade 200explorer-address-pending-size— → icon-size-sm 12spinner-indicator— spinner

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.

Layout spec
toggle
IconButton sm · eye · pressed (active) while shown · last in the view group · folds into More under 440
tooltip
“Show hidden files (Ctrl+Shift+.)” / “Hide hidden files (Ctrl+Shift+.)” · macOS (⌘⇧.)
More row
native menu, no shortcut column — fine
create field
max explorer-create-field-max-width 240 · caption at space-sm right after it
Grid
no caption
target ring
same as the drop ring · radius (4)
Tokens used
explorer-create-field-max-width— → size-240explorer-hidden-fg— shown hidden itemsexplorer-drop-target-border— target

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.

Layout spec
band
explorer-autoscroll-zone 24, top + bottom · no indicator
speed
20 rows/s × t² (ease-in) · rows of the current view · rule constant
outside
keeps 20 rows/s · app drives the scroll offset itself
applies to
drag-select · item drag (listing) · item drag over the sidebar tree
tree expand
explorer-drag-expand-delay 800 ms · cue = drop-target ring
Tokens used
explorer-autoscroll-zone— → size-24explorer-drag-expand-delay— → duration-800 (new primitive)

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.

Layout spec
position
empty-area menu · create / paste | Undo · Redo | Properties (unchanged)
empty
always shown · disabled bare Undo / Redo
shortcut column
every row bound to a command shows its first binding — Undo / Redo included
stale
disabled · tooltip “Can't undo. {reason}” (GTK · macOS) · Windows: same sentence as the row label
Tokens used
state-disabled-fg— disabled rows (kit)

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.

Layout spec
counting
explorer.op.counting_copy / counting_move · no bar · ≤ 4 updates / s
after
“Copying {i} of {n} · {name}” · n = files found · name = current file name
bar
bytes; byte total 0 or unknown → file count
Tokens used
explorer-progress-fill— barexplorer-progress-track— track

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.

Layout spec
order
Kind · Size · Modified · Created · Path · Permissions
Path
one item: own absolute path + Copy (replaces Location) · several: Location (common folder) + Copy
folder
Permissions row, same format as files
link
Kind · Link target · Target kind · Target size · Target modified · Path — no lstat values
broken
Kind “Broken link” · header glyph link in explorer-link-broken-fg · “Target not found” under Link target
partial
warning line under Size · alertTriangle 12 · explorer-props-partial-fg
Tokens used
explorer-props-partial-fg— → accent-warningexplorer-link-broken-fg— broken link

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.

Layout spec
threshold
body height < explorer-preview-body-min-height 120 → body hidden
kept
header (name + kind · size), no bottom rule
width
unchanged rules
Tokens used
explorer-preview-body-min-height— → size-120explorer-preview-header-height— 40

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.

Layout spec
choice
(b) show disabled · tooltip = reason (Linux / macOS)
rows
Cut · Paste · New folder · New file · Rename · Move to Trash
read rows
Copy path · Copy · Open stay enabled
toolbar
create group disabled · tooltip = reason
shortcut
info toast in the cell · the same reason string
reasons
old Tasty → update hint · headless → can't change files
Tokens used
state-disabled-fg— disabled row / buttontoast-accent-info— reason toast

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.

Layout spec
input row
stays in place · Input readOnly · text kept
indicator
Spinner icon-size-sm · trailing slot · tooltip “Waiting for {host}”
Esc / blur
ignored while waiting
success
row → item at its sorted place, selected
failure
field editable again, text kept · error toast (unchanged)
Rename popup
open · field readOnly · Spinner line · Cancel + Rename disabled · closes on success
Tokens used
input-readonly-fg— sent textspinner-indicator— waiting

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.

Layout spec
rename popup ground
dialog-bg → bg-panel (was surface-raised in the product)
field · waiting
Input readOnly · state-disabled-fill (surface-raised) + border-default on bg-panel
field · normal
Input · input-bg + input-border
on surface-raised
Input ground="raised" · bg-panel + border-strong · all states
disabled on raised
state-disabled-fill-raised + state-disabled-border-raised
ink
unchanged: readonly text-secondary · disabled text-disabled
Tokens used
dialog-bg— → bg-panelinput-readonly-bg— → state-disabled-fillinput-raised-bg— → bg-panelinput-raised-border— → border-stronginput-readonly-raised-bg— → state-disabled-fill-raisedinput-readonly-raised-border— → state-disabled-border-raised

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.

Layout spec
tone
warning · stays until dismissed
title
“Connection to {host} lost · result unknown”
body
caption · text-secondary · stopped at the item · may have changed · reload on reconnect
actions
× only — no Retry, no Undo
conflict popup
closes with the job
verb
{op} = copy / move / move to Trash / rename / new folder / new file
Tokens used
toast-accent-warning— card

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.

Layout spec
host mark
remote glyph (workspace-mirror-icon-size) + host · mono caption · workspace-mirror-fg · first on the line
queued
“+n queued” = this cell's remote queue
queue header
“On {host} · one at a time” · caption · muted
rest
same bar · Cancel · rows as local
Tokens used
workspace-mirror-fg— host markworkspace-mirror-gap— glyph ↔ host

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.

Layout spec
inside one remote
Move to / Copy to as local
local ↔ remote · remote ↔ remote
refused · “different computer”
remote can't write
refused · “this remote can't change files”
conflict popup
unchanged
Tokens used
explorer-drag-refused-fg— refused line

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.

Layout spec
label
“Delete permanently…” · danger · trash glyph
position
right after Move to Trash, same group
shortcut
first configured binding · none → blank
empty area / no selection
row hidden · key does nothing
remote
row hidden · key → info toast
toolbar
no button
Tokens used
accent-danger— danger row (MenuItem danger)

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.

Layout spec
scope
Window scrim (existing centred-confirm policy) · bound to the source explorer
frame
info-modal-width 440 · max info-modal-max-height 360 · bg-panel · 1px border-strong · shadow-modal
padding
space-md top · size-14 sides (dialog shell)
title
trash glyph icon-size-md accent-danger · font-size-max semibold
summary
body · text-secondary · names the one item or counts top-level items + folders
list
bg-app well · 1px border-default · rows: glyph 14 · name body · tag caption · parent mono caption, front ellipsis · scroll past explorer-delete-list-max-height 160
warning
alertTriangle accent-danger + “This can't be undone.” text-primary semibold
footer
Cancel ghost sm · Delete permanently danger sm · right-aligned · wraps on narrow
link
link glyph · “The item it points to is kept.” · broken link glyph in explorer-link-broken-fg
Tokens used
info-modal-width— frame widthinfo-modal-max-height— frame maxexplorer-delete-list-max-height— new · → size-160accent-danger— glyphsscrim-bg— window dim

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.

Layout spec
default focus
Cancel
Tab order
Cancel → Delete permanently → (wraps)
↵ / Space
press the focused button
opening key
auto-repeat ignored · a held Enter hits Cancel
Esc · close · scrim click
cancel · 0 deletes
single fire
danger button disabled after the first press
focus ring
focus-ring-width · border-focus · 1px offset
narrow
frame = window width − scrim margin · footer wraps
Tokens used
border-focus— focus ringfocus-ring-width— 2

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”.

Layout spec
snapshot
paths + source explorer, taken when the confirm opens
source gone
confirm closes · info toast · 0 deletes
target gone / replaced
skipped · result line “Changed since you confirmed — skipped”
cancel
no toast (nothing happened)
Tokens used
toast-accent-info— source gone

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”.

Layout spec
label
“Deleting {i} of {n} · {name}” · top-level items
bar
track only · no fill · no percent
bytes
not shown
cancel ×
tooltip “Stop after this item” · stops at the next item boundary
queue row
“Delete {n} items permanently” · _one
Tokens used
explorer-progress-track— track only

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.

Layout spec
done
success · “Deleted {n} items permanently” / _one “Deleted “{name}” permanently” · standard time · no Undo
stopped
info · “Stopped · {i} of {n} deleted. Deleted items can't be restored.”
partial
warning · stays · “Deleted {i} of {n} · {k} left” · list of what is left · Retry {k}… · Copy paths
folder part-way
listed as left · “Folder kept; some of its contents were deleted”
failed
danger · “Couldn't delete {n} items” · “· nothing was deleted” only when the job touched nothing
Retry
label ends in “…” · reopens the confirm for the left items
history
no Undo / Redo entry for any outcome
Tokens used
toast-accent-success— donetoast-accent-warning— partialtoast-accent-danger— failed

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.

Layout spec
kind
max(items' kind, Warning) · Success / Info → Warning · Danger stays
title
items' result, unchanged
line
ResultLine: folder path (left-ellipsis) + fixed reason (OS error)
order
after item failure lines · counts toward 'and N more'
actions
Copy paths (+ folder) · Undo if allowed · no Retry
lifetime
until closed
i18n
explorer.result.staging_left — en / ko / ja below
Tokens used
toast-max-width— card widthtext-secondary— pathtext-muted— reason

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.

Layout spec
place
Settings › General › General, after Language · SettingsRow + Select field-width-lg 200 · RowCaption under the row
minutes (default)
%Y-%m-%d %H:%M → 2026-10-21 12:12
seconds
%Y-%m-%d %H:%M:%S → 2026-10-21 12:12:05
seconds_offset
%Y-%m-%d %H:%M:%S %:z → 2026-10-21 12:12:05 +09:00 (local offset, DST-aware per value)
applies to
Properties Modified · Created · Target modified only
example
fixed sample instant rendered in the chosen format, mono, text-secondary
Tokens used
settings-label-width— 150field-width-lg— 200settings-row-caption-gap— 4

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 ×.

Layout spec
footer
padding space-sm explorer-props-padding-x · top 1px explorer-props-footer-border · justify end
button
secondary sm “Open” · same action as Enter for the popup's own item · popup stays open
shown
one item: file · folder · link (target) · remote (if its list opens on Enter)
disabled
broken link · tooltip “Target not found”
hidden
several items · can't read (Retry stays the only button)
keys
no new shortcut · Tab reaches it · Space / Enter
Tokens used
explorer-props-footer-border— new · → separatorexplorer-props-value-fg— new · → text-secondary (names the existing value ink)

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.

Layout spec
entry
Edit ghost sm at the end of the Permissions value line · hidden when the box is open
box
2026-10-21: under the row, spans the CONTENT width (grid-column 1 / -1 of the field row — label column included; 330 at popup 360) · grid left-aligned in the box · Unix and Windows boxes alike · 1px explorer-props-perm-border · radius · padding space-sm · gap space-sm · bg stays bg-panel
grid
role column + 3 columns explorer-props-perm-cell-width 56 (unchanged) = 224 + padding 2 × 8 + border 2 = 242 ≤ 330 · rows row-min-height 24 · Checkbox per bit
long words
column heads centred and role names left-aligned inside their 56 cell; they wrap to a second line rather than ellipsise (ja 書き込み / 読み取り / グループ ≈ 48 fit on one line at caption size) · heads bottom-aligned so a wrapped head grows upward
value line
text value, Edit and the locked line stay in the value column; only the editor box spans
readout
mode text + octal, mono caption, text-secondary — updates as boxes change
Apply
primary sm, disabled until the mode differs · applying: Spinner + “Applying…”, grid + buttons disabled
success
box closes, row value = new mode (re-read)
failure
alertCircle + “Couldn't change permissions” explorer-props-perm-error-fg + OS error mono muted · grid re-read · box stays · queue result shows as today
locked
euid ≠ owner and ≠ root → no Edit, muted line under the value
Windows
one Checkbox “Read-only” + caption · same Apply / Cancel · never called ACL
not editable
links · several · remote · can't read → no Edit, no line
kept
setuid / setgid / sticky preserved, line shown only when present · never recursive
Tokens used
explorer-props-perm-border— new · → border-strongexplorer-props-perm-cell-width— new · → size-56explorer-props-perm-error-fg— new · → accent-danger (bg-panel: Mocha 7.08 · Latte 4.80)explorer-props-perm-locked-fg— new · → text-muted

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.

Layout spec
box
max-width 100% of the field row (label column included) · never wider than the popup content · border, radius, padding space-sm, gap space-sm as batch 8
grid
box ≥ explorer-props-perm-grid-min-width (4 × 56 + 2 × 8 + 2 × 1 = 242) — batch 8 as is
stack
186 ≤ box < 242 · head row = 3 cells of 56 (heads centred, bottom-aligned, wrap in the cell) · per role: name line (caption, value-fg, wraps anywhere) → gap explorer-props-perm-role-gap 4 → row of 3 cells, min-height row-min-height 24 · role groups gap space-xs
list
box < explorer-props-perm-stack-min-width (3 × 56 + 18 = 186) · per role: name line → role-gap → three Checkbox with label (Read / Write / Execute), one per 24 row · role groups gap space-sm · no head row · no lower bound
switch
by the box's own border-box width, on every layout pass — dragging the cell divider re-lays the open box at once (no animation, terminal motion 0); checked state, pending changes, focus and the applying / failed state are kept
readout
mode text and “· octal” are two unbreakable parts; wraps between them
buttons
flex-wrap, justify end, gap space-sm · Cancel then Apply; Apply drops to line 2 (right) when both don't fit
Windows
one labelled Checkbox + hint — no grid, so no layout switch; the box only caps at 100% and the hint wraps; buttons wrap as above
keys
DOM order = Tab order in all layouts: Owner R W X → Group R W X → Others R W X → Cancel → Apply · focus ring unchanged (Checkbox focus-visible)
a11y
grid / stack checkboxes keep aria-label “Role Column”; list draws the column word as the visible label
copy
no new strings — the list labels reuse explorer.props.perm.read / .write / .execute
Tokens used
explorer-props-perm-grid-min-width— new · calc(4 × perm-cell-width + 2 × space-sm + 2 × border-width) = 242explorer-props-perm-stack-min-width— new · calc(3 × perm-cell-width + 2 × space-sm + 2 × border-width) = 186explorer-props-perm-role-gap— new · → space-xs (role name → its checkboxes, stack / list)

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.