Changeset 0.21.5 (#91)
This commit is contained in:
@@ -240,6 +240,12 @@ function showChatContextMenu(e, chatId) {
|
||||
<span class="ctx-icon">+</span> New project…
|
||||
</button>`;
|
||||
|
||||
// Workspace override (v0.21.5)
|
||||
items += '<div class="ctx-divider"></div>';
|
||||
items += `<button class="ctx-item" onclick="showWorkspacePicker('${chatId}');dismissChatContextMenu()">
|
||||
<span class="ctx-icon">📁</span> Set workspace…
|
||||
</button>`;
|
||||
|
||||
menu.innerHTML = items;
|
||||
|
||||
// Position near cursor
|
||||
@@ -392,6 +398,16 @@ function _registerProjectPanel() {
|
||||
<label class="project-panel-label">Notes</label>
|
||||
<div id="projectNoteList" class="project-panel-list"></div>
|
||||
</div>
|
||||
<div class="project-panel-section">
|
||||
<label class="project-panel-label">Workspace</label>
|
||||
<div class="project-panel-ws-row" id="projectWsRow">
|
||||
<select id="projectWsSelect" class="project-panel-select">
|
||||
<option value="">None</option>
|
||||
</select>
|
||||
<button class="btn-small" id="projectWsCreate" title="Create workspace">+ New</button>
|
||||
</div>
|
||||
<div class="project-panel-hint">Workspace files are available to AI in all project chats. Enables the Editor surface.</div>
|
||||
</div>
|
||||
<div class="project-panel-section project-panel-footer">
|
||||
<label class="project-panel-checkbox">
|
||||
<input type="checkbox" id="projectArchiveToggle">
|
||||
@@ -413,6 +429,10 @@ function _registerProjectPanel() {
|
||||
// Wire archive toggle (v0.19.2)
|
||||
el.querySelector('#projectArchiveToggle').addEventListener('change', _toggleProjectArchive);
|
||||
|
||||
// Wire workspace select + create (v0.21.5)
|
||||
el.querySelector('#projectWsSelect').addEventListener('change', _saveProjectWorkspace);
|
||||
el.querySelector('#projectWsCreate').addEventListener('click', _createProjectWorkspace);
|
||||
|
||||
PanelRegistry.register('project', {
|
||||
element: el,
|
||||
label: 'Project',
|
||||
@@ -457,6 +477,9 @@ async function _loadProjectPanel(projectId) {
|
||||
|
||||
// Load notes
|
||||
await _renderProjectNotes(projectId);
|
||||
|
||||
// Load workspace picker (v0.21.5)
|
||||
await _renderProjectWorkspace(projectId);
|
||||
}
|
||||
|
||||
async function _saveProjectPrompt() {
|
||||
@@ -643,6 +666,143 @@ async function _toggleProjectArchive() {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Workspace picker (v0.21.5) ───────────────
|
||||
|
||||
async function _renderProjectWorkspace(projectId) {
|
||||
const sel = document.getElementById('projectWsSelect');
|
||||
if (!sel) return;
|
||||
|
||||
// Fetch current project state + available workspaces in parallel
|
||||
const [proj, workspaces] = await Promise.all([
|
||||
API.getProject(projectId).catch(() => null),
|
||||
API.listWorkspaces().catch(() => []),
|
||||
]);
|
||||
|
||||
const currentWsId = proj?.workspace_id || '';
|
||||
const wsList = Array.isArray(workspaces) ? workspaces : (workspaces?.data || []);
|
||||
|
||||
sel.innerHTML = '<option value="">None</option>';
|
||||
for (const ws of wsList) {
|
||||
const opt = document.createElement('option');
|
||||
opt.value = ws.id;
|
||||
opt.textContent = ws.name || ws.id.slice(0, 8);
|
||||
if (ws.id === currentWsId) opt.selected = true;
|
||||
sel.appendChild(opt);
|
||||
}
|
||||
}
|
||||
|
||||
async function _saveProjectWorkspace() {
|
||||
if (!_projectPanelId) return;
|
||||
const wsId = document.getElementById('projectWsSelect').value;
|
||||
try {
|
||||
await API.updateProject(_projectPanelId, { workspace_id: wsId || null });
|
||||
UI.toast(wsId ? 'Workspace linked' : 'Workspace unlinked', 'success');
|
||||
// Trigger editor surface check
|
||||
if (typeof EditorMode !== 'undefined') EditorMode.check();
|
||||
} catch (e) {
|
||||
UI.toast('Failed to update workspace', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
async function _createProjectWorkspace() {
|
||||
if (!_projectPanelId) return;
|
||||
const proj = App.projects.find(p => p.id === _projectPanelId);
|
||||
const name = prompt('Workspace name:', (proj?.name || 'Project') + ' Workspace');
|
||||
if (!name || !name.trim()) return;
|
||||
|
||||
try {
|
||||
const ws = await API.createWorkspace({
|
||||
name: name.trim(),
|
||||
owner_type: 'user',
|
||||
owner_id: API.user?.id || '',
|
||||
});
|
||||
// Auto-bind to project
|
||||
await API.updateProject(_projectPanelId, { workspace_id: ws.id });
|
||||
UI.toast('Workspace created and linked', 'success');
|
||||
// Refresh the picker
|
||||
await _renderProjectWorkspace(_projectPanelId);
|
||||
// Trigger editor surface check
|
||||
if (typeof EditorMode !== 'undefined') EditorMode.check();
|
||||
} catch (e) {
|
||||
UI.toast('Failed to create workspace: ' + (e.message || e), 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// ── Channel workspace picker (v0.21.5) ───────
|
||||
|
||||
async function showWorkspacePicker(chatId) {
|
||||
let workspaces = [];
|
||||
try {
|
||||
const resp = await API.listWorkspaces();
|
||||
workspaces = Array.isArray(resp) ? resp : (resp?.data || []);
|
||||
} catch (_) {}
|
||||
|
||||
// Get current channel workspace
|
||||
let currentWsId = '';
|
||||
try {
|
||||
const ch = await API.getChannel(chatId);
|
||||
currentWsId = ch.workspace_id || '';
|
||||
} catch (_) {}
|
||||
|
||||
// Build options
|
||||
const options = [{ value: '', label: 'None (inherit from project)' }];
|
||||
for (const ws of workspaces) {
|
||||
options.push({ value: ws.id, label: ws.name || ws.id.slice(0, 8) });
|
||||
}
|
||||
options.push({ value: '__new__', label: '+ Create new workspace…' });
|
||||
|
||||
// Use simple select via popup
|
||||
const overlay = document.createElement('div');
|
||||
overlay.className = 'editor-quick-open'; // reuse overlay style
|
||||
overlay.innerHTML = `
|
||||
<div class="editor-qo-dialog" style="width:340px">
|
||||
<div style="padding:10px 14px; font-weight:600; border-bottom:1px solid var(--border)">Set Workspace</div>
|
||||
<div class="editor-qo-results" id="wsPickerList"></div>
|
||||
</div>`;
|
||||
|
||||
const list = overlay.querySelector('#wsPickerList');
|
||||
for (const opt of options) {
|
||||
const row = document.createElement('div');
|
||||
row.className = 'editor-qo-row' + (opt.value === currentWsId ? ' active' : '');
|
||||
row.style.fontWeight = opt.value === currentWsId ? '600' : '';
|
||||
row.textContent = opt.label;
|
||||
row.addEventListener('click', async () => {
|
||||
overlay.remove();
|
||||
if (opt.value === '__new__') {
|
||||
const name = prompt('Workspace name:');
|
||||
if (!name || !name.trim()) return;
|
||||
try {
|
||||
const ws = await API.createWorkspace({
|
||||
name: name.trim(),
|
||||
owner_type: 'user',
|
||||
owner_id: API.user?.id || '',
|
||||
});
|
||||
await API.updateChannel(chatId, { workspace_id: ws.id });
|
||||
UI.toast('Workspace created and linked', 'success');
|
||||
if (typeof EditorMode !== 'undefined') EditorMode.check();
|
||||
} catch (e) {
|
||||
UI.toast('Failed: ' + (e.message || e), 'error');
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
await API.updateChannel(chatId, { workspace_id: opt.value || '' });
|
||||
UI.toast(opt.value ? 'Workspace linked' : 'Workspace unlinked', 'success');
|
||||
if (typeof EditorMode !== 'undefined') EditorMode.check();
|
||||
} catch (e) {
|
||||
UI.toast('Failed: ' + (e.message || e), 'error');
|
||||
}
|
||||
}
|
||||
});
|
||||
list.appendChild(row);
|
||||
}
|
||||
|
||||
overlay.addEventListener('click', (e) => {
|
||||
if (e.target === overlay) overlay.remove();
|
||||
});
|
||||
|
||||
document.body.appendChild(overlay);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════
|
||||
// CHANNEL REORDER WITHIN PROJECT (v0.19.2)
|
||||
// ═══════════════════════════════════════════
|
||||
|
||||
Reference in New Issue
Block a user