Compare commits

...

381 commits

Author SHA1 Message Date
三咲雅 misaki masa
9accf929f4
feat: trash bin (#4144)
Some checks are pending
Cachix / Publish Flake (push) Waiting to run
Check / clippy (push) Waiting to run
Check / rustfmt (push) Waiting to run
Check / stylua (push) Waiting to run
Draft / build-unix (gcc-aarch64-linux-gnu, ubuntu-latest, aarch64-unknown-linux-gnu) (push) Waiting to run
Draft / build-unix (gcc-i686-linux-gnu, ubuntu-latest, i686-unknown-linux-gnu) (push) Waiting to run
Draft / build-unix (gcc-riscv64-linux-gnu, ubuntu-latest, riscv64gc-unknown-linux-gnu) (push) Waiting to run
Draft / build-unix (gcc-sparc64-linux-gnu, ubuntu-latest, sparc64-unknown-linux-gnu) (push) Waiting to run
Draft / build-unix (macos-latest, aarch64-apple-darwin) (push) Waiting to run
Draft / build-unix (macos-latest, x86_64-apple-darwin) (push) Waiting to run
Draft / build-unix (ubuntu-latest, x86_64-unknown-linux-gnu) (push) Waiting to run
Draft / build-windows (windows-latest, aarch64-pc-windows-msvc) (push) Waiting to run
Draft / build-windows (windows-latest, x86_64-pc-windows-msvc) (push) Waiting to run
Draft / build-musl (aarch64-unknown-linux-musl) (push) Waiting to run
Draft / build-musl (x86_64-unknown-linux-musl) (push) Waiting to run
Draft / build-snap (amd64, ubuntu-latest) (push) Waiting to run
Draft / build-snap (arm64, ubuntu-24.04-arm) (push) Waiting to run
Draft / snap (push) Blocked by required conditions
Draft / draft (push) Blocked by required conditions
Draft / nightly (push) Blocked by required conditions
Test / test (macos-latest) (push) Waiting to run
Test / test (ubuntu-latest) (push) Waiting to run
Test / test (windows-latest) (push) Waiting to run
2026-07-22 03:13:58 +08:00
nabsei
6aa75594db
docs: add a note about following the contribution guidelines in the PR template (#4140)
Co-authored-by: 三咲雅 misaki masa <sxyazi@gmail.com>
2026-07-21 08:59:20 +08:00
Omenabaka
60d94608cb
fix: magick preview of multiframe files (#4024)
Co-authored-by: 三咲雅 misaki masa <sxyazi@gmail.com>
2026-07-19 23:58:16 +08:00
三咲雅 misaki masa
52640fcece
feat: new hub VFS kind (#4131) 2026-07-17 03:04:19 +08:00
三咲雅 misaki masa
b25ae9f82d
fix: fail when package source is missing (#4124) 2026-07-14 01:11:34 +08:00
三咲雅 misaki masa
4dab480347
fix: preserve search results on refresh (#4123) 2026-07-13 22:51:42 +08:00
三咲雅 misaki masa
d39637f40c
feat!: support VFS services with same name but different schemes (#4120) 2026-07-13 20:01:47 +08:00
三咲雅 misaki masa
ddc6632505
fix: flush last input before submitting the completion (#4119) 2026-07-13 09:01:59 +08:00
三咲雅 misaki masa
6d84921e70
feat!: custom VFS provider (#4118) 2026-07-13 02:01:37 +08:00
三咲雅 misaki masa
dbb0cc0d55
feat: experimental %y, %Y, %t, %T, %yN, %YN, %tN, %TN shell formatting parameters (#4108) 2026-07-08 17:04:12 +08:00
三咲雅 misaki masa
aa850f9a3b
feat: input history (#4104) 2026-07-06 18:43:08 +08:00
三咲雅 misaki masa
c92c4aba88
feat: make visual mode support wraparound scrolling (#4101) 2026-07-05 18:06:25 +08:00
ebugusey
6e0aaee822
feat: add ya CLI completions to Windows package (#4099) 2026-07-03 22:09:57 +08:00
三咲雅 misaki masa
b8ceaea940
feat!: show file icons in trash/delete/overwrite confirmations (#4096) 2026-07-02 04:09:12 +08:00
三咲雅 misaki masa
21550a7eb5
feat: new target parameter for create action (#4088) 2026-06-28 02:10:23 +08:00
三咲雅 misaki masa
39527f488f
perf: switch memory allocator back to jemalloc (#4084) 2026-06-26 22:38:48 +08:00
三咲雅 misaki masa
de611b0e6b
perf: switch memory allocator to mimalloc (#4083) 2026-06-26 14:34:28 +08:00
三咲雅 misaki masa
ef654f4420
feat: context-aware icons for inputs (#4080) 2026-06-24 23:14:16 +08:00
Alastair
2a106aa231
fix: filter keybindings by chord in command palette (#4078)
Co-authored-by: Alastair Scheuermann <alastairsounds@users.noreply.github.com>
Co-authored-by: sxyazi <sxyazi@gmail.com>
2026-06-24 21:50:51 +08:00
SpiritCroc
b1ee2e72ff
feat: H/M/L Vim-like motion for moving cursor relative to viewport (#3970)
Co-authored-by: sxyazi <sxyazi@gmail.com>
2026-06-24 17:42:20 +08:00
三咲雅 misaki masa
581886a355
feat!: make help menu a command palette (#4074) 2026-06-22 23:57:29 +08:00
三咲雅 misaki masa
4f45ddb514
fix: ignore NumLock state when comparing keybindings (#4071) 2026-06-20 23:57:12 +08:00
三咲雅 misaki masa
0ba1811af3
fix: use cleaned and normalized $PWD as startup CWD to avoid MSYS2/git-bash path quirks (#4068) 2026-06-20 12:47:00 +08:00
三咲雅 misaki masa
feeea673f1
fix: normalize \\?\-prefixed Verbatim paths when creating relative symlinks on Windows (#4067) 2026-06-20 12:07:33 +08:00
三咲雅 misaki masa
ef5db6e257
fix: sort interactive cd candidates case-insensitively so short exact matches match first (#4066) 2026-06-20 11:15:40 +08:00
三咲雅 misaki masa
0daa5df644
fix: use WebP as magick preset preloader cache format to keep image transparency (#4065) 2026-06-20 10:37:24 +08:00
三咲雅 misaki masa
6144d2748e
fix: keep package hashes indifferent to line endings when ya pkg pulls packages (#4064) 2026-06-20 09:34:41 +08:00
三咲雅 misaki masa
84a5be0b6a
feat: Lua API for printing escape sequences to the terminal (#4059) 2026-06-19 23:14:13 +08:00
Alastair
ab8d634f8e
fix: split graceful vs passive stop in terminal stream (#4055)
Co-authored-by: Alastair Scheuermann <alastairsounds@users.noreply.github.com>
Co-authored-by: sxyazi <sxyazi@gmail.com>
2026-06-19 09:30:52 +08:00
三咲雅 misaki masa
85132ff343
fix: quit once event stream closes in case terminal won't send a SIGHUP signal (#4052) 2026-06-18 21:21:38 +08:00
三咲雅 misaki masa
53d36b5186
refactor: switch to ratatui-core (#4050) 2026-06-18 16:23:26 +08:00
三咲雅 misaki masa
446a5721bf
refactor: move Lua bindings down into each subcrates (#4049) 2026-06-18 15:06:31 +08:00
三咲雅 misaki masa
f1e93d7f52
feat: new input DDS event watching changes in input content (#4043) 2026-06-14 19:58:23 +08:00
三咲雅 misaki masa
b37be0529a
feat: new ui.Input element (#4040) 2026-06-13 19:37:29 +08:00
三咲雅 misaki masa
9b920ed2e3
refactor: unify rendering behavior with a new renderer (#4038) 2026-06-11 20:44:22 +08:00
三咲雅 misaki masa
07a5deb109
feat: dynamic keymap Lua API (#4031) 2026-06-10 15:09:49 +08:00
Biswapriyo Nath
4086fbb166
feat(nix): shell completions for ya CLI (#4030) 2026-06-10 13:22:31 +08:00
sxyazi
9cfeb67db7
refactor: make Lua bindings Send (#4027) 2026-06-09 22:52:43 +08:00
三咲雅 misaki masa
f6113d3865
feat: advanced app overall style (#4023) 2026-06-07 21:46:07 +08:00
三咲雅 misaki masa
ea7fa2127c
perf: eliminate hacks in image and UI conflict resolution (#4022) 2026-06-06 14:35:12 +08:00
cyanyux
8a4b0b208e
fix(snap): make bundled magick find libgomp at runtime (#4016) 2026-06-06 13:53:59 +08:00
三咲雅 misaki masa
9bdd6777e4
feat: keymap Lua bindings (#4021) 2026-06-06 13:45:52 +08:00
三咲雅 misaki masa
8e80798984
feat: new gait for input backward and forward actions (#4012) 2026-06-01 13:16:01 +08:00
三咲雅 misaki masa
e892bf7d90
fix: decouple yazi_boot crate init from yazi_term init (#4011) 2026-05-31 13:07:11 +08:00
三咲雅 misaki masa
601cb10b32
feat: Unix domain socket on Windows as the underlying transport layer for DDS (#4009) 2026-05-29 15:33:03 +08:00
三咲雅 misaki masa
6c30430666
fix: a TOCTOU race condition while creating temporary directories (#4008) 2026-05-28 18:07:31 +08:00
三咲雅 misaki masa
7a51626505
feat: drag and drop (#4005) 2026-05-28 12:05:06 +08:00
三咲雅 misaki masa
cf8b54179e
feat: terminal drag and drop protocol (#3999) 2026-05-25 10:30:04 +08:00
三咲雅 misaki masa
f7fea2b19a
docs: update contributing guidelines 2026-05-25 09:31:04 +08:00
Dongda Li
d9cd1907d9
feat: image preview with Überzug++ on Niri (#3990)
Co-authored-by: 三咲雅 misaki masa <sxyazi@gmail.com>
2026-05-23 11:02:40 +08:00
JinHyuk Sung
5d643ee01a
fix: restrict preview cache directory permissions (#3984)
Co-authored-by: sxyazi <sxyazi@gmail.com>
2026-05-22 18:56:19 +08:00
三咲雅 misaki masa
c037855c4f
feat!: new virtual terminal backend (#3989) 2026-05-22 17:00:06 +08:00
三咲雅 misaki masa
3f5cc47a48
fix: normalize main entry IDs for consistent internal sharing (#3966) 2026-05-15 07:30:38 +08:00
Daniel Vincent
fde563380b
feat: bulk create (#3793)
Co-authored-by: sxyazi <sxyazi@gmail.com>
2026-05-12 02:12:08 +08:00
三咲雅 misaki masa
247f925e53
fix: get rid of the Cargo curse (#3952) 2026-05-10 00:57:57 +08:00
Immanuel Tikhonov
22fb9e0d09
fix: keep repeated ! negations in a condition right-associative (#3946)
Co-authored-by: sxyazi <sxyazi@gmail.com>
2026-05-09 22:32:18 +08:00
三咲雅 misaki masa
92b9ea3794
fix: make the input component sync and reactive (#3949) 2026-05-09 09:26:07 +08:00
三咲雅 misaki masa
3968c4799c
docs: rename id of the fetcher rule to group (#3943) 2026-05-08 08:57:50 +08:00
三咲雅 misaki masa
aa526434f0
chore: bump to v26.5.6 (#3935) 2026-05-06 02:02:39 +08:00
三咲雅 misaki masa
10fc76db07
feat: custom styles for plugins (#3934) 2026-05-05 06:03:25 +08:00
三咲雅 misaki masa
5ad1e003f2
chore: use Tombi for TOML schema validation (#3932) 2026-05-05 01:37:17 +08:00
三咲雅 misaki masa
62dd22b8da
fix: put package cache in cache dir instead of state dir (#3930) 2026-05-02 22:15:25 +08:00
三咲雅 misaki masa
740d891989
feat: new key-close DDS event (#3925) 2026-05-01 23:09:13 +08:00
三咲雅 misaki masa
1caa5807c6
fix: mock OS-level reaping on Windows to avoid child processes potentially detached (#3922) 2026-04-30 18:50:51 +08:00
三咲雅 misaki masa
c4c533e3ef
feat: connect to SFTP servers with no authentication (#3916)
Co-authored-by: yadokani389 <155296849+yadokani389@users.noreply.github.com>
2026-04-26 16:07:11 +08:00
yadokani389
88848932fc
fix: use cspell package in devShell (#3912) 2026-04-26 15:27:21 +08:00
WINLAIC
a2996908de
feat: drag-resize panes with mouse (#3890)
Co-authored-by: Lingxuan Ye <yelingxuan@xiaomi.com>
Co-authored-by: sxyazi <sxyazi@gmail.com>
2026-04-22 19:35:19 +08:00
sxyazi
4a2e5addcd
feat: new app:theme action that hot-reload user themes/flavors (#3906) 2026-04-21 23:34:56 +08:00
三咲雅 misaki masa
369c47539c
feat: dynamic open/opener Lua API (#3901) 2026-04-19 16:58:57 +08:00
三咲雅 misaki masa
d1a27bca1f
feat: warn ya.mgr_emit() of its deprecation (#3902) 2026-04-19 16:51:56 +08:00
三咲雅 misaki masa
ae4c138f49
fix: paste --force doesn't work on existing read-only files with the same name (#3894) 2026-04-17 00:16:29 +08:00
sxyazi
463150848d
feat: dynamic previewer Lua API (#3891) 2026-04-16 17:05:46 +08:00
sxyazi
80a54c3dd5
fix: use u64 for parsing Linux partition blocks (#3873) 2026-04-11 09:25:17 +08:00
三咲雅 misaki masa
9abbc12212
docs: update readme 2026-04-11 08:12:19 +08:00
三咲雅 misaki masa
74d903a49a
feat: expose stash action and give it a new search source (#3872) 2026-04-11 07:59:38 +08:00
三咲雅 misaki masa
f6408656b4
fix!: make ui.Style immutable (#3862) 2026-04-07 11:06:31 +08:00
sxyazi
354cdef907
feat: hover cursor over the new file after extracting (#3854) 2026-04-06 15:32:18 +08:00
sxyazi
ec178fdb52
refactor: unify progress representation and add cleanup state (#3853) 2026-04-05 14:05:42 +08:00
三咲雅 misaki masa
4bb4f37555
refactor: simplify the remote progressive file copier (#3852) 2026-04-04 21:28:57 +08:00
三咲雅 misaki masa
0cedbd9c7b
feat: hover cursor over the new file after copying/cutting/linking/hardlinking (#3846) 2026-04-03 20:45:34 +08:00
sxyazi
ad655eda52
refactor: simplify FromStr implementations with strum 2026-04-03 09:13:52 +08:00
三咲雅 misaki masa
4857d46918
feat: make Action a deserializer (#3841) 2026-04-02 19:15:36 +08:00
三咲雅 misaki masa
6b143c1f4d
fix: enable sysfs files in the preset empty previewer (#3833) 2026-03-31 22:13:14 +08:00
三咲雅 misaki masa
4225c34fea
refactor: pull the process API out into yazi-binding crate (#3830) 2026-03-31 22:04:12 +08:00
三咲雅 misaki masa
607bc0dd2b
refactor: prepare for custom VFS provider (#3821) 2026-03-31 19:18:18 +08:00
Daniel Ramos Rodríguez
2514725e56
fix: handle file URIs in the desktop entry (#3827) 2026-03-31 10:50:12 +08:00
Omenabaka
935eb6b298
feat: allow active tab's inner separator to blend with its background (#3817) 2026-03-27 07:22:20 +08:00
sxyazi
b67798ea19
feat: Vim-like lua action that runs an inline Lua snippet (#3813) 2026-03-25 19:57:31 +08:00
三咲雅 misaki masa
5b2dd41e64
fix: ensure the status bar always has the directory length available (#3810) 2026-03-24 22:12:23 +08:00
三咲雅 misaki masa
e2fae151a5
refactor: simplify the text wrapper (#3808) 2026-03-24 20:14:04 +08:00
sxyazi
5c05350d52
perf: avoid unnecessary allocations in code highlighting (#3804) 2026-03-23 18:06:04 +08:00
三咲雅 misaki masa
d4924ebcad
fix: treat nfs4 mounts as soundless for directory refresh (#3800) 2026-03-22 06:36:34 +08:00
三咲雅 misaki masa
c703332b4b
feat: new bulk_exit action that customizes the prompt for bulk operations (#3792) 2026-03-21 17:26:44 +08:00
Mario Stief
7b61eb1595
feat: new --discard for ya pkg that discard local changes made to packages (#3781)
Co-authored-by: sxyazi <sxyazi@gmail.com>
2026-03-20 15:47:06 +08:00
三咲雅 misaki masa
a246e9c7c0
feat: make cmp component follow input cursor (#3784) 2026-03-19 21:19:10 +08:00
三咲雅 misaki masa
cc2414728c
feat: new ya.exec() API and ya exec subcommand to execute an action and await its result (#3780) 2026-03-18 19:18:36 +08:00
三咲雅 misaki masa
6a1ddb71e0
feat: deserialize from Data (#3778) 2026-03-18 14:13:23 +08:00
sxyazi
da96795303
refactor: make Clippy happy 2026-03-17 09:41:46 +08:00
三咲雅 misaki masa
de01a56ac5
fix: use edit opener for files with MIME application/wine-extension-ini (#3771) 2026-03-16 13:29:03 +08:00
三咲雅 misaki masa
d22c96b69c
perf: reuse previewed and spotted widgets when possible (#3765) 2026-03-15 15:18:58 +08:00
三咲雅 misaki masa
fa1ee46edc
refactor: implement FromLua instead of TryFrom<Value> (#3760) 2026-03-13 20:28:44 +08:00
三咲雅 misaki masa
3534ba0705
feat: new experimental ya.co() API that creates a coroutine (#3757) 2026-03-12 16:18:30 +08:00
三咲雅 misaki masa
e4829372db
feat: link to the quick-start in yazi --help where lists all common keybinds (#3754) 2026-03-11 13:34:11 +08:00
三咲雅 misaki masa
1be991ec72
docs: update image 2026-03-11 13:06:06 +08:00
IcEarthlight
dee27a2377
feat: new ind-hidden and key-hidden DDS events to change hidden status in Lua (#3748)
Co-authored-by: sxyazi <sxyazi@gmail.com>
2026-03-10 12:56:07 +08:00
三咲雅 misaki masa
8932ddd66d
perf: preset multi spotter only updates sizes for folders to cut memory usage (#3751) 2026-03-10 12:39:19 +08:00
itsvyle
741f84e22b
feat: certificate authentication for SFTP VFS provider (#3716)
Co-authored-by: sxyazi <sxyazi@gmail.com>
2026-03-07 15:45:27 +08:00
三咲雅 misaki masa
f10b8910ad
fix: be a little defensive while parsing the output of 7zz -ba (#3744) 2026-03-06 23:09:40 +08:00
Daniel Vincent
dbf81deeab
feat: multi-file spotter (#3733)
Co-authored-by: sxyazi <sxyazi@gmail.com>
2026-03-06 09:27:32 +08:00
三咲雅 misaki masa
be91b4111c
fix: shift of auto-wrap points in both code and JSON previewers (#3739) 2026-03-05 20:16:03 +08:00
三咲雅 misaki masa
9648d8a43b
fix: wrong intersection check in preview and spot rendering (#3736) 2026-03-04 15:36:13 +08:00
Yashank
b92b576ce1
feat: new hovered condition specifying different icons for hovered files (#3728)
Co-authored-by: sxyazi <sxyazi@gmail.com>
2026-03-03 20:18:55 +08:00
bitbloxhub
0efeaf5f64
chore: update flake inputs (#3731) 2026-03-03 09:41:15 +08:00
三咲雅 misaki masa
0a83f0bd27
feat: keep span info when config parsing fails (#3727) 2026-03-02 16:47:28 +08:00
三咲雅 misaki masa
3cdc3ecb70
feat: new hey DDS event fires when static messages are restored from persistence (#3725) 2026-02-28 13:01:03 +08:00
三咲雅 misaki masa
884c265de1
fix: avoid blocking on read-half of the watch channel (#3721) 2026-02-26 20:36:22 +08:00
三咲雅 misaki masa
05aeae3b09
fix: ensure cargo-deb picks up the right build artifacts (#3720) 2026-02-26 14:18:00 +08:00
三咲雅 misaki masa
9eb4d03a44
feat: dual watcher for local files (#3713) 2026-02-26 08:16:58 +08:00
三咲雅 misaki masa
0bb6379a2a
docs: update readme 2026-02-26 08:08:36 +08:00
三咲雅 misaki masa
8b773a62a5
fix: make group order of files from flat view stable on select (#3712) 2026-02-23 19:01:04 +08:00
三咲雅 misaki masa
16aa3cd0fc
fix: escape backslashes in ImageMagick font path parameter (#3708) 2026-02-22 19:59:47 +08:00
三咲雅 misaki masa
d84f0b1265
refactor: async Command over emitting shell actions (#3706) 2026-02-22 10:31:15 +08:00
三咲雅 misaki masa
e49cdfe220
fix: fallback in Windows casefold for MS-DOS devices (#3700) 2026-02-19 13:22:14 +08:00
三咲雅 misaki masa
9af792ef69
feat: new --in for search action to set search directory (#3696) 2026-02-18 17:16:05 +08:00
三咲雅 misaki masa
578016e00f
refactor: use the term "action" instead of "command" (#3694) 2026-02-18 17:08:48 +08:00
三咲雅 misaki masa
7f339381f1
feat: new marker_symbol option to specify the symbol used for marking files (#3689) 2026-02-16 21:42:36 +08:00
三咲雅 misaki masa
31cfc58188
feat: new download DDS event fires when remote files are downloaded (#3687) 2026-02-16 12:52:58 +08:00
SuniRein
c0f96f553b
feat: new sort_fallback option to control fallback sorting behavior (#3077)
Co-authored-by: sxyazi <sxyazi@gmail.com>
2026-02-15 14:00:58 +08:00
三咲雅 misaki masa
13bdb164a4
feat!: new ind-app-title DDS event to customize the app title (#3684) 2026-02-13 13:03:44 +08:00
三咲雅 misaki masa
c3bc4fecad
fix: Chafa v1.18.1 causes random ghost keypresses when previewing images (#3678) 2026-02-12 00:12:42 +08:00
三咲雅 misaki masa
98b1614016
feat: new fs.unique() creates a unique file or directory (#3677) 2026-02-11 12:51:49 +08:00
三咲雅 misaki masa
a8a4b684a1
feat: deduplicate empty directories in preset archive previewer (#3676) 2026-02-11 11:07:23 +08:00
三咲雅 misaki masa
30ec603441
refactor: async fs.access() instead of sync io.open() (#3670) 2026-02-10 11:29:46 +08:00
三咲雅 misaki masa
1a121bbfb0
feat: new fs.access() API to access the filesystem (#3668) 2026-02-09 08:50:01 +08:00
三咲雅 misaki masa
0aeeb3b0f7
feat!: custom tab name (#3666) 2026-02-08 10:46:38 +08:00
sxyazi
dc66f79cef
feat!: finer control over concurrent workers (#3661) 2026-02-06 22:27:08 +08:00
三咲雅 misaki masa
6757fed5aa
feat: new which:dismiss command to dismiss the which component (#3654) 2026-02-02 02:53:02 +08:00
sxyazi
339002ee48
fix: make ya pkg ignore default remote name in user Git config (#3648) 2026-01-31 18:43:25 +08:00
sxyazi
a015d4a7ef
feat: new relay-notify-push DDS event to allow custom notification handlers (#3642) 2026-01-31 09:25:17 +08:00
三咲雅 misaki masa
dfdb235d74
feat: allow using ps.sub() in init.lua directly without a plugin (#3638) 2026-01-30 10:12:21 +08:00
三咲雅 misaki masa
1ea0ea072c
refactor: move Lua bindings into their own yazi-binding crate (#3636) 2026-01-29 23:12:29 +08:00
三咲雅 misaki masa
eaeda8b4fa
perf: reduce memory allocations by using Lua 5.5 external strings (#3634) 2026-01-29 07:58:15 +08:00
三咲雅 misaki masa
cfd9a22125
feat!: upgrade Lua to 5.5 (#3633) 2026-01-29 01:08:22 +08:00
三咲雅 misaki masa
6d942ea011
feat: subscribe to and intercept app-level events through DDS (#3632) 2026-01-29 00:34:28 +08:00
三咲雅 misaki masa
583345296f
refactor: move Term to its own yazi-term crate (#3629) 2026-01-28 22:26:47 +08:00
三咲雅 misaki masa
24c60419bb
refactor: new yazi-emulator crate (#3624) 2026-01-27 22:37:34 +08:00
三咲雅 misaki masa
e0911f2507
refactor: new yazi-tty crate (#3623) 2026-01-27 22:03:48 +08:00
三咲雅 misaki masa
6150571738
fix: ignore target-dir to avoid deadlocks during cargo install (#3620) 2026-01-27 08:49:39 +08:00
三咲雅 misaki masa
592e70a1c9
feat: new cx.which API to access the which component state (#3617) 2026-01-27 01:29:14 +08:00
三咲雅 misaki masa
06c665a086
fix: account for border when calculating message widths (#3615) 2026-01-26 17:23:57 +08:00
三咲雅 misaki masa
92880b844b
refactor: make command data cloneable (#3613) 2026-01-26 07:36:35 +08:00
三咲雅 misaki masa
face6aed40
feat: new ind-which-show DDS event to change the which component behavior (#3608) 2026-01-25 03:06:50 +08:00
Ciarán O'Brien
afc694c122
fix: archive extraction fails for target paths with non-ASCII characters on Windows (#3607)
Co-authored-by: sxyazi <sxyazi@gmail.com>
2026-01-24 18:03:51 +08:00
Jared Cervantes
49b8324bbf
fix: remove duplicate categories in the .desktop file (#3602)
Signed-off-by: Jared Cervantes <jared@jaredcervantes.com>
2026-01-23 21:14:41 +08:00
sxyazi
4e0acf8cbf
chore: bump version to 26.1.22 2026-01-22 23:31:21 +08:00
三咲雅 misaki masa
d17e7e14cd
feat: experimental module-level async support (#3594) 2026-01-21 19:19:39 +08:00
三咲雅 misaki masa
e6d995710f
fix: properly drop the permit for terminal resources (#3592) 2026-01-20 18:25:02 +08:00
sxyazi
ca4cc59413
fix: make environment detection work on WSL 2 (#3587) 2026-01-19 01:08:15 +08:00
AminurAlam
97d0c6bb23
fix: sort extension excludes directories since only files have extensions (#3582) 2026-01-18 01:09:47 +08:00
三咲雅 misaki masa
a38a5e6377
fix: set orphan=true for xdg-open to work around its odd behavior with programs that are both CLI/GUI (#3581) 2026-01-17 23:03:08 +08:00
三咲雅 misaki masa
756964b10a
fix: build error on NetBSD (#3506) 2026-01-17 13:11:25 +08:00
WindFade
8a909697f8
feat: disable ANSI escape sequences in ya pkg when stdout is not a TTY (#3566)
Co-authored-by: sxyazi <sxyazi@gmail.com>
2026-01-16 14:03:27 +08:00
Marcus Geiger
798d38e494
feat: re-enable the TIFF feature (#3573)
Co-authored-by: sxyazi <sxyazi@gmail.com>
2026-01-16 13:53:26 +08:00
sxyazi
0fc343b95a
fix: merge duplicate top directories in archive previews 2026-01-15 14:38:32 +08:00
三咲雅 misaki masa
0c8c973e6c
feat: check and refresh the file list when the terminal gains focus (#3561) 2026-01-13 16:53:00 +08:00
sxyazi
41e5717930
chore: update icons 2026-01-11 19:00:50 +08:00
三咲雅 misaki masa
babc80d842
feat: exclude Apple Resource Forks (__MACOSX) from archive preview (#3548) 2026-01-11 18:16:05 +08:00
itsvyle
233d721e7c
feat: tree view for the preset archive previewer (#3525)
Co-authored-by: sxyazi <sxyazi@gmail.com>
2026-01-11 13:20:43 +08:00
三咲雅 misaki masa
16d20e2171
feat: new Path.os() API creates an OS-native Path (#3541) 2026-01-09 19:51:37 +08:00
三咲雅 misaki masa
b1dd185692
fix: smart-case in interactive cd broken due to a typo (#3540) 2026-01-09 08:00:55 +08:00
三咲雅 misaki masa
ebedab3e29
fix: shell formatting for non-spread opener rules (#3532) 2026-01-08 17:04:53 +08:00
itsvyle
83674f19b4
feat: support compressed tarballs (.tar.gz, .tar.bz2, etc.) in the preset archive previewer (#3518)
Co-authored-by: sxyazi <sxyazi@gmail.com>
2026-01-08 15:35:29 +08:00
Gabriel Wicke
c6e1702c61
fix: trust directory stats outside of Linux and macOS (#3528)
Co-authored-by: sxyazi <sxyazi@gmail.com>
2026-01-08 15:34:36 +08:00
Gabriel Wicke
30ac041d9e
refactor: make partition heuristic predicate a positive match (#3526) 2026-01-08 02:25:50 +08:00
三咲雅 misaki masa
c9327ca851
fix: account for URL covariance in Url:join() (#3514) 2026-01-06 11:55:48 +08:00
sxyazi
3c39a326ab
chore: bump version to 26.1.4 (#3498) 2026-01-04 12:35:06 +08:00
三咲雅 misaki masa
e611de5284
fix: make the SFTP VFS provider's create_dir() and create_new() correctly report "file exists" errors (#3495) 2026-01-03 19:01:38 +08:00
三咲雅 misaki masa
81c66a645f
fix: a race condition generating unique filenames for concurrent file operations (#3494) 2026-01-03 16:40:49 +08:00
leiserfg
a6fcbb04df
Support 8-bit images in RGB, CIELAB, and GRAY color spaces (#3358)
Co-authored-by: sxyazi <sxyazi@gmail.com>
2026-01-02 22:57:06 +08:00
Integral
0000141700
fix: ya pkg fails to write package.toml when the config directory does not exist (#3482)
Co-authored-by: sxyazi <sxyazi@gmail.com>
2026-01-01 09:25:23 +08:00
三咲雅 misaki masa
456b88d660
feat: support VFS for preset previewers that rely on external commands (#3477) 2026-01-01 09:13:56 +08:00
三咲雅 misaki masa
f31147d246
chore: bump version to 25.12.29 (#3471) 2025-12-29 08:21:43 +08:00
三咲雅 misaki masa
faf33e1b03
fix: stricter casefold matching (#3470) 2025-12-28 16:05:06 +08:00
三咲雅 misaki masa
77d36ff5a5
feat: new fs.copy() and fs.rename() APIs (#3467) 2025-12-27 17:52:15 +08:00
Francesco Marchisotti
c5a3e42986
feat: ISO 8601 date format for spotter (#3465)
Co-authored-by: 三咲雅 misaki masa <sxyazi@gmail.com>
2025-12-27 08:00:22 +08:00
三咲雅 misaki masa
366fca6b10
perf: raise the max allowed concurrent connections per SFTP service (#3466) 2025-12-27 07:40:30 +08:00
三咲雅 misaki masa
2f66561a82
fix: Command:output() should set stderr to piped instead of stdin (#3458) 2025-12-24 16:18:11 +08:00
三咲雅 misaki masa
8c81172a19
feat: new duplicate DDS event for copying files (#3456) 2025-12-24 13:28:38 +08:00
sxyazi
6e3c96289e
feat: make task post-hooks stateful (#3454) 2025-12-24 01:15:18 +08:00
三咲雅 misaki masa
329c5d28f0
fix: a race condition when terminal resumes while signal system is restarting (#3448) 2025-12-19 18:46:38 +08:00
三咲雅 misaki masa
e7cd66370f
fix: avoid UI-dependent async APIs from being called during app initialization to avoid deadlocks (#3445) 2025-12-19 00:21:13 +08:00
三咲雅 misaki masa
e9eb8b9155
fix: unify path expansion and absolutization behavior (#3438) 2025-12-17 23:00:05 +08:00
三咲雅 misaki masa
80b3b8465d
perf: immediate task cancellation (#3429) 2025-12-14 21:02:22 +08:00
三咲雅 misaki masa
5fe58ba2b1
feat: include context in error logs when a task fails (#3427) 2025-12-13 19:43:29 +08:00
三咲雅 misaki masa
ac7718c226
feat: new experimental ya.async() API (#3422) 2025-12-11 01:10:28 +08:00
Matthieu Coudron
85a99937a1
feat: install manpages from flake (#3417) 2025-12-09 22:55:24 +08:00
三咲雅 misaki masa
55ef88b726
feat!: rounded corners for indicator bar (#3419) 2025-12-09 22:25:01 +08:00
三咲雅 misaki masa
396f634bb8
fix: add preset VFS config file to avoid TOML parsing errors (#3414) 2025-12-08 16:24:19 +08:00
三咲雅 misaki masa
9ae8b90d23
feat: prioritize symbolic links when cutting remote files (#3412) 2025-12-07 18:00:49 +08:00
三咲雅 misaki masa
d2e9e72684
refactor: pull directory traversal out into a function (#3411) 2025-12-07 01:28:45 +08:00
三咲雅 misaki masa
c569263a50
fix: always follow symlinks when pasting remote files (#3406) 2025-12-05 23:46:23 +08:00
sxyazi
205b7fe11f
refactor: rename the "provider" term in VFS config to "service" (#3403) 2025-12-05 15:04:39 +08:00
三咲雅 misaki masa
dccc9b2c2a
fix: escape control characters in filenames (#3400) 2025-12-04 16:13:39 +08:00
三咲雅 misaki masa
c6e03e91d2
feat: remote filesystem support for interactive cd (#3398) 2025-12-03 17:06:42 +08:00
三咲雅 misaki masa
c7739c5941
feat: remote file management (#3396) 2025-12-02 19:33:27 +08:00
rezky_nightky
878b74acb7
docs: little typo in CHANGELOG (#3394) 2025-12-02 18:38:09 +08:00
三咲雅 misaki masa
44e244b9d6
perf: concurrent chunked upload and download of a single file over SFTP (#3393) 2025-12-01 22:39:36 +08:00
三咲雅 misaki masa
d910104c00
fix: errors out if the target package directory doesn't exist previously (#3392) 2025-12-01 11:11:00 +08:00
三咲雅 misaki masa
d0adb9c015
feat: new ind-sort and key-sort DDS events to change sorting in Lua (#3391) 2025-12-01 10:51:27 +08:00
三咲雅 misaki masa
0fb652d2ec
feat: new null plugin as a general-purpose error handler (#3387) 2025-11-30 14:44:17 +08:00
三咲雅 misaki masa
76196aab70
refactor!: return Path instead of Url from File.link_to (#3385) 2025-11-30 12:08:59 +08:00
三咲雅 misaki masa
ade1025a74
feat: timeouts for SFTP operations (#3384) 2025-11-29 15:02:25 +08:00
三咲雅 misaki masa
81ccdd8b64
feat: include stderr along with existing error code when video preview fails (#3383) 2025-11-29 10:06:05 +08:00
sxyazi
0b8aaba29b
refactor: make Clippy happy 2025-11-29 07:39:04 +08:00
三咲雅 misaki masa
8c2a6dba90
feat: check if already in visual mode before entering visual mode (#3381) 2025-11-29 05:27:35 +08:00
三咲雅 misaki masa
a8f2498471
feat: new stash command to let users customize back and forward command behavior (#3380) 2025-11-29 05:27:17 +08:00
三咲雅 misaki masa
3561867b99
feat: WTF-8 validator (#3374) 2025-11-27 16:01:06 +08:00
三咲雅 misaki masa
329c80cbee
feat: support Unix path backend for the SFTP provider (#3371) 2025-11-26 17:27:52 +08:00
三咲雅 misaki masa
a1fb206a59
feat: ya.quote() support escaping strings containing invalid UTF-8 (#3369) 2025-11-26 08:57:35 +08:00
三咲雅 misaki masa
2d55c9427d
fix: prevent quotes in file(1) arguments from being stripped under MSYS2 (#3364) 2025-11-25 09:10:11 +08:00
XYenon
c725c91df1
fix(nix): shell env (#3363) 2025-11-24 19:55:41 +08:00
sxyazi
449450d0db
feat!: introduce Path as a subset of Url to enforce type safety (#3361) 2025-11-24 15:23:06 +08:00
sxyazi
76fe6d7244
fix: user-prepended open rules do not override presets (#3360) 2025-11-24 12:30:52 +08:00
三咲雅 misaki masa
e5ee31cb7e
feat: new Unix path backend (#3359) 2025-11-24 11:35:10 +08:00
三咲雅 misaki masa
a08b345a02
docs: document the applicability of all yazi-* crates (#3345) 2025-11-20 09:00:01 +08:00
三咲雅 misaki masa
d3ad1c0920
refactor: refine PathLike and StrandLike traits (#3340) 2025-11-19 18:07:04 +08:00
walldmtd
9cd742811a
fix: -flatten flag should come after the input file in preset ImageMagick previewer (#3339)
Co-authored-by: 三咲雅 misaki masa <sxyazi@gmail.com>
2025-11-19 13:26:35 +08:00
三咲雅 misaki masa
f9abe886cc
feat: multi-path architecture (#3334) 2025-11-18 13:56:20 +08:00
Zane Weissman
577065cbd0
feat: new overall option to set the overall background color (#3317) 2025-11-12 12:41:13 +08:00
三咲雅 misaki masa
4b56e73235
feat: dynamic paths (#3316) 2025-11-06 18:01:02 +08:00
Ruowen
cc8f9a7177
fix(nix): bump flake.lock to fix build failure due to rust-overlay (#3314)
Signed-off-by: Ruowen Qin <chinqrw@gmail.com>
2025-11-06 17:15:27 +08:00
三咲雅 misaki masa
587950bb5b
fix: u16 instead of u32 as the type of max_width and max_height options (#3313) 2025-11-05 08:53:30 +08:00
三咲雅 misaki masa
97c63e2708
fix: make file counter more distinguishable in dark mode (#3311) 2025-11-05 01:20:56 +08:00
三咲雅 misaki masa
604b86612a
refactor: simplify paths (#3297) 2025-10-31 20:26:26 +08:00
Pui Yong Qing
b766cff69f
fix: follow upstream renaming of poppler_utils to poppler-utils in Nix flake (#3296) 2025-10-31 20:11:59 +08:00
三咲雅 misaki masa
a3e9ea0d16
refactor: generalize Loc, LocBuf, Uri, and Urn (#3295) 2025-10-30 19:37:46 +08:00
三咲雅 misaki masa
564b885196
feat: ya emit and ya emit-to support invalid UTF-8 as command argument values (#3290) 2025-10-28 20:37:26 +08:00
三咲雅 misaki masa
440e67164a
feat: respect the user's image_filter setting for the preset magick previewer (#3286) 2025-10-28 00:30:09 +08:00
三咲雅 misaki masa
ff5f656a31
feat: add CHANGELOG.md (#3285) 2025-10-27 20:06:26 +08:00
三咲雅 misaki masa
c11ed02760
fix: fallback to PollWatcher instead of panicking when RecommendedWatcher initialization fails (#3281) 2025-10-27 13:11:30 +08:00
Carlos Eduardo
641bc6ff47
fix: make nix-shell use the nightly Rust toolchain (#3267) 2025-10-26 12:38:19 +08:00
三咲雅 misaki masa
157156b5b8
fix: a race condition in concurrent directory loading on a slow device (#3271) 2025-10-22 12:16:41 +08:00
sxyazi
b16c4894dc
docs: remove dead Hyper from the list of terminals supporting image preview
Fixes #3269
2025-10-22 12:12:59 +08:00
三咲雅 misaki masa
4b08e8d899
feat: new mimetype fetcher dedicated to remote files (#3268) 2025-10-21 15:14:08 +08:00
Kryštof Gärtner
4e32044450
feat: new casefy command for case conversion (#3235)
Co-authored-by: sxyazi <sxyazi@gmail.com>
2025-10-20 14:42:47 +08:00
三咲雅 misaki masa
782ecb16b2
feat!: live editing of remote files (#3264) 2025-10-20 13:48:57 +08:00
三咲雅 misaki masa
46569a1f48
fix: disable kitty keyboard protocol on Windows due to crossterm inability to handle it (#3250) 2025-10-16 21:45:49 +08:00
三咲雅 misaki masa
e067a705ac
feat: new download/upload commands to cache/sync remote or local files to the peer (#3243) 2025-10-16 01:22:55 +08:00
三咲雅 misaki masa
c68e2df8c0
feat: encode SFTP paths on Windows in a revised WTF-8 (#3238) 2025-10-13 19:50:46 +08:00
三咲雅 misaki masa
554cb52cc5
feat: shell formatting (#3232) 2025-10-06 23:44:32 +08:00
三咲雅 misaki masa
cd6881c9fe
feat: support task initialization failure status (#3227) 2025-10-03 22:35:48 +08:00
三咲雅 misaki masa
149a5feffb
fix!: open command doesn't work under empty directories (#3226) 2025-10-03 18:18:27 +08:00
三咲雅 misaki masa
d8b0425a10
fix: incorrect $0 and $@ parameters in shell command under empty directories (#3225) 2025-10-03 17:18:39 +08:00
三咲雅 misaki masa
4d39194cd8
feat!: folder mime-type support (#3222) 2025-10-01 20:03:41 +08:00
三咲雅 misaki masa
879ed49996
feat: filesystem-level error (#3217) 2025-09-28 23:08:48 +08:00
三咲雅 misaki masa
7b36b7fe14
refactor: invert the dependency relationship between yazi-fs and yazi-vfs (#3215) 2025-09-28 15:50:25 +08:00
三咲雅 misaki masa
25d5554a9a
fix: wrong architecture label in aarch64 .deb packages (#3209) 2025-09-26 19:05:48 +08:00
三咲雅 misaki masa
51635996e0
fix: only create mapped local working directories for virtual file systems (#3207) 2025-09-26 17:48:41 +08:00
三咲雅 misaki masa
d905cbf2d7
feat: dedicated mime fetcher, previewer, and spotter for VFS (#3203) 2025-09-25 21:52:27 +08:00
三咲雅 misaki masa
6b09f2b665
feat: casefold support for the SFTP provider (#3201) 2025-09-24 23:33:45 +08:00
三咲雅 misaki masa
9f39363dd4
fix: renew package rev only when it's empty (#3200) 2025-09-24 20:17:16 +08:00
三咲雅 misaki masa
f3e029ec25
feat: include $FZF_DEFAULT_COMMAND in debug output (#3199) 2025-09-24 19:53:13 +08:00
三咲雅 misaki masa
86f0885737
feat: SFTP connection pool (#3198) 2025-09-24 19:49:37 +08:00
sxyazi
9a804bac9c
fix: do not passthrough DECRQM query to tmux 2025-09-23 19:05:30 +08:00
三咲雅 misaki masa
c0c0877d42
fix: record the follow state of symlink correctly (#3195) 2025-09-23 15:12:18 +08:00
三咲雅 misaki masa
e9742cb809
fix: check compatibility when reusing previewer bytecode cache (#3190) 2025-09-21 23:33:30 +08:00
三咲雅 misaki masa
8f9cce126c
feat: new --bg specifying image background color for the preset svg and magick previewers (#3189) 2025-09-21 22:18:00 +08:00
WhoSowSee
ba99ce4f8f
feat: unify the password input visually when extracting archives (#3179) 2025-09-21 11:48:17 +08:00
三咲雅 misaki masa
4e3ac54c8e
feat: new yazi-vfs crate (#3187) 2025-09-21 11:44:52 +08:00
三咲雅 misaki masa
30c0279570
feat: introduce ChaType for better cross-filesystem consistency (#3175) 2025-09-18 07:20:19 +08:00
三咲雅 misaki masa
ece59974fe
refactor: use Cha for better cross-platform consistency (#3174) 2025-09-17 23:18:54 +08:00
三咲雅 misaki masa
6cfa92f112
feat: SFTP virtual filesystem provider (#3172) 2025-09-17 08:34:35 +08:00
三咲雅 misaki masa
7deeaa4356
feat: size calculator for remote file systems (#3170) 2025-09-15 22:41:17 +08:00
三咲雅 misaki masa
a3fc9a0ec7
fix: force Git checkout for plugin cache repositories (#3169) 2025-09-14 19:42:30 +08:00
三咲雅 misaki masa
4a3710b1c4
feat: new yazi-sftp crate (#3166) 2025-09-14 02:50:28 +08:00
Phil Clifford
f0502a46b7
fix: add fd as a dependency to the .deb packaging workflow (#3128) 2025-09-13 11:30:49 +08:00
三咲雅 misaki masa
01fd967fa3
fix: DECSCUSR over DECSET 12 (#3165) 2025-09-13 11:03:08 +08:00
三咲雅 misaki masa
3713478753
feat: multi-entry support for plugin system (#3154) 2025-09-08 23:38:10 +08:00
三咲雅 misaki masa
fa0e7e62c1
feat: cosmetic work for the "URL" and "repository" spelling (#3152) 2025-09-08 22:09:49 +08:00
sxyazi
81a0cf4a1e
ci: enable Dependabot 2025-09-06 23:40:06 +08:00
adamnemecek
021d86f0d1
refactor: use Self where possible (#3138) 2025-09-06 00:48:30 +08:00
三咲雅 misaki masa
e795d6cf42
feat: offload mimetype fetching on opening files to the task scheduler (#3141) 2025-09-06 00:18:31 +08:00
yuvals1
3c42195bac
fix: generate unique image IDs for Kgp to tolerate tmux (#3038)
Co-authored-by: sxyazi <sxyazi@gmail.com>
2025-09-04 22:53:57 +08:00
Cattyman
49910fcfe7
feat: progress of each task (#3121)
Co-authored-by: sxyazi <sxyazi@gmail.com>
2025-09-04 18:55:28 +08:00
三咲雅 misaki masa
b997e56a60
fix: Windows path length may exceed MAX_PATH (#3137) 2025-09-04 17:02:18 +08:00
三咲雅 misaki masa
cfc7238638
feat: port task manager to Lua (#3134) 2025-09-03 22:25:16 +08:00
三咲雅 misaki masa
d670da80d3
fix: make casefold work when ancestor directories are symbolic links (#3132) 2025-09-03 00:15:57 +08:00
三咲雅 misaki masa
d7df581b67
feat: tasks without progress (#3131) 2025-09-02 16:58:34 +08:00
Copilot
eb537733a2
fix: Darwin framework deprecation in Nix build (#3119)
Co-authored-by: sxyazi <17523360+sxyazi@users.noreply.github.com>
2025-08-28 23:26:01 +08:00
三咲雅 misaki masa
8a0265ad64
perf: zero-copy UrlBuf to Url conversion (#3117) 2025-08-28 23:06:04 +08:00
三咲雅 misaki masa
dd7afaa64a
refactor: Url::pair() without allocation (#3116) 2025-08-27 23:52:09 +08:00
三咲雅 misaki masa
c2a94daad6
feat: casefold (#3114) 2025-08-27 15:55:35 +08:00
三咲雅 misaki masa
12172b7301
feat: support VFS for the file watcher (#3108) 2025-08-25 21:40:38 +08:00
三咲雅 misaki masa
c27ef58116
feat: support VFS for join(), starts_with(), and ends_with() of Url (#3094) 2025-08-23 23:06:11 +08:00
三咲雅 misaki masa
0054cf0b87
perf: lock-free hashing for string interns (#3091) 2025-08-21 09:40:52 +08:00
三咲雅 misaki masa
84907297a0
perf: string interning to reduce memory usage of mimetype and URL domain (#3084) 2025-08-19 21:58:57 +08:00
三咲雅 misaki masa
7a9bfff95d
perf: switch to a higher-performance hash algorithm (#3083) 2025-08-19 15:58:39 +08:00
三咲雅 misaki masa
ae75fc0f7b
feat: make mimetype matching more robust (#3082) 2025-08-19 15:17:32 +08:00
三咲雅 misaki masa
fe9ce240b5
fix: respect URL covariance when updating mimetypes (#3079) 2025-08-18 23:20:03 +08:00
boydaihungst
a41931275c
fix: erase overlapping image portions when previewing errors (#3067)
Co-authored-by: sxyazi <sxyazi@gmail.com>
2025-08-18 19:55:28 +08:00
三咲雅 misaki masa
3ca33c1f84
refactor: introduce UrlBuf (#3072) 2025-08-18 02:25:59 +08:00
三咲雅 misaki masa
9810196565
refactor: introduce LocBuf (#3062) 2025-08-17 16:25:04 +08:00
三咲雅 misaki masa
2ec3a6c645
fix: avoid appending a newline when reading clipboard contents (#3059) 2025-08-14 11:26:46 +08:00
三咲雅 misaki masa
1bfcbc1664
feat: introduce the concept of Uri (#3049) 2025-08-13 01:38:43 +08:00
Raphael Amorim
a9bfeeeaad
docs: update Rio image support status (#3052)
Co-authored-by: 三咲雅 misaki masa <sxyazi@gmail.com>
2025-08-13 00:53:00 +08:00
三咲雅 misaki masa
a109aa92e7
refactor: refine URN for archives (#3043) 2025-08-11 01:02:53 +08:00
三咲雅 misaki masa
292d37ccde
refactor: simplify cursor repositioning (#3040) 2025-08-08 02:41:41 +08:00
三咲雅 misaki masa
41b00c8d4e
feat: allow DDS events to change command arguments (#3037) 2025-08-06 22:23:22 +08:00
三咲雅 misaki masa
0b45ed868f
feat: VFS support for the enter and leave commands (#3035) 2025-08-05 20:45:23 +08:00
三咲雅 misaki masa
d5f225bc4a
feat: virtual file system (#3034) 2025-08-05 02:23:57 +08:00
三咲雅 misaki masa
da97e5a8b4
feat: URL covariance for the mimetype agency (#3030) 2025-07-31 00:48:54 +08:00
三咲雅 misaki masa
9adc00c3a9
fix: keep original scheme when expanding URLs (#3028) 2025-07-30 02:08:17 +08:00
三咲雅 misaki masa
9c1f303f2c
feat: URL covariance for selected and yanked files (#3025) 2025-07-29 15:03:22 +08:00
三咲雅 misaki masa
9535caad5b
fix: make URLs with invalid UTF-8 serializable (#3023) 2025-07-28 17:21:47 +08:00
三咲雅 misaki masa
0e3cd8545c
feat: URL schemas (#3019) 2025-07-27 18:06:13 +08:00
三咲雅 misaki masa
c2883f1e05
refactor: pull filesystem calls out into a designated yazi_fs crate (#3013) 2025-07-23 22:43:11 +08:00
Andrew Rabert
1f596a01fc
feat: new bulk_rename command always renames files with the editor (#2984)
Co-authored-by: sxyazi <sxyazi@gmail.com>
2025-07-23 15:02:01 +08:00
三咲雅 misaki masa
bfe12c8052
fix: suspend only when there is a parent process (#3008) 2025-07-21 14:42:10 +08:00
三咲雅 misaki masa
74bd98031e
feat: key-* DDS events to allow canceling user key commands (#3005) 2025-07-20 14:11:57 +08:00
三咲雅 misaki masa
c28d2e8180
refactor: eliminate the tab conceptual hierarchy (#2999) 2025-07-18 14:31:16 +08:00
三咲雅 misaki masa
0fd74ae44c
perf!: cacheable iterator (#2997) 2025-07-18 07:48:00 +08:00
三咲雅 misaki masa
5f8a1496fe
refactor: invert the dependency relationship between yazi-parser and yazi-proxy (#2992) 2025-07-16 22:46:10 +08:00
三咲雅 misaki masa
548bb0d063
refactor: upgrade to mlua v0.11.0 (#2989) 2025-07-16 11:02:34 +08:00
三咲雅 misaki masa
60a2382087
refactor: switch to actor model (#2986) 2025-07-14 23:08:11 +08:00
三咲雅 misaki masa
6366e46c23
refactor: use term "core" instead of "context" (#2978) 2025-07-11 09:00:22 +08:00
三咲雅 misaki masa
c9c85130e1
refactor: pull DDS events out into a macro (#2977) 2025-07-11 08:55:56 +08:00
三咲雅 misaki masa
18795320bb
fix: stringify!(r#move) is "r#move" instead of "move" (#2973) 2025-07-10 10:33:43 +08:00
Euclid Ye
bc9dbd35e3
feat: new "Show media info" for play opener on Windows (#2962) 2025-07-10 00:38:59 +08:00
simonborje
559a46e399
feat: make ticket optional for cmp:trigger (#2968)
Co-authored-by: sxyazi <sxyazi@gmail.com>
2025-07-10 00:12:51 +08:00
三咲雅 misaki masa
452b435b71
feat: allow dynamic adjustment of layout ratio via rt.mgr.ratio (#2964) 2025-07-08 23:59:50 +08:00
Euclid Ye
5f7c0e813f
fix: respect user's system media opener instead of hardcoding mpv (#2959)
Co-authored-by: sxyazi <sxyazi@gmail.com>
2025-07-07 21:44:55 +08:00
三咲雅 misaki masa
917e1f54a1
perf: do not listen for file changes in inactive tabs (#2958) 2025-07-06 22:27:56 +08:00
三咲雅 misaki masa
52ecb31f5a
refactor: introduce the SStr type for static strings (#2957) 2025-07-06 22:16:29 +08:00
三咲雅 misaki masa
a426d449f4
refactor: new yazi-parser crate (#2956) 2025-07-06 19:17:00 +08:00
三咲雅 misaki masa
ec7eb3e416
fix: fallback to PollWatcher on NetBSD (#2941) 2025-07-03 21:41:49 +08:00
Sebastian Hjelm
39f50c898b
fix: expose ya CLI in the Snap build (#2904) 2025-07-03 17:10:03 +08:00
三咲雅 misaki masa
ffdd74b6ab
refactor: move several UI-related APIs from the ya namespace to ui (#2939) 2025-07-03 15:58:51 +08:00
三咲雅 misaki masa
4ff7dae721
fix: Cargo doesn't respect .cargo/config.toml for the crates (#2936) 2025-06-30 23:18:30 +08:00
三咲雅 misaki masa
5a66559d1c
feat: improve the UX of the input component (#2935) 2025-06-30 16:27:25 +08:00
三咲雅 misaki masa
8657e6b6f5
feat: display newlines in input as spaces to improve readability (#2932) 2025-06-28 23:59:29 +08:00
TKperson
a8a1b5625b
fix: preserve open order for files with post-resolved mime-types (#2931)
Co-authored-by: TKperson <TKperson@users.noreply.github.com>
Co-authored-by: sxyazi <sxyazi@gmail.com>
2025-06-28 18:07:59 +08:00
三咲雅 misaki masa
8ef28ec678
refactor: deprecate ui.Rect.default (#2927) 2025-06-27 23:03:16 +08:00
三咲雅 misaki masa
e16c0da547
feat: allow custom mouse click behavior for individual files (#2925) 2025-06-27 20:50:19 +08:00
Yuriy Poltorak
2768fd22b1
feat: support .deb packages (#2807)
Co-authored-by: sxyazi <sxyazi@gmail.com>
2025-06-27 17:43:59 +08:00
三咲雅 misaki masa
2d1cf621db
fix: incomplete cache calculation algorithm (#2922) 2025-06-26 20:12:54 +08:00
三咲雅 misaki masa
9d05946f4b
feat!: make ya.confirm() and ui.Pos() public API (#2921) 2025-06-26 18:12:09 +08:00
三咲雅 misaki masa
590d336716
feat: fill in error messages if preview fails (#2917) 2025-06-24 16:47:39 +08:00
三咲雅 misaki masa
e218f1d8bb
feat: filter by full path (prefix + filename) in search view instead of just filename (#2915) 2025-06-24 13:29:34 +08:00
三咲雅 misaki masa
1ed244a0a5
perf: store only Urn instead of full Url in find results (#2914) 2025-06-24 13:13:51 +08:00
三咲雅 misaki masa
ab0fe93150
feat: improve the UX of the pick component (#2906) 2025-06-22 21:23:46 +08:00
三咲雅 misaki masa
3a9591a3d9
feat: improve error handling in config file reading (#2901) 2025-06-21 22:23:59 +08:00
三咲雅 misaki masa
f5d0f47b51
fix: Cargo doesn't respect the workspace profile settings (#2899) 2025-06-21 14:58:58 +08:00
三咲雅 misaki masa
1a6abae974
feat: invalid UTF-8 support to Command:arg() and Command:env() APIs (#2895) 2025-06-18 22:01:36 +08:00
三咲雅 misaki masa
750ca0c0d4
fix: throw catchable errors instead of crashing when publishing a message containing invalid UTF-8 over DDS (#2893) 2025-06-18 19:30:31 +08:00
三咲雅 misaki masa
a0ce7c0ae7
refactor: reuse ordered-float as it's already in the dependency tree (#2891) 2025-06-18 13:29:39 +08:00
三咲雅 misaki masa
002b742cd9
feat: support tables with invalid UTF-8 keys in sendable data (#2890) 2025-06-18 10:46:31 +08:00
三咲雅 misaki masa
ec90f26ac3
feat: allow Url to accept invalid UTF-8 characters (#2889) 2025-06-17 18:24:43 +08:00
Mika Vilpas
c8148d2d75
fix: out-of-bounds error with empty backstack (#2887) 2025-06-17 08:29:46 +08:00
三咲雅 misaki masa
a0ab614108
fix: proper invalid UTF-8 path support (#2884) 2025-06-16 21:25:39 +08:00
三咲雅 misaki masa
56a4e87d7e
fix: interoperability between number, integer, and id (#2883) 2025-06-16 13:18:07 +08:00
三咲雅 misaki masa
a076ff8c82
refactor: satisfy mismatched-lifetime-syntaxes lint (#2881) 2025-06-16 11:52:35 +08:00
三咲雅 misaki masa
52d37fa25d
perf: app data instead of Lua registry to reduce stack pushes (#2880) 2025-06-16 11:25:02 +08:00
三咲雅 misaki masa
51560c30b7
perf: do not pre-allocate memory for Lua tables (#2879) 2025-06-16 11:19:18 +08:00
三咲雅 misaki masa
917fee939f
perf: make preload tasks discardable (#2875) 2025-06-14 16:13:48 +08:00
三咲雅 misaki masa
aae3f9ea4a
refactor: promote Id to the task scheduler (#2873) 2025-06-14 12:07:22 +08:00
三咲雅 misaki masa
0eec8fc80e
feat: zoom in or out of the preview image (#2864) 2025-06-11 15:24:11 +08:00
三咲雅 misaki masa
414d46fd16
perf: copy-on-write on command data & avoid converting primitive types to strings thereby allocating memory (#2862) 2025-06-11 10:13:51 +08:00
三咲雅 misaki masa
0752b53298
perf: sequence-based rendering merge strategy (#2861) 2025-06-11 08:49:05 +08:00
三咲雅 misaki masa
693dff25e3
feat: search view shares file selection and yank state (#2855) 2025-06-09 16:55:50 +08:00
Axlefublr
c0615e5f45
feat: port several widespread GUI keys to the input component (#2849)
Co-authored-by: sxyazi <sxyazi@gmail.com>
2025-06-09 08:22:13 +08:00
三咲雅 misaki masa
b637c32ef7
feat: increase terminal response timeout to better tolerate slow SSH network environments (#2843) 2025-06-05 23:03:46 +08:00
三咲雅 misaki masa
d55e002d5a
fix: cd on the initial creation of a tab should not be added to the backstack (#2842) 2025-06-05 20:11:37 +08:00
三咲雅 misaki masa
145349b0e6
feat: allow upgrading only specific packages (#2841) 2025-06-05 19:46:49 +08:00
Ruowen
20446544ef
fix(nix): missing ffmpeg and resvg dependencies (#2825)
Signed-off-by: Ruowen Qin <chinqrw@gmail.com>
2025-06-04 00:08:18 +08:00
三咲雅 misaki masa
f866d75ef0
fix: incomplete hidden state check on Windows for Cha (#2835) 2025-06-03 23:54:50 +08:00
三咲雅 misaki masa
2d95221f59
perf: use AnyUserData::type_id() to reduce stack pushes (#2834) 2025-06-03 23:40:23 +08:00
三咲雅 misaki masa
d5038eeed5
perf: reduce file change event frequency (#2820) 2025-06-01 09:20:17 +08:00
三咲雅 misaki masa
b65a88075a
chore: bump version to 25.5.31 (#2815) 2025-05-31 00:39:47 +08:00
三咲雅 misaki masa
4fdc0f120f
fix: make every effort to carry hidden states for dummy files (#2814) 2025-05-31 00:08:50 +08:00
三咲雅 misaki masa
4076e35a2f
fix: forward --end-of-word of the input should consider the mode's delta (#2811) 2025-05-30 18:50:00 +08:00
三咲雅 misaki masa
e6471b7fe0
fix: expose ui.Wrap (#2810) 2025-05-30 18:14:57 +08:00
1537 changed files with 65856 additions and 24482 deletions

View file

@ -1,5 +1,5 @@
[env]
MACOSX_DEPLOYMENT_TARGET = "10.11"
MACOSX_DEPLOYMENT_TARGET = "10.12"
JEMALLOC_SYS_WITH_LG_PAGE = "16"
# environment variable for tikv-jemalloc-sys

View file

@ -22,10 +22,10 @@ body:
validations:
required: true
- type: textarea
id: debug
id: env
attributes:
label: "`yazi --debug` output"
description: Please run `yazi --debug` and paste the debug information here.
label: "`ya env` output"
description: Run `ya env` and paste the environment information here.
render: Shell
validations:
required: true

View file

@ -26,10 +26,10 @@ body:
validations:
required: true
- type: textarea
id: debug
id: env
attributes:
label: "`yazi --debug` output"
description: Please run `yazi --debug` and paste the debug information here.
label: "`ya env` output"
description: Run `ya env` and paste the environment information here.
render: Shell
validations:
required: true
@ -65,6 +65,6 @@ body:
options:
- label: I tried the [latest nightly build](https://yazi-rs.github.io/docs/installation#binaries), and the issue is still reproducible
required: true
- label: I updated the debug information (`yazi --debug`) input box to the nightly that I tried
- label: I updated the environment information (`ya env`) field to the nightly that I tried
required: true
- label: I can reproduce it after disabling all custom configs/plugins (`mv ~/.config/yazi ~/.config/yazi-backup`)

View file

@ -1,5 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: 🚧 Build Issues
url: https://github.com/sxyazi/yazi/discussions/new?category=3-build-issues
about: If you have issues building Yazi from source code
- name: 📝 Documentation Improvement
url: https://github.com/yazi-rs/yazi-rs.github.io
about: If you'd like to help improve the documentation

View file

@ -4,10 +4,10 @@ labels: [feature]
assignees: []
body:
- type: textarea
id: debug
id: env
attributes:
label: "`yazi --debug` output"
description: Please run `yazi --debug` and paste the debug information here.
label: "`ya env` output"
description: Run `ya env` and paste the environment information here.
render: Shell
validations:
required: true

15
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,15 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
commit-message:
prefix: "ci"
- package-ecosystem: "npm"
directory: "/scripts/validate-form"
schedule:
interval: "weekly"
commit-message:
prefix: "ci"

View file

@ -15,3 +15,10 @@ A clear and concise description of the rationale of the changes, to help our rev
If it has already been detailed in the associated issue, please skip this section.
-->
## Checklist
- [ ] I have read [CONTRIBUTING.md](https://github.com/sxyazi/yazi/blob/main/CONTRIBUTING.md)
- [ ] I confirm this PR follows the [AI Policy](https://github.com/sxyazi/yazi/blob/main/CONTRIBUTING.md#ai-policy)
<!-- AI bots are not allowed to open PRs in this repository. All PRs must be made by humans and comply with the AI policy. -->

View file

@ -4,6 +4,9 @@ on:
push:
branches: [main]
permissions:
contents: read
jobs:
publish:
name: Publish Flake
@ -12,13 +15,13 @@ jobs:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: Install Nix
uses: cachix/install-nix-action@v31
- name: Authenticate with Cachix
uses: cachix/cachix-action@v16
uses: cachix/cachix-action@v17
with:
name: yazi
authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"

View file

@ -10,11 +10,14 @@ env:
SCCACHE_GHA_ENABLED: true
RUSTC_WRAPPER: sccache
permissions:
contents: read
jobs:
clippy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: Setup Rust toolchain
run: |
@ -22,7 +25,7 @@ jobs:
rustup component add clippy
- name: Setup sccache
uses: mozilla-actions/sccache-action@v0.0.9
uses: mozilla-actions/sccache-action@v0.0.10
- name: Clippy
run: cargo clippy --all
@ -31,7 +34,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v7
- name: Setup Rust toolchain
run: |
@ -39,16 +42,16 @@ jobs:
rustup component add rustfmt --toolchain nightly
- name: Setup sccache
uses: mozilla-actions/sccache-action@v0.0.9
uses: mozilla-actions/sccache-action@v0.0.10
- name: Rustfmt
run: rustfmt +nightly --check **/*.rs
run: find . -name '*.rs' -not -path './target/*' -exec rustfmt +nightly --check {} +
stylua:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: JohnnyMorganz/stylua-action@v4
- uses: actions/checkout@v7
- uses: JohnnyMorganz/stylua-action@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
version: latest

View file

@ -9,6 +9,9 @@ on:
env:
SCCACHE_GHA_ENABLED: true
permissions:
contents: read
jobs:
build-unix:
strategy:
@ -39,26 +42,29 @@ jobs:
CARGO_TARGET_RISCV64GC_UNKNOWN_LINUX_GNU_LINKER: riscv64-linux-gnu-gcc
CARGO_TARGET_SPARC64_UNKNOWN_LINUX_GNU_LINKER: sparc64-linux-gnu-gcc
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: Install gcc
if: matrix.gcc != ''
run: sudo apt update && sudo apt install -yq ${{ matrix.gcc }}
- name: Setup Rust toolchain
run: rustup toolchain install stable --profile minimal --target ${{ matrix.target }}
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Setup sccache
uses: mozilla-actions/sccache-action@v0.0.9
uses: mozilla-actions/sccache-action@v0.0.10
- name: Build
run: ./scripts/build.sh ${{ matrix.target }}
- name: Upload artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: yazi-${{ matrix.target }}.zip
path: yazi-${{ matrix.target }}.zip
name: ${{ matrix.target }}
path: |
yazi-${{ matrix.target }}.zip
yazi-${{ matrix.target }}.deb
build-windows:
strategy:
@ -75,13 +81,14 @@ jobs:
CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER: lld-link.exe
CARGO_TARGET_AARCH64_PC_WINDOWS_MSVC_LINKER: lld-link.exe
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: Setup Rust toolchain
run: rustup toolchain install stable --profile minimal --target ${{ matrix.target }}
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Setup sccache
uses: mozilla-actions/sccache-action@v0.0.9
uses: mozilla-actions/sccache-action@v0.0.10
- name: Build
run: cargo build --profile release-windows --locked --target ${{ matrix.target }}
@ -90,17 +97,18 @@ jobs:
env:
TARGET_NAME: yazi-${{ matrix.target }}
run: |
New-Item -ItemType Directory -Path ${env:TARGET_NAME}
New-Item -ItemType Directory -Path "${env:TARGET_NAME}\completions"
Copy-Item -Path "target\${{ matrix.target }}\release-windows\ya.exe" -Destination ${env:TARGET_NAME}
Copy-Item -Path "target\${{ matrix.target }}\release-windows\yazi.exe" -Destination ${env:TARGET_NAME}
Copy-Item -Path "yazi-boot\completions" -Destination ${env:TARGET_NAME} -Recurse
Copy-Item -Path "yazi-cli\completions\*" -Destination "${env:TARGET_NAME}\completions"
Copy-Item -Path "yazi-boot\completions\*" -Destination "${env:TARGET_NAME}\completions"
Copy-Item -Path "README.md", "LICENSE" -Destination ${env:TARGET_NAME}
Compress-Archive -Path ${env:TARGET_NAME} -DestinationPath "${env:TARGET_NAME}.zip"
- name: Upload artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: yazi-${{ matrix.target }}.zip
name: ${{ matrix.target }}
path: yazi-${{ matrix.target }}.zip
build-musl:
@ -109,28 +117,29 @@ jobs:
matrix:
include:
- target: x86_64-unknown-linux-musl
image: rust-musl-cross:x86_64-musl
- target: aarch64-unknown-linux-musl
image: rust-musl-cross:aarch64-musl
container:
image: docker://ghcr.io/rust-cross/${{ matrix.image }}
image: docker://ghcr.io/cross-rs/${{ matrix.target }}:edge
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: Add musl target
run: rustup target add ${{ matrix.target }}
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Setup sccache
uses: mozilla-actions/sccache-action@v0.0.9
uses: mozilla-actions/sccache-action@v0.0.10
- name: Build
run: ./scripts/build.sh ${{ matrix.target }}
- name: Upload artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: yazi-${{ matrix.target }}.zip
path: yazi-${{ matrix.target }}.zip
name: ${{ matrix.target }}
path: |
yazi-${{ matrix.target }}.zip
yazi-${{ matrix.target }}.deb
build-snap:
strategy:
@ -142,12 +151,12 @@ jobs:
arch: arm64
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Setup LXD
uses: canonical/setup-lxd@v0.1.2
uses: canonical/setup-lxd@v1
- name: Setup snapcraft
run: sudo snap install --classic snapcraft
@ -159,18 +168,18 @@ jobs:
run: mv yazi_*.snap yazi-${{ matrix.arch }}.snap
- name: Upload artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: yazi-${{ matrix.arch }}.snap
name: snap-${{ matrix.arch }}
path: yazi-${{ matrix.arch }}.snap
snap:
runs-on: ubuntu-latest
needs: [build-snap]
steps:
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v8
with:
pattern: yazi-*.snap
pattern: snap-*
merge-multiple: true
- name: Setup snapcraft
@ -181,14 +190,14 @@ jobs:
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_TOKEN }}
run: |
parallel 'snapcraft push -v --release latest/candidate {}' ::: yazi-*.snap
parallel 'snapcraft upload -v --release latest/candidate {}' ::: yazi-*.snap || true
- name: Push snap to edge channel
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_TOKEN }}
run: |
parallel 'snapcraft push -v --release latest/edge {}' ::: yazi-*.snap
parallel 'snapcraft upload -v --release latest/edge {}' ::: yazi-*.snap || true
draft:
if: startsWith(github.ref, 'refs/tags/')
@ -197,16 +206,17 @@ jobs:
runs-on: ubuntu-latest
needs: [build-unix, build-windows, build-musl, build-snap]
steps:
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v8
with:
merge-multiple: true
- name: Draft
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v3
with:
draft: true
files: |
yazi-*.zip
yazi-*.deb
yazi-*.snap
generate_release_notes: true
@ -221,11 +231,12 @@ jobs:
echo 'NIGHTLY_BODY<<EOF' >> $GITHUB_ENV
echo "From commit: ${GITHUB_SHA:0:8}" >> $GITHUB_ENV
echo "Generated on: $(date -u +"%Y-%m-%d %H:%M") UTC" >> $GITHUB_ENV
echo "Nightly changelog: https://github.com/sxyazi/yazi/blob/main/CHANGELOG.md#unreleased" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v8
with:
merge-multiple: true
@ -236,12 +247,13 @@ jobs:
git tag --force nightly && git push --force origin tag nightly
- name: Nightly
uses: softprops/action-gh-release@v2
uses: softprops/action-gh-release@v3
with:
tag_name: nightly
prerelease: true
files: |
yazi-*.zip
yazi-*.deb
yazi-*.snap
name: Nightly Build
body: ${{ env.NIGHTLY_BODY }}

View file

@ -16,7 +16,7 @@ jobs:
pull-requests: write
discussions: write
steps:
- uses: dessant/lock-threads@v5
- uses: dessant/lock-threads@v6
with:
issue-inactive-days: "30"
issue-comment: >

View file

@ -4,11 +4,14 @@ on:
release:
types: [published]
permissions:
contents: read
jobs:
winget:
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v8
with:
merge-multiple: true
@ -19,6 +22,13 @@ jobs:
installers-regex: 'yazi-(x86_64|aarch64)-pc-windows-msvc\.zip$'
token: ${{ secrets.WINGET_TOKEN }}
snapcraft:
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v8
with:
merge-multiple: true
- name: Promote snap to stable
env:
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_TOKEN }}

View file

@ -11,6 +11,9 @@ env:
RUSTC_WRAPPER: sccache
CARGO_TERM_COLOR: always
permissions:
contents: read
jobs:
test:
strategy:
@ -18,13 +21,13 @@ jobs:
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: Setup Rust toolchain
run: rustup toolchain install stable --profile minimal
- name: Setup sccache
uses: mozilla-actions/sccache-action@v0.0.9
uses: mozilla-actions/sccache-action@v0.0.10
- name: Build
run: cargo build --verbose

View file

@ -1,4 +1,4 @@
name: Validate Form
name: Validate Issue
on:
issues:
@ -12,20 +12,20 @@ jobs:
permissions:
issues: write
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v7
- name: Setup Node.js
uses: actions/setup-node@v4
uses: actions/setup-node@v7
with:
node-js: 20
node-version: 20
- name: Install Dependencies
run: |
cd scripts/validate-form
npm ci
- name: Validate Form
uses: actions/github-script@v7
- name: Validate Issue
uses: actions/github-script@v9
with:
script: |
const script = require('./scripts/validate-form/main.js')

36
.github/workflows/validate-pr.yml vendored Normal file
View file

@ -0,0 +1,36 @@
name: Validate PR
on:
pull_request:
types: [opened, edited, reopened, synchronize]
permissions:
contents: read
pull-requests: read
jobs:
check-list:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
ref: ${{ github.event.repository.default_branch }}
- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version: 20
- name: Install Dependencies
run: |
cd scripts/validate-form
npm ci
- name: Validate PR
uses: actions/github-script@v9
with:
script: |
const script = require('./scripts/validate-form/main.js')
await script({github, context, core})
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

9
.luarc.json Normal file
View file

@ -0,0 +1,9 @@
{
"$schema": "https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json",
"runtime.version": "Lua 5.5",
"runtime.special": {
"fail": "error"
},
"workspace.library": ["~/.config/yazi/plugins/types.yazi/"],
"diagnostics.disable": ["redefined-local"]
}

View file

@ -1,2 +0,0 @@
# this file caused some issues with the build system
yazi-plugin/preset/plugins/mime.lua

1785
CHANGELOG.md Normal file

File diff suppressed because it is too large Load diff

View file

@ -11,6 +11,7 @@ This guide will help you understand how to contribute to the project.
3. [Development Setup](#development-setup)
4. [How to Contribute](#how-to-contribute)
5. [Pull Requests](#pull-requests)
6. [AI Policy](#ai-policy)
## Getting Started
@ -164,3 +165,10 @@ We want you to succeed, and it can be discouraging to find that a lot of re-work
- Your code passes all tests and lints.
- Your pull request description clearly explains the changes and why they are needed.
4. Address any review comments. Make sure to push updates to the same branch on your fork.
## AI Policy
1. All issue, PR, and discussion descriptions must be written by humans, not AI.
2. Any use of AI must be disclosed. You must declare which model you used and the extent of AI assistance.
3. Any AI-generated code must be reviewed, tested, and simplified by a human before publishing. This requires you to fully understand how it interacts with the greater system without AI assistance.
4. Any AI tools used must explicitly state they do not assert copyright over the work.

4134
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,20 @@
[workspace]
resolver = "2"
resolver = "3"
members = [ "yazi-*" ]
default-members = [ "yazi-fm", "yazi-cli" ]
[workspace.package]
edition = "2024"
version = "26.5.6"
license = "MIT"
authors = [ "sxyazi <sxyazi@gmail.com>" ]
homepage = "https://yazi-rs.github.io"
repository = "https://github.com/sxyazi/yazi"
rust-version = "1.95.0"
[profile.dev]
debug = "line-tables-only"
[profile.release]
codegen-units = 1
lto = true
@ -19,36 +31,67 @@ codegen-units = 256
incremental = true
lto = false
[profile.dev.package."*"]
debug = false
[workspace.dependencies]
ansi-to-tui = "7.0.0"
anyhow = "1.0.98"
ansi-to-tui = "8.0.1"
anyhow = "1.0.104"
arc-swap = { version = "1.9.2", features = [ "serde" ] }
base64 = "0.22.1"
bitflags = "2.9.1"
clap = { version = "4.5.39", features = [ "derive" ] }
bitflags = { version = "2.13.1", features = [ "serde" ] }
chrono = "0.4.45"
clap = { version = "4.6.2", features = [ "derive" ] }
compact_str = { version = "0.10.0", features = [ "serde" ] }
core-foundation-sys = "0.8.7"
crossterm = { version = "0.29.0", features = [ "event-stream" ] }
data-encoding = "2.11.0"
dirs = "6.0.0"
foldhash = "0.1.5"
futures = "0.3.31"
globset = "0.4.16"
indexmap = { version = "2.9.0", features = [ "serde" ] }
libc = "0.2.172"
lru = "0.14.0"
mlua = { version = "0.10.5", features = [ "anyhow", "async", "error-send", "lua54", "macros", "serialize" ] }
objc = "0.2.7"
parking_lot = "0.12.3"
dyn-clone = "1.0.20"
either = { version = "1.16.0" }
foldhash = "0.2.0"
futures = "0.3.33"
globset = "0.4.19"
hashbrown = { version = "0.17.1", features = [ "serde" ] }
image = { version = "0.25.10", default-features = false, features = [ "avif", "bmp", "dds", "exr", "ff", "gif", "hdr", "ico", "jpeg", "png", "pnm", "qoi", "tga", "tiff", "webp" ] }
indexmap = { version = "2.14.0", features = [ "serde" ] }
inventory = "0.3.24"
libc = "0.2.186"
lru = "0.18.1"
mlua = { version = "0.12.0", features = [ "anyhow", "async", "error-send", "lua55", "macros", "serde" ] }
objc2 = "0.6.4"
ordered-float = { version = "5.3.0", features = [ "serde" ] }
parking_lot = "0.12.5"
paste = "1.0.15"
ratatui = { version = "0.29.0", features = [ "unstable-rendered-line-info" ] }
regex = "1.11.1"
percent-encoding = "2.3.2"
rand = { version = "0.10.2", default-features = false, features = [ "std", "sys_rng" ] }
ratatui-core = { version = "0.1.2", default-features = false, features = [ "std", "layout-cache", "serde", "underline-color" ] }
ratatui-widgets = { version = "0.3.2", default-features = false, features = [ "std", "unstable-rendered-line-info" ] }
regex = "1.13.1"
russh = { version = "0.62.2", default-features = false, features = [ "ring", "rsa" ] }
scopeguard = "1.2.0"
serde = { version = "1.0.219", features = [ "derive" ] }
serde_json = "1.0.140"
syntect = { version = "5.2.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] }
tokio = { version = "1.45.1", features = [ "full" ] }
tokio-stream = "0.1.17"
tokio-util = "0.7.15"
toml = { version = "0.8.22" }
tracing = { version = "0.1.41", features = [ "max_level_debug", "release_max_level_debug" ] }
twox-hash = { version = "2.1.0", default-features = false, features = [ "std", "random", "xxhash3_128" ] }
unicode-width = { version = "0.2.0", default-features = false }
uzers = "0.12.1"
serde = { version = "1.0.229", features = [ "derive" ] }
serde_json = "1.0.151"
serde_with = "3.21.0"
strum = { version = "0.28.0", features = [ "derive" ] }
syntect = { version = "5.3.0", default-features = false, features = [ "parsing", "plist-load", "regex-onig" ] }
thiserror = "2.0.19"
tokio = { version = "1.53.0", features = [ "full" ] }
tokio-stream = "0.1.18"
tokio-util = "0.7.18"
toml = { version = "1.1.3" }
tracing = { version = "0.1.44", features = [ "max_level_debug", "release_max_level_debug" ] }
twox-hash = { version = "2.1.3", default-features = false, features = [ "std", "random", "xxhash3_128" ] }
typed-path = "0.12.3"
unicode-width = { version = "0.2.2", default-features = false }
uzers = "0.12.2"
[workspace.lints.clippy]
format_push_string = "warn"
if_same_then_else = "allow"
implicit_clone = "warn"
len_without_is_empty = "allow"
missing_safety_doc = "allow"
module_inception = "allow"
option_map_unit_fn = "allow"
unit_arg = "allow"
use_self = "warn"

View file

@ -1,16 +1,10 @@
<div align="center">
<sup>Special thanks to:</sup>
<br><br>
<a href="https://www.warp.dev/?utm_source=github&utm_medium=referral&utm_campaign=yazi" target="_blank">
<img alt="Warp sponsorship" width="300" src="https://github.com/user-attachments/assets/c7f141e7-9751-407d-bb0e-d6f2c487b34f">
<br>
<b>Warp, the intelligent terminal</b>
<br>
<sup>Yazi's AI-powered terminal of choice!<br>Available for macOS, Linux and Windows</sup>
</a>
</div>
<sup>Special thanks to:</sup><br>
<br>
| <a href="https://go.warp.dev/yazi" target="_blank"><img alt="Warp sponsorship" width=350 src="https://github.com/warpdotdev/brand-assets/blob/main/Github/Sponsor/Warp-Github-LG-02.png"><br><b>Warp, built for coding with multiple AI agents</b><br><sup>Available for macOS, Linux and Windows</sup></a> |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
</div>
## Yazi - ⚡️ Blazing Fast Terminal File Manager
@ -23,13 +17,14 @@ Yazi (means "duck") is a terminal file manager written in Rust, based on non-blo
- 🖼️ **Built-in Support for Multiple Image Protocols**: Also integrated with Überzug++ and Chafa, covering almost all terminals.
- 🌟 **Built-in Code Highlighting and Image Decoding**: Combined with the pre-loading mechanism, greatly accelerates image and normal file loading.
- 🔌 **Concurrent Plugin System**: UI plugins (rewriting most of the UI), functional plugins, custom previewer/preloader/spotter/fetcher; Just some pieces of Lua.
- ☁️ **Virtual Filesystem**: Remote file management, custom search engines.
- 📡 **Data Distribution Service**: Built on a client-server architecture (no additional server process required), integrated with a Lua-based publish-subscribe model, achieving cross-instance communication and state persistence.
- 📦 **Package Manager**: Install plugins and themes with one command, keeping them up-to-date, or pin them to a specific version.
- 🧰 Integration with ripgrep, fd, fzf, zoxide
- 💫 Vim-like input/pick/confirm/which/notify component, auto-completion for cd paths
- 🏷️ Multi-Tab Support, Cross-directory selection, Scrollable Preview (for videos, PDFs, archives, code, directories, etc.)
- 🔄 Bulk Renaming, Archive Extraction, Visual Mode, File Chooser, [Git Integration](https://github.com/yazi-rs/plugins/tree/main/git.yazi), [Mount Manager](https://github.com/yazi-rs/plugins/tree/main/mount.yazi)
- 🎨 Theme System, Mouse Support, Trash Bin, Custom Layouts, CSI u, OSC 52
- 🔄 Bulk Rename/Create, Archive Extraction, Visual Mode, File Chooser, [Git Integration](https://github.com/yazi-rs/plugins/tree/main/git.yazi), [Mount Manager](https://github.com/yazi-rs/plugins/tree/main/mount.yazi)
- 🎨 Theme System, Mouse Support, Drag and Drop, Trash Bin, Custom Layouts, CSI u, OSC 52
- ... and more!
https://github.com/sxyazi/yazi/assets/17523360/92ff23fa-0cd5-4f04-b387-894c12265cc7
@ -52,25 +47,24 @@ Yazi is currently in heavy development, expect breaking changes.
## Image Preview
| Platform | Protocol | Support |
| ---------------------------------------------------------------------------- | -------------------------------------- | ----------------------------------------------------- |
| [kitty](https://github.com/kovidgoyal/kitty) (>= 0.28.0) | [Kitty unicode placeholders][kgp] | ✅ Built-in |
| [iTerm2](https://iterm2.com) | [Inline images protocol][iip] | ✅ Built-in |
| [WezTerm](https://github.com/wez/wezterm) | [Inline images protocol][iip] | ✅ Built-in |
| [Konsole](https://invent.kde.org/utilities/konsole) | [Kitty old protocol][kgp-old] | ✅ Built-in |
| [foot](https://codeberg.org/dnkl/foot) | [Sixel graphics format][sixel] | ✅ Built-in |
| [Ghostty](https://github.com/ghostty-org/ghostty) | [Kitty unicode placeholders][kgp] | ✅ Built-in |
| [Windows Terminal](https://github.com/microsoft/terminal) (>= v1.22.10352.0) | [Sixel graphics format][sixel] | ✅ Built-in |
| [st with Sixel patch](https://github.com/bakkeby/st-flexipatch) | [Sixel graphics format][sixel] | ✅ Built-in |
| [Warp](https://www.warp.dev) | [Inline images protocol][iip] | ✅ Built-in |
| [Tabby](https://github.com/Eugeny/tabby) | [Inline images protocol][iip] | ✅ Built-in |
| [VSCode](https://github.com/microsoft/vscode) | [Inline images protocol][iip] | ✅ Built-in |
| [Rio](https://github.com/raphamorim/rio) | [Inline images protocol][iip] | ❌ Rio doesn't correctly clear images [#709][rio-bug] |
| [Black Box](https://gitlab.gnome.org/raggesilver/blackbox) | [Sixel graphics format][sixel] | ✅ Built-in |
| [Hyper](https://github.com/vercel/hyper) | [Inline images protocol][iip] | ✅ Built-in |
| [Bobcat](https://github.com/ismail-yilmaz/Bobcat) | [Inline images protocol][iip] | ✅ Built-in |
| X11 / Wayland | Window system protocol | ☑️ [Überzug++][ueberzug] required |
| Fallback | [ASCII art (Unicode block)][ascii-art] | ☑️ [Chafa][chafa] required |
| Platform | Protocol | Support |
| ---------------------------------------------------------------------------- | -------------------------------------- | -------------------------------------- |
| [kitty](https://github.com/kovidgoyal/kitty) (>= 0.28.0) | [Kitty unicode placeholders][kgp] | ✅ Built-in |
| [iTerm2](https://iterm2.com) | [Inline images protocol][iip] | ✅ Built-in |
| [WezTerm](https://github.com/wez/wezterm) | [Inline images protocol][iip] | ✅ Built-in |
| [Konsole](https://invent.kde.org/utilities/konsole) | [Kitty old protocol][kgp-old] | ✅ Built-in |
| [foot](https://codeberg.org/dnkl/foot) | [Sixel graphics format][sixel] | ✅ Built-in |
| [Ghostty](https://github.com/ghostty-org/ghostty) | [Kitty unicode placeholders][kgp] | ✅ Built-in |
| [Windows Terminal](https://github.com/microsoft/terminal) (>= v1.22.10352.0) | [Sixel graphics format][sixel] | ✅ Built-in |
| [st with Sixel patch](https://github.com/bakkeby/st-flexipatch) | [Sixel graphics format][sixel] | ✅ Built-in |
| [Warp](https://www.warp.dev) (macOS/Linux only) | [Inline images protocol][iip] | ✅ Built-in |
| [Tabby](https://github.com/Eugeny/tabby) | [Inline images protocol][iip] | ✅ Built-in |
| [VSCode](https://github.com/microsoft/vscode) | [Inline images protocol][iip] | ✅ Built-in |
| [Rio](https://github.com/raphamorim/rio) (>= 0.3.9) | [Kitty unicode placeholders][kgp] | ✅ Built-in |
| [Black Box](https://gitlab.gnome.org/raggesilver/blackbox) | [Sixel graphics format][sixel] | ✅ Built-in |
| [Bobcat](https://github.com/ismail-yilmaz/Bobcat) | [Inline images protocol][iip] | ✅ Built-in |
| X11 / Wayland | Window system protocol | ☑️ [Überzug++][ueberzug] required |
| Fallback | [ASCII art (Unicode block)][ascii-art] | ☑️ [Chafa][chafa] required (>= 1.16.0) |
See https://yazi-rs.github.io/docs/image-preview for details.
@ -87,9 +81,13 @@ See https://yazi-rs.github.io/docs/image-preview for details.
[ueberzug]: https://github.com/jstkdng/ueberzugpp
[chafa]: https://hpjansson.org/chafa/
<!-- Rio bug -->
## Special Thanks
[rio-bug]: https://github.com/raphamorim/rio/issues/709
<img alt="RustRover logo" align="right" width="200" src="https://resources.jetbrains.com/storage/products/company/brand/logos/RustRover.svg">
Thanks to RustRover team for providing open-source licenses to support the maintenance of Yazi.
Active code contributors can contact @sxyazi to get a license (if any are still available).
## License

View file

@ -1,11 +1,11 @@
[Desktop Entry]
Name=Yazi
Name=Yazi File Manager
Icon=yazi
Comment=Blazing fast terminal file manager written in Rust, based on async I/O
Terminal=true
TryExec=yazi
Exec=yazi %u
Exec=yazi %f
Type=Application
MimeType=inode/directory
Categories=Utility;Core;System;FileTools;FileManager;ConsoleOnly
Categories=System;FileManager;FileTools;ConsoleOnly
Keywords=File;Manager;Explorer;Browser;Launcher

View file

@ -1 +1 @@
{"version":"0.2","language":"en","flagWords":[],"words":["Punct","KEYMAP","splitn","crossterm","YAZI","peekable","ratatui","syntect","pbpaste","pbcopy","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","Konsole","Überzug","pkgs","pdftoppm","poppler","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek","cwds","tcsi","Hyprland","Wayfire","SWAYSOCK","btime","nsec","codegen","gethostname","fchmod","fdfind","Rustc","rustc","ffprobe","vframes","luma","obase","outln","errln","tmtheme","twox","cfgs","fstype","objc","rdev","runloop","exfat","rclone","DECRQSS","DECSCUSR","libvterm","Uninit","lockin","rposition","resvg"]}
{"flagWords":[],"version":"0.2","language":"en","words":["Punct","KEYMAP","splitn","crossterm","YAZI","peekable","ratatui","syntect","pbpaste","pbcopy","oneshot","Posix","Lsar","XADDOS","zoxide","cands","Deque","precache","imageops","IFBLK","IFCHR","IFDIR","IFIFO","IFLNK","IFMT","IFSOCK","IRGRP","IROTH","IRUSR","ISGID","ISUID","ISVTX","IWGRP","IWOTH","IWUSR","IXGRP","IXOTH","IXUSR","libc","winsize","TIOCGWINSZ","xpixel","ypixel","ioerr","appender","Catppuccin","macchiato","gitmodules","Dotfiles","bashprofile","vimrc","flac","webp","exiftool","mediainfo","ripgrep","indexmap","indexmap","unwatch","canonicalize","serde","fsevent","Ueberzug","iterm","wezterm","sixel","chafa","ueberzugpp","Konsole","Überzug","pkgs","pdftoppm","poppler","singlefile","jpegopt","EXIF","rustfmt","mktemp","nanos","xclip","xsel","natord","Mintty","nixos","nixpkgs","SIGTSTP","SIGCONT","SIGCONT","mlua","nonstatic","userdata","metatable","natsort","backstack","luajit","Succ","Succ","cand","fileencoding","foldmethod","lightgreen","darkgray","lightred","lightyellow","lightcyan","nushell","msvc","aarch","linemode","sxyazi","rsplit","ZELLIJ","bitflags","bitflags","USERPROFILE","Neovim","vergen","gitcl","Renderable","preloaders","prec","Upserting","prio","Ghostty","Catmull","Lanczos","cmds","unyank","scrolloff","headsup","unsub","uzers","scopeguard","SPDLOG","globset","filetime","magick","magick","prefetcher","Prework","prefetchers","PREWORKERS","conds","translit","rxvt","Urxvt","realpath","realname","REPARSE","hardlink","hardlinking","nlink","nlink","linemodes","SIGSTOP","sevenzip","rsplitn","replacen","DECSET","DECRQM","repeek","cwds","tcsi","Hyprland","Wayfire","SWAYSOCK","btime","nsec","codegen","gethostname","fchmod","fdfind","Rustc","rustc","ffprobe","vframes","luma","obase","outln","errln","tmtheme","twox","cfgs","fstype","objc","rdev","runloop","exfat","rclone","DECRQSS","DECSCUSR","libvterm","Uninit","lockin","rposition","resvg","foldhash","tilded","futs","chdir","hashbrown","JEMALLOC","RUSTFLAGS","RDONLY","GETPATH","fcntl","casefold","inodes","Splatable","casefied","thiserror","memchr","memmem","russh","deadpool","keepalive","nodelay","publickey","deadpool","initing","treelize","TOCTOU","fellback","watchee","Textlike","sstr","pointee","writef","wakeup","nonblocking","sigwinch","timespec","termios","tcgetattr","tcsetattr","tcgetwinsize","rustix","codepoint","codepoints","Raterm","mimetypes","Mimelist","renderables","redrawer","Padable"]}

12
flake.lock generated
View file

@ -20,11 +20,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1747312588,
"narHash": "sha256-MmJvj6mlWzeRwKGLcwmZpKaOPZ5nJb/6al5CXqJsgjo=",
"lastModified": 1783279667,
"narHash": "sha256-/NAkDSsve+GNM0Bt6tleJdCGfsTlK89nPjkVOzZMo0s=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b1bebd0fe266bbd1820019612ead889e96a8fa2d",
"rev": "f205b5574fd0cb7da5b702a2da51507b7f4fdd1b",
"type": "github"
},
"original": {
@ -48,11 +48,11 @@
]
},
"locked": {
"lastModified": 1747363019,
"narHash": "sha256-N4dwkRBmpOosa4gfFkFf/LTD8oOcNkAyvZ07JvRDEf0=",
"lastModified": 1783320166,
"narHash": "sha256-l7C/OsjcnWDOk2K3ssj+SBduwL67LashjBqis9+t468=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "0e624f2b1972a34be1a9b35290ed18ea4b419b6f",
"rev": "20ee15370c9256669d66968b89ee20a4b0a4e673",
"type": "github"
},
"original": {

View file

@ -32,7 +32,7 @@
rev = self.shortRev or self.dirtyShortRev or "dirty";
date = self.lastModifiedDate or self.lastModified or "19700101";
version =
(builtins.fromTOML (builtins.readFile ./yazi-fm/Cargo.toml)).package.version
(builtins.fromTOML (builtins.readFile ./Cargo.toml)).workspace.package.version
+ "pre${builtins.substring 0 8 date}_${rev}";
in
{
@ -45,21 +45,28 @@
rustPlatform
;
};
yazi = pkgs.callPackage ./nix/yazi.nix { inherit (self.packages.${system}) yazi-unwrapped; };
yazi = pkgs.callPackage ./nix/yazi.nix {
inherit (self.packages.${system}) yazi-unwrapped;
};
default = self.packages.${system}.yazi;
};
devShells = {
default = pkgs.callPackage ./nix/shell.nix { };
default = pkgs.callPackage ./nix/shell.nix {
inherit toolchain;
inherit (self.packages.${system}) yazi yazi-unwrapped;
};
};
formatter = pkgs.nixfmt-rfc-style;
formatter = pkgs.nixfmt-tree;
}
)
// {
overlays = {
default = self.overlays.yazi;
yazi = _: prev: { inherit (self.packages.${prev.stdenv.system}) yazi yazi-unwrapped; };
yazi = _: prev: {
inherit (self.packages.${prev.stdenv.system}) yazi yazi-unwrapped;
};
};
};
}

View file

@ -1,14 +1,14 @@
{
callPackage,
rust-bin,
nodePackages,
mkShell,
yazi,
toolchain,
cspell,
yazi-unwrapped,
}:
let
mainPkg = callPackage ./yazi.nix { };
in
mainPkg.overrideAttrs (oa: {
nativeBuildInputs = [
(rust-bin.stable.latest.default.override {
mkShell {
packages = yazi.passthru.runtimePaths ++ [
(toolchain.override {
extensions = [
"rust-src"
"rustfmt"
@ -16,9 +16,10 @@ mainPkg.overrideAttrs (oa: {
"clippy"
];
})
cspell
];
nodePackages.cspell
] ++ (oa.nativeBuildInputs or [ ]);
inputsFrom = [ yazi-unwrapped ];
env.RUST_BACKTRACE = "1";
})
}

View file

@ -6,8 +6,7 @@
lib,
installShellFiles,
stdenv,
darwin,
fetchFromGitHub,
rust-jemalloc-sys,
imagemagick,
@ -23,15 +22,12 @@ let
];
};
in
rustPlatform.buildRustPackage rec {
rustPlatform.buildRustPackage (finalAttrs: {
pname = "yazi";
inherit version src;
cargoLock = {
lockFile = "${src}/Cargo.lock";
#outputHashes = {
# "mlua-0.10.0" = "sha256-Xg6/jc+UP8tbJJ6x1sbAgt8ZHt051xEBBcjmikQqYlw=";
#};
};
env = {
@ -47,7 +43,7 @@ rustPlatform.buildRustPackage rec {
buildInputs = [
rust-jemalloc-sys
] ++ lib.optionals stdenv.isDarwin (with darwin.apple_sdk.frameworks; [ Foundation ]);
];
postInstall = ''
installShellCompletion --cmd yazi \
@ -55,20 +51,38 @@ rustPlatform.buildRustPackage rec {
--fish ./yazi-boot/completions/yazi.fish \
--zsh ./yazi-boot/completions/_yazi
installShellCompletion --cmd ya \
--bash ./yazi-cli/completions/ya.bash \
--fish ./yazi-cli/completions/ya.fish \
--zsh ./yazi-cli/completions/_ya
# Resize logo
for RES in 16 24 32 48 64 128 256; do
mkdir -p $out/share/icons/hicolor/"$RES"x"$RES"/apps
magick assets/logo.png -resize "$RES"x"$RES" $out/share/icons/hicolor/"$RES"x"$RES"/apps/yazi.png
done
installManPage ${finalAttrs.passthru.srcs.man_src}/yazi{.1,-config.5}
mkdir -p $out/share/applications
install -m644 assets/yazi.desktop $out/share/applications/
'';
passthru.srcs = {
man_src = fetchFromGitHub {
name = "manpages"; # needed to ensure name is unique
owner = "yazi-rs";
repo = "manpages";
rev = "8950e968f4a1ad0b83d5836ec54a070855068dbf";
hash = "sha256-kEVXejDg4ChFoMNBvKlwdFEyUuTcY2VuK9j0PdafKus=";
};
};
meta = {
description = "Blazing fast terminal file manager written in Rust, based on async I/O";
homepage = "https://github.com/sxyazi/yazi";
license = lib.licenses.mit;
mainProgram = "yazi";
};
}
})

View file

@ -12,11 +12,12 @@
# default optional deps
jq,
poppler_utils,
poppler-utils,
_7zz,
ffmpegthumbnailer,
ffmpeg,
fd,
ripgrep,
resvg,
fzf,
zoxide,
imagemagick,
@ -38,11 +39,12 @@ let
defaultDeps = [
jq
poppler_utils
poppler-utils
_7zz
ffmpegthumbnailer
ffmpeg
fd
ripgrep
resvg
fzf
zoxide
imagemagick
@ -94,6 +96,8 @@ runCommand yazi-unwrapped.name
inherit (yazi-unwrapped) pname version meta;
nativeBuildInputs = [ makeWrapper ];
passthru.runtimePaths = runtimePaths;
}
''
mkdir -p $out/bin

View file

@ -4,21 +4,31 @@ set -euo pipefail
export ARTIFACT_NAME="yazi-$1"
export YAZI_GEN_COMPLETIONS=1
# Build for the target
# Build the target
git config --global --add safe.directory "*"
cargo build --release --locked --target "$1"
# Copy the binaries to a known location
mkdir -p "target/release"
cp "target/$1/release/ya" "target/release/ya"
cp "target/$1/release/yazi" "target/release/yazi"
# Package deb
if [[ "$ARTIFACT_NAME" == *-linux-* ]] && { [[ "$ARTIFACT_NAME" == *-aarch64-* ]] || [[ "$ARTIFACT_NAME" == *-x86_64-* ]]; }; then
cargo install cargo-deb
cargo deb -p yazi-packing --no-build --target "$1" -o "$ARTIFACT_NAME.deb"
fi
# Create the artifact
mkdir -p "$ARTIFACT_NAME/completions"
cp "target/$1/release/ya" "$ARTIFACT_NAME"
cp "target/$1/release/yazi" "$ARTIFACT_NAME"
cp "target/release/ya" "$ARTIFACT_NAME"
cp "target/release/yazi" "$ARTIFACT_NAME"
cp yazi-cli/completions/* "$ARTIFACT_NAME/completions"
cp yazi-boot/completions/* "$ARTIFACT_NAME/completions"
cp README.md LICENSE "$ARTIFACT_NAME"
# Zip the artifact
if ! command -v zip &> /dev/null
then
sudo apt-get update && sudo apt-get install -yq zip
if ! command -v zip &> /dev/null; then
apt-get update && apt-get install -yq zip
fi
zip -r "$ARTIFACT_NAME.zip" "$ARTIFACT_NAME"

View file

@ -7,7 +7,14 @@ cd "$SCRIPT_DIR/.."
echo "Bumping version: $1"
TOML_FILES="$(git ls-files '*Cargo.toml')"
perl -pi -e "s/^version .*= .*\$/version = \"$1\"/" -- $TOML_FILES
perl -pi -e "s/^(yazi-[a-z]+)\\s*=\\s*{.*\$/\\1 = { path = \"..\/\\1\", version = \"$1\" }/" -- $TOML_FILES
perl -pi -e 's/^version .*= .*$/version = "'"$1"'"/' -- $TOML_FILES
perl -pi -e 's/^(yazi-[a-z]+)\s*=\s*{.*$/\1 = { path = "..\/\1", version = "'"$1"'" }/' -- $TOML_FILES
ESLINT_USE_FLAT_CONFIG=true eslint -c ~/.config/rules/eslint/eslint.config.cjs --fix -- $TOML_FILES
# Insert "## [v$1]" after "## [Unreleased]"
perl -0777 -pe "s/^(## \[Unreleased\]\s*)/\\1## [v$1]\n\n/m" -i CHANGELOG.md
# Determine previous version and append compare link
prev_ver=$(grep -oE '^\[v[0-9][^]]+\]' CHANGELOG.md | tail -n1 | tr -d '[]')
link="\[v$1\]: https://github.com/sxyazi/yazi/compare/$prev_ver...v$1"
perl -pi -e 's{(\['"$prev_ver"'\]:[^\n]+\n)}{$1'"$link"'\n}s' CHANGELOG.md
eslint -c ~/.config/rules/eslint/eslint.config.js --fix -- $TOML_FILES

View file

@ -1,19 +0,0 @@
#!/usr/bin/env bash
cargo publish -p yazi-macro && sleep 30
cargo publish -p yazi-codegen && sleep 30
cargo publish -p yazi-shared && sleep 30
cargo publish -p yazi-ffi && sleep 30
cargo publish -p yazi-fs && sleep 30
cargo publish -p yazi-config && sleep 30
cargo publish -p yazi-proxy && sleep 30
cargo publish -p yazi-adapter && sleep 30
cargo publish -p yazi-boot && sleep 30
cargo publish -p yazi-binding && sleep 30
cargo publish -p yazi-dds && sleep 30
cargo publish -p yazi-scheduler && sleep 30
cargo publish -p yazi-plugin && sleep 30
cargo publish -p yazi-widgets && sleep 30
cargo publish -p yazi-core && sleep 30
cargo publish -p yazi-fm && sleep 30
cargo publish -p yazi-cli

View file

@ -2,19 +2,32 @@ const LABEL_NAME = "needs info"
const RE_VERSION = /Yazi\s+Version\s*:\s\d+\.\d+\.\d+\s\(/gm
const RE_DEPENDENCIES = /Dependencies\s+[/a-z]+\s*:\s/gm
const RE_CHECKLIST = /#{3}\s+Checklist\s+(?:^-\s+\[x]\s+.+?(?:\n|\r\n|$)){2}/gm
const RE_PR_CHECKLIST = /#{2}\s+Checklist\s+(?:^-\s+\[x]\s+.+?(?:\n|\r\n|$)){2}/gm
function pullRequestBody(content) {
if (RE_PR_CHECKLIST.test(content)) {
return null
}
return "All required checklist items must be checked in the PR description."
}
function bugReportBody(creator, content, hash) {
if (RE_DEPENDENCIES.test(content) && RE_CHECKLIST.test(content) && new RegExp(` \\(${hash}[a-f0-9]? `).test(content)) {
return null
}
return `Hey @${creator}, I noticed that you did not correctly follow the issue template. Please ensure that:
return `Hey @${creator}, thank you for opening the issue to help improve Yazi, appreciate it!
I noticed that you did not correctly follow the issue template. Please ensure that:
- The bug can still be reproduced on the [newest nightly build](https://yazi-rs.github.io/docs/installation/#binaries).
- The debug information (\`yazi --debug\`) is updated for the newest nightly.
- The environment info (\`ya env\`) is updated for the newest nightly.
- The non-optional items in the checklist are checked.
Issues with \`${LABEL_NAME}\` will be marked ready once edited with the proper content, or closed after 2 days of inactivity.
Our maintainers work on Yazi in their free time, this helps them work efficiently, understand your setup quickly, and find a more appropriate solution. Thanks for your understanding! 🙏
`
}
@ -23,16 +36,26 @@ function featureRequestBody(creator, content) {
return null
}
return `Hey @${creator}, I noticed that you did not correctly follow the issue template. Please ensure that:
return `Hey @${creator}, thank you for opening the issue to help improve Yazi, appreciate it!
I noticed that you did not correctly follow the issue template. Please ensure that:
- The requested feature does not exist in the [newest nightly build](https://yazi-rs.github.io/docs/installation/#binaries).
- The debug information (\`yazi --debug\`) is updated for the newest nightly.
- The environment info (\`ya env\`) is updated for the newest nightly.
- The non-optional items in the checklist are checked.
Issues with \`${LABEL_NAME}\` will be marked ready once edited with the proper content, or closed after 2 days of inactivity.
Our maintainers work on Yazi in their free time, this helps them work efficiently, understand your setup quickly, and find a more appropriate solution. Thanks for your understanding! 🙏
`
}
function skipValidation(context) {
const login = context.payload.issue?.user?.login || context.payload.pull_request?.user?.login
const owner = context.payload.repository?.owner?.login || context.repo.owner
return !!login && login === owner
}
module.exports = async ({ github, context, core }) => {
async function nightlyHash() {
try {
@ -66,7 +89,7 @@ module.exports = async ({ github, context, core }) => {
const { data: events } = await github.rest.issues.listEvents({
...context.repo,
issue_number: id,
per_page : 100,
per_page: 100,
})
const all = events.filter(v => v.event === "labeled" && v.label?.name === LABEL_NAME)
@ -82,7 +105,7 @@ module.exports = async ({ github, context, core }) => {
const { data: events } = await github.rest.issues.listEvents({
...context.repo,
issue_number: id,
per_page : 100,
per_page: 100,
})
const all = events.filter(v => v.event === "unlabeled" && v.label?.name === LABEL_NAME)
@ -101,14 +124,16 @@ module.exports = async ({ github, context, core }) => {
await github.rest.issues.removeLabel({
...context.repo,
issue_number: id,
name : LABEL_NAME,
name: LABEL_NAME,
})
await hideOldComments(id)
} else if (mark && !marked && !await removedLabelManually(id)) {
await github.rest.issues.addLabels({
...context.repo,
issue_number: id,
labels : [LABEL_NAME],
labels: [LABEL_NAME],
})
await hideOldComments(id)
await github.rest.issues.createComment({
...context.repo,
issue_number: id,
@ -120,11 +145,42 @@ module.exports = async ({ github, context, core }) => {
}
}
async function hideOldComments(id) {
try {
const comments = await github.paginate(github.rest.issues.listComments, {
...context.repo,
issue_number: id,
per_page: 100,
})
for (const c of comments) {
const byBot = c.user?.login?.endsWith("[bot]") || c.user?.type === "Bot"
const contains = c?.body?.includes("or closed after 2 days of inactivity")
if (!byBot || !contains || !c.node_id) continue
try {
await github.graphql(
`mutation($subjectId: ID!, $classifier: ReportedContentClassifiers!) {
minimizeComment(input: {subjectId: $subjectId, classifier: $classifier}) {
minimizedComment { isMinimized }
}
}`,
{ subjectId: c.node_id, classifier: "OUTDATED" },
)
} catch (e) {
core.error(`Error minimizing comment ${c.id}: ${e.message}`)
}
}
} catch (e) {
core.error(`Error listing comments: ${e.message}`)
}
}
async function closeOldIssues() {
try {
const { data: issues } = await github.rest.issues.listForRepo({
...context.repo,
state : "open",
state: "open",
labels: LABEL_NAME,
})
@ -137,13 +193,13 @@ module.exports = async ({ github, context, core }) => {
await github.rest.issues.update({
...context.repo,
issue_number: issue.number,
state : "closed",
state: "closed",
state_reason: "not_planned",
})
await github.rest.issues.createComment({
...context.repo,
issue_number: issue.number,
body : `This issue has been automatically closed because it was marked as \`${LABEL_NAME}\` for more than 2 days without updates.
body: `This issue has been automatically closed because it was marked as \`${LABEL_NAME}\` for more than 2 days without updates.
If the problem persists, please file a new issue and complete the issue template so we can capture all the details necessary to investigate further.`,
})
}
@ -153,26 +209,58 @@ If the problem persists, please file a new issue and complete the issue template
}
}
async function main() {
const hash = await nightlyHash()
if (!hash) return
async function closeUnsupportedIssue(id) {
try {
await github.rest.issues.update({
...context.repo,
issue_number: id,
state: "closed",
state_reason: "not_planned",
})
await github.rest.issues.createComment({
...context.repo,
issue_number: id,
body: `Unsupported issue template.
Either the [Bug Report](https://github.com/sxyazi/yazi/issues/new?template=bug.yml) or [Feature Request](https://github.com/sxyazi/yazi/issues/new?template=feature.yml) template should be used.`,
})
} catch (e) {
core.error(`Error closing unsupported issue: ${e.message}`)
}
}
async function main() {
if (context.eventName === "schedule") {
await closeOldIssues()
return
}
if (skipValidation(context)) {
return
}
if (context.eventName === "issues") {
const id = context.payload.issue.number
const content = context.payload.issue.body || ""
const creator = context.payload.issue.user.login
const hash = await nightlyHash()
if (!hash) return
if (await hasLabel(id, "bug")) {
const body = bugReportBody(creator, content, hash)
await updateLabels(id, !!body, body)
} else if (await hasLabel(id, "feature")) {
const body = featureRequestBody(creator, content)
await updateLabels(id, !!body, body)
} else if (context.payload.action === "opened") {
await closeUnsupportedIssue(id)
}
} else if (context.eventName === "pull_request" || context.eventName === "pull_request_target") {
const content = context.payload.pull_request.body || ""
const body = pullRequestBody(content)
if (body) {
core.setFailed(body)
}
}
}

View file

@ -8,247 +8,225 @@
"name": "validate-form",
"version": "1.0.0",
"dependencies": {
"@actions/core": "^1.10.1",
"@actions/github": "^6.0.0"
"@actions/core": "^3.0.1",
"@actions/github": "^9.1.1"
}
},
"node_modules/@actions/core": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.11.1.tgz",
"integrity": "sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==",
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-3.0.1.tgz",
"integrity": "sha512-a6d/Nwahm9fliVGRhdhofo40HjHQasUPusmc7vBfyky+7Z+P2A1J68zyFVaNcEclc/Se+eO595oAr5nwEIoIUA==",
"license": "MIT",
"dependencies": {
"@actions/exec": "^1.1.1",
"@actions/http-client": "^2.0.1"
"@actions/exec": "^3.0.0",
"@actions/http-client": "^4.0.0"
}
},
"node_modules/@actions/exec": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz",
"integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==",
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-3.0.0.tgz",
"integrity": "sha512-6xH/puSoNBXb72VPlZVm7vQ+svQpFyA96qdDBvhB8eNZOE8LtPf9L4oAsfzK/crCL8YZ+19fKYVnM63Sl+Xzlw==",
"license": "MIT",
"dependencies": {
"@actions/io": "^1.0.1"
"@actions/io": "^3.0.2"
}
},
"node_modules/@actions/github": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/@actions/github/-/github-6.0.1.tgz",
"integrity": "sha512-xbZVcaqD4XnQAe35qSQqskb3SqIAfRyLBrHMd/8TuL7hJSz2QtbDwnNM8zWx4zO5l2fnGtseNE3MbEvD7BxVMw==",
"version": "9.1.1",
"resolved": "https://registry.npmjs.org/@actions/github/-/github-9.1.1.tgz",
"integrity": "sha512-tL5JbYOBZHc0ngEnCsaDcryUizIUIlQyIMwy1Wkx93H5HzbBJ7TbiPx2PnFjBwZW0Vh05JmfFZhecE6gglYegA==",
"license": "MIT",
"dependencies": {
"@actions/http-client": "^2.2.0",
"@octokit/core": "^5.0.1",
"@octokit/plugin-paginate-rest": "^9.2.2",
"@octokit/plugin-rest-endpoint-methods": "^10.4.0",
"@octokit/request": "^8.4.1",
"@octokit/request-error": "^5.1.1",
"undici": "^5.28.5"
"@actions/http-client": "^3.0.2",
"@octokit/core": "^7.0.6",
"@octokit/plugin-paginate-rest": "^14.0.0",
"@octokit/plugin-rest-endpoint-methods": "^17.0.0",
"@octokit/request": "^10.0.7",
"@octokit/request-error": "^7.1.0",
"undici": "^6.23.0"
}
},
"node_modules/@actions/http-client": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.3.tgz",
"integrity": "sha512-mx8hyJi/hjFvbPokCg4uRd4ZX78t+YyRPtnKWwIl+RzNaVuFpQHfmlGVfsKEJN8LwTCvL+DfVgAM04XaHkm6bA==",
"node_modules/@actions/github/node_modules/@actions/http-client": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-3.0.2.tgz",
"integrity": "sha512-JP38FYYpyqvUsz+Igqlc/JG6YO9PaKuvqjM3iGvaLqFnJ7TFmcLyy2IDrY0bI0qCQug8E9K+elv5ZNfw62ZJzA==",
"license": "MIT",
"dependencies": {
"tunnel": "^0.0.6",
"undici": "^5.25.4"
"undici": "^6.23.0"
}
},
"node_modules/@actions/http-client": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-4.0.1.tgz",
"integrity": "sha512-+Nvd1ImaOZBSoPbsUtEhv+1z99H12xzncCkz0a3RuehINE81FZSe2QTj3uvAPTcJX/SCzUQHQ0D1GrPMbrPitg==",
"license": "MIT",
"dependencies": {
"tunnel": "^0.0.6",
"undici": "^6.23.0"
}
},
"node_modules/@actions/io": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.3.tgz",
"integrity": "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==",
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-3.0.2.tgz",
"integrity": "sha512-nRBchcMM+QK1pdjO7/idu86rbJI5YHUKCvKs0KxnSYbVe3F51UfGxuZX4Qy/fWlp6l7gWFwIkrOzN+oUK03kfw==",
"license": "MIT"
},
"node_modules/@fastify/busboy": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz",
"integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==",
"license": "MIT",
"engines": {
"node": ">=14"
}
},
"node_modules/@octokit/auth-token": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz",
"integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==",
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz",
"integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==",
"license": "MIT",
"engines": {
"node": ">= 18"
"node": ">= 20"
}
},
"node_modules/@octokit/core": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.1.tgz",
"integrity": "sha512-dKYCMuPO1bmrpuogcjQ8z7ICCH3FP6WmxpwC03yjzGfZhj9fTJg6+bS1+UAplekbN2C+M61UNllGOOoAfGCrdQ==",
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.6.tgz",
"integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==",
"license": "MIT",
"dependencies": {
"@octokit/auth-token": "^4.0.0",
"@octokit/graphql": "^7.1.0",
"@octokit/request": "^8.4.1",
"@octokit/request-error": "^5.1.1",
"@octokit/types": "^13.0.0",
"before-after-hook": "^2.2.0",
"universal-user-agent": "^6.0.0"
"@octokit/auth-token": "^6.0.0",
"@octokit/graphql": "^9.0.3",
"@octokit/request": "^10.0.6",
"@octokit/request-error": "^7.0.2",
"@octokit/types": "^16.0.0",
"before-after-hook": "^4.0.0",
"universal-user-agent": "^7.0.0"
},
"engines": {
"node": ">= 18"
"node": ">= 20"
}
},
"node_modules/@octokit/endpoint": {
"version": "9.0.6",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz",
"integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==",
"version": "11.0.3",
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.3.tgz",
"integrity": "sha512-FWFlNxghg4HrXkD3ifYbS/IdL/mDHjh9QcsNyhQjN8dplUoZbejsdpmuqdA76nxj2xoWPs7p8uX2SNr9rYu0Ag==",
"license": "MIT",
"dependencies": {
"@octokit/types": "^13.1.0",
"universal-user-agent": "^6.0.0"
"@octokit/types": "^16.0.0",
"universal-user-agent": "^7.0.2"
},
"engines": {
"node": ">= 18"
"node": ">= 20"
}
},
"node_modules/@octokit/graphql": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.1.tgz",
"integrity": "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==",
"version": "9.0.3",
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.3.tgz",
"integrity": "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==",
"license": "MIT",
"dependencies": {
"@octokit/request": "^8.4.1",
"@octokit/types": "^13.0.0",
"universal-user-agent": "^6.0.0"
"@octokit/request": "^10.0.6",
"@octokit/types": "^16.0.0",
"universal-user-agent": "^7.0.0"
},
"engines": {
"node": ">= 18"
"node": ">= 20"
}
},
"node_modules/@octokit/openapi-types": {
"version": "24.2.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz",
"integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==",
"version": "27.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-27.0.0.tgz",
"integrity": "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==",
"license": "MIT"
},
"node_modules/@octokit/plugin-paginate-rest": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.2.tgz",
"integrity": "sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ==",
"version": "14.0.0",
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-14.0.0.tgz",
"integrity": "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==",
"license": "MIT",
"dependencies": {
"@octokit/types": "^12.6.0"
"@octokit/types": "^16.0.0"
},
"engines": {
"node": ">= 18"
"node": ">= 20"
},
"peerDependencies": {
"@octokit/core": "5"
}
},
"node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": {
"version": "20.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz",
"integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==",
"license": "MIT"
},
"node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": {
"version": "12.6.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
"integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
"license": "MIT",
"dependencies": {
"@octokit/openapi-types": "^20.0.0"
"@octokit/core": ">=6"
}
},
"node_modules/@octokit/plugin-rest-endpoint-methods": {
"version": "10.4.1",
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.4.1.tgz",
"integrity": "sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg==",
"version": "17.0.0",
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-17.0.0.tgz",
"integrity": "sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==",
"license": "MIT",
"dependencies": {
"@octokit/types": "^12.6.0"
"@octokit/types": "^16.0.0"
},
"engines": {
"node": ">= 18"
"node": ">= 20"
},
"peerDependencies": {
"@octokit/core": "5"
}
},
"node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/openapi-types": {
"version": "20.0.0",
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz",
"integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==",
"license": "MIT"
},
"node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": {
"version": "12.6.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz",
"integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==",
"license": "MIT",
"dependencies": {
"@octokit/openapi-types": "^20.0.0"
"@octokit/core": ">=6"
}
},
"node_modules/@octokit/request": {
"version": "8.4.1",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz",
"integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==",
"version": "10.0.8",
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.8.tgz",
"integrity": "sha512-SJZNwY9pur9Agf7l87ywFi14W+Hd9Jg6Ifivsd33+/bGUQIjNujdFiXII2/qSlN2ybqUHfp5xpekMEjIBTjlSw==",
"license": "MIT",
"dependencies": {
"@octokit/endpoint": "^9.0.6",
"@octokit/request-error": "^5.1.1",
"@octokit/types": "^13.1.0",
"universal-user-agent": "^6.0.0"
"@octokit/endpoint": "^11.0.3",
"@octokit/request-error": "^7.0.2",
"@octokit/types": "^16.0.0",
"fast-content-type-parse": "^3.0.0",
"json-with-bigint": "^3.5.3",
"universal-user-agent": "^7.0.2"
},
"engines": {
"node": ">= 18"
"node": ">= 20"
}
},
"node_modules/@octokit/request-error": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz",
"integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==",
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.1.0.tgz",
"integrity": "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==",
"license": "MIT",
"dependencies": {
"@octokit/types": "^13.1.0",
"deprecation": "^2.0.0",
"once": "^1.4.0"
"@octokit/types": "^16.0.0"
},
"engines": {
"node": ">= 18"
"node": ">= 20"
}
},
"node_modules/@octokit/types": {
"version": "13.10.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz",
"integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==",
"version": "16.0.0",
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-16.0.0.tgz",
"integrity": "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==",
"license": "MIT",
"dependencies": {
"@octokit/openapi-types": "^24.2.0"
"@octokit/openapi-types": "^27.0.0"
}
},
"node_modules/before-after-hook": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
"integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz",
"integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==",
"license": "Apache-2.0"
},
"node_modules/deprecation": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
"integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==",
"license": "ISC"
"node_modules/fast-content-type-parse": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz",
"integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fastify"
},
{
"type": "opencollective",
"url": "https://opencollective.com/fastify"
}
],
"license": "MIT"
},
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"license": "ISC",
"dependencies": {
"wrappy": "1"
}
"node_modules/json-with-bigint": {
"version": "3.5.8",
"resolved": "https://registry.npmjs.org/json-with-bigint/-/json-with-bigint-3.5.8.tgz",
"integrity": "sha512-eq/4KP6K34kwa7TcFdtvnftvHCD9KvHOGGICWwMFc4dOOKF5t4iYqnfLK8otCRCRv06FXOzGGyqE8h8ElMvvdw==",
"license": "MIT"
},
"node_modules/tunnel": {
"version": "0.0.6",
@ -260,27 +238,18 @@
}
},
"node_modules/undici": {
"version": "5.29.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz",
"integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==",
"version": "6.27.0",
"resolved": "https://registry.npmjs.org/undici/-/undici-6.27.0.tgz",
"integrity": "sha512-YmfV3YnEDzXRC5lZ2jWtWWHKGUm1zIt8AhesR1tens+HTNv+YZlN/dp6G727LOvMJ8xjP9Be7Y2Sdr96LDm+pg==",
"license": "MIT",
"dependencies": {
"@fastify/busboy": "^2.0.0"
},
"engines": {
"node": ">=14.0"
"node": ">=18.17"
}
},
"node_modules/universal-user-agent": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz",
"integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==",
"license": "ISC"
},
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz",
"integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==",
"license": "ISC"
}
}

View file

@ -6,7 +6,7 @@
},
"private": true,
"dependencies": {
"@actions/core": "^1.10.1",
"@actions/github": "^6.0.0"
"@actions/core": "^3.0.1",
"@actions/github": "^9.1.1"
}
}

View file

@ -18,6 +18,10 @@ apps:
command: yazi
environment:
PATH: $SNAP/bin:$SNAP/usr/bin:$PATH
ya:
command: ya
environment:
PATH: $SNAP/bin:$SNAP/usr/bin:$PATH
parts:
yazi:
@ -30,7 +34,7 @@ parts:
- chafa
- fd-find
- ffmpeg
- fzf
# - fzf
- libglu1-mesa
- libglut3.12
- poppler-utils
@ -119,17 +123,26 @@ parts:
# expected binary without much overhead, and works across platforms.
magick:
plugin: autotools
source: https://imagemagick.org/archive/ImageMagick.tar.gz
source-type: tar
source: https://github.com/ImageMagick/ImageMagick.git
source-type: git
source-tag: 7.1.2-0
source-depth: 1
stage-packages:
- libgomp1
autotools-configure-parameters:
- --prefix=/usr
build-attributes:
- enable-patchelf
organize:
# `libgomp1` installs libgomp into the multiarch directory, but the
# `magick` binary's RPATH only searches `$ORIGIN/../lib` (i.e. usr/lib).
# Move it alongside the Magick libraries so the bundled binary can load
# it at runtime; otherwise magick fails with:
# "libgomp.so.1: cannot open shared object file: No such file or directory"
usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/libgomp.so*: usr/lib/
prime:
- usr/bin/magick
- usr/lib/$CRAFT_ARCH_TRIPLET_BUILD_FOR/libgomp.so.1
- usr/lib/libgomp.so*
- usr/lib/libMagickCore-7.Q16HDRI.so*
- usr/lib/libMagickWand-7.Q16HDRI.so*

View file

@ -1,3 +1,4 @@
syntax = "Lua54"
indent_width = 2
call_parentheses = "NoSingleTable"
collapse_simple_statement = "FunctionOnly"

58
yazi-actor/Cargo.toml Normal file
View file

@ -0,0 +1,58 @@
[package]
name = "yazi-actor"
description = "Yazi actor model"
version.workspace = true
edition.workspace = true
license.workspace = true
authors.workspace = true
homepage.workspace = true
repository.workspace = true
rust-version.workspace = true
[lints]
workspace = true
[features]
default = [ "vendored-lua" ]
vendored-lua = [ "mlua/vendored" ]
[dependencies]
yazi-binding = { path = "../yazi-binding", version = "26.5.6" }
yazi-boot = { path = "../yazi-boot", version = "26.5.6" }
yazi-config = { path = "../yazi-config", version = "26.5.6" }
yazi-core = { path = "../yazi-core", version = "26.5.6" }
yazi-dds = { path = "../yazi-dds", version = "26.5.6" }
yazi-emulator = { path = "../yazi-emulator", version = "26.5.6" }
yazi-fs = { path = "../yazi-fs", version = "26.5.6" }
yazi-macro = { path = "../yazi-macro", version = "26.5.6" }
yazi-parser = { path = "../yazi-parser", version = "26.5.6" }
yazi-plugin = { path = "../yazi-plugin", version = "26.5.6" }
yazi-proxy = { path = "../yazi-proxy", version = "26.5.6" }
yazi-runner = { path = "../yazi-runner", version = "26.5.6" }
yazi-scheduler = { path = "../yazi-scheduler", version = "26.5.6" }
yazi-shared = { path = "../yazi-shared", version = "26.5.6" }
yazi-shim = { path = "../yazi-shim", version = "26.5.6" }
yazi-term = { path = "../yazi-term", version = "26.5.6" }
yazi-tty = { path = "../yazi-tty", version = "26.5.9" }
yazi-tui = { path = "../yazi-tui", version = "26.5.6" }
yazi-vfs = { path = "../yazi-vfs", version = "26.5.6" }
yazi-watcher = { path = "../yazi-watcher", version = "26.5.6" }
yazi-widgets = { path = "../yazi-widgets", version = "26.5.6" }
# External dependencies
anyhow = { workspace = true }
either = { workspace = true }
futures = { workspace = true }
hashbrown = { workspace = true }
indexmap = { workspace = true }
inventory = { workspace = true }
mlua = { workspace = true }
paste = { workspace = true }
ratatui-core = { workspace = true }
scopeguard = { workspace = true }
tokio = { workspace = true }
tokio-stream = { workspace = true }
tracing = { workspace = true }
[target."cfg(unix)".dependencies]
libc = { workspace = true }

5
yazi-actor/README.md Normal file
View file

@ -0,0 +1,5 @@
# yazi-actor
This crate is part of [Yazi][source], and it is not supposed to be used outside, as there are no guarantees about the stability of its API.
[source]: https://github.com/sxyazi/yazi

15
yazi-actor/src/actor.rs Normal file
View file

@ -0,0 +1,15 @@
use anyhow::Result;
use yazi_parser::spark::SparkKind;
use yazi_shared::data::Data;
use crate::Ctx;
pub trait Actor {
type Form;
const NAME: &str;
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data>;
fn hook(_cx: &Ctx, _form: &Self::Form) -> Option<SparkKind> { None }
}

View file

@ -0,0 +1,42 @@
use anyhow::Result;
use mlua::IntoLua;
use tracing::error;
use yazi_actor::lives::Lives;
use yazi_binding::runtime_scope;
use yazi_dds::{LOCAL, Payload, REMOTE};
use yazi_macro::succ;
use yazi_plugin::LUA;
use yazi_shared::data::Data;
use crate::{Actor, Ctx};
pub struct AcceptPayload;
impl Actor for AcceptPayload {
type Form = Payload<'static>;
const NAME: &str = "accept_payload";
fn act(cx: &mut Ctx, payload: Payload) -> Result<Data> {
let kind = payload.body.kind();
let lock = if payload.receiver == 0 || payload.receiver != payload.sender {
REMOTE.read()
} else {
LOCAL.read()
};
let Some(handlers) = lock.get(kind).filter(|&m| !m.is_empty()).cloned() else { succ!() };
drop(lock);
let kind = kind.to_owned();
succ!(Lives::scope(cx.core, |_| {
let body = payload.body.into_lua(&LUA)?;
for (id, cb) in handlers {
if let Err(e) = runtime_scope!(LUA, &id, cb.call::<()>(body.clone())) {
error!("Failed to run `{kind}` event handler in your `{id}` plugin: {e}");
}
}
Ok(())
})?);
}
}

View file

@ -0,0 +1,32 @@
use anyhow::Result;
use yazi_actor::Ctx;
use yazi_boot::BOOT;
use yazi_core::mgr::CdSource;
use yazi_macro::{act, succ};
use yazi_parser::VoidForm;
use yazi_shared::{data::Data, strand::StrandLike, url::UrlLike};
use crate::Actor;
pub struct Bootstrap;
impl Actor for Bootstrap {
type Form = VoidForm;
const NAME: &str = "bootstrap";
fn act(cx: &mut Ctx, _: Self::Form) -> Result<Data> {
cx.mgr.tabs.resize_with(BOOT.files.len(), Default::default);
for (i, file) in BOOT.files.iter().enumerate().rev() {
cx.tab = i;
if file.is_empty() {
act!(mgr:cd, cx, (BOOT.cwds[i].clone(), CdSource::Tab))?;
} else if let Ok(u) = BOOT.cwds[i].try_join(file) {
act!(mgr:reveal, cx, (u, CdSource::Tab))?;
}
}
succ!();
}
}

View file

@ -0,0 +1,24 @@
use anyhow::Result;
use yazi_core::notify::{MessageLevel, MessageOpt};
use yazi_macro::act;
use yazi_parser::app::DeprecateForm;
use yazi_shared::data::Data;
use crate::{Actor, Ctx};
pub struct Deprecate;
impl Actor for Deprecate {
type Form = DeprecateForm;
const NAME: &str = "deprecate";
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
act!(notify:push, cx, MessageOpt {
title: "Deprecated API".to_owned(),
content: form.content.into_owned(),
level: MessageLevel::Warn,
timeout: std::time::Duration::from_secs(20),
})
}
}

43
yazi-actor/src/app/dnd.rs Normal file
View file

@ -0,0 +1,43 @@
use anyhow::Result;
use mlua::{ObjectLike, Table};
use tracing::error;
use yazi_actor::lives::Lives;
use yazi_binding::runtime_scope;
use yazi_macro::succ;
use yazi_parser::app::DndForm;
use yazi_plugin::LUA;
use yazi_shared::data::Data;
use crate::{Actor, Ctx};
pub struct Dnd;
impl Actor for Dnd {
type Form = DndForm;
const NAME: &str = "dnd";
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
let Some(size) = cx.term.as_ref().and_then(|t| t.size().ok()) else { succ!() };
let area = yazi_binding::elements::Rect::from(size);
let result = Lives::scope(cx.core, move |_| {
runtime_scope!(LUA, "root", {
let root = LUA.globals().raw_get::<Table>("Root")?.call_method::<Table>("new", area)?;
if form.event.is_drag() {
root.call_method::<()>("drag", form.event)?;
} else {
root.call_method::<()>("drop", form.event)?;
}
Ok(())
})
});
if let Err(ref e) = result {
error!("{e}");
}
succ!(result?);
}
}

View file

@ -0,0 +1,17 @@
use anyhow::Result;
use yazi_actor::Ctx;
use yazi_macro::act;
use yazi_parser::VoidForm;
use yazi_shared::data::Data;
use crate::Actor;
pub struct Focus;
impl Actor for Focus {
type Form = VoidForm;
const NAME: &str = "focus";
fn act(cx: &mut Ctx, _: Self::Form) -> Result<Data> { act!(mgr:refresh, cx) }
}

24
yazi-actor/src/app/lua.rs Normal file
View file

@ -0,0 +1,24 @@
use anyhow::Result;
use yazi_binding::runtime_scope;
use yazi_macro::succ;
use yazi_parser::app::LuaForm;
use yazi_plugin::LUA;
use yazi_shared::data::{Data, Sendable};
use crate::{Actor, Ctx, lives::Lives};
pub struct Lua;
impl Actor for Lua {
type Form = LuaForm;
const NAME: &str = "lua";
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
let chunk = LUA.load(&*form.code).set_name("anonymous");
let result = Lives::scope(cx.core, |_| {
runtime_scope!(LUA, "inline", Sendable::value_to_data(&LUA, chunk.eval()?))
});
succ!(result?);
}
}

View file

@ -1,14 +1,19 @@
yazi_macro::mod_flat!(
accept_payload
bootstrap
deprecate
dnd
focus
lua
mouse
notify
plugin
plugin_do
quit
reflow
render
resize
resume
stop
update_notify
theme
title
update_progress
);

View file

@ -0,0 +1,52 @@
use anyhow::Result;
use mlua::{ObjectLike, Table};
use tracing::error;
use yazi_actor::lives::Lives;
use yazi_binding::runtime_scope;
use yazi_macro::succ;
use yazi_parser::app::MouseForm;
use yazi_plugin::LUA;
use yazi_shared::data::Data;
use yazi_term::event::MouseEventKind;
use crate::{Actor, Ctx};
pub struct Mouse;
impl Actor for Mouse {
type Form = MouseForm;
const NAME: &str = "mouse";
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
let Some(size) = cx.term.as_ref().and_then(|t| t.size().ok()) else { succ!() };
let area = yazi_binding::elements::Rect::from(size);
let result = Lives::scope(cx.core, move |_| {
runtime_scope!(LUA, "root", {
let root = LUA.globals().raw_get::<Table>("Root")?.call_method::<Table>("new", area)?;
match form.event.kind {
MouseEventKind::Down(_) => root.call_method("click", (form.event, false))?,
MouseEventKind::Up(_) => root.call_method("click", (form.event, true))?,
MouseEventKind::ScrollDown => root.call_method("scroll", (form.event, 1))?,
MouseEventKind::ScrollUp => root.call_method("scroll", (form.event, -1))?,
MouseEventKind::ScrollRight => root.call_method("touch", (form.event, 1))?,
MouseEventKind::ScrollLeft => root.call_method("touch", (form.event, -1))?,
MouseEventKind::Moved => root.call_method("move", form.event)?,
MouseEventKind::Drag(_) => root.call_method("drag", form.event)?,
}
Ok(())
})
});
if let Err(ref e) = result {
error!("{e}");
}
succ!(result?);
}
}

View file

@ -0,0 +1,40 @@
use anyhow::Result;
use yazi_core::app::PluginMode;
use yazi_macro::{act, succ};
use yazi_parser::app::PluginForm;
use yazi_proxy::AppProxy;
use yazi_runner::loader::LOADER;
use yazi_scheduler::NotifyProxy;
use yazi_shared::data::Data;
use crate::{Actor, Ctx};
pub struct Plugin;
impl Actor for Plugin {
type Form = PluginForm;
const NAME: &str = "plugin";
fn act(cx: &mut Ctx, Self::Form { mut opt }: Self::Form) -> Result<Data> {
let mut hits = false;
if let Some(chunk) = LOADER.read().get(&*opt.id) {
hits = true;
opt.mode = opt.mode.auto_then(chunk.sync_entry);
}
if opt.mode == PluginMode::Async {
succ!(cx.core.tasks.scheduler.plugin_entry(opt.into()));
} else if opt.mode == PluginMode::Sync && hits {
return act!(app:plugin_do, cx, opt);
}
tokio::spawn(async move {
match LOADER.ensure(&opt.id, |_| ()).await {
Ok(()) => AppProxy::plugin_do(opt),
Err(e) => NotifyProxy::push_error("Plugin load failed", e.to_string()),
}
});
succ!();
}
}

View file

@ -0,0 +1,58 @@
use anyhow::Result;
use mlua::ObjectLike;
use scopeguard::defer;
use tracing::{error, warn};
use yazi_binding::runtime_mut;
use yazi_core::app::PluginMode;
use yazi_macro::succ;
use yazi_parser::app::PluginForm;
use yazi_plugin::LUA;
use yazi_runner::{entry::EntryJob, loader::{LOADER, Loader}};
use yazi_scheduler::NotifyProxy;
use yazi_shared::data::Data;
use crate::{Actor, Ctx, lives::Lives};
pub struct PluginDo;
impl Actor for PluginDo {
type Form = PluginForm;
const NAME: &str = "plugin_do";
fn act(cx: &mut Ctx, Self::Form { opt }: Self::Form) -> Result<Data> {
let loader = LOADER.read();
let Some(chunk) = loader.get(&*opt.id) else {
succ!(warn!("plugin `{}` not found", opt.id));
};
if let Err(e) = Loader::compatible_or_error(&opt.id, chunk) {
succ!(NotifyProxy::push_error("Incompatible plugin", e.to_string()));
}
if opt.mode.auto_then(chunk.sync_entry) != PluginMode::Sync {
succ!(cx.core.tasks.scheduler.plugin_entry(opt.into()));
}
let blocking = runtime_mut!(LUA)?.critical_push(&opt.id, true);
defer! { _ = runtime_mut!(LUA).map(|mut r| r.critical_pop(blocking)) }
let plugin = match LOADER.load_chunk(&LUA, &opt.id, chunk) {
Ok(t) => t,
Err(e) => succ!(warn!("{e}")),
};
drop(loader);
let result = Lives::scope(cx.core, |_| {
if let Some(cb) = opt.callback {
cb(&LUA, plugin)
} else {
plugin.call_method("entry", EntryJob { args: opt.args, ..Default::default() })
}
});
if let Err(ref e) = result {
error!("Sync plugin `{}` failed: {e}", opt.id);
}
succ!(result?);
}
}

View file

@ -0,0 +1,50 @@
use std::process;
use anyhow::Result;
use yazi_boot::ARGS;
use yazi_fs::engine::{Engine, local::Local};
use yazi_parser::app::QuitForm;
use yazi_shared::{data::Data, strand::{StrandBuf, StrandLike, ToStrand}};
use yazi_tui::Raterm;
use crate::{Actor, Ctx};
pub struct Quit;
impl Actor for Quit {
type Form = QuitForm;
const NAME: &str = "quit";
fn act(cx: &mut Ctx, Self::Form { opt }: Self::Form) -> Result<Data> {
cx.tasks.shutdown();
cx.mgr.shutdown();
futures::executor::block_on(async {
_ = futures::join!(
yazi_dds::shutdown(),
yazi_dds::STATE.drain(),
Self::cwd_to_file(cx, opt.no_cwd_file),
Self::selected_to_file(opt.selected)
);
});
Raterm::stop();
process::exit(opt.code);
}
}
impl Quit {
async fn cwd_to_file(cx: &Ctx<'_>, no: bool) {
if let Some(p) = ARGS.cwd_file.as_ref().filter(|_| !no) {
let cwd = cx.mgr.cwd().to_strand();
Local::regular(p).write(cwd.encoded_bytes()).await.ok();
}
}
async fn selected_to_file(selected: Option<StrandBuf>) {
if let (Some(s), Some(p)) = (selected, &ARGS.chooser_file) {
Local::regular(p).write(s.encoded_bytes()).await.ok();
}
}
}

View file

@ -0,0 +1,54 @@
use anyhow::Result;
use mlua::{LuaString, Value};
use ratatui_core::layout::Position;
use tracing::error;
use yazi_actor::lives::Lives;
use yazi_config::LAYOUT;
use yazi_macro::{render, succ};
use yazi_parser::app::ReflowForm;
use yazi_shared::data::Data;
use crate::{Actor, Ctx};
pub struct Reflow;
impl Actor for Reflow {
type Form = ReflowForm;
const NAME: &str = "reflow";
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
let Some(size) = cx.term.as_ref().and_then(|t| t.size().ok()) else { succ!() };
let mut layout = LAYOUT.get();
let result = Lives::scope(cx.core, |_| {
let comps = (form.reflow)((Position::ORIGIN, size).into())?;
for v in comps.sequence_values::<Value>() {
let Value::Table(t) = v? else {
error!("`reflow()` must return a table of components");
continue;
};
let id: LuaString = t.get("_id")?;
match &*id.as_bytes() {
b"current" => layout.current = *t.raw_get::<yazi_binding::elements::Rect>("_area")?,
b"preview" => layout.preview = *t.raw_get::<yazi_binding::elements::Rect>("_area")?,
b"progress" => layout.progress = *t.raw_get::<yazi_binding::elements::Rect>("_area")?,
_ => {}
}
}
Ok(())
});
if layout != LAYOUT.get() {
LAYOUT.set(layout);
render!();
}
if let Err(ref e) = result {
error!("Failed to `reflow()` the `Root` component:\n{e}");
}
succ!();
}
}

View file

@ -0,0 +1,25 @@
use anyhow::Result;
use yazi_actor::Ctx;
use yazi_macro::act;
use yazi_parser::app::ReflowForm;
use yazi_shared::data::Data;
use crate::Actor;
pub struct Resize;
impl Actor for Resize {
type Form = ReflowForm;
const NAME: &str = "resize";
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
act!(app:reflow, cx, form)?;
cx.current_mut().arrow(0);
cx.parent_mut().map(|f| f.arrow(0));
cx.current_mut().sync_page(true);
act!(mgr:peek, cx)
}
}

View file

@ -0,0 +1,29 @@
use anyhow::Result;
use yazi_macro::{act, render, succ};
use yazi_parser::app::ReflowForm;
use yazi_shared::data::Data;
use yazi_tui::Raterm;
use crate::{Actor, Ctx};
pub struct Resume;
impl Actor for Resume {
type Form = ReflowForm;
const NAME: &str = "resume";
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
cx.active_mut().preview.reset();
drop(cx.term.take());
*cx.term = Some(Raterm::start()?);
// While the app resumes, it's possible that the terminal size has changed.
// We need to trigger a resize, and render the UI based on the resized area.
act!(app:resize, cx, form.reflow)?;
act!(app:title, cx).ok();
succ!(render!());
}
}

View file

@ -0,0 +1,22 @@
use anyhow::Result;
use yazi_macro::succ;
use yazi_parser::VoidForm;
use yazi_shared::data::Data;
use crate::{Actor, Ctx};
pub struct Stop;
impl Actor for Stop {
type Form = VoidForm;
const NAME: &str = "stop";
fn act(cx: &mut Ctx, _: Self::Form) -> Result<Data> {
cx.active_mut().preview.reset_image();
*cx.term = None;
succ!();
}
}

View file

@ -0,0 +1,25 @@
use anyhow::Result;
use yazi_actor::Ctx;
use yazi_config::{THEME, build_flavor};
use yazi_emulator::EMULATOR;
use yazi_macro::{render, succ};
use yazi_parser::VoidForm;
use yazi_shared::data::Data;
use yazi_shim::serde::Overlay;
use crate::Actor;
pub struct Theme;
impl Actor for Theme {
type Form = VoidForm;
const NAME: &str = "theme";
fn act(_cx: &mut Ctx, _: Self::Form) -> Result<Data> {
THEME.overlay(build_flavor(EMULATOR.light, true)?);
yazi_plugin::theme::reset()?;
succ!(render!());
}
}

View file

@ -0,0 +1,32 @@
use anyhow::Result;
use yazi_actor::Ctx;
use yazi_macro::{succ, writef};
use yazi_parser::{app::TitleForm, spark::SparkKind};
use yazi_shared::{Source, data::Data};
use yazi_tty::{TTY, sequence::SetTitle};
use yazi_tui::RatermState;
use crate::Actor;
pub struct Title;
impl Actor for Title {
type Form = TitleForm;
const NAME: &str = "title";
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
let s = form.value.unwrap_or_else(|| format!("Yazi: {}", cx.tab().name()).into());
writef!(TTY.writer(), "{}", SetTitle(&s))?;
yazi_tui::STATE.set(RatermState { title: !s.is_empty(), ..yazi_tui::STATE.get() });
succ!()
}
fn hook(cx: &Ctx, _form: &Self::Form) -> Option<SparkKind> {
match cx.source() {
Source::Ind => Some(SparkKind::IndAppTitle),
_ => None,
}
}
}

View file

@ -0,0 +1,40 @@
use anyhow::Result;
use yazi_actor::Ctx;
use yazi_macro::{act, render, render_partial, succ};
use yazi_parser::app::UpdateProgressForm;
use yazi_shared::data::Data;
use crate::Actor;
pub struct UpdateProgress;
impl Actor for UpdateProgress {
type Form = UpdateProgressForm;
const NAME: &str = "update_progress";
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
// Update the progress of all tasks.
let tasks = &mut cx.tasks;
let progressed = tasks.summary != form.summary;
tasks.summary = form.summary;
// If the task manager is visible, update the snaps with a full render.
if tasks.visible {
let new = tasks.paginate();
if tasks.snaps != new {
tasks.snaps = new;
act!(tasks:arrow, cx)?;
succ!(render!());
}
}
if !progressed {
succ!()
} else if tasks.summary.total == 0 {
succ!(render!())
} else {
succ!(render_partial!())
}
}
}

View file

@ -0,0 +1,19 @@
use anyhow::Result;
use yazi_macro::{render, succ};
use yazi_parser::ArrowForm;
use yazi_shared::data::Data;
use yazi_widgets::Scrollable;
use crate::{Actor, Ctx};
pub struct Arrow;
impl Actor for Arrow {
type Form = ArrowForm;
const NAME: &str = "arrow";
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
succ!(render!(cx.cmp.scroll(form.step)));
}
}

View file

@ -0,0 +1,47 @@
use std::mem;
use anyhow::Result;
use yazi_macro::{act, render, succ};
use yazi_parser::cmp::CloseForm;
use yazi_shared::data::Data;
use yazi_widgets::input::parser::CompleteOpt;
use crate::{Actor, Ctx};
pub struct Close;
impl Actor for Close {
type Form = CloseForm;
const NAME: &str = "close";
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
if form.submit && cx.cmp.visible {
Self::flush_last_input(cx)?;
}
let cmp = &mut cx.cmp;
if let Some(item) = cmp.selected().filter(|_| form.submit).cloned() {
return act!(input:complete, cx, CompleteOpt { name: item.name, is_dir: item.is_dir, ticket: cmp.ticket });
}
cmp.caches.clear();
cmp.ticket = Default::default();
cmp.handle.take().map(|h| h.abort());
succ!(render!(mem::replace(&mut cmp.visible, false)));
}
}
impl Close {
fn flush_last_input(cx: &mut Ctx) -> Result<Data> {
let Some(guard) = cx.input.lock() else { succ!() };
if cx.cmp.ticket == guard.ticket.current() {
succ!();
}
let before = guard.partition().0.to_owned();
drop(guard);
act!(cmp:trigger, cx, before)
}
}

View file

@ -0,0 +1,76 @@
use std::{mem, ops::ControlFlow};
use anyhow::Result;
use yazi_core::cmp::CmpItem;
use yazi_macro::{render, succ};
use yazi_parser::cmp::ShowForm;
use yazi_shared::{data::Data, path::{DynPath, PathDyn}, strand::StrandLike};
use crate::{Actor, Ctx};
const LIMIT: usize = 30;
pub struct Show;
impl Actor for Show {
type Form = ShowForm;
const NAME: &str = "show";
fn act(cx: &mut Ctx, Self::Form { opt }: Self::Form) -> Result<Data> {
let cmp = &mut cx.cmp;
if cmp.ticket != opt.ticket {
succ!();
}
if !opt.cache.is_empty() {
cmp.caches.insert(opt.cache_name.clone(), opt.cache);
}
let Some(cache) = cmp.caches.get(&opt.cache_name) else {
succ!();
};
cmp.matches = Self::match_candidates(opt.word.dyn_path(), cache);
if cmp.matches.is_empty() {
succ!(render!(mem::replace(&mut cmp.visible, false)));
}
cmp.offset = 0;
cmp.cursor = 0;
cmp.visible = true;
succ!(render!());
}
}
impl Show {
fn match_candidates(word: PathDyn, cache: &[CmpItem]) -> Vec<CmpItem> {
let smart = !word.encoded_bytes().iter().any(|&b| b.is_ascii_uppercase());
let flow = cache.iter().try_fold((Vec::new(), Vec::new()), |(mut exact, mut fuzzy), item| {
let starts_with = if smart {
item.name.starts_with_ignore_ascii_case(word)
} else {
item.name.starts_with(word)
};
if starts_with {
exact.push(item);
if exact.len() >= LIMIT {
return ControlFlow::Break((exact, fuzzy));
}
} else if fuzzy.len() < LIMIT - exact.len() && item.name.contains(word) {
// Here we don't break the control flow, since we want more exact matching.
fuzzy.push(item)
}
ControlFlow::Continue((exact, fuzzy))
});
let (exact, fuzzy) = match flow {
ControlFlow::Continue(v) => v,
ControlFlow::Break(v) => v,
};
let it = fuzzy.into_iter().take(LIMIT - exact.len());
exact.into_iter().chain(it).cloned().collect()
}
}

View file

@ -0,0 +1,155 @@
use std::{io, mem};
use anyhow::Result;
use yazi_core::cmp::{CmpItem, CmpOpt};
use yazi_fs::{engine::{DirReader, FileHolder}, path::clean_url};
use yazi_macro::{act, render, succ};
use yazi_parser::cmp::TriggerForm;
use yazi_proxy::CmpProxy;
use yazi_shared::{AnyAsciiChar, BytePredictor, data::Data, natsort, path::{DynPath, PathBufDyn, PathLike}, spec::Spec, strand::{AsStrand, StrandLike}, url::{UrlBuf, UrlCow, UrlLike}};
use yazi_vfs::engine;
use crate::{Actor, Ctx};
pub struct Trigger;
impl Actor for Trigger {
type Form = TriggerForm;
const NAME: &str = "trigger";
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
if form.ticket.is_some_and(|t| t != cx.cmp.ticket) {
succ!();
} else if form.ticket.is_none() {
cx.cmp.ticket = cx.input.lock().map(|g| g.ticket.current()).unwrap_or_default();
}
cx.cmp.handle.take().map(|h| h.abort());
let Some((parent, word)) = Self::split_url(&form.word) else {
return act!(cmp:close, cx, false);
};
let ticket = cx.cmp.ticket;
if cx.cmp.caches.contains_key(&parent) {
return act!(cmp:show, cx, CmpOpt { cache: vec![], cache_name: parent, word, ticket });
}
cx.cmp.handle = Some(tokio::spawn(async move {
let mut dir = engine::read_dir(&parent).await?;
let mut cache = vec![];
// "/" is both a directory separator and the root directory per se
// As there's no parent directory for the FS root, it is a special case
if parent.loc() == "/" {
cache.push(CmpItem { name: Default::default(), is_dir: true });
}
while let Ok(Some(ent)) = dir.next().await {
if let Ok(ft) = ent.file_type().await {
cache.push(CmpItem { name: ent.name().into_owned(), is_dir: ft.is_dir() });
}
}
if !cache.is_empty() {
cache
.sort_unstable_by(|a, b| natsort(a.name.encoded_bytes(), b.name.encoded_bytes(), true));
CmpProxy::show(CmpOpt { cache, cache_name: parent, word, ticket });
}
Ok::<_, io::Error>(())
}));
succ!(render!(mem::replace(&mut cx.cmp.visible, false)));
}
}
impl Trigger {
fn split_url(s: &str) -> Option<(UrlBuf, PathBufDyn)> {
let (spec, path) = Spec::parse(s.as_bytes()).ok()?;
if path.is_empty() && !AnyAsciiChar::SEP.predicate(s.bytes().last()?) {
return None; // We don't complete a `sftp://test`, but `sftp://test/`
}
// Spec
let spec = spec.zeroed();
if spec.kind.is_local() && path.as_strand() == "~" {
return None; // We don't complete a `~`, but `~/`
}
// Child
let child = path.rsplit_pred(AnyAsciiChar::SEP).map_or(path.dyn_path(), |(_, c)| c).to_owned();
// Parent
let url = UrlCow::try_from((spec.clone().zeroed(), path)).ok()?;
let abs = if let Some(u) = engine::try_absolute(&url) { u } else { url };
let parent = abs.loc().try_strip_suffix(&child).ok()?;
Some((clean_url(UrlCow::try_from((spec, parent)).ok()?), child))
}
}
#[cfg(test)]
mod tests {
use yazi_fs::CWD;
use yazi_shared::url::UrlLike;
use super::*;
fn compare(s: &str, parent: &str, child: &str) {
let (mut p, c) = Trigger::split_url(s).unwrap();
if let Ok(u) = p.try_strip_prefix(yazi_fs::CWD.load().as_ref()) {
p = UrlBuf::Regular(u.as_os().unwrap().into());
}
assert_eq!((p, c.to_str().unwrap()), (parent.parse().unwrap(), child));
}
#[cfg(unix)]
#[test]
fn test_split() {
yazi_shared::init_tests();
yazi_config::init_tests();
yazi_fs::init();
assert_eq!(Trigger::split_url(""), None);
assert_eq!(Trigger::split_url("sftp://vps"), None);
compare(" ", "", " ");
compare("/", "/", "");
compare("//", "/", "");
compare("///", "/", "");
compare("/foo", "/", "foo");
compare("//foo", "/", "foo");
compare("///foo", "/", "foo");
compare("/foo/", "/foo/", "");
compare("//foo/", "/foo/", "");
compare("/foo/bar", "/foo/", "bar");
compare("///foo/bar", "/foo/", "bar");
CWD.set(&"sftp://vps".parse::<UrlBuf>().unwrap(), || {});
compare("sftp://vps/a", "sftp://vps/.", "a");
compare("sftp://vps//a", "sftp://vps//", "a");
compare("test-scope://aws/a", "test-scope://aws/.", "a");
compare("test-scope://aws//a", "test-scope://aws//", "a");
}
#[cfg(windows)]
#[test]
fn test_split() {
yazi_shared::init_tests();
yazi_config::init_tests();
yazi_fs::init();
compare("foo", "", "foo");
compare(r"foo\", r"foo\", "");
compare(r"foo\bar", r"foo\", "bar");
compare(r"foo\bar\", r"foo\bar\", "");
compare(r"C:\", r"C:\", "");
compare(r"C:\foo", r"C:\", "foo");
compare(r"C:\foo\", r"C:\foo\", "");
compare(r"C:\foo\bar", r"C:\foo\", "bar");
}
}

View file

@ -0,0 +1,26 @@
use anyhow::Result;
use yazi_macro::{render, succ};
use yazi_parser::ArrowForm;
use yazi_shared::data::Data;
use crate::{Actor, Ctx};
pub struct Arrow;
impl Actor for Arrow {
type Form = ArrowForm;
const NAME: &str = "arrow";
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
let confirm = &mut cx.core.confirm;
let area = cx.core.mgr.area(confirm.position);
let len = confirm.list.line_count(area.width);
let old = confirm.offset;
confirm.offset = form.step.add(confirm.offset, len, area.height as _, 0, 0);
succ!(render!(old != confirm.offset));
}
}

View file

@ -0,0 +1,20 @@
use anyhow::Result;
use yazi_macro::{render, succ};
use yazi_parser::confirm::CloseForm;
use yazi_shared::data::Data;
use crate::{Actor, Ctx};
pub struct Close;
impl Actor for Close {
type Form = CloseForm;
const NAME: &str = "close";
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
cx.confirm.token.complete(form.submit);
cx.confirm.visible = false;
succ!(render!());
}
}

View file

@ -0,0 +1,31 @@
use anyhow::Result;
use yazi_macro::{act, render, succ};
use yazi_parser::confirm::ShowForm;
use yazi_shared::data::Data;
use crate::{Actor, Ctx};
pub struct Show;
impl Actor for Show {
type Form = ShowForm;
const NAME: &str = "show";
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
act!(confirm:close, cx)?;
let confirm = &mut cx.confirm;
confirm.title = form.cfg.title;
confirm.body = form.cfg.body;
confirm.list = form.cfg.list;
confirm.position = form.cfg.position;
confirm.offset = 0;
confirm.token = form.token;
confirm.visible = true;
succ!(render!());
}
}

123
yazi-actor/src/context.rs Normal file
View file

@ -0,0 +1,123 @@
use std::ops::{Deref, DerefMut};
use anyhow::{Result, anyhow};
use yazi_core::{Core, mgr::Tabs, tab::{Folder, Tab}};
use yazi_fs::file::File;
use yazi_shared::{Source, event::Action, id::Id, url::UrlBuf};
use yazi_tui::Raterm;
pub struct Ctx<'a> {
pub core: &'a mut Core,
pub term: &'a mut Option<Raterm>,
pub tab: usize,
pub level: usize,
pub source: Source,
#[cfg(debug_assertions)]
pub backtrace: Vec<&'static str>,
}
impl Deref for Ctx<'_> {
type Target = Core;
fn deref(&self) -> &Self::Target { self.core }
}
impl DerefMut for Ctx<'_> {
fn deref_mut(&mut self) -> &mut Self::Target { self.core }
}
impl<'a> Ctx<'a> {
pub fn new(action: &Action, core: &'a mut Core, term: &'a mut Option<Raterm>) -> Result<Self> {
let tab = if let Ok(id) = action.get::<Id>("tab") {
core
.mgr
.tabs
.iter()
.position(|t| t.id == id)
.ok_or_else(|| anyhow!("Tab with id {id} not found"))?
} else {
core.mgr.tabs.cursor
};
Ok(Self {
core,
term,
tab,
level: 0,
source: action.source,
#[cfg(debug_assertions)]
backtrace: vec![],
})
}
pub fn renew<'b>(cx: &'a mut Ctx<'b>) -> Self {
let tab = cx.core.mgr.tabs.cursor;
Self {
core: cx.core,
term: cx.term,
tab,
level: cx.level,
source: cx.source,
#[cfg(debug_assertions)]
backtrace: vec![],
}
}
pub fn active(core: &'a mut Core, term: &'a mut Option<Raterm>) -> Self {
let tab = core.mgr.tabs.cursor;
Self {
core,
term,
tab,
level: 0,
source: Source::Unknown,
#[cfg(debug_assertions)]
backtrace: vec![],
}
}
}
impl<'a> Ctx<'a> {
#[inline]
pub fn tabs(&self) -> &Tabs { &self.mgr.tabs }
#[inline]
pub fn tabs_mut(&mut self) -> &mut Tabs { &mut self.mgr.tabs }
#[inline]
pub fn tab(&self) -> &Tab { &self.tabs()[self.tab] }
#[inline]
pub fn tab_mut(&mut self) -> &mut Tab { &mut self.core.mgr.tabs[self.tab] }
#[inline]
pub fn cwd(&self) -> &UrlBuf { self.tab().cwd() }
#[inline]
pub fn parent(&self) -> Option<&Folder> { self.tab().parent.as_ref() }
#[inline]
pub fn parent_mut(&mut self) -> Option<&mut Folder> { self.tab_mut().parent.as_mut() }
#[inline]
pub fn current(&self) -> &Folder { &self.tab().current }
#[inline]
pub fn current_mut(&mut self) -> &mut Folder { &mut self.tab_mut().current }
#[inline]
pub fn hovered(&self) -> Option<&File> { self.tab().hovered() }
#[inline]
pub fn hovered_url(&self) -> Option<&UrlBuf> { self.tab().hovered_url() }
#[inline]
pub fn hovered_folder(&self) -> Option<&Folder> { self.tab().hovered_folder() }
#[inline]
pub fn hovered_folder_mut(&mut self) -> Option<&mut Folder> {
self.tab_mut().hovered_folder_mut()
}
pub fn source(&self) -> Source { if self.level != 1 { Source::Ind } else { self.source } }
}

View file

@ -0,0 +1 @@
yazi_macro::mod_flat!(preflight);

View file

@ -0,0 +1,37 @@
use anyhow::Result;
use mlua::{ErrorContext, ExternalError, IntoLua, Value};
use yazi_binding::runtime_scope;
use yazi_dds::LOCAL;
use yazi_parser::spark::{Spark, SparkKind};
use yazi_plugin::LUA;
use crate::{Ctx, lives::Lives};
pub struct Preflight;
impl Preflight {
pub fn act<'a>(cx: &mut Ctx, opt: (SparkKind, Spark<'a>)) -> Result<Spark<'a>> {
let kind = opt.0;
let Some(handlers) = LOCAL.read().get(kind.into()).filter(|&m| !m.is_empty()).cloned() else {
return Ok(opt.1);
};
Ok(Lives::scope(cx.core, |_| {
let mut body = opt.1.into_lua(&LUA)?;
for (id, cb) in handlers {
match runtime_scope!(LUA, &id, cb.call::<Value>(&body)) {
Ok(Value::Nil) => {
Err(format!("`{kind}` event cancelled by `{id}` plugin on preflight").into_lua_err())?
}
Ok(v) => body = v,
Err(e) => Err(
format!("Failed to run `{kind}` event handler in `{id}` plugin: {e}").into_lua_err(),
)?,
};
}
Spark::from_lua(&LUA, kind, body)
.with_context(|e| format!("Unexpected return type from `{kind}` event handlers: {e}"))
})?)
}
}

View file

@ -0,0 +1,19 @@
use anyhow::Result;
use yazi_macro::{render, succ};
use yazi_parser::ArrowForm;
use yazi_shared::data::Data;
use yazi_widgets::Scrollable;
use crate::{Actor, Ctx};
pub struct Arrow;
impl Actor for Arrow {
type Form = ArrowForm;
const NAME: &str = "arrow";
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
succ!(render!(cx.help.scroll(form.step)));
}
}

View file

@ -0,0 +1,25 @@
use anyhow::Result;
use yazi_macro::{emit, render, succ};
use yazi_parser::help::CloseForm;
use yazi_shared::data::Data;
use crate::{Actor, Ctx};
pub struct Close;
impl Actor for Close {
type Form = CloseForm;
const NAME: &str = "close";
fn act(cx: &mut Ctx, opt: Self::Form) -> Result<Data> {
let help = &mut cx.help;
if let Some(chord) = help.bindings.get(help.cursor).filter(|_| opt.submit) {
emit!(Seq(chord.to_seq(help.layer)));
}
help.visible = false;
succ!(render!());
}
}

View file

@ -0,0 +1,24 @@
use anyhow::Result;
use yazi_macro::{act, render, succ};
use yazi_parser::VoidForm;
use yazi_shared::data::Data;
use yazi_widgets::input::InputMode;
use crate::{Actor, Ctx};
pub struct Escape;
impl Actor for Escape {
type Form = VoidForm;
const NAME: &str = "escape";
fn act(cx: &mut Ctx, _: Self::Form) -> Result<Data> {
if cx.help.input.mode() == InputMode::Normal {
return act!(help:close, cx);
}
act!(escape, cx.help.input)?;
succ!(render!());
}
}

View file

@ -0,0 +1 @@
yazi_macro::mod_flat!(arrow close escape toggle);

View file

@ -0,0 +1,39 @@
use anyhow::Result;
use ratatui_core::layout::Margin;
use yazi_config::popup::Help;
use yazi_macro::{render, succ};
use yazi_parser::help::ToggleForm;
use yazi_shared::data::Data;
use yazi_widgets::input::Input;
use crate::{Actor, Ctx};
pub struct Toggle;
impl Actor for Toggle {
type Form = ToggleForm;
const NAME: &str = "toggle";
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
let position = Help::position();
let area = cx.mgr.area(position);
let input_area = area.inner(Margin::new(1, 1));
let help = &mut cx.help;
help.visible = true;
help.layer = form.layer;
help.position = position;
help.height = area.height;
help.input = Input::default();
help.input.repos(input_area);
help.keyword.clear();
help.offset = 0;
help.cursor = 0;
help.filter_apply();
succ!(render!());
}
}

View file

@ -0,0 +1,47 @@
use anyhow::Result;
use yazi_core::input::InputMutGuard;
use yazi_macro::{act, render, succ};
use yazi_parser::{input::CloseForm, spark::SparkKind};
use yazi_shared::{Source, data::Data};
use yazi_widgets::input::InputEvent;
use crate::{Actor, Ctx};
pub struct Close;
impl Actor for Close {
type Form = CloseForm;
const NAME: &str = "close";
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
let Some(mut guard) = cx.input.lock_mut() else {
succ!();
};
guard.ticket.next();
if let Some(cb) = guard.cb.take() {
let value = guard.value().to_owned();
cb(if form.submit { InputEvent::Submit(value) } else { InputEvent::Cancel(value) });
}
if form.submit
&& let InputMutGuard::Main(input) = guard
{
input.histories.remember(&input.main.history.name, input.main.value());
}
cx.input.main.visible = false;
act!(cmp:close, cx)?;
succ!(render!());
}
fn hook(cx: &Ctx, _form: &Self::Form) -> Option<SparkKind> {
match cx.source() {
Source::Key => Some(SparkKind::KeyInputClose),
Source::Ind => Some(SparkKind::IndInputClose),
_ => None,
}
}
}

View file

@ -0,0 +1,26 @@
use anyhow::Result;
use yazi_macro::{act, succ};
use yazi_shared::data::Data;
use yazi_widgets::input::parser::CompleteOpt;
use crate::{Actor, Ctx};
pub struct Complete;
impl Actor for Complete {
type Form = CompleteOpt;
const NAME: &str = "complete";
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
let Some(mut guard) = cx.input.lock_mut() else {
succ!();
};
if guard.ticket.current() != form.ticket {
succ!();
}
act!(complete, guard, form)
}
}

View file

@ -0,0 +1,34 @@
use anyhow::Result;
use yazi_macro::{act, render, succ};
use yazi_parser::VoidForm;
use yazi_shared::data::Data;
use yazi_widgets::input::InputOp;
use crate::{Actor, Ctx};
pub struct Escape;
impl Actor for Escape {
type Form = VoidForm;
const NAME: &str = "escape";
fn act(cx: &mut Ctx, _: Self::Form) -> Result<Data> {
use yazi_widgets::input::InputMode as M;
let Some(mut guard) = cx.input.lock_mut() else {
succ!();
};
let (mode, op) = (guard.snap().mode, guard.snap().op);
act!(escape, guard)?;
drop(guard);
match mode {
M::Normal if op == InputOp::None => act!(input:close, cx),
M::Insert => act!(cmp:close, cx),
M::Normal | M::Replace => Ok(().into()),
}?;
succ!(render!());
}
}

View file

@ -0,0 +1 @@
yazi_macro::mod_flat!(close complete escape recall remember show);

View file

@ -0,0 +1,32 @@
use anyhow::Result;
use yazi_core::input::InputMutGuard;
use yazi_macro::succ;
use yazi_parser::input::RecallForm;
use yazi_shared::data::Data;
use crate::{Actor, Ctx};
pub struct Recall;
impl Actor for Recall {
type Form = RecallForm;
const NAME: &str = "recall";
fn act(cx: &mut Ctx, form: Self::Form) -> Result<Data> {
let Some(input) = cx.input.lock_mut() else {
succ!();
};
match input {
InputMutGuard::Main(input) => {
let entries = input.histories.get(&input.main.history.name);
input.main.recall(entries, form.step)
}
InputMutGuard::Alt(input, mut guard) => {
let entries = input.histories.get(&guard.history.name);
guard.recall(entries, form.step)
}
}
}
}

View file

@ -0,0 +1,33 @@
use anyhow::Result;
use yazi_core::input::InputMutGuard;
use yazi_macro::succ;
use yazi_parser::VoidForm;
use yazi_shared::data::Data;
use crate::{Actor, Ctx};
pub struct Remember;
impl Actor for Remember {
type Form = VoidForm;
const NAME: &str = "remember";
fn act(cx: &mut Ctx, _: Self::Form) -> Result<Data> {
let Some(mut input) = cx.input.lock_mut() else {
succ!();
};
match &mut input {
InputMutGuard::Main(input) => {
input.histories.remember(&input.main.history.name, input.main.value());
}
InputMutGuard::Alt(input, guard) => {
input.histories.remember(&guard.history.name, guard.value());
}
}
input.history.take();
succ!();
}
}

View file

@ -0,0 +1,36 @@
use std::ops::DerefMut;
use anyhow::Result;
use yazi_config::{THEME, YAZI};
use yazi_macro::{act, render, succ};
use yazi_parser::input::ShowForm;
use yazi_shared::data::Data;
use yazi_shim::ratatui::Padable;
use crate::{Actor, Ctx};
pub struct Show;
impl Actor for Show {
type Form = ShowForm;
const NAME: &str = "show";
fn act(cx: &mut Ctx, Self::Form { mut opt }: Self::Form) -> Result<Data> {
act!(input:close, cx)?;
let area = cx.mgr.area(opt.position).padding(cx.input.padding());
let input = &mut cx.input;
input.main.name = opt.name.clone();
input.main.title = opt.title.clone();
input.main.position = opt.position;
input.main.visible = true;
opt.styles = (&THEME.input).into();
opt.blinking = YAZI.input.cursor_blink;
*input.main.deref_mut() = yazi_widgets::input::Input::new(opt)?;
input.main.repos(area);
succ!(render!());
}
}

7
yazi-actor/src/lib.rs Normal file
View file

@ -0,0 +1,7 @@
extern crate self as yazi_actor;
yazi_macro::mod_pub!(app cmp confirm core help input lives mgr notify pick spot tasks which);
yazi_macro::mod_flat!(actor context);
pub fn init() { lives::init(); }

View file

@ -0,0 +1,29 @@
use std::ops::Deref;
use mlua::{AnyUserData, UserData, UserDataMethods};
use super::{Lives, PtrCell};
pub(super) struct Behavior {
inner: PtrCell<yazi_scheduler::Behavior>,
}
impl Deref for Behavior {
type Target = yazi_scheduler::Behavior;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl Behavior {
pub(super) fn make(inner: &yazi_scheduler::Behavior) -> mlua::Result<AnyUserData> {
let inner = PtrCell::from(inner);
Lives::scoped_userdata(Self { inner })
}
}
impl UserData for Behavior {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("reset", |_, me, ()| Ok(me.reset()));
}
}

View file

@ -1,29 +1,32 @@
use std::ops::Deref;
use mlua::{AnyUserData, IntoLua, MetaMethod, UserData, Value};
use mlua::{AnyUserData, IntoLua, LuaString, MetaMethod, UserData, UserDataMethods, UserDataRef, Value};
use paste::paste;
use super::{Lives, PtrCell};
pub(super) struct Ctx {
inner: PtrCell<crate::Ctx>,
pub(super) type CoreRef = UserDataRef<Core>;
pub(super) struct Core {
inner: PtrCell<yazi_core::Core>,
c_active: Option<Value>,
c_tabs: Option<Value>,
c_tasks: Option<Value>,
c_yanked: Option<Value>,
c_input: Option<Value>,
c_which: Option<Value>,
c_layer: Option<Value>,
}
impl Deref for Ctx {
type Target = crate::Ctx;
impl Deref for Core {
type Target = yazi_core::Core;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl Ctx {
#[inline]
pub(super) fn make(inner: &crate::Ctx) -> mlua::Result<AnyUserData> {
impl Core {
pub(super) fn make(inner: &yazi_core::Core) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self {
inner: inner.into(),
@ -31,14 +34,16 @@ impl Ctx {
c_tabs: None,
c_tasks: None,
c_yanked: None,
c_input: None,
c_which: None,
c_layer: None,
})
}
}
impl UserData for Ctx {
fn add_methods<M: mlua::UserDataMethods<Self>>(methods: &mut M) {
methods.add_meta_method_mut(MetaMethod::Index, |lua, me, key: mlua::String| {
impl UserData for Core {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_meta_method_mut(MetaMethod::Index, |lua, me, key: LuaString| {
macro_rules! reuse {
($key:ident, $value:expr) => {
match paste! { &me.[<c_ $key>] } {
@ -51,14 +56,14 @@ impl UserData for Ctx {
}
};
}
Ok(match key.as_bytes().as_ref() {
Ok(match &*key.as_bytes() {
b"active" => reuse!(active, super::Tab::make(me.active())),
b"tabs" => reuse!(tabs, super::Tabs::make(&me.mgr.tabs)),
b"tasks" => reuse!(tasks, super::Tasks::make(&me.tasks)),
b"yanked" => reuse!(yanked, super::Yanked::make(&me.mgr.yanked)),
b"layer" => {
reuse!(layer, Ok::<_, mlua::Error>(yazi_plugin::bindings::Layer::from(me.layer())))
}
b"input" => reuse!(input, super::Input::make(&me.input)),
b"which" => reuse!(which, super::Which::make(&me.which)),
b"layer" => reuse!(layer, Ok::<_, mlua::Error>(me.layer())),
_ => Value::Nil,
})
});

View file

@ -1,38 +1,35 @@
use std::ops::{Deref, Range};
use mlua::{AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods, Value};
use yazi_binding::cached_field;
use mlua::{AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods};
use yazi_shim::mlua::UserDataFieldsExt;
use super::{File, Filter, Lives, PtrCell};
pub(super) struct Files {
pub(super) struct Entries {
window: Range<usize>,
folder: PtrCell<yazi_core::tab::Folder>,
tab: PtrCell<yazi_core::tab::Tab>,
v_filter: Option<Value>,
}
impl Deref for Files {
type Target = yazi_fs::Files;
impl Deref for Entries {
type Target = yazi_fs::Entries;
fn deref(&self) -> &Self::Target { &self.folder.files }
fn deref(&self) -> &Self::Target { &self.folder.entries }
}
impl Files {
#[inline]
impl Entries {
pub(super) fn make(
window: Range<usize>,
folder: &yazi_core::tab::Folder,
tab: &yazi_core::tab::Tab,
) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self { window, folder: folder.into(), tab: tab.into(), v_filter: None })
Lives::scoped_userdata(Self { window, folder: folder.into(), tab: tab.into() })
}
}
impl UserData for Files {
impl UserData for Entries {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
cached_field!(fields, filter, |_, me| me.filter().map(Filter::make).transpose());
fields.add_static_field("filter", |_, me| me.filter().map(Filter::make).transpose());
}
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {

View file

@ -0,0 +1,157 @@
use std::{ops::Deref, ptr};
use mlua::{AnyUserData, IntoLua, UserData, UserDataFields, UserDataMethods};
use yazi_binding::{Range, style::Style};
use yazi_config::THEME;
use yazi_fs::file::FileInventory;
use yazi_shared::{path::DynPath, url::UrlLike};
use super::{FILE_CACHE, Lives};
use crate::lives::{CoreRef, PtrCell};
pub(super) struct File {
idx: usize,
folder: PtrCell<yazi_core::tab::Folder>,
tab: PtrCell<yazi_core::tab::Tab>,
}
impl Deref for File {
type Target = yazi_fs::file::File;
fn deref(&self) -> &Self::Target { &self.folder.entries[self.idx] }
}
impl AsRef<yazi_fs::file::File> for File {
fn as_ref(&self) -> &yazi_fs::file::File { self }
}
impl File {
pub(super) fn make(
idx: usize,
folder: &yazi_core::tab::Folder,
tab: &yazi_core::tab::Tab,
) -> mlua::Result<AnyUserData> {
use hashbrown::hash_map::Entry;
Ok(
match unsafe { (*FILE_CACHE.get()).assume_init_mut() }.entry(PtrCell(&folder.entries[idx])) {
Entry::Occupied(oe) => oe.into_mut().clone(),
Entry::Vacant(ve) => {
let ud = Lives::scoped_userdata(Self { idx, folder: folder.into(), tab: tab.into() })?;
ve.insert(ud.clone());
ud
}
},
)
}
#[inline]
fn is_hovered(&self) -> bool { self.idx == self.folder.cursor }
}
impl UserData for File {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
yazi_binding::impl_file_fields!(fields);
fields.add_field_method_get("idx", |_, me| Ok(me.idx + 1));
fields.add_field_method_get("is_hovered", |_, me| Ok(me.is_hovered()));
fields.add_field_method_get("in_current", |_, me| Ok(ptr::eq(&*me.folder, &me.tab.current)));
fields.add_field_method_get("in_preview", |_, me| {
Ok(me.idx == me.folder.cursor && me.tab.hovered().is_some_and(|f| f.url == me.folder.url))
});
}
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
yazi_binding::impl_file_methods!(methods);
methods.add_method("icon", |lua, me, ()| {
yazi_binding::deprecate!(
lua,
"{}: `File:icon()` is deprecated, use `th.icon:match(file)` instead"
);
// TODO: use a cache
Ok(yazi_config::THEME.icon.matches(me, me.is_hovered()))
});
methods.add_method("size", |_, me, ()| {
Ok(if me.is_dir() {
me.folder.entries.sizes.get(&me.entry_key()).copied()
} else {
Some(me.len)
})
});
methods.add_method("mime", |lua, me, ()| {
let core: CoreRef = lua.named_registry_value("cx")?;
core.mgr.mimetype.get(&me.url).map(|s| lua.create_string(s)).transpose()
});
methods.add_method("prefix", |lua, me, ()| {
if !me.url.has_trail() {
return Ok(None);
}
let mut comp = me.url.try_strip_prefix(me.url.trail()).unwrap_or(me.url.loc()).components();
comp.next_back();
Some(lua.create_string(comp.dyn_path().encoded_bytes())).transpose()
});
methods.add_method("style", |lua, me, ()| {
let core: CoreRef = lua.named_registry_value("cx")?;
let mime = core.mgr.mimetype.get(&me.url).unwrap_or_default();
Ok(THEME.filetype.match_style(me, mime).map(Style::from))
});
methods.add_method("is_yanked", |lua, me, ()| {
let core: CoreRef = lua.named_registry_value("cx")?;
Ok(if !core.mgr.yanked.contains(&me.url) {
0u8
} else if core.mgr.yanked.cut {
2u8
} else {
1u8
})
});
methods.add_method("is_marked", |_, me, ()| {
let Some(visual) = me.tab.mode.visual() else {
return Ok(0u8);
};
if !visual.contains(me.idx, me.tab.current.cursor, me.folder.entries.len()) {
return Ok(0u8);
}
if me.folder.url != me.tab.current.url {
return Ok(0u8);
}
Ok(if me.tab.mode.is_select() { 1u8 } else { 2u8 })
});
methods.add_method("is_selected", |_, me, ()| Ok(me.tab.selected.contains(&me.url)));
methods.add_method("found", |lua, me, ()| {
let core: CoreRef = lua.named_registry_value("cx")?;
let Some(finder) = &core.active().finder else {
return Ok(None);
};
let Some(idx) = finder.matched_idx(&me.folder, me.entry_key()) else {
return Ok(None);
};
lua.create_sequence_from([idx.into_lua(lua)?, finder.matched.len().into_lua(lua)?]).map(Some)
});
methods.add_method("highlights", |lua, me, ()| {
let core: CoreRef = lua.named_registry_value("cx")?;
let Some(finder) = &core.active().finder else {
return Ok(None);
};
if me.folder.url != me.tab.current.url {
return Ok(None);
}
let Some(Some(h)) = me.url.name().map(|s| finder.filter.highlighted(s)) else {
return Ok(None);
};
lua.create_sequence_from(h.into_iter().map(Range::from)).map(Some)
});
}
}
inventory::submit! {
FileInventory {
register: |_| {},
borrow: |ud, f| f(&*ud.borrow::<File>()?),
}
}

View file

@ -15,7 +15,6 @@ impl Deref for Filter {
}
impl Filter {
#[inline]
pub(super) fn make(inner: &yazi_fs::Filter) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self { inner: inner.into() })
}

View file

@ -15,7 +15,6 @@ impl Deref for Finder {
}
impl Finder {
#[inline]
pub(super) fn make(inner: &yazi_core::tab::Finder) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self { inner: inner.into() })
}

View file

@ -0,0 +1,52 @@
use std::ops::{Deref, Range};
use mlua::{AnyUserData, UserData, UserDataFields};
use yazi_config::LAYOUT;
use yazi_shim::mlua::UserDataFieldsExt;
use super::{Entries, File, Lives, PtrCell};
pub(super) struct Folder {
window: Range<usize>,
inner: PtrCell<yazi_core::tab::Folder>,
tab: PtrCell<yazi_core::tab::Tab>,
}
impl Deref for Folder {
type Target = yazi_core::tab::Folder;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl Folder {
pub(super) fn make(
window: Option<Range<usize>>,
inner: &yazi_core::tab::Folder,
tab: &yazi_core::tab::Tab,
) -> mlua::Result<AnyUserData> {
let window = match window {
Some(w) => w,
None => {
let limit = LAYOUT.get().preview.height as usize;
inner.offset..inner.entries.len().min(inner.offset + limit)
}
};
Lives::scoped_userdata(Self { window, inner: inner.into(), tab: tab.into() })
}
}
impl UserData for Folder {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_cached_field("cwd", |_, me| Ok(me.url.clone()));
fields.add_static_field("files", |_, me| Entries::make(0..me.entries.len(), me, &me.tab));
fields.add_cached_field("stage", |_, me| Ok(me.stage.clone()));
fields.add_static_field("window", |_, me| Entries::make(me.window.clone(), me, &me.tab));
fields.add_field_method_get("offset", |_, me| Ok(me.offset));
fields.add_field_method_get("cursor", |_, me| Ok(me.cursor));
fields.add_static_field("hovered", |_, me| {
me.hovered().map(|_| File::make(me.cursor, me, &me.tab)).transpose()
});
}
}

View file

@ -0,0 +1,29 @@
use std::ops::Deref;
use mlua::{AnyUserData, UserData, UserDataFields};
use yazi_shim::mlua::UserDataFieldsExt;
use super::{Lives, PtrCell};
use crate::lives::InputAlt;
pub(super) struct Input {
inner: PtrCell<yazi_core::input::Input>,
}
impl Deref for Input {
type Target = yazi_core::input::Input;
fn deref(&self) -> &Self::Target { &self.inner }
}
impl Input {
pub(super) fn make(inner: &yazi_core::input::Input) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self { inner: inner.into() })
}
}
impl UserData for Input {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_cached_field("alt", |_, me| me.alt.as_ref().map(InputAlt::make).transpose());
}
}

View file

@ -0,0 +1,26 @@
use std::ops::Deref;
use mlua::{AnyUserData, UserData, UserDataFields};
use yazi_shim::mlua::UserDataFieldsExt;
use super::{Lives, PtrCell};
pub(super) struct InputAlt(PtrCell<yazi_core::input::InputAlt>);
impl Deref for InputAlt {
type Target = yazi_core::input::InputAlt;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl InputAlt {
pub(super) fn make(inner: &yazi_core::input::InputAlt) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self(inner.into()))
}
}
impl UserData for InputAlt {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_cached_field("value", |lua, me| lua.create_string(me.lock().value()));
}
}

View file

@ -0,0 +1,51 @@
use std::mem::MaybeUninit;
use hashbrown::HashMap;
use mlua::{AnyUserData, UserData};
use scopeguard::defer;
use tracing::error;
use yazi_plugin::LUA;
use super::{Core, PtrCell};
use crate::lives::MutCell;
pub(super) static TO_DESTROY: MutCell<Vec<AnyUserData>> = MutCell::new(Vec::new());
pub(super) static FILE_CACHE: MutCell<
MaybeUninit<HashMap<PtrCell<yazi_fs::file::File>, AnyUserData>>,
> = MutCell::new(MaybeUninit::uninit());
pub struct Lives;
impl Lives {
pub fn scope<T, F>(core: &mut yazi_core::Core, f: F) -> mlua::Result<T>
where
F: FnOnce(&mut yazi_core::Core) -> mlua::Result<T>,
{
defer! {
unsafe {
(*FILE_CACHE.get()).assume_init_mut().clear();
for ud in (*TO_DESTROY.get()).drain(..) {
ud.destroy().expect("failed to destruct scoped userdata");
}
}
}
LUA.set_named_registry_value("cx", Core::make(core)?)?;
LUA.globals().raw_set("cx", Core::make(core)?)?;
let result = f(core);
if let Err(ref e) = result {
error!("{e}");
}
result
}
pub(crate) fn scoped_userdata<T>(data: T) -> mlua::Result<AnyUserData>
where
T: UserData + 'static,
{
let ud = LUA.create_userdata(data)?;
unsafe { &mut *TO_DESTROY.get() }.push(ud.clone());
Ok(ud)
}
}

View file

@ -0,0 +1,5 @@
yazi_macro::mod_flat!(behavior core entries file filter finder folder input input_alt lives mode mut_cell preference preview ptr selected tab tabs task tasks which yanked);
pub(super) fn init() {
unsafe { FILE_CACHE.get().write(std::mem::MaybeUninit::new(<_>::default())) };
}

View file

@ -1,6 +1,7 @@
use std::ops::Deref;
use mlua::{AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods};
use yazi_binding::deprecate;
use super::{Lives, PtrCell};
@ -15,7 +16,6 @@ impl Deref for Mode {
}
impl Mode {
#[inline]
pub(super) fn make(inner: &yazi_core::tab::Mode) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self { inner: inner.into() })
}
@ -23,9 +23,13 @@ impl Mode {
impl UserData for Mode {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("is_normal", |_, me| Ok(me.is_normal()));
fields.add_field_method_get("is_select", |_, me| Ok(me.is_select()));
fields.add_field_method_get("is_unset", |_, me| Ok(me.is_unset()));
fields.add_field_method_get("is_visual", |_, me| Ok(me.is_visual()));
fields.add_field_method_get("is_visual", |lua, me| {
deprecate!(lua, "{}: `mode.is_visual` is deprecated, use `not mode.is_normal` instead. \nSee #4101 for more details: https://github.com/sxyazi/yazi/pull/4101.");
Ok(!me.is_normal())
});
}
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {

View file

@ -0,0 +1,11 @@
use std::cell::UnsafeCell;
pub(super) struct MutCell<T>(UnsafeCell<T>);
unsafe impl<T> Sync for MutCell<T> {}
impl<T> MutCell<T> {
pub(super) const fn new(value: T) -> Self { Self(UnsafeCell::new(value)) }
pub(super) fn get(&self) -> *mut T { self.0.get() }
}

View file

@ -1,15 +1,12 @@
use std::ops::Deref;
use mlua::{AnyUserData, UserData, UserDataFields, Value};
use yazi_binding::cached_field;
use mlua::{AnyUserData, UserData, UserDataFields};
use yazi_shim::{mlua::UserDataFieldsExt, strum::IntoStr};
use super::{Lives, PtrCell};
pub(super) struct Preference {
inner: PtrCell<yazi_core::tab::Preference>,
v_sort_by: Option<Value>,
v_linemode: Option<Value>,
}
impl Deref for Preference {
@ -19,21 +16,24 @@ impl Deref for Preference {
}
impl Preference {
#[inline]
pub(super) fn make(inner: &yazi_core::tab::Preference) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self { inner: inner.into(), v_sort_by: None, v_linemode: None })
Lives::scoped_userdata(Self { inner: inner.into() })
}
}
impl UserData for Preference {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
cached_field!(fields, sort_by, |_, me| Ok(me.sort_by.to_string()));
// Display
fields.add_cached_field("name", |lua, me| lua.create_string(&me.name));
fields.add_cached_field("linemode", |lua, me| lua.create_string(&*me.linemode));
fields.add_field_method_get("show_hidden", |_, me| Ok(me.show_hidden));
// Sorting
fields.add_cached_field("sort_by", |_, me| Ok(me.sort_by.into_str()));
fields.add_field_method_get("sort_sensitive", |_, me| Ok(me.sort_sensitive));
fields.add_field_method_get("sort_reverse", |_, me| Ok(me.sort_reverse));
fields.add_field_method_get("sort_dir_first", |_, me| Ok(me.sort_dir_first));
fields.add_field_method_get("sort_translit", |_, me| Ok(me.sort_translit));
cached_field!(fields, linemode, |_, me| Ok(me.linemode.to_string()));
fields.add_field_method_get("show_hidden", |_, me| Ok(me.show_hidden));
fields.add_field_method_get("sort_fallback", |_, me| Ok(me.sort_fallback.into_str()));
}
}

View file

@ -1,15 +1,13 @@
use std::ops::Deref;
use mlua::{AnyUserData, UserData, UserDataFields, Value};
use yazi_binding::cached_field;
use mlua::{AnyUserData, UserData, UserDataFields};
use yazi_config::LAYOUT;
use yazi_shim::mlua::UserDataFieldsExt;
use super::{Folder, Lives, PtrCell};
pub(super) struct Preview {
tab: PtrCell<yazi_core::tab::Tab>,
v_folder: Option<Value>,
}
impl Deref for Preview {
@ -19,21 +17,20 @@ impl Deref for Preview {
}
impl Preview {
#[inline]
pub(super) fn make(tab: &yazi_core::tab::Tab) -> mlua::Result<AnyUserData> {
Lives::scoped_userdata(Self { tab: tab.into(), v_folder: None })
Lives::scoped_userdata(Self { tab: tab.into() })
}
}
impl UserData for Preview {
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
fields.add_field_method_get("skip", |_, me| Ok(me.skip));
cached_field!(fields, folder, |_, me| {
fields.add_static_field("folder", |_, me| {
me.tab
.hovered_folder()
.map(|f| {
let limit = LAYOUT.get().preview.height as usize;
Folder::make(Some(me.skip..f.files.len().min(me.skip + limit)), f, &me.tab)
Folder::make(Some(me.skip..f.entries.len().min(me.skip + limit)), f, &me.tab)
})
.transpose()
});

View file

@ -0,0 +1,17 @@
use mlua::AnyUserData;
use super::{Lives, PtrCell};
#[derive(Clone, Copy)]
pub(super) struct Selected;
impl Selected {
pub(super) fn make(inner: &yazi_core::tab::Selected) -> mlua::Result<AnyUserData> {
let inner = PtrCell::from(inner);
Lives::scoped_userdata(yazi_binding::Iter::new(
inner.as_static().files().cloned(),
Some(inner.len()),
))
}
}

Some files were not shown because too many files have changed in this diff Show more