bead 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.
- bead/__init__.py +11 -0
- bead/__main__.py +11 -0
- bead/active_learning/__init__.py +15 -0
- bead/active_learning/config.py +231 -0
- bead/active_learning/loop.py +566 -0
- bead/active_learning/models/__init__.py +24 -0
- bead/active_learning/models/base.py +852 -0
- bead/active_learning/models/binary.py +910 -0
- bead/active_learning/models/categorical.py +943 -0
- bead/active_learning/models/cloze.py +862 -0
- bead/active_learning/models/forced_choice.py +956 -0
- bead/active_learning/models/free_text.py +773 -0
- bead/active_learning/models/lora.py +365 -0
- bead/active_learning/models/magnitude.py +835 -0
- bead/active_learning/models/multi_select.py +795 -0
- bead/active_learning/models/ordinal_scale.py +811 -0
- bead/active_learning/models/peft_adapter.py +155 -0
- bead/active_learning/models/random_effects.py +639 -0
- bead/active_learning/selection.py +354 -0
- bead/active_learning/strategies.py +391 -0
- bead/active_learning/trainers/__init__.py +26 -0
- bead/active_learning/trainers/base.py +210 -0
- bead/active_learning/trainers/data_collator.py +172 -0
- bead/active_learning/trainers/dataset_utils.py +261 -0
- bead/active_learning/trainers/huggingface.py +304 -0
- bead/active_learning/trainers/lightning.py +324 -0
- bead/active_learning/trainers/metrics.py +424 -0
- bead/active_learning/trainers/mixed_effects.py +551 -0
- bead/active_learning/trainers/model_wrapper.py +509 -0
- bead/active_learning/trainers/registry.py +104 -0
- bead/adapters/__init__.py +11 -0
- bead/adapters/huggingface.py +61 -0
- bead/behavioral/__init__.py +116 -0
- bead/behavioral/analytics.py +646 -0
- bead/behavioral/extraction.py +343 -0
- bead/behavioral/merging.py +343 -0
- bead/cli/__init__.py +11 -0
- bead/cli/active_learning.py +513 -0
- bead/cli/active_learning_commands.py +779 -0
- bead/cli/completion.py +359 -0
- bead/cli/config.py +624 -0
- bead/cli/constraint_builders.py +286 -0
- bead/cli/deployment.py +859 -0
- bead/cli/deployment_trials.py +493 -0
- bead/cli/deployment_ui.py +332 -0
- bead/cli/display.py +378 -0
- bead/cli/items.py +960 -0
- bead/cli/items_factories.py +776 -0
- bead/cli/list_constraints.py +714 -0
- bead/cli/lists.py +490 -0
- bead/cli/main.py +430 -0
- bead/cli/models.py +877 -0
- bead/cli/resource_loaders.py +621 -0
- bead/cli/resources.py +1036 -0
- bead/cli/shell.py +356 -0
- bead/cli/simulate.py +840 -0
- bead/cli/templates.py +1158 -0
- bead/cli/training.py +1080 -0
- bead/cli/utils.py +614 -0
- bead/cli/workflow.py +1273 -0
- bead/config/__init__.py +68 -0
- bead/config/active_learning.py +1009 -0
- bead/config/config.py +192 -0
- bead/config/defaults.py +118 -0
- bead/config/deployment.py +217 -0
- bead/config/env.py +147 -0
- bead/config/item.py +45 -0
- bead/config/list.py +193 -0
- bead/config/loader.py +149 -0
- bead/config/logging.py +42 -0
- bead/config/model.py +49 -0
- bead/config/paths.py +46 -0
- bead/config/profiles.py +320 -0
- bead/config/resources.py +47 -0
- bead/config/serialization.py +210 -0
- bead/config/simulation.py +206 -0
- bead/config/template.py +238 -0
- bead/config/validation.py +267 -0
- bead/data/__init__.py +65 -0
- bead/data/base.py +87 -0
- bead/data/identifiers.py +97 -0
- bead/data/language_codes.py +61 -0
- bead/data/metadata.py +270 -0
- bead/data/range.py +123 -0
- bead/data/repository.py +358 -0
- bead/data/serialization.py +249 -0
- bead/data/timestamps.py +89 -0
- bead/data/validation.py +349 -0
- bead/data_collection/__init__.py +11 -0
- bead/data_collection/jatos.py +223 -0
- bead/data_collection/merger.py +154 -0
- bead/data_collection/prolific.py +198 -0
- bead/deployment/__init__.py +5 -0
- bead/deployment/distribution.py +402 -0
- bead/deployment/jatos/__init__.py +1 -0
- bead/deployment/jatos/api.py +200 -0
- bead/deployment/jatos/exporter.py +210 -0
- bead/deployment/jspsych/__init__.py +9 -0
- bead/deployment/jspsych/biome.json +44 -0
- bead/deployment/jspsych/config.py +411 -0
- bead/deployment/jspsych/generator.py +598 -0
- bead/deployment/jspsych/package.json +51 -0
- bead/deployment/jspsych/pnpm-lock.yaml +2141 -0
- bead/deployment/jspsych/randomizer.py +299 -0
- bead/deployment/jspsych/src/lib/list-distributor.test.ts +327 -0
- bead/deployment/jspsych/src/lib/list-distributor.ts +1282 -0
- bead/deployment/jspsych/src/lib/randomizer.test.ts +232 -0
- bead/deployment/jspsych/src/lib/randomizer.ts +367 -0
- bead/deployment/jspsych/src/plugins/cloze-dropdown.ts +252 -0
- bead/deployment/jspsych/src/plugins/forced-choice.ts +265 -0
- bead/deployment/jspsych/src/plugins/plugins.test.ts +141 -0
- bead/deployment/jspsych/src/plugins/rating.ts +248 -0
- bead/deployment/jspsych/src/slopit/index.ts +9 -0
- bead/deployment/jspsych/src/types/jatos.d.ts +256 -0
- bead/deployment/jspsych/src/types/jspsych.d.ts +228 -0
- bead/deployment/jspsych/templates/experiment.css +1 -0
- bead/deployment/jspsych/templates/experiment.js.template +289 -0
- bead/deployment/jspsych/templates/index.html +51 -0
- bead/deployment/jspsych/templates/randomizer.js +241 -0
- bead/deployment/jspsych/templates/randomizer.js.template +313 -0
- bead/deployment/jspsych/trials.py +723 -0
- bead/deployment/jspsych/tsconfig.json +23 -0
- bead/deployment/jspsych/tsup.config.ts +30 -0
- bead/deployment/jspsych/ui/__init__.py +1 -0
- bead/deployment/jspsych/ui/components.py +383 -0
- bead/deployment/jspsych/ui/styles.py +411 -0
- bead/dsl/__init__.py +80 -0
- bead/dsl/ast.py +168 -0
- bead/dsl/context.py +178 -0
- bead/dsl/errors.py +71 -0
- bead/dsl/evaluator.py +570 -0
- bead/dsl/grammar.lark +81 -0
- bead/dsl/parser.py +231 -0
- bead/dsl/stdlib.py +929 -0
- bead/evaluation/__init__.py +13 -0
- bead/evaluation/convergence.py +485 -0
- bead/evaluation/interannotator.py +398 -0
- bead/items/__init__.py +40 -0
- bead/items/adapters/__init__.py +70 -0
- bead/items/adapters/anthropic.py +224 -0
- bead/items/adapters/api_utils.py +167 -0
- bead/items/adapters/base.py +216 -0
- bead/items/adapters/google.py +259 -0
- bead/items/adapters/huggingface.py +1074 -0
- bead/items/adapters/openai.py +323 -0
- bead/items/adapters/registry.py +202 -0
- bead/items/adapters/sentence_transformers.py +224 -0
- bead/items/adapters/togetherai.py +309 -0
- bead/items/binary.py +515 -0
- bead/items/cache.py +558 -0
- bead/items/categorical.py +593 -0
- bead/items/cloze.py +757 -0
- bead/items/constructor.py +784 -0
- bead/items/forced_choice.py +413 -0
- bead/items/free_text.py +681 -0
- bead/items/generation.py +432 -0
- bead/items/item.py +396 -0
- bead/items/item_template.py +787 -0
- bead/items/magnitude.py +573 -0
- bead/items/multi_select.py +621 -0
- bead/items/ordinal_scale.py +569 -0
- bead/items/scoring.py +448 -0
- bead/items/validation.py +723 -0
- bead/lists/__init__.py +30 -0
- bead/lists/balancer.py +263 -0
- bead/lists/constraints.py +1067 -0
- bead/lists/experiment_list.py +286 -0
- bead/lists/list_collection.py +378 -0
- bead/lists/partitioner.py +1141 -0
- bead/lists/stratification.py +254 -0
- bead/participants/__init__.py +73 -0
- bead/participants/collection.py +699 -0
- bead/participants/merging.py +312 -0
- bead/participants/metadata_spec.py +491 -0
- bead/participants/models.py +276 -0
- bead/resources/__init__.py +29 -0
- bead/resources/adapters/__init__.py +19 -0
- bead/resources/adapters/base.py +104 -0
- bead/resources/adapters/cache.py +128 -0
- bead/resources/adapters/glazing.py +508 -0
- bead/resources/adapters/registry.py +117 -0
- bead/resources/adapters/unimorph.py +796 -0
- bead/resources/classification.py +856 -0
- bead/resources/constraint_builders.py +329 -0
- bead/resources/constraints.py +165 -0
- bead/resources/lexical_item.py +223 -0
- bead/resources/lexicon.py +744 -0
- bead/resources/loaders.py +209 -0
- bead/resources/template.py +441 -0
- bead/resources/template_collection.py +707 -0
- bead/resources/template_generation.py +349 -0
- bead/simulation/__init__.py +29 -0
- bead/simulation/annotators/__init__.py +15 -0
- bead/simulation/annotators/base.py +175 -0
- bead/simulation/annotators/distance_based.py +135 -0
- bead/simulation/annotators/lm_based.py +114 -0
- bead/simulation/annotators/oracle.py +182 -0
- bead/simulation/annotators/random.py +181 -0
- bead/simulation/dsl_extension/__init__.py +3 -0
- bead/simulation/noise_models/__init__.py +13 -0
- bead/simulation/noise_models/base.py +42 -0
- bead/simulation/noise_models/random_noise.py +82 -0
- bead/simulation/noise_models/systematic.py +132 -0
- bead/simulation/noise_models/temperature.py +86 -0
- bead/simulation/runner.py +144 -0
- bead/simulation/strategies/__init__.py +23 -0
- bead/simulation/strategies/base.py +123 -0
- bead/simulation/strategies/binary.py +103 -0
- bead/simulation/strategies/categorical.py +123 -0
- bead/simulation/strategies/cloze.py +224 -0
- bead/simulation/strategies/forced_choice.py +127 -0
- bead/simulation/strategies/free_text.py +105 -0
- bead/simulation/strategies/magnitude.py +116 -0
- bead/simulation/strategies/multi_select.py +129 -0
- bead/simulation/strategies/ordinal_scale.py +131 -0
- bead/templates/__init__.py +27 -0
- bead/templates/adapters/__init__.py +17 -0
- bead/templates/adapters/base.py +128 -0
- bead/templates/adapters/cache.py +178 -0
- bead/templates/adapters/huggingface.py +312 -0
- bead/templates/combinatorics.py +103 -0
- bead/templates/filler.py +605 -0
- bead/templates/renderers.py +177 -0
- bead/templates/resolver.py +178 -0
- bead/templates/strategies.py +1806 -0
- bead/templates/streaming.py +195 -0
- bead-0.1.0.dist-info/METADATA +212 -0
- bead-0.1.0.dist-info/RECORD +231 -0
- bead-0.1.0.dist-info/WHEEL +4 -0
- bead-0.1.0.dist-info/entry_points.txt +2 -0
- bead-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
"""Material Design CSS generation for jsPsych experiments.
|
|
2
|
+
|
|
3
|
+
This module provides the MaterialDesignStylesheet class for generating
|
|
4
|
+
Material Design 3 CSS for bead jsPsych experiments.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Literal
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class MaterialDesignStylesheet:
|
|
13
|
+
"""Generator for Material Design 3 CSS.
|
|
14
|
+
|
|
15
|
+
Generates Material Design 3 compliant CSS for jsPsych experiments.
|
|
16
|
+
|
|
17
|
+
Features
|
|
18
|
+
--------
|
|
19
|
+
- Color theming (light/dark/auto)
|
|
20
|
+
- Typography (Roboto font family)
|
|
21
|
+
- Elevation (shadows)
|
|
22
|
+
- Ripple effects for buttons
|
|
23
|
+
- Form controls (inputs, dropdowns, radio buttons)
|
|
24
|
+
|
|
25
|
+
See Also
|
|
26
|
+
--------
|
|
27
|
+
generate_css : Generate complete Material Design CSS with theme options.
|
|
28
|
+
|
|
29
|
+
Examples
|
|
30
|
+
--------
|
|
31
|
+
>>> stylesheet = MaterialDesignStylesheet()
|
|
32
|
+
>>> css = stylesheet.generate_css(theme="light")
|
|
33
|
+
>>> print(css[:100])
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(self) -> None:
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
def generate_css(
|
|
40
|
+
self,
|
|
41
|
+
theme: Literal["light", "dark", "auto"] = "light",
|
|
42
|
+
primary_color: str = "#6200EE",
|
|
43
|
+
secondary_color: str = "#03DAC6",
|
|
44
|
+
) -> str:
|
|
45
|
+
"""Generate complete Material Design CSS.
|
|
46
|
+
|
|
47
|
+
Parameters
|
|
48
|
+
----------
|
|
49
|
+
theme : Literal["light", "dark", "auto"]
|
|
50
|
+
Color theme (light, dark, or auto).
|
|
51
|
+
primary_color : str
|
|
52
|
+
Primary color as hex code.
|
|
53
|
+
secondary_color : str
|
|
54
|
+
Secondary color as hex code.
|
|
55
|
+
|
|
56
|
+
Returns
|
|
57
|
+
-------
|
|
58
|
+
str
|
|
59
|
+
Complete CSS stylesheet as string.
|
|
60
|
+
|
|
61
|
+
Examples
|
|
62
|
+
--------
|
|
63
|
+
>>> stylesheet = MaterialDesignStylesheet()
|
|
64
|
+
>>> css = stylesheet.generate_css(theme="light", primary_color="#1976D2")
|
|
65
|
+
>>> "--primary-color" in css
|
|
66
|
+
True
|
|
67
|
+
"""
|
|
68
|
+
# determine theme colors
|
|
69
|
+
if theme == "light":
|
|
70
|
+
background = "#FFFFFF"
|
|
71
|
+
surface = "#FFFFFF"
|
|
72
|
+
on_surface = "#000000"
|
|
73
|
+
on_primary = "#FFFFFF"
|
|
74
|
+
elif theme == "dark":
|
|
75
|
+
background = "#121212"
|
|
76
|
+
surface = "#1E1E1E"
|
|
77
|
+
on_surface = "#FFFFFF"
|
|
78
|
+
on_primary = "#000000"
|
|
79
|
+
else: # auto
|
|
80
|
+
# use CSS media query for system preference
|
|
81
|
+
background = "#FFFFFF"
|
|
82
|
+
surface = "#FFFFFF"
|
|
83
|
+
on_surface = "#000000"
|
|
84
|
+
on_primary = "#FFFFFF"
|
|
85
|
+
|
|
86
|
+
css = f"""
|
|
87
|
+
/* Material Design 3 Stylesheet for bead jsPsych Experiments */
|
|
88
|
+
/* Theme: {theme} */
|
|
89
|
+
|
|
90
|
+
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap');
|
|
91
|
+
|
|
92
|
+
:root {{
|
|
93
|
+
/* Color palette */
|
|
94
|
+
--primary-color: {primary_color};
|
|
95
|
+
--secondary-color: {secondary_color};
|
|
96
|
+
--background: {background};
|
|
97
|
+
--surface: {surface};
|
|
98
|
+
--on-surface: {on_surface};
|
|
99
|
+
--on-primary: {on_primary};
|
|
100
|
+
--error: #B00020;
|
|
101
|
+
--on-error: #FFFFFF;
|
|
102
|
+
|
|
103
|
+
/* Typography */
|
|
104
|
+
--font-family: 'Roboto', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
105
|
+
--font-size-body: 16px;
|
|
106
|
+
--font-size-title: 24px;
|
|
107
|
+
--font-size-label: 14px;
|
|
108
|
+
|
|
109
|
+
/* Spacing */
|
|
110
|
+
--spacing-xs: 4px;
|
|
111
|
+
--spacing-sm: 8px;
|
|
112
|
+
--spacing-md: 16px;
|
|
113
|
+
--spacing-lg: 24px;
|
|
114
|
+
--spacing-xl: 32px;
|
|
115
|
+
|
|
116
|
+
/* Elevation shadows */
|
|
117
|
+
--elevation-1: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
|
|
118
|
+
--elevation-2: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);
|
|
119
|
+
--elevation-3: 0 10px 20px rgba(0, 0, 0, 0.19), 0 6px 6px rgba(0, 0, 0, 0.23);
|
|
120
|
+
|
|
121
|
+
/* Border radius */
|
|
122
|
+
--radius-sm: 4px;
|
|
123
|
+
--radius-md: 8px;
|
|
124
|
+
--radius-lg: 12px;
|
|
125
|
+
--radius-full: 9999px;
|
|
126
|
+
}}
|
|
127
|
+
|
|
128
|
+
/* Dark theme media query for auto mode */
|
|
129
|
+
@media (prefers-color-scheme: dark) {{
|
|
130
|
+
:root {{
|
|
131
|
+
--background: #121212;
|
|
132
|
+
--surface: #1E1E1E;
|
|
133
|
+
--on-surface: #FFFFFF;
|
|
134
|
+
--on-primary: #000000;
|
|
135
|
+
}}
|
|
136
|
+
}}
|
|
137
|
+
|
|
138
|
+
/* Base styles */
|
|
139
|
+
body {{
|
|
140
|
+
font-family: var(--font-family);
|
|
141
|
+
font-size: var(--font-size-body);
|
|
142
|
+
color: var(--on-surface);
|
|
143
|
+
background-color: var(--background);
|
|
144
|
+
margin: 0;
|
|
145
|
+
padding: 0;
|
|
146
|
+
line-height: 1.5;
|
|
147
|
+
}}
|
|
148
|
+
|
|
149
|
+
/* Button styles */
|
|
150
|
+
.bead-button {{
|
|
151
|
+
font-family: var(--font-family);
|
|
152
|
+
font-size: var(--font-size-body);
|
|
153
|
+
font-weight: 500;
|
|
154
|
+
padding: 10px 24px;
|
|
155
|
+
border: none;
|
|
156
|
+
border-radius: var(--radius-sm);
|
|
157
|
+
background-color: var(--primary-color);
|
|
158
|
+
color: var(--on-primary);
|
|
159
|
+
cursor: pointer;
|
|
160
|
+
box-shadow: var(--elevation-2);
|
|
161
|
+
transition: box-shadow 0.2s, background-color 0.2s;
|
|
162
|
+
text-transform: uppercase;
|
|
163
|
+
letter-spacing: 0.5px;
|
|
164
|
+
}}
|
|
165
|
+
|
|
166
|
+
.bead-button:hover {{
|
|
167
|
+
box-shadow: var(--elevation-3);
|
|
168
|
+
}}
|
|
169
|
+
|
|
170
|
+
.bead-button:active {{
|
|
171
|
+
box-shadow: var(--elevation-1);
|
|
172
|
+
}}
|
|
173
|
+
|
|
174
|
+
.bead-button:disabled {{
|
|
175
|
+
background-color: rgba(0, 0, 0, 0.12);
|
|
176
|
+
color: rgba(0, 0, 0, 0.38);
|
|
177
|
+
box-shadow: none;
|
|
178
|
+
cursor: not-allowed;
|
|
179
|
+
}}
|
|
180
|
+
|
|
181
|
+
/* Rating scale styles */
|
|
182
|
+
.bead-rating-container {{
|
|
183
|
+
max-width: 800px;
|
|
184
|
+
margin: 0 auto;
|
|
185
|
+
padding: var(--spacing-xl);
|
|
186
|
+
}}
|
|
187
|
+
|
|
188
|
+
.bead-rating-prompt {{
|
|
189
|
+
font-size: var(--font-size-title);
|
|
190
|
+
font-weight: 500;
|
|
191
|
+
margin-bottom: var(--spacing-lg);
|
|
192
|
+
text-align: center;
|
|
193
|
+
}}
|
|
194
|
+
|
|
195
|
+
.bead-rating-scale {{
|
|
196
|
+
display: flex;
|
|
197
|
+
justify-content: center;
|
|
198
|
+
gap: var(--spacing-sm);
|
|
199
|
+
margin: var(--spacing-lg) 0;
|
|
200
|
+
}}
|
|
201
|
+
|
|
202
|
+
.bead-rating-option {{
|
|
203
|
+
display: flex;
|
|
204
|
+
flex-direction: column;
|
|
205
|
+
align-items: center;
|
|
206
|
+
gap: var(--spacing-xs);
|
|
207
|
+
}}
|
|
208
|
+
|
|
209
|
+
.bead-rating-button {{
|
|
210
|
+
width: 48px;
|
|
211
|
+
height: 48px;
|
|
212
|
+
border: 2px solid var(--primary-color);
|
|
213
|
+
border-radius: var(--radius-full);
|
|
214
|
+
background-color: transparent;
|
|
215
|
+
color: var(--primary-color);
|
|
216
|
+
font-size: var(--font-size-body);
|
|
217
|
+
font-weight: 500;
|
|
218
|
+
cursor: pointer;
|
|
219
|
+
transition: background-color 0.2s, color 0.2s;
|
|
220
|
+
}}
|
|
221
|
+
|
|
222
|
+
.bead-rating-button:hover {{
|
|
223
|
+
background-color: rgba(98, 0, 238, 0.08);
|
|
224
|
+
}}
|
|
225
|
+
|
|
226
|
+
.bead-rating-button.selected {{
|
|
227
|
+
background-color: var(--primary-color);
|
|
228
|
+
color: var(--on-primary);
|
|
229
|
+
}}
|
|
230
|
+
|
|
231
|
+
.bead-rating-label {{
|
|
232
|
+
font-size: var(--font-size-label);
|
|
233
|
+
color: var(--on-surface);
|
|
234
|
+
text-align: center;
|
|
235
|
+
max-width: 80px;
|
|
236
|
+
}}
|
|
237
|
+
|
|
238
|
+
.bead-rating-button-container {{
|
|
239
|
+
display: flex;
|
|
240
|
+
justify-content: center;
|
|
241
|
+
margin-top: var(--spacing-xl);
|
|
242
|
+
}}
|
|
243
|
+
|
|
244
|
+
/* Text field styles */
|
|
245
|
+
.bead-text-field {{
|
|
246
|
+
font-family: var(--font-family);
|
|
247
|
+
font-size: var(--font-size-body);
|
|
248
|
+
padding: 12px 16px;
|
|
249
|
+
border: 1px solid rgba(0, 0, 0, 0.38);
|
|
250
|
+
border-radius: var(--radius-sm);
|
|
251
|
+
background-color: transparent;
|
|
252
|
+
color: var(--on-surface);
|
|
253
|
+
transition: border-color 0.2s;
|
|
254
|
+
}}
|
|
255
|
+
|
|
256
|
+
.bead-text-field:focus {{
|
|
257
|
+
outline: none;
|
|
258
|
+
border-color: var(--primary-color);
|
|
259
|
+
border-width: 2px;
|
|
260
|
+
}}
|
|
261
|
+
|
|
262
|
+
/* Dropdown styles */
|
|
263
|
+
.bead-dropdown {{
|
|
264
|
+
font-family: var(--font-family);
|
|
265
|
+
font-size: var(--font-size-body);
|
|
266
|
+
padding: 12px 16px;
|
|
267
|
+
border: 1px solid rgba(0, 0, 0, 0.38);
|
|
268
|
+
border-radius: var(--radius-sm);
|
|
269
|
+
background-color: var(--surface);
|
|
270
|
+
color: var(--on-surface);
|
|
271
|
+
cursor: pointer;
|
|
272
|
+
transition: border-color 0.2s;
|
|
273
|
+
}}
|
|
274
|
+
|
|
275
|
+
.bead-dropdown:focus {{
|
|
276
|
+
outline: none;
|
|
277
|
+
border-color: var(--primary-color);
|
|
278
|
+
border-width: 2px;
|
|
279
|
+
}}
|
|
280
|
+
|
|
281
|
+
/* Radio group styles */
|
|
282
|
+
.bead-radio-group {{
|
|
283
|
+
display: flex;
|
|
284
|
+
flex-direction: column;
|
|
285
|
+
gap: var(--spacing-sm);
|
|
286
|
+
}}
|
|
287
|
+
|
|
288
|
+
.bead-radio-option {{
|
|
289
|
+
display: flex;
|
|
290
|
+
align-items: center;
|
|
291
|
+
gap: var(--spacing-sm);
|
|
292
|
+
padding: var(--spacing-sm);
|
|
293
|
+
border-radius: var(--radius-sm);
|
|
294
|
+
cursor: pointer;
|
|
295
|
+
transition: background-color 0.2s;
|
|
296
|
+
}}
|
|
297
|
+
|
|
298
|
+
.bead-radio-option:hover {{
|
|
299
|
+
background-color: rgba(0, 0, 0, 0.04);
|
|
300
|
+
}}
|
|
301
|
+
|
|
302
|
+
/* Card styles */
|
|
303
|
+
.bead-card {{
|
|
304
|
+
background-color: var(--surface);
|
|
305
|
+
border-radius: var(--radius-md);
|
|
306
|
+
padding: var(--spacing-md);
|
|
307
|
+
box-shadow: var(--elevation-1);
|
|
308
|
+
transition: box-shadow 0.2s;
|
|
309
|
+
}}
|
|
310
|
+
|
|
311
|
+
.bead-card:hover {{
|
|
312
|
+
box-shadow: var(--elevation-2);
|
|
313
|
+
}}
|
|
314
|
+
|
|
315
|
+
/* Progress indicator styles */
|
|
316
|
+
.bead-progress {{
|
|
317
|
+
width: 100%;
|
|
318
|
+
height: 4px;
|
|
319
|
+
background-color: rgba(98, 0, 238, 0.12);
|
|
320
|
+
position: fixed;
|
|
321
|
+
top: 0;
|
|
322
|
+
left: 0;
|
|
323
|
+
z-index: 1000;
|
|
324
|
+
}}
|
|
325
|
+
|
|
326
|
+
.bead-progress-bar {{
|
|
327
|
+
height: 100%;
|
|
328
|
+
background-color: var(--primary-color);
|
|
329
|
+
transition: width 0.3s;
|
|
330
|
+
}}
|
|
331
|
+
|
|
332
|
+
/* Cloze task styles */
|
|
333
|
+
.bead-cloze-container {{
|
|
334
|
+
max-width: 900px;
|
|
335
|
+
margin: 0 auto;
|
|
336
|
+
padding: var(--spacing-xl);
|
|
337
|
+
}}
|
|
338
|
+
|
|
339
|
+
.bead-cloze-text {{
|
|
340
|
+
font-size: var(--font-size-body);
|
|
341
|
+
line-height: 2;
|
|
342
|
+
margin: var(--spacing-lg) 0;
|
|
343
|
+
}}
|
|
344
|
+
|
|
345
|
+
.bead-cloze-field {{
|
|
346
|
+
min-width: 120px;
|
|
347
|
+
margin: 0 var(--spacing-xs);
|
|
348
|
+
display: inline-block;
|
|
349
|
+
}}
|
|
350
|
+
|
|
351
|
+
.bead-cloze-button-container {{
|
|
352
|
+
display: flex;
|
|
353
|
+
justify-content: center;
|
|
354
|
+
margin-top: var(--spacing-xl);
|
|
355
|
+
}}
|
|
356
|
+
|
|
357
|
+
/* Forced choice styles */
|
|
358
|
+
.bead-forced-choice-container {{
|
|
359
|
+
max-width: 1000px;
|
|
360
|
+
margin: 0 auto;
|
|
361
|
+
padding: var(--spacing-xl);
|
|
362
|
+
}}
|
|
363
|
+
|
|
364
|
+
.bead-forced-choice-prompt {{
|
|
365
|
+
font-size: var(--font-size-title);
|
|
366
|
+
font-weight: 500;
|
|
367
|
+
margin-bottom: var(--spacing-lg);
|
|
368
|
+
text-align: center;
|
|
369
|
+
}}
|
|
370
|
+
|
|
371
|
+
.bead-forced-choice-alternatives {{
|
|
372
|
+
display: grid;
|
|
373
|
+
grid-template-columns: 1fr 1fr;
|
|
374
|
+
gap: var(--spacing-lg);
|
|
375
|
+
margin: var(--spacing-lg) 0;
|
|
376
|
+
}}
|
|
377
|
+
|
|
378
|
+
.bead-alternative {{
|
|
379
|
+
padding: var(--spacing-lg);
|
|
380
|
+
transition: transform 0.2s, box-shadow 0.2s;
|
|
381
|
+
}}
|
|
382
|
+
|
|
383
|
+
.bead-alternative:hover {{
|
|
384
|
+
transform: translateY(-2px);
|
|
385
|
+
}}
|
|
386
|
+
|
|
387
|
+
.bead-alternative.selected {{
|
|
388
|
+
border: 2px solid var(--primary-color);
|
|
389
|
+
box-shadow: var(--elevation-3);
|
|
390
|
+
}}
|
|
391
|
+
|
|
392
|
+
.bead-alternative-label {{
|
|
393
|
+
font-size: var(--font-size-label);
|
|
394
|
+
font-weight: 700;
|
|
395
|
+
color: var(--primary-color);
|
|
396
|
+
text-transform: uppercase;
|
|
397
|
+
letter-spacing: 1px;
|
|
398
|
+
margin-bottom: var(--spacing-sm);
|
|
399
|
+
}}
|
|
400
|
+
|
|
401
|
+
.bead-alternative-content {{
|
|
402
|
+
font-size: var(--font-size-body);
|
|
403
|
+
margin-bottom: var(--spacing-md);
|
|
404
|
+
min-height: 60px;
|
|
405
|
+
}}
|
|
406
|
+
|
|
407
|
+
.bead-choice-button {{
|
|
408
|
+
width: 100%;
|
|
409
|
+
}}
|
|
410
|
+
"""
|
|
411
|
+
return css
|
bead/dsl/__init__.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Domain-Specific Language (DSL) for constraint expressions.
|
|
2
|
+
|
|
3
|
+
Supports boolean operators (and, or, not), comparison operators, membership
|
|
4
|
+
tests, arithmetic, function calls, attribute access, and list literals.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from bead.dsl.ast import (
|
|
10
|
+
ASTNode,
|
|
11
|
+
AttributeAccess,
|
|
12
|
+
BinaryOp,
|
|
13
|
+
FunctionCall,
|
|
14
|
+
ListLiteral,
|
|
15
|
+
Literal,
|
|
16
|
+
UnaryOp,
|
|
17
|
+
Variable,
|
|
18
|
+
)
|
|
19
|
+
from bead.dsl.context import EvaluationContext
|
|
20
|
+
from bead.dsl.errors import DSLError, EvaluationError, ParseError
|
|
21
|
+
from bead.dsl.evaluator import Evaluator
|
|
22
|
+
from bead.dsl.parser import parse
|
|
23
|
+
from bead.dsl.stdlib import SIMULATION_FUNCTIONS, STDLIB_FUNCTIONS, register_stdlib
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
# AST nodes
|
|
27
|
+
"ASTNode",
|
|
28
|
+
"Literal",
|
|
29
|
+
"Variable",
|
|
30
|
+
"BinaryOp",
|
|
31
|
+
"UnaryOp",
|
|
32
|
+
"FunctionCall",
|
|
33
|
+
"ListLiteral",
|
|
34
|
+
"AttributeAccess",
|
|
35
|
+
# Errors
|
|
36
|
+
"DSLError",
|
|
37
|
+
"ParseError",
|
|
38
|
+
"EvaluationError",
|
|
39
|
+
# Parser
|
|
40
|
+
"parse",
|
|
41
|
+
# Evaluation
|
|
42
|
+
"Evaluator",
|
|
43
|
+
"EvaluationContext",
|
|
44
|
+
"evaluate",
|
|
45
|
+
# Standard library
|
|
46
|
+
"STDLIB_FUNCTIONS",
|
|
47
|
+
"SIMULATION_FUNCTIONS",
|
|
48
|
+
"register_stdlib",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# Convenience function
|
|
53
|
+
def evaluate(node: ASTNode, context: EvaluationContext, use_cache: bool = True) -> Any:
|
|
54
|
+
"""Evaluate a constraint expression.
|
|
55
|
+
|
|
56
|
+
Parameters
|
|
57
|
+
----------
|
|
58
|
+
node : ASTNode
|
|
59
|
+
Parsed AST node to evaluate.
|
|
60
|
+
context : EvaluationContext
|
|
61
|
+
Evaluation context with variables and functions.
|
|
62
|
+
use_cache : bool
|
|
63
|
+
Whether to use caching.
|
|
64
|
+
|
|
65
|
+
Returns
|
|
66
|
+
-------
|
|
67
|
+
Any
|
|
68
|
+
Result of evaluation.
|
|
69
|
+
|
|
70
|
+
Examples
|
|
71
|
+
--------
|
|
72
|
+
>>> from bead.dsl import parse, EvaluationContext
|
|
73
|
+
>>> ctx = EvaluationContext()
|
|
74
|
+
>>> ctx.set_variable("x", 10)
|
|
75
|
+
>>> node = parse("x > 5")
|
|
76
|
+
>>> evaluate(node, ctx)
|
|
77
|
+
True
|
|
78
|
+
"""
|
|
79
|
+
evaluator = Evaluator(use_cache=use_cache)
|
|
80
|
+
return evaluator.evaluate(node, context)
|
bead/dsl/ast.py
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""Abstract Syntax Tree node definitions for constraint DSL.
|
|
2
|
+
|
|
3
|
+
This module defines the AST nodes that represent parsed constraint expressions.
|
|
4
|
+
Each node type corresponds to a construct in the constraint DSL grammar.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from bead.data.base import BeadBaseModel
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ASTNode(BeadBaseModel):
|
|
13
|
+
"""Base class for all AST nodes.
|
|
14
|
+
|
|
15
|
+
All AST nodes inherit from BeadBaseModel to get:
|
|
16
|
+
- Automatic validation
|
|
17
|
+
- Serialization support
|
|
18
|
+
- Metadata tracking
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class Literal(ASTNode):
|
|
25
|
+
"""Literal value node (string, number, boolean).
|
|
26
|
+
|
|
27
|
+
Examples
|
|
28
|
+
--------
|
|
29
|
+
>>> node = Literal(value="hello")
|
|
30
|
+
>>> node.value
|
|
31
|
+
'hello'
|
|
32
|
+
>>> node = Literal(value=42)
|
|
33
|
+
>>> node.value
|
|
34
|
+
42
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
value: str | int | float | bool
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class Variable(ASTNode):
|
|
41
|
+
"""Variable reference node.
|
|
42
|
+
|
|
43
|
+
References a variable in the evaluation context (e.g., item attributes).
|
|
44
|
+
|
|
45
|
+
Examples
|
|
46
|
+
--------
|
|
47
|
+
>>> node = Variable(name="lemma")
|
|
48
|
+
>>> node.name
|
|
49
|
+
'lemma'
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
name: str
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class BinaryOp(ASTNode):
|
|
56
|
+
"""Binary operation node.
|
|
57
|
+
|
|
58
|
+
Represents operations like: a == b, x > y, p and q
|
|
59
|
+
|
|
60
|
+
Attributes
|
|
61
|
+
----------
|
|
62
|
+
operator : str
|
|
63
|
+
The operator (==, !=, <, >, <=, >=, and, or, in, etc.)
|
|
64
|
+
left : ASTNode
|
|
65
|
+
Left operand
|
|
66
|
+
right : ASTNode
|
|
67
|
+
Right operand
|
|
68
|
+
|
|
69
|
+
Examples
|
|
70
|
+
--------
|
|
71
|
+
>>> left = Variable(name="pos")
|
|
72
|
+
>>> right = Literal(value="VERB")
|
|
73
|
+
>>> node = BinaryOp(operator="==", left=left, right=right)
|
|
74
|
+
>>> node.operator
|
|
75
|
+
'=='
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
operator: str
|
|
79
|
+
left: ASTNode
|
|
80
|
+
right: ASTNode
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class UnaryOp(ASTNode):
|
|
84
|
+
"""Unary operation node.
|
|
85
|
+
|
|
86
|
+
Represents operations like: not x, -y
|
|
87
|
+
|
|
88
|
+
Examples
|
|
89
|
+
--------
|
|
90
|
+
>>> operand = Variable(name="is_transitive")
|
|
91
|
+
>>> node = UnaryOp(operator="not", operand=operand)
|
|
92
|
+
>>> node.operator
|
|
93
|
+
'not'
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
operator: str
|
|
97
|
+
operand: ASTNode
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class FunctionCall(ASTNode):
|
|
101
|
+
"""Function call node.
|
|
102
|
+
|
|
103
|
+
Represents function calls and method calls like:
|
|
104
|
+
- len(x), startswith("pre")
|
|
105
|
+
- obj.method(arg)
|
|
106
|
+
|
|
107
|
+
Examples
|
|
108
|
+
--------
|
|
109
|
+
>>> func = Variable(name="len")
|
|
110
|
+
>>> arg = Variable(name="lemma")
|
|
111
|
+
>>> node = FunctionCall(function=func, arguments=[arg])
|
|
112
|
+
>>> node.function.name
|
|
113
|
+
'len'
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
function: ASTNode # Variable for functions, AttributeAccess for methods
|
|
117
|
+
arguments: list[ASTNode]
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class ListLiteral(ASTNode):
|
|
121
|
+
"""List literal node.
|
|
122
|
+
|
|
123
|
+
Represents list literals like: ["a", "b", "c"]
|
|
124
|
+
|
|
125
|
+
Examples
|
|
126
|
+
--------
|
|
127
|
+
>>> elements = [Literal(value="a"), Literal(value="b")]
|
|
128
|
+
>>> node = ListLiteral(elements=elements)
|
|
129
|
+
>>> len(node.elements)
|
|
130
|
+
2
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
elements: list[ASTNode]
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class AttributeAccess(ASTNode):
|
|
137
|
+
"""Attribute access node.
|
|
138
|
+
|
|
139
|
+
Represents attribute access like: item.lemma, obj.property
|
|
140
|
+
|
|
141
|
+
Examples
|
|
142
|
+
--------
|
|
143
|
+
>>> obj = Variable(name="item")
|
|
144
|
+
>>> node = AttributeAccess(object=obj, attribute="lemma")
|
|
145
|
+
>>> node.attribute
|
|
146
|
+
'lemma'
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
object: ASTNode
|
|
150
|
+
attribute: str
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class Subscript(ASTNode):
|
|
154
|
+
"""Subscript access node.
|
|
155
|
+
|
|
156
|
+
Represents subscript access like: item['key'], obj[0]
|
|
157
|
+
|
|
158
|
+
Examples
|
|
159
|
+
--------
|
|
160
|
+
>>> obj = Variable(name="item")
|
|
161
|
+
>>> key = Literal(value="key")
|
|
162
|
+
>>> node = Subscript(object=obj, index=key)
|
|
163
|
+
>>> node.index.value
|
|
164
|
+
'key'
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
object: ASTNode
|
|
168
|
+
index: ASTNode
|