fix: few minor UI and behavior adjustments
This commit is contained in:
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -654,6 +654,7 @@ const TrackGridItem: React.FC<TrackGridItemProps> = ({
|
||||
trackIndex={index}
|
||||
previewWaveformPeaks={recordingAudioPreviewPeaks}
|
||||
isPreview
|
||||
isAudioRegion
|
||||
/>
|
||||
)}
|
||||
{isAutomationActive && activeTrackAutomationType && (
|
||||
|
||||
Reference in New Issue
Block a user