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,293 @@
1
+ /**
2
+ * STYL++ Advanced Runtime
3
+ * Integrates physics engine, animations, and advanced effects
4
+ * Runs in browser for real-time rendering
5
+ */
6
+
7
+ const StylppCompiler = require('../compiler/index.js');
8
+ const PhysicsEngine = require('../compiler/physics.js');
9
+ const { AnimationTimeline, EasingFunctions } = require('../compiler/animation.js');
10
+ const { Matrix3D, ParticleSystem, AdvancedFilter } = require('../compiler/effects.js');
11
+
12
+ class AdvancedStylppRuntime {
13
+ constructor(options = {}) {
14
+ this.options = {
15
+ fps: 60,
16
+ enablePhysics: true,
17
+ enableAnimations: true,
18
+ enableEffects: true,
19
+ ...options
20
+ };
21
+
22
+ this.compiler = new StylppCompiler({
23
+ minify: false,
24
+ sourceMap: false,
25
+ vendorPrefix: true
26
+ });
27
+
28
+ this.physicsEngine = new PhysicsEngine();
29
+ this.animationTimelines = new Map();
30
+ this.particleSystems = new Map();
31
+ this.frameCallbacks = [];
32
+ this.running = false;
33
+ this.lastFrameTime = Date.now();
34
+ this.fps = 0;
35
+
36
+ if (typeof window !== 'undefined') {
37
+ this.init();
38
+ }
39
+ }
40
+
41
+ /**
42
+ * Initialize runtime
43
+ */
44
+ init() {
45
+ this.processTags();
46
+ this.setupPhysicsElements();
47
+ this.start();
48
+ }
49
+
50
+ /**
51
+ * Process STYL++ style tags
52
+ */
53
+ processTags() {
54
+ const stylppTags = document.querySelectorAll('style[type="text/stylpp"]');
55
+
56
+ stylppTags.forEach((tag, index) => {
57
+ const code = tag.textContent;
58
+ const id = tag.id || `stylpp-${index}`;
59
+
60
+ const result = this.compiler.compile(code, `${id}.stylpp`);
61
+
62
+ if (result.success) {
63
+ const styleElement = document.createElement('style');
64
+ styleElement.type = 'text/css';
65
+ styleElement.id = `stylpp-compiled-${id}`;
66
+ styleElement.textContent = result.css;
67
+ document.head.appendChild(styleElement);
68
+ } else {
69
+ console.error(`STYL++ Error in ${id}:`, result.errors[0].message);
70
+ }
71
+ });
72
+ }
73
+
74
+ /**
75
+ * Setup physics for elements with physics attributes
76
+ */
77
+ setupPhysicsElements() {
78
+ if (!this.options.enablePhysics) return;
79
+
80
+ // Elements with physics data attributes
81
+ const physicsElements = document.querySelectorAll('[data-physics]');
82
+
83
+ physicsElements.forEach((element) => {
84
+ const config = {
85
+ mass: parseFloat(element.dataset.mass || 1),
86
+ bounce: parseFloat(element.dataset.bounce || 0.6),
87
+ friction: parseFloat(element.dataset.friction || 0.2),
88
+ pinned: element.dataset.pinned !== undefined,
89
+ width: element.offsetWidth,
90
+ height: element.offsetHeight
91
+ };
92
+
93
+ const id = element.id || `physics-${Math.random().toString(36).substr(2, 9)}`;
94
+ element.id = id;
95
+
96
+ this.physicsEngine.createBody(id, config);
97
+
98
+ // Store reference for updates
99
+ element._physicsId = id;
100
+ });
101
+ }
102
+
103
+ /**
104
+ * Create animation for element
105
+ */
106
+ animateElement(elementId, keyframes, duration = 1000, easing = 'easeOutCubic') {
107
+ const timeline = new AnimationTimeline(duration);
108
+
109
+ const times = Object.keys(keyframes).map(t => parseInt(t));
110
+ const minTime = Math.min(...times);
111
+ const maxTime = Math.max(...times);
112
+ const range = maxTime - minTime;
113
+
114
+ times.forEach(time => {
115
+ const normalizedTime = ((time - minTime) / range) * duration;
116
+ timeline.at(normalizedTime, keyframes[time], easing);
117
+ });
118
+
119
+ const element = document.getElementById(elementId);
120
+ if (!element) return null;
121
+
122
+ timeline.onUpdate = (properties) => {
123
+ Object.entries(properties).forEach(([key, value]) => {
124
+ if (key.includes('translate') || key.includes('rotate') || key.includes('scale')) {
125
+ element.style.transform = `${key}(${value}${key.includes('rotate') ? 'deg' : 'px'})`;
126
+ } else {
127
+ element.style[key] = `${value}px`;
128
+ }
129
+ });
130
+ };
131
+
132
+ this.animationTimelines.set(elementId, timeline);
133
+ timeline.play();
134
+
135
+ return timeline;
136
+ }
137
+
138
+ /**
139
+ * Create particle effect
140
+ */
141
+ createParticleSystem(elementId, config = {}) {
142
+ const particles = new ParticleSystem(config);
143
+ this.particleSystems.set(elementId, particles);
144
+ return particles;
145
+ }
146
+
147
+ /**
148
+ * Apply advanced filter to element
149
+ */
150
+ applyFilter(elementId, filterType, ...args) {
151
+ const element = document.getElementById(elementId);
152
+ if (!element) return;
153
+
154
+ const filter = AdvancedFilter[filterType](...args);
155
+
156
+ if (typeof filter === 'string') {
157
+ element.style.filter = filter;
158
+ } else {
159
+ Object.entries(filter).forEach(([key, value]) => {
160
+ if (key === 'backdrop') {
161
+ element.style.backdropFilter = value;
162
+ } else {
163
+ element.style[key] = value;
164
+ }
165
+ });
166
+ }
167
+ }
168
+
169
+ /**
170
+ * Apply 3D transform
171
+ */
172
+ apply3DTransform(elementId, transformFn) {
173
+ const element = document.getElementById(elementId);
174
+ if (!element) return;
175
+
176
+ const matrix = new Matrix3D();
177
+ transformFn(matrix);
178
+
179
+ element.style.transform = matrix.toCSS();
180
+ }
181
+
182
+ /**
183
+ * Start animation loop
184
+ */
185
+ start() {
186
+ this.running = true;
187
+ this.loop();
188
+ }
189
+
190
+ /**
191
+ * Stop animation loop
192
+ */
193
+ stop() {
194
+ this.running = false;
195
+ }
196
+
197
+ /**
198
+ * Main animation loop
199
+ */
200
+ loop = () => {
201
+ if (!this.running) return;
202
+
203
+ const now = Date.now();
204
+ const deltaTime = (now - this.lastFrameTime) / 1000;
205
+ this.lastFrameTime = now;
206
+
207
+ // Update physics
208
+ if (this.options.enablePhysics) {
209
+ this.physicsEngine.step(deltaTime);
210
+ this.updatePhysicsElements();
211
+ }
212
+
213
+ // Update animations
214
+ if (this.options.enableAnimations) {
215
+ this.animationTimelines.forEach((timeline) => {
216
+ timeline.update(deltaTime * 1000);
217
+ });
218
+ }
219
+
220
+ // Update particles
221
+ this.particleSystems.forEach((system) => {
222
+ system.update(deltaTime);
223
+ });
224
+
225
+ // Run frame callbacks
226
+ this.frameCallbacks.forEach(callback => callback(deltaTime));
227
+
228
+ // Calculate FPS
229
+ this.fps = 1 / deltaTime;
230
+
231
+ requestAnimationFrame(this.loop);
232
+ };
233
+
234
+ /**
235
+ * Update elements with physics transformations
236
+ */
237
+ updatePhysicsElements() {
238
+ this.physicsEngine.bodies.forEach((body) => {
239
+ const element = document.getElementById(body.id);
240
+ if (!element) return;
241
+
242
+ element.style.transform = `translate3d(${body.position.x.toFixed(2)}px, ${body.position.y.toFixed(2)}px, 0)`;
243
+ });
244
+ }
245
+
246
+ /**
247
+ * Add callback for each frame
248
+ */
249
+ onFrame(callback) {
250
+ this.frameCallbacks.push(callback);
251
+ return () => {
252
+ this.frameCallbacks = this.frameCallbacks.filter(cb => cb !== callback);
253
+ };
254
+ }
255
+
256
+ /**
257
+ * Get FPS counter
258
+ */
259
+ getFPS() {
260
+ return this.fps.toFixed(1);
261
+ }
262
+
263
+ /**
264
+ * Create spring layout
265
+ */
266
+ createSpringLayout(selectorString) {
267
+ const elements = document.querySelectorAll(selectorString);
268
+ const elementIds = Array.from(elements).map((el, i) => {
269
+ if (!el.id) el.id = `spring-${i}`;
270
+ return el.id;
271
+ });
272
+
273
+ this.physicsEngine.createSpringLayout(elementIds, {
274
+ stiffness: 0.3,
275
+ damping: 0.2,
276
+ restDistance: 150
277
+ });
278
+
279
+ return elementIds;
280
+ }
281
+
282
+ /**
283
+ * Compile inline STYL++
284
+ */
285
+ compile(code) {
286
+ return this.compiler.compile(code);
287
+ }
288
+ }
289
+
290
+ // Export
291
+ if (typeof module !== 'undefined' && module.exports) {
292
+ module.exports = AdvancedStylppRuntime;
293
+ }
@@ -0,0 +1,239 @@
1
+ /**
2
+ * STYL++ Browser Runtime
3
+ * Enables real-time compilation in the browser
4
+ * Supports hot reload and error reporting
5
+ */
6
+
7
+ const StylppCompiler = require('../compiler/index.js');
8
+
9
+ class StylppRuntime {
10
+ constructor(options = {}) {
11
+ this.options = {
12
+ hotReload: true,
13
+ autoCompile: true,
14
+ showErrors: true,
15
+ ...options
16
+ };
17
+
18
+ this.compiler = new StylppCompiler(this.options);
19
+ this.styles = new Map();
20
+ this.watchers = new Map();
21
+ this.errors = [];
22
+
23
+ this.init();
24
+ }
25
+
26
+ /**
27
+ * Initialize the runtime
28
+ */
29
+ init() {
30
+ if (typeof window !== 'undefined') {
31
+ this.processTags();
32
+ if (this.options.hotReload) {
33
+ this.setupWebSocket();
34
+ }
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Process all <style type="text/stylpp"> tags
40
+ */
41
+ processTags() {
42
+ const stylppTags = document.querySelectorAll('style[type="text/stylpp"]');
43
+
44
+ stylppTags.forEach((tag, index) => {
45
+ const code = tag.textContent;
46
+ const id = tag.id || `stylpp-${index}`;
47
+
48
+ const result = this.compiler.compile(code, `${id}.stylpp`);
49
+
50
+ if (result.success) {
51
+ const styleElement = document.createElement('style');
52
+ styleElement.type = 'text/css';
53
+ styleElement.id = `stylpp-compiled-${id}`;
54
+ styleElement.textContent = result.css;
55
+ document.head.appendChild(styleElement);
56
+
57
+ this.styles.set(id, {
58
+ code: code,
59
+ css: result.css,
60
+ element: styleElement,
61
+ sourceElement: tag
62
+ });
63
+ } else {
64
+ this.handleError(result.errors[0], id);
65
+ }
66
+ });
67
+ }
68
+
69
+ /**
70
+ * Setup WebSocket for hot reload
71
+ */
72
+ setupWebSocket() {
73
+ if (typeof WebSocket !== 'undefined') {
74
+ try {
75
+ const ws = new WebSocket('ws://localhost:8765');
76
+
77
+ ws.onmessage = (event) => {
78
+ const data = JSON.parse(event.data);
79
+ if (data.type === 'stylpp-change') {
80
+ this.recompile(data.id, data.code);
81
+ }
82
+ };
83
+
84
+ ws.onerror = () => {
85
+ console.log('STYL++ hot reload not available (ws server not running)');
86
+ };
87
+ } catch (e) {
88
+ // WebSocket not available, skip
89
+ }
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Recompile a style
95
+ * @param {string} id - Style ID
96
+ * @param {string} code - New code
97
+ */
98
+ recompile(id, code) {
99
+ const result = this.compiler.compile(code, `${id}.stylpp`);
100
+
101
+ if (result.success) {
102
+ const style = this.styles.get(id);
103
+ if (style) {
104
+ style.code = code;
105
+ style.css = result.css;
106
+ style.element.textContent = result.css;
107
+ this.clearError(id);
108
+ }
109
+ } else {
110
+ this.handleError(result.errors[0], id);
111
+ }
112
+ }
113
+
114
+ /**
115
+ * Handle compilation error
116
+ * @param {Object} error - Error object
117
+ * @param {string} id - Style ID
118
+ */
119
+ handleError(error, id) {
120
+ this.errors.push({ id, error, timestamp: Date.now() });
121
+
122
+ if (this.options.showErrors && typeof console !== 'undefined') {
123
+ console.error(`STYL++ Error in ${id}:`, error.message);
124
+ }
125
+
126
+ // Display error in UI if available
127
+ if (typeof document !== 'undefined') {
128
+ this.displayErrorUI(error, id);
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Clear error for a style
134
+ * @param {string} id - Style ID
135
+ */
136
+ clearError(id) {
137
+ this.errors = this.errors.filter(e => e.id !== id);
138
+ const errorElement = document.getElementById(`stylpp-error-${id}`);
139
+ if (errorElement) {
140
+ errorElement.remove();
141
+ }
142
+ }
143
+
144
+ /**
145
+ * Display error in UI
146
+ * @param {Object} error - Error object
147
+ * @param {string} id - Style ID
148
+ */
149
+ displayErrorUI(error, id) {
150
+ let errorElement = document.getElementById(`stylpp-error-${id}`);
151
+
152
+ if (!errorElement) {
153
+ errorElement = document.createElement('div');
154
+ errorElement.id = `stylpp-error-${id}`;
155
+ errorElement.style.cssText = `
156
+ position: fixed;
157
+ top: 10px;
158
+ right: 10px;
159
+ background: #ff4444;
160
+ color: white;
161
+ padding: 15px;
162
+ border-radius: 4px;
163
+ font-family: monospace;
164
+ font-size: 12px;
165
+ max-width: 400px;
166
+ z-index: 9999;
167
+ box-shadow: 0 2px 8px rgba(0,0,0,0.2);
168
+ `;
169
+ document.body.appendChild(errorElement);
170
+ }
171
+
172
+ const message = `
173
+ <strong>STYL++ Error</strong><br>
174
+ ${error.message}
175
+ ${error.line ? `<br>Line: ${error.line}` : ''}
176
+ `;
177
+
178
+ errorElement.innerHTML = message;
179
+ }
180
+
181
+ /**
182
+ * Compile and inject code
183
+ * @param {string} code - STYL++ code
184
+ * @param {string} id - Unique ID for the style
185
+ * @returns {Object} Result
186
+ */
187
+ compile(code, id = `stylpp-${Date.now()}`) {
188
+ const result = this.compiler.compile(code, `${id}.stylpp`);
189
+
190
+ if (result.success) {
191
+ const styleElement = document.createElement('style');
192
+ styleElement.type = 'text/css';
193
+ styleElement.id = `stylpp-compiled-${id}`;
194
+ styleElement.textContent = result.css;
195
+ document.head.appendChild(styleElement);
196
+
197
+ this.styles.set(id, {
198
+ code: code,
199
+ css: result.css,
200
+ element: styleElement
201
+ });
202
+ } else {
203
+ this.handleError(result.errors[0], id);
204
+ }
205
+
206
+ return result;
207
+ }
208
+
209
+ /**
210
+ * Get compiled CSS
211
+ * @param {string} id - Style ID
212
+ * @returns {string|null} CSS or null if not found
213
+ */
214
+ getCSS(id) {
215
+ const style = this.styles.get(id);
216
+ return style ? style.css : null;
217
+ }
218
+
219
+ /**
220
+ * Get all compiled styles
221
+ * @returns {Object} Map of all styles
222
+ */
223
+ getAllStyles() {
224
+ return this.styles;
225
+ }
226
+
227
+ /**
228
+ * Get compilation errors
229
+ * @returns {Array} Array of errors
230
+ */
231
+ getErrors() {
232
+ return this.errors;
233
+ }
234
+ }
235
+
236
+ // Export for use in both Node.js and Browser environments
237
+ if (typeof module !== 'undefined' && module.exports) {
238
+ module.exports = StylppRuntime;
239
+ }
Binary file