chore: vendor @joan/procedural-glyph-engine for portable builds

Move @joan/procedural-glyph-engine from absolute path dep
(/private/tmp/orby-pkg) to vendored local dep (web/vendor/) so the
SPA builds in Docker and on other machines without the temp dir.

- Vendor Orby v5.0.0 into web/vendor/
- Switch package.json dep to file:../vendor
- Update Dockerfile to COPY vendor into build context
- Use npm install instead of npm ci (file: deps need install)
- Fix missing trailing newline in Dockerfile
This commit is contained in:
2026-08-05 17:27:04 +02:00
parent 38c472a118
commit 3cd4cf98c3
40 changed files with 27241 additions and 325 deletions

112
web/vendor/docs/QUICKSTART.md vendored Normal file
View File

@@ -0,0 +1,112 @@
# Orby — quick start
**A procedural glyph engine by Joan Sterjo.**
The download kit is self-contained. Start with the path that matches how you
want to use the engine; no registry download or third-party runtime dependency
is required.
## 1. Open the standalone Studio
Open `joan-engine-v5.standalone.html` in a modern browser. It contains the
Studio, engine modules, and styles in one file, so it works without a build
step or local server. Use this path to explore states, tune a recipe, and export
PNG, SVG, animated SVG, or `.joan.json` output.
## 2. Import the native ES modules
Serve the extracted folder from a local HTTP server:
```sh
cd joan-procedural-glyph-engine-5.0.0
python3 -m http.server 4173
```
Then import the engine directly from the included source:
```html
<canvas id="ai-glyph" width="160" height="160"></canvas>
<script type="module">
import { createGlyph } from "./src/joan-engine.js";
const glyph = createGlyph("#ai-glyph", {
sprite: "ai.idle",
seed: "product-session",
gridSize: 68,
});
await glyph.transitionTo("ai.thinking");
await glyph.transitionTo("status.success", {
transition: "path-draw",
});
</script>
```
Open `examples/canvas.html` for a complete interactive version.
## 3. Install the extracted folder locally
From an application beside the extracted kit:
```sh
npm install ./joan-procedural-glyph-engine-5.0.0
```
Use the package and its TypeScript declarations through the included exports:
```js
import { createGlyph } from "@joan/procedural-glyph-engine";
import { playStateSequence } from
"@joan/procedural-glyph-engine/state-sequence";
const glyph = createGlyph("#ai-glyph", {
sprite: "ai.ambient-idle",
seed: "conversation-42",
gridSize: 68,
});
const playback = playStateSequence(glyph, [
{ sprite: "ai.ambient-idle", holdMs: 5_000 },
{ sprite: "ai.progress", transition: "contour-trace" },
{ sprite: "status.success", transition: "path-draw" },
]);
await playback.finished;
```
## Web component
Import `src/web-component-register.js`, then use `<joan-glyph>` anywhere in the
page. Its intrinsic default is 68 × 68 CSS pixels.
```html
<script type="module" src="./src/web-component-register.js"></script>
<joan-glyph sprite="ai.thinking" seed="conversation-42"></joan-glyph>
```
Open `examples/web-component.html` for a runnable example.
## Runtime essentials
- `glyph.transitionTo(sprite, options)` performs and awaits a visual handoff.
- `glyph.setSprite(sprite)` changes state immediately and remains chainable.
- `glyph.setOptions(patch)` updates the live engine safely.
- `glyph.signal(type, payload)` injects short-lived product input.
- `glyph.exportConfig()` returns a JSON-safe recipe.
- `glyph.toSVG()` and `glyph.downloadPNG()` create portable output.
- `glyph.destroy()` releases animation frames, observers, and listeners.
See `README.md` for the complete state catalog, configuration surface, API,
events, custom fields and glyphs, orchestration, exports, performance guidance,
accessibility, and testing.
## Verify the kit
`SHA256SUMS.txt` lists every payload file included in the archive. The website also
publishes a checksum beside the ZIP so the archive itself can be verified
before extraction.
Orby is source-available under the Apache License 2.0 subject to the Commons
Clause License Condition v1.0. You may use, copy, modify, and redistribute it,
but may not sell Orby or a product or service whose value derives entirely or
substantially from Orby's functionality. See `LICENSE` and `NOTICE` in the kit.

162
web/vendor/docs/assets/readme-hero.svg vendored Normal file
View File

@@ -0,0 +1,162 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 760" role="img" aria-labelledby="title description" preserveAspectRatio="xMidYMid meet">
<title id="title">Orby — A procedural glyph engine by Joan Sterjo.</title>
<desc id="description">A monochrome editorial banner for Orby, a procedural glyph engine by Joan Sterjo, showing a procedural pixel orb and engine statistics: 25 semantic states, 19 fields, 12 transitions, and 9 pixel shapes.</desc>
<defs>
<pattern id="page-grid" width="64" height="64" patternUnits="userSpaceOnUse">
<path d="M64 0H0V64" fill="none" stroke="#ffffff" stroke-opacity="0.045" stroke-width="1"/>
<circle cx="1" cy="1" r="1" fill="#ffffff" fill-opacity="0.09"/>
</pattern>
<pattern id="micro-grid" width="12" height="12" patternUnits="userSpaceOnUse">
<rect x="5.25" y="5.25" width="1.5" height="1.5" fill="#f2f1ee" fill-opacity="0.46"/>
</pattern>
<pattern id="dither-a" width="32" height="32" patternUnits="userSpaceOnUse">
<rect x="3" y="3" width="3" height="3" fill="#f2f1ee"/>
<rect x="19" y="7" width="2" height="2" fill="#f2f1ee" fill-opacity="0.72"/>
<rect x="10" y="22" width="4" height="4" fill="#f2f1ee" fill-opacity="0.9"/>
<rect x="27" y="26" width="2" height="2" fill="#f2f1ee" fill-opacity="0.5"/>
</pattern>
<pattern id="dither-b" width="48" height="48" patternUnits="userSpaceOnUse" patternTransform="rotate(9)">
<rect x="5" y="8" width="6" height="6" fill="#f2f1ee"/>
<rect x="29" y="3" width="3" height="3" fill="#f2f1ee" fill-opacity="0.7"/>
<rect x="18" y="31" width="4" height="4" fill="#f2f1ee" fill-opacity="0.86"/>
<rect x="41" y="38" width="2" height="2" fill="#f2f1ee" fill-opacity="0.48"/>
</pattern>
<linearGradient id="field-tone" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#f2f1ee" stop-opacity="0.92"/>
<stop offset="0.38" stop-color="#c9c8c4" stop-opacity="0.62"/>
<stop offset="0.72" stop-color="#8b8d90" stop-opacity="0.25"/>
<stop offset="1" stop-color="#ffffff" stop-opacity="0.08"/>
</linearGradient>
<radialGradient id="orb-ground" cx="35%" cy="28%" r="78%">
<stop offset="0" stop-color="#252628"/>
<stop offset="0.55" stop-color="#111214"/>
<stop offset="1" stop-color="#080809"/>
</radialGradient>
<linearGradient id="density-gradient" x1="0" y1="0" x2="1" y2="0">
<stop offset="0" stop-color="#ffffff"/>
<stop offset="0.24" stop-color="#ffffff" stop-opacity="0.9"/>
<stop offset="0.56" stop-color="#ffffff" stop-opacity="0.38"/>
<stop offset="0.82" stop-color="#ffffff" stop-opacity="0.12"/>
<stop offset="1" stop-color="#ffffff" stop-opacity="0.02"/>
</linearGradient>
<clipPath id="orb-clip">
<circle cx="1232" cy="362" r="238"/>
</clipPath>
<mask id="orb-density" maskUnits="userSpaceOnUse" x="975" y="105" width="514" height="514">
<rect x="975" y="105" width="514" height="514" fill="url(#density-gradient)"/>
</mask>
<mask id="crescent" maskUnits="userSpaceOnUse" x="975" y="105" width="514" height="514">
<rect x="975" y="105" width="514" height="514" fill="#000000"/>
<circle cx="1202" cy="362" r="222" fill="#ffffff"/>
<circle cx="1280" cy="326" r="204" fill="#000000"/>
</mask>
</defs>
<rect width="1600" height="760" fill="#050505"/>
<rect width="1600" height="760" fill="url(#page-grid)"/>
<path d="M0 80H1600M800 0V760M0 632H1600" fill="none" stroke="#ffffff" stroke-opacity="0.08"/>
<rect x="24" y="24" width="1552" height="712" rx="24" fill="none" stroke="#ffffff" stroke-opacity="0.12"/>
<g fill="none" stroke="#f2f1ee" stroke-opacity="0.38">
<path d="M48 72V48H72"/>
<path d="M1528 48H1552V72"/>
<path d="M48 688V712H72"/>
<path d="M1528 712H1552V688"/>
</g>
<g font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace" fill="#8b8d90">
<text x="88" y="84" font-size="13" letter-spacing="4">SYSTEM / 05</text>
<text x="312" y="84" font-size="13" letter-spacing="4" fill="#c9c8c4">DETERMINISTIC · LIVE · PORTABLE</text>
<text x="1512" y="84" text-anchor="end" font-size="13" letter-spacing="3">V5.0.0</text>
</g>
<g font-family="-apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif">
<text x="82" y="238" fill="#f2f1ee" font-size="128" font-weight="650" letter-spacing="12">ORBY</text>
<text x="88" y="292" fill="#c9c8c4" font-size="18" font-weight="600" letter-spacing="3">A PROCEDURAL GLYPH ENGINE BY JOAN STERJO.</text>
<text x="86" y="398" fill="#f2f1ee" font-size="43" font-weight="420" letter-spacing="-1.3">
<tspan x="86" dy="0">A living signal for every</tspan>
<tspan x="86" dy="51">state of intelligence.</tspan>
</text>
<text x="88" y="514" fill="#8b8d90" font-size="18" font-weight="400">
<tspan x="88" dy="0">Deterministic pixel motion for expressive AI states,</tspan>
<tspan x="88" dy="29">responsive transitions, and coherent product presence.</tspan>
</text>
</g>
<g aria-hidden="true">
<path d="M941 363c-68-58-105-120-88-171 20-60 111-83 224-64 114 19 231 76 305 146 74 70 105 145 77 197-23 43-91 62-179 52" fill="none" stroke="#f2f1ee" stroke-opacity="0.11" stroke-dasharray="2 9"/>
<path d="M1004 576c-78 23-137 12-153-29-22-57 47-151 161-221 114-70 249-102 333-72 69 25 76 87 27 158" fill="none" stroke="#c9c8c4" stroke-opacity="0.13" stroke-dasharray="1 8"/>
<circle cx="1232" cy="362" r="266" fill="none" stroke="#ffffff" stroke-opacity="0.07"/>
<circle cx="1232" cy="362" r="250" fill="none" stroke="#c9c8c4" stroke-opacity="0.18" stroke-dasharray="2 7"/>
<circle cx="1232" cy="362" r="238" fill="url(#orb-ground)" stroke="#f2f1ee" stroke-opacity="0.38"/>
<g clip-path="url(#orb-clip)">
<rect x="990" y="120" width="484" height="484" fill="url(#micro-grid)" opacity="0.48"/>
<rect x="984" y="114" width="496" height="496" fill="url(#dither-a)" mask="url(#orb-density)" opacity="0.82"/>
<rect x="984" y="114" width="496" height="496" fill="url(#dither-b)" mask="url(#crescent)" opacity="0.95"/>
<path d="M1017 425c82-53 172-45 212 10 39 54 9 136-69 178" fill="none" stroke="#f2f1ee" stroke-opacity="0.42" stroke-width="2" stroke-dasharray="2 10"/>
<path d="M1091 172c60 56 146 74 207 39 57-32 79-99 55-159" fill="none" stroke="#f2f1ee" stroke-opacity="0.24" stroke-dasharray="2 8"/>
<path d="M1140 322c18-63 83-99 143-79 60 20 92 87 69 148-24 61-92 89-150 62-57-26-80-95-51-151" fill="none" stroke="#f2f1ee" stroke-opacity="0.36" stroke-dasharray="1 7"/>
<path d="M1178 342c9-37 47-59 83-49 37 10 58 49 46 86-12 36-51 56-87 42-35-13-52-54-37-88" fill="none" stroke="#f2f1ee" stroke-opacity="0.28" stroke-dasharray="1 6"/>
<path d="M994 362H1470M1232 124V600" stroke="#ffffff" stroke-opacity="0.055" stroke-dasharray="1 11"/>
</g>
<circle cx="1232" cy="362" r="176" fill="none" stroke="#ffffff" stroke-opacity="0.16" stroke-dasharray="1 8"/>
<circle cx="1232" cy="362" r="72" fill="none" stroke="#f2f1ee" stroke-opacity="0.32" stroke-dasharray="3 8"/>
<circle cx="1232" cy="362" r="20" fill="none" stroke="#f2f1ee" stroke-opacity="0.62"/>
<rect x="1228" y="358" width="8" height="8" fill="#f2f1ee"/>
<g fill="none" stroke="#c9c8c4" stroke-opacity="0.5">
<rect x="948" y="178" width="10" height="10"/>
<rect x="1440" y="148" width="8" height="8"/>
<rect x="1498" y="336" width="12" height="12"/>
<rect x="1434" y="568" width="9" height="9"/>
<rect x="968" y="552" width="12" height="12"/>
</g>
<g fill="#f2f1ee">
<rect x="923" y="300" width="5" height="5" opacity="0.75"/>
<rect x="1481" y="242" width="6" height="6" opacity="0.55"/>
<rect x="1518" y="453" width="5" height="5" opacity="0.8"/>
<rect x="1032" y="612" width="4" height="4" opacity="0.52"/>
</g>
</g>
<g font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace">
<text x="854" y="84" fill="#8b8d90" font-size="12" letter-spacing="3">LIVE FIELD / 68×68</text>
<text x="1510" y="675" fill="#8b8d90" text-anchor="end" font-size="12" letter-spacing="3">STATE / AI.IDLE</text>
</g>
<g transform="translate(88 664)" font-family="ui-monospace, SFMono-Regular, Menlo, Consolas, monospace">
<g>
<text y="0" fill="#f2f1ee" font-size="30">25</text>
<text y="28" fill="#8b8d90" font-size="11" letter-spacing="2.4">SEMANTIC STATES</text>
</g>
<g transform="translate(190 0)">
<text y="0" fill="#f2f1ee" font-size="30">19</text>
<text y="28" fill="#8b8d90" font-size="11" letter-spacing="2.4">FIELDS</text>
</g>
<g transform="translate(330 0)">
<text y="0" fill="#f2f1ee" font-size="30">12</text>
<text y="28" fill="#8b8d90" font-size="11" letter-spacing="2.4">TRANSITIONS</text>
</g>
<g transform="translate(510 0)">
<text y="0" fill="#f2f1ee" font-size="30">9</text>
<text y="28" fill="#8b8d90" font-size="11" letter-spacing="2.4">PIXEL SHAPES</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.9 KiB