hatui-kit 0.1.0__py3-none-any.whl

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 (97) hide show
  1. hatui/__init__.py +11 -0
  2. hatui/__main__.py +5 -0
  3. hatui/app.py +200 -0
  4. hatui/core/__init__.py +1 -0
  5. hatui/core/actions.py +46 -0
  6. hatui/core/context.py +33 -0
  7. hatui/core/input_manager.py +167 -0
  8. hatui/core/screen_buffer.py +150 -0
  9. hatui/core/style.py +381 -0
  10. hatui/core/terminal_env.py +67 -0
  11. hatui/core/util.py +0 -0
  12. hatui/core/widget.py +203 -0
  13. hatui/demo/__init__.py +1 -0
  14. hatui/demo/screens/dashboard/modals/debug.yaml +72 -0
  15. hatui/demo/screens/dashboard/modals/help.yaml +43 -0
  16. hatui/demo/screens/dashboard/providers/phase8.yaml +429 -0
  17. hatui/demo/screens/dashboard/providers.yaml +487 -0
  18. hatui/demo/screens/dashboard/screen.yaml +64 -0
  19. hatui/demo/screens/dashboard/tabs/analysis.yaml +65 -0
  20. hatui/demo/screens/dashboard/tabs/forensics.yaml +71 -0
  21. hatui/demo/screens/dashboard/tabs/hacker.yaml +80 -0
  22. hatui/demo/screens/dashboard/tabs/logs.yaml +39 -0
  23. hatui/demo/screens/dashboard/tabs/overview.yaml +180 -0
  24. hatui/demo/screens/dashboard/tabs/visuals.yaml +84 -0
  25. hatui/demo/screens/dashboard.yaml +166 -0
  26. hatui/demo_assets.py +16 -0
  27. hatui/providers/__init__.py +54 -0
  28. hatui/providers/base.py +61 -0
  29. hatui/providers/builtins.py +546 -0
  30. hatui/providers/helpers.py +169 -0
  31. hatui/runtime/__init__.py +24 -0
  32. hatui/runtime/action_registry.py +102 -0
  33. hatui/runtime/bindings.py +40 -0
  34. hatui/runtime/bootstrap.py +94 -0
  35. hatui/runtime/cli.py +66 -0
  36. hatui/runtime/defaults.py +219 -0
  37. hatui/runtime/engine.py +135 -0
  38. hatui/runtime/formatters.py +65 -0
  39. hatui/runtime/loader.py +140 -0
  40. hatui/runtime/provider_manager.py +138 -0
  41. hatui/runtime/registries.py +65 -0
  42. hatui/runtime/render_policy.py +144 -0
  43. hatui/runtime/router.py +74 -0
  44. hatui/runtime/store.py +29 -0
  45. hatui/runtime/validation.py +433 -0
  46. hatui/runtime/watcher.py +167 -0
  47. hatui/widgets/__init__.py +89 -0
  48. hatui/widgets/alert_stack_widget.py +64 -0
  49. hatui/widgets/alert_widget.py +83 -0
  50. hatui/widgets/banner_widget.py +76 -0
  51. hatui/widgets/border_widget.py +93 -0
  52. hatui/widgets/box_widget.py +81 -0
  53. hatui/widgets/center_widget.py +29 -0
  54. hatui/widgets/chart_widget.py +222 -0
  55. hatui/widgets/code_block_widget.py +91 -0
  56. hatui/widgets/column_widget.py +48 -0
  57. hatui/widgets/diff_viewer_widget.py +88 -0
  58. hatui/widgets/divider_widget.py +66 -0
  59. hatui/widgets/event_feed_widget.py +72 -0
  60. hatui/widgets/flow_widget.py +77 -0
  61. hatui/widgets/gauge_widget.py +109 -0
  62. hatui/widgets/heatmap_widget.py +153 -0
  63. hatui/widgets/hex_dump_widget.py +91 -0
  64. hatui/widgets/histogram_widget.py +96 -0
  65. hatui/widgets/inspector_widget.py +85 -0
  66. hatui/widgets/kv_inspector_widget.py +134 -0
  67. hatui/widgets/label_widget.py +99 -0
  68. hatui/widgets/list_widget.py +185 -0
  69. hatui/widgets/log_widget.py +106 -0
  70. hatui/widgets/menu_widget.py +31 -0
  71. hatui/widgets/metric_grid_widget.py +103 -0
  72. hatui/widgets/mini_chart_widget.py +79 -0
  73. hatui/widgets/modal_host_widget.py +85 -0
  74. hatui/widgets/modal_widget.py +112 -0
  75. hatui/widgets/paragraph_widget.py +94 -0
  76. hatui/widgets/progress_bar_widget.py +124 -0
  77. hatui/widgets/root_services.py +267 -0
  78. hatui/widgets/root_widget.py +130 -0
  79. hatui/widgets/row_widget.py +48 -0
  80. hatui/widgets/scroll_widget.py +224 -0
  81. hatui/widgets/selection.py +52 -0
  82. hatui/widgets/signal_strip_widget.py +79 -0
  83. hatui/widgets/sparkline_widget.py +84 -0
  84. hatui/widgets/stat_widget.py +107 -0
  85. hatui/widgets/status_matrix_widget.py +234 -0
  86. hatui/widgets/status_strip_widget.py +85 -0
  87. hatui/widgets/table_widget.py +239 -0
  88. hatui/widgets/tabs_widget.py +188 -0
  89. hatui/widgets/text_widget.py +64 -0
  90. hatui/widgets/timeline_widget.py +81 -0
  91. hatui/widgets/tree_widget.py +341 -0
  92. hatui/widgets/visualization.py +110 -0
  93. hatui_kit-0.1.0.dist-info/METADATA +154 -0
  94. hatui_kit-0.1.0.dist-info/RECORD +97 -0
  95. hatui_kit-0.1.0.dist-info/WHEEL +4 -0
  96. hatui_kit-0.1.0.dist-info/entry_points.txt +2 -0
  97. hatui_kit-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,80 @@
1
+ title: Hacker
2
+ route: hacker
3
+ child:
4
+ type: column
5
+ name: hacker_screen
6
+ children:
7
+ - type: box
8
+ name: hacker_strip_box
9
+ title: channels
10
+ padding: 0
11
+ weight: 2
12
+ child:
13
+ type: signal_strip
14
+ name: hacker_signal_strip_widget
15
+ items_key: hacker.signal_strip
16
+ - type: row
17
+ name: hacker_primary_row
18
+ weight: 4
19
+ children:
20
+ - type: box
21
+ name: hacker_matrix_box
22
+ title: status matrix
23
+ padding: 1
24
+ weight: 3
25
+ child:
26
+ type: status_matrix
27
+ name: hacker_status_matrix
28
+ items_key: hacker.entities
29
+ selectable: true
30
+ selected_index_key: selection.matrix_index
31
+ selected_item_key: selection.matrix_item
32
+ columns: 2
33
+ density: normal
34
+ - type: box
35
+ name: hacker_histogram_box
36
+ title: burst distribution
37
+ padding: 1
38
+ weight: 2
39
+ child:
40
+ type: histogram
41
+ name: hacker_packet_histogram
42
+ buckets_key: hacker.packet_histogram
43
+ label_width: 9
44
+ - type: row
45
+ name: hacker_secondary_row
46
+ weight: 5
47
+ children:
48
+ - type: box
49
+ name: hacker_alert_stack_box
50
+ title: alert stack
51
+ padding: 1
52
+ weight: 2
53
+ child:
54
+ type: alert_stack
55
+ name: hacker_alert_stack
56
+ items_key: hacker.alerts
57
+ focusable: false
58
+ - type: box
59
+ name: hacker_heatmap_box
60
+ title: activity field
61
+ padding: 1
62
+ weight: 1
63
+ child:
64
+ type: heatmap
65
+ name: hacker_heatmap
66
+ matrix_key: viz.heatmap
67
+ - type: box
68
+ name: hacker_inspector_box
69
+ title: entity inspector
70
+ padding: 1
71
+ weight: 2
72
+ child:
73
+ type: kv_inspector
74
+ name: hacker_kv_inspector
75
+ data_key: hacker.selected_detail
76
+ - type: status_strip
77
+ name: hacker_status_strip
78
+ segments_key: ui.status_strip
79
+ bg_color: "@surface_alt"
80
+ weight: 1
@@ -0,0 +1,39 @@
1
+ title: Logs
2
+ route: logs
3
+ child:
4
+ type: column
5
+ name: logs_screen
6
+ children:
7
+ - type: box
8
+ name: logs_box
9
+ title: event log
10
+ padding: 1
11
+ weight: 8
12
+ child:
13
+ type: scroll
14
+ name: log_scroll
15
+ focus_fg_color: "@focus_fg"
16
+ child:
17
+ type: log
18
+ name: log_widget
19
+ lines_key: logs.main
20
+ max_lines: 80
21
+ show_timestamp: true
22
+ - type: box
23
+ name: status_box
24
+ title: operator note
25
+ padding: 1
26
+ weight: 2
27
+ child:
28
+ type: scroll
29
+ name: logs_note_scroll
30
+ focus_fg_color: "@focus_fg"
31
+ child:
32
+ type: paragraph
33
+ name: logs_note
34
+ text_key: ui.logs_note
35
+ - type: status_strip
36
+ name: logs_status_strip
37
+ segments_key: ui.status_strip
38
+ bg_color: "@surface_alt"
39
+ weight: 1
@@ -0,0 +1,180 @@
1
+ title: Overview
2
+ route: overview
3
+ child:
4
+ type: column
5
+ name: overview_screen
6
+ children:
7
+ - type: box
8
+ name: header_box
9
+ title: msys
10
+ title_align: center
11
+ padding: 1
12
+ style: rounded
13
+ fg_color: "@accent_alt"
14
+ weight: 4
15
+ child:
16
+ type: column
17
+ name: header_column
18
+ children:
19
+ - type: banner
20
+ name: header_banner
21
+ text_key: ui.header_text
22
+ fg_color: "@accent_alt"
23
+ weight: 3
24
+ - type: label
25
+ name: header_label
26
+ text_key: ui.header_subtitle
27
+ align: center
28
+ fg_color: "@text_muted"
29
+ weight: 1
30
+ - type: row
31
+ name: stats_row
32
+ weight: 3
33
+ children:
34
+ - type: box
35
+ name: cpu_box
36
+ title: cpu
37
+ padding: 1
38
+ weight: 1
39
+ child:
40
+ type: stat
41
+ name: cpu_stat
42
+ label: load avg
43
+ data_key: stats.cpu
44
+ accent_color: "@error"
45
+ - type: box
46
+ name: mem_box
47
+ title: memory
48
+ padding: 1
49
+ weight: 1
50
+ child:
51
+ type: stat
52
+ name: mem_stat
53
+ label: resident
54
+ data_key: stats.memory
55
+ accent_color: "@success"
56
+ - type: box
57
+ name: net_box
58
+ title: network
59
+ padding: 1
60
+ weight: 1
61
+ child:
62
+ type: stat
63
+ name: net_stat
64
+ label: rx / tx
65
+ data_key: stats.network
66
+ accent_color: "@accent"
67
+ - type: row
68
+ name: telemetry_row
69
+ weight: 7
70
+ children:
71
+ - type: box
72
+ name: overview_box
73
+ title: overview
74
+ padding: 1
75
+ weight: 3
76
+ child:
77
+ type: column
78
+ name: overview_column
79
+ children:
80
+ - type: scroll
81
+ name: overview_text_scroll
82
+ focus_fg_color: "@focus_fg"
83
+ weight: 3
84
+ child:
85
+ type: paragraph
86
+ name: overview_text
87
+ text_key: ui.overview_text
88
+ fg_color: "@text"
89
+ - type: alert
90
+ name: overview_alert
91
+ message_key: alerts.message
92
+ level_key: alerts.level
93
+ weight: 1
94
+ - type: menu
95
+ name: quick_actions
96
+ selected_index_key: selection.quick_action_index
97
+ selected_fg_color: "@selection_fg"
98
+ selected_bg_color: "@selection_bg"
99
+ bullet: "» "
100
+ weight: 2
101
+ items:
102
+ - label: route overview
103
+ action: route_set
104
+ payload:
105
+ route: overview
106
+ - label: route forensics
107
+ action: route_set
108
+ payload:
109
+ route: forensics
110
+ - label: route logs
111
+ action: route_set
112
+ payload:
113
+ route: logs
114
+ - label: route visuals
115
+ action: route_set
116
+ payload:
117
+ route: visuals
118
+ - label: route analysis
119
+ action: route_set
120
+ payload:
121
+ route: analysis
122
+ - label: route hacker
123
+ action: route_set
124
+ payload:
125
+ route: hacker
126
+ - label: open help overlay
127
+ action: route_push
128
+ payload:
129
+ route: help_modal
130
+ - label: toggle compact mode
131
+ action: store_toggle
132
+ payload:
133
+ path: flags.compact_mode
134
+ - type: progress_bar
135
+ name: scan_bar
136
+ label: deep scan
137
+ value_key: signals.scan_progress
138
+ show_percentage: true
139
+ fill_color: "@error"
140
+ weight: 1
141
+ - type: progress_bar
142
+ name: uplink_bar
143
+ label: uplink sync
144
+ value_key: signals.uplink_progress
145
+ show_percentage: true
146
+ fill_color: "@success"
147
+ weight: 1
148
+ - type: box
149
+ name: telemetry_box
150
+ title: telemetry
151
+ padding: 1
152
+ weight: 2
153
+ child:
154
+ type: column
155
+ name: telemetry_column
156
+ children:
157
+ - type: label
158
+ name: sparkline_label
159
+ text: packet burst
160
+ fg_color: "@text"
161
+ weight: 1
162
+ - type: sparkline
163
+ name: telemetry_sparkline
164
+ values_key: telemetry.packet_series
165
+ fg_color: "@accent"
166
+ weight: 1
167
+ - type: mini_chart
168
+ name: telemetry_mini_chart
169
+ values_key: telemetry.mini_chart
170
+ fg_color: "@success"
171
+ weight: 4
172
+ - type: paragraph
173
+ name: telemetry_copy
174
+ text_key: telemetry.summary
175
+ weight: 2
176
+ - type: status_strip
177
+ name: overview_status_strip
178
+ segments_key: ui.status_strip
179
+ bg_color: bright_black
180
+ weight: 1
@@ -0,0 +1,84 @@
1
+ title: Visuals
2
+ route: visuals
3
+ child:
4
+ type: column
5
+ name: visuals_screen
6
+ children:
7
+ - type: row
8
+ name: visuals_charts_row
9
+ weight: 5
10
+ children:
11
+ - type: box
12
+ name: line_chart_box
13
+ title: line chart
14
+ padding: 1
15
+ weight: 2
16
+ child:
17
+ type: line_chart
18
+ name: link_line_chart
19
+ series_key: viz.line_chart_series
20
+ show_scale: true
21
+ - type: box
22
+ name: area_chart_box
23
+ title: area chart
24
+ padding: 1
25
+ weight: 2
26
+ child:
27
+ type: area_chart
28
+ name: latency_area_chart
29
+ series_key: viz.area_chart_series
30
+ show_scale: true
31
+ - type: row
32
+ name: visuals_density_row
33
+ weight: 5
34
+ children:
35
+ - type: box
36
+ name: bar_chart_box
37
+ title: throughput bars
38
+ padding: 1
39
+ weight: 2
40
+ child:
41
+ type: bar_chart
42
+ name: throughput_bar_chart
43
+ bars_key: viz.throughput_bars
44
+ show_labels: true
45
+ - type: box
46
+ name: gauge_box
47
+ title: gauges
48
+ padding: 1
49
+ weight: 1
50
+ child:
51
+ type: column
52
+ name: gauge_column
53
+ children:
54
+ - type: gauge
55
+ name: burst_gauge
56
+ label: burst
57
+ value_key: signals.packet_sample
58
+ weight: 1
59
+ - type: gauge
60
+ name: sync_gauge
61
+ label: uplink
62
+ value_key: signals.uplink_progress
63
+ fill_color: "@success"
64
+ weight: 1
65
+ - type: gauge
66
+ name: scan_gauge
67
+ label: scan
68
+ value_key: signals.scan_progress
69
+ fill_color: "@warn"
70
+ weight: 1
71
+ - type: box
72
+ name: heatmap_box
73
+ title: heatmap
74
+ padding: 1
75
+ weight: 2
76
+ child:
77
+ type: heatmap
78
+ name: queue_heatmap
79
+ matrix_key: viz.heatmap
80
+ - type: status_strip
81
+ name: visuals_status_strip
82
+ segments_key: ui.status_strip
83
+ bg_color: "@surface_alt"
84
+ weight: 1
@@ -0,0 +1,166 @@
1
+ theme:
2
+ preset: clean_ops
3
+ colors:
4
+ text: "#d7d7e0"
5
+ text_muted: "#5a5a78"
6
+ surface: default
7
+ surface_alt: "#1d2533"
8
+ border: "#5a5a78"
9
+ accent: "#59d0ff"
10
+ accent_alt: "#cbb7ff"
11
+ accent_hot: "#ff4d6d"
12
+ focus_fg: "#59d0ff"
13
+ focus_bg: "#1d2533"
14
+ selection_fg: "#10131a"
15
+ selection_bg: "#59d0ff"
16
+ info: "#59d0ff"
17
+ warn: "#ffd166"
18
+ error: "#ff4d6d"
19
+ success: "#4de0a8"
20
+ fonts:
21
+ text: JetBrains Mono
22
+ border: JetBrains Mono
23
+ heading: JetBrains Mono
24
+ border:
25
+ style: sharp
26
+ text:
27
+ font_name: "@font:text"
28
+ widgets:
29
+ tabs:
30
+ fg_color: "@text_muted"
31
+ bg_color: "@surface"
32
+ active_fg_color: "@focus_fg"
33
+ active_bg_color: "@surface_alt"
34
+ status_strip:
35
+ fg_color: "@text"
36
+ bg_color: "@surface_alt"
37
+ signal_strip:
38
+ fg_color: "@text"
39
+ bg_color: "@surface"
40
+ info_fg_color: "@info"
41
+ warn_fg_color: "@warn"
42
+ error_fg_color: "@error"
43
+ success_fg_color: "@success"
44
+ status_matrix:
45
+ active_fg_color: "@selection_fg"
46
+ active_bg_color: "@selection_bg"
47
+ info_fg_color: "@info"
48
+ warn_fg_color: "@warn"
49
+ error_fg_color: "@error"
50
+ success_fg_color: "@success"
51
+ unknown_fg_color: "@text_muted"
52
+ stat:
53
+ accent_color: "@accent"
54
+ metric_grid:
55
+ accent_color: "@accent_alt"
56
+ progress_bar:
57
+ fill_color: "@accent"
58
+ chart:
59
+ axis_color: "@border"
60
+ fill_color: "@accent"
61
+ gauge:
62
+ fill_color: "@accent"
63
+ empty_color: "@border"
64
+ histogram:
65
+ fill_color: "@accent_alt"
66
+ alert:
67
+ info_fg_color: "@info"
68
+ warn_fg_color: "@warn"
69
+ error_fg_color: "@error"
70
+ success_fg_color: "@success"
71
+ alert_stack:
72
+ selected_fg_color: "@selection_fg"
73
+ selected_bg_color: "@selection_bg"
74
+ info_fg_color: "@info"
75
+ warn_fg_color: "@warn"
76
+ error_fg_color: "@error"
77
+ success_fg_color: "@success"
78
+ heatmap:
79
+ fg_color: "@accent"
80
+ color_ramp: ["@text_muted", "@accent", "@success", "@warn", "@error"]
81
+ bg_ramp: ["@surface", "@surface_alt", "@success", "@warn", "@error"]
82
+ cell_char: " "
83
+ list:
84
+ selected_fg_color: "@selection_fg"
85
+ selected_bg_color: "@selection_bg"
86
+ tree:
87
+ selected_fg_color: "@selection_fg"
88
+ selected_bg_color: "@selection_bg"
89
+ table:
90
+ header_color: "@text"
91
+ selected_fg_color: "@selection_fg"
92
+ selected_bg_color: "@selection_bg"
93
+ scroll:
94
+ scrollbar_fg_color: "@border"
95
+ scrollbar_bg_color: "@surface_alt"
96
+ code_block:
97
+ line_number_color: "@text_muted"
98
+ hex_dump:
99
+ offset_color: "@text_muted"
100
+ divider:
101
+ fg_color: "@border"
102
+ bg_color: "@surface"
103
+ log:
104
+ warn_fg_color: "@warn"
105
+ error_fg_color: "@error"
106
+ success_fg_color: "@success"
107
+ timeline:
108
+ info_fg_color: "@info"
109
+ warn_fg_color: "@warn"
110
+ error_fg_color: "@error"
111
+ success_fg_color: "@success"
112
+ event_feed:
113
+ info_fg_color: "@info"
114
+ warn_fg_color: "@warn"
115
+ error_fg_color: "@error"
116
+ success_fg_color: "@success"
117
+ inspector:
118
+ key_color: "@text_muted"
119
+ value_color: "@text"
120
+ kv_inspector:
121
+ title_color: "@accent"
122
+ key_color: "@text_muted"
123
+ value_color: "@text"
124
+ diff_viewer:
125
+ added_fg_color: "@success"
126
+ removed_fg_color: "@error"
127
+ hint_fg_color: "@warn"
128
+ flow:
129
+ active_fg_color: "@accent"
130
+ idle_fg_color: "@text_muted"
131
+ modal:
132
+ backdrop_bg_color: "#11131a"
133
+
134
+ state:
135
+ flags:
136
+ compact_mode: false
137
+ selection:
138
+ quick_action_index: 0
139
+ relay_index: 0
140
+ matrix_index: 0
141
+
142
+ router:
143
+ routes:
144
+ - overview
145
+ - forensics
146
+ - visuals
147
+ - analysis
148
+ - hacker
149
+ - logs
150
+ - debug_modal
151
+ - help_modal
152
+ initial: overview
153
+
154
+ dev:
155
+ watch:
156
+ enabled: false
157
+ interval: 0.25
158
+ debounce: 0.2
159
+ paths:
160
+ - dashboard/**/*.yaml
161
+
162
+ providers:
163
+ include: dashboard/providers.yaml
164
+
165
+ screen:
166
+ include: dashboard/screen.yaml
hatui/demo_assets.py ADDED
@@ -0,0 +1,16 @@
1
+ from __future__ import annotations
2
+
3
+ from contextlib import contextmanager
4
+ from importlib.resources import as_file, files
5
+ from pathlib import Path
6
+ from typing import Iterator
7
+
8
+
9
+ _BUNDLED_DEMO_SPEC = files("hatui.demo").joinpath("screens/dashboard.yaml")
10
+
11
+
12
+ @contextmanager
13
+ def bundled_demo_spec_path() -> Iterator[Path]:
14
+ """Yield a filesystem path to the bundled demo spec."""
15
+ with as_file(_BUNDLED_DEMO_SPEC) as spec_path:
16
+ yield spec_path
@@ -0,0 +1,54 @@
1
+ """Built-in provider implementations."""
2
+
3
+ from hatui.providers.base import Provider, ProviderResult
4
+ from hatui.providers.builtins import (
5
+ ClockProvider,
6
+ CommandOutputProvider,
7
+ ComposeProvider,
8
+ ConstantProvider,
9
+ DemoLogsProvider,
10
+ EnvProvider,
11
+ FileProvider,
12
+ FrameProvider,
13
+ HostInfoProvider,
14
+ HttpJsonProvider,
15
+ NormalizeStateProvider,
16
+ RandomProvider,
17
+ RecordsProvider,
18
+ RollingWindowProvider,
19
+ BucketProvider,
20
+ SystemStatsProvider,
21
+ ThresholdProvider,
22
+ TemplateProvider,
23
+ TransformProvider,
24
+ WaveformProvider,
25
+ EventStreamProvider,
26
+ GridHistoryProvider
27
+ )
28
+
29
+ __all__ = [
30
+ "ClockProvider",
31
+ "CommandOutputProvider",
32
+ "ComposeProvider",
33
+ "ConstantProvider",
34
+ "DemoLogsProvider",
35
+ "EnvProvider",
36
+ "EventStreamProvider",
37
+ "FileProvider",
38
+ "FrameProvider",
39
+ "GridHistoryProvider",
40
+ "HostInfoProvider",
41
+ "HttpJsonProvider",
42
+ "NormalizeStateProvider",
43
+ "Provider",
44
+ "ProviderResult",
45
+ "RandomProvider",
46
+ "RecordsProvider",
47
+ "BucketProvider",
48
+ "RollingWindowProvider",
49
+ "SystemStatsProvider",
50
+ "TemplateProvider",
51
+ "ThresholdProvider",
52
+ "TransformProvider",
53
+ "WaveformProvider",
54
+ ]
@@ -0,0 +1,61 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from hatui.runtime.bindings import resolve_path
6
+
7
+
8
+ @dataclass
9
+ class ProviderResult:
10
+ state: str
11
+ value: object = None
12
+ reason: str | None = None
13
+
14
+
15
+ SKIP_RESULT = "skip"
16
+ VALUE_RESULT = "value"
17
+
18
+
19
+ class Provider:
20
+ spec_schema: dict[str, object] = {}
21
+ required_spec_keys: set[str] = set()
22
+
23
+ def __init__(self, spec: dict):
24
+ self.spec = spec
25
+ self.name = spec.get("name", spec.get("type", self.__class__.__name__))
26
+ self.target = spec.get("target")
27
+ self.metadata_target = spec.get("metadata_target")
28
+ self.interval = float(spec.get("interval", 0.0) or 0.0)
29
+ self.group = spec.get("group", "default")
30
+ self.depends_on = list(spec.get("depends_on", []) or [])
31
+ self.enabled = spec.get("enabled", True)
32
+ self._last_run = 0.0
33
+
34
+ def setup(self, context):
35
+ pass
36
+
37
+ def is_enabled(self, context) -> bool:
38
+ if isinstance(self.enabled, bool):
39
+ return self.enabled
40
+ if isinstance(self.enabled, str):
41
+ return bool(resolve_path(context.data, self.enabled, False))
42
+ return bool(self.enabled)
43
+
44
+ def should_run(self, context) -> bool:
45
+ if self.interval <= 0:
46
+ return True
47
+ return (context.elapsed_time - self._last_run) >= self.interval
48
+
49
+ def update(self, delta_time: float, context):
50
+ if not self.is_enabled(context):
51
+ return ProviderResult(state=SKIP_RESULT, reason="disabled")
52
+ if not self.should_run(context):
53
+ return ProviderResult(state=SKIP_RESULT, reason="interval")
54
+ self._last_run = context.elapsed_time
55
+ return ProviderResult(state=VALUE_RESULT, value=self.provide(delta_time, context))
56
+
57
+ def provide(self, delta_time: float, context):
58
+ raise NotImplementedError
59
+
60
+ def teardown(self, context):
61
+ pass