tjs-lang 0.6.44 → 0.7.3
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.
- package/CLAUDE.md +85 -422
- package/README.md +15 -82
- package/bin/benchmarks.ts +7 -7
- package/bin/dev.ts +2 -1
- package/demo/autocomplete.test.ts +1 -1
- package/demo/docs.json +744 -48
- package/demo/src/demo-nav.ts +5 -5
- package/demo/src/index.ts +28 -36
- package/demo/src/module-sw.ts +1 -1
- package/demo/src/playground-shared.ts +17 -17
- package/demo/src/playground.ts +13 -1
- package/demo/src/style.ts +4 -1
- package/demo/src/tjs-playground.ts +5 -5
- package/demo/src/user-store.ts +2 -1
- package/demo/static/favicon.svg +17 -24
- package/demo/static/tosi-platform.json +9304 -0
- package/dist/index.js +158 -156
- package/dist/index.js.map +14 -13
- package/dist/scripts/compat-effect.d.ts +16 -0
- package/dist/scripts/compat-kysely.d.ts +13 -0
- package/dist/scripts/compat-radash.d.ts +13 -0
- package/dist/scripts/compat-superstruct.d.ts +13 -0
- package/dist/scripts/compat-ts-pattern.d.ts +13 -0
- package/dist/scripts/compat-zod.d.ts +12 -0
- package/dist/src/lang/emitters/from-ts.d.ts +1 -1
- package/dist/src/lang/emitters/js-tests.d.ts +4 -0
- package/dist/src/lang/emitters/js.d.ts +2 -2
- package/dist/src/lang/index.d.ts +1 -0
- package/dist/src/lang/json-schema.d.ts +40 -0
- package/dist/src/lang/parser-transforms.d.ts +14 -0
- package/dist/src/lang/runtime.d.ts +39 -6
- package/dist/src/types/Type.d.ts +5 -0
- package/dist/tjs-full.js +158 -156
- package/dist/tjs-full.js.map +14 -13
- package/dist/tjs-vm.js +44 -43
- package/dist/tjs-vm.js.map +5 -5
- package/docs/README.md +21 -20
- package/docs/WASM-QUICKSTART.md +283 -0
- package/docs/diagrams/architecture-shift.svg +117 -0
- package/docs/diagrams/compile-runtime.svg +130 -0
- package/docs/diagrams/icon-riff-1.svg +55 -0
- package/docs/diagrams/icon-riff-2.svg +62 -0
- package/docs/diagrams/icon-riff-3.svg +61 -0
- package/docs/diagrams/platform-overview.svg +114 -0
- package/docs/diagrams/safe-eval.svg +147 -0
- package/docs/eval-v4/arch-comparison.svg +277 -0
- package/docs/eval-v4/bundler-tree.svg +250 -0
- package/docs/eval-v4/http-lifecycle.svg +148 -0
- package/docs/function-predicate-design.md +8 -8
- package/docs/native-engine-integration.md +2 -2
- package/editors/codemirror/autocomplete.test.ts +29 -29
- package/package.json +10 -4
- package/src/cli/commands/convert.test.ts +11 -8
- package/src/cli/tjs.ts +1 -1
- package/src/lang/codegen.test.ts +117 -112
- package/src/lang/docs.test.ts +22 -22
- package/src/lang/docs.ts +5 -8
- package/src/lang/emitters/dts.test.ts +13 -13
- package/src/lang/emitters/from-ts.ts +36 -9
- package/src/lang/emitters/js-tests.ts +143 -28
- package/src/lang/emitters/js.ts +49 -28
- package/src/lang/features.test.ts +259 -43
- package/src/lang/from-ts.test.ts +3 -3
- package/src/lang/function-predicate.test.ts +1 -1
- package/src/lang/index.ts +8 -47
- package/src/lang/json-schema.test.ts +261 -0
- package/src/lang/json-schema.ts +167 -0
- package/src/lang/parser-params.ts +28 -44
- package/src/lang/parser-transforms.ts +255 -0
- package/src/lang/parser.test.ts +32 -13
- package/src/lang/parser.ts +49 -11
- package/src/lang/perf.test.ts +11 -11
- package/src/lang/roundtrip.test.ts +3 -3
- package/src/lang/runtime.test.ts +167 -0
- package/src/lang/runtime.ts +234 -46
- package/src/lang/transpiler.test.ts +21 -21
- package/src/lang/typescript-syntax.test.ts +11 -9
- package/src/types/Type.ts +38 -1
- package/src/use-cases/bootstrap.test.ts +7 -7
- package/src/use-cases/client-server.test.ts +1 -1
- package/src/use-cases/malicious-actor.test.ts +1 -1
- package/src/use-cases/rag-processor.test.ts +1 -1
- package/src/use-cases/sophisticated-agents.test.ts +2 -2
- package/src/use-cases/transpiler-llm.test.ts +1 -1
- package/src/use-cases/unbundled-imports.test.ts +9 -9
- package/tjs-lang.svg +17 -25
package/docs/README.md
CHANGED
|
@@ -10,46 +10,47 @@ ASTs that run sandboxed with fuel (gas) limits.
|
|
|
10
10
|
|
|
11
11
|
| Guide | Description |
|
|
12
12
|
|-------|-------------|
|
|
13
|
-
| [TJS Language Guide](
|
|
14
|
-
| [AJS Runtime Guide](
|
|
15
|
-
| [TJS for TypeScript Developers](
|
|
16
|
-
| [TJS for JavaScript Developers](
|
|
13
|
+
| [TJS Language Guide](https://github.com/tonioloewald/tjs-lang/blob/main/DOCS-TJS.md) | Complete TJS language reference |
|
|
14
|
+
| [AJS Runtime Guide](https://github.com/tonioloewald/tjs-lang/blob/main/DOCS-AJS.md) | Agent language and VM documentation |
|
|
15
|
+
| [TJS for TypeScript Developers](https://github.com/tonioloewald/tjs-lang/blob/main/TJS-FOR-TS.md) | Coming from TypeScript? Start here |
|
|
16
|
+
| [TJS for JavaScript Developers](https://github.com/tonioloewald/tjs-lang/blob/main/TJS-FOR-JS.md) | Coming from JavaScript? Start here |
|
|
17
17
|
|
|
18
18
|
## Architecture & Design
|
|
19
19
|
|
|
20
20
|
| Document | Description |
|
|
21
21
|
|----------|-------------|
|
|
22
|
-
| [Technical Context](
|
|
23
|
-
| [
|
|
24
|
-
| [
|
|
22
|
+
| [Technical Context](https://github.com/tonioloewald/tjs-lang/blob/main/CONTEXT.md) | Architecture deep dive, design decisions |
|
|
23
|
+
| [WASM Quick Start](https://github.com/tonioloewald/tjs-lang/blob/main/docs/WASM-QUICKSTART.md) | Build WASM-accelerated libraries with zero toolchain setup |
|
|
24
|
+
| [Design Patterns](https://github.com/tonioloewald/tjs-lang/blob/main/guides/patterns.md) | Common patterns in TJS and AJS |
|
|
25
|
+
| [Schema Validation](https://github.com/tonioloewald/tjs-lang/blob/main/guides/tosijs-schema.md) | Working with tosijs-schema |
|
|
25
26
|
|
|
26
27
|
## Examples
|
|
27
28
|
|
|
28
29
|
Interactive examples are available in the [TJS Playground](https://tjs-platform.web.app).
|
|
29
30
|
|
|
30
|
-
Source code for all examples lives in [`guides/examples/`](
|
|
31
|
+
Source code for all examples lives in [`guides/examples/`](https://github.com/tonioloewald/tjs-lang/tree/main/guides/examples):
|
|
31
32
|
|
|
32
|
-
- **TJS examples** — [`guides/examples/tjs/`](
|
|
33
|
-
- **AJS examples** — [`guides/examples/ajs/`](
|
|
33
|
+
- **TJS examples** — [`guides/examples/tjs/`](https://github.com/tonioloewald/tjs-lang/tree/main/guides/examples/tjs) (type annotations, classes, WASM, etc.)
|
|
34
|
+
- **AJS examples** — [`guides/examples/ajs/`](https://github.com/tonioloewald/tjs-lang/tree/main/guides/examples/ajs) (agents, LLM, APIs, etc.)
|
|
34
35
|
|
|
35
36
|
Key examples:
|
|
36
37
|
|
|
37
38
|
| Example | Description |
|
|
38
39
|
|---------|-------------|
|
|
39
|
-
| [TJS Grammar Reference](
|
|
40
|
-
| [Hello TJS](
|
|
41
|
-
| [Full-Stack Demo](
|
|
42
|
-
| [WASM Starfield](
|
|
43
|
-
| [WASM Vector Search](
|
|
40
|
+
| [TJS Grammar Reference](https://github.com/tonioloewald/tjs-lang/blob/main/guides/examples/tjs/tjs-grammar-demo.md) | Comprehensive TJS feature demo |
|
|
41
|
+
| [Hello TJS](https://github.com/tonioloewald/tjs-lang/blob/main/guides/examples/tjs/hello-tjs.md) | Getting started with TJS |
|
|
42
|
+
| [Full-Stack Demo](https://github.com/tonioloewald/tjs-lang/blob/main/guides/examples/tjs/full-stack-demo-user-service.md) | User service with runtime validation |
|
|
43
|
+
| [WASM Starfield](https://github.com/tonioloewald/tjs-lang/blob/main/guides/examples/tjs/wasm-starfield.md) | SIMD-accelerated particle system with mouse steering |
|
|
44
|
+
| [WASM Vector Search](https://github.com/tonioloewald/tjs-lang/blob/main/guides/examples/tjs/wasm-vector-search.md) | SIMD cosine similarity benchmark vs JS scalar |
|
|
44
45
|
|
|
45
46
|
## Additional Resources
|
|
46
47
|
|
|
47
48
|
| Resource | Description |
|
|
48
49
|
|----------|-------------|
|
|
49
|
-
| [Roadmap](
|
|
50
|
-
| [TODO](
|
|
51
|
-
| [Benchmarks](
|
|
52
|
-
| [CLAUDE.md](
|
|
50
|
+
| [Roadmap](https://github.com/tonioloewald/tjs-lang/blob/main/PLAN.md) | Project roadmap and planned features |
|
|
51
|
+
| [TODO](https://github.com/tonioloewald/tjs-lang/blob/main/TODO.md) | Current task list |
|
|
52
|
+
| [Benchmarks](https://github.com/tonioloewald/tjs-lang/blob/main/guides/benchmarks.md) | Performance benchmarks |
|
|
53
|
+
| [CLAUDE.md](https://github.com/tonioloewald/tjs-lang/blob/main/CLAUDE.md) | AI assistant instructions for this codebase |
|
|
53
54
|
|
|
54
55
|
## npm Package
|
|
55
56
|
|
|
@@ -61,4 +62,4 @@ npm install tjs-lang
|
|
|
61
62
|
import { Agent, AgentVM, ajs, tjs } from 'tjs-lang'
|
|
62
63
|
```
|
|
63
64
|
|
|
64
|
-
See the [main README](
|
|
65
|
+
See the [main README](https://github.com/tonioloewald/tjs-lang#readme) for installation and quick start.
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
# WASM Quick Start
|
|
2
|
+
|
|
3
|
+
**Build WASM-accelerated libraries in TJS with zero toolchain setup.**
|
|
4
|
+
|
|
5
|
+
No Emscripten. No Rust. No `.wasm` files. No webpack plugins. Write WASM inline,
|
|
6
|
+
get compiled bytecode embedded in your JavaScript output. Ship one `.js` file.
|
|
7
|
+
|
|
8
|
+
## Why TJS for WASM?
|
|
9
|
+
|
|
10
|
+
The traditional WASM workflow:
|
|
11
|
+
|
|
12
|
+
1. Install a C/Rust/Go toolchain
|
|
13
|
+
2. Write code in a separate language
|
|
14
|
+
3. Compile to `.wasm` (configure build system)
|
|
15
|
+
4. Load the `.wasm` file at runtime (async, CORS, bundler config)
|
|
16
|
+
5. Marshal data between JS and WASM heaps (manual, error-prone)
|
|
17
|
+
6. Ship multiple files
|
|
18
|
+
|
|
19
|
+
The TJS workflow:
|
|
20
|
+
|
|
21
|
+
1. Write `wasm { }` inside your function
|
|
22
|
+
2. Run `tjs emit myfile.tjs`
|
|
23
|
+
3. Ship the output `.js`
|
|
24
|
+
|
|
25
|
+
That's it. The compiler handles bytecode generation, base64 embedding, memory
|
|
26
|
+
management, and typed array marshaling. Your output is a single self-contained
|
|
27
|
+
JavaScript file.
|
|
28
|
+
|
|
29
|
+
## Your First WASM Block
|
|
30
|
+
|
|
31
|
+
```tjs
|
|
32
|
+
function add(! a: 0, b: 0):! 0 {
|
|
33
|
+
return wasm {
|
|
34
|
+
a + b
|
|
35
|
+
} fallback {
|
|
36
|
+
return a + b
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
console.log(add(3, 4)) // 7 — computed in WASM
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**What's happening:**
|
|
44
|
+
|
|
45
|
+
- `wasm { }` — JS-like syntax compiled to WASM bytecode at transpile time
|
|
46
|
+
- `fallback { }` — JS fallback if WASM isn't available (e.g., older runtimes)
|
|
47
|
+
- `!` — unsafe marker, skips runtime type checking for speed
|
|
48
|
+
- `:!` — assert-returns, the return type contract
|
|
49
|
+
|
|
50
|
+
Run it:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
bun src/cli/tjs.ts run myfile.tjs
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Or emit standalone JS:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
bun src/cli/tjs.ts emit myfile.tjs > myfile.js
|
|
60
|
+
node myfile.js # works anywhere — WASM is embedded as base64
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## What the Compiler Produces
|
|
64
|
+
|
|
65
|
+
For the `add` function above, `tjs emit` generates something like:
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
/**
|
|
69
|
+
* WASM: __tjs_wasm_0
|
|
70
|
+
* (func $compute (param $a f64) (param $b f64) (result f64)
|
|
71
|
+
* local.get 0
|
|
72
|
+
* local.get 1
|
|
73
|
+
* f64.add
|
|
74
|
+
* )
|
|
75
|
+
*/
|
|
76
|
+
;(async()=>{
|
|
77
|
+
// ... base64 decode, instantiate, register globalThis.__tjs_wasm_0
|
|
78
|
+
})();
|
|
79
|
+
|
|
80
|
+
function add(a, b) {
|
|
81
|
+
// calls __tjs_wasm_0(a, b) with fallback
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The WAT (WebAssembly Text) is included as a comment so you can inspect what was
|
|
86
|
+
compiled. The binary is embedded as base64 — no external files.
|
|
87
|
+
|
|
88
|
+
## Numeric Types
|
|
89
|
+
|
|
90
|
+
WASM blocks infer types from TJS parameter annotations:
|
|
91
|
+
|
|
92
|
+
| TJS annotation | WASM type | Example |
|
|
93
|
+
|---------------|-----------|---------|
|
|
94
|
+
| `0` | `i32` (integer) | `function f(x: 0)` |
|
|
95
|
+
| `0.0` | `f64` (float) | `function f(x: 0.0)` |
|
|
96
|
+
| `+0` | `i32` (non-negative) | `function f(x: +0)` |
|
|
97
|
+
| `Float32Array` | `i32` (pointer) | `function f(arr: Float32Array, len: 0)` |
|
|
98
|
+
|
|
99
|
+
Integers use `i32`, floats use `f64`. Typed arrays are passed as memory pointers
|
|
100
|
+
(the compiler handles marshaling automatically).
|
|
101
|
+
|
|
102
|
+
## SIMD: 4x Throughput
|
|
103
|
+
|
|
104
|
+
Process 4 float32 values per instruction using `f32x4_*` intrinsics:
|
|
105
|
+
|
|
106
|
+
```tjs
|
|
107
|
+
function scale(! arr: Float32Array, len: 0, factor: 0.0) {
|
|
108
|
+
wasm {
|
|
109
|
+
let s = f32x4_splat(factor)
|
|
110
|
+
for (let i = 0; i < len; i += 4) {
|
|
111
|
+
let off = i * 4
|
|
112
|
+
let v = f32x4_load(arr, off)
|
|
113
|
+
f32x4_store(arr, off, f32x4_mul(v, s))
|
|
114
|
+
}
|
|
115
|
+
} fallback {
|
|
116
|
+
for (let i = 0; i < len; i++) arr[i] *= factor
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const data = new Float32Array([1, 2, 3, 4, 5, 6, 7, 8])
|
|
121
|
+
scale(data, 8, 10.0)
|
|
122
|
+
console.log(Array.from(data)) // [10, 20, 30, 40, 50, 60, 70, 80]
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Available SIMD Intrinsics
|
|
126
|
+
|
|
127
|
+
| Intrinsic | Operation |
|
|
128
|
+
|-----------|-----------|
|
|
129
|
+
| `f32x4_load(arr, byteOffset)` | Load 4 floats from memory |
|
|
130
|
+
| `f32x4_store(arr, byteOffset, vec)` | Store 4 floats to memory |
|
|
131
|
+
| `f32x4_splat(value)` | Fill all 4 lanes with one value |
|
|
132
|
+
| `f32x4_add(a, b)` | Lane-wise addition |
|
|
133
|
+
| `f32x4_sub(a, b)` | Lane-wise subtraction |
|
|
134
|
+
| `f32x4_mul(a, b)` | Lane-wise multiplication |
|
|
135
|
+
| `f32x4_div(a, b)` | Lane-wise division |
|
|
136
|
+
| `f32x4_neg(v)` | Negate all lanes |
|
|
137
|
+
| `f32x4_sqrt(v)` | Square root of all lanes |
|
|
138
|
+
| `f32x4_extract_lane(vec, N)` | Get one float (lane 0-3) |
|
|
139
|
+
| `f32x4_replace_lane(vec, N, val)` | Set one float in lane |
|
|
140
|
+
|
|
141
|
+
These map directly to WASM SIMD opcodes — no auto-vectorization, no surprises.
|
|
142
|
+
|
|
143
|
+
## Memory Management
|
|
144
|
+
|
|
145
|
+
### Regular typed arrays: automatic copy
|
|
146
|
+
|
|
147
|
+
Pass a normal `Float32Array` and TJS copies it into WASM memory before the call,
|
|
148
|
+
copies results back after. Zero effort:
|
|
149
|
+
|
|
150
|
+
```tjs
|
|
151
|
+
function double(! arr: Float32Array, len: 0) {
|
|
152
|
+
wasm {
|
|
153
|
+
for (let i = 0; i < len; i += 4) {
|
|
154
|
+
let off = i * 4
|
|
155
|
+
let v = f32x4_load(arr, off)
|
|
156
|
+
f32x4_store(arr, off, f32x4_add(v, v))
|
|
157
|
+
}
|
|
158
|
+
} fallback {
|
|
159
|
+
for (let i = 0; i < len; i++) arr[i] *= 2
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const data = new Float32Array([1, 2, 3, 4])
|
|
164
|
+
double(data, 4)
|
|
165
|
+
// data is [2, 4, 6, 8] — copy-out happened automatically
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### `wasmBuffer()`: zero-copy shared memory
|
|
169
|
+
|
|
170
|
+
For hot paths with large arrays, eliminate the copy overhead:
|
|
171
|
+
|
|
172
|
+
```tjs
|
|
173
|
+
const positions = wasmBuffer(Float32Array, 100000)
|
|
174
|
+
|
|
175
|
+
// JS writes directly to WASM memory
|
|
176
|
+
for (let i = 0; i < 100000; i++) positions[i] = Math.random()
|
|
177
|
+
|
|
178
|
+
// WASM reads/writes the same memory — no copy
|
|
179
|
+
function process(! arr: Float32Array, len: 0, delta: 0.0) {
|
|
180
|
+
wasm {
|
|
181
|
+
let vd = f32x4_splat(delta)
|
|
182
|
+
for (let i = 0; i < len; i += 4) {
|
|
183
|
+
let off = i * 4
|
|
184
|
+
f32x4_store(arr, off, f32x4_add(f32x4_load(arr, off), vd))
|
|
185
|
+
}
|
|
186
|
+
} fallback {
|
|
187
|
+
for (let i = 0; i < len; i++) arr[i] += delta
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
process(positions, 100000, 0.01)
|
|
192
|
+
// JS sees mutations immediately — same backing memory
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
**How it works:** All WASM blocks in a file share one `WebAssembly.Memory` (64MB).
|
|
196
|
+
`wasmBuffer` is a bump allocator that hands out views into this memory. When a
|
|
197
|
+
typed array's `.buffer === wasmMemory.buffer`, the wrapper skips the copy and
|
|
198
|
+
passes the byte offset directly.
|
|
199
|
+
|
|
200
|
+
**Supported types:** `Float32Array`, `Float64Array`, `Int32Array`, `Uint8Array`
|
|
201
|
+
|
|
202
|
+
**Trade-off:** `wasmBuffer` allocations are permanent (bump allocator, no free).
|
|
203
|
+
Use them for long-lived buffers, not temporary scratch space.
|
|
204
|
+
|
|
205
|
+
## Patterns
|
|
206
|
+
|
|
207
|
+
### Dot product (horizontal SIMD reduction)
|
|
208
|
+
|
|
209
|
+
```tjs
|
|
210
|
+
function dot(! a: Float32Array, b: Float32Array, len: 0):! 0.0 {
|
|
211
|
+
return wasm {
|
|
212
|
+
let acc = f32x4_splat(0.0)
|
|
213
|
+
for (let i = 0; i < len; i += 4) {
|
|
214
|
+
let off = i * 4
|
|
215
|
+
acc = f32x4_add(acc, f32x4_mul(f32x4_load(a, off), f32x4_load(b, off)))
|
|
216
|
+
}
|
|
217
|
+
f32x4_extract_lane(acc, 0) + f32x4_extract_lane(acc, 1)
|
|
218
|
+
+ f32x4_extract_lane(acc, 2) + f32x4_extract_lane(acc, 3)
|
|
219
|
+
} fallback {
|
|
220
|
+
let sum = 0
|
|
221
|
+
for (let i = 0; i < len; i++) sum += a[i] * b[i]
|
|
222
|
+
return sum
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Normalize array to [0, 1]
|
|
228
|
+
|
|
229
|
+
```tjs
|
|
230
|
+
function normalize(! arr: Float32Array, len: 0) {
|
|
231
|
+
wasm {
|
|
232
|
+
let max = 0.0
|
|
233
|
+
for (let i = 0; i < len; i++) {
|
|
234
|
+
let off = i * 4
|
|
235
|
+
let v = f32x4_extract_lane(f32x4_load(arr, off), 0)
|
|
236
|
+
if (v > max) { max = v }
|
|
237
|
+
}
|
|
238
|
+
if (max > 0.0) {
|
|
239
|
+
let inv = f32x4_splat(1.0 / max)
|
|
240
|
+
for (let i = 0; i < len; i += 4) {
|
|
241
|
+
let off = i * 4
|
|
242
|
+
f32x4_store(arr, off, f32x4_mul(f32x4_load(arr, off), inv))
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
} fallback {
|
|
246
|
+
let max = 0
|
|
247
|
+
for (let i = 0; i < len; i++) if (arr[i] > max) max = arr[i]
|
|
248
|
+
if (max > 0) for (let i = 0; i < len; i++) arr[i] /= max
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Tips
|
|
254
|
+
|
|
255
|
+
- **Always provide a `fallback`** — it's your safety net and makes code testable
|
|
256
|
+
without WASM
|
|
257
|
+
- **Use `!` (unsafe) on WASM functions** — type checking overhead defeats the
|
|
258
|
+
purpose of dropping to WASM
|
|
259
|
+
- **Align to 4 elements** for SIMD — `f32x4` processes 4 floats at a time; pad
|
|
260
|
+
arrays to multiples of 4
|
|
261
|
+
- **Use `wasmBuffer` for hot loops** — the copy overhead of regular arrays can
|
|
262
|
+
negate WASM's speed advantage for large data
|
|
263
|
+
- **WASM blocks share memory per file** — all blocks in one `.tjs` file use the
|
|
264
|
+
same 64MB `WebAssembly.Memory`
|
|
265
|
+
- **Check the WAT comments** — the compiler includes human-readable WAT in the
|
|
266
|
+
output so you can verify what was compiled
|
|
267
|
+
|
|
268
|
+
## Limitations
|
|
269
|
+
|
|
270
|
+
- No function calls inside WASM blocks (only intrinsics and arithmetic)
|
|
271
|
+
- No imports/exports beyond the function itself
|
|
272
|
+
- `wasmBuffer` allocations are permanent (bump allocator)
|
|
273
|
+
- Array lengths must be multiples of 4 for SIMD operations
|
|
274
|
+
- SIMD is f32 only (f32x4) — no i32x4, f64x2, etc. yet
|
|
275
|
+
|
|
276
|
+
## Next Steps
|
|
277
|
+
|
|
278
|
+
- **[WASM Basics](https://github.com/tonioloewald/tjs-lang/blob/main/guides/examples/tjs/wasm-basics.md)** — Runnable examples: integer math, floats, array processing
|
|
279
|
+
- **[WASM SIMD](https://github.com/tonioloewald/tjs-lang/blob/main/guides/examples/tjs/wasm-simd.md)** — SIMD patterns: scale, dot product, benchmarking
|
|
280
|
+
- **[WASM Memory](https://github.com/tonioloewald/tjs-lang/blob/main/guides/examples/tjs/wasm-memory.md)** — Data marshaling deep dive, `wasmBuffer()` patterns
|
|
281
|
+
- **[WASM Starfield](https://github.com/tonioloewald/tjs-lang/blob/main/guides/examples/tjs/wasm-starfield.md)** — Full demo: 50K SIMD-accelerated particles
|
|
282
|
+
- **[WASM Vector Search](https://github.com/tonioloewald/tjs-lang/blob/main/guides/examples/tjs/wasm-vector-search.md)** — Benchmark: SIMD cosine similarity vs JS scalar
|
|
283
|
+
- **[TJS Language Guide — WASM section](https://github.com/tonioloewald/tjs-lang/blob/main/DOCS-TJS.md)** — Complete language reference
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 680 400" font-family="system-ui, -apple-system, sans-serif">
|
|
2
|
+
<style>
|
|
3
|
+
/* TJS Brand Palette */
|
|
4
|
+
.bg { fill: #fafafa; stroke: #e4e4e4; }
|
|
5
|
+
|
|
6
|
+
.section-old { fill: white; stroke: #ff1c24; stroke-width: 1.5; }
|
|
7
|
+
.section-new { fill: white; stroke: #006736; stroke-width: 1.5; }
|
|
8
|
+
.section-old-header { fill: #fff3f3; }
|
|
9
|
+
.section-new-header { fill: #f0fff5; }
|
|
10
|
+
|
|
11
|
+
.section-label-old { fill: #ff1c24; font-size: 13px; font-weight: 700; }
|
|
12
|
+
.section-label-new { fill: #006736; font-size: 13px; font-weight: 700; }
|
|
13
|
+
|
|
14
|
+
.node { fill: #f5f5f5; stroke: #9e9e9e; stroke-width: 1.5; }
|
|
15
|
+
.node-edge { fill: #eef6ff; stroke: #3da8f4; stroke-width: 1.5; }
|
|
16
|
+
.node-text { fill: #444; font-size: 14px; font-weight: 500; }
|
|
17
|
+
.node-text-edge { fill: #1a6ab5; font-size: 14px; font-weight: 600; }
|
|
18
|
+
|
|
19
|
+
.arrow-fwd { stroke: #9e9e9e; stroke-width: 1.5; fill: none; }
|
|
20
|
+
.arrow-ret-bad { stroke: #ff1c24; stroke-width: 1.5; fill: none; }
|
|
21
|
+
.arrow-good { stroke: #006736; stroke-width: 1.5; fill: none; }
|
|
22
|
+
|
|
23
|
+
.lbl-fwd { fill: #888; font-size: 10px; }
|
|
24
|
+
.lbl-ret-bad { fill: #ff1c24; font-size: 10px; font-weight: 600; }
|
|
25
|
+
.lbl-good { fill: #006736; font-size: 10px; font-weight: 500; }
|
|
26
|
+
.lbl-ret-good { fill: #006736; font-size: 10px; font-weight: 600; }
|
|
27
|
+
|
|
28
|
+
.callout-bad { fill: #fff3f3; stroke: #ff1c24; stroke-dasharray: 4 3; }
|
|
29
|
+
.callout-good { fill: #f0fff5; stroke: #006736; stroke-dasharray: 4 3; }
|
|
30
|
+
.callout-text-bad { fill: #ff1c24; font-size: 11px; }
|
|
31
|
+
.callout-text-good { fill: #006736; font-size: 11px; }
|
|
32
|
+
|
|
33
|
+
.shadow { filter: drop-shadow(0 2px 3px rgba(0,0,0,0.1)); }
|
|
34
|
+
|
|
35
|
+
.ah { fill: #9e9e9e; }
|
|
36
|
+
.ah-bad { fill: #ff1c24; }
|
|
37
|
+
.ah-good { fill: #006736; }
|
|
38
|
+
</style>
|
|
39
|
+
|
|
40
|
+
<defs>
|
|
41
|
+
<marker id="a" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
|
42
|
+
<path d="M0 0 L10 5 L0 10z" class="ah"/>
|
|
43
|
+
</marker>
|
|
44
|
+
<marker id="ab" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
|
45
|
+
<path d="M0 0 L10 5 L0 10z" class="ah-bad"/>
|
|
46
|
+
</marker>
|
|
47
|
+
<marker id="ag" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
|
48
|
+
<path d="M0 0 L10 5 L0 10z" class="ah-good"/>
|
|
49
|
+
</marker>
|
|
50
|
+
</defs>
|
|
51
|
+
|
|
52
|
+
<rect width="680" height="400" rx="12" class="bg"/>
|
|
53
|
+
|
|
54
|
+
<!-- OLD WAY -->
|
|
55
|
+
<g class="shadow">
|
|
56
|
+
<rect x="20" y="16" width="640" height="170" rx="8" class="section-old"/>
|
|
57
|
+
</g>
|
|
58
|
+
<rect x="20" y="16" width="640" height="32" rx="8" class="section-old-header"/>
|
|
59
|
+
<rect x="20" y="36" width="640" height="12" class="section-old-header"/>
|
|
60
|
+
<text x="40" y="38" class="section-label-old">OLD WAY: Data-to-Code</text>
|
|
61
|
+
|
|
62
|
+
<rect x="50" y="70" width="120" height="50" rx="8" class="node"/>
|
|
63
|
+
<text x="110" y="100" text-anchor="middle" class="node-text">Client</text>
|
|
64
|
+
|
|
65
|
+
<rect x="280" y="70" width="120" height="50" rx="8" class="node"/>
|
|
66
|
+
<text x="340" y="100" text-anchor="middle" class="node-text">Server</text>
|
|
67
|
+
|
|
68
|
+
<rect x="510" y="70" width="120" height="50" rx="8" class="node"/>
|
|
69
|
+
<text x="570" y="100" text-anchor="middle" class="node-text">Database</text>
|
|
70
|
+
|
|
71
|
+
<line x1="170" y1="85" x2="275" y2="85" class="arrow-fwd" marker-end="url(#a)"/>
|
|
72
|
+
<text x="222" y="78" text-anchor="middle" class="lbl-fwd">request</text>
|
|
73
|
+
|
|
74
|
+
<line x1="400" y1="85" x2="505" y2="85" class="arrow-fwd" marker-end="url(#a)"/>
|
|
75
|
+
<text x="452" y="78" text-anchor="middle" class="lbl-fwd">fetch 100 rows</text>
|
|
76
|
+
|
|
77
|
+
<line x1="505" y1="105" x2="400" y2="105" class="arrow-ret-bad" marker-end="url(#ab)"/>
|
|
78
|
+
<text x="452" y="118" text-anchor="middle" class="lbl-ret-bad">100 rows</text>
|
|
79
|
+
|
|
80
|
+
<line x1="275" y1="105" x2="170" y2="105" class="arrow-ret-bad" marker-end="url(#ab)"/>
|
|
81
|
+
<text x="222" y="118" text-anchor="middle" class="lbl-ret-bad">5 rows</text>
|
|
82
|
+
|
|
83
|
+
<rect x="170" y="140" width="330" height="28" rx="14" class="callout-bad"/>
|
|
84
|
+
<text x="335" y="158" text-anchor="middle" class="callout-text-bad">High latency. High bandwidth. Validate at every layer.</text>
|
|
85
|
+
|
|
86
|
+
<!-- TJS WAY -->
|
|
87
|
+
<g class="shadow">
|
|
88
|
+
<rect x="20" y="210" width="640" height="174" rx="8" class="section-new"/>
|
|
89
|
+
</g>
|
|
90
|
+
<rect x="20" y="210" width="640" height="32" rx="8" class="section-new-header"/>
|
|
91
|
+
<rect x="20" y="230" width="640" height="12" class="section-new-header"/>
|
|
92
|
+
<text x="40" y="232" class="section-label-new">TJS WAY: Code-to-Data</text>
|
|
93
|
+
|
|
94
|
+
<rect x="50" y="264" width="120" height="50" rx="8" class="node"/>
|
|
95
|
+
<text x="110" y="294" text-anchor="middle" class="node-text">Client</text>
|
|
96
|
+
|
|
97
|
+
<rect x="280" y="264" width="120" height="50" rx="8" class="node-edge"/>
|
|
98
|
+
<text x="340" y="294" text-anchor="middle" class="node-text-edge">Edge</text>
|
|
99
|
+
|
|
100
|
+
<rect x="510" y="264" width="120" height="50" rx="8" class="node"/>
|
|
101
|
+
<text x="570" y="294" text-anchor="middle" class="node-text">Database</text>
|
|
102
|
+
|
|
103
|
+
<line x1="170" y1="279" x2="275" y2="279" class="arrow-good" marker-end="url(#ag)"/>
|
|
104
|
+
<text x="222" y="272" text-anchor="middle" class="lbl-good">send agent</text>
|
|
105
|
+
|
|
106
|
+
<line x1="400" y1="279" x2="505" y2="279" class="arrow-good" marker-end="url(#ag)"/>
|
|
107
|
+
<text x="452" y="272" text-anchor="middle" class="lbl-good">run at data</text>
|
|
108
|
+
|
|
109
|
+
<line x1="505" y1="299" x2="400" y2="299" class="arrow-good" marker-end="url(#ag)"/>
|
|
110
|
+
<text x="452" y="312" text-anchor="middle" class="lbl-ret-good">5 rows</text>
|
|
111
|
+
|
|
112
|
+
<line x1="275" y1="299" x2="170" y2="299" class="arrow-good" marker-end="url(#ag)"/>
|
|
113
|
+
<text x="222" y="312" text-anchor="middle" class="lbl-ret-good">5 rows</text>
|
|
114
|
+
|
|
115
|
+
<rect x="170" y="334" width="330" height="28" rx="14" class="callout-good"/>
|
|
116
|
+
<text x="335" y="352" text-anchor="middle" class="callout-text-good">Low latency. Zero waste. Validate once.</text>
|
|
117
|
+
</svg>
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 660 420" font-family="system-ui, -apple-system, sans-serif">
|
|
2
|
+
<style>
|
|
3
|
+
/* TJS Brand Palette */
|
|
4
|
+
.bg { fill: #fafafa; stroke: #e4e4e4; }
|
|
5
|
+
|
|
6
|
+
.section-compile { fill: white; stroke: #3da8f4; stroke-width: 1.5; }
|
|
7
|
+
.section-runtime { fill: white; stroke: #181340; stroke-width: 1.5; }
|
|
8
|
+
.section-label-compile { fill: #3da8f4; font-size: 12px; font-weight: 700; letter-spacing: 1px; }
|
|
9
|
+
.section-label-runtime { fill: #181340; font-size: 12px; font-weight: 700; letter-spacing: 1px; }
|
|
10
|
+
|
|
11
|
+
.box-code { fill: #f8f8f8; stroke: #9e9e9e; }
|
|
12
|
+
.box-action { fill: #eef6ff; stroke: #3da8f4; stroke-width: 1.5; }
|
|
13
|
+
.box-error { fill: #fff3f3; stroke: #ff1c24; stroke-width: 1.5; }
|
|
14
|
+
.box-pass { fill: #f0fff5; stroke: #006736; stroke-width: 1.5; }
|
|
15
|
+
.box-metadata { fill: #f8f8f8; stroke: #9e9e9e; }
|
|
16
|
+
|
|
17
|
+
.diamond { fill: #fffbe6; stroke: #fdbe52; stroke-width: 1.5; }
|
|
18
|
+
|
|
19
|
+
.arrow { stroke: #9e9e9e; stroke-width: 1.5; fill: none; }
|
|
20
|
+
.arrow-pass { stroke: #006736; stroke-width: 1.5; fill: none; }
|
|
21
|
+
.arrow-fail { stroke: #ff1c24; stroke-width: 1.5; fill: none; }
|
|
22
|
+
|
|
23
|
+
.label { font-size: 13px; font-weight: 500; }
|
|
24
|
+
.label-sm { font-size: 11px; }
|
|
25
|
+
.label-xs { font-size: 9px; }
|
|
26
|
+
.label-mono { font-size: 11px; font-family: monospace; }
|
|
27
|
+
.label-mono-sm { font-size: 12px; font-family: monospace; }
|
|
28
|
+
|
|
29
|
+
.text-action { fill: #1a6ab5; }
|
|
30
|
+
.text-dark { fill: #444; }
|
|
31
|
+
.text-muted { fill: #999; }
|
|
32
|
+
.text-pass { fill: #006736; font-size: 10px; font-weight: 600; }
|
|
33
|
+
.text-fail { fill: #ff1c24; font-size: 10px; font-weight: 600; }
|
|
34
|
+
.text-error { fill: #cc1118; }
|
|
35
|
+
|
|
36
|
+
.shadow { filter: drop-shadow(0 2px 3px rgba(0,0,0,0.1)); }
|
|
37
|
+
|
|
38
|
+
.arrowhead { fill: #9e9e9e; }
|
|
39
|
+
.arrowhead-pass { fill: #006736; }
|
|
40
|
+
.arrowhead-fail { fill: #ff1c24; }
|
|
41
|
+
</style>
|
|
42
|
+
|
|
43
|
+
<defs>
|
|
44
|
+
<marker id="arr" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
|
45
|
+
<path d="M0 0 L10 5 L0 10z" class="arrowhead"/>
|
|
46
|
+
</marker>
|
|
47
|
+
<marker id="arr-pass" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
|
48
|
+
<path d="M0 0 L10 5 L0 10z" class="arrowhead-pass"/>
|
|
49
|
+
</marker>
|
|
50
|
+
<marker id="arr-fail" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
|
51
|
+
<path d="M0 0 L10 5 L0 10z" class="arrowhead-fail"/>
|
|
52
|
+
</marker>
|
|
53
|
+
</defs>
|
|
54
|
+
|
|
55
|
+
<rect width="660" height="420" rx="12" class="bg"/>
|
|
56
|
+
|
|
57
|
+
<!-- COMPILE TIME -->
|
|
58
|
+
<g id="section-compile" class="section shadow">
|
|
59
|
+
<rect x="20" y="16" width="620" height="140" rx="8" class="section-compile"/>
|
|
60
|
+
</g>
|
|
61
|
+
<text x="40" y="42" class="section-label-compile">COMPILE TIME</text>
|
|
62
|
+
|
|
63
|
+
<!-- Source code: two-line box, h=44 -->
|
|
64
|
+
<g id="node-source" class="node flow-step">
|
|
65
|
+
<rect x="40" y="56" width="200" height="44" rx="6" class="box-code"/>
|
|
66
|
+
<text x="140" y="75" text-anchor="middle" class="label-mono text-dark">greet(name: 'World')</text>
|
|
67
|
+
<text x="140" y="90" text-anchor="middle" class="label-xs text-muted">source</text>
|
|
68
|
+
</g>
|
|
69
|
+
|
|
70
|
+
<line x1="245" y1="78" x2="290" y2="78" class="arrow connector" marker-end="url(#arr)"/>
|
|
71
|
+
|
|
72
|
+
<g id="node-parse" class="node flow-step">
|
|
73
|
+
<rect x="295" y="60" width="100" height="36" rx="6" class="box-action"/>
|
|
74
|
+
<text x="345" y="83" text-anchor="middle" class="label text-action">Parse</text>
|
|
75
|
+
</g>
|
|
76
|
+
|
|
77
|
+
<line x1="400" y1="78" x2="445" y2="78" class="arrow connector" marker-end="url(#arr)"/>
|
|
78
|
+
|
|
79
|
+
<!-- Extract type: two-line box, h=44 -->
|
|
80
|
+
<g id="node-extract" class="node flow-step">
|
|
81
|
+
<rect x="450" y="56" width="170" height="44" rx="6" class="box-action"/>
|
|
82
|
+
<text x="535" y="75" text-anchor="middle" class="label text-action">Extract type</text>
|
|
83
|
+
<text x="535" y="90" text-anchor="middle" class="label-xs text-action">name = string</text>
|
|
84
|
+
</g>
|
|
85
|
+
|
|
86
|
+
<!-- Arrow between sections -->
|
|
87
|
+
<line x1="330" y1="156" x2="330" y2="194" class="arrow connector" marker-end="url(#arr)"/>
|
|
88
|
+
|
|
89
|
+
<!-- RUNTIME — expanded to h=210 -->
|
|
90
|
+
<g id="section-runtime" class="section shadow">
|
|
91
|
+
<rect x="20" y="198" width="620" height="210" rx="8" class="section-runtime"/>
|
|
92
|
+
</g>
|
|
93
|
+
<text x="40" y="224" class="section-label-runtime">RUNTIME</text>
|
|
94
|
+
|
|
95
|
+
<g id="node-metadata" class="node">
|
|
96
|
+
<rect x="40" y="238" width="280" height="34" rx="6" class="box-metadata"/>
|
|
97
|
+
<text x="180" y="260" text-anchor="middle" class="label-mono text-dark">greet.__tjs = { params: { name: ... } }</text>
|
|
98
|
+
</g>
|
|
99
|
+
|
|
100
|
+
<g id="node-call" class="node">
|
|
101
|
+
<rect x="40" y="292" width="140" height="34" rx="6" class="box-error"/>
|
|
102
|
+
<text x="110" y="314" text-anchor="middle" class="label-mono-sm text-error">greet(123)</text>
|
|
103
|
+
</g>
|
|
104
|
+
|
|
105
|
+
<line x1="185" y1="309" x2="230" y2="309" class="arrow connector" marker-end="url(#arr)"/>
|
|
106
|
+
|
|
107
|
+
<!-- Diamond -->
|
|
108
|
+
<g id="gate-type-check" class="gate">
|
|
109
|
+
<polygon points="310,286 374,309 310,332 246,309" class="diamond"/>
|
|
110
|
+
<text x="310" y="313" text-anchor="middle" fill="#8b6914" font-size="12" class="label">Type Check</text>
|
|
111
|
+
</g>
|
|
112
|
+
|
|
113
|
+
<!-- Pass -->
|
|
114
|
+
<line x1="374" y1="309" x2="424" y2="309" class="arrow-pass connector" marker-end="url(#arr-pass)"/>
|
|
115
|
+
<text x="399" y="301" text-anchor="middle" class="text-pass">Pass</text>
|
|
116
|
+
|
|
117
|
+
<g id="node-execute" class="node outcome-pass">
|
|
118
|
+
<rect x="429" y="293" width="100" height="34" rx="6" class="box-pass"/>
|
|
119
|
+
<text x="479" y="314" text-anchor="middle" class="label" fill="#005028">Execute</text>
|
|
120
|
+
</g>
|
|
121
|
+
|
|
122
|
+
<!-- Fail -->
|
|
123
|
+
<line x1="310" y1="332" x2="310" y2="358" class="arrow-fail connector" marker-end="url(#arr-fail)"/>
|
|
124
|
+
<text x="330" y="350" class="text-fail">Fail</text>
|
|
125
|
+
|
|
126
|
+
<g id="node-error" class="node outcome-fail">
|
|
127
|
+
<rect x="225" y="362" width="170" height="34" rx="6" class="box-error"/>
|
|
128
|
+
<text x="310" y="383" text-anchor="middle" class="label-sm text-error">MonadicError (no throw)</text>
|
|
129
|
+
</g>
|
|
130
|
+
</svg>
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<!-- Riff 1: "The Aware Runtime" — owl face as a live terminal/monitor
|
|
3
|
+
The screen shows -> (the TJS return type arrow), suggesting the owl
|
|
4
|
+
IS the runtime. Eyes are active. Blue sky palette from the animation. -->
|
|
5
|
+
<svg viewBox="0 0 48 48" width="48" height="48" xmlns="http://www.w3.org/2000/svg">
|
|
6
|
+
<style>
|
|
7
|
+
.bg { fill: #3da8f4; }
|
|
8
|
+
.screen { fill: #ffffff; stroke: #000; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
|
|
9
|
+
.outline { fill: none; stroke: #000; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
|
|
10
|
+
.wing { fill: #9e9e9e; stroke: #000; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
|
|
11
|
+
.body { fill: #e4e4e4; stroke: #000; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
|
|
12
|
+
.feet { fill: none; stroke: #000; stroke-width: 2; stroke-linejoin: round; stroke-linecap: round; }
|
|
13
|
+
.eye { fill: #006736; stroke: #000; stroke-width: 1.5; stroke-linecap: round; }
|
|
14
|
+
.symbol { fill: none; stroke: #006736; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
|
|
15
|
+
.cloud { fill: rgba(255,255,255,0.85); }
|
|
16
|
+
</style>
|
|
17
|
+
|
|
18
|
+
<!-- Background: blue sky -->
|
|
19
|
+
<rect width="48" height="48" rx="8" class="bg"/>
|
|
20
|
+
|
|
21
|
+
<!-- Small cloud bottom-left (from platform icon) -->
|
|
22
|
+
<ellipse cx="8" cy="43" rx="7" ry="4" class="cloud"/>
|
|
23
|
+
<ellipse cx="15" cy="44" rx="5" ry="3" class="cloud"/>
|
|
24
|
+
|
|
25
|
+
<!-- Left wing -->
|
|
26
|
+
<path d="M7.5,16 C4.5,16,2,18.5,2,21.5 C2,24,4,26,6.5,26 L6.6,26 C7.4,26,8,26.6,8,27.4 C8,29.9,10.1,32,12.6,32 L16,32 C18.6,32,20.7,29.9,20.7,27.3 L20.7,21.4 C20.7,18.4,18.3,16,15.3,16 Z" class="wing"/>
|
|
27
|
+
|
|
28
|
+
<!-- Right wing -->
|
|
29
|
+
<path d="M40.5,16 C43.5,16,46,18.5,46,21.5 C46,24,44,26,41.5,26 L41.4,26 C40.6,26,40,26.6,40,27.4 C40,29.9,37.9,32,35.4,32 L32,32 C29.4,32,27.3,29.9,27.3,27.3 L27.3,21.4 C27.3,18.4,29.7,16,32.7,16 Z" class="wing"/>
|
|
30
|
+
|
|
31
|
+
<!-- Body -->
|
|
32
|
+
<path d="M17,29 L33,29 C33,34.5,28.5,39,23,39 C19.7,39,17,36.3,17,33 Z" class="body"/>
|
|
33
|
+
|
|
34
|
+
<!-- Feet -->
|
|
35
|
+
<path d="M20,43 L18,41 L16,43" class="feet"/>
|
|
36
|
+
<path d="M32,43 L30,41 L28,43" class="feet"/>
|
|
37
|
+
|
|
38
|
+
<!-- Rain lines (subtle, from animation) -->
|
|
39
|
+
<path d="M35,35 L32.2,35 L32.2,37.8" class="feet"/>
|
|
40
|
+
<path d="M31,39 L28.2,39 L28.2,41.8" class="feet"/>
|
|
41
|
+
|
|
42
|
+
<!-- Head/Screen -->
|
|
43
|
+
<rect x="6" y="6" width="30" height="23" rx="4" class="screen"/>
|
|
44
|
+
|
|
45
|
+
<!-- Divider line (nose) -->
|
|
46
|
+
<line x1="21" y1="11" x2="21" y2="23" class="outline"/>
|
|
47
|
+
|
|
48
|
+
<!-- Eyes: green rectangles (like code blocks / the runtime watching) -->
|
|
49
|
+
<rect x="10" y="13" width="7" height="6" rx="1.5" class="eye"/>
|
|
50
|
+
<rect x="25" y="13" width="7" height="6" rx="1.5" class="eye"/>
|
|
51
|
+
|
|
52
|
+
<!-- TJS arrow symbol on forehead area -->
|
|
53
|
+
<path d="M15,9 L19,9" class="symbol"/>
|
|
54
|
+
<path d="M17.5,7.5 L19,9 L17.5,10.5" class="symbol"/>
|
|
55
|
+
</svg>
|