@hraness/slopcamera 3.3.5 → 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.
- package/README.md +6 -4
- package/apps/desktop/application/operation.ts +1 -1
- package/apps/desktop/cli/capability-manifest.ts +13 -0
- package/apps/desktop/cli/commands.ts +22 -20
- package/apps/desktop/cli/help.ts +12 -1
- package/apps/desktop/cli/html-overlay-renderer.ts +16 -0
- package/apps/desktop/cli/portable-surface.ts +2 -0
- package/apps/desktop/code/public.ts +1 -0
- package/apps/desktop/core/visual-style-look.ts +34 -0
- package/apps/desktop/dist/cli/main.js +134 -110
- package/apps/desktop/html-overlay/runtime.ts +15 -0
- package/dist/cli.js +6 -2
- package/dist/{index-11cb717y.js → index-abnk4h9c.js} +3 -2
- package/dist/index.js +1 -1
- package/docs/README.md +1 -0
- package/examples/style-portfolio/ANIMATION-STUDIES.md +34 -0
- package/examples/style-portfolio/FILM-PLATE.md +19 -0
- package/examples/style-portfolio/README.md +137 -0
- package/examples/style-portfolio/animation-studies.html +597 -0
- package/examples/style-portfolio/assets/instrument-serif-OFL.txt +93 -0
- package/examples/style-portfolio/assets/instrument-serif-latin-400.woff2 +0 -0
- package/examples/style-portfolio/build-gallery.ts +403 -0
- package/examples/style-portfolio/film-plate.html +76 -0
- package/examples/style-portfolio/finish-film.ts +152 -0
- package/examples/style-portfolio/market-street/README.md +43 -0
- package/examples/style-portfolio/market-street/mesh.py +145 -0
- package/examples/style-portfolio/market-street/scene.py +760 -0
- package/examples/style-portfolio/market-street/source.json +1 -0
- package/examples/style-portfolio/market-street/street_data.py +72 -0
- package/examples/style-portfolio/render-film-plate.ts +128 -0
- package/examples/style-portfolio/render.ts +89 -0
- package/examples/style-portfolio/verify-animation-source.mjs +94 -0
- package/package.json +11 -2
- package/skills/slopcamera/SKILL.md +9 -0
- package/skills/slopcamera/references/gateway-media.md +5 -0
- package/skills/slopcamera/references/install.md +1 -1
- package/skills/slopcamera/references/music-video.md +5 -0
- package/skills/slopcamera/references/native-studio.md +4 -0
- package/skills/slopcamera/references/visual-style-direction.md +143 -0
- package/src/cli.ts +28 -0
- package/src/index.ts +1 -0
- package/src/version.ts +1 -1
- package/src/visual-style.ts +375 -0
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
@@ -5722,26 +5722,28 @@ async function handleCreditsWait(
|
|
|
5722
5722
|
} catch (error) {
|
|
5723
5723
|
throw creditsErrorToCli(error);
|
|
5724
5724
|
}
|
|
5725
|
+
// The issuing poll reports state "consumed" alongside the one-time token,
|
|
5726
|
+
// so the token check must precede state matching.
|
|
5727
|
+
if (status.deviceToken !== undefined) {
|
|
5728
|
+
// The token is returned exactly once; persist before reporting.
|
|
5729
|
+
await store.writeDeviceToken(status.deviceToken);
|
|
5730
|
+
await store.clearPendingClaim();
|
|
5731
|
+
const view = {
|
|
5732
|
+
claimId: pending.claimId,
|
|
5733
|
+
configured: true,
|
|
5734
|
+
state: "paid" as const,
|
|
5735
|
+
next: ["slopcamera credits status", "slopcamera ai image generate --hosted --model <id> --prompt <text>"],
|
|
5736
|
+
};
|
|
5737
|
+
writeValue(context.io, command.json, view, () => [
|
|
5738
|
+
"paid\tcredits wallet is configured on this device",
|
|
5739
|
+
"",
|
|
5740
|
+
"Next:",
|
|
5741
|
+
" slopcamera credits status",
|
|
5742
|
+
" slopcamera ai image generate --hosted --model <id> --prompt <text>",
|
|
5743
|
+
].join("\n"));
|
|
5744
|
+
return;
|
|
5745
|
+
}
|
|
5725
5746
|
if (status.state === "paid") {
|
|
5726
|
-
if (status.deviceToken !== undefined) {
|
|
5727
|
-
// The token is returned exactly once; persist before reporting.
|
|
5728
|
-
await store.writeDeviceToken(status.deviceToken);
|
|
5729
|
-
await store.clearPendingClaim();
|
|
5730
|
-
const view = {
|
|
5731
|
-
claimId: pending.claimId,
|
|
5732
|
-
configured: true,
|
|
5733
|
-
state: "paid" as const,
|
|
5734
|
-
next: ["slopcamera credits status", "slopcamera ai image generate --hosted --model <id> --prompt <text>"],
|
|
5735
|
-
};
|
|
5736
|
-
writeValue(context.io, command.json, view, () => [
|
|
5737
|
-
"paid\tcredits wallet is configured on this device",
|
|
5738
|
-
"",
|
|
5739
|
-
"Next:",
|
|
5740
|
-
" slopcamera credits status",
|
|
5741
|
-
" slopcamera ai image generate --hosted --model <id> --prompt <text>",
|
|
5742
|
-
].join("\n"));
|
|
5743
|
-
return;
|
|
5744
|
-
}
|
|
5745
5747
|
await store.clearPendingClaim();
|
|
5746
5748
|
const view = {
|
|
5747
5749
|
claimId: pending.claimId,
|
package/apps/desktop/cli/help.ts
CHANGED
|
@@ -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
|
+
}
|