@deepseek-harness-tui/dsh-tui 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -5
- package/assets/deepy/README.md +60 -0
- package/assets/deepy/frames.json +1 -0
- package/assets/whale-girl/whale-girl-happy.png +0 -0
- package/assets/whaleGirl/README.md +7 -0
- package/assets/whaleGirl/frames.json +1 -0
- package/assets/whaleGirl/img/building/00.png +0 -0
- package/assets/whaleGirl/img/building/01.png +0 -0
- package/assets/whaleGirl/img/building/02.png +0 -0
- package/assets/whaleGirl/img/building/03.png +0 -0
- package/assets/whaleGirl/img/building/04.png +0 -0
- package/assets/whaleGirl/img/building/05.png +0 -0
- package/assets/whaleGirl/img/building/06.png +0 -0
- package/assets/whaleGirl/img/building/07.png +0 -0
- package/assets/whaleGirl/img/building/08.png +0 -0
- package/assets/whaleGirl/img/building/09.png +0 -0
- package/assets/whaleGirl/img/building/10.png +0 -0
- package/assets/whaleGirl/img/building/11.png +0 -0
- package/assets/whaleGirl/img/building/12.png +0 -0
- package/assets/whaleGirl/img/building/13.png +0 -0
- package/assets/whaleGirl/img/building/14.png +0 -0
- package/assets/whaleGirl/img/building/15.png +0 -0
- package/assets/whaleGirl/img/building/16.png +0 -0
- package/assets/whaleGirl/img/building/17.png +0 -0
- package/assets/whaleGirl/img/building/18.png +0 -0
- package/assets/whaleGirl/img/building/19.png +0 -0
- package/assets/whaleGirl/img/building/20.png +0 -0
- package/assets/whaleGirl/img/building/21.png +0 -0
- package/assets/whaleGirl/img/building/22.png +0 -0
- package/assets/whaleGirl/img/building/23.png +0 -0
- package/assets/whaleGirl/img/carrying/00.png +0 -0
- package/assets/whaleGirl/img/carrying/01.png +0 -0
- package/assets/whaleGirl/img/carrying/02.png +0 -0
- package/assets/whaleGirl/img/carrying/03.png +0 -0
- package/assets/whaleGirl/img/carrying/04.png +0 -0
- package/assets/whaleGirl/img/carrying/05.png +0 -0
- package/assets/whaleGirl/img/carrying/06.png +0 -0
- package/assets/whaleGirl/img/carrying/07.png +0 -0
- package/assets/whaleGirl/img/compacting/00.png +0 -0
- package/assets/whaleGirl/img/compacting/01.png +0 -0
- package/assets/whaleGirl/img/compacting/02.png +0 -0
- package/assets/whaleGirl/img/compacting/03.png +0 -0
- package/assets/whaleGirl/img/compacting/04.png +0 -0
- package/assets/whaleGirl/img/compacting/05.png +0 -0
- package/assets/whaleGirl/img/compacting/06.png +0 -0
- package/assets/whaleGirl/img/compacting/07.png +0 -0
- package/assets/whaleGirl/img/compacting/08.png +0 -0
- package/assets/whaleGirl/img/compacting/09.png +0 -0
- package/assets/whaleGirl/img/conducting/00.png +0 -0
- package/assets/whaleGirl/img/conducting/01.png +0 -0
- package/assets/whaleGirl/img/conducting/02.png +0 -0
- package/assets/whaleGirl/img/conducting/03.png +0 -0
- package/assets/whaleGirl/img/conducting/04.png +0 -0
- package/assets/whaleGirl/img/conducting/05.png +0 -0
- package/assets/whaleGirl/img/conducting/06.png +0 -0
- package/assets/whaleGirl/img/conducting/07.png +0 -0
- package/assets/whaleGirl/img/conducting/08.png +0 -0
- package/assets/whaleGirl/img/conducting/09.png +0 -0
- package/assets/whaleGirl/img/conducting/10.png +0 -0
- package/assets/whaleGirl/img/conducting/11.png +0 -0
- package/assets/whaleGirl/img/drag/00.png +0 -0
- package/assets/whaleGirl/img/drag/01.png +0 -0
- package/assets/whaleGirl/img/drag/02.png +0 -0
- package/assets/whaleGirl/img/drag/03.png +0 -0
- package/assets/whaleGirl/img/drag/04.png +0 -0
- package/assets/whaleGirl/img/drag/05.png +0 -0
- package/assets/whaleGirl/img/drag/06.png +0 -0
- package/assets/whaleGirl/img/drag/07.png +0 -0
- package/assets/whaleGirl/img/drag/08.png +0 -0
- package/assets/whaleGirl/img/drag/09.png +0 -0
- package/assets/whaleGirl/img/error/00.png +0 -0
- package/assets/whaleGirl/img/error/01.png +0 -0
- package/assets/whaleGirl/img/error/02.png +0 -0
- package/assets/whaleGirl/img/error/03.png +0 -0
- package/assets/whaleGirl/img/error/04.png +0 -0
- package/assets/whaleGirl/img/error/05.png +0 -0
- package/assets/whaleGirl/img/error/06.png +0 -0
- package/assets/whaleGirl/img/error/07.png +0 -0
- package/assets/whaleGirl/img/error/08.png +0 -0
- package/assets/whaleGirl/img/error/09.png +0 -0
- package/assets/whaleGirl/img/happy/00.png +0 -0
- package/assets/whaleGirl/img/happy/01.png +0 -0
- package/assets/whaleGirl/img/happy/02.png +0 -0
- package/assets/whaleGirl/img/happy/03.png +0 -0
- package/assets/whaleGirl/img/happy/04.png +0 -0
- package/assets/whaleGirl/img/happy/05.png +0 -0
- package/assets/whaleGirl/img/happy/06.png +0 -0
- package/assets/whaleGirl/img/happy/07.png +0 -0
- package/assets/whaleGirl/img/happy/08.png +0 -0
- package/assets/whaleGirl/img/happy/09.png +0 -0
- package/assets/whaleGirl/img/happy/10.png +0 -0
- package/assets/whaleGirl/img/idle/00.png +0 -0
- package/assets/whaleGirl/img/idle/01.png +0 -0
- package/assets/whaleGirl/img/idle/02.png +0 -0
- package/assets/whaleGirl/img/idle/03.png +0 -0
- package/assets/whaleGirl/img/idle/04.png +0 -0
- package/assets/whaleGirl/img/idle/05.png +0 -0
- package/assets/whaleGirl/img/idle/06.png +0 -0
- package/assets/whaleGirl/img/idle/07.png +0 -0
- package/assets/whaleGirl/img/idle/08.png +0 -0
- package/assets/whaleGirl/img/idle/09.png +0 -0
- package/assets/whaleGirl/img/idle/10.png +0 -0
- package/assets/whaleGirl/img/idle/11.png +0 -0
- package/assets/whaleGirl/img/idle/12.png +0 -0
- package/assets/whaleGirl/img/idle/13.png +0 -0
- package/assets/whaleGirl/img/idle/14.png +0 -0
- package/assets/whaleGirl/img/idle/15.png +0 -0
- package/assets/whaleGirl/img/idle/16.png +0 -0
- package/assets/whaleGirl/img/idle/17.png +0 -0
- package/assets/whaleGirl/img/idle/18.png +0 -0
- package/assets/whaleGirl/img/idle/19.png +0 -0
- package/assets/whaleGirl/img/idle/20.png +0 -0
- package/assets/whaleGirl/img/idle-look/00.png +0 -0
- package/assets/whaleGirl/img/idle-look/01.png +0 -0
- package/assets/whaleGirl/img/idle-look/02.png +0 -0
- package/assets/whaleGirl/img/idle-look/03.png +0 -0
- package/assets/whaleGirl/img/idle-look/04.png +0 -0
- package/assets/whaleGirl/img/idle-look/05.png +0 -0
- package/assets/whaleGirl/img/idle-look/06.png +0 -0
- package/assets/whaleGirl/img/idle-look/07.png +0 -0
- package/assets/whaleGirl/img/idle-look/08.png +0 -0
- package/assets/whaleGirl/img/idle-look/09.png +0 -0
- package/assets/whaleGirl/img/idle-look/10.png +0 -0
- package/assets/whaleGirl/img/idle-look/11.png +0 -0
- package/assets/whaleGirl/img/idle-look/12.png +0 -0
- package/assets/whaleGirl/img/idle-spout/00.png +0 -0
- package/assets/whaleGirl/img/idle-spout/01.png +0 -0
- package/assets/whaleGirl/img/idle-spout/02.png +0 -0
- package/assets/whaleGirl/img/idle-spout/03.png +0 -0
- package/assets/whaleGirl/img/idle-spout/04.png +0 -0
- package/assets/whaleGirl/img/idle-spout/05.png +0 -0
- package/assets/whaleGirl/img/idle-spout/06.png +0 -0
- package/assets/whaleGirl/img/idle-spout/07.png +0 -0
- package/assets/whaleGirl/img/idle-spout/08.png +0 -0
- package/assets/whaleGirl/img/idle-spout/09.png +0 -0
- package/assets/whaleGirl/img/idle-spout/10.png +0 -0
- package/assets/whaleGirl/img/music/00.png +0 -0
- package/assets/whaleGirl/img/music/01.png +0 -0
- package/assets/whaleGirl/img/music/02.png +0 -0
- package/assets/whaleGirl/img/music/03.png +0 -0
- package/assets/whaleGirl/img/music/04.png +0 -0
- package/assets/whaleGirl/img/music/05.png +0 -0
- package/assets/whaleGirl/img/music/06.png +0 -0
- package/assets/whaleGirl/img/music/07.png +0 -0
- package/assets/whaleGirl/img/notification/00.png +0 -0
- package/assets/whaleGirl/img/notification/01.png +0 -0
- package/assets/whaleGirl/img/notification/02.png +0 -0
- package/assets/whaleGirl/img/notification/03.png +0 -0
- package/assets/whaleGirl/img/notification/04.png +0 -0
- package/assets/whaleGirl/img/notification/05.png +0 -0
- package/assets/whaleGirl/img/notification/06.png +0 -0
- package/assets/whaleGirl/img/notification/07.png +0 -0
- package/assets/whaleGirl/img/poke-left/00.png +0 -0
- package/assets/whaleGirl/img/poke-left/01.png +0 -0
- package/assets/whaleGirl/img/poke-left/02.png +0 -0
- package/assets/whaleGirl/img/poke-left/03.png +0 -0
- package/assets/whaleGirl/img/poke-left/04.png +0 -0
- package/assets/whaleGirl/img/poke-left/05.png +0 -0
- package/assets/whaleGirl/img/poke-left/06.png +0 -0
- package/assets/whaleGirl/img/poke-left/07.png +0 -0
- package/assets/whaleGirl/img/poke-left/08.png +0 -0
- package/assets/whaleGirl/img/poke-left/09.png +0 -0
- package/assets/whaleGirl/img/poke-left/10.png +0 -0
- package/assets/whaleGirl/img/poke-right/00.png +0 -0
- package/assets/whaleGirl/img/poke-right/01.png +0 -0
- package/assets/whaleGirl/img/poke-right/02.png +0 -0
- package/assets/whaleGirl/img/poke-right/03.png +0 -0
- package/assets/whaleGirl/img/poke-right/04.png +0 -0
- package/assets/whaleGirl/img/poke-right/05.png +0 -0
- package/assets/whaleGirl/img/poke-right/06.png +0 -0
- package/assets/whaleGirl/img/poke-right/07.png +0 -0
- package/assets/whaleGirl/img/poke-right/08.png +0 -0
- package/assets/whaleGirl/img/poke-right/09.png +0 -0
- package/assets/whaleGirl/img/poke-right/10.png +0 -0
- package/assets/whaleGirl/img/sleeping/00.png +0 -0
- package/assets/whaleGirl/img/sleeping/01.png +0 -0
- package/assets/whaleGirl/img/sleeping/02.png +0 -0
- package/assets/whaleGirl/img/sleeping/03.png +0 -0
- package/assets/whaleGirl/img/sleeping/04.png +0 -0
- package/assets/whaleGirl/img/sleeping/05.png +0 -0
- package/assets/whaleGirl/img/smile-hearts/00.png +0 -0
- package/assets/whaleGirl/img/smile-hearts/01.png +0 -0
- package/assets/whaleGirl/img/smile-hearts/02.png +0 -0
- package/assets/whaleGirl/img/smile-hearts/03.png +0 -0
- package/assets/whaleGirl/img/smile-hearts/04.png +0 -0
- package/assets/whaleGirl/img/smile-hearts/05.png +0 -0
- package/assets/whaleGirl/img/smile-hearts/06.png +0 -0
- package/assets/whaleGirl/img/smile-hearts/07.png +0 -0
- package/assets/whaleGirl/img/smile-hearts/08.png +0 -0
- package/assets/whaleGirl/img/smile-hearts/09.png +0 -0
- package/assets/whaleGirl/img/smile-hearts/10.png +0 -0
- package/assets/whaleGirl/img/smile-hearts/11.png +0 -0
- package/assets/whaleGirl/img/smile-hearts/12.png +0 -0
- package/assets/whaleGirl/img/smile-hearts/13.png +0 -0
- package/assets/whaleGirl/img/smile-hearts/14.png +0 -0
- package/assets/whaleGirl/img/smile-hearts/15.png +0 -0
- package/assets/whaleGirl/img/swim/00.png +0 -0
- package/assets/whaleGirl/img/swim/01.png +0 -0
- package/assets/whaleGirl/img/swim/02.png +0 -0
- package/assets/whaleGirl/img/swim/03.png +0 -0
- package/assets/whaleGirl/img/swim/04.png +0 -0
- package/assets/whaleGirl/img/swim/05.png +0 -0
- package/assets/whaleGirl/img/swim/06.png +0 -0
- package/assets/whaleGirl/img/swim/07.png +0 -0
- package/assets/whaleGirl/img/thinking/00.png +0 -0
- package/assets/whaleGirl/img/thinking/01.png +0 -0
- package/assets/whaleGirl/img/thinking/02.png +0 -0
- package/assets/whaleGirl/img/thinking/03.png +0 -0
- package/assets/whaleGirl/img/thinking/04.png +0 -0
- package/assets/whaleGirl/img/thinking/05.png +0 -0
- package/assets/whaleGirl/img/thinking/06.png +0 -0
- package/assets/whaleGirl/img/thinking/07.png +0 -0
- package/assets/whaleGirl/img/thinking/08.png +0 -0
- package/assets/whaleGirl/img/thinking/09.png +0 -0
- package/assets/whaleGirl/img/thinking/10.png +0 -0
- package/assets/whaleGirl/img/thinking/11.png +0 -0
- package/assets/whaleGirl/img/thinking/12.png +0 -0
- package/assets/whaleGirl/img/thinking/13.png +0 -0
- package/assets/whaleGirl/img/thinking/14.png +0 -0
- package/assets/whaleGirl/img/thinking/15.png +0 -0
- package/assets/whaleGirl/img/thinking/16.png +0 -0
- package/assets/whaleGirl/img/thumbs-up/00.png +0 -0
- package/assets/whaleGirl/img/thumbs-up/01.png +0 -0
- package/assets/whaleGirl/img/thumbs-up/02.png +0 -0
- package/assets/whaleGirl/img/thumbs-up/03.png +0 -0
- package/assets/whaleGirl/img/thumbs-up/04.png +0 -0
- package/assets/whaleGirl/img/thumbs-up/05.png +0 -0
- package/assets/whaleGirl/img/thumbs-up/06.png +0 -0
- package/assets/whaleGirl/img/thumbs-up/07.png +0 -0
- package/assets/whaleGirl/img/thumbs-up/08.png +0 -0
- package/assets/whaleGirl/img/thumbs-up/09.png +0 -0
- package/assets/whaleGirl/img/thumbs-up/10.png +0 -0
- package/assets/whaleGirl/img/thumbs-up/11.png +0 -0
- package/assets/whaleGirl/img/thumbs-up/12.png +0 -0
- package/assets/whaleGirl/img/tickle/00.png +0 -0
- package/assets/whaleGirl/img/tickle/01.png +0 -0
- package/assets/whaleGirl/img/tickle/02.png +0 -0
- package/assets/whaleGirl/img/tickle/03.png +0 -0
- package/assets/whaleGirl/img/tickle/04.png +0 -0
- package/assets/whaleGirl/img/tickle/05.png +0 -0
- package/assets/whaleGirl/img/tickle/06.png +0 -0
- package/assets/whaleGirl/img/tickle/07.png +0 -0
- package/assets/whaleGirl/img/tickle/08.png +0 -0
- package/assets/whaleGirl/img/tickle/09.png +0 -0
- package/assets/whaleGirl/img/tickle/10.png +0 -0
- package/assets/whaleGirl/img/tickle/11.png +0 -0
- package/assets/whaleGirl/img/tickle/12.png +0 -0
- package/assets/whaleGirl/img/tickle/13.png +0 -0
- package/assets/whaleGirl/img/timings.json +1 -0
- package/assets/whaleGirl/img/typing/00.png +0 -0
- package/assets/whaleGirl/img/typing/01.png +0 -0
- package/assets/whaleGirl/img/typing/02.png +0 -0
- package/assets/whaleGirl/img/typing/03.png +0 -0
- package/assets/whaleGirl/img/typing/04.png +0 -0
- package/assets/whaleGirl/img/typing/05.png +0 -0
- package/assets/whaleGirl/img/typing/06.png +0 -0
- package/assets/whaleGirl/img/typing/07.png +0 -0
- package/assets/whaleGirl/img/typing/08.png +0 -0
- package/assets/whaleGirl/img/typing/09.png +0 -0
- package/assets/whaleGirl/img/typing/10.png +0 -0
- package/assets/whaleGirl/img/typing/11.png +0 -0
- package/assets/whaleGirl/img/typing/12.png +0 -0
- package/assets/whaleGirl/img/typing/13.png +0 -0
- package/assets/whaleGirl/img/waking/00.png +0 -0
- package/assets/whaleGirl/img/waking/01.png +0 -0
- package/assets/whaleGirl/img/waking/02.png +0 -0
- package/assets/whaleGirl/img/waking/03.png +0 -0
- package/assets/whaleGirl/img/waking/04.png +0 -0
- package/assets/whaleGirl/img/waking/05.png +0 -0
- package/assets/whaleGirl/img/waking/06.png +0 -0
- package/assets/whaleGirl/img/waking/07.png +0 -0
- package/assets/whaleGirl/img/waking/08.png +0 -0
- package/assets/whaleGirl/img/waking/09.png +0 -0
- package/assets/whaleGirl/img/waking/10.png +0 -0
- package/bin/dsh-tui.js +8 -3
- package/cordis.patch.yml +7 -1
- package/guide/dsh-tui-guide/configuration.en.md +6 -1
- package/guide/dsh-tui-guide/configuration.md +6 -1
- package/guide/dsh-tui-guide/getting-started.en.md +6 -3
- package/guide/dsh-tui-guide/getting-started.md +4 -2
- package/guide/dsh-tui-guide/interaction.en.md +68 -4
- package/guide/dsh-tui-guide/interaction.md +49 -3
- package/guide/dsh-tui-guide/plugins.en.md +16 -6
- package/guide/dsh-tui-guide/plugins.md +14 -6
- package/guide/dsh-tui-guide/themes.en.md +7 -2
- package/guide/dsh-tui-guide/themes.md +5 -2
- package/guide/dsh-tui-guide/user-guide.en.md +124 -21
- package/guide/dsh-tui-guide/user-guide.md +96 -16
- package/lib/settings.json +215 -1
- package/lib/types/adapter/channel/ui-policy.d.ts +6 -2
- package/lib/types/adapter/channel/ui-policy.d.ts.map +1 -1
- package/lib/types/adapter/channel/ui-policy.js +14 -1
- package/lib/types/adapter/index.d.ts +1 -1
- package/lib/types/adapter/index.js +1 -1
- package/lib/types/adapter/kernel/host-facade.d.ts +4 -1
- package/lib/types/adapter/kernel/host-facade.d.ts.map +1 -1
- package/lib/types/adapter/kernel/host-facade.js +8 -1
- package/lib/types/adapter/kernel/runtime.d.ts.map +1 -1
- package/lib/types/adapter/kernel/runtime.js +13 -0
- package/lib/types/adapter/kernel/slices/index.d.ts +1 -0
- package/lib/types/adapter/kernel/slices/index.d.ts.map +1 -1
- package/lib/types/adapter/kernel/slices/index.js +3 -0
- package/lib/types/adapter/kernel/slices/panels.d.ts +3 -0
- package/lib/types/adapter/kernel/slices/panels.d.ts.map +1 -0
- package/lib/types/adapter/kernel/slices/panels.js +14 -0
- package/lib/types/adapter/ports/channel-capabilities.d.ts +48 -0
- package/lib/types/adapter/ports/channel-capabilities.d.ts.map +1 -0
- package/lib/types/adapter/ports/channel-capabilities.js +2 -0
- package/lib/types/adapter/ports/channel-catalog.d.ts +7 -0
- package/lib/types/adapter/ports/channel-catalog.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel-display.d.ts +10 -0
- package/lib/types/adapter/ports/channel-display.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel-ui.d.ts +62 -2
- package/lib/types/adapter/ports/channel-ui.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel-view.d.ts +94 -0
- package/lib/types/adapter/ports/channel-view.d.ts.map +1 -1
- package/lib/types/adapter/ports/channel.d.ts +1 -1
- package/lib/types/adapter/ports/channel.js +1 -1
- package/lib/types/adapter/ports/descriptor.d.ts +1 -1
- package/lib/types/adapter/ports/descriptor.js +1 -1
- package/lib/types/adapter/ports/index.d.ts +1 -1
- package/lib/types/adapter/ports/index.js +1 -1
- package/lib/types/adapter/ports/panels.d.ts +28 -0
- package/lib/types/adapter/ports/panels.d.ts.map +1 -0
- package/lib/types/adapter/ports/panels.js +8 -0
- package/lib/types/adapter/ports/workspace.d.ts +1 -1
- package/lib/types/adapter/ports/workspace.js +1 -1
- package/lib/types/adapter/spec/index.d.ts +2 -2
- package/lib/types/adapter/spec/index.js +2 -2
- package/lib/types/adapter/spec/profile.d.ts +6 -6
- package/lib/types/adapter/spec/profile.d.ts.map +1 -1
- package/lib/types/adapter/spec/profile.js +6 -6
- package/lib/types/adapter/spec/protocol-constants.d.ts +3 -3
- package/lib/types/adapter/spec/protocol-constants.d.ts.map +1 -1
- package/lib/types/adapter/spec/protocol-constants.js +5 -5
- package/lib/types/adapter/spec/tui-contributions.d.ts +4 -4
- package/lib/types/adapter/spec/tui-contributions.d.ts.map +1 -1
- package/lib/types/adapter/spec/tui-contributions.js +5 -5
- package/lib/types/adapter/standard/descriptor.js +1 -1
- package/lib/types/adapter/standard/index.d.ts +3 -3
- package/lib/types/adapter/standard/index.js +3 -3
- package/lib/types/adapter/standard/registry.d.ts +3 -2
- package/lib/types/adapter/standard/registry.d.ts.map +1 -1
- package/lib/types/adapter/standard/registry.js +6 -5
- package/lib/types/adapter/standard/tui-extension.d.ts +1 -1
- package/lib/types/adapter/standard/tui-extension.js +1 -1
- package/lib/types/adapter/upstream/index.d.ts +1 -0
- package/lib/types/adapter/upstream/index.d.ts.map +1 -1
- package/lib/types/adapter/upstream/index.js +1 -0
- package/lib/types/adapter/upstream/panels-driver.d.ts +18 -0
- package/lib/types/adapter/upstream/panels-driver.d.ts.map +1 -0
- package/lib/types/adapter/upstream/panels-driver.js +159 -0
- package/lib/types/api.d.ts +2 -0
- package/lib/types/api.d.ts.map +1 -1
- package/lib/types/api.js +3 -1
- package/lib/types/commands.d.ts.map +1 -1
- package/lib/types/commands.js +12 -4
- package/lib/types/components/ActivityLine.d.ts +6 -9
- package/lib/types/components/ActivityLine.d.ts.map +1 -1
- package/lib/types/components/ActivityLine.js +7 -8
- package/lib/types/components/Chat/JobCard.d.ts +18 -1
- package/lib/types/components/Chat/JobCard.d.ts.map +1 -1
- package/lib/types/components/Chat/JobCard.js +90 -22
- package/lib/types/components/Chat/JobGroupHeader.d.ts +27 -0
- package/lib/types/components/Chat/JobGroupHeader.d.ts.map +1 -0
- package/lib/types/components/Chat/JobGroupHeader.js +62 -0
- package/lib/types/components/GoalTodoPanel.d.ts +13 -1
- package/lib/types/components/GoalTodoPanel.d.ts.map +1 -1
- package/lib/types/components/GoalTodoPanel.js +55 -14
- package/lib/types/components/JobsPanel.d.ts +42 -2
- package/lib/types/components/JobsPanel.d.ts.map +1 -1
- package/lib/types/components/JobsPanel.js +147 -14
- package/lib/types/components/LogoV2.d.ts +43 -3
- package/lib/types/components/LogoV2.d.ts.map +1 -1
- package/lib/types/components/LogoV2.js +42 -15
- package/lib/types/components/MessageList.d.ts +8 -2
- package/lib/types/components/MessageList.d.ts.map +1 -1
- package/lib/types/components/MessageList.js +212 -18
- package/lib/types/components/OverlayAbove.d.ts +11 -1
- package/lib/types/components/OverlayAbove.d.ts.map +1 -1
- package/lib/types/components/OverlayAbove.js +2 -12
- package/lib/types/components/PageMargin.d.ts.map +1 -1
- package/lib/types/components/PageMargin.js +3 -1
- package/lib/types/components/PluginSceneBoundary.d.ts +1 -1
- package/lib/types/components/PluginSceneBoundary.js +1 -1
- package/lib/types/components/PromptInput.d.ts +20 -22
- package/lib/types/components/PromptInput.d.ts.map +1 -1
- package/lib/types/components/PromptInput.js +413 -156
- package/lib/types/components/SearchBox.d.ts +22 -5
- package/lib/types/components/SearchBox.d.ts.map +1 -1
- package/lib/types/components/SearchBox.js +17 -13
- package/lib/types/components/SubagentCard.d.ts.map +1 -1
- package/lib/types/components/SubagentCard.js +17 -2
- package/lib/types/components/SubagentDashboard.d.ts +18 -3
- package/lib/types/components/SubagentDashboard.d.ts.map +1 -1
- package/lib/types/components/SubagentDashboard.js +53 -5
- package/lib/types/components/SubagentDetailScene.d.ts +7 -1
- package/lib/types/components/SubagentDetailScene.d.ts.map +1 -1
- package/lib/types/components/SubagentDetailScene.js +54 -5
- package/lib/types/components/SurfaceEdges.d.ts +21 -0
- package/lib/types/components/SurfaceEdges.d.ts.map +1 -0
- package/lib/types/components/SurfaceEdges.js +7 -0
- package/lib/types/components/ThemePicker.d.ts +9 -0
- package/lib/types/components/ThemePicker.d.ts.map +1 -1
- package/lib/types/components/ThemePicker.js +74 -10
- package/lib/types/components/ThemePreviewPane.d.ts +26 -0
- package/lib/types/components/ThemePreviewPane.d.ts.map +1 -0
- package/lib/types/components/ThemePreviewPane.js +99 -0
- package/lib/types/components/design-system/Divider.d.ts.map +1 -1
- package/lib/types/components/design-system/Divider.js +14 -9
- package/lib/types/components/launchpadActions.d.ts +73 -0
- package/lib/types/components/launchpadActions.d.ts.map +1 -0
- package/lib/types/components/launchpadActions.js +111 -0
- package/lib/types/components/launchpadLayout.d.ts +62 -0
- package/lib/types/components/launchpadLayout.d.ts.map +1 -0
- package/lib/types/components/launchpadLayout.js +176 -0
- package/lib/types/components/onboardingModel.d.ts +83 -0
- package/lib/types/components/onboardingModel.d.ts.map +1 -0
- package/lib/types/components/onboardingModel.js +128 -0
- package/lib/types/components/sidePanel/InfoPanel.d.ts +17 -0
- package/lib/types/components/sidePanel/InfoPanel.d.ts.map +1 -0
- package/lib/types/components/sidePanel/InfoPanel.js +124 -0
- package/lib/types/components/sidePanel/PanelBar.d.ts +45 -0
- package/lib/types/components/sidePanel/PanelBar.d.ts.map +1 -0
- package/lib/types/components/sidePanel/PanelBar.js +104 -0
- package/lib/types/components/sidePanel/PanelErrorBoundary.d.ts +21 -0
- package/lib/types/components/sidePanel/PanelErrorBoundary.d.ts.map +1 -0
- package/lib/types/components/sidePanel/PanelErrorBoundary.js +30 -0
- package/lib/types/components/sidePanel/PanelHost.d.ts +36 -0
- package/lib/types/components/sidePanel/PanelHost.d.ts.map +1 -0
- package/lib/types/components/sidePanel/PanelHost.js +81 -0
- package/lib/types/components/sidePanel/PanelPicker.d.ts +24 -0
- package/lib/types/components/sidePanel/PanelPicker.d.ts.map +1 -0
- package/lib/types/components/sidePanel/PanelPicker.js +45 -0
- package/lib/types/components/sidePanel/PanelStore.d.ts +55 -0
- package/lib/types/components/sidePanel/PanelStore.d.ts.map +1 -0
- package/lib/types/components/sidePanel/PanelStore.js +109 -0
- package/lib/types/components/sidePanel/SidePanelColumn.d.ts +31 -0
- package/lib/types/components/sidePanel/SidePanelColumn.d.ts.map +1 -0
- package/lib/types/components/sidePanel/SidePanelColumn.js +70 -0
- package/lib/types/components/sidePanel/SidePanelLayout.d.ts +54 -0
- package/lib/types/components/sidePanel/SidePanelLayout.d.ts.map +1 -0
- package/lib/types/components/sidePanel/SidePanelLayout.js +81 -0
- package/lib/types/components/sidePanel/SidePanelRuntimeContext.d.ts +43 -0
- package/lib/types/components/sidePanel/SidePanelRuntimeContext.d.ts.map +1 -0
- package/lib/types/components/sidePanel/SidePanelRuntimeContext.js +26 -0
- package/lib/types/components/sidePanel/TrajectoryPanel.d.ts +7 -0
- package/lib/types/components/sidePanel/TrajectoryPanel.d.ts.map +1 -0
- package/lib/types/components/sidePanel/TrajectoryPanel.js +263 -0
- package/lib/types/components/sidePanel/WorkspacePanel.d.ts +4 -0
- package/lib/types/components/sidePanel/WorkspacePanel.d.ts.map +1 -0
- package/lib/types/components/sidePanel/WorkspacePanel.js +171 -0
- package/lib/types/components/sidePanel/builtinPanels.d.ts +3 -0
- package/lib/types/components/sidePanel/builtinPanels.d.ts.map +1 -0
- package/lib/types/components/sidePanel/builtinPanels.js +229 -0
- package/lib/types/components/sidePanel/companion/CompanionPanel.d.ts +53 -0
- package/lib/types/components/sidePanel/companion/CompanionPanel.d.ts.map +1 -0
- package/lib/types/components/sidePanel/companion/CompanionPanel.js +528 -0
- package/lib/types/components/sidePanel/companion/SplashMascot.d.ts +32 -0
- package/lib/types/components/sidePanel/companion/SplashMascot.d.ts.map +1 -0
- package/lib/types/components/sidePanel/companion/SplashMascot.js +103 -0
- package/lib/types/components/sidePanel/companion/deepy.d.ts +75 -0
- package/lib/types/components/sidePanel/companion/deepy.d.ts.map +1 -0
- package/lib/types/components/sidePanel/companion/deepy.js +222 -0
- package/lib/types/components/sidePanel/companion/mood.d.ts +176 -0
- package/lib/types/components/sidePanel/companion/mood.d.ts.map +1 -0
- package/lib/types/components/sidePanel/companion/mood.js +212 -0
- package/lib/types/components/sidePanel/companion/pose.d.ts +43 -0
- package/lib/types/components/sidePanel/companion/pose.d.ts.map +1 -0
- package/lib/types/components/sidePanel/companion/pose.js +34 -0
- package/lib/types/components/sidePanel/companion/skins.d.ts +102 -0
- package/lib/types/components/sidePanel/companion/skins.d.ts.map +1 -0
- package/lib/types/components/sidePanel/companion/skins.js +408 -0
- package/lib/types/components/sidePanel/dimensions.d.ts +60 -0
- package/lib/types/components/sidePanel/dimensions.d.ts.map +1 -0
- package/lib/types/components/sidePanel/dimensions.js +75 -0
- package/lib/types/components/sidePanel/jobsFocusStore.d.ts +21 -0
- package/lib/types/components/sidePanel/jobsFocusStore.d.ts.map +1 -0
- package/lib/types/components/sidePanel/jobsFocusStore.js +30 -0
- package/lib/types/components/sidePanel/panelBridge.d.ts +44 -0
- package/lib/types/components/sidePanel/panelBridge.d.ts.map +1 -0
- package/lib/types/components/sidePanel/panelBridge.js +55 -0
- package/lib/types/components/sidePanel/pluginPanelAdapter.d.ts +22 -0
- package/lib/types/components/sidePanel/pluginPanelAdapter.d.ts.map +1 -0
- package/lib/types/components/sidePanel/pluginPanelAdapter.js +148 -0
- package/lib/types/components/sidePanel/types.d.ts +110 -0
- package/lib/types/components/sidePanel/types.d.ts.map +1 -0
- package/lib/types/components/sidePanel/types.js +1 -0
- package/lib/types/components/sidePanel/usePanelInput.d.ts +5 -0
- package/lib/types/components/sidePanel/usePanelInput.d.ts.map +1 -0
- package/lib/types/components/sidePanel/usePanelInput.js +44 -0
- package/lib/types/components/sidePanel/useSidePanel.d.ts +47 -0
- package/lib/types/components/sidePanel/useSidePanel.d.ts.map +1 -0
- package/lib/types/components/sidePanel/useSidePanel.js +284 -0
- package/lib/types/components/workspaces/HomeWorkspaceRow.d.ts +3 -1
- package/lib/types/components/workspaces/HomeWorkspaceRow.d.ts.map +1 -1
- package/lib/types/components/workspaces/HomeWorkspaceRow.js +3 -2
- package/lib/types/deepseekPricing.d.ts +10 -9
- package/lib/types/deepseekPricing.d.ts.map +1 -1
- package/lib/types/deepseekPricing.js +18 -11
- package/lib/types/dsh-adapter/channel/attached-context.d.ts +58 -0
- package/lib/types/dsh-adapter/channel/attached-context.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/attached-context.js +109 -0
- package/lib/types/dsh-adapter/channel/binding-events.d.ts +6 -0
- package/lib/types/dsh-adapter/channel/binding-events.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/binding-events.js +43 -0
- package/lib/types/dsh-adapter/channel/capabilities.d.ts +82 -0
- package/lib/types/dsh-adapter/channel/capabilities.d.ts.map +1 -0
- package/lib/types/dsh-adapter/channel/capabilities.js +165 -0
- package/lib/types/dsh-adapter/channel/command-completions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/command-completions.js +8 -0
- package/lib/types/dsh-adapter/channel/context-bookkeeping.d.ts +4 -12
- package/lib/types/dsh-adapter/channel/context-bookkeeping.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/context-bookkeeping.js +9 -4
- package/lib/types/dsh-adapter/channel/input-delivery.d.ts +13 -2
- package/lib/types/dsh-adapter/channel/input-delivery.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/input-delivery.js +18 -1
- package/lib/types/dsh-adapter/channel/mode-actions.d.ts +0 -3
- package/lib/types/dsh-adapter/channel/mode-actions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/mode-actions.js +7 -2
- package/lib/types/dsh-adapter/channel/mode-permission-actions.d.ts +8 -1
- package/lib/types/dsh-adapter/channel/mode-permission-actions.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/mode-permission-actions.js +0 -1
- package/lib/types/dsh-adapter/channel/preferences.d.ts +1 -1
- package/lib/types/dsh-adapter/channel/preferences.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/preferences.js +9 -1
- package/lib/types/dsh-adapter/channel/projection.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/projection.js +42 -37
- package/lib/types/dsh-adapter/channel/reports.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/reports.js +13 -4
- package/lib/types/dsh-adapter/channel/session-reset.d.ts +5 -1
- package/lib/types/dsh-adapter/channel/session-reset.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/session-reset.js +1 -0
- package/lib/types/dsh-adapter/channel/state.d.ts +18 -2
- package/lib/types/dsh-adapter/channel/state.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/state.js +2 -1
- package/lib/types/dsh-adapter/channel/types.d.ts +1 -1
- package/lib/types/dsh-adapter/channel/types.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/usage.d.ts +18 -1
- package/lib/types/dsh-adapter/channel/usage.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel/usage.js +97 -2
- package/lib/types/dsh-adapter/channel.d.ts +1 -1
- package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
- package/lib/types/dsh-adapter/channel.js +56 -3
- package/lib/types/dsh-adapter/context-occupancy.d.ts +204 -0
- package/lib/types/dsh-adapter/context-occupancy.d.ts.map +1 -0
- package/lib/types/dsh-adapter/context-occupancy.js +299 -0
- package/lib/types/dsh-adapter/contract.d.ts +14 -0
- package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
- package/lib/types/dsh-adapter/contract.js +24 -0
- package/lib/types/dsh-adapter/index.d.ts +32 -0
- package/lib/types/dsh-adapter/index.d.ts.map +1 -1
- package/lib/types/dsh-adapter/index.js +20 -1
- package/lib/types/dsh-adapter/migrate/sessionize.d.ts.map +1 -1
- package/lib/types/dsh-adapter/migrate/sessionize.js +11 -12
- package/lib/types/dsh-adapter/panel-runtime-registry.d.ts +39 -0
- package/lib/types/dsh-adapter/panel-runtime-registry.d.ts.map +1 -0
- package/lib/types/dsh-adapter/panel-runtime-registry.js +84 -0
- package/lib/types/dsh-adapter/panels.d.ts +251 -0
- package/lib/types/dsh-adapter/panels.d.ts.map +1 -0
- package/lib/types/dsh-adapter/panels.js +407 -0
- package/lib/types/dsh-adapter/plugin-host.js +1 -1
- package/lib/types/dsh-adapter/plugin.d.ts +17 -1
- package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
- package/lib/types/dsh-adapter/plugin.js +169 -32
- package/lib/types/dsh-adapter/presets.d.ts +26 -6
- package/lib/types/dsh-adapter/presets.d.ts.map +1 -1
- package/lib/types/dsh-adapter/presets.js +29 -7
- package/lib/types/dsh-adapter/startup-args.d.ts +8 -0
- package/lib/types/dsh-adapter/startup-args.d.ts.map +1 -0
- package/lib/types/dsh-adapter/startup-args.js +34 -0
- package/lib/types/history.d.ts +24 -7
- package/lib/types/history.d.ts.map +1 -1
- package/lib/types/history.js +77 -18
- package/lib/types/i18n.d.ts +734 -15
- package/lib/types/i18n.d.ts.map +1 -1
- package/lib/types/i18n.js +302 -6
- package/lib/types/ink/components/App.d.ts +14 -1
- package/lib/types/ink/components/App.d.ts.map +1 -1
- package/lib/types/ink/components/App.js +62 -61
- package/lib/types/ink/components/TerminalSizeContext.d.ts +7 -0
- package/lib/types/ink/components/TerminalSizeContext.d.ts.map +1 -1
- package/lib/types/ink/hooks/use-input.d.ts +11 -0
- package/lib/types/ink/hooks/use-input.d.ts.map +1 -1
- package/lib/types/ink/hooks/use-input.js +8 -2
- package/lib/types/ink/hooks/use-terminal-viewport.d.ts.map +1 -1
- package/lib/types/ink/hooks/use-terminal-viewport.js +3 -1
- package/lib/types/ink/ink.d.ts +18 -0
- package/lib/types/ink/ink.d.ts.map +1 -1
- package/lib/types/ink/ink.js +51 -8
- package/lib/types/ink/output.d.ts +10 -1
- package/lib/types/ink/output.d.ts.map +1 -1
- package/lib/types/ink/output.js +3 -3
- package/lib/types/ink/parse-keypress.d.ts +36 -3
- package/lib/types/ink/parse-keypress.d.ts.map +1 -1
- package/lib/types/ink/parse-keypress.js +262 -108
- package/lib/types/ink/render-node-to-output.d.ts +1 -1
- package/lib/types/ink/render-node-to-output.d.ts.map +1 -1
- package/lib/types/ink/render-node-to-output.js +37 -5
- package/lib/types/ink/screen.d.ts +26 -1
- package/lib/types/ink/screen.d.ts.map +1 -1
- package/lib/types/ink/screen.js +96 -1
- package/lib/types/ink/selection.d.ts +23 -1
- package/lib/types/ink/selection.d.ts.map +1 -1
- package/lib/types/ink/selection.js +102 -26
- package/lib/types/ink/sixel-codec.d.ts +11 -0
- package/lib/types/ink/sixel-codec.d.ts.map +1 -1
- package/lib/types/ink/sixel-codec.js +42 -8
- package/lib/types/math/renderer.d.ts +2 -0
- package/lib/types/math/renderer.d.ts.map +1 -1
- package/lib/types/math/renderer.js +4 -0
- package/lib/types/onboardingPrefs.d.ts +52 -0
- package/lib/types/onboardingPrefs.d.ts.map +1 -0
- package/lib/types/onboardingPrefs.js +107 -0
- package/lib/types/panels.d.ts +4 -0
- package/lib/types/panels.d.ts.map +1 -0
- package/lib/types/panels.js +4 -0
- package/lib/types/screens/Chat.d.ts +22 -1
- package/lib/types/screens/Chat.d.ts.map +1 -1
- package/lib/types/screens/Chat.js +1126 -299
- package/lib/types/screens/Launchpad.d.ts +194 -0
- package/lib/types/screens/Launchpad.d.ts.map +1 -0
- package/lib/types/screens/Launchpad.js +607 -0
- package/lib/types/screens/Onboarding.d.ts +37 -0
- package/lib/types/screens/Onboarding.d.ts.map +1 -0
- package/lib/types/screens/Onboarding.js +492 -0
- package/lib/types/screens/SessionSupervisor.d.ts.map +1 -1
- package/lib/types/screens/SessionSupervisor.js +18 -12
- package/lib/types/screens/StatusLine.d.ts.map +1 -1
- package/lib/types/screens/StatusLine.js +32 -14
- package/lib/types/screens/StatusMetrics.d.ts +22 -0
- package/lib/types/screens/StatusMetrics.d.ts.map +1 -1
- package/lib/types/screens/StatusMetrics.js +20 -0
- package/lib/types/screens/chatOverlay.d.ts +18 -1
- package/lib/types/screens/chatOverlay.d.ts.map +1 -1
- package/lib/types/screens/chatOverlay.js +3 -0
- package/lib/types/screens/sessionSupervisor/model.d.ts +1 -2
- package/lib/types/screens/sessionSupervisor/model.d.ts.map +1 -1
- package/lib/types/screens/sessionSupervisor/model.js +4 -2
- package/lib/types/screens/sessionSupervisor/useSessionSupervisor.d.ts.map +1 -1
- package/lib/types/screens/sessionSupervisor/useSessionSupervisor.js +7 -3
- package/lib/types/settings/definitions.d.ts +93 -0
- package/lib/types/settings/definitions.d.ts.map +1 -1
- package/lib/types/settings/definitions.js +83 -0
- package/lib/types/tips.js +5 -5
- package/lib/types/tuiDisplayPrefs.d.ts +42 -2
- package/lib/types/tuiDisplayPrefs.d.ts.map +1 -1
- package/lib/types/tuiDisplayPrefs.js +74 -0
- package/lib/types/update.d.ts +53 -9
- package/lib/types/update.d.ts.map +1 -1
- package/lib/types/update.js +101 -44
- package/lib/types/utils/bmp.d.ts.map +1 -1
- package/lib/types/utils/bmp.js +8 -1
- package/lib/types/utils/clipboard.d.ts.map +1 -1
- package/lib/types/utils/clipboard.js +25 -6
- package/lib/types/utils/credentials.d.ts.map +1 -1
- package/lib/types/utils/credentials.js +5 -1
- package/lib/types/utils/draftWordBoundary.d.ts +23 -0
- package/lib/types/utils/draftWordBoundary.d.ts.map +1 -0
- package/lib/types/utils/draftWordBoundary.js +161 -0
- package/lib/types/utils/fileTarget.d.ts.map +1 -1
- package/lib/types/utils/fileTarget.js +7 -9
- package/lib/types/utils/inputPaste.d.ts +62 -0
- package/lib/types/utils/inputPaste.d.ts.map +1 -0
- package/lib/types/utils/inputPaste.js +111 -0
- package/lib/types/utils/intl.d.ts +8 -0
- package/lib/types/utils/intl.d.ts.map +1 -1
- package/lib/types/utils/intl.js +11 -0
- package/lib/types/utils/keymap.d.ts +13 -3
- package/lib/types/utils/keymap.d.ts.map +1 -1
- package/lib/types/utils/keymap.js +27 -5
- package/package.json +19 -8
- package/{dsh-ecosystem-spec → tui-profile}/registry/README.md +1 -1
- package/{dsh-ecosystem-spec → tui-profile}/registry/registry-0.15.json +1 -1
- /package/{dsh-ecosystem-spec → tui-profile}/protocols/profile-definitions.d.ts +0 -0
- /package/{dsh-ecosystem-spec → tui-profile}/protocols/profile-definitions.js +0 -0
- /package/{dsh-ecosystem-spec → tui-profile}/protocols/tui-channel-http.d.ts +0 -0
- /package/{dsh-ecosystem-spec → tui-profile}/protocols/tui-channel-http.js +0 -0
- /package/{dsh-ecosystem-spec → tui-profile}/protocols/tui-channel.d.ts +0 -0
- /package/{dsh-ecosystem-spec → tui-profile}/protocols/tui-channel.js +0 -0
- /package/{dsh-ecosystem-spec → tui-profile}/protocols/tui-contributions.d.ts +0 -0
- /package/{dsh-ecosystem-spec → tui-profile}/protocols/tui-contributions.js +0 -0
- /package/{dsh-ecosystem-spec → tui-profile}/registry/contracts/decision-events-v1alpha1.json +0 -0
- /package/{dsh-ecosystem-spec → tui-profile}/registry/contracts/scene-v1alpha1.json +0 -0
- /package/{dsh-ecosystem-spec → tui-profile}/registry/contracts/settings-section-v1alpha1.json +0 -0
- /package/{dsh-ecosystem-spec → tui-profile}/registry/contracts/tui-channel-v1alpha1.json +0 -0
- /package/{dsh-ecosystem-spec → tui-profile}/registry/contracts/workspace-provider-v1alpha1.json +0 -0
- /package/{dsh-ecosystem-spec → tui-profile}/registry/host-descriptor.tui.example.json +0 -0
- /package/{dsh-ecosystem-spec → tui-profile}/registry/permissions-0.1.json +0 -0
- /package/{dsh-ecosystem-spec → tui-profile}/schemas/conformance-claim.schema.json +0 -0
- /package/{dsh-ecosystem-spec → tui-profile}/schemas/effect-ledger-record.schema.json +0 -0
- /package/{dsh-ecosystem-spec → tui-profile}/schemas/host-descriptor.schema.json +0 -0
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import React from 'react';
|
|
3
|
-
|
|
3
|
+
// 粘贴/清洗语义已抽到 utils/inputPaste.ts(方案 B:PromptInput 与 Launchpad 共享;
|
|
4
|
+
// 行为逐字节不变,只是搬了家)。
|
|
5
|
+
import { sanitizeEditableText, sanitizePastedText } from '../utils/inputPaste.js';
|
|
4
6
|
import { constants as fsConstants } from 'node:fs';
|
|
5
7
|
import { open, unlink } from 'node:fs/promises';
|
|
6
8
|
import { basename, resolve } from 'node:path';
|
|
@@ -19,12 +21,13 @@ import instances from '../ink/instances.js';
|
|
|
19
21
|
import { stringWidth } from '../ink/stringWidth.js';
|
|
20
22
|
import { truncateToWidth } from '../ink/truncateToWidth.js';
|
|
21
23
|
import { getGraphemeSegmenter } from '../utils/intl.js';
|
|
24
|
+
import { draftWordRangeAt, isDraftWordBoundary } from '../utils/draftWordBoundary.js';
|
|
22
25
|
import { formatClipboardInsert, readClipboard } from '../utils/clipboard.js';
|
|
23
26
|
import { imagePathMediaType, parsePastedImagePath, stageClipboardFilePaths } from '../utils/pastedImagePath.js';
|
|
24
27
|
import { editInExternalEditor } from '../utils/externalEditor.js';
|
|
25
28
|
import { setPromptEditorNode, EditorButton } from './PromptEditor.js';
|
|
26
29
|
import { isHiddenCommandName, parseCommandName } from '../commands.js';
|
|
27
|
-
import { appendHistory, HISTORY_LIMIT, loadHistoryOldestFirst } from '../history.js';
|
|
30
|
+
import { appendHistory, HISTORY_LIMIT, historyProjectKey, loadHistoryOldestFirst } from '../history.js';
|
|
28
31
|
import { mentionAtCaret } from '../utils/mentions.js';
|
|
29
32
|
import { preserveSelection } from '../utils/fileSuggestions.js';
|
|
30
33
|
import { isMod } from '../utils/modifiers.js';
|
|
@@ -35,6 +38,7 @@ import { HelpMenu } from './HelpMenu.js';
|
|
|
35
38
|
import { OverlayAbove } from './OverlayAbove.js';
|
|
36
39
|
import { SuggestionCard, cardContentWidth } from './SuggestionCard.js';
|
|
37
40
|
import { filterLiveImageBindings, isUsableDraftSnapshot, resolveBindingGeneration, } from './promptDraftCache.js';
|
|
41
|
+
export { sanitizeEditableText, sanitizePastedText } from '../utils/inputPaste.js';
|
|
38
42
|
/**
|
|
39
43
|
* Visible text of the session-entry control at the head of the input row:
|
|
40
44
|
* U+2338 APL FUNCTIONAL SYMBOL QUAD COLON plus a separator space.
|
|
@@ -53,6 +57,67 @@ import { filterLiveImageBindings, isUsableDraftSnapshot, resolveBindingGeneratio
|
|
|
53
57
|
*/
|
|
54
58
|
const HOME_BUTTON_TEXT = '⌸ ';
|
|
55
59
|
const HOME_BUTTON_COLS = stringWidth(HOME_BUTTON_TEXT);
|
|
60
|
+
/** Undo depth for both stacks (draft `Ctrl+Z` and vim `u`). */
|
|
61
|
+
const UNDO_LIMIT = 100;
|
|
62
|
+
/** Total characters kept across all snapshots; the oldest are dropped first. */
|
|
63
|
+
const UNDO_MAX_CHARS = 1_000_000;
|
|
64
|
+
/** Layer-3 idle window: a pause longer than this starts a new step. */
|
|
65
|
+
const UNDO_IDLE_MS = 700;
|
|
66
|
+
/**
|
|
67
|
+
* Common prefix/suffix of `prev` and `next` — the single edited span. Undo
|
|
68
|
+
* grouping works on this span rather than on a stdin chunk: one chunk may
|
|
69
|
+
* carry several characters (or several keys), so the unit is one real text
|
|
70
|
+
* mutation however it arrived. The common prefix must not pass either caret:
|
|
71
|
+
* repeated characters otherwise make an edit at the head look like a tail edit.
|
|
72
|
+
*/
|
|
73
|
+
function diffSpan(prev, next, cursorBefore, cursorAfter) {
|
|
74
|
+
let start = 0;
|
|
75
|
+
const common = Math.min(prev.length, next.length, cursorBefore, cursorAfter);
|
|
76
|
+
while (start < common && prev.charCodeAt(start) === next.charCodeAt(start))
|
|
77
|
+
start++;
|
|
78
|
+
let prevEnd = prev.length;
|
|
79
|
+
let nextEnd = next.length;
|
|
80
|
+
while (prevEnd > start &&
|
|
81
|
+
nextEnd > start &&
|
|
82
|
+
prev.charCodeAt(prevEnd - 1) === next.charCodeAt(nextEnd - 1)) {
|
|
83
|
+
prevEnd--;
|
|
84
|
+
nextEnd--;
|
|
85
|
+
}
|
|
86
|
+
return { start, inserted: next.slice(start, nextEnd) };
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Open a deletion run at the first character it removed: remember the word that
|
|
90
|
+
* character belongs to, and how much has been consumed so far.
|
|
91
|
+
*/
|
|
92
|
+
function startDeleteRun(text, start, removed) {
|
|
93
|
+
const word = draftWordRangeAt(text, start);
|
|
94
|
+
return { wordStart: word.start, wordEnd: word.end, lo: start, hi: start + removed };
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Whether a same-direction deletion extends the run recorded on `last`. The
|
|
98
|
+
* run's word interval and consumed range live in `last.text` coordinates, so
|
|
99
|
+
* the comparison survives the draft shrinking under it; extending past the word
|
|
100
|
+
* (or off the text) starts a new step. Only called once the direction, caret
|
|
101
|
+
* continuity and idle window have already agreed, so the new text is always
|
|
102
|
+
* adjacent to the consumed range.
|
|
103
|
+
*/
|
|
104
|
+
function extendDeleteRun(last, backward, removed) {
|
|
105
|
+
const run = last.deleteRun;
|
|
106
|
+
if (run === null || removed <= 0)
|
|
107
|
+
return false;
|
|
108
|
+
if (backward) {
|
|
109
|
+
const lo = run.lo - removed;
|
|
110
|
+
if (lo < 0 || lo < run.wordStart)
|
|
111
|
+
return false;
|
|
112
|
+
run.lo = lo;
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
const hi = run.hi + removed;
|
|
116
|
+
if (hi > last.text.length || hi > run.wordEnd)
|
|
117
|
+
return false;
|
|
118
|
+
run.hi = hi;
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
56
121
|
/**
|
|
57
122
|
* Paste fold with a visible preview and no black box:
|
|
58
123
|
* a paste that leaves the input this big folds into a one-line chip
|
|
@@ -65,66 +130,6 @@ const HOME_BUTTON_COLS = stringWidth(HOME_BUTTON_TEXT);
|
|
|
65
130
|
const FOLD_MIN_LINES = 6;
|
|
66
131
|
const FOLD_MIN_CHARS = 600;
|
|
67
132
|
const isBigInput = (text) => text.split('\n').length >= FOLD_MIN_LINES || text.length >= FOLD_MIN_CHARS;
|
|
68
|
-
/**
|
|
69
|
-
* Editable prompt text must have one stable source-to-screen geometry. The
|
|
70
|
-
* renderer interprets ANSI as zero-width styling and expands tabs relative to
|
|
71
|
-
* global tab stops; keeping either in `value` would let wrapping/click mapping
|
|
72
|
-
* count different cells and could split an escape sequence during selection.
|
|
73
|
-
* Strip terminal controls and expand tabs at ingress while preserving newlines.
|
|
74
|
-
*/
|
|
75
|
-
const EDITABLE_CONTROL = /[\u0000-\u0009\u000b-\u001f\u007f]/u;
|
|
76
|
-
/**
|
|
77
|
-
* Raw win32-input-mode records (`CSI Vk;Sc;Uc;Kd;Cs;Rc _`) that reached the
|
|
78
|
-
* editable buffer as text instead of being translated. `stripAnsi` consumes
|
|
79
|
-
* the record head and leaves its terminating `_` in the draft — the stray
|
|
80
|
-
* underscore users see after a multi-line paste (issue #1090). Only the full
|
|
81
|
-
* record grammar (exactly five `;` separators) matches, so a real `_` and
|
|
82
|
-
* ordinary bracket text survive untouched.
|
|
83
|
-
*/
|
|
84
|
-
const WIN32_RECORD_RESIDUE = /\u001b\[\d*(?:;\d*){5}_/gu;
|
|
85
|
-
/**
|
|
86
|
-
* The same record with its ESC byte missing: what a record split across
|
|
87
|
-
* reads leaves behind when the escape timer flushed the prefix before the
|
|
88
|
-
* tail arrived. Printable, so it is stripped only from paste payloads
|
|
89
|
-
* ({@link sanitizePastedText}) — and only when the same payload also carries
|
|
90
|
-
* a full ESC-bearing record as in-payload evidence of that split; typed text
|
|
91
|
-
* and literal clipboard/bracketed-paste bytes are left untouched.
|
|
92
|
-
*/
|
|
93
|
-
const WIN32_RECORD_RESIDUE_TAIL = /\[\d*(?:;\d*){5}_/gu;
|
|
94
|
-
/**
|
|
95
|
-
* Normalize editable text so no terminal control characters remain in state.
|
|
96
|
-
*/
|
|
97
|
-
export function sanitizeEditableText(text) {
|
|
98
|
-
// Fast path for ordinary and multi-line drafts: newline is intentionally
|
|
99
|
-
// absent from the probe, so large clean text returns without the
|
|
100
|
-
// stripAnsi/control-normalization passes.
|
|
101
|
-
if (!EDITABLE_CONTROL.test(text))
|
|
102
|
-
return text;
|
|
103
|
-
// Record residue goes first: `stripAnsi` would consume the CSI head and
|
|
104
|
-
// leave only the terminating `_` behind.
|
|
105
|
-
return stripAnsi(text.replace(WIN32_RECORD_RESIDUE, ''))
|
|
106
|
-
.replace(/\r\n?/gu, '\n')
|
|
107
|
-
.replace(/\t/gu, ' ')
|
|
108
|
-
.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/gu, '');
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* Paste-payload ingress: strip the ESC-less tail of a split record before
|
|
112
|
-
* normalizing. The tail is printable, so it survives `sanitizeEditableText`'s
|
|
113
|
-
* control probe untouched; typed text keeps it because only a paste payload
|
|
114
|
-
* can carry a partial record.
|
|
115
|
-
*
|
|
116
|
-
* The five separators only prove the *shape*, not that a record was split:
|
|
117
|
-
* a user can legitimately paste the literal `[13;28;13;1;0;1_`. Strip the
|
|
118
|
-
* ESC-less form only when the same payload also carries a full ESC-bearing
|
|
119
|
-
* record — only then is there in-payload evidence of a split stream.
|
|
120
|
-
* Otherwise the bytes are ordinary text and must survive verbatim.
|
|
121
|
-
*/
|
|
122
|
-
export function sanitizePastedText(text) {
|
|
123
|
-
// Probe with String#match: the /g detection regex carries lastIndex state
|
|
124
|
-
// across `.test` calls, so a previous success could skip a later match.
|
|
125
|
-
const hasRecordStream = text.match(WIN32_RECORD_RESIDUE) !== null;
|
|
126
|
-
return sanitizeEditableText(hasRecordStream ? text.replace(WIN32_RECORD_RESIDUE_TAIL, '') : text);
|
|
127
|
-
}
|
|
128
133
|
const COMPOSER_IMAGE_TOKEN = /\[Image #\d+\]/gu;
|
|
129
134
|
/** Format label for one image media type, matching the image preview card's
|
|
130
135
|
* title (`JPEG`, `PNG`, `WEBP`, `GIF`). */
|
|
@@ -414,7 +419,7 @@ const DOUBLE_CLICK_MS = 500;
|
|
|
414
419
|
* working) interrupts the turn and delivers them right away; Ctrl+Enter
|
|
415
420
|
* aborts the turn and sends the current input immediately.
|
|
416
421
|
*/
|
|
417
|
-
export function PromptInput({ channel, suspended = false, draftCache, helpOpen, onToggleHelp, onRunCommand, selectionActive, fillText, onFillConsumed, onRewindRequest, onBackgroundRequest, onOpenSessions, backgroundAgentsNeedingInput, controllerRef, onCaretImage, caretPreviewOpen = false, onDismissCaretPreview, }) {
|
|
422
|
+
export function PromptInput({ channel, toastSuppressed, suspended = false, draftCache, helpOpen, onToggleHelp, onRunCommand, selectionActive, fillText, onFillConsumed, onRewindRequest, onBackgroundRequest, onOpenSessions, backgroundAgentsNeedingInput, controllerRef, onCaretImage, caretPreviewOpen = false, onDismissCaretPreview, now = Date.now, }) {
|
|
418
423
|
const [themeName] = useTheme();
|
|
419
424
|
// Raw stdout writer for OSC 52 clipboard writes (selection copy) — must
|
|
420
425
|
// bypass the frame pipeline; null outside a mounted Ink App.
|
|
@@ -507,7 +512,20 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
507
512
|
const vimInsertRef = React.useRef(true);
|
|
508
513
|
vimEnabledRef.current = vimEnabled;
|
|
509
514
|
vimInsertRef.current = vimInsert;
|
|
510
|
-
/**
|
|
515
|
+
/**
|
|
516
|
+
* Draft-scoped undo stack (word-level `Ctrl+Z`). This is the DRAFT's own
|
|
517
|
+
* history: a submit, a recall or a session switch ends it. Entries hold image
|
|
518
|
+
* bindings exactly like the draft does, so dropping one must release the
|
|
519
|
+
* capabilities only it kept alive.
|
|
520
|
+
*/
|
|
521
|
+
const draftUndoRef = React.useRef([]);
|
|
522
|
+
/**
|
|
523
|
+
* Vim NORMAL-mode undo stack (vim `u`), deliberately separate from
|
|
524
|
+
* `draftUndoRef`: unifying the two transaction models is a user-visible vim
|
|
525
|
+
* behavior change and is out of this change's scope. `/vim` toggling clears
|
|
526
|
+
* THIS stack — a later re-enable must never `u` back past edits made while
|
|
527
|
+
* vim was off.
|
|
528
|
+
*/
|
|
511
529
|
const vimUndoRef = React.useRef([]);
|
|
512
530
|
/** Pending vim operator: `d` pressed, awaiting its second key. */
|
|
513
531
|
const vimPendingRef = React.useRef('');
|
|
@@ -575,8 +593,11 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
575
593
|
const history = React.useRef([]);
|
|
576
594
|
const historyIndex = React.useRef(-1);
|
|
577
595
|
const historyDraft = React.useRef({ text: '', images: [] });
|
|
578
|
-
/** The persisted history is read lazily, once per mount
|
|
579
|
-
|
|
596
|
+
/** The persisted history is read lazily, once per mount and project: the
|
|
597
|
+
* normalized project key it was seeded for, so a workspace switch re-seeds
|
|
598
|
+
* (see seedHistory) while a respelled path to the same directory does not.
|
|
599
|
+
* `null` = never seeded; `''` = seeded without a workspace. */
|
|
600
|
+
const historySeededProject = React.useRef(null);
|
|
580
601
|
/** Visible `[Image #N]` labels are presentation only; this sidecar carries
|
|
581
602
|
* the non-reusable capability for the current draft. History/rewind text
|
|
582
603
|
* restored without this map can never bind to a later image by accident. */
|
|
@@ -606,6 +627,7 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
606
627
|
const detachDraftImages = () => {
|
|
607
628
|
advanceDraftRevision();
|
|
608
629
|
draftImagesRef.current.clear();
|
|
630
|
+
clearDraftUndo();
|
|
609
631
|
clearVimUndo();
|
|
610
632
|
};
|
|
611
633
|
const stageIdIsRetained = (stageId) => {
|
|
@@ -613,7 +635,8 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
613
635
|
if (current === stageId)
|
|
614
636
|
return true;
|
|
615
637
|
}
|
|
616
|
-
return
|
|
638
|
+
return draftUndoRef.current.some(entry => entry.images.some(image => image.stageId === stageId))
|
|
639
|
+
|| vimUndoRef.current.some(entry => entry.images.some(image => image.stageId === stageId))
|
|
617
640
|
|| history.current.some(entry => entry.images.some(image => image.stageId === stageId))
|
|
618
641
|
|| historyDraft.current.images.some(image => image.stageId === stageId)
|
|
619
642
|
|| channel.pending.some(item => item.images?.some(image => image.stageId === stageId) === true);
|
|
@@ -624,14 +647,109 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
624
647
|
channel.discardStagedImage(stageId);
|
|
625
648
|
}
|
|
626
649
|
};
|
|
650
|
+
const clearDraftUndo = () => {
|
|
651
|
+
const previous = draftUndoRef.current;
|
|
652
|
+
draftUndoRef.current = [];
|
|
653
|
+
discardUnretainedImages(previous.flatMap(entry => entry.images.map(image => image.stageId)));
|
|
654
|
+
};
|
|
627
655
|
const clearVimUndo = () => {
|
|
628
656
|
const previous = vimUndoRef.current;
|
|
629
657
|
vimUndoRef.current = [];
|
|
630
658
|
discardUnretainedImages(previous.flatMap(entry => entry.images.map(image => image.stageId)));
|
|
631
659
|
};
|
|
632
|
-
|
|
660
|
+
/** Push one step and enforce the depth/character guards. */
|
|
661
|
+
const pushDraftUndo = (snapshot, kind, caretAfter, deleteRun = null) => {
|
|
662
|
+
draftUndoRef.current.push({ ...snapshot, atMs: now(), caretAfter, kind, deleteRun });
|
|
663
|
+
const dropped = [];
|
|
664
|
+
while (draftUndoRef.current.length > UNDO_LIMIT)
|
|
665
|
+
dropped.push(draftUndoRef.current.shift());
|
|
666
|
+
let chars = draftUndoRef.current.reduce((total, entry) => total + entry.text.length, 0);
|
|
667
|
+
while (chars > UNDO_MAX_CHARS && draftUndoRef.current.length > 1) {
|
|
668
|
+
const oldest = draftUndoRef.current.shift();
|
|
669
|
+
chars -= oldest.text.length;
|
|
670
|
+
dropped.push(oldest);
|
|
671
|
+
}
|
|
672
|
+
if (dropped.length > 0) {
|
|
673
|
+
discardUnretainedImages(dropped.flatMap(entry => entry.images.map(image => image.stageId)));
|
|
674
|
+
}
|
|
675
|
+
};
|
|
676
|
+
/**
|
|
677
|
+
* Record one real text mutation. `group` folds it into the current step when
|
|
678
|
+
* the direction, caret continuity, idle window and word rule all agree;
|
|
679
|
+
* `step` always starts a sealed step. A chunk carrying several characters is
|
|
680
|
+
* judged per mutation, never per stdin read.
|
|
681
|
+
*/
|
|
682
|
+
const recordDraftEdit = (prev, prevCursor, block, next, nextCursor, mode) => {
|
|
683
|
+
const span = diffSpan(prev, next, prevCursor, nextCursor);
|
|
684
|
+
// Backspace consumes the character BEFORE the caret; Delete consumes the
|
|
685
|
+
// one AT it. The two directions anchor differently, so their runs must
|
|
686
|
+
// never fold into one another.
|
|
687
|
+
const backward = span.start < prevCursor;
|
|
688
|
+
const kind = span.inserted.length > 0 ? 'insert' : backward ? 'deleteBackward' : 'deleteForward';
|
|
689
|
+
const removed = prev.length - next.length;
|
|
690
|
+
const caretAfter = nextCursor;
|
|
691
|
+
if (mode === 'group') {
|
|
692
|
+
const last = draftUndoRef.current[draftUndoRef.current.length - 1];
|
|
693
|
+
// Continuity: this edit must begin where the last one ended. An insertion
|
|
694
|
+
// begins at the caret; a Backspace consumes the character BEFORE it, so
|
|
695
|
+
// the pre-edit caret is its start — comparing `span.start` there would
|
|
696
|
+
// make every repeated Backspace a step of its own.
|
|
697
|
+
const editStart = kind === 'insert' ? span.start : prevCursor;
|
|
698
|
+
if (last !== undefined &&
|
|
699
|
+
last.caretAfter !== null &&
|
|
700
|
+
last.kind === kind &&
|
|
701
|
+
last.caretAfter === editStart &&
|
|
702
|
+
now() - last.atMs <= UNDO_IDLE_MS &&
|
|
703
|
+
// An insertion extends a word when its seam is not a boundary; a
|
|
704
|
+
// deletion continues while it stays inside the word its run began in.
|
|
705
|
+
// The boundary question is deliberately NOT asked of a deletion: at the
|
|
706
|
+
// seam, `isDraftWordBoundary(prev, span.start)` reads the removed
|
|
707
|
+
// character's LEFT neighbour, which stops the run one keystroke early
|
|
708
|
+
// at a word's left edge and at the text head (and mis-splits a
|
|
709
|
+
// re-segmented CJK suffix).
|
|
710
|
+
(kind === 'insert'
|
|
711
|
+
? !isDraftWordBoundary(next, span.start)
|
|
712
|
+
: extendDeleteRun(last, backward, removed))) {
|
|
713
|
+
last.atMs = now();
|
|
714
|
+
last.caretAfter = caretAfter;
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
pushDraftUndo({ text: prev, cursor: prevCursor, images: imageRefsFor(prev), block }, kind, mode === 'group' ? caretAfter : null,
|
|
719
|
+
// A sealed step never groups, so only a groupable deletion needs (and
|
|
720
|
+
// pays for) the word interval.
|
|
721
|
+
mode === 'group' && kind !== 'insert' ? startDeleteRun(prev, span.start, removed) : null);
|
|
722
|
+
};
|
|
723
|
+
/**
|
|
724
|
+
* Clear the draft while keeping ONE undo step, so `Ctrl+Z` can bring the
|
|
725
|
+
* whole draft back (text, caret, images and fold block). The snapshot is
|
|
726
|
+
* taken before the images are released: afterwards the capabilities would
|
|
727
|
+
* already be gone and the restored `[Image #N]` tokens would be inert.
|
|
728
|
+
*/
|
|
729
|
+
const clearDraftUndoably = () => {
|
|
730
|
+
syncImageGeneration();
|
|
731
|
+
// Nothing to come back to on an already-empty draft: pushing a snapshot
|
|
732
|
+
// there would waste the next Ctrl+Z (and hide an older real step).
|
|
733
|
+
if (valueRef.current !== '') {
|
|
734
|
+
pushDraftUndo({
|
|
735
|
+
text: valueRef.current,
|
|
736
|
+
cursor: cursorRef.current,
|
|
737
|
+
images: imageRefsFor(valueRef.current),
|
|
738
|
+
block: foldBlockRef.current,
|
|
739
|
+
}, 'clear', null);
|
|
740
|
+
}
|
|
741
|
+
discardDraftImages({ keepUndo: true });
|
|
742
|
+
setInput('', 0, 'silent');
|
|
743
|
+
setSelectedCommand(0);
|
|
744
|
+
setFileSelected(0);
|
|
745
|
+
};
|
|
746
|
+
const discardDraftImages = (options) => {
|
|
633
747
|
advanceDraftRevision();
|
|
634
748
|
const stageIds = [...draftImagesRef.current.values()];
|
|
749
|
+
// The draft stack may be kept so Ctrl+Z can undo the clear; the vim stack
|
|
750
|
+
// is never restored by `u` across a whole-draft replacement.
|
|
751
|
+
if (options?.keepUndo !== true)
|
|
752
|
+
clearDraftUndo();
|
|
635
753
|
clearVimUndo();
|
|
636
754
|
draftImagesRef.current.clear();
|
|
637
755
|
discardUnretainedImages(stageIds);
|
|
@@ -651,6 +769,7 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
651
769
|
if (draftImagesGenerationRef.current !== generation) {
|
|
652
770
|
// The channel has already cleared every old-generation capability.
|
|
653
771
|
// Only detach the UI sidecar; calling discard would be redundant.
|
|
772
|
+
draftUndoRef.current = [];
|
|
654
773
|
vimUndoRef.current = [];
|
|
655
774
|
detachDraftImages();
|
|
656
775
|
draftImagesGenerationRef.current = generation;
|
|
@@ -721,6 +840,12 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
721
840
|
const next = sanitizeEditableText(previous + text);
|
|
722
841
|
if (next !== previous)
|
|
723
842
|
inputEditSequenceRef.current += 1;
|
|
843
|
+
// An external injection is a NEW draft baseline. It bypasses setInput,
|
|
844
|
+
// so the stack would still describe the pre-injection text: one Ctrl+Z
|
|
845
|
+
// would then erase the injected text together with the typing it
|
|
846
|
+
// joined (the injection is not an edit of the draft the user made).
|
|
847
|
+
if (next !== previous)
|
|
848
|
+
clearDraftUndo();
|
|
724
849
|
valueRef.current = next;
|
|
725
850
|
cursorRef.current = next.length;
|
|
726
851
|
setValue(next);
|
|
@@ -739,14 +864,16 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
739
864
|
// The selection stays: copy never clears it (Esc/typing/delete do).
|
|
740
865
|
return true;
|
|
741
866
|
},
|
|
867
|
+
consumeEscape: consumeEditingEscape,
|
|
742
868
|
toggleVim: () => {
|
|
743
869
|
const next = !vimEnabledRef.current;
|
|
744
870
|
vimEnabledRef.current = next;
|
|
745
871
|
setVimEnabled(next);
|
|
746
872
|
// Every toggle lands in INSERT: a fresh vim user keeps typing
|
|
747
873
|
// normally until they press Esc for the first time. Turning the
|
|
748
|
-
// mode off also clears the undo stack — a later re-enable must
|
|
749
|
-
// never `u` its way back past edits made while vim was off.
|
|
874
|
+
// mode off also clears the VIM undo stack — a later re-enable must
|
|
875
|
+
// never `u` its way back past edits made while vim was off. The
|
|
876
|
+
// draft stack (Ctrl+Z) is a different history and is left alone.
|
|
750
877
|
vimInsertRef.current = true;
|
|
751
878
|
setVimInsert(true);
|
|
752
879
|
vimPendingRef.current = '';
|
|
@@ -769,7 +896,9 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
769
896
|
syncImageGeneration();
|
|
770
897
|
discardDraftImages();
|
|
771
898
|
updateFoldBlock(null);
|
|
772
|
-
|
|
899
|
+
// A recall is not undoable (D4): the stack was just cleared above, so
|
|
900
|
+
// the fill must not push a snapshot of the draft it replaced.
|
|
901
|
+
setInput(fillText, fillText.length, 'silent');
|
|
773
902
|
onFillConsumed?.();
|
|
774
903
|
}
|
|
775
904
|
}, [fillText, onFillConsumed]);
|
|
@@ -797,9 +926,10 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
797
926
|
*
|
|
798
927
|
* `advanceDraftRevision` invalidates any image read/stage still in
|
|
799
928
|
* flight, so a late continuation cannot bind a capability into a composer
|
|
800
|
-
* that no longer exists. `clearVimUndo`
|
|
801
|
-
* only
|
|
802
|
-
* nothing will ever restore them once this composer is
|
|
929
|
+
* that no longer exists. `clearDraftUndo`/`clearVimUndo` release the
|
|
930
|
+
* capabilities that only an undo stack was holding: they are not part of
|
|
931
|
+
* the draft, and nothing will ever restore them once this composer is
|
|
932
|
+
* gone.
|
|
803
933
|
*
|
|
804
934
|
* The stageIds the DRAFT holds are deliberately NOT revoked — they are
|
|
805
935
|
* part of the snapshot the slot now keeps, and the restore filters out
|
|
@@ -807,6 +937,7 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
807
937
|
* an image draft used to come back as inert text.
|
|
808
938
|
*/
|
|
809
939
|
advanceDraftRevision();
|
|
940
|
+
clearDraftUndo();
|
|
810
941
|
clearVimUndo();
|
|
811
942
|
const images = [...draftImagesRef.current.entries()]
|
|
812
943
|
.map(([token, stageId]) => [token, stageId]);
|
|
@@ -980,7 +1111,13 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
980
1111
|
}
|
|
981
1112
|
}
|
|
982
1113
|
};
|
|
983
|
-
|
|
1114
|
+
/**
|
|
1115
|
+
* Apply `next` at `cursorOffset` and record the edit on the undo stack.
|
|
1116
|
+
* `snapshotBlock` overrides the block the step snapshots: a caller that must
|
|
1117
|
+
* clear the fold block BEFORE writing (the external-editor refill) still
|
|
1118
|
+
* wants the block that was live at edit time.
|
|
1119
|
+
*/
|
|
1120
|
+
const setInput = (next, cursorOffset = next.length, undo = 'group', snapshotBlock) => {
|
|
984
1121
|
// Apply the same ingress normalization to fills/history/editor results as
|
|
985
1122
|
// to paste. Map the requested caret through the sanitized prefix so an
|
|
986
1123
|
// ANSI sequence removed before it cannot leave the caret past the text.
|
|
@@ -1022,6 +1159,16 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
1022
1159
|
// one continues in its direction of travel (←/word-left/↑ → the token's
|
|
1023
1160
|
// start, →/word-right/↓ → its end); with no travel, the nearer edge.
|
|
1024
1161
|
offset = snapOffImageToken(boundImageSpans(next), offset, offset < prevCursor ? 'start' : offset > prevCursor ? 'end' : 'nearest');
|
|
1162
|
+
// Undo bookkeeping runs before the mirrors move: it snapshots the state
|
|
1163
|
+
// this edit replaces, and `diffSpan` needs both texts. `reset` ends the
|
|
1164
|
+
// draft's history, `silent` (undo itself, a recall) leaves it untouched.
|
|
1165
|
+
if (next !== prev) {
|
|
1166
|
+
if (undo === 'reset')
|
|
1167
|
+
clearDraftUndo();
|
|
1168
|
+
else if (undo !== 'silent') {
|
|
1169
|
+
recordDraftEdit(prev, prevCursor, snapshotBlock === undefined ? block : snapshotBlock, next, offset, undo);
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1025
1172
|
// The synchronous mirrors are what batch-dispatched events (one stdin
|
|
1026
1173
|
// read → several keys, no render in between) read on their next turn.
|
|
1027
1174
|
if (next !== prev)
|
|
@@ -1044,12 +1191,12 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
1044
1191
|
setValue(next);
|
|
1045
1192
|
setCursor(offset);
|
|
1046
1193
|
};
|
|
1047
|
-
const deleteInputRange = (start, end) => {
|
|
1194
|
+
const deleteInputRange = (start, end, undo = 'group') => {
|
|
1048
1195
|
if (start >= end)
|
|
1049
1196
|
return;
|
|
1050
1197
|
const text = valueRef.current;
|
|
1051
1198
|
const range = expandImageTokenRange(boundImageSpans(text), start, end);
|
|
1052
|
-
setInput(text.slice(0, range.start) + text.slice(range.end), range.start);
|
|
1199
|
+
setInput(text.slice(0, range.start) + text.slice(range.end), range.start, undo);
|
|
1053
1200
|
};
|
|
1054
1201
|
/**
|
|
1055
1202
|
* Write the selection [start, end) (snapped to grapheme boundaries,
|
|
@@ -1112,7 +1259,7 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
1112
1259
|
const suffix = mention.pathEnd === undefined ? '' : value.slice(mention.pathEnd, mention.end);
|
|
1113
1260
|
const insert = candidate.kind === 'directory' ? `${body}${suffix}` : `${body}${suffix} `;
|
|
1114
1261
|
const next = value.slice(0, mention.start) + insert + value.slice(mention.end);
|
|
1115
|
-
setInput(next, mention.start + insert.length);
|
|
1262
|
+
setInput(next, mention.start + insert.length, 'step');
|
|
1116
1263
|
setFileSelected(0);
|
|
1117
1264
|
};
|
|
1118
1265
|
const imageRefsFor = (text) => {
|
|
@@ -1145,14 +1292,35 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
1145
1292
|
* persisted entries first, this run's submits behind them — instead of two
|
|
1146
1293
|
* lists to merge at recall time. Restored text carries no image capability
|
|
1147
1294
|
* (the file stores text only), which is also what keeps a recalled entry
|
|
1148
|
-
* from binding to a later staged image by accident.
|
|
1295
|
+
* from binding to a later staged image by accident. The walk is scoped to
|
|
1296
|
+
* the channel's workspace, keyed the same way the store keys it
|
|
1297
|
+
* (historyProjectKey), so a workspace switch re-seeds it with that
|
|
1298
|
+
* project's history instead of carrying the previous one over — and a
|
|
1299
|
+
* respelled path to the same directory is not a switch.
|
|
1300
|
+
* @returns Whether the re-seed cut short a walk in progress: the composer
|
|
1301
|
+
* then shows a recalled entry of the previous project while the draft that
|
|
1302
|
+
* walk started from is still held in `historyDraft`.
|
|
1149
1303
|
*/
|
|
1150
1304
|
const seedHistory = () => {
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1305
|
+
// The store's own key, not the raw cwd: `/repo` and `/repo/` are one
|
|
1306
|
+
// project, so respelling the path must not look like a workspace switch.
|
|
1307
|
+
const project = historyProjectKey(channel.cwd) ?? '';
|
|
1308
|
+
if (historySeededProject.current === project)
|
|
1309
|
+
return false;
|
|
1310
|
+
const interrupted = historyIndex.current >= 0;
|
|
1311
|
+
historySeededProject.current = project;
|
|
1312
|
+
history.current = loadHistoryOldestFirst(channel.cwd).map(entry => ({ text: entry.text, images: [] }));
|
|
1155
1313
|
historyIndex.current = -1;
|
|
1314
|
+
return interrupted;
|
|
1315
|
+
};
|
|
1316
|
+
/** End the walk and put back the draft it started from. */
|
|
1317
|
+
const restoreHistoryDraft = () => {
|
|
1318
|
+
historyIndex.current = -1;
|
|
1319
|
+
updateFoldBlock(null);
|
|
1320
|
+
restoreDraftImages(historyDraft.current);
|
|
1321
|
+
// A recall is not undoable (D4): `restoreDraftImages` just ended the
|
|
1322
|
+
// history, so the restore must not push a step of its own.
|
|
1323
|
+
setInput(historyDraft.current.text, historyDraft.current.text.length, 'silent');
|
|
1156
1324
|
};
|
|
1157
1325
|
const rememberHistory = (text, images) => {
|
|
1158
1326
|
// Both entries into the walk (a submit and ↑) must see the persisted
|
|
@@ -1165,14 +1333,15 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
1165
1333
|
if (history.current.length > HISTORY_LIMIT)
|
|
1166
1334
|
history.current.shift();
|
|
1167
1335
|
historyIndex.current = -1;
|
|
1168
|
-
void appendHistory(text);
|
|
1336
|
+
void appendHistory(text, channel.cwd);
|
|
1169
1337
|
};
|
|
1170
1338
|
const clearDeliveredDraft = () => {
|
|
1171
1339
|
syncImageGeneration();
|
|
1172
1340
|
// Delivery captured the opaque refs and history retains them; only the
|
|
1173
|
-
// editable-draft binding is ending here.
|
|
1341
|
+
// editable-draft binding is ending here. `reset` ends the draft's undo
|
|
1342
|
+
// history: a submitted message must not come back with Ctrl+Z.
|
|
1174
1343
|
detachDraftImages();
|
|
1175
|
-
setInput('', 0);
|
|
1344
|
+
setInput('', 0, 'reset');
|
|
1176
1345
|
setSelectedCommand(0);
|
|
1177
1346
|
setFileSelected(0);
|
|
1178
1347
|
};
|
|
@@ -1189,8 +1358,40 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
1189
1358
|
syncImageGeneration();
|
|
1190
1359
|
advanceDraftRevision();
|
|
1191
1360
|
replaceDraftImages(entry.images);
|
|
1361
|
+
// A recall is not undoable (D4): both stacks end with the draft it replaced.
|
|
1362
|
+
clearDraftUndo();
|
|
1192
1363
|
clearVimUndo();
|
|
1193
1364
|
};
|
|
1365
|
+
/**
|
|
1366
|
+
* `Ctrl+Z`: pop back to the state before the current undo step — text,
|
|
1367
|
+
* caret, images and fold block together. An empty stack is a no-op (never
|
|
1368
|
+
* the rewind picker, never a clear). The restore is `silent`, so repeated
|
|
1369
|
+
* Ctrl+Z walks further back instead of pushing what it just left. Vim `u`
|
|
1370
|
+
* uses its own stack (see {@link vimUndoRef}).
|
|
1371
|
+
*/
|
|
1372
|
+
const undoDraft = () => {
|
|
1373
|
+
// A session replacement invalidates the whole stack. Check that BEFORE
|
|
1374
|
+
// popping: restoring first and discarding afterwards would put the old
|
|
1375
|
+
// session's text (and its image tokens) back into the new composer.
|
|
1376
|
+
syncImageGeneration();
|
|
1377
|
+
const entry = draftUndoRef.current.pop();
|
|
1378
|
+
if (entry === undefined)
|
|
1379
|
+
return;
|
|
1380
|
+
advanceDraftRevision();
|
|
1381
|
+
replaceDraftImages(entry.images);
|
|
1382
|
+
// Drop the live block first: setInput's atomicity rules describe the block
|
|
1383
|
+
// that was live at edit time, and re-shifting it here would corrupt the
|
|
1384
|
+
// snapshot's. The snapshot's own block goes back afterwards, and only if
|
|
1385
|
+
// its range still fits the text it was captured from.
|
|
1386
|
+
updateFoldBlock(null);
|
|
1387
|
+
setInput(entry.text, entry.cursor, 'silent');
|
|
1388
|
+
const block = entry.block !== null && entry.block.start < entry.block.end && entry.block.end <= entry.text.length
|
|
1389
|
+
? entry.block
|
|
1390
|
+
: null;
|
|
1391
|
+
updateFoldBlock(block);
|
|
1392
|
+
setSelectedCommand(0);
|
|
1393
|
+
setFileSelected(0);
|
|
1394
|
+
};
|
|
1194
1395
|
const submitText = (text, notice) => {
|
|
1195
1396
|
const trimmed = text.trim();
|
|
1196
1397
|
if (!trimmed)
|
|
@@ -1258,7 +1459,7 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
1258
1459
|
text: item.text,
|
|
1259
1460
|
images: item.images ?? [],
|
|
1260
1461
|
});
|
|
1261
|
-
setInput(item.text);
|
|
1462
|
+
setInput(item.text, item.text.length, 'silent');
|
|
1262
1463
|
updateFoldBlock(null);
|
|
1263
1464
|
setSelectedCommand(0);
|
|
1264
1465
|
setFileSelected(0);
|
|
@@ -1480,6 +1681,19 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
1480
1681
|
lastClickColRef.current = -1;
|
|
1481
1682
|
lastClickRowRef.current = -1;
|
|
1482
1683
|
};
|
|
1684
|
+
/** Local Esc layers, shared by the prompt listener and Chat's delegation.
|
|
1685
|
+
* Refs preserve this order even when expansion and Esc share a stdin batch. */
|
|
1686
|
+
const consumeEditingEscape = () => {
|
|
1687
|
+
if (selectionRef.current && !helpOpen && !overlayOpen && !fileOverlayOpen) {
|
|
1688
|
+
clearSelection();
|
|
1689
|
+
return true;
|
|
1690
|
+
}
|
|
1691
|
+
if (expandedRef.current) {
|
|
1692
|
+
collapseEditor();
|
|
1693
|
+
return true;
|
|
1694
|
+
}
|
|
1695
|
+
return false;
|
|
1696
|
+
};
|
|
1483
1697
|
/** The editor's explicit send (Ctrl+Enter / Send button): the Enter main
|
|
1484
1698
|
* path, then collapse — an empty draft just collapses. */
|
|
1485
1699
|
const submitFromEditor = () => {
|
|
@@ -1498,7 +1712,8 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
1498
1712
|
const next = sel
|
|
1499
1713
|
? current.slice(0, sel.start) + text + current.slice(sel.end)
|
|
1500
1714
|
: current.slice(0, position) + text + current.slice(position);
|
|
1501
|
-
|
|
1715
|
+
// A paste is ONE undo step however many characters it carries.
|
|
1716
|
+
setInput(next, position + text.length, 'step');
|
|
1502
1717
|
setSelectedCommand(0);
|
|
1503
1718
|
setFileSelected(0);
|
|
1504
1719
|
return { next, at: position };
|
|
@@ -1650,27 +1865,18 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
1650
1865
|
// ONLY drops the highlight (text untouched), and Backspace/Delete
|
|
1651
1866
|
// delete the selected span. Arrows/typing handle the selection at their
|
|
1652
1867
|
// own arms below.
|
|
1653
|
-
if (key.escape &&
|
|
1868
|
+
if (key.escape && consumeEditingEscape()) {
|
|
1654
1869
|
event.stopImmediatePropagation();
|
|
1655
|
-
clearSelection();
|
|
1656
1870
|
return;
|
|
1657
1871
|
}
|
|
1658
1872
|
if ((key.backspace || key.delete) && selection) {
|
|
1659
|
-
|
|
1873
|
+
// Removing a selection is atomic: it must undo in one step, not fold
|
|
1874
|
+
// into whatever the caret-continuity rule would otherwise see.
|
|
1875
|
+
deleteInputRange(selection.start, selection.end, 'step');
|
|
1660
1876
|
setSelectedCommand(0);
|
|
1661
1877
|
setFileSelected(0);
|
|
1662
1878
|
return;
|
|
1663
1879
|
}
|
|
1664
|
-
// ── 全屏草稿编辑(expandEditor,默认 Ctrl+Shift+E / 输入行 ✎)─────
|
|
1665
|
-
// 展开态拥有屏幕;Esc 收起(有选区时上面的 selection 分支已先行只清
|
|
1666
|
-
// 选区)。滚轮不经此——编辑区的 onWheel 位置路由直接驱动滚动窗口。
|
|
1667
|
-
if (key.escape && expandedRef.current) {
|
|
1668
|
-
// Collapse runs AHEAD of the fold-block/vim Esc meanings: the
|
|
1669
|
-
// fullscreen cover is the outermost modal layer.
|
|
1670
|
-
event?.stopImmediatePropagation();
|
|
1671
|
-
collapseEditor();
|
|
1672
|
-
return;
|
|
1673
|
-
}
|
|
1674
1880
|
// Fold block: Esc expands it — it must NEVER clear text
|
|
1675
1881
|
// that LOOKS like one line; Backspace at the block's tail / Delete at
|
|
1676
1882
|
// its head deletes the WHOLE block in one key; ←/→ jump over the
|
|
@@ -1690,7 +1896,7 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
1690
1896
|
if ((key.backspace && cursor === block.end) || (key.delete && cursor === block.start)) {
|
|
1691
1897
|
const next = value.slice(0, block.start) + value.slice(block.end);
|
|
1692
1898
|
updateFoldBlock(null);
|
|
1693
|
-
setInput(next, block.start);
|
|
1899
|
+
setInput(next, block.start, 'step', block);
|
|
1694
1900
|
setSelectedCommand(0);
|
|
1695
1901
|
setFileSelected(0);
|
|
1696
1902
|
return;
|
|
@@ -1711,7 +1917,7 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
1711
1917
|
* active selection is REPLACED by the insert (standard editor
|
|
1712
1918
|
* semantics). Returns the insertion offset so callers can derive the
|
|
1713
1919
|
* inserted span (paste fold). */
|
|
1714
|
-
const insertAtCaret = (text) => {
|
|
1920
|
+
const insertAtCaret = (text, undo = 'group') => {
|
|
1715
1921
|
if (helpOpen)
|
|
1716
1922
|
onToggleHelp();
|
|
1717
1923
|
const sel = selectionRef.current;
|
|
@@ -1719,7 +1925,9 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
1719
1925
|
const next = sel
|
|
1720
1926
|
? value.slice(0, sel.start) + text + value.slice(sel.end)
|
|
1721
1927
|
: value.slice(0, cursor) + text + value.slice(cursor);
|
|
1722
|
-
|
|
1928
|
+
// Replacing a selection is atomic (like deleting one): the replaced span
|
|
1929
|
+
// must not fold into the surrounding typed run.
|
|
1930
|
+
setInput(next, at + text.length, sel ? 'step' : undo);
|
|
1723
1931
|
setSelectedCommand(0);
|
|
1724
1932
|
setFileSelected(0);
|
|
1725
1933
|
return at;
|
|
@@ -1758,7 +1966,8 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
1758
1966
|
});
|
|
1759
1967
|
return;
|
|
1760
1968
|
}
|
|
1761
|
-
|
|
1969
|
+
// One paste is ONE undo step however many characters it carries.
|
|
1970
|
+
const at = insertAtCaret(text, 'step');
|
|
1762
1971
|
// A big paste becomes a fold block right away (hover peeks
|
|
1763
1972
|
// at it); an existing block is replaced by the new paste's span.
|
|
1764
1973
|
// The EXPANDED editor never folds — pasting there is plain text
|
|
@@ -1950,8 +2159,12 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
1950
2159
|
if (!draftImageLeaseIsCurrent(editorLease))
|
|
1951
2160
|
return;
|
|
1952
2161
|
if (outcome.kind === 'edited') {
|
|
2162
|
+
// Snapshot the block BEFORE clearing it: the refill is one undo
|
|
2163
|
+
// step (D4), and Ctrl+Z must bring back the fold chip the draft
|
|
2164
|
+
// had, not the already-cleared null.
|
|
2165
|
+
const beforeBlock = foldBlockRef.current;
|
|
1953
2166
|
updateFoldBlock(null);
|
|
1954
|
-
setInput(outcome.text);
|
|
2167
|
+
setInput(outcome.text, outcome.text.length, 'step', beforeBlock);
|
|
1955
2168
|
setSelectedCommand(0);
|
|
1956
2169
|
setFileSelected(0);
|
|
1957
2170
|
}
|
|
@@ -2094,7 +2307,7 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
2094
2307
|
const command = suggestions[selectedCommand];
|
|
2095
2308
|
if (command) {
|
|
2096
2309
|
updateFoldBlock(null);
|
|
2097
|
-
setInput(command.replacement);
|
|
2310
|
+
setInput(command.replacement, command.replacement.length, 'step');
|
|
2098
2311
|
}
|
|
2099
2312
|
return;
|
|
2100
2313
|
}
|
|
@@ -2182,10 +2395,18 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
2182
2395
|
setSelectedCommand(index => index <= 0 ? suggestions.length - 1 : index - 1);
|
|
2183
2396
|
return;
|
|
2184
2397
|
}
|
|
2185
|
-
|
|
2186
|
-
|
|
2398
|
+
// A workspace switch mid-walk keeps the draft that walk started from:
|
|
2399
|
+
// the composer shows the previous project's entry, not a new draft.
|
|
2400
|
+
const interrupted = seedHistory();
|
|
2401
|
+
if (history.current.length === 0) {
|
|
2402
|
+
if (interrupted)
|
|
2403
|
+
restoreHistoryDraft();
|
|
2187
2404
|
return;
|
|
2188
|
-
|
|
2405
|
+
}
|
|
2406
|
+
if (interrupted) {
|
|
2407
|
+
historyIndex.current = history.current.length - 1;
|
|
2408
|
+
}
|
|
2409
|
+
else if (historyIndex.current < 0) {
|
|
2189
2410
|
historyDraft.current = {
|
|
2190
2411
|
text: value,
|
|
2191
2412
|
images: imageRefsFor(value),
|
|
@@ -2201,7 +2422,7 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
2201
2422
|
retractRecalledCopy(entry.text);
|
|
2202
2423
|
updateFoldBlock(null);
|
|
2203
2424
|
restoreDraftImages(entry);
|
|
2204
|
-
setInput(entry.text);
|
|
2425
|
+
setInput(entry.text, entry.text.length, 'silent');
|
|
2205
2426
|
return;
|
|
2206
2427
|
}
|
|
2207
2428
|
if (key.downArrow) {
|
|
@@ -2253,20 +2474,23 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
2253
2474
|
setSelectedCommand(index => index >= suggestions.length - 1 ? 0 : index + 1);
|
|
2254
2475
|
return;
|
|
2255
2476
|
}
|
|
2477
|
+
// A walk cut short by a workspace switch has no position in the new
|
|
2478
|
+
// project's list; ↓ just ends it and returns the draft.
|
|
2479
|
+
if (seedHistory()) {
|
|
2480
|
+
restoreHistoryDraft();
|
|
2481
|
+
return;
|
|
2482
|
+
}
|
|
2256
2483
|
if (historyIndex.current < 0)
|
|
2257
2484
|
return;
|
|
2258
2485
|
if (historyIndex.current >= history.current.length - 1) {
|
|
2259
|
-
|
|
2260
|
-
updateFoldBlock(null);
|
|
2261
|
-
restoreDraftImages(historyDraft.current);
|
|
2262
|
-
setInput(historyDraft.current.text);
|
|
2486
|
+
restoreHistoryDraft();
|
|
2263
2487
|
}
|
|
2264
2488
|
else {
|
|
2265
2489
|
historyIndex.current += 1;
|
|
2266
2490
|
const entry = history.current[historyIndex.current];
|
|
2267
2491
|
if (entry !== undefined) {
|
|
2268
2492
|
restoreDraftImages(entry);
|
|
2269
|
-
setInput(entry.text);
|
|
2493
|
+
setInput(entry.text, entry.text.length, 'silent');
|
|
2270
2494
|
}
|
|
2271
2495
|
}
|
|
2272
2496
|
return;
|
|
@@ -2355,10 +2579,19 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
2355
2579
|
deleteInputRange(cursor, end);
|
|
2356
2580
|
return;
|
|
2357
2581
|
}
|
|
2582
|
+
if (actionMatches('undo', input, key)) {
|
|
2583
|
+
// Word-level draft undo (remappable via /settings). Only the prompt
|
|
2584
|
+
// draft has an undo space: with the keyboard owned by a panel or a
|
|
2585
|
+
// dialog this handler is inactive (useInput isActive), and an empty
|
|
2586
|
+
// stack is a no-op rather than the rewind picker.
|
|
2587
|
+
undoDraft();
|
|
2588
|
+
return;
|
|
2589
|
+
}
|
|
2358
2590
|
if (isMod(key) && input === 'w') {
|
|
2359
|
-
// Delete the word before the cursor:
|
|
2360
|
-
// trailing whitespace, then the
|
|
2361
|
-
//
|
|
2591
|
+
// Delete the word before the cursor: readline's whitespace-delimited
|
|
2592
|
+
// rule (skip trailing whitespace, then the word). The ICU/CJK
|
|
2593
|
+
// alternative is a separate editor-UX change, not part of draft undo.
|
|
2594
|
+
// The deletion start never crosses into the block.
|
|
2362
2595
|
const before = value.slice(0, cursor);
|
|
2363
2596
|
let end = before.length;
|
|
2364
2597
|
while (end > 0 && /\s/.test(before[end - 1]))
|
|
@@ -2375,25 +2608,30 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
2375
2608
|
// NORMAL submode: bare characters and Esc are vim keys; help/command/
|
|
2376
2609
|
// file overlays, modified combos, Tab, Enter, arrows and other
|
|
2377
2610
|
// structured keys keep their existing handlers.
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
setFileSelected(0);
|
|
2383
|
-
};
|
|
2611
|
+
/**
|
|
2612
|
+
* Push one vim NORMAL-mode step. Vim edits never touch the draft stack:
|
|
2613
|
+
* `u` and `Ctrl+Z` are two independent histories (see {@link vimUndoRef}).
|
|
2614
|
+
*/
|
|
2384
2615
|
const vimPushUndo = () => {
|
|
2385
2616
|
const images = imageRefsFor(valueRef.current);
|
|
2386
|
-
const evicted = vimUndoRef.current.length >=
|
|
2617
|
+
const evicted = vimUndoRef.current.length >= UNDO_LIMIT ? vimUndoRef.current.shift() : undefined;
|
|
2387
2618
|
vimUndoRef.current.push({ text: valueRef.current, cursor: cursorRef.current, images });
|
|
2388
|
-
if (evicted !== undefined)
|
|
2619
|
+
if (evicted !== undefined) {
|
|
2389
2620
|
discardUnretainedImages(evicted.images.map(image => image.stageId));
|
|
2621
|
+
}
|
|
2622
|
+
};
|
|
2623
|
+
const vimNormalEdit = (next, cursorOffset) => {
|
|
2624
|
+
updateFoldBlock(null);
|
|
2625
|
+
setInput(next, cursorOffset, 'silent');
|
|
2626
|
+
setSelectedCommand(0);
|
|
2627
|
+
setFileSelected(0);
|
|
2390
2628
|
};
|
|
2391
2629
|
const vimDeleteRange = (start, end) => {
|
|
2392
2630
|
if (start >= end)
|
|
2393
2631
|
return;
|
|
2394
2632
|
vimPushUndo();
|
|
2395
2633
|
updateFoldBlock(null);
|
|
2396
|
-
deleteInputRange(start, end);
|
|
2634
|
+
deleteInputRange(start, end, 'silent');
|
|
2397
2635
|
setSelectedCommand(0);
|
|
2398
2636
|
setFileSelected(0);
|
|
2399
2637
|
};
|
|
@@ -2510,7 +2748,7 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
2510
2748
|
case 'd':
|
|
2511
2749
|
vimPendingRef.current = 'd';
|
|
2512
2750
|
return;
|
|
2513
|
-
case 'u': { // undo the last vim edit
|
|
2751
|
+
case 'u': { // undo the last vim edit (its own stack, not Ctrl+Z's)
|
|
2514
2752
|
syncImageGeneration();
|
|
2515
2753
|
const prev = vimUndoRef.current.pop();
|
|
2516
2754
|
if (prev === undefined)
|
|
@@ -2518,7 +2756,7 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
2518
2756
|
advanceDraftRevision();
|
|
2519
2757
|
replaceDraftImages(prev.images);
|
|
2520
2758
|
updateFoldBlock(null);
|
|
2521
|
-
setInput(prev.text, prev.cursor);
|
|
2759
|
+
setInput(prev.text, prev.cursor, 'silent');
|
|
2522
2760
|
setSelectedCommand(0);
|
|
2523
2761
|
setFileSelected(0);
|
|
2524
2762
|
return;
|
|
@@ -2591,12 +2829,17 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
2591
2829
|
return;
|
|
2592
2830
|
// `/` opens the slash-command menu even in NORMAL: insert it and
|
|
2593
2831
|
// switch to INSERT so the rest of the command types normally
|
|
2594
|
-
// (the menu then owns the keys while it is open).
|
|
2832
|
+
// (the menu then owns the keys while it is open). The insertion is
|
|
2833
|
+
// a NORMAL command, so it lands on the vim undo stack (`u` reverts
|
|
2834
|
+
// it); anything typed afterwards is a draft edit whose first
|
|
2835
|
+
// snapshot already contains the `/`, so `Ctrl+Z` never peels the
|
|
2836
|
+
// `/` off on its own.
|
|
2595
2837
|
if (input === '/') {
|
|
2596
2838
|
const text = valueRef.current;
|
|
2597
2839
|
const pos = cursorRef.current;
|
|
2598
2840
|
const next = text.slice(0, pos) + '/' + text.slice(pos);
|
|
2599
|
-
|
|
2841
|
+
vimPushUndo(); // NORMAL command: one vim-undo step, not a draft edit
|
|
2842
|
+
setInput(next, pos + 1, 'silent');
|
|
2600
2843
|
setSelectedCommand(0);
|
|
2601
2844
|
setFileSelected(0);
|
|
2602
2845
|
vimInsertRef.current = true;
|
|
@@ -2634,11 +2877,7 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
2634
2877
|
// A single Esc closes the open command menu first;
|
|
2635
2878
|
// the double-tap-clear semantics only apply to ordinary input.
|
|
2636
2879
|
if (overlayOpen) {
|
|
2637
|
-
|
|
2638
|
-
discardDraftImages();
|
|
2639
|
-
setInput('', 0);
|
|
2640
|
-
setSelectedCommand(0);
|
|
2641
|
-
setFileSelected(0);
|
|
2880
|
+
clearDraftUndoably();
|
|
2642
2881
|
return;
|
|
2643
2882
|
}
|
|
2644
2883
|
// File overlay: Esc dismisses the menu for THIS token only — clearing
|
|
@@ -2660,6 +2899,18 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
2660
2899
|
});
|
|
2661
2900
|
return;
|
|
2662
2901
|
}
|
|
2902
|
+
// "Send to Chat" chips peel before the draft: the first Esc drops the
|
|
2903
|
+
// contexts panels staged and leaves the typed text untouched, so a second
|
|
2904
|
+
// Esc then clears the input as usual. The press is CONSUMED — this rung
|
|
2905
|
+
// has exactly one meaning, and a running turn must not be interrupted by
|
|
2906
|
+
// the same key that only dropped a chip.
|
|
2907
|
+
const stagedContexts = channel.attachedContexts ?? [];
|
|
2908
|
+
if (stagedContexts.length > 0) {
|
|
2909
|
+
event?.stopImmediatePropagation();
|
|
2910
|
+
for (const staged of [...stagedContexts])
|
|
2911
|
+
channel.detachContext(staged.id);
|
|
2912
|
+
return;
|
|
2913
|
+
}
|
|
2663
2914
|
// A single Esc clears the current input (if any); the double-tap
|
|
2664
2915
|
// path below handles rewind/clear on an already-empty input. A BIG
|
|
2665
2916
|
// input folds into a block instead (Esc = the fold toggle; the
|
|
@@ -2672,28 +2923,19 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
2672
2923
|
setFileSelected(0);
|
|
2673
2924
|
return;
|
|
2674
2925
|
}
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
setSelectedCommand(0);
|
|
2679
|
-
setFileSelected(0);
|
|
2926
|
+
// Esc clearing the draft is undoable (D4): Ctrl+Z brings the whole
|
|
2927
|
+
// draft back, images and fold block included.
|
|
2928
|
+
clearDraftUndoably();
|
|
2680
2929
|
return;
|
|
2681
2930
|
}
|
|
2682
|
-
// Double-tap Esc
|
|
2683
|
-
//
|
|
2684
|
-
//
|
|
2931
|
+
// Double-tap Esc opens the rewind picker. Reaching here means the draft
|
|
2932
|
+
// is empty: every non-empty branch above returns after clearing, so the
|
|
2933
|
+
// second tap can only be the rewind gesture.
|
|
2685
2934
|
if (escPendingRef.current) {
|
|
2686
2935
|
escPendingRef.current = false;
|
|
2687
2936
|
if (escTimerRef.current)
|
|
2688
2937
|
clearTimeout(escTimerRef.current);
|
|
2689
|
-
|
|
2690
|
-
onRewindRequest?.();
|
|
2691
|
-
}
|
|
2692
|
-
else {
|
|
2693
|
-
syncImageGeneration();
|
|
2694
|
-
discardDraftImages();
|
|
2695
|
-
setInput('', 0);
|
|
2696
|
-
}
|
|
2938
|
+
onRewindRequest?.();
|
|
2697
2939
|
return;
|
|
2698
2940
|
}
|
|
2699
2941
|
escPendingRef.current = true;
|
|
@@ -2717,7 +2959,9 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
2717
2959
|
const next = sel
|
|
2718
2960
|
? value.slice(0, sel.start) + input + value.slice(sel.end)
|
|
2719
2961
|
: value.slice(0, cursor) + input + value.slice(cursor);
|
|
2720
|
-
|
|
2962
|
+
// Replacing a selection is atomic, exactly like `insertAtCaret`: the
|
|
2963
|
+
// replaced span must not fold into the surrounding typed run.
|
|
2964
|
+
setInput(next, at + input.length, sel ? 'step' : 'group');
|
|
2721
2965
|
setSelectedCommand(0);
|
|
2722
2966
|
setFileSelected(0);
|
|
2723
2967
|
}
|
|
@@ -3010,6 +3254,11 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
3010
3254
|
prevContentRowsRef.current = contentRows;
|
|
3011
3255
|
}, [contentRows]);
|
|
3012
3256
|
const lastNotification = channel.notifications[channel.notifications.length - 1];
|
|
3257
|
+
// The pet panel can "say" a notice with its speech bubble instead: while
|
|
3258
|
+
// that panel is the ACTIVE one the host suppresses the duplicate toast
|
|
3259
|
+
// here (errors always toast — they may need action). Same render, same
|
|
3260
|
+
// commit, so the swap cannot flash one frame of toast first.
|
|
3261
|
+
const toastVisible = lastNotification !== undefined && (toastSuppressed?.(lastNotification) ?? false) !== true;
|
|
3013
3262
|
// Park the native terminal cursor at the input caret (via the renderer's
|
|
3014
3263
|
// cursor-declaration mechanism). Terminal emulators render IME preedit
|
|
3015
3264
|
// text and screen-reader focus at the physical cursor, so parking it at
|
|
@@ -3246,6 +3495,13 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
3246
3495
|
// 顶边框右侧的会话名标签 chip:色随强调色;超宽截断,宽度
|
|
3247
3496
|
// 随终端列数伸缩但不超过 28 显示单元。默认关闭——`/settings` 的
|
|
3248
3497
|
// 「会话名标签」开关(dsh-tui.promptSessionLabel)开启后显示。
|
|
3498
|
+
// "Send to Chat" chips (side-panel §6.7): the contexts panels staged for the
|
|
3499
|
+
// next submission, rendered as one strip directly above the prompt border.
|
|
3500
|
+
// Read defensively — hosts and older fixtures mount this composer with a
|
|
3501
|
+
// partial channel stub (Chat guards `agentViewRows` for the same reason),
|
|
3502
|
+
// and a missing projection means "nothing attached". A render-time TypeError
|
|
3503
|
+
// here would be swallowed by ink and silently freeze the screen.
|
|
3504
|
+
const attachedContexts = channel.attachedContexts ?? [];
|
|
3249
3505
|
const sessionTitle = channel.sessionTitle ?? '';
|
|
3250
3506
|
const topRightLabel = channel.promptSessionLabel === true && sessionTitle !== ''
|
|
3251
3507
|
? {
|
|
@@ -3338,7 +3594,8 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
3338
3594
|
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [floatersOpen && (_jsxs(OverlayAbove, { maxHeight: Math.max(terminalRows - 6, 1), children: [helpOpen && (_jsx(Box, { marginBottom: 1, children: _jsx(HelpMenu, { commands: channel.commandList, viewportHeight: helpViewportHeight, viewportWidth: columns, scrollRef: helpScrollRef, onCommandPick: (name) => {
|
|
3339
3595
|
// 点击命令行 = 填入 /name 并关闭帮助(Tab 补全的鼠标等价)
|
|
3340
3596
|
updateFoldBlock(null);
|
|
3341
|
-
|
|
3597
|
+
// `/${name} ` — the whole fill is one undo step.
|
|
3598
|
+
setInput(`/${name} `, name.length + 2, 'step');
|
|
3342
3599
|
onToggleHelp();
|
|
3343
3600
|
} }) })), !helpOpen && channel.pending.length > 0 && (_jsxs(Box, { flexDirection: "column", paddingLeft: 2, paddingBottom: 1, children: [channel.pending.some(item => item.placement === 'steer') && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { dimColor: true, children: ["\u26A1 ", t('input-pending-steer-label')] }), channel.pending
|
|
3344
3601
|
.filter(item => item.placement === 'steer')
|
|
@@ -3369,7 +3626,7 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
|
|
|
3369
3626
|
: undefined, rows: peekVisualLines.map((row, index) => (_jsx(Text, { wrap: "truncate-end", children: row }, index))), onRowPick: () => {
|
|
3370
3627
|
updateFoldBlock(null);
|
|
3371
3628
|
setHovered(false);
|
|
3372
|
-
} }) }))] })), lastNotification && (_jsx(Box, { position: "absolute", marginTop: -1, height: 1, width: "100%", paddingLeft: 2, paddingRight: 1, flexDirection: "column", justifyContent: "flex-end", overflow: "hidden", children: _jsx(Box, { justifyContent: "flex-end", children: _jsx(Text, { color: lastNotification.color, dimColor: !lastNotification.color, wrap: "truncate", children: lastNotification.text }) }) })), _jsx(EffortInputBorder, { effort: channel.reasoningEffort, levels: channel.effortLevels, columns: columns, onLight: isLightThemeActive(themeName), idleColor: promptAccent, topRightLabel: topRightLabel, children: _jsxs(Box, { flexDirection: "row", alignItems: "flex-start", width: "100%", children: [onOpenSessions !== undefined && (_jsx(Box, { flexShrink: 0, onClick: (event) => {
|
|
3629
|
+
} }) }))] })), attachedContexts.length > 0 && (_jsx(Box, { flexDirection: "row", width: "100%", height: 1, overflow: "hidden", paddingLeft: 2, columnGap: 2, children: attachedContexts.map(item => (_jsx(Box, { overflow: "hidden", children: _jsx(Text, { color: promptAccent, wrap: "truncate-end", children: t('prompt-attached-context-chip', { title: item.title }) }) }, item.id))) })), lastNotification && toastVisible && (_jsx(Box, { position: "absolute", marginTop: -1, height: 1, width: "100%", paddingLeft: 2, paddingRight: 1, flexDirection: "column", justifyContent: "flex-end", overflow: "hidden", children: _jsx(Box, { justifyContent: "flex-end", children: _jsx(Text, { color: lastNotification.color, dimColor: !lastNotification.color, wrap: "truncate", children: lastNotification.text }) }) })), _jsx(EffortInputBorder, { effort: channel.reasoningEffort, levels: channel.effortLevels, columns: columns, onLight: isLightThemeActive(themeName), idleColor: promptAccent, topRightLabel: topRightLabel, children: _jsxs(Box, { flexDirection: "row", alignItems: "flex-start", width: "100%", children: [onOpenSessions !== undefined && (_jsx(Box, { flexShrink: 0, onClick: (event) => {
|
|
3373
3630
|
event.stopImmediatePropagation();
|
|
3374
3631
|
onOpenSessions?.();
|
|
3375
3632
|
}, onMouseEnter: () => {
|