From 6e070ae7f42ef39a4aaed4d437184f3a5cfcffb2 Mon Sep 17 00:00:00 2001 From: 0thernet Date: Fri, 2 Oct 2026 12:26:05 -0400 Subject: [PATCH 1/5] Sharpen README, docs entry points and npm metadata for discovery Lead the README with the canonical definition and a techniques link instead of "Watch the films", give sections headings that match real searches, add a FAQ, and improve the hero alt text. Name Claude Code, Codex, Blender and the MCP server in the npm description and docs indexes, and add matching npm keywords; the standalone check pins the new description and keywords. Co-Authored-By: Claude Opus 5.5 --- README.md | 79 +++++++++++++++++++++++++++++-------- apps/web/src/docs/index.md | 2 + docs/README.md | 2 + package.json | 11 +++++- scripts/check-standalone.ts | 9 ++++- 5 files changed, 83 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index fc6a06ec..25ee3a70 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,26 @@ # SlopCamera -[![Rain, bottled: a cloud rains inside a glass vessel with brass fittings](apps/web/media/rain-bottled-poster-479a32b54a03.webp)](https://slopcamera.com/docs/how-to/remix-the-showcase#rain-bottled) +[![Rain, bottled: a still from a SlopCamera Blender showcase film, in which a cloud rains inside a glass vessel with brass fittings](apps/web/media/rain-bottled-poster-479a32b54a03.webp)](https://slopcamera.com/docs/how-to/remix-the-showcase#rain-bottled) **A multimedia studio for your coding agent.** SlopCamera lets your coding agent make images, diagrams, animation, 3D scenes, and video from source files -it can keep revising. Use it with Codex, Claude Code, or another coding agent. -Direct the style, timing, camera, and sound, then refine the result. +it can keep revising. It is a free, open-source Bun CLI, TypeScript SDK, +Agent Skill, and MCP server for Codex, Claude Code, and other coding agents. -Your agent writes the scene or edit as source. SlopCamera renders it with the -appropriate engine and keeps the inputs so the agent can change a shot, -rework a composition, or deliver another format. Combine authored scenes, -generated media, and your own footage in the same project. +Your agent writes the scene, diagram, or edit as a short source file. +SlopCamera checks it, renders it with the right engine on your machine, and +keeps the inputs, so the next change is an edit to that file and a new render. +Combine authored scenes, generated media, and your own footage in one project. -[Watch the films](https://slopcamera.com/#examples) · [Install](#install-slopcamera) · [Direct a film](https://slopcamera.com/docs/how-to/direct-a-film) · [Docs](https://slopcamera.com/docs) · [GitHub examples](examples/showcase) +[Install](#install-slopcamera) · [Techniques](https://slopcamera.com/docs/reference/techniques) · [First diagram](https://slopcamera.com/docs/tutorials/first-diagram) · [Docs](https://slopcamera.com/docs) · [Examples](examples/showcase) -## What you can make +## What you can make with SlopCamera - **Films and animation.** Author motion graphics, illustrated scenes, and native Blender shots. Control camera, materials, lighting, and timing. -- **Visual explanations.** Make diagrams and educational films with editable +- **Diagrams as code.** Write diagram JSON and get an editable tldraw file, + light and dark SVGs, and PNGs after a strict check. +- **Visual explanations.** Make educational films with Manim, editable labels, mathematics, narration, and graphics. - **Images and designs.** Create illustrations, trace raster artwork to SVG, or revise a parametric architectural model. @@ -104,7 +106,7 @@ CLI updates in SlopCamera 3.10.0 and newer are enabled by default for supported Bun and npm global installations. See [update controls](docs/reference/capabilities.md#cli-updates) to keep a version or check for a release. -## Make a film. Then direct it again. +## First project: make a film, then direct it again [Last tram to the moon](https://slopcamera.com/docs/tutorials/first-animation) is a complete first project: original illustrated artwork, a twelve-second @@ -127,7 +129,7 @@ production instructions, and a suggested creative revision. For a smaller first task, [make a diagram](https://slopcamera.com/docs/tutorials/first-diagram) and export its editable tldraw document, light and dark SVGs, and PNGs. -## What SlopCamera does +## Techniques: diagrams, motion graphics, 3D, and video Each technique pairs a short source file the agent writes with commands that render and check it. The @@ -155,13 +157,13 @@ own provider access, and cinematic effects, particles, and character performance can be planned from source. The gallery has no rendered examples of these yet. -### Film native worlds and educational animation +### Blender, CadQuery, and Manim for coding agents SlopCamera can direct Blender for sets, materials, lighting, skinned characters, cloth, and liquid caches; CadQuery for parametric solids and STEP; and Manim Community for mathematical animation. Seven editable starters include a product, character, shaded street, cloth, liquid, CAD bracket, and -educational presenter. Watch the +educational presenter. Open [Rain, bottled](https://slopcamera.com/docs/how-to/remix-the-showcase#rain-bottled), then change its [weather, materials, and camera](examples/showcase/studio-relaunch/rain-bottled). For 2D motion, try the @@ -174,7 +176,7 @@ separately, and native source execution requires explicit current-user trust. See [Native film studio](docs/studio.md) for the starters and engine requirements. -### Generate media +### Generate images, video, and speech Discover image, video, speech, and transcription models through your own Vercel AI Gateway access, with `AI_GATEWAY_API_KEY` set in the local process @@ -192,7 +194,7 @@ or OpenAI key with `slopcamera image generate --provider …`. The continuous landscapes or linked image sections and produces a transparent pixel mask that can take a site's tint. -### Edit footage and deliver finished videos +### Edit footage and deliver every aspect ratio Import existing footage or recording bundles. Remove pauses and filler words, align sound, reframe speakers, zoom into screen actions, and add captions, @@ -249,7 +251,7 @@ changes, inspect the resulting files, and report their paths. A successful plan does not show that provider access, native trust, or model quality is in place. -## Compared with other tools +## SlopCamera vs Remotion and HyperFrames Details as of 28 September 2026. @@ -313,6 +315,49 @@ thread through hraness](https://hraness.com/writing/the-thread-through-hraness) follows it across projects, and the [ALGAL vision](https://algal.computer/docs/vision/) states the bet behind it. +## Frequently asked questions + +### What is SlopCamera? + +SlopCamera is an open-source CLI, TypeScript SDK, Agent Skill, and MCP server +that lets coding agents make images, diagrams, animation, 3D scenes, and +video. The agent writes a short source file for a diagram, HTML scene, 3D +scene, native Blender, CadQuery, or Manim program, or video edit, and the +`slopcamera` CLI renders it. A revision is an edit to that file and a new render. + +### Does SlopCamera work with Claude Code and Codex? + +Yes. Install the CLI, then run `slopcamera skill install --target claude` for +Claude Code, or `slopcamera skill install` for Codex. Other agents that read +`~/.agents/skills` use `--target agents`, and MCP clients can connect to +`slopcamera mcp`. + +### Can a coding agent make AI video with SlopCamera? + +Yes, through your own Vercel AI Gateway access: the agent can generate short +video shots, images, and voiceover, and combine them with authored scenes and +your footage. Local editing and rendering need no model account. + +### Can my agent drive Blender? + +Yes. SlopCamera directs an installed Blender for sets, materials, lighting, +skinned characters, cloth, and liquid caches, starting from editable +starters. Blender installs separately, and native source runs only with +explicit current-user trust. + +### Is SlopCamera a Remotion alternative? + +It depends on the job. Choose Remotion when your team writes React components +and renders at scale. Choose SlopCamera when one agent needs diagrams, 3D, +native films, and footage edits in one local project. See the +[comparison above](#slopcamera-vs-remotion-and-hyperframes). + +### What does SlopCamera cost? + +SlopCamera is free and open source under the MIT license. Generation uses your +own Vercel AI Gateway account, or prepaid Hraness Credits for prompt-only +hosted images, and model usage is billed separately. + ## Documentation - **Learn:** [Your first diagram](docs/tutorials/first-diagram.md) · [Your first native film](docs/tutorials/first-native-film.md). diff --git a/apps/web/src/docs/index.md b/apps/web/src/docs/index.md index 0f876cd5..75aefea6 100644 --- a/apps/web/src/docs/index.md +++ b/apps/web/src/docs/index.md @@ -1,5 +1,7 @@ Slopcamera is a media studio for coding agents. Your agent writes a short source file, the CLI checks and renders it on your machine, and a revision is an edit to that file and a new render. These pages cover the CLI, SDK and Agent Skill. +It works with Claude Code, Codex, Cursor, and other coding agents through the CLI, the Agent Skill, and an MCP server. The source can be a diagram, an HTML animation, a 3D scene, a Blender, CadQuery, or Manim program, or edit decisions over your own footage. + Install the v{{PUBLISHED_VERSION}} release, or [build from source](/docs/how-to/install-from-source) to develop Slopcamera: ```sh diff --git a/docs/README.md b/docs/README.md index a80e2bd4..ee0cff7a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,6 +2,8 @@ SlopCamera is a media studio for coding agents. Your agent works with editable source and a media project. The CLI checks and renders them, and a revision keeps those inputs available for the next version. Choose a guide for the work you want to do. +It works with Claude Code, Codex, Cursor, and other coding agents through the CLI, the Agent Skill, and an MCP server. The source can be a diagram, an HTML animation, a 3D scene, a Blender, CadQuery, or Manim program, or edit decisions over your own footage. + Install the [current SlopCamera release](../README.md#install-slopcamera) for the CLI, SDK and matching Agent Skill. Each guide lists the tools it needs and explains when an example requires a source checkout. The [capability reference](reference/capabilities.md) covers formats, interfaces and local runtime requirements. ## Learn by making something diff --git a/package.json b/package.json index 698c234b..e80db251 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@hraness/slopcamera", "version": "3.11.0", - "description": "SlopCamera lets your coding agent make images, diagrams, animation, 3D scenes, and video from source files it can keep revising.", + "description": "SlopCamera lets Codex, Claude Code, and other coding agents make images, diagrams, animation, 3D scenes, Blender films, and edited video from source files they can keep revising.", "license": "MIT", "type": "module", "sideEffects": [ @@ -77,7 +77,14 @@ "cli", "typescript", "bun", - "local-first" + "local-first", + "claude-code", + "codex", + "blender", + "manim", + "3d", + "animation", + "diagram-as-code" ], "exports": { ".": { diff --git a/scripts/check-standalone.ts b/scripts/check-standalone.ts index 92e40a02..b6601b53 100644 --- a/scripts/check-standalone.ts +++ b/scripts/check-standalone.ts @@ -325,7 +325,7 @@ const problems = [ ...inventoryUpdate.problems, ]; const rootPackage = await readJson(join(ROOT, "package.json")); -const expectedDescription = "SlopCamera lets your coding agent make images, diagrams, animation, 3D scenes, and video from source files it can keep revising."; +const expectedDescription = "SlopCamera lets Codex, Claude Code, and other coding agents make images, diagrams, animation, 3D scenes, Blender films, and edited video from source files they can keep revising."; const expectedKeywords = [ "ai-media-generation", "ai-video-generation", @@ -345,6 +345,13 @@ const expectedKeywords = [ "typescript", "bun", "local-first", + "claude-code", + "codex", + "blender", + "manim", + "3d", + "animation", + "diagram-as-code", ] as const; if (rootPackage.name !== "@hraness/slopcamera") { problems.push("package.json name must be @hraness/slopcamera"); From 40d3368f8e7f4adedab8b56f4b201e12170b51dc Mon Sep 17 00:00:00 2001 From: 0thernet Date: Fri, 2 Oct 2026 12:50:00 -0400 Subject: [PATCH 2/5] Standardize public copy on the canonical name Slopcamera Rename SlopCamera to Slopcamera across the README, npm description, docs indexes, site copy, metadata, skill prose and CLI messages. Move the pinned values deliberately: the canonical npm description in check-standalone, six legacy-identity line hashes (counts unchanged), the support-anchor digest, and the homepage, llms.txt and README parity expectations. Code identifiers, package and CLI names and paths are unchanged. Blog bodies and launch beats stay as reviewed until a fresh editorial review. Co-Authored-By: Claude Opus 5.5 --- README.md | 62 +++++++++---------- STYLE.md | 2 +- apps/api/README.md | 2 +- apps/api/src/openapi.ts | 4 +- apps/desktop/README.md | 2 +- apps/desktop/cli/update-verification.ts | 18 +++--- apps/desktop/cli/update.test.ts | 2 +- apps/desktop/dist/cli/main.js | 2 +- apps/web/README.md | 2 +- apps/web/package.json | 2 +- apps/web/scripts/preview-layout.test.ts | 2 +- apps/web/scripts/site-copy-content.test.ts | 2 +- .../site-support-browser-contract.test.ts | 2 +- .../scripts/site-support-browser-contract.ts | 2 +- apps/web/site.test.ts | 26 ++++---- apps/web/src/agent-pages.ts | 6 +- apps/web/src/docs-registry.ts | 46 +++++++------- ...remotion-alternatives-for-coding-agents.md | 6 +- .../explanation/slopcamera-vs-hyperframes.md | 10 +-- .../explanation/slopcamera-vs-remotion.md | 10 +-- apps/web/src/docs/explanation/use-cases.md | 2 +- .../src/docs/explanation/why-slopcamera.md | 2 +- apps/web/src/docs/reference/capabilities.md | 18 +++--- .../src/portfolio-messaging.generated.json | 10 +-- apps/web/src/preview.html | 8 +-- apps/web/src/site-content.ts | 4 +- apps/web/src/social-image.ts | 12 ++-- docs/README.md | 16 ++--- docs/architecture.md | 2 +- docs/publishing.md | 2 +- docs/reference/capabilities.md | 14 ++--- docs/research/tesseract-public-ideas.md | 4 +- examples/html/launch-film/README.md | 2 +- examples/pixel-landscape/README.md | 6 +- .../studio-relaunch/one-shoot/README.md | 6 +- package.json | 2 +- scripts/check-standalone.ts | 2 +- scripts/legacy-identity.inventory.json | 12 ++-- scripts/package-smoke.ts | 4 +- scripts/standalone-support-fixture.test.ts | 2 +- scripts/standalone-support-fixture.ts | 2 +- skills/slopcamera/SKILL.md | 4 +- skills/slopcamera/references/install.md | 2 +- skills/slopcamera/references/launch-films.md | 2 +- .../references/visual-communication.md | 2 +- 45 files changed, 176 insertions(+), 176 deletions(-) diff --git a/README.md b/README.md index 25ee3a70..49d2376d 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,20 @@ -# SlopCamera +# Slopcamera -[![Rain, bottled: a still from a SlopCamera Blender showcase film, in which a cloud rains inside a glass vessel with brass fittings](apps/web/media/rain-bottled-poster-479a32b54a03.webp)](https://slopcamera.com/docs/how-to/remix-the-showcase#rain-bottled) +[![Rain, bottled: a still from a Slopcamera Blender showcase film, in which a cloud rains inside a glass vessel with brass fittings](apps/web/media/rain-bottled-poster-479a32b54a03.webp)](https://slopcamera.com/docs/how-to/remix-the-showcase#rain-bottled) -**A multimedia studio for your coding agent.** SlopCamera lets your coding +**A multimedia studio for your coding agent.** Slopcamera lets your coding agent make images, diagrams, animation, 3D scenes, and video from source files it can keep revising. It is a free, open-source Bun CLI, TypeScript SDK, Agent Skill, and MCP server for Codex, Claude Code, and other coding agents. Your agent writes the scene, diagram, or edit as a short source file. -SlopCamera checks it, renders it with the right engine on your machine, and +Slopcamera checks it, renders it with the right engine on your machine, and keeps the inputs, so the next change is an edit to that file and a new render. Combine authored scenes, generated media, and your own footage in one project. [Install](#install-slopcamera) · [Techniques](https://slopcamera.com/docs/reference/techniques) · [First diagram](https://slopcamera.com/docs/tutorials/first-diagram) · [Docs](https://slopcamera.com/docs) · [Examples](examples/showcase) -## What you can make with SlopCamera +## What you can make with Slopcamera - **Films and animation.** Author motion graphics, illustrated scenes, and native Blender shots. Control camera, materials, lighting, and timing. @@ -28,15 +28,15 @@ Combine authored scenes, generated media, and your own footage in one project. then export horizontal, vertical, square, or portrait versions. The [techniques catalog](https://slopcamera.com/docs/reference/techniques) -connects each job to its supported commands and requirements. SlopCamera is -free and open source. Local editing and rendering need no SlopCamera account. +connects each job to its supported commands and requirements. Slopcamera is +free and open source. Local editing and rendering need no Slopcamera account. Generation uses your own Vercel AI Gateway account, or prepaid Hraness Credits for prompt-only hosted images; model usage is billed separately. -## Install SlopCamera +## Install Slopcamera Install [Bun 1.3.14 or newer](https://bun.sh), then install the -[SlopCamera v3.10.3 release](https://github.com/hraness/slopcamera/releases/tag/v3.10.3) +[Slopcamera v3.10.3 release](https://github.com/hraness/slopcamera/releases/tag/v3.10.3) and its matching Agent Skill: ```sh @@ -102,7 +102,7 @@ placement and resuming long runs. -CLI updates in SlopCamera 3.10.0 and newer are enabled by default for supported +CLI updates in Slopcamera 3.10.0 and newer are enabled by default for supported Bun and npm global installations. See [update controls](docs/reference/capabilities.md#cli-updates) to keep a version or check for a release. @@ -136,7 +136,7 @@ render and check it. The [techniques reference](https://slopcamera.com/docs/reference/techniques) lists them all with their requirements. -| Technique | The agent writes | SlopCamera renders | Example | +| Technique | The agent writes | Slopcamera renders | Example | | --- | --- | --- | --- | | Diagrams | Diagram JSON | `.tldr`, light and dark SVG and PNG, after a strict check | [Pipeline](https://slopcamera.com/docs/tutorials/first-diagram#stack-layout) | | Vector tracing | A path to a raster image | SVG traced locally with VTracer (macOS and Linux) | [Color and duotone](https://slopcamera.com/docs/how-to/vectorize-images#inspect-a-reproducible-example) | @@ -148,7 +148,7 @@ them all with their requirements. | Explainer video | A Manim scene, captions, and a presenter | A lesson video with typeset math | [Geometry lesson](https://slopcamera.com/docs/how-to/educational-video#inspect-the-finished-example) | | Footage edits | Cuts, captions, reframes, and color as project decisions | 16:9, 9:16, 1:1, and 4:5 exports from one edit | [Four formats](https://slopcamera.com/docs/how-to/edit-video#landscape-delivery) | -SlopCamera also composes vector icon scenes with the bundled icon.place library +Slopcamera also composes vector icon scenes with the bundled icon.place library and reads Soundfish scores or MIDI files into beat grids for music-timed scenes, both locally and without a model. Neither renders audio. @@ -159,7 +159,7 @@ of these yet. ### Blender, CadQuery, and Manim for coding agents -SlopCamera can direct Blender for sets, materials, lighting, skinned +Slopcamera can direct Blender for sets, materials, lighting, skinned characters, cloth, and liquid caches; CadQuery for parametric solids and STEP; and Manim Community for mathematical animation. Seven editable starters include a product, character, shaded street, cloth, liquid, CAD bracket, and @@ -205,7 +205,7 @@ and captioned versions in each format from the same edit. > the speaker framed, add captions and `logo.svg`, and show a preview before > export. -SlopCamera edits recordings you already have; it does not record the screen, +Slopcamera edits recordings you already have; it does not record the screen, camera, or microphone. Standalone media imports need an existing project; `html render`, `studio assemble`, or `direct assemble` can create one. Read [`PRIVACY.md`](PRIVACY.md) before @@ -220,12 +220,12 @@ slopcamera workflows show social-variants --json Start with [Edit a video](docs/how-to/edit-video.md) or [Run workflows](docs/how-to/run-workflows.md). -## How SlopCamera works +## How Slopcamera works Each kind of work keeps its own source file. A native scene holds a rig or simulation; a portable scene holds geometry, cameras, and media placement; a diagram holds its shapes and labels; a video project holds cuts and delivery -settings. SlopCamera connects them through files: a rendered diagram can +settings. Slopcamera connects them through files: a rendered diagram can appear on a screen in a 3D scene, and native frames can become a clip in a video project. @@ -251,22 +251,22 @@ changes, inspect the resulting files, and report their paths. A successful plan does not show that provider access, native trust, or model quality is in place. -## SlopCamera vs Remotion and HyperFrames +## Slopcamera vs Remotion and HyperFrames Details as of 28 September 2026. | | Agent writes | License | Cloud rendering | Choose it when | | --- | --- | --- | --- | --- | -| SlopCamera | Diagram JSON, HTML scenes, scene JSON, Blender, CadQuery, or Manim programs, and video edits | MIT | Video renders on your machine; a hosted API checks and renders diagrams and generates images | One agent needs diagrams, 3D, native films, and footage edits in one local project | +| Slopcamera | Diagram JSON, HTML scenes, scene JSON, Blender, CadQuery, or Manim programs, and video edits | MIT | Video renders on your machine; a hosted API checks and renders diagrams and generates images | One agent needs diagrams, 3D, native films, and footage edits in one local project | | [Remotion](https://www.remotion.dev/) | React components | Source-available; free for individuals, non-profits, and organizations of up to 3 people | AWS Lambda in your account | Your team writes React and renders at scale | | [HyperFrames](https://github.com/heygen-com/hyperframes) | HTML, CSS, and JavaScript animation | Apache 2.0 | HeyGen-hosted rendering or AWS Lambda | You want HTML motion graphics rendered to MP4 | Remotion and HyperFrames have much larger communities. For a one-off image or clip, a hosted app needs no install. Read -[SlopCamera vs Remotion](https://slopcamera.com/docs/explanation/slopcamera-vs-remotion), -[SlopCamera vs HyperFrames](https://slopcamera.com/docs/explanation/slopcamera-vs-hyperframes), +[Slopcamera vs Remotion](https://slopcamera.com/docs/explanation/slopcamera-vs-remotion), +[Slopcamera vs HyperFrames](https://slopcamera.com/docs/explanation/slopcamera-vs-hyperframes), [Remotion alternatives for coding agents](https://slopcamera.com/docs/explanation/remotion-alternatives-for-coding-agents), -or [Why SlopCamera](https://slopcamera.com/docs/explanation/why-slopcamera). +or [Why Slopcamera](https://slopcamera.com/docs/explanation/why-slopcamera). ## Important limitations @@ -292,7 +292,7 @@ or [Why SlopCamera](https://slopcamera.com/docs/explanation/why-slopcamera). ## Design and trust -There is no SlopCamera account or hosted project database. Video editing and +There is no Slopcamera account or hosted project database. Video editing and rendering run on your machine; the optional hosted API checks and renders diagrams and generates images. Gateway generation and cloud analysis use credentials from the local process and ask for your @@ -317,22 +317,22 @@ vision](https://algal.computer/docs/vision/) states the bet behind it. ## Frequently asked questions -### What is SlopCamera? +### What is Slopcamera? -SlopCamera is an open-source CLI, TypeScript SDK, Agent Skill, and MCP server +Slopcamera is an open-source CLI, TypeScript SDK, Agent Skill, and MCP server that lets coding agents make images, diagrams, animation, 3D scenes, and video. The agent writes a short source file for a diagram, HTML scene, 3D scene, native Blender, CadQuery, or Manim program, or video edit, and the `slopcamera` CLI renders it. A revision is an edit to that file and a new render. -### Does SlopCamera work with Claude Code and Codex? +### Does Slopcamera work with Claude Code and Codex? Yes. Install the CLI, then run `slopcamera skill install --target claude` for Claude Code, or `slopcamera skill install` for Codex. Other agents that read `~/.agents/skills` use `--target agents`, and MCP clients can connect to `slopcamera mcp`. -### Can a coding agent make AI video with SlopCamera? +### Can a coding agent make AI video with Slopcamera? Yes, through your own Vercel AI Gateway access: the agent can generate short video shots, images, and voiceover, and combine them with authored scenes and @@ -340,21 +340,21 @@ your footage. Local editing and rendering need no model account. ### Can my agent drive Blender? -Yes. SlopCamera directs an installed Blender for sets, materials, lighting, +Yes. Slopcamera directs an installed Blender for sets, materials, lighting, skinned characters, cloth, and liquid caches, starting from editable starters. Blender installs separately, and native source runs only with explicit current-user trust. -### Is SlopCamera a Remotion alternative? +### Is Slopcamera a Remotion alternative? It depends on the job. Choose Remotion when your team writes React components -and renders at scale. Choose SlopCamera when one agent needs diagrams, 3D, +and renders at scale. Choose Slopcamera when one agent needs diagrams, 3D, native films, and footage edits in one local project. See the [comparison above](#slopcamera-vs-remotion-and-hyperframes). -### What does SlopCamera cost? +### What does Slopcamera cost? -SlopCamera is free and open source under the MIT license. Generation uses your +Slopcamera is free and open source under the MIT license. Generation uses your own Vercel AI Gateway account, or prepaid Hraness Credits for prompt-only hosted images, and model usage is billed separately. diff --git a/STYLE.md b/STYLE.md index 225efd37..53e203bc 100644 --- a/STYLE.md +++ b/STYLE.md @@ -192,7 +192,7 @@ Readers trust a page that states its limits plainly. They skim a page that repea - Give a product's article collection one authored visual direction: consistent drawing technique, line weight, texture, framing, and degree of abstraction. Start with a reviewed image from the collection and the product's brand palette. - Use a quiet neutral ground, one dominant brand color, and at most one supporting accent. Keep the accent subordinate and use tonal variations for depth. Avoid unrelated bright colors, glossy stock-art treatments, and a different style for each topic. -- Generate editorial artwork through [SlopCamera](https://slopcamera.com). Retain its authored prompt, reference assets, and generation record with the source. Inspect the image at article and card sizes in both page themes before accepting it. +- Generate editorial artwork through [Slopcamera](https://slopcamera.com). Retain its authored prompt, reference assets, and generation record with the source. Inspect the image at article and card sizes in both page themes before accepting it. - Make each illustration explain the article's central idea through one clear visual relationship. Avoid generic technology collages, decorative interface fragments, and text baked into images. A diagram may use labels when those labels carry the explanation. - Give every illustrated article a complete, intentional card and lead-image treatment. Preserve the actual generation history when replacing an old asset; credit the tool that made the new image and link its current public site. diff --git a/apps/api/README.md b/apps/api/README.md index 8c7f72f1..d92222ec 100644 --- a/apps/api/README.md +++ b/apps/api/README.md @@ -1,6 +1,6 @@ # @hraness/slopcamera-api -Hosted SlopCamera tool surface for agent platforms. Serves the canonical MCP tool registry over REST (`/v1/tools/{name}/call`) and a stateless MCP endpoint (`/v1/mcp`), with R2 artifact tickets and Credits-billed image generation. +Hosted Slopcamera tool surface for agent platforms. Serves the canonical MCP tool registry over REST (`/v1/tools/{name}/call`) and a stateless MCP endpoint (`/v1/mcp`), with R2 artifact tickets and Credits-billed image generation. ```sh bun run dev # http://localhost:8787 diff --git a/apps/api/src/openapi.ts b/apps/api/src/openapi.ts index 8f885be3..4f7d5fc6 100644 --- a/apps/api/src/openapi.ts +++ b/apps/api/src/openapi.ts @@ -9,10 +9,10 @@ export function openApiDocument(baseUrl: string): Record { return { openapi: "3.1.0", info: { - title: "SlopCamera hosted tools", + title: "Slopcamera hosted tools", version: SLOPCAMERA_VERSION, description: - "Bounded SlopCamera tool surface for agent platforms. Authoring, validation, planning and audit tools run free with rate limits; image generation is billed through Hraness Credits. Binary artifacts return as ticketed URLs backed by short-lived object storage, never inline.", + "Bounded Slopcamera tool surface for agent platforms. Authoring, validation, planning and audit tools run free with rate limits; image generation is billed through Hraness Credits. Binary artifacts return as ticketed URLs backed by short-lived object storage, never inline.", }, servers: [{ url: baseUrl }], paths: { diff --git a/apps/desktop/README.md b/apps/desktop/README.md index 1627fc67..77c172ce 100644 --- a/apps/desktop/README.md +++ b/apps/desktop/README.md @@ -1,4 +1,4 @@ -# SlopCamera local host and CLI +# Slopcamera local host and CLI Slopcamera reads finished recording bundles from repository-local directories, combines independent camera and audio takes on one typed project clock, then applies non-destructive edits through the `slopcamera` CLI. The CLI is the product surface for agents. `slopcamera status` and `slopcamera tui` show what is running, credits and the newest agent outputs in the terminal. diff --git a/apps/desktop/cli/update-verification.ts b/apps/desktop/cli/update-verification.ts index 930ac788..9408516c 100644 --- a/apps/desktop/cli/update-verification.ts +++ b/apps/desktop/cli/update-verification.ts @@ -14,13 +14,13 @@ const runGh: Gh = args => new Promise((resolve, reject) => { env: { ...process.env, GH_HOST: "github.com", GH_PROMPT_DISABLED: "1", GH_PAGER: "cat" }, }, (error, stdout) => { // gh's error can include authenticated response bodies. Keep diagnostics fixed. - if (error !== null) reject(new Error("SlopCamera release verification failed. Check GitHub CLI access and retry slopcamera update.")); + if (error !== null) reject(new Error("Slopcamera release verification failed. Check GitHub CLI access and retry slopcamera update.")); else resolve(stdout); }); }); function record(value: unknown): Record { - if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("Invalid SlopCamera release metadata."); + if (typeof value !== "object" || value === null || Array.isArray(value)) throw new Error("Invalid Slopcamera release metadata."); return value as Record; } @@ -30,27 +30,27 @@ export async function verifySlopcameraUpdate( gh: Gh = runGh, ): Promise { if (!/^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$/u.test(artifact.version) - || artifact.version.length > 50 || !/^[a-f0-9]{64}$/u.test(artifact.sha256)) throw new Error("Invalid SlopCamera update identity."); + || artifact.version.length > 50 || !/^[a-f0-9]{64}$/u.test(artifact.sha256)) throw new Error("Invalid Slopcamera update identity."); const tag = `v${artifact.version}`; const json = async (path: string) => { const bytes = await gh(["api", path]); - if (bytes.length > maximumMetadataBytes) throw new Error("SlopCamera release metadata is too large."); + if (bytes.length > maximumMetadataBytes) throw new Error("Slopcamera release metadata is too large."); return record(JSON.parse(bytes.toString("utf8")) as unknown); }; await gh(["release", "verify", tag, "--repo", repository]); await gh(["release", "verify-asset", tag, artifact.path, "--repo", repository]); const reference = record((await json(`repos/${repository}/git/ref/tags/${tag}`)).object); if (reference.type !== "tag" || typeof reference.sha !== "string" || !/^[a-f0-9]{40}$/u.test(reference.sha)) { - throw new Error("SlopCamera updates require an annotated release tag."); + throw new Error("Slopcamera updates require an annotated release tag."); } const annotated = await json(`repos/${repository}/git/tags/${reference.sha}`); const source = record(annotated.object); if (annotated.tag !== tag || source.type !== "commit" || typeof source.sha !== "string" || !/^[a-f0-9]{40}$/u.test(source.sha)) { - throw new Error("SlopCamera release tag does not identify its source commit."); + throw new Error("Slopcamera release tag does not identify its source commit."); } const release = await json(`repos/${repository}/releases/tags/${tag}`); if (release.tag_name !== tag || release.immutable !== true || !Array.isArray(release.assets)) { - throw new Error("SlopCamera release is not immutable."); + throw new Error("Slopcamera release is not immutable."); } const bundles = release.assets.map(record).filter(asset => asset.name === "provenance.jsonl"); const bundle = bundles[0]; @@ -58,11 +58,11 @@ export async function verifySlopcameraUpdate( || typeof bundle.id !== "number" || !Number.isSafeInteger(bundle.id) || bundle.id <= 0 || typeof bundle.size !== "number" || !Number.isSafeInteger(bundle.size) || bundle.size < 1 || bundle.size > maximumMetadataBytes || typeof bundle.digest !== "string" || !/^sha256:[a-f0-9]{64}$/u.test(bundle.digest)) { - throw new Error("SlopCamera release lacks a checked provenance bundle."); + throw new Error("Slopcamera release lacks a checked provenance bundle."); } const bytes = await gh(["api", `repos/${repository}/releases/assets/${bundle.id}`, "--header", "Accept: application/octet-stream"]); if (bytes.length !== bundle.size || `sha256:${createHash("sha256").update(bytes).digest("hex")}` !== bundle.digest) { - throw new Error("SlopCamera provenance bundle differs from the immutable release."); + throw new Error("Slopcamera provenance bundle differs from the immutable release."); } const directory = await mkdtemp(join(tmpdir(), "slopcamera-update-verification-")); try { diff --git a/apps/desktop/cli/update.test.ts b/apps/desktop/cli/update.test.ts index 0d442916..a767d808 100644 --- a/apps/desktop/cli/update.test.ts +++ b/apps/desktop/cli/update.test.ts @@ -40,7 +40,7 @@ test("a product failure still releases its installation", async () => { import { slopcameraUpdatePolicy } from "./update-policy"; -test("SlopCamera keeps local media offline and does not infer help for portable work", () => { +test("Slopcamera keeps local media offline and does not infer help for portable work", () => { for (const argv of [[], ["--help"], ["help", "render"], ["render", "--help"], ["--version"], ["__complete", "render"], ["diagram", "--help"]]) { expect(slopcameraUpdatePolicy(argv).effectFree).toBe(true); } diff --git a/apps/desktop/dist/cli/main.js b/apps/desktop/dist/cli/main.js index 4155caa1..a8380f1c 100755 --- a/apps/desktop/dist/cli/main.js +++ b/apps/desktop/dist/cli/main.js @@ -5728,4 +5728,4 @@ out/ |______| `:""}var JG1={};import{lstat as rV1,realpath as BU0}from"fs/promises";import{tmpdir as oV1}from"os";import{basename as iV1,dirname as aV1}from"path";async function eV1(){let $;try{W70();let Z=await $G1(),_=QG1(Z),X=ZG1(_.input),J=await _G1(_.temporaryRoot),F=await C70(_.options.inheritedFileDescriptors,async()=>await r70(X,_.options,J));$={ok:!0,protocol:N7,result:F}}catch(Z){$={error:XG1(Z),ok:!1,protocol:N7}}let Q=JSON.stringify($);if(Buffer.byteLength(Q)>Vz)Q=JSON.stringify({error:{code:"output_limit",details:{maximumBytes:Vz},message:"The vectorization worker response exceeds its IPC limit."},ok:!1,protocol:N7});await Bun.write(Bun.stdout,Q)}async function $G1(){let $=Bun.stdin.stream().getReader(),Q=[],Z=0;try{while(!0){let{done:_,value:X}=await $.read();if(_)break;if(Z+=X.byteLength,Z>MW)throw await $.cancel(),new _0("input_limit","The vectorization worker request exceeds its IPC limit.",{bytes:Z,maximumBytes:MW});Q.push(X)}}finally{$.releaseLock()}return Buffer.concat(Q,Z).toString("utf8")}function QG1($){let Q;try{Q=JSON.parse($)}catch(Z){throw new _0("invalid_input","The vectorization worker request is malformed.",{},{cause:Z})}if(!Ti(Q)||Q.protocol!==N7||!Ti(Q.input)||!Ti(Q.options)||typeof Q.temporaryRoot!=="string"||Q.temporaryRoot.length===0||typeof Q.input.kind!=="string"||typeof Q.input.value!=="string"||!["bytes","path"].includes(Q.input.kind))throw new _0("invalid_input","The vectorization worker request is invalid.");return Q}function ZG1($){if($.kind==="path")return $.value;if($.value.length>Math.ceil(MW/3)*4||$.value.length%4!==0)throw new _0("input_limit","Encoded raster input exceeds its IPC limit.");let Q=Buffer.from($.value,"base64");if(Q.toString("base64")!==$.value)throw new _0("invalid_input","Encoded raster input is not canonical base64.");return Q}async function _G1($){let Q,Z;try{let _=await Promise.all([BU0($),BU0(oV1())]);Q=_[0],Z=_[1];let X=await rV1($);if(!X.isDirectory()||X.isSymbolicLink()||aV1(Q)!==Z||!/^slopcamera-vectorize-[A-Za-z0-9_-]{6,}$/u.test(iV1(Q)))throw new _0("invalid_input","The supervisor temporary directory is invalid.")}catch(_){if(_ instanceof _0)throw _;throw new _0("invalid_input","The supervisor temporary directory could not be verified.",{},{cause:_})}return Q}function XG1($){let Q=$ instanceof _0&&tV1.has($.code)?$.code:"trace_failed",Z=$ instanceof Error?$.message:"The vectorization worker failed.",_=$ instanceof _0&&Buffer.byteLength(JSON.stringify($.details))<=65536?$.details:{};return{code:Q,details:_,message:Z}}function Ti($){return typeof $==="object"&&$!==null&&!Array.isArray($)}var tV1;var wU0=I(async()=>{cc();A3();Kz();Nz();RE();tV1=new Set(["input_limit","invalid_input","output_limit","quality_limit","timeout","tool_download","tool_integrity","tool_platform","tool_version","trace_failed","unsafe_svg"]);M70();await eV1()});var UU0={};bi(UU0,{runMainEntrypoint:()=>FG1,main:()=>zU0,isEmbeddedVectorizeWorkerInvocation:()=>Ri});function Ri($,Q=import.meta.path){return Q.includes("$bunfs")&&$.length===1&&$[0]?.startsWith("/$bunfs/")===!0&&$[0].endsWith("/vectorize/worker.js")}async function zU0($=process.argv.slice(2),Q={},Z){if(Ri($))return await wU0().then(() => JG1),typeof process.exitCode==="number"?process.exitCode:0;if($[0]==="support")return await hi($.slice(1),Z===void 0?{}:{env:Z});if(ZU0($))return await XU0($,{stdout:process.stdout,stderr:process.stderr,env:process.env},{env:process.env,platform:process.platform,now:()=>new Date});let _=Ai($),X=await DU0(_,Q);if(X!==void 0)return X;let J=aC(_);if(J.kind==="help"||J.kind==="version"||J.kind==="complete"||J.kind==="capabilities"){let Y=LU0(_,{isTTY:process.stdout.isTTY===!0,term:process.env.TERM,columns:process.stdout.columns});if(Y!=="")tX.stdout(Y);return await SS(_,{io:tX})}let F=await uM(tX.cwd(),tX.env);if(J.kind==="html-render"||J.kind==="spatial-world"||J.kind==="spatial-asset"||J.kind==="spatial-design"||J.kind==="directing"||J.kind==="studio"||J.kind==="html-film"||J.kind==="spatial-scene"&&(J.action==="render"||J.action==="camera-track"||J.action==="render-audit"||J.action==="review")){let Y=new AbortController,H=()=>Y.abort(new j("cancelled","Command interrupted."));process.on("SIGINT",H),process.on("SIGTERM",H);try{return await SS(_,{io:tX,paths:F,runner:new o3,abortSignal:Y.signal,...Q.onUsefulResult===void 0?{}:{onUsefulResult:Q.onUsefulResult}})}finally{process.off("SIGINT",H),process.off("SIGTERM",H)}}return await SS(_,{io:tX,paths:F,runner:new o3,...Q.onUsefulResult===void 0?{}:{onUsefulResult:Q.onUsefulResult}})}async function FG1($={},Q=!1){try{let Z=process.argv.slice(2),_=Q&&!Ri(Z)?vi():void 0,X=!1;if(process.exitCode=await zU0(Z,_===void 0?$:{...$,onUsefulResult:()=>{X=!0}},_),_!==void 0&&X&&process.exitCode===0)await gi({env:_})}catch(Z){let _=OH(Z);process.stderr.write(`slopcamera: ${_.message} -`),process.exitCode=mC[_.code]}}var CU0=I(async()=>{lS();yf();cz0();c1();JU0();lH();z2();OU0()});var AU0={};bi(AU0,{verifySlopcameraUpdate:()=>jG1});import{execFile as YG1}from"child_process";import{createHash as HG1}from"crypto";import{mkdtemp as KG1,rm as WG1,writeFile as VG1}from"fs/promises";import{tmpdir as GG1}from"os";import{join as MU0}from"path";function mS($){if(typeof $!=="object"||$===null||Array.isArray($))throw Error("Invalid SlopCamera release metadata.");return $}async function jG1($,Q=NG1){if(!/^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$/u.test($.version)||$.version.length>50||!/^[a-f0-9]{64}$/u.test($.sha256))throw Error("Invalid SlopCamera update identity.");let Z=`v${$.version}`,_=async(G)=>{let N=await Q(["api",G]);if(N.length>qi)throw Error("SlopCamera release metadata is too large.");return mS(JSON.parse(N.toString("utf8")))};await Q(["release","verify",Z,"--repo",WH]),await Q(["release","verify-asset",Z,$.path,"--repo",WH]);let X=mS((await _(`repos/${WH}/git/ref/tags/${Z}`)).object);if(X.type!=="tag"||typeof X.sha!=="string"||!/^[a-f0-9]{40}$/u.test(X.sha))throw Error("SlopCamera updates require an annotated release tag.");let J=await _(`repos/${WH}/git/tags/${X.sha}`),F=mS(J.object);if(J.tag!==Z||F.type!=="commit"||typeof F.sha!=="string"||!/^[a-f0-9]{40}$/u.test(F.sha))throw Error("SlopCamera release tag does not identify its source commit.");let Y=await _(`repos/${WH}/releases/tags/${Z}`);if(Y.tag_name!==Z||Y.immutable!==!0||!Array.isArray(Y.assets))throw Error("SlopCamera release is not immutable.");let H=Y.assets.map(mS).filter((G)=>G.name==="provenance.jsonl"),K=H[0];if(H.length!==1||K===void 0||K.state!=="uploaded"||typeof K.id!=="number"||!Number.isSafeInteger(K.id)||K.id<=0||typeof K.size!=="number"||!Number.isSafeInteger(K.size)||K.size<1||K.size>qi||typeof K.digest!=="string"||!/^sha256:[a-f0-9]{64}$/u.test(K.digest))throw Error("SlopCamera release lacks a checked provenance bundle.");let W=await Q(["api",`repos/${WH}/releases/assets/${K.id}`,"--header","Accept: application/octet-stream"]);if(W.length!==K.size||`sha256:${HG1("sha256").update(W).digest("hex")}`!==K.digest)throw Error("SlopCamera provenance bundle differs from the immutable release.");let V=await KG1(MU0(GG1(),"slopcamera-update-verification-"));try{let G=MU0(V,"provenance.jsonl");await VG1(G,W,{mode:384,flag:"wx"}),await Q(["attestation","verify",$.path,"--repo",WH,"--signer-workflow",`${WH}/.github/workflows/release.yml`,"--signer-digest",F.sha,"--source-digest",F.sha,"--source-ref",`refs/tags/${Z}`,"--deny-self-hosted-runners","--bundle",G])}finally{await WG1(V,{recursive:!0,force:!0})}}var WH="hraness/slopcamera",qi=2097152,NG1=($)=>new Promise((Q,Z)=>{YG1("gh",[...$],{encoding:"buffer",timeout:30000,maxBuffer:qi,env:{...process.env,GH_HOST:"github.com",GH_PROMPT_DISABLED:"1",GH_PAGER:"cat"}},(_,X)=>{if(_!==null)Z(Error("SlopCamera release verification failed. Check GitHub CLI access and retry slopcamera update."));else Q(X)})});var TU0=()=>{};import{fileURLToPath as DG1}from"url";function Si($){let[Q,Z]=$,_=["diagram","mcp","canvas","skill"].includes(Q??"")||Q==="style"&&!$.includes("--help")&&!$.includes("-h")||Q==="code"&&["search","execute"].includes(Z??"")||Q==="media"&&Z==="soundtrack"||Q==="image"&&["vectorize","icon","gallery"].includes(Z??"")||Q==="image"&&Z==="generate"&&$.includes("--output"),X=["status","tui","commands","outputs","legacy","support"].includes(Q??"");return{effectFree:Q===void 0||["help","--help","-h","version","--version","__complete","capabilities"].includes(Q)||Q==="diagram"&&["help","--help","-h"].includes(Z??"")||!_&&!X&&($.includes("--help")||$.includes("-h")),offline:Q==="diagram"||Q==="image"&&["vectorize","icon"].includes(Z??"")||Q==="media"&&Z==="soundtrack"}}async function OG1($=process.argv.slice(2),Q={}){let Z=Q.main??(async()=>(await CU0().then(() => UU0)).runMainEntrypoint({},!0));if(import.meta.path.includes("$bunfs")){await Z();return}let _=$[0]==="update"&&$.slice(1).some((J)=>J==="--help"||J==="-h")?["help","update"]:$,X=await(Q.update??(async(J)=>(await import("@hraness/cli-update")).runCliUpdate(J)))({packageName:"@hraness/slopcamera",version:RX,binName:"slopcamera",entrypoint:DG1(import.meta.url),argv:_,provider:{kind:"github",repository:"hraness/slopcamera",assetName:"hraness-slopcamera-{version}.tgz"},...Si(_),verifyArtifact:async(J)=>(await Promise.resolve().then(() => (TU0(),AU0))).verifySlopcameraUpdate(J)});if(X.handled){process.exitCode=X.exitCode;return}try{await Z()}finally{await X.release()}}if(import.meta.main)await OG1();export{OG1 as runSlopcameraEntrypoint}; +`),process.exitCode=mC[_.code]}}var CU0=I(async()=>{lS();yf();cz0();c1();JU0();lH();z2();OU0()});var AU0={};bi(AU0,{verifySlopcameraUpdate:()=>jG1});import{execFile as YG1}from"child_process";import{createHash as HG1}from"crypto";import{mkdtemp as KG1,rm as WG1,writeFile as VG1}from"fs/promises";import{tmpdir as GG1}from"os";import{join as MU0}from"path";function mS($){if(typeof $!=="object"||$===null||Array.isArray($))throw Error("Invalid Slopcamera release metadata.");return $}async function jG1($,Q=NG1){if(!/^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$/u.test($.version)||$.version.length>50||!/^[a-f0-9]{64}$/u.test($.sha256))throw Error("Invalid Slopcamera update identity.");let Z=`v${$.version}`,_=async(G)=>{let N=await Q(["api",G]);if(N.length>qi)throw Error("Slopcamera release metadata is too large.");return mS(JSON.parse(N.toString("utf8")))};await Q(["release","verify",Z,"--repo",WH]),await Q(["release","verify-asset",Z,$.path,"--repo",WH]);let X=mS((await _(`repos/${WH}/git/ref/tags/${Z}`)).object);if(X.type!=="tag"||typeof X.sha!=="string"||!/^[a-f0-9]{40}$/u.test(X.sha))throw Error("Slopcamera updates require an annotated release tag.");let J=await _(`repos/${WH}/git/tags/${X.sha}`),F=mS(J.object);if(J.tag!==Z||F.type!=="commit"||typeof F.sha!=="string"||!/^[a-f0-9]{40}$/u.test(F.sha))throw Error("Slopcamera release tag does not identify its source commit.");let Y=await _(`repos/${WH}/releases/tags/${Z}`);if(Y.tag_name!==Z||Y.immutable!==!0||!Array.isArray(Y.assets))throw Error("Slopcamera release is not immutable.");let H=Y.assets.map(mS).filter((G)=>G.name==="provenance.jsonl"),K=H[0];if(H.length!==1||K===void 0||K.state!=="uploaded"||typeof K.id!=="number"||!Number.isSafeInteger(K.id)||K.id<=0||typeof K.size!=="number"||!Number.isSafeInteger(K.size)||K.size<1||K.size>qi||typeof K.digest!=="string"||!/^sha256:[a-f0-9]{64}$/u.test(K.digest))throw Error("Slopcamera release lacks a checked provenance bundle.");let W=await Q(["api",`repos/${WH}/releases/assets/${K.id}`,"--header","Accept: application/octet-stream"]);if(W.length!==K.size||`sha256:${HG1("sha256").update(W).digest("hex")}`!==K.digest)throw Error("Slopcamera provenance bundle differs from the immutable release.");let V=await KG1(MU0(GG1(),"slopcamera-update-verification-"));try{let G=MU0(V,"provenance.jsonl");await VG1(G,W,{mode:384,flag:"wx"}),await Q(["attestation","verify",$.path,"--repo",WH,"--signer-workflow",`${WH}/.github/workflows/release.yml`,"--signer-digest",F.sha,"--source-digest",F.sha,"--source-ref",`refs/tags/${Z}`,"--deny-self-hosted-runners","--bundle",G])}finally{await WG1(V,{recursive:!0,force:!0})}}var WH="hraness/slopcamera",qi=2097152,NG1=($)=>new Promise((Q,Z)=>{YG1("gh",[...$],{encoding:"buffer",timeout:30000,maxBuffer:qi,env:{...process.env,GH_HOST:"github.com",GH_PROMPT_DISABLED:"1",GH_PAGER:"cat"}},(_,X)=>{if(_!==null)Z(Error("Slopcamera release verification failed. Check GitHub CLI access and retry slopcamera update."));else Q(X)})});var TU0=()=>{};import{fileURLToPath as DG1}from"url";function Si($){let[Q,Z]=$,_=["diagram","mcp","canvas","skill"].includes(Q??"")||Q==="style"&&!$.includes("--help")&&!$.includes("-h")||Q==="code"&&["search","execute"].includes(Z??"")||Q==="media"&&Z==="soundtrack"||Q==="image"&&["vectorize","icon","gallery"].includes(Z??"")||Q==="image"&&Z==="generate"&&$.includes("--output"),X=["status","tui","commands","outputs","legacy","support"].includes(Q??"");return{effectFree:Q===void 0||["help","--help","-h","version","--version","__complete","capabilities"].includes(Q)||Q==="diagram"&&["help","--help","-h"].includes(Z??"")||!_&&!X&&($.includes("--help")||$.includes("-h")),offline:Q==="diagram"||Q==="image"&&["vectorize","icon"].includes(Z??"")||Q==="media"&&Z==="soundtrack"}}async function OG1($=process.argv.slice(2),Q={}){let Z=Q.main??(async()=>(await CU0().then(() => UU0)).runMainEntrypoint({},!0));if(import.meta.path.includes("$bunfs")){await Z();return}let _=$[0]==="update"&&$.slice(1).some((J)=>J==="--help"||J==="-h")?["help","update"]:$,X=await(Q.update??(async(J)=>(await import("@hraness/cli-update")).runCliUpdate(J)))({packageName:"@hraness/slopcamera",version:RX,binName:"slopcamera",entrypoint:DG1(import.meta.url),argv:_,provider:{kind:"github",repository:"hraness/slopcamera",assetName:"hraness-slopcamera-{version}.tgz"},...Si(_),verifyArtifact:async(J)=>(await Promise.resolve().then(() => (TU0(),AU0))).verifySlopcameraUpdate(J)});if(X.handled){process.exitCode=X.exitCode;return}try{await Z()}finally{await X.release()}}if(import.meta.main)await OG1();export{OG1 as runSlopcameraEntrypoint}; diff --git a/apps/web/README.md b/apps/web/README.md index 0902ff80..29451014 100644 --- a/apps/web/README.md +++ b/apps/web/README.md @@ -1,4 +1,4 @@ -# SlopCamera web +# Slopcamera web `slopcamera.com` is the static public site and documentation for Slopcamera. It presents the agent-directed media studio, SDK, Bun CLI, and local media runtime without diff --git a/apps/web/package.json b/apps/web/package.json index 4b5fae5e..159551d4 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -2,7 +2,7 @@ "name": "@hraness/slopcamera-web", "version": "0.0.0", "private": true, - "description": "The static public site for SlopCamera.", + "description": "The static public site for Slopcamera.", "type": "module", "packageManager": "bun@1.3.14", "dependencies": { diff --git a/apps/web/scripts/preview-layout.test.ts b/apps/web/scripts/preview-layout.test.ts index 9d510281..9b91cd47 100644 --- a/apps/web/scripts/preview-layout.test.ts +++ b/apps/web/scripts/preview-layout.test.ts @@ -147,7 +147,7 @@ const scenario: PreviewCase = { name: "fixture", width: 320, height: 180, dpr: 2 function evidence(): PreviewEvidence { return { width: 320, height: 180, dpr: 2, theme: "system", dark: false, forcedColors: false, - columns: 2, actions: 0, loadedFonts: 2, text: "SlopCamera", maxScrollY: 240, reachedScrollY: 240, + columns: 2, actions: 0, loadedFonts: 2, text: "Slopcamera", maxScrollY: 240, reachedScrollY: 240, failures: [], elements: Array.from({ length: 18 }, (_, index) => ({ key: `element-${index}`, rect: [0, index * 10, 100, 10], styles: { color: "rgb(0, 0, 0)" }, })), diff --git a/apps/web/scripts/site-copy-content.test.ts b/apps/web/scripts/site-copy-content.test.ts index 936f3212..1f953b47 100644 --- a/apps/web/scripts/site-copy-content.test.ts +++ b/apps/web/scripts/site-copy-content.test.ts @@ -19,7 +19,7 @@ const read = async (path: string) => { return source } const compact = (value: string) => value.replace(/\*\*|`/gu, "").replace(/\s+/gu, " ") -const definition = "SlopCamera lets your coding agent make images, diagrams, animation, 3D scenes, and video from source files it can keep revising." +const definition = "Slopcamera lets your coding agent make images, diagrams, animation, 3D scenes, and video from source files it can keep revising." describe("media studio public copy (pure, process-free)", () => { test("the source install is complete in the guide and never renames historical archive bytes", async () => { diff --git a/apps/web/scripts/site-support-browser-contract.test.ts b/apps/web/scripts/site-support-browser-contract.test.ts index 3e15b7a7..8dacf305 100644 --- a/apps/web/scripts/site-support-browser-contract.test.ts +++ b/apps/web/scripts/site-support-browser-contract.test.ts @@ -66,7 +66,7 @@ const evidence = (delta: number): ShellEvidence => { const appearance = selectors.flatMap((selector, index) => Array.from({ length: index < 4 ? 1 : 3 }, (_, i) => element(`${selector}[${i}]`))) const skip = { ...element(".skip-link[0]"), styles: { ...element(".skip-link[0]").styles, position: "fixed" }, geometrySpace: "viewport" as const, scrollY: 0, documentRect: [0, 0, 400, 20] } return { direction: "ltr", dom: "exact-outside-footer", recovery: false, elements: [ - { ...element("body[0]", 1000 + delta), text: `Retained${delta ? "SlopCamera Docs GitHub Install Slopcamera by Hraness" : ""}Accept cookies` }, + { ...element("body[0]", 1000 + delta), text: `Retained${delta ? "Slopcamera Docs GitHub Install Slopcamera by Hraness" : ""}Accept cookies` }, { ...element("#hraness-site-footer[0]", 100 + delta), rect: [0, 900, 400, 100 + delta] }, element("#main[0]")], focus: [element(".topbar[0]")], hover: [element(".topbar[0]")], skip, obstructions: [], appearance: shellAppearanceSteps.map(step => ({ step: step.name, active: step.active, elements: appearance })) } diff --git a/apps/web/scripts/site-support-browser-contract.ts b/apps/web/scripts/site-support-browser-contract.ts index 7d043665..384d8bf6 100644 --- a/apps/web/scripts/site-support-browser-contract.ts +++ b/apps/web/scripts/site-support-browser-contract.ts @@ -216,7 +216,7 @@ export async function observeSupportFooter(page: Page, scenario: ShellCase, foun } const selector = '[data-slot="hraness-support-link"]', link = page.locator(selector) assert.equal(await link.count(), 1); assert.equal(await link.getAttribute("href"), supportHref) - assert.equal(await link.getAttribute("aria-label"), "Support SlopCamera: optional paid membership") + assert.equal(await link.getAttribute("aria-label"), "Support Slopcamera: optional paid membership") // v0.14 renders the support target as the muted question-mark icon only; the // accessible name stays on the link and its title carries the proposition. assert.equal(await link.textContent(), ""); assert.equal(await link.getAttribute("target"), null) diff --git a/apps/web/site.test.ts b/apps/web/site.test.ts index 743c642f..3c33d595 100644 --- a/apps/web/site.test.ts +++ b/apps/web/site.test.ts @@ -82,7 +82,7 @@ import { replaceSiteSlot } from "./src/site-template" import { statusPageRoutes } from "./src/status-page-content" const appDirectory = dirname(fileURLToPath(import.meta.url)) const repositoryDirectory = join(appDirectory, "..", "..") -const searchDescription = "SlopCamera lets your coding agent make images, diagrams, animation, 3D scenes, and video from source files it can keep revising." +const searchDescription = "Slopcamera lets your coding agent make images, diagrams, animation, 3D scenes, and video from source files it can keep revising." let builtAssets: Awaited> // Each build compiles the independent ordinary-site and preview graphs. These @@ -367,7 +367,7 @@ test("ships agent instructions for video editing and Gateway media generation", readFile(join(repositoryDirectory, "skills/slopcamera/references/gateway-media.md"), "utf8"), ]) - expect(skill).toContain("# Create visual media with SlopCamera") + expect(skill).toContain("# Create visual media with Slopcamera") expect(skill).toContain("[Video projects](references/video-projects.md)") expect(skill).toContain("[Gateway media](references/gateway-media.md)") // Slopcamera edits existing recordings; the Skill must not offer to record. @@ -994,8 +994,8 @@ describe("static Slopcamera site", () => { test("publishes one canonical Slopcamera identity across discovery metadata", async () => { const html = await readSource("index.html") - expect(html).toContain("SlopCamera: video, diagrams and 3D your coding agent can revise") - expect(html).toContain('') + expect(html).toContain("Slopcamera: video, diagrams and 3D your coding agent can revise") + expect(html).toContain('') expect(html).toContain(``) expect(html).toContain(``) expect(html).toContain(``) @@ -1033,7 +1033,7 @@ describe("static Slopcamera site", () => { for (const html of [source, built]) { expect(html.match(/SlopCamera<\/h1>/u) + expect(html).toMatch(/

Slopcamera<\/h1>/u) expect(html).toMatch(/
/u) expect(html).toContain("A domain-specific harness for visual creation.") expect(html).toContain('') @@ -1244,7 +1244,7 @@ describe("static Slopcamera site", () => { expect(html).toContain(stylesheets) expect(html).not.toMatch(/\{\{|${page.title} · SlopCamera`) + expect(html).toContain(`${page.title} · Slopcamera`) expect(html).toContain(``) expect(html).toContain(``) expect(html).toContain(``) @@ -1628,11 +1628,11 @@ describe("static Slopcamera site", () => { test("ships share cards only from the shared web-discovery template", async () => { // One declaration drives every card; pages pass only their own copy. - expect(slopcameraSocialSite.name).toBe("SlopCamera") + expect(slopcameraSocialSite.name).toBe("Slopcamera") expect(slopcameraSocialSite.domain).toBe("slopcamera.com") // The card's header is the site's sticky header: the same foil mask, the // name as the nav shows it, and the palette every page's declares. - expect(slopcameraSocialSite.brand).toBe("SlopCamera") + expect(slopcameraSocialSite.brand).toBe("Slopcamera") expect(slopcameraSocialSite.brandMark).toBe(`data:image/svg+xml;base64,${ Buffer.from(await readSource("marks/slopcamera.svg")).toString("base64")}`) expect(await readSource("site-foil.css")).toContain('--hraness-foil-mask: url("./marks/slopcamera.svg")') @@ -1646,7 +1646,7 @@ describe("static Slopcamera site", () => { // The home card is the header over the hero: the hero H1, no eyebrow // (the hero has none), and no tagline repeated beneath it. expect(homeSocialImage.page).toEqual({ headline: productMessaging.hero.heading, description: "", layout: "product" }) - expect(homeSocialImage.alt).toBe(`SlopCamera: ${productMessaging.hero.heading}`) + expect(homeSocialImage.alt).toBe(`Slopcamera: ${productMessaging.hero.heading}`) expect(socialImages).toHaveLength(1 + 1 + blogPosts.length + docPages.length) expect(new Set(socialImages.map(image => image.file)).size).toBe(socialImages.length) for (const image of socialImages.slice(1)) { @@ -1662,7 +1662,7 @@ describe("static Slopcamera site", () => { }) const doc = socialImageForDocument("docs/how-to/edit-video.html") expect(doc).toMatchObject({ file: "og/docs/how-to/edit-video.png", page: { headline: "Edit and deliver video", eyebrow: "Guide", path: "/docs/how-to/edit-video" } }) - expect(doc.alt).toBe("Edit and deliver video, from SlopCamera") + expect(doc.alt).toBe("Edit and deliver video, from Slopcamera") for (const image of [homeSocialImage, doc]) { const png = await renderSocialImage(image) const view = new DataView(png.buffer, png.byteOffset, png.byteLength) @@ -2234,8 +2234,8 @@ describe("static Slopcamera site", () => { expect(sitemap.match(//gu)).toHaveLength(1 + indexableBlogPosts.length + datedDocs.length) for (const page of datedDocs) expect(sitemap).toContain(`${docsCanonicalUrl(page)}\n ${page.modified}`) expect(sitemap.slice(0, sitemap.indexOf("https://slopcamera.com/docs"))).not.toContain("") - expect(llmsTxt).toMatch(/^# SlopCamera\n/u) - expect(llmsTxt).toContain("> SlopCamera lets your coding agent make images, diagrams, animation, 3D scenes, and video from source files it can keep revising.") + expect(llmsTxt).toMatch(/^# Slopcamera\n/u) + expect(llmsTxt).toContain("> Slopcamera lets your coding agent make images, diagrams, animation, 3D scenes, and video from source files it can keep revising.") for (const route of ["docs/reference/techniques", "docs/explanation/slopcamera-vs-remotion", "docs/explanation/slopcamera-vs-hyperframes", "docs/explanation/remotion-alternatives-for-coding-agents"]) { expect(llmsTxt).toContain(`(https://slopcamera.com/${route}.md)`) } @@ -2454,7 +2454,7 @@ describe("static Slopcamera site", () => { // The in-flow product content footer lands immediately before the shared // footer, carries the authored camera mark (never an emoji glyph), and // reuses the header's own destinations. - expect(contentFooter).toContain('