@firstpick/pi-package-webui 0.2.7 → 0.2.8
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.
- package/WEBUI_TUI_NATIVE_PARITY.json +3 -3
- package/bin/pi-webui.mjs +195 -0
- package/package.json +1 -1
- package/public/app.js +616 -77
- package/public/index.html +43 -2
- package/public/styles.css +223 -7
- package/tests/mobile-static.test.mjs +34 -4
package/public/index.html
CHANGED
|
@@ -108,11 +108,12 @@
|
|
|
108
108
|
id="gitWorkflowButton"
|
|
109
109
|
class="composer-icon-button composer-git-button"
|
|
110
110
|
type="button"
|
|
111
|
+
hidden
|
|
111
112
|
title="Guided Git workflow"
|
|
112
113
|
aria-label="Start guided Git workflow: git add dot, run git-staged-msg, preview messages, commit short or long, then git push. Cancel is available at each step."
|
|
113
114
|
data-tooltip="GitHub workflow: 1. Run git add . 2. Run /git-staged-msg 3. Preview short + long messages 4. Commit with short or long message 5. Run git push Cancel is available at each step."
|
|
114
115
|
><svg class="composer-icon composer-icon-github" viewBox="0 0 16 16" aria-hidden="true" focusable="false"><path fill="currentColor" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8Z"/></svg></button>
|
|
115
|
-
<div class="composer-publish-menu">
|
|
116
|
+
<div class="composer-publish-menu" hidden>
|
|
116
117
|
<button
|
|
117
118
|
id="publishButton"
|
|
118
119
|
class="composer-icon-button composer-publish-button"
|
|
@@ -133,7 +134,7 @@
|
|
|
133
134
|
</button>
|
|
134
135
|
</div>
|
|
135
136
|
</div>
|
|
136
|
-
<div class="composer-publish-menu composer-native-command-menu">
|
|
137
|
+
<div class="composer-publish-menu composer-native-command-menu" hidden>
|
|
137
138
|
<button
|
|
138
139
|
id="nativeCommandMenuButton"
|
|
139
140
|
class="composer-icon-button composer-publish-button composer-native-command-button"
|
|
@@ -154,6 +155,45 @@
|
|
|
154
155
|
</button>
|
|
155
156
|
</div>
|
|
156
157
|
</div>
|
|
158
|
+
<div class="composer-publish-menu composer-options-menu">
|
|
159
|
+
<button
|
|
160
|
+
id="optionsMenuButton"
|
|
161
|
+
class="composer-icon-button composer-publish-button composer-options-button"
|
|
162
|
+
type="button"
|
|
163
|
+
title="Open common Pi options"
|
|
164
|
+
aria-label="Open common Pi options"
|
|
165
|
+
aria-haspopup="menu"
|
|
166
|
+
aria-expanded="false"
|
|
167
|
+
aria-controls="optionsMenu"
|
|
168
|
+
data-tooltip="Options: resume, reload, name, clone, settings, export, fork, or tree."
|
|
169
|
+
><svg class="composer-icon" viewBox="0 0 24 24" aria-hidden="true" focusable="false"><path d="M4 7h16M4 12h16M4 17h16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/><circle cx="8" cy="7" r="1.6" fill="currentColor"/><circle cx="16" cy="12" r="1.6" fill="currentColor"/><circle cx="11" cy="17" r="1.6" fill="currentColor"/></svg></button>
|
|
170
|
+
<div id="optionsMenu" class="composer-publish-menu-panel composer-options-menu-panel" role="menu" aria-label="Common Pi options">
|
|
171
|
+
<button id="optionsTreeButton" class="composer-publish-menu-item composer-options-menu-item" type="button" role="menuitem" data-command="/tree">
|
|
172
|
+
<span>Tree</span>
|
|
173
|
+
</button>
|
|
174
|
+
<button id="optionsForkButton" class="composer-publish-menu-item composer-options-menu-item" type="button" role="menuitem" data-command="/fork">
|
|
175
|
+
<span>Fork</span>
|
|
176
|
+
</button>
|
|
177
|
+
<button id="optionsExportButton" class="composer-publish-menu-item composer-options-menu-item" type="button" role="menuitem" data-command="/export">
|
|
178
|
+
<span>Export</span>
|
|
179
|
+
</button>
|
|
180
|
+
<button id="optionsSettingsButton" class="composer-publish-menu-item composer-options-menu-item" type="button" role="menuitem" data-command="/settings">
|
|
181
|
+
<span>Settings</span>
|
|
182
|
+
</button>
|
|
183
|
+
<button id="optionsCloneButton" class="composer-publish-menu-item composer-options-menu-item" type="button" role="menuitem" data-command="/clone">
|
|
184
|
+
<span>Clone Session</span>
|
|
185
|
+
</button>
|
|
186
|
+
<button id="optionsNameButton" class="composer-publish-menu-item composer-options-menu-item" type="button" role="menuitem" data-command="/name">
|
|
187
|
+
<span>Name Session</span>
|
|
188
|
+
</button>
|
|
189
|
+
<button id="optionsReloadButton" class="composer-publish-menu-item composer-options-menu-item" type="button" role="menuitem" data-command="/reload">
|
|
190
|
+
<span>Reload Pi</span>
|
|
191
|
+
</button>
|
|
192
|
+
<button id="optionsResumeButton" class="composer-publish-menu-item composer-options-menu-item" type="button" role="menuitem" data-command="/resume">
|
|
193
|
+
<span>Resume Session</span>
|
|
194
|
+
</button>
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
157
197
|
</div>
|
|
158
198
|
<div class="spacer"></div>
|
|
159
199
|
<button
|
|
@@ -329,6 +369,7 @@
|
|
|
329
369
|
</button>
|
|
330
370
|
</h2>
|
|
331
371
|
<div id="sidePanelSectionCommands" class="side-panel-section-content" hidden>
|
|
372
|
+
<input id="commandSearchInput" class="commands-search" type="search" placeholder="Search commands…" autocomplete="off" spellcheck="false" aria-label="Search commands" />
|
|
332
373
|
<div id="commandsBox" class="commands-box muted">Loading…</div>
|
|
333
374
|
</div>
|
|
334
375
|
</section>
|
package/public/styles.css
CHANGED
|
@@ -636,6 +636,7 @@ body.side-panel-collapsed .terminal-tabs-shell {
|
|
|
636
636
|
.side-panel-section {
|
|
637
637
|
display: grid;
|
|
638
638
|
gap: 0.55rem;
|
|
639
|
+
min-width: 0;
|
|
639
640
|
}
|
|
640
641
|
.side-panel-section + .side-panel-section { margin-top: 0.78rem; }
|
|
641
642
|
.side-panel-section h2 {
|
|
@@ -684,6 +685,9 @@ body.side-panel-collapsed .terminal-tabs-shell {
|
|
|
684
685
|
.side-panel-section:not(.collapsed) .side-panel-section-chevron {
|
|
685
686
|
transform: rotate(90deg);
|
|
686
687
|
}
|
|
688
|
+
.side-panel-section-content {
|
|
689
|
+
min-width: 0;
|
|
690
|
+
}
|
|
687
691
|
.side-panel-section.collapsed .side-panel-section-content,
|
|
688
692
|
.side-panel-section-content[hidden] {
|
|
689
693
|
display: none;
|
|
@@ -1481,11 +1485,20 @@ body.side-panel-collapsed .terminal-tabs-shell {
|
|
|
1481
1485
|
gap: 0.5rem;
|
|
1482
1486
|
}
|
|
1483
1487
|
.footer-line-meta {
|
|
1484
|
-
display:
|
|
1485
|
-
|
|
1488
|
+
display: flex;
|
|
1489
|
+
flex-wrap: nowrap;
|
|
1486
1490
|
gap: 0.5rem;
|
|
1487
1491
|
color: rgba(var(--ctp-subtext-rgb), 0.76);
|
|
1488
1492
|
}
|
|
1493
|
+
.footer-line-meta .footer-meta {
|
|
1494
|
+
flex: 0 1 max-content;
|
|
1495
|
+
}
|
|
1496
|
+
.footer-line-meta .footer-details-toggle {
|
|
1497
|
+
flex: 0 0 auto;
|
|
1498
|
+
}
|
|
1499
|
+
.footer-workspace {
|
|
1500
|
+
flex: 1 1 8rem;
|
|
1501
|
+
}
|
|
1489
1502
|
.footer-line-tui {
|
|
1490
1503
|
align-items: center;
|
|
1491
1504
|
gap: 0.5rem;
|
|
@@ -1597,6 +1610,9 @@ button.footer-meta {
|
|
|
1597
1610
|
cursor: pointer;
|
|
1598
1611
|
}
|
|
1599
1612
|
.footer-meta-action {
|
|
1613
|
+
position: relative;
|
|
1614
|
+
border-color: rgba(148, 226, 213, 0.26);
|
|
1615
|
+
box-shadow: inset 0 1px 0 rgba(255,255,255,0.045), inset 0 0 0 1px rgba(148, 226, 213, 0.055), 0 0.45rem 1rem rgba(0, 0, 0, 0.10);
|
|
1600
1616
|
transition: border-color 140ms ease, box-shadow 140ms ease, transform 140ms ease;
|
|
1601
1617
|
}
|
|
1602
1618
|
.footer-meta-action:hover,
|
|
@@ -1648,12 +1664,20 @@ button.footer-meta {
|
|
|
1648
1664
|
color: var(--ctp-teal);
|
|
1649
1665
|
text-shadow: 0 0 0.6rem rgba(148, 226, 213, 0.22);
|
|
1650
1666
|
}
|
|
1667
|
+
.footer-thinking .footer-meta-value {
|
|
1668
|
+
color: var(--ctp-mauve);
|
|
1669
|
+
text-shadow: 0 0 0.6rem rgba(203, 166, 247, 0.22);
|
|
1670
|
+
}
|
|
1651
1671
|
.footer-model.footer-meta-action {
|
|
1652
1672
|
border-color: rgba(148, 226, 213, 0.24);
|
|
1653
1673
|
}
|
|
1674
|
+
.footer-thinking.footer-meta-action {
|
|
1675
|
+
border-color: rgba(203, 166, 247, 0.24);
|
|
1676
|
+
}
|
|
1654
1677
|
.footer-model-picker {
|
|
1655
1678
|
position: absolute;
|
|
1656
|
-
|
|
1679
|
+
left: var(--footer-model-picker-left, auto);
|
|
1680
|
+
right: var(--footer-model-picker-right, 0.95rem);
|
|
1657
1681
|
bottom: calc(100% + 0.5rem);
|
|
1658
1682
|
z-index: 40;
|
|
1659
1683
|
display: grid;
|
|
@@ -1728,6 +1752,42 @@ button.footer-meta {
|
|
|
1728
1752
|
color: var(--ctp-pink);
|
|
1729
1753
|
text-shadow: 0 0 0.55rem rgba(245, 194, 231, 0.18);
|
|
1730
1754
|
}
|
|
1755
|
+
.footer-changes,
|
|
1756
|
+
.footer-git-extra {
|
|
1757
|
+
flex: 0 0 auto;
|
|
1758
|
+
}
|
|
1759
|
+
.footer-changes {
|
|
1760
|
+
border-color: rgba(249, 226, 175, 0.36);
|
|
1761
|
+
background:
|
|
1762
|
+
linear-gradient(120deg, rgba(249, 226, 175, 0.14), rgba(250, 179, 135, 0.08)),
|
|
1763
|
+
linear-gradient(180deg, rgba(var(--ctp-surface-rgb), 0.54), rgba(var(--ctp-crust-rgb), 0.36));
|
|
1764
|
+
}
|
|
1765
|
+
.footer-changes .footer-meta-label {
|
|
1766
|
+
color: rgba(249, 226, 175, 0.88);
|
|
1767
|
+
text-shadow: 0 0 0.52rem rgba(249, 226, 175, 0.20);
|
|
1768
|
+
}
|
|
1769
|
+
.footer-changes .footer-meta-value {
|
|
1770
|
+
color: var(--ctp-yellow);
|
|
1771
|
+
font-weight: 950;
|
|
1772
|
+
letter-spacing: 0.01em;
|
|
1773
|
+
text-shadow: 0 0 0.72rem rgba(249, 226, 175, 0.28);
|
|
1774
|
+
}
|
|
1775
|
+
.footer-git-extra {
|
|
1776
|
+
border-color: rgba(137, 180, 250, 0.34);
|
|
1777
|
+
background:
|
|
1778
|
+
linear-gradient(120deg, rgba(137, 180, 250, 0.13), rgba(148, 226, 213, 0.08)),
|
|
1779
|
+
linear-gradient(180deg, rgba(var(--ctp-surface-rgb), 0.50), rgba(var(--ctp-crust-rgb), 0.36));
|
|
1780
|
+
}
|
|
1781
|
+
.footer-git-extra .footer-meta-label {
|
|
1782
|
+
color: rgba(137, 180, 250, 0.88);
|
|
1783
|
+
text-shadow: 0 0 0.52rem rgba(137, 180, 250, 0.20);
|
|
1784
|
+
}
|
|
1785
|
+
.footer-git-extra .footer-meta-value {
|
|
1786
|
+
color: var(--ctp-sky);
|
|
1787
|
+
font-weight: 900;
|
|
1788
|
+
letter-spacing: 0.01em;
|
|
1789
|
+
text-shadow: 0 0 0.68rem rgba(137, 180, 250, 0.26);
|
|
1790
|
+
}
|
|
1731
1791
|
.footer-runtime .footer-meta-value {
|
|
1732
1792
|
color: var(--ctp-yellow);
|
|
1733
1793
|
}
|
|
@@ -3141,6 +3201,9 @@ summary { cursor: pointer; color: var(--warning); }
|
|
|
3141
3201
|
overflow: visible;
|
|
3142
3202
|
isolation: isolate;
|
|
3143
3203
|
}
|
|
3204
|
+
.composer-publish-menu[hidden] {
|
|
3205
|
+
display: none !important;
|
|
3206
|
+
}
|
|
3144
3207
|
.composer-publish-menu:hover,
|
|
3145
3208
|
.composer-publish-menu:focus-within,
|
|
3146
3209
|
.composer-publish-menu.open {
|
|
@@ -3207,6 +3270,22 @@ summary { cursor: pointer; color: var(--warning); }
|
|
|
3207
3270
|
.composer-native-command-menu.open .composer-native-command-button {
|
|
3208
3271
|
border-color: rgba(203, 166, 247, 0.62);
|
|
3209
3272
|
}
|
|
3273
|
+
.composer-options-button {
|
|
3274
|
+
color: var(--ctp-sky);
|
|
3275
|
+
border-color: rgba(137, 220, 235, 0.40);
|
|
3276
|
+
background:
|
|
3277
|
+
linear-gradient(120deg, rgba(137, 220, 235, 0.15), rgba(166, 227, 161, 0.10)),
|
|
3278
|
+
linear-gradient(180deg, rgba(var(--ctp-surface-rgb), 0.88), rgba(var(--ctp-crust-rgb), 0.88));
|
|
3279
|
+
}
|
|
3280
|
+
.composer-options-button:hover,
|
|
3281
|
+
.composer-options-button.menu-open {
|
|
3282
|
+
color: #11111b;
|
|
3283
|
+
background: linear-gradient(120deg, var(--ctp-sky), var(--ctp-teal), var(--ctp-green));
|
|
3284
|
+
border-color: transparent;
|
|
3285
|
+
}
|
|
3286
|
+
.composer-options-menu.open .composer-options-button {
|
|
3287
|
+
border-color: rgba(137, 220, 235, 0.62);
|
|
3288
|
+
}
|
|
3210
3289
|
.composer-publish-menu-panel {
|
|
3211
3290
|
position: absolute;
|
|
3212
3291
|
z-index: 100;
|
|
@@ -3228,6 +3307,9 @@ summary { cursor: pointer; color: var(--warning); }
|
|
|
3228
3307
|
.composer-publish-menu.open .composer-publish-menu-panel {
|
|
3229
3308
|
display: flex;
|
|
3230
3309
|
}
|
|
3310
|
+
.composer-options-menu-panel {
|
|
3311
|
+
max-height: min(88vh, 36rem);
|
|
3312
|
+
}
|
|
3231
3313
|
.composer-publish-menu.open .composer-publish-button {
|
|
3232
3314
|
border-color: rgba(250, 179, 135, 0.58);
|
|
3233
3315
|
}
|
|
@@ -3270,6 +3352,20 @@ summary { cursor: pointer; color: var(--warning); }
|
|
|
3270
3352
|
background: linear-gradient(120deg, var(--ctp-mauve), var(--ctp-blue));
|
|
3271
3353
|
box-shadow: 0 0 1rem rgba(203, 166, 247, 0.20);
|
|
3272
3354
|
}
|
|
3355
|
+
.composer-options-menu-item {
|
|
3356
|
+
color: var(--ctp-sky);
|
|
3357
|
+
border-color: rgba(137, 220, 235, 0.32);
|
|
3358
|
+
background:
|
|
3359
|
+
linear-gradient(120deg, rgba(137, 220, 235, 0.12), rgba(166, 227, 161, 0.08)),
|
|
3360
|
+
var(--ctp-crust);
|
|
3361
|
+
}
|
|
3362
|
+
.composer-options-menu-item:hover,
|
|
3363
|
+
.composer-options-menu-item:focus-visible {
|
|
3364
|
+
color: #11111b;
|
|
3365
|
+
border-color: transparent;
|
|
3366
|
+
background: linear-gradient(120deg, var(--ctp-sky), var(--ctp-teal));
|
|
3367
|
+
box-shadow: 0 0 1rem rgba(137, 220, 235, 0.20);
|
|
3368
|
+
}
|
|
3273
3369
|
.composer button[data-tooltip] {
|
|
3274
3370
|
position: relative;
|
|
3275
3371
|
}
|
|
@@ -3368,18 +3464,40 @@ summary { cursor: pointer; color: var(--warning); }
|
|
|
3368
3464
|
padding: 0.72rem;
|
|
3369
3465
|
box-shadow: inset 0 1px 0 rgba(255,255,255,0.035);
|
|
3370
3466
|
}
|
|
3467
|
+
.commands-search {
|
|
3468
|
+
width: 100%;
|
|
3469
|
+
min-width: 0;
|
|
3470
|
+
margin-bottom: 0.55rem;
|
|
3471
|
+
padding: 0.5rem 0.62rem;
|
|
3472
|
+
color: var(--ctp-text);
|
|
3473
|
+
border-color: rgba(148, 226, 213, 0.24);
|
|
3474
|
+
background:
|
|
3475
|
+
linear-gradient(120deg, rgba(148, 226, 213, 0.10), rgba(203, 166, 247, 0.08)),
|
|
3476
|
+
rgba(var(--ctp-crust-rgb), 0.58);
|
|
3477
|
+
font-size: 0.86rem;
|
|
3478
|
+
}
|
|
3479
|
+
.commands-search::placeholder {
|
|
3480
|
+
color: rgba(var(--ctp-subtext-rgb), 0.62);
|
|
3481
|
+
}
|
|
3371
3482
|
.commands-box {
|
|
3483
|
+
max-width: 100%;
|
|
3372
3484
|
max-height: min(32rem, 52vh);
|
|
3373
|
-
overflow: auto;
|
|
3485
|
+
overflow-y: auto;
|
|
3486
|
+
overflow-x: hidden;
|
|
3374
3487
|
font-size: 0.86rem;
|
|
3375
3488
|
}
|
|
3376
3489
|
.command-item {
|
|
3377
3490
|
display: block;
|
|
3378
3491
|
width: 100%;
|
|
3492
|
+
max-width: 100%;
|
|
3493
|
+
min-width: 0;
|
|
3379
3494
|
min-height: auto;
|
|
3380
3495
|
margin: 0 0 0.5rem;
|
|
3381
3496
|
padding: 0.48rem 0.56rem;
|
|
3382
3497
|
text-align: left;
|
|
3498
|
+
white-space: normal;
|
|
3499
|
+
overflow-wrap: anywhere;
|
|
3500
|
+
line-height: 1.35;
|
|
3383
3501
|
background: rgba(var(--ctp-surface-rgb), 0.18);
|
|
3384
3502
|
border-color: rgba(180, 190, 254, 0.12);
|
|
3385
3503
|
box-shadow: none;
|
|
@@ -3393,6 +3511,8 @@ summary { cursor: pointer; color: var(--warning); }
|
|
|
3393
3511
|
}
|
|
3394
3512
|
.command-item code {
|
|
3395
3513
|
color: var(--ctp-green);
|
|
3514
|
+
overflow-wrap: anywhere;
|
|
3515
|
+
word-break: break-word;
|
|
3396
3516
|
text-shadow: 0 0 0.7rem rgba(166, 227, 161, 0.25);
|
|
3397
3517
|
}
|
|
3398
3518
|
.event-log {
|
|
@@ -3648,12 +3768,96 @@ summary { cursor: pointer; color: var(--warning); }
|
|
|
3648
3768
|
.native-selector-meta {
|
|
3649
3769
|
font-size: 0.76rem;
|
|
3650
3770
|
}
|
|
3771
|
+
.native-settings-panel {
|
|
3772
|
+
display: grid;
|
|
3773
|
+
gap: 0.82rem;
|
|
3774
|
+
}
|
|
3775
|
+
.native-settings-note {
|
|
3776
|
+
display: grid;
|
|
3777
|
+
gap: 0.2rem;
|
|
3778
|
+
padding: 0.72rem 0.82rem;
|
|
3779
|
+
border: 1px solid rgba(148, 226, 213, 0.18);
|
|
3780
|
+
border-radius: 0.9rem;
|
|
3781
|
+
background: linear-gradient(120deg, rgba(148, 226, 213, 0.10), rgba(137, 180, 250, 0.08));
|
|
3782
|
+
color: rgba(var(--ctp-text-rgb), 0.86);
|
|
3783
|
+
}
|
|
3784
|
+
.native-settings-note span {
|
|
3785
|
+
color: rgba(var(--ctp-subtext-rgb), 0.78);
|
|
3786
|
+
font-size: 0.84rem;
|
|
3787
|
+
}
|
|
3788
|
+
.native-settings-section {
|
|
3789
|
+
border: 1px solid rgba(180, 190, 254, 0.15);
|
|
3790
|
+
border-radius: 1rem;
|
|
3791
|
+
background: rgba(var(--ctp-mantle-rgb), 0.42);
|
|
3792
|
+
overflow: clip;
|
|
3793
|
+
}
|
|
3794
|
+
.native-settings-section-summary {
|
|
3795
|
+
display: grid;
|
|
3796
|
+
gap: 0.18rem;
|
|
3797
|
+
padding: 0.78rem 0.9rem;
|
|
3798
|
+
cursor: pointer;
|
|
3799
|
+
list-style-position: inside;
|
|
3800
|
+
}
|
|
3801
|
+
.native-settings-section-summary:hover {
|
|
3802
|
+
background: rgba(var(--ctp-surface-rgb), 0.24);
|
|
3803
|
+
}
|
|
3804
|
+
.native-settings-section-title,
|
|
3805
|
+
.native-settings-label-row {
|
|
3806
|
+
display: flex;
|
|
3807
|
+
flex-wrap: wrap;
|
|
3808
|
+
align-items: center;
|
|
3809
|
+
gap: 0.38rem;
|
|
3810
|
+
}
|
|
3811
|
+
.native-settings-section-title strong {
|
|
3812
|
+
color: var(--text);
|
|
3813
|
+
}
|
|
3814
|
+
.native-settings-section-description {
|
|
3815
|
+
color: rgba(var(--ctp-subtext-rgb), 0.76);
|
|
3816
|
+
font-size: 0.82rem;
|
|
3817
|
+
}
|
|
3818
|
+
.native-settings-section .native-settings-grid {
|
|
3819
|
+
padding: 0 0.82rem 0.82rem;
|
|
3820
|
+
}
|
|
3651
3821
|
.native-settings-grid,
|
|
3652
3822
|
.native-tree-options {
|
|
3653
3823
|
display: grid;
|
|
3654
3824
|
grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
|
|
3655
3825
|
gap: 0.72rem;
|
|
3656
3826
|
}
|
|
3827
|
+
.native-settings-badge {
|
|
3828
|
+
display: inline-flex;
|
|
3829
|
+
align-items: center;
|
|
3830
|
+
padding: 0.08rem 0.34rem;
|
|
3831
|
+
border: 1px solid rgba(180, 190, 254, 0.24);
|
|
3832
|
+
border-radius: 999px;
|
|
3833
|
+
color: rgba(var(--ctp-subtext-rgb), 0.86);
|
|
3834
|
+
font-size: 0.62rem;
|
|
3835
|
+
font-weight: 900;
|
|
3836
|
+
letter-spacing: 0.07em;
|
|
3837
|
+
text-transform: uppercase;
|
|
3838
|
+
}
|
|
3839
|
+
.native-settings-badge-now,
|
|
3840
|
+
.native-settings-badge-browser {
|
|
3841
|
+
border-color: rgba(166, 227, 161, 0.34);
|
|
3842
|
+
color: var(--ctp-green);
|
|
3843
|
+
background: rgba(166, 227, 161, 0.08);
|
|
3844
|
+
}
|
|
3845
|
+
.native-settings-badge-reload {
|
|
3846
|
+
border-color: rgba(249, 226, 175, 0.38);
|
|
3847
|
+
color: var(--ctp-yellow);
|
|
3848
|
+
background: rgba(249, 226, 175, 0.08);
|
|
3849
|
+
}
|
|
3850
|
+
.native-settings-badge-tui,
|
|
3851
|
+
.native-settings-badge-startup {
|
|
3852
|
+
border-color: rgba(137, 180, 250, 0.34);
|
|
3853
|
+
color: var(--ctp-blue);
|
|
3854
|
+
background: rgba(137, 180, 250, 0.08);
|
|
3855
|
+
}
|
|
3856
|
+
.native-settings-badge-safety {
|
|
3857
|
+
border-color: rgba(243, 139, 168, 0.42);
|
|
3858
|
+
color: var(--ctp-red);
|
|
3859
|
+
background: rgba(243, 139, 168, 0.08);
|
|
3860
|
+
}
|
|
3657
3861
|
.native-settings-field,
|
|
3658
3862
|
.native-settings-toggle {
|
|
3659
3863
|
display: grid;
|
|
@@ -3674,6 +3878,13 @@ summary { cursor: pointer; color: var(--warning); }
|
|
|
3674
3878
|
letter-spacing: 0.08em;
|
|
3675
3879
|
text-transform: uppercase;
|
|
3676
3880
|
}
|
|
3881
|
+
.native-settings-field select,
|
|
3882
|
+
.native-settings-field input.dialog-input {
|
|
3883
|
+
width: 100%;
|
|
3884
|
+
}
|
|
3885
|
+
.native-settings-toggle input[type="checkbox"] {
|
|
3886
|
+
margin-top: 0.14rem;
|
|
3887
|
+
}
|
|
3677
3888
|
.native-settings-hint {
|
|
3678
3889
|
display: block;
|
|
3679
3890
|
margin-top: 0.18rem;
|
|
@@ -3857,9 +4068,13 @@ summary { cursor: pointer; color: var(--warning); }
|
|
|
3857
4068
|
body.side-panel-collapsed .layout { grid-template-columns: 1fr; height: auto; min-height: 100dvh; overflow: visible; }
|
|
3858
4069
|
.chat-panel { min-height: 70dvh; }
|
|
3859
4070
|
.side-panel { max-height: none; }
|
|
3860
|
-
.footer-line-meta {
|
|
3861
|
-
|
|
3862
|
-
|
|
4071
|
+
.footer-line-meta {
|
|
4072
|
+
display: grid;
|
|
4073
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
4074
|
+
}
|
|
4075
|
+
.footer-workspace { grid-column: 1 / -1; }
|
|
4076
|
+
.footer-model { grid-column: 1; }
|
|
4077
|
+
.footer-thinking { grid-column: 2; }
|
|
3863
4078
|
}
|
|
3864
4079
|
|
|
3865
4080
|
@media (max-width: 720px), (max-device-width: 720px), (pointer: coarse) and (hover: none) {
|
|
@@ -4190,6 +4405,7 @@ summary { cursor: pointer; color: var(--warning); }
|
|
|
4190
4405
|
.footer-changes { order: 5; }
|
|
4191
4406
|
.footer-runtime { order: 6; }
|
|
4192
4407
|
.footer-model { order: 7; }
|
|
4408
|
+
.footer-thinking { order: 8; }
|
|
4193
4409
|
body.footer-details-expanded .footer-line-meta {
|
|
4194
4410
|
grid-template-columns: 1fr;
|
|
4195
4411
|
}
|
|
@@ -190,6 +190,7 @@ assert.match(css, /\.composer-publish-menu:hover > \.composer-publish-button\[da
|
|
|
190
190
|
assert.match(css, /\.composer-publish-menu-panel \{[\s\S]*?display:\s*none;[\s\S]*?flex-direction:\s*column/, "Publish workflow menu should hide when closed and expand like grouped tabs");
|
|
191
191
|
assert.match(css, /\.composer-publish-menu:hover \.composer-publish-menu-panel,[\s\S]*?\.composer-publish-menu:focus-within \.composer-publish-menu-panel,[\s\S]*?\.composer-publish-menu\.open \.composer-publish-menu-panel \{\n\s+display:\s*flex;/, "Publish workflow menu should open on hover, focus, or explicit open state");
|
|
192
192
|
assert.match(css, /\.composer-native-command-button \{[\s\S]*?color:\s*var\(--ctp-mauve\)/, "skills/tools command menu should have a distinct slash-command button style");
|
|
193
|
+
assert.match(css, /\.composer-options-menu-panel \{[\s\S]*?max-height:\s*min\(88vh, 36rem\)/, "Options menu should be tall enough for common commands without scrolling on normal viewports");
|
|
193
194
|
assert.match(css, /\.composer-native-command-menu-item \{[\s\S]*?color:\s*var\(--ctp-mauve\)/, "skills/tools command menu items should be styled separately from publish actions");
|
|
194
195
|
assert.match(css, /\.composer-actions-panel > \.composer-publish-menu[\s\S]*?grid-column: span 1/, "Publish and command menu buttons should fit beside Git workflow in mobile actions");
|
|
195
196
|
assert.match(css, /\.composer-actions-panel[\s\S]*?bottom:\s*calc\(100% \+ 0\.42rem\)/, "mobile composer actions should open as an above-composer sheet");
|
|
@@ -216,10 +217,21 @@ assert.match(css, /body:not\(\.pi-run-active\):not\(\.mobile-keyboard-open\) \.c
|
|
|
216
217
|
assert.match(css, /button\[hidden\] \{ display: none !important; \}/, "hidden bottom-row controls should not occupy layout space");
|
|
217
218
|
assert.match(css, /\.statusbar-tui-footer \{[\s\S]*?gap:\s*0/, "default TUI-like footer should reduce statusbar chrome around the compact line");
|
|
218
219
|
assert.match(css, /\.statusbar-git-footer \{[\s\S]*?gap:\s*0\.58rem/, "enabled git-footer extension should keep the styled Web UI footer spacing");
|
|
220
|
+
assert.match(css, /\.footer-line-meta \{[\s\S]*?display:\s*flex;[\s\S]*?flex-wrap:\s*nowrap/, "git-footer metadata should keep cwd, git chips, model, and effort on one row when space allows");
|
|
221
|
+
assert.match(css, /\.footer-line-meta \.footer-meta \{[\s\S]*?flex:\s*0 1 max-content/, "git-footer non-cwd boxes should shrink to content-sized chips while preferring complete text");
|
|
222
|
+
assert.match(css, /\.footer-workspace \{[\s\S]*?flex:\s*1 1 8rem/, "git-footer cwd chip should dynamically take remaining row space");
|
|
223
|
+
assert.match(css, /\.footer-thinking \.footer-meta-value \{[\s\S]*?color:\s*var\(--ctp-mauve\)/, "git-footer effort chip should have its own styling");
|
|
224
|
+
assert.match(css, /\.footer-changes \{[\s\S]*?border-color:\s*rgba\(249, 226, 175, 0\.36\)/, "git-footer changes chip should use a higher-contrast warning tint");
|
|
225
|
+
assert.match(css, /\.footer-changes \.footer-meta-value \{[\s\S]*?color:\s*var\(--ctp-yellow\)[\s\S]*?font-weight:\s*950/, "git-footer changes value should be bright and bold");
|
|
226
|
+
assert.match(css, /\.footer-git-extra \.footer-meta-value \{[\s\S]*?color:\s*var\(--ctp-sky\)[\s\S]*?font-weight:\s*900/, "git-footer extras value should be bright enough to read at footer size");
|
|
227
|
+
assert.match(css, /\.footer-changes,[\s\S]*?\.footer-git-extra \{[\s\S]*?flex:\s*0 0 auto/, "git-footer changes and extras chips should resist truncating short status values");
|
|
228
|
+
assert.match(css, /\.footer-meta-action \{[\s\S]*?position:\s*relative;[\s\S]*?border-color:\s*rgba\(148, 226, 213, 0\.26\)/, "clickable footer boxes should have a subtle always-visible highlight");
|
|
229
|
+
assert.doesNotMatch(css, /\.footer-meta-action::after/, "clickable footer boxes should not show a corner indicator dot");
|
|
230
|
+
assert.match(css, /@media \(max-width: 1050px\)[\s\S]*?\.footer-line-meta \{[\s\S]*?display:\s*grid;[\s\S]*?\.footer-model \{ grid-column: 1; \}[\s\S]*?\.footer-thinking \{ grid-column: 2; \}/, "narrow git-footer metadata should keep model and effort on the same row");
|
|
219
231
|
assert.match(css, /\.footer-line-tui \{[\s\S]*?white-space:\s*nowrap/, "default Web UI footer should use a minimal TUI-like line");
|
|
220
232
|
assert.match(css, /\.footer-tui-cwd[\s\S]*?max-width:\s*38%/, "TUI-like footer should keep cwd compact on desktop");
|
|
221
233
|
assert.match(css, /\.footer-tui-model[\s\S]*?text-align:\s*right/, "TUI-like footer should right-align model information on desktop");
|
|
222
|
-
assert.match(css, /\.footer-model-picker[\s\S]*?position:\s*absolute/, "footer model
|
|
234
|
+
assert.match(css, /\.footer-model-picker[\s\S]*?position:\s*absolute[\s\S]*?left:\s*var\(--footer-model-picker-left, auto\)[\s\S]*?right:\s*var\(--footer-model-picker-right, 0\.95rem\)/, "footer model and effort pickers should render as anchored dropdown popovers");
|
|
223
235
|
assert.match(css, /@media \(max-width: 720px\), \(max-device-width: 720px\), \(pointer: coarse\) and \(hover: none\)[\s\S]*?\.footer-model-picker \{[\s\S]*?position:\s*fixed/, "mobile footer model picker should escape footer-details stacking as a fixed overlay on narrow, device-width-narrow, or touch-only devices");
|
|
224
236
|
assert.match(css, /bottom:\s*var\(--footer-model-picker-bottom/, "mobile footer model picker should be anchored by a JS-computed viewport offset");
|
|
225
237
|
assert.match(css, /\.footer-model-option\.active/, "footer model picker should style the selected scoped model");
|
|
@@ -355,7 +367,8 @@ assert.match(app, /restoreSidePanelSectionState\(\);\nbindSidePanelSectionToggle
|
|
|
355
367
|
assert.match(app, /OPTIONAL_FEATURES_STORAGE_KEY/, "optional feature disable toggles should persist in browser storage");
|
|
356
368
|
assert.match(app, /GIT_FOOTER_WEBUI_STATUS_KEY = "git-footer-webui"/, "git footer Web UI data should be received as an extension-owned status payload");
|
|
357
369
|
assert.match(app, /function parseGitFooterWebuiPayloadRaw\(raw\)[\s\S]*GIT_FOOTER_WEBUI_PAYLOAD_TYPE[\s\S]*GIT_FOOTER_WEBUI_PAYLOAD_VERSION/, "Web UI footer should parse the structured payload emitted by git-footer-status");
|
|
358
|
-
assert.match(app, /function renderFooter\(\)[\s\S]*parseGitFooterWebuiPayload\(\)[\s\S]*renderGitFooterPayload\(gitFooterPayload\)/, "detailed footer rendering should prefer the git-footer-status extension payload");
|
|
370
|
+
assert.match(app, /function renderFooter\(\)[\s\S]*parseGitFooterWebuiPayload\(\)[\s\S]*renderGitFooterPayload\(footerPayloadWithLiveModel\(gitFooterPayload\)\)/, "detailed footer rendering should prefer the git-footer-status extension payload");
|
|
371
|
+
assert.match(app, /function footerPayloadWithLiveModel\(payload\)[\s\S]*?shortModelLabel\(currentState\.model\)[\s\S]*?footerThinkingDisplay\(\)[\s\S]*?key: "thinking", label: "effort"/, "git footer payload rendering should split model and effort chips from live Web UI state");
|
|
359
372
|
assert.match(app, /function renderGitFooterPayload\(payload\)[\s\S]*classList\.remove\("statusbar-tui-footer"\)[\s\S]*classList\.add\("statusbar-git-footer"\)[\s\S]*payload\.main\.map\(renderGitFooterPayloadMetric\)[\s\S]*payload\.meta\.map/, "enabled git footer payload should use the styled extension chip renderer, not the default TUI line");
|
|
360
373
|
assert.match(app, /function renderGitFooterPayloadMetric\(chip\)[\s\S]*footerMetric\(chip\.icon/, "git footer main payload chips should render as styled metrics");
|
|
361
374
|
assert.match(app, /function renderGitFooterPayloadMeta\(chip, tab\)[\s\S]*footerMeta\(chip\.label, chip\.value, footerMetaClassForPayload\(chip\)/, "git footer meta payload chips should render as styled metadata");
|
|
@@ -369,6 +382,8 @@ assert.doesNotMatch(app, /footerMeta\("runtime"/, "minimal Web UI footer should
|
|
|
369
382
|
assert.match(app, /statusEntries\.has\(GIT_FOOTER_WEBUI_STATUS_KEY\)/, "optional feature detection should recognize the git-footer-status Web UI payload");
|
|
370
383
|
assert.match(app, /\/git-footer-refresh --webui-silent/, "Web UI should quietly request the extension-owned footer payload when idle and missing");
|
|
371
384
|
assert.match(app, /function requestGitFooterWebuiPayload\(tabContext = activeTabContext\(\), \{ force = false \} = \{\}\)[\s\S]*?!force && statusEntries\.has\(GIT_FOOTER_WEBUI_STATUS_KEY\)/, "git footer payload refresh should support forced refresh even when a live payload already exists");
|
|
385
|
+
assert.doesNotMatch(app, /function requestGitFooterWebuiPayload\([\s\S]*?statusEntries\.delete\(GIT_FOOTER_WEBUI_STATUS_KEY\)/, "forced git footer refreshes should keep the existing payload visible while the refresh runs");
|
|
386
|
+
assert.match(app, /function applyOptimisticModelSelection\(model, tabContext = activeTabContext\(\)\)[\s\S]*?currentState = \{ \.\.\.currentState, model: nextModel \}[\s\S]*?renderStatus\(\)[\s\S]*?requestGitFooterWebuiPayload\(tabContext, \{ force: true \}\)/, "model changes should update current state and footer immediately before async refreshes complete");
|
|
372
387
|
assert.match(app, /function gitFooterRelevantStateChanged\(previousState, nextState\)[\s\S]*?previousState\.thinkingLevel !== nextState\.thinkingLevel[\s\S]*?modelStateKey\(previousState\.model\) !== modelStateKey\(nextState\.model\)/, "state refresh should detect model and thinking changes that make the git footer payload stale");
|
|
373
388
|
assert.match(app, /requestGitFooterWebuiPayload\(tabContext, \{ force: shouldRefreshGitFooter \}\)/, "state refresh should force-refresh the git footer when model or thinking state changes");
|
|
374
389
|
assert.match(app, /if \(response\.data\?\.level\) requestGitFooterWebuiPayload\(tabContext, \{ force: true \}\)/, "thinking shortcut should immediately force-refresh the git footer payload");
|
|
@@ -381,6 +396,8 @@ assert.doesNotMatch(workspaceInfoSource, /runCommand\("git"|branchStatus|isRepo/
|
|
|
381
396
|
assert.match(app, /function renderOptionalFeatureDependentDisplays\(\)[\s\S]*renderOptionalFeatureControls\(\);[\s\S]*renderThemeSelect\(\);[\s\S]*renderWidgets\(\);[\s\S]*renderStatus\(\);[\s\S]*renderCommands\(\);[\s\S]*renderAllMessages\(\{ preserveScroll: true \}\);[\s\S]*if \(streamRawText\) renderStreamingAssistantText\(\);/, "optional feature toggles should immediately refresh visible controls, commands, transcript, and live stream displays");
|
|
382
397
|
assert.match(app, /function setOptionalFeatureDisabled\(featureId, disabled\)[\s\S]*renderOptionalFeatureDependentDisplays\(\);[\s\S]*const tabContext = activeTabContext\(\);[\s\S]*refreshCommands\(tabContext\)/, "optional feature enable/disable should re-render the GUI and then refresh command capabilities");
|
|
383
398
|
assert.match(app, /function setOptionalControlState\(button, available, unavailableTitle\)[\s\S]*setAttribute\("aria-label", nextAriaLabel\)[\s\S]*setAttribute\("data-tooltip", nextTooltip\)/, "optional feature button disabled state should update accessible labels and visible tooltips");
|
|
399
|
+
assert.match(app, /const hasGitWorkflow = isOptionalFeatureEnabled\("gitWorkflow"\);\n\s+elements\.gitWorkflowButton\.hidden = !hasGitWorkflow/, "guided git workflow composer button should be hidden when unavailable or disabled");
|
|
400
|
+
assert.match(app, /elements\.publishButton\.hidden = !hasPublishWorkflow[\s\S]*elements\.nativeCommandMenuButton\.hidden = !hasNativeCommandMenu/, "optional publish and skills/tools menu buttons should be hidden when no enabled menu items are available");
|
|
384
401
|
assert.match(app, /\["skills", "tuiSkillsCommand"\][\s\S]*\["tools", "tuiToolsCommand"\]/, "optional feature toggles should gate /skills and /tools command surfaces");
|
|
385
402
|
assert.match(app, /function setNativeCommandMenuOpen\(open\)/, "frontend should track the skills/tools command menu open state separately from Publish");
|
|
386
403
|
assert.match(app, /nativeSkillsButton\.hidden = !isOptionalFeatureEnabled\("tuiSkillsCommand"\)[\s\S]*nativeToolsButton\.hidden = !isOptionalFeatureEnabled\("tuiToolsCommand"\)/, "skills/tools menu items should be hidden by their optional feature toggles");
|
|
@@ -532,6 +549,8 @@ assert.match(app, /sendPromptFromModeButton\("steer", elements\.steerButton\)/,
|
|
|
532
549
|
assert.match(app, /sendPromptFromModeButton\("follow-up", elements\.followUpButton\)/, "Follow-up should show tooltip instead of silently doing nothing when input is empty");
|
|
533
550
|
assert.match(app, /function runPublishWorkflow\(command\)[\s\S]*?sendPrompt\("prompt", command\)/, "Publish workflows should send slash commands directly without replacing the draft");
|
|
534
551
|
assert.match(app, /async function runNativeCommandMenu\(command\)[\s\S]*?await handleNativeSlashSelectorCommand\(command\)/, "skills/tools command menu should open native selector dialogs directly");
|
|
552
|
+
assert.match(app, /async function runNativeCommandMenu\(command\)[\s\S]*?sendPrompt\("prompt", command\)/, "generic native command menu should fall back to slash-command prompt execution");
|
|
553
|
+
assert.match(app, /function setOptionsMenuOpen\(open\)/, "Options menu should have explicit open state");
|
|
535
554
|
assert.match(app, /function nativeToolOriginTag\(resource\)[\s\S]*?sourceInfo\?\.source === "builtin"[\s\S]*?label: "Pi Native"[\s\S]*?label: "External"/, "Tools Setup should classify built-in Pi tools separately from external tools");
|
|
536
555
|
assert.match(app, /renderNativeResourceToggles\(tools, \{[\s\S]*?getResourceTag: nativeToolOriginTag/, "Tools Setup should render Pi Native\/External tags");
|
|
537
556
|
assert.match(app, /const tags = Array\.isArray\(item\.tags\)[\s\S]*?item\.badge, \.\.\.tags/, "native selector filtering should include extra resource tags");
|
|
@@ -539,19 +558,26 @@ assert.match(app, /publishMenuContainer\?\.addEventListener\("pointerenter", \(\
|
|
|
539
558
|
assert.match(app, /publishMenuContainer\?\.addEventListener\("pointerleave", \(\) => setPublishMenuOpen\(false\)\)/, "Publish menu should collapse after hover leaves");
|
|
540
559
|
assert.match(app, /nativeCommandMenuContainer\?\.addEventListener\("pointerenter", \(\) => \{[\s\S]*?setNativeCommandMenuOpen\(true\);[\s\S]*?\}\)/, "skills/tools command menu should expand on hover");
|
|
541
560
|
assert.match(app, /nativeCommandMenuContainer\?\.addEventListener\("pointerleave", \(\) => setNativeCommandMenuOpen\(false\)\)/, "skills/tools command menu should collapse after hover leaves");
|
|
561
|
+
assert.match(app, /optionsMenuContainer\?\.addEventListener\("pointerenter", \(\) => \{[\s\S]*?setOptionsMenuOpen\(true\);[\s\S]*?\}\)/, "Options menu should expand on hover");
|
|
562
|
+
assert.match(app, /optionsMenuContainer\?\.addEventListener\("pointerleave", \(\) => setOptionsMenuOpen\(false\)\)/, "Options menu should collapse after hover leaves");
|
|
542
563
|
assert.match(app, /releaseNpmButton\.addEventListener\("click", \(\) => runPublishWorkflow\("\/release-npm"\)\)/, "Publish menu should launch /release-npm");
|
|
543
564
|
assert.match(app, /releaseAurButton\.addEventListener\("click", \(\) => runPublishWorkflow\("\/release-aur"\)\)/, "Publish menu should launch /release-aur");
|
|
544
565
|
assert.match(app, /nativeSkillsButton\.addEventListener\("click", \(\) => runNativeCommandMenu\("\/skills"\)\)/, "skills/tools command menu should launch /skills");
|
|
545
566
|
assert.match(app, /nativeToolsButton\.addEventListener\("click", \(\) => runNativeCommandMenu\("\/tools"\)\)/, "skills/tools command menu should launch /tools");
|
|
567
|
+
for (const command of ["resume", "reload", "name", "clone", "settings", "export", "fork", "tree"]) {
|
|
568
|
+
const id = command.replace(/^./, (letter) => letter.toUpperCase());
|
|
569
|
+
assert.match(app, new RegExp(`options${id}Button\\.addEventListener\\("click", \\(\\) => runNativeCommandMenu\\("\\/${command}"\\)\\)`), `Options menu should launch /${command}`);
|
|
570
|
+
}
|
|
546
571
|
assert.match(app, /async function sendPrompt\(kind = "prompt", explicitMessage\)/, "prompt sending should accept direct messages that bypass the input field");
|
|
547
572
|
assert.match(app, /const rawMessage = usesPromptInput \? elements\.promptInput\.value : explicitMessage/, "direct prompt sends should not read the input textarea");
|
|
548
573
|
assert.match(app, /if \(usesPromptInput\) \{[\s\S]*?if \(targetStillActive\) \{[\s\S]*?elements\.promptInput\.value = "";/, "direct prompt sends should preserve the input textarea draft");
|
|
549
574
|
assert.match(app, /make\("button", "command-item"\)[\s\S]*?sendPrompt\("prompt", `\/\$\{command\.name\}`\)/, "side-panel command clicks should send the slash command directly");
|
|
550
|
-
assert.match(app, /const NATIVE_SELECTOR_COMMANDS = new Set\(\["model", "settings", "theme", "fork", "clone", "resume", "tree", "login", "logout", "scoped-models", "tools", "skills"\]\)/, "frontend should route native slash commands into selector UIs");
|
|
575
|
+
assert.match(app, /const NATIVE_SELECTOR_COMMANDS = new Set\(\["model", "settings", "theme", "fork", "clone", "name", "resume", "tree", "login", "logout", "scoped-models", "tools", "skills"\]\)/, "frontend should route native slash commands into selector UIs");
|
|
551
576
|
assert.match(app, /async function handleNativeSlashSelectorCommand\(message/, "frontend should intercept exact native slash commands before prompt forwarding");
|
|
552
577
|
assert.match(app, /kind === "prompt" && attachments\.length === 0 && await handleNativeSlashSelectorCommand/, "prompt sending should open native selector dialogs before marking a run active");
|
|
553
578
|
assert.match(app, /function openNativeModelSelector\(\)[\s\S]*?nativeCommandApi\("\/api\/models"\)/, "native /model selector should load models through the active tab API");
|
|
554
579
|
assert.match(app, /function openNativeSettingsDialog\(\)[\s\S]*?\/api\/steering-mode[\s\S]*?\/api\/follow-up-mode[\s\S]*?\/api\/auto-compaction/, "native /settings selector should expose queue and compaction controls");
|
|
580
|
+
assert.match(app, /function openNativeNameDialog\(\)[\s\S]*?sendPrompt\("prompt", `\/name \$\{name\}`\)/, "native /name selector should prompt before running the slash command");
|
|
555
581
|
assert.match(app, /function openNativeForkSelector\(\)[\s\S]*?\/api\/fork-messages[\s\S]*?\/api\/fork/, "native /fork selector should pair fork-point loading with the fork action");
|
|
556
582
|
assert.match(app, /function openNativeResumeSelector\(scope = "current"\)[\s\S]*?\/api\/sessions\?scope=\$\{encodeURIComponent\(selectedScope\)\}/, "native /resume selector should list current-cwd or all sessions");
|
|
557
583
|
assert.match(app, /function openNativeTreeSelector\(\)[\s\S]*?\/api\/session-tree[\s\S]*?\/api\/tree-navigate/, "native /tree selector should list tree entries and navigate through the backend helper");
|
|
@@ -629,7 +655,7 @@ assert.match(app, /case "webui_tab_renamed":/, "frontend should update tab label
|
|
|
629
655
|
assert.match(app, /terminalTabsToggleButton\.addEventListener\("click"/, "terminal tabs trigger should be wired in JS");
|
|
630
656
|
assert.match(app, /composerActionsButton\.addEventListener\("click"/, "composer actions trigger should be wired in JS");
|
|
631
657
|
assert.match(app, /function setMobileFooterExpanded\(/, "mobile footer should preserve expansion state for compatibility");
|
|
632
|
-
assert.match(app, /function updateFooterModelPickerPosition\(\)/, "
|
|
658
|
+
assert.match(app, /function updateFooterModelPickerPosition\(\)[\s\S]*?footerActivePickerTarget\(\)[\s\S]*?--footer-model-picker-left/, "footer picker should align desktop dropdowns above the active model or effort chip");
|
|
633
659
|
assert.match(app, /mobileFooterExpanded = false;[\s\S]*?document\.body\.classList\.remove\("footer-details-expanded"\)/, "opening mobile model picker should collapse legacy footer details so they cannot cover the dropdown");
|
|
634
660
|
assert.match(app, /function renderTuiFooterLine\([\s\S]*footer-line footer-line-tui/, "footer should render a minimal TUI-like line instead of metadata chips");
|
|
635
661
|
assert.match(app, /footerTuiItem\(model, "footer-tui-model", \{[\s\S]*setFooterModelPickerOpen\(!footerModelPickerOpen\)/, "footer model item should be clickable");
|
|
@@ -637,6 +663,10 @@ assert.match(app, /function renderFooterModelPicker\(\)/, "footer should render
|
|
|
637
663
|
assert.match(app, /api\("\/api\/scoped-models", \{ tabId: tabContext\.tabId \}\)/, "footer model picker should load scoped models instead of all available models");
|
|
638
664
|
assert.match(app, /for \(const model of footerScopedModels\)/, "footer model picker should render only scoped models");
|
|
639
665
|
assert.match(app, /api\("\/api\/model", \{ method: "POST"/, "footer model picker should apply selected model through the model API");
|
|
666
|
+
assert.match(app, /chip\.key === "thinking"[\s\S]*?setFooterThinkingPickerOpen\(!footerThinkingPickerOpen\)/, "git footer effort chip should open its own picker");
|
|
667
|
+
assert.match(app, /function renderFooterThinkingPicker\(\)[\s\S]*?Thinking effort[\s\S]*?for \(const level of footerThinkingLevels\(\)\)/, "footer should render a thinking effort picker dropdown");
|
|
668
|
+
assert.match(app, /api\("\/api\/thinking", \{ method: "POST", body: \{ level: nextLevel \}/, "footer thinking picker should apply selected effort through the thinking API");
|
|
669
|
+
assert.match(app, /function isFooterPickerOpen\(\)[\s\S]*?footerModelPickerOpen \|\| footerThinkingPickerOpen/, "footer picker overlay state should cover model and thinking pickers");
|
|
640
670
|
assert.doesNotMatch(app.match(/function renderMinimalFooter\(\)[\s\S]*?\n\}/)?.[0] || "", /footer-details-toggle/, "minimal default footer should not render a details toggle chip");
|
|
641
671
|
assert.match(app, /bindMobileViewChanges\(/, "side panel state should react to mobile breakpoint changes");
|
|
642
672
|
assert.match(app, /function restoreSidePanelState\(\) \{\n\s+if \(isMobileView\(\)\)/, "mobile should start with side panel collapsed even if desktop state was expanded");
|