@noorslvt/shev 0.2.0 → 0.2.1
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/Cargo.lock +1 -1
- package/Cargo.toml +1 -1
- package/README.md +25 -6
- package/assets/readme/app-decision.png +0 -0
- package/assets/readme/app-home.png +0 -0
- package/assets/readme/hero.jpg +0 -0
- package/install.sh +1 -1
- package/package.json +1 -1
- package/src/bin/shev.rs +65 -2
package/Cargo.lock
CHANGED
package/Cargo.toml
CHANGED
package/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+

|
|
2
|
+
|
|
1
3
|
# shev
|
|
2
4
|
|
|
3
5
|
**A simple Jev-like decision maker. Without the hype.**
|
|
@@ -46,19 +48,32 @@ profile; open a new terminal and type `shev`—no manual shell edits needed.
|
|
|
46
48
|
Prefer npm?
|
|
47
49
|
|
|
48
50
|
```sh
|
|
49
|
-
npm install -g
|
|
51
|
+
npm install -g @noorslvt/shev
|
|
50
52
|
shev
|
|
51
53
|
```
|
|
52
54
|
|
|
53
55
|
This installs the package **`@noorslvt/shev`**, with the terminal commands
|
|
54
56
|
`shev` and `shev-cli`. The unscoped npm registry name `shev` belongs to an
|
|
55
|
-
unrelated project; **do not use `npm install -g shev`** for this tool.
|
|
56
|
-
|
|
57
|
+
unrelated project; **do not use `npm install -g shev`** for this tool. You can
|
|
58
|
+
also install straight from the repo without the registry:
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
npm install -g git+https://github.com/aditauqir/shev.git
|
|
62
|
+
```
|
|
57
63
|
|
|
58
64
|
Both installers prefer a matching prebuilt release and otherwise build with
|
|
59
|
-
Rust (installing the minimal Rust toolchain if necessary). npm
|
|
60
|
-
|
|
61
|
-
supported by the npm launcher
|
|
65
|
+
Rust (installing the minimal Rust toolchain if necessary). npm runs a
|
|
66
|
+
`postinstall` step that fetches the right binary or builds it; macOS/Linux are
|
|
67
|
+
supported by the npm launcher, and Windows users can use WSL.
|
|
68
|
+
|
|
69
|
+
Recent npm releases gate install scripts. If your install is skipped, `shev`
|
|
70
|
+
tells you what to run; you can also fix it up front:
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
# if npm blocked the install scripts:
|
|
74
|
+
npm approve-scripts @noorslvt/shev # or:
|
|
75
|
+
npm rebuild -g @noorslvt/shev --foreground-scripts
|
|
76
|
+
```
|
|
62
77
|
|
|
63
78
|
Got Rust already? Same thing, just as short:
|
|
64
79
|
|
|
@@ -145,6 +160,10 @@ Text panels use **`#0F0E08`** over the dimmed photo wallpaper. Type `/` for comm
|
|
|
145
160
|
Panels are inset from the terminal edges. Skills/tools menus show names only;
|
|
146
161
|
long menus scroll around the selected row instead of clipping it off-screen.
|
|
147
162
|
|
|
163
|
+

|
|
164
|
+
|
|
165
|
+

|
|
166
|
+
|
|
148
167
|
| Command | What it does |
|
|
149
168
|
| --- | --- |
|
|
150
169
|
| `/file <path>` | Load a message from a file (drag-and-drop works too) |
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/install.sh
CHANGED
package/package.json
CHANGED
package/src/bin/shev.rs
CHANGED
|
@@ -1970,6 +1970,17 @@ fn render_header(frame: &mut Frame, app: &mut App, area: Rect) {
|
|
|
1970
1970
|
Span::styled("shev", accent_bold()),
|
|
1971
1971
|
Span::styled(" — decisions, without the hype", dim()),
|
|
1972
1972
|
]);
|
|
1973
|
+
let title_width: u16 = title
|
|
1974
|
+
.spans
|
|
1975
|
+
.iter()
|
|
1976
|
+
.map(|span| span.content.chars().count() as u16)
|
|
1977
|
+
.sum();
|
|
1978
|
+
// the wallpaper paints half-block glyphs into every cell; without a solid fill
|
|
1979
|
+
// first, restyling the bar leaves those glyphs glowing after the title.
|
|
1980
|
+
frame.render_widget(
|
|
1981
|
+
Paragraph::new(Line::from(" ".repeat(area.width as usize))).style(base()),
|
|
1982
|
+
area,
|
|
1983
|
+
);
|
|
1973
1984
|
frame.render_widget(Paragraph::new(title).style(base()), area);
|
|
1974
1985
|
|
|
1975
1986
|
let button = if app.home {
|
|
@@ -1981,7 +1992,8 @@ fn render_header(frame: &mut Frame, app: &mut App, area: Rect) {
|
|
|
1981
1992
|
return;
|
|
1982
1993
|
};
|
|
1983
1994
|
let width = label.chars().count() as u16;
|
|
1984
|
-
|
|
1995
|
+
// only place the button when it will not sit on top of the tagline
|
|
1996
|
+
if area.width < title_width + width + 2 {
|
|
1985
1997
|
return;
|
|
1986
1998
|
}
|
|
1987
1999
|
let rect = Rect {
|
|
@@ -2094,7 +2106,14 @@ fn mode_line(app: &App) -> Line<'static> {
|
|
|
2094
2106
|
Stage::Intent => ("Intent", "drives the response".to_string()),
|
|
2095
2107
|
Stage::Working => (
|
|
2096
2108
|
"Build",
|
|
2097
|
-
format!(
|
|
2109
|
+
format!(
|
|
2110
|
+
"{} asking {}…",
|
|
2111
|
+
SPINNER[app.spinner % SPINNER.len()],
|
|
2112
|
+
match app.provider {
|
|
2113
|
+
Provider::DeepSeek => "DeepSeek",
|
|
2114
|
+
Provider::Bonsai => "bonsai (local)",
|
|
2115
|
+
}
|
|
2116
|
+
),
|
|
2098
2117
|
),
|
|
2099
2118
|
Stage::Result => (
|
|
2100
2119
|
"Build",
|
|
@@ -3706,6 +3725,50 @@ mod tests {
|
|
|
3706
3725
|
assert!(screen.contains("/ commands"), "{screen}");
|
|
3707
3726
|
}
|
|
3708
3727
|
|
|
3728
|
+
#[test]
|
|
3729
|
+
fn the_header_bar_does_not_let_the_wallpaper_bleed_through() {
|
|
3730
|
+
let mut app = app_for_test();
|
|
3731
|
+
app.stage = Stage::Message;
|
|
3732
|
+
let screen = draw_to_string(&mut app, 60, 24);
|
|
3733
|
+
let header = screen.lines().nth(1).expect("a header row");
|
|
3734
|
+
assert!(
|
|
3735
|
+
header.contains("shev — decisions, without the hype"),
|
|
3736
|
+
"{header:?}"
|
|
3737
|
+
);
|
|
3738
|
+
// the bar is inset two columns from each edge; the wallpaper lives in that
|
|
3739
|
+
// margin, but nothing inside the bar may keep its half-block glyphs, or the
|
|
3740
|
+
// leftover cells glow as a bright strip after the title
|
|
3741
|
+
let chars: Vec<char> = header.chars().collect();
|
|
3742
|
+
let bar: String = chars[2..chars.len() - 2].iter().collect();
|
|
3743
|
+
assert!(
|
|
3744
|
+
!bar.contains('▀'),
|
|
3745
|
+
"wallpaper glyphs leaked into the header bar: {bar:?}"
|
|
3746
|
+
);
|
|
3747
|
+
}
|
|
3748
|
+
|
|
3749
|
+
#[test]
|
|
3750
|
+
fn the_working_line_names_the_model_in_use() {
|
|
3751
|
+
let line = |app: &App| -> String {
|
|
3752
|
+
mode_line(app)
|
|
3753
|
+
.spans
|
|
3754
|
+
.iter()
|
|
3755
|
+
.map(|span| span.content.to_string())
|
|
3756
|
+
.collect()
|
|
3757
|
+
};
|
|
3758
|
+
|
|
3759
|
+
let mut app = app_for_test();
|
|
3760
|
+
app.stage = Stage::Working;
|
|
3761
|
+
|
|
3762
|
+
app.provider = Provider::Bonsai;
|
|
3763
|
+
let bonsai = line(&app);
|
|
3764
|
+
assert!(bonsai.contains("asking bonsai (local)"), "{bonsai}");
|
|
3765
|
+
assert!(!bonsai.to_lowercase().contains("deepseek"), "{bonsai}");
|
|
3766
|
+
|
|
3767
|
+
app.provider = Provider::DeepSeek;
|
|
3768
|
+
let deepseek = line(&app);
|
|
3769
|
+
assert!(deepseek.contains("asking DeepSeek"), "{deepseek}");
|
|
3770
|
+
}
|
|
3771
|
+
|
|
3709
3772
|
#[test]
|
|
3710
3773
|
fn shows_a_decision_as_a_clean_list_with_the_box_underneath() {
|
|
3711
3774
|
let mut app = app_for_test();
|