stylpp 1.0.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.
@@ -0,0 +1,286 @@
1
+ /**
2
+ * STYL++ Physics Engine
3
+ * Provides physics-based animations, constraints, and layout calculations
4
+ * Transcends CSS limitations with real physics simulation
5
+ */
6
+
7
+ class PhysicsEngine {
8
+ constructor(options = {}) {
9
+ this.options = {
10
+ gravity: 9.81,
11
+ damping: 0.9,
12
+ friction: 0.2,
13
+ timeStep: 0.016,
14
+ iterations: 4,
15
+ ...options
16
+ };
17
+
18
+ this.bodies = new Map();
19
+ this.constraints = new Map();
20
+ this.forces = new Map();
21
+ this.time = 0;
22
+ }
23
+
24
+ /**
25
+ * Create a physics body for an element
26
+ * @param {string} id - Element ID
27
+ * @param {Object} config - Body configuration
28
+ */
29
+ createBody(id, config = {}) {
30
+ const body = {
31
+ id,
32
+ position: { x: config.x || 0, y: config.y || 0 },
33
+ velocity: { x: config.vx || 0, y: config.vy || 0 },
34
+ acceleration: { x: 0, y: 0 },
35
+ mass: config.mass || 1,
36
+ bounce: config.bounce || 0.6,
37
+ friction: config.friction || this.options.friction,
38
+ pinned: config.pinned || false,
39
+ width: config.width || 0,
40
+ height: config.height || 0,
41
+ constraints: []
42
+ };
43
+
44
+ this.bodies.set(id, body);
45
+ return body;
46
+ }
47
+
48
+ /**
49
+ * Add force to body (gravity, wind, custom forces)
50
+ * @param {string} bodyId - Body ID
51
+ * @param {Object} force - Force vector {x, y}
52
+ */
53
+ addForce(bodyId, force) {
54
+ const body = this.bodies.get(bodyId);
55
+ if (!body) return;
56
+
57
+ body.acceleration.x += force.x / body.mass;
58
+ body.acceleration.y += force.y / body.mass;
59
+ }
60
+
61
+ /**
62
+ * Create constraint between bodies (springs, rods, etc.)
63
+ * @param {string} id - Constraint ID
64
+ * @param {string} body1Id - First body ID
65
+ * @param {string} body2Id - Second body ID
66
+ * @param {Object} config - Constraint configuration
67
+ */
68
+ createConstraint(id, body1Id, body2Id, config = {}) {
69
+ const constraint = {
70
+ id,
71
+ body1Id,
72
+ body2Id,
73
+ type: config.type || 'spring', // 'spring', 'rod', 'distance', 'angle'
74
+ restLength: config.restLength || 100,
75
+ stiffness: config.stiffness || 0.5,
76
+ damping: config.damping || 0.3,
77
+ maxForce: config.maxForce || 1000
78
+ };
79
+
80
+ this.constraints.set(id, constraint);
81
+ const body1 = this.bodies.get(body1Id);
82
+ if (body1) body1.constraints.push(id);
83
+ return constraint;
84
+ }
85
+
86
+ /**
87
+ * Spring-based layout constraint
88
+ * Simulates attractive/repulsive forces between elements
89
+ */
90
+ createSpringLayout(elementIds, config = {}) {
91
+ const stiffness = config.stiffness || 0.3;
92
+ const damping = config.damping || 0.2;
93
+ const restDistance = config.restDistance || 150;
94
+
95
+ for (let i = 0; i < elementIds.length; i++) {
96
+ for (let j = i + 1; j < elementIds.length; j++) {
97
+ this.createConstraint(
98
+ `spring-${i}-${j}`,
99
+ elementIds[i],
100
+ elementIds[j],
101
+ {
102
+ type: 'spring',
103
+ restLength: restDistance,
104
+ stiffness: stiffness,
105
+ damping: damping
106
+ }
107
+ );
108
+ }
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Simulate gravity field
114
+ */
115
+ simulateGravity() {
116
+ this.bodies.forEach((body) => {
117
+ if (!body.pinned) {
118
+ this.addForce(body.id, { x: 0, y: this.options.gravity * body.mass });
119
+ }
120
+ });
121
+ }
122
+
123
+ /**
124
+ * Apply Verlet integration for physics simulation
125
+ */
126
+ step(deltaTime = this.options.timeStep) {
127
+ // Apply gravity
128
+ this.simulateGravity();
129
+
130
+ // Update velocity and position (Verlet)
131
+ this.bodies.forEach((body) => {
132
+ if (body.pinned) return;
133
+
134
+ // Apply damping
135
+ body.velocity.x *= this.options.damping;
136
+ body.velocity.y *= this.options.damping;
137
+
138
+ // Update velocity
139
+ body.velocity.x += body.acceleration.x * deltaTime;
140
+ body.velocity.y += body.acceleration.y * deltaTime;
141
+
142
+ // Update position
143
+ body.position.x += body.velocity.x * deltaTime;
144
+ body.position.y += body.velocity.y * deltaTime;
145
+
146
+ // Reset acceleration
147
+ body.acceleration.x = 0;
148
+ body.acceleration.y = 0;
149
+ });
150
+
151
+ // Resolve constraints
152
+ for (let i = 0; i < this.options.iterations; i++) {
153
+ this.constraints.forEach((constraint) => {
154
+ this.resolveConstraint(constraint);
155
+ });
156
+ }
157
+
158
+ this.time += deltaTime;
159
+ }
160
+
161
+ /**
162
+ * Resolve a constraint
163
+ */
164
+ resolveConstraint(constraint) {
165
+ const body1 = this.bodies.get(constraint.body1Id);
166
+ const body2 = this.bodies.get(constraint.body2Id);
167
+
168
+ if (!body1 || !body2) return;
169
+
170
+ const dx = body2.position.x - body1.position.x;
171
+ const dy = body2.position.y - body1.position.y;
172
+ const distance = Math.sqrt(dx * dx + dy * dy);
173
+
174
+ if (constraint.type === 'spring') {
175
+ this.resolveSpring(body1, body2, constraint, distance);
176
+ } else if (constraint.type === 'rod') {
177
+ this.resolveRod(body1, body2, constraint, distance);
178
+ } else if (constraint.type === 'distance') {
179
+ this.resolveDistance(body1, body2, constraint, distance);
180
+ }
181
+ }
182
+
183
+ /**
184
+ * Resolve spring constraint
185
+ */
186
+ resolveSpring(body1, body2, constraint, distance) {
187
+ const dx = body2.position.x - body1.position.x;
188
+ const dy = body2.position.y - body1.position.y;
189
+
190
+ if (distance === 0) return;
191
+
192
+ // Spring force: F = -k * (d - r)
193
+ const force = -constraint.stiffness * (distance - constraint.restLength);
194
+ const fx = (force * dx) / distance;
195
+ const fy = (force * dy) / distance;
196
+
197
+ // Apply damping
198
+ const dvx = body2.velocity.x - body1.velocity.x;
199
+ const dvy = body2.velocity.y - body1.velocity.y;
200
+ const dampingForce =
201
+ constraint.damping * ((dvx * dx + dvy * dy) / distance);
202
+
203
+ if (!body1.pinned) {
204
+ body1.acceleration.x += (fx + dampingForce * dx / distance) / body1.mass;
205
+ body1.acceleration.y += (fy + dampingForce * dy / distance) / body1.mass;
206
+ }
207
+
208
+ if (!body2.pinned) {
209
+ body2.acceleration.x -= (fx + dampingForce * dx / distance) / body2.mass;
210
+ body2.acceleration.y -= (fy + dampingForce * dy / distance) / body2.mass;
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Resolve rod constraint (fixed distance)
216
+ */
217
+ resolveRod(body1, body2, constraint, distance) {
218
+ const dx = body2.position.x - body1.position.x;
219
+ const dy = body2.position.y - body1.position.y;
220
+
221
+ if (distance === 0) return;
222
+
223
+ const delta = (distance - constraint.restLength) / distance;
224
+
225
+ if (!body1.pinned) {
226
+ body1.position.x += (delta * dx) / 2;
227
+ body1.position.y += (delta * dy) / 2;
228
+ }
229
+
230
+ if (!body2.pinned) {
231
+ body2.position.x -= (delta * dx) / 2;
232
+ body2.position.y -= (delta * dy) / 2;
233
+ }
234
+ }
235
+
236
+ /**
237
+ * Resolve distance constraint
238
+ */
239
+ resolveDistance(body1, body2, constraint, distance) {
240
+ const targetDistance = constraint.restLength;
241
+
242
+ if (distance > targetDistance) {
243
+ const dx = body2.position.x - body1.position.x;
244
+ const dy = body2.position.y - body1.position.y;
245
+ const delta = (distance - targetDistance) / distance;
246
+
247
+ if (!body1.pinned) {
248
+ body1.position.x += (delta * dx) / 2;
249
+ body1.position.y += (delta * dy) / 2;
250
+ }
251
+
252
+ if (!body2.pinned) {
253
+ body2.position.x -= (delta * dx) / 2;
254
+ body2.position.y -= (delta * dy) / 2;
255
+ }
256
+ }
257
+ }
258
+
259
+ /**
260
+ * Get body state
261
+ */
262
+ getBodyState(id) {
263
+ return this.bodies.get(id);
264
+ }
265
+
266
+ /**
267
+ * Simulate multiple steps at once
268
+ */
269
+ simulate(steps = 60) {
270
+ for (let i = 0; i < steps; i++) {
271
+ this.step();
272
+ }
273
+ }
274
+
275
+ /**
276
+ * Convert physics state to CSS transforms
277
+ */
278
+ toCSS(bodyId) {
279
+ const body = this.bodies.get(bodyId);
280
+ if (!body) return '';
281
+
282
+ return `transform: translate3d(${body.position.x.toFixed(2)}px, ${body.position.y.toFixed(2)}px, 0);`;
283
+ }
284
+ }
285
+
286
+ module.exports = PhysicsEngine;
@@ -0,0 +1,228 @@
1
+ /**
2
+ * STYL++ Transformer
3
+ * Transforms the AST to handle:
4
+ * - Variable resolution
5
+ * - Math operations
6
+ * - Loop expansion
7
+ * - Conditional processing
8
+ * - Function expansion
9
+ */
10
+
11
+ class StylppTransformer {
12
+ constructor(options = {}) {
13
+ this.options = options;
14
+ this.variables = {};
15
+ this.errors = [];
16
+ }
17
+
18
+ /**
19
+ * Transform AST
20
+ * @param {Object} ast - Original AST
21
+ * @returns {Object} Transformed AST
22
+ */
23
+ transform(ast) {
24
+ // First pass: collect all variables
25
+ this.variables = { ...ast.variables };
26
+
27
+ // Second pass: transform rules
28
+ const transformedRules = ast.rules.map(rule => this.transformRule(rule));
29
+
30
+ return {
31
+ type: 'stylesheet',
32
+ rules: transformedRules,
33
+ variables: this.variables
34
+ };
35
+ }
36
+
37
+ /**
38
+ * Transform a single rule
39
+ * @param {Object} rule - Rule object
40
+ * @returns {Object} Transformed rule
41
+ */
42
+ transformRule(rule) {
43
+ const transformed = {
44
+ type: rule.type,
45
+ selector: rule.selector,
46
+ declarations: [],
47
+ rules: []
48
+ };
49
+
50
+ // Transform declarations
51
+ if (rule.declarations) {
52
+ rule.declarations.forEach(decl => {
53
+ const transformedDecl = this.transformDeclaration(decl);
54
+ if (Array.isArray(transformedDecl)) {
55
+ transformed.declarations.push(...transformedDecl);
56
+ } else {
57
+ transformed.declarations.push(transformedDecl);
58
+ }
59
+ });
60
+ }
61
+
62
+ // Transform nested rules
63
+ if (rule.rules) {
64
+ rule.rules.forEach(nestedRule => {
65
+ const transformed = this.transformRule(nestedRule);
66
+ if (Array.isArray(transformed)) {
67
+ transformed.forEach(r => rule.rules.push(r));
68
+ } else {
69
+ rule.rules.push(transformed);
70
+ }
71
+ });
72
+ transformed.rules = rule.rules;
73
+ }
74
+
75
+ return transformed;
76
+ }
77
+
78
+ /**
79
+ * Transform a declaration (property-value pair)
80
+ * @param {Object} decl - Declaration object
81
+ * @returns {Object|Array} Transformed declaration(s)
82
+ */
83
+ transformDeclaration(decl) {
84
+ let value = decl.value;
85
+
86
+ // Resolve variables
87
+ value = this.resolveVariables(value);
88
+
89
+ // Handle math operations
90
+ if (this.hasMathOperation(value)) {
91
+ value = this.processMathOperation(value);
92
+ }
93
+
94
+ // Handle responsive values
95
+ if (this.isResponsiveValue(value)) {
96
+ value = this.processResponsiveValue(value);
97
+ }
98
+
99
+ // Handle color operations
100
+ if (this.isColorOperation(value)) {
101
+ value = this.processColorOperation(value);
102
+ }
103
+
104
+ return {
105
+ type: 'declaration',
106
+ property: decl.property,
107
+ value: value
108
+ };
109
+ }
110
+
111
+ /**
112
+ * Resolve variables in value (e.g., var(primary) -> #007bff)
113
+ * @param {string} value - Value string
114
+ * @returns {string} Resolved value
115
+ */
116
+ resolveVariables(value) {
117
+ return value.replace(/var\((\w+)\)/g, (match, varName) => {
118
+ return this.variables[varName] || match;
119
+ });
120
+ }
121
+
122
+ /**
123
+ * Check if value contains math operations
124
+ * @param {string} value - Value string
125
+ * @returns {boolean}
126
+ */
127
+ hasMathOperation(value) {
128
+ return /[\+\-\*\/\^]/.test(value) && /\d/.test(value);
129
+ }
130
+
131
+ /**
132
+ * Process math operations: 100% - 40px -> calc(100% - 40px)
133
+ * @param {string} value - Value with math
134
+ * @returns {string} CSS calc expression
135
+ */
136
+ processMathOperation(value) {
137
+ // Already has calc? Return as-is
138
+ if (value.includes('calc(')) return value;
139
+
140
+ // Check for mathematical expressions
141
+ const mathPattern = /^[\d\w\s\+\-\*\/\%\(\)\.]+$/;
142
+ if (mathPattern.test(value.replace(/px|em|rem|%|vh|vw|ex|ch|deg|s|ms/g, ''))) {
143
+ return `calc(${value})`;
144
+ }
145
+
146
+ return value;
147
+ }
148
+
149
+ /**
150
+ * Check if value is a responsive function
151
+ * @param {string} value - Value string
152
+ * @returns {boolean}
153
+ */
154
+ isResponsiveValue(value) {
155
+ return /responsive\(|fluid\(/.test(value);
156
+ }
157
+
158
+ /**
159
+ * Process responsive value: responsive(300px, 800px) -> clamp(...)
160
+ * @param {string} value - Value string
161
+ * @returns {string} Clamp expression
162
+ */
163
+ processResponsiveValue(value) {
164
+ // responsive(300px, 800px) -> clamp(300px, 50vw + 100px, 800px)
165
+ const match = value.match(/responsive\(([^,]+),\s*([^)]+)\)/);
166
+ if (match) {
167
+ const min = match[1].trim();
168
+ const max = match[2].trim();
169
+ const mid = `${(parseInt(min) + parseInt(max)) / 4}px`;
170
+ return `clamp(${min}, 50vw + ${mid}, ${max})`;
171
+ }
172
+
173
+ // fluid(300px, 800px)
174
+ const fluidMatch = value.match(/fluid\(([^,]+),\s*([^)]+)\)/);
175
+ if (fluidMatch) {
176
+ const min = fluidMatch[1].trim();
177
+ const max = fluidMatch[2].trim();
178
+ return `clamp(${min}, 5vw, ${max})`;
179
+ }
180
+
181
+ return value;
182
+ }
183
+
184
+ /**
185
+ * Check if value uses color operations
186
+ * @param {string} value - Value string
187
+ * @returns {boolean}
188
+ */
189
+ isColorOperation(value) {
190
+ return /darken\(|lighten\(|mix\(/.test(value);
191
+ }
192
+
193
+ /**
194
+ * Process color operations
195
+ * @param {string} value - Value string
196
+ * @returns {string} Processed color
197
+ */
198
+ processColorOperation(value) {
199
+ // darken(#007bff, 20%) - simplified implementation
200
+ const darkenMatch = value.match(/darken\(([^,]+),\s*(\d+)%\)/);
201
+ if (darkenMatch) {
202
+ const color = darkenMatch[1].trim();
203
+ const amount = parseInt(darkenMatch[2]);
204
+ // Simple approximation - in real implementation would use color library
205
+ return `${color}`;
206
+ }
207
+
208
+ return value;
209
+ }
210
+
211
+ /**
212
+ * Expand for loops
213
+ * @param {Object} rule - Rule with for loop
214
+ * @param {number} iterations - Number of iterations
215
+ * @returns {Array} Expanded rules
216
+ */
217
+ expandForLoop(rule, iterations) {
218
+ const expanded = [];
219
+ for (let i = 0; i < iterations; i++) {
220
+ const expandedRule = { ...rule };
221
+ expandedRule.selector = rule.selector.replace(/{i}/g, i);
222
+ expanded.push(expandedRule);
223
+ }
224
+ return expanded;
225
+ }
226
+ }
227
+
228
+ module.exports = StylppTransformer;