@akiojin/gwt 6.30.3 → 9.0.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.
Files changed (98) hide show
  1. package/.cargo/config.toml +2 -0
  2. package/.claude-plugin/marketplace.json +18 -0
  3. package/.coderabbit.yaml +8 -0
  4. package/.codex/skills/gwt-fix-issue/scripts/inspect_issue.py +833 -0
  5. package/.dockerignore +63 -0
  6. package/.gitattributes +27 -0
  7. package/.husky/commit-msg +2 -0
  8. package/.husky/pre-commit +9 -0
  9. package/.husky/pre-push +12 -0
  10. package/.markdownlint.json +18 -0
  11. package/.markdownlintignore +2 -0
  12. package/Dockerfile +58 -0
  13. package/README.ja.md +161 -484
  14. package/README.md +164 -444
  15. package/cliff.toml +56 -0
  16. package/clippy.toml +2 -0
  17. package/cmake/ci-disable-native.cmake +16 -0
  18. package/codecov.yml +16 -0
  19. package/commitlint.config.cjs +107 -0
  20. package/deny.toml +35 -0
  21. package/docker-compose.yml +59 -0
  22. package/messages/errors.toml +52 -0
  23. package/package.json +12 -22
  24. package/rustfmt.toml +8 -0
  25. package/scripts/check-e2e-coverage-threshold.mjs +238 -0
  26. package/scripts/entrypoint.sh +36 -25
  27. package/scripts/install-linux-deps.sh +46 -0
  28. package/scripts/postinstall.js +79 -227
  29. package/scripts/release_issue_refs.py +317 -0
  30. package/scripts/run-local-backend-tests-on-commit.sh +15 -0
  31. package/scripts/run-local-e2e-coverage-on-commit.sh +69 -0
  32. package/scripts/run-local-e2e-on-commit.sh +60 -0
  33. package/scripts/test-all.sh +13 -0
  34. package/scripts/test_release_issue_refs.py +257 -0
  35. package/scripts/validate-skill-frontmatter.sh +108 -0
  36. package/scripts/verify-ci-node-toolchain.sh +76 -0
  37. package/scripts/verify-husky-hooks.sh +6 -0
  38. package/scripts/voice-eval.sh +48 -0
  39. package/tests/voice_eval/README.md +53 -0
  40. package/tests/voice_eval/manifest.template.json +55 -0
  41. package/tests/voice_eval/samples/.gitkeep +1 -0
  42. package/tests/voice_eval/script-ja.txt +10 -0
  43. package/vendor/ratatui-core/src/backend/test.rs +1077 -0
  44. package/vendor/ratatui-core/src/backend.rs +405 -0
  45. package/vendor/ratatui-core/src/buffer/assert.rs +71 -0
  46. package/vendor/ratatui-core/src/buffer/buffer.rs +1388 -0
  47. package/vendor/ratatui-core/src/buffer/cell.rs +377 -0
  48. package/vendor/ratatui-core/src/buffer.rs +9 -0
  49. package/vendor/ratatui-core/src/layout/alignment.rs +89 -0
  50. package/vendor/ratatui-core/src/layout/constraint.rs +526 -0
  51. package/vendor/ratatui-core/src/layout/direction.rs +63 -0
  52. package/vendor/ratatui-core/src/layout/flex.rs +212 -0
  53. package/vendor/ratatui-core/src/layout/layout.rs +2838 -0
  54. package/vendor/ratatui-core/src/layout/margin.rs +79 -0
  55. package/vendor/ratatui-core/src/layout/offset.rs +66 -0
  56. package/vendor/ratatui-core/src/layout/position.rs +253 -0
  57. package/vendor/ratatui-core/src/layout/rect/iter.rs +356 -0
  58. package/vendor/ratatui-core/src/layout/rect/ops.rs +136 -0
  59. package/vendor/ratatui-core/src/layout/rect.rs +1114 -0
  60. package/vendor/ratatui-core/src/layout/size.rs +147 -0
  61. package/vendor/ratatui-core/src/layout.rs +333 -0
  62. package/vendor/ratatui-core/src/lib.rs +82 -0
  63. package/vendor/ratatui-core/src/style/anstyle.rs +348 -0
  64. package/vendor/ratatui-core/src/style/color.rs +788 -0
  65. package/vendor/ratatui-core/src/style/palette/material.rs +608 -0
  66. package/vendor/ratatui-core/src/style/palette/tailwind.rs +653 -0
  67. package/vendor/ratatui-core/src/style/palette.rs +6 -0
  68. package/vendor/ratatui-core/src/style/palette_conversion.rs +82 -0
  69. package/vendor/ratatui-core/src/style/stylize.rs +668 -0
  70. package/vendor/ratatui-core/src/style.rs +1069 -0
  71. package/vendor/ratatui-core/src/symbols/bar.rs +51 -0
  72. package/vendor/ratatui-core/src/symbols/block.rs +51 -0
  73. package/vendor/ratatui-core/src/symbols/border.rs +709 -0
  74. package/vendor/ratatui-core/src/symbols/braille.rs +21 -0
  75. package/vendor/ratatui-core/src/symbols/half_block.rs +3 -0
  76. package/vendor/ratatui-core/src/symbols/line.rs +259 -0
  77. package/vendor/ratatui-core/src/symbols/marker.rs +82 -0
  78. package/vendor/ratatui-core/src/symbols/merge.rs +748 -0
  79. package/vendor/ratatui-core/src/symbols/pixel.rs +30 -0
  80. package/vendor/ratatui-core/src/symbols/scrollbar.rs +46 -0
  81. package/vendor/ratatui-core/src/symbols/shade.rs +5 -0
  82. package/vendor/ratatui-core/src/symbols.rs +15 -0
  83. package/vendor/ratatui-core/src/terminal/frame.rs +192 -0
  84. package/vendor/ratatui-core/src/terminal/terminal.rs +926 -0
  85. package/vendor/ratatui-core/src/terminal/viewport.rs +58 -0
  86. package/vendor/ratatui-core/src/terminal.rs +40 -0
  87. package/vendor/ratatui-core/src/text/grapheme.rs +84 -0
  88. package/vendor/ratatui-core/src/text/line.rs +1678 -0
  89. package/vendor/ratatui-core/src/text/masked.rs +149 -0
  90. package/vendor/ratatui-core/src/text/span.rs +904 -0
  91. package/vendor/ratatui-core/src/text/text.rs +1434 -0
  92. package/vendor/ratatui-core/src/text.rs +64 -0
  93. package/vendor/ratatui-core/src/widgets/stateful_widget.rs +193 -0
  94. package/vendor/ratatui-core/src/widgets/widget.rs +174 -0
  95. package/vendor/ratatui-core/src/widgets.rs +9 -0
  96. package/bin/gwt.js +0 -131
  97. package/scripts/postinstall.test.js +0 -71
  98. package/scripts/release-download.js +0 -66
@@ -0,0 +1,64 @@
1
+ //! Primitives for styled text.
2
+ //!
3
+ //! A terminal UI is at its root a lot of strings. In order to make it accessible and stylish, those
4
+ //! strings may be associated to a set of styles. `ratatui` has three ways to represent them:
5
+ //! - A single line string where all graphemes have the same style is represented by a [`Span`].
6
+ //! - A single line string where each grapheme may have its own style is represented by [`Line`].
7
+ //! - A multiple line string where each grapheme may have its own style is represented by a
8
+ //! [`Text`].
9
+ //!
10
+ //! These types form a hierarchy: [`Line`] is a collection of [`Span`] and each line of [`Text`] is
11
+ //! a [`Line`].
12
+ //!
13
+ //! Keep it mind that a lot of widgets will use those types to advertise what kind of string is
14
+ //! supported for their properties. Moreover, `ratatui` provides convenient `From` implementations
15
+ //! so that you can start by using simple `String` or `&str` and then promote them to the previous
16
+ //! primitives when you need additional styling capabilities.
17
+ //!
18
+ //! For example, for the `Block` widget, all the following calls are valid to set its `title`
19
+ //! property (which is a [`Line`] under the hood):
20
+ //!
21
+ //! ```rust,ignore
22
+ //! use ratatui_core::{
23
+ //! style::{Color, Style},
24
+ //! text::{Line, Span},
25
+ //! widgets::Block,
26
+ //! };
27
+ //!
28
+ //! // A simple string with no styling.
29
+ //! // Converted to Line(vec![
30
+ //! // Span { content: Cow::Borrowed("My title"), style: Style { .. } }
31
+ //! // ])
32
+ //! let block = Block::new().title("My title");
33
+ //!
34
+ //! // A simple string with a unique style.
35
+ //! // Converted to Line(vec![
36
+ //! // Span { content: Cow::Borrowed("My title"), style: Style { fg: Some(Color::Yellow), .. }
37
+ //! // ])
38
+ //! let block = Block::new().title(Span::styled("My title", Style::default().fg(Color::Yellow)));
39
+ //!
40
+ //! // A string with multiple styles.
41
+ //! // Converted to Line(vec![
42
+ //! // Span { content: Cow::Borrowed("My"), style: Style { fg: Some(Color::Yellow), .. } },
43
+ //! // Span { content: Cow::Borrowed(" title"), .. }
44
+ //! // ])
45
+ //! let block = Block::new().title(vec![
46
+ //! Span::styled("My", Style::default().fg(Color::Yellow)),
47
+ //! Span::raw(" title"),
48
+ //! ]);
49
+ //! ```
50
+
51
+ mod grapheme;
52
+ pub use grapheme::StyledGrapheme;
53
+
54
+ mod line;
55
+ pub use line::{Line, ToLine};
56
+
57
+ mod masked;
58
+ pub use masked::Masked;
59
+
60
+ mod span;
61
+ pub use span::{Span, ToSpan};
62
+
63
+ mod text;
64
+ pub use text::{Text, ToText};
@@ -0,0 +1,193 @@
1
+ use crate::buffer::Buffer;
2
+ use crate::layout::Rect;
3
+
4
+ /// A `StatefulWidget` is a widget that can take advantage of some local state to remember things
5
+ /// between two draw calls.
6
+ ///
7
+ /// For a comprehensive guide to widgets, including trait explanations, implementation patterns,
8
+ /// and available widgets, see the [`widgets`] module documentation.
9
+ ///
10
+ /// [`widgets`]: ../../ratatui/widgets/index.html
11
+ ///
12
+ /// Most widgets can be drawn directly based on the input parameters. However, some features may
13
+ /// require some kind of associated state to be implemented.
14
+ ///
15
+ /// For example, the `List` widget can highlight the item currently selected. This can be translated
16
+ /// in an offset, which is the number of elements to skip in order to have the selected item within
17
+ /// the viewport currently allocated to this widget. The widget can therefore only provide the
18
+ /// following behavior: whenever the selected item is out of the viewport scroll to a predefined
19
+ /// position (making the selected item the last viewable item or the one in the middle for example).
20
+ /// Nonetheless, if the widget has access to the last computed offset then it can implement a
21
+ /// natural scrolling experience where the last offset is reused until the selected item is out of
22
+ /// the viewport.
23
+ ///
24
+ /// ## Examples
25
+ ///
26
+ /// ```rust,ignore
27
+ /// use std::io;
28
+ ///
29
+ /// use ratatui::{
30
+ /// backend::TestBackend,
31
+ /// widgets::{List, ListItem, ListState, StatefulWidget, Widget},
32
+ /// Terminal,
33
+ /// };
34
+ ///
35
+ /// // Let's say we have some events to display.
36
+ /// struct Events {
37
+ /// // `items` is the state managed by your application.
38
+ /// items: Vec<String>,
39
+ /// // `state` is the state that can be modified by the UI. It stores the index of the selected
40
+ /// // item as well as the offset computed during the previous draw call (used to implement
41
+ /// // natural scrolling).
42
+ /// state: ListState,
43
+ /// }
44
+ ///
45
+ /// impl Events {
46
+ /// fn new(items: Vec<String>) -> Events {
47
+ /// Events {
48
+ /// items,
49
+ /// state: ListState::default(),
50
+ /// }
51
+ /// }
52
+ ///
53
+ /// pub fn set_items(&mut self, items: Vec<String>) {
54
+ /// self.items = items;
55
+ /// // We reset the state as the associated items have changed. This effectively reset
56
+ /// // the selection as well as the stored offset.
57
+ /// self.state = ListState::default();
58
+ /// }
59
+ ///
60
+ /// // Select the next item. This will not be reflected until the widget is drawn in the
61
+ /// // `Terminal::draw` callback using `Frame::render_stateful_widget`.
62
+ /// pub fn next(&mut self) {
63
+ /// let i = match self.state.selected() {
64
+ /// Some(i) => {
65
+ /// if i >= self.items.len() - 1 {
66
+ /// 0
67
+ /// } else {
68
+ /// i + 1
69
+ /// }
70
+ /// }
71
+ /// None => 0,
72
+ /// };
73
+ /// self.state.select(Some(i));
74
+ /// }
75
+ ///
76
+ /// // Select the previous item. This will not be reflected until the widget is drawn in the
77
+ /// // `Terminal::draw` callback using `Frame::render_stateful_widget`.
78
+ /// pub fn previous(&mut self) {
79
+ /// let i = match self.state.selected() {
80
+ /// Some(i) => {
81
+ /// if i == 0 {
82
+ /// self.items.len() - 1
83
+ /// } else {
84
+ /// i - 1
85
+ /// }
86
+ /// }
87
+ /// None => 0,
88
+ /// };
89
+ /// self.state.select(Some(i));
90
+ /// }
91
+ ///
92
+ /// // Unselect the currently selected item if any. The implementation of `ListState` makes
93
+ /// // sure that the stored offset is also reset.
94
+ /// pub fn unselect(&mut self) {
95
+ /// self.state.select(None);
96
+ /// }
97
+ /// }
98
+ ///
99
+ /// # let backend = TestBackend::new(5, 5);
100
+ /// # let mut terminal = Terminal::new(backend).unwrap();
101
+ ///
102
+ /// let mut events = Events::new(vec![String::from("Item 1"), String::from("Item 2")]);
103
+ ///
104
+ /// loop {
105
+ /// terminal.draw(|f| {
106
+ /// // The items managed by the application are transformed to something
107
+ /// // that is understood by ratatui.
108
+ /// let items: Vec<ListItem> = events
109
+ /// .items
110
+ /// .iter()
111
+ /// .map(|i| ListItem::new(i.as_str()))
112
+ /// .collect();
113
+ /// // The `List` widget is then built with those items.
114
+ /// let list = List::new(items);
115
+ /// // Finally the widget is rendered using the associated state. `events.state` is
116
+ /// // effectively the only thing that we will "remember" from this draw call.
117
+ /// f.render_stateful_widget(list, f.size(), &mut events.state);
118
+ /// });
119
+ ///
120
+ /// // In response to some input events or an external http request or whatever:
121
+ /// events.next();
122
+ /// }
123
+ /// ```
124
+ pub trait StatefulWidget {
125
+ /// State associated with the stateful widget.
126
+ ///
127
+ /// If you don't need this then you probably want to implement [`Widget`] instead.
128
+ ///
129
+ /// [`Widget`]: super::Widget
130
+ type State: ?Sized;
131
+ /// Draws the current state of the widget in the given buffer. That is the only method required
132
+ /// to implement a custom stateful widget.
133
+ fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State);
134
+ }
135
+
136
+ #[cfg(test)]
137
+ mod tests {
138
+ use alloc::format;
139
+ use alloc::string::{String, ToString};
140
+
141
+ use rstest::{fixture, rstest};
142
+
143
+ use super::*;
144
+ use crate::buffer::Buffer;
145
+ use crate::layout::Rect;
146
+ use crate::text::Line;
147
+ use crate::widgets::Widget;
148
+
149
+ #[fixture]
150
+ fn buf() -> Buffer {
151
+ Buffer::empty(Rect::new(0, 0, 20, 1))
152
+ }
153
+
154
+ #[fixture]
155
+ fn state() -> String {
156
+ "world".to_string()
157
+ }
158
+
159
+ struct PersonalGreeting;
160
+
161
+ impl StatefulWidget for PersonalGreeting {
162
+ type State = String;
163
+ fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
164
+ Line::from(format!("Hello {state}")).render(area, buf);
165
+ }
166
+ }
167
+
168
+ #[rstest]
169
+ fn render(mut buf: Buffer, mut state: String) {
170
+ let widget = PersonalGreeting;
171
+ widget.render(buf.area, &mut buf, &mut state);
172
+ assert_eq!(buf, Buffer::with_lines(["Hello world "]));
173
+ }
174
+
175
+ struct Bytes;
176
+
177
+ /// A widget with an unsized state type.
178
+ impl StatefulWidget for Bytes {
179
+ type State = [u8];
180
+ fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
181
+ let slice = core::str::from_utf8(state).unwrap();
182
+ Line::from(format!("Bytes: {slice}")).render(area, buf);
183
+ }
184
+ }
185
+
186
+ #[rstest]
187
+ fn render_unsized_state_type(mut buf: Buffer) {
188
+ let widget = Bytes;
189
+ let state = b"hello";
190
+ widget.render(buf.area, &mut buf, &mut state.clone());
191
+ assert_eq!(buf, Buffer::with_lines(["Bytes: hello "]));
192
+ }
193
+ }
@@ -0,0 +1,174 @@
1
+ use alloc::string::String;
2
+
3
+ use crate::buffer::Buffer;
4
+ use crate::layout::Rect;
5
+ use crate::style::Style;
6
+
7
+ /// A `Widget` is a type that can be drawn on a [`Buffer`] in a given [`Rect`].
8
+ ///
9
+ /// For a comprehensive guide to widgets, including trait explanations, implementation patterns,
10
+ /// and available widgets, see the [`widgets`] module documentation.
11
+ ///
12
+ /// [`widgets`]: ../../ratatui/widgets/index.html
13
+ ///
14
+ /// Prior to Ratatui 0.26.0, widgets generally were created for each frame as they were consumed
15
+ /// during rendering. This meant that they were not meant to be stored but used as *commands* to
16
+ /// draw common figures in the UI.
17
+ ///
18
+ /// Starting with Ratatui 0.26.0, all the internal widgets implement Widget for a reference to
19
+ /// themselves. This allows you to store a reference to a widget and render it later. Widget crates
20
+ /// should consider also doing this to allow for more flexibility in how widgets are used.
21
+ ///
22
+ /// In Ratatui 0.26.0, we also added an unstable `WidgetRef` trait and implemented this on all the
23
+ /// internal widgets. In addition to the above benefit of rendering references to widgets, this also
24
+ /// allows you to render boxed widgets. This is useful when you want to store a collection of
25
+ /// widgets with different types. You can then iterate over the collection and render each widget.
26
+ /// See <https://github.com/ratatui/ratatui/issues/1287> for more information.
27
+ ///
28
+ /// In general where you expect a widget to immutably work on its data, we recommended to implement
29
+ /// `Widget` for a reference to the widget (`impl Widget for &MyWidget`). If you need to store state
30
+ /// between draw calls, implement `StatefulWidget` if you want the Widget to be immutable, or
31
+ /// implement `Widget` for a mutable reference to the widget (`impl Widget for &mut MyWidget`) if
32
+ /// you want the widget to be mutable. The mutable widget pattern is used infrequently in apps, but
33
+ /// can be quite useful.
34
+ ///
35
+ /// A blanket implementation of `Widget` for `&W` where `W` implements `WidgetRef` is provided.
36
+ /// Widget is also implemented for `&str` and `String` types.
37
+ ///
38
+ /// # Examples
39
+ ///
40
+ /// ```rust,ignore
41
+ /// use ratatui::{
42
+ /// backend::TestBackend,
43
+ /// widgets::{Clear, Widget},
44
+ /// Terminal,
45
+ /// };
46
+ /// # let backend = TestBackend::new(5, 5);
47
+ /// # let mut terminal = Terminal::new(backend).unwrap();
48
+ ///
49
+ /// terminal.draw(|frame| {
50
+ /// frame.render_widget(Clear, frame.area());
51
+ /// });
52
+ /// ```
53
+ ///
54
+ /// It's common to render widgets inside other widgets:
55
+ ///
56
+ /// ```rust
57
+ /// use ratatui_core::buffer::Buffer;
58
+ /// use ratatui_core::layout::Rect;
59
+ /// use ratatui_core::text::Line;
60
+ /// use ratatui_core::widgets::Widget;
61
+ ///
62
+ /// struct MyWidget;
63
+ ///
64
+ /// impl Widget for MyWidget {
65
+ /// fn render(self, area: Rect, buf: &mut Buffer) {
66
+ /// Line::raw("Hello").render(area, buf);
67
+ /// }
68
+ /// }
69
+ /// ```
70
+ pub trait Widget {
71
+ /// Draws the current state of the widget in the given buffer. That is the only method required
72
+ /// to implement a custom widget.
73
+ fn render(self, area: Rect, buf: &mut Buffer)
74
+ where
75
+ Self: Sized;
76
+ }
77
+
78
+ /// Renders a string slice as a widget.
79
+ ///
80
+ /// This implementation allows a string slice (`&str`) to act as a widget, meaning it can be drawn
81
+ /// onto a [`Buffer`] in a specified [`Rect`]. The slice represents a static string which can be
82
+ /// rendered by reference, thereby avoiding the need for string cloning or ownership transfer when
83
+ /// drawing the text to the screen.
84
+ impl Widget for &str {
85
+ fn render(self, area: Rect, buf: &mut Buffer) {
86
+ buf.set_stringn(area.x, area.y, self, area.width as usize, Style::new());
87
+ }
88
+ }
89
+
90
+ /// Renders a `String` object as a widget.
91
+ ///
92
+ /// This implementation enables an owned `String` to be treated as a widget, which can be rendered
93
+ /// on a [`Buffer`] within the bounds of a given [`Rect`].
94
+ impl Widget for String {
95
+ fn render(self, area: Rect, buf: &mut Buffer) {
96
+ buf.set_stringn(area.x, area.y, self, area.width as usize, Style::new());
97
+ }
98
+ }
99
+
100
+ impl<W: Widget> Widget for Option<W> {
101
+ fn render(self, area: Rect, buf: &mut Buffer) {
102
+ if let Some(widget) = self {
103
+ widget.render(area, buf);
104
+ }
105
+ }
106
+ }
107
+
108
+ #[cfg(test)]
109
+ mod tests {
110
+ use rstest::{fixture, rstest};
111
+
112
+ use super::*;
113
+ use crate::buffer::Buffer;
114
+ use crate::layout::Rect;
115
+ use crate::text::Line;
116
+
117
+ #[fixture]
118
+ fn buf() -> Buffer {
119
+ Buffer::empty(Rect::new(0, 0, 20, 1))
120
+ }
121
+
122
+ struct Greeting;
123
+
124
+ impl Widget for Greeting {
125
+ fn render(self, area: Rect, buf: &mut Buffer) {
126
+ Line::from("Hello").render(area, buf);
127
+ }
128
+ }
129
+
130
+ #[rstest]
131
+ fn render(mut buf: Buffer) {
132
+ let widget = Greeting;
133
+ widget.render(buf.area, &mut buf);
134
+ assert_eq!(buf, Buffer::with_lines(["Hello "]));
135
+ }
136
+
137
+ #[rstest]
138
+ fn render_str(mut buf: Buffer) {
139
+ "hello world".render(buf.area, &mut buf);
140
+ assert_eq!(buf, Buffer::with_lines(["hello world "]));
141
+ }
142
+
143
+ #[rstest]
144
+ fn render_str_truncate(mut buf: Buffer) {
145
+ let area = Rect::new(buf.area.x, buf.area.y, 11, buf.area.height);
146
+ "hello world, just hello".render(area, &mut buf);
147
+ assert_eq!(buf, Buffer::with_lines(["hello world "]));
148
+ }
149
+
150
+ #[rstest]
151
+ fn render_option_str(mut buf: Buffer) {
152
+ Some("hello world").render(buf.area, &mut buf);
153
+ assert_eq!(buf, Buffer::with_lines(["hello world "]));
154
+ }
155
+
156
+ #[rstest]
157
+ fn render_string(mut buf: Buffer) {
158
+ String::from("hello world").render(buf.area, &mut buf);
159
+ assert_eq!(buf, Buffer::with_lines(["hello world "]));
160
+ }
161
+
162
+ #[rstest]
163
+ fn render_string_truncate(mut buf: Buffer) {
164
+ let area = Rect::new(buf.area.x, buf.area.y, 11, buf.area.height);
165
+ String::from("hello world, just hello").render(area, &mut buf);
166
+ assert_eq!(buf, Buffer::with_lines(["hello world "]));
167
+ }
168
+
169
+ #[rstest]
170
+ fn render_option_string(mut buf: Buffer) {
171
+ Some(String::from("hello world")).render(buf.area, &mut buf);
172
+ assert_eq!(buf, Buffer::with_lines(["hello world "]));
173
+ }
174
+ }
@@ -0,0 +1,9 @@
1
+ #![warn(missing_docs)]
2
+ //! The `widgets` module contains the `Widget` and `StatefulWidget` traits, which are used to
3
+ //! render UI elements on the screen.
4
+
5
+ pub use self::stateful_widget::StatefulWidget;
6
+ pub use self::widget::Widget;
7
+
8
+ mod stateful_widget;
9
+ mod widget;
package/bin/gwt.js DELETED
@@ -1,131 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Wrapper script to execute the gwt Rust binary.
4
- * This allows npm/bunx distribution of the Rust CLI.
5
- *
6
- * If the binary is not found (e.g., bunx skips postinstall),
7
- * it will be downloaded on-demand from GitHub Releases.
8
- */
9
-
10
- import { spawn } from 'child_process';
11
- import { dirname, join } from 'path';
12
- import { fileURLToPath } from 'url';
13
- import { existsSync, createWriteStream, mkdirSync, chmodSync, unlinkSync } from 'fs';
14
- import { get } from 'https';
15
- import {
16
- REPO,
17
- getPlatformArtifact,
18
- getSupportedPlatformKeys,
19
- getVersionedDownloadUrl,
20
- } from '../scripts/release-download.js';
21
-
22
- const __filename = fileURLToPath(import.meta.url);
23
- const __dirname = dirname(__filename);
24
-
25
- const BIN_NAME = process.platform === 'win32' ? 'gwt.exe' : 'gwt';
26
- const BIN_PATH = join(__dirname, BIN_NAME);
27
-
28
- async function downloadFile(url, dest) {
29
- return new Promise((resolve, reject) => {
30
- const file = createWriteStream(dest);
31
-
32
- const request = (url) => {
33
- get(url, (res) => {
34
- if (res.statusCode === 301 || res.statusCode === 302) {
35
- request(res.headers.location);
36
- return;
37
- }
38
-
39
- if (res.statusCode !== 200) {
40
- reject(new Error(`Failed to download: HTTP ${res.statusCode}`));
41
- return;
42
- }
43
-
44
- res.pipe(file);
45
- file.on('finish', () => {
46
- file.close();
47
- resolve();
48
- });
49
- }).on('error', (err) => {
50
- if (existsSync(dest)) unlinkSync(dest);
51
- reject(err);
52
- });
53
- };
54
-
55
- request(url);
56
- });
57
- }
58
-
59
- function requirePlatformArtifact() {
60
- const artifact = getPlatformArtifact();
61
- if (!artifact) {
62
- console.error(`Unsupported platform: ${process.platform}-${process.arch}`);
63
- console.error(`Supported platforms: ${getSupportedPlatformKeys().join(', ')}`);
64
- process.exit(1);
65
- }
66
- return artifact;
67
- }
68
-
69
- async function downloadBinary(url) {
70
- if (!existsSync(__dirname)) {
71
- mkdirSync(__dirname, { recursive: true });
72
- }
73
-
74
- await downloadFile(url, BIN_PATH);
75
-
76
- if (process.platform !== 'win32') {
77
- chmodSync(BIN_PATH, 0o755);
78
- }
79
-
80
- console.log('gwt binary installed successfully!');
81
- console.log('');
82
- }
83
-
84
- function runBinary() {
85
- const child = spawn(BIN_PATH, process.argv.slice(2), {
86
- stdio: 'inherit',
87
- env: process.env,
88
- });
89
-
90
- child.on('error', (err) => {
91
- console.error('Failed to start gwt:', err.message);
92
- process.exit(1);
93
- });
94
-
95
- child.on('exit', (code, signal) => {
96
- if (signal) {
97
- process.kill(process.pid, signal);
98
- } else {
99
- process.exit(code ?? 0);
100
- }
101
- });
102
- }
103
-
104
- async function main() {
105
- if (!existsSync(BIN_PATH)) {
106
- const artifact = requirePlatformArtifact();
107
- let versionedUrl = null;
108
- try {
109
- const resolved = getVersionedDownloadUrl(artifact);
110
- versionedUrl = resolved.url;
111
- console.log(`Downloading gwt binary for ${process.platform}-${process.arch}...`);
112
- console.log(`Downloading from: ${versionedUrl}`);
113
- await downloadBinary(versionedUrl);
114
- } catch (error) {
115
- console.error('Failed to download gwt binary:', error.message);
116
- console.error('');
117
- console.error('You can manually download the binary from:');
118
- if (versionedUrl) {
119
- console.error(versionedUrl);
120
- }
121
- console.error(`https://github.com/${REPO}/releases`);
122
- console.error('');
123
- console.error('Or build from source with: cargo build --release');
124
- process.exit(1);
125
- }
126
- }
127
-
128
- runBinary();
129
- }
130
-
131
- main();
@@ -1,71 +0,0 @@
1
- import { test } from 'node:test';
2
- import assert from 'node:assert/strict';
3
-
4
- import {
5
- RETRY_CONFIG,
6
- buildReleaseDownloadUrl,
7
- formatFailureGuidance,
8
- getRetryDelayMs,
9
- isRetryableError,
10
- isRetryableStatus,
11
- } from './postinstall.js';
12
- import {
13
- getPackageVersion,
14
- getVersionedDownloadUrl,
15
- } from './release-download.js';
16
-
17
- test('buildReleaseDownloadUrl uses version tag', () => {
18
- const url = buildReleaseDownloadUrl('6.17.0', 'gwt-linux-x86_64');
19
- assert.equal(
20
- url,
21
- 'https://github.com/akiojin/gwt/releases/download/v6.17.0/gwt-linux-x86_64',
22
- );
23
- });
24
-
25
- test('getRetryDelayMs applies exponential backoff with cap', () => {
26
- assert.equal(getRetryDelayMs(1, RETRY_CONFIG), 500);
27
- assert.equal(getRetryDelayMs(2, RETRY_CONFIG), 1000);
28
- assert.equal(getRetryDelayMs(3, RETRY_CONFIG), 2000);
29
- assert.equal(getRetryDelayMs(5, RETRY_CONFIG), 5000);
30
- });
31
-
32
- test('isRetryableStatus matches 403/404/5xx', () => {
33
- assert.equal(isRetryableStatus(403), true);
34
- assert.equal(isRetryableStatus(404), true);
35
- assert.equal(isRetryableStatus(500), true);
36
- assert.equal(isRetryableStatus(429), false);
37
- assert.equal(isRetryableStatus(400), false);
38
- });
39
-
40
- test('isRetryableError uses statusCode or treats network errors as retryable', () => {
41
- const notFound = new Error('not found');
42
- notFound.statusCode = 404;
43
- assert.equal(isRetryableError(notFound), true);
44
-
45
- const badRequest = new Error('bad request');
46
- badRequest.statusCode = 400;
47
- assert.equal(isRetryableError(badRequest), false);
48
-
49
- const networkError = new Error('socket hang up');
50
- assert.equal(isRetryableError(networkError), true);
51
- });
52
-
53
- test('formatFailureGuidance includes versioned URL and release page', () => {
54
- const guidance = formatFailureGuidance('1.2.3', 'gwt-linux-x86_64');
55
- assert.equal(
56
- guidance.versionedUrl,
57
- 'https://github.com/akiojin/gwt/releases/download/v1.2.3/gwt-linux-x86_64',
58
- );
59
- assert.equal(guidance.releasePageUrl, 'https://github.com/akiojin/gwt/releases');
60
- assert.equal(guidance.buildCommand, 'cargo build --release');
61
- });
62
-
63
- test('getVersionedDownloadUrl uses package.json version', () => {
64
- const version = getPackageVersion();
65
- const result = getVersionedDownloadUrl('gwt-linux-x86_64');
66
- assert.equal(result.version, version);
67
- assert.equal(
68
- result.url,
69
- `https://github.com/akiojin/gwt/releases/download/v${version}/gwt-linux-x86_64`,
70
- );
71
- });