@hraness/slopcamera 3.3.6 → 3.4.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.
Files changed (43) hide show
  1. package/README.md +6 -4
  2. package/apps/desktop/application/operation.ts +1 -1
  3. package/apps/desktop/cli/capability-manifest.ts +13 -0
  4. package/apps/desktop/cli/commands.ts +1 -1
  5. package/apps/desktop/cli/help.ts +12 -1
  6. package/apps/desktop/cli/html-overlay-renderer.ts +16 -0
  7. package/apps/desktop/cli/portable-surface.ts +2 -0
  8. package/apps/desktop/code/public.ts +1 -0
  9. package/apps/desktop/core/visual-style-look.ts +34 -0
  10. package/apps/desktop/dist/cli/main.js +132 -108
  11. package/apps/desktop/html-overlay/runtime.ts +15 -0
  12. package/dist/cli.js +6 -2
  13. package/dist/{index-10bz03js.js → index-abnk4h9c.js} +3 -2
  14. package/dist/index.js +1 -1
  15. package/docs/README.md +1 -0
  16. package/examples/style-portfolio/ANIMATION-STUDIES.md +34 -0
  17. package/examples/style-portfolio/FILM-PLATE.md +19 -0
  18. package/examples/style-portfolio/README.md +137 -0
  19. package/examples/style-portfolio/animation-studies.html +597 -0
  20. package/examples/style-portfolio/assets/instrument-serif-OFL.txt +93 -0
  21. package/examples/style-portfolio/assets/instrument-serif-latin-400.woff2 +0 -0
  22. package/examples/style-portfolio/build-gallery.ts +403 -0
  23. package/examples/style-portfolio/film-plate.html +76 -0
  24. package/examples/style-portfolio/finish-film.ts +152 -0
  25. package/examples/style-portfolio/market-street/README.md +43 -0
  26. package/examples/style-portfolio/market-street/mesh.py +145 -0
  27. package/examples/style-portfolio/market-street/scene.py +760 -0
  28. package/examples/style-portfolio/market-street/source.json +1 -0
  29. package/examples/style-portfolio/market-street/street_data.py +72 -0
  30. package/examples/style-portfolio/render-film-plate.ts +128 -0
  31. package/examples/style-portfolio/render.ts +89 -0
  32. package/examples/style-portfolio/verify-animation-source.mjs +94 -0
  33. package/package.json +11 -2
  34. package/skills/slopcamera/SKILL.md +9 -0
  35. package/skills/slopcamera/references/gateway-media.md +5 -0
  36. package/skills/slopcamera/references/install.md +1 -1
  37. package/skills/slopcamera/references/music-video.md +5 -0
  38. package/skills/slopcamera/references/native-studio.md +4 -0
  39. package/skills/slopcamera/references/visual-style-direction.md +143 -0
  40. package/src/cli.ts +28 -0
  41. package/src/index.ts +1 -0
  42. package/src/version.ts +1 -1
  43. package/src/visual-style.ts +375 -0
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![Slopcamera: a visual studio for coding agents](https://slopcamera.com/og.png)](https://slopcamera.com)
4
4
 
5
- **Slopcamera is a local visual studio for coding agents. Author scenes, combine
5
+ **Slopcamera (formerly Atet) is a local visual studio for coding agents. Author scenes, combine
6
6
  generated and recorded media, and export images, diagrams, animation, and video
7
7
  from retained sources.**
8
8
 
@@ -44,14 +44,14 @@ the rendered output with source, runtime requirements and reproduction steps.
44
44
 
45
45
  ## Install Slopcamera
46
46
 
47
- Slopcamera v3.3.5 adds paid hosted image generation through Hraness Credits top-up (`slopcamera credits`, `ai image --hosted`), public hosted model discovery, and checked patent-style vector drawing sheets.
47
+ Slopcamera v3.3.6 fixes `credits wait` dropping the issued device token on pickup, on top of v3.3.5's paid hosted image generation through Hraness Credits top-up (`slopcamera credits`, `ai image --hosted`), public hosted model discovery, and checked patent-style vector drawing sheets.
48
48
 
49
49
  Install [Bun 1.3.14 or newer](https://bun.sh), then install the verified
50
- [Slopcamera v3.3.5 release](https://github.com/hraness/slopcamera/releases/tag/v3.3.5)
50
+ [Slopcamera v3.3.6 release](https://github.com/hraness/slopcamera/releases/tag/v3.3.6)
51
51
  from its canonical archive:
52
52
 
53
53
  ```sh
54
- bun add --global https://github.com/hraness/slopcamera/releases/download/v3.3.5/hraness-slopcamera-3.3.5.tgz
54
+ bun add --global https://github.com/hraness/slopcamera/releases/download/v3.3.6/hraness-slopcamera-3.3.6.tgz
55
55
  slopcamera skill install --target agents
56
56
  ```
57
57
 
@@ -134,6 +134,8 @@ architecture from dimensions, [build a parametric design](https://slopcamera.com
134
134
 
135
135
  ## What Slopcamera does
136
136
 
137
+ Slopcamera is a local visual studio for coding agents.
138
+
137
139
  ### Author scenes and direct cameras
138
140
 
139
141
  Place geometry, images, video, diagrams, and text in a portable scene. Inspect
@@ -4,7 +4,7 @@ import type { z } from "zod";
4
4
 
5
5
  import type { ApplicationContext } from "./context";
6
6
 
7
- export const SLOPCAMERA_APPLICATION_TOOL_VERSION = "slopcamera-3.3.6" as const;
7
+ export const SLOPCAMERA_APPLICATION_TOOL_VERSION = "slopcamera-3.4.0" as const;
8
8
 
9
9
  export const OPERATION_KINDS = [
10
10
  "scene.audit",
@@ -104,6 +104,19 @@ const gatewayOperations = [
104
104
  ] as const;
105
105
 
106
106
  const capabilityModules = Object.freeze([
107
+ defineSlopcameraCapabilityModule({
108
+ commands: [{ effect: "pure", name: "style list" }, { effect: "pure", name: "style show" }],
109
+ description: "Renderer-independent visual direction, delivery recommendations, and deterministic exposure timing for film and animation.",
110
+ moduleId: "slopcamera.capability.visual-style",
111
+ operationKeys: [],
112
+ profiles: [{
113
+ id: "visual-style-direction-v1", kind: "authoring",
114
+ qualification: { evidence: ["src/visual-style.test.ts", "src/visual-style.property.test.ts"], level: "unit", status: "qualified" },
115
+ requirements: [],
116
+ }],
117
+ stability: "stable", title: "Visual style direction", toolNames: [],
118
+ trust: "portable", version: 1, workflowKeys: [],
119
+ }),
107
120
  defineSlopcameraCapabilityModule({
108
121
  commands: [
109
122
  { effect: "pure", name: "operations list" },
@@ -362,7 +362,7 @@ import {
362
362
  workflowRunStore,
363
363
  } from "./workflow-runs";
364
364
 
365
- export const SLOPCAMERA_VERSION = "3.3.6";
365
+ export const SLOPCAMERA_VERSION = "3.4.0";
366
366
 
367
367
  // Legacy direct renders predate per-target output contracts. Keep them
368
368
  // bounded generously enough for long-form production while preventing one
@@ -24,6 +24,7 @@ Commands:
24
24
  image vectorize|generate|gallery|icon
25
25
  Create local SVGs, generated images, or review galleries
26
26
  html catalog|scaffold|render Author HTML scenes and export video with local audio
27
+ style list|show Discover reusable art direction and review criteria
27
28
  workflows list|show|plan|run Plan or run a reviewed reusable workflow
28
29
  code init|check|plan|run Author, preflight, and run trusted TypeScript workflows
29
30
  runs list|show|resume|approve|cancel
@@ -293,6 +294,15 @@ composes a labelled contact sheet plus receipt for agent review. Texture cells r
293
294
  candidate 2×2 so seams are inspectable (--tile/--no-tile overrides). It never replaces existing
294
295
  outputs and never promotes a candidate into authored source; review the sheet and select
295
296
  explicitly.`,
297
+ style: `Usage:
298
+ slopcamera style list [--json]
299
+ slopcamera style show <id> [--json]
300
+
301
+ Styles describe palette, shape, materials, camera, exposure cadence, finishing targets,
302
+ recommended delivery, and visual review criteria. They work across authored scenes and
303
+ generation prompts, independently of the seven HTML library profiles. Discovery is local
304
+ and read-only: it does not render, select a model, or apply an effect. A profile is art
305
+ direction, not proof of visual quality or historically authentic footage.`,
296
306
  html: `Usage:
297
307
  slopcamera html catalog [--json]
298
308
  slopcamera html scaffold <${HTML_OVERLAY_SCAFFOLD_KIND_HELP}> --output <file.html>
@@ -673,12 +683,13 @@ export function commandHelp(topic: readonly string[]): string {
673
683
 
674
684
  export function completions(words: readonly string[]): readonly string[] {
675
685
  const topLevel = [
676
- "capabilities", "operations", "diagram", "direct", "studio", "image", "html", "workflows", "code", "runs", "doctor", "ai", "credits", "media", "menubar", "support", "outputs", "recordings", "projects", "project", "scene", "inspect", "events", "edit", "analyze", "align", "faces", "fillers", "render", "assets",
686
+ "capabilities", "operations", "diagram", "direct", "studio", "image", "html", "style", "workflows", "code", "runs", "doctor", "ai", "credits", "media", "menubar", "support", "outputs", "recordings", "projects", "project", "scene", "inspect", "events", "edit", "analyze", "align", "faces", "fillers", "render", "assets",
677
687
  ];
678
688
  if (words.length <= 1) return topLevel;
679
689
  const command = words[0];
680
690
  if (command === "support") return ["protocol", "offer", "shown", "release", "dismiss", "snooze", "enable", "status"];
681
691
  if (command === "html") return ["catalog", "scaffold", "render"];
692
+ if (command === "style") return ["list", "show"];
682
693
  if (command === "direct") return ["init", "anchor", "plan", "start", "inspect", "revise", "generate", "resume", "review", "assemble", "cleanup"];
683
694
  if (command === "studio") return words[2] === "assets" || words[1] === "assets" ? ["search", "describe", "plan", "import"] : ["init", "bundle", "plan", "probe", "run", "encode", "asset", "assemble", "inspect", "reconcile", "assets"];
684
695
  if (command === "operations") return ["list", "show"];
@@ -135,6 +135,7 @@ type BrowserRuntimeSnapshotLease = Readonly<
135
135
  interface HtmlOverlayHostController {
136
136
  renderFrame(frame: HtmlOverlayRuntimeFrame): Promise<void>;
137
137
  securityViolationCount(): number;
138
+ settlePresentation(): Promise<void>;
138
139
  }
139
140
 
140
141
  interface PreparedRoute {
@@ -2332,6 +2333,21 @@ export class PlaywrightHtmlOverlayRenderer implements HtmlOverlayRenderer {
2332
2333
  );
2333
2334
  }
2334
2335
  await inspectGpu();
2336
+ // Wait for the compositor to present the frame the callbacks
2337
+ // just produced. renderFrame resolves when authored work returns,
2338
+ // not when the new surface reaches the screen; under heavy GPU
2339
+ // load a screenshot taken immediately can capture the previous
2340
+ // presented frame. Two animation frames bound the wait: the draw
2341
+ // is committed before the first callback fires and presented
2342
+ // before the second.
2343
+ await boundedBrowserStep(
2344
+ async () => await host.evaluate(
2345
+ async (controller) => await controller.settlePresentation(),
2346
+ ),
2347
+ signal,
2348
+ this.#browserStepTimeoutMs,
2349
+ `frame ${String(frameIndex)} presentation settle`,
2350
+ );
2335
2351
  await boundedBrowserStep(
2336
2352
  async () => await page.screenshot({
2337
2353
  omitBackground: HTML_OVERLAY_RENDERER_CONTRACT.screenshot.omitBackground,
@@ -220,12 +220,14 @@ export async function runPortableSurface(
220
220
  dependencies: PortableSurfaceDependencies = {},
221
221
  ): Promise<number | undefined> {
222
222
  const argv = canonicalizeUnifiedCliArgs(argvInput);
223
+ if (argv[0] === "style" && (argv.includes("--help") || argv.includes("-h"))) return undefined;
223
224
  if (argv[0] === "html") {
224
225
  if (argv[1] === "render" || argv.includes("--help") || argv.includes("-h")) return undefined;
225
226
  if (argv[1] === "catalog") return runHtmlCatalog(argv, dependencies);
226
227
  return await runHtmlScaffold(argv, dependencies);
227
228
  }
228
229
  const delegatesToHeadless = argv[0] === "diagram"
230
+ || argv[0] === "style"
229
231
  || argv[0] === "mcp"
230
232
  || argv[0] === "canvas"
231
233
  || argv[0] === "skill"
@@ -10,6 +10,7 @@ export {
10
10
  } from "./define-workflow";
11
11
  export * from "../../../src/spatial-scene/index";
12
12
  export * from "../../../src/studio/index";
13
+ export { createVisualStyleVideoLook, VisualStyleVideoLookOptionsSchema } from "../core/visual-style-look";
13
14
  export { StudioRunInputSchema, BoundStudioRunInputSchema, StudioRunOutputSchema, type StudioRunInput, type BoundStudioRunInput, type StudioRunOutput } from "../application/studio-port";
14
15
  export type { SpatialRenderInput, BoundSpatialRenderInput } from "../application/operations/spatial-render";
15
16
  export type { SpatialRenderRequest, SpatialRenderResult } from "../application/spatial-render";
@@ -0,0 +1,34 @@
1
+ import { z } from "zod";
2
+ import { getVisualStyleProfile } from "../../../src/visual-style";
3
+ import type { VideoEffectInput, VideoLookV1 } from "../contracts/video-effects";
4
+ import { createVideoLook } from "./video-effects";
5
+
6
+ export const VisualStyleVideoLookOptionsSchema = z.strictObject({
7
+ height: z.number().int().min(64).max(4320).default(1080),
8
+ seed: z.number().int().safe().min(0).max(2_147_483_647).default(20260923),
9
+ });
10
+
11
+ /**
12
+ * Translate only supported finishing controls into the existing ordered look
13
+ * graph. Scene construction, motion, gate weave, grain size, and true halation
14
+ * remain authoring decisions; screen diffusion is not a film-stock simulation.
15
+ */
16
+ export function createVisualStyleVideoLook(style: unknown, options: unknown = {}): VideoLookV1 {
17
+ const profile = getVisualStyleProfile(style);
18
+ const settings = VisualStyleVideoLookOptionsSchema.parse(options);
19
+ const finish = profile.finishing;
20
+ const effects: VideoEffectInput[] = [{
21
+ kind: "color-grade", amount: 1,
22
+ grade: { kind: "custom", controls: { saturation: finish.saturation, contrast: finish.contrast } },
23
+ }];
24
+ if (finish.halation > 0) effects.push({
25
+ kind: "diffusion", amount: finish.halation,
26
+ radiusPx: Math.max(0.25, 1.8 * settings.height / 1080), blendMode: "screen",
27
+ });
28
+ if (finish.grain > 0) effects.push({
29
+ kind: "film-grain", amount: finish.grain, seed: settings.seed,
30
+ cadence: "frame-varying", chroma: finish.saturation === 0 ? 0 : 0.12,
31
+ });
32
+ if (finish.vignette > 0) effects.push({ kind: "vignette", amount: finish.vignette });
33
+ return createVideoLook(effects);
34
+ }