diff --git a/src/main.js b/src/main.js index 86a4a2b..db4d467 100644 --- a/src/main.js +++ b/src/main.js @@ -15,6 +15,27 @@ const { simpleParser } = require('mailparser'); const WORDPRESS_ZIP_URL = 'https://github.com/WordPress/wordpress-develop/archive/refs/heads/trunk.zip'; const WORDPRESS_GIT_URL = 'https://github.com/WordPress/wordpress-develop.git'; +const GITHUB_CLIENT_ID = '05eaaa972c8117f72465'; // GitHub OAuth App Client ID for device flow + +// GitHub authentication state +let githubToken = null; + +// Load GitHub token from store on startup +async function loadGitHubToken() { + const s = await getStore(); + githubToken = s.get('githubToken') || null; +} + +// Save GitHub token to store +async function saveGitHubToken(token) { + githubToken = token; + const s = await getStore(); + if (token === null || token === undefined) { + s.delete('githubToken'); + } else { + s.set('githubToken', token); + } +} // Provide a PATH shim so npm's spawned scripts can find a 'node' binary that maps to Electron's Node let nodeShimDir = null; @@ -99,6 +120,8 @@ const wpDebugWatchers = {}; const smtpServers = {}; /** @type {{ child: import('child_process').ChildProcess, url?: string } | null */ let playgroundWebServer = null; +/** @type {Map} */ +const submitPrAbortStates = new Map(); function smtpStoreKey(sitePath) { return `siteMail:${sitePath}`; @@ -222,9 +245,9 @@ async function stopSmtpServerForSite(sitePath) { } function createWindow() { - const mainWindow = new BrowserWindow({ - width: 1000, - height: 700, + const mainWindow = new BrowserWindow({ + width: 1440, + height: 960, icon: process.platform === 'linux' ? path.join(__dirname, '..', 'build', 'icon.png') : undefined, webPreferences: { preload: path.join(__dirname, 'preload.js'), @@ -250,46 +273,472 @@ function buildPatchHtml(content) { } async function createMinimalPatchForDir(dir) { - // Ensure we have origin/trunk and HEAD reference - try { await git.resolveRef({ fs, dir, ref: 'refs/remotes/origin/trunk' }); } - catch { await git.fetch({ fs, http, dir, url: WORDPRESS_GIT_URL, depth: 1, singleBranch: true, ref: 'trunk' }); } - let headOid = null; - try { headOid = await git.resolveRef({ fs, dir, ref: 'HEAD' }); } catch {} - if (!headOid) { - // fallback to local trunk if HEAD missing - try { headOid = await git.resolveRef({ fs, dir, ref: 'refs/heads/trunk' }); } catch {} - } + let baseOid = null; + let baseRef = null; - // Add untracked files to the index (except those in .gitignore) - const matrix = await git.statusMatrix({ fs, dir }); - for (const [filepath, head, workdir, stage] of matrix) { - // If file is untracked (head=0, workdir=2, stage=0) - if (head === 0 && workdir === 2 && stage === 0) { - try { - await git.add({ fs, dir, filepath }); - } catch (e) { - // Ignore errors for files that can't be added (e.g., in .gitignore) + const tryResolve = async (ref) => { + try { + const oid = await git.resolveRef({ fs, dir, ref }); + return oid; + } catch { + return null; + } + }; + + const candidateRefs = ['trunk', 'refs/heads/trunk', 'HEAD']; + for (const ref of candidateRefs) { + if (baseOid) break; + const oid = await tryResolve(ref); + if (oid) { + baseOid = oid; + baseRef = ref; + } + } + + if (!baseOid) { + throw new Error('Unable to resolve local trunk for diff generation.'); + } + + // Add untracked files to the index (except those in .gitignore) + const statusOpts = baseRef ? { fs, dir, ref: baseRef } : { fs, dir }; + const matrix = await git.statusMatrix(statusOpts); + for (const [filepath, head, workdir, stage] of matrix) { + if (head === 0 && workdir === 2 && stage === 0) { + try { + await git.add({ fs, dir, filepath }); + } catch (e) {} + } + } + + const matrixAfterAdd = await git.statusMatrix(statusOpts); + const changed = matrixAfterAdd.filter(([filepath, head, workdir]) => head !== workdir); + let patch = ''; + for (const [filepath, head, workdir] of changed) { + const abs = require('path').join(dir, filepath); + const workBuf = workdir ? await fs.promises.readFile(abs).catch(() => null) : null; + const base = head && baseOid ? await git.readBlob({ fs, dir, oid: baseOid, filepath }).catch(() => null) : null; + const a = base ? Buffer.from(base.blob).toString('utf8') : ''; + const b = workBuf ? workBuf.toString('utf8') : a; + if (a === b) continue; + if ((a.indexOf('\0') !== -1) || (b.indexOf('\0') !== -1)) continue; + const filePatch = JsDiff.createTwoFilesPatch(`a/${filepath}`, `b/${filepath}`, a, b, '', '', { context: 3 }); + patch += filePatch + '\n'; + } + return patch || 'No changes.'; +} + +// GitHub API helper functions +function githubAPI(path, options = {}) { + return new Promise((resolve, reject) => { + const url = new URL(path, 'https://api.github.com'); + const reqOptions = { + hostname: url.hostname, + path: url.pathname + url.search, + method: options.method || 'GET', + headers: { + 'User-Agent': 'WordPress-Dev-App', + 'Accept': 'application/vnd.github+json', + ...(githubToken && { 'Authorization': `Bearer ${githubToken}` }), + ...(options.headers || {}) + } + }; + + const req = https.request(reqOptions, (res) => { + let data = ''; + res.on('data', (chunk) => data += chunk); + res.on('end', () => { + try { + const json = JSON.parse(data); + if (res.statusCode >= 200 && res.statusCode < 300) { + resolve(json); + } else { + reject(new Error(json.message || `HTTP ${res.statusCode}`)); + } + } catch (e) { + reject(e); + } + }); + }); + + req.on('error', reject); + + if (options.body) { + req.write(JSON.stringify(options.body)); + } + + req.end(); + }); +} + +// GitHub Device OAuth Flow - uses github.com not api.github.com +function githubOAuthRequest(path, body) { + return new Promise((resolve, reject) => { + const data = JSON.stringify(body); + const reqOptions = { + hostname: 'github.com', + path: path, + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + 'Content-Length': data.length + } + }; + + const req = https.request(reqOptions, (res) => { + let responseData = ''; + res.on('data', (chunk) => responseData += chunk); + res.on('end', () => { + try { + resolve(JSON.parse(responseData)); + } catch (e) { + reject(e); + } + }); + }); + + req.on('error', reject); + req.write(data); + req.end(); + }); +} + +async function waitWithAbort(ms, shouldAbort) { + const start = Date.now(); + while (Date.now() - start < ms) { + if (typeof shouldAbort === 'function' && shouldAbort()) { + const err = new Error('Aborted by user'); + err.code = 'PR_ABORTED'; + throw err; + } + const elapsed = Date.now() - start; + const remaining = Math.max(ms - elapsed, 0); + const slice = Math.min(remaining, 250); + await new Promise((resolve) => setTimeout(resolve, slice)); + } +} + +async function initiateDeviceOAuth() { + const response = await githubOAuthRequest('/login/device/code', { + client_id: GITHUB_CLIENT_ID, + scope: 'repo' + }); + + return { + device_code: response.device_code, + user_code: response.user_code, + verification_uri: response.verification_uri, + expires_in: response.expires_in, + interval: response.interval || 5 + }; +} + +async function pollDeviceOAuth(deviceCode, interval, shouldAbort) { + if (typeof shouldAbort === 'function' && shouldAbort()) { + throw new Error('Aborted by user'); + } + const response = await githubOAuthRequest('/login/oauth/access_token', { + client_id: GITHUB_CLIENT_ID, + device_code: deviceCode, + grant_type: 'urn:ietf:params:oauth:grant-type:device_code' + }); + + if (response.error) { + if (response.error === 'authorization_pending') { + await waitWithAbort(interval * 1000, shouldAbort); + return pollDeviceOAuth(deviceCode, interval, shouldAbort); + } + throw new Error(response.error_description || response.error); + } + + return response.access_token; +} + +// Check if user has a fork +async function checkForFork(username) { + try { + await githubAPI(`/repos/${username}/wordpress-develop`); + return true; + } catch { + return false; + } +} + +// Create a fork +async function createFork() { + await githubAPI('/repos/WordPress/wordpress-develop/forks', { + method: 'POST' + }); + + // Wait a bit for fork to be ready + await new Promise(resolve => setTimeout(resolve, 3000)); +} + +// Get authenticated user +async function getAuthenticatedUser() { + const user = await githubAPI('/user'); + return user.login; +} + +// Submit PR workflow +async function submitPR(sitePath, onProgress, abortState) { + try { + const ensureNotAborted = () => { + if (abortState?.aborted) { + const err = new Error('Aborted by user'); + err.code = 'PR_ABORTED'; + throw err; } - } - } + }; + const safeProgress = (payload) => { + if (abortState?.aborted) return; + onProgress(payload); + }; - // Compare working tree vs HEAD (which points to trunk tip after clone) - const matrixAfterAdd = await git.statusMatrix({ fs, dir }); - const changed = matrixAfterAdd.filter(([filepath, head, workdir, stage]) => head !== workdir); - let patch = ''; - for (const [filepath, head, workdir] of changed) { - const abs = require('path').join(dir, filepath); - const workBuf = workdir ? await fs.promises.readFile(abs).catch(() => null) : null; - const base = head && headOid ? await git.readBlob({ fs, dir, oid: headOid, filepath }).catch(() => null) : null; - const a = base ? Buffer.from(base.blob).toString('utf8') : ''; - const b = workBuf ? workBuf.toString('utf8') : a; - if (a === b) continue; - // Skip likely-binary - if ((a.indexOf('\0') !== -1) || (b.indexOf('\0') !== -1)) continue; - const filePatch = JsDiff.createTwoFilesPatch(`a/${filepath}`, `b/${filepath}`, a, b, '', '', { context: 3 }); - patch += filePatch + '\n'; - } - return patch || 'No changes.'; + ensureNotAborted(); + // Step 1: Authenticate if needed (or if token is invalid) + const needsAuth = async () => { + ensureNotAborted(); + if (!githubToken) return true; + // Test if token is valid + try { + await githubAPI('/user'); + return false; + } catch (e) { + // Token is invalid, clear it + await saveGitHubToken(null); + return true; + } + }; + + if (await needsAuth()) { + safeProgress({ step: 'auth', message: 'Starting GitHub authentication...' }); + const deviceAuth = await initiateDeviceOAuth(); + + safeProgress({ + step: 'auth_code', + message: `Please visit ${deviceAuth.verification_uri} and enter code: ${deviceAuth.user_code}`, + verification_uri: deviceAuth.verification_uri, + user_code: deviceAuth.user_code + }); + + githubToken = await pollDeviceOAuth(deviceAuth.device_code, deviceAuth.interval, () => abortState?.aborted); + await saveGitHubToken(githubToken); + safeProgress({ step: 'auth', message: 'Authenticated successfully!' }); + } + + // Step 2: Get username and check for fork + safeProgress({ step: 'fork', message: 'Checking for fork...' }); + const username = await getAuthenticatedUser(); + const hasFork = await checkForFork(username); + + if (!hasFork) { + safeProgress({ step: 'fork', message: 'Creating fork...' }); + await createFork(); + safeProgress({ step: 'fork', message: `Fork created: https://github.com/${username}/wordpress-develop` }); + } else { + safeProgress({ step: 'fork', message: `Using existing fork: https://github.com/${username}/wordpress-develop` }); + } + + ensureNotAborted(); + + // Step 3: Create a new branch with timestamp + const branchName = `patch-${Date.now()}`; + safeProgress({ + step: 'branch', + message: `Creating branch: ${branchName}`, + gitCommand: `git checkout -b ${branchName}` + }); + + await git.branch({ fs, dir: sitePath, ref: branchName, checkout: true }); + + ensureNotAborted(); + + // Step 4: Stage and commit all changes + safeProgress({ + step: 'commit', + message: 'Staging all changes...', + gitCommand: 'git add .' + }); + + // First, unstage anything that is currently staged so we only commit what we explicitly add + const allFiles = await git.statusMatrix({ fs, dir: sitePath }); + const stagedFiles = allFiles.filter(([, , , stage]) => stage === 2); + for (const [filepath] of stagedFiles) { + try { + await git.resetIndex({ fs, dir: sitePath, filepath }); + } catch (e) { + // Ignore errors + } + } + + ensureNotAborted(); + // Now get fresh status and add only the files we want + const matrix = await git.statusMatrix({ fs, dir: sitePath }); + + // Add untracked files (same as createMinimalPatchForDir) + for (const [filepath, head, workdir, stage] of matrix) { + // Skip .github/workflows/ files - they require special workflow scope + if (filepath.startsWith('.github/workflows/')) { + continue; + } + + // If file is untracked (head=0, workdir=2, stage=0) + if (head === 0 && workdir === 2 && stage === 0) { + try { + await git.add({ fs, dir: sitePath, filepath }); + } catch (e) { + // Ignore errors for files that can't be added (e.g., in .gitignore) + } + } + } + + // Now stage only files with actual changes (same logic as createMinimalPatchForDir) + ensureNotAborted(); + const matrixAfterAdd = await git.statusMatrix({ fs, dir: sitePath }); + const changed = matrixAfterAdd.filter(([filepath, head, workdir]) => head !== workdir); + for (const [filepath, head, workdir] of changed) { + try { + // Skip .github/workflows/ files - they require special workflow scope + if (filepath.startsWith('.github/workflows/')) { + continue; + } + + // For deleted files (workdir === 0), use remove instead of add + if (workdir === 0) { + await git.remove({ fs, dir: sitePath, filepath }); + } else { + await git.add({ fs, dir: sitePath, filepath }); + } + } catch (e) { + // Ignore errors for files that can't be staged + } + } + + safeProgress({ + step: 'commit', + message: 'Committing changes...', + gitCommand: 'git commit -m "WordPress core patch"' + }); + + ensureNotAborted(); + await git.commit({ + fs, + dir: sitePath, + message: 'WordPress core patch', + author: { + name: username, + email: `${username}@users.noreply.github.com` + } + }); + + // Step 5: Add fork remote if it doesn't exist + ensureNotAborted(); + const remotes = await git.listRemotes({ fs, dir: sitePath }); + const forkRemote = remotes.find(r => r.remote === 'fork'); + + if (!forkRemote) { + safeProgress({ + step: 'push', + message: 'Adding fork remote...', + gitCommand: `git remote add fork https://github.com/${username}/wordpress-develop.git` + }); + ensureNotAborted(); + await git.addRemote({ + fs, + dir: sitePath, + remote: 'fork', + url: `https://github.com/${username}/wordpress-develop.git` + }); + } + + // Step 6: Push to fork + const emitPushProgress = (payload) => { + if (abortState?.aborted) return; + safeProgress({ + step: 'push', + timestamp: Date.now(), + ...payload + }); + }; + emitPushProgress({ + message: `Pushing to fork...`, + gitCommand: `git push fork ${branchName}`, + logType: 'info' + }); + + ensureNotAborted(); + + console.log('[git push] Starting push operation'); + console.log('[git push] Remote:', 'fork'); + console.log('[git push] Branch:', branchName); + console.log('[git push] Token length:', githubToken ? githubToken.length : 0); + + let pushAbortController = null; + try { + pushAbortController = new AbortController(); + if (abortState) abortState.controller = pushAbortController; + const pushResult = await git.push({ + fs, + http, + dir: sitePath, + remote: 'fork', + ref: branchName, + signal: pushAbortController.signal, + onAuth: () => { + console.log('[git push] Auth callback invoked'); + return { username: githubToken, password: 'x-oauth-basic' }; + }, + onAuthFailure: ({ url, auth }) => { + console.error('[git push] Auth failed for URL:', url); + console.error('[git push] Auth used:', auth); + }, + onAuthSuccess: ({ url, auth }) => { + console.log('[git push] Auth succeeded for URL:', url); + }, + onMessage: (msg) => { + console.log('[git push message]', msg); + const normalized = String(msg || '') + .replace(/\r/g, '\n') + .split(/\n+/) + .map((line) => line.trim()) + .filter(Boolean); + normalized.forEach((line) => emitPushProgress({ message: line, logType: 'remote' })); + }, + onProgress: (evt) => { + console.log('[git push progress]', evt); + const { phase, loaded, total } = evt; + if (phase) { + const msg = total ? `${phase}: ${loaded}/${total}` : phase; + emitPushProgress({ message: msg, logType: 'progress', phase, loaded, total }); + } + } + }); + console.log('[git push] Push completed successfully', pushResult); + } catch (pushError) { + console.error('[git push error]', pushError); + console.error('[git push error stack]', pushError.stack); + if (pushError.name === 'AbortError' || abortState?.aborted) { + throw new Error('Aborted by user'); + } + // If push fails with 403, it might be a token permission issue + if (pushError.message && pushError.message.includes('403')) { + throw new Error('Push failed: Token may not have correct permissions. Please re-authorize the app with full repository access.'); + } + throw pushError; + } finally { + if (abortState) abortState.controller = null; + } + + // Step 7: Open PR URL + const prUrl = `https://github.com/WordPress/wordpress-develop/compare/trunk...${username}:wordpress-develop:${branchName}?expand=1`; + safeProgress({ step: 'done', message: 'Opening PR page...', prUrl }); + + return { ok: true, prUrl, branch: branchName }; + + } catch (error) { + return { ok: false, error: error.message }; + } } ipcMain.handle('git:get-patch', async (_e, sitePath) => { @@ -314,30 +763,81 @@ ipcMain.handle('git:create-patch', async (_e, sitePath) => { } }); +async function promptAndSavePatchFile(patchText) { + const { filePath, canceled } = await dialog.showSaveDialog({ + title: 'Save Diff File', + defaultPath: path.join(os.homedir(), 'wordpress.patch'), + filters: [ + { name: 'Patch Files', extensions: ['patch', 'diff'] }, + { name: 'All Files', extensions: ['*'] } + ] + }); + + if (canceled || !filePath) { + return { ok: false, canceled: true }; + } + + await fs.promises.writeFile(filePath, patchText, 'utf8'); + return { ok: true, filePath }; +} + ipcMain.handle('git:save-patch', async (_e, sitePath) => { try { const patch = await createMinimalPatchForDir(sitePath); - const { filePath, canceled } = await dialog.showSaveDialog({ - title: 'Save Diff File', - defaultPath: path.join(os.homedir(), 'wordpress.patch'), - filters: [ - { name: 'Patch Files', extensions: ['patch', 'diff'] }, - { name: 'All Files', extensions: ['*'] } - ] - }); - - if (canceled || !filePath) { - return { ok: false, canceled: true }; - } - - await fs.promises.writeFile(filePath, patch, 'utf8'); - return { ok: true, filePath }; + return await promptAndSavePatchFile(patch); } catch (e) { return { ok: false, error: String(e) }; } }); -app.whenReady().then(() => { +ipcMain.handle('git:save-patch-content', async (_e, payload) => { + try { + const patchText = (payload && typeof payload.patch === 'string' && payload.patch.length) + ? payload.patch + : await createMinimalPatchForDir(payload?.sitePath); + return await promptAndSavePatchFile(patchText || 'No changes.'); + } catch (e) { + return { ok: false, error: String(e) }; + } +}); + +ipcMain.handle('git:submit-pr', async (event, sitePath) => { + const abortState = { aborted: false, controller: null }; + submitPrAbortStates.set(event.sender.id, abortState); + try { + const result = await submitPR(sitePath, (progress) => { + event.sender.send('git:submit-pr:progress', progress); + }, abortState); + return result; + } finally { + submitPrAbortStates.delete(event.sender.id); + } +}); + +ipcMain.handle('git:submit-pr:abort', async (event) => { + const abortState = submitPrAbortStates.get(event.sender.id); + if (abortState) { + abortState.aborted = true; + if (abortState.controller && typeof abortState.controller.abort === 'function') { + try { abortState.controller.abort(); } catch (e) { + console.warn('[git push] abort signal failed', e && e.message ? e.message : e); + } + } + } + return true; +}); + +ipcMain.handle('github:clear-token', async () => { + await saveGitHubToken(null); + return true; +}); + +ipcMain.handle('github:is-connected', async () => { + return githubToken !== null && githubToken !== undefined; +}); + +app.whenReady().then(async () => { + await loadGitHubToken(); createWindow(); app.on('activate', function () { diff --git a/src/preload.js b/src/preload.js index ebc8f7d..0e158d0 100644 --- a/src/preload.js +++ b/src/preload.js @@ -72,6 +72,19 @@ contextBridge.exposeInMainWorld('api', { getPatch: (sitePath) => ipcRenderer.invoke('git:get-patch', sitePath) , savePatch: (sitePath) => ipcRenderer.invoke('git:save-patch', sitePath) +, + savePatchContent: (sitePath, patchText) => ipcRenderer.invoke('git:save-patch-content', { sitePath, patch: patchText }) +, + submitPR: async (sitePath, onProgress) => { + const progressHandler = (_e, progress) => { + if (onProgress) onProgress(progress); + }; + ipcRenderer.on('git:submit-pr:progress', progressHandler); + const result = await ipcRenderer.invoke('git:submit-pr', sitePath); + ipcRenderer.removeListener('git:submit-pr:progress', progressHandler); + return result; + }, + abortSubmitPR: () => ipcRenderer.invoke('git:submit-pr:abort') , startWpDebug: async (sitePath, onData) => { const handler = (_e, payload) => { @@ -170,5 +183,8 @@ contextBridge.exposeInMainWorld('api', { ipcRenderer.on('smtp:started', h); return () => ipcRenderer.removeListener('smtp:started', h); } +, + disconnectGitHub: () => ipcRenderer.invoke('github:clear-token') +, + isGitHubConnected: () => ipcRenderer.invoke('github:is-connected') }); - diff --git a/src/renderer/index.html b/src/renderer/index.html index ff5d1f8..91638b2 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -19,6 +19,20 @@ #log { white-space: pre-wrap; background: #111; color: #eee; padding: 12px; border-radius: 6px; height: 220px; overflow: auto; } .patch-modal-header .components-modal__header-heading { color: #1e1e1e !important; } .components-dropdown-menu__toggle { display: flex; flex-direction: row-reverse; gap: 4px; } + /* Add consistent border to dropdown sidekick buttons */ + .dropdown-with-sidekick .components-dropdown-menu__toggle { + border: 1px solid !important; + border-left: none !important; + border-radius: 0 10px 10px 0 !important; + } + .components-dropdown-menu .components-button.is-secondary.components-dropdown-menu__toggle { + border-color: #949494 !important; + border-left: 1px solid rgba(0,0,0,0.1) !important; + } + .components-dropdown-menu .components-button.is-primary.components-dropdown-menu__toggle { + border-color: #3858e9 !important; + border-left: 1px solid rgba(255,255,255,0.2) !important; + } diff --git a/src/renderer/index.js b/src/renderer/index.js index ccd0521..3125f2d 100644 --- a/src/renderer/index.js +++ b/src/renderer/index.js @@ -41555,10 +41555,10 @@ If there's a particular need for this, please submit a feature request at https: } return React6.createElement.apply(null, createElementArgArray); }; - (function(_jsx42) { + (function(_jsx40) { var JSX; /* @__PURE__ */ (function(_JSX) { - })(JSX || (JSX = _jsx42.JSX || (_jsx42.JSX = {}))); + })(JSX || (JSX = _jsx40.JSX || (_jsx40.JSX = {}))); })(jsx17 || (jsx17 = {})); function css() { for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { @@ -50293,36 +50293,12 @@ If there's a particular need for this, please submit a feature request at https: }); var close_default = close; - // node_modules/@wordpress/icons/build-module/library/copy.js + // node_modules/@wordpress/icons/build-module/library/pencil.js var import_jsx_runtime31 = __toESM(require_jsx_runtime()); - var copy2 = /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(SVG, { + var pencil = /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(SVG, { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(Path, { - fillRule: "evenodd", - clipRule: "evenodd", - d: "M5 4.5h11a.5.5 0 0 1 .5.5v11a.5.5 0 0 1-.5.5H5a.5.5 0 0 1-.5-.5V5a.5.5 0 0 1 .5-.5ZM3 5a2 2 0 0 1 2-2h11a2 2 0 0 1 2 2v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5Zm17 3v10.75c0 .69-.56 1.25-1.25 1.25H6v1.5h12.75a2.75 2.75 0 0 0 2.75-2.75V8H20Z" - }) - }); - var copy_default = copy2; - - // node_modules/@wordpress/icons/build-module/library/download.js - var import_jsx_runtime32 = __toESM(require_jsx_runtime()); - var download = /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(SVG, { - xmlns: "http://www.w3.org/2000/svg", - viewBox: "0 0 24 24", - children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Path, { - d: "M18 11.3l-1-1.1-4 4V3h-1.5v11.3L7 10.2l-1 1.1 6.2 5.8 5.8-5.8zm.5 3.7v3.5h-13V15H4v5h16v-5h-1.5z" - }) - }); - var download_default = download; - - // node_modules/@wordpress/icons/build-module/library/pencil.js - var import_jsx_runtime33 = __toESM(require_jsx_runtime()); - var pencil = /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(SVG, { - xmlns: "http://www.w3.org/2000/svg", - viewBox: "0 0 24 24", - children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Path, { d: "m19 7-3-3-8.5 8.5-1 4 4-1L19 7Zm-7 11.5H5V20h7v-1.5Z" }) }); @@ -50332,22 +50308,22 @@ If there's a particular need for this, please submit a feature request at https: var edit_default = pencil_default; // node_modules/@wordpress/icons/build-module/library/menu.js - var import_jsx_runtime34 = __toESM(require_jsx_runtime()); - var menu = /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(SVG, { + var import_jsx_runtime32 = __toESM(require_jsx_runtime()); + var menu = /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(SVG, { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", - children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Path, { + children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Path, { d: "M5 5v1.5h14V5H5zm0 7.8h14v-1.5H5v1.5zM5 19h14v-1.5H5V19z" }) }); var menu_default = menu; // node_modules/@wordpress/icons/build-module/library/plus.js - var import_jsx_runtime35 = __toESM(require_jsx_runtime()); - var plus = /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(SVG, { + var import_jsx_runtime33 = __toESM(require_jsx_runtime()); + var plus = /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(SVG, { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", - children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(Path, { + children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Path, { d: "M11 12.5V17.5H12.5V12.5H17.5V11H12.5V6H11V11H6V12.5H11Z" }) }); @@ -50425,7 +50401,7 @@ If there's a particular need for this, please submit a feature request at https: })(labelStyles, ";" + (false ? "" : "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIkB3b3JkcHJlc3MvY29tcG9uZW50cy9zcmMvYmFzZS1jb250cm9sL3N0eWxlcy9iYXNlLWNvbnRyb2wtc3R5bGVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQXVFNEMiLCJmaWxlIjoiQHdvcmRwcmVzcy9jb21wb25lbnRzL3NyYy9iYXNlLWNvbnRyb2wvc3R5bGVzL2Jhc2UtY29udHJvbC1zdHlsZXMudHMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEV4dGVybmFsIGRlcGVuZGVuY2llc1xuICovXG5pbXBvcnQgc3R5bGVkIGZyb20gJ0BlbW90aW9uL3N0eWxlZCc7XG5pbXBvcnQgeyBjc3MgfSBmcm9tICdAZW1vdGlvbi9yZWFjdCc7XG5cbi8qKlxuICogSW50ZXJuYWwgZGVwZW5kZW5jaWVzXG4gKi9cbmltcG9ydCB7IGJhc2VMYWJlbFR5cG9ncmFwaHksIGJveFNpemluZ1Jlc2V0LCBmb250LCBDT0xPUlMgfSBmcm9tICcuLi8uLi91dGlscyc7XG5pbXBvcnQgeyBzcGFjZSB9IGZyb20gJy4uLy4uL3V0aWxzL3NwYWNlJztcblxuZXhwb3J0IGNvbnN0IFdyYXBwZXIgPSBzdHlsZWQuZGl2YFxuXHRmb250LWZhbWlseTogJHsgZm9udCggJ2RlZmF1bHQuZm9udEZhbWlseScgKSB9O1xuXHRmb250LXNpemU6ICR7IGZvbnQoICdkZWZhdWx0LmZvbnRTaXplJyApIH07XG5cblx0JHsgYm94U2l6aW5nUmVzZXQgfVxuYDtcblxuY29uc3QgZGVwcmVjYXRlZE1hcmdpbkZpZWxkID0gKCB7IF9fbmV4dEhhc05vTWFyZ2luQm90dG9tID0gZmFsc2UgfSApID0+IHtcblx0cmV0dXJuIChcblx0XHQhIF9fbmV4dEhhc05vTWFyZ2luQm90dG9tICYmXG5cdFx0Y3NzYFxuXHRcdFx0bWFyZ2luLWJvdHRvbTogJHsgc3BhY2UoIDIgKSB9O1xuXHRcdGBcblx0KTtcbn07XG5cbmV4cG9ydCBjb25zdCBTdHlsZWRGaWVsZCA9IHN0eWxlZC5kaXZgXG5cdCR7IGRlcHJlY2F0ZWRNYXJnaW5GaWVsZCB9XG5cblx0LmNvbXBvbmVudHMtcGFuZWxfX3JvdyAmIHtcblx0XHRtYXJnaW4tYm90dG9tOiBpbmhlcml0O1xuXHR9XG5gO1xuXG5jb25zdCBsYWJlbFN0eWxlcyA9IGNzc2Bcblx0JHsgYmFzZUxhYmVsVHlwb2dyYXBoeSB9O1xuXG5cdGRpc3BsYXk6IGJsb2NrO1xuXHRtYXJnaW4tYm90dG9tOiAkeyBzcGFjZSggMiApIH07XG5cdC8qKlxuXHQgKiBSZW1vdmVzIENocm9tZS9TYWZhcmkvRmlyZWZveCB1c2VyIGFnZW50IHN0eWxlc2hlZXQgcGFkZGluZyBmcm9tXG5cdCAqIFN0eWxlZExhYmVsIHdoZW4gaXQgaXMgcmVuZGVyZWQgYXMgYSBsZWdlbmQuXG5cdCAqL1xuXHRwYWRkaW5nOiAwO1xuYDtcblxuZXhwb3J0IGNvbnN0IFN0eWxlZExhYmVsID0gc3R5bGVkLmxhYmVsYFxuXHQkeyBsYWJlbFN0eWxlcyB9XG5gO1xuXG5jb25zdCBkZXByZWNhdGVkTWFyZ2luSGVscCA9ICggeyBfX25leHRIYXNOb01hcmdpbkJvdHRvbSA9IGZhbHNlIH0gKSA9PiB7XG5cdHJldHVybiAoXG5cdFx0ISBfX25leHRIYXNOb01hcmdpbkJvdHRvbSAmJlxuXHRcdGNzc2Bcblx0XHRcdG1hcmdpbi1ib3R0b206IHJldmVydDtcblx0XHRgXG5cdCk7XG59O1xuXG5leHBvcnQgY29uc3QgU3R5bGVkSGVscCA9IHN0eWxlZC5wYFxuXHRtYXJnaW4tdG9wOiAkeyBzcGFjZSggMiApIH07XG5cdG1hcmdpbi1ib3R0b206IDA7XG5cdGZvbnQtc2l6ZTogJHsgZm9udCggJ2hlbHBUZXh0LmZvbnRTaXplJyApIH07XG5cdGZvbnQtc3R5bGU6IG5vcm1hbDtcblx0Y29sb3I6ICR7IENPTE9SUy5ncmF5WyA3MDAgXSB9O1xuXG5cdCR7IGRlcHJlY2F0ZWRNYXJnaW5IZWxwIH1cbmA7XG5cbmV4cG9ydCBjb25zdCBTdHlsZWRWaXN1YWxMYWJlbCA9IHN0eWxlZC5zcGFuYFxuXHQkeyBsYWJlbFN0eWxlcyB9XG5gO1xuIl19 */")); // node_modules/@wordpress/components/build-module/base-control/index.js - var import_jsx_runtime36 = __toESM(require_jsx_runtime()); + var import_jsx_runtime34 = __toESM(require_jsx_runtime()); var UnconnectedBaseControl = (props) => { const { __nextHasNoMarginBottom = false, @@ -50444,26 +50420,26 @@ If there's a particular need for this, please submit a feature request at https: hint: "Set the `__nextHasNoMarginBottom` prop to true to start opting into the new styles, which will become the default in a future version." }); } - return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(Wrapper, { + return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(Wrapper, { className, - children: [/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(StyledField, { + children: [/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(StyledField, { className: "components-base-control__field", __nextHasNoMarginBottom, - children: [label && id3 && (hideLabelFromVision ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(component_default2, { + children: [label && id3 && (hideLabelFromVision ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(component_default2, { as: "label", htmlFor: id3, children: label - }) : /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(StyledLabel, { + }) : /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(StyledLabel, { className: "components-base-control__label", htmlFor: id3, children: label - })), label && !id3 && (hideLabelFromVision ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(component_default2, { + })), label && !id3 && (hideLabelFromVision ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(component_default2, { as: "label", children: label - }) : /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(VisualLabel, { + }) : /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(VisualLabel, { children: label })), children] - }), !!help && /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(StyledHelp, { + }), !!help && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(StyledHelp, { id: id3 ? id3 + "__help" : void 0, className: "components-base-control__help", __nextHasNoMarginBottom, @@ -50477,7 +50453,7 @@ If there's a particular need for this, please submit a feature request at https: children, ...restProps } = props; - return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(StyledVisualLabel, { + return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(StyledVisualLabel, { ref, ...restProps, className: clsx_default("components-base-control__label", className), @@ -50512,7 +50488,7 @@ If there's a particular need for this, please submit a feature request at https: var base_control_default = BaseControl; // node_modules/@wordpress/components/build-module/dashicon/index.js - var import_jsx_runtime37 = __toESM(require_jsx_runtime()); + var import_jsx_runtime35 = __toESM(require_jsx_runtime()); function Dashicon({ icon, className, @@ -50534,7 +50510,7 @@ If there's a particular need for this, please submit a feature request at https: ...sizeStyles, ...style }; - return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { + return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: iconClass, style: styles, ...extraProps @@ -50543,14 +50519,14 @@ If there's a particular need for this, please submit a feature request at https: var dashicon_default = Dashicon; // node_modules/@wordpress/components/build-module/icon/index.js - var import_jsx_runtime38 = __toESM(require_jsx_runtime()); + var import_jsx_runtime36 = __toESM(require_jsx_runtime()); function Icon({ icon = null, size: size4 = "string" === typeof icon ? 20 : 24, ...additionalProps }) { if ("string" === typeof icon) { - return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(dashicon_default, { + return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(dashicon_default, { icon, size: size4, ...additionalProps @@ -50574,7 +50550,7 @@ If there's a particular need for this, please submit a feature request at https: height: size4, ...additionalProps }; - return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(SVG, { + return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(SVG, { ...appliedProps }); } @@ -50590,7 +50566,7 @@ If there's a particular need for this, please submit a feature request at https: var icon_default = Icon; // node_modules/@wordpress/components/build-module/button/index.js - var import_jsx_runtime39 = __toESM(require_jsx_runtime()); + var import_jsx_runtime37 = __toESM(require_jsx_runtime()); var disabledEventsOnDisabledButton = ["onMouseDown", "onClick"]; function useDeprecatedProps2({ __experimentalIsFocusable, @@ -50742,24 +50718,24 @@ If there's a particular need for this, please submit a feature request at https: "aria-describedby": describedById, ref }; - const elementChildren = /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_jsx_runtime39.Fragment, { - children: [icon && iconPosition === "left" && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(icon_default, { + const elementChildren = /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_jsx_runtime37.Fragment, { + children: [icon && iconPosition === "left" && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(icon_default, { icon, size: iconSize - }), text && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_jsx_runtime39.Fragment, { + }), text && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_jsx_runtime37.Fragment, { children: text - }), children, icon && iconPosition === "right" && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(icon_default, { + }), children, icon && iconPosition === "right" && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(icon_default, { icon, size: iconSize })] }); - const element = Tag === "a" ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("a", { + const element = Tag === "a" ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("a", { ...anchorProps, ...additionalProps, ...disableEventProps, ...commonProps, children: elementChildren - }) : /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("button", { + }) : /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("button", { ...buttonProps, ...additionalProps, ...disableEventProps, @@ -50772,12 +50748,12 @@ If there's a particular need for this, please submit a feature request at https: placement: tooltipPosition && // Convert legacy `position` values to be used with the new `placement` prop positionToPlacement(tooltipPosition) } : {}; - return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_jsx_runtime39.Fragment, { - children: [/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(tooltip_default, { + return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(import_jsx_runtime37.Fragment, { + children: [/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(tooltip_default, { ...tooltipProps, children: element - }), description && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(component_default2, { - children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { + }), description && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(component_default2, { + children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { id: descriptionId, children: description }) @@ -51154,7 +51130,7 @@ If there's a particular need for this, please submit a feature request at https: } // node_modules/@wordpress/components/build-module/slot-fill/slot.js - var import_jsx_runtime40 = __toESM(require_jsx_runtime()); + var import_jsx_runtime38 = __toESM(require_jsx_runtime()); function isFunction(maybeFunc) { return typeof maybeFunc === "function"; } @@ -51195,7 +51171,7 @@ If there's a particular need for this, please submit a feature request at https: // it allows us to render wrappers only when the fills are actually present. (element) => !isEmptyElement(element) ); - return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_jsx_runtime40.Fragment, { + return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_jsx_runtime38.Fragment, { children: isFunction(children) ? children(fills) : fills }); } @@ -51271,7 +51247,7 @@ If there's a particular need for this, please submit a feature request at https: var v4_default = v4; // node_modules/@wordpress/components/build-module/style-provider/index.js - var import_jsx_runtime41 = __toESM(require_jsx_runtime()); + var import_jsx_runtime39 = __toESM(require_jsx_runtime()); var uuidCache = /* @__PURE__ */ new Set(); var containerCacheMap = /* @__PURE__ */ new WeakMap(); var memoizedCreateCacheWithContainer = (container) => { @@ -51299,7 +51275,7 @@ If there's a particular need for this, please submit a feature request at https: return null; } const cache2 = memoizedCreateCacheWithContainer(document2.head); - return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(CacheProvider, { + return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(CacheProvider, { value: cache2, children }); @@ -51307,7 +51283,7 @@ If there's a particular need for this, please submit a feature request at https: var style_provider_default = StyleProvider; // node_modules/@wordpress/components/build-module/slot-fill/bubbles-virtually/fill.js - var import_jsx_runtime42 = __toESM(require_jsx_runtime()); + var import_jsx_runtime40 = __toESM(require_jsx_runtime()); function Fill2({ name, children @@ -51329,7 +51305,7 @@ If there's a particular need for this, please submit a feature request at https: if (!slot || !slot.ref.current) { return null; } - const wrappedChildren = /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(style_provider_default, { + const wrappedChildren = /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(style_provider_default, { document: slot.ref.current.ownerDocument, children: typeof children === "function" ? children((_slot$fillProps = slot.fillProps) !== null && _slot$fillProps !== void 0 ? _slot$fillProps : {}) : children }); @@ -51337,7 +51313,7 @@ If there's a particular need for this, please submit a feature request at https: } // node_modules/@wordpress/components/build-module/slot-fill/bubbles-virtually/slot.js - var import_jsx_runtime43 = __toESM(require_jsx_runtime()); + var import_jsx_runtime41 = __toESM(require_jsx_runtime()); function Slot2(props, forwardedRef) { const { name, @@ -51367,7 +51343,7 @@ If there's a particular need for this, please submit a feature request at https: (0, import_react.useLayoutEffect)(() => { registry.updateSlot(name, ref, fillPropsRef.current); }); - return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(component_default, { + return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(component_default, { as, ref: useMergeRefs([forwardedRef, ref]), ...restProps @@ -51376,7 +51352,7 @@ If there's a particular need for this, please submit a feature request at https: var slot_default2 = (0, import_react.forwardRef)(Slot2); // node_modules/@wordpress/components/build-module/slot-fill/bubbles-virtually/slot-fill-provider.js - var import_jsx_runtime44 = __toESM(require_jsx_runtime()); + var import_jsx_runtime42 = __toESM(require_jsx_runtime()); function createSlotRegistry() { const slots = observableMap(); const fills = observableMap(); @@ -51439,14 +51415,14 @@ If there's a particular need for this, please submit a feature request at https: children }) { const [registry] = (0, import_react.useState)(createSlotRegistry); - return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(slot_fill_context_default.Provider, { + return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(slot_fill_context_default.Provider, { value: registry, children }); } // node_modules/@wordpress/components/build-module/slot-fill/provider.js - var import_jsx_runtime45 = __toESM(require_jsx_runtime()); + var import_jsx_runtime43 = __toESM(require_jsx_runtime()); function createSlotRegistry2() { const slots = {}; const fills = {}; @@ -51514,7 +51490,7 @@ If there's a particular need for this, please submit a feature request at https: children }) { const [contextValue] = (0, import_react.useState)(createSlotRegistry2); - return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(context_default.Provider, { + return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(context_default.Provider, { value: contextValue, children }); @@ -51522,7 +51498,7 @@ If there's a particular need for this, please submit a feature request at https: var provider_default = SlotFillProvider2; // node_modules/@wordpress/components/build-module/slot-fill/index.js - var import_jsx_runtime46 = __toESM(require_jsx_runtime()); + var import_jsx_runtime44 = __toESM(require_jsx_runtime()); // node_modules/@wordpress/components/build-module/slot-fill/bubbles-virtually/use-slot.js function useSlot2(name) { @@ -51535,10 +51511,10 @@ If there's a particular need for this, please submit a feature request at https: // node_modules/@wordpress/components/build-module/slot-fill/index.js function Fill3(props) { - return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_jsx_runtime46.Fragment, { - children: [/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Fill, { + return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { + children: [/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Fill, { ...props - }), /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Fill2, { + }), /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Fill2, { ...props })] }); @@ -51549,12 +51525,12 @@ If there's a particular need for this, please submit a feature request at https: ...restProps } = props; if (bubblesVirtually) { - return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(slot_default2, { + return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(slot_default2, { ...restProps, ref }); } - return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(slot_default, { + return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(slot_default, { ...restProps }); } @@ -51565,12 +51541,12 @@ If there's a particular need for this, please submit a feature request at https: }) { const parent = (0, import_react.useContext)(slot_fill_context_default); if (!parent.isDefault && passthrough) { - return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_jsx_runtime46.Fragment, { + return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_jsx_runtime44.Fragment, { children }); } - return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(provider_default, { - children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(SlotFillProvider, { + return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(provider_default, { + children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(SlotFillProvider, { children }) }); @@ -51607,17 +51583,17 @@ If there's a particular need for this, please submit a feature request at https: } // node_modules/@wordpress/components/build-module/popover/index.js - var import_jsx_runtime47 = __toESM(require_jsx_runtime()); + var import_jsx_runtime45 = __toESM(require_jsx_runtime()); var SLOT_NAME = "Popover"; - var ArrowTriangle = () => /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(SVG, { + var ArrowTriangle = () => /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(SVG, { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 100 100", className: "components-popover__triangle", role: "presentation", - children: [/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Path, { + children: [/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Path, { className: "components-popover__triangle-bg", d: "M 0 0 L 50 50 L 100 0" - }), /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Path, { + }), /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Path, { className: "components-popover__triangle-border", d: "M 0 0 L 50 50 L 100 0", vectorEffect: "non-scaling-stroke" @@ -51827,7 +51803,7 @@ If there's a particular need for this, please submit a feature request at https: } }; const isPositioned = (!shouldAnimate || animationFinished) && x !== null && y !== null; - let content = /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(motion.div, { + let content = /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(motion.div, { className: clsx_default(className, { "is-expanded": isExpanded, "is-positioned": isPositioned, @@ -51839,40 +51815,40 @@ If there's a particular need for this, please submit a feature request at https: ref: mergedFloatingRef, ...dialogProps, tabIndex: -1, - children: [isExpanded && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(scroll_lock_default, {}), isExpanded && /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)("div", { + children: [isExpanded && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(scroll_lock_default, {}), isExpanded && /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)("div", { className: "components-popover__header", - children: [/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { + children: [/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { className: "components-popover__header-title", children: headerTitle - }), /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(button_default, { + }), /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(button_default, { className: "components-popover__close", size: "small", icon: close_default, onClick: onClose, label: __("Close") })] - }), /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { + }), /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { className: "components-popover__content", children - }), hasArrow && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)("div", { + }), hasArrow && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)("div", { ref: arrowCallbackRef, className: ["components-popover__arrow", `is-${computedPlacement.split("-")[0]}`].join(" "), style: { left: typeof arrowData?.x !== "undefined" && Number.isFinite(arrowData.x) ? `${arrowData.x}px` : "", top: typeof arrowData?.y !== "undefined" && Number.isFinite(arrowData.y) ? `${arrowData.y}px` : "" }, - children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(ArrowTriangle, {}) + children: /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(ArrowTriangle, {}) })] }); const shouldRenderWithinSlot = slot.ref && !inline3; const hasAnchor = anchorRef || anchorRect || anchor; if (shouldRenderWithinSlot) { - content = /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Fill3, { + content = /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Fill3, { name: slotName, children: content }); } else if (!inline3) { - content = (0, import_react_dom.createPortal)(/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(StyleProvider, { + content = (0, import_react_dom.createPortal)(/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(StyleProvider, { document, children: content }), getPopoverFallbackContainer()); @@ -51880,8 +51856,8 @@ If there's a particular need for this, please submit a feature request at https: if (hasAnchor) { return content; } - return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_jsx_runtime47.Fragment, { - children: [/* @__PURE__ */ (0, import_jsx_runtime47.jsx)("span", { + return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_jsx_runtime45.Fragment, { + children: [/* @__PURE__ */ (0, import_jsx_runtime45.jsx)("span", { ref: anchorRefFallback }), content] }); @@ -51890,7 +51866,7 @@ If there's a particular need for this, please submit a feature request at https: function PopoverSlot({ name = SLOT_NAME }, ref) { - return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Slot3, { + return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Slot3, { bubblesVirtually: true, name, className: "popover-slot", @@ -51919,7 +51895,7 @@ If there's a particular need for this, please submit a feature request at https: } // node_modules/@wordpress/components/build-module/dropdown/index.js - var import_jsx_runtime48 = __toESM(require_jsx_runtime()); + var import_jsx_runtime46 = __toESM(require_jsx_runtime()); var UnconnectedDropdown = (props, forwardedRef) => { const { renderContent, @@ -51978,12 +51954,12 @@ If there's a particular need for this, please submit a feature request at https: const popoverPropsHaveAnchor = !!popoverProps?.anchor || // Note: `anchorRef`, `getAnchorRect` and `anchorRect` are deprecated and // be removed from `Popover` from WordPress 6.3 !!popoverProps?.anchorRef || !!popoverProps?.getAnchorRect || !!popoverProps?.anchorRect; - return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)("div", { + return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)("div", { className, ref: useMergeRefs([containerRef, forwardedRef, setFallbackPopoverAnchor]), tabIndex: -1, style, - children: [renderToggle(args), isOpen && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(popover_default, { + children: [renderToggle(args), isOpen && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(popover_default, { position: position2, onClose: close2, onFocusOutside: closeIfFocusOutside, @@ -52073,10 +52049,10 @@ If there's a particular need for this, please submit a feature request at https: } // node_modules/@wordpress/components/build-module/elevation/component.js - var import_jsx_runtime49 = __toESM(require_jsx_runtime()); + var import_jsx_runtime47 = __toESM(require_jsx_runtime()); function UnconnectedElevation(props, forwardedRef) { const elevationProps = useElevation(props); - return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(component_default, { + return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(component_default, { ...elevationProps, ref: forwardedRef }); @@ -52263,7 +52239,7 @@ If there's a particular need for this, please submit a feature request at https: } // node_modules/@wordpress/components/build-module/card/card/component.js - var import_jsx_runtime50 = __toESM(require_jsx_runtime()); + var import_jsx_runtime48 = __toESM(require_jsx_runtime()); function UnconnectedCard(props, forwardedRef) { const { children, @@ -52289,19 +52265,19 @@ If there's a particular need for this, please submit a feature request at https: CardFooter: contextProps }; }, [isBorderless, size4]); - return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(ContextSystemProvider, { + return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ContextSystemProvider, { value: contextProviderValue, - children: /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(component_default, { + children: /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(component_default, { ...otherProps, ref: forwardedRef, - children: [/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(component_default, { + children: [/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(component_default, { className: cx3(Content), children - }), /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(component_default5, { + }), /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(component_default5, { className: elevationClassName, isInteractive: false, value: elevation ? 1 : 0 - }), /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(component_default5, { + }), /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(component_default5, { className: elevationClassName, isInteractive: false, value: elevation @@ -52380,10 +52356,10 @@ If there's a particular need for this, please submit a feature request at https: } // node_modules/@wordpress/components/build-module/scrollable/component.js - var import_jsx_runtime51 = __toESM(require_jsx_runtime()); + var import_jsx_runtime49 = __toESM(require_jsx_runtime()); function UnconnectedScrollable(props, forwardedRef) { const scrollableProps = useScrollable(props); - return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(component_default, { + return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(component_default, { ...scrollableProps, ref: forwardedRef }); @@ -52418,19 +52394,19 @@ If there's a particular need for this, please submit a feature request at https: } // node_modules/@wordpress/components/build-module/card/card-body/component.js - var import_jsx_runtime52 = __toESM(require_jsx_runtime()); + var import_jsx_runtime50 = __toESM(require_jsx_runtime()); function UnconnectedCardBody(props, forwardedRef) { const { isScrollable, ...otherProps } = useCardBody(props); if (isScrollable) { - return /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(component_default7, { + return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(component_default7, { ...otherProps, ref: forwardedRef }); } - return /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(component_default, { + return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(component_default, { ...otherProps, ref: forwardedRef }); @@ -52439,7 +52415,7 @@ If there's a particular need for this, please submit a feature request at https: var component_default8 = CardBody; // node_modules/@wordpress/components/build-module/navigable-container/container.js - var import_jsx_runtime53 = __toESM(require_jsx_runtime()); + var import_jsx_runtime51 = __toESM(require_jsx_runtime()); var noop5 = () => { }; var MENU_ITEM_ROLES = ["menuitem", "menuitemradio", "menuitemcheckbox"]; @@ -52563,7 +52539,7 @@ If there's a particular need for this, please submit a feature request at https: forwardedRef, ...restProps } = this.props; - return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)("div", { + return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)("div", { ref: this.bindContainer, ...restProps, children @@ -52571,7 +52547,7 @@ If there's a particular need for this, please submit a feature request at https: } }; var forwardedNavigableContainer = (props, ref) => { - return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(NavigableContainer, { + return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(NavigableContainer, { ...props, forwardedRef: ref }); @@ -52580,7 +52556,7 @@ If there's a particular need for this, please submit a feature request at https: var container_default = (0, import_react.forwardRef)(forwardedNavigableContainer); // node_modules/@wordpress/components/build-module/navigable-container/menu.js - var import_jsx_runtime54 = __toESM(require_jsx_runtime()); + var import_jsx_runtime52 = __toESM(require_jsx_runtime()); function UnforwardedNavigableMenu({ role = "menu", orientation = "vertical", @@ -52609,7 +52585,7 @@ If there's a particular need for this, please submit a feature request at https: } return void 0; }; - return /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(container_default, { + return /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(container_default, { ref, stopNavigationEvents: true, onlyBrowserTabstops: false, @@ -52623,7 +52599,7 @@ If there's a particular need for this, please submit a feature request at https: var menu_default2 = NavigableMenu; // node_modules/@wordpress/components/build-module/dropdown-menu/index.js - var import_jsx_runtime55 = __toESM(require_jsx_runtime()); + var import_jsx_runtime53 = __toESM(require_jsx_runtime()); function mergeProps2(defaultProps = {}, props = {}) { const mergedProps = { ...defaultProps, @@ -52670,7 +52646,7 @@ If there's a particular need for this, please submit a feature request at https: className: "components-dropdown-menu__popover", variant }, popoverProps); - return /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(dropdown_default, { + return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(dropdown_default, { className, popoverProps: mergedPopoverProps, renderToggle: ({ @@ -52696,7 +52672,7 @@ If there's a particular need for this, please submit a feature request at https: "is-opened": isOpen }) }, restToggleProps); - return /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Toggle, { + return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(Toggle, { ...mergedToggleProps, icon, onClick: (event) => { @@ -52726,10 +52702,10 @@ If there's a particular need for this, please submit a feature request at https: "no-icons": noIcons }) }, menuProps); - return /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(menu_default2, { + return /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(menu_default2, { ...mergedMenuProps, role: "menu", - children: [isFunction2(children) ? children(props) : null, controlSets?.flatMap((controlSet, indexOfSet) => controlSet.map((control, indexOfControl) => /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(button_default, { + children: [isFunction2(children) ? children(props) : null, controlSets?.flatMap((controlSet, indexOfSet) => controlSet.map((control, indexOfControl) => /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(button_default, { onClick: (event) => { event.stopPropagation(); props.onClose(); @@ -52848,7 +52824,7 @@ If there's a particular need for this, please submit a feature request at https: } // node_modules/@wordpress/components/build-module/modal/index.js - var import_jsx_runtime56 = __toESM(require_jsx_runtime()); + var import_jsx_runtime54 = __toESM(require_jsx_runtime()); var ModalContext = (0, import_react.createContext)(/* @__PURE__ */ new Set()); var bodyOpenClasses = /* @__PURE__ */ new Map(); function UnforwardedModal(props, forwardedRef) { @@ -53005,14 +52981,14 @@ If there's a particular need for this, please submit a feature request at https: }; const modal = ( // eslint-disable-next-line jsx-a11y/no-static-element-interactions - /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("div", { + /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("div", { ref: useMergeRefs([ref, forwardedRef]), className: clsx_default("components-modal__screen-overlay", overlayClassname, overlayClassnameProp), onKeyDown: withIgnoreIMEEvents(handleEscapeKeyDown), ...shouldCloseOnClickOutside ? overlayPressHandlers : {}, - children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(style_provider_default, { + children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(style_provider_default, { document, - children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("div", { + children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("div", { className: clsx_default("components-modal__frame", sizeClass, className), style: { ...frameStyle, @@ -53025,7 +53001,7 @@ If there's a particular need for this, please submit a feature request at https: "aria-describedby": aria.describedby, tabIndex: -1, onKeyDown, - children: /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("div", { + children: /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("div", { className: clsx_default("components-modal__content", { "hide-header": __experimentalHideHeader, "is-scrollable": hasScrollableContent, @@ -53036,31 +53012,31 @@ If there's a particular need for this, please submit a feature request at https: ref: contentRef, "aria-label": hasScrollableContent ? __("Scrollable section") : void 0, tabIndex: hasScrollableContent ? 0 : void 0, - children: [!__experimentalHideHeader && /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("div", { + children: [!__experimentalHideHeader && /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("div", { className: "components-modal__header", - children: [/* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("div", { + children: [/* @__PURE__ */ (0, import_jsx_runtime54.jsxs)("div", { className: "components-modal__header-heading-container", - children: [icon && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("span", { + children: [icon && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("span", { className: "components-modal__icon-container", "aria-hidden": true, children: icon - }), title && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("h1", { + }), title && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("h1", { id: headingId, className: "components-modal__header-heading", children: title })] - }), headerActions, isDismissible && /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(import_jsx_runtime56.Fragment, { - children: [/* @__PURE__ */ (0, import_jsx_runtime56.jsx)(component_default4, { + }), headerActions, isDismissible && /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(import_jsx_runtime54.Fragment, { + children: [/* @__PURE__ */ (0, import_jsx_runtime54.jsx)(component_default4, { marginBottom: 0, marginLeft: 2 - }), /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(button_default, { + }), /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(button_default, { size: "compact", onClick: (event) => closeModal().then(() => onRequestClose(event)), icon: close_default, label: closeButtonLabel || __("Close") })] })] - }), /* @__PURE__ */ (0, import_jsx_runtime56.jsx)("div", { + }), /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("div", { ref: useMergeRefs([childrenContainerRef, focusOnMount === "firstContentElement" ? focusOnMountRef : null]), children })] @@ -53069,7 +53045,7 @@ If there's a particular need for this, please submit a feature request at https: }) }) ); - return (0, import_react_dom.createPortal)(/* @__PURE__ */ (0, import_jsx_runtime56.jsx)(ModalContext.Provider, { + return (0, import_react_dom.createPortal)(/* @__PURE__ */ (0, import_jsx_runtime54.jsx)(ModalContext.Provider, { value: nestedDismissers, children: modal }), document.body); @@ -53118,12 +53094,12 @@ If there's a particular need for this, please submit a feature request at https: })(commonPathProps, ";stroke:currentColor;stroke-linecap:round;transform-origin:50% 50%;animation:1.4s linear infinite both ", spinAnimation, ";" + (false ? "" : "/*# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIkB3b3JkcHJlc3MvY29tcG9uZW50cy9zcmMvc3Bpbm5lci9zdHlsZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBMEMyQyIsImZpbGUiOiJAd29yZHByZXNzL2NvbXBvbmVudHMvc3JjL3NwaW5uZXIvc3R5bGVzLnRzIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBFeHRlcm5hbCBkZXBlbmRlbmNpZXNcbiAqL1xuaW1wb3J0IHN0eWxlZCBmcm9tICdAZW1vdGlvbi9zdHlsZWQnO1xuaW1wb3J0IHsgY3NzLCBrZXlmcmFtZXMgfSBmcm9tICdAZW1vdGlvbi9yZWFjdCc7XG5cbi8qKlxuICogSW50ZXJuYWwgZGVwZW5kZW5jaWVzXG4gKi9cbmltcG9ydCB7IENPTE9SUywgQ09ORklHIH0gZnJvbSAnLi4vdXRpbHMnO1xuXG5jb25zdCBzcGluQW5pbWF0aW9uID0ga2V5ZnJhbWVzYFxuXHRmcm9tIHtcblx0XHR0cmFuc2Zvcm06IHJvdGF0ZSgwZGVnKTtcblx0fVxuXHR0byB7XG5cdFx0dHJhbnNmb3JtOiByb3RhdGUoMzYwZGVnKTtcblx0fVxuIGA7XG5cbmV4cG9ydCBjb25zdCBTdHlsZWRTcGlubmVyID0gc3R5bGVkLnN2Z2Bcblx0d2lkdGg6ICR7IENPTkZJRy5zcGlubmVyU2l6ZSB9cHg7XG5cdGhlaWdodDogJHsgQ09ORklHLnNwaW5uZXJTaXplIH1weDtcblx0ZGlzcGxheTogaW5saW5lLWJsb2NrO1xuXHRtYXJnaW46IDVweCAxMXB4IDA7XG5cdHBvc2l0aW9uOiByZWxhdGl2ZTtcblx0Y29sb3I6ICR7IENPTE9SUy50aGVtZS5hY2NlbnQgfTtcblx0b3ZlcmZsb3c6IHZpc2libGU7XG5cdG9wYWNpdHk6IDE7XG5cdGJhY2tncm91bmQtY29sb3I6IHRyYW5zcGFyZW50O1xuYDtcblxuY29uc3QgY29tbW9uUGF0aFByb3BzID0gY3NzYFxuXHRmaWxsOiB0cmFuc3BhcmVudDtcblx0c3Ryb2tlLXdpZHRoOiAxLjVweDtcbmA7XG5cbmV4cG9ydCBjb25zdCBTcGlubmVyVHJhY2sgPSBzdHlsZWQuY2lyY2xlYFxuXHQkeyBjb21tb25QYXRoUHJvcHMgfTtcblx0c3Ryb2tlOiAkeyBDT0xPUlMuZ3JheVsgMzAwIF0gfTtcbmA7XG5cbmV4cG9ydCBjb25zdCBTcGlubmVySW5kaWNhdG9yID0gc3R5bGVkLnBhdGhgXG5cdCR7IGNvbW1vblBhdGhQcm9wcyB9O1xuXHRzdHJva2U6IGN1cnJlbnRDb2xvcjtcblx0c3Ryb2tlLWxpbmVjYXA6IHJvdW5kO1xuXHR0cmFuc2Zvcm0tb3JpZ2luOiA1MCUgNTAlO1xuXHRhbmltYXRpb246IDEuNHMgbGluZWFyIGluZmluaXRlIGJvdGggJHsgc3BpbkFuaW1hdGlvbiB9O1xuYDtcbiJdfQ== */")); // node_modules/@wordpress/components/build-module/spinner/index.js - var import_jsx_runtime57 = __toESM(require_jsx_runtime()); + var import_jsx_runtime55 = __toESM(require_jsx_runtime()); function UnforwardedSpinner({ className, ...props }, forwardedRef) { - return /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(StyledSpinner, { + return /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(StyledSpinner, { className: clsx_default("components-spinner", className), viewBox: "0 0 100 100", width: "16", @@ -53133,12 +53109,12 @@ If there's a particular need for this, please submit a feature request at https: focusable: "false", ...props, ref: forwardedRef, - children: [/* @__PURE__ */ (0, import_jsx_runtime57.jsx)(SpinnerTrack, { + children: [/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(SpinnerTrack, { cx: "50", cy: "50", r: "50", vectorEffect: "non-scaling-stroke" - }), /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(SpinnerIndicator, { + }), /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(SpinnerIndicator, { d: "m 50 0 a 50 50 0 0 1 50 50", vectorEffect: "non-scaling-stroke" })] @@ -53148,7 +53124,7 @@ If there's a particular need for this, please submit a feature request at https: var spinner_default = Spinner; // node_modules/@wordpress/components/build-module/tab-panel/index.js - var import_jsx_runtime58 = __toESM(require_jsx_runtime()); + var import_jsx_runtime56 = __toESM(require_jsx_runtime()); var extractTabName = (id3) => { if (typeof id3 === "undefined" || id3 === null) { return; @@ -53231,21 +53207,21 @@ If there's a particular need for this, please submit a feature request at https: setTabStoreSelectedId(firstEnabledTab.name); } }, [tabs, selectedTab?.disabled, setTabStoreSelectedId, instanceId]); - return /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { + return /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)("div", { className, ref, - children: [/* @__PURE__ */ (0, import_jsx_runtime58.jsx)(TabList, { + children: [/* @__PURE__ */ (0, import_jsx_runtime56.jsx)(TabList, { store: tabStore, className: "components-tab-panel__tabs", children: tabs.map((tab) => { - return /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Tab, { + return /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(Tab, { id: prependInstanceId(tab.name), className: clsx_default("components-tab-panel__tabs-item", tab.className, { [activeClass]: tab.name === selectedTabName }), disabled: tab.disabled, "aria-controls": `${prependInstanceId(tab.name)}-view`, - render: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(button_default, { + render: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(button_default, { __next40pxDefaultSize: true, icon: tab.icon, label: tab.icon && tab.title, @@ -53254,7 +53230,7 @@ If there's a particular need for this, please submit a feature request at https: children: !tab.icon && tab.title }, tab.name); }) - }), selectedTab && /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(TabPanel, { + }), selectedTab && /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(TabPanel, { id: `${prependInstanceId(selectedTab.name)}-view`, store: tabStore, tabId: prependInstanceId(selectedTab.name), @@ -53267,7 +53243,7 @@ If there's a particular need for this, please submit a feature request at https: var tab_panel_default = TabPanel3; // node_modules/@wordpress/components/build-module/text-control/index.js - var import_jsx_runtime59 = __toESM(require_jsx_runtime()); + var import_jsx_runtime57 = __toESM(require_jsx_runtime()); function UnforwardedTextControl(props, ref) { const { __nextHasNoMarginBottom, @@ -53289,7 +53265,7 @@ If there's a particular need for this, please submit a feature request at https: size: void 0, __next40pxDefaultSize }); - return /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(base_control_default, { + return /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(base_control_default, { __nextHasNoMarginBottom, __associatedWPComponentName: "TextControl", label, @@ -53297,7 +53273,7 @@ If there's a particular need for this, please submit a feature request at https: id: id3, help, className, - children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)("input", { + children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)("input", { className: clsx_default("components-text-control__input", { "is-next-40px-default-size": __next40pxDefaultSize }), @@ -53316,7 +53292,7 @@ If there's a particular need for this, please submit a feature request at https: // src/renderer/index.jsx var import_xterm = __toESM(require_xterm()); - var import_jsx_runtime60 = __toESM(require_jsx_runtime()); + var import_jsx_runtime58 = __toESM(require_jsx_runtime()); var TERMINAL_ALLOWED_SCRIPTS = ["build", "build:dev", "dev", "test", "watch", "grunt"]; var TERMINAL_INSTALL_ALIASES = ["npm install", "npm i", "install"]; var RENAME_INPUT_ID = "rename-site-name-input"; @@ -53690,10 +53666,11 @@ If there's a particular need for this, please submit a feature request at https: removeSetupLog(sitePath); }, [refresh, removeSetupLog]); const onDelete = (0, import_react69.useCallback)(async (sitePath) => { + setSites((prevSites) => prevSites.filter((s) => s !== sitePath)); + removeSetupLog(sitePath); await window.api.deleteSite(sitePath); await refresh(); - removeSetupLog(sitePath); - }, [refresh, removeSetupLog]); + }, [refresh, removeSetupLog, setSites]); const onRename = (0, import_react69.useCallback)(async (sitePath, newLabel) => { try { await window.api.setSiteLabel(sitePath, newLabel); @@ -53725,11 +53702,11 @@ If there's a particular need for this, please submit a feature request at https: const handleSelectSite = (0, import_react69.useCallback)((sitePath) => { setActiveSite(sitePath); }, []); - return /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { display: "flex", height: "100vh", fontFamily: "-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif" }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { width: sidebarCollapsed ? 56 : 280, background: "#1f1f1f", color: "#f7f7f7", display: "flex", flexDirection: "column", transition: "width 0.2s ease", borderRight: "1px solid #2b2b2b" }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { padding: sidebarCollapsed ? "12px 8px" : "16px", borderBottom: "1px solid #2b2b2b" }, children: /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(component_default3, { align: "center", justify: "space-between", children: [ - !sidebarCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { fontWeight: 600 }, children: "WordPress Core" }) : null, - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)( + return /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { display: "flex", height: "100vh", fontFamily: "-apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif" }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { width: sidebarCollapsed ? 56 : 280, background: "#1f1f1f", color: "#f7f7f7", display: "flex", flexDirection: "column", transition: "width 0.2s ease", borderRight: "1px solid #2b2b2b" }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { padding: sidebarCollapsed ? "12px 8px" : "16px", borderBottom: "1px solid #2b2b2b" }, children: /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(component_default3, { align: "center", justify: "space-between", children: [ + !sidebarCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { fontWeight: 600 }, children: "WordPress Core" }) : null, + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( button_default, { icon: sidebarCollapsed ? chevron_right_default : chevron_left_default, @@ -53742,13 +53719,13 @@ If there's a particular need for this, please submit a feature request at https: } ) ] }) }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { flex: 1, overflowY: "auto", padding: sidebarCollapsed ? "12px 8px" : "12px 16px", display: "flex", flexDirection: "column", gap: 8 }, children: sortedSites.length > 0 ? sortedSites.map((sitePath) => { + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { flex: 1, overflowY: "auto", padding: sidebarCollapsed ? "12px 8px" : "12px 16px", display: "flex", flexDirection: "column", gap: 8 }, children: sortedSites.length > 0 ? sortedSites.map((sitePath) => { const meta = siteMeta?.[sitePath] || {}; const siteName = meta.label && meta.label.trim() || sitePath.split("/").pop() || sitePath; const createdLabel = meta.createdAt ? new Date(meta.createdAt).toLocaleString() : ""; const isActive = activeSite === sitePath; const statusLabel = meta.initialized ? "Initialized" : "Not initialized"; - return /* @__PURE__ */ (0, import_jsx_runtime60.jsx)( + return /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( button_default, { onClick: () => handleSelectSite(sitePath), @@ -53764,19 +53741,19 @@ If there's a particular need for this, please submit a feature request at https: padding: sidebarCollapsed ? "8px 0" : "10px 12px", borderRadius: 6 }, - children: sidebarCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { style: { fontWeight: 600 }, children: siteName.slice(0, 1).toUpperCase() }) : /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { display: "flex", flexDirection: "column", alignItems: "flex-start", gap: 2 }, children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { style: { fontWeight: 600 }, children: siteName }) }) + children: sidebarCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { style: { fontWeight: 600 }, children: siteName.slice(0, 1).toUpperCase() }) : /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { display: "flex", flexDirection: "column", alignItems: "flex-start", gap: 2 }, children: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { style: { fontWeight: 600 }, children: siteName }) }) }, sitePath ); - }) : !sidebarCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { fontSize: 12, color: "rgba(255,255,255,0.7)" }, children: "No sites yet." }) : null }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)( + }) : !sidebarCollapsed ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { fontSize: 12, color: "rgba(255,255,255,0.7)" }, children: "No sites yet." }) : null }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( "div", { style: { padding: sidebarCollapsed ? "12px 8px 20px" : "16px 16px 24px", borderTop: "1px solid #2b2b2b" }, - children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)( + children: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( button_default, { icon: plus_default, @@ -53790,9 +53767,9 @@ If there's a particular need for this, please submit a feature request at https: } ) ] }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { flex: 1, background: "#fff", color: "#1d2327", display: "flex", flexDirection: "column" }, children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { flex: 1, overflowY: "auto", padding: "32px 32px 48px" }, children: /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { maxWidth: 1040, margin: "0 auto" }, children: [ - webAvailable ? /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(component_default3, { align: "center", justify: "flex-end", style: { gap: 8, marginBottom: 24 }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)( + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { flex: 1, background: "#fff", color: "#1d2327", display: "flex", flexDirection: "column" }, children: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { flex: 1, overflowY: "auto", padding: "32px 32px 48px" }, children: /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { maxWidth: 1040, margin: "0 auto" }, children: [ + webAvailable ? /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(component_default3, { align: "center", justify: "flex-end", style: { gap: 8, marginBottom: 24 }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( button_default, { isBusy: webStarting, @@ -53801,34 +53778,34 @@ If there's a particular need for this, please submit a feature request at https: children: webUrl ? "Stop Playground web server" : "Start Playground web server" } ), - webStarting || webUrl ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { style: { fontSize: 12 }, children: webStarting ? "Starting\u2026" : /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("a", { href: webUrl || "http://127.0.0.1:39372/", onClick: (e) => { + webStarting || webUrl ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { style: { fontSize: 12 }, children: webStarting ? "Starting\u2026" : /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("a", { href: webUrl || "http://127.0.0.1:39372/", onClick: (e) => { e.preventDefault(); window.api.openExternal(webUrl || "http://127.0.0.1:39372/"); }, children: webUrl || "http://127.0.0.1:39372/" }) }) : null ] }) : null, - webStarting || webUrl || webError || webLogs ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(component_default6, { style: { marginBottom: 24 }, children: /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(component_default8, { children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, justifyContent: "space-between" }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { fontWeight: 600 }, children: "Playground web server" }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { fontSize: 12, color: "#666" }, children: webStarting ? "Starting\u2026" : webUrl ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("a", { href: webUrl, onClick: (e) => { + webStarting || webUrl || webError || webLogs ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(component_default6, { style: { marginBottom: 24 }, children: /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(component_default8, { children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, justifyContent: "space-between" }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { fontWeight: 600 }, children: "Playground web server" }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { fontSize: 12, color: "#666" }, children: webStarting ? "Starting\u2026" : webUrl ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("a", { href: webUrl, onClick: (e) => { e.preventDefault(); window.api.openExternal(webUrl); }, children: webUrl }) : "Stopped" }) ] }), - webError ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { marginTop: 6, color: "#C00", fontSize: 12 }, children: webError }) : null, - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { ref: webLogRef, style: { marginTop: 8, whiteSpace: "pre-wrap", background: "#111", color: "#eee", padding: 8, borderRadius: 6, height: 140, overflow: "auto" }, children: webLogs }) + webError ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { marginTop: 6, color: "#C00", fontSize: 12 }, children: webError }) : null, + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { ref: webLogRef, style: { marginTop: 8, whiteSpace: "pre-wrap", background: "#111", color: "#eee", padding: 8, borderRadius: 6, height: 140, overflow: "auto" }, children: webLogs }) ] }) }) : null, - /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { id: "sites", children: [ - pendingSite && /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(component_default6, { style: { marginBottom: 24 }, children: /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(component_default8, { children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { fontWeight: 600 }, children: "Setting up new site\u2026" }), - downloadPhase && /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { fontSize: 12, color: "#555", marginBottom: 6 }, children: downloadPhase }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { ref: termRef, style: { whiteSpace: "pre-wrap", background: "#111", color: "#eee", padding: 8, borderRadius: 6, height: 140, overflow: "auto" }, children: terminalMsgs }) + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { id: "sites", children: [ + pendingSite && /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(component_default6, { style: { marginBottom: 24 }, children: /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(component_default8, { children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { fontWeight: 600 }, children: "Setting up new site\u2026" }), + downloadPhase && /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { fontSize: 12, color: "#555", marginBottom: 6 }, children: downloadPhase }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { ref: termRef, style: { whiteSpace: "pre-wrap", background: "#111", color: "#eee", padding: 8, borderRadius: 6, height: 140, overflow: "auto" }, children: terminalMsgs }) ] }) }), - sortedSites.length > 0 ? sortedSites.map((s) => /* @__PURE__ */ (0, import_jsx_runtime60.jsx)( + sortedSites.length > 0 ? sortedSites.map((s) => /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( "div", { style: { display: activeSite === s ? "block" : "none" }, "aria-hidden": activeSite === s ? false : true, - children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)( + children: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( SiteRow, { sitePath: s, @@ -53846,27 +53823,27 @@ If there's a particular need for this, please submit a feature request at https: ) }, s - )) : /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(component_default6, { children: /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(component_default8, { children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { marginBottom: 8 }, children: "No sites yet." }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { children: "Use the sidebar to create your first site." }) + )) : /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(component_default6, { children: /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(component_default8, { children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { marginBottom: 8 }, children: "No sites yet." }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { children: "Use the sidebar to create your first site." }) ] }) }) ] }) ] }) }) }), - createModalOpen ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)( + createModalOpen ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( modal_default, { className: "create-site-modal", title: "Create WordPress Core site", onRequestClose: closeCreateModal, shouldCloseOnClickOutside: !createSubmitting, - children: /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)( + children: /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)( "form", { onSubmit: handleCreateModalSubmit, onKeyDown: handleCreateModalKeyDown, style: { display: "flex", flexDirection: "column", gap: 16, color: "#1d2327", colorScheme: "light" }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)( + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( text_control_default, { id: CREATE_SITE_NAME_INPUT_ID, @@ -53878,9 +53855,9 @@ If there's a particular need for this, please submit a feature request at https: autoFocus: true } ), - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("label", { htmlFor: CREATE_SITE_LOCATION_INPUT_ID, style: { fontSize: 12, fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.02em", color: "#1d2327" }, children: "Site location" }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap" }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)( + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("label", { htmlFor: CREATE_SITE_LOCATION_INPUT_ID, style: { fontSize: 12, fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.02em", color: "#1d2327" }, children: "Site location" }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap" }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( "input", { ref: createDirInputRef, @@ -53902,13 +53879,13 @@ If there's a particular need for this, please submit a feature request at https: style: { height: 40, color: "#1d2327", background: "#fff", border: "1px solid #8c8f94", borderRadius: 4, padding: "6px 10px" } } ), - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { style: { fontSize: 12, color: "#3c434a" }, children: createSiteDir || "No folder selected yet." }) + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { style: { fontSize: 12, color: "#3c434a" }, children: createSiteDir || "No folder selected yet." }) ] }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { id: CREATE_SITE_LOCATION_HELP_ID, style: { fontSize: 12, color: "#3c434a", marginTop: -4 }, children: "Choose the parent folder where you want this new site created. We'll add a new directory inside it for the project." }), - createSiteError ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { color: "#d63638", fontSize: 12 }, children: createSiteError }) : null, - /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { display: "flex", justifyContent: "flex-end", gap: 8 }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(button_default, { type: "button", variant: "secondary", onClick: closeCreateModal, disabled: createSubmitting, children: "Cancel" }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(button_default, { type: "submit", variant: "primary", isBusy: createSubmitting, disabled: createSubmitting, children: "Create site" }) + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { id: CREATE_SITE_LOCATION_HELP_ID, style: { fontSize: 12, color: "#3c434a", marginTop: -4 }, children: "Choose the parent folder where you want this new site created. We'll add a new directory inside it for the project." }), + createSiteError ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { color: "#d63638", fontSize: 12 }, children: createSiteError }) : null, + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { display: "flex", justifyContent: "flex-end", gap: 8 }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(button_default, { type: "button", variant: "secondary", onClick: closeCreateModal, disabled: createSubmitting, children: "Cancel" }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(button_default, { type: "submit", variant: "primary", isBusy: createSubmitting, disabled: createSubmitting, children: "Create site" }) ] }) ] } @@ -53926,9 +53903,6 @@ If there's a particular need for this, please submit a feature request at https: const [installing, setInstalling] = (0, import_react69.useState)(false); const [npmLogs, setNpmLogs] = (0, import_react69.useState)(""); const [runtimeLogs, setRuntimeLogs] = (0, import_react69.useState)(""); - const [isPatchOpen, setIsPatchOpen] = (0, import_react69.useState)(false); - const [patchText, setPatchText] = (0, import_react69.useState)(""); - const [patchLoading, setPatchLoading] = (0, import_react69.useState)(false); const [emails, setEmails] = (0, import_react69.useState)([]); const [smtpPort, setSmtpPort] = (0, import_react69.useState)(0); const newEmailUnsubRef = (0, import_react69.useRef)(null); @@ -53943,6 +53917,30 @@ If there's a particular need for this, please submit a feature request at https: const [statusLoading, setStatusLoading] = (0, import_react69.useState)(true); const [waitingForWatch, setWaitingForWatch] = (0, import_react69.useState)(false); const setupLogsRef = (0, import_react69.useRef)(""); + const [prSubmitting, setPRSubmitting] = (0, import_react69.useState)(false); + const [prModalOpen, setPRModalOpen] = (0, import_react69.useState)(false); + const [prMode, setPRMode] = (0, import_react69.useState)("review"); + const [prProgress, setPRProgress] = (0, import_react69.useState)({ step: "", message: "" }); + const [prAuthCode, setPRAuthCode] = (0, import_react69.useState)(null); + const [prProgressLog, setPRProgressLog] = (0, import_react69.useState)([]); + const prLogContainerRef = (0, import_react69.useRef)(null); + (0, import_react69.useEffect)(() => { + if (prMode === "submitting" && prLogContainerRef.current) { + prLogContainerRef.current.scrollTop = prLogContainerRef.current.scrollHeight; + } + }, [prProgressLog, prMode]); + const [prReviewPatch, setPRReviewPatch] = (0, import_react69.useState)(""); + const [prReviewLoading, setPRReviewLoading] = (0, import_react69.useState)(false); + const [prReviewError, setPRReviewError] = (0, import_react69.useState)(""); + const [prStopPending, setPRStopPending] = (0, import_react69.useState)(false); + const [gitHubConnected, setGitHubConnected] = (0, import_react69.useState)(false); + (0, import_react69.useEffect)(() => { + window.api.isGitHubConnected().then(setGitHubConnected); + }, []); + const addPRLog = (0, import_react69.useCallback)((message, command = null, extra = {}) => { + if (!message && !command) return; + setPRProgressLog((prev2) => [...prev2, { message, command, timestamp: extra.timestamp || Date.now(), ...extra }]); + }, []); const npmRef = (0, import_react69.useRef)(null); const runtimeRef = (0, import_react69.useRef)(null); const currentRunIdRef = (0, import_react69.useRef)(null); @@ -54526,26 +54524,164 @@ Try "help" for the list of supported commands. const confirmAnd = async (m, a) => { if (window.confirm(m)) await a(); }; - const openPatchModal = async () => { - setIsPatchOpen(true); - setPatchLoading(true); - setPatchText(""); + const downloadPatchFile = async () => { + if (prReviewLoading || !hasReviewDiff) return; + try { + const res = await window.api.savePatchContent(sitePath, prReviewPatch || ""); + if (res && res.ok && res.filePath) { + alert(`Diff saved to: ${res.filePath}`); + } else if (res && res.canceled) { + } else { + alert(`Error saving diff: ${res && res.error ? res.error : "Unknown error"}`); + } + } catch (e) { + alert(`Error saving diff: ${e && e.message ? e.message : String(e)}`); + } + }; + const refreshPRReviewPatch = (0, import_react69.useCallback)(async () => { + setPRReviewLoading(true); + setPRReviewError(""); try { const res = await window.api.getPatch(sitePath); - if (res && res.ok) setPatchText(res.patch && res.patch.trim().length ? res.patch : "No changes."); - else setPatchText(res && res.error ? `Error: ${res.error}` : "Failed to generate patch"); + if (res && res.ok) { + const patchString = res.patch && res.patch.trim().length ? res.patch : "No changes."; + setPRReviewPatch(patchString); + } else { + throw new Error(res && res.error ? res.error : "Failed to generate diff"); + } } catch (e) { - setPatchText(`Error: ${e && e.message ? e.message : String(e)}`); + setPRReviewError(e && e.message ? e.message : String(e)); } finally { - setPatchLoading(false); + setPRReviewLoading(false); } - }; - const copyPatch = async () => { + }, [sitePath]); + const copyPRReviewPatch = (0, import_react69.useCallback)(async () => { + if (!prReviewPatch) return; try { - await navigator.clipboard.writeText(patchText); + await navigator.clipboard.writeText(prReviewPatch); } catch { } - }; + }, [prReviewPatch]); + const hasReviewDiff = (0, import_react69.useMemo)(() => { + const trimmed = (prReviewPatch || "").trim(); + if (!trimmed) return false; + return trimmed !== "No changes."; + }, [prReviewPatch]); + const resetPRModalState = (0, import_react69.useCallback)(() => { + setPRModalOpen(false); + setPRSubmitting(false); + setPRMode("review"); + setPRProgress({ step: "", message: "" }); + setPRAuthCode(null); + setPRProgressLog([]); + setPRReviewPatch(""); + setPRReviewError(""); + setPRReviewLoading(false); + setPRStopPending(false); + }, []); + const stopPRSubmission = (0, import_react69.useCallback)(async ({ returnToReview = true, refreshDiff = false, afterStop } = {}) => { + if (!prSubmitting) { + if (afterStop) afterStop(); + return; + } + if (prStopPending) return; + setPRStopPending(true); + try { + await window.api.abortSubmitPR(); + } catch (e) { + console.error(e); + } finally { + setPRStopPending(false); + } + setPRSubmitting(false); + setPRAuthCode(null); + addPRLog("Stopped by user"); + setPRProgress({ step: "error", message: "Stopped" }); + if (returnToReview) { + setPRMode("review"); + if (refreshDiff) { + refreshPRReviewPatch(); + } + } + if (afterStop) afterStop(); + }, [prSubmitting, prStopPending, addPRLog, refreshPRReviewPatch]); + const closePRModal = (0, import_react69.useCallback)(() => { + if (prSubmitting && prProgress.step !== "error" && prProgress.step !== "done") { + stopPRSubmission({ returnToReview: false, afterStop: resetPRModalState }); + } else { + resetPRModalState(); + } + }, [prSubmitting, prProgress.step, resetPRModalState, stopPRSubmission]); + const openPRModal = (0, import_react69.useCallback)(() => { + setPRModalOpen(true); + setPRMode("review"); + setPRSubmitting(false); + setPRProgress({ step: "review", message: "" }); + setPRAuthCode(null); + setPRProgressLog([]); + setPRReviewPatch(""); + setPRReviewError(""); + setPRReviewLoading(true); + setPRStopPending(false); + refreshPRReviewPatch(); + }, [refreshPRReviewPatch]); + const beginPRSubmission = (0, import_react69.useCallback)(async () => { + if (prSubmitting) return; + setPRMode("submitting"); + setPRSubmitting(true); + setPRProgress({ step: "", message: "Starting..." }); + setPRAuthCode(null); + setPRProgressLog([]); + try { + const result = await window.api.submitPR(sitePath, (progress2) => { + setPRProgress(progress2); + if (progress2.step === "auth_code") { + setPRAuthCode({ + code: progress2.user_code, + uri: progress2.verification_uri + }); + } else if (progress2.step === "auth" || progress2.step === "fork") { + setPRAuthCode(null); + addPRLog(progress2.message, null, { logType: "info", timestamp: progress2.timestamp }); + } else if (progress2.step === "branch" || progress2.step === "commit") { + addPRLog(progress2.message, progress2.gitCommand, { logType: "info", timestamp: progress2.timestamp }); + } else if (progress2.step === "push") { + const logMeta = { + logType: progress2.logType || "info", + phase: progress2.phase, + loaded: progress2.loaded, + total: progress2.total, + timestamp: progress2.timestamp + }; + addPRLog(progress2.message, progress2.gitCommand, logMeta); + } + }); + if (result.ok) { + await window.api.openExternal(result.prUrl); + addPRLog("\u2713 PR page opened successfully!"); + setPRProgress({ step: "done", message: "Success!" }); + setGitHubConnected(true); + setPRSubmitting(false); + setTimeout(() => { + resetPRModalState(); + }, 3e3); + } else { + addPRLog(`\u2717 Error: ${result.error}`); + setPRProgress({ step: "error", message: `Error: ${result.error}` }); + setPRSubmitting(false); + } + } catch (e) { + if (e.message !== "Aborted by user") { + addPRLog(`\u2717 Error: ${e.message || String(e)}`); + setPRProgress({ step: "error", message: `Error: ${e.message || String(e)}` }); + } else { + setPRProgress({ step: "error", message: "Stopped" }); + setPRMode("review"); + } + setPRSubmitting(false); + setPRAuthCode(null); + } + }, [addPRLog, prSubmitting, resetPRModalState, setGitHubConnected, sitePath]); const savePatch = async () => { try { const res = await window.api.savePatch(sitePath); @@ -54616,7 +54752,7 @@ Try "help" for the list of supported commands. description: "Install npm packages so commands can run.", done: hasNodeModules, ready: true, - action: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)( + action: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( button_default, { isBusy: installing, @@ -54633,7 +54769,7 @@ Try "help" for the list of supported commands. description: "Compile WordPress Core once to generate the initial dist files.", done: hasBuilt, ready: hasNodeModules, - action: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)( + action: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( button_default, { isBusy: building, @@ -54650,8 +54786,8 @@ Try "help" for the list of supported commands. description: "Launch the development server once to complete the WordPress setup wizard.", done: false, ready: hasBuilt, - action: /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)( + action: /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( button_default, { isBusy: starting, @@ -54664,7 +54800,7 @@ Try "help" for the list of supported commands. children: running ? "Stop dev server" : "Start dev server and finish the wizard" } ), - starting || serverUrl ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { style: { fontSize: 12 }, children: starting ? "Starting..." : serverUrl ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("a", { href: serverUrl, onClick: (e) => { + starting || serverUrl ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { style: { fontSize: 12 }, children: starting ? "Starting..." : serverUrl ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("a", { href: serverUrl, onClick: (e) => { e.preventDefault(); window.api.openExternal(serverUrl); }, children: serverUrl }) : null }) : null @@ -54686,12 +54822,12 @@ Try "help" for the list of supported commands. } return { ...step, status }; }); - return /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("section", { style: { display: "flex", flexDirection: "column", gap: 24, paddingBottom: 48 }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(component_default3, { align: "flex-start", justify: "space-between", style: { gap: 16, flexWrap: "wrap" }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { flex: "1 1 440px", minWidth: 0 }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("h1", { style: { margin: 0, fontSize: 28, lineHeight: 1.2 }, children: displayName }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)( + return /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("section", { style: { display: "flex", flexDirection: "column", gap: 24, paddingBottom: 48 }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(component_default3, { align: "flex-start", justify: "space-between", style: { gap: 16, flexWrap: "wrap" }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { flex: "1 1 440px", minWidth: 0 }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("h1", { style: { margin: 0, fontSize: 28, lineHeight: 1.2 }, children: displayName }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( button_default, { icon: edit_default, @@ -54703,75 +54839,37 @@ Try "help" for the list of supported commands. } ) ] }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 8, fontSize: 12, color: "#3c434a", flexWrap: "wrap" }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { style: { display: "inline-flex", alignItems: "center", padding: "2px 8px", borderRadius: 999, fontSize: 11, fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.02em", ...statusStyles }, children: initialized ? "Initialized" : "Uninitialized" }), - createdLabel ? /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("span", { children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 8, fontSize: 12, color: "#3c434a", flexWrap: "wrap" }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { style: { display: "inline-flex", alignItems: "center", padding: "2px 8px", borderRadius: 999, fontSize: 11, fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.02em", ...statusStyles }, children: initialized ? "Initialized" : "Uninitialized" }), + createdLabel ? /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("span", { children: [ "Created ", createdLabel - ] }) : null, - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)( - dropdown_menu_default, - { - label: "Open directory in", - text: "Open directory in", - variant: "tertiary", - size: "small", - icon: chevron_down_default, - iconPosition: "right", - style: { marginLeft: 4, fontSize: 12 }, - controls: [ - { - title: "Finder", - onClick: () => window.api.openDirectory(sitePath) - }, - ...[ - { - key: "vscode", - title: availableEditors.vscode ? "VS Code" : "VS Code (not installed)", - onClick: async () => { - const res = await window.api.openInEditor(sitePath, "vscode"); - if (res && !res.ok) { - alert(`Failed to open in VS Code: ${res.error || "Unknown error"}`); - } - }, - isDisabled: !availableEditors.vscode, - available: availableEditors.vscode - }, - { - key: "phpstorm", - title: availableEditors.phpstorm ? "PHPStorm" : "PHPStorm (not installed)", - onClick: async () => { - const res = await window.api.openInEditor(sitePath, "phpstorm"); - if (res && !res.ok) { - alert(`Failed to open in PHPStorm: ${res.error || "Unknown error"}`); - } - }, - isDisabled: !availableEditors.phpstorm, - available: availableEditors.phpstorm - }, - { - key: "cursor", - title: availableEditors.cursor ? "Cursor" : "Cursor (not installed)", - onClick: async () => { - const res = await window.api.openInEditor(sitePath, "cursor"); - if (res && !res.ok) { - alert(`Failed to open in Cursor: ${res.error || "Unknown error"}`); - } - }, - isDisabled: !availableEditors.cursor, - available: availableEditors.cursor - } - ].sort((a, b) => (b.available ? 1 : 0) - (a.available ? 1 : 0)) - ] - } - ) + ] }) : null ] }) ] }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { display: "flex", alignItems: "flex-start", gap: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)( + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { display: "flex", alignItems: "flex-start", gap: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( dropdown_menu_default, { - label: "More", - text: "", + icon: menu_default, + id: "site-actions-menu", + label: "Site actions", + toggleProps: { + "aria-label": "Site actions menu", + style: { + border: "none", + borderRadius: 999, + boxShadow: "none", + background: "", + padding: 0, + width: 36, + height: 36, + minWidth: 36, + display: "inline-flex", + alignItems: "center", + justifyContent: "center" + } + }, + popoverProps: { placement: "bottom-end" }, controls: [ { title: "Copy path", @@ -54793,12 +54891,12 @@ Try "help" for the list of supported commands. } ) }) ] }), - !skipInit ? /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { padding: 20, border: "1px solid #dcdcde", borderRadius: 12, background: "#fff" }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { fontWeight: 600, fontSize: 16, color: "#1d2327" }, children: "Initial setup checklist" }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { marginTop: 4, fontSize: 13, color: "#3c434a" }, children: "Complete each step to prepare this site for development." }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { marginTop: 16, display: "flex", flexDirection: "column", gap: 12 }, children: stepItems.map((step) => { + !skipInit ? /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { padding: 20, border: "1px solid #dcdcde", borderRadius: 12, background: "#fff" }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { fontWeight: 600, fontSize: 16, color: "#1d2327" }, children: "Initial setup checklist" }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { marginTop: 4, fontSize: 13, color: "#3c434a" }, children: "Complete each step to prepare this site for development." }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { marginTop: 16, display: "flex", flexDirection: "column", gap: 12 }, children: stepItems.map((step) => { const visuals = checklistVisuals[step.status] || checklistVisuals.locked; - return /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)( + return /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)( "div", { style: { @@ -54814,7 +54912,7 @@ Try "help" for the list of supported commands. alignItems: "center" }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { gridRow: "1 / span 2", alignSelf: "center", display: "flex", alignItems: "center", justifyContent: "center", width: 28 }, children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)( + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { gridRow: "1 / span 2", alignSelf: "center", display: "flex", alignItems: "center", justifyContent: "center", width: 28 }, children: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( "span", { style: { @@ -54834,22 +54932,22 @@ Try "help" for the list of supported commands. children: visuals.indicatorContent } ) }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { gridColumn: "2 / 3", display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 12, minWidth: 0, flexWrap: "wrap" }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { fontWeight: 600, color: "#1d2327", lineHeight: 1.4 }, children: step.label }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { fontSize: 11, fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.04em", color: visuals.color, marginLeft: "auto", whiteSpace: "nowrap" }, children: visuals.label }) + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { gridColumn: "2 / 3", display: "flex", alignItems: "baseline", justifyContent: "space-between", gap: 12, minWidth: 0, flexWrap: "wrap" }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { fontWeight: 600, color: "#1d2327", lineHeight: 1.4 }, children: step.label }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { fontSize: 11, fontWeight: 600, textTransform: "uppercase", letterSpacing: "0.04em", color: visuals.color, marginLeft: "auto", whiteSpace: "nowrap" }, children: visuals.label }) ] }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { gridColumn: "2 / 3", fontSize: 12, color: "#3c434a", lineHeight: 1.5 }, children: step.description }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { gridRow: "1 / span 2", gridColumn: "3 / 4", alignSelf: "center", display: "flex", alignItems: "center" }, children: step.action }) + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { gridColumn: "2 / 3", fontSize: 12, color: "#3c434a", lineHeight: 1.5 }, children: step.description }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { gridRow: "1 / span 2", gridColumn: "3 / 4", alignSelf: "center", display: "flex", alignItems: "center" }, children: step.action }) ] }, step.key ); }) }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { marginTop: 12 }, children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(button_default, { variant: "link", onClick: markSkipWizard, style: { textDecoration: "underline" }, children: "Skip initialization wizard" }) }) + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { marginTop: 12 }, children: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(button_default, { variant: "link", onClick: markSkipWizard, style: { textDecoration: "underline" }, children: "Skip initialization wizard" }) }) ] }) : null, - skipInit ? /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { display: "flex", alignItems: "stretch", gap: 12, flexWrap: "wrap" }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)( + skipInit ? /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { display: "flex", alignItems: "stretch", gap: 12, flexWrap: "wrap" }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)( button_default, { isBusy: isServerStarting, @@ -54857,7 +54955,7 @@ Try "help" for the list of supported commands. onClick: toggleDevServer, style: { display: "inline-flex", alignItems: "center", gap: 10, minWidth: 220, justifyContent: "center", padding: "12px 20px", fontSize: 15, borderRadius: 12 }, children: [ - isDevProcessActive ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)( + isDevProcessActive ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( "span", { "aria-hidden": "true", @@ -54872,20 +54970,108 @@ Try "help" for the list of supported commands. } } ) : null, - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("span", { style: { fontWeight: 600 }, children: isDevProcessActive ? isServerStarting ? "Starting dev server..." : "Stop dev server" : "Start dev server" }) + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { style: { fontWeight: 600 }, children: isDevProcessActive ? isServerStarting ? "Starting dev server..." : "Stop dev server" : "Start dev server" }) ] } ), - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)( - button_default, - { - variant: "secondary", - onClick: openPatchModal, - style: { padding: "10px 16px", borderRadius: 10 }, - children: "Submit patch" - } - ), - running && serverUrl ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)( + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { display: "flex", alignItems: "stretch", gap: 0 }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( + button_default, + { + variant: "secondary", + onClick: async () => { + const firstAvailable = availableEditors.cursor ? "cursor" : availableEditors.vscode ? "vscode" : availableEditors.phpstorm ? "phpstorm" : null; + if (firstAvailable) { + const res = await window.api.openInEditor(sitePath, firstAvailable); + if (res && !res.ok) { + alert(`Failed to open: ${res.error || "Unknown error"}`); + } + } else { + window.api.openDirectory(sitePath); + } + }, + style: { padding: "10px 16px", borderRadius: "10px 0 0 10px", borderRight: "1px solid rgba(0,0,0,0.1)", marginRight: 0 }, + children: (() => { + const firstAvailable = availableEditors.cursor ? "Cursor" : availableEditors.vscode ? "VS Code" : availableEditors.phpstorm ? "PHPStorm" : null; + return firstAvailable ? `Open in ${firstAvailable}` : "Open directory"; + })() + } + ), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( + dropdown_menu_default, + { + label: "Open in options", + variant: "secondary", + className: "dropdown-with-sidekick", + icon: chevron_down_default, + style: { borderRadius: "0 10px 10px 0" }, + popoverProps: { placement: "bottom-end" }, + controls: [ + { + title: "Finder", + onClick: () => window.api.openDirectory(sitePath) + }, + { + title: availableEditors.cursor ? "Cursor" : "Cursor (not installed)", + onClick: async () => { + const res = await window.api.openInEditor(sitePath, "cursor"); + if (res && !res.ok) { + alert(`Failed to open in Cursor: ${res.error || "Unknown error"}`); + } + }, + isDisabled: !availableEditors.cursor + }, + { + title: availableEditors.vscode ? "VS Code" : "VS Code (not installed)", + onClick: async () => { + const res = await window.api.openInEditor(sitePath, "vscode"); + if (res && !res.ok) { + alert(`Failed to open in VS Code: ${res.error || "Unknown error"}`); + } + }, + isDisabled: !availableEditors.vscode + }, + { + title: availableEditors.phpstorm ? "PHPStorm" : "PHPStorm (not installed)", + onClick: async () => { + const res = await window.api.openInEditor(sitePath, "phpstorm"); + if (res && !res.ok) { + alert(`Failed to open in PHPStorm: ${res.error || "Unknown error"}`); + } + }, + isDisabled: !availableEditors.phpstorm + } + ] + } + ) + ] }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 4 }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( + button_default, + { + variant: "secondary", + onClick: openPRModal, + isBusy: prSubmitting && prModalOpen, + style: { padding: "10px 20px", borderRadius: 10, alignSelf: "flex-start" }, + children: "Submit patch / PR" + } + ), + gitHubConnected ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( + button_default, + { + variant: "link", + onClick: async () => { + if (confirm("Disconnect GitHub? You will need to re-authorize on your next PR submission.")) { + await window.api.disconnectGitHub(); + setGitHubConnected(false); + } + }, + style: { alignSelf: "flex-start", padding: 0, textDecoration: "underline" }, + children: "Disconnect GitHub" + } + ) : /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { style: { fontSize: 12, color: "#3c434a", paddingLeft: 2 } }) + ] }), + running && serverUrl ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( button_default, { variant: "secondary", @@ -54898,24 +55084,24 @@ Try "help" for the list of supported commands. } ) : null ] }), - isServerStarting || serverUrl ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { fontSize: 13, color: "#1d2327", paddingLeft: 2, display: "flex", flexDirection: "column", gap: 4 }, children: serverUrl ? /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(import_jsx_runtime60.Fragment, { children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("a", { href: serverUrl, onClick: (e) => { + isServerStarting || serverUrl ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { fontSize: 13, color: "#1d2327", paddingLeft: 2, display: "flex", flexDirection: "column", gap: 4 }, children: serverUrl ? /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(import_jsx_runtime58.Fragment, { children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("a", { href: serverUrl, onClick: (e) => { e.preventDefault(); window.api.openExternal(serverUrl); }, children: serverUrl }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("span", { style: { fontSize: 12, color: "#3c434a" }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("span", { style: { fontSize: 12, color: "#3c434a" }, children: [ "Log in with ", - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("code", { children: "admin" }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("code", { children: "admin" }), " / ", - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("code", { children: "admin" }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("code", { children: "admin" }), "." ] }) ] }) : "Dev server is starting\u2026" }) : null ] }) : null, - /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { fontWeight: 600, marginBottom: 8 }, children: "Terminal" }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)( + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { fontWeight: 600, marginBottom: 8 }, children: "Terminal" }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( "div", { ref: terminalContainerRef, @@ -54928,57 +55114,57 @@ Try "help" for the list of supported commands. } } ), - /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { marginTop: 8, fontSize: 12, color: "#3c434a" }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { marginTop: 8, fontSize: 12, color: "#3c434a" }, children: [ "Type ", - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("code", { children: "help" }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("code", { children: "help" }), " to list supported commands. Press ", - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("code", { children: "Ctrl+C" }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("code", { children: "Ctrl+C" }), " to stop the current command." ] }) ] }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { fontWeight: 600, marginBottom: 8 }, children: "Server & WordPress logs" }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { ref: runtimeRef, onScroll: makeOnScroll("runtime"), style: { whiteSpace: "pre-wrap", background: "#111", color: "#eee", padding: 12, borderRadius: 6, height: 220, overflow: "auto" }, children: runtimeLogs }) + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { fontWeight: 600, marginBottom: 8 }, children: "Server & WordPress logs" }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { ref: runtimeRef, onScroll: makeOnScroll("runtime"), style: { whiteSpace: "pre-wrap", background: "#111", color: "#eee", padding: 12, borderRadius: 6, height: 220, overflow: "auto" }, children: runtimeLogs }) ] }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { fontWeight: 600, marginBottom: 8 }, children: "Mail" }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 8 }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { fontSize: 12, color: "#666" }, children: smtpPort ? `SMTP listening on 127.0.0.1:${smtpPort}` : "SMTP will start with the dev server." }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(button_default, { size: "small", variant: "secondary", onClick: clearEmails, children: "Clear emails" }) }) + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { fontWeight: 600, marginBottom: 8 }, children: "Mail" }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 8 }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { fontSize: 12, color: "#666" }, children: smtpPort ? `SMTP listening on 127.0.0.1:${smtpPort}` : "SMTP will start with the dev server." }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(button_default, { size: "small", variant: "secondary", onClick: clearEmails, children: "Clear emails" }) }) ] }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { border: "1px solid #ddd", borderRadius: 6, maxHeight: 220, overflow: "auto" }, children: emails && emails.length ? emails.map((m) => { + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { border: "1px solid #ddd", borderRadius: 6, maxHeight: 220, overflow: "auto" }, children: emails && emails.length ? emails.map((m) => { const when = m.sentAt || m.date; const whenStr = when ? new Date(when).toLocaleString() : ""; - return /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)( + return /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)( "div", { onClick: () => openEmail(m), style: { padding: "8px 10px", cursor: "pointer", borderBottom: "1px solid #eee", display: "flex", gap: 8 }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { flex: "0 0 180px", color: "#555", fontSize: 12 }, children: whenStr }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { flex: "0 0 220px", color: "#333", fontSize: 12, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: m.from || "" }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { flex: "1 1 auto", fontWeight: 600, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: m.subject || "(no subject)" }) + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { flex: "0 0 180px", color: "#555", fontSize: 12 }, children: whenStr }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { flex: "0 0 220px", color: "#333", fontSize: 12, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: m.from || "" }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { flex: "1 1 auto", fontWeight: 600, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: m.subject || "(no subject)" }) ] }, m.id ); - }) : /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { padding: 12, color: "#666" }, children: "No emails yet." }) }) + }) : /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { padding: 12, color: "#666" }, children: "No emails yet." }) }) ] }) ] }), - renameModalOpen ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)( + renameModalOpen ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( modal_default, { title: "Rename site", onRequestClose: closeRenameModal, shouldCloseOnClickOutside: !renaming, - children: /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)( + children: /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)( "form", { onSubmit: handleRenameSubmit, onKeyDown: handleRenameFormKeyDown, style: { display: "flex", flexDirection: "column", gap: 12 }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)( + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( text_control_default, { id: RENAME_INPUT_ID, @@ -54989,73 +55175,206 @@ Try "help" for the list of supported commands. autoFocus: true } ), - renameError ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { color: "#d63638", fontSize: 12 }, children: renameError }) : null, - /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { display: "flex", justifyContent: "flex-end", gap: 8 }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(button_default, { type: "button", variant: "secondary", onClick: closeRenameModal, disabled: renaming, children: "Cancel" }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(button_default, { type: "submit", variant: "primary", isBusy: renaming, disabled: renaming, children: "Save" }) + renameError ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { color: "#d63638", fontSize: 12 }, children: renameError }) : null, + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { display: "flex", justifyContent: "flex-end", gap: 8 }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(button_default, { type: "button", variant: "secondary", onClick: closeRenameModal, disabled: renaming, children: "Cancel" }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(button_default, { type: "submit", variant: "primary", isBusy: renaming, disabled: renaming, children: "Save" }) ] }) ] } ) } ) : null, - isPatchOpen && /* @__PURE__ */ (0, import_jsx_runtime60.jsx)( + prModalOpen && /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( modal_default, { - title: "Patch", - onRequestClose: () => setIsPatchOpen(false), - shouldCloseOnClickOutside: true, + title: "Contribute your code changes to WordPress Core", + onRequestClose: closePRModal, + shouldCloseOnClickOutside: prMode === "review" && !prSubmitting, isFullScreen: true, headerClassName: "patch-modal-header", - children: /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { display: "flex", flexDirection: "column", height: "80vh", gap: 12 }, children: [ - !patchLoading && /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { padding: "12px 16px", background: "#f0f6fc", border: "1px solid #d0d7de", borderRadius: 6, fontSize: 14, lineHeight: 1.5, color: "#24292f" }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("strong", { children: "Next steps:" }), - " Save this patch and submit it to the relevant WordPress Trac ticket at ", - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("a", { href: "#", onClick: (e) => { - e.preventDefault(); - window.api.openExternal("https://core.trac.wordpress.org"); - }, style: { color: "#0969da", cursor: "pointer" }, children: "core.trac.wordpress.org" }) + children: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { padding: 24, height: "100%", display: "flex", flexDirection: "column", gap: 16, overflow: "hidden" }, children: prMode === "review" ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(import_jsx_runtime58.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { display: "flex", gap: 24, flex: 1, minHeight: 0, overflow: "hidden" }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { flex: 2, display: "flex", flexDirection: "column", minHeight: 0, overflow: "hidden" }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", flexWrap: "wrap", gap: 12, marginBottom: 8 }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { fontSize: 16, fontWeight: 600 }, children: "Review diff against trunk" }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { fontSize: 12, color: "#63707b" }, children: "Comparing to the latest wordpress-develop/trunk." }) + ] }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { display: "flex", gap: 8, flexWrap: "wrap" }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( + button_default, + { + variant: "secondary", + onClick: refreshPRReviewPatch, + isBusy: prReviewLoading, + disabled: prReviewLoading, + children: "Refresh diff" + } + ), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( + button_default, + { + variant: "secondary", + onClick: copyPRReviewPatch, + disabled: !hasReviewDiff || prReviewLoading, + children: "Copy diff" + } + ) + ] }) + ] }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { border: "1px solid #d0d7de", borderRadius: 8, background: "#0b0d12", flex: 1, minHeight: 0, position: "relative", overflow: "hidden" }, children: prReviewLoading ? /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { display: "flex", alignItems: "center", justifyContent: "center", height: "100%", color: "#f6f8fa", gap: 8 }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(spinner_default, {}), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { children: "Generating diff\u2026" }) + ] }) : prReviewError ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { padding: 16, color: "#d63638", background: "#fff1f0", borderRadius: 8 }, children: prReviewError }) : /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("pre", { style: { margin: 0, whiteSpace: "pre-wrap", padding: 16, color: "#f6f8fa", height: "100%", overflowY: "auto", fontSize: 13 }, children: prReviewPatch || "No changes." }) }), + !prReviewLoading && !hasReviewDiff ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { fontSize: 13, color: "#6c6f72", marginTop: 8 }, children: "No changes detected relative to trunk." }) : null ] }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { position: "relative", flex: 1, minHeight: 0 }, children: patchLoading ? /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", height: "100%", gap: 16 }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(spinner_default, {}), - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { color: "#666", fontSize: 14 }, children: "Generating patch..." }) - ] }) : /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(import_jsx_runtime60.Fragment, { children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { position: "absolute", top: 8, right: 8, zIndex: 2, display: "flex", gap: 8 }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)( - button_default, - { - icon: download_default, - label: "Save", - onClick: savePatch, - style: { - background: "#fff", - border: "1px solid #ddd", - color: "#111", - boxShadow: "none" + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { flex: "0 0 320px", display: "flex", flexDirection: "column", gap: 16, overflow: "hidden" }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { border: "1px solid #d0d7de", borderRadius: 12, padding: 16, background: "#fff", display: "flex", flexDirection: "column", gap: 12 }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { fontSize: 15, fontWeight: 600 }, children: "Submit patch via Trac" }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { fontSize: 13, color: "#495157", marginTop: 4 }, children: "Download the diff and attach it to a WordPress Core Trac ticket." }) + ] }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( + button_default, + { + variant: "secondary", + onClick: downloadPatchFile, + disabled: prReviewLoading || !hasReviewDiff, + children: "Download .patch" } - } - ), - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)( + ), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( + button_default, + { + variant: "secondary", + onClick: copyPRReviewPatch, + disabled: !hasReviewDiff || prReviewLoading, + children: "Copy diff" + } + ) + ] }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { fontSize: 12, color: "#6c6f72" }, children: [ + "Need a refresher? ", + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("a", { href: "#", onClick: (e) => { + e.preventDefault(); + window.api.openExternal("https://adamadam.blog/how-to-contribute-your-first-patch-to-wordpress-core-via-trac/"); + }, style: { color: "#0969da" }, children: "How to upload patches to Trac" }), + "." + ] }) + ] }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { border: "1px solid #d0d7de", borderRadius: 12, padding: 16, background: "#fff", display: "flex", flexDirection: "column", gap: 12 }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { fontSize: 15, fontWeight: 600 }, children: "Submit via GitHub" }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { fontSize: 13, color: "#495157", marginTop: 4 }, children: "We\u2019ll authenticate with GitHub, push a topic branch to your fork, and open the PR form for you." }) + ] }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( button_default, { - icon: copy_default, - label: "Copy", - onClick: copyPatch, - style: { - background: "#fff", - border: "1px solid #ddd", - color: "#111", - boxShadow: "none" - } + variant: "primary", + onClick: beginPRSubmission, + disabled: prReviewLoading || !hasReviewDiff, + isBusy: prSubmitting, + children: "Submit as a pull request" } - ) - ] }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("pre", { style: { margin: 0, whiteSpace: "pre-wrap", background: "#111", color: "#eee", padding: 12, borderRadius: 6, height: "100%", overflowY: "auto" }, children: patchText && patchText.trim().length ? patchText : "No changes." }) - ] }) }) - ] }) + ), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { fontSize: 12, color: "#6c6f72" }, children: gitHubConnected ? "Connected to GitHub. You can disconnect from the main screen." : "" }) + ] }) + ] }) + ] }) }) : prAuthCode ? /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 16, height: "100%", justifyContent: "center", alignItems: "center", textAlign: "center" }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { padding: 24, background: "#f6f8fa", borderRadius: 12, border: "1px solid #d0d7de", maxWidth: 420 }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { fontSize: 14, marginBottom: 8, fontWeight: 600 }, children: "Visit this page on any device:" }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( + "a", + { + href: "#", + onClick: (e) => { + e.preventDefault(); + window.api.openExternal(prAuthCode.uri); + }, + style: { fontSize: 16, color: "#0969da", cursor: "pointer", textDecoration: "underline" }, + children: prAuthCode.uri + } + ), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { fontSize: 14, marginTop: 24, marginBottom: 8, fontWeight: 600 }, children: "Enter the code:" }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { fontSize: 28, fontWeight: "bold", fontFamily: "monospace", letterSpacing: "0.2em", color: "#24292f" }, children: prAuthCode.code }) + ] }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, fontSize: 14, color: "#666" }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(spinner_default, {}), + "Waiting for authorization\u2026" + ] }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(button_default, { variant: "secondary", onClick: () => stopPRSubmission({ refreshDiff: true }), style: { color: "#cf222e" }, children: "Cancel" }) + ] }) : /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { display: "flex", flexDirection: "column", height: "100%" }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( + "div", + { + ref: prLogContainerRef, + style: { flex: 1, border: "1px solid #d0d7de", borderRadius: 8, background: "#f6f8fa", padding: 16, overflowY: "auto" }, + children: prProgressLog.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { textAlign: "center", padding: "20px", color: "#666" }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(spinner_default, {}), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { marginTop: 12 }, children: "Starting\u2026" }) + ] }) : /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { fontFamily: "monospace", fontSize: 13, lineHeight: 1.8 }, children: [ + prProgressLog.map((log, idx) => /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { marginBottom: 12 }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( + "div", + { + style: { + color: log.logType === "remote" ? "#0f172a" : log.logType === "progress" ? "#1f2328" : "#24292f", + fontWeight: log.logType === "info" ? 600 : 400, + whiteSpace: "pre-wrap" + }, + children: log.message + } + ), + log.command && /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { marginLeft: 16, marginTop: 4, color: "#666", fontSize: 12 }, children: [ + "$ ", + log.command + ] }), + log.logType === "progress" && typeof log.loaded === "number" && typeof log.total === "number" ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { marginTop: 4, height: 4, background: "#d8dee4", borderRadius: 999 }, children: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( + "div", + { + style: { + width: `${Math.min(100, Math.round(log.loaded / Math.max(log.total, 1) * 100))}%`, + height: "100%", + background: "#0969da", + borderRadius: 999 + } + } + ) }) : null, + log.timestamp ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { marginTop: 4, fontSize: 11, color: "#6e7781" }, children: new Date(log.timestamp).toLocaleTimeString([], { hour12: false }) }) : null + ] }, idx)), + prSubmitting && prProgress.step !== "error" && prProgress.step !== "done" && /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { display: "flex", flexDirection: "column", gap: 4, marginTop: 8 }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8 }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(spinner_default, {}), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("span", { style: { color: "#666" }, children: "Working\u2026" }) + ] }), + prProgress.step === "push" && prProgress.message ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { fontSize: 12, color: "#444", fontFamily: "monospace" }, children: prProgress.message }) : null + ] }) + ] }) + } + ), + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { display: "flex", gap: 8, justifyContent: "flex-end", marginTop: 16 }, children: prSubmitting && prProgress.step !== "error" && prProgress.step !== "done" ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( + button_default, + { + variant: "secondary", + onClick: () => stopPRSubmission({ refreshDiff: true }), + isBusy: prStopPending, + disabled: prStopPending, + style: { color: "#cf222e" }, + children: "Stop" + } + ) : /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( + button_default, + { + variant: "secondary", + onClick: closePRModal, + children: "Dismiss" + } + ) }) + ] }) }) } ), - isEmailOpen && activeEmail && /* @__PURE__ */ (0, import_jsx_runtime60.jsx)( + isEmailOpen && activeEmail && /* @__PURE__ */ (0, import_jsx_runtime58.jsx)( modal_default, { title: activeEmail.subject || "Email", @@ -55065,37 +55384,37 @@ Try "help" for the list of supported commands. }, shouldCloseOnClickOutside: true, isFullScreen: true, - children: /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { padding: 8 }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { style: { marginBottom: 8, fontSize: 12, color: "#444" }, children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("strong", { children: "From:" }), + children: /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { padding: 8 }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { style: { marginBottom: 8, fontSize: 12, color: "#444" }, children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("strong", { children: "From:" }), " ", activeEmail.from || "" ] }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("strong", { children: "To:" }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("strong", { children: "To:" }), " ", activeEmail.to || "" ] }), - activeEmail.cc ? /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("strong", { children: "CC:" }), + activeEmail.cc ? /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("strong", { children: "CC:" }), " ", activeEmail.cc ] }) : null, - /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)("div", { children: [ - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("strong", { children: "Date:" }), + /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)("div", { children: [ + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("strong", { children: "Date:" }), " ", activeEmail.sentAt ? new Date(activeEmail.sentAt).toLocaleString() : activeEmail.date ? new Date(activeEmail.date).toLocaleString() : "" ] }) ] }), - /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(tab_panel_default, { className: "email-tabs", activeClass: "is-active", onSelect: (n) => setEmailViewTab(n), tabs: [{ name: "rendered", title: "Rendered" }, { name: "raw", title: "Raw" }], children: (tab) => tab.name === "rendered" ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { style: { border: "1px solid #ddd", borderRadius: 6, padding: 12, minHeight: "60vh", background: "#fff" }, children: activeEmail.html ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("div", { dangerouslySetInnerHTML: { __html: String(activeEmail.html) } }) : /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("pre", { style: { whiteSpace: "pre-wrap", margin: 0 }, children: activeEmail.text || "" }) }) : /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("pre", { style: { whiteSpace: "pre-wrap", margin: 0, background: "#111", color: "#eee", padding: 12, borderRadius: 6, minHeight: "60vh", overflow: "auto" }, children: activeEmail.raw || activeEmail.text || "" }) }) + /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(tab_panel_default, { className: "email-tabs", activeClass: "is-active", onSelect: (n) => setEmailViewTab(n), tabs: [{ name: "rendered", title: "Rendered" }, { name: "raw", title: "Raw" }], children: (tab) => tab.name === "rendered" ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { style: { border: "1px solid #ddd", borderRadius: 6, padding: 12, minHeight: "60vh", background: "#fff" }, children: activeEmail.html ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("div", { dangerouslySetInnerHTML: { __html: String(activeEmail.html) } }) : /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("pre", { style: { whiteSpace: "pre-wrap", margin: 0 }, children: activeEmail.text || "" }) }) : /* @__PURE__ */ (0, import_jsx_runtime58.jsx)("pre", { style: { whiteSpace: "pre-wrap", margin: 0, background: "#111", color: "#eee", padding: 12, borderRadius: 6, minHeight: "60vh", overflow: "auto" }, children: activeEmail.raw || activeEmail.text || "" }) }) ] }) } ) ] }); } var root = (0, import_client2.createRoot)(document.getElementById("root")); - root.render(/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(App, {})); + root.render(/* @__PURE__ */ (0, import_jsx_runtime58.jsx)(App, {})); })(); /*! Bundled license information: diff --git a/src/renderer/index.jsx b/src/renderer/index.jsx index b575c0b..e8b4673 100644 --- a/src/renderer/index.jsx +++ b/src/renderer/index.jsx @@ -11,7 +11,7 @@ import { TextControl, Spinner } from '@wordpress/components'; -import { plus, chevronLeft, chevronRight, copy as copyIcon, edit, download, chevronDown } from '@wordpress/icons'; +import { plus, chevronLeft, chevronRight, edit, chevronDown, menu } from '@wordpress/icons'; import '@wordpress/components/build-style/style.css'; import { Terminal } from 'xterm'; import 'xterm/css/xterm.css'; @@ -382,10 +382,13 @@ function App() { }, [refresh, removeSetupLog]); const onDelete = useCallback(async (sitePath) => { + // Remove from UI immediately + setSites(prevSites => prevSites.filter(s => s !== sitePath)); + removeSetupLog(sitePath); + // Delete from filesystem in background await window.api.deleteSite(sitePath); await refresh(); - removeSetupLog(sitePath); - }, [refresh, removeSetupLog]); + }, [refresh, removeSetupLog, setSites]); const onRename = useCallback(async (sitePath, newLabel) => { try { @@ -647,9 +650,6 @@ function SiteRow({ sitePath, initialized, createdAt, label, onInitialized, onFor const [installing, setInstalling] = useState(false); const [npmLogs, setNpmLogs] = useState(''); const [runtimeLogs, setRuntimeLogs] = useState(''); - const [isPatchOpen, setIsPatchOpen] = useState(false); - const [patchText, setPatchText] = useState(''); - const [patchLoading, setPatchLoading] = useState(false); const [emails, setEmails] = useState([]); const [smtpPort, setSmtpPort] = useState(0); const newEmailUnsubRef = useRef(null); @@ -664,6 +664,33 @@ function SiteRow({ sitePath, initialized, createdAt, label, onInitialized, onFor const [statusLoading, setStatusLoading] = useState(true); const [waitingForWatch, setWaitingForWatch] = useState(false); const setupLogsRef = useRef(''); + const [prSubmitting, setPRSubmitting] = useState(false); + const [prModalOpen, setPRModalOpen] = useState(false); + const [prMode, setPRMode] = useState('review'); + const [prProgress, setPRProgress] = useState({ step: '', message: '' }); + const [prAuthCode, setPRAuthCode] = useState(null); + const [prProgressLog, setPRProgressLog] = useState([]); + const prLogContainerRef = useRef(null); + useEffect(() => { + if (prMode === 'submitting' && prLogContainerRef.current) { + prLogContainerRef.current.scrollTop = prLogContainerRef.current.scrollHeight; + } + }, [prProgressLog, prMode]); + const [prReviewPatch, setPRReviewPatch] = useState(''); + const [prReviewLoading, setPRReviewLoading] = useState(false); + const [prReviewError, setPRReviewError] = useState(''); + const [prStopPending, setPRStopPending] = useState(false); + const [gitHubConnected, setGitHubConnected] = useState(false); + + // Check GitHub connection status + useEffect(() => { + window.api.isGitHubConnected().then(setGitHubConnected); + }, []); + + const addPRLog = useCallback((message, command = null, extra = {}) => { + if (!message && !command) return; + setPRProgressLog((prev) => [...prev, { message, command, timestamp: extra.timestamp || Date.now(), ...extra }]); + }, []); // sticky refs per log const npmRef = useRef(null); @@ -1220,24 +1247,173 @@ function SiteRow({ sitePath, initialized, createdAt, label, onInitialized, onFor }, [sitePath]); const confirmAnd = async (m,a)=>{ if(window.confirm(m)) await a(); }; - const openPatchModal = async ()=>{ - setIsPatchOpen(true); - setPatchLoading(true); - setPatchText(''); + const downloadPatchFile = async ()=>{ + if (prReviewLoading || !hasReviewDiff) return; + try { + const res = await window.api.savePatchContent(sitePath, prReviewPatch || ''); + if (res && res.ok && res.filePath) { + alert(`Diff saved to: ${res.filePath}`); + } else if (res && res.canceled) { + // User canceled, do nothing + } else { + alert(`Error saving diff: ${res && res.error ? res.error : 'Unknown error'}`); + } + } catch (e) { + alert(`Error saving diff: ${e && e.message ? e.message : String(e)}`); + } + }; + + const refreshPRReviewPatch = useCallback(async () => { + setPRReviewLoading(true); + setPRReviewError(''); try { const res = await window.api.getPatch(sitePath); - if (res && res.ok) setPatchText((res.patch && res.patch.trim().length) ? res.patch : 'No changes.'); - else setPatchText(res && res.error ? `Error: ${res.error}` : 'Failed to generate patch'); + if (res && res.ok) { + const patchString = res.patch && res.patch.trim().length ? res.patch : 'No changes.'; + setPRReviewPatch(patchString); + } else { + throw new Error(res && res.error ? res.error : 'Failed to generate diff'); + } } catch (e) { - setPatchText(`Error: ${e && e.message ? e.message : String(e)}`); + setPRReviewError(e && e.message ? e.message : String(e)); } finally { - setPatchLoading(false); + setPRReviewLoading(false); } - }; + }, [sitePath]); - const copyPatch = async ()=>{ - try { await navigator.clipboard.writeText(patchText); } catch {} - }; + const copyPRReviewPatch = useCallback(async () => { + if (!prReviewPatch) return; + try { await navigator.clipboard.writeText(prReviewPatch); } catch {} + }, [prReviewPatch]); + + const hasReviewDiff = useMemo(() => { + const trimmed = (prReviewPatch || '').trim(); + if (!trimmed) return false; + return trimmed !== 'No changes.'; + }, [prReviewPatch]); + + const resetPRModalState = useCallback(() => { + setPRModalOpen(false); + setPRSubmitting(false); + setPRMode('review'); + setPRProgress({ step: '', message: '' }); + setPRAuthCode(null); + setPRProgressLog([]); + setPRReviewPatch(''); + setPRReviewError(''); + setPRReviewLoading(false); + setPRStopPending(false); + }, []); + + const stopPRSubmission = useCallback(async ({ returnToReview = true, refreshDiff = false, afterStop } = {}) => { + if (!prSubmitting) { + if (afterStop) afterStop(); + return; + } + if (prStopPending) return; + setPRStopPending(true); + try { + await window.api.abortSubmitPR(); + } catch (e) { + console.error(e); + } finally { + setPRStopPending(false); + } + setPRSubmitting(false); + setPRAuthCode(null); + addPRLog('Stopped by user'); + setPRProgress({ step: 'error', message: 'Stopped' }); + if (returnToReview) { + setPRMode('review'); + if (refreshDiff) { + refreshPRReviewPatch(); + } + } + if (afterStop) afterStop(); + }, [prSubmitting, prStopPending, addPRLog, refreshPRReviewPatch]); + + const closePRModal = useCallback(() => { + if (prSubmitting && prProgress.step !== 'error' && prProgress.step !== 'done') { + stopPRSubmission({ returnToReview: false, afterStop: resetPRModalState }); + } else { + resetPRModalState(); + } + }, [prSubmitting, prProgress.step, resetPRModalState, stopPRSubmission]); + + const openPRModal = useCallback(() => { + setPRModalOpen(true); + setPRMode('review'); + setPRSubmitting(false); + setPRProgress({ step: 'review', message: '' }); + setPRAuthCode(null); + setPRProgressLog([]); + setPRReviewPatch(''); + setPRReviewError(''); + setPRReviewLoading(true); + setPRStopPending(false); + refreshPRReviewPatch(); + }, [refreshPRReviewPatch]); + + const beginPRSubmission = useCallback(async () => { + if (prSubmitting) return; + setPRMode('submitting'); + setPRSubmitting(true); + setPRProgress({ step: '', message: 'Starting...' }); + setPRAuthCode(null); + setPRProgressLog([]); + + try { + const result = await window.api.submitPR(sitePath, (progress) => { + setPRProgress(progress); + + if (progress.step === 'auth_code') { + setPRAuthCode({ + code: progress.user_code, + uri: progress.verification_uri + }); + } else if (progress.step === 'auth' || progress.step === 'fork') { + setPRAuthCode(null); + addPRLog(progress.message, null, { logType: 'info', timestamp: progress.timestamp }); + } else if (progress.step === 'branch' || progress.step === 'commit') { + addPRLog(progress.message, progress.gitCommand, { logType: 'info', timestamp: progress.timestamp }); + } else if (progress.step === 'push') { + const logMeta = { + logType: progress.logType || 'info', + phase: progress.phase, + loaded: progress.loaded, + total: progress.total, + timestamp: progress.timestamp + }; + addPRLog(progress.message, progress.gitCommand, logMeta); + } + }); + + if (result.ok) { + await window.api.openExternal(result.prUrl); + addPRLog('✓ PR page opened successfully!'); + setPRProgress({ step: 'done', message: 'Success!' }); + setGitHubConnected(true); + setPRSubmitting(false); + setTimeout(() => { + resetPRModalState(); + }, 3000); + } else { + addPRLog(`✗ Error: ${result.error}`); + setPRProgress({ step: 'error', message: `Error: ${result.error}` }); + setPRSubmitting(false); + } + } catch (e) { + if (e.message !== 'Aborted by user') { + addPRLog(`✗ Error: ${e.message || String(e)}`); + setPRProgress({ step: 'error', message: `Error: ${e.message || String(e)}` }); + } else { + setPRProgress({ step: 'error', message: 'Stopped' }); + setPRMode('review'); + } + setPRSubmitting(false); + setPRAuthCode(null); + } + }, [addPRLog, prSubmitting, resetPRModalState, setGitHubConnected, sitePath]); const savePatch = async ()=>{ try { @@ -1404,65 +1580,30 @@ function SiteRow({ sitePath, initialized, createdAt, label, onInitialized, onFor {initialized ? 'Initialized' : 'Uninitialized'} {createdLabel ? Created {createdLabel} : null} - window.api.openDirectory(sitePath) - }, - ...[ - { - key: 'vscode', - title: availableEditors.vscode ? 'VS Code' : 'VS Code (not installed)', - onClick: async () => { - const res = await window.api.openInEditor(sitePath, 'vscode'); - if (res && !res.ok) { - alert(`Failed to open in VS Code: ${res.error || 'Unknown error'}`); - } - }, - isDisabled: !availableEditors.vscode, - available: availableEditors.vscode - }, - { - key: 'phpstorm', - title: availableEditors.phpstorm ? 'PHPStorm' : 'PHPStorm (not installed)', - onClick: async () => { - const res = await window.api.openInEditor(sitePath, 'phpstorm'); - if (res && !res.ok) { - alert(`Failed to open in PHPStorm: ${res.error || 'Unknown error'}`); - } - }, - isDisabled: !availableEditors.phpstorm, - available: availableEditors.phpstorm - }, - { - key: 'cursor', - title: availableEditors.cursor ? 'Cursor' : 'Cursor (not installed)', - onClick: async () => { - const res = await window.api.openInEditor(sitePath, 'cursor'); - if (res && !res.ok) { - alert(`Failed to open in Cursor: ${res.error || 'Unknown error'}`); - } - }, - isDisabled: !availableEditors.cursor, - available: availableEditors.cursor - } - ].sort((a, b) => (b.available ? 1 : 0) - (a.available ? 1 : 0)) - ]} - />
{isDevProcessActive ? (isServerStarting ? 'Starting dev server...' : 'Stop dev server') : 'Start dev server'} - +
+ + window.api.openDirectory(sitePath) + }, + { + title: availableEditors.cursor ? 'Cursor' : 'Cursor (not installed)', + onClick: async () => { + const res = await window.api.openInEditor(sitePath, 'cursor'); + if (res && !res.ok) { + alert(`Failed to open in Cursor: ${res.error || 'Unknown error'}`); + } + }, + isDisabled: !availableEditors.cursor + }, + { + title: availableEditors.vscode ? 'VS Code' : 'VS Code (not installed)', + onClick: async () => { + const res = await window.api.openInEditor(sitePath, 'vscode'); + if (res && !res.ok) { + alert(`Failed to open in VS Code: ${res.error || 'Unknown error'}`); + } + }, + isDisabled: !availableEditors.vscode + }, + { + title: availableEditors.phpstorm ? 'PHPStorm' : 'PHPStorm (not installed)', + onClick: async () => { + const res = await window.api.openInEditor(sitePath, 'phpstorm'); + if (res && !res.ok) { + alert(`Failed to open in PHPStorm: ${res.error || 'Unknown error'}`); + } + }, + isDisabled: !availableEditors.phpstorm + } + ]} + /> +
+
+ + {gitHubConnected ? ( + + ) : ( + + )} +
{running && serverUrl ? ( + +
+ +
+ {prReviewLoading ? ( +
+ + Generating diff… +
+ ) : prReviewError ? ( +
{prReviewError}
+ ) : ( +
+                          {prReviewPatch || 'No changes.'}
+                        
+ )} +
+ {!prReviewLoading && !hasReviewDiff ? ( +
No changes detected relative to trunk.
+ ) : null} + +
+
+
+
Submit patch via Trac
+
Download the diff and attach it to a WordPress Core Trac ticket.
+
+
+ + +
+
+ Need a refresher? { e.preventDefault(); window.api.openExternal('https://adamadam.blog/how-to-contribute-your-first-patch-to-wordpress-core-via-trac/'); }} style={{ color: '#0969da' }}>How to upload patches to Trac. +
+
+
+
+
Submit via GitHub
+
We’ll authenticate with GitHub, push a topic branch to your fork, and open the PR form for you.
+
+ +
+ {gitHubConnected ? 'Connected to GitHub. You can disconnect from the main screen.' : ''} +
+
+
+ + + ) : prAuthCode ? ( +
+
+
Visit this page on any device:
+ { e.preventDefault(); window.api.openExternal(prAuthCode.uri); }} + style={{ fontSize: 16, color: '#0969da', cursor: 'pointer', textDecoration: 'underline' }} + > + {prAuthCode.uri} + +
Enter the code:
+
+ {prAuthCode.code} +
+
+
-
Generating patch...
+ Waiting for authorization…
- ) : ( - <> -
+ +
+ ) : ( +
+
+ {prProgressLog.length === 0 ? ( +
+ +
Starting…
+
+ ) : ( +
+ {prProgressLog.map((log, idx) => ( +
+
{log.message}
+ {log.command && ( +
+ $ {log.command} +
+ )} + {log.logType === 'progress' && typeof log.loaded === 'number' && typeof log.total === 'number' ? ( +
+
+
+ ) : null} + {log.timestamp ? ( +
+ {new Date(log.timestamp).toLocaleTimeString([], { hour12: false })} +
+ ) : null} +
+ ))} + {prSubmitting && prProgress.step !== 'error' && prProgress.step !== 'done' && ( +
+
+ + Working… +
+ {prProgress.step === 'push' && prProgress.message ? ( +
+ {prProgress.message} +
+ ) : null} +
+ )} +
+ )} +
+
+ {prSubmitting && prProgress.step !== 'error' && prProgress.step !== 'done' ? ( + ) : (
-
-                    {patchText && patchText.trim().length ? patchText : 'No changes.'}
-                  
- - )} -
+ variant="secondary" + onClick={closePRModal} + > + Dismiss + + )} +
+
+ )} )}