fix: few minor UI and behavior adjustments

This commit is contained in:
Xiaohan-Tian
2026-05-11 23:23:11 -07:00
parent f57dd7c1da
commit 64dda5bb01
6 changed files with 81 additions and 26 deletions
+48 -21
View File
@@ -329,7 +329,7 @@ const ClipTab: React.FC<ClipTabProps> = ({ bpm, keySignature }) => {
setGenStatus('polling');
setGenHint('Generating clip...');
while (true) {
if (signal.aborted) return;
@@ -655,7 +655,7 @@ const FullSongTab: React.FC = () => {
type ResultItem = { progress: number; stage: string; status: number };
type PollResponse = { data: Array<{ status: number; result: string }>; code: number };
while (true) {
if (signal.aborted) return;
@@ -905,6 +905,19 @@ const SeparatorTab: React.FC<{ mode: KGOneMode }> = ({ mode }) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// Reset separator results whenever the active project changes
useEffect(() => {
stemAudioUrls.forEach(s => URL.revokeObjectURL(s.url));
setStemAudioUrls([]);
setGenStatus('idle');
setGenHint('');
setErrorMsg('');
setIsImporting(false);
setImportError('');
originalRegionRef.current = null;
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [projectName]);
useEffect(() => {
setModel(availableSeparatorModels[0].value);
}, [availableSeparatorModels]);
@@ -1116,7 +1129,7 @@ const SeparatorTab: React.FC<{ mode: KGOneMode }> = ({ mode }) => {
let files: string[] = [];
while (true) {
if (signal.aborted) return;
@@ -1363,7 +1376,8 @@ const SeparatorTab: React.FC<{ mode: KGOneMode }> = ({ mode }) => {
<div className="kgone-local-mode-text">
Only Vocal and Instrument (Medium Accuracy) is available while not integrated with K.G.One Music Studio server.
Processing in local may take long time depending on your hardware. When fallback to CPU happens, the webpage may
temporarily hang with little or no UI response until processing advances.
temporarily hang with little or no UI response until processing advances.{' '}
<a href="https://github.com/KGAudioLab/K.G.One" target="_blank" rel="noopener noreferrer">Learn more about K.G.One Music Studio server integration.</a>
</div>
<div className="kgone-runtime-row">
<div className="kgone-provider-chip">Provider: {localProviderLabel}</div>
@@ -1740,7 +1754,7 @@ const RemixTab: React.FC = () => {
type ResultItem = { progress: number; stage: string; status: number };
type PollResponse = { data: Array<{ status: number; result: string }>; code: number };
while (true) {
if (signal.aborted) return;
@@ -2021,7 +2035,7 @@ const RemixTab: React.FC = () => {
const RepaintTab: React.FC = () => {
const { selectedRegionIds, projectName, bpm, timeSignature, maxBars,
isLooping, loopingRange, refreshProjectState } = useProjectStore();
isLooping, loopingRange, refreshProjectState } = useProjectStore();
// Form state (mirrors FullSongTab)
const [caption, setCaption] = useState('');
@@ -2258,7 +2272,7 @@ const RepaintTab: React.FC = () => {
type ResultItem = { progress: number; stage: string; status: number };
type PollResponse = { data: Array<{ status: number; result: string }>; code: number };
while (true) {
if (signal.aborted) return;
@@ -2573,24 +2587,37 @@ const KGOnePanel: React.FC<KGOnePanelProps> = ({ isVisible }) => {
return (
<div className={`kgone-panel${isVisible ? '' : ' is-hidden'}`}>
<div className="kgone-panel-header">
<h3>K.G.One Music Generator</h3>
<h3>{mode === 'local-separator' ? 'Music Generator' : 'K.G.One Music Generator'}</h3>
</div>
<div className="kgone-tabs">
{/* Clip tab temporarily disabled, will enable in the future */}
{KGONE_TABS.map(tab => (
<button
key={tab}
className={`kgone-tab${activeTab === tab ? ' active' : ''}${disabledTabs.has(tab) ? ' is-disabled' : ''}`}
onClick={() => {
if (disabledTabs.has(tab)) return;
setActiveTab(tab);
}}
disabled={disabledTabs.has(tab)}
>
{formatKGOneTabLabel(tab)}
</button>
))}
{KGONE_TABS.map(tab => {
const isDisabled = disabledTabs.has(tab);
const button = (
<button
key={tab}
className={`kgone-tab${activeTab === tab ? ' active' : ''}${isDisabled ? ' is-disabled' : ''}`}
onClick={() => {
if (isDisabled) return;
setActiveTab(tab);
}}
disabled={isDisabled}
style={isDisabled ? { width: '100%' } : undefined}
>
{formatKGOneTabLabel(tab)}
</button>
);
return isDisabled ? (
<span
key={tab}
title="Requires K.G.One Music Studio server integration"
style={{ flex: 1 }}
>
{button}
</span>
) : button;
})}
</div>
<div className="kgone-panel-body">
+24
View File
@@ -122,6 +122,30 @@ const Toolbar: React.FC = () => {
return;
}
// Untitled Project is ephemeral — no existing save to preserve, so rename directly
if (savedProjectName === RESERVED_PROJECT_NAME) {
const storage = KGProjectStorage.getInstance();
const exists = await storage.exists(newName);
if (exists) {
const confirmed = await showConfirm(
`Project "${newName}" already exists. Do you want to overwrite it?`
);
if (!confirmed) return;
setProjectName(newName);
await saveProject(newName, savedProjectName, setStatus, (finalName) => {
setSavedProjectName(finalName);
if (finalName !== newName) setProjectName(finalName);
}, true /* forceOverwrite */);
return;
}
setProjectName(newName);
await saveProject(newName, savedProjectName, setStatus, (finalName) => {
setSavedProjectName(finalName);
if (finalName !== newName) setProjectName(finalName);
});
return;
}
// Ask whether the user wants to rename or save as a copy
const choice = await showChoice(
"Would you like to rename this project, or save it as a new copy?",
@@ -591,7 +591,7 @@ const GeneralSettings: React.FC = () => {
disabled={kgoneServerManaged}
/>
<div className="settings-help" style={{ fontSize: '12px', color: '#888', marginTop: '4px' }}>
Base URL of a running K.G.One server. Used for full-song generation, clip generation, and stem separation.
Base URL of a running K.G.One Music Studio server. Used for full-song generation, clip generation, and stem separation.
</div>
</div>
</div>
+4 -2
View File
@@ -45,6 +45,7 @@ interface RegionItemProps {
audioBuffer?: AudioBuffer;
previewWaveformPeaks?: AudioRecordingPeak[];
isPreview?: boolean;
isAudioRegion?: boolean;
}
const RegionItem: React.FC<RegionItemProps> = ({
@@ -71,6 +72,7 @@ const RegionItem: React.FC<RegionItemProps> = ({
audioBuffer,
previewWaveformPeaks,
isPreview = false,
isAudioRegion = false,
}) => {
// Get selection state and time signature from store
const { selectedRegionIds, timeSignature, bpm } = useProjectStore();
@@ -652,7 +654,7 @@ const RegionItem: React.FC<RegionItemProps> = ({
return (
<div
key={id}
className={`track-region ${isDragging ? 'dragging' : ''} ${isSelected ? (isPrimarySelected ? 'selected' : 'selected-secondary') : ''} ${audioRegion ? 'audio-region' : ''}`}
className={`track-region ${isDragging ? 'dragging' : ''} ${isSelected ? (isPrimarySelected ? 'selected' : 'selected-secondary') : ''} ${(audioRegion || isAudioRegion) ? 'audio-region' : ''}`}
style={{ ...style, cursor: isPreview ? 'default' : cursor, ...(isFineDragging ? { transform: `translateX(${fineTranslateX}px)`, zIndex: 100 } : {}) }}
onMouseMove={isPreview ? undefined : handleMouseMove}
onMouseLeave={isPreview ? undefined : handleMouseLeave}
@@ -666,7 +668,7 @@ const RegionItem: React.FC<RegionItemProps> = ({
<div className="region-header">
{name}
</div>
<div className={`region-content${audioRegion ? ' audio-region-content' : ''}`} ref={regionContentRef}>
<div className={`region-content${(audioRegion || isAudioRegion) ? ' audio-region-content' : ''}`} ref={regionContentRef}>
{!isPreview && <div className="region-left-buttons">
{!audioRegion && (
<button
+1
View File
@@ -654,6 +654,7 @@ const TrackGridItem: React.FC<TrackGridItemProps> = ({
trackIndex={index}
previewWaveformPeaks={recordingAudioPreviewPeaks}
isPreview
isAudioRegion
/>
)}
{isAutomationActive && activeTrackAutomationType && (