Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ EFFECTS_GENERATION_TIMEOUT_MS=1800000
WORKSPACE_STORAGE_MODE=beatapi

# Official hosted deployments only: BeatAPI-managed fallback for generation
# input types that /v1/files does not accept, such as reference video.
# input types that /v1/files does not accept.
BEATAPI_MANAGED_R2_REGION=auto
BEATAPI_MANAGED_R2_ENDPOINT=
BEATAPI_MANAGED_R2_ACCESS_KEY_ID=
Expand Down
2 changes: 2 additions & 0 deletions PROVIDERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ The adapter uses:

Model support is defined in `src/core/effects/effect-registry.ts`, not discovered dynamically. This keeps Canvas and Studio behavior deterministic. When adding a model, update the registry, request mapping, media capability rules, and tests together.

Kling 2.6 and Kling 3.0 Motion Control are exposed as BeatAPI models. Each run requires exactly one character image and one MP4/MOV motion video uploaded through the connected BeatAPI account. The Workspace never asks users for a KIE key; BeatAPI owns the upstream provider route, billing, polling, and output persistence.

An API with a different request or polling contract still needs its own adapter. This repository does not ship placeholder KIE, Vidu, Evolink, Gemini, Fal, Replicate, or payment-provider integrations.

## Storage
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ No shared R2 secret is committed to this repository. “Preconfigured” means t

## Supported model catalog

The canonical catalog lives in `src/core/effects/effect-registry.ts`. The current workspace exposes four image models and five video models through one BeatAPI adapter. Update the registry and adapter together when BeatAPI's public contract changes.
The canonical catalog lives in `src/core/effects/effect-registry.ts`. The current workspace exposes four image models and seven video models through one BeatAPI adapter, including Kling 2.6 and Kling 3.0 Motion Control. Update the registry and adapter together when BeatAPI's public contract changes.

## Commands

Expand Down
18 changes: 15 additions & 3 deletions messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@
"toolbar": {
"addNode": "Add node",
"generationNode": "Generation node",
"uploadNode": "Upload node",
"uploadNode": "Upload media",
"upload": "Upload",
"assets": "Assets",
"batchScenes": "Batch scenes"
Expand All @@ -133,7 +133,7 @@
"templates": "Templates",
"historyAssets": "History assets",
"addNodeTitle": "Add node",
"uploadNodeTitle": "Upload node",
"uploadNodeTitle": "Upload media",
"recentAssetsTitle": "Recent assets",
"recentImages": "Recent images",
"recentVideos": "Recent videos",
Expand Down Expand Up @@ -190,6 +190,7 @@
"promptPlaceholder": "Describe the image or video you want to generate. When you select a canvas node, it becomes the current reference or branch anchor."
},
"actions": {
"uploadMedia": "Upload media",
"uploadImage": "Upload image",
"uploadVideo": "Upload video",
"combine": "Combine cards",
Expand Down Expand Up @@ -275,6 +276,8 @@
"addNodeSuccess": "Prompt node added to the canvas.",
"uploadImageInvalid": "Images must be JPG, PNG, or WebP and smaller than {maxSizeMb}MB.",
"uploadVideoInvalid": "Videos must be MP4, WebM, or MOV and smaller than {maxSizeMb}MB.",
"uploadMediaInvalid": "{fileName} is not a supported image or video.",
"openingMediaPicker": "Opening media picker...",
"openingImagePicker": "Opening image picker...",
"openingVideoPicker": "Opening video picker...",
"uploading": "Uploading asset...",
Expand Down Expand Up @@ -358,6 +361,8 @@
"variant": "Variant",
"quality": "Quality",
"language": "Language",
"characterOrientation": "Character orientation",
"backgroundSource": "Background source",
"tokens": {
"quality": "Quality",
"fast": "Fast",
Expand All @@ -371,7 +376,11 @@
"landscape": "Landscape",
"portrait": "Portrait",
"chinese": "Chinese",
"english": "English"
"english": "English",
"imageOrientation": "Follow image",
"videoOrientation": "Follow video",
"inputImage": "Image background",
"inputVideo": "Video background"
},
"status": {
"queued": "Queued",
Expand Down Expand Up @@ -402,6 +411,8 @@
"videoGeneration": "Video generation",
"image": "Image",
"video": "Video",
"previewLoading": "Loading preview...",
"previewUnavailable": "Preview unavailable",
"processing": "Processing...",
"pending": "Pending...",
"generationFailed": "Generation failed",
Expand Down Expand Up @@ -511,6 +522,7 @@
"workspaceNameLabel": "Workspace name",
"projects": "Projects",
"projectsLabel": "Open projects",
"githubRepository": "Open BeatAPI Workspace on GitHub",
"projectAssets": {
"triggerLabel": "Project assets",
"title": "Project assets",
Expand Down
18 changes: 15 additions & 3 deletions messages/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@
"toolbar": {
"addNode": "新增节点",
"generationNode": "生成节点",
"uploadNode": "上传节点",
"uploadNode": "上传素材",
"upload": "上传",
"assets": "资产库",
"batchScenes": "批量场景"
Expand All @@ -134,7 +134,7 @@
"templates": "模板库",
"historyAssets": "历史素材",
"addNodeTitle": "新增节点",
"uploadNodeTitle": "上传节点",
"uploadNodeTitle": "上传素材",
"recentAssetsTitle": "最近素材",
"recentImages": "最近图片",
"recentVideos": "最近视频",
Expand Down Expand Up @@ -191,6 +191,7 @@
"promptPlaceholder": "描述你想生成的图片或视频。选中画布节点后,它会成为当前参考素材或分支锚点。"
},
"actions": {
"uploadMedia": "上传素材",
"uploadImage": "上传图片",
"uploadVideo": "上传视频",
"combine": "合并卡片",
Expand Down Expand Up @@ -276,6 +277,8 @@
"addNodeSuccess": "提示节点已加入画布。",
"uploadImageInvalid": "图片需为 JPG、PNG 或 WebP,且小于 {maxSizeMb}MB。",
"uploadVideoInvalid": "视频需为 MP4、WebM 或 MOV,且小于 {maxSizeMb}MB。",
"uploadMediaInvalid": "{fileName} 不是支持的图片或视频格式。",
"openingMediaPicker": "正在打开素材选择器...",
"openingImagePicker": "正在打开图片选择器...",
"openingVideoPicker": "正在打开视频选择器...",
"uploading": "上传素材中...",
Expand Down Expand Up @@ -359,6 +362,8 @@
"variant": "版本",
"quality": "质量",
"language": "语种",
"characterOrientation": "角色朝向",
"backgroundSource": "背景来源",
"tokens": {
"quality": "质量",
"fast": "快速",
Expand All @@ -372,7 +377,11 @@
"landscape": "横向",
"portrait": "纵向",
"chinese": "中文",
"english": "英文"
"english": "英文",
"imageOrientation": "跟随图片",
"videoOrientation": "跟随视频",
"inputImage": "图片背景",
"inputVideo": "视频背景"
},
"status": {
"queued": "排队中",
Expand Down Expand Up @@ -403,6 +412,8 @@
"videoGeneration": "视频生成",
"image": "图片",
"video": "视频",
"previewLoading": "正在加载预览...",
"previewUnavailable": "暂时无法预览",
"processing": "处理中...",
"pending": "等待中...",
"generationFailed": "生成失败",
Expand Down Expand Up @@ -512,6 +523,7 @@
"workspaceNameLabel": "空间名称",
"projects": "项目",
"projectsLabel": "打开项目列表",
"githubRepository": "在 GitHub 打开 BeatAPI Workspace",
"projectAssets": {
"triggerLabel": "项目素材",
"title": "项目素材",
Expand Down
7 changes: 7 additions & 0 deletions src/components/app/product-page-shell.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ const apiConfigSource = readFileSync(
test('workspace header keeps mode switching, shared assets, and API configuration actions', () => {
assert.match(shellSource, /WorkspaceApiConfigDialog/);
assert.match(shellSource, /ProjectAssetsDialog/);
assert.match(shellSource, /GitHubIcon/);
assert.match(
shellSource,
/https:\/\/github\.com\/BeatAPI\/beatapi-workspace/
);
assert.match(shellSource, /target="_blank"/);
assert.match(shellSource, /rel="noreferrer"/);
assert.match(shellSource, />Studio<\/span>/);
assert.match(shellSource, />Canvas<\/span>/);
assert.match(shellSource, /\{ workspaceMode \}/);
Expand Down
11 changes: 11 additions & 0 deletions src/components/app/product-page-shell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { LayoutPanelTop, Workflow } from 'lucide-react';

import { WorkspaceApiConfigDialog } from '@/components/app/workspace-api-config-dialog';
import { ProjectAssetsDialog } from '@/components/app/project-assets-dialog';
import { GitHubIcon } from '@/components/icons/github';
import { Link } from '@/core/i18n/navigation';
import { useTranslations } from '@/core/workspace-lib/shims/next-intl';
import { apiJsonPatch, apiJsonPost } from '@/lib/api-client';
Expand Down Expand Up @@ -159,6 +160,16 @@ export function ProductPageShell({
<WorkspaceApiConfigDialog
providerId={envConfigs.generation_provider}
/>
<a
href="https://github.com/BeatAPI/beatapi-workspace"
target="_blank"
rel="noreferrer"
aria-label={t('header.githubRepository')}
title={t('header.githubRepository')}
className="inline-flex size-9 shrink-0 items-center justify-center rounded-xl border border-white/[0.09] bg-white/[0.035] text-[var(--beat-text-2)] transition hover:border-white/[0.16] hover:bg-white/[0.075] hover:text-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--beat-graph)]/70"
>
<GitHubIcon className="size-[17px]" aria-hidden="true" />
</a>
</div>
</div>
</header>
Expand Down
69 changes: 69 additions & 0 deletions src/components/beatcanvas/beatcanvas-composer-parameter-picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import type {
WorkspaceAspectRatio,
WorkspaceBackgroundSource,
WorkspaceCharacterOrientation,
WorkspaceDuration,
WorkspaceLanguage,
WorkspaceModelMode,
Expand Down Expand Up @@ -33,6 +35,8 @@ export function BeatCanvasComposerParameterPicker({
isOpen,
labels,
onDraftAspectRatioChange,
onDraftBackgroundSourceChange,
onDraftCharacterOrientationChange,
onDraftDurationChange,
onDraftLanguageChange,
onDraftModeChange,
Expand All @@ -42,6 +46,8 @@ export function BeatCanvasComposerParameterPicker({
onOpenChange,
parameterSummaryLabel,
selectedAspectRatioOptions,
selectedBackgroundSourceOptions,
selectedCharacterOrientationOptions,
selectedDurationOptions,
selectedLanguageOptions,
selectedModeOptions,
Expand All @@ -60,6 +66,14 @@ export function BeatCanvasComposerParameterPicker({
draftId: string,
aspectRatio: WorkspaceAspectRatio
) => void;
onDraftBackgroundSourceChange: (
draftId: string,
backgroundSource: WorkspaceBackgroundSource
) => void;
onDraftCharacterOrientationChange: (
draftId: string,
characterOrientation: WorkspaceCharacterOrientation
) => void;
onDraftDurationChange: (draftId: string, duration: WorkspaceDuration) => void;
onDraftLanguageChange: (draftId: string, language: WorkspaceLanguage) => void;
onDraftModeChange: (draftId: string, mode: WorkspaceModelMode) => void;
Expand All @@ -78,6 +92,8 @@ export function BeatCanvasComposerParameterPicker({
onOpenChange: (nextOpen: boolean) => void;
parameterSummaryLabel: string;
selectedAspectRatioOptions: WorkspaceAspectRatio[];
selectedBackgroundSourceOptions: WorkspaceBackgroundSource[];
selectedCharacterOrientationOptions: WorkspaceCharacterOrientation[];
selectedDurationOptions: WorkspaceDuration[];
selectedLanguageOptions: WorkspaceLanguage[];
selectedModeOptions: WorkspaceModelMode[];
Expand Down Expand Up @@ -236,6 +252,59 @@ export function BeatCanvasComposerParameterPicker({
</section>
) : null}

{selectedCharacterOrientationOptions.length > 0 ? (
<section className="space-y-1.5">
<p className={composerSectionLabelClassName}>
{labels.characterOrientationLabel}
</p>
<div className="grid grid-cols-2 gap-1.5">
{selectedCharacterOrientationOptions.map((item) => (
<button
key={item}
type="button"
disabled={isDraftBusy}
onClick={() =>
onDraftCharacterOrientationChange(
activeDraftCard.id,
item
)
}
className={getParameterChipClassName(
activeDraftCard.characterOrientation === item
)}
>
{normalizeComposerToken(item, labels)}
</button>
))}
</div>
</section>
) : null}

{selectedBackgroundSourceOptions.length > 0 ? (
<section className="space-y-1.5">
<p className={composerSectionLabelClassName}>
{labels.backgroundSourceLabel}
</p>
<div className="grid grid-cols-2 gap-1.5">
{selectedBackgroundSourceOptions.map((item) => (
<button
key={item}
type="button"
disabled={isDraftBusy}
onClick={() =>
onDraftBackgroundSourceChange(activeDraftCard.id, item)
}
className={getParameterChipClassName(
activeDraftCard.backgroundSource === item
)}
>
{normalizeComposerToken(item, labels)}
</button>
))}
</div>
</section>
) : null}

{selectedLanguageOptions.length > 0 ? (
<section className="space-y-1.5">
<p className={composerSectionLabelClassName}>
Expand Down
Loading
Loading