a2ui-rails 0.1.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.
- checksums.yaml +7 -0
- data/README.md +373 -0
- data/lib/a2ui/modules/action_handler.rb +25 -0
- data/lib/a2ui/modules/data_validator.rb +22 -0
- data/lib/a2ui/modules/input_parser.rb +25 -0
- data/lib/a2ui/modules/layout_adapter.rb +25 -0
- data/lib/a2ui/modules/surface.rb +66 -0
- data/lib/a2ui/modules/surface_manager.rb +64 -0
- data/lib/a2ui/modules/ui_generator.rb +25 -0
- data/lib/a2ui/modules/ui_updater.rb +26 -0
- data/lib/a2ui/modules.rb +17 -0
- data/lib/a2ui/signatures/adapt_layout.rb +19 -0
- data/lib/a2ui/signatures/generate_ui.rb +20 -0
- data/lib/a2ui/signatures/handle_action.rb +23 -0
- data/lib/a2ui/signatures/parse_input.rb +21 -0
- data/lib/a2ui/signatures/update_ui.rb +21 -0
- data/lib/a2ui/signatures/validate_data.rb +18 -0
- data/lib/a2ui/signatures.rb +12 -0
- data/lib/a2ui/types/action.rb +9 -0
- data/lib/a2ui/types/action_response_type.rb +13 -0
- data/lib/a2ui/types/alignment.rb +13 -0
- data/lib/a2ui/types/boolean_value.rb +9 -0
- data/lib/a2ui/types/button_component.rb +12 -0
- data/lib/a2ui/types/card_component.rb +11 -0
- data/lib/a2ui/types/check_box_component.rb +10 -0
- data/lib/a2ui/types/column_component.rb +12 -0
- data/lib/a2ui/types/context_binding.rb +9 -0
- data/lib/a2ui/types/data_driven_children.rb +9 -0
- data/lib/a2ui/types/data_update.rb +9 -0
- data/lib/a2ui/types/distribution.rb +15 -0
- data/lib/a2ui/types/divider_component.rb +9 -0
- data/lib/a2ui/types/explicit_children.rb +8 -0
- data/lib/a2ui/types/icon_component.rb +10 -0
- data/lib/a2ui/types/image_component.rb +11 -0
- data/lib/a2ui/types/image_fit.rb +14 -0
- data/lib/a2ui/types/input_type.rb +16 -0
- data/lib/a2ui/types/list_component.rb +10 -0
- data/lib/a2ui/types/literal_value.rb +8 -0
- data/lib/a2ui/types/number_value.rb +9 -0
- data/lib/a2ui/types/object_value.rb +9 -0
- data/lib/a2ui/types/orientation.rb +11 -0
- data/lib/a2ui/types/path_reference.rb +8 -0
- data/lib/a2ui/types/row_component.rb +12 -0
- data/lib/a2ui/types/screen_size.rb +12 -0
- data/lib/a2ui/types/select_component.rb +12 -0
- data/lib/a2ui/types/slider_component.rb +13 -0
- data/lib/a2ui/types/stream_action.rb +16 -0
- data/lib/a2ui/types/stream_op.rb +10 -0
- data/lib/a2ui/types/string_value.rb +9 -0
- data/lib/a2ui/types/text_component.rb +10 -0
- data/lib/a2ui/types/text_field_component.rb +13 -0
- data/lib/a2ui/types/text_usage_hint.rb +16 -0
- data/lib/a2ui/types/user_action.rb +11 -0
- data/lib/a2ui/types/validation_issue.rb +10 -0
- data/lib/a2ui/types.rb +91 -0
- data/lib/a2ui/version.rb +6 -0
- data/lib/a2ui.rb +7 -0
- metadata +130 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module A2UI
|
|
5
|
+
class AdaptLayout < DSPy::Signature
|
|
6
|
+
description 'Adapt components for screen size.'
|
|
7
|
+
|
|
8
|
+
input do
|
|
9
|
+
const :components, T::Array[Component]
|
|
10
|
+
const :root_id, String
|
|
11
|
+
const :screen, ScreenSize
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
output do
|
|
15
|
+
const :components, T::Array[Component], description: 'Adapted components'
|
|
16
|
+
const :hidden_ids, T::Array[String], default: [], description: 'IDs to hide'
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module A2UI
|
|
5
|
+
class GenerateUI < DSPy::Signature
|
|
6
|
+
description 'Generate UI components from natural language.'
|
|
7
|
+
|
|
8
|
+
input do
|
|
9
|
+
const :request, String, description: 'What UI to create'
|
|
10
|
+
const :surface_id, String
|
|
11
|
+
const :available_data, String, default: '{}', description: 'Available data model as JSON'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
output do
|
|
15
|
+
const :root_id, String, description: 'Root component ID'
|
|
16
|
+
const :components, T::Array[Component], description: 'Component adjacency list'
|
|
17
|
+
const :initial_data, T::Array[DataUpdate], default: [], description: 'Initial data values'
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module A2UI
|
|
5
|
+
class HandleAction < DSPy::Signature
|
|
6
|
+
description 'Process user action and generate response.'
|
|
7
|
+
|
|
8
|
+
input do
|
|
9
|
+
const :action, UserAction
|
|
10
|
+
const :current_data, String, default: '{}', description: 'Surface data as JSON'
|
|
11
|
+
const :business_rules, String, default: '', description: 'Domain constraints'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
output do
|
|
15
|
+
const :response_type, ActionResponseType
|
|
16
|
+
const :streams, T::Array[StreamOp], default: []
|
|
17
|
+
const :components, T::Array[Component], default: []
|
|
18
|
+
const :data_updates, T::Array[DataUpdate], default: []
|
|
19
|
+
const :redirect_url, String, default: '', description: 'URL if navigating'
|
|
20
|
+
const :message, String, default: '', description: 'User-facing message'
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module A2UI
|
|
5
|
+
class ParseInput < DSPy::Signature
|
|
6
|
+
description 'Convert natural language to data updates.'
|
|
7
|
+
|
|
8
|
+
input do
|
|
9
|
+
const :text, String, description: 'User natural language input'
|
|
10
|
+
const :target_path, String, description: 'Path to update'
|
|
11
|
+
const :expected_schema, String, default: '', description: 'Expected structure hint'
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
output do
|
|
15
|
+
const :updates, T::Array[DataUpdate]
|
|
16
|
+
const :confidence, Float, description: '0.0-1.0 confidence score'
|
|
17
|
+
const :needs_clarification, T::Boolean
|
|
18
|
+
const :question, String, default: '', description: 'Clarification question if needed'
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module A2UI
|
|
5
|
+
class UpdateUI < DSPy::Signature
|
|
6
|
+
description 'Generate incremental Turbo Stream updates.'
|
|
7
|
+
|
|
8
|
+
input do
|
|
9
|
+
const :request, String, description: 'What to update'
|
|
10
|
+
const :surface_id, String
|
|
11
|
+
const :current_components, T::Array[Component], default: []
|
|
12
|
+
const :current_data, String, default: '{}', description: 'Current data as JSON'
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
output do
|
|
16
|
+
const :streams, T::Array[StreamOp], description: 'Turbo Stream operations'
|
|
17
|
+
const :components, T::Array[Component], default: [], description: 'New/updated components'
|
|
18
|
+
const :data_updates, T::Array[DataUpdate], default: []
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module A2UI
|
|
5
|
+
class ValidateData < DSPy::Signature
|
|
6
|
+
description 'Validate data model against rules.'
|
|
7
|
+
|
|
8
|
+
input do
|
|
9
|
+
const :data, String, description: 'Data model as JSON'
|
|
10
|
+
const :rules, String, description: 'Validation rules'
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
output do
|
|
14
|
+
const :valid, T::Boolean
|
|
15
|
+
const :issues, T::Array[ValidationIssue], default: []
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'dspy'
|
|
5
|
+
require_relative 'types'
|
|
6
|
+
|
|
7
|
+
require_relative 'signatures/generate_ui'
|
|
8
|
+
require_relative 'signatures/update_ui'
|
|
9
|
+
require_relative 'signatures/handle_action'
|
|
10
|
+
require_relative 'signatures/validate_data'
|
|
11
|
+
require_relative 'signatures/parse_input'
|
|
12
|
+
require_relative 'signatures/adapt_layout'
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module A2UI
|
|
5
|
+
class ButtonComponent < T::Struct
|
|
6
|
+
const :id, String
|
|
7
|
+
const :label, Value, description: 'Button label text'
|
|
8
|
+
const :action, Action
|
|
9
|
+
const :disabled, T::Boolean, default: false
|
|
10
|
+
const :variant, String, default: 'primary', description: 'primary, secondary, or danger'
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module A2UI
|
|
5
|
+
class CardComponent < T::Struct
|
|
6
|
+
const :id, String
|
|
7
|
+
const :child_id, String, description: 'ID of the child component'
|
|
8
|
+
const :title, String, default: ''
|
|
9
|
+
const :elevation, Integer, default: 1
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module A2UI
|
|
5
|
+
class ColumnComponent < T::Struct
|
|
6
|
+
const :id, String
|
|
7
|
+
const :children, Children
|
|
8
|
+
const :distribution, Distribution, default: Distribution::Start
|
|
9
|
+
const :alignment, Alignment, default: Alignment::Stretch
|
|
10
|
+
const :gap, Integer, default: 8
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module A2UI
|
|
5
|
+
class DataDrivenChildren < T::Struct
|
|
6
|
+
const :path, String, description: 'JSON Pointer to array in data model'
|
|
7
|
+
const :template_id, String, description: 'Component ID to use as template for each item'
|
|
8
|
+
end
|
|
9
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module A2UI
|
|
5
|
+
class DataUpdate < T::Struct
|
|
6
|
+
const :path, String, description: 'JSON Pointer path (e.g., /reservation/guests)'
|
|
7
|
+
const :entries, T::Array[DataValue], description: 'Values to set at this path'
|
|
8
|
+
end
|
|
9
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module A2UI
|
|
5
|
+
class Distribution < T::Enum
|
|
6
|
+
enums do
|
|
7
|
+
Start = new('start')
|
|
8
|
+
Center = new('center')
|
|
9
|
+
End = new('end')
|
|
10
|
+
SpaceBetween = new('space_between')
|
|
11
|
+
SpaceAround = new('space_around')
|
|
12
|
+
SpaceEvenly = new('space_evenly')
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module A2UI
|
|
5
|
+
class ImageComponent < T::Struct
|
|
6
|
+
const :id, String
|
|
7
|
+
const :src, Value, description: 'Image URL'
|
|
8
|
+
const :alt, String, default: ''
|
|
9
|
+
const :fit, ImageFit, default: ImageFit::Contain
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module A2UI
|
|
5
|
+
class InputType < T::Enum
|
|
6
|
+
enums do
|
|
7
|
+
Text = new('text')
|
|
8
|
+
Number = new('number')
|
|
9
|
+
Date = new('date')
|
|
10
|
+
Longtext = new('longtext')
|
|
11
|
+
Email = new('email')
|
|
12
|
+
Tel = new('tel')
|
|
13
|
+
Url = new('url')
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module A2UI
|
|
5
|
+
class RowComponent < T::Struct
|
|
6
|
+
const :id, String
|
|
7
|
+
const :children, Children
|
|
8
|
+
const :distribution, Distribution, default: Distribution::Start
|
|
9
|
+
const :alignment, Alignment, default: Alignment::Center
|
|
10
|
+
const :gap, Integer, default: 8
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module A2UI
|
|
5
|
+
class SelectComponent < T::Struct
|
|
6
|
+
const :id, String
|
|
7
|
+
const :value, PathReference, description: 'Path to selected value'
|
|
8
|
+
const :options_path, PathReference, description: 'Path to array of {value, label} options'
|
|
9
|
+
const :label, String, default: ''
|
|
10
|
+
const :placeholder, String, default: 'Select...'
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module A2UI
|
|
5
|
+
class SliderComponent < T::Struct
|
|
6
|
+
const :id, String
|
|
7
|
+
const :value, PathReference, description: 'Path to numeric value'
|
|
8
|
+
const :min, Float, default: 0.0
|
|
9
|
+
const :max, Float, default: 100.0
|
|
10
|
+
const :step, Float, default: 1.0
|
|
11
|
+
const :label, String, default: ''
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module A2UI
|
|
5
|
+
class StreamAction < T::Enum
|
|
6
|
+
enums do
|
|
7
|
+
Append = new('append')
|
|
8
|
+
Prepend = new('prepend')
|
|
9
|
+
Replace = new('replace')
|
|
10
|
+
Update = new('update')
|
|
11
|
+
Remove = new('remove')
|
|
12
|
+
Before = new('before')
|
|
13
|
+
After = new('after')
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module A2UI
|
|
5
|
+
class StreamOp < T::Struct
|
|
6
|
+
const :action, StreamAction
|
|
7
|
+
const :target, String, description: 'Target element ID'
|
|
8
|
+
const :component_ids, T::Array[String], default: [], description: 'Components to render'
|
|
9
|
+
end
|
|
10
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module A2UI
|
|
5
|
+
class TextFieldComponent < T::Struct
|
|
6
|
+
const :id, String
|
|
7
|
+
const :value, PathReference, description: 'Path to bound value in data model'
|
|
8
|
+
const :input_type, InputType, default: InputType::Text
|
|
9
|
+
const :label, String, default: ''
|
|
10
|
+
const :placeholder, String, default: ''
|
|
11
|
+
const :is_required, T::Boolean, default: false, description: 'Whether the field is required'
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module A2UI
|
|
5
|
+
class TextUsageHint < T::Enum
|
|
6
|
+
enums do
|
|
7
|
+
H1 = new('h1')
|
|
8
|
+
H2 = new('h2')
|
|
9
|
+
H3 = new('h3')
|
|
10
|
+
H4 = new('h4')
|
|
11
|
+
H5 = new('h5')
|
|
12
|
+
Body = new('body')
|
|
13
|
+
Caption = new('caption')
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module A2UI
|
|
5
|
+
class UserAction < T::Struct
|
|
6
|
+
const :name, String, description: 'Action name from component'
|
|
7
|
+
const :surface_id, String
|
|
8
|
+
const :source_id, String, description: 'Component ID that triggered action'
|
|
9
|
+
const :context, T::Hash[String, String], default: {}, description: 'Resolved context values'
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# typed: strict
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module A2UI
|
|
5
|
+
class ValidationIssue < T::Struct
|
|
6
|
+
const :path, String, description: 'JSON Pointer to invalid field'
|
|
7
|
+
const :message, String
|
|
8
|
+
const :code, String, description: 'Machine-readable code like required, invalid_format'
|
|
9
|
+
end
|
|
10
|
+
end
|