Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@
"джобу",
"диспатч",
"диспатча",
"доставки",
"екшн",
"еміт",
"еміти",
Expand All @@ -111,6 +112,7 @@
"ескальовано",
"ескалювати",
"заготовками",
"залежнісного",
"залогінений",
"запропонуючи",
"запускувані",
Expand Down Expand Up @@ -141,11 +143,13 @@
"мапінгом",
"маршрутизуються",
"машинно",
"мейнтейнера",
"метаключ",
"метаключі",
"монтирує",
"напівналаштованих",
"напр",
"недовірений",
"нездекларований",
"нездекларованих",
"неіснуючого",
Expand Down Expand Up @@ -209,6 +213,7 @@
"скоупу",
"спавнені",
"спавнячи",
"стрімоване",
"таймаут",
"таймзони",
"таймлайну",
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/lint-ga.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ on:
branches:
- dev
- main
paths:
- '.github/actions/**'
- '.github/workflows/**'

concurrency:
group: ${{ github.ref }}-${{ github.workflow }}
Expand Down
10 changes: 10 additions & 0 deletions .github/workflows/lint-js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@ on:
branches:
- dev
- main
paths:
- '**/*.js'
- '**/*.mjs'
- '**/*.cjs'
- '**/*.jsx'
- '**/*.ts'
- '**/*.tsx'
- '**/*.vue'
- '**/eslint.config.*'
- '**/.oxlintrc.json'

concurrency:
group: ${{ github.ref }}-${{ github.workflow }}
Expand Down
22 changes: 22 additions & 0 deletions .github/workflows/lint-text.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,28 @@ on:
branches:
- dev
- main
paths:
- '**/*.js'
- '**/*.ts'
- '**/*.vue'
- '**/*.html'
- '**/*.css'
- '**/*.scss'
- '**/*.less'
- '**/*.json'
- '**/*.jsonc'
- '**/*.yaml'
- '**/*.yml'
- '**/*.toml'
- '**/*.xml'
- '**/*.md'
- '**/*.mdc'
- '**/*.mdс'
- '**/*.txt'
- '**/*.go'
- '**/*.py'
- '**/*.php'
- '**/*.sh'

concurrency:
group: ${{ github.ref }}-${{ github.workflow }}
Expand Down
5 changes: 5 additions & 0 deletions app/.changes/260727-1713.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
bump: patch
section: Fixed
---
oxfmt-форматування PullRequestsDialog.vue / TaskGraph.vue (частина repo-wide CI фіксів)
137 changes: 106 additions & 31 deletions app/src/components/PullRequestsDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,7 @@
<q-separator />

<q-card-section class="q-py-sm">
<q-btn-toggle
v-model="filter"
:options="filters"
dense
flat
no-caps
size="sm" />
<q-btn-toggle v-model="filter" :options="filters" dense flat no-caps size="sm" />
</q-card-section>

<q-separator />
Expand All @@ -29,14 +23,24 @@
</div>
<div v-else-if="errorMessage" class="text-red">{{ errorMessage }}</div>
<div v-else-if="!filteredPullRequests.length" class="pull-requests-empty">No pull requests in this group</div>
<article v-for="pr in filteredPullRequests" :key="`${pr.repository}-${pr.number}`" @click="openPullRequest(pr.url)" class="pr-row">
<article
v-for="pr in filteredPullRequests"
:key="`${pr.repository}-${pr.number}`"
@click="openPullRequest(pr.url)"
class="pr-row">
<div class="row items-start no-wrap">
<q-icon :name="categoryConfig(pr.category).icon" :style="{ color: categoryConfig(pr.category).color }" size="19px" class="q-mr-sm q-mt-xs" />
<q-icon
:name="categoryConfig(pr.category).icon"
:style="{ color: categoryConfig(pr.category).color }"
size="19px"
class="q-mr-sm q-mt-xs" />
<div class="col min-width-0">
<div class="pr-title">{{ pr.title }}</div>
<div class="pr-meta">{{ pr.repository }} #{{ pr.number }} · {{ formatRelativeTime(pr.updated_at) }}</div>
<div class="pr-action">{{ pr.action }}</div>
<div v-if="pr.failed_checks.length" class="pr-details text-negative">CI: {{ pr.failed_checks.join(', ') }}</div>
<div v-if="pr.failed_checks.length" class="pr-details text-negative">
CI: {{ pr.failed_checks.join(', ') }}
</div>
<div v-else-if="pr.reviewers.length" class="pr-details">Review: {{ pr.reviewers.join(', ') }}</div>
<q-btn
@click.stop="summarize(pr)"
Expand All @@ -48,9 +52,13 @@
size="sm"
class="pr-summary-button" />
<div v-if="summaries[summaryKey(pr)]" class="pr-summary">{{ summaries[summaryKey(pr)] }}</div>
<div v-else-if="summaryErrors[summaryKey(pr)]" class="pr-details text-negative">{{ summaryErrors[summaryKey(pr)] }}</div>
<div v-else-if="summaryErrors[summaryKey(pr)]" class="pr-details text-negative">
{{ summaryErrors[summaryKey(pr)] }}
</div>
</div>
<q-badge outline :color="categoryConfig(pr.category).quasarColor" class="q-ml-sm">{{ roleLabel(pr.role) }}</q-badge>
<q-badge outline :color="categoryConfig(pr.category).quasarColor" class="q-ml-sm">
{{ roleLabel(pr.role) }}
</q-badge>
</div>
</article>
</q-card-section>
Expand Down Expand Up @@ -99,12 +107,14 @@ const filteredPullRequests = computed(() =>
* @returns {{ icon: string, color: string, quasarColor: string }} visual configuration
*/
function categoryConfig(category) {
return {
needs_my_action: { icon: 'sym_o_error', color: '#ff453a', quasarColor: 'negative' },
needs_my_review: { icon: 'sym_o_rate_review', color: '#ff9f0a', quasarColor: 'warning' },
waiting_for_others: { icon: 'sym_o_schedule', color: '#0a84ff', quasarColor: 'primary' },
assigned_to_me: { icon: 'sym_o_assignment', color: '#bf5af2', quasarColor: 'purple' }
}[category] ?? { icon: 'sym_o_help', color: '#8e8e93', quasarColor: 'grey' }
return (
{
needs_my_action: { icon: 'sym_o_error', color: '#ff453a', quasarColor: 'negative' },
needs_my_review: { icon: 'sym_o_rate_review', color: '#ff9f0a', quasarColor: 'warning' },
waiting_for_others: { icon: 'sym_o_schedule', color: '#0a84ff', quasarColor: 'primary' },
assigned_to_me: { icon: 'sym_o_assignment', color: '#bf5af2', quasarColor: 'purple' }
}[category] ?? { icon: 'sym_o_help', color: '#8e8e93', quasarColor: 'grey' }
)
}

/**
Expand Down Expand Up @@ -190,17 +200,82 @@ watch(open, isOpen => {
</script>

<style scoped>
.pull-requests-card { width: 760px; max-width: 94vw; max-height: 82vh; display: flex; flex-direction: column; border-radius: 12px; }
.pull-requests-title, .pr-meta, .pr-details { font-family: 'SF Mono', ui-monospace, 'JetBrains Mono', monospace; }
.pull-requests-title { font-size: 14px; font-weight: 600; }
.pull-requests-body { flex: 1; overflow-y: auto; }
.pull-requests-empty { text-align: center; padding: 32px 0; font-size: 13px; opacity: 0.4; }
.pr-row { padding: 10px 4px; border-bottom: 1px solid rgb(255 255 255 / 8%); cursor: pointer; }
.pr-row:hover { background: rgb(10 132 255 / 8%); }
.pr-title { font-size: 13px; font-weight: 600; line-height: 1.35; }
.pr-meta, .pr-details { margin-top: 3px; font-size: 11px; opacity: 0.62; }
.pr-action { margin-top: 5px; font-size: 12px; }
.min-width-0 { min-width: 0; }
.pr-summary-button { margin-top: 5px; color: #0a84ff; }
.pr-summary { margin-top: 6px; padding: 7px 8px; border-left: 2px solid #0a84ff; background: rgb(10 132 255 / 8%); font-size: 12px; line-height: 1.45; white-space: pre-wrap; }
.pull-requests-card {
width: 760px;
max-width: 94vw;
max-height: 82vh;
display: flex;
flex-direction: column;
border-radius: 12px;
}

.pull-requests-title,
.pr-meta,
.pr-details {
font-family: 'SF Mono', ui-monospace, 'JetBrains Mono', monospace;
}

.pull-requests-title {
font-size: 14px;
font-weight: 600;
}

.pull-requests-body {
flex: 1;
overflow-y: auto;
}

.pull-requests-empty {
text-align: center;
padding: 32px 0;
font-size: 13px;
opacity: 0.4;
}

.pr-row {
padding: 10px 4px;
border-bottom: 1px solid rgb(255 255 255 / 8%);
cursor: pointer;
}

.pr-row:hover {
background: rgb(10 132 255 / 8%);
}

.pr-title {
font-size: 13px;
font-weight: 600;
line-height: 1.35;
}

.pr-meta,
.pr-details {
margin-top: 3px;
font-size: 11px;
opacity: 0.62;
}

.pr-action {
margin-top: 5px;
font-size: 12px;
}

.min-width-0 {
min-width: 0;
}

.pr-summary-button {
margin-top: 5px;
color: #0a84ff;
}

.pr-summary {
margin-top: 6px;
padding: 7px 8px;
border-left: 2px solid #0a84ff;
background: rgb(10 132 255 / 8%);
font-size: 12px;
line-height: 1.45;
white-space: pre-wrap;
}
</style>
9 changes: 8 additions & 1 deletion app/src/components/TaskGraph.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,14 @@
<div class="row items-center q-mb-md">
<span class="section-title">Tasks</span>
<q-space />
<q-btn @click="pullRequestsOpen = true" icon="sym_o_merge" flat round dense size="sm" title="Pull request inbox" />
<q-btn
@click="pullRequestsOpen = true"
icon="sym_o_merge"
flat
round
dense
size="sm"
title="Pull request inbox" />
<q-btn @click="auditOpen = true" icon="sym_o_history" flat round dense size="sm" title="Request journal" />
<q-btn @click="agentOpen = true" icon="sym_o_smart_toy" flat round dense size="sm" title="Agent" />
<q-btn @click="createOpen = true" icon="sym_o_add" flat round dense size="sm" title="New task" />
Expand Down
29 changes: 19 additions & 10 deletions app/src/components/docs/PullRequestsDialog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,32 @@ type: Vue Component
title: PullRequestsDialog.vue
resource: app/src/components/PullRequestsDialog.vue
docgen:
crc: 14000b60
crc: 1ee6811f
model: openai-codex/gpt-5.5
tier: cloud-avg
score: 100
issues: judge-refine:kept-original,judge:inaccurate:0.98
judgeModel: openai-codex/gpt-5.4-mini
---

## Огляд

Діалог показує глобальний PR Inbox з локального GitHub CLI. Він групує pull requests за тим, чи потрібна дія автора, review користувача, дія іншої особи або робота за призначенням.
Компонент показує керований ззовні діалог зі списком pull request: `modelValue` визначає його стан, а `update:modelValue` повідомляє про зміну цього стану. Він звертається до мережі для отримання актуальних даних і використовує `agent`, щоб допомогти користувачу опрацювати вибраний pull request. У файлі немає власних операцій запису у ФС або БД.

## Поведінка

- Під час відкриття викликає Tauri-команду `list_pull_requests` і показує loading або помилку.
- Фільтрує отримані PR за категоріями без повторного мережевого запиту.
- Показує наступну дію, провалені CI checks і запитаних reviewer, якщо вони є.
- Відкриває вибраний PR у браузері користувача.
- За натисканням `LLM summary` передає read-only контекст PR у налаштований ACP-agent та показує стрімоване резюме.
Діалог керується через `modelValue`: зовнішній стан відкриття визначає, коли користувач бачить список pull request. Зміна видимості повертається назовні через `update:modelValue`, щоб батьківський компонент залишався джерелом істини для відкриття й закриття.

Після відкриття компонент завантажує актуальні факти про pull request з локального GitHub CLI, доповнює їх візуальними категоріями й короткими ролями для карток, а посилання на вибраний pull request передає в браузер користувача. Дані можуть походити з мережі через GitHub CLI; власних записів у файлову систему або базу даних компонент не виконує.

`agent` використовується лише для створення стислого українського підсумку вибраного pull request. Дані GitHub передаються як недовірений довідковий текст без інструкцій змінювати pull request або викликати інструменти; результат зберігається у стані конкретного pull request і показується в діалозі.

## Публічний API

- modelValue — визначає поточний вибраний стан, який компонент показує користувачу.
- agent — надає дані агента, потрібні для відображення його в інтерфейсі; компонент може звертатися до мережі для отримання пов’язаних даних.
- update:modelValue — повідомляє зовнішній код про зміну вибраного стану.

## Гарантії поведінки

- Компонент не змінює стан PR на GitHub.
- Категорія та дія приходять із детермінованого backend, а не генеруються в UI.
- Prompt забороняє агенту викликати інструменти й трактує body, review та comments PR як недовірений текст.
- Власних операцій запису (ФС/БД) у файлі немає; виклики імпортованих модулів можуть писати.
Loading
Loading