From eeb9f895b48c9f5f17e7b71a9429650f2e4d001b Mon Sep 17 00:00:00 2001 From: Jeffrey Smith Date: Thu, 2 Apr 2026 22:10:17 +0000 Subject: [PATCH] removing select stale packages --- packages/editor/css/main.css | 531 ------------------- packages/editor/js/main.js | 473 ----------------- packages/editor/manifest.json | 19 - packages/git-board/css/main.css | 450 ---------------- packages/git-board/js/main.js | 491 ----------------- packages/git-board/manifest.json | 114 ---- packages/git-board/script.star | 205 ------- packages/gitea-client/manifest.json | 72 --- packages/gitea-client/script.star | 186 ------- packages/gitea-client/star/ci.star | 25 - packages/gitea-client/star/http_helpers.star | 60 --- packages/gitea-client/star/issues.star | 94 ---- packages/gitea-client/star/repos.star | 21 - packages/tasks/README.md | 22 - packages/tasks/css/main.css | 280 ---------- packages/tasks/js/main.js | 397 -------------- packages/tasks/manifest.json | 74 --- packages/tasks/script.star | 263 --------- 18 files changed, 3777 deletions(-) delete mode 100644 packages/editor/css/main.css delete mode 100644 packages/editor/js/main.js delete mode 100644 packages/editor/manifest.json delete mode 100644 packages/git-board/css/main.css delete mode 100644 packages/git-board/js/main.js delete mode 100644 packages/git-board/manifest.json delete mode 100644 packages/git-board/script.star delete mode 100644 packages/gitea-client/manifest.json delete mode 100644 packages/gitea-client/script.star delete mode 100644 packages/gitea-client/star/ci.star delete mode 100644 packages/gitea-client/star/http_helpers.star delete mode 100644 packages/gitea-client/star/issues.star delete mode 100644 packages/gitea-client/star/repos.star delete mode 100644 packages/tasks/README.md delete mode 100644 packages/tasks/css/main.css delete mode 100644 packages/tasks/js/main.js delete mode 100644 packages/tasks/manifest.json delete mode 100644 packages/tasks/script.star diff --git a/packages/editor/css/main.css b/packages/editor/css/main.css deleted file mode 100644 index cdd7782..0000000 --- a/packages/editor/css/main.css +++ /dev/null @@ -1,531 +0,0 @@ -/* ========================================== - Armature — Editor Surface (v0.25.0) - ========================================== - Replaces editor-mode.css for the pane-based editor. - Covers: topbar, bootstrap, and component-specific - overrides within the editor context. - ========================================== */ - -/* ── Surface Shell ─────────────────────────── */ - -.ext-editor { - display: flex; - flex-direction: column; - height: 100%; - overflow: hidden; - background: var(--bg); -} - -/* ── Topbar ────────────────────────────────── */ - -.ext-editor-topbar { - display: flex; - align-items: center; - gap: var(--sp-2); - padding: 0 var(--sp-3); - height: 40px; - flex-shrink: 0; - background: var(--bg-secondary); - border-bottom: 1px solid var(--border); - font-size: 13px; - position: relative; - z-index: 20; -} - -.ext-editor-topbar-back { - display: flex; - align-items: center; - gap: var(--sp-1); - color: var(--text-3); - text-decoration: none; - font-size: 12px; - font-weight: 500; - padding: var(--sp-1) var(--sp-2); - border-radius: var(--radius-sm); - transition: color 0.15s, background 0.15s; -} - -.ext-editor-topbar-back:hover { - color: var(--text); - background: var(--bg-hover); -} - -.ext-editor-topbar-sep { - width: 1px; - height: 18px; - background: var(--border); -} - -.ext-editor-topbar-name { - font-size: 13px; - font-weight: 600; - color: var(--text); -} - -/* ── Workspace Selector ────────────────────── */ - -.ext-editor-ws-selector { - position: relative; -} - -.ext-editor-ws-selector-btn { - display: flex; - align-items: center; - gap: var(--sp-2); - background: none; - border: 1px solid transparent; - color: var(--text); - font-size: 13px; - font-weight: 600; - font-family: inherit; - padding: var(--sp-1) var(--sp-2); - border-radius: var(--radius-sm); - cursor: pointer; - transition: border-color 0.15s, background 0.15s; -} - -.ext-editor-ws-selector-btn:hover { - border-color: var(--border); - background: var(--bg-hover); -} - -.ext-editor-ws-dropdown { - display: none; - position: absolute; - top: 100%; - left: 0; - margin-top: var(--sp-1); - background: var(--bg-secondary); - border: 1px solid var(--border); - border-radius: var(--radius); - min-width: 220px; - max-height: 320px; - overflow-y: auto; - z-index: 1000; - box-shadow: 0 4px 16px rgba(0,0,0,0.4); - padding: var(--sp-1) 0; -} - -.ext-editor-ws-dropdown.open { display: block; } - -.ext-editor-ws-list { - max-height: 240px; - overflow-y: auto; -} - -.ext-editor-ws-dropdown-item { - display: block; - width: 100%; - text-align: left; - background: none; - border: none; - color: var(--text); - font-size: 12px; - font-family: inherit; - padding: var(--sp-2) var(--sp-3); - cursor: pointer; - transition: background 0.1s; -} - -.ext-editor-ws-dropdown-item:hover { - background: var(--bg-hover); -} - -.ext-editor-ws-dropdown-item.active { - color: var(--accent); - font-weight: 600; -} - -.ext-editor-ws-dropdown-divider { - height: 1px; - background: var(--border); - margin: var(--sp-1) 0; -} - -.ext-editor-ws-new { - display: flex; - align-items: center; - gap: var(--sp-2); - color: var(--accent); -} - -.ext-editor-topbar-branch { - display: flex; - align-items: center; - gap: var(--sp-1); - background: var(--purple-dim); - padding: 2px var(--sp-2); - border-radius: var(--radius-sm); -} - -.ext-editor-topbar-branch-text { - font-size: 11px; - font-weight: 600; - color: var(--purple); - font-family: var(--mono, 'SF Mono', monospace); -} - -/* ── Body ──────────────────────────────────── */ - -.ext-editor-body { - flex: 1; - min-height: 0; - overflow: hidden; -} - -/* ── Bootstrap (no workspace) ──────────────── */ - -.ext-editor-bootstrap { - flex: 1; - display: flex; - align-items: center; - justify-content: center; -} - -.ext-editor-bootstrap-card { - text-align: center; - padding: var(--sp-10); - background: var(--bg-secondary); - border: 1px solid var(--border); - border-radius: var(--radius-lg); - max-width: 360px; -} - -.ext-editor-bootstrap-input { - width: 100%; - padding: var(--sp-2) var(--sp-3); - background: var(--bg); - border: 1px solid var(--border); - border-radius: var(--radius); - color: var(--text); - font-size: 13px; - font-family: inherit; - outline: none; - margin-bottom: var(--sp-3); - box-sizing: border-box; -} - -.ext-editor-bootstrap-input:focus { - border-color: var(--accent); -} - -.ext-editor-bootstrap-btn { - width: 100%; - padding: var(--sp-3) var(--sp-4); - background: var(--accent); - color: #fff; - border: none; - border-radius: var(--radius); - font-size: 13px; - font-weight: 600; - font-family: inherit; - cursor: pointer; - transition: opacity 0.15s; -} - -.ext-editor-bootstrap-btn:hover { opacity: 0.9; } -.ext-editor-bootstrap-btn:disabled { opacity: 0.5; cursor: not-allowed; } - -/* Workspace list in bootstrap */ -.ext-editor-bootstrap-ws-item { - display: flex; - align-items: center; - justify-content: space-between; - width: 100%; - padding: var(--sp-3) var(--sp-3); - background: var(--bg); - border: 1px solid var(--border); - border-radius: var(--radius); - color: var(--text); - font-size: 13px; - font-family: inherit; - cursor: pointer; - transition: border-color 0.15s, background 0.15s; - margin-bottom: var(--sp-2); - text-align: left; -} - -.ext-editor-bootstrap-ws-item:hover { - border-color: var(--accent); - background: var(--bg-hover); -} - -.ext-editor-bootstrap-ws-name { - font-weight: 600; -} - -.ext-editor-bootstrap-ws-date { - font-size: 11px; - color: var(--text-3); -} - -/* ── FileTree overrides (in editor context) ── */ - -.ext-editor .file-tree { - height: 100%; - display: flex; - flex-direction: column; - border-right: 1px solid var(--border); -} - -.ext-editor .file-tree-header { - display: flex; - align-items: center; - gap: var(--sp-2); - padding: var(--sp-2) var(--sp-3); - border-bottom: 1px solid var(--border); -} - -.ext-editor .file-tree-title { - font-size: 11px; - font-weight: 600; - color: var(--text-2); - text-transform: uppercase; - letter-spacing: 0.4px; - flex: 1; -} - -.ext-editor .file-tree-items { - flex: 1; - overflow-y: auto; - padding: var(--sp-1) 0; -} - -.ext-editor .file-tree-row { - display: flex; - align-items: center; - gap: var(--sp-1); - padding: 3px var(--sp-2); - cursor: pointer; - font-size: 12px; - color: var(--text-2); - transition: background 0.1s; -} - -.ext-editor .file-tree-row:hover { - background: var(--bg-hover); -} - -.ext-editor .file-tree-row.active { - background: var(--accent-dim); - color: var(--text); -} - -.ext-editor .file-tree-arrow { - width: 12px; - font-size: 10px; - color: var(--text-3); - text-align: center; -} - -.ext-editor .file-tree-icon { - font-size: 13px; - width: 18px; - text-align: center; -} - -.ext-editor .file-tree-name { - flex: 1; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -/* Git status indicators */ -.ext-editor .file-tree-row.git-modified .file-tree-name { color: var(--warning); } -.ext-editor .file-tree-row.git-added .file-tree-name { color: var(--success); } -.ext-editor .file-tree-row.git-untracked .file-tree-name { color: var(--text-3); font-style: italic; } -.ext-editor .file-tree-row.git-deleted .file-tree-name { color: var(--danger); text-decoration: line-through; } - -/* Context menu */ -.ext-editor-file-tree-ctx-menu { - position: fixed; - background: var(--bg-secondary); - border: 1px solid var(--border); - border-radius: var(--radius); - padding: var(--sp-1) 0; - min-width: 120px; - z-index: 1000; - box-shadow: 0 4px 12px rgba(0,0,0,0.4); -} - -.ext-editor-file-tree-ctx-item { - padding: var(--sp-2) var(--sp-3); - font-size: 12px; - color: var(--text); - cursor: pointer; -} - -.ext-editor-file-tree-ctx-item:hover { - background: var(--bg-hover); -} - -/* ── CodeEditor overrides ──────────────────── */ - -.ext-editor .code-editor { - height: 100%; - display: flex; - flex-direction: column; -} - -.ext-editor .code-editor-tabs { - display: flex; - align-items: center; - gap: 0; - background: var(--bg-secondary); - border-bottom: 1px solid var(--border); - height: 32px; - overflow-x: auto; - flex-shrink: 0; -} - -.ext-editor .code-editor-tabs::-webkit-scrollbar { height: 0; } - -.ext-editor .code-editor-tab { - display: flex; - align-items: center; - gap: var(--sp-1); - padding: 0 var(--sp-3); - height: 100%; - font-size: 12px; - color: var(--text-3); - cursor: pointer; - border-right: 1px solid var(--border); - transition: background 0.1s; - white-space: nowrap; -} - -.ext-editor .code-editor-tab:hover { background: var(--bg-hover); } -.ext-editor .code-editor-tab.active { color: var(--text); background: var(--bg); } -.ext-editor .code-editor-tab.modified .code-editor-tab-modified { color: var(--warning); } - -.ext-editor .code-editor-tab-icon { font-size: 12px; } -.ext-editor .code-editor-tab-modified { font-size: 10px; color: var(--text-3); } - -.ext-editor .code-editor-tab-close { - background: none; - border: none; - color: var(--text-3); - font-size: 12px; - cursor: pointer; - padding: 0 2px; - margin-left: var(--sp-1); - border-radius: var(--radius-sm); - line-height: 1; -} - -.ext-editor .code-editor-tab-close:hover { - background: var(--danger-dim); - color: var(--danger); -} - -.ext-editor .code-editor-content { - flex: 1; - min-height: 0; - overflow: hidden; - position: relative; -} - -.ext-editor .code-editor-welcome { - height: 100%; -} - -.ext-editor .code-editor-cm-wrap { - height: 100%; - overflow: auto; -} - -.ext-editor .code-editor-cm-wrap .cm-editor { - height: 100%; -} - -.ext-editor .code-editor-statusbar { - display: flex; - align-items: center; - gap: var(--sp-4); - padding: 0 var(--sp-3); - height: 24px; - flex-shrink: 0; - background: var(--bg-secondary); - border-top: 1px solid var(--border); - font-size: 11px; - color: var(--text-3); - font-family: var(--mono, 'SF Mono', monospace); -} - -.ext-editor .code-editor-textarea-fallback { - width: 100%; - height: 100%; - background: var(--bg); - color: var(--text); - border: none; - padding: var(--sp-3); - font-family: var(--mono, 'SF Mono', monospace); - font-size: 13px; - resize: none; - outline: none; - box-sizing: border-box; -} - -/* ── Tabbed assist pane overrides ──────────── */ - -.ext-editor .pane-tabbed { - border-left: 1px solid var(--border); -} - -/* ChatPane in editor tabbed pane */ -.ext-editor .chat-pane { - position: absolute; - inset: 0; -} - -/* Notes in editor pane */ -.ext-editor .ext-notes-editor { - position: absolute; - inset: 0; - display: flex; - flex-direction: column; - overflow: hidden; -} - -.ext-editor .ext-notes-editor-list-view { - flex: 1; - display: flex; - flex-direction: column; - overflow: hidden; -} - -.ext-editor .ext-notes-list { - flex: 1; - overflow-y: auto; -} - -/* Compact notes toolbar for narrow pane */ -.ext-editor .ext-notes-toolbar { - display: flex; - flex-wrap: wrap; - gap: var(--sp-1); - padding: var(--sp-2) var(--sp-2); - border-bottom: 1px solid var(--border); -} - -.ext-editor .ext-notes-toolbar .sw-btn--sm { - font-size: 11px; - padding: 3px var(--sp-2); -} - -.ext-editor .ext-notes-search-row { - padding: var(--sp-1) var(--sp-2); -} - -.ext-editor .ext-notes-filter-row { - padding: 2px var(--sp-2) var(--sp-1); - display: flex; - gap: var(--sp-1); -} - -.ext-editor .ext-notes-filter-select { - font-size: 11px; - flex: 1; - min-width: 0; -} diff --git a/packages/editor/js/main.js b/packages/editor/js/main.js deleted file mode 100644 index 79a8594..0000000 --- a/packages/editor/js/main.js +++ /dev/null @@ -1,473 +0,0 @@ -// ========================================== -// Armature — Editor Package (v0.31.0) -// ========================================== -// Installable .pkg that provides the code editor surface. -// Mounts into #extension-mount (surface-extension template). -// -// Uses Component.mount() for all sub-components — single source -// of truth for DOM structure. No duplicated template partials. -// -// Dependencies (loaded by base.html): -// FileTree, CodeEditor, ChatPane, NotePanel, note-graph, PaneContainer -// API, UI, App, sw, esc (ui-primitives) -// -// Dynamically loaded: -// codemirror.bundle.js -// ========================================== - -(function () { - 'use strict'; - - const SURFACE_ID = 'editor'; - const PREFIX = 'ed'; - const NOTES_PREFIX = 'edNotes'; - const STATE_DEBOUNCE_MS = 2000; - - if (window.__SURFACE__ !== SURFACE_ID) return; - - const base = window.__BASE__ || ''; - - // ── Dynamic Script Loader ──────────────────── - - function _loadScript(src) { - return new Promise((resolve, reject) => { - if (document.querySelector('script[src*="' + src.split('?')[0] + '"]')) { - resolve(); - return; - } - const s = document.createElement('script'); - s.src = base + src; - s.type = 'module'; - s.onload = resolve; - s.onerror = () => { resolve(); }; // Non-fatal - document.head.appendChild(s); - }); - } - - // ── Init ───────────────────────────────────── - - async function _init() { - const mount = document.getElementById('extension-mount'); - if (!mount) return; - - // Hide server-rendered user menu (we mount our own in the topbar) - const serverMenu = document.getElementById('userMenuWrap'); - if (serverMenu) serverMenu.style.display = 'none'; - - // Wrap in surface container for CSS scoping - const surface = document.createElement('div'); - surface.className = 'ext-editor'; - surface.id = 'editorSurface'; - mount.appendChild(surface); - - // Read workspace ID from query param - const params = new URL(window.location.href).searchParams; - const wsId = params.get('ws') || ''; - - // Load workspace name - let wsName = 'Editor'; - if (wsId && typeof API !== 'undefined') { - try { - const ws = await API._get('/api/v1/workspaces/' + wsId); - wsName = ws?.name || ws?.data?.name || wsName; - } catch (_) {} - } - - // Build topbar - const topbar = _buildTopbar(wsName); - surface.appendChild(topbar); - - // Mount user menu via SDK (flyout drops down from topbar) - if (typeof sw !== 'undefined' && sw.userMenu) { - sw.userMenu(topbar, { flyout: 'down' }); - } - - // Build body + bootstrap - const body = document.createElement('div'); - body.className = 'ext-editor-body'; - body.id = 'editorBody'; - surface.appendChild(body); - - const bootstrap = _buildBootstrap(); - surface.appendChild(bootstrap); - - // Load dynamic dependencies (codemirror only — ChatPane, NotePanel, note-graph are platform scripts in base.html) - const ver = window.__VERSION__ || ''; - const verQ = ver ? '?v=' + ver : ''; - await _loadScript('/vendor/codemirror/codemirror.bundle.js' + verQ); - - _initWsSelector(wsId); - - if (!wsId) { - body.style.display = 'none'; - bootstrap.style.display = ''; - _loadBootstrapList(); - _initBootstrapCreate(); - return; - } - - _mountEditor(wsId, wsName); - } - - // ── Topbar ────────────────────────────────── - - function _buildTopbar(wsName) { - const el = document.createElement('div'); - el.className = 'ext-editor-topbar'; - el.id = 'editorTopbar'; - el.innerHTML = - '' + - '' + - 'Back' + - '' + - '
' + - '' + - '
' + - '' + - '
' + - '
' + - '
' + - '' + - '
' + - '
' + - '' + - '
' + - ''; - return el; - } - - // ── Bootstrap (no workspace) ──────────────── - - function _buildBootstrap() { - const el = document.createElement('div'); - el.className = 'ext-editor-bootstrap'; - el.id = 'editorBootstrap'; - el.style.display = 'none'; - el.innerHTML = - '
' + - '' + - '' + - '' + - '

Open a Workspace

' + - '
' + - '
Loading workspaces\u2026
' + - '
' + - '
' + - '
' + - 'or create new' + - '
' + - '
' + - '' + - '' + - '
'; - return el; - } - - // ── Workspace Selector ────────────────────── - - function _initWsSelector(currentWsId) { - const btn = document.getElementById('editorWsSelectorBtn'); - const dropdown = document.getElementById('editorWsDropdown'); - if (!btn || !dropdown) return; - - btn.addEventListener('click', (e) => { - e.stopPropagation(); - if (dropdown.classList.toggle('open')) _loadWsDropdown(currentWsId); - }); - document.addEventListener('click', (e) => { - if (!e.target.closest('#editorWsSelector')) dropdown.classList.remove('open'); - }); - document.getElementById('editorWsNewBtn')?.addEventListener('click', async () => { - dropdown.classList.remove('open'); - const name = window.prompt('Workspace name:'); - if (!name) return; - try { - const userId = sw.user?.id; - if (!userId) throw new Error('Not authenticated'); - const resp = await API.createWorkspace({ name: name.trim(), owner_type: 'user', owner_id: userId }); - const newId = resp.id || resp.data?.id; - if (newId) window.location.href = base + '/s/editor?ws=' + newId; - } catch (e) { - if (typeof UI !== 'undefined') UI.toast('Failed: ' + e.message, 'error'); - } - }); - } - - async function _loadWsDropdown(currentWsId) { - const listEl = document.getElementById('editorWsList'); - if (!listEl) return; - listEl.innerHTML = '
Loading\u2026
'; - try { - const resp = await API._get('/api/v1/workspaces'); - const workspaces = resp.data || resp || []; - listEl.innerHTML = ''; - if (!workspaces.length) { - listEl.innerHTML = '
No workspaces
'; - return; - } - workspaces.forEach(ws => { - const item = document.createElement('button'); - item.className = 'ext-editor-ws-dropdown-item' + (ws.id === currentWsId ? ' active' : ''); - item.textContent = ws.name || ws.id?.slice(0, 8); - item.addEventListener('click', () => { window.location.href = base + '/s/editor?ws=' + ws.id; }); - listEl.appendChild(item); - }); - } catch (_) { - listEl.innerHTML = '
Failed to load
'; - } - } - - // ── Bootstrap ──────────────────────────────── - - async function _loadBootstrapList() { - const listEl = document.getElementById('editorBootstrapList'); - if (!listEl) return; - try { - const resp = await API._get('/api/v1/workspaces'); - const workspaces = resp.data || resp || []; - if (!workspaces.length) { - listEl.innerHTML = '
No workspaces yet
'; - return; - } - listEl.innerHTML = ''; - workspaces.forEach(ws => { - const item = document.createElement('button'); - item.className = 'ext-editor-bootstrap-ws-item'; - item.innerHTML = - '' + esc(ws.name || ws.id?.slice(0, 8)) + '' + - '' + esc(ws.created_at ? new Date(ws.created_at).toLocaleDateString() : '') + ''; - item.addEventListener('click', () => { window.location.href = base + '/s/editor?ws=' + ws.id; }); - listEl.appendChild(item); - }); - } catch (_) { - listEl.innerHTML = '
Failed to load workspaces
'; - } - } - - function _initBootstrapCreate() { - const btn = document.getElementById('editorBootstrapBtn'); - const input = document.getElementById('editorBootstrapName'); - if (!btn || !input) return; - btn.addEventListener('click', async () => { - const name = input.value.trim() || 'workspace'; - btn.disabled = true; - btn.textContent = 'Creating\u2026'; - try { - const userId = sw.user?.id; - if (!userId) throw new Error('Not authenticated'); - const resp = await API.createWorkspace({ name, owner_type: 'user', owner_id: userId }); - const newId = resp.id || resp.data?.id; - if (!newId) throw new Error('No workspace ID returned'); - window.location.href = base + '/s/editor?ws=' + newId; - } catch (e) { - btn.disabled = false; - btn.textContent = 'Create Workspace'; - if (typeof UI !== 'undefined') UI.toast('Failed: ' + e.message, 'error'); - } - }); - } - - // ── Mount Pane Layout ─────────────────────── - - function _mountEditor(wsId, wsName) { - const body = document.getElementById('editorBody'); - if (!body) return; - - // ── Layout via SDK ── - const layout = sw.layout(body, 'editor', { workspaceId: wsId }); - if (!layout) return; - - const filesPaneEl = layout._panes.get('files')?.el; - const editorPaneEl = layout._panes.get('editor')?.el; - const assistPaneInfo = layout._panes.get('assist'); - - // ── FileTree via SDK ── - let fileTree; - if (filesPaneEl) { - fileTree = sw.fileTree(filesPaneEl, { - id: PREFIX, workspaceId: wsId, - onSelect: (path) => { codeEditor?.openFile(path); _saveState(wsId, codeEditor); }, - onDelete: (path) => _deleteFile(wsId, path, fileTree, codeEditor), - onNewFile: () => _createNewFile(wsId, fileTree), - }); - layout._panes.get('files').component = fileTree; - } - - // ── CodeEditor via SDK ── - let codeEditor; - if (editorPaneEl) { - codeEditor = sw.codeEditor(editorPaneEl, { - id: PREFIX, workspaceId: wsId, - onSave: () => fileTree?.refresh(), - onActivate: (path) => { fileTree?.setActiveFile(path); _saveState(wsId, codeEditor); }, - }); - layout._panes.get('editor').component = codeEditor; - } - - // ── Assist pane (tabbed: chat + notes) via SDK ── - if (assistPaneInfo?.tabs) { - // Chat tab — standalone mode handles everything (streaming, model selector, history) - const chatPanel = assistPaneInfo.getTabPanel('chat'); - if (chatPanel) { - const chatPane = sw.chat(chatPanel, { - id: PREFIX, - standalone: true, - getContext: () => _getFileContext(codeEditor), - }); - if (chatPane) { - const chatTab = assistPaneInfo.tabs.find(t => t.id === 'chat'); - if (chatTab) chatTab.instance = chatPane; - } - } - - // Notes tab - const notesPanel = assistPaneInfo.getTabPanel('notes'); - if (notesPanel) { - const notePanel = sw.notes(notesPanel, { projectId: null }); - if (notePanel) { - const notesTab = assistPaneInfo.tabs.find(t => t.id === 'notes'); - if (notesTab) { - notesTab.instance = notePanel; - const _loadNotes = () => { - if (!notesTab._loaded) { - notesTab._loaded = true; - notePanel.loadNotesList(); - notePanel.loadNoteFolders(); - } - }; - notesTab.btn.addEventListener('click', _loadNotes); - if (notesTab._activated) _loadNotes(); - } - } - } - } - - // Git branch - _refreshGitBranch(wsId, codeEditor); - - // Toolbar - document.getElementById('editorRefreshBtn')?.addEventListener('click', () => { - fileTree?.refresh(); - _refreshGitBranch(wsId, codeEditor); - }); - - // Initial load - fileTree?.refresh(); - _restoreState(wsId, codeEditor); - - console.log('[EditorPkg] Mounted for workspace', wsId); - } - - // ── File Operations ───────────────────────── - - async function _deleteFile(wsId, path, fileTree, codeEditor) { - const ok = typeof sw !== 'undefined' && sw.confirm - ? await sw.confirm('Delete ' + path + '?', { destructive: true }) - : window.confirm('Delete ' + path + '?'); - if (!ok) return; - try { - await API.deleteWorkspaceFile(wsId, path); - if (codeEditor?.getOpenFiles().includes(path)) await codeEditor.closeFile(path); - fileTree?.refresh(); - } catch (e) { - if (typeof UI !== 'undefined') UI.toast('Delete failed: ' + e.message, 'error'); - } - } - - async function _createNewFile(wsId, fileTree) { - const name = window.prompt('File name (e.g. src/main.go):'); - if (!name) return; - try { - await API.writeWorkspaceFile(wsId, name.trim(), ''); - fileTree?.refresh(); - if (typeof UI !== 'undefined') UI.toast('Created ' + name.trim(), 'success'); - } catch (e) { - if (typeof UI !== 'undefined') UI.toast('Failed: ' + e.message, 'error'); - } - } - - async function _refreshGitBranch(wsId, codeEditor) { - try { - const resp = await API.getWorkspaceGitBranches(wsId); - const branch = resp.current || null; - if (branch) { - const badge = document.getElementById('editorBranchBadge'); - const name = document.getElementById('editorBranchName'); - if (badge) badge.style.display = ''; - if (name) name.textContent = branch; - } - if (codeEditor) codeEditor.setBranch(branch); - } catch (_) {} - } - - // ── File Context (editor-specific, passed to ChatPane via getContext) ── - - function _getFileContext(editor) { - if (!editor) return null; - try { - const openFiles = editor.getOpenFiles(); - if (!openFiles.length) return null; - const activeTab = document.querySelector('.code-editor-tab.active'); - const path = activeTab?.dataset?.path || openFiles[0]; - const inst = CodeEditor._instances?.get(PREFIX); - if (inst?._files) { - const file = inst._files.get(path); - if (file?.view) return { path, content: file.view.state.doc.toString().slice(0, 4000) }; - } - } catch (_) {} - return null; - } - - // ── State Persistence (localStorage) ───────── - - const STATE_KEY_PREFIX = 'sb:editor:state:'; - let _stateSaveTimer = null; - - function _stateKey(wsId) { - return STATE_KEY_PREFIX + (sw?.user?.id || '') + ':' + wsId; - } - - function _restoreState(wsId, codeEditor) { - if (!wsId) return; - try { - const raw = localStorage.getItem(_stateKey(wsId)); - if (!raw) return; - const state = JSON.parse(raw); - if (state.open_tabs && Array.isArray(state.open_tabs)) { - for (const path of state.open_tabs) codeEditor.openFile(path); - if (state.active_tab) codeEditor.activateFile(state.active_tab); - } - } catch (_) {} - } - - function _saveState(wsId, codeEditor) { - if (_stateSaveTimer) clearTimeout(_stateSaveTimer); - _stateSaveTimer = setTimeout(() => { - if (!wsId) return; - try { - const openFiles = codeEditor.getOpenFiles(); - const activeTab = document.querySelector('.code-editor-tab.active'); - localStorage.setItem(_stateKey(wsId), JSON.stringify({ - open_tabs: openFiles, - active_tab: activeTab?.dataset?.path || '', - updated_at: new Date().toISOString(), - })); - } catch (_) {} - }, STATE_DEBOUNCE_MS); - } - - // ── Boot ───────────────────────────────────── - - document.addEventListener('DOMContentLoaded', _init); -})(); diff --git a/packages/editor/manifest.json b/packages/editor/manifest.json deleted file mode 100644 index a641068..0000000 --- a/packages/editor/manifest.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "id": "editor", - "title": "Editor", - "type": "full", - "version": "0.31.0", - "tier": "browser", - "author": "Armature", - "icon": "✏️", - "description": "Code editor with workspace management, file tree, and AI assist (requires legacy sw.* SDK — dormant until rewritten)", - "requires": ["legacy-sdk"], - "route": "/s/editor", - "layout": "editor", - "permissions": [], - "settings": [ - { "key": "font_size", "label": "Font Size", "type": "number", "default": 13 }, - { "key": "tab_size", "label": "Tab Size", "type": "number", "default": 4 }, - { "key": "word_wrap", "label": "Word Wrap", "type": "boolean", "default": false } - ] -} diff --git a/packages/git-board/css/main.css b/packages/git-board/css/main.css deleted file mode 100644 index 4fa0cfa..0000000 --- a/packages/git-board/css/main.css +++ /dev/null @@ -1,450 +0,0 @@ -/* Git Board — Surface Styles */ - -.ext-git-board-shell { - display: flex; - flex-direction: column; - height: 100%; - overflow: hidden; -} - -/* ── Header ──────────────────────────────── */ - -.ext-git-board-header { - display: flex; - align-items: center; - justify-content: space-between; - padding: var(--sp-3) var(--sp-4); - border-bottom: 1px solid var(--border); - flex-shrink: 0; -} - -.ext-git-board-header__left, -.ext-git-board-header__right { - display: flex; - align-items: center; - gap: var(--sp-3); -} - -.ext-git-board-title { - font-size: 18px; - font-weight: 600; - color: var(--text); - margin: 0; -} - -.ext-git-board-repo-picker { - background: var(--bg-raised); - border: 1px solid var(--border); - border-radius: var(--radius); - color: var(--text); - font-family: var(--mono); - font-size: 13px; - padding: 5px var(--sp-2); - max-width: 260px; -} - -/* ── Connection Setup ─────────────────────── */ - -.ext-git-board-setup { - max-width: 480px; - margin: 60px auto; - text-align: center; - padding: 0 var(--sp-4); -} - -.ext-git-board-setup h2 { - color: var(--text); - font-size: 20px; - margin: 0 0 var(--sp-2); -} - -.ext-git-board-setup p { - color: var(--text-2); - font-size: 14px; - line-height: 1.5; - margin: 0 0 var(--sp-5); -} - - -.ext-git-board-setup__hint { - font-size: 12px; - color: var(--text-3); -} - -/* ── Kanban Board ────────────────────────── */ - -.ext-git-board-board { - display: flex; - gap: var(--sp-3); - padding: var(--sp-3) var(--sp-4); - flex: 1; - overflow-x: auto; - overflow-y: hidden; -} - -.ext-git-board-column { - min-width: 260px; - max-width: 320px; - flex: 1; - display: flex; - flex-direction: column; - background: var(--bg-surface); - border: 1px solid var(--border); - border-radius: var(--radius-lg); - overflow: hidden; -} - -.ext-git-board-column__header { - display: flex; - align-items: center; - justify-content: space-between; - padding: var(--sp-3) var(--sp-3); - border-bottom: 1px solid var(--border); -} - -.ext-git-board-column__title { - font-size: 13px; - font-weight: 600; - color: var(--text); - text-transform: uppercase; - letter-spacing: 0.03em; -} - -.ext-git-board-column__count { - background: var(--bg-raised); - color: var(--text-2); - font-size: 11px; - font-weight: 600; - padding: 2px 7px; - border-radius: var(--radius-lg); -} - -.ext-git-board-column__cards { - flex: 1; - overflow-y: auto; - padding: var(--sp-2); - display: flex; - flex-direction: column; - gap: var(--sp-2); -} - -/* ── Cards ───────────────────────────────── */ - -.ext-git-board-card { - display: block; - background: var(--bg); - border: 1px solid var(--border); - border-radius: var(--radius); - padding: var(--sp-3); - text-decoration: none; - color: var(--text); - transition: border-color var(--transition), background var(--transition); - cursor: pointer; -} - -.ext-git-board-card:hover { - border-color: var(--accent); - background: var(--bg-hover); -} - -.ext-git-board-card--pr { - border-left: 3px solid var(--accent); -} - -.ext-git-board-card__header { - display: flex; - align-items: center; - gap: var(--sp-2); - margin-bottom: var(--sp-1); -} - -.ext-git-board-card__number { - font-family: var(--mono); - font-size: 12px; - color: var(--text-3); -} - -.ext-git-board-card__assignee { - font-size: 11px; - color: var(--accent); - margin-left: auto; -} - -.ext-git-board-card__branch { - font-family: var(--mono); - font-size: 11px; - color: var(--text-3); - margin-left: auto; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - max-width: 150px; -} - -.ext-git-board-card__title { - font-size: 13px; - font-weight: 500; - line-height: 1.4; - color: var(--text); -} - -.ext-git-board-card__labels { - display: flex; - flex-wrap: wrap; - gap: var(--sp-1); - margin-top: var(--sp-2); -} - -.ext-git-board-card__labels .ext-git-board-badge { - font-size: 10px; - padding: 1px var(--sp-2); -} - -.ext-git-board-card__meta { - display: flex; - justify-content: space-between; - margin-top: var(--sp-2); - font-size: 11px; - color: var(--text-3); -} - -/* ── DnD States ─────────────────────────── */ - -.ext-git-board-card[draggable="true"] { - cursor: grab; - user-select: none; -} -.ext-git-board-card[draggable="true"]:active { - cursor: grabbing; - opacity: 0.6; -} -.ext-git-board-column--dragover { - border-color: var(--accent); - background: color-mix(in srgb, var(--accent) 6%, var(--bg-surface)); -} -.ext-git-board-column--dragover .ext-git-board-column__header { - border-bottom-color: var(--accent); -} - -/* ── Empty state ─────────────────────────── */ - -.ext-git-board-empty { - display: flex; - align-items: center; - justify-content: center; - flex: 1; - color: var(--text-3); - font-size: 14px; -} - -/* ── Issue Detail Modal ─────────────────── */ - -.ext-git-board-modal-overlay { - position: fixed; - inset: 0; - background: rgba(0,0,0,0.5); - z-index: 1000; - display: flex; - align-items: flex-start; - justify-content: center; - padding: var(--sp-10) var(--sp-4); - overflow-y: auto; -} - -.ext-git-board-modal { - background: var(--bg); - border: 1px solid var(--border); - border-radius: var(--radius-lg); - width: 100%; - max-width: 680px; - max-height: calc(100vh - 80px); - max-height: calc(100dvh - 80px); - display: flex; - flex-direction: column; - box-shadow: 0 8px 32px rgba(0,0,0,0.3); -} - -.ext-git-board-modal__header { - display: flex; - align-items: flex-start; - justify-content: space-between; - padding: var(--sp-4) var(--sp-5); - border-bottom: 1px solid var(--border); - flex-shrink: 0; -} - -.ext-git-board-modal__title-row { - display: flex; - align-items: baseline; - gap: var(--sp-2); - flex: 1; - min-width: 0; -} - -.ext-git-board-modal__title { - font-size: 18px; - font-weight: 600; - color: var(--text); - margin: 0; - word-break: break-word; -} - -.ext-git-board-modal__close { - background: none; - border: none; - color: var(--text-3); - font-size: 18px; - cursor: pointer; - padding: 2px var(--sp-2); - border-radius: var(--radius); - flex-shrink: 0; -} -.ext-git-board-modal__close:hover { - color: var(--text); - background: var(--bg-hover); -} - -.ext-git-board-modal__body { - overflow-y: auto; - padding: var(--sp-4) var(--sp-5); - flex: 1; - min-height: 0; -} - -.ext-git-board-modal__meta { - display: flex; - align-items: center; - gap: var(--sp-3); - flex-wrap: wrap; - margin-bottom: var(--sp-3); - font-size: 12px; - color: var(--text-2); -} - -.ext-git-board-modal__date { - color: var(--text-3); -} - -.ext-git-board-modal__extlink { - margin-left: auto; - color: var(--accent); - text-decoration: none; - font-size: 12px; -} -.ext-git-board-modal__extlink:hover { - text-decoration: underline; -} - -.ext-git-board-modal__description { - margin-bottom: var(--sp-5); - padding-bottom: var(--sp-4); - border-bottom: 1px solid var(--border); -} - -.ext-git-board-modal__body-text { - font-family: var(--font); - font-size: 13px; - line-height: 1.6; - color: var(--text); - white-space: pre-wrap; - word-break: break-word; - margin: 0; - background: none; - border: none; - padding: 0; -} - -.ext-git-board-modal__empty { - color: var(--text-3); - font-size: 13px; - font-style: italic; - margin: 0; -} - -.ext-git-board-modal__section-title { - font-size: 13px; - font-weight: 600; - color: var(--text-2); - text-transform: uppercase; - letter-spacing: 0.03em; - margin: 0 0 var(--sp-3); -} - -/* ── Comments ────────────────────────────── */ - -.ext-git-board-comment { - padding: var(--sp-3) 0; - border-bottom: 1px solid var(--border); -} -.ext-git-board-comment:last-child { - border-bottom: none; -} - -.ext-git-board-comment__header { - display: flex; - align-items: baseline; - gap: var(--sp-2); - margin-bottom: var(--sp-1); - font-size: 12px; -} -.ext-git-board-comment__header strong { - color: var(--accent); -} - -.ext-git-board-comment__date { - color: var(--text-3); - font-size: 11px; -} - -.ext-git-board-comment__body { - font-size: 13px; - line-height: 1.5; - color: var(--text); - white-space: pre-wrap; - word-break: break-word; -} - -/* ── Add Comment ─────────────────────────── */ - -.ext-git-board-modal__add-comment { - margin-top: var(--sp-4); - padding-top: var(--sp-4); - border-top: 1px solid var(--border); -} - -.ext-git-board-modal__textarea { - width: 100%; - background: var(--bg-raised); - border: 1px solid var(--border); - border-radius: var(--radius); - color: var(--text); - font-family: var(--font); - font-size: 13px; - padding: var(--sp-2) var(--sp-3); - resize: vertical; - outline: none; - box-sizing: border-box; -} -.ext-git-board-modal__textarea:focus { - border-color: var(--accent); -} - -.ext-git-board-modal__actions { - display: flex; - gap: var(--sp-2); - margin-top: var(--sp-2); - justify-content: flex-end; -} - -/* ── Badge variants ──────────────────────── */ - -.ext-git-board-badge--green { - background: var(--green); - color: #fff; -} -.ext-git-board-badge--muted { - background: var(--bg-raised); - color: var(--text-3); -} - -/* git-board danger button override — uses kernel .sw-btn--danger */ diff --git a/packages/git-board/js/main.js b/packages/git-board/js/main.js deleted file mode 100644 index 15f5131..0000000 --- a/packages/git-board/js/main.js +++ /dev/null @@ -1,491 +0,0 @@ -/** - * Git Board — Surface Entry Point - * - * Kanban board for Gitea issues and PRs. - * Calls Starlark backend via /s/git-board/api/*. - * Authentication via gitea-client library connections. - * - * Features: DnD between columns, issue detail modal with comments. - */ -(async function () { - 'use strict'; - - var mount = document.getElementById('extension-mount'); - if (!mount) return; - - var base = window.__BASE__ || ''; - var ver = window.__VERSION__ || '0'; - var slug = 'git-board'; - - // ── Boot SDK ─────────────────────────────── - try { - if (!window.preact) { - var { h, render } = await import(base + '/js/sw/vendor/preact.module.js'); - var hooksModule = await import(base + '/js/sw/vendor/hooks.module.js'); - var htmModule = await import(base + '/js/sw/vendor/htm.module.js'); - window.preact = { h, render }; - window.hooks = hooksModule; - window.html = htmModule.default.bind(h); - } - var sdk = await import(base + '/js/sw/sdk/index.js?v=' + ver); - await sdk.boot(); - } catch (e) { - mount.innerHTML = '

SDK boot failed: ' + e.message + '

'; - return; - } - - var { html } = window; - var { useState, useEffect, useCallback, useRef, useMemo } = hooks; - var { render } = preact; - - var API = base + '/s/' + slug + '/api'; - - async function api(path, opts) { - var token = sw.auth._getToken(); - var fetchOpts = { headers: { 'Authorization': 'Bearer ' + token } }; - if (opts && opts.method) fetchOpts.method = opts.method; - if (opts && opts.body) { - fetchOpts.body = JSON.stringify(opts.body); - fetchOpts.headers['Content-Type'] = 'application/json'; - } - var resp = await fetch(API + path, fetchOpts); - var text = await resp.text(); - try { return JSON.parse(text); } catch (_) { return { error: text }; } - } - - // ── App ──────────────────────────────────── - - function App() { - var [owner, setOwner] = useState(''); - var [repo, setRepo] = useState(''); - var [repos, setRepos] = useState([]); - var [board, setBoard] = useState(null); - var [loading, setLoading] = useState(false); - var [needsConn, setNeedsConn] = useState(false); - var [modal, setModal] = useState(null); // {owner, repo, number} - var menuRef = useRef(null); - - useEffect(function () { - if (menuRef.current && sw.userMenu) { - sw.userMenu(menuRef.current, { placement: 'down-left' }); - } - }, []); - - useEffect(function () { - api('/repos').then(function (d) { - if (d.error) { - if (d.error.indexOf('no gitea connection configured') !== -1) { - setNeedsConn(true); - } else { sw.toast(d.error, 'error'); } - return; - } - setRepos(d.data || []); - if (d.data && d.data.length > 0 && !owner) { - setOwner(d.data[0].owner); - setRepo(d.data[0].name); - } - }); - }, []); - - var loadBoard = useCallback(function () { - if (!owner || !repo) return; - setLoading(true); - api('/board?owner=' + encodeURIComponent(owner) + '&repo=' + encodeURIComponent(repo)) - .then(function (d) { - if (d.error) { - sw.toast(d.error, 'error'); - if (d.error.indexOf('no gitea connection configured') !== -1) setNeedsConn(true); - } else { - setBoard(d); - setNeedsConn(false); - } - setLoading(false); - }); - }, [owner, repo]); - - useEffect(function () { loadBoard(); }, [loadBoard]); - - var onDrop = useCallback(function (issueNumber, targetCol) { - if (!board) return; - var issue = (board.issues || []).find(function (i) { return i.number === issueNumber; }); - if (!issue) return; - - var patch = {}; - if (targetCol === 'open') { - // Move to Open: unassign + reopen - if (issue.state === 'closed') patch.state = 'open'; - // Note: Gitea PATCH /issues doesn't support clearing assignee via empty string, - // but we do our best — the board will re-split on refresh. - } else if (targetCol === 'in_progress') { - // Move to In Progress: assign to current user + ensure open - if (issue.state === 'closed') patch.state = 'open'; - // Gitea needs assignees array — not supported by our simple update_issue. - // For now, just reopen. User assigns via modal. - if (issue.state === 'closed') patch.state = 'open'; - } else if (targetCol === 'done') { - patch.state = 'closed'; - } - - if (!patch.state) return; // no meaningful change - - // Optimistic update - var newIssues = (board.issues || []).map(function (i) { - if (i.number !== issueNumber) return i; - var copy = {}; - for (var k in i) copy[k] = i[k]; - if (patch.state) copy.state = patch.state; - return copy; - }); - setBoard({ issues: newIssues, pull_requests: board.pull_requests || [] }); - - api('/issue/' + owner + '/' + repo + '/' + issueNumber, { - method: 'POST', body: patch - }).then(function (d) { - if (d.error) { - sw.toast('Update failed: ' + d.error, 'error'); - loadBoard(); // revert - } - }); - }, [board, owner, repo, loadBoard]); - - var openModal = useCallback(function (number) { - setModal({ owner: owner, repo: repo, number: number }); - }, [owner, repo]); - - var closeModal = useCallback(function (changed) { - setModal(null); - if (changed) loadBoard(); - }, [loadBoard]); - - return html` -
- ${needsConn ? html`<${ConnectionSetup} />` : html` -
-
-
-

Git Board

- <${RepoPicker} repos=${repos} owner=${owner} repo=${repo} - onSelect=${function (o, r) { setOwner(o); setRepo(r); }} /> -
-
- -
-
- ${board ? html`<${Board} data=${board} onDrop=${onDrop} onCardClick=${openModal} />` : html` -
${loading ? 'Loading…' : 'Select a repository'}
- `} -
- `} - ${modal && html`<${IssueModal} ...${modal} onClose=${closeModal} />`} - `; - } - - // ── Connection Setup ───────────────────────── - - function ConnectionSetup() { - return html` -
-
-

Git Board

-
-
-

Connect to Gitea

-

Git Board requires a Gitea connection to fetch repositories, issues, and pull requests.

-

Ask your admin to add a Gitea connection in - Admin → Connections, or add a personal one in - Settings → Connections.

- -

Connections are managed centrally — no API tokens in extension settings.

-
-
- `; - } - - // ── Repo Picker ──────────────────────────── - - function RepoPicker({ repos, owner, repo, onSelect }) { - var current = owner + '/' + repo; - return html` - - `; - } - - // ── Kanban Board with DnD ───────────────── - - function Board({ data, onDrop, onCardClick }) { - var issues = data.issues || []; - var prs = data.pull_requests || []; - - var openUnassigned = issues.filter(function (i) { return i.state === 'open' && !i.assignee; }); - var inProgress = issues.filter(function (i) { return i.state === 'open' && !!i.assignee; }); - - return html` -
- <${Column} id="open" title="Open" count=${openUnassigned.length} onDrop=${onDrop}> - ${openUnassigned.map(function (i) { - return html`<${IssueCard} key=${i.number} issue=${i} onClick=${onCardClick} />`; - })} - - <${Column} id="in_progress" title="In Progress" count=${inProgress.length} onDrop=${onDrop}> - ${inProgress.map(function (i) { - return html`<${IssueCard} key=${i.number} issue=${i} onClick=${onCardClick} />`; - })} - - <${Column} id="done" title="Done" count=${0} onDrop=${onDrop}> - - <${Column} id="prs" title="Pull Requests" count=${prs.length}> - ${prs.map(function (p) { return html`<${PRCard} key=${p.number} pr=${p} />`; })} - -
- `; - } - - function Column({ id, title, count, children, onDrop }) { - var [over, setOver] = useState(false); - - var handlers = onDrop ? { - onDragOver: function (e) { e.preventDefault(); setOver(true); }, - onDragLeave: function () { setOver(false); }, - onDrop: function (e) { - e.preventDefault(); - setOver(false); - var num = parseInt(e.dataTransfer.getData('text/plain'), 10); - if (num && onDrop) onDrop(num, id); - } - } : {}; - - return html` -
-
- ${title} - ${count || 0} -
-
${children}
-
- `; - } - - function IssueCard({ issue, onClick }) { - return html` -
-
- #${issue.number} - ${issue.assignee && html`@${esc(issue.assignee)}`} -
-
${esc(issue.title)}
-
- ${(issue.labels || []).map(function (l) { - return html`${esc(l)}`; - })} -
-
- `; - } - - function PRCard({ pr }) { - return html` - -
- #${pr.number} - ${esc(pr.head)} → ${esc(pr.base)} -
-
${esc(pr.title)}
-
- @${esc(pr.user)} - ${timeAgo(pr.created_at)} -
-
- `; - } - - // ── Issue Detail Modal ──────────────────── - - function IssueModal({ owner, repo, number, onClose }) { - var [issue, setIssue] = useState(null); - var [loading, setLoading] = useState(true); - var [comment, setComment] = useState(''); - var [posting, setPosting] = useState(false); - var [changed, setChanged] = useState(false); - var bodyRef = useRef(null); - - useEffect(function () { - setLoading(true); - api('/issue/' + owner + '/' + repo + '/' + number).then(function (d) { - if (d.error) { sw.toast(d.error, 'error'); onClose(false); return; } - setIssue(d); - setLoading(false); - }); - }, [owner, repo, number]); - - // Close on Escape - useEffect(function () { - var handler = function (e) { if (e.key === 'Escape') onClose(changed); }; - document.addEventListener('keydown', handler); - return function () { document.removeEventListener('keydown', handler); }; - }, [changed]); - - var postComment = useCallback(function () { - if (!comment.trim() || posting) return; - setPosting(true); - api('/issue/' + owner + '/' + repo + '/' + number + '/comment', { - method: 'POST', body: { body: comment.trim() } - }).then(function (d) { - if (d.error) { sw.toast(d.error, 'error'); } - else { - // Append to local comments - setIssue(function (prev) { - if (!prev) return prev; - var copy = {}; - for (var k in prev) copy[k] = prev[k]; - copy.comments = (prev.comments || []).concat([d]); - return copy; - }); - setComment(''); - setChanged(true); - } - setPosting(false); - }); - }, [comment, posting, owner, repo, number]); - - var toggleState = useCallback(function () { - if (!issue) return; - var newState = issue.state === 'open' ? 'closed' : 'open'; - api('/issue/' + owner + '/' + repo + '/' + number, { - method: 'POST', body: { state: newState } - }).then(function (d) { - if (d.error) { sw.toast(d.error, 'error'); return; } - setIssue(function (prev) { - if (!prev) return prev; - var copy = {}; - for (var k in prev) copy[k] = prev[k]; - copy.state = newState; - return copy; - }); - setChanged(true); - }); - }, [issue, owner, repo, number]); - - return html` -
-
-
-
- ${loading ? 'Loading…' : html` - #${number} -

${esc(issue && issue.title)}

- `} -
- -
- - ${!loading && issue && html` -
-
- ${issue.state} - ${issue.assignee && html`@${esc(issue.assignee)}`} - ${issue.created_at && html`${new Date(issue.created_at).toLocaleDateString()}`} - Open in Gitea ↗ -
- - ${issue.labels && issue.labels.length > 0 && html` -
- ${issue.labels.map(function (l) { return html`${esc(l)}`; })} -
- `} - -
- ${issue.body ? html`
${esc(issue.body)}
` - : html`

No description.

`} -
- -
-

Comments (${(issue.comments || []).length})

- ${(issue.comments || []).length === 0 && html` -

No comments yet.

- `} - ${(issue.comments || []).map(function (c, i) { - return html` -
-
- @${esc(c.user)} - ${timeAgo(c.created_at)} -
-
${esc(c.body)}
-
- `; - })} -
- -
-