@flatkit/compiler 0.24.0 → 0.26.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/dist/analysis.js +1 -1
- package/dist/{chunk-7KBWT2PA.js → chunk-IYPM3CU5.js} +50 -2
- package/dist/{chunk-7KBWT2PA.js.map → chunk-IYPM3CU5.js.map} +1 -1
- package/dist/{chunk-5WQFNGZQ.js → chunk-RNQ2IQLD.js} +13 -10
- package/dist/chunk-RNQ2IQLD.js.map +1 -0
- package/dist/cli/flatc.js +2 -2
- package/dist/index.js +2 -2
- package/package.json +11 -5
- package/prompts/flatink-core.md +49 -46
- package/prompts/flatink-lite.md +20 -17
- package/prompts/role-asset-creator.md +11 -11
- package/prompts/role-coder.md +27 -28
- package/prompts/role-motion-designer.md +12 -12
- package/dist/chunk-5WQFNGZQ.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/flatc.ts"],"sourcesContent":["// ─────────────────────────────────────────────────────────────────────────────\n// flatc — the \"modern SWF\" command-line compiler.\n//\n// The VSCode-first flow: you write a `.flatink` program (composition + logic, DSL)\n// + `.flat` asset libs (visuals, exported by the editor) + media, then:\n//\n// flatc game.flatink hero.flat decor.flat -o game.flatpack\n//\n// → a single `.flatpack` (the baked Doc as JSON) that the player runs. Media referenced\n// by `asset \"id\" \"path\" kind` are EMBEDDED (paths relative to the program).\n// ─────────────────────────────────────────────────────────────────────────────\nimport { readFileSync, writeFileSync, existsSync, readdirSync, watch, mkdirSync, copyFileSync } from 'node:fs'\nimport { resolve, dirname, basename, extname, join, relative, isAbsolute } from 'node:path'\nimport { compileFlatpack, packToJSON, type MediaMap } from '../compile'\nimport { parseProgramFull, parseFlatLib } from '@flatkit/engine/flatFormat'\nimport { hasPackage } from '@flatkit/engine/stdlib'\nimport { parseUnits } from '@flatkit/engine/dsl'\nimport { sanitizeDoc } from '@flatkit/engine/validateDoc'\nimport { unitsToFunctions } from '@flatkit/engine/scriptDoc'\nimport { containerBBox, containerBBoxUnion } from '@flatkit/engine/groups'\nimport { isInstance, isGroup } from '@flatkit/engine/layers'\nimport { IDENTITY } from '@flatkit/engine/transform'\nimport { lintDocReport, docHasErrors } from '../programDoc'\nimport { formatDiagnostics, programDiagnostics } from '../check'\nimport { playHeadless, type Gesture } from '@flatkit/player/debug'\nimport type { FuncDef } from '@flatkit/engine/actions'\nimport type { Doc, Instance, Item, Layer, SymbolDef } from '@flatkit/types'\n\nconst MIME: Record<string, string> = {\n '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.gif': 'image/gif',\n '.webp': 'image/webp', '.svg': 'image/svg+xml', '.avif': 'image/avif',\n '.mp3': 'audio/mpeg', '.wav': 'audio/wav', '.ogg': 'audio/ogg', '.m4a': 'audio/mp4',\n // Fonts (RFC 8081 media types) — matches the editor's import (`font/woff2` default, FontFace API).\n '.woff2': 'font/woff2', '.woff': 'font/woff', '.ttf': 'font/ttf', '.otf': 'font/otf',\n // NB: video (.mp4/.webm/…) intentionally omitted — no video runtime in the editor or player yet.\n}\nconst mimeFor = (path: string): string => MIME[extname(path).toLowerCase()] ?? 'application/octet-stream'\n\n/**\n * True if `target` resolves to a file inside `baseDir` (or baseDir itself). Guards against a hostile\n * program escaping its folder via `../` in a media path or a `use` package name — compiling an untrusted\n * `.flatink` must never read (and embed) arbitrary host files.\n */\nfunction isWithin(baseDir: string, target: string): boolean {\n const rel = relative(baseDir, target)\n return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel))\n}\n\nfunction printHelp(): void {\n process.stdout.write(`flatc — compile a FlatInk program into a .flatpack\n\nUsage:\n flatc <program.flatink> [assets.flat …] [-o output.flatpack]\n flatc <program.flatink> --watch\n flatc --play <program.flatink | scene.flatpack> --script <gestures.json>\n flatc --render <program.flatink | scene.flatpack> -o out.png [--frame N] [--at k=v[,k2=v2]] [--scale S]\n flatc --preview <library.flat> [--symbol NAME] [-o out.flatpack | --render -o out.png]\n flatc <library.flat> [more.flat …] --check\n\n program.flatink the program (composition + logic, text DSL)\n assets.flat visual asset libs (default: every .flat in the program's folder)\n -o, --out output file (default: <program>.flatpack — the CANONICAL name, JSON inside)\n --assets MODE media baking: 'inline' (default, base64 in the .flatpack) or 'external'\n (sidecar <out>.assets/ folder; asset.data = relative key — serve the folder and\n play with sameOriginAssetResolver(<flatpackUrl>))\n --check semantic lint only (no .flatpack); exits ≠0 on ERROR (warnings do not stop). Lints a\n program .flatink OR an asset library .flat (per-symbol; several .flat are merged)\n --no-libs do NOT auto-discover the .flat files sitting next to the program (use it in a working\n folder, where a neighbouring scratch file is not a dependency)\n --watch recompile on every change in the folder (agent → player loop)\n --play run the file WITHOUT a canvas, replay --script and print { sends, vars } (JSON)\n --trace (with --play) HUMAN-READABLE log per gesture: emitted sends + variable diff (debug)\n --script <f> JSON gesture script: [{ \"type\": \"down|move|up|cancel\", \"x\", \"y\" }, { \"type\": \"set\", \"name\", \"value\" }, { \"type\": \"wait\", \"frames\": N }, { \"type\": \"wheel\", \"dy\": N }]\n semantic (by NAME, the engine resolves coords): { \"type\": \"drag\", \"source\", \"target\" } · { \"type\": \"tap\", \"target\" }\n · { \"type\": \"scratch\", \"target\" } (sweeps a reveal zone) · { \"type\": \"connect\", \"source\", \"target\" } (pulls a link wire)\n { \"type\": \"key\", \"name\": \"ArrowRight\", \"frames\": N } holds a key (keys.<name> = 1) N steps, then releases it\n \"wait\" lets the simulation run N fixed steps (60 Hz): \"every frame\" + playhead advance like in real playback\n \"expect\" self-verifies: { \"type\": \"expect\", \"sends\": [\"done\"], \"vars\": { \"score\": 3 } } → exits ≠0 on mismatch\n (sends = sequence of names emitted SINCE the last expect; vars = current state). Great in CI.\n --render render a PNG IMAGE (headless skia): see what we draw (positioning)\n --preview wrap ONE symbol of a <library.flat> into a playable Doc (a single centered instance on an\n auto-sized stage) → a .flatpack to drop in the browser player, or a PNG with --render.\n No wrapper .flatink to author by hand. Output defaults to <library>.<symbol>.flatpack/.png\n --symbol NAME (with --preview) which symbol to wrap (default: the first in the lib)\n --bbox all|frame0 (with --preview) auto-size to the UNION over all frames (default 'all', no clipping of\n drifting/rotating/growing motion) or just frame 0 ('frame0', the old behavior)\n --pad N (with --preview) padding in px around the symbol's bounds (default 24)\n --set p=v[,p2=v2] (with --preview) set the symbol's exposed params; a state param takes a state NAME\n or a number (e.g. --set door=open), others take a number → baked into the preview\n --frame N (with --render) target frame (default 0)\n --at k=v[,k2=v2] (with --render) force variables → capture a given state (e.g. a step of an escape)\n --steps N (with --render) run N fixed sim steps (60 Hz, every-frame) BEFORE capture → see a\n stateful act unfold without forcing every derived variable by hand in --at\n --scale S|auto (with --render) resolution factor (default 2); 'auto' picks one from the canvas size\n (enlarges small/thin assets so fine filaments stay legible, large assets stay 1x)\n -h, --help this help\n\nMedia referenced by 'asset \"id\" \"path\" kind' are embedded (paths relative to the program).\n`)\n}\n\n/** How media is baked: `inline` = base64 data-URI in the .flatpack; `external` = relative key + sidecar files. */\ntype AssetMode = 'inline' | 'external'\ntype MediaCopy = { src: string; key: string } // external mode: source file → relative key (forward slashes)\ntype BuildResult = { doc: Doc; flatLibs: number; packages: number; media: number; mediaCopies: MediaCopy[]; src: string }\n\n/**\n * Reads a `.flatink`, resolves libs/packages/media, compiles → standalone Doc. Throws on compile error.\n * `assetMode`: `inline` embeds each media as a base64 data-URI (default); `external` keeps `asset.data` as a\n * relative key (`<assetsDir>/<path>`) and returns the files to copy beside the .flatpack (no base64 bloat).\n */\nfunction buildDocFromProgram(programPath: string, explicitFlats: string[] = [], assetMode: AssetMode = 'inline', assetsDir = '', noLibs = false): BuildResult {\n const baseDir = dirname(programPath)\n const programSrc = readFileSync(programPath, 'utf8')\n const prog = parseProgramFull(programSrc)\n\n // .flat libs: explicit if provided, otherwise auto-discover the .flat files in the program's folder\n // (`--no-libs` opts out — in a working folder, a neighbouring scratch file is not a dependency).\n const flatPaths = new Set<string>(explicitFlats.map((p) => resolve(p)))\n if (!flatPaths.size && !noLibs) for (const f of readdirSync(baseDir).filter((f) => f.endsWith('.flat'))) flatPaths.add(join(baseDir, f))\n\n // PACKAGES: non-stdlib `use \"x\"` → local files inlined (x.flatink = functions, x.flat = symbols).\n const pkgFunctions: FuncDef[] = []\n const localResolved = new Set<string>()\n for (const name of prog.imports ?? []) {\n if (hasPackage(name)) continue // stdlib → left as a reference\n const fink = join(baseDir, name + '.flatink')\n const fflat = join(baseDir, name + '.flat')\n if (!isWithin(baseDir, fink) || !isWithin(baseDir, fflat)) { process.stderr.write(`flatc: package outside the program folder (ignored): \"${name}\"\\n`); continue }\n let found = false\n if (existsSync(fink)) { for (const f of unitsToFunctions(parseUnits(readFileSync(fink, 'utf8')).units)) pkgFunctions.push(f, { ...f, name: `${name}.${f.name}` }); found = true }\n if (existsSync(fflat)) { flatPaths.add(fflat); found = true }\n if (found) localResolved.add(name)\n else process.stderr.write(`flatc: package not found: \"${name}\" (neither stdlib, nor ${name}.flatink / ${name}.flat)\\n`)\n }\n // Parse each lib HERE so a failure names the FILE. Libs are auto-discovered from the folder, so the one\n // that fails is often a neighbour the author never mentioned — a bare \"compile error: …\" sent them hunting\n // through their own program for a fault that was never in it.\n const assetSrcs = [...flatPaths].map((p) => {\n const src = readFileSync(p, 'utf8')\n try { parseFlatLib(src) } catch (e) { throw new Error(`${basename(p)}: ${(e as Error).message} (pass --no-libs to ignore the .flat files sitting next to the program)`, { cause: e }) }\n if (programConstructsIn(src).length) process.stderr.write(`flatc: ${basename(p)}: looks like a PROGRAM saved as .flat — loaded as a symbol library, so its scene/object blocks are ignored. Rename it to .flatink, or pass --no-libs.\\n`)\n return src\n })\n\n // Media: each `asset … \"path\" …` resolved relative to the program. `inline` → base64 data-URI baked in;\n // `external` → `asset.data` becomes a relative key and the file is copied next to the .flatpack.\n const media: MediaMap = {}\n const mediaCopies: MediaCopy[] = []\n for (const a of prog.assets ?? []) {\n const mp = resolve(baseDir, a.data)\n if (!isWithin(baseDir, mp)) { process.stderr.write(`flatc: media outside the program folder (ignored): ${a.data}\\n`); continue }\n if (!existsSync(mp)) { process.stderr.write(`flatc: missing media (ignored): ${a.data}\\n`); continue }\n const mime = mimeFor(mp)\n if (assetMode === 'external') {\n const key = `${assetsDir}/${a.data.replace(/\\\\/g, '/')}` // URL-form relative key (resolved by the host)\n media[a.data] = { mime, data: key }\n mediaCopies.push({ src: mp, key })\n } else {\n media[a.data] = { mime, data: `data:${mime};base64,${readFileSync(mp).toString('base64')}` }\n }\n }\n\n let doc = compileFlatpack(programSrc, assetSrcs, media)\n if (pkgFunctions.length) doc = { ...doc, functions: [...(doc.functions ?? []), ...pkgFunctions] }\n const stdImports = (doc.imports ?? []).filter(hasPackage)\n doc = { ...doc, imports: stdImports.length ? stdImports : undefined }\n // The source travels with the Doc: the source-level passes (`programDiagnostics`) need the author's text,\n // since a parse-level drop and an `object` block binding to nothing leave no trace in the compiled Doc.\n return { doc, flatLibs: flatPaths.size, packages: localResolved.size, media: Object.keys(media).length, mediaCopies, src: programSrc }\n}\n\n/** Compile once (write or --check). Returns the exit code. */\nfunction compileOnce(programPath: string, explicitFlats: string[], out: string, checkOnly: boolean, assetMode: AssetMode = 'inline', noLibs = false): number {\n const outPath = out ? resolve(out) : join(dirname(programPath), basename(programPath, extname(programPath)) + '.flatpack')\n // External mode: sidecar folder next to the .flatpack, e.g. `game.flatpack` → `game.assets/`.\n const assetsDir = assetMode === 'external' ? basename(outPath, extname(outPath)) + '.assets' : ''\n let built: BuildResult\n try { built = buildDocFromProgram(programPath, explicitFlats, assetMode, assetsDir, noLibs) }\n catch (e) { process.stderr.write(`flatc: compile error: ${(e as Error).message}\\n`); return 1 }\n const { doc } = built\n // The SAME pass the `checkProgram` API runs — source-level diagnostics (statements the parser dropped,\n // `object` blocks binding to nothing) merged with the semantic lint of the Doc. Shared on purpose: a\n // check that lives only in the CLI is a check an integrator has to shell out for.\n const diagnostics = programDiagnostics(doc, built.src)\n const report = formatDiagnostics(diagnostics)\n const hasErrors = diagnostics.some((d) => d.severity === 'error')\n if (checkOnly) {\n if (report) process.stderr.write(report + '\\n')\n if (hasErrors) return 1\n // Success line deliberately avoids the word \"error\" (the report on a FAILURE already prints \"error\" to\n // stderr + exits ≠0) so a `grep error` over the output can't false-positive — the real signal is the exit\n // code; on success, only warnings remain (non-blocking), surfaced as a count.\n const warnings = diagnostics.length\n process.stdout.write(`flatc: check passed ✓${warnings ? ` · ${warnings} warning(s)` : ''}\\n`)\n return 0\n }\n if (report) process.stderr.write(report + '\\n') // compile anyway: diagnostics as warnings\n writeFileSync(outPath, packToJSON(doc))\n const outDir = dirname(outPath)\n for (const c of built.mediaCopies) {\n const dest = join(outDir, ...c.key.split('/'))\n if (!isWithin(outDir, dest)) continue // defensive: never write outside the output folder\n mkdirSync(dirname(dest), { recursive: true })\n copyFileSync(c.src, dest)\n }\n const where = assetMode === 'external' ? ` (external → ${assetsDir}/)` : ''\n process.stdout.write(`flatc: ${basename(outPath)} ✓ ${doc.symbols.length} symbol(s) · ${built.flatLibs} lib(s) · ${built.packages} package(s) · ${built.media} media${where}\\n`)\n return 0\n}\n\n/** PROGRAM-only constructs found in what is supposed to be a `.flat` symbol library. `parseFlatLib` reads a\n * `.flat` as a bag of symbols and silently ignores everything else, so a whole program saved under the wrong\n * extension parsed to \"0 symbol(s)\" and passed `--check` with nothing verified — the worst possible answer\n * for tooling, since a check written against the wrong extension ALWAYS passes and looks like a safety net.\n * Both constructs are anchored at line start: only a real block matches, not a mention inside a string. */\nfunction programConstructsIn(src: string): string[] {\n const found: string[] = []\n if (/^[ \\t]*scene\\s*\\{/m.test(src)) found.push('a `scene { … }` block')\n if (/^[ \\t]*object\\s+\"/m.test(src)) found.push('`object \"…\" { … }` blocks')\n return found\n}\n\n/**\n * `--check` for a `.flat` asset LIBRARY (one or several): parse via `parseFlatLib` (NOT the program parser,\n * which would choke on `symbol`/`params`/`layer`), merge the symbols into an empty-scene Doc, and run the SAME\n * Doc lint as a program -> per-symbol diagnostics (`[<Symbol>] line:col: …`). Reuses `lintDocReport`/\n * `docHasErrors`, so every existing check (params-in-expr, undeclared color param in a paint, unknown\n * functions/objects…) applies for free, with the same `[scope] line:col: level: msg` format and exit code.\n * No `behaviorDiagnostics` (those are scene-level — a pure lib has no scene).\n */\nfunction checkFlatLibs(flatPaths: string[]): number {\n const symbols: SymbolDef[] = []\n for (const p of flatPaths) {\n let src: string\n try { src = readFileSync(p, 'utf8') } catch (e) { process.stderr.write(`flatc: cannot read: ${(e as Error).message}\\n`); return 1 }\n const disguised = programConstructsIn(src)\n if (disguised.length) { // a PROGRAM saved as `.flat` — see programConstructsIn\n process.stderr.write(`flatc: ${basename(p)}: this is a PROGRAM, not a symbol library — it contains ${disguised.join(' and ')}, which a .flat never carries and which --check therefore does NOT verify. Rename it to .flatink.\\n`)\n return 1\n }\n try { symbols.push(...parseFlatLib(src).symbols) } // ids are uid-unique across calls -> safe to merge libs\n catch (e) { process.stderr.write(`flatc: ${basename(p)}: ${(e as Error).message}\\n`); return 1 } // a malformed lib -> a clean parse error (not \"[scene] …\")\n }\n const doc: Doc = { width: 1, height: 1, timeline: { fps: 24, durationFrames: 1, tracks: [] }, variables: {}, layers: [], symbols }\n const report = lintDocReport(doc) // scene scope is empty -> only per-symbol diagnostics\n if (report) process.stderr.write(report + '\\n')\n if (docHasErrors(doc)) return 1\n // \"check passed\", NOT \"no errors\" — the word \"error\" must not appear on success (a `grep error` trap); the\n // exit code is the signal, and any remaining lines are warnings (non-blocking), reported as a count.\n const warnings = report ? report.split('\\n').filter(Boolean).length : 0\n process.stdout.write(`flatc: check passed ✓ ${symbols.length} symbol(s)${warnings ? ` · ${warnings} warning(s)` : ''}\\n`)\n return 0\n}\n\n/** Loads a Doc from a `.flatink` (compiled) or a `.flatpack`/`.flatpack.json` (already-baked JSON). */\nfunction loadDoc(filePath: string, noLibs = false): Doc {\n if (filePath.endsWith('.flatink')) return buildDocFromProgram(filePath, [], 'inline', '', noLibs).doc\n // .flatpack (canonical) or .flatpack.json (alias): untrusted JSON → normalize before use.\n return sanitizeDoc(JSON.parse(readFileSync(filePath, 'utf8')))\n}\n\n/** --play: runs the file headless, replays --script, prints { sends, vars }. */\nfunction playOnce(filePath: string, scriptPath: string, trace: boolean, noLibs = false): number {\n if (!scriptPath) { process.stderr.write('flatc: --play requires --script <gestures.json>\\n'); return 1 }\n if (!existsSync(scriptPath)) { process.stderr.write(`flatc: script not found: ${scriptPath}\\n`); return 1 }\n let doc: Doc, gestures: Gesture[]\n try { doc = loadDoc(filePath, noLibs) } catch (e) { process.stderr.write(`flatc: cannot read: ${(e as Error).message}\\n`); return 1 }\n try { gestures = JSON.parse(readFileSync(scriptPath, 'utf8')) as Gesture[] } catch (e) { process.stderr.write(`flatc: invalid JSON script: ${(e as Error).message}\\n`); return 1 }\n if (!Array.isArray(gestures)) { process.stderr.write('flatc: the script must be an array of gestures\\n'); return 1 }\n const res = playHeadless(doc, gestures, { trace })\n if (!trace) process.stdout.write(JSON.stringify(res, null, 2) + '\\n')\n else // --trace: readable log (one gesture per line) → inspection / debug-player.\n for (const s of res.steps ?? []) {\n // `name` · `name=value` · `name{a=1, b=2}` (record payload) — the three shapes the host receives.\n const send = (e: { name: string; value?: number | string; fields?: Record<string, number> }): string =>\n e.fields ? `${e.name}{${Object.entries(e.fields).map(([k, v]) => `${k}=${v}`).join(', ')}}`\n : e.value !== undefined ? `${e.name}=${e.value}` : e.name\n const sends = s.sends.length ? ' sends:[' + s.sends.map(send).join(', ') + ']' : ''\n const vars = Object.entries(s.changed).map(([k, [a, b]]) => `${k}:${JSON.stringify(a)}→${JSON.stringify(b)}`)\n process.stdout.write(`${s.gesture.padEnd(28)}${sends}${vars.length ? ' vars{' + vars.join(' ') + '}' : ''}\\n`)\n }\n // `expect`: any detected mismatch → stderr log + exit code ≠0 (CI self-verification).\n if (res.expectFailures?.length) {\n for (const f of res.expectFailures) process.stderr.write(`flatc: ✗ ${f}\\n`)\n return 1\n }\n return 0\n}\n\n/** Parses `--at`: \"k=v[,k2=v2]\" → variable table (state override for rendering). */\nfunction parseVars(spec: string, into: Record<string, number>): void {\n for (const pair of spec.split(',')) {\n const i = pair.indexOf('=')\n if (i < 0) continue\n const k = pair.slice(0, i).trim(); const v = Number(pair.slice(i + 1).trim())\n if (k && Number.isFinite(v)) into[k] = v\n }\n}\n\n/** `--scale auto`: pick a resolution factor from the canvas size — enlarge small/thin content (so fine\n * filaments stay legible) while leaving large content at 1×. `max(160/long, 48/short)`, clamped [1, 8]. */\nfunction autoScale(w: number, h: number): number {\n const maxSide = Math.max(1, w, h), minSide = Math.max(1, Math.min(w, h))\n return Math.max(1, Math.min(8, Math.round(Math.max(160 / maxSide, 48 / minSide))))\n}\n\n/** Renders a Doc to a PNG file (headless skia). Async (SVG decode + raster). Shared by --render and --preview. */\nasync function renderDocToFile(doc: Doc, outPath: string, frame: number, vars: Record<string, number>, scale: number, steps: number): Promise<number> {\n try {\n const { renderDocToPng } = await import('./render')\n const png = await renderDocToPng(doc, { frame, vars: Object.keys(vars).length ? vars : undefined, scale, steps: steps || undefined })\n writeFileSync(outPath, png)\n } catch (e) {\n process.stderr.write(`flatc: render failed: ${(e as Error).message}\\n`)\n if (process.env.FLATC_DEBUG && (e as Error).stack) process.stderr.write(`${(e as Error).stack}\\n`) // FLATC_DEBUG=1 → full stack for diagnosis\n return 1\n }\n process.stdout.write(`flatc: ${basename(outPath)} ✓ ${doc.width}×${doc.height} ×${scale}${frame ? ` · frame ${frame}` : ''}${steps ? ` · ${steps} step(s)` : ''}${Object.keys(vars).length ? ` · ${Object.entries(vars).map(([k, v]) => `${k}=${v}`).join(' ')}` : ''}\\n`)\n return 0\n}\n\n/** --render: renders the file to PNG (headless skia). Async (SVG decode + raster). */\nasync function renderOnce(filePath: string, out: string, frame: number, vars: Record<string, number>, scale: number, steps: number, scaleAuto: boolean, noLibs = false): Promise<number> {\n let doc: Doc\n // `--no-libs` was parsed and then not passed on, so `--render` still auto-discovered the neighbouring\n // .flat files -- and failed on one, advising the very flag that had been given. Same hole in `--play`.\n try { doc = loadDoc(filePath, noLibs) } catch (e) { process.stderr.write(`flatc: cannot read: ${(e as Error).message}\\n`); return 1 }\n // A frame past the end WRAPS silently, so a render can show frame 20 while the caller asked for 200\n // and reads the result as a renderer bug. It cost exactly that once.\n const dur = doc.timeline?.durationFrames ?? 0\n if (dur > 0 && frame >= dur) process.stderr.write(`flatc: --frame ${frame} is past the timeline (durationFrames ${dur}); the playhead WRAPS, so this renders frame ${frame % dur}\\n`)\n const outPath = out ? resolve(out) : join(dirname(filePath), basename(filePath, extname(filePath)) + '.png')\n return renderDocToFile(doc, outPath, frame, vars, scaleAuto ? autoScale(doc.width, doc.height) : scale, steps)\n}\n\nconst gcd = (a: number, b: number): number => { a = Math.abs(a); b = Math.abs(b); while (b) { const t = a % b; a = b; b = t } return a || 1 }\nconst lcm = (a: number, b: number): number => Math.max(1, Math.round((a / gcd(a, b)) * b))\n\n/** Every instance in a symbol's subtree (descends into local groups; does NOT cross into other symbols). */\nfunction* instancesOf(sym: SymbolDef): Generator<Instance> {\n const stack: Item[] = []\n for (const l of sym.layers) stack.push(...l.items)\n for (let it = stack.pop(); it; it = stack.pop()) {\n if (isInstance(it)) yield it\n else if (isGroup(it)) for (const l of it.layers) stack.push(...l.items)\n }\n}\n\n/**\n * Preview window length (frames). A `synced` instance loops on the PARENT's frame, so the symbol's own\n * duration suffices — that's the historical behavior, kept for every symbol with no MovieClip descendant.\n * But an `independent` (`loop`) descendant runs on its OWN duration, immune to the parent's wrap: to SHOW it\n * loop CLEANLY the window must be a common multiple of every such clip's duration (and the root's) — its\n * \"loop seam\". `once` clips only need the window to REACH their last frame (they hold afterwards). We walk\n * the subtree, fold each independent clip's duration into the seam (LCM), and extend past the longest clip,\n * keeping the window a whole number of seams. Capped, so coprime durations can't blow the window up.\n */\nfunction previewDuration(root: SymbolDef, symbols: SymbolDef[]): number {\n const byId = new Map(symbols.map((s) => [s.id, s]))\n const seen = new Set<string>()\n let seam = Math.max(1, root.timeline?.durationFrames ?? 1) // clean-loop period (multiple of every loop clip)\n let longest = seam // window must reach the last frame of any clip\n const walk = (sym: SymbolDef | undefined): void => {\n if (!sym || seen.has(sym.id)) return\n seen.add(sym.id)\n for (const it of instancesOf(sym)) {\n const child = byId.get(it.symbolId)\n const cdur = Math.max(1, child?.timeline?.durationFrames ?? 1)\n const mode = it.playback?.mode\n if (mode === 'independent') { seam = lcm(seam, cdur); longest = Math.max(longest, cdur) }\n else if (mode === 'once') longest = Math.max(longest, cdur)\n walk(child)\n }\n }\n walk(root)\n let dur = seam\n while (dur < longest) dur += seam // keep a clean loop seam while covering the longest MovieClip\n return Math.min(dur, 6000) // hard cap (~4 min @24fps) against a pathological coprime-duration blow-up\n}\n\n/**\n * Wraps ONE symbol from a `.flat` library into a minimal, playable Doc: a single centered instance on a\n * canvas auto-sized to the symbol's frame-0 bounds (+ padding). Lets you preview a library asset without\n * hand-authoring a wrapper `.flatink`. The root timeline borrows the symbol's own fps and a duration long\n * enough to show every nested MovieClip (`loop`/`once`) loop cleanly (`previewDuration`). Throws if the lib\n * is empty or the named symbol is missing.\n */\nfunction buildPreviewDoc(flatPath: string, symbolName: string, pad: number, bboxMode: 'all' | 'frame0', setSpec: Record<string, string> = {}): { doc: Doc; symbol: SymbolDef; others: string[] } {\n const { symbols } = parseFlatLib(readFileSync(flatPath, 'utf8'))\n if (!symbols.length) throw new Error('no symbols found in this .flat library')\n let symbol = symbols[0]\n if (symbolName) {\n const found = symbols.find((s) => s.name === symbolName)\n if (!found) throw new Error(`symbol \"${symbolName}\" not found — available: ${symbols.map((s) => s.name).join(', ')}`)\n symbol = found\n }\n\n const instance: Instance = { id: 'preview_instance', kind: 'instance', name: symbol.name, transform: { ...IDENTITY }, symbolId: symbol.id }\n const layer: Layer = { id: 'preview_layer', name: 'preview', visible: true, locked: false, opacity: 1, items: [instance] }\n const tl = symbol.timeline\n const dur = previewDuration(symbol, symbols)\n const timeline = { fps: tl?.fps ?? 24, durationFrames: dur, tracks: [] }\n // ALL symbols stay in the library (the chosen one may instance the others) — only `instance` is on stage.\n let doc: Doc = { width: 1, height: 1, layers: [layer], symbols, timeline }\n\n // Auto-size & center. Default `--bbox all`: UNION over every frame of the symbol (sub-timelines NOT\n // frozen) so motion that drifts/rotates/grows is never clipped. `--bbox frame0` keeps the old frame-0\n // measure (+`--pad` to absorb motion).\n // Sample at most ~240 evenly-spaced frames (+ the last) so a long/abusive `durationFrames` can't blow\n // up memory/time; a sparse union is virtually as tight as every-frame for real motion.\n const sampleFrames = (n: number): number[] => {\n const step = Math.max(1, Math.ceil(n / 240))\n const fs: number[] = []\n for (let f = 0; f < n; f += step) fs.push(f)\n if (fs[fs.length - 1] !== n - 1) fs.push(n - 1)\n return fs\n }\n const bb = bboxMode === 'all' ? containerBBoxUnion(doc, instance, sampleFrames(dur)) : containerBBox(doc, instance, 0)\n if (bb) {\n doc = { ...doc, width: Math.max(1, Math.ceil(bb.maxX - bb.minX) + pad * 2), height: Math.max(1, Math.ceil(bb.maxY - bb.minY) + pad * 2) }\n instance.transform = { ...IDENTITY, e: pad - bb.minX, f: pad - bb.minY }\n } else {\n doc = { ...doc, width: 512, height: 512 } // empty/degenerate symbol: fall back to a fixed centered stage\n instance.transform = { ...IDENTITY, e: 256, f: 256 }\n }\n\n // `--set param=value`: set the preview instance's exposed params (call-site values, raw literals). The\n // renderer resolves them per the symbol's ParamDef / state machines (color → fill, number/bool → scope,\n // state name → driven frame). A light validation warns on names the symbol does not expose.\n const entries = Object.entries(setSpec)\n if (entries.length) {\n const known = new Set([...(symbol.states ?? []).map((s) => s.param), ...(symbol.params ?? []).map((p) => p.name)])\n for (const [k, v] of entries) {\n if (!known.has(k)) process.stderr.write(`flatc: --set ${k}: \"${symbol.name}\" exposes no such param (${[...known].join(', ') || 'none'})\\n`)\n else (instance.params ??= {})[k] = v\n }\n }\n return { doc, symbol, others: symbols.filter((s) => s !== symbol).map((s) => s.name) }\n}\n\n/** --preview: wraps one symbol of a `.flat` into a playable Doc, then writes a .flatpack (default) or a PNG (with --render). */\nasync function previewOnce(flatPath: string, symbolName: string, out: string, frame: number, vars: Record<string, number>, scale: number, steps: number, doRender: boolean, pad: number, bboxMode: 'all' | 'frame0', setSpec: Record<string, string>, scaleAuto: boolean): Promise<number> {\n let built: { doc: Doc; symbol: SymbolDef; others: string[] }\n try { built = buildPreviewDoc(flatPath, symbolName, pad, bboxMode, setSpec) }\n catch (e) { process.stderr.write(`flatc: ${(e as Error).message}\\n`); return 1 }\n const { doc, symbol, others } = built\n const stem = `${basename(flatPath, extname(flatPath))}.${symbol.name}`\n let code: number\n if (doRender) {\n // auto scale from the CONTENT size (minus the padding), so thin filaments still trigger an upscale.\n const sc = scaleAuto ? autoScale(doc.width - 2 * pad, doc.height - 2 * pad) : scale\n code = await renderDocToFile(doc, out ? resolve(out) : join(dirname(flatPath), stem + '.png'), frame, vars, sc, steps)\n } else {\n const outPath = out ? resolve(out) : join(dirname(flatPath), stem + '.flatpack')\n writeFileSync(outPath, packToJSON(doc))\n process.stdout.write(`flatc: ${basename(outPath)} ✓ preview of \"${symbol.name}\" · ${doc.width}×${doc.height} · play it in the browser player\\n`)\n code = 0\n }\n if (!symbolName && others.length) process.stderr.write(`flatc: previewed \"${symbol.name}\"; other symbols in this lib: ${others.join(', ')} (pick with --symbol <name>)\\n`)\n return code\n}\n\nexport function run(argv: string[]): number | Promise<number> {\n const args = argv.slice(2)\n let out = '', scriptPath = '', symbolName = ''\n let checkOnly = false, doWatch = false, doPlay = false, doRender = false, doTrace = false, doPreview = false, noLibs = false\n let frame = 0, scale = 2, steps = 0, pad = 24\n let scaleAuto = false\n let bboxMode: 'all' | 'frame0' = 'all'\n let assetMode: AssetMode = 'inline'\n const vars: Record<string, number> = {}\n const setSpec: Record<string, string> = {} // `--set param=value` (state name or number) for --preview\n const positional: string[] = []\n for (let i = 0; i < args.length; i++) {\n const a = args[i]\n if (a === '-o' || a === '--out') out = args[++i] ?? ''\n else if (a === '--script') scriptPath = resolve(args[++i] ?? '')\n else if (a === '--assets') assetMode = args[++i] === 'external' ? 'external' : 'inline'\n else if (a === '--check') checkOnly = true\n else if (a === '--no-libs') noLibs = true\n else if (a === '--watch') doWatch = true\n else if (a === '--play') doPlay = true\n else if (a === '--trace') doTrace = true\n else if (a === '--render') doRender = true\n else if (a === '--preview') doPreview = true\n else if (a === '--symbol') symbolName = args[++i] ?? ''\n else if (a === '--pad') pad = Math.max(0, Number(args[++i] ?? '24') || 0)\n else if (a === '--bbox') bboxMode = args[++i] === 'frame0' ? 'frame0' : 'all'\n else if (a === '--frame') frame = Number(args[++i] ?? '0') || 0\n else if (a === '--steps') steps = Math.max(0, Number(args[++i] ?? '0') || 0)\n else if (a === '--scale') { const v = args[++i] ?? '2'; if (v === 'auto') scaleAuto = true; else scale = Number(v) || 2 }\n else if (a === '--at') parseVars(args[++i] ?? '', vars)\n else if (a === '--set') for (const pair of (args[++i] ?? '').split(',')) { const j = pair.indexOf('='); if (j > 0) setSpec[pair.slice(0, j).trim()] = pair.slice(j + 1).trim() }\n else if (a === '-h' || a === '--help') { printHelp(); return 0 }\n else positional.push(a)\n }\n if (!positional.length) { printHelp(); return 1 }\n\n const filePath = resolve(positional[0])\n if (!existsSync(filePath)) { process.stderr.write(`flatc: not found: ${filePath}\\n`); return 1 }\n\n const explicitFlats = positional.slice(1)\n if (doPreview) return previewOnce(filePath, symbolName, out, frame, vars, scale, steps, doRender, pad, bboxMode, setSpec, scaleAuto)\n if (doRender) return renderOnce(filePath, out, frame, vars, scale, steps, scaleAuto, noLibs)\n if (doPlay) return playOnce(filePath, scriptPath, doTrace, noLibs)\n // `--check <library>.flat`: a `.flat` first positional is an asset LIB, not a program → lint via parseFlatLib\n // (the following positionals are more `.flat` libs to merge). Every other path is unchanged.\n const action: () => number = checkOnly && filePath.endsWith('.flat')\n ? () => checkFlatLibs([filePath, ...explicitFlats])\n : () => compileOnce(filePath, explicitFlats, out, checkOnly, assetMode, noLibs)\n if (doWatch) {\n const code = action()\n const baseDir = dirname(filePath)\n let timer: ReturnType<typeof setTimeout> | undefined\n // We ignore changes to the OUTPUT (.flatpack) — otherwise writing it would re-trigger the compile in a loop.\n watch(baseDir, { recursive: false }, (_e, filename) => {\n if (filename && (filename.endsWith('.flatpack') || filename.endsWith('.flatpack.json'))) return\n clearTimeout(timer); timer = setTimeout(action, 80)\n })\n process.stdout.write(`flatc: watching ${baseDir} … (Ctrl+C to stop)\\n`)\n return code\n }\n return action()\n}\n"],"mappings":"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","names":["f"]}
|
package/dist/cli/flatc.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
run
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-RNQ2IQLD.js";
|
|
4
4
|
import {
|
|
5
5
|
allScopeVariables,
|
|
6
6
|
checkProgram,
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
scopeProgram,
|
|
24
24
|
scopeRegions,
|
|
25
25
|
splitScopeProgram
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-IYPM3CU5.js";
|
|
27
27
|
import {
|
|
28
28
|
compileFlatpack,
|
|
29
29
|
packToJSON
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flatkit/compiler",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0",
|
|
4
4
|
"description": "The FlatInk language (parser + AST) and compiler (.flatink → .flatpack). Ships the flatc CLI.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Zwyk Studio",
|
|
@@ -38,7 +38,13 @@
|
|
|
38
38
|
"types": "./dist/analysis.d.ts",
|
|
39
39
|
"import": "./dist/analysis.js",
|
|
40
40
|
"require": "./dist/analysis.js"
|
|
41
|
-
}
|
|
41
|
+
},
|
|
42
|
+
"./render": {
|
|
43
|
+
"types": "./dist/cli/render.d.ts",
|
|
44
|
+
"import": "./dist/cli/render.js",
|
|
45
|
+
"require": "./dist/cli/render.js"
|
|
46
|
+
},
|
|
47
|
+
"./prompts/*": "./prompts/*"
|
|
42
48
|
},
|
|
43
49
|
"bin": {
|
|
44
50
|
"flatc": "./bin/flatc.mjs"
|
|
@@ -49,9 +55,9 @@
|
|
|
49
55
|
"prompts"
|
|
50
56
|
],
|
|
51
57
|
"dependencies": {
|
|
52
|
-
"@flatkit/engine": "0.
|
|
53
|
-
"@flatkit/
|
|
54
|
-
"@flatkit/
|
|
58
|
+
"@flatkit/engine": "0.26.0",
|
|
59
|
+
"@flatkit/types": "0.26.0",
|
|
60
|
+
"@flatkit/player": "0.26.0"
|
|
55
61
|
},
|
|
56
62
|
"peerDependencies": {
|
|
57
63
|
"skia-canvas": "^3.0.8"
|
package/prompts/flatink-core.md
CHANGED
|
@@ -18,25 +18,25 @@ A `.flatink` is split in **two halves with different grammars**:
|
|
|
18
18
|
## `.flatink` skeleton
|
|
19
19
|
|
|
20
20
|
```
|
|
21
|
-
size 480 320
|
|
22
|
-
background #0a0e1c
|
|
23
|
-
timeline 30 300
|
|
24
|
-
use "collision"
|
|
25
|
-
asset "logo" "logo.svg" image
|
|
26
|
-
var score = 0
|
|
21
|
+
size 480 320 // REQUIRED, must be the very first line (canvas units)
|
|
22
|
+
background #0a0e1c // optional
|
|
23
|
+
timeline 30 300 // optional root timeline: fps, duration(frames). default 24 fps / 60 frames
|
|
24
|
+
use "collision" // optional stdlib/local packages
|
|
25
|
+
asset "logo" "logo.svg" image // optional media declarations
|
|
26
|
+
var score = 0 // optional global state
|
|
27
27
|
|
|
28
28
|
scene {
|
|
29
29
|
layer "bg" { rect 0 0 480 320 fill #0a0e1c }
|
|
30
30
|
layer "game" {
|
|
31
|
-
|
|
31
|
+
group "Sun" at 240,160 { layer "art" { circle 0 0 40 fill #ffcc00 } }
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
object "Sun" {
|
|
35
|
+
object "Sun" { // behavior attaches BY NAME, to a group -- never to a bare shape
|
|
36
36
|
when clicked { score = score + 1 }
|
|
37
|
-
|
|
37
|
+
rotationDeg = clock * 30 // degrees; `clock` is monotone, `time` restarts every loop
|
|
38
38
|
}
|
|
39
|
-
every frame { if
|
|
39
|
+
every frame { if score >= 10 { send "win" } }
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
## Drawing (inside `scene`/`symbol` layers)
|
|
@@ -46,24 +46,24 @@ Coordinates are plain numbers; canvas origin is **top-left**. Layers stack botto
|
|
|
46
46
|
```
|
|
47
47
|
circle <cx> <cy> <r>
|
|
48
48
|
ellipse <cx> <cy> <rx> <ry>
|
|
49
|
-
rect <x> <y> <w> <h> [<r> | <rx> <ry>]
|
|
50
|
-
path "M0 0 L10 0 L10 10 Z"
|
|
49
|
+
rect <x> <y> <w> <h> [<r> | <rx> <ry>] // optional rounded corners
|
|
50
|
+
path "M0 0 L10 0 L10 10 Z" // raw SVG path data
|
|
51
51
|
text "Hi" font "sans-serif" size 24 align center line 1.2 color #fff box 200 40
|
|
52
|
-
image "logo" 80 80 at -40,-40
|
|
53
|
-
group "Name" at x,y pivot px,py { layer "c" { … } }
|
|
54
|
-
instance "Symbol" as "Name" at x,y
|
|
52
|
+
image "logo" 80 80 at -40,-40 // origin = top-left → center with at -w/2,-h/2
|
|
53
|
+
group "Name" at x,y pivot px,py { layer "c" { … } } // nests its own layers
|
|
54
|
+
instance "Symbol" as "Name" at x,y // place a symbol from a .flat lib
|
|
55
55
|
```
|
|
56
56
|
|
|
57
57
|
Paint / style (work on shapes; most on text & groups too):
|
|
58
58
|
```
|
|
59
59
|
fill #rrggbb | nofill
|
|
60
60
|
stroke #rrggbb <width> [cap butt|round|square] [join …] [miter n] [dash a,b,…]
|
|
61
|
-
opacity 0..1
|
|
62
|
-
fill linear(90, 0:#bdecff, 1:#2f8fe0)
|
|
63
|
-
fill radial(0.5, 0.5, 0.5, 0:#fff, 1:#000)
|
|
61
|
+
opacity 0..1 // also 8-digit hex alpha #rrggbbaa
|
|
62
|
+
fill linear(90, 0:#bdecff, 1:#2f8fe0) // angle 0 = →, 90 = ↓ ; stops offset:color
|
|
63
|
+
fill radial(0.5, 0.5, 0.5, 0:#fff, 1:#000) // cx, cy, r (0..1), then stops
|
|
64
64
|
filter glow <blur> <color> | shadow <dx> <dy> <blur> <color> | blur <r> | adjust <b> <c> <s> <h>
|
|
65
|
-
tint <color> <amount(0..1)>
|
|
66
|
-
nohit
|
|
65
|
+
tint <color> <amount(0..1)> // Flash-style tint
|
|
66
|
+
nohit // drawn but ignored by hit-test
|
|
67
67
|
```
|
|
68
68
|
|
|
69
69
|
## Animation — timeline / cel / pose (in a `symbol`)
|
|
@@ -74,13 +74,13 @@ the **poses** of containers declared once in the layer roster, plus (optionally)
|
|
|
74
74
|
|
|
75
75
|
```
|
|
76
76
|
symbol "Wheel" {
|
|
77
|
-
timeline 24 24
|
|
77
|
+
timeline 24 24 // fps, durationFrames
|
|
78
78
|
layer "spin" {
|
|
79
|
-
group "Rim" at 100,100 pivot 0,0 {
|
|
79
|
+
group "Rim" at 100,100 pivot 0,0 { // roster: declared ONCE, posed by the cels below
|
|
80
80
|
layer "art" { circle 0 0 40 nofill stroke #333 8 }
|
|
81
81
|
}
|
|
82
82
|
cel 0 tween { pose "Rim" rotate 0 }
|
|
83
|
-
cel 24 { pose "Rim" rotate 360 }
|
|
83
|
+
cel 24 { pose "Rim" rotate 360 } // one full turn around the pivot, in DEGREES
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
@@ -117,9 +117,9 @@ Additive position offsets: `dx dy` → **`pos = at + (dx, dy)`** (binding-only;
|
|
|
117
117
|
|
|
118
118
|
```
|
|
119
119
|
object "Needle" {
|
|
120
|
-
rotation = atan2(mouse.y - 160, mouse.x - 240)
|
|
120
|
+
rotation = atan2(mouse.y - 160, mouse.x - 240) // RADIANS
|
|
121
121
|
opacity = lit ? 1 : 0.3
|
|
122
|
-
dx = 30 * sin(clock)
|
|
122
|
+
dx = 30 * sin(clock) // sways AROUND its declared at — no base to re-inject
|
|
123
123
|
}
|
|
124
124
|
```
|
|
125
125
|
Or bind a channel on a symbol container directly: `group "Fan" pivot 0,0 expr rotation "turns(time)" { … }`.
|
|
@@ -128,9 +128,9 @@ Or bind a channel on a symbol container directly: `group "Fan" pivot 0,0 expr ro
|
|
|
128
128
|
snapping to it. Per instance, zero cost when unused, and it snaps to the target on a seek (so tune it by
|
|
129
129
|
PLAYING the preview, not by scrubbing).
|
|
130
130
|
```
|
|
131
|
-
group "Cable" spring rotation "hookX" stiffness 0.08 damping 0.86 { … }
|
|
132
|
-
group "Panel" smooth y "target" k 0.15 { … }
|
|
133
|
-
object "Dial" { spring rotation = aim { stiffness 0.08 damping 0.86 } }
|
|
131
|
+
group "Cable" spring rotation "hookX" stiffness 0.08 damping 0.86 { … } // in a .flat: overshoot, settle
|
|
132
|
+
group "Panel" smooth y "target" k 0.15 { … } // 1st order: no overshoot
|
|
133
|
+
object "Dial" { spring rotation = aim { stiffness 0.08 damping 0.86 } } // scene-side form, in .flatink
|
|
134
134
|
```
|
|
135
135
|
The quoted target's names must EXIST in that scope (a symbol `param`, a scene `var`) or `--check` errors.
|
|
136
136
|
|
|
@@ -145,37 +145,37 @@ Actions (one per line): `<var> = <expr>` · `arr[i] = <expr>` · `if/else if/els
|
|
|
145
145
|
|
|
146
146
|
Drag & interactors (each writes into your vars; all accept `{ enabled <expr> }`):
|
|
147
147
|
```
|
|
148
|
-
drag x, y [{ confine to <Zone> · snap <grid> · enabled <expr> }]
|
|
149
|
-
turn <angle> around <x>,<y> [{ snap <deg> }]
|
|
150
|
-
turnDeg <angle> around <x>,<y> [{ snap <deg> }]
|
|
151
|
-
trace <progress> along <Group> [{ tolerance <px> }]
|
|
152
|
-
reveal <progress> [{ brush <px> }]
|
|
153
|
-
link <endX>,<endY>,<target> to <Group>
|
|
148
|
+
drag x, y [{ confine to <Zone> · snap <grid> · enabled <expr> }] // dragX / dragY too
|
|
149
|
+
turn <angle> around <x>,<y> [{ snap <deg> }] // → <angle> in RADIANS → rotation = <angle> directly
|
|
150
|
+
turnDeg <angle> around <x>,<y> [{ snap <deg> }] // → <angle> in DEGREES → pair with rotationDeg = <angle>
|
|
151
|
+
trace <progress> along <Group> [{ tolerance <px> }]// follow a path → 0..1 monotone
|
|
152
|
+
reveal <progress> [{ brush <px> }] // scratch/wipe → 0..1 cumulative
|
|
153
|
+
link <endX>,<endY>,<target> to <Group> // elastic thread → target = hit index 1..n (0=none)
|
|
154
154
|
```
|
|
155
155
|
|
|
156
156
|
State & helpers:
|
|
157
157
|
```
|
|
158
|
-
var x = 0 var arr = [0,0,0] var z = fill(8, 0)
|
|
159
|
-
fn dist(ax,ay,bx,by) = hypot(ax-bx, ay-by)
|
|
160
|
-
fn reset() { score = 0 go to frame 0 }
|
|
161
|
-
self.hovered self.grabbed self.pressed
|
|
162
|
-
feedback lift tilt dim shake(<expr>)
|
|
158
|
+
var x = 0 var arr = [0,0,0] var z = fill(8, 0) // runtime state (arrays via fill)
|
|
159
|
+
fn dist(ax,ay,bx,by) = hypot(ax-bx, ay-by) // value fn
|
|
160
|
+
fn reset() { score = 0 go to frame 0 } // procedure fn
|
|
161
|
+
self.hovered self.grabbed self.pressed // own interaction state (0/1)
|
|
162
|
+
feedback lift tilt dim shake(<expr>) // one-liner reactions (auto use "feedback")
|
|
163
163
|
```
|
|
164
164
|
|
|
165
165
|
## Factoring (compile-time, zero runtime cost)
|
|
166
166
|
|
|
167
167
|
```
|
|
168
|
-
def gap = 70
|
|
169
|
-
repeat i from 0 to 4 { circle $(40 + i*gap) 80 6 fill #ffd98a }
|
|
170
|
-
symbol "Card"(label, tint = "#fff") { … text "$(label)" … fill $(tint) … }
|
|
168
|
+
def gap = 70 // compile-time constant, used via $()
|
|
169
|
+
repeat i from 0 to 4 { circle $(40 + i*gap) 80 6 fill #ffd98a } // $(expr) = compile-time arithmetic
|
|
170
|
+
symbol "Card"(label, tint = "#fff") { … text "$(label)" … fill $(tint) … } // parameterized symbol
|
|
171
171
|
instance "Card"($(i+1)) as "C$(i)" at $(80 + i*90),200
|
|
172
|
-
each "Key" as i { when clicked { input = input*10 + (i+1) } }
|
|
173
|
-
match Word1, Word2 onto Good, Bad {
|
|
172
|
+
each "Key" as i { when clicked { input = input*10 + (i+1) } } // shared behavior over instances
|
|
173
|
+
match Word1, Word2 onto Good, Bad { // declarative drag+drop pairing
|
|
174
174
|
correct Word1 -> Good, Word2 -> Bad
|
|
175
175
|
on done { send "win" }
|
|
176
176
|
}
|
|
177
|
-
at center | at center,540 | at 120,center
|
|
178
|
-
align top of "Bin" [offset dx,dy]
|
|
177
|
+
at center | at center,540 | at 120,center // canvas-relative anchor
|
|
178
|
+
align top of "Bin" [offset dx,dy] // pin origin onto another item's bbox
|
|
179
179
|
```
|
|
180
180
|
|
|
181
181
|
## Expressions & stdlib
|
|
@@ -195,6 +195,9 @@ ramp over `dur` s for a readable timed feedback — capture the instant with **`
|
|
|
195
195
|
|
|
196
196
|
## CRITICAL GOTCHAS — do not get these wrong
|
|
197
197
|
|
|
198
|
+
0. **A comment is `//`, everywhere.** `#` opens a COLOUR (`#ffcc00`). Used as a comment it survives in
|
|
199
|
+
the header half and is a parse ERROR inside `scene { … }` — reported as `"layer" expected, "#" found`,
|
|
200
|
+
which points nowhere near the real cause.
|
|
198
201
|
1. **`size W H` is required and MUST be the first line** of a `.flatink`. A `.flat` has no `size`.
|
|
199
202
|
2. **Two grammars.** Drawing keywords live in `scene`/`symbol` layers; logic keywords live in
|
|
200
203
|
`object`/`every frame`/`fn`. Don't mix (no `var`/`when` inside `scene`; no `circle` inside `object`).
|
package/prompts/flatink-lite.md
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
You write **FlatInk**, a text language for animations & interactive scenes. Output **only valid source**
|
|
4
4
|
in one fenced block. You won't have a compiler — get it right in one pass. Keep DSL keywords in English.
|
|
5
|
+
**Comments are `//`, everywhere. `#` opens a COLOUR, and breaks inside `scene { … }`.**
|
|
5
6
|
|
|
6
7
|
## Two file types
|
|
7
8
|
- **`.flat`** = a symbol library: one or more `symbol "Name" { … }`. **No `size` line.** Not playable alone.
|
|
@@ -12,27 +13,29 @@ group/instance); everything after = behavior (`object`, `every frame`, `var`, `f
|
|
|
12
13
|
|
|
13
14
|
## `.flatink` skeleton
|
|
14
15
|
```
|
|
15
|
-
size 480 320
|
|
16
|
-
background #0a0e1c
|
|
17
|
-
var score = 0
|
|
16
|
+
size 480 320 // REQUIRED first line (canvas units; origin = top-left)
|
|
17
|
+
background #0a0e1c // optional
|
|
18
|
+
var score = 0 // optional global runtime state
|
|
18
19
|
scene {
|
|
19
|
-
layer "bg" { rect 0 0 480 320 fill #0a0e1c }
|
|
20
|
-
layer "game" {
|
|
20
|
+
layer "bg" { rect 0 0 480 320 fill #0a0e1c } // layers stack bottom -> top
|
|
21
|
+
layer "game" {
|
|
22
|
+
group "Sun" at 240,160 { layer "art" { circle 0 0 40 fill #ffcc00 } }
|
|
23
|
+
}
|
|
21
24
|
}
|
|
22
|
-
object "Sun" {
|
|
25
|
+
object "Sun" { // behavior attaches by name, to a GROUP (a bare shape is an error)
|
|
23
26
|
when clicked { score = score + 1 }
|
|
24
|
-
|
|
27
|
+
rotationDeg = clock * 30 // channel binding, every frame (degrees; `clock` never wraps)
|
|
25
28
|
}
|
|
26
|
-
every frame { if
|
|
29
|
+
every frame { if score >= 10 { send "win" } }
|
|
27
30
|
```
|
|
28
31
|
|
|
29
32
|
## Drawing (in scene/symbol layers)
|
|
30
33
|
```
|
|
31
34
|
circle cx cy r · ellipse cx cy rx ry · rect x y w h [r | rx ry] · path "M0 0 L10 0 L10 10 Z"
|
|
32
35
|
text "Hi" font "sans-serif" size 24 align center line 1.2 color #fff box 200 40 [bold] [italic] [wrap]
|
|
33
|
-
image "id" w h at -w/2,-h/2
|
|
34
|
-
group "Name" at x,y pivot px,py { layer "c" { … } }
|
|
35
|
-
instance "Symbol" as "Name" at x,y
|
|
36
|
+
image "id" w h at -w/2,-h/2 // origin top-left → center yourself ; needs: asset "id" "f.png" image
|
|
37
|
+
group "Name" at x,y pivot px,py { layer "c" { … } } // nests its own layers
|
|
38
|
+
instance "Symbol" as "Name" at x,y // place a symbol from a .flat
|
|
36
39
|
```
|
|
37
40
|
Style: `fill #rrggbb | nofill` · `stroke #rgb <w> [cap round][join round][dash a,b]` · `opacity 0..1` ·
|
|
38
41
|
`fill linear(90, 0:#a, 1:#b)` (0=→,90=↓) · `fill radial(0.5,0.5,0.5, 0:#fff,1:#000)` ·
|
|
@@ -41,13 +44,13 @@ Style: `fill #rrggbb | nofill` · `stroke #rgb <w> [cap round][join round][dash
|
|
|
41
44
|
## Animation (in a symbol): timeline / cel / pose
|
|
42
45
|
```
|
|
43
46
|
symbol "Wheel" {
|
|
44
|
-
timeline 24 24
|
|
47
|
+
timeline 24 24 // fps, durationFrames (loops [0,dur))
|
|
45
48
|
layer "spin" {
|
|
46
|
-
group "Rim" at 100,100 pivot 0,0 {
|
|
49
|
+
group "Rim" at 100,100 pivot 0,0 { // roster: declared ONCE, posed by cels below
|
|
47
50
|
layer "art" { circle 0 0 40 nofill stroke #333 8 }
|
|
48
51
|
}
|
|
49
|
-
cel 0 tween { pose "Rim" rotate 0 }
|
|
50
|
-
cel 24 { pose "Rim" rotate 360 }
|
|
52
|
+
cel 0 tween { pose "Rim" rotate 0 } // tween = interpolate to next cel; no tween = hold
|
|
53
|
+
cel 24 { pose "Rim" rotate 360 } // DEGREES, around the pivot
|
|
51
54
|
}
|
|
52
55
|
}
|
|
53
56
|
pose "Name" [at x,y] [rotate deg] [scale s | scaleX sx scaleY sy] [opacity o] [spin cw|ccw] [turns n]
|
|
@@ -64,9 +67,9 @@ Actions (one per line): `<var> = <expr>` · `arr[i] = <expr>` · `if/else if/els
|
|
|
64
67
|
`repeat i from a to b {}` · `play`/`pause` · `go to frame n [and play]` · `send "evt" [, <expr> | , text("id") | , { a = <expr>, b }]` · `sound "id"`.
|
|
65
68
|
Drag/interactors (write into your vars; all take `{ enabled <expr> }`):
|
|
66
69
|
```
|
|
67
|
-
drag x, y [{ confine to <Zone> · snap <grid> }]
|
|
70
|
+
drag x, y [{ confine to <Zone> · snap <grid> }] // then USE them: x = px y = py
|
|
68
71
|
turn <angle> around x,y · trace <progress> along <Group> · reveal <progress>
|
|
69
|
-
link endX,endY,target to <Group>
|
|
72
|
+
link endX,endY,target to <Group> // target = hit index 1..n (0=none), WORLD coords
|
|
70
73
|
```
|
|
71
74
|
Self-state & feedback: `self.hovered self.grabbed self.pressed` (0/1) ·
|
|
72
75
|
`feedback lift tilt dim shake(<expr>)`.
|
|
@@ -11,10 +11,10 @@ Author verifies with `flatc --preview Asset.flat --render -o out.png`.
|
|
|
11
11
|
|
|
12
12
|
```
|
|
13
13
|
symbol "Mug" {
|
|
14
|
-
layer "back" { ellipse 0 30 26 8 fill #00000022 }
|
|
14
|
+
layer "back" { ellipse 0 30 26 8 fill #00000022 } // layers stack bottom → top
|
|
15
15
|
layer "body" {
|
|
16
16
|
path "M-22 -28 L22 -28 L18 28 L-18 28 Z" fill #e8553a stroke #b23a24 3 join round
|
|
17
|
-
path "M22 -18 a14 14 0 0 1 0 28" nofill stroke #b23a24 5 cap round
|
|
17
|
+
path "M22 -18 a14 14 0 0 1 0 28" nofill stroke #b23a24 5 cap round // handle
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
20
|
```
|
|
@@ -26,20 +26,20 @@ symbol "Mug" {
|
|
|
26
26
|
|
|
27
27
|
```
|
|
28
28
|
circle cx cy r ellipse cx cy rx ry
|
|
29
|
-
rect x y w h [r | rx ry] path "M… L… C… Z"
|
|
29
|
+
rect x y w h [r | rx ry] path "M… L… C… Z" // raw SVG path data
|
|
30
30
|
text "Hi" font "sans-serif" size 24 align center line 1.2 color #fff box 200 40 [bold] [italic]
|
|
31
|
-
image "id" w h at -w/2,-h/2
|
|
31
|
+
image "id" w h at -w/2,-h/2 // needs: asset "id" "file.png" image (declared in the .flatink that uses it)
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
Paint & finish:
|
|
35
35
|
```
|
|
36
36
|
fill #rrggbb | nofill
|
|
37
37
|
stroke #rrggbb <width> [cap butt|round|square] [join round|bevel|miter] [miter n] [dash a,b]
|
|
38
|
-
opacity 0..1
|
|
39
|
-
fill linear(90, 0:#bdecff, 1:#2f8fe0)
|
|
40
|
-
fill radial(0.5, 0.5, 0.5, 0:#fff, 1:#000)
|
|
38
|
+
opacity 0..1 // or 8-digit hex alpha #rrggbbaa
|
|
39
|
+
fill linear(90, 0:#bdecff, 1:#2f8fe0) // angle 0 = →, 90 = ↓
|
|
40
|
+
fill radial(0.5, 0.5, 0.5, 0:#fff, 1:#000) // cx, cy, r in 0..1, then stops
|
|
41
41
|
filter glow <blur> <color> | shadow <dx> <dy> <blur> <color> | blur <r> | adjust <b> <c> <s> <h>
|
|
42
|
-
tint <color> <amount(0..1)> nohit
|
|
42
|
+
tint <color> <amount(0..1)> nohit // nohit = drawn but click-through
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
## Repetition without copy-paste (compile-time)
|
|
@@ -47,7 +47,7 @@ tint <color> <amount(0..1)> nohit # nohit = drawn but click-
|
|
|
47
47
|
```
|
|
48
48
|
def n = 6
|
|
49
49
|
layer "rays" {
|
|
50
|
-
repeat i from 0 to n { rect -2 -40 4 16 fill #ffd24d }
|
|
50
|
+
repeat i from 0 to n { rect -2 -40 4 16 fill #ffd24d } // $(…) interpolates compile-time math
|
|
51
51
|
}
|
|
52
52
|
```
|
|
53
53
|
`$(expr)` injects arithmetic into any coordinate: `circle $(60 + i*40) 80 6 …`. Nested loops = grids.
|
|
@@ -64,8 +64,8 @@ symbol "Boat" {
|
|
|
64
64
|
bool flag = true "Show the pennant"
|
|
65
65
|
}
|
|
66
66
|
layer "body" {
|
|
67
|
-
path "M-40 0 L40 0 L24 20 L-24 20 Z" fill hull
|
|
68
|
-
path "M0 -50 L0 0 L26 -10 Z" fill sail
|
|
67
|
+
path "M-40 0 L40 0 L24 20 L-24 20 Z" fill hull // a color param used as a fill…
|
|
68
|
+
path "M0 -50 L0 0 L26 -10 Z" fill sail // …or stroke <param> <width>
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
```
|