fix: preserve analysis bands across input workflows
This commit is contained in:
@@ -34,24 +34,24 @@
|
||||
<section class="workflow-card input-card" id="input-step" data-workflow-section="1">
|
||||
<div class="step-heading">
|
||||
<span class="step-number">1</span>
|
||||
<div><h2>입력 준비</h2><p>기본 예제를 쓰거나 파일을 순서대로 교체하세요.</p></div>
|
||||
<div><h2>입력 준비</h2><p>기본 예제를 쓰거나 파일을 교체하세요. Experiment부터 열어도 대역은 유지됩니다.</p></div>
|
||||
</div>
|
||||
|
||||
<div class="workspace-tools input-file-grid" aria-label="입력 파일">
|
||||
<label class="file-button file-choice" title="Scene JSON을 선택하세요."><span class="file-sequence">1</span><span><strong>Scene</strong><small>장면·음원</small></span><input id="scene-input" type="file" accept="application/json,.json" /></label>
|
||||
<label class="file-button file-choice" title="사용자 배열이 있을 때 Array JSON을 선택하세요."><span class="file-sequence">2</span><span><strong>Array</strong><small>마이크 배열</small></span><input id="array-input" type="file" accept="application/json,.json" /></label>
|
||||
<label class="file-button file-choice" title="현재 Scene과 Array를 참조하는 Experiment JSON을 선택하세요."><span class="file-sequence">3</span><span><strong>Experiment</strong><small>분석 설정</small></span><input id="experiment-input" type="file" accept="application/json,.json" /></label>
|
||||
<label class="file-button file-choice" title="Experiment를 먼저 열 수 있습니다. 참조 ID가 다른 동안에는 실행만 비활성화됩니다."><span class="file-sequence">3</span><span><strong>Experiment</strong><small>분석 설정 · 선로드 가능</small></span><input id="experiment-input" type="file" accept="application/json,.json" /></label>
|
||||
<label class="file-button file-choice" title="Scene에 사용한 원본 파일을 다시 선택하세요."><span class="file-sequence">4</span><span><strong>Assets</strong><small>오디오·이미지</small></span><input id="asset-input" type="file" accept="audio/*,image/*,.wav,.flac,.mp3,.ogg,.m4a,.png,.jpg,.jpeg,.webp,.svg" multiple /></label>
|
||||
</div>
|
||||
|
||||
<div class="input-summary">
|
||||
<div class="compact-section-heading"><h3>현재 장면</h3><a class="builder-link" href="/apps/dataset-builder/">Builder에서 편집 →</a></div>
|
||||
<div class="compact-section-heading"><h3>현재 장면</h3><a class="builder-link" href="/apps/dataset-builder/" target="_blank" rel="noopener" title="현재 Camera의 Experiment를 유지한 채 새 탭에서 Scene을 편집합니다. 저장 후 이 Camera 탭으로 돌아와 Scene JSON을 여세요.">Builder 새 탭 ↗</a></div>
|
||||
<div class="source-summary" id="source-summary"></div>
|
||||
<div class="asset-list" id="asset-list"></div>
|
||||
</div>
|
||||
|
||||
<div class="array-card">
|
||||
<div class="array-heading"><div><span class="compact-label">MIC ARRAY</span><h3 id="array-title">배열</h3><a class="builder-link" href="/apps/array-builder/">Array Builder에서 만들기 →</a></div><strong id="microphone-count">— ch</strong></div>
|
||||
<div class="array-heading"><div><span class="compact-label">MIC ARRAY</span><h3 id="array-title">배열</h3><a class="builder-link" href="/apps/array-builder/" target="_blank" rel="noopener" title="현재 Camera의 Experiment를 유지한 채 새 탭에서 배열을 편집합니다. 저장 후 이 Camera 탭으로 돌아와 Array JSON을 여세요.">Array Builder 새 탭 ↗</a></div><strong id="microphone-count">— ch</strong></div>
|
||||
<label class="field">
|
||||
<span>배열 선택</span>
|
||||
<select id="array-preset">
|
||||
@@ -200,7 +200,7 @@
|
||||
</div>
|
||||
<p id="live-status" aria-live="polite">trajectory가 있는 Scene을 불러오거나 이동 예제를 여세요.</p>
|
||||
<div class="live-actions">
|
||||
<button class="button button--ghost" id="load-moving-example-button" type="button">이동 예제 열기</button>
|
||||
<button class="button button--ghost" id="load-moving-example-button" type="button" title="Scene·Array·Experiment와 분석 대역을 이동 예제 전체로 교체합니다.">이동 예제 전체 열기</button>
|
||||
<button class="button button--primary" id="prepare-live-button" type="button" disabled>Map 준비</button>
|
||||
<button class="button button--ghost" id="live-play-button" type="button" disabled>▶ 재생</button>
|
||||
</div>
|
||||
|
||||
@@ -981,7 +981,8 @@ function applyValidation(validation) {
|
||||
if (!state.lastRun && document.documentElement.dataset.runState === "idle") {
|
||||
setRunStatus(
|
||||
`${state.arrayDefinition.microphones.length} ch · ` +
|
||||
`${state.experiment.scan.columns}×${state.experiment.scan.rows} · 실행 준비 완료`,
|
||||
`${state.experiment.scan.columns}×${state.experiment.scan.rows} · ` +
|
||||
`분석 대역 ${state.experiment.frequencyBands.length}개 · 실행 준비 완료`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1470,6 +1471,18 @@ function toggleLivePlayback() {
|
||||
|
||||
async function loadMovingExample() {
|
||||
if (state.active) return;
|
||||
const bandCount = state.experiment?.frequencyBands?.length ?? 0;
|
||||
if (
|
||||
(bandCount > 1 || hasUnsavedWork()) &&
|
||||
!window.confirm(
|
||||
"이동 예제는 현재 Scene·Array·Experiment 전체와 " +
|
||||
String(bandCount) +
|
||||
"개 분석 대역을 교체합니다. 계속할까요?",
|
||||
)
|
||||
) {
|
||||
setRunStatus("이동 예제를 열지 않았습니다. 현재 입력과 분석 대역을 유지합니다.");
|
||||
return;
|
||||
}
|
||||
beginInputOperation();
|
||||
try {
|
||||
setRunStatus("이동 음원 예제를 불러오고 있습니다…");
|
||||
@@ -1494,7 +1507,7 @@ async function loadMovingExample() {
|
||||
assetRuntime.reset();
|
||||
markChanged({ syncControls: true, renderBands: true, dirty: "none" });
|
||||
renderAssets();
|
||||
setRunStatus("이동 음원 예제를 열었습니다. ‘라이브 준비’를 누르세요.");
|
||||
setRunStatus("Scene·Array·Experiment를 이동 음원 예제 전체로 교체했습니다. ‘Map 준비’를 누르세요.");
|
||||
} catch (error) {
|
||||
setTransientError(error, "이동 예제 불러오기 실패");
|
||||
} finally {
|
||||
@@ -1561,10 +1574,10 @@ async function importDocument(kind, input) {
|
||||
} else if (kind === "scene") {
|
||||
issues = await validateScene(value);
|
||||
} else {
|
||||
issues = await validateExperiment(value, {
|
||||
arrayDefinition: state.arrayDefinition,
|
||||
scene: state.scene,
|
||||
});
|
||||
// An Experiment is a useful editable document even before its referenced
|
||||
// Scene or Array has been loaded. Validate its own contract here, then let
|
||||
// validateCurrent() keep Run disabled until the document IDs line up.
|
||||
issues = await validateExperiment(value);
|
||||
}
|
||||
if (generation !== state.importGenerations[kind]) return;
|
||||
if (issues.length > 0) throw new Error(issues.slice(0, 4).map(formatIssue).join(" · "));
|
||||
@@ -1588,7 +1601,21 @@ async function importDocument(kind, input) {
|
||||
dirty: kind === "experiment" ? "none" : "experiment",
|
||||
});
|
||||
renderAssets();
|
||||
setRunStatus(`${file.name} ${kind} 문서를 불러왔습니다.`);
|
||||
const bandStatus = kind === "scene"
|
||||
? ` · 분석 대역 ${state.experiment.frequencyBands.length}개 유지`
|
||||
: kind === "experiment"
|
||||
? ` · 분석 대역 ${state.experiment.frequencyBands.length}개`
|
||||
: "";
|
||||
const pendingReferences = kind === "experiment"
|
||||
? [
|
||||
value.sceneId !== state.scene?.id ? `Scene ${value.sceneId}` : null,
|
||||
value.arrayId !== state.arrayDefinition?.id ? `Array ${value.arrayId}` : null,
|
||||
].filter(Boolean)
|
||||
: [];
|
||||
const referenceStatus = pendingReferences.length > 0
|
||||
? ` · ${pendingReferences.join(" · ")} 연결 대기`
|
||||
: "";
|
||||
setRunStatus(`${file.name} ${kind} 문서를 불러왔습니다.${bandStatus}${referenceStatus}`);
|
||||
} catch (error) {
|
||||
setTransientError(error, `${file.name} 불러오기 실패`);
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user