rip-lang 3.8.10 → 3.9.1

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,114 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Sierpinski Triangle — Rip UI</title>
7
+ <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet">
8
+ <style>
9
+ * { margin: 0; padding: 0; box-sizing: border-box; }
10
+ body { background: #0a0a0a; color: white; font-family: 'JetBrains Mono', monospace; overflow: hidden; }
11
+ .scene { position: relative; width: 100vw; height: 100vh; }
12
+ .stats { position: fixed; top: 20px; left: 24px; z-index: 10; }
13
+ .stats-title { font-size: 18px; font-weight: bold; }
14
+ .stats-line { font-size: 13px; color: rgba(255,255,255,0.45); margin-top: 3px; }
15
+ .stats-note { font-size: 11px; color: rgba(255,255,255,0.25); margin-top: 8px; max-width: 280px; line-height: 1.5; }
16
+ .source-toggle { display: inline-block; margin-top: 10px; padding: 4px 10px; background: rgba(255,255,255,0.08); border: 1px solid rgba(255,255,255,0.15); border-radius: 4px; color: rgba(255,255,255,0.5); font-family: inherit; font-size: 12px; cursor: pointer; transition: all 0.2s; }
17
+ .source-toggle:hover { color: white; border-color: rgba(255,255,255,0.4); background: rgba(255,255,255,0.12); }
18
+ .source-panel { position: fixed; top: 0; right: 0; bottom: 0; width: min(520px, 90vw); z-index: 100; background: rgba(10,10,10,0.92); backdrop-filter: blur(12px); border-left: 1px solid rgba(255,255,255,0.1); transform: translateX(100%); transition: transform 0.3s ease; overflow-y: auto; }
19
+ .source-panel.open { transform: translateX(0); }
20
+ .source-header { display: flex; justify-content: space-between; align-items: center; padding: 16px 20px; border-bottom: 1px solid rgba(255,255,255,0.08); }
21
+ .source-label { font-size: 13px; font-weight: bold; color: rgba(255,255,255,0.6); letter-spacing: 0.05em; }
22
+ .source-close { background: none; border: none; color: rgba(255,255,255,0.4); font-size: 20px; cursor: pointer; padding: 4px 8px; border-radius: 4px; font-family: inherit; transition: all 0.2s; }
23
+ .source-close:hover { color: white; background: rgba(255,255,255,0.1); }
24
+ .source-code { padding: 16px 20px; font-size: 13px; line-height: 1.7; color: rgba(255,255,255,0.85); white-space: pre; overflow-x: auto; }
25
+ .container { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%) scale(var(--scale, 1)); width: 1000px; height: 870px; transform-origin: center center; }
26
+ .dot {
27
+ position: absolute; width: 24px; height: 24px; border-radius: 50%;
28
+ display: flex; align-items: center; justify-content: center;
29
+ font-size: 10px; font-weight: bold; color: rgba(255,255,255,0.9);
30
+ user-select: none; cursor: default; transition: transform 0.1s;
31
+ }
32
+ .dot:hover { transform: scale(2.5); z-index: 1; }
33
+ </style>
34
+ <script type="module" src="https://shreeve.github.io/rip-lang/dist/rip-ui.min.js"></script>
35
+ <script>
36
+ function updateScale() {
37
+ var s = Math.min(window.innerWidth / 1350, window.innerHeight / 1250, 1);
38
+ document.documentElement.style.setProperty('--scale', s);
39
+ }
40
+ updateScale();
41
+ window.addEventListener('resize', updateScale);
42
+ </script>
43
+ </head>
44
+ <body>
45
+ <div class="source-panel" id="source-panel">
46
+ <div class="source-header">
47
+ <span class="source-label">SOURCE — 48 LINES OF RIP</span>
48
+ <button class="source-close" onclick="document.getElementById('source-panel').classList.remove('open')">&times;</button>
49
+ </div>
50
+ <pre class="source-code" id="source-code"></pre>
51
+ </div>
52
+ <script type="text/rip">
53
+ # Sierpinski Triangle — 729 reactive dots at 60fps
54
+ # No virtual DOM. No tree diffing. Just fine-grained reactivity.
55
+
56
+ App = component
57
+ elapsed := 0
58
+ fps := 0
59
+
60
+ dots := do ->
61
+ result = []
62
+ build = (x, y, s) ->
63
+ if s <= 25
64
+ result.push { x, y, id: result.length }
65
+ else
66
+ build x , y - s / 4, s / 2
67
+ build x - s / 2, y + s / 4, s / 2
68
+ build x + s / 2, y + s / 4, s / 2
69
+ build 500, 435, 1000
70
+ result
71
+
72
+ mounted: ->
73
+ start = performance.now()
74
+ frames = 0
75
+ last = start
76
+ tick = ->
77
+ now = performance.now()
78
+ elapsed = (now - start) / 1000
79
+ frames++
80
+ if now - last >= 1000
81
+ fps = frames
82
+ frames = 0
83
+ last = now
84
+ requestAnimationFrame tick
85
+ requestAnimationFrame tick
86
+
87
+ render
88
+ .scene
89
+ .stats
90
+ .stats-title "Sierpinski Triangle"
91
+ .stats-line dots.length + " dots · " + dots.length * 2 + " reactive bindings"
92
+ .stats-line fps + " fps"
93
+ .stats-note "Each dot updates independently via fine-grained reactivity"
94
+ button.source-toggle @click: (-> document.getElementById('source-panel').classList.toggle('open')), "View Source"
95
+
96
+ .container
97
+ for dot in dots
98
+ .dot style: "left:" + (dot.x - 12) + "px;top:" + (dot.y - 12) + "px;background:hsl(" + ((dot.id / 729 * 360 + elapsed * 40) % 360) + " 70% 50%)"
99
+ Math.floor(elapsed % 10) + 1
100
+
101
+ App.new().mount document.body
102
+
103
+ # Populate source panel with this page's own Rip source
104
+ ripScript = document.querySelector('script[type="text/rip"]')
105
+ lines = ripScript.textContent.split("\n")
106
+ while lines.length and lines[0].trim() is ''
107
+ lines.shift()
108
+ cutoff = lines.findIndex((l) -> l.includes('Populate source panel'))
109
+ lines = lines.slice(0, cutoff).filter((l, i, a) -> i < a.length - 1 or l.trim() isnt '')
110
+ sourceEl = document.getElementById('source-code')
111
+ sourceEl.textContent = lines.join("\n")
112
+ </script>
113
+ </body>
114
+ </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rip-lang",
3
- "version": "3.8.10",
3
+ "version": "3.9.1",
4
4
  "description": "A modern language that compiles to JavaScript",
5
5
  "type": "module",
6
6
  "main": "src/compiler.js",
@@ -67,7 +67,7 @@
67
67
  "author": "Steve Shreeve <steve.shreeve@gmail.com>",
68
68
  "license": "MIT",
69
69
  "devDependencies": {
70
- "@rip-lang/api": "workspace:*",
71
- "@rip-lang/ui": "workspace:*"
70
+ "@rip-lang/api": "1.1.5",
71
+ "@rip-lang/ui": "0.2.0"
72
72
  }
73
73
  }
package/src/browser.js CHANGED
@@ -71,8 +71,14 @@ export { processRipScripts };
71
71
  * Import a .rip file as an ES module
72
72
  * Fetches the URL, compiles Rip→JS, dynamically imports via Blob URL
73
73
  * Usage: const { launch } = await importRip('/ui.rip')
74
+ *
75
+ * Pre-compiled modules can be registered on importRip.modules to skip fetching.
76
+ * The rip-ui bundle uses this to embed ui.rip without a server round-trip.
74
77
  */
75
78
  export async function importRip(url) {
79
+ for (const [key, mod] of Object.entries(importRip.modules)) {
80
+ if (url.includes(key)) return mod;
81
+ }
76
82
  const source = await fetch(url).then(r => {
77
83
  if (!r.ok) throw new Error(`importRip: ${url} (${r.status})`);
78
84
  return r.text();
@@ -86,6 +92,7 @@ export async function importRip(url) {
86
92
  URL.revokeObjectURL(blobUrl);
87
93
  }
88
94
  }
95
+ importRip.modules = {};
89
96
 
90
97
  /**
91
98
  * Browser Console REPL
@@ -128,14 +135,16 @@ if (typeof globalThis !== 'undefined') {
128
135
  globalThis.__ripExports = { compile, compileToJS, formatSExpr, VERSION, BUILD_DATE, getReactiveRuntime, getComponentRuntime };
129
136
  }
130
137
 
131
- // Auto-process scripts when this module loads
132
- // Expose __ripScriptsReady promise so other modules can await completion
138
+ // Auto-process <script type="text/rip"> blocks.
139
+ // Deferred via queueMicrotask so bundled entry code (e.g. rip-ui.min.js registering
140
+ // importRip.modules) runs before script processing begins.
133
141
  if (typeof document !== 'undefined') {
134
- if (document.readyState === 'loading') {
135
- globalThis.__ripScriptsReady = new Promise(resolve => {
136
- document.addEventListener('DOMContentLoaded', () => processRipScripts().then(resolve));
137
- });
138
- } else {
139
- globalThis.__ripScriptsReady = processRipScripts();
140
- }
142
+ globalThis.__ripScriptsReady = new Promise(resolve => {
143
+ const run = () => processRipScripts().then(resolve);
144
+ if (document.readyState === 'loading') {
145
+ document.addEventListener('DOMContentLoaded', () => queueMicrotask(run));
146
+ } else {
147
+ queueMicrotask(run);
148
+ }
149
+ });
141
150
  }
package/src/compiler.js CHANGED
@@ -1094,6 +1094,9 @@ export class CodeGenerator {
1094
1094
  if (!this.reactiveVars) this.reactiveVars = new Set();
1095
1095
  let varName = str(name) ?? name;
1096
1096
  this.reactiveVars.add(varName);
1097
+ if (this.is(expr, 'block') && expr.length > 2) {
1098
+ return `const ${varName} = __computed(() => ${this.generateFunctionBody(expr)})`;
1099
+ }
1097
1100
  return `const ${varName} = __computed(() => ${this.generate(expr, 'value')})`;
1098
1101
  }
1099
1102
 
@@ -3253,7 +3256,7 @@ export class Compiler {
3253
3256
  // Component Support (prototype installation)
3254
3257
  // =============================================================================
3255
3258
 
3256
- installComponentSupport(CodeGenerator);
3259
+ installComponentSupport(CodeGenerator, Lexer);
3257
3260
 
3258
3261
  // =============================================================================
3259
3262
  // Type Support (enum generator)