violit 0.0.5__py3-none-any.whl → 0.0.6__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.
violit/component.py CHANGED
@@ -1,38 +1,38 @@
1
- from typing import Any, Optional
2
- import html
3
-
4
- class Component:
5
- def __init__(self, tag: Optional[str], id: str, escape_content: bool = False, **props):
6
- self.tag = tag
7
- self.id = id
8
- self.escape_content = escape_content # XSS protection
9
- self.props = props
10
-
11
- def render(self) -> str:
12
- if self.tag is None:
13
- content = str(self.props.get('content', ''))
14
- # Escape content if enabled
15
- return html.escape(content) if self.escape_content else content
16
-
17
- attrs = []
18
- for k, v in self.props.items():
19
- if k == 'content': continue
20
- clean_k = k.replace('_', '-') if not k.startswith('on') else k
21
- if clean_k.startswith('on'):
22
- attrs.append(f'{clean_k}="{v}"')
23
- else:
24
- if v is True: attrs.append(clean_k)
25
- elif v is False or v is None: continue
26
- else:
27
- # Escape attribute values for XSS protection
28
- escaped_v = html.escape(str(v), quote=True)
29
- attrs.append(f'{clean_k}="{escaped_v}"')
30
-
31
- props_str = " ".join(attrs)
32
- content = self.props.get('content', '')
33
-
34
- # Escape content if enabled
35
- if self.escape_content:
36
- content = html.escape(str(content))
37
-
38
- return f"<{self.tag} id=\"{self.id}\" {props_str}>{content}</{self.tag}>"
1
+ from typing import Any, Optional
2
+ import html
3
+
4
+ class Component:
5
+ def __init__(self, tag: Optional[str], id: str, escape_content: bool = False, **props):
6
+ self.tag = tag
7
+ self.id = id
8
+ self.escape_content = escape_content # XSS protection
9
+ self.props = props
10
+
11
+ def render(self) -> str:
12
+ if self.tag is None:
13
+ content = str(self.props.get('content', ''))
14
+ # Escape content if enabled
15
+ return html.escape(content) if self.escape_content else content
16
+
17
+ attrs = []
18
+ for k, v in self.props.items():
19
+ if k == 'content': continue
20
+ clean_k = k.replace('_', '-') if not k.startswith('on') else k
21
+ if clean_k.startswith('on'):
22
+ attrs.append(f'{clean_k}="{v}"')
23
+ else:
24
+ if v is True: attrs.append(clean_k)
25
+ elif v is False or v is None: continue
26
+ else:
27
+ # Escape attribute values for XSS protection
28
+ escaped_v = html.escape(str(v), quote=True)
29
+ attrs.append(f'{clean_k}="{escaped_v}"')
30
+
31
+ props_str = " ".join(attrs)
32
+ content = self.props.get('content', '')
33
+
34
+ # Escape content if enabled
35
+ if self.escape_content:
36
+ content = html.escape(str(content))
37
+
38
+ return f"<{self.tag} id=\"{self.id}\" {props_str}>{content}</{self.tag}>"
violit/state.py CHANGED
@@ -83,6 +83,108 @@ class State:
83
83
  def __gt__(self, other): return ComputedState(lambda: self.value > other)
84
84
  def __ge__(self, other): return ComputedState(lambda: self.value >= other)
85
85
 
86
+ # Reactive Arithmetic Operators
87
+ def __add__(self, other):
88
+ other_val = other.value if isinstance(other, (State, ComputedState)) else other
89
+ def compute():
90
+ self_val = self.value
91
+ # If either is a string, concatenate as strings
92
+ if isinstance(self_val, str) or isinstance(other_val, str):
93
+ return str(self_val) + str(other_val)
94
+ else:
95
+ return self_val + other_val
96
+ return ComputedState(compute)
97
+
98
+ def __radd__(self, other):
99
+ other_val = other.value if isinstance(other, (State, ComputedState)) else other
100
+ def compute():
101
+ self_val = self.value
102
+ # If either is a string, concatenate as strings
103
+ if isinstance(self_val, str) or isinstance(other_val, str):
104
+ return str(other_val) + str(self_val)
105
+ else:
106
+ return other_val + self_val
107
+ return ComputedState(compute)
108
+
109
+ def __sub__(self, other):
110
+ other_val = other.value if isinstance(other, (State, ComputedState)) else other
111
+ return ComputedState(lambda: self.value - other_val)
112
+
113
+ def __rsub__(self, other):
114
+ other_val = other.value if isinstance(other, (State, ComputedState)) else other
115
+ return ComputedState(lambda: other_val - self.value)
116
+
117
+ def __mul__(self, other):
118
+ other_val = other.value if isinstance(other, (State, ComputedState)) else other
119
+ def compute():
120
+ self_val = self.value
121
+ # String repetition: "ab" * 3 or 3 * "ab"
122
+ if isinstance(self_val, str) or isinstance(other_val, str):
123
+ # One must be int for string repetition to work
124
+ if isinstance(self_val, str) and isinstance(other_val, int):
125
+ return self_val * other_val
126
+ elif isinstance(other_val, str) and isinstance(self_val, int):
127
+ return self_val * other_val
128
+ else:
129
+ # Both strings? Convert to numeric or error
130
+ return self_val * other_val
131
+ else:
132
+ return self_val * other_val
133
+ return ComputedState(compute)
134
+
135
+ def __rmul__(self, other):
136
+ other_val = other.value if isinstance(other, (State, ComputedState)) else other
137
+ def compute():
138
+ self_val = self.value
139
+ # String repetition: "ab" * 3 or 3 * "ab"
140
+ if isinstance(self_val, str) or isinstance(other_val, str):
141
+ if isinstance(other_val, str) and isinstance(self_val, int):
142
+ return other_val * self_val
143
+ elif isinstance(self_val, str) and isinstance(other_val, int):
144
+ return other_val * self_val
145
+ else:
146
+ return other_val * self_val
147
+ else:
148
+ return other_val * self_val
149
+ return ComputedState(compute)
150
+
151
+ def __truediv__(self, other):
152
+ other_val = other.value if isinstance(other, (State, ComputedState)) else other
153
+ return ComputedState(lambda: self.value / other_val)
154
+
155
+ def __rtruediv__(self, other):
156
+ other_val = other.value if isinstance(other, (State, ComputedState)) else other
157
+ return ComputedState(lambda: other_val / self.value)
158
+
159
+ def __floordiv__(self, other):
160
+ other_val = other.value if isinstance(other, (State, ComputedState)) else other
161
+ return ComputedState(lambda: self.value // other_val)
162
+
163
+ def __rfloordiv__(self, other):
164
+ other_val = other.value if isinstance(other, (State, ComputedState)) else other
165
+ return ComputedState(lambda: other_val // self.value)
166
+
167
+ def __mod__(self, other):
168
+ other_val = other.value if isinstance(other, (State, ComputedState)) else other
169
+ return ComputedState(lambda: self.value % other_val)
170
+
171
+ def __rmod__(self, other):
172
+ other_val = other.value if isinstance(other, (State, ComputedState)) else other
173
+ return ComputedState(lambda: other_val % self.value)
174
+
175
+ def __pow__(self, other):
176
+ other_val = other.value if isinstance(other, (State, ComputedState)) else other
177
+ return ComputedState(lambda: self.value ** other_val)
178
+
179
+ def __rpow__(self, other):
180
+ other_val = other.value if isinstance(other, (State, ComputedState)) else other
181
+ return ComputedState(lambda: other_val ** self.value)
182
+
183
+ # String formatting support
184
+ def __format__(self, format_spec):
185
+ """Support for f-string formatting: f'{state:03d}'"""
186
+ return format(self.value, format_spec)
187
+
86
188
 
87
189
  class ComputedState:
88
190
  """A state derived from other states (e.g. expressions)"""
@@ -107,3 +209,102 @@ class ComputedState:
107
209
  def __or__(self, other):
108
210
  val_other = other.value if hasattr(other, 'value') else other
109
211
  return ComputedState(lambda: self.value or val_other)
212
+
213
+ # Reactive Arithmetic Operators (Mirroring State)
214
+ def __add__(self, other):
215
+ other_val = other.value if hasattr(other, 'value') else other
216
+ def compute():
217
+ self_val = self.value
218
+ # If either is a string, concatenate as strings
219
+ if isinstance(self_val, str) or isinstance(other_val, str):
220
+ return str(self_val) + str(other_val)
221
+ else:
222
+ return self_val + other_val
223
+ return ComputedState(compute)
224
+
225
+ def __radd__(self, other):
226
+ other_val = other.value if hasattr(other, 'value') else other
227
+ def compute():
228
+ self_val = self.value
229
+ # If either is a string, concatenate as strings
230
+ if isinstance(self_val, str) or isinstance(other_val, str):
231
+ return str(other_val) + str(self_val)
232
+ else:
233
+ return other_val + self_val
234
+ return ComputedState(compute)
235
+
236
+ def __sub__(self, other):
237
+ other_val = other.value if hasattr(other, 'value') else other
238
+ return ComputedState(lambda: self.value - other_val)
239
+
240
+ def __rsub__(self, other):
241
+ other_val = other.value if hasattr(other, 'value') else other
242
+ return ComputedState(lambda: other_val - self.value)
243
+
244
+ def __mul__(self, other):
245
+ other_val = other.value if hasattr(other, 'value') else other
246
+ def compute():
247
+ self_val = self.value
248
+ # String repetition: "ab" * 3 or 3 * "ab"
249
+ if isinstance(self_val, str) or isinstance(other_val, str):
250
+ if isinstance(self_val, str) and isinstance(other_val, int):
251
+ return self_val * other_val
252
+ elif isinstance(other_val, str) and isinstance(self_val, int):
253
+ return self_val * other_val
254
+ else:
255
+ return self_val * other_val
256
+ else:
257
+ return self_val * other_val
258
+ return ComputedState(compute)
259
+
260
+ def __rmul__(self, other):
261
+ other_val = other.value if hasattr(other, 'value') else other
262
+ def compute():
263
+ self_val = self.value
264
+ # String repetition: "ab" * 3 or 3 * "ab"
265
+ if isinstance(self_val, str) or isinstance(other_val, str):
266
+ if isinstance(other_val, str) and isinstance(self_val, int):
267
+ return other_val * self_val
268
+ elif isinstance(self_val, str) and isinstance(other_val, int):
269
+ return other_val * self_val
270
+ else:
271
+ return other_val * self_val
272
+ else:
273
+ return other_val * self_val
274
+ return ComputedState(compute)
275
+
276
+ def __truediv__(self, other):
277
+ other_val = other.value if hasattr(other, 'value') else other
278
+ return ComputedState(lambda: self.value / other_val)
279
+
280
+ def __rtruediv__(self, other):
281
+ other_val = other.value if hasattr(other, 'value') else other
282
+ return ComputedState(lambda: other_val / self.value)
283
+
284
+ def __floordiv__(self, other):
285
+ other_val = other.value if hasattr(other, 'value') else other
286
+ return ComputedState(lambda: self.value // other_val)
287
+
288
+ def __rfloordiv__(self, other):
289
+ other_val = other.value if hasattr(other, 'value') else other
290
+ return ComputedState(lambda: other_val // self.value)
291
+
292
+ def __mod__(self, other):
293
+ other_val = other.value if hasattr(other, 'value') else other
294
+ return ComputedState(lambda: self.value % other_val)
295
+
296
+ def __rmod__(self, other):
297
+ other_val = other.value if hasattr(other, 'value') else other
298
+ return ComputedState(lambda: other_val % self.value)
299
+
300
+ def __pow__(self, other):
301
+ other_val = other.value if hasattr(other, 'value') else other
302
+ return ComputedState(lambda: self.value ** other_val)
303
+
304
+ def __rpow__(self, other):
305
+ other_val = other.value if hasattr(other, 'value') else other
306
+ return ComputedState(lambda: other_val ** self.value)
307
+
308
+ # String formatting support
309
+ def __format__(self, format_spec):
310
+ return format(self.value, format_spec)