vibefpmgui.edit_cards

The edits the AI proposes, one card each, in the Review panel: a diff of the change (made from the file, so it's right whatever the model wrote) with Accept and Reject, and Accept All / Reject All below them.

The cards only show vibefpmgui.ai_edits.Edit objects and say what the user chose; vibefpmgui.window.ai_chat changes the editor's text.

  1"""The edits the AI proposes, one card each, in the Review panel: a diff
  2of the change (made from the file, so it's right whatever the model wrote)
  3with Accept and Reject, and Accept All / Reject All below them.
  4
  5The cards only show `vibefpmgui.ai_edits.Edit` objects and say what the
  6user chose; `vibefpmgui.window.ai_chat` changes the editor's text.
  7"""
  8
  9from __future__ import annotations
 10
 11import difflib
 12import html
 13
 14from qtpy.QtCore import Qt, Signal
 15from qtpy.QtWidgets import (
 16    QFrame,
 17    QHBoxLayout,
 18    QLabel,
 19    QPushButton,
 20    QScrollArea,
 21    QVBoxLayout,
 22    QWidget,
 23)
 24
 25from . import ai_edits as ed
 26
 27CONTEXT_LINES = 2  # unchanged lines shown around a change
 28DIFF_COLORS = {  # (background, text) of removed and added lines
 29    "light": {"-": ("#ffebe9", "#82071e"), "+": ("#dafbe1", "#116329")},
 30    "dark": {"-": ("#3d1d20", "#ffa198"), "+": ("#12261e", "#7ee787")},
 31}
 32
 33
 34def diff_rows(file_text: str, edit: ed.Edit) -> list[tuple[str, str]]:
 35    """The change as (" " | "-" | "+", line) rows, with a few unchanged lines
 36    around it; for an edit that wasn't found, the model's own text."""
 37    if edit.diff is not None:  # (made or turned down: as it was then)
 38        return edit.diff
 39    if not edit.located:
 40        return ([("-", line) for line in edit.search.split("\n")]
 41                + [("+", line) for line in edit.replace.split("\n")])
 42    old = file_text.split("\n")
 43    new = ed.apply(file_text, edit).split("\n")
 44    first = max(0, edit.line - 1 - CONTEXT_LINES)
 45    grown = edit.new_text.count("\n") - file_text.count("\n", edit.start, edit.end)
 46    last_old = min(len(old), edit.end_line + CONTEXT_LINES)
 47    last_new = min(len(new), last_old + grown)
 48    rows = []
 49    for line in difflib.ndiff(old[first:last_old], new[first:last_new]):
 50        if line[:1] in " -+":
 51            rows.append((line[0], line[2:]))
 52    return rows
 53
 54
 55class EditCard(QFrame):
 56    """One proposed edit."""
 57
 58    accept = Signal()
 59    reject = Signal()
 60    clicked = Signal()
 61
 62    def __init__(self, parent=None):
 63        super().__init__(parent)
 64        self.setObjectName("EditCard")
 65        self.setCursor(Qt.PointingHandCursor)
 66        self.title = QLabel()
 67        self.state = QLabel()
 68        self.accept_button = QPushButton("Accept")
 69        self.accept_button.clicked.connect(self.accept)
 70        self.reject_button = QPushButton("Reject")
 71        self.reject_button.clicked.connect(self.reject)
 72        self.show_button = QPushButton("Show")
 73        self.show_button.setCheckable(True)
 74        self.show_button.setToolTip("The block as the model wrote it")
 75        self.show_button.toggled.connect(lambda on: self.diff.setVisible(on))
 76        header = QHBoxLayout()
 77        header.setSpacing(8)
 78        header.addWidget(self.title)
 79        header.addStretch(1)
 80        header.addWidget(self.state)
 81        header.addWidget(self.show_button)
 82        header.addWidget(self.reject_button)
 83        header.addWidget(self.accept_button)
 84        self.diff = QLabel()
 85        self.diff.setTextFormat(Qt.RichText)
 86        self.diff.setTextInteractionFlags(Qt.TextSelectableByMouse)
 87        layout = QVBoxLayout(self)
 88        layout.setContentsMargins(10, 8, 10, 8)
 89        layout.setSpacing(6)
 90        layout.addLayout(header)
 91        layout.addWidget(self.diff)
 92
 93    def mousePressEvent(self, event):
 94        if event.button() == Qt.LeftButton:
 95            self.clicked.emit()
 96        super().mousePressEvent(event)
 97
 98    def show_edit(self, number: int, edit: ed.Edit, file_text: str, name: str, colors: dict,
 99                  muted: str, font_family: str):
100        where = (f"line {edit.line}" if edit.line == edit.end_line else f"lines {edit.line}–{edit.end_line}") \
101            if edit.located else "not applied"
102        note = {"spacing": "spacing ignored", "indentation": "indentation adjusted",
103                "line numbers": "line numbers taken out"}.get(edit.how, "")
104        extra = f" <span style='color:{muted}'>· {note}</span>" if note else ""
105        self.title.setText(f"<b>{number}.</b> {html.escape(name)} · {where}{extra}")
106        pending = edit.status == ed.PENDING and edit.located
107        self.accept_button.setVisible(pending)
108        self.reject_button.setVisible(pending)
109        self.show_button.setVisible(edit.status == ed.FAILED)
110        state = {ed.APPLIED: "✓ applied", ed.REJECTED: "rejected",
111                 ed.STALE: "⚠ the code changed since: not applied",
112                 ed.FAILED: f"⚠ {edit.problem}"}.get(edit.status, "")
113        self.state.setText(f"<span style='color:{muted}'>{html.escape(state)}</span>" if state else "")
114        rows = []
115        for kind, line in diff_rows(file_text, edit):
116            text = html.escape(line) or " "
117            if kind == " ":
118                rows.append(f"<div style='color:{muted}'>&nbsp; {text}</div>")
119            else:
120                background, color = colors[kind]
121                rows.append(f"<div style='background:{background}; color:{color}'>{kind} {text}</div>")
122        self.diff.setText(f"<div style='font-family:\"{font_family}\"; white-space:pre'>{''.join(rows)}</div>")
123        self.diff.setVisible(edit.status != ed.FAILED or self.show_button.isChecked())
124        self.setEnabled(True)
125        dim = edit.status in (ed.REJECTED, ed.STALE)
126        self.diff.setStyleSheet("color: gray;" if dim else "")
127
128
129class EditList(QWidget):
130    """The cards, and Accept All / Reject All (with ``scrolling`` False, as
131    tall as the cards: in something that scrolls itself)."""
132
133    accept_requested = Signal(int)
134    reject_requested = Signal(int)
135    accept_all_requested = Signal()
136    reject_all_requested = Signal()
137    edit_clicked = Signal(int)
138
139    def __init__(self, parent=None, scrolling: bool = True):
140        super().__init__(parent)
141        self.cards: list[EditCard] = []
142        self.is_light = True
143        self.muted = "#8b949e"
144        self.font_family = "Menlo"
145        self.container = QWidget()
146        self.container.setObjectName("EditCards")
147        self.cards_layout = QVBoxLayout(self.container)
148        self.cards_layout.setContentsMargins(0, 0, 0, 0)
149        self.cards_layout.setSpacing(6)
150        if scrolling:
151            self.cards_layout.addStretch(1)
152            self.scroll = QScrollArea()
153            self.scroll.setWidgetResizable(True)
154            self.scroll.setFrameShape(QFrame.NoFrame)
155            self.scroll.setWidget(self.container)
156        self.summary = QLabel()
157        self.accept_all = QPushButton("Accept All")
158        self.accept_all.setToolTip("Make every pending edit (one step to undo)")
159        self.accept_all.clicked.connect(self.accept_all_requested)
160        self.reject_all = QPushButton("Reject All")
161        self.reject_all.clicked.connect(self.reject_all_requested)
162        footer = QHBoxLayout()
163        footer.addWidget(self.summary, 1)
164        footer.addWidget(self.reject_all)
165        footer.addWidget(self.accept_all)
166        layout = QVBoxLayout(self)
167        layout.setContentsMargins(0, 0, 0, 0)
168        layout.setSpacing(6)
169        layout.addWidget(self.scroll if scrolling else self.container, 1)
170        layout.addLayout(footer)
171
172    def set_palette(self, pal: dict, is_light: bool):
173        self.is_light = is_light
174        self.muted = pal["muted"]
175        self.setStyleSheet(f"#EditCard {{ border: 1px solid {pal['line']}; border-radius: 6px; "
176                           f"background: {pal['bg']}; }}"
177                           f"QScrollArea, #EditCards {{ background: {pal['bg']}; border: none; }}")
178
179    def show_edits(self, edits: list[ed.Edit], file_text: str, name: str, font_family: str):
180        """Show ``edits`` (located in ``file_text``, the editor's text)."""
181        self.font_family = font_family
182        while len(self.cards) < len(edits):
183            card = EditCard()
184            i = len(self.cards)
185            card.accept.connect(lambda i=i: self.accept_requested.emit(i))
186            card.reject.connect(lambda i=i: self.reject_requested.emit(i))
187            card.clicked.connect(lambda i=i: self.edit_clicked.emit(i))
188            self.cards_layout.insertWidget(len(self.cards), card)
189            self.cards.append(card)
190        while len(self.cards) > len(edits):
191            self.cards.pop().deleteLater()
192        colors = DIFF_COLORS["light" if self.is_light else "dark"]
193        for i, (card, edit) in enumerate(zip(self.cards, edits, strict=True)):
194            card.show_edit(i + 1, edit, file_text, name, colors, self.muted, font_family)
195        pending = sum(e.status == ed.PENDING and e.located for e in edits)
196        counts = {s: sum(e.status == s for e in edits) for s in (ed.APPLIED, ed.REJECTED, ed.FAILED, ed.STALE)}
197        parts = [f"{len(edits)} edit{'s' * (len(edits) != 1)} proposed"]
198        parts += [f"{n} {word}" for word, n in (("applied", counts[ed.APPLIED]), ("rejected", counts[ed.REJECTED]),
199                                                ("not found", counts[ed.FAILED]), ("out of date", counts[ed.STALE]))
200                  if n]
201        self.summary.setText(f"<span style='color:{self.muted}'>{' · '.join(parts)}</span>")
202        self.accept_all.setEnabled(pending > 0)
203        self.accept_all.setText(f"Accept All ({pending})" if pending else "Accept All")
204        self.reject_all.setEnabled(pending > 0)
205        self.setVisible(bool(edits))
CONTEXT_LINES = 2
DIFF_COLORS = {'light': {'-': ('#ffebe9', '#82071e'), '+': ('#dafbe1', '#116329')}, 'dark': {'-': ('#3d1d20', '#ffa198'), '+': ('#12261e', '#7ee787')}}
def diff_rows(file_text: str, edit: vibefpmgui.ai_edits.Edit) -> list[tuple[str, str]]:
35def diff_rows(file_text: str, edit: ed.Edit) -> list[tuple[str, str]]:
36    """The change as (" " | "-" | "+", line) rows, with a few unchanged lines
37    around it; for an edit that wasn't found, the model's own text."""
38    if edit.diff is not None:  # (made or turned down: as it was then)
39        return edit.diff
40    if not edit.located:
41        return ([("-", line) for line in edit.search.split("\n")]
42                + [("+", line) for line in edit.replace.split("\n")])
43    old = file_text.split("\n")
44    new = ed.apply(file_text, edit).split("\n")
45    first = max(0, edit.line - 1 - CONTEXT_LINES)
46    grown = edit.new_text.count("\n") - file_text.count("\n", edit.start, edit.end)
47    last_old = min(len(old), edit.end_line + CONTEXT_LINES)
48    last_new = min(len(new), last_old + grown)
49    rows = []
50    for line in difflib.ndiff(old[first:last_old], new[first:last_new]):
51        if line[:1] in " -+":
52            rows.append((line[0], line[2:]))
53    return rows

The change as (" " | "-" | "+", line) rows, with a few unchanged lines around it; for an edit that wasn't found, the model's own text.

class EditCard(PySide6.QtWidgets.QFrame):
 56class EditCard(QFrame):
 57    """One proposed edit."""
 58
 59    accept = Signal()
 60    reject = Signal()
 61    clicked = Signal()
 62
 63    def __init__(self, parent=None):
 64        super().__init__(parent)
 65        self.setObjectName("EditCard")
 66        self.setCursor(Qt.PointingHandCursor)
 67        self.title = QLabel()
 68        self.state = QLabel()
 69        self.accept_button = QPushButton("Accept")
 70        self.accept_button.clicked.connect(self.accept)
 71        self.reject_button = QPushButton("Reject")
 72        self.reject_button.clicked.connect(self.reject)
 73        self.show_button = QPushButton("Show")
 74        self.show_button.setCheckable(True)
 75        self.show_button.setToolTip("The block as the model wrote it")
 76        self.show_button.toggled.connect(lambda on: self.diff.setVisible(on))
 77        header = QHBoxLayout()
 78        header.setSpacing(8)
 79        header.addWidget(self.title)
 80        header.addStretch(1)
 81        header.addWidget(self.state)
 82        header.addWidget(self.show_button)
 83        header.addWidget(self.reject_button)
 84        header.addWidget(self.accept_button)
 85        self.diff = QLabel()
 86        self.diff.setTextFormat(Qt.RichText)
 87        self.diff.setTextInteractionFlags(Qt.TextSelectableByMouse)
 88        layout = QVBoxLayout(self)
 89        layout.setContentsMargins(10, 8, 10, 8)
 90        layout.setSpacing(6)
 91        layout.addLayout(header)
 92        layout.addWidget(self.diff)
 93
 94    def mousePressEvent(self, event):
 95        if event.button() == Qt.LeftButton:
 96            self.clicked.emit()
 97        super().mousePressEvent(event)
 98
 99    def show_edit(self, number: int, edit: ed.Edit, file_text: str, name: str, colors: dict,
100                  muted: str, font_family: str):
101        where = (f"line {edit.line}" if edit.line == edit.end_line else f"lines {edit.line}–{edit.end_line}") \
102            if edit.located else "not applied"
103        note = {"spacing": "spacing ignored", "indentation": "indentation adjusted",
104                "line numbers": "line numbers taken out"}.get(edit.how, "")
105        extra = f" <span style='color:{muted}'>· {note}</span>" if note else ""
106        self.title.setText(f"<b>{number}.</b> {html.escape(name)} · {where}{extra}")
107        pending = edit.status == ed.PENDING and edit.located
108        self.accept_button.setVisible(pending)
109        self.reject_button.setVisible(pending)
110        self.show_button.setVisible(edit.status == ed.FAILED)
111        state = {ed.APPLIED: "✓ applied", ed.REJECTED: "rejected",
112                 ed.STALE: "⚠ the code changed since: not applied",
113                 ed.FAILED: f"⚠ {edit.problem}"}.get(edit.status, "")
114        self.state.setText(f"<span style='color:{muted}'>{html.escape(state)}</span>" if state else "")
115        rows = []
116        for kind, line in diff_rows(file_text, edit):
117            text = html.escape(line) or " "
118            if kind == " ":
119                rows.append(f"<div style='color:{muted}'>&nbsp; {text}</div>")
120            else:
121                background, color = colors[kind]
122                rows.append(f"<div style='background:{background}; color:{color}'>{kind} {text}</div>")
123        self.diff.setText(f"<div style='font-family:\"{font_family}\"; white-space:pre'>{''.join(rows)}</div>")
124        self.diff.setVisible(edit.status != ed.FAILED or self.show_button.isChecked())
125        self.setEnabled(True)
126        dim = edit.status in (ed.REJECTED, ed.STALE)
127        self.diff.setStyleSheet("color: gray;" if dim else "")

One proposed edit.

EditCard(parent=None)
63    def __init__(self, parent=None):
64        super().__init__(parent)
65        self.setObjectName("EditCard")
66        self.setCursor(Qt.PointingHandCursor)
67        self.title = QLabel()
68        self.state = QLabel()
69        self.accept_button = QPushButton("Accept")
70        self.accept_button.clicked.connect(self.accept)
71        self.reject_button = QPushButton("Reject")
72        self.reject_button.clicked.connect(self.reject)
73        self.show_button = QPushButton("Show")
74        self.show_button.setCheckable(True)
75        self.show_button.setToolTip("The block as the model wrote it")
76        self.show_button.toggled.connect(lambda on: self.diff.setVisible(on))
77        header = QHBoxLayout()
78        header.setSpacing(8)
79        header.addWidget(self.title)
80        header.addStretch(1)
81        header.addWidget(self.state)
82        header.addWidget(self.show_button)
83        header.addWidget(self.reject_button)
84        header.addWidget(self.accept_button)
85        self.diff = QLabel()
86        self.diff.setTextFormat(Qt.RichText)
87        self.diff.setTextInteractionFlags(Qt.TextSelectableByMouse)
88        layout = QVBoxLayout(self)
89        layout.setContentsMargins(10, 8, 10, 8)
90        layout.setSpacing(6)
91        layout.addLayout(header)
92        layout.addWidget(self.diff)

__init__(self, /, parent: PySide6.QtWidgets.QWidget | None = None, f: PySide6.QtCore.Qt.WindowType = Default(Qt.WindowFlags), *, frameShape: PySide6.QtWidgets.QFrame.Shape | None = None, frameShadow: PySide6.QtWidgets.QFrame.Shadow | None = None, lineWidth: int | None = None, midLineWidth: int | None = None, frameWidth: int | None = None, frameRect: PySide6.QtCore.QRect | None = None) -> None

Initialize self. See help(type(self)) for accurate signature.

def accept(unknown):

The type of the None singleton.

def reject(unknown):

The type of the None singleton.

def clicked(unknown):

The type of the None singleton.

title
state
accept_button
reject_button
show_button
diff
def mousePressEvent(self, event):
94    def mousePressEvent(self, event):
95        if event.button() == Qt.LeftButton:
96            self.clicked.emit()
97        super().mousePressEvent(event)

mousePressEvent(self, event: PySide6.QtGui.QMouseEvent, /) -> None

def show_edit( self, number: int, edit: vibefpmgui.ai_edits.Edit, file_text: str, name: str, colors: dict, muted: str, font_family: str):
 99    def show_edit(self, number: int, edit: ed.Edit, file_text: str, name: str, colors: dict,
100                  muted: str, font_family: str):
101        where = (f"line {edit.line}" if edit.line == edit.end_line else f"lines {edit.line}–{edit.end_line}") \
102            if edit.located else "not applied"
103        note = {"spacing": "spacing ignored", "indentation": "indentation adjusted",
104                "line numbers": "line numbers taken out"}.get(edit.how, "")
105        extra = f" <span style='color:{muted}'>· {note}</span>" if note else ""
106        self.title.setText(f"<b>{number}.</b> {html.escape(name)} · {where}{extra}")
107        pending = edit.status == ed.PENDING and edit.located
108        self.accept_button.setVisible(pending)
109        self.reject_button.setVisible(pending)
110        self.show_button.setVisible(edit.status == ed.FAILED)
111        state = {ed.APPLIED: "✓ applied", ed.REJECTED: "rejected",
112                 ed.STALE: "⚠ the code changed since: not applied",
113                 ed.FAILED: f"⚠ {edit.problem}"}.get(edit.status, "")
114        self.state.setText(f"<span style='color:{muted}'>{html.escape(state)}</span>" if state else "")
115        rows = []
116        for kind, line in diff_rows(file_text, edit):
117            text = html.escape(line) or " "
118            if kind == " ":
119                rows.append(f"<div style='color:{muted}'>&nbsp; {text}</div>")
120            else:
121                background, color = colors[kind]
122                rows.append(f"<div style='background:{background}; color:{color}'>{kind} {text}</div>")
123        self.diff.setText(f"<div style='font-family:\"{font_family}\"; white-space:pre'>{''.join(rows)}</div>")
124        self.diff.setVisible(edit.status != ed.FAILED or self.show_button.isChecked())
125        self.setEnabled(True)
126        dim = edit.status in (ed.REJECTED, ed.STALE)
127        self.diff.setStyleSheet("color: gray;" if dim else "")
staticMetaObject = PySide6.QtCore.QMetaObject("EditCard" inherits "QFrame": Methods: #33 type=Signal, signature=accept() #34 type=Signal, signature=reject() #35 type=Signal, signature=clicked() )
class EditList(PySide6.QtWidgets.QWidget):
130class EditList(QWidget):
131    """The cards, and Accept All / Reject All (with ``scrolling`` False, as
132    tall as the cards: in something that scrolls itself)."""
133
134    accept_requested = Signal(int)
135    reject_requested = Signal(int)
136    accept_all_requested = Signal()
137    reject_all_requested = Signal()
138    edit_clicked = Signal(int)
139
140    def __init__(self, parent=None, scrolling: bool = True):
141        super().__init__(parent)
142        self.cards: list[EditCard] = []
143        self.is_light = True
144        self.muted = "#8b949e"
145        self.font_family = "Menlo"
146        self.container = QWidget()
147        self.container.setObjectName("EditCards")
148        self.cards_layout = QVBoxLayout(self.container)
149        self.cards_layout.setContentsMargins(0, 0, 0, 0)
150        self.cards_layout.setSpacing(6)
151        if scrolling:
152            self.cards_layout.addStretch(1)
153            self.scroll = QScrollArea()
154            self.scroll.setWidgetResizable(True)
155            self.scroll.setFrameShape(QFrame.NoFrame)
156            self.scroll.setWidget(self.container)
157        self.summary = QLabel()
158        self.accept_all = QPushButton("Accept All")
159        self.accept_all.setToolTip("Make every pending edit (one step to undo)")
160        self.accept_all.clicked.connect(self.accept_all_requested)
161        self.reject_all = QPushButton("Reject All")
162        self.reject_all.clicked.connect(self.reject_all_requested)
163        footer = QHBoxLayout()
164        footer.addWidget(self.summary, 1)
165        footer.addWidget(self.reject_all)
166        footer.addWidget(self.accept_all)
167        layout = QVBoxLayout(self)
168        layout.setContentsMargins(0, 0, 0, 0)
169        layout.setSpacing(6)
170        layout.addWidget(self.scroll if scrolling else self.container, 1)
171        layout.addLayout(footer)
172
173    def set_palette(self, pal: dict, is_light: bool):
174        self.is_light = is_light
175        self.muted = pal["muted"]
176        self.setStyleSheet(f"#EditCard {{ border: 1px solid {pal['line']}; border-radius: 6px; "
177                           f"background: {pal['bg']}; }}"
178                           f"QScrollArea, #EditCards {{ background: {pal['bg']}; border: none; }}")
179
180    def show_edits(self, edits: list[ed.Edit], file_text: str, name: str, font_family: str):
181        """Show ``edits`` (located in ``file_text``, the editor's text)."""
182        self.font_family = font_family
183        while len(self.cards) < len(edits):
184            card = EditCard()
185            i = len(self.cards)
186            card.accept.connect(lambda i=i: self.accept_requested.emit(i))
187            card.reject.connect(lambda i=i: self.reject_requested.emit(i))
188            card.clicked.connect(lambda i=i: self.edit_clicked.emit(i))
189            self.cards_layout.insertWidget(len(self.cards), card)
190            self.cards.append(card)
191        while len(self.cards) > len(edits):
192            self.cards.pop().deleteLater()
193        colors = DIFF_COLORS["light" if self.is_light else "dark"]
194        for i, (card, edit) in enumerate(zip(self.cards, edits, strict=True)):
195            card.show_edit(i + 1, edit, file_text, name, colors, self.muted, font_family)
196        pending = sum(e.status == ed.PENDING and e.located for e in edits)
197        counts = {s: sum(e.status == s for e in edits) for s in (ed.APPLIED, ed.REJECTED, ed.FAILED, ed.STALE)}
198        parts = [f"{len(edits)} edit{'s' * (len(edits) != 1)} proposed"]
199        parts += [f"{n} {word}" for word, n in (("applied", counts[ed.APPLIED]), ("rejected", counts[ed.REJECTED]),
200                                                ("not found", counts[ed.FAILED]), ("out of date", counts[ed.STALE]))
201                  if n]
202        self.summary.setText(f"<span style='color:{self.muted}'>{' · '.join(parts)}</span>")
203        self.accept_all.setEnabled(pending > 0)
204        self.accept_all.setText(f"Accept All ({pending})" if pending else "Accept All")
205        self.reject_all.setEnabled(pending > 0)
206        self.setVisible(bool(edits))

The cards, and Accept All / Reject All (with scrolling False, as tall as the cards: in something that scrolls itself).

EditList(parent=None, scrolling: bool = True)
140    def __init__(self, parent=None, scrolling: bool = True):
141        super().__init__(parent)
142        self.cards: list[EditCard] = []
143        self.is_light = True
144        self.muted = "#8b949e"
145        self.font_family = "Menlo"
146        self.container = QWidget()
147        self.container.setObjectName("EditCards")
148        self.cards_layout = QVBoxLayout(self.container)
149        self.cards_layout.setContentsMargins(0, 0, 0, 0)
150        self.cards_layout.setSpacing(6)
151        if scrolling:
152            self.cards_layout.addStretch(1)
153            self.scroll = QScrollArea()
154            self.scroll.setWidgetResizable(True)
155            self.scroll.setFrameShape(QFrame.NoFrame)
156            self.scroll.setWidget(self.container)
157        self.summary = QLabel()
158        self.accept_all = QPushButton("Accept All")
159        self.accept_all.setToolTip("Make every pending edit (one step to undo)")
160        self.accept_all.clicked.connect(self.accept_all_requested)
161        self.reject_all = QPushButton("Reject All")
162        self.reject_all.clicked.connect(self.reject_all_requested)
163        footer = QHBoxLayout()
164        footer.addWidget(self.summary, 1)
165        footer.addWidget(self.reject_all)
166        footer.addWidget(self.accept_all)
167        layout = QVBoxLayout(self)
168        layout.setContentsMargins(0, 0, 0, 0)
169        layout.setSpacing(6)
170        layout.addWidget(self.scroll if scrolling else self.container, 1)
171        layout.addLayout(footer)

__init__(self, /, parent: PySide6.QtWidgets.QWidget | None = None, f: PySide6.QtCore.Qt.WindowType = Default(Qt.WindowFlags), *, modal: bool | None = None, windowModality: PySide6.QtCore.Qt.WindowModality | None = None, enabled: bool | None = None, geometry: PySide6.QtCore.QRect | None = None, frameGeometry: PySide6.QtCore.QRect | None = None, normalGeometry: PySide6.QtCore.QRect | None = None, x: int | None = None, y: int | None = None, pos: PySide6.QtCore.QPoint | None = None, frameSize: PySide6.QtCore.QSize | None = None, size: PySide6.QtCore.QSize | None = None, width: int | None = None, height: int | None = None, rect: PySide6.QtCore.QRect | None = None, childrenRect: PySide6.QtCore.QRect | None = None, childrenRegion: PySide6.QtGui.QRegion | None = None, sizePolicy: PySide6.QtWidgets.QSizePolicy | None = None, minimumSize: PySide6.QtCore.QSize | None = None, maximumSize: PySide6.QtCore.QSize | None = None, minimumWidth: int | None = None, minimumHeight: int | None = None, maximumWidth: int | None = None, maximumHeight: int | None = None, sizeIncrement: PySide6.QtCore.QSize | None = None, baseSize: PySide6.QtCore.QSize | None = None, palette: PySide6.QtGui.QPalette | None = None, font: PySide6.QtGui.QFont | None = None, cursor: PySide6.QtGui.QCursor | None = None, mouseTracking: bool | None = None, tabletTracking: bool | None = None, isActiveWindow: bool | None = None, focusPolicy: PySide6.QtCore.Qt.FocusPolicy | None = None, focus: bool | None = None, contextMenuPolicy: PySide6.QtCore.Qt.ContextMenuPolicy | None = None, updatesEnabled: bool | None = None, visible: bool | None = None, minimized: bool | None = None, maximized: bool | None = None, fullScreen: bool | None = None, sizeHint: PySide6.QtCore.QSize | None = None, minimumSizeHint: PySide6.QtCore.QSize | None = None, acceptDrops: bool | None = None, windowTitle: str | None = None, windowIcon: PySide6.QtGui.QIcon | None = None, windowIconText: str | None = None, windowOpacity: float | None = None, windowModified: bool | None = None, toolTip: str | None = None, toolTipDuration: int | None = None, statusTip: str | None = None, whatsThis: str | None = None, accessibleName: str | None = None, accessibleDescription: str | None = None, accessibleIdentifier: str | None = None, layoutDirection: PySide6.QtCore.Qt.LayoutDirection | None = None, autoFillBackground: bool | None = None, styleSheet: str | None = None, locale: PySide6.QtCore.QLocale | None = None, windowFilePath: str | None = None, inputMethodHints: PySide6.QtCore.Qt.InputMethodHint | None = None) -> None

Initialize self. See help(type(self)) for accurate signature.

def accept_requested(unknown):

The type of the None singleton.

def reject_requested(unknown):

The type of the None singleton.

def accept_all_requested(unknown):

The type of the None singleton.

def reject_all_requested(unknown):

The type of the None singleton.

def edit_clicked(unknown):

The type of the None singleton.

cards: list[EditCard]
is_light
muted
font_family
container
cards_layout
summary
accept_all
reject_all
def set_palette(self, pal: dict, is_light: bool):
173    def set_palette(self, pal: dict, is_light: bool):
174        self.is_light = is_light
175        self.muted = pal["muted"]
176        self.setStyleSheet(f"#EditCard {{ border: 1px solid {pal['line']}; border-radius: 6px; "
177                           f"background: {pal['bg']}; }}"
178                           f"QScrollArea, #EditCards {{ background: {pal['bg']}; border: none; }}")
def show_edits( self, edits: list[vibefpmgui.ai_edits.Edit], file_text: str, name: str, font_family: str):
180    def show_edits(self, edits: list[ed.Edit], file_text: str, name: str, font_family: str):
181        """Show ``edits`` (located in ``file_text``, the editor's text)."""
182        self.font_family = font_family
183        while len(self.cards) < len(edits):
184            card = EditCard()
185            i = len(self.cards)
186            card.accept.connect(lambda i=i: self.accept_requested.emit(i))
187            card.reject.connect(lambda i=i: self.reject_requested.emit(i))
188            card.clicked.connect(lambda i=i: self.edit_clicked.emit(i))
189            self.cards_layout.insertWidget(len(self.cards), card)
190            self.cards.append(card)
191        while len(self.cards) > len(edits):
192            self.cards.pop().deleteLater()
193        colors = DIFF_COLORS["light" if self.is_light else "dark"]
194        for i, (card, edit) in enumerate(zip(self.cards, edits, strict=True)):
195            card.show_edit(i + 1, edit, file_text, name, colors, self.muted, font_family)
196        pending = sum(e.status == ed.PENDING and e.located for e in edits)
197        counts = {s: sum(e.status == s for e in edits) for s in (ed.APPLIED, ed.REJECTED, ed.FAILED, ed.STALE)}
198        parts = [f"{len(edits)} edit{'s' * (len(edits) != 1)} proposed"]
199        parts += [f"{n} {word}" for word, n in (("applied", counts[ed.APPLIED]), ("rejected", counts[ed.REJECTED]),
200                                                ("not found", counts[ed.FAILED]), ("out of date", counts[ed.STALE]))
201                  if n]
202        self.summary.setText(f"<span style='color:{self.muted}'>{' · '.join(parts)}</span>")
203        self.accept_all.setEnabled(pending > 0)
204        self.accept_all.setText(f"Accept All ({pending})" if pending else "Accept All")
205        self.reject_all.setEnabled(pending > 0)
206        self.setVisible(bool(edits))

Show edits (located in file_text, the editor's text).

staticMetaObject = PySide6.QtCore.QMetaObject("EditList" inherits "QWidget": Methods: #33 type=Signal, signature=accept_requested(int), parameters=int #34 type=Signal, signature=reject_requested(int), parameters=int #35 type=Signal, signature=accept_all_requested() #36 type=Signal, signature=reject_all_requested() #37 type=Signal, signature=edit_clicked(int), parameters=int )