Bootstrap collapse; drop Alpine; fix expand anim

Alessandro committed Oct 19, 2025 at 17:22 UTC 3b7c5a12f2c8e9392c27e1c8bb68e8b2c5d0e60a
5 files changed +51 -34
webui/components/sidebar/bottom/preferences/preferences-panel.html
+4 -12
@@ -7,15 +7,15 @@
7 <body>
8 <div x-data>
9 <template x-if="$store.preferences">
10 - <div class="pref-section" x-data="{ prefOpen: true }">
10 + <div class="pref-section">
11 <span>
12 - <h3 class="pref-header" @click="prefOpen = !prefOpen">
12 + <h3 class="pref-header" data-bs-toggle="collapse" data-bs-target="#pref-list">
13 Preferences
14 - <svg class="arrow-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16" :class="{'rotated': !prefOpen}">
14 + <svg class="arrow-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16">
15 <path d="M8 4l8 8-8 8" />
16 </svg>
17 </h3>
18 - <ul class="config-list" id="pref-list" x-show="prefOpen" x-collapse x-transition>
18 + <ul class="config-list collapse show" id="pref-list">
19 <li x-data x-init="$watch('$store.preferences.autoScroll', v => $store.preferences.applyAutoScroll(v))">
20 <span>Autoscroll</span>
21 <label class="switch">
@@ -81,14 +81,6 @@
81 cursor: pointer;
82 user-select: none;
83 }
84 - .pref-section .arrow-icon {
85 - margin-left: 0.5rem;
86 - transition: transform .15s ease;
87 - transform: rotate(90deg);
88 - }
89 - .pref-section .arrow-icon.rotated {
90 - transform: rotate(0deg);
91 - }
84 .pref-section .config-list {
85 list-style: none;
86 padding: 0.05rem 0;
webui/components/sidebar/left-sidebar.html
+10 -15
@@ -118,11 +118,17 @@
118 display: contents;
119 }
120
121 - /* Ensure the collapse wrapper participates in flex sizing */
122 - #tasks-section div[x-show][x-collapse] {
123 - display: contents;
121 + /* Bootstrap collapse elements need proper display for animation to work */
122 + #tasks-section .collapse {
123 + display: flex;
124 + flex-direction: column;
125 + flex: 1;
126 + min-height: 0;
127 + }
128 + /* During the collapsing transition, allow height animation to control size */
129 + #tasks-section .collapsing {
130 + flex: 0 0 auto !important;
131 }
125 -
132 /* Common section header styling (Chats, Tasks) - matching Preferences style */
133 .section-header {
134 display: flex;
@@ -141,17 +147,6 @@
147 cursor: pointer;
148 }
149
144 - /* Arrow icon for collapsible sections */
145 - .section-header .arrow-icon {
146 - margin-left: 0.5rem;
147 - transition: transform 0.15s ease;
148 - transform: rotate(90deg);
149 - }
150 -
151 - .section-header .arrow-icon.rotated {
152 - transform: rotate(0deg);
153 - }
154 -
150 @media (max-width: 768px) {
151 #left-panel {
152 position: fixed;
webui/components/sidebar/tasks/tasks-list.html
+15 -6
@@ -5,16 +5,17 @@
5 </script>
6 </head>
7 <body>
8 - <div x-data="{ tasksOpen: true }" class="tasks-list-root">
8 + <div class="tasks-list-root">
9 <template x-if="$store.tasks">
10 <div x-data class="tasks-list-inner">
11 - <h3 class="section-header section-header-collapsible" @click="tasksOpen = !tasksOpen">
11 + <h3 class="section-header section-header-collapsible" data-bs-toggle="collapse" data-bs-target="#tasks-collapse">
12 Tasks
13 - <svg class="arrow-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16" :class="{'rotated': !tasksOpen}">
13 + <svg class="arrow-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" width="16" height="16">
14 <path d="M8 4l8 8-8 8" />
15 </svg>
16 </h3>
17 - <div class="tasks-list-body" x-show="tasksOpen" x-collapse x-transition>
17 + <div class="collapse show" id="tasks-collapse">
18 + <div class="tasks-list-body">
19 <div class="tasks-list-container" x-data>
20 <ul class="config-list tasks-config-list" x-show="$store.tasks.tasks.length > 0">
21 <template x-for="task in $store.tasks.tasks" :key="task.id">
@@ -54,6 +55,7 @@
55 <p><i>No tasks to list.</i></p>
56 </div>
57 </div>
58 + </div>
59 </div>
60 </div>
61 </template>
@@ -64,10 +66,17 @@
66 <style>
67 /* Root wrappers provide flex context when embedded */
68 .tasks-list-root,
67 - .tasks-list-inner,
68 - .tasks-list-body {
69 + .tasks-list-inner {
70 display: contents;
71 }
72 +
73 + /* tasks-list-body is the flex container inside the collapse wrapper */
74 + .tasks-list-body {
75 + display: flex;
76 + flex-direction: column;
77 + flex: 1 1 auto;
78 + min-height: 0;
79 + }
80 .tasks-list-container .section-header {
81 position: sticky;
82 top: 0;
webui/index.css
+19
@@ -1641,3 +1641,22 @@ nav ul li a img {
1641 .spin {
1642 animation: spin 1s linear infinite;
1643 }
1644 +
1645 +/* Bootstrap Collapse */
1646 +.collapse:not(.show) {
1647 + display: none !important;
1648 +}
1649 +.collapsing {
1650 + height: 0;
1651 + overflow: hidden;
1652 + transition: height .35s ease;
1653 +}
1654 +
1655 +/* Arrow rotation based on Bootstrap's .collapsed class */
1656 +[data-bs-toggle="collapse"] .arrow-icon {
1657 + transform: rotate(90deg);
1658 + transition: transform .15s ease;
1659 +}
1660 +[data-bs-toggle="collapse"].collapsed .arrow-icon {
1661 + transform: rotate(0deg);
1662 +}
webui/index.html
+3 -1
@@ -102,8 +102,10 @@
102
103 <script type="module" src="index.js"></script>
104
105 + <!-- Bootstrap JS (only for logic, importing bundled CSS => UI conflicts) -->
106 + <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
107 +
108 <!-- Then load Alpine.js -->
106 - <script defer src="vendor/alpine/alpine.collapse.min.js"></script>
109 <!-- <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.3/dist/cdn.min.js"></script> -->
110 <script type="module" src="js/initFw.js"></script>
111