@deepblend/dsh-blender-ui 0.3.7 → 0.3.8
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 +68 -14
- package/package.json +2 -2
package/lib/client.js
CHANGED
|
@@ -603,7 +603,9 @@ window.__ModuleLoader__.load({
|
|
|
603
603
|
'visual.noSecondOpinion': '没有第二意见。',
|
|
604
604
|
'visual.none': '这个 revision 还没有视觉评审。可以用 blender_visual_review 跑一次。',
|
|
605
605
|
'revisions.current': '当前',
|
|
606
|
-
'revisions.compare': '
|
|
606
|
+
'revisions.compare': '与当前版对比',
|
|
607
|
+
'revisions.compareCurrent': '与另一版对比',
|
|
608
|
+
'revisions.compareHint': '选择已保存版本与当前版对比;选择当前版会对比相邻的另一版。需要至少两版,查看不会改动作品。',
|
|
607
609
|
'revisions.restore': '恢复',
|
|
608
610
|
'host.reading': '读取 Host 状态…',
|
|
609
611
|
'diagnostics.exportHint': '导出一份可以附在问题里的诊断信息(版本、配置、项目与任务的摘要)',
|
|
@@ -1168,7 +1170,9 @@ window.__ModuleLoader__.load({
|
|
|
1168
1170
|
'visual.noSecondOpinion': 'No second opinion.',
|
|
1169
1171
|
'visual.none': 'This revision has no visual review yet. Run one with blender_visual_review.',
|
|
1170
1172
|
'revisions.current': 'current',
|
|
1171
|
-
'revisions.compare': '
|
|
1173
|
+
'revisions.compare': 'Compare with current',
|
|
1174
|
+
'revisions.compareCurrent': 'Compare with another version',
|
|
1175
|
+
'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
1176
|
'revisions.restore': 'restore',
|
|
1173
1177
|
'host.reading': 'Reading Host state…',
|
|
1174
1178
|
'diagnostics.exportHint': 'Export diagnostics you can attach to a bug report (versions, config, a summary of projects and jobs)',
|
|
@@ -2657,6 +2661,22 @@ window.__ModuleLoader__.load({
|
|
|
2657
2661
|
try { const value = storage?.getItem(APPEARANCE_KEY); return APPEARANCES.includes(value) ? value : 'system' } catch { return 'system' }
|
|
2658
2662
|
}
|
|
2659
2663
|
|
|
2664
|
+
function previewRevisionPair(state) {
|
|
2665
|
+
const revisions = state.previews?.revisions || []
|
|
2666
|
+
const pick = wanted => revisions.some(entry => entry.revision === wanted) ? wanted : revisions.at(-1)?.revision || null
|
|
2667
|
+
return { left: pick(state.compareLeft || state.currentRevision), right: pick(state.compareRight || state.currentRevision) }
|
|
2668
|
+
}
|
|
2669
|
+
function revisionComparisonTarget(state, revision) {
|
|
2670
|
+
if (!state.activeProjectId) return null
|
|
2671
|
+
const saved = new Set((state.selected?.revisions || []).map(entry => entry.revision))
|
|
2672
|
+
const available = [...new Set((state.previews?.revisions || []).map(entry => entry.revision).filter(id => saved.has(id)))]
|
|
2673
|
+
.sort((a, b) => Number(a.slice(1)) - Number(b.slice(1)))
|
|
2674
|
+
if (!available.includes(revision) || !available.includes(state.currentRevision)) return null
|
|
2675
|
+
const index = available.indexOf(revision)
|
|
2676
|
+
const left = revision === state.currentRevision ? available[index - 1] || available[index + 1] : revision
|
|
2677
|
+
return left ? { left, right: state.currentRevision } : null
|
|
2678
|
+
}
|
|
2679
|
+
|
|
2660
2680
|
function createWorkbenchStore(options) {
|
|
2661
2681
|
const settings = options || {}
|
|
2662
2682
|
const fetchImpl = settings.fetch ?? ((...args) => fetch(...args))
|
|
@@ -2674,6 +2694,7 @@ window.__ModuleLoader__.load({
|
|
|
2674
2694
|
let tick = 0
|
|
2675
2695
|
let live = false
|
|
2676
2696
|
let loadSequence = 0
|
|
2697
|
+
let diffSequence = 0
|
|
2677
2698
|
let creationAttempt = null
|
|
2678
2699
|
const draftStorage = creationDraftStorage(settings)
|
|
2679
2700
|
let creationDraftId = newCreationKey(), creationDraftRoot = null, persistedCreationSignature = null
|
|
@@ -2732,7 +2753,14 @@ window.__ModuleLoader__.load({
|
|
|
2732
2753
|
for (const listener of [...listeners]) listener(snapshot)
|
|
2733
2754
|
}
|
|
2734
2755
|
/** Change the snapshot and tell everyone. @param {object} patch */
|
|
2735
|
-
const set = (patch) => {
|
|
2756
|
+
const set = (patch) => {
|
|
2757
|
+
if (['projectsRoot', 'activeProjectId', 'currentRevision', 'view', 'compareMode', 'compareLeft', 'compareRight']
|
|
2758
|
+
.some(key => Object.hasOwn(patch, key) && patch[key] !== data[key])) {
|
|
2759
|
+
diffSequence += 1
|
|
2760
|
+
patch = { ...patch, diff: null, diffError: null }
|
|
2761
|
+
}
|
|
2762
|
+
data = { ...data, ...patch }; notify()
|
|
2763
|
+
}
|
|
2736
2764
|
/** Change one nested table. @param {'forms'|'busy'|'notices'} table @param {string} key @param {any} value */
|
|
2737
2765
|
const setIn = (table, key, value) => { data = { ...data, [table]: { ...data[table], [key]: value } }; notify() }
|
|
2738
2766
|
|
|
@@ -3264,8 +3292,15 @@ window.__ModuleLoader__.load({
|
|
|
3264
3292
|
pickCompare: (side, revision) => set(side === 'left'
|
|
3265
3293
|
? { compareLeft: revision, diff: null, diffError: null }
|
|
3266
3294
|
: { compareRight: revision, diff: null, diffError: null }),
|
|
3267
|
-
/**
|
|
3268
|
-
compareFrom: (revision) =>
|
|
3295
|
+
/** View a saved version against the current one, without changing the project. */
|
|
3296
|
+
compareFrom: (revision) => {
|
|
3297
|
+
const pair = revisionComparisonTarget(data, revision)
|
|
3298
|
+
if (pair) {
|
|
3299
|
+
diffSequence += 1
|
|
3300
|
+
set({ compareLeft: pair.left, compareRight: pair.right, compareMode: 'revisions', view: 'preview',
|
|
3301
|
+
diff: null, diffError: null, editorComparison: null })
|
|
3302
|
+
}
|
|
3303
|
+
},
|
|
3269
3304
|
|
|
3270
3305
|
updateBrief: (field, value, referenceId) => {
|
|
3271
3306
|
const projectId = data.activeProjectId, current = data.briefDrafts[projectId]
|
|
@@ -3565,14 +3600,32 @@ window.__ModuleLoader__.load({
|
|
|
3565
3600
|
},
|
|
3566
3601
|
|
|
3567
3602
|
diff: async (from, to) => {
|
|
3568
|
-
|
|
3603
|
+
const projectId = data.activeProjectId, pair = previewRevisionPair(data)
|
|
3604
|
+
if (!running || !projectId || data.view !== 'preview' || data.compareMode !== 'revisions' || pair.left !== from || pair.right !== to) return
|
|
3605
|
+
const sequence = ++diffSequence
|
|
3606
|
+
const stamp = JSON.stringify([data.projectsRoot, projectId, data.currentRevision, from, to])
|
|
3607
|
+
const current = () => {
|
|
3608
|
+
const selected = previewRevisionPair(data)
|
|
3609
|
+
return running && sequence === diffSequence && data.view === 'preview' && data.compareMode === 'revisions'
|
|
3610
|
+
&& selected.left === from && selected.right === to
|
|
3611
|
+
&& JSON.stringify([data.projectsRoot, data.activeProjectId, data.currentRevision, from, to]) === stamp
|
|
3612
|
+
}
|
|
3613
|
+
set({ diff: null, diffError: null })
|
|
3569
3614
|
try {
|
|
3570
|
-
const response = await fetchImpl(`${projectRoute(
|
|
3615
|
+
const response = await fetchImpl(`${projectRoute(projectId, '/diff')}?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}`, { headers: { accept: 'application/json' } })
|
|
3571
3616
|
const parsed = await response.json()
|
|
3572
|
-
if (
|
|
3573
|
-
|
|
3617
|
+
if (!current()) return
|
|
3618
|
+
if (response.ok && parsed?.ok) {
|
|
3619
|
+
if (parsed.projectId !== undefined && parsed.projectId !== projectId
|
|
3620
|
+
|| parsed.diff?.projectId !== undefined && parsed.diff.projectId !== projectId
|
|
3621
|
+
|| parsed.diff?.fromRevision !== from || parsed.diff?.toRevision !== to
|
|
3622
|
+
|| parsed.fromRevision !== undefined && parsed.fromRevision !== from
|
|
3623
|
+
|| parsed.toRevision !== undefined && parsed.toRevision !== to) {
|
|
3624
|
+
set({ diff: null, diffError: { code: 'UI_DIFF_MISMATCH', message: t('preview.diffFailed') } })
|
|
3625
|
+
} else set({ diff: parsed.diff })
|
|
3626
|
+
} else set({ diff: null, diffError: parsed?.error || { code: `HTTP_${response.status}`, message: t('preview.diffFailed') } })
|
|
3574
3627
|
} catch (error) {
|
|
3575
|
-
set({ diff: null, diffError: { code: 'UI_FETCH_FAILED', message: String((error && error.message) || error) } })
|
|
3628
|
+
if (current()) set({ diff: null, diffError: { code: 'UI_FETCH_FAILED', message: String((error && error.message) || error) } })
|
|
3576
3629
|
}
|
|
3577
3630
|
},
|
|
3578
3631
|
|
|
@@ -3799,6 +3852,7 @@ window.__ModuleLoader__.load({
|
|
|
3799
3852
|
void load()
|
|
3800
3853
|
},
|
|
3801
3854
|
stop() {
|
|
3855
|
+
diffSequence += 1
|
|
3802
3856
|
closeCreationDraft()
|
|
3803
3857
|
draftLifecycle?.removeEventListener?.('pagehide', closeCreationDraft)
|
|
3804
3858
|
draftLifecycle?.removeEventListener?.('pageshow', reopenCreationDraft)
|
|
@@ -4825,9 +4879,7 @@ window.__ModuleLoader__.load({
|
|
|
4825
4879
|
const previews = state.previews
|
|
4826
4880
|
if (previews === null) return el('div', { 'data-view': 'preview', className: 'db-muted' }, t('common.noProject'))
|
|
4827
4881
|
const revisions = previews.revisions
|
|
4828
|
-
const
|
|
4829
|
-
const left = pick(state.compareLeft || state.currentRevision)
|
|
4830
|
-
const right = pick(state.compareRight || state.currentRevision)
|
|
4882
|
+
const { left, right } = previewRevisionPair(state)
|
|
4831
4883
|
const entryOf = id => revisions.find(entry => entry.revision === id) || null
|
|
4832
4884
|
|
|
4833
4885
|
/** One image (or the reason there is none), with its digest and its time. */
|
|
@@ -5174,6 +5226,7 @@ window.__ModuleLoader__.load({
|
|
|
5174
5226
|
ErrorBox({ error: state.error }),
|
|
5175
5227
|
el('div', { className: 'db-card' },
|
|
5176
5228
|
el('h4', null, t('revisions.title', { count: revisions.length })),
|
|
5229
|
+
el('p', { className: 'db-muted' }, t('revisions.compareHint')),
|
|
5177
5230
|
el('ul', { className: 'db-list' }, revisions.map(entry => el('li', { key: entry.revision, 'data-revision': entry.revision },
|
|
5178
5231
|
el('div', { className: 'db-inline' },
|
|
5179
5232
|
el('span', { className: 'db-mono' }, entry.revision),
|
|
@@ -5181,7 +5234,8 @@ window.__ModuleLoader__.load({
|
|
|
5181
5234
|
el('span', { className: 'db-muted' }, entry.kind || '—'),
|
|
5182
5235
|
el('span', { className: 'db-muted' }, formatTime(entry.createdAt)),
|
|
5183
5236
|
el('span', { className: 'db-muted db-mono' }, `digest ${shortDigest(entry.digest)}`),
|
|
5184
|
-
Button({ action: `compare-from:${entry.revision}`,
|
|
5237
|
+
Button({ action: `compare-from:${entry.revision}`, disabled: revisionComparisonTarget(state, entry.revision) === null,
|
|
5238
|
+
onClick: () => actions.compareFrom(entry.revision), children: entry.isCurrent ? t('revisions.compareCurrent') : t('revisions.compare') }),
|
|
5185
5239
|
entry.isCurrent ? null : Button({ action: `restore:${entry.revision}`, disabled: state.busy.restore, onClick: () => actions.restoreRevision(entry.revision), children: t('revisions.restore') }),
|
|
5186
5240
|
),
|
|
5187
5241
|
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.8",
|
|
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.8"
|
|
23
23
|
},
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"repository": {
|