@deepblend/dsh-blender-ui 0.3.4 → 0.3.5
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 +110 -20
- package/lib/index.js +10 -2
- package/package.json +2 -2
package/lib/client.js
CHANGED
|
@@ -488,6 +488,18 @@ window.__ModuleLoader__.load({
|
|
|
488
488
|
'recipes.license': '作者与许可',
|
|
489
489
|
'recipes.unavailable': '部分配方无法载入;可继续使用下方可用的配方。',
|
|
490
490
|
'projects.creating': '创建中…',
|
|
491
|
+
'projects.incomplete': '尚未完成创建',
|
|
492
|
+
'projects.recovered': '已打开之前创建的 {id},未重复创建或渲染',
|
|
493
|
+
'projects.replyMissing': '未能收到创建结果。输入已保留;重试上次创建会检查同一次请求。',
|
|
494
|
+
'projects.environmentFailed': 'Blender 环境尚未就绪。输入已保留;请先检查安装和路径,修复后再重试。',
|
|
495
|
+
'projects.creationFailed': '未能确认创建结果。输入已保留;可重试上次创建,或刷新列表检查已保存的项目。',
|
|
496
|
+
'projects.retryCreation': '重试上次创建',
|
|
497
|
+
'projects.originalTitle': '上次创建:{title}',
|
|
498
|
+
'projects.refreshList': '刷新项目列表',
|
|
499
|
+
'projects.creationDetails': '详细诊断',
|
|
500
|
+
'projects.environmentGuide': '查看环境检查指南',
|
|
501
|
+
'projects.creationConflict': '上次创建记录需要检查。输入已保留;请刷新列表并查看详细诊断,确认项目状态。',
|
|
502
|
+
'projects.hostNeedsRestart': '当前运行的 Host 尚不支持创建恢复。请更新 DeepBlend 并重启 DSH,输入仍保留在当前页面。',
|
|
491
503
|
'projects.createButton': '创建',
|
|
492
504
|
'scene.currentRevision': '当前 revision',
|
|
493
505
|
'scene.revisionCount': 'revision 数',
|
|
@@ -1025,6 +1037,18 @@ window.__ModuleLoader__.load({
|
|
|
1025
1037
|
'recipes.license': 'Author and license',
|
|
1026
1038
|
'recipes.unavailable': 'Some recipes could not be loaded. Available recipes are shown below.',
|
|
1027
1039
|
'projects.creating': 'Creating…',
|
|
1040
|
+
'projects.incomplete': 'Creation is not complete',
|
|
1041
|
+
'projects.recovered': 'Opened the previously created {id}; no duplicate creation or render',
|
|
1042
|
+
'projects.replyMissing': 'The creation result could not be received. Your inputs are kept; retry the previous creation to check the same request.',
|
|
1043
|
+
'projects.environmentFailed': 'Blender is not ready. Your inputs are kept; check its installation and path, then retry.',
|
|
1044
|
+
'projects.creationFailed': 'The creation result could not be confirmed. Your inputs are kept; retry the previous creation, or refresh the list to check saved projects.',
|
|
1045
|
+
'projects.retryCreation': 'Retry previous creation',
|
|
1046
|
+
'projects.originalTitle': 'Previous creation: {title}',
|
|
1047
|
+
'projects.refreshList': 'Refresh project list',
|
|
1048
|
+
'projects.creationDetails': 'Diagnostic details',
|
|
1049
|
+
'projects.environmentGuide': 'Open the environment check guide',
|
|
1050
|
+
'projects.creationConflict': 'The previous creation record needs inspection. Your inputs are kept; refresh the list and read the diagnostic details to check its state.',
|
|
1051
|
+
'projects.hostNeedsRestart': 'The running Host does not support creation recovery. Update DeepBlend and restart DSH; your inputs remain on this page.',
|
|
1028
1052
|
'projects.createButton': 'Create',
|
|
1029
1053
|
'scene.currentRevision': 'current revision',
|
|
1030
1054
|
'scene.revisionCount': 'revisions',
|
|
@@ -1267,6 +1291,7 @@ window.__ModuleLoader__.load({
|
|
|
1267
1291
|
const PANEL_LABEL = 'Blender'
|
|
1268
1292
|
/** The host API this half was written against; a mismatch is a deployment state. */
|
|
1269
1293
|
const EXPECTED_HOST_API = 6
|
|
1294
|
+
const CREATION_REQUEST_PROTOCOL = 'deepblend.creation-request/v1'
|
|
1270
1295
|
/** Polling cadence while something is live; M3 writes progress once a second. */
|
|
1271
1296
|
const POLL_LIVE_MS = 1500
|
|
1272
1297
|
/** Polling cadence when nothing is running. */
|
|
@@ -1329,6 +1354,23 @@ window.__ModuleLoader__.load({
|
|
|
1329
1354
|
return `${base}${path}?v=${version}`
|
|
1330
1355
|
}
|
|
1331
1356
|
|
|
1357
|
+
const creationSignature = body => JSON.stringify(body, (_, value) => value && typeof value === 'object' && !Array.isArray(value)
|
|
1358
|
+
? Object.fromEntries(Object.keys(value).sort().map(key => [key, value[key]])) : value)
|
|
1359
|
+
let creationKeySequence = 0
|
|
1360
|
+
function newCreationKey() {
|
|
1361
|
+
const bytes = new Uint32Array(4)
|
|
1362
|
+
if (globalThis.crypto?.getRandomValues) globalThis.crypto.getRandomValues(bytes)
|
|
1363
|
+
else for (let i = 0; i < bytes.length; i++) bytes[i] = Math.floor(Math.random() * 4294967296)
|
|
1364
|
+
return `create-${Date.now().toString(36)}-${(++creationKeySequence).toString(36)}-${[...bytes].map(n => n.toString(16).padStart(8, '0')).join('')}`
|
|
1365
|
+
}
|
|
1366
|
+
function creationErrorMessage(code) {
|
|
1367
|
+
if (code === 'UI_FETCH_FAILED' || /^HTTP_/.test(code)) return t('projects.replyMissing')
|
|
1368
|
+
if (code === 'BLENDER_NOT_FOUND' || code === 'RUNTIME_UNAVAILABLE') return t('projects.environmentFailed')
|
|
1369
|
+
if (code === 'CREATION_REQUEST_CONFLICT') return t('projects.creationConflict')
|
|
1370
|
+
if (code === 'UI_HOST_API_STALE') return t('projects.hostNeedsRestart')
|
|
1371
|
+
return t('projects.creationFailed')
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1332
1374
|
const imageDownloadKey = (projectId, artifact) => JSON.stringify([projectId, artifact?.path, artifact?.sha256])
|
|
1333
1375
|
function imageDownloadValid(artifact) {
|
|
1334
1376
|
return Boolean(artifact && typeof artifact.path === 'string' && /\.png$/i.test(artifact.path)
|
|
@@ -2436,6 +2478,8 @@ window.__ModuleLoader__.load({
|
|
|
2436
2478
|
diffError: null,
|
|
2437
2479
|
previewBusy: false,
|
|
2438
2480
|
imageDownloads: {},
|
|
2481
|
+
creationWork: null,
|
|
2482
|
+
creationRequestProtocol: null,
|
|
2439
2483
|
inspectionForms: {},
|
|
2440
2484
|
inspectionWork: {},
|
|
2441
2485
|
inspectionRevisions: {},
|
|
@@ -2496,6 +2540,7 @@ window.__ModuleLoader__.load({
|
|
|
2496
2540
|
let tick = 0
|
|
2497
2541
|
let live = false
|
|
2498
2542
|
let loadSequence = 0
|
|
2543
|
+
let creationAttempt = null
|
|
2499
2544
|
let fileChoiceSignature = null, fileChoicesStopped = false
|
|
2500
2545
|
const photographyControllers = new Map()
|
|
2501
2546
|
const photographyWork = (projectId, changes) => set({ photographyWork: { ...data.photographyWork, [projectId]: { ...data.photographyWork[projectId], ...changes } } })
|
|
@@ -2550,7 +2595,7 @@ window.__ModuleLoader__.load({
|
|
|
2550
2595
|
const setIn = (table, key, value) => { data = { ...data, [table]: { ...data[table], [key]: value } }; notify() }
|
|
2551
2596
|
|
|
2552
2597
|
/** The project every per-project route is addressed to. */
|
|
2553
|
-
const target = () => data.projectId ??
|
|
2598
|
+
const target = () => data.projectId ?? data.projects.find(project => !project.creationPending)?.projectId ?? null
|
|
2554
2599
|
|
|
2555
2600
|
/** Read everything the panel shows, in one pass. */
|
|
2556
2601
|
const load = async () => {
|
|
@@ -2564,7 +2609,8 @@ window.__ModuleLoader__.load({
|
|
|
2564
2609
|
return
|
|
2565
2610
|
}
|
|
2566
2611
|
const payload = stateResult.payload
|
|
2567
|
-
const
|
|
2612
|
+
const pending = payload.projects?.some(project => project.projectId === projectId && project.creationPending)
|
|
2613
|
+
const active = pending ? null : projectId ?? payload.projects?.find(project => !project.creationPending)?.projectId ?? null
|
|
2568
2614
|
const patch = {
|
|
2569
2615
|
status: 'ok',
|
|
2570
2616
|
error: null,
|
|
@@ -2572,6 +2618,7 @@ window.__ModuleLoader__.load({
|
|
|
2572
2618
|
projects: payload.projects || [],
|
|
2573
2619
|
recipeCatalog: payload.recipeCatalog ?? { recipes: [], errors: [] },
|
|
2574
2620
|
projectsRoot: payload.projectsRoot ?? null,
|
|
2621
|
+
creationRequestProtocol: payload.creationRequestProtocol ?? null,
|
|
2575
2622
|
selected: payload.selected ?? null,
|
|
2576
2623
|
activeProjectId: active,
|
|
2577
2624
|
currentRevision: payload.selected ? payload.selected.currentRevision : null,
|
|
@@ -2637,6 +2684,44 @@ window.__ModuleLoader__.load({
|
|
|
2637
2684
|
/** A write just happened: re-run every read, including the view-specific ones. */
|
|
2638
2685
|
const reload = () => { tick += 1; void load() }
|
|
2639
2686
|
|
|
2687
|
+
const creationBody = () => editorClone({ title: data.forms.title.trim(),
|
|
2688
|
+
goal: data.forms.goal.length > 0 ? data.forms.goal : undefined,
|
|
2689
|
+
renderPreview: Boolean(data.forms.recipe), recipe: data.forms.recipe ? {
|
|
2690
|
+
id: data.forms.recipe.id, version: data.forms.recipe.version, digest: data.forms.recipe.digest,
|
|
2691
|
+
parameters: data.forms.recipeParameters,
|
|
2692
|
+
} : undefined })
|
|
2693
|
+
|
|
2694
|
+
async function submitCreation(attempt) {
|
|
2695
|
+
if (data.creationRequestProtocol !== CREATION_REQUEST_PROTOCOL) {
|
|
2696
|
+
set({ creationWork: { status: 'error', title: attempt.body.title, code: 'UI_HOST_API_STALE' } })
|
|
2697
|
+
setIn('notices', 'projects', { kind: 'creation', ok: false, message: t('projects.hostNeedsRestart') })
|
|
2698
|
+
return
|
|
2699
|
+
}
|
|
2700
|
+
setIn('busy', 'create', true)
|
|
2701
|
+
set({ creationWork: { status: 'busy', title: attempt.body.title }, previewResult: null })
|
|
2702
|
+
setIn('notices', 'projects', null)
|
|
2703
|
+
setIn('notices', 'preview', null)
|
|
2704
|
+
const outcome = await postJson(fetchImpl, ROUTES.projects, { ...editorClone(attempt.body), creationKey: attempt.key })
|
|
2705
|
+
setIn('busy', 'create', false)
|
|
2706
|
+
if (outcome.ok) {
|
|
2707
|
+
const sameInputs = creationSignature(creationBody()) === attempt.signature
|
|
2708
|
+
set({ ...(sameInputs ? { forms: { ...data.forms, title: '', goal: '', recipe: null, recipeParameters: {} } } : {}),
|
|
2709
|
+
creationWork: null, projectId: outcome.payload.project.projectId,
|
|
2710
|
+
...(attempt.body.renderPreview ? { view: 'preview', compareMode: 'result' } : {}) })
|
|
2711
|
+
if (creationAttempt === attempt) creationAttempt = null
|
|
2712
|
+
setIn('notices', attempt.body.renderPreview ? 'preview' : 'projects', { kind: 'creation', projectId: outcome.payload.project.projectId, ok: true, message: outcome.payload.project.creationReplayed
|
|
2713
|
+
? t('projects.recovered', { id: outcome.payload.project.projectId })
|
|
2714
|
+
: t('projects.created', { id: outcome.payload.project.projectId }) })
|
|
2715
|
+
} else {
|
|
2716
|
+
set({ creationWork: { status: 'error', title: attempt.body.title, code: outcome.error.code } })
|
|
2717
|
+
setIn('notices', 'projects', { kind: 'creation', ok: false, message: creationErrorMessage(outcome.error.code),
|
|
2718
|
+
technicalDetails: `${outcome.error.code}: ${outcome.error.message}` })
|
|
2719
|
+
}
|
|
2720
|
+
// A failed response may follow a successful Host commit. Refreshing is
|
|
2721
|
+
// read-only; retries remain explicit and reuse the captured request key.
|
|
2722
|
+
reload()
|
|
2723
|
+
}
|
|
2724
|
+
|
|
2640
2725
|
/** Run one write, report it in this view's notice, then reload. */
|
|
2641
2726
|
const write = async (view, path, body, describe) => {
|
|
2642
2727
|
const outcome = await postJson(fetchImpl, path, body)
|
|
@@ -3282,24 +3367,12 @@ window.__ModuleLoader__.load({
|
|
|
3282
3367
|
createProject: async () => {
|
|
3283
3368
|
if (data.busy.create) return
|
|
3284
3369
|
if (!recipeSelectionCurrent(data) || !recipeValuesValid(data.forms.recipe, data.forms.recipeParameters)) return
|
|
3285
|
-
const
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
goal: data.forms.goal.length > 0 ? data.forms.goal : undefined,
|
|
3290
|
-
renderPreview: withRecipe,
|
|
3291
|
-
recipe: data.forms.recipe ? { id: data.forms.recipe.id, version: data.forms.recipe.version, digest: data.forms.recipe.digest, parameters: data.forms.recipeParameters } : undefined,
|
|
3292
|
-
})
|
|
3293
|
-
setIn('busy', 'create', false)
|
|
3294
|
-
if (outcome.ok) {
|
|
3295
|
-
set({ forms: { ...data.forms, title: '', goal: '', recipe: null, recipeParameters: {} }, projectId: outcome.payload.project.projectId, ...(withRecipe ? { view: 'preview', compareMode: 'result' } : {}) })
|
|
3296
|
-
setIn('notices', 'projects', { ok: true, message: t('projects.created', { id: outcome.payload.project.projectId }) })
|
|
3297
|
-
reload()
|
|
3298
|
-
} else {
|
|
3299
|
-
setIn('notices', 'projects', { ok: false, message: `${outcome.error.code}: ${outcome.error.message}` })
|
|
3300
|
-
if (outcome.error.code === 'RECIPE_CHANGED' || outcome.error.code === 'RECIPE_NOT_FOUND') reload()
|
|
3301
|
-
}
|
|
3370
|
+
const body = creationBody()
|
|
3371
|
+
const signature = creationSignature(body)
|
|
3372
|
+
if (!creationAttempt || creationAttempt.signature !== signature) creationAttempt = { body, signature, key: newCreationKey() }
|
|
3373
|
+
return submitCreation(creationAttempt)
|
|
3302
3374
|
},
|
|
3375
|
+
retryCreation: () => data.busy.create || !creationAttempt ? undefined : submitCreation(creationAttempt),
|
|
3303
3376
|
|
|
3304
3377
|
applyPatch: async () => {
|
|
3305
3378
|
const projectId = data.activeProjectId
|
|
@@ -3365,6 +3438,7 @@ window.__ModuleLoader__.load({
|
|
|
3365
3438
|
},
|
|
3366
3439
|
|
|
3367
3440
|
renderPreview: async () => {
|
|
3441
|
+
setIn('notices', 'preview', null)
|
|
3368
3442
|
set({ previewBusy: true, previewResult: null })
|
|
3369
3443
|
const outcome = await postJson(fetchImpl, projectRoute(target(), '/preview'), { revision: data.selected?.scene?.revision })
|
|
3370
3444
|
set({ previewBusy: false, ...(outcome.ok ? { compareMode: 'renders' } : {}) })
|
|
@@ -3489,10 +3563,12 @@ window.__ModuleLoader__.load({
|
|
|
3489
3563
|
Button({
|
|
3490
3564
|
tone: project.projectId === state.activeProjectId ? 'primary' : undefined,
|
|
3491
3565
|
action: `select-project:${project.projectId}`,
|
|
3566
|
+
disabled: project.creationPending,
|
|
3492
3567
|
onClick: () => actions.selectProject(project.projectId),
|
|
3493
3568
|
children: project.title || project.projectId,
|
|
3494
3569
|
}),
|
|
3495
3570
|
el('span', { className: 'db-muted db-mono' }, project.projectId),
|
|
3571
|
+
project.creationPending ? Badge({ tone: 'live', name: 'creation-pending', children: t('projects.incomplete') }) : null,
|
|
3496
3572
|
project.unfinishedJobs > 0 ? Badge({ tone: 'live', name: 'unfinished', children: t('projects.unfinishedJobs', { count: project.unfinishedJobs }) }) : null,
|
|
3497
3573
|
el('span', { className: 'db-muted' }, t('projects.revisionCount', { count: project.revisionCount })),
|
|
3498
3574
|
),
|
|
@@ -3526,12 +3602,24 @@ window.__ModuleLoader__.load({
|
|
|
3526
3602
|
Button({
|
|
3527
3603
|
tone: 'primary',
|
|
3528
3604
|
action: 'create-project',
|
|
3529
|
-
disabled: state.busy.create || state.forms.title.trim().length === 0 || !recipeSelectionCurrent(state) || !recipeValuesValid(state.forms.recipe, state.forms.recipeParameters),
|
|
3605
|
+
disabled: state.busy.create || state.creationRequestProtocol !== CREATION_REQUEST_PROTOCOL || state.forms.title.trim().length === 0 || !recipeSelectionCurrent(state) || !recipeValuesValid(state.forms.recipe, state.forms.recipeParameters),
|
|
3530
3606
|
onClick: actions.createProject,
|
|
3531
3607
|
children: state.busy.create ? (state.forms.recipe ? t('recipes.creatingPreview') : t('projects.creating')) : (state.forms.recipe ? t('recipes.createPreview') : t('projects.createButton')),
|
|
3532
3608
|
}),
|
|
3533
3609
|
),
|
|
3534
3610
|
state.notices.projects ? Notice(state.notices.projects, { marginTop: '8px' }) : null,
|
|
3611
|
+
state.status === 'ok' && state.creationRequestProtocol !== CREATION_REQUEST_PROTOCOL
|
|
3612
|
+
? el('p', { className: 'db-muted', 'data-creation-host-stale': true }, t('projects.hostNeedsRestart')) : null,
|
|
3613
|
+
state.creationWork?.status === 'error' ? el('div', { 'data-creation-recovery': true, style: { marginTop: '8px' } },
|
|
3614
|
+
el('p', { className: 'db-muted' }, t('projects.originalTitle', { title: state.creationWork.title })),
|
|
3615
|
+
el('div', { className: 'db-inline' },
|
|
3616
|
+
Button({ action: 'retry-creation', disabled: state.busy.create, onClick: actions.retryCreation, children: t('projects.retryCreation') }),
|
|
3617
|
+
Button({ action: 'refresh-creation-projects', onClick: actions.reload, children: t('projects.refreshList') }),
|
|
3618
|
+
['BLENDER_NOT_FOUND', 'RUNTIME_UNAVAILABLE'].includes(state.creationWork.code)
|
|
3619
|
+
? el('a', { href: 'https://github.com/pearjelly/deep-blend/blob/main/deepblend/docs/environment-check.md', target: '_blank', rel: 'noreferrer' }, t('projects.environmentGuide')) : null),
|
|
3620
|
+
state.notices.projects?.technicalDetails ? el('details', { 'data-creation-diagnostics': true },
|
|
3621
|
+
el('summary', null, t('projects.creationDetails')), el('pre', { className: 'db-pre' }, state.notices.projects.technicalDetails)) : null,
|
|
3622
|
+
) : null,
|
|
3535
3623
|
),
|
|
3536
3624
|
el('details', { className: 'db-storage' }, el('summary', null, t('projects.storage')),
|
|
3537
3625
|
el('span', { className: 'db-muted db-mono' }, `projectsRoot: ${state.projectsRoot || '—'}`)),
|
|
@@ -4546,6 +4634,8 @@ window.__ModuleLoader__.load({
|
|
|
4546
4634
|
children: state.previewBusy ? t('preview.rendering') : t('preview.render'),
|
|
4547
4635
|
}),
|
|
4548
4636
|
Notice(state.previewResult, { border: 0, padding: '0 6px', marginBottom: 0 }),
|
|
4637
|
+
state.notices.preview?.kind === 'creation' && state.notices.preview.projectId === state.activeProjectId
|
|
4638
|
+
? Notice(state.notices.preview, { border: 0, padding: '0 6px', marginBottom: 0 }) : null,
|
|
4549
4639
|
),
|
|
4550
4640
|
el('div', { className: 'db-tabs' },
|
|
4551
4641
|
el('span', { className: 'db-muted' }, t('preview.compare')),
|
package/lib/index.js
CHANGED
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
BlenderError,
|
|
33
33
|
BlenderErrorCode,
|
|
34
34
|
HOST_API_VERSION,
|
|
35
|
+
CREATION_REQUEST_VERSION,
|
|
35
36
|
UI_PANEL_ID,
|
|
36
37
|
UI_ROUTES,
|
|
37
38
|
UI_ROUTE_PREFIX,
|
|
@@ -419,6 +420,7 @@ export default class BlenderUiHost extends Service {
|
|
|
419
420
|
export function statusForError(error) {
|
|
420
421
|
if (error?.code === 'UI_HOST_API_STALE') return 503
|
|
421
422
|
const referenceStatuses = { ASSET_REQUEST_INVALID: 400, ASSET_CONTENT_MISMATCH: 415, ASSET_TOO_LARGE: 413,
|
|
423
|
+
CREATION_REQUEST_INVALID: 400, CREATION_REQUEST_CONFLICT: 409, PROJECT_ID_INVALID: 400,
|
|
422
424
|
ASSET_FORMAT_UNAVAILABLE: 415, SCENE_VALIDATION_FAILED: 422, SCENE_SPEC_INVALID: 422,
|
|
423
425
|
PATH_SEGMENT_INVALID: 400, ASSET_HASH_MISMATCH: 409, ASSET_SOURCE_NOT_FOUND: 404 }
|
|
424
426
|
if (referenceStatuses[error.code]) return referenceStatuses[error.code]
|
|
@@ -623,13 +625,15 @@ export function createHandlers(ctx) {
|
|
|
623
625
|
/** Everything the panel needs to render itself from scratch, in one request. */
|
|
624
626
|
state: async ({ query }) => {
|
|
625
627
|
const listed = await studio().listProjects()
|
|
626
|
-
const projectId = query.projectId ?? listed.projects
|
|
628
|
+
const projectId = query.projectId ?? listed.projects.find(project => !project.creationPending)?.projectId ?? null
|
|
629
|
+
const creationPending = listed.projects.some(project => project.projectId === projectId && project.creationPending)
|
|
627
630
|
return {
|
|
628
631
|
panelId: UI_PANEL_ID,
|
|
629
632
|
recipeCatalog: studio().listRecipes?.() ?? { recipes: [], errors: [] },
|
|
630
633
|
projects: listed.projects.map(record => buildProjectView(record)),
|
|
631
634
|
projectsRoot: listed.projectsRoot,
|
|
632
|
-
|
|
635
|
+
creationRequestProtocol: studio().creationRequestProtocol ?? null,
|
|
636
|
+
selected: projectId === null || creationPending
|
|
633
637
|
? null
|
|
634
638
|
: await buildProjectState(studio(), projectId, query.revision ?? undefined, approvalThreshold()),
|
|
635
639
|
}
|
|
@@ -644,12 +648,16 @@ export function createHandlers(ctx) {
|
|
|
644
648
|
},
|
|
645
649
|
|
|
646
650
|
'projects.create': async ({ body }) => {
|
|
651
|
+
if (body.creationKey !== undefined && studio().creationRequestProtocol !== CREATION_REQUEST_VERSION) {
|
|
652
|
+
throw new BlenderError('UI_HOST_API_STALE', 'The running Host does not support creation recovery. Restart the profile with the updated DeepBlend packages before retrying.')
|
|
653
|
+
}
|
|
647
654
|
const created = await studio().createProject({
|
|
648
655
|
title: body.title,
|
|
649
656
|
goal: body.goal,
|
|
650
657
|
sceneSpec: body.sceneSpec,
|
|
651
658
|
recipe: body.recipe,
|
|
652
659
|
projectId: body.projectId,
|
|
660
|
+
creationKey: body.creationKey,
|
|
653
661
|
saveCheckpoint: true,
|
|
654
662
|
renderPreview: body.renderPreview === true,
|
|
655
663
|
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepblend/dsh-blender-ui",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "DeepBlend workbench UI: Host half (typed HTTP API) and Web Client half (sidebar, panels, tool cards).",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
}
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@deepblend/dsh-blender-contracts": "0.3.
|
|
22
|
+
"@deepblend/dsh-blender-contracts": "0.3.5"
|
|
23
23
|
},
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"repository": {
|