draftomen 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. draftomen/__init__.py +17 -0
  2. draftomen/assets/draftomen.icns +0 -0
  3. draftomen/assets/draftomen.ico +0 -0
  4. draftomen/assets/draftomen_logo.png +0 -0
  5. draftomen/audit.py +606 -0
  6. draftomen/backtest.py +449 -0
  7. draftomen/benchmark.py +834 -0
  8. draftomen/carddb.py +1869 -0
  9. draftomen/cardimages.py +313 -0
  10. draftomen/cli.py +891 -0
  11. draftomen/config.py +133 -0
  12. draftomen/deckbuilder.py +2723 -0
  13. draftomen/events.py +772 -0
  14. draftomen/logfollow.py +451 -0
  15. draftomen/mock_session.py +745 -0
  16. draftomen/paths.py +120 -0
  17. draftomen/pickengine.py +1117 -0
  18. draftomen/pool.py +1259 -0
  19. draftomen/preferences.py +289 -0
  20. draftomen/qml/AboutDialog.qml +156 -0
  21. draftomen/qml/AppBar.qml +120 -0
  22. draftomen/qml/BacktestView.qml +316 -0
  23. draftomen/qml/BuildView.qml +1151 -0
  24. draftomen/qml/CardPreview.qml +434 -0
  25. draftomen/qml/DimensionalButton.qml +42 -0
  26. draftomen/qml/DimensionalComboBox.qml +182 -0
  27. draftomen/qml/DimensionalSurface.qml +118 -0
  28. draftomen/qml/DimensionalTabButton.qml +41 -0
  29. draftomen/qml/LiveDraftView.qml +468 -0
  30. draftomen/qml/Main.qml +175 -0
  31. draftomen/qml/NavigationRail.qml +226 -0
  32. draftomen/qml/PoolSummaryPanel.qml +322 -0
  33. draftomen/qml/PrivacyDialog.qml +96 -0
  34. draftomen/qml/RecentPickThumbnail.qml +83 -0
  35. draftomen/qml/RecentPicksGallery.qml +333 -0
  36. draftomen/qml/RecommendationRow.qml +404 -0
  37. draftomen/qml/SettingsSwitch.qml +141 -0
  38. draftomen/qml/SettingsView.qml +531 -0
  39. draftomen/qml/StateBanner.qml +189 -0
  40. draftomen/qml/StatusStrip.qml +84 -0
  41. draftomen/qml/Theme.qml +71 -0
  42. draftomen/qml/qmldir +23 -0
  43. draftomen/qt_adapter.py +884 -0
  44. draftomen/qt_gui.py +338 -0
  45. draftomen/qt_mock.py +63 -0
  46. draftomen/ranking.py +126 -0
  47. draftomen/replay.py +480 -0
  48. draftomen/session.py +3668 -0
  49. draftomen/setinfo.py +26 -0
  50. draftomen/seventeen.py +2766 -0
  51. draftomen/splash.py +617 -0
  52. draftomen/tui.py +4007 -0
  53. draftomen/watch.py +336 -0
  54. draftomen-0.3.0.dist-info/METADATA +156 -0
  55. draftomen-0.3.0.dist-info/RECORD +59 -0
  56. draftomen-0.3.0.dist-info/WHEEL +5 -0
  57. draftomen-0.3.0.dist-info/entry_points.txt +6 -0
  58. draftomen-0.3.0.dist-info/licenses/LICENSE +21 -0
  59. draftomen-0.3.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,118 @@
1
+ import QtQuick 2.15
2
+
3
+ Item {
4
+ id: root
5
+ objectName: "dimensionalSurface"
6
+
7
+ property bool stateEnabled: true
8
+ property bool stateHovered: false
9
+ property bool statePressed: false
10
+ property bool stateFocused: false
11
+ property bool stateSelected: false
12
+ property bool stateExpanded: false
13
+ property bool accented: false
14
+ property color accentColor: Theme.primary
15
+
16
+ readonly property real faceY: root.statePressed ? Theme.controlDepth : 0
17
+ readonly property color fillColor: {
18
+ if (!root.stateEnabled)
19
+ return Theme.controlDisabled
20
+ return root.accented ? root.accentColor : Theme.neutral
21
+ }
22
+ readonly property color topFillColor: {
23
+ if (!root.stateEnabled)
24
+ return Qt.lighter(root.fillColor, 1.04)
25
+ if (root.statePressed)
26
+ return Qt.darker(root.fillColor, 1.08)
27
+ if (root.stateHovered || root.stateExpanded)
28
+ return Qt.lighter(root.fillColor, 1.18)
29
+ if (root.stateSelected)
30
+ return Qt.lighter(root.fillColor, 1.13)
31
+ return Qt.lighter(root.fillColor, 1.08)
32
+ }
33
+ readonly property color bottomFillColor: {
34
+ if (!root.stateEnabled)
35
+ return Qt.darker(root.fillColor, 1.08)
36
+ if (root.statePressed)
37
+ return Qt.darker(root.fillColor, 1.2)
38
+ if (root.stateHovered || root.stateExpanded)
39
+ return Qt.lighter(root.fillColor, 1.02)
40
+ if (root.stateSelected)
41
+ return Qt.darker(root.fillColor, 1.03)
42
+ return Qt.darker(root.fillColor, 1.1)
43
+ }
44
+ readonly property color faceOutlineColor: {
45
+ if (!root.stateEnabled)
46
+ return Theme.outlineDisabled
47
+ if (root.accented)
48
+ return Qt.darker(root.accentColor, root.statePressed ? 1.45 : 1.3)
49
+ if (root.stateHovered || root.stateSelected || root.stateExpanded)
50
+ return Qt.lighter(Theme.controlNeutralBorder, 1.18)
51
+ return Theme.controlNeutralBorder
52
+ }
53
+ readonly property color outlineColor: root.stateFocused
54
+ ? Theme.focus : root.faceOutlineColor
55
+ readonly property int outlineWidth: root.stateFocused ? 2 : 1
56
+
57
+ Rectangle {
58
+ id: lowerEdge
59
+ x: 0
60
+ y: Theme.controlDepth
61
+ width: root.width
62
+ height: Math.max(0, root.height - Theme.controlDepth)
63
+ radius: Theme.controlRadius
64
+ color: root.stateEnabled ? Theme.controlEdge : Theme.outlineDisabled
65
+ opacity: root.statePressed ? 0 : (root.stateHovered ? 1 : 0.9)
66
+ }
67
+
68
+ Rectangle {
69
+ id: face
70
+ x: 0
71
+ y: root.faceY
72
+ width: root.width
73
+ height: Math.max(0, root.height - Theme.controlDepth)
74
+ radius: Theme.controlRadius
75
+ border.color: root.faceOutlineColor
76
+ border.width: 1
77
+ gradient: Gradient {
78
+ GradientStop {
79
+ position: 0
80
+ color: root.topFillColor
81
+ }
82
+ GradientStop {
83
+ position: 1
84
+ color: root.bottomFillColor
85
+ }
86
+ }
87
+ }
88
+
89
+ Rectangle {
90
+ anchors.fill: face
91
+ anchors.margins: 1
92
+ radius: Math.max(0, Theme.controlRadius - 1)
93
+ color: "transparent"
94
+ border.color: Theme.controlHighlight
95
+ border.width: 1
96
+ opacity: {
97
+ if (!root.stateEnabled)
98
+ return 0.07
99
+ if (root.statePressed)
100
+ return 0.1
101
+ if (root.stateHovered)
102
+ return root.accented ? 0.36 : 0.24
103
+ return root.accented ? 0.25 : 0.14
104
+ }
105
+ }
106
+
107
+ Rectangle {
108
+ x: face.x - Theme.controlFocusGap
109
+ y: face.y - Theme.controlFocusGap
110
+ width: face.width + Theme.controlFocusGap * 2
111
+ height: face.height + Theme.controlFocusGap * 2
112
+ radius: Theme.controlRadius + Theme.controlFocusGap
113
+ color: "transparent"
114
+ border.color: root.outlineColor
115
+ border.width: root.outlineWidth
116
+ visible: root.stateEnabled && root.stateFocused
117
+ }
118
+ }
@@ -0,0 +1,41 @@
1
+ import QtQuick 2.15
2
+ import QtQuick.Controls 2.15
3
+
4
+ TabButton {
5
+ id: control
6
+
7
+ property bool expanded: false
8
+ property bool accented: checked
9
+ property color accentColor: Theme.primary
10
+ property color accentContentColor: Theme.primaryContent
11
+ readonly property color contentColor: {
12
+ if (!control.enabled)
13
+ return Theme.controlDisabledContent
14
+ return control.accented ? control.accentContentColor : Theme.neutralContent
15
+ }
16
+
17
+ implicitHeight: Theme.targetHeight
18
+
19
+ background: DimensionalSurface {
20
+ stateEnabled: control.enabled
21
+ stateHovered: control.hovered
22
+ statePressed: control.down
23
+ stateFocused: control.activeFocus
24
+ stateSelected: control.checked
25
+ stateExpanded: control.expanded
26
+ accented: control.accented
27
+ accentColor: control.accentColor
28
+ }
29
+
30
+ contentItem: Text {
31
+ transform: Translate {
32
+ y: control.down ? Theme.controlPressOffset : 0
33
+ }
34
+ text: control.text
35
+ color: control.contentColor
36
+ font: control.font
37
+ horizontalAlignment: Text.AlignHCenter
38
+ verticalAlignment: Text.AlignVCenter
39
+ elide: Text.ElideRight
40
+ }
41
+ }
@@ -0,0 +1,468 @@
1
+ pragma ComponentBehavior: Bound
2
+
3
+ import QtQuick 2.15
4
+ import QtQuick.Controls 2.15
5
+ import QtQuick.Layouts 1.15
6
+
7
+ Item {
8
+ id: root
9
+ objectName: "liveDraftView"
10
+
11
+ required property var sessionState
12
+ required property var recommendationModel
13
+ required property bool narrow
14
+ required property var displayPreferences
15
+
16
+ // Keep the documented 1440px layout wide while making room for the
17
+ // larger details pane; the recommendation list remains scrollable.
18
+ readonly property int wideCardDetailsWidth: 550
19
+ readonly property int wideRecommendationsMinimumListWidth: 662
20
+ readonly property int wideRecommendationsMinimumWidth:
21
+ root.wideRecommendationsMinimumListWidth
22
+ + root.wideCardDetailsWidth + Theme.gutter
23
+ readonly property int wideCardPreviewMinimumHeight: 410
24
+ readonly property int widePoolDetailsMinimumHeight: 180
25
+ readonly property int wideRecommendationsMinimumHeight:
26
+ root.wideCardPreviewMinimumHeight + Theme.gutter
27
+ + root.widePoolDetailsMinimumHeight
28
+ readonly property real wideRecommendationsAvailableHeight:
29
+ root.height - draftHeader.implicitHeight
30
+ - (stateBanner.visible ? stateBanner.implicitHeight : 0)
31
+ - Theme.gutter * 2
32
+ readonly property bool wideRecommendations: !root.narrow
33
+ && root.width >= root.wideRecommendationsMinimumWidth
34
+ && root.wideRecommendationsAvailableHeight
35
+ >= root.wideRecommendationsMinimumHeight
36
+
37
+ // Preserve the normal recommendation/detail balance while reserving
38
+ // enough height for the details tab at the supported compact minimum.
39
+ readonly property int narrowRecommendationPreferredHeight: 220
40
+ readonly property int narrowDetailsPreferredHeight: 340
41
+ readonly property int narrowDetailsMinimumHeight: 276
42
+ readonly property int narrowLayoutSpacing: 8
43
+ readonly property real narrowRecommendationAvailableHeight:
44
+ root.height - draftHeader.implicitHeight
45
+ - (stateBanner.visible ? stateBanner.implicitHeight : 0)
46
+ - Theme.gutter * 2
47
+ - detailTabs.implicitHeight - root.narrowLayoutSpacing * 2
48
+ - root.narrowDetailsMinimumHeight
49
+ readonly property real narrowRecommendationMinimumHeight: Math.max(
50
+ 0, Math.min(
51
+ root.narrowRecommendationPreferredHeight,
52
+ root.narrowRecommendationAvailableHeight
53
+ )
54
+ )
55
+ readonly property bool hasRecommendations: sessionState.recommendations
56
+ && sessionState.recommendations.cards
57
+ && sessionState.recommendations.cards.length > 0
58
+ readonly property bool hasSetupGuidance: Boolean(
59
+ sessionState.status && sessionState.status.setup_guidance
60
+ )
61
+ readonly property string confidenceSummary: {
62
+ const recommendations = sessionState.recommendations
63
+ return recommendations && recommendations.confidence_summary
64
+ ? String(recommendations.confidence_summary) : ""
65
+ }
66
+
67
+ readonly property string draftHeading: {
68
+ const draft = sessionState.draft
69
+ if (!draft) {
70
+ if (root.hasSetupGuidance)
71
+ return "Arena setup needed"
72
+ return sessionState.status.message
73
+ }
74
+ if (draft.completed)
75
+ return "Draft complete"
76
+ if (draft.pack_number === null || draft.pick_number === null)
77
+ return draft.event_name
78
+ return "Pack " + (draft.pack_number + 1)
79
+ + " · Pick " + (draft.pick_number + 1)
80
+ }
81
+ readonly property string emptyHeading: {
82
+ if (sessionState.draft && sessionState.draft.completed)
83
+ return "Draft complete"
84
+ if (sessionState.status.phase === "starting")
85
+ return "Loading live draft data"
86
+ if (root.hasSetupGuidance)
87
+ return "Arena setup needed"
88
+ return "Ready for your next Quick Draft"
89
+ }
90
+ readonly property var selectedRecommendation: {
91
+ const recommendations = sessionState.recommendations
92
+ if (!recommendations || !recommendations.cards)
93
+ return null
94
+ const cards = recommendations.cards
95
+ for (let index = 0; index < cards.length; index++) {
96
+ const recommendation = cards[index]
97
+ if (recommendation.card.grp_id === recommendations.selected_grp_id)
98
+ return recommendation
99
+ }
100
+ return cards.length > 0 ? cards[0] : null
101
+ }
102
+
103
+ property bool recommendationFocusPublishedWhileVisible: false
104
+
105
+ onVisibleChanged: {
106
+ if (!root.visible) {
107
+ root.recommendationFocusPublishedWhileVisible = false
108
+ return
109
+ }
110
+ Qt.callLater(function() {
111
+ if (!root.visible || root.recommendationFocusPublishedWhileVisible
112
+ || !root.selectedRecommendation) {
113
+ return
114
+ }
115
+ root.recommendationFocusPublishedWhileVisible = true
116
+ sessionProvider.chooseRecommendation(
117
+ root.selectedRecommendation.card.grp_id
118
+ )
119
+ })
120
+ }
121
+
122
+ ColumnLayout {
123
+ anchors.fill: parent
124
+ spacing: Theme.gutter
125
+
126
+ RowLayout {
127
+ id: draftHeader
128
+ Layout.fillWidth: true
129
+ spacing: 16
130
+
131
+ ColumnLayout {
132
+ Layout.fillWidth: true
133
+ Layout.minimumWidth: 0
134
+ spacing: 2
135
+
136
+ Label {
137
+ text: root.draftHeading
138
+ color: Theme.text
139
+ font.pixelSize: Theme.textPixelSize(22)
140
+ font.bold: true
141
+ Layout.fillWidth: true
142
+ elide: Text.ElideRight
143
+ }
144
+
145
+ Label {
146
+ text: root.hasRecommendations
147
+ ? root.sessionState.recommendations.cards.length
148
+ + " cards available"
149
+ : root.sessionState.status.message
150
+ color: Theme.textMuted
151
+ Layout.fillWidth: true
152
+ elide: Text.ElideRight
153
+ }
154
+ }
155
+
156
+ ColumnLayout {
157
+ visible: root.hasRecommendations
158
+ Layout.fillWidth: true
159
+ Layout.minimumWidth: 0
160
+ Layout.alignment: Qt.AlignVCenter
161
+ spacing: 2
162
+
163
+ Label {
164
+ objectName: "recommendationConfidenceSummary"
165
+ visible: root.confidenceSummary.length > 0
166
+ text: root.confidenceSummary
167
+ color: Theme.text
168
+ font.pixelSize: Theme.textPixelSize(12)
169
+ font.bold: true
170
+ wrapMode: Text.WordWrap
171
+ }
172
+
173
+ Label {
174
+ text: root.sessionState.status.message
175
+ color: Theme.textMuted
176
+ font.pixelSize: Theme.textPixelSize(12)
177
+ Layout.fillWidth: true
178
+ elide: Text.ElideRight
179
+ }
180
+ }
181
+
182
+ DimensionalComboBox {
183
+ id: rankingSelector
184
+ objectName: "rankingSelector"
185
+ Layout.preferredWidth: root.narrow ? 138 : 166
186
+ model: [
187
+ { key: "score", label: "DO Score" },
188
+ { key: "win_rate", label: "17L WR" },
189
+ { key: "alsa", label: "ALSA" },
190
+ { key: "mana_value", label: "Mana value" }
191
+ ]
192
+ textRole: "label"
193
+ valueRole: "key"
194
+ currentIndex: {
195
+ const recommendations = root.sessionState.recommendations
196
+ if (!recommendations)
197
+ return 0
198
+ for (let index = 0; index < model.length; index++)
199
+ if (model[index].key === recommendations.ranking_mode)
200
+ return index
201
+ return 0
202
+ }
203
+ Accessible.name: "Recommendation ranking"
204
+ Accessible.description: "Choose DO Score, 17L WR, ALSA, or mana value."
205
+ onActivated: sessionProvider.changeRanking(currentValue)
206
+ }
207
+ }
208
+
209
+ StateBanner {
210
+ id: stateBanner
211
+ objectName: "liveStateBanner"
212
+ Layout.fillWidth: true
213
+ sessionState: root.sessionState
214
+ }
215
+
216
+ Rectangle {
217
+ visible: !root.hasRecommendations
218
+ Layout.fillWidth: true
219
+ Layout.fillHeight: true
220
+ color: Theme.surfaceLow
221
+ border.color: Theme.outline
222
+ border.width: 1
223
+ radius: Theme.radius
224
+
225
+ ColumnLayout {
226
+ anchors.centerIn: parent
227
+ width: Math.min(parent.width - 48, 520)
228
+ spacing: 14
229
+
230
+ Label {
231
+ objectName: "preDraftHeading"
232
+ Layout.fillWidth: true
233
+ text: root.emptyHeading
234
+ color: Theme.text
235
+ font.pixelSize: Theme.textPixelSize(22)
236
+ font.bold: true
237
+ horizontalAlignment: Text.AlignHCenter
238
+ }
239
+
240
+ Label {
241
+ objectName: "preDraftGuidance"
242
+ Layout.fillWidth: true
243
+ text: root.hasSetupGuidance
244
+ ? root.sessionState.status.message
245
+ : "Draft Omen follows Arena automatically and never writes to the game."
246
+ color: root.hasSetupGuidance ? Theme.text : Theme.textMuted
247
+ horizontalAlignment: Text.AlignHCenter
248
+ wrapMode: Text.WordWrap
249
+ }
250
+
251
+ Repeater {
252
+ model: [
253
+ ["Card metadata", root.sessionState.card_data.message],
254
+ ["Arena log", root.sessionState.status.message],
255
+ ["Arena account", root.sessionState.active_account
256
+ ? root.sessionState.active_account.screen_name
257
+ || root.sessionState.active_account.account_id
258
+ : "Not detected"],
259
+ ["Draft", root.sessionState.draft
260
+ ? root.sessionState.draft.event_name
261
+ : "No Quick Draft detected"],
262
+ ["Ratings", root.sessionState.ratings.message]
263
+ ]
264
+
265
+ delegate: Rectangle {
266
+ required property var modelData
267
+
268
+ Layout.fillWidth: true
269
+ Layout.preferredHeight: 46
270
+ color: Theme.surface
271
+ radius: Theme.radius
272
+
273
+ RowLayout {
274
+ anchors.fill: parent
275
+ anchors.leftMargin: 12
276
+ anchors.rightMargin: 12
277
+
278
+ Label {
279
+ Layout.preferredWidth: 126
280
+ text: modelData[0]
281
+ color: Theme.text
282
+ font.bold: true
283
+ }
284
+ Label {
285
+ Layout.fillWidth: true
286
+ text: modelData[1]
287
+ color: Theme.textMuted
288
+ elide: Text.ElideRight
289
+ }
290
+ }
291
+ }
292
+ }
293
+ }
294
+ }
295
+
296
+ RowLayout {
297
+ visible: root.wideRecommendations && root.hasRecommendations
298
+ Layout.fillWidth: true
299
+ Layout.fillHeight: true
300
+ spacing: Theme.gutter
301
+
302
+ Rectangle {
303
+ Layout.fillWidth: true
304
+ Layout.fillHeight: true
305
+ Layout.minimumWidth: root.wideRecommendationsMinimumListWidth
306
+ color: "transparent"
307
+
308
+ ColumnLayout {
309
+ anchors.fill: parent
310
+ spacing: 6
311
+
312
+ RowLayout {
313
+ Layout.fillWidth: true
314
+ Layout.leftMargin: 14
315
+ Layout.rightMargin: 12
316
+ spacing: 8
317
+
318
+ Label {
319
+ objectName: "recommendationHeaderRank"
320
+ Layout.preferredWidth: 30
321
+ text: "#"
322
+ color: Theme.textMuted
323
+ font.pixelSize: Theme.textPixelSize(11)
324
+ }
325
+ Label {
326
+ objectName: "recommendationHeaderCard"
327
+ Layout.fillWidth: true
328
+ text: "CARD"
329
+ color: Theme.textMuted
330
+ font.pixelSize: Theme.textPixelSize(11)
331
+ }
332
+ Label { Layout.preferredWidth: 70; text: "COLORS"; color: Theme.textMuted; font.pixelSize: Theme.textPixelSize(11); horizontalAlignment: Text.AlignHCenter }
333
+ Label { Layout.preferredWidth: 58; text: "DO"; color: Theme.textMuted; font.pixelSize: Theme.textPixelSize(11); horizontalAlignment: Text.AlignRight }
334
+ Label { Layout.preferredWidth: 68; text: "17L WR"; color: Theme.textMuted; font.pixelSize: Theme.textPixelSize(11); horizontalAlignment: Text.AlignRight }
335
+ Label { Layout.preferredWidth: 44; text: "GRADE"; color: Theme.textMuted; font.pixelSize: Theme.textPixelSize(11); horizontalAlignment: Text.AlignHCenter }
336
+ Label { Layout.preferredWidth: 82; text: "FIT"; color: Theme.textMuted; font.pixelSize: Theme.textPixelSize(11); horizontalAlignment: Text.AlignRight }
337
+ }
338
+
339
+ ListView {
340
+ Layout.fillWidth: true
341
+ Layout.fillHeight: true
342
+ delegate: RecommendationRow {
343
+ required property var modelData
344
+ objectName: "wideRecommendationRow" + modelData.rank
345
+
346
+ width: ListView.view.width
347
+ recommendation: modelData
348
+ selected: root.sessionState.recommendations.selected_grp_id
349
+ === modelData.card.grp_id
350
+ wide: root.wideRecommendations
351
+ secondaryStats: root.displayPreferences.secondaryStats
352
+ onChosen: grpId => sessionProvider.chooseRecommendation(grpId)
353
+ }
354
+ clip: true
355
+ model: root.wideRecommendations
356
+ ? root.recommendationModel : null
357
+ Accessible.name: "Ranked recommendations"
358
+
359
+ }
360
+ }
361
+ }
362
+
363
+ ColumnLayout {
364
+ Layout.preferredWidth: root.wideCardDetailsWidth
365
+ Layout.maximumWidth: root.wideCardDetailsWidth
366
+ Layout.fillHeight: true
367
+ Layout.minimumWidth: 350
368
+ spacing: Theme.gutter
369
+
370
+ CardPreview {
371
+ objectName: "wideLiveCardPreview"
372
+ Layout.fillWidth: true
373
+ Layout.preferredHeight: 430
374
+ Layout.minimumHeight: root.wideCardPreviewMinimumHeight
375
+ recommendation: root.selectedRecommendation
376
+ detailedIntel: true
377
+ detailedImageMaximumWidth: 250
378
+ detailedImageWidthRatio: 0.58
379
+ loading: root.sessionState.card_data.phase === "loading"
380
+ imageState: root.sessionState.card_image
381
+ constrainImageFrameToHeight: true
382
+ }
383
+
384
+ PoolSummaryPanel {
385
+ objectName: "wideLivePoolDetails"
386
+ Layout.fillWidth: true
387
+ Layout.fillHeight: true
388
+ Layout.minimumHeight: root.widePoolDetailsMinimumHeight
389
+ pool: root.sessionState.pool
390
+ narrow: root.narrow
391
+ }
392
+ }
393
+ }
394
+
395
+ ColumnLayout {
396
+ visible: !root.wideRecommendations && root.hasRecommendations
397
+ Layout.fillWidth: true
398
+ Layout.fillHeight: true
399
+ spacing: root.narrowLayoutSpacing
400
+
401
+ ListView {
402
+ objectName: "narrowRecommendationList"
403
+ Layout.fillWidth: true
404
+ Layout.fillHeight: true
405
+ Layout.minimumHeight: root.narrowRecommendationMinimumHeight
406
+ spacing: root.displayPreferences.compactDensity ? 3 : 6
407
+ clip: true
408
+ model: root.wideRecommendations ? null : root.recommendationModel
409
+ Accessible.name: "Ranked recommendations"
410
+
411
+ delegate: RecommendationRow {
412
+ required property var modelData
413
+ objectName: "narrowRecommendationRow" + modelData.rank
414
+
415
+ width: ListView.view.width
416
+ recommendation: modelData
417
+ selected: root.sessionState.recommendations.selected_grp_id
418
+ === modelData.card.grp_id
419
+ wide: false
420
+ secondaryStats: root.displayPreferences.secondaryStats
421
+ onChosen: grpId => sessionProvider.chooseRecommendation(grpId)
422
+ }
423
+ }
424
+
425
+ TabBar {
426
+ id: detailTabs
427
+ objectName: "liveDetailTabs"
428
+ Layout.fillWidth: true
429
+ currentIndex: 0
430
+ Accessible.name: "Live draft details"
431
+
432
+ DimensionalTabButton {
433
+ objectName: "liveCardDetailsTab"
434
+ text: "Card details"
435
+ Accessible.name: "Card details"
436
+ }
437
+ DimensionalTabButton {
438
+ objectName: "livePoolTab"
439
+ text: "Pool"
440
+ Accessible.name: "Pool details"
441
+ }
442
+ }
443
+
444
+ StackLayout {
445
+ Layout.fillWidth: true
446
+ Layout.preferredHeight: root.narrowDetailsPreferredHeight
447
+ Layout.minimumHeight: root.narrowDetailsMinimumHeight
448
+ currentIndex: detailTabs.currentIndex
449
+
450
+ CardPreview {
451
+ objectName: "narrowLiveCardPreview"
452
+ recommendation: root.selectedRecommendation
453
+ detailedIntel: true
454
+ loading: root.sessionState.card_data.phase === "loading"
455
+ imageState: root.sessionState.card_image
456
+ constrainImageFrameToHeight: true
457
+ }
458
+
459
+ PoolSummaryPanel {
460
+ objectName: "narrowLivePoolDetails"
461
+ pool: root.sessionState.pool
462
+ narrow: root.narrow
463
+ }
464
+ }
465
+ }
466
+ }
467
+ }
468
+