@deepblend/dsh-blender-ui 0.3.7 → 0.3.9
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 +114 -20
- package/package.json +2 -2
package/lib/client.js
CHANGED
|
@@ -542,6 +542,9 @@ window.__ModuleLoader__.load({
|
|
|
542
542
|
'revisions.currentSuffix': ' (当前)',
|
|
543
543
|
'revisions.missing': '没有这个 revision',
|
|
544
544
|
'preview.noneForRevision': '这个 revision 还没有预览图(渲染一次预览即可)。',
|
|
545
|
+
'preview.reading': '正在读取所选项目的预览…',
|
|
546
|
+
'preview.readFailed': '未能读取这个项目的预览。刷新后重试;保存版本和未提交修改仍保留。',
|
|
547
|
+
'preview.sourceMismatch': '预览回执与所选项目不一致。刷新后重试。',
|
|
545
548
|
'preview.noPrevious': '还没有上一次渲染:再点一次「渲染预览」,或者先在某个更早的 revision 上渲一次。',
|
|
546
549
|
'preview.notRenderedHere': '这个 revision 还没有由面板渲过预览(上方的「渲染预览」会生成第一张)。',
|
|
547
550
|
'preview.rendering': '渲染中…',
|
|
@@ -603,7 +606,9 @@ window.__ModuleLoader__.load({
|
|
|
603
606
|
'visual.noSecondOpinion': '没有第二意见。',
|
|
604
607
|
'visual.none': '这个 revision 还没有视觉评审。可以用 blender_visual_review 跑一次。',
|
|
605
608
|
'revisions.current': '当前',
|
|
606
|
-
'revisions.compare': '
|
|
609
|
+
'revisions.compare': '与当前版对比',
|
|
610
|
+
'revisions.compareCurrent': '与另一版对比',
|
|
611
|
+
'revisions.compareHint': '选择已保存版本与当前版对比;选择当前版会对比相邻的另一版。需要至少两版,查看不会改动作品。',
|
|
607
612
|
'revisions.restore': '恢复',
|
|
608
613
|
'host.reading': '读取 Host 状态…',
|
|
609
614
|
'diagnostics.exportHint': '导出一份可以附在问题里的诊断信息(版本、配置、项目与任务的摘要)',
|
|
@@ -1107,6 +1112,9 @@ window.__ModuleLoader__.load({
|
|
|
1107
1112
|
'revisions.currentSuffix': ' (current)',
|
|
1108
1113
|
'revisions.missing': 'no such revision',
|
|
1109
1114
|
'preview.noneForRevision': 'This revision has no preview image yet (render a preview and it will).',
|
|
1115
|
+
'preview.reading': 'Reading previews for the selected project…',
|
|
1116
|
+
'preview.readFailed': 'Could not read this project’s previews. Refresh to retry; saved versions and unsaved edits are kept.',
|
|
1117
|
+
'preview.sourceMismatch': 'The preview response does not match the selected project. Refresh to retry.',
|
|
1110
1118
|
'preview.noPrevious': 'No previous render yet: press “Render preview” again, or render once on an earlier revision.',
|
|
1111
1119
|
'preview.notRenderedHere': 'This revision has no preview rendered from this panel yet (the “Render preview” button above makes the first one).',
|
|
1112
1120
|
'preview.rendering': 'Rendering…',
|
|
@@ -1168,7 +1176,9 @@ window.__ModuleLoader__.load({
|
|
|
1168
1176
|
'visual.noSecondOpinion': 'No second opinion.',
|
|
1169
1177
|
'visual.none': 'This revision has no visual review yet. Run one with blender_visual_review.',
|
|
1170
1178
|
'revisions.current': 'current',
|
|
1171
|
-
'revisions.compare': '
|
|
1179
|
+
'revisions.compare': 'Compare with current',
|
|
1180
|
+
'revisions.compareCurrent': 'Compare with another version',
|
|
1181
|
+
'revisions.compareHint': 'Compare a saved version with the current one. The current version uses its nearest other version. At least two versions are needed; viewing does not change the project.',
|
|
1172
1182
|
'revisions.restore': 'restore',
|
|
1173
1183
|
'host.reading': 'Reading Host state…',
|
|
1174
1184
|
'diagnostics.exportHint': 'Export diagnostics you can attach to a bug report (versions, config, a summary of projects and jobs)',
|
|
@@ -2518,7 +2528,7 @@ window.__ModuleLoader__.load({
|
|
|
2518
2528
|
if (typeof payload?.hostApiVersion === 'number' && payload.hostApiVersion < EXPECTED_HOST_API) {
|
|
2519
2529
|
return { status: 'stale', payload: null, error: staleHostError(expectedRoute, { payload, status: response.status }) }
|
|
2520
2530
|
}
|
|
2521
|
-
if (payload && payload.ok) return { status: 'ok', payload, error: null }
|
|
2531
|
+
if (payload && payload.ok && response.ok !== false) return { status: 'ok', payload, error: null }
|
|
2522
2532
|
return {
|
|
2523
2533
|
status: 'error',
|
|
2524
2534
|
payload: null,
|
|
@@ -2598,6 +2608,8 @@ window.__ModuleLoader__.load({
|
|
|
2598
2608
|
unfinishedJobs: [],
|
|
2599
2609
|
previews: null,
|
|
2600
2610
|
artifactBase: '',
|
|
2611
|
+
previewContext: null,
|
|
2612
|
+
previewReadStatus: 'idle',
|
|
2601
2613
|
view: 'projects',
|
|
2602
2614
|
projectId: null,
|
|
2603
2615
|
activeProjectId: null,
|
|
@@ -2657,6 +2669,22 @@ window.__ModuleLoader__.load({
|
|
|
2657
2669
|
try { const value = storage?.getItem(APPEARANCE_KEY); return APPEARANCES.includes(value) ? value : 'system' } catch { return 'system' }
|
|
2658
2670
|
}
|
|
2659
2671
|
|
|
2672
|
+
function previewRevisionPair(state) {
|
|
2673
|
+
const revisions = state.previews?.revisions || []
|
|
2674
|
+
const pick = wanted => revisions.some(entry => entry.revision === wanted) ? wanted : revisions.at(-1)?.revision || null
|
|
2675
|
+
return { left: pick(state.compareLeft || state.currentRevision), right: pick(state.compareRight || state.currentRevision) }
|
|
2676
|
+
}
|
|
2677
|
+
function revisionComparisonTarget(state, revision) {
|
|
2678
|
+
if (!state.activeProjectId) return null
|
|
2679
|
+
const saved = new Set((state.selected?.revisions || []).map(entry => entry.revision))
|
|
2680
|
+
const available = [...new Set((state.previews?.revisions || []).map(entry => entry.revision).filter(id => saved.has(id)))]
|
|
2681
|
+
.sort((a, b) => Number(a.slice(1)) - Number(b.slice(1)))
|
|
2682
|
+
if (!available.includes(revision) || !available.includes(state.currentRevision)) return null
|
|
2683
|
+
const index = available.indexOf(revision)
|
|
2684
|
+
const left = revision === state.currentRevision ? available[index - 1] || available[index + 1] : revision
|
|
2685
|
+
return left ? { left, right: state.currentRevision } : null
|
|
2686
|
+
}
|
|
2687
|
+
|
|
2660
2688
|
function createWorkbenchStore(options) {
|
|
2661
2689
|
const settings = options || {}
|
|
2662
2690
|
const fetchImpl = settings.fetch ?? ((...args) => fetch(...args))
|
|
@@ -2674,6 +2702,7 @@ window.__ModuleLoader__.load({
|
|
|
2674
2702
|
let tick = 0
|
|
2675
2703
|
let live = false
|
|
2676
2704
|
let loadSequence = 0
|
|
2705
|
+
let diffSequence = 0
|
|
2677
2706
|
let creationAttempt = null
|
|
2678
2707
|
const draftStorage = creationDraftStorage(settings)
|
|
2679
2708
|
let creationDraftId = newCreationKey(), creationDraftRoot = null, persistedCreationSignature = null
|
|
@@ -2732,7 +2761,27 @@ window.__ModuleLoader__.load({
|
|
|
2732
2761
|
for (const listener of [...listeners]) listener(snapshot)
|
|
2733
2762
|
}
|
|
2734
2763
|
/** Change the snapshot and tell everyone. @param {object} patch */
|
|
2735
|
-
const set = (patch) => {
|
|
2764
|
+
const set = (patch) => {
|
|
2765
|
+
// Automatic edit images are source snapshots too. They cannot override
|
|
2766
|
+
// fresh rows from another store, even when its project/revision IDs match.
|
|
2767
|
+
if (Object.hasOwn(patch, 'projectsRoot') && patch.projectsRoot !== data.projectsRoot) {
|
|
2768
|
+
patch = { ...patch, editorComparison: null }
|
|
2769
|
+
}
|
|
2770
|
+
// Retire the old source before selection/creation navigation can expose
|
|
2771
|
+
// it with a new project. A load supplies the complete new source atomically.
|
|
2772
|
+
if (!Object.hasOwn(patch, 'previews') && ['projectId', 'projectsRoot', 'activeProjectId']
|
|
2773
|
+
.some(key => Object.hasOwn(patch, key) && patch[key] !== data[key])) {
|
|
2774
|
+
const requested = Object.hasOwn(patch, 'projectId') ? patch.projectId : data.projectId
|
|
2775
|
+
const active = Object.hasOwn(patch, 'activeProjectId') ? patch.activeProjectId : data.activeProjectId
|
|
2776
|
+
patch = { ...patch, previews: null, artifactBase: '', previewContext: null, previewReadStatus: requested || active ? 'loading' : 'idle' }
|
|
2777
|
+
}
|
|
2778
|
+
if (['projectsRoot', 'activeProjectId', 'currentRevision', 'view', 'compareMode', 'compareLeft', 'compareRight']
|
|
2779
|
+
.some(key => Object.hasOwn(patch, key) && patch[key] !== data[key])) {
|
|
2780
|
+
diffSequence += 1
|
|
2781
|
+
patch = { ...patch, diff: null, diffError: null }
|
|
2782
|
+
}
|
|
2783
|
+
data = { ...data, ...patch }; notify()
|
|
2784
|
+
}
|
|
2736
2785
|
/** Change one nested table. @param {'forms'|'busy'|'notices'} table @param {string} key @param {any} value */
|
|
2737
2786
|
const setIn = (table, key, value) => { data = { ...data, [table]: { ...data[table], [key]: value } }; notify() }
|
|
2738
2787
|
|
|
@@ -2818,6 +2867,7 @@ window.__ModuleLoader__.load({
|
|
|
2818
2867
|
currentRevision: payload.selected ? payload.selected.currentRevision : null,
|
|
2819
2868
|
jobs: payload.selected ? payload.selected.jobs || [] : [],
|
|
2820
2869
|
unfinishedJobs: payload.selected ? payload.selected.unfinishedJobs || [] : [],
|
|
2870
|
+
previews: null, artifactBase: '', previewContext: null, previewReadStatus: active === null ? 'idle' : 'loading',
|
|
2821
2871
|
}
|
|
2822
2872
|
live = patch.unfinishedJobs.length > 0
|
|
2823
2873
|
|
|
@@ -2833,11 +2883,22 @@ window.__ModuleLoader__.load({
|
|
|
2833
2883
|
}
|
|
2834
2884
|
const previewResult = await readRoute(fetchImpl, `${projectRoute(active, '/previews')}?t=${tick}`, 'project.previews')
|
|
2835
2885
|
if (previewResult.status === 'ok') {
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2886
|
+
const result = previewResult.payload
|
|
2887
|
+
if (result.previews?.projectId === active && Array.isArray(result.previews.revisions)
|
|
2888
|
+
&& (result.projectId === undefined || result.projectId === active)
|
|
2889
|
+
&& result.artifactBase === `/deepblend/artifacts/${encodeURIComponent(active)}/`) {
|
|
2890
|
+
patch.previews = result.previews
|
|
2891
|
+
patch.artifactBase = result.artifactBase
|
|
2892
|
+
patch.previewContext = { projectId: active, projectsRoot: patch.projectsRoot }
|
|
2893
|
+
patch.previewReadStatus = 'ok'
|
|
2894
|
+
} else {
|
|
2895
|
+
patch.previewReadStatus = 'error'
|
|
2896
|
+
patch.error = { code: 'UI_PREVIEW_MISMATCH', message: t('preview.sourceMismatch') }
|
|
2897
|
+
}
|
|
2898
|
+
} else {
|
|
2899
|
+
patch.previewReadStatus = 'error'
|
|
2840
2900
|
patch.error = previewResult.error
|
|
2901
|
+
if (previewResult.status === 'stale') patch.status = 'stale'
|
|
2841
2902
|
}
|
|
2842
2903
|
} else {
|
|
2843
2904
|
patch.previews = null
|
|
@@ -3264,8 +3325,15 @@ window.__ModuleLoader__.load({
|
|
|
3264
3325
|
pickCompare: (side, revision) => set(side === 'left'
|
|
3265
3326
|
? { compareLeft: revision, diff: null, diffError: null }
|
|
3266
3327
|
: { compareRight: revision, diff: null, diffError: null }),
|
|
3267
|
-
/**
|
|
3268
|
-
compareFrom: (revision) =>
|
|
3328
|
+
/** View a saved version against the current one, without changing the project. */
|
|
3329
|
+
compareFrom: (revision) => {
|
|
3330
|
+
const pair = revisionComparisonTarget(data, revision)
|
|
3331
|
+
if (pair) {
|
|
3332
|
+
diffSequence += 1
|
|
3333
|
+
set({ compareLeft: pair.left, compareRight: pair.right, compareMode: 'revisions', view: 'preview',
|
|
3334
|
+
diff: null, diffError: null, editorComparison: null })
|
|
3335
|
+
}
|
|
3336
|
+
},
|
|
3269
3337
|
|
|
3270
3338
|
updateBrief: (field, value, referenceId) => {
|
|
3271
3339
|
const projectId = data.activeProjectId, current = data.briefDrafts[projectId]
|
|
@@ -3565,14 +3633,32 @@ window.__ModuleLoader__.load({
|
|
|
3565
3633
|
},
|
|
3566
3634
|
|
|
3567
3635
|
diff: async (from, to) => {
|
|
3568
|
-
|
|
3636
|
+
const projectId = data.activeProjectId, pair = previewRevisionPair(data)
|
|
3637
|
+
if (!running || !projectId || data.view !== 'preview' || data.compareMode !== 'revisions' || pair.left !== from || pair.right !== to) return
|
|
3638
|
+
const sequence = ++diffSequence
|
|
3639
|
+
const stamp = JSON.stringify([data.projectsRoot, projectId, data.currentRevision, from, to])
|
|
3640
|
+
const current = () => {
|
|
3641
|
+
const selected = previewRevisionPair(data)
|
|
3642
|
+
return running && sequence === diffSequence && data.view === 'preview' && data.compareMode === 'revisions'
|
|
3643
|
+
&& selected.left === from && selected.right === to
|
|
3644
|
+
&& JSON.stringify([data.projectsRoot, data.activeProjectId, data.currentRevision, from, to]) === stamp
|
|
3645
|
+
}
|
|
3646
|
+
set({ diff: null, diffError: null })
|
|
3569
3647
|
try {
|
|
3570
|
-
const response = await fetchImpl(`${projectRoute(
|
|
3648
|
+
const response = await fetchImpl(`${projectRoute(projectId, '/diff')}?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}`, { headers: { accept: 'application/json' } })
|
|
3571
3649
|
const parsed = await response.json()
|
|
3572
|
-
if (
|
|
3573
|
-
|
|
3650
|
+
if (!current()) return
|
|
3651
|
+
if (response.ok && parsed?.ok) {
|
|
3652
|
+
if (parsed.projectId !== undefined && parsed.projectId !== projectId
|
|
3653
|
+
|| parsed.diff?.projectId !== undefined && parsed.diff.projectId !== projectId
|
|
3654
|
+
|| parsed.diff?.fromRevision !== from || parsed.diff?.toRevision !== to
|
|
3655
|
+
|| parsed.fromRevision !== undefined && parsed.fromRevision !== from
|
|
3656
|
+
|| parsed.toRevision !== undefined && parsed.toRevision !== to) {
|
|
3657
|
+
set({ diff: null, diffError: { code: 'UI_DIFF_MISMATCH', message: t('preview.diffFailed') } })
|
|
3658
|
+
} else set({ diff: parsed.diff })
|
|
3659
|
+
} else set({ diff: null, diffError: parsed?.error || { code: `HTTP_${response.status}`, message: t('preview.diffFailed') } })
|
|
3574
3660
|
} catch (error) {
|
|
3575
|
-
set({ diff: null, diffError: { code: 'UI_FETCH_FAILED', message: String((error && error.message) || error) } })
|
|
3661
|
+
if (current()) set({ diff: null, diffError: { code: 'UI_FETCH_FAILED', message: String((error && error.message) || error) } })
|
|
3576
3662
|
}
|
|
3577
3663
|
},
|
|
3578
3664
|
|
|
@@ -3799,6 +3885,8 @@ window.__ModuleLoader__.load({
|
|
|
3799
3885
|
void load()
|
|
3800
3886
|
},
|
|
3801
3887
|
stop() {
|
|
3888
|
+
loadSequence += 1
|
|
3889
|
+
diffSequence += 1
|
|
3802
3890
|
closeCreationDraft()
|
|
3803
3891
|
draftLifecycle?.removeEventListener?.('pagehide', closeCreationDraft)
|
|
3804
3892
|
draftLifecycle?.removeEventListener?.('pageshow', reopenCreationDraft)
|
|
@@ -4823,11 +4911,15 @@ window.__ModuleLoader__.load({
|
|
|
4823
4911
|
const state = ctx.state
|
|
4824
4912
|
const actions = ctx.actions
|
|
4825
4913
|
const previews = state.previews
|
|
4826
|
-
if (previews === null)
|
|
4914
|
+
if (previews === null) {
|
|
4915
|
+
if (state.previewReadStatus === 'error') return el('div', { 'data-view': 'preview', 'data-preview-state': 'error', className: 'db-card' },
|
|
4916
|
+
el('div', { className: 'db-muted' }, t('preview.readFailed')),
|
|
4917
|
+
Button({ action: 'preview-reload', onClick: actions.reload, children: t('common.refresh') }))
|
|
4918
|
+
if (state.previewReadStatus === 'loading') return el('div', { 'data-view': 'preview', 'data-preview-state': 'loading', className: 'db-muted' }, t('preview.reading'))
|
|
4919
|
+
return el('div', { 'data-view': 'preview', className: 'db-muted' }, t('common.noProject'))
|
|
4920
|
+
}
|
|
4827
4921
|
const revisions = previews.revisions
|
|
4828
|
-
const
|
|
4829
|
-
const left = pick(state.compareLeft || state.currentRevision)
|
|
4830
|
-
const right = pick(state.compareRight || state.currentRevision)
|
|
4922
|
+
const { left, right } = previewRevisionPair(state)
|
|
4831
4923
|
const entryOf = id => revisions.find(entry => entry.revision === id) || null
|
|
4832
4924
|
|
|
4833
4925
|
/** One image (or the reason there is none), with its digest and its time. */
|
|
@@ -5174,6 +5266,7 @@ window.__ModuleLoader__.load({
|
|
|
5174
5266
|
ErrorBox({ error: state.error }),
|
|
5175
5267
|
el('div', { className: 'db-card' },
|
|
5176
5268
|
el('h4', null, t('revisions.title', { count: revisions.length })),
|
|
5269
|
+
el('p', { className: 'db-muted' }, t('revisions.compareHint')),
|
|
5177
5270
|
el('ul', { className: 'db-list' }, revisions.map(entry => el('li', { key: entry.revision, 'data-revision': entry.revision },
|
|
5178
5271
|
el('div', { className: 'db-inline' },
|
|
5179
5272
|
el('span', { className: 'db-mono' }, entry.revision),
|
|
@@ -5181,7 +5274,8 @@ window.__ModuleLoader__.load({
|
|
|
5181
5274
|
el('span', { className: 'db-muted' }, entry.kind || '—'),
|
|
5182
5275
|
el('span', { className: 'db-muted' }, formatTime(entry.createdAt)),
|
|
5183
5276
|
el('span', { className: 'db-muted db-mono' }, `digest ${shortDigest(entry.digest)}`),
|
|
5184
|
-
Button({ action: `compare-from:${entry.revision}`,
|
|
5277
|
+
Button({ action: `compare-from:${entry.revision}`, disabled: revisionComparisonTarget(state, entry.revision) === null,
|
|
5278
|
+
onClick: () => actions.compareFrom(entry.revision), children: entry.isCurrent ? t('revisions.compareCurrent') : t('revisions.compare') }),
|
|
5185
5279
|
entry.isCurrent ? null : Button({ action: `restore:${entry.revision}`, disabled: state.busy.restore, onClick: () => actions.restoreRevision(entry.revision), children: t('revisions.restore') }),
|
|
5186
5280
|
),
|
|
5187
5281
|
el('div', { className: 'db-muted' }, entry.summary || t('common.noNote')),
|
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.9",
|
|
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.9"
|
|
23
23
|
},
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"repository": {
|