fix: sửa các tools của AI
This commit is contained in:
+71
-7
@@ -3190,6 +3190,7 @@ const App = () => {
|
||||
try {
|
||||
const profile = await window.SonicAPI.getProfile();
|
||||
setCurrentUser(profile);
|
||||
localStorage.setItem('sonic_user', JSON.stringify(profile));
|
||||
if (profile.must_change_password) {
|
||||
setIsMandatoryLogin(true);
|
||||
setAuthMode('force_change');
|
||||
@@ -3199,12 +3200,19 @@ const App = () => {
|
||||
setAuthModalOpen(false);
|
||||
}
|
||||
} catch (err) {
|
||||
localStorage.removeItem('sonic_token');
|
||||
localStorage.removeItem('sonic_user');
|
||||
setCurrentUser(null);
|
||||
setIsMandatoryLogin(true);
|
||||
setAuthMode('login');
|
||||
setAuthModalOpen(true);
|
||||
const cached = localStorage.getItem('sonic_user');
|
||||
if (cached) {
|
||||
try { setCurrentUser(JSON.parse(cached)); } catch (_) {}
|
||||
setIsMandatoryLogin(false);
|
||||
setAuthModalOpen(false);
|
||||
} else {
|
||||
localStorage.removeItem('sonic_token');
|
||||
localStorage.removeItem('sonic_user');
|
||||
setCurrentUser(null);
|
||||
setIsMandatoryLogin(true);
|
||||
setAuthMode('login');
|
||||
setAuthModalOpen(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
checkAuthStatus();
|
||||
@@ -7470,6 +7478,45 @@ const App = () => {
|
||||
handlePlayheadSet(time);
|
||||
return { success: true, time: parseFloat(time.toFixed(3)) };
|
||||
},
|
||||
selectItem: (args) => {
|
||||
if (args.select_all) {
|
||||
setSelectedTrackId(null);
|
||||
clearLocalSelection();
|
||||
setSelectionMode('global');
|
||||
setSelectionStart(0);
|
||||
const maxDur = tracks.reduce((max, t) => {
|
||||
const dur = t.buffer ? t.buffer.duration : 0;
|
||||
const clips = t.clips || [];
|
||||
const clipMax = clips.reduce((m, c) => Math.max(m, (c.startTime || 0) + (c.buffer ? c.buffer.duration : 0)), 0);
|
||||
return Math.max(max, dur, clipMax);
|
||||
}, 0);
|
||||
setSelectionEnd(Math.max(maxDur, currentTime + 10));
|
||||
return { success: true, selection: 'all', duration: parseFloat(Math.max(maxDur, currentTime + 10).toFixed(3)) };
|
||||
}
|
||||
const tid = args.track_id || selectedTrackId;
|
||||
const track = tracks.find(t => t.id === tid);
|
||||
if (!track) return { success: false, error: `Track ${tid} not found` };
|
||||
const clips = track.clips && track.clips.length > 0 ? track.clips : (track.buffer ? [{ id: 'default_' + track.id, name: track.name, buffer: track.buffer, startTime: track.startTime || 0 }] : []);
|
||||
if (args.item_name) {
|
||||
const match = clips.find(c => c.name && c.name.toLowerCase().includes(args.item_name.toLowerCase()));
|
||||
if (!match) return { success: false, error: `No clip matching "${args.item_name}" on track ${track.name}` };
|
||||
setSelectedTrackId(tid);
|
||||
clearLocalSelection();
|
||||
setSelectionMode('global');
|
||||
const start = match.startTime || 0;
|
||||
const end = start + (match.buffer ? match.buffer.duration : 2);
|
||||
setSelectionStart(start);
|
||||
setSelectionEnd(end);
|
||||
return { success: true, trackId: tid, trackName: track.name, clipId: match.id, clipName: match.name, start: parseFloat(start.toFixed(3)), end: parseFloat(end.toFixed(3)) };
|
||||
}
|
||||
setSelectedTrackId(tid);
|
||||
clearLocalSelection();
|
||||
setSelectionMode('global');
|
||||
const trackEnd = track.buffer ? track.buffer.duration : (clips.length > 0 ? Math.max(...clips.map(c => (c.startTime || 0) + (c.buffer ? c.buffer.duration : 0))) : 4);
|
||||
setSelectionStart(0);
|
||||
setSelectionEnd(trackEnd);
|
||||
return { success: true, trackId: tid, trackName: track.name, duration: parseFloat(trackEnd.toFixed(3)) };
|
||||
},
|
||||
addMarker: (args) => {
|
||||
const trackId = args.track_id || selectedTrackId;
|
||||
const time = args.time ?? currentTime;
|
||||
@@ -7741,7 +7788,7 @@ const App = () => {
|
||||
onClick: () => setMenuOpen(menuOpen === menu.label ? null : menu.label),
|
||||
className: `px-3 py-1 text-xs font-medium transition rounded ${menuOpen === menu.label ? 'bg-zinc-700 text-zinc-100' : 'text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800'}`
|
||||
}, menu.label), menuOpen === menu.label && /*#__PURE__*/React.createElement("div", {
|
||||
className: "absolute top-full left-0 mt-0.5 bg-[#2a2a2a] border border-zinc-700 rounded-lg shadow-2xl py-1 w-52 z-50",
|
||||
className: "absolute top-full left-0 mt-0.5 bg-[#2a2a2a] border border-zinc-700 rounded-lg shadow-2xl py-1 w-64 z-50",
|
||||
onClick: () => setMenuOpen(null)
|
||||
}, menu.items.map((item, i) => item.sep ? /*#__PURE__*/React.createElement("div", {
|
||||
key: i,
|
||||
@@ -8892,6 +8939,23 @@ const App = () => {
|
||||
setEditNameInput(track.name);
|
||||
}
|
||||
}, track.name)), /*#__PURE__*/React.createElement("div", {
|
||||
className: "flex flex-wrap gap-0.5 max-w-[100px] mb-0.5"
|
||||
}, (track.clips && track.clips.length > 0 ? track.clips : track.buffer ? [{ id: 'default', name: track.name, startTime: track.startTime }] : []).slice(0, 3).map(c => /*#__PURE__*/React.createElement("span", {
|
||||
key: c.id,
|
||||
className: "text-[9px] font-mono text-zinc-500 bg-zinc-800/60 rounded px-0.5 truncate max-w-[90px] cursor-pointer hover:text-cyan-400 hover:bg-zinc-700",
|
||||
title: c.name || track.name,
|
||||
onClick: e => {
|
||||
e.stopPropagation();
|
||||
setSelectedTrackId(track.id);
|
||||
clearLocalSelection();
|
||||
setSelectionMode('global');
|
||||
const start = c.startTime || 0;
|
||||
const end = start + (c.buffer ? c.buffer.duration : 2);
|
||||
setSelectionStart(start);
|
||||
setSelectionEnd(end);
|
||||
showToast(`Selected: ${c.name || track.name}`, 'info');
|
||||
}
|
||||
}, c.name || track.name))), /*#__PURE__*/React.createElement("div", {
|
||||
className: "flex items-center gap-1"
|
||||
}, /*#__PURE__*/React.createElement("button", {
|
||||
onClick: e => {
|
||||
|
||||
@@ -3190,6 +3190,7 @@ const App = () => {
|
||||
try {
|
||||
const profile = await window.SonicAPI.getProfile();
|
||||
setCurrentUser(profile);
|
||||
localStorage.setItem('sonic_user', JSON.stringify(profile));
|
||||
if (profile.must_change_password) {
|
||||
setIsMandatoryLogin(true);
|
||||
setAuthMode('force_change');
|
||||
@@ -3199,12 +3200,21 @@ const App = () => {
|
||||
setAuthModalOpen(false);
|
||||
}
|
||||
} catch (err) {
|
||||
localStorage.removeItem('sonic_token');
|
||||
localStorage.removeItem('sonic_user');
|
||||
setCurrentUser(null);
|
||||
setIsMandatoryLogin(true);
|
||||
setAuthMode('login');
|
||||
setAuthModalOpen(true);
|
||||
const cached = localStorage.getItem('sonic_user');
|
||||
if (cached) {
|
||||
try {
|
||||
setCurrentUser(JSON.parse(cached));
|
||||
} catch (_) {}
|
||||
setIsMandatoryLogin(false);
|
||||
setAuthModalOpen(false);
|
||||
} else {
|
||||
localStorage.removeItem('sonic_token');
|
||||
localStorage.removeItem('sonic_user');
|
||||
setCurrentUser(null);
|
||||
setIsMandatoryLogin(true);
|
||||
setAuthMode('login');
|
||||
setAuthModalOpen(true);
|
||||
}
|
||||
}
|
||||
};
|
||||
checkAuthStatus();
|
||||
@@ -7635,6 +7645,73 @@ const App = () => {
|
||||
time: parseFloat(time.toFixed(3))
|
||||
};
|
||||
},
|
||||
selectItem: args => {
|
||||
if (args.select_all) {
|
||||
setSelectedTrackId(null);
|
||||
clearLocalSelection();
|
||||
setSelectionMode('global');
|
||||
setSelectionStart(0);
|
||||
const maxDur = tracks.reduce((max, t) => {
|
||||
const dur = t.buffer ? t.buffer.duration : 0;
|
||||
const clips = t.clips || [];
|
||||
const clipMax = clips.reduce((m, c) => Math.max(m, (c.startTime || 0) + (c.buffer ? c.buffer.duration : 0)), 0);
|
||||
return Math.max(max, dur, clipMax);
|
||||
}, 0);
|
||||
setSelectionEnd(Math.max(maxDur, currentTime + 10));
|
||||
return {
|
||||
success: true,
|
||||
selection: 'all',
|
||||
duration: parseFloat(Math.max(maxDur, currentTime + 10).toFixed(3))
|
||||
};
|
||||
}
|
||||
const tid = args.track_id || selectedTrackId;
|
||||
const track = tracks.find(t => t.id === tid);
|
||||
if (!track) return {
|
||||
success: false,
|
||||
error: `Track ${tid} not found`
|
||||
};
|
||||
const clips = track.clips && track.clips.length > 0 ? track.clips : track.buffer ? [{
|
||||
id: 'default_' + track.id,
|
||||
name: track.name,
|
||||
buffer: track.buffer,
|
||||
startTime: track.startTime || 0
|
||||
}] : [];
|
||||
if (args.item_name) {
|
||||
const match = clips.find(c => c.name && c.name.toLowerCase().includes(args.item_name.toLowerCase()));
|
||||
if (!match) return {
|
||||
success: false,
|
||||
error: `No clip matching "${args.item_name}" on track ${track.name}`
|
||||
};
|
||||
setSelectedTrackId(tid);
|
||||
clearLocalSelection();
|
||||
setSelectionMode('global');
|
||||
const start = match.startTime || 0;
|
||||
const end = start + (match.buffer ? match.buffer.duration : 2);
|
||||
setSelectionStart(start);
|
||||
setSelectionEnd(end);
|
||||
return {
|
||||
success: true,
|
||||
trackId: tid,
|
||||
trackName: track.name,
|
||||
clipId: match.id,
|
||||
clipName: match.name,
|
||||
start: parseFloat(start.toFixed(3)),
|
||||
end: parseFloat(end.toFixed(3))
|
||||
};
|
||||
}
|
||||
setSelectedTrackId(tid);
|
||||
clearLocalSelection();
|
||||
setSelectionMode('global');
|
||||
const trackEnd = track.buffer ? track.buffer.duration : clips.length > 0 ? Math.max(...clips.map(c => (c.startTime || 0) + (c.buffer ? c.buffer.duration : 0))) : 4;
|
||||
setSelectionStart(0);
|
||||
setSelectionEnd(trackEnd);
|
||||
return {
|
||||
success: true,
|
||||
trackId: tid,
|
||||
trackName: track.name,
|
||||
duration: parseFloat(trackEnd.toFixed(3))
|
||||
};
|
||||
},
|
||||
addMarker: args => {
|
||||
const trackId = args.track_id || selectedTrackId;
|
||||
const time = args.time ?? currentTime;
|
||||
@@ -7928,7 +8005,7 @@ const App = () => {
|
||||
onClick: () => setMenuOpen(menuOpen === menu.label ? null : menu.label),
|
||||
className: `px-3 py-1 text-xs font-medium transition rounded ${menuOpen === menu.label ? 'bg-zinc-700 text-zinc-100' : 'text-zinc-400 hover:text-zinc-200 hover:bg-zinc-800'}`
|
||||
}, menu.label), menuOpen === menu.label && /*#__PURE__*/React.createElement("div", {
|
||||
className: "absolute top-full left-0 mt-0.5 bg-[#2a2a2a] border border-zinc-700 rounded-lg shadow-2xl py-1 w-52 z-50",
|
||||
className: "absolute top-full left-0 mt-0.5 bg-[#2a2a2a] border border-zinc-700 rounded-lg shadow-2xl py-1 w-64 z-50",
|
||||
onClick: () => setMenuOpen(null)
|
||||
}, menu.items.map((item, i) => item.sep ? /*#__PURE__*/React.createElement("div", {
|
||||
key: i,
|
||||
@@ -9081,6 +9158,27 @@ const App = () => {
|
||||
setEditNameInput(track.name);
|
||||
}
|
||||
}, track.name)), /*#__PURE__*/React.createElement("div", {
|
||||
className: "flex flex-wrap gap-0.5 max-w-[100px] mb-0.5"
|
||||
}, (track.clips && track.clips.length > 0 ? track.clips : track.buffer ? [{
|
||||
id: 'default',
|
||||
name: track.name,
|
||||
startTime: track.startTime
|
||||
}] : []).slice(0, 3).map(c => /*#__PURE__*/React.createElement("span", {
|
||||
key: c.id,
|
||||
className: "text-[9px] font-mono text-zinc-500 bg-zinc-800/60 rounded px-0.5 truncate max-w-[90px] cursor-pointer hover:text-cyan-400 hover:bg-zinc-700",
|
||||
title: c.name || track.name,
|
||||
onClick: e => {
|
||||
e.stopPropagation();
|
||||
setSelectedTrackId(track.id);
|
||||
clearLocalSelection();
|
||||
setSelectionMode('global');
|
||||
const start = c.startTime || 0;
|
||||
const end = start + (c.buffer ? c.buffer.duration : 2);
|
||||
setSelectionStart(start);
|
||||
setSelectionEnd(end);
|
||||
showToast(`Selected: ${c.name || track.name}`, 'info');
|
||||
}
|
||||
}, c.name || track.name))), /*#__PURE__*/React.createElement("div", {
|
||||
className: "flex items-center gap-1"
|
||||
}, /*#__PURE__*/React.createElement("button", {
|
||||
onClick: e => {
|
||||
|
||||
@@ -166,6 +166,17 @@ const AIGateway = (function() {
|
||||
length_bars: { type: 'number', description: 'Độ dài vùng chọn (bar). Dùng cùng start_bar thay cho end_bar.' }
|
||||
}
|
||||
}
|
||||
}, {
|
||||
name: 'select_item',
|
||||
description: 'Chọn một item/clip/sample cụ thể trên track. Nếu không có track_id và item_name thì chọn toàn bộ timeline.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
track_id: { type: 'string', description: 'ID của track. Nếu không có thì dùng track đang chọn.' },
|
||||
item_name: { type: 'string', description: 'Tên của clip/sample cần chọn. VD: "Cut_Cartoon Capers Loop.mp3". Nếu không có thì chọn toàn bộ track.' },
|
||||
select_all: { type: 'boolean', description: 'Chọn toàn bộ timeline (bỏ qua track_id và item_name)' }
|
||||
}
|
||||
}
|
||||
}, {
|
||||
name: 'cut_audio',
|
||||
description: 'Cắt đoạn audio đang được chọn từ track nguồn, snap zero-crossing, tạo track mới chứa đoạn cắt',
|
||||
|
||||
@@ -63,6 +63,7 @@ const DAWCommandDispatcher = (function() {
|
||||
register('SET_SELECTION', (args) => api.setSelection(args));
|
||||
register('SET_BPM', (args) => api.setBpm(args));
|
||||
register('SET_PLAYHEAD', (args) => api.setPlayhead(args));
|
||||
register('SELECT_ITEM', (args) => api.selectItem(args));
|
||||
register('ADD_MARKER', (args) => api.addMarker(args));
|
||||
register('CREATE_MIDI_ITEM', (args) => AIGateway.createMidiItem(args));
|
||||
register('MODIFY_MIDI_NOTES', (args) => AIGateway.modifyMidiNotes(args));
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user