aitoearn-dsh-plugin-test 0.0.2 → 0.0.4
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/lib/client.js +34 -36
- package/lib/client.js.map +2 -2
- package/package.json +57 -40
- package/presets/aitoearn/lib/mcp.js +85 -21
- package/presets/aitoearn/lib/mcp.js.map +2 -2
- package/presets/aitoearn/package.json +14 -13
- package/skills/cloudedit/SKILL.md +113 -0
- package/skills/cloudedit/references/amk-scenarios/edit-asr.md +160 -0
- package/skills/cloudedit/references/edit-position.md +64 -0
- package/skills/cloudedit/references/edit-project.md +192 -0
- package/skills/cloudedit/references/edit-quality-gate.md +136 -0
- package/skills/cloudedit/references/edit-shader.md +114 -0
- package/skills/cloudedit/references/edit-text.md +227 -0
- package/skills/cloudedit/references/edit-timeline.md +29 -0
- package/skills/cloudedit-planner/SKILL.md +102 -0
- package/skills/cloudedit-planner/evals/evals.json +80 -0
- package/skills/cloudedit-planner/references/editing-plan.md +63 -0
- package/skills/cloudedit-planner/references/executable-handoff.md +25 -0
- package/skills/cloudedit-planner/references/media-evidence-models.md +32 -0
- package/skills/cloudedit-planner/references/media-understanding.md +25 -0
- package/skills/cloudedit-planner/references/processing-and-lineage.md +22 -0
- package/skills/cloudedit-planner/references/subtitle-timing.md +50 -0
- package/src/dsh/client/AitoearnCard.js +2 -2
- package/src/dsh/client/AitoearnCard.js.map +1 -1
- package/src/dsh/client/card-store.d.ts +2 -2
- package/src/dsh/client/card-store.js +18 -9
- package/src/dsh/client/card-store.js.map +1 -1
- package/src/dsh/client/index.js +15 -13
- package/src/dsh/client/index.js.map +1 -1
- package/src/dsh/index.d.ts +2 -1
- package/src/dsh/index.js +4 -6
- package/src/dsh/index.js.map +1 -1
- package/src/dsh/mcp.js +90 -21
- package/src/dsh/mcp.js.map +1 -1
- package/src/dsh/settings-schema.d.ts +16 -12
- package/src/dsh/settings-schema.js +8 -10
- package/src/dsh/settings-schema.js.map +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../../tools/agent-plugin/src/dsh/mcp.ts", "../../../../../../../tools/agent-plugin/src/dsh/auth.ts", "../../../../../../../tools/agent-plugin/src/dsh/tool-name.ts"],
|
|
4
|
-
"sourcesContent": ["import { createRequire } from 'node:module'\nimport { Context, Fiber } from '@deepseek-ai/cordis'\nimport { StreamableHttpConfig } from '@deepseek-ai/dsh-mcp-client'\nimport {} from '@deepseek-ai/dsh-settings'\nimport {} from '@deepseek-ai/dsh-settings/types'\nimport {} from '@deepseek-ai/dsh-tools'\nimport { AitoearnPluginConfig, authorizationHeader, pluginConfigFromSettings, resolveAuth, SETTINGS_NAMESPACE } from './auth.js'\nimport { MCP_SERVER_NAME, patchRawToolNames } from './tool-name.js'\n\nconst runtimeRequire = createRequire(import.meta.url)\n\nexport const name = 'aitoearn-mcp'\nexport const inject = ['tools']\n\nexport async function apply(ctx: Context, config: AitoearnPluginConfig = {}): Promise<void> {\n let current = () => config\n let restoreNames: (() => void) | undefined\n let fiber: Fiber | undefined\n\n const
|
|
5
|
-
"mappings": "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
|
|
4
|
+
"sourcesContent": ["import { createRequire } from 'node:module'\nimport { Context, Fiber } from '@deepseek-ai/cordis'\nimport { StreamableHttpConfig } from '@deepseek-ai/dsh-mcp-client'\nimport {} from '@deepseek-ai/dsh-settings'\nimport {} from '@deepseek-ai/dsh-settings/types'\nimport {} from '@deepseek-ai/dsh-tools'\nimport { AitoearnPluginConfig, authorizationHeader, pluginConfigFromSettings, resolveAuth, SETTINGS_NAMESPACE } from './auth.js'\nimport { MCP_SERVER_NAME, patchRawToolNames } from './tool-name.js'\n\nconst runtimeRequire = createRequire(import.meta.url)\n\nexport const name = 'aitoearn-mcp'\nexport const inject = ['tools']\n\nexport async function apply(ctx: Context, config: AitoearnPluginConfig = {}): Promise<void> {\n let current = () => config\n let restoreNames: (() => void) | undefined\n let fiber: Fiber | undefined\n let generation = 0\n let mountChain: Promise<void> = Promise.resolve()\n let remountTimer: ReturnType<typeof setTimeout> | undefined\n let mountedFingerprint: string | undefined\n\n const unmountAsync = async () => {\n restoreNames?.()\n restoreNames = undefined\n const previous = fiber\n fiber = undefined\n if (previous) {\n try {\n await previous.dispose()\n }\n catch (error) {\n ctx.logger.warn(error, 'aitoearn-mcp: failed to dispose previous MCP client')\n }\n }\n }\n\n const doMount = async (gen: number) => {\n const auth = resolveAuth(current())\n if (auth.apiKey.length === 0 || auth.mcpUrl.length === 0) {\n await unmountAsync()\n mountedFingerprint = undefined\n ctx.logger.warn('aitoearn-mcp: skip mounting; API key or MCP URL is not configured yet')\n return\n }\n const fingerprint = `${auth.mcpUrl}\\n${auth.apiKey}`\n if (fingerprint === mountedFingerprint && fiber) {\n return\n }\n await unmountAsync()\n if (gen !== generation) {\n return\n }\n restoreNames = patchRawToolNames(ctx.tools, MCP_SERVER_NAME, (publicName) => {\n ctx.logger.info(`kept qualified MCP tool name ${publicName}`)\n })\n try {\n const mcpClient: typeof import('@deepseek-ai/dsh-mcp-client') = runtimeRequire('@deepseek-ai/dsh-mcp-client')\n const mcpConfig: StreamableHttpConfig = {\n serverName: MCP_SERVER_NAME,\n transport: 'streamable-http',\n url: auth.mcpUrl,\n headers: authorizationHeader(auth.apiKey),\n failOnStartupError: false,\n toolCallTimeoutMs: 120000,\n }\n const next = ctx.plugin(mcpClient, mcpConfig)\n fiber = next\n await next\n }\n catch (error) {\n restoreNames?.()\n restoreNames = undefined\n fiber = undefined\n ctx.logger.warn(error, 'aitoearn-mcp: mount failed; will retry on next settings change')\n return\n }\n if (gen !== generation) {\n await unmountAsync()\n mountedFingerprint = undefined\n return\n }\n mountedFingerprint = fingerprint\n ctx.logger.info(`aitoearn-mcp: mounted MCP tools from ${auth.mcpUrl}`)\n }\n\n const scheduleMount = () => {\n generation += 1\n const gen = generation\n mountChain = mountChain\n .then(() => doMount(gen))\n .catch((error: Error) => {\n ctx.logger.warn(error, 'aitoearn-mcp: mount failed')\n })\n return mountChain\n }\n\n const scheduleRemountSoon = () => {\n if (remountTimer) {\n clearTimeout(remountTimer)\n }\n remountTimer = setTimeout(() => {\n remountTimer = undefined\n void scheduleMount()\n }, 50)\n }\n\n ctx.inject(['settings'], (scoped) => {\n // Host `describe()` serves the live value unredacted by default, so the\n // preset can read the stored key; remote callers get the redacted view.\n const readSection = (): AitoearnPluginConfig => {\n const descriptor = scoped.settings.describe().find(candidate => String(candidate.ns) === SETTINGS_NAMESPACE)\n return pluginConfigFromSettings(descriptor?.value, config)\n }\n current = readSection\n ctx.on('settings/document-updated', (namespace) => {\n if (String(namespace) === SETTINGS_NAMESPACE) {\n scheduleRemountSoon()\n }\n })\n // The initial apply-time mount may run before the settings section is\n // readable; mount again once it is, so a stored key takes effect without\n // requiring a manual settings save.\n void scheduleMount()\n })\n\n ctx.effect(() => () => {\n if (remountTimer) {\n clearTimeout(remountTimer)\n remountTimer = undefined\n }\n void unmountAsync()\n }, 'aitoearn-mcp.client')\n await scheduleMount()\n}\n", "export const MCP_PATH = '/api/ai/agent/mcp'\nexport const API_KEY_ENV = 'AITOEARN_API_KEY'\nexport const DEFAULT_ENVIRONMENT = 'global'\nexport const SETTINGS_NAMESPACE = 'aitoearn'\n\nexport const ENVIRONMENT_ORIGINS = {\n global: 'https://aitoearn.ai',\n cn: 'https://aitoearn.cn',\n} as const\n\nexport const DEFAULT_MCP_URL = `${ENVIRONMENT_ORIGINS.global}${MCP_PATH}`\n\nexport type AitoearnEnvironment = 'global' | 'cn' | 'custom'\n\nexport interface AitoearnAuth {\n mcpUrl: string\n apiKey: string\n}\n\nexport interface AitoearnPluginConfig {\n environment?: string\n baseUrl?: string\n apiKey?: string\n}\n\nexport function pluginConfigFromSettings(\n section: unknown,\n fallback: AitoearnPluginConfig = {},\n): AitoearnPluginConfig {\n if (section === null || typeof section !== 'object' || Array.isArray(section))\n return fallback\n const record = section as Record<string, unknown>\n return {\n ...fallback,\n ...(typeof record['environment'] === 'string' ? { environment: record['environment'] } : {}),\n ...(typeof record['baseUrl'] === 'string' ? { baseUrl: record['baseUrl'] } : {}),\n ...(typeof record['apiKey'] === 'string' ? { apiKey: record['apiKey'] } : {}),\n }\n}\n\nexport function resolveEnvironment(value: string | undefined): AitoearnEnvironment {\n if (value === 'cn' || value === 'custom' || value === 'global')\n return value\n return DEFAULT_ENVIRONMENT\n}\n\nexport function resolveOrigin(environment: AitoearnEnvironment, baseUrl?: string): string {\n if (environment !== 'custom')\n return ENVIRONMENT_ORIGINS[environment]\n const trimmed = baseUrl?.trim() ?? ''\n if (trimmed.length === 0)\n return ''\n try {\n const url = new URL(trimmed)\n if (url.protocol !== 'http:' && url.protocol !== 'https:')\n return ''\n return url.origin\n }\n catch {\n return ''\n }\n}\n\nexport function resolveMcpUrl(config: AitoearnPluginConfig): string {\n const environment = resolveEnvironment(config.environment)\n const origin = resolveOrigin(environment, config.baseUrl)\n if (origin.length === 0)\n return ''\n return `${origin}${MCP_PATH}`\n}\n\nexport function resolveAuth(\n config: AitoearnPluginConfig,\n env: NodeJS.ProcessEnv = process.env,\n): AitoearnAuth {\n return {\n mcpUrl: resolveMcpUrl(config),\n apiKey: config.apiKey?.trim() || env[API_KEY_ENV]?.trim() || '',\n }\n}\n\nexport function authorizationHeader(apiKey: string): Record<string, string> {\n if (apiKey.length === 0)\n return {}\n return { Authorization: `Bearer ${apiKey}` }\n}\n", "import { ToolRuntime } from '@deepseek-ai/dsh-tools'\n\nexport const MCP_SERVER_NAME = 'aitoearn'\n\nconst HASHED_PUBLIC_NAME = /_[0-9a-f]{12}$/\n\nexport interface RawPublicName {\n modelName: string\n stripped: boolean\n}\n\nexport function qualifiedPrefix(serverName = MCP_SERVER_NAME): string {\n return `mcp__${serverName}__`\n}\n\nexport function toRawPublicName(publicName: string, serverName = MCP_SERVER_NAME): RawPublicName {\n const prefix = qualifiedPrefix(serverName)\n if (!publicName.startsWith(prefix))\n return { modelName: publicName, stripped: false }\n if (publicName.length === 64 && HASHED_PUBLIC_NAME.test(publicName))\n return { modelName: publicName, stripped: false }\n const raw = publicName.slice(prefix.length)\n if (raw.length === 0)\n return { modelName: publicName, stripped: false }\n return { modelName: raw, stripped: true }\n}\n\nexport function patchRawToolNames(\n tools: Pick<ToolRuntime, 'register'>,\n serverName = MCP_SERVER_NAME,\n onKeepQualified?: (publicName: string) => void,\n): () => void {\n const original = tools.register.bind(tools)\n tools.register = (definition) => {\n const next = toRawPublicName(definition.name, serverName)\n if (!next.stripped) {\n if (definition.name.startsWith(qualifiedPrefix(serverName)))\n onKeepQualified?.(definition.name)\n return original(definition)\n }\n return original({ ...definition, name: next.modelName })\n }\n return () => {\n tools.register = original\n }\n}\n"],
|
|
5
|
+
"mappings": ";AAAA,SAAS,qBAAqB;;;ACAvB,IAAM,WAAW;AACjB,IAAM,cAAc;AACpB,IAAM,sBAAsB;AAC5B,IAAM,qBAAqB;AAE3B,IAAM,sBAAsB;AAAA,EACjC,QAAQ;AAAA,EACR,IAAI;AACN;AAEO,IAAM,kBAAkB,GAAG,oBAAoB,MAAM,GAAG,QAAQ;AAehE,SAAS,yBACd,SACA,WAAiC,CAAC,GACZ;AACtB,MAAI,YAAY,QAAQ,OAAO,YAAY,YAAY,MAAM,QAAQ,OAAO;AAC1E,WAAO;AACT,QAAM,SAAS;AACf,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAI,OAAO,OAAO,aAAa,MAAM,WAAW,EAAE,aAAa,OAAO,aAAa,EAAE,IAAI,CAAC;AAAA,IAC1F,GAAI,OAAO,OAAO,SAAS,MAAM,WAAW,EAAE,SAAS,OAAO,SAAS,EAAE,IAAI,CAAC;AAAA,IAC9E,GAAI,OAAO,OAAO,QAAQ,MAAM,WAAW,EAAE,QAAQ,OAAO,QAAQ,EAAE,IAAI,CAAC;AAAA,EAC7E;AACF;AAEO,SAAS,mBAAmB,OAAgD;AACjF,MAAI,UAAU,QAAQ,UAAU,YAAY,UAAU;AACpD,WAAO;AACT,SAAO;AACT;AAEO,SAAS,cAAc,aAAkC,SAA0B;AACxF,MAAI,gBAAgB;AAClB,WAAO,oBAAoB,WAAW;AACxC,QAAM,UAAU,SAAS,KAAK,KAAK;AACnC,MAAI,QAAQ,WAAW;AACrB,WAAO;AACT,MAAI;AACF,UAAM,MAAM,IAAI,IAAI,OAAO;AAC3B,QAAI,IAAI,aAAa,WAAW,IAAI,aAAa;AAC/C,aAAO;AACT,WAAO,IAAI;AAAA,EACb,QACM;AACJ,WAAO;AAAA,EACT;AACF;AAEO,SAAS,cAAc,QAAsC;AAClE,QAAM,cAAc,mBAAmB,OAAO,WAAW;AACzD,QAAM,SAAS,cAAc,aAAa,OAAO,OAAO;AACxD,MAAI,OAAO,WAAW;AACpB,WAAO;AACT,SAAO,GAAG,MAAM,GAAG,QAAQ;AAC7B;AAEO,SAAS,YACd,QACA,MAAyB,QAAQ,KACnB;AACd,SAAO;AAAA,IACL,QAAQ,cAAc,MAAM;AAAA,IAC5B,QAAQ,OAAO,QAAQ,KAAK,KAAK,IAAI,WAAW,GAAG,KAAK,KAAK;AAAA,EAC/D;AACF;AAEO,SAAS,oBAAoB,QAAwC;AAC1E,MAAI,OAAO,WAAW;AACpB,WAAO,CAAC;AACV,SAAO,EAAE,eAAe,UAAU,MAAM,GAAG;AAC7C;;;ACnFO,IAAM,kBAAkB;AAE/B,IAAM,qBAAqB;AAOpB,SAAS,gBAAgB,aAAa,iBAAyB;AACpE,SAAO,QAAQ,UAAU;AAC3B;AAEO,SAAS,gBAAgB,YAAoB,aAAa,iBAAgC;AAC/F,QAAM,SAAS,gBAAgB,UAAU;AACzC,MAAI,CAAC,WAAW,WAAW,MAAM;AAC/B,WAAO,EAAE,WAAW,YAAY,UAAU,MAAM;AAClD,MAAI,WAAW,WAAW,MAAM,mBAAmB,KAAK,UAAU;AAChE,WAAO,EAAE,WAAW,YAAY,UAAU,MAAM;AAClD,QAAM,MAAM,WAAW,MAAM,OAAO,MAAM;AAC1C,MAAI,IAAI,WAAW;AACjB,WAAO,EAAE,WAAW,YAAY,UAAU,MAAM;AAClD,SAAO,EAAE,WAAW,KAAK,UAAU,KAAK;AAC1C;AAEO,SAAS,kBACd,OACA,aAAa,iBACb,iBACY;AACZ,QAAM,WAAW,MAAM,SAAS,KAAK,KAAK;AAC1C,QAAM,WAAW,CAAC,eAAe;AAC/B,UAAM,OAAO,gBAAgB,WAAW,MAAM,UAAU;AACxD,QAAI,CAAC,KAAK,UAAU;AAClB,UAAI,WAAW,KAAK,WAAW,gBAAgB,UAAU,CAAC;AACxD,0BAAkB,WAAW,IAAI;AACnC,aAAO,SAAS,UAAU;AAAA,IAC5B;AACA,WAAO,SAAS,EAAE,GAAG,YAAY,MAAM,KAAK,UAAU,CAAC;AAAA,EACzD;AACA,SAAO,MAAM;AACX,UAAM,WAAW;AAAA,EACnB;AACF;;;AFpCA,IAAM,iBAAiB,cAAc,YAAY,GAAG;AAE7C,IAAM,OAAO;AACb,IAAM,SAAS,CAAC,OAAO;AAE9B,eAAsB,MAAM,KAAc,SAA+B,CAAC,GAAkB;AAC1F,MAAI,UAAU,MAAM;AACpB,MAAI;AACJ,MAAI;AACJ,MAAI,aAAa;AACjB,MAAI,aAA4B,QAAQ,QAAQ;AAChD,MAAI;AACJ,MAAI;AAEJ,QAAM,eAAe,YAAY;AAC/B,mBAAe;AACf,mBAAe;AACf,UAAM,WAAW;AACjB,YAAQ;AACR,QAAI,UAAU;AACZ,UAAI;AACF,cAAM,SAAS,QAAQ;AAAA,MACzB,SACO,OAAO;AACZ,YAAI,OAAO,KAAK,OAAO,qDAAqD;AAAA,MAC9E;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UAAU,OAAO,QAAgB;AACrC,UAAM,OAAO,YAAY,QAAQ,CAAC;AAClC,QAAI,KAAK,OAAO,WAAW,KAAK,KAAK,OAAO,WAAW,GAAG;AACxD,YAAM,aAAa;AACnB,2BAAqB;AACrB,UAAI,OAAO,KAAK,uEAAuE;AACvF;AAAA,IACF;AACA,UAAM,cAAc,GAAG,KAAK,MAAM;AAAA,EAAK,KAAK,MAAM;AAClD,QAAI,gBAAgB,sBAAsB,OAAO;AAC/C;AAAA,IACF;AACA,UAAM,aAAa;AACnB,QAAI,QAAQ,YAAY;AACtB;AAAA,IACF;AACA,mBAAe,kBAAkB,IAAI,OAAO,iBAAiB,CAAC,eAAe;AAC3E,UAAI,OAAO,KAAK,gCAAgC,UAAU,EAAE;AAAA,IAC9D,CAAC;AACD,QAAI;AACF,YAAM,YAA0D,eAAe,6BAA6B;AAC5G,YAAM,YAAkC;AAAA,QACtC,YAAY;AAAA,QACZ,WAAW;AAAA,QACX,KAAK,KAAK;AAAA,QACV,SAAS,oBAAoB,KAAK,MAAM;AAAA,QACxC,oBAAoB;AAAA,QACpB,mBAAmB;AAAA,MACrB;AACA,YAAM,OAAO,IAAI,OAAO,WAAW,SAAS;AAC5C,cAAQ;AACR,YAAM;AAAA,IACR,SACO,OAAO;AACZ,qBAAe;AACf,qBAAe;AACf,cAAQ;AACR,UAAI,OAAO,KAAK,OAAO,gEAAgE;AACvF;AAAA,IACF;AACA,QAAI,QAAQ,YAAY;AACtB,YAAM,aAAa;AACnB,2BAAqB;AACrB;AAAA,IACF;AACA,yBAAqB;AACrB,QAAI,OAAO,KAAK,wCAAwC,KAAK,MAAM,EAAE;AAAA,EACvE;AAEA,QAAM,gBAAgB,MAAM;AAC1B,kBAAc;AACd,UAAM,MAAM;AACZ,iBAAa,WACV,KAAK,MAAM,QAAQ,GAAG,CAAC,EACvB,MAAM,CAAC,UAAiB;AACvB,UAAI,OAAO,KAAK,OAAO,4BAA4B;AAAA,IACrD,CAAC;AACH,WAAO;AAAA,EACT;AAEA,QAAM,sBAAsB,MAAM;AAChC,QAAI,cAAc;AAChB,mBAAa,YAAY;AAAA,IAC3B;AACA,mBAAe,WAAW,MAAM;AAC9B,qBAAe;AACf,WAAK,cAAc;AAAA,IACrB,GAAG,EAAE;AAAA,EACP;AAEA,MAAI,OAAO,CAAC,UAAU,GAAG,CAAC,WAAW;AAGnC,UAAM,cAAc,MAA4B;AAC9C,YAAM,aAAa,OAAO,SAAS,SAAS,EAAE,KAAK,eAAa,OAAO,UAAU,EAAE,MAAM,kBAAkB;AAC3G,aAAO,yBAAyB,YAAY,OAAO,MAAM;AAAA,IAC3D;AACA,cAAU;AACV,QAAI,GAAG,6BAA6B,CAAC,cAAc;AACjD,UAAI,OAAO,SAAS,MAAM,oBAAoB;AAC5C,4BAAoB;AAAA,MACtB;AAAA,IACF,CAAC;AAID,SAAK,cAAc;AAAA,EACrB,CAAC;AAED,MAAI,OAAO,MAAM,MAAM;AACrB,QAAI,cAAc;AAChB,mBAAa,YAAY;AACzB,qBAAe;AAAA,IACjB;AACA,SAAK,aAAa;AAAA,EACpB,GAAG,qBAAqB;AACxB,QAAM,cAAc;AACtB;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -4,19 +4,20 @@
|
|
|
4
4
|
"version": "0.0.1",
|
|
5
5
|
"private": true,
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@deepseek-ai/cordis": "4.0.
|
|
8
|
-
"@deepseek-ai/
|
|
9
|
-
"@deepseek-ai/dsh-client-
|
|
10
|
-
"@deepseek-ai/dsh-client-
|
|
11
|
-
"@deepseek-ai/dsh-client-ui-
|
|
12
|
-
"@deepseek-ai/dsh-client-ui-
|
|
13
|
-
"@deepseek-ai/dsh-client-ui-settings
|
|
14
|
-
"@deepseek-ai/dsh-client-ui-
|
|
15
|
-
"@deepseek-ai/dsh-
|
|
16
|
-
"@deepseek-ai/dsh-
|
|
17
|
-
"@deepseek-ai/dsh-
|
|
18
|
-
"@deepseek-ai/dsh-
|
|
19
|
-
"@deepseek-ai/
|
|
7
|
+
"@deepseek-ai/cordis": "4.0.4",
|
|
8
|
+
"@deepseek-ai/cosmokit": "1.8.5",
|
|
9
|
+
"@deepseek-ai/dsh-client-locale": "0.2.0-rc.2",
|
|
10
|
+
"@deepseek-ai/dsh-client-store": "0.2.0-rc.2",
|
|
11
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.2.0-rc.2",
|
|
12
|
+
"@deepseek-ai/dsh-client-ui-renderer": "0.2.0-rc.2",
|
|
13
|
+
"@deepseek-ai/dsh-client-ui-settings": "0.2.0-rc.2",
|
|
14
|
+
"@deepseek-ai/dsh-client-ui-settings-plugins": "0.2.0-rc.2",
|
|
15
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.2.0-rc.2",
|
|
16
|
+
"@deepseek-ai/dsh-mcp-client": "0.2.0-rc.2",
|
|
17
|
+
"@deepseek-ai/dsh-settings": "0.2.0-rc.2",
|
|
18
|
+
"@deepseek-ai/dsh-system-prompt": "0.2.0-rc.2",
|
|
19
|
+
"@deepseek-ai/dsh-tools": "0.2.0-rc.2",
|
|
20
|
+
"@deepseek-ai/schemastery": "3.18.4",
|
|
20
21
|
"react": "19.3.0"
|
|
21
22
|
}
|
|
22
23
|
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cloudedit
|
|
3
|
+
description: Domain fundamentals for editing MediaKit Edit cloud projects. Read it before CloudEdit project, timeline, material, or export tools. Covers projects, timelines, canvases, materials, assets, and elements; project-structure and timeline validation; subtitle timing when the user asks for subtitles; material registration from get_media_metadata; transitions, masks, and LUT color grading; and export when the user asks for a file.
|
|
4
|
+
requiredTools:
|
|
5
|
+
- get_media_metadata
|
|
6
|
+
- create_project
|
|
7
|
+
- list_projects
|
|
8
|
+
- read_project
|
|
9
|
+
- sync_materials
|
|
10
|
+
- list_materials
|
|
11
|
+
- inspect_materials
|
|
12
|
+
- list_scenario_skills
|
|
13
|
+
- read_scenario_skill
|
|
14
|
+
- preview_timeline
|
|
15
|
+
- inspect_elements
|
|
16
|
+
- edit_element
|
|
17
|
+
- export_project
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Purpose
|
|
21
|
+
|
|
22
|
+
This skill provides domain knowledge for MediaKit Edit projects. Its primary purpose is to help you register materials, edit them on a nonlinear timeline, and export only when the user asks for a file.
|
|
23
|
+
|
|
24
|
+
## Tools
|
|
25
|
+
|
|
26
|
+
CloudEdit tools are already registered. Call them by id. This environment has no shell. Never look for, install, or run mediakit-cli, and never use a terminal command.
|
|
27
|
+
|
|
28
|
+
If a tool call fails, say which tool failed and what it returned. Do not look for an MCP process, and do not switch to another channel.
|
|
29
|
+
|
|
30
|
+
## Role
|
|
31
|
+
|
|
32
|
+
When editing a CloudEdit project, act as an audio and video editor.
|
|
33
|
+
|
|
34
|
+
Keep your default personality while using the MCP, and keep your tone concise, direct, and friendly. Communicate efficiently and make the current editing stage clear to the user without unnecessary detail. Prioritize actionable guidance and clearly state assumptions, environmental prerequisites, and next steps. Do not give lengthy explanations unless explicitly asked.
|
|
35
|
+
|
|
36
|
+
### Progress Updates
|
|
37
|
+
|
|
38
|
+
Before calling any CloudEdit tool, briefly describe the operation you are about to perform in natural language. Follow these principles and examples:
|
|
39
|
+
|
|
40
|
+
- Group related operations logically: if you will run several related editing operations, describe them together in one preamble instead of sending a separate message for every command.
|
|
41
|
+
- Be concise: use no more than one or two sentences and emphasize an immediately actionable next step. A quick update can be only a few words.
|
|
42
|
+
- Build on prior context: after the first tool call, connect the next preamble to completed work so the user understands the momentum and the next action.
|
|
43
|
+
- Keep the tone relaxed, friendly, and curious: add a little personality so the collaboration feels engaging.
|
|
44
|
+
- Exception: unless it belongs to a larger grouped operation, avoid adding a preamble for every simple read operation, such as reading a single file.
|
|
45
|
+
|
|
46
|
+
For example:
|
|
47
|
+
|
|
48
|
+
“I’ve reviewed the current project materials. Next, I’ll implement the editing plan we discussed.”
|
|
49
|
+
“Next, I’ll create the tracks and update the relevant materials.”
|
|
50
|
+
“I’m about to plan the concrete edit.”
|
|
51
|
+
“The selected ranges are on the timeline. Next I’ll read them back.”
|
|
52
|
+
“The earlier material could not be queried. I’ll check the material pool.”
|
|
53
|
+
“I’ve diagnosed the missing-element issue and confirmed that a timeline change caused it. Next, I’ll edit against the updated timeline.”
|
|
54
|
+
“That’s an interesting idea. I’m checking how the current timeline implements it.”
|
|
55
|
+
“This export takes a while. I’ll continue once the file is ready.”
|
|
56
|
+
|
|
57
|
+
## Unable to Complete
|
|
58
|
+
|
|
59
|
+
When the issue cannot be resolved from the available environment and reliable verification evidence, clearly explain the current result, the problem, and a dependable proposed solution. This does not necessarily mean the task has ended or failed; ask the user for the specific new information needed to achieve the original goal.
|
|
60
|
+
|
|
61
|
+
- Clearly state the current result.
|
|
62
|
+
- Explain what was verified.
|
|
63
|
+
- Do not claim unverified content succeeded. Never use assumptions, inference, or guesses as verification. All verification must have a quality-assurance basis.
|
|
64
|
+
- If a verification step cannot be performed, give the user executable steps.
|
|
65
|
+
- Offer a natural, concrete next step instead of a vague “try again later.”
|
|
66
|
+
|
|
67
|
+
Never use assumptions, inference, or guesses to present unreliable results, verification, content, or solutions.
|
|
68
|
+
|
|
69
|
+
## Interruption
|
|
70
|
+
|
|
71
|
+
`asr_subtitles`, `probe_video_metadata`, `probe_audio_metadata`, `media_understanding`, `analyze_video_highlights`, and `export_project` return the finished task body on that same call. Read `subtitles`, metadata, `contents`, `highlight_info`, or `video_url` from `result`. If `status` is `failed`, read `error` (`code`, `message`, `param`, `type`) and do not submit the same task again.
|
|
72
|
+
|
|
73
|
+
## Discussing the Result with the User
|
|
74
|
+
|
|
75
|
+
During execution, apply the plan already agreed, or the user's exact instruction. Do not open another content-understanding pass, and do not call `media_understanding` or `analyze_video_highlights` when the plan already has in and out times. A missing source time is a planning gap. Do not fill it with a shell command. Ask only when a choice would change the result and cannot be resolved from the plan or the material. When the timeline is ready, tell the user what changed and what they can review in the editor.
|
|
76
|
+
|
|
77
|
+
## Mandatory Gate
|
|
78
|
+
|
|
79
|
+
After completing a logical editing batch and before reporting completion, read and execute the `timeline_review` profile in the [editing quality gate](./references/edit-quality-gate.md). Trigger the gate by user intent and editing stage, not after every tool write.
|
|
80
|
+
|
|
81
|
+
Gate evidence begins after the final write in the batch. Do not report that the gate passed until the final revision readback required by the gate is complete.
|
|
82
|
+
|
|
83
|
+
Run an additional `structure_checkpoint` before entering a dependent subtitle, audio, effect, or packaging stage only when structural changes affect that downstream work. Combine consecutive structural writes in the same batch into one check.
|
|
84
|
+
|
|
85
|
+
## Subtitles
|
|
86
|
+
|
|
87
|
+
Read [edit-asr](./references/amk-scenarios/edit-asr.md) only when this batch creates or changes subtitles. Do not call `asr_subtitles` to understand dialogue. The alignment ledger in that guide applies only to that subtitle batch.
|
|
88
|
+
|
|
89
|
+
## Final Deliverable
|
|
90
|
+
|
|
91
|
+
The exported artifact is the final result of the editing process. MediaKit CloudEdit is a human-agent collaboration: the user previews the editing project, reviews your work, and makes adjustments. **Unless the user explicitly requests an export or there is a concrete, viewable video file, do not export immediately after editing. Users generally prefer to review the editing project before deciding the next step.**
|
|
92
|
+
|
|
93
|
+
## Project Fundamentals
|
|
94
|
+
|
|
95
|
+
`references/edit-project.md` explains how to keep video-editing or video-processing work editable in MediaKit Edit cloud editing. It covers material understanding, editing, text (subtitles and secondary titles), visual overlays, filters, effects, project/editor inspection, previewing, exporting, and validation. **Read this document before editing video with the MediaKit Edit MCP.**
|
|
96
|
+
|
|
97
|
+
## Subskills
|
|
98
|
+
|
|
99
|
+
Subskills are stored in the `references` directory.
|
|
100
|
+
|
|
101
|
+
| name | description | source |
|
|
102
|
+
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- |
|
|
103
|
+
| edit-shader | Guidance for applying Shader-based video effects, transitions, filters, and color grading (LUT and manual grading). Use when the user requests filters, effects, transitions, or color grading; when adding, deleting, or replacing transitions; or when a structural edit involves main-track clips that already have transitions. If `preview_timeline.has_transition=true`, this guide must be read. | references/edit-shader.md |
|
|
104
|
+
| edit-timeline | Guidance for timeline updates, including tracks and changes to the temporal structure of elements. | references/edit-timeline.md |
|
|
105
|
+
| edit-text | Guidance for changing text position, style, size, and animation, including common add and update scenarios. | references/edit-text.md |
|
|
106
|
+
| edit-position | Element and canvas configuration, positioning, and geometry, including dimensions, horizontal and vertical placement, adaptive fitting, rotation, and cropping. | references/edit-position.md |
|
|
107
|
+
| edit-quality-gate | Editor project gate for completed logical editing batches. Reads back revisions, tracks, elements, materials, and parameters through the available MCP without claiming to validate the viewer, perceived audio, or final file. | references/edit-quality-gate.md |
|
|
108
|
+
|
|
109
|
+
## Subtitle mapping
|
|
110
|
+
|
|
111
|
+
| name | description | source |
|
|
112
|
+
| -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ |
|
|
113
|
+
| edit-asr | When the user asked for subtitles, map completed ASR segments onto each final media occurrence, then create, read back, and validate them. Do not open this guide to retrieve a transcript or to understand speech. | references/amk-scenarios/edit-asr.md |
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Aligning ASR Subtitles with the Editing Timeline
|
|
2
|
+
|
|
3
|
+
Read this guide only when this batch creates or changes subtitles. It converts completed `asr_subtitles` times from a complete source into timeline times after trimming, splitting, moving, speed changes, reuse, or transitions. Subtitle style and layout follow the [text editing guide](../edit-text.md). Do not read it to retrieve a transcript or to understand dialogue.
|
|
4
|
+
|
|
5
|
+
## Core Constraints
|
|
6
|
+
|
|
7
|
+
- ASR timestamps belong to **source time**, measured from the start of the complete original material.
|
|
8
|
+
- Subtitle `from_time_ms` and `duration_ms` belong to **timeline time**, measured from the start of the edited project.
|
|
9
|
+
- Source time and timeline time are different clocks. Never copy a raw ASR timestamp directly into a subtitle element.
|
|
10
|
+
- Run ASR once for each complete original material. When the project contains materials A and B, keep their ASR results separate. Match each result to timeline elements by exact source/material ID, not by file order, similar content, or timestamp.
|
|
11
|
+
- Mapping must use `preview_timeline` after the final structural edit. Its `timeline_range` and `source_range` are a paired logical projection from the same call. When transitions exist, never combine a preview time range directly with the raw source range from `inspect_elements`.
|
|
12
|
+
- The formulas in this guide calculate only the source-time-to-logical-time relationship within one element. Do not use them to predict timeline changes caused by transitions, ripple, absorb, or movement.
|
|
13
|
+
- Complete all structural edits that change dialogue positions before generating subtitles. If later structural changes are still required, execute the `structure_checkpoint` in the [editing quality gate](../edit-quality-gate.md).
|
|
14
|
+
- Add subtitles only for dialogue that is actually audible in the final edit. For muted media, replacement audio, or a video’s embedded audio coexisting with extracted audio, use the element that actually produces sound and avoid duplicate subtitles. Spatial cropping, scaling, and positioning do not affect subtitle timing.
|
|
15
|
+
|
|
16
|
+
## Time Ranges
|
|
17
|
+
|
|
18
|
+
Use half-open ranges `[start, end)` throughout. The start is included and the end is excluded. For each final media occurrence, keep these ranges distinct:
|
|
19
|
+
|
|
20
|
+
- ASR range `[A0, A1)`: where one recognized utterance occurs in the complete original material;
|
|
21
|
+
- element source range `[S0, S1)`: the source content represented by the final timeline element;
|
|
22
|
+
- element timeline range `[T0, T1)`: where that element appears in the edited project.
|
|
23
|
+
|
|
24
|
+
The ASR result answers "what is spoken, and when, in the complete source." The element's paired source and timeline ranges answer "which part of that source is present, and where it appears in the final edit."
|
|
25
|
+
|
|
26
|
+
## Result Readiness and Alignment Ledger
|
|
27
|
+
|
|
28
|
+
Require a terminal, completed ASR result for the complete original material before creating subtitles. `submitted`, `queued`, `processing`, partial, timed-out, or failed is not ready. Query the existing task until it finishes; do not resubmit it. Never invent subtitle text or distribute subtitles evenly while the result is unresolved.
|
|
29
|
+
|
|
30
|
+
Before mapping, record one alignment ledger for each final media occurrence:
|
|
31
|
+
|
|
32
|
+
- exact source/material ID;
|
|
33
|
+
- completed whole-source ASR result ID and each candidate segment's ID, text, and source range `[A0, A1)`;
|
|
34
|
+
- final media element ID;
|
|
35
|
+
- latest `preview_timeline` revision, paired source range `[S0, S1)`, paired timeline range `[T0, T1)`, and playback rate `R`;
|
|
36
|
+
- retained intersection `[I0, I1)` and calculated subtitle timeline range.
|
|
37
|
+
|
|
38
|
+
Only the completed whole-source ASR result supplies subtitle text and source timestamps. A title, summary, score, OCR result, filename, or progress message cannot replace it.
|
|
39
|
+
|
|
40
|
+
## Source-to-Timeline Workflow
|
|
41
|
+
|
|
42
|
+
Follow this order for every source material:
|
|
43
|
+
|
|
44
|
+
1. Run ASR on the complete original audio/video. Keep every returned text segment attached to that source ID and its source-time range.
|
|
45
|
+
2. Complete the timeline structure before placing subtitles.
|
|
46
|
+
3. Call `preview_timeline` and read the final paired `source_range` and `timeline_range` for each media occurrence.
|
|
47
|
+
4. For each occurrence, select only ASR segments from the same source/material ID, intersect them with the element's source range, then map the retained intersection to timeline time.
|
|
48
|
+
5. Write subtitles only after every pending entry passes the checks in this guide.
|
|
49
|
+
|
|
50
|
+
Do not run one combined mapping across A and B. Process each final occurrence independently. The same source may be transcribed once and used in several occurrences, but each occurrence needs its own intersection and timeline mapping.
|
|
51
|
+
|
|
52
|
+
## Establishing Element Mapping Ranges
|
|
53
|
+
|
|
54
|
+
Use the element ID to associate two kinds of facts for the same media element:
|
|
55
|
+
|
|
56
|
+
- `preview_timeline`: the final logical range `[T0, T1)` and, when present, its paired projected source range `[S0, S1)`;
|
|
57
|
+
- `inspect_elements`: the material ID, raw `from_time_ms`/`duration_ms`, explicit `source_start_ms`/`source_end_ms`, `playback_rate`, and audio state.
|
|
58
|
+
|
|
59
|
+
If `playback_rate` is absent, treat it as `1`. The ASR input must correspond to the source material actually referenced by the element. When the same material appears multiple times, it may be transcribed once, but each element instance must be mapped separately.
|
|
60
|
+
|
|
61
|
+
Prefer the `[T0, T1)` and `[S0, S1)` pair from the same `preview_timeline` item. If that item has no `source_range`, complete the source range paired with the preview range as follows:
|
|
62
|
+
|
|
63
|
+
1. Obtain the raw logical range `[U0, U1)` and playback rate `R` from `inspect_elements`.
|
|
64
|
+
2. If an explicit source range exists, set `[B0, B1)` to `source_start_ms`/`source_end_ms`. Otherwise, obtain material duration `D` from `inspect_materials` (pass `material_ids` as an array even for a single material) and set `[B0, B1) = [0, D)`.
|
|
65
|
+
3. Project the source range using the boundary differences returned by the preview; do not infer it from transition parameters:
|
|
66
|
+
|
|
67
|
+
```text
|
|
68
|
+
S0 = B0 + (T0 - U0) × R
|
|
69
|
+
S1 = B1 + (T1 - U1) × R
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Before continuing, confirm that `S0 >= B0`, `S1 <= B1`, and `S0 < S1`, and that `(S1 - S0)` matches `(T1 - T0) × R` within integer-millisecond rounding error. If complete facts are unavailable or the relationship is inconsistent, stop mapping and first read back or correct the element state.
|
|
73
|
+
|
|
74
|
+
## Mapping ASR Segments
|
|
75
|
+
|
|
76
|
+
Calculate in milliseconds throughout. If ASR returns seconds, multiply by `1000`; round to integer milliseconds only when generating the pending subtitle entries.
|
|
77
|
+
|
|
78
|
+
For an ASR segment `[A0, A1)` and the final element source range `[S0, S1)`, calculate the retained source interval:
|
|
79
|
+
|
|
80
|
+
```text
|
|
81
|
+
I0 = max(A0, S0)
|
|
82
|
+
I1 = min(A1, S1)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
If `I0 >= I1`, that utterance is not present in this final occurrence, so do not create a subtitle. If `I0 < I1`, map only the retained interval:
|
|
86
|
+
|
|
87
|
+
```text
|
|
88
|
+
subtitle from_time_ms = T0 + (I0 - S0) / R
|
|
89
|
+
subtitle endTimeMs = T0 + (I1 - S0) / R
|
|
90
|
+
subtitle duration_ms = subtitle endTimeMs - subtitle from_time_ms
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Example: material A
|
|
94
|
+
|
|
95
|
+
ASR analyzes complete material A and returns:
|
|
96
|
+
|
|
97
|
+
```text
|
|
98
|
+
A [1000, 3000): "sub1"
|
|
99
|
+
A [7000, 9000): "sub2"
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The final 1x element uses source range `A [5000, 11000)` and appears on timeline `[1000, 7000)`.
|
|
103
|
+
|
|
104
|
+
- `"sub1"` has no intersection with `[5000, 11000)`, so no subtitle is created.
|
|
105
|
+
- `"sub2"` intersects the retained source at `[7000, 9000)`.
|
|
106
|
+
- The source point `7000ms` is `2000ms` after the element's source start. Because the element starts at timeline `1000ms`, the subtitle starts at `1000 + 2000 = 3000ms`.
|
|
107
|
+
- The source point `9000ms` maps to timeline `5000ms`.
|
|
108
|
+
|
|
109
|
+
The resulting subtitle is:
|
|
110
|
+
|
|
111
|
+
```text
|
|
112
|
+
text: "sub2"
|
|
113
|
+
from_time_ms: 3000
|
|
114
|
+
duration_ms: 2000
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
If the same source range instead starts at timeline `0ms`, the ASR segment maps to `[2000, 4000)`, not `[3000, 5000)`. Always include `T0`; do not infer the answer from the source range alone.
|
|
118
|
+
|
|
119
|
+
### Example: playback speed
|
|
120
|
+
|
|
121
|
+
If an ASR segment occupies `[12000, 14000)` in the source, the element's paired source range is `[10000, 16000)`, its timeline start is `30000ms`, and `R = 2`, the subtitle occupies `[31000, 32000)` on the timeline.
|
|
122
|
+
|
|
123
|
+
For trimming, splitting, and material reuse, calculate intersections separately for every preview element instance. For positional changes caused by movement, transitions, ripple, or absorb, use only the latest preview result. Handle speed-up or slowdown through `R` in the formula.
|
|
124
|
+
|
|
125
|
+
For a verified contiguous 1x element, the equivalent shortcut is `timeline_time = T0 + source_time - S0`. Use it only after reading back the final structure. Do not use this shortcut when transitions, speed changes, movement, splitting, or reuse change the final paired ranges.
|
|
126
|
+
|
|
127
|
+
## Trimming into the Middle of a Sentence
|
|
128
|
+
|
|
129
|
+
A time intersection proves only that part of the speech remains; it does not determine which words should remain:
|
|
130
|
+
|
|
131
|
+
- If word-level timestamps are available, retain only the words actually audible within the intersection and use their real start and end times.
|
|
132
|
+
- If only sentence-level timestamps are available, re-run recognition on the retained range when tool capabilities allow, or segment the sentence again from evidence that can be listened to.
|
|
133
|
+
- If the remaining fragment cannot be determined reliably, do not truncate text proportionally by duration and do not complete or retain an entire sentence that is no longer audible. Add it as a manual-review item.
|
|
134
|
+
|
|
135
|
+
Correct subtitle text only when audio or reliable context supports the recognition correction. Do not polish, rewrite, or invent dialogue.
|
|
136
|
+
|
|
137
|
+
## Writing Subtitles
|
|
138
|
+
|
|
139
|
+
Sort subtitles by their final timeline positions before writing. Every item must record the media element ID, subtitle text, intersected source range, and target timeline range. When trimming occurs, a material is reused, or recognition is abnormal, also record the ASR segment ID and a handling note.
|
|
140
|
+
|
|
141
|
+
- Create separate `text` elements for consecutive sentences. Each element’s `from_time_ms` and `duration_ms` must cover only the actual speech for that sentence.
|
|
142
|
+
- When one utterance spans multiple retained elements, intersect it with each element separately. Write separate text only when the words belonging to each part can be determined.
|
|
143
|
+
- If rounding makes adjacent subtitles overlap, tighten them to the true speech boundary. Do not write a segment whose rounded end time is not greater than its start time.
|
|
144
|
+
- Prefer reusing an existing subtitle track. If no recognizable dialogue exists, do not create an empty track. For simultaneous speakers, keep each speaker on a separate line for the overlapping interval.
|
|
145
|
+
- Follow the [text editing guide](../edit-text.md) for subtitle visual parameters.
|
|
146
|
+
|
|
147
|
+
## Alignment Checks
|
|
148
|
+
|
|
149
|
+
After writing, read back the subtitles from this batch and their corresponding media elements, and confirm that:
|
|
150
|
+
|
|
151
|
+
- the ASR result and final media element refer to the same source/material ID;
|
|
152
|
+
- each retained source range equals the intersection of its ASR range and final element source range;
|
|
153
|
+
- subtitle text, element ID, and final timing match the pending entry;
|
|
154
|
+
- every subtitle lies within `[T0, T1)` of its corresponding preview element and is mapped from the paired `[S0, S1)` using the playback rate;
|
|
155
|
+
- there are no duplicate subtitles, unintended overlaps, empty text, or subtitles covering content that was trimmed out;
|
|
156
|
+
- the recorded alignment ledger matches the read-back element IDs, source ranges, timeline ranges, and mapping revision.
|
|
157
|
+
|
|
158
|
+
Then execute the `timeline_review` profile in the [editing quality gate](../edit-quality-gate.md). Do not repeat the general revision, track, and text-parameter checks here. Existing MCP readback can validate only project-parameter relationships; real perceived synchronization must still be checked by playing the edit in the editor.
|
|
159
|
+
|
|
160
|
+
If later edits trim, split, move, delete, change speed, or modify transitions again, remap and read back only the affected elements from the latest `preview_timeline`. Never guess using a uniform offset.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
## Element Position and Canvas
|
|
2
|
+
|
|
3
|
+
Elements use `pos_x` and `pos_y` for precise placement on the Canvas, where `(0, 0)` starts at the top-left corner.
|
|
4
|
+
|
|
5
|
+
- **Contrary to the usual interpretation, `flip_x` mirrors vertically and `flip_y` mirrors horizontally.**
|
|
6
|
+
|
|
7
|
+
## Canvas Size
|
|
8
|
+
|
|
9
|
+
### Initializing the Canvas
|
|
10
|
+
|
|
11
|
+
Use `create_project` and `update_project` to initialize or change the canvas size.
|
|
12
|
+
|
|
13
|
+
- When calling `create_project`, pass `canvas` only if the user explicitly specifies a canvas size. Otherwise, do not initialize the canvas size proactively.
|
|
14
|
+
- A user-specified canvas size takes precedence over material dimensions. Do not override it unless the user requests a change.
|
|
15
|
+
|
|
16
|
+
### Inferring the Canvas from Context
|
|
17
|
+
|
|
18
|
+
Before formal editing, if the user did not specify a canvas size, `update_project` may update the canvas based on context. Apply this priority order:
|
|
19
|
+
|
|
20
|
+
1. If the user specified an aspect ratio or a platform frame, use that with the material dimensions from `get_media_metadata`.
|
|
21
|
+
2. If all materials are landscape or all are portrait, default to `16:9` or `9:16`, respectively.
|
|
22
|
+
|
|
23
|
+
If only an orientation or aspect ratio is known, with no exact dimensions, refer to [Standard Canvas Sizes](#standard-canvas-sizes). If orientations are mixed and no applicable rule exists, do not force a canvas size; preserve the editor’s behavior of inferring it from added elements.
|
|
24
|
+
|
|
25
|
+
After setting a context-derived canvas, tell the user the selected dimensions and rationale in the final response.
|
|
26
|
+
|
|
27
|
+
Changing the canvas size does not resize elements. After the change, use `preview_timeline` to check that element sizes, positions, and text sizes match the canvas. The fastest adaptation is:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
"updates": [
|
|
31
|
+
{
|
|
32
|
+
"id": "element-id",
|
|
33
|
+
"fit": "contain"
|
|
34
|
+
}
|
|
35
|
+
]
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Standard Canvas Sizes
|
|
39
|
+
|
|
40
|
+
The following dimensions are expressed as width × height. Landscape is `16:9`; portrait is `9:16`:
|
|
41
|
+
|
|
42
|
+
- 720P: 1280 × 720 or 720 × 1280.
|
|
43
|
+
- 1080P: 1920 × 1080 or 1080 × 1920.
|
|
44
|
+
- 4K: 3840 × 2160 or 2160 × 3840.
|
|
45
|
+
|
|
46
|
+
## Vertical and Horizon
|
|
47
|
+
|
|
48
|
+
`vertical` is mutually exclusive with `pos_y`, and `horizon` is mutually exclusive with `pos_x`. Use `vertical` and `horizon` for nine-grid layouts. Except for text elements, their calculation depends on both the element dimensions and canvas dimensions.
|
|
49
|
+
|
|
50
|
+
Text normally needs only `horizon` and `vertical`. When `width` is omitted for horizontal text, it
|
|
51
|
+
defaults to 80% of the Canvas width. Horizontal text generally does not need an explicit height, and
|
|
52
|
+
vertical text generally does not need an explicit width. Semantic text placement automatically keeps a
|
|
53
|
+
10 px safe inset on every Canvas edge, and offsets cannot move the result out of bounds. Non-text
|
|
54
|
+
elements continue to use the full Canvas for placement calculations.
|
|
55
|
+
|
|
56
|
+
## Canvas Placement with `fit`
|
|
57
|
+
|
|
58
|
+
- **When adding an element to a track, pass `fit` so it adapts to the canvas.**
|
|
59
|
+
|
|
60
|
+
`fit` places an element adaptively according to the canvas dimensions and the source material dimensions. Unless the user explicitly requires the image to contain no empty space, normally use `contain` when adding an element across the canvas. **Use `fit` to quickly place an element across the canvas or adapt elements after changing the canvas size.**
|
|
61
|
+
|
|
62
|
+
## Rotation
|
|
63
|
+
|
|
64
|
+
Rotation uses the element center as its axis and proceeds clockwise, in the range 0–360 degrees.
|