omegon 0.6.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.
- package/.gitattributes +3 -0
- package/AGENTS.md +16 -0
- package/LICENSE +15 -0
- package/README.md +289 -0
- package/bin/pi.mjs +30 -0
- package/extensions/00-secrets/index.ts +1126 -0
- package/extensions/01-auth/auth.ts +401 -0
- package/extensions/01-auth/index.ts +289 -0
- package/extensions/auto-compact.ts +42 -0
- package/extensions/bootstrap/deps.ts +291 -0
- package/extensions/bootstrap/index.ts +811 -0
- package/extensions/chronos/chronos.sh +487 -0
- package/extensions/chronos/index.ts +148 -0
- package/extensions/cleave/assessment.ts +754 -0
- package/extensions/cleave/bridge.ts +31 -0
- package/extensions/cleave/conflicts.ts +250 -0
- package/extensions/cleave/dispatcher.ts +808 -0
- package/extensions/cleave/guardrails.ts +426 -0
- package/extensions/cleave/index.ts +3121 -0
- package/extensions/cleave/lifecycle-emitter.ts +20 -0
- package/extensions/cleave/openspec.ts +811 -0
- package/extensions/cleave/planner.ts +260 -0
- package/extensions/cleave/review.ts +579 -0
- package/extensions/cleave/skills.ts +355 -0
- package/extensions/cleave/types.ts +261 -0
- package/extensions/cleave/workspace.ts +861 -0
- package/extensions/cleave/worktree.ts +243 -0
- package/extensions/core-renderers.ts +253 -0
- package/extensions/dashboard/context-gauge.ts +58 -0
- package/extensions/dashboard/file-watch.ts +14 -0
- package/extensions/dashboard/footer.ts +1145 -0
- package/extensions/dashboard/git.ts +185 -0
- package/extensions/dashboard/index.ts +478 -0
- package/extensions/dashboard/memory-audit.ts +34 -0
- package/extensions/dashboard/overlay-data.ts +705 -0
- package/extensions/dashboard/overlay.ts +365 -0
- package/extensions/dashboard/render-utils.ts +54 -0
- package/extensions/dashboard/types.ts +191 -0
- package/extensions/dashboard/uri-helper.ts +45 -0
- package/extensions/debug.ts +69 -0
- package/extensions/defaults.ts +282 -0
- package/extensions/design-tree/dashboard-state.ts +161 -0
- package/extensions/design-tree/design-card.ts +362 -0
- package/extensions/design-tree/index.ts +2130 -0
- package/extensions/design-tree/lifecycle-emitter.ts +41 -0
- package/extensions/design-tree/tree.ts +1607 -0
- package/extensions/design-tree/types.ts +163 -0
- package/extensions/distill.ts +127 -0
- package/extensions/effort/index.ts +395 -0
- package/extensions/effort/tiers.ts +146 -0
- package/extensions/effort/types.ts +105 -0
- package/extensions/lib/git-state.ts +227 -0
- package/extensions/lib/local-models.ts +157 -0
- package/extensions/lib/model-preferences.ts +51 -0
- package/extensions/lib/model-routing.ts +720 -0
- package/extensions/lib/operator-fallback.ts +205 -0
- package/extensions/lib/operator-profile.ts +360 -0
- package/extensions/lib/slash-command-bridge.ts +253 -0
- package/extensions/lib/typebox-helpers.ts +16 -0
- package/extensions/local-inference/index.ts +727 -0
- package/extensions/mcp-bridge/README.md +220 -0
- package/extensions/mcp-bridge/index.ts +951 -0
- package/extensions/mcp-bridge/lib.ts +365 -0
- package/extensions/mcp-bridge/mcp.json +3 -0
- package/extensions/mcp-bridge/package.json +11 -0
- package/extensions/model-budget.ts +752 -0
- package/extensions/offline-driver.ts +403 -0
- package/extensions/openspec/archive-gate.ts +164 -0
- package/extensions/openspec/branch-cleanup.ts +64 -0
- package/extensions/openspec/dashboard-state.ts +50 -0
- package/extensions/openspec/index.ts +1917 -0
- package/extensions/openspec/lifecycle-emitter.ts +65 -0
- package/extensions/openspec/lifecycle-files.ts +70 -0
- package/extensions/openspec/lifecycle.ts +50 -0
- package/extensions/openspec/reconcile.ts +187 -0
- package/extensions/openspec/spec.ts +1385 -0
- package/extensions/openspec/types.ts +98 -0
- package/extensions/project-memory/DESIGN-global-mind.md +198 -0
- package/extensions/project-memory/README.md +202 -0
- package/extensions/project-memory/api-types.ts +382 -0
- package/extensions/project-memory/compaction-policy.ts +29 -0
- package/extensions/project-memory/core.ts +164 -0
- package/extensions/project-memory/embeddings.ts +230 -0
- package/extensions/project-memory/extraction-v2.ts +861 -0
- package/extensions/project-memory/factstore.ts +2177 -0
- package/extensions/project-memory/index.ts +3459 -0
- package/extensions/project-memory/injection-metrics.ts +91 -0
- package/extensions/project-memory/jsonl-io.ts +12 -0
- package/extensions/project-memory/lifecycle.ts +331 -0
- package/extensions/project-memory/migration.ts +293 -0
- package/extensions/project-memory/package.json +9 -0
- package/extensions/project-memory/sci-renderers.ts +7 -0
- package/extensions/project-memory/template.ts +103 -0
- package/extensions/project-memory/triggers.ts +52 -0
- package/extensions/project-memory/types.ts +102 -0
- package/extensions/render/composition/fonts/Inter-Bold.ttf +0 -0
- package/extensions/render/composition/fonts/Inter-Regular.ttf +0 -0
- package/extensions/render/composition/fonts/Tomorrow-Bold.ttf +0 -0
- package/extensions/render/composition/fonts/Tomorrow-Regular.ttf +0 -0
- package/extensions/render/composition/package-lock.json +534 -0
- package/extensions/render/composition/package.json +22 -0
- package/extensions/render/composition/render.mjs +246 -0
- package/extensions/render/composition/test-comp.tsx +87 -0
- package/extensions/render/composition/types.ts +24 -0
- package/extensions/render/excalidraw/UPSTREAM.md +81 -0
- package/extensions/render/excalidraw/elements.ts +764 -0
- package/extensions/render/excalidraw/index.ts +66 -0
- package/extensions/render/excalidraw/types.ts +223 -0
- package/extensions/render/excalidraw-renderer/pyproject.toml +8 -0
- package/extensions/render/excalidraw-renderer/render_excalidraw.py +182 -0
- package/extensions/render/excalidraw-renderer/render_template.html +59 -0
- package/extensions/render/index.ts +830 -0
- package/extensions/render/native-diagrams/index.ts +57 -0
- package/extensions/render/native-diagrams/motifs.ts +542 -0
- package/extensions/render/native-diagrams/raster.ts +8 -0
- package/extensions/render/native-diagrams/scene.ts +75 -0
- package/extensions/render/native-diagrams/spec.ts +204 -0
- package/extensions/render/native-diagrams/svg.ts +116 -0
- package/extensions/sci-ui.ts +304 -0
- package/extensions/session-log.ts +174 -0
- package/extensions/shared-state.ts +146 -0
- package/extensions/spinner-verbs.ts +91 -0
- package/extensions/style.ts +281 -0
- package/extensions/terminal-title.ts +191 -0
- package/extensions/tool-profile/index.ts +291 -0
- package/extensions/tool-profile/profiles.ts +290 -0
- package/extensions/types.d.ts +9 -0
- package/extensions/vault/index.ts +185 -0
- package/extensions/version-check.ts +90 -0
- package/extensions/view/index.ts +859 -0
- package/extensions/view/uri-resolver.ts +148 -0
- package/extensions/web-search/index.ts +182 -0
- package/extensions/web-search/providers.ts +121 -0
- package/extensions/web-ui/index.ts +110 -0
- package/extensions/web-ui/server.ts +265 -0
- package/extensions/web-ui/state.ts +462 -0
- package/extensions/web-ui/static/index.html +145 -0
- package/extensions/web-ui/types.ts +284 -0
- package/package.json +76 -0
- package/prompts/init.md +75 -0
- package/prompts/new-repo.md +54 -0
- package/prompts/oci-login.md +56 -0
- package/prompts/status.md +50 -0
- package/settings.json +4 -0
- package/skills/cleave/SKILL.md +218 -0
- package/skills/git/SKILL.md +209 -0
- package/skills/git/_reference/ci-validation.md +204 -0
- package/skills/oci/SKILL.md +338 -0
- package/skills/openspec/SKILL.md +346 -0
- package/skills/pi-extensions/SKILL.md +191 -0
- package/skills/pi-tui/SKILL.md +517 -0
- package/skills/python/SKILL.md +189 -0
- package/skills/rust/SKILL.md +268 -0
- package/skills/security/SKILL.md +206 -0
- package/skills/style/SKILL.md +264 -0
- package/skills/typescript/SKILL.md +225 -0
- package/skills/vault/SKILL.md +102 -0
- package/themes/alpharius-legacy.json +85 -0
- package/themes/alpharius.conf +59 -0
- package/themes/alpharius.json +88 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* render.mjs — CLI entry point for the composition render pipeline.
|
|
4
|
+
* Browser-free React → still/GIF/MP4 using Satori + resvg + gifenc.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* node render.mjs --composition <path> --mode still|gif|mp4
|
|
8
|
+
* [--frame <n>] [--width <px>] [--height <px>] [--fps <n>]
|
|
9
|
+
* [--duration <frames>] [--output <path>] [--props <json>]
|
|
10
|
+
*
|
|
11
|
+
* All output is written to stdout as JSON: {ok:true,...} or {ok:false,error}.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { readFileSync, writeFileSync, mkdirSync, readdirSync } from 'node:fs';
|
|
15
|
+
import { resolve, dirname, extname, basename } from 'node:path';
|
|
16
|
+
import { tmpdir } from 'node:os';
|
|
17
|
+
import { spawnSync } from 'node:child_process';
|
|
18
|
+
import { fileURLToPath } from 'node:url';
|
|
19
|
+
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Arg parsing
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
function parseArgs(argv) {
|
|
25
|
+
const args = {};
|
|
26
|
+
for (let i = 0; i < argv.length; i++) {
|
|
27
|
+
const arg = argv[i];
|
|
28
|
+
if (arg.startsWith('--')) {
|
|
29
|
+
const key = arg.slice(2);
|
|
30
|
+
const val = argv[i + 1] && !argv[i + 1].startsWith('--') ? argv[++i] : 'true';
|
|
31
|
+
args[key] = val;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return args;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const args = parseArgs(process.argv.slice(2));
|
|
38
|
+
|
|
39
|
+
const compositionPath = args.composition;
|
|
40
|
+
const mode = (args.mode || 'still').toLowerCase();
|
|
41
|
+
const startFrame = parseInt(args.frame ?? '0', 10);
|
|
42
|
+
const width = parseInt(args.width ?? '1920', 10);
|
|
43
|
+
const height = parseInt(args.height ?? '1080', 10);
|
|
44
|
+
const fps = parseInt(args.fps ?? '30', 10);
|
|
45
|
+
const duration = parseInt(args.duration ?? '1', 10); // frames
|
|
46
|
+
const outputPath = args.output ?? './out.png';
|
|
47
|
+
let userProps = {};
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
if (args.props) userProps = JSON.parse(args.props);
|
|
51
|
+
} catch {
|
|
52
|
+
fail('--props must be valid JSON');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
// Helpers
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
|
|
59
|
+
function ok(extra) {
|
|
60
|
+
console.log(JSON.stringify({ ok: true, ...extra }));
|
|
61
|
+
process.exit(0);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function fail(error) {
|
|
65
|
+
console.log(JSON.stringify({ ok: false, error: String(error) }));
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (!compositionPath) fail('--composition is required');
|
|
70
|
+
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
// Font loading
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
|
|
75
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
76
|
+
const __dirname = dirname(__filename);
|
|
77
|
+
const fontsDir = resolve(__dirname, 'fonts');
|
|
78
|
+
|
|
79
|
+
function loadFonts() {
|
|
80
|
+
const fonts = [];
|
|
81
|
+
let entries;
|
|
82
|
+
try {
|
|
83
|
+
entries = readdirSync(fontsDir);
|
|
84
|
+
} catch {
|
|
85
|
+
return fonts; // no fonts dir — satori will use system defaults
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
for (const file of entries) {
|
|
89
|
+
if (extname(file).toLowerCase() !== '.ttf') continue;
|
|
90
|
+
const namePart = basename(file, '.ttf'); // e.g. "Inter-Bold"
|
|
91
|
+
const [family, weightStr] = namePart.split('-');
|
|
92
|
+
const weightMap = {
|
|
93
|
+
thin: 100, extralight: 200, light: 300,
|
|
94
|
+
regular: 400, medium: 500, semibold: 600,
|
|
95
|
+
bold: 700, extrabold: 800, black: 900,
|
|
96
|
+
};
|
|
97
|
+
const weight = weightMap[(weightStr ?? 'regular').toLowerCase()] ?? 400;
|
|
98
|
+
const style = namePart.toLowerCase().includes('italic') ? 'italic' : 'normal';
|
|
99
|
+
try {
|
|
100
|
+
const data = readFileSync(resolve(fontsDir, file));
|
|
101
|
+
fonts.push({ name: family, data: data.buffer, weight, style });
|
|
102
|
+
} catch {
|
|
103
|
+
// skip unreadable fonts
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return fonts;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
// Import user composition via jiti (handles .tsx / .ts / .js)
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
|
|
113
|
+
async function loadComposition(path) {
|
|
114
|
+
const { createJiti } = await import('jiti');
|
|
115
|
+
const jiti = createJiti(import.meta.url, { jsx: true });
|
|
116
|
+
const absPath = resolve(process.cwd(), path);
|
|
117
|
+
const mod = await jiti.import(absPath);
|
|
118
|
+
// Support both default export and named Component export
|
|
119
|
+
const Component = mod?.default ?? mod?.Component;
|
|
120
|
+
if (typeof Component !== 'function') {
|
|
121
|
+
throw new Error(`Composition at "${path}" must have a default export that is a React component function`);
|
|
122
|
+
}
|
|
123
|
+
return Component;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
// Render a single frame → raw RGBA Uint8Array
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
|
|
130
|
+
async function renderFrame(Component, frameIndex, fonts, React, satori, Resvg) {
|
|
131
|
+
const frameProps = {
|
|
132
|
+
frame: frameIndex,
|
|
133
|
+
fps,
|
|
134
|
+
durationInFrames: duration,
|
|
135
|
+
width,
|
|
136
|
+
height,
|
|
137
|
+
props: userProps,
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
// Build the React element (no JSX — render.mjs is plain .mjs)
|
|
141
|
+
const element = React.createElement(Component, frameProps);
|
|
142
|
+
|
|
143
|
+
const svg = await satori(element, { width, height, fonts });
|
|
144
|
+
|
|
145
|
+
const resvg = new Resvg(svg, {
|
|
146
|
+
fitTo: { mode: 'width', value: width },
|
|
147
|
+
});
|
|
148
|
+
const rendered = resvg.render();
|
|
149
|
+
return rendered; // ResvgRenderImage with .asPng() and .pixels (RGBA Uint8Array)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ---------------------------------------------------------------------------
|
|
153
|
+
// Main
|
|
154
|
+
// ---------------------------------------------------------------------------
|
|
155
|
+
|
|
156
|
+
async function main() {
|
|
157
|
+
const fonts = loadFonts();
|
|
158
|
+
const Component = await loadComposition(compositionPath);
|
|
159
|
+
|
|
160
|
+
// Lazy-import heavy deps after validation
|
|
161
|
+
const React = (await import('react')).default;
|
|
162
|
+
const { default: satori } = await import('satori');
|
|
163
|
+
const { Resvg } = await import('@resvg/resvg-js');
|
|
164
|
+
|
|
165
|
+
// -------------------------------------------------------------------------
|
|
166
|
+
// STILL mode
|
|
167
|
+
// -------------------------------------------------------------------------
|
|
168
|
+
if (mode === 'still') {
|
|
169
|
+
const rendered = await renderFrame(Component, startFrame, fonts, React, satori, Resvg);
|
|
170
|
+
const png = rendered.asPng();
|
|
171
|
+
const outPath = resolve(process.cwd(), outputPath);
|
|
172
|
+
writeFileSync(outPath, png);
|
|
173
|
+
ok({ path: outPath, sizeBytes: png.byteLength });
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// -------------------------------------------------------------------------
|
|
177
|
+
// GIF mode
|
|
178
|
+
// -------------------------------------------------------------------------
|
|
179
|
+
else if (mode === 'gif') {
|
|
180
|
+
const gifencMod = await import('gifenc');
|
|
181
|
+
const gifencExports = gifencMod.default ?? gifencMod['module.exports'] ?? gifencMod;
|
|
182
|
+
const { GIFEncoder, quantize, applyPalette } = gifencExports;
|
|
183
|
+
|
|
184
|
+
const gif = GIFEncoder();
|
|
185
|
+
const delayMs = Math.round(1000 / fps);
|
|
186
|
+
|
|
187
|
+
for (let f = 0; f < duration; f++) {
|
|
188
|
+
const rendered = await renderFrame(Component, f, fonts, React, satori, Resvg);
|
|
189
|
+
// pixels is a Uint8Array of RGBA bytes, width×height×4
|
|
190
|
+
const pixels = new Uint8Array(rendered.pixels);
|
|
191
|
+
const palette = quantize(pixels, 256, { format: 'rgba4444' });
|
|
192
|
+
const index = applyPalette(pixels, palette, 'rgba4444');
|
|
193
|
+
gif.writeFrame(index, width, height, { palette, delay: delayMs });
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
gif.finish();
|
|
197
|
+
const data = gif.bytesView();
|
|
198
|
+
const outPath = resolve(process.cwd(), outputPath);
|
|
199
|
+
writeFileSync(outPath, data);
|
|
200
|
+
ok({ path: outPath, sizeBytes: data.byteLength });
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// -------------------------------------------------------------------------
|
|
204
|
+
// MP4 mode
|
|
205
|
+
// -------------------------------------------------------------------------
|
|
206
|
+
else if (mode === 'mp4') {
|
|
207
|
+
const tmp = resolve(tmpdir(), `pi-render-${Date.now()}`);
|
|
208
|
+
mkdirSync(tmp, { recursive: true });
|
|
209
|
+
|
|
210
|
+
const padLen = String(duration - 1).length;
|
|
211
|
+
|
|
212
|
+
for (let f = 0; f < duration; f++) {
|
|
213
|
+
const rendered = await renderFrame(Component, f, fonts, React, satori, Resvg);
|
|
214
|
+
const png = rendered.asPng();
|
|
215
|
+
const frameName = String(f).padStart(padLen, '0') + '.png';
|
|
216
|
+
writeFileSync(resolve(tmp, frameName), png);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const outPath = resolve(process.cwd(), outputPath);
|
|
220
|
+
const result = spawnSync('ffmpeg', [
|
|
221
|
+
'-y',
|
|
222
|
+
'-framerate', String(fps),
|
|
223
|
+
'-i', resolve(tmp, `%0${padLen}d.png`),
|
|
224
|
+
'-c:v', 'libx264',
|
|
225
|
+
'-pix_fmt', 'yuv420p',
|
|
226
|
+
'-movflags', '+faststart',
|
|
227
|
+
outPath,
|
|
228
|
+
], { stdio: 'pipe' });
|
|
229
|
+
|
|
230
|
+
if (result.status !== 0) {
|
|
231
|
+
const stderr = result.stderr?.toString() ?? 'unknown ffmpeg error';
|
|
232
|
+
fail(`ffmpeg failed: ${stderr}`);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
ok({ path: outPath, sizeBytes: readFileSync(outPath).byteLength });
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// -------------------------------------------------------------------------
|
|
239
|
+
// Unknown mode
|
|
240
|
+
// -------------------------------------------------------------------------
|
|
241
|
+
else {
|
|
242
|
+
fail(`Unknown --mode "${mode}". Valid values: still, gif, mp4`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
main().catch((err) => fail(err?.message ?? String(err)));
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* test-comp.tsx — minimal smoke-test composition.
|
|
3
|
+
*
|
|
4
|
+
* Renders a progress bar animating from 0% → 100% over durationInFrames.
|
|
5
|
+
* Uses Verdant palette: bg #0a0a0a, teal #7ec8c8, text #e0e0e0.
|
|
6
|
+
* Props-based (no Remotion hooks).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import React from 'react';
|
|
10
|
+
import type { FrameProps } from './types.js';
|
|
11
|
+
|
|
12
|
+
export default function TestComp({
|
|
13
|
+
frame,
|
|
14
|
+
durationInFrames,
|
|
15
|
+
width,
|
|
16
|
+
height,
|
|
17
|
+
}: FrameProps): React.ReactElement {
|
|
18
|
+
const progress = durationInFrames > 1 ? frame / (durationInFrames - 1) : 1;
|
|
19
|
+
const pct = Math.round(progress * 100);
|
|
20
|
+
const barWidth = Math.round(progress * (width - 80));
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div
|
|
24
|
+
style={{
|
|
25
|
+
display: 'flex',
|
|
26
|
+
flexDirection: 'column',
|
|
27
|
+
alignItems: 'center',
|
|
28
|
+
justifyContent: 'center',
|
|
29
|
+
width,
|
|
30
|
+
height,
|
|
31
|
+
background: '#0a0a0a',
|
|
32
|
+
fontFamily: 'Inter, sans-serif',
|
|
33
|
+
color: '#e0e0e0',
|
|
34
|
+
}}
|
|
35
|
+
>
|
|
36
|
+
{/* Title */}
|
|
37
|
+
<div
|
|
38
|
+
style={{
|
|
39
|
+
display: 'flex',
|
|
40
|
+
fontSize: 32,
|
|
41
|
+
fontWeight: 700,
|
|
42
|
+
marginBottom: 40,
|
|
43
|
+
color: '#7ec8c8',
|
|
44
|
+
letterSpacing: 2,
|
|
45
|
+
}}
|
|
46
|
+
>
|
|
47
|
+
pi-kit render pipeline
|
|
48
|
+
</div>
|
|
49
|
+
|
|
50
|
+
{/* Bar track */}
|
|
51
|
+
<div
|
|
52
|
+
style={{
|
|
53
|
+
display: 'flex',
|
|
54
|
+
width: width - 80,
|
|
55
|
+
height: 24,
|
|
56
|
+
background: '#1a1a1a',
|
|
57
|
+
borderRadius: 12,
|
|
58
|
+
overflow: 'hidden',
|
|
59
|
+
}}
|
|
60
|
+
>
|
|
61
|
+
{/* Bar fill */}
|
|
62
|
+
<div
|
|
63
|
+
style={{
|
|
64
|
+
display: 'flex',
|
|
65
|
+
width: barWidth,
|
|
66
|
+
height: 24,
|
|
67
|
+
background: '#7ec8c8',
|
|
68
|
+
borderRadius: 12,
|
|
69
|
+
}}
|
|
70
|
+
/>
|
|
71
|
+
</div>
|
|
72
|
+
|
|
73
|
+
{/* Percentage label */}
|
|
74
|
+
<div
|
|
75
|
+
style={{
|
|
76
|
+
display: 'flex',
|
|
77
|
+
marginTop: 20,
|
|
78
|
+
fontSize: 24,
|
|
79
|
+
color: '#e0e0e0',
|
|
80
|
+
fontVariantNumeric: 'tabular-nums',
|
|
81
|
+
}}
|
|
82
|
+
>
|
|
83
|
+
{pct}%
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core types for the composition render pipeline.
|
|
3
|
+
* Browser-free React → video/image renderer using Satori + resvg + gifenc.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Props injected into every frame component.
|
|
8
|
+
* Mirrors the Remotion useCurrentFrame() / useVideoConfig() pattern
|
|
9
|
+
* but passed directly as props instead of via hooks.
|
|
10
|
+
*/
|
|
11
|
+
export interface FrameProps {
|
|
12
|
+
/** Current frame index (0-based) */
|
|
13
|
+
frame: number;
|
|
14
|
+
/** Frames per second */
|
|
15
|
+
fps: number;
|
|
16
|
+
/** Total number of frames in the composition */
|
|
17
|
+
durationInFrames: number;
|
|
18
|
+
/** Canvas width in pixels */
|
|
19
|
+
width: number;
|
|
20
|
+
/** Canvas height in pixels */
|
|
21
|
+
height: number;
|
|
22
|
+
/** User-defined props passed through from the render tool */
|
|
23
|
+
props?: Record<string, unknown>;
|
|
24
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Excalidraw Factory Layer — Upstream Tracking
|
|
2
|
+
|
|
3
|
+
## Source
|
|
4
|
+
|
|
5
|
+
Vendored from [`@swiftlysingh/excalidraw-cli`](https://github.com/swiftlysingh/excalidraw-cli)
|
|
6
|
+
- **Version at vendor time:** 1.1.0
|
|
7
|
+
- **Commit:** `e1338e8342dfa740119aac5e83aab71673c70c68` (main, 2026-03-03)
|
|
8
|
+
- **License:** MIT
|
|
9
|
+
- **npm:** `@swiftlysingh/excalidraw-cli@1.1.0`
|
|
10
|
+
|
|
11
|
+
## What We Vendored
|
|
12
|
+
|
|
13
|
+
| Upstream File | Local File | Changes |
|
|
14
|
+
|---|---|---|
|
|
15
|
+
| `src/types/excalidraw.ts` | `types.ts` | Minimal — removed unused `ExcalidrawImage`, `ExcalidrawFileData`; added our `SemanticPurpose` and palette types |
|
|
16
|
+
| `src/factory/element-factory.ts` | `elements.ts` | Merged node-factory, connection-factory, text-factory into single module; replaced `nanoid` with built-in `crypto.randomUUID`; added semantic palette integration |
|
|
17
|
+
| `src/factory/node-factory.ts` | (merged into `elements.ts`) | — |
|
|
18
|
+
| `src/factory/connection-factory.ts` | (merged into `elements.ts`) | — |
|
|
19
|
+
| `src/factory/text-factory.ts` | (merged into `elements.ts`) | — |
|
|
20
|
+
|
|
21
|
+
### Files NOT vendored (unnecessary for our use case)
|
|
22
|
+
|
|
23
|
+
- `src/cli.ts` — CLI entry point
|
|
24
|
+
- `src/parser/dsl-parser.ts` — DSL parser (`(Start) -> [Process]` syntax)
|
|
25
|
+
- `src/parser/dot-parser.ts` — Graphviz DOT parser
|
|
26
|
+
- `src/parser/json-parser.ts` — JSON input parser
|
|
27
|
+
- `src/layout/elk-layout.ts` — ELK.js auto-layout (we do manual positioning)
|
|
28
|
+
- `src/layout/arrow-router.ts` — Arrow routing (simplified inline)
|
|
29
|
+
- `src/generator/excalidraw-generator.ts` — Full pipeline orchestrator
|
|
30
|
+
- `src/factory/image-factory.ts` — Image element support
|
|
31
|
+
|
|
32
|
+
## How to Sync with Upstream
|
|
33
|
+
|
|
34
|
+
When upstream updates (or if something breaks):
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# 1. Check what changed
|
|
38
|
+
cd /tmp && git clone https://github.com/swiftlysingh/excalidraw-cli.git
|
|
39
|
+
diff -u excalidraw-cli/src/types/excalidraw.ts \
|
|
40
|
+
~/.pi/agent/git/github.com/cwilson613/pi-kit/extensions/render/excalidraw/types.ts
|
|
41
|
+
|
|
42
|
+
# 2. Key files to diff:
|
|
43
|
+
# src/types/excalidraw.ts → types.ts
|
|
44
|
+
# src/factory/element-factory.ts → elements.ts (base element creation)
|
|
45
|
+
# src/factory/node-factory.ts → elements.ts (shape creation)
|
|
46
|
+
# src/factory/connection-factory.ts → elements.ts (arrow creation)
|
|
47
|
+
# src/factory/text-factory.ts → elements.ts (text creation)
|
|
48
|
+
|
|
49
|
+
# 3. Watch for:
|
|
50
|
+
# - New required properties in ExcalidrawElementBase
|
|
51
|
+
# - Changed binding format (ExcalidrawArrowBinding)
|
|
52
|
+
# - New element types
|
|
53
|
+
# - Changed default values
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Excalidraw Format Version Risks
|
|
57
|
+
|
|
58
|
+
The `.excalidraw` JSON format is defined by the Excalidraw project, not by excalidraw-cli.
|
|
59
|
+
Key things that could break:
|
|
60
|
+
|
|
61
|
+
1. **New required fields on elements** — Excalidraw sometimes adds mandatory fields.
|
|
62
|
+
Check: https://github.com/excalidraw/excalidraw/blob/master/packages/element/src/types.ts
|
|
63
|
+
2. **Binding format changes** — Arrow bindings (`startBinding`/`endBinding`) have changed before.
|
|
64
|
+
The `mode: "orbit" | "point"` and `fixedPoint: [number, number]` format is current as of v0.18.
|
|
65
|
+
3. **Version field** — Currently `"version": 2`. If they bump to 3, older files may not load.
|
|
66
|
+
4. **`index` field** — Fractional indexing for element ordering. Currently alphabetic (`"a"`, `"ab"`).
|
|
67
|
+
|
|
68
|
+
## Also Relevant
|
|
69
|
+
|
|
70
|
+
- **Render pipeline** (`references/excalidraw/`): Uses `@excalidraw/excalidraw` from esm.sh CDN.
|
|
71
|
+
If the CDN version updates and introduces breaking changes to `exportToSvg`, the renderer breaks.
|
|
72
|
+
Pin version in `render_template.html` import URL to mitigate.
|
|
73
|
+
- **Official skeleton API**: `@excalidraw/element` has `convertToExcalidrawElements()` but is
|
|
74
|
+
prerelease-only with a hard DOM canvas dependency. If they ship a stable Node-compatible version,
|
|
75
|
+
consider switching from vendored factories to the official API.
|
|
76
|
+
|
|
77
|
+
## Last Synced
|
|
78
|
+
|
|
79
|
+
- **Date:** 2026-03-03
|
|
80
|
+
- **Upstream version:** 1.1.0
|
|
81
|
+
- **By:** cwilson (omegon session)
|