From ffba2136dbc88ef6a570398184c355eaeed5424e Mon Sep 17 00:00:00 2001 From: Raj Siva-Rajah <5361908+binaryfire@users.noreply.github.com> Date: Wed, 28 Oct 2026 19:06:19 +0000 Subject: [PATCH 01/16] Format maintained frontend sources with Vite+ Apply the shared formatter conventions to frontend files whose changes are entirely formatting. Preserve their runtime behavior, upstream names, comments and structure. Keep this mechanical pass separate so Git blame can ignore it without hiding the dashboard fixes or tooling changes that follow. Files that also require functional changes stay with their corresponding changes. --- .github/scripts/submission-cleanup.cjs | 163 +++++++--- .github/scripts/submission-cleanup.test.cjs | 152 ++++++--- .../resources/js/components/ui/attachment.jsx | 303 ++++++++---------- .../resources/js/components/ui/bubble.jsx | 176 +++++----- .../resources/js/components/ui/button.jsx | 97 +++--- .../resources/js/components/ui/input.jsx | 37 +-- .../resources/js/components/ui/marker.jsx | 100 +++--- .../js/components/ui/message-scroller.jsx | 190 +++++------ .../resources/js/components/ui/message.jsx | 148 ++++----- .../ai/workbench/resources/js/pages/Chat.jsx | 10 +- .../resources/exceptions/renderer/scripts.js | 2 +- .../resources/views/html/themes/default.css | 9 +- src/telescope/resources/js/base.js | 2 +- .../resources/js/components/Alert.vue | 24 +- .../resources/js/components/CopyClipboard.vue | 14 +- .../js/components/ExceptionCodePreview.vue | 8 +- .../resources/js/components/IndexScreen.vue | 207 ++++++------ .../resources/js/components/PreviewScreen.vue | 72 ++--- .../js/components/RelatedEntries.vue | 144 +++++---- .../resources/js/components/Stacktrace.vue | 10 +- .../resources/js/screens/batches/preview.vue | 4 +- .../resources/js/screens/cache/index.vue | 6 +- .../resources/js/screens/cache/preview.vue | 15 +- .../js/screens/client-requests/index.vue | 8 +- .../js/screens/client-requests/preview.vue | 10 +- .../resources/js/screens/commands/preview.vue | 8 +- .../resources/js/screens/dumps/index.vue | 92 +++--- .../resources/js/screens/events/preview.vue | 8 +- .../js/screens/exceptions/preview.vue | 36 ++- .../resources/js/screens/gates/index.vue | 8 +- .../resources/js/screens/gates/preview.vue | 11 +- .../resources/js/screens/jobs/index.vue | 6 +- .../resources/js/screens/jobs/preview.vue | 22 +- .../resources/js/screens/logs/index.vue | 6 +- .../resources/js/screens/logs/preview.vue | 17 +- .../resources/js/screens/mail/index.vue | 18 +- .../resources/js/screens/mail/preview.vue | 17 +- .../resources/js/screens/models/index.vue | 6 +- .../resources/js/screens/models/preview.vue | 9 +- .../js/screens/notifications/preview.vue | 4 +- .../resources/js/screens/queries/preview.vue | 8 +- .../resources/js/screens/redis/preview.vue | 4 +- .../resources/js/screens/requests/index.vue | 6 +- .../resources/js/screens/requests/preview.vue | 10 +- .../resources/js/screens/reverb/preview.vue | 2 +- .../resources/js/screens/schedule/preview.vue | 14 +- .../resources/js/screens/views/preview.vue | 11 +- src/telescope/resources/sass/base.scss | 4 +- src/telescope/resources/sass/styles.scss | 4 +- src/wayfinder/resources/js/wayfinder.ts | 19 +- src/wayfinder/tests/build.ts | 14 +- 51 files changed, 1147 insertions(+), 1128 deletions(-) diff --git a/.github/scripts/submission-cleanup.cjs b/.github/scripts/submission-cleanup.cjs index 230e53555c..ed49a1bab1 100644 --- a/.github/scripts/submission-cleanup.cjs +++ b/.github/scripts/submission-cleanup.cjs @@ -11,7 +11,8 @@ module.exports = async function cleanup({ github, context, core, now = new Date( async function permissionFor(login) { if (!permissions.has(login)) { const { data } = await github.rest.repos.getCollaboratorPermissionLevel({ - ...repository, username: login, + ...repository, + username: login, }); permissions.set(login, data); } @@ -23,22 +24,27 @@ module.exports = async function cleanup({ github, context, core, now = new Date( if (!user || user.type === 'Bot') return false; const permission = await permissionFor(user.login); - return ['admin', 'write', 'maintain'].includes(permission.permission) - || (allowTriage && permission.role_name === 'triage'); + return ( + ['admin', 'write', 'maintain'].includes(permission.permission) || + (allowTriage && permission.role_name === 'triage') + ); } function isExempt(item) { - return item.labels.some(label => label.name === EXEMPT_LABEL); + return item.labels.some((label) => label.name === EXEMPT_LABEL); } async function commentsFor(number) { return github.paginate(github.rest.issues.listComments, { - ...repository, issue_number: number, per_page: 100, + ...repository, + issue_number: number, + per_page: 100, }); } async function authorEditAndDraftStart(item) { - const { node } = await github.graphql(` + const { node } = await github.graphql( + ` query($id: ID!) { node(id: $id) { ... on Issue { lastEditedAt editor { login } } @@ -54,7 +60,9 @@ module.exports = async function cleanup({ github, context, core, now = new Date( } } } - `, { id: item.node_id }); + `, + { id: item.node_id }, + ); return { editedAt: node.editor?.login === item.user.login ? node.lastEditedAt : null, @@ -63,14 +71,16 @@ module.exports = async function cleanup({ github, context, core, now = new Date( } async function recordDraftActivity(pullRequest, comments) { - const previous = comments.find(comment => - comment.user?.login === 'github-actions[bot]' && ACTIVITY_MARKER.test(comment.body)); + const previous = comments.find( + (comment) => comment.user?.login === 'github-actions[bot]' && ACTIVITY_MARKER.test(comment.body), + ); const activity = { head: pullRequest.head.sha, at: now.toISOString() }; - const body = `External draft pull requests are closed after seven days without contributor activity. ` - + `Pushes, edits, and author replies count; bot comments do not. ` - + `Maintainers can exempt directed work with the \`${EXEMPT_LABEL}\` label. ` - + `See the [contribution guide](${GUIDE}#pull-request-requirements).\n\n` - + ``; + const body = + `External draft pull requests are closed after seven days without contributor activity. ` + + `Pushes, edits, and author replies count; bot comments do not. ` + + `Maintainers can exempt directed work with the \`${EXEMPT_LABEL}\` label. ` + + `See the [contribution guide](${GUIDE}#pull-request-requirements).\n\n` + + ``; core.info(`Record draft activity for #${pullRequest.number}${dryRun ? ' (dry run)' : ''}`); if (dryRun) return; @@ -82,13 +92,20 @@ module.exports = async function cleanup({ github, context, core, now = new Date( } async function checkDraft(pullRequest, recordActivity = false) { - if (pullRequest.state !== 'open' || !pullRequest.draft || isExempt(pullRequest) - || !pullRequest.user || pullRequest.user.type === 'Bot' - || await isMaintainer(pullRequest.user)) return; + if ( + pullRequest.state !== 'open' || + !pullRequest.draft || + isExempt(pullRequest) || + !pullRequest.user || + pullRequest.user.type === 'Bot' || + (await isMaintainer(pullRequest.user)) + ) + return; const comments = await commentsFor(pullRequest.number); - const notice = comments.find(comment => - comment.user?.login === 'github-actions[bot]' && ACTIVITY_MARKER.test(comment.body)); + const notice = comments.find( + (comment) => comment.user?.login === 'github-actions[bot]' && ACTIVITY_MARKER.test(comment.body), + ); const recorded = notice ? JSON.parse(notice.body.match(ACTIVITY_MARKER)[1]) : null; // Commit dates do not record push times. Remember observed head changes, @@ -99,16 +116,23 @@ module.exports = async function cleanup({ github, context, core, now = new Date( } const edits = await authorEditAndDraftStart(pullRequest); - let lastActivity = Math.max(Date.parse(recorded.at), Date.parse(pullRequest.created_at), + let lastActivity = Math.max( + Date.parse(recorded.at), + Date.parse(pullRequest.created_at), edits.editedAt ? Date.parse(edits.editedAt) : 0, - edits.draftStartedAt ? Date.parse(edits.draftStartedAt) : 0); + edits.draftStartedAt ? Date.parse(edits.draftStartedAt) : 0, + ); if (now.getTime() - lastActivity < 7 * DAY) return; const reviews = await github.paginate(github.rest.pulls.listReviews, { - ...repository, pull_number: pullRequest.number, per_page: 100, + ...repository, + pull_number: pullRequest.number, + per_page: 100, }); const reviewComments = await github.paginate(github.rest.pulls.listReviewComments, { - ...repository, pull_number: pullRequest.number, per_page: 100, + ...repository, + pull_number: pullRequest.number, + per_page: 100, }); for (const response of [...comments, ...reviews, ...reviewComments]) { @@ -120,18 +144,27 @@ module.exports = async function cleanup({ github, context, core, now = new Date( if (now.getTime() - lastActivity < 7 * DAY) return; const { data: current } = await github.rest.pulls.get({ - ...repository, pull_number: pullRequest.number, + ...repository, + pull_number: pullRequest.number, }); - if (current.state !== 'open' || !current.draft || isExempt(current) - || current.head.sha !== pullRequest.head.sha || current.updated_at !== pullRequest.updated_at) return; + if ( + current.state !== 'open' || + !current.draft || + isExempt(current) || + current.head.sha !== pullRequest.head.sha || + current.updated_at !== pullRequest.updated_at + ) + return; core.info(`Close inactive external draft #${current.number}${dryRun ? ' (dry run)' : ''}`); if (dryRun) return; await github.rest.issues.createComment({ - ...repository, issue_number: current.number, - body: `Closing this external draft after seven days without contributor activity. ` - + `Bot comments do not extend the deadline. You can reopen it when you resume the work. ` - + `See the [contribution guide](${GUIDE}#pull-request-requirements).`, + ...repository, + issue_number: current.number, + body: + `Closing this external draft after seven days without contributor activity. ` + + `Bot comments do not extend the deadline. You can reopen it when you resume the work. ` + + `See the [contribution guide](${GUIDE}#pull-request-requirements).`, }); await github.rest.pulls.update({ ...repository, pull_number: current.number, state: 'closed' }); } @@ -139,58 +172,88 @@ module.exports = async function cleanup({ github, context, core, now = new Date( async function checkInformationRequest(issue) { if (issue.pull_request || isExempt(issue) || !issue.user || issue.user.type === 'Bot') return; const events = await github.paginate(github.rest.issues.listEventsForTimeline, { - ...repository, issue_number: issue.number, per_page: 100, + ...repository, + issue_number: issue.number, + per_page: 100, }); - const request = events.filter(event => event.event === 'labeled' && event.label.name === INFO_LABEL) + const request = events + .filter((event) => event.event === 'labeled' && event.label.name === INFO_LABEL) .sort((left, right) => Date.parse(right.created_at) - Date.parse(left.created_at))[0]; - if (!request || now.getTime() - Date.parse(request.created_at) < 14 * DAY - || !await isMaintainer(request.actor, true)) return; + if ( + !request || + now.getTime() - Date.parse(request.created_at) < 14 * DAY || + !(await isMaintainer(request.actor, true)) + ) + return; const requestedAt = Date.parse(request.created_at); const comments = await commentsFor(issue.number); - const authorResponded = comments.some(comment => comment.user?.login === issue.user.login - && comment.user.type !== 'Bot' - && Math.max(Date.parse(comment.created_at), Date.parse(comment.updated_at)) >= requestedAt); - const authorReopened = events.some(event => event.event === 'reopened' - && event.actor?.login === issue.user.login && Date.parse(event.created_at) >= requestedAt); + const authorResponded = comments.some( + (comment) => + comment.user?.login === issue.user.login && + comment.user.type !== 'Bot' && + Math.max(Date.parse(comment.created_at), Date.parse(comment.updated_at)) >= requestedAt, + ); + const authorReopened = events.some( + (event) => + event.event === 'reopened' && + event.actor?.login === issue.user.login && + Date.parse(event.created_at) >= requestedAt, + ); const edits = await authorEditAndDraftStart(issue); if (authorResponded || authorReopened || (edits.editedAt && Date.parse(edits.editedAt) >= requestedAt)) return; const { data: current } = await github.rest.issues.get({ ...repository, issue_number: issue.number }); - if (current.state !== 'open' || isExempt(current) - || !current.labels.some(label => label.name === INFO_LABEL) || current.updated_at !== issue.updated_at) return; + if ( + current.state !== 'open' || + isExempt(current) || + !current.labels.some((label) => label.name === INFO_LABEL) || + current.updated_at !== issue.updated_at + ) + return; core.info(`Close unanswered information request #${current.number}${dryRun ? ' (dry run)' : ''}`); if (dryRun) return; await github.rest.issues.createComment({ - ...repository, issue_number: current.number, - body: `Closing this issue because the reporter has not responded within 14 days of the maintainer's ` - + `request for information. This does not mean the reported problem is fixed. ` - + `Please provide the requested information here so the issue can be reconsidered. ` - + `See the [contribution guide](${GUIDE}#bug-reports).`, + ...repository, + issue_number: current.number, + body: + `Closing this issue because the reporter has not responded within 14 days of the maintainer's ` + + `request for information. This does not mean the reported problem is fixed. ` + + `Please provide the requested information here so the issue can be reconsidered. ` + + `See the [contribution guide](${GUIDE}#bug-reports).`, }); await github.rest.issues.update({ - ...repository, issue_number: current.number, state: 'closed', state_reason: 'not_planned', + ...repository, + issue_number: current.number, + state: 'closed', + state_reason: 'not_planned', }); } if (context.eventName === 'pull_request_target') { if (context.payload.sender?.type === 'Bot') return; const { data: pullRequest } = await github.rest.pulls.get({ - ...repository, pull_number: context.payload.pull_request.number, + ...repository, + pull_number: context.payload.pull_request.number, }); await checkDraft(pullRequest, true); return; } for await (const { data: pullRequests } of github.paginate.iterator(github.rest.pulls.list, { - ...repository, state: 'open', per_page: 100, + ...repository, + state: 'open', + per_page: 100, })) { for (const pullRequest of pullRequests) await checkDraft(pullRequest); } for await (const { data: issues } of github.paginate.iterator(github.rest.issues.listForRepo, { - ...repository, state: 'open', labels: INFO_LABEL, per_page: 100, + ...repository, + state: 'open', + labels: INFO_LABEL, + per_page: 100, })) { for (const issue of issues) await checkInformationRequest(issue); } diff --git a/.github/scripts/submission-cleanup.test.cjs b/.github/scripts/submission-cleanup.test.cjs index b6f141ad66..6411a52e38 100644 --- a/.github/scripts/submission-cleanup.test.cjs +++ b/.github/scripts/submission-cleanup.test.cjs @@ -11,23 +11,38 @@ const recentDate = '2026-10-19T12:00:00Z'; function draft(overrides = {}) { return { - number: 1, node_id: 'PR_1', state: 'open', draft: true, user: author, - labels: [], head: { sha: 'old-head' }, created_at: oldDate, updated_at: oldDate, + number: 1, + node_id: 'PR_1', + state: 'open', + draft: true, + user: author, + labels: [], + head: { sha: 'old-head' }, + created_at: oldDate, + updated_at: oldDate, ...overrides, }; } function issue(overrides = {}) { return { - number: 2, node_id: 'I_2', state: 'open', user: author, - labels: [{ name: 'not enough info' }], created_at: oldDate, updated_at: oldDate, + number: 2, + node_id: 'I_2', + state: 'open', + user: author, + labels: [{ name: 'not enough info' }], + created_at: oldDate, + updated_at: oldDate, ...overrides, }; } function notice(at = oldDate, head = 'old-head') { return { - id: 10, user: bot, created_at: oldDate, updated_at: oldDate, + id: 10, + user: bot, + created_at: oldDate, + updated_at: oldDate, body: ``, }; } @@ -47,27 +62,38 @@ async function run(options = {}) { const pullRequests = options.pullRequests || []; const issues = options.issues || []; const methods = [ - 'repos.getCollaboratorPermissionLevel', 'pulls.list', 'pulls.get', 'pulls.listReviews', - 'pulls.listReviewComments', 'pulls.update', 'issues.listForRepo', 'issues.get', - 'issues.listComments', 'issues.listEventsForTimeline', 'issues.createComment', - 'issues.updateComment', 'issues.update', + 'repos.getCollaboratorPermissionLevel', + 'pulls.list', + 'pulls.get', + 'pulls.listReviews', + 'pulls.listReviewComments', + 'pulls.update', + 'issues.listForRepo', + 'issues.get', + 'issues.listComments', + 'issues.listEventsForTimeline', + 'issues.createComment', + 'issues.updateComment', + 'issues.update', ]; const rest = { repos: {}, pulls: {}, issues: {} }; for (const method of methods) { const [resource, name] = method.split('.'); - rest[resource][name] = async parameters => { + rest[resource][name] = async (parameters) => { calls.push({ method, ...parameters }); if (method === 'repos.getCollaboratorPermissionLevel') { - return { data: options.permissions?.[parameters.username] || { - permission: parameters.username === 'maintainer' ? 'write' : 'read', - } }; + return { + data: options.permissions?.[parameters.username] || { + permission: parameters.username === 'maintainer' ? 'write' : 'read', + }, + }; } if (method === 'pulls.get') { - const current = pullRequests.find(item => item.number === parameters.pull_number); + const current = pullRequests.find((item) => item.number === parameters.pull_number); return { data: { ...current, ...options.currentPullRequest } }; } if (method === 'issues.get') { - const current = issues.find(item => item.number === parameters.issue_number); + const current = issues.find((item) => item.number === parameters.issue_number); return { data: { ...current, ...options.currentIssue } }; } mutations.push({ method, ...parameters }); @@ -91,7 +117,8 @@ async function run(options = {}) { } }; const github = { - rest, paginate, + rest, + paginate, graphql: async (query, variables) => ({ node: options.edits?.[variables.id] || { lastEditedAt: null, editor: null }, }), @@ -100,16 +127,22 @@ async function run(options = {}) { github, context: { repo: { owner: 'hypervel', repo: 'components' }, - eventName: options.eventName || 'schedule', payload: options.payload || {}, + eventName: options.eventName || 'schedule', + payload: options.payload || {}, }, - core: { info: message => logs.push(message) }, now, dryRun: options.dryRun || false, + core: { info: (message) => logs.push(message) }, + now, + dryRun: options.dryRun || false, }); return { mutations, logs, calls }; } test('closes an inactive external draft with an explanation and no branch deletion', async () => { const { mutations } = await run({ pullRequests: [draft()], comments: { 1: [notice()] } }); - assert.deepEqual(mutations.map(change => change.method), ['issues.createComment', 'pulls.update']); + assert.deepEqual( + mutations.map((change) => change.method), + ['issues.createComment', 'pulls.update'], + ); assert.match(mutations[0].body, /seven days/); assert.match(mutations[0].body, /contributions\.md/); assert.equal(mutations[1].state, 'closed'); @@ -124,7 +157,8 @@ test('gives existing drafts a full seven days from their first notice', async () test('records a newly observed head even if the commit has an old date', async () => { const { mutations } = await run({ - pullRequests: [draft({ head: { sha: 'new-head' } })], comments: { 1: [notice()] }, + pullRequests: [draft({ head: { sha: 'new-head' } })], + comments: { 1: [notice()] }, }); assert.equal(mutations.length, 1); assert.equal(mutations[0].method, 'issues.updateComment'); @@ -140,18 +174,23 @@ test('ignores contributor-supplied tracking markers', async () => { test('honors the seven-day boundary', async () => { const before = await run({ - pullRequests: [draft()], comments: { 1: [notice('2026-10-13T12:00:01Z')] }, + pullRequests: [draft()], + comments: { 1: [notice('2026-10-13T12:00:01Z')] }, }); assert.equal(before.mutations.length, 0); const atDeadline = await run({ - pullRequests: [draft()], comments: { 1: [notice('2026-10-13T12:00:00Z')] }, + pullRequests: [draft()], + comments: { 1: [notice('2026-10-13T12:00:00Z')] }, }); assert.equal(atDeadline.mutations.at(-1).method, 'pulls.update'); }); test('exempts ready, closed, maintainer-authored, bot-authored, and labeled pull requests', async () => { for (const overrides of [ - { draft: false }, { state: 'closed' }, { user: maintainer }, { user: bot }, + { draft: false }, + { state: 'closed' }, + { user: maintainer }, + { user: bot }, { labels: [{ name: 'maintainer-directed' }] }, ]) { const { mutations } = await run({ pullRequests: [draft(overrides)] }); @@ -167,7 +206,9 @@ test('author comments, comment edits, reviews, and review replies extend the dea { reviewComments: [response()] }, ]) { const { mutations } = await run({ - pullRequests: [draft()], comments: { 1: [notice()] }, ...additions, + pullRequests: [draft()], + comments: { 1: [notice()] }, + ...additions, }); assert.equal(mutations.length, 0); } @@ -175,7 +216,8 @@ test('author comments, comment edits, reviews, and review replies extend the dea test('bot comments and maintainer reviews do not reset the draft timer', async () => { const { mutations } = await run({ - pullRequests: [draft()], comments: { 1: [notice(), response(bot)] }, + pullRequests: [draft()], + comments: { 1: [notice(), response(bot)] }, reviews: [{ user: maintainer, submitted_at: recentDate }], reviewComments: [response(bot)], }); @@ -188,7 +230,9 @@ test('author body edits and recent draft conversion or reopening extend the dead { timelineItems: { nodes: [{ createdAt: recentDate }] } }, ]) { const { mutations } = await run({ - pullRequests: [draft()], comments: { 1: [notice()] }, edits: { PR_1: edit }, + pullRequests: [draft()], + comments: { 1: [notice()] }, + edits: { PR_1: edit }, }); assert.equal(mutations.length, 0); } @@ -196,11 +240,16 @@ test('author body edits and recent draft conversion or reopening extend the dead test('does not close a draft changed while its activity was being checked', async () => { for (const currentPullRequest of [ - { draft: false }, { state: 'closed' }, { head: { sha: 'new-head' } }, - { updated_at: recentDate }, { labels: [{ name: 'maintainer-directed' }] }, + { draft: false }, + { state: 'closed' }, + { head: { sha: 'new-head' } }, + { updated_at: recentDate }, + { labels: [{ name: 'maintainer-directed' }] }, ]) { const { mutations } = await run({ - pullRequests: [draft()], comments: { 1: [notice()] }, currentPullRequest, + pullRequests: [draft()], + comments: { 1: [notice()] }, + currentPullRequest, }); assert.equal(mutations.length, 0); } @@ -208,7 +257,9 @@ test('does not close a draft changed while its activity was being checked', asyn test('human pull request events record activity without closing anything', async () => { const { mutations } = await run({ - pullRequests: [draft()], comments: { 1: [notice()] }, eventName: 'pull_request_target', + pullRequests: [draft()], + comments: { 1: [notice()] }, + eventName: 'pull_request_target', payload: { pull_request: { number: 1 }, sender: author }, }); assert.equal(mutations.length, 1); @@ -217,7 +268,8 @@ test('human pull request events record activity without closing anything', async test('bot-triggered edits do not update the draft activity notice', async () => { const { mutations } = await run({ - pullRequests: [draft()], eventName: 'pull_request_target', + pullRequests: [draft()], + eventName: 'pull_request_target', payload: { pull_request: { number: 1 }, sender: bot }, }); assert.equal(mutations.length, 0); @@ -225,7 +277,10 @@ test('bot-triggered edits do not update the draft activity notice', async () => test('closes an unanswered maintainer information request as not planned, not fixed', async () => { const { mutations } = await run({ issues: [issue()], events: [request()] }); - assert.deepEqual(mutations.map(change => change.method), ['issues.createComment', 'issues.update']); + assert.deepEqual( + mutations.map((change) => change.method), + ['issues.createComment', 'issues.update'], + ); assert.match(mutations[0].body, /does not mean the reported problem is fixed/); assert.equal(mutations[1].state_reason, 'not_planned'); }); @@ -240,7 +295,8 @@ test('requires a maintainer information request and honors its fourteen-day boun test('accepts information requests from repository triagers', async () => { const { mutations } = await run({ - issues: [issue()], events: [request(oldDate, { login: 'triager', type: 'User' })], + issues: [issue()], + events: [request(oldDate, { login: 'triager', type: 'User' })], permissions: { triager: { permission: 'read', role_name: 'triage' } }, }); assert.equal(mutations.at(-1).method, 'issues.update'); @@ -248,7 +304,9 @@ test('accepts information requests from repository triagers', async () => { test('a reporter response stops closure even if the maintainer has not replied for fourteen days', async () => { const { mutations } = await run({ - issues: [issue()], events: [request()], comments: { 2: [response(author, '2026-10-02T12:00:00Z')] }, + issues: [issue()], + events: [request()], + comments: { 2: [response(author, '2026-10-02T12:00:00Z')] }, }); assert.equal(mutations.length, 0); }); @@ -265,7 +323,9 @@ test('reporter body edits, comment edits, and reopenings count as responses', as test('bot and other-user replies are not reporter responses', async () => { const { mutations } = await run({ - issues: [issue()], events: [request()], comments: { 2: [response(bot), response(maintainer)] }, + issues: [issue()], + events: [request()], + comments: { 2: [response(bot), response(maintainer)] }, edits: { I_2: { editor: { login: bot.login }, lastEditedAt: recentDate } }, }); assert.equal(mutations.at(-1).method, 'issues.update'); @@ -273,7 +333,8 @@ test('bot and other-user replies are not reporter responses', async () => { test('reapplying the information label starts a new request', async () => { const { mutations } = await run({ - issues: [issue()], events: [request(), request(recentDate)], + issues: [issue()], + events: [request(), request(recentDate)], }); assert.equal(mutations.length, 0); }); @@ -281,7 +342,8 @@ test('reapplying the information label starts a new request', async () => { test('exempts directed issues and never treats a pull request as an information-request issue', async () => { for (const overrides of [ { labels: [{ name: 'not enough info' }, { name: 'maintainer-directed' }] }, - { pull_request: {} }, { user: bot }, + { pull_request: {} }, + { user: bot }, ]) { assert.equal((await run({ issues: [issue(overrides)], events: [request()] })).mutations.length, 0); } @@ -289,7 +351,9 @@ test('exempts directed issues and never treats a pull request as an information- test('rechecks issue state, exemption, label, and activity before closing', async () => { for (const currentIssue of [ - { state: 'closed' }, { labels: [] }, { updated_at: recentDate }, + { state: 'closed' }, + { labels: [] }, + { updated_at: recentDate }, { labels: [{ name: 'not enough info' }, { name: 'maintainer-directed' }] }, ]) { assert.equal((await run({ issues: [issue()], events: [request()], currentIssue })).mutations.length, 0); @@ -298,17 +362,21 @@ test('rechecks issue state, exemption, label, and activity before closing', asyn test('processes subsequent API pages', async () => { const { mutations } = await run({ - pullRequests: [draft({ draft: false }), draft({ number: 3 })], comments: { 3: [notice()] }, + pullRequests: [draft({ draft: false }), draft({ number: 3 })], + comments: { 3: [notice()] }, }); assert.equal(mutations.at(-1).pull_number, 3); }); test('dry runs make no writes for either closures or initial notices', async () => { const { mutations, logs } = await run({ - pullRequests: [draft(), draft({ number: 3 })], comments: { 1: [notice()] }, - issues: [issue()], events: [request()], dryRun: true, + pullRequests: [draft(), draft({ number: 3 })], + comments: { 1: [notice()] }, + issues: [issue()], + events: [request()], + dryRun: true, }); assert.equal(mutations.length, 0); assert.equal(logs.length, 3); - assert.ok(logs.every(message => message.endsWith('(dry run)'))); + assert.ok(logs.every((message) => message.endsWith('(dry run)'))); }); diff --git a/dogfood/ai/workbench/resources/js/components/ui/attachment.jsx b/dogfood/ai/workbench/resources/js/components/ui/attachment.jsx index 0d0d0da552..30a05e0a98 100644 --- a/dogfood/ai/workbench/resources/js/components/ui/attachment.jsx +++ b/dogfood/ai/workbench/resources/js/components/ui/attachment.jsx @@ -1,193 +1,166 @@ -import * as React from "react" -import { cva } from "class-variance-authority"; -import { Slot } from "radix-ui" +import * as React from 'react'; +import { cva } from 'class-variance-authority'; +import { Slot } from 'radix-ui'; -import { cn } from "@/lib/utils" -import { Button } from "@/components/ui/button" +import { cn } from '@/lib/utils'; +import { Button } from '@/components/ui/button'; const attachmentVariants = cva( - "group/attachment relative flex w-fit max-w-full min-w-0 shrink-0 flex-wrap rounded-xl border bg-card text-card-foreground transition-colors focus-within:ring-1 focus-within:ring-ring/50 has-[>a,>button]:hover:bg-muted/50 data-[state=error]:border-destructive/30 data-[state=idle]:border-dashed", - { - variants: { - size: { - default: - "gap-2 text-sm has-data-[slot=attachment-content]:px-2.5 has-data-[slot=attachment-content]:py-2 has-data-[slot=attachment-media]:p-2", - sm: "gap-2.5 text-xs has-data-[slot=attachment-content]:px-2 has-data-[slot=attachment-content]:py-1.5 has-data-[slot=attachment-media]:p-1.5", - xs: "gap-1.5 rounded-lg text-xs has-data-[slot=attachment-content]:px-1.5 has-data-[slot=attachment-content]:py-1 has-data-[slot=attachment-media]:p-1", - }, - orientation: { - horizontal: "min-w-40 items-center", - vertical: "w-24 flex-col has-data-[slot=attachment-content]:w-30", - }, + 'group/attachment relative flex w-fit max-w-full min-w-0 shrink-0 flex-wrap rounded-xl border bg-card text-card-foreground transition-colors focus-within:ring-1 focus-within:ring-ring/50 has-[>a,>button]:hover:bg-muted/50 data-[state=error]:border-destructive/30 data-[state=idle]:border-dashed', + { + variants: { + size: { + default: + 'gap-2 text-sm has-data-[slot=attachment-content]:px-2.5 has-data-[slot=attachment-content]:py-2 has-data-[slot=attachment-media]:p-2', + sm: 'gap-2.5 text-xs has-data-[slot=attachment-content]:px-2 has-data-[slot=attachment-content]:py-1.5 has-data-[slot=attachment-media]:p-1.5', + xs: 'gap-1.5 rounded-lg text-xs has-data-[slot=attachment-content]:px-1.5 has-data-[slot=attachment-content]:py-1 has-data-[slot=attachment-media]:p-1', + }, + orientation: { + horizontal: 'min-w-40 items-center', + vertical: 'w-24 flex-col has-data-[slot=attachment-content]:w-30', + }, + }, }, - } -) +); -function Attachment({ - className, - state = "done", - size = "default", - orientation = "horizontal", - ...props -}) { - return ( -
- ); +function Attachment({ className, state = 'done', size = 'default', orientation = 'horizontal', ...props }) { + return ( +
+ ); } const attachmentMediaVariants = cva( - "relative flex aspect-square w-10 shrink-0 items-center justify-center overflow-hidden rounded-lg bg-muted text-foreground group-data-[orientation=vertical]/attachment:w-full group-data-[size=sm]/attachment:w-8 group-data-[size=xs]/attachment:w-7 group-data-[size=xs]/attachment:rounded-md group-data-[state=error]/attachment:bg-destructive/10 group-data-[state=error]/attachment:text-destructive group-data-[orientation=vertical]/attachment:*:data-[slot=spinner]:size-6! [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 group-data-[orientation=vertical]/attachment:[&_svg:not([class*='size-'])]:size-6 group-data-[size=xs]/attachment:[&_svg:not([class*='size-'])]:size-3.5", - { - variants: { - variant: { - icon: "", - image: - "opacity-60 group-data-[state=done]/attachment:opacity-100 group-data-[state=idle]/attachment:opacity-100 *:[img]:aspect-square *:[img]:w-full *:[img]:object-cover", - }, + "relative flex aspect-square w-10 shrink-0 items-center justify-center overflow-hidden rounded-lg bg-muted text-foreground group-data-[orientation=vertical]/attachment:w-full group-data-[size=sm]/attachment:w-8 group-data-[size=xs]/attachment:w-7 group-data-[size=xs]/attachment:rounded-md group-data-[state=error]/attachment:bg-destructive/10 group-data-[state=error]/attachment:text-destructive group-data-[orientation=vertical]/attachment:*:data-[slot=spinner]:size-6! [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 group-data-[orientation=vertical]/attachment:[&_svg:not([class*='size-'])]:size-6 group-data-[size=xs]/attachment:[&_svg:not([class*='size-'])]:size-3.5", + { + variants: { + variant: { + icon: '', + image: 'opacity-60 group-data-[state=done]/attachment:opacity-100 group-data-[state=idle]/attachment:opacity-100 *:[img]:aspect-square *:[img]:w-full *:[img]:object-cover', + }, + }, + defaultVariants: { + variant: 'icon', + }, }, - defaultVariants: { - variant: "icon", - }, - } -) +); -function AttachmentMedia({ - className, - variant = "icon", - ...props -}) { - return ( -
- ); +function AttachmentMedia({ className, variant = 'icon', ...props }) { + return ( +
+ ); } -function AttachmentContent({ - className, - ...props -}) { - return ( -
- ); +function AttachmentContent({ className, ...props }) { + return ( +
+ ); } -function AttachmentTitle({ - className, - ...props -}) { - return ( - - ); +function AttachmentTitle({ className, ...props }) { + return ( + + ); } -function AttachmentDescription({ - className, - ...props -}) { - return ( - - ); +function AttachmentDescription({ className, ...props }) { + return ( + + ); } -function AttachmentActions({ - className, - ...props -}) { - return ( -
- ); +function AttachmentActions({ className, ...props }) { + return ( +
+ ); } -function AttachmentAction({ - className, - variant, - size = "icon-xs", - ...props -}) { - return ( -