Changeset 0.22.4 (#146)

This commit is contained in:
2026-03-02 22:16:08 +00:00
parent 9940fb5831
commit 3953dcf364
25 changed files with 2254 additions and 145 deletions

View File

@@ -409,6 +409,26 @@ function _registerProjectPanel() {
</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" id="projectGitSection" style="display:none">
<label class="project-panel-label">Git Settings</label>
<div style="display:flex;flex-direction:column;gap:6px">
<input type="text" id="projectGitRemote" class="project-panel-input" placeholder="Git remote URL (https://...)" spellcheck="false">
<input type="text" id="projectGitBranch" class="project-panel-input" placeholder="Branch (default: main)" spellcheck="false">
<select id="projectGitCredential" class="project-panel-select">
<option value="">No credential</option>
</select>
<button class="btn-small" id="projectGitSave">Save Git Config</button>
</div>
<div class="project-panel-hint">Configure git remote to sync workspace files with a repository.</div>
</div>
<div class="project-panel-section" id="projectFilesSection">
<label class="project-panel-label">Project Files</label>
<div id="projectFileList" class="project-panel-list"></div>
<div style="margin-top:6px">
<input type="file" id="projectFileInput" style="display:none" multiple>
<button class="btn-small" id="projectFileUploadBtn">Upload File</button>
</div>
</div>
<div class="project-panel-section project-panel-footer">
<label class="project-panel-checkbox">
<input type="checkbox" id="projectArchiveToggle">
@@ -434,6 +454,15 @@ function _registerProjectPanel() {
el.querySelector('#projectWsSelect').addEventListener('change', _saveProjectWorkspace);
el.querySelector('#projectWsCreate').addEventListener('click', _createProjectWorkspace);
// Wire git settings (v0.22.4)
el.querySelector('#projectGitSave').addEventListener('click', _saveProjectGitConfig);
// Wire project files (v0.22.4)
el.querySelector('#projectFileUploadBtn').addEventListener('click', () => {
document.getElementById('projectFileInput').click();
});
el.querySelector('#projectFileInput').addEventListener('change', _handleProjectFileUpload);
PanelRegistry.register('project', {
element: el,
label: 'Project',
@@ -481,6 +510,12 @@ async function _loadProjectPanel(projectId) {
// Load workspace picker (v0.21.5)
await _renderProjectWorkspace(projectId);
// Load git settings (v0.22.4) — depends on workspace being loaded
await _renderProjectGitSettings(projectId);
// Load project files (v0.22.4)
await _renderProjectFiles(projectId);
}
async function _saveProjectPrompt() {
@@ -700,6 +735,8 @@ async function _saveProjectWorkspace() {
UI.toast(wsId ? 'Workspace linked' : 'Workspace unlinked', 'success');
// Trigger editor surface check
if (typeof EditorMode !== 'undefined') EditorMode.check();
// Refresh git settings (v0.22.4)
await _renderProjectGitSettings(_projectPanelId);
} catch (e) {
UI.toast('Failed to update workspace', 'error');
}
@@ -731,6 +768,106 @@ async function _createProjectWorkspace() {
// ── Channel workspace picker (v0.21.5) ───────
// ── Workspace Git Config (v0.22.4) ───────────
async function _renderProjectGitSettings(projectId) {
const gitSection = document.getElementById('projectGitSection');
const wsId = document.getElementById('projectWsSelect')?.value;
if (!wsId) {
gitSection.style.display = 'none';
return;
}
gitSection.style.display = '';
try {
const ws = await API.getWorkspace(wsId);
document.getElementById('projectGitRemote').value = ws.git_remote_url || '';
document.getElementById('projectGitBranch').value = ws.git_branch || '';
// Load git credentials
const credSel = document.getElementById('projectGitCredential');
credSel.innerHTML = '<option value="">No credential</option>';
try {
const creds = await API.listGitCredentials();
const list = Array.isArray(creds) ? creds : (creds?.data || []);
for (const c of list) {
const opt = document.createElement('option');
opt.value = c.id;
opt.textContent = c.name || c.id.slice(0, 8);
if (c.id === ws.git_credential_id) opt.selected = true;
credSel.appendChild(opt);
}
} catch (_) {}
} catch (_) {
gitSection.style.display = 'none';
}
}
async function _saveProjectGitConfig() {
const wsId = document.getElementById('projectWsSelect')?.value;
if (!wsId) return;
const remote = document.getElementById('projectGitRemote').value.trim();
const branch = document.getElementById('projectGitBranch').value.trim() || null;
const credId = document.getElementById('projectGitCredential').value || null;
try {
await API.updateWorkspace(wsId, {
git_remote_url: remote || null,
git_branch: branch,
git_credential_id: credId,
});
UI.toast('Git config saved', 'success');
} catch (e) {
UI.toast('Failed to save git config: ' + (e.message || e), 'error');
}
}
// ── Project Files (v0.22.4) ──────────────────
async function _renderProjectFiles(projectId) {
const list = document.getElementById('projectFileList');
if (!list) return;
try {
const resp = await API.projectListFiles(projectId);
const files = resp?.files || [];
if (!files.length) {
list.innerHTML = '<div class="project-panel-empty">No files uploaded</div>';
return;
}
list.innerHTML = files.map(f => {
const size = f.size_bytes > 1024 * 1024
? (f.size_bytes / 1024 / 1024).toFixed(1) + ' MB'
: (f.size_bytes / 1024).toFixed(1) + ' KB';
return `<div class="project-panel-item" style="display:flex;justify-content:space-between;align-items:center">
<span title="${esc(f.filename)}">${esc(f.filename)}</span>
<span class="text-muted" style="font-size:11px">${size}</span>
</div>`;
}).join('');
} catch (e) {
list.innerHTML = '<div class="project-panel-empty">Failed to load files</div>';
}
}
async function _handleProjectFileUpload() {
const input = document.getElementById('projectFileInput');
if (!input.files?.length || !_projectPanelId) return;
for (const file of input.files) {
try {
await API.projectUploadFile(_projectPanelId, file);
UI.toast(`Uploaded ${file.name}`, 'success');
} catch (e) {
UI.toast(`Upload failed: ${e.message || e}`, 'error');
}
}
input.value = '';
await _renderProjectFiles(_projectPanelId);
}
async function showWorkspacePicker(chatId) {
let workspaces = [];
try {