Skip to content

Commit 9e7af64

Browse files
committed
fix responsive on mobile
1 parent 2eda9b4 commit 9e7af64

File tree

3 files changed

+169
-134
lines changed

3 files changed

+169
-134
lines changed

examples/server/webui/index.html

Lines changed: 158 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -8,162 +8,185 @@
88
</head>
99

1010
<body>
11-
<div id="app" class="flex flex-row opacity-0"> <!-- opacity-0 will be removed on app mounted -->
12-
<!-- sidebar -->
13-
<div class="flex flex-col bg-black bg-opacity-5 w-64 py-8 px-4 h-screen overflow-y-auto">
14-
<h2 class="font-bold mb-4 ml-4">Conversations</h2>
15-
16-
<!-- list of conversations -->
17-
<div :class="{
18-
'btn btn-ghost justify-start': true,
19-
'btn-active': messages.length === 0,
20-
}" @click="newConversation">
21-
+ New conversation
22-
</div>
23-
<div v-for="conv in conversations" :class="{
24-
'btn btn-ghost justify-start font-normal': true,
25-
'btn-active': conv.id === viewingConvId,
26-
}" @click="setViewingConv(conv.id)">
27-
<span class="truncate">{{ conv.messages[0].content }}</span>
28-
</div>
29-
<div class="text-center text-xs opacity-40 mt-auto mx-4">
30-
Conversations are saved to browser's localStorage
31-
</div>
32-
</div>
11+
<div id="app" class="opacity-0"> <!-- opacity-0 will be removed on app mounted -->
12+
<div class="flex flex-row drawer lg:drawer-open">
13+
<input id="toggle-drawer" type="checkbox" class="drawer-toggle" checked />
3314

34-
<div class="chat-screen flex flex-col w-screen h-screen px-8 mx-auto">
35-
<!-- header -->
36-
<div class="flex flex-row items-center">
37-
<div class="grow text-2xl font-bold mt-8 mb-6">
38-
🦙 llama.cpp
39-
</div>
15+
<!-- sidebar -->
16+
<div class="drawer-side h-screen lg:h-screen z-50 lg:max-w-64">
17+
<label for="toggle-drawer" aria-label="close sidebar" class="drawer-overlay"></label>
18+
<div class="flex flex-col bg-base-200 min-h-full py-4 px-4">
19+
<div class="flex flex-row items-center justify-between mb-4 mt-4">
20+
<h2 class="font-bold ml-4">Conversations</h2>
4021

41-
<!-- action buttons (top right) -->
42-
<div class="flex items-center">
43-
<div v-if="messages.length > 0" class="dropdown dropdown-end">
44-
<!-- "more" button -->
45-
<div tabindex="0" role="button" class="btn m-1">
46-
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-chevron-down" viewBox="0 0 16 16">
47-
<path fill-rule="evenodd" d="M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708"/>
22+
<!-- close sidebar button -->
23+
<label for="toggle-drawer" class="btn btn-ghost lg:hidden">
24+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-arrow-bar-left" viewBox="0 0 16 16">
25+
<path fill-rule="evenodd" d="M12.5 15a.5.5 0 0 1-.5-.5v-13a.5.5 0 0 1 1 0v13a.5.5 0 0 1-.5.5M10 8a.5.5 0 0 1-.5.5H3.707l2.147 2.146a.5.5 0 0 1-.708.708l-3-3a.5.5 0 0 1 0-.708l3-3a.5.5 0 1 1 .708.708L3.707 7.5H9.5a.5.5 0 0 1 .5.5"/>
4826
</svg>
49-
</div>
50-
<!-- "more" dropdown menu -->
51-
<ul tabindex="0" class="dropdown-content menu bg-base-100 rounded-box z-[1] w-52 p-2 shadow">
52-
<li @click="downloadConv(viewingConvId)" :disabled="isGenerating"><a>Download</a></li>
53-
<li class="text-error" @click="deleteConv(viewingConvId)" :disabled="isGenerating"><a>Delete</a></li>
54-
</ul>
27+
</label>
28+
</div>
29+
30+
<!-- list of conversations -->
31+
<div :class="{
32+
'btn btn-ghost justify-start': true,
33+
'btn-active': messages.length === 0,
34+
}" @click="newConversation">
35+
+ New conversation
5536
</div>
56-
<button class="btn" @click="showConfigDialog = true" :disabled="isGenerating">
57-
<!-- edit config button -->
58-
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-gear" viewBox="0 0 16 16">
59-
<path d="M8 4.754a3.246 3.246 0 1 0 0 6.492 3.246 3.246 0 0 0 0-6.492M5.754 8a2.246 2.246 0 1 1 4.492 0 2.246 2.246 0 0 1-4.492 0"/>
60-
<path d="M9.796 1.343c-.527-1.79-3.065-1.79-3.592 0l-.094.319a.873.873 0 0 1-1.255.52l-.292-.16c-1.64-.892-3.433.902-2.54 2.541l.159.292a.873.873 0 0 1-.52 1.255l-.319.094c-1.79.527-1.79 3.065 0 3.592l.319.094a.873.873 0 0 1 .52 1.255l-.16.292c-.892 1.64.901 3.434 2.541 2.54l.292-.159a.873.873 0 0 1 1.255.52l.094.319c.527 1.79 3.065 1.79 3.592 0l.094-.319a.873.873 0 0 1 1.255-.52l.292.16c1.64.893 3.434-.902 2.54-2.541l-.159-.292a.873.873 0 0 1 .52-1.255l.319-.094c1.79-.527 1.79-3.065 0-3.592l-.319-.094a.873.873 0 0 1-.52-1.255l.16-.292c.893-1.64-.902-3.433-2.541-2.54l-.292.159a.873.873 0 0 1-1.255-.52zm-2.633.283c.246-.835 1.428-.835 1.674 0l.094.319a1.873 1.873 0 0 0 2.693 1.115l.291-.16c.764-.415 1.6.42 1.184 1.185l-.159.292a1.873 1.873 0 0 0 1.116 2.692l.318.094c.835.246.835 1.428 0 1.674l-.319.094a1.873 1.873 0 0 0-1.115 2.693l.16.291c.415.764-.42 1.6-1.185 1.184l-.291-.159a1.873 1.873 0 0 0-2.693 1.116l-.094.318c-.246.835-1.428.835-1.674 0l-.094-.319a1.873 1.873 0 0 0-2.692-1.115l-.292.16c-.764.415-1.6-.42-1.184-1.185l.159-.291A1.873 1.873 0 0 0 1.945 8.93l-.319-.094c-.835-.246-.835-1.428 0-1.674l.319-.094A1.873 1.873 0 0 0 3.06 4.377l-.16-.292c-.415-.764.42-1.6 1.185-1.184l.292.159a1.873 1.873 0 0 0 2.692-1.115z"/>
37+
<div v-for="conv in conversations" :class="{
38+
'btn btn-ghost justify-start font-normal': true,
39+
'btn-active': conv.id === viewingConvId,
40+
}" @click="setViewingConv(conv.id)">
41+
<span class="truncate">{{ conv.messages[0].content }}</span>
42+
</div>
43+
<div class="text-center text-xs opacity-40 mt-auto mx-4">
44+
Conversations are saved to browser's localStorage
45+
</div>
46+
</div>
47+
</div>
48+
49+
<!-- main view -->
50+
<div class="chat-screen drawer-content grow flex flex-col h-screen w-screen mx-auto px-4">
51+
<!-- header -->
52+
<div class="flex flex-row items-center mt-6 mb-6">
53+
<!-- open sidebar button -->
54+
<label for="toggle-drawer" class="btn btn-ghost lg:hidden">
55+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-list" viewBox="0 0 16 16">
56+
<path fill-rule="evenodd" d="M2.5 12a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5m0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5m0-4a.5.5 0 0 1 .5-.5h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5"/>
6157
</svg>
62-
</button>
63-
64-
<!-- theme controller is copied from https://daisyui.com/components/theme-controller/ -->
65-
<div class="dropdown dropdown-end dropdown-bottom">
66-
<div tabindex="0" role="button" class="btn m-1">
67-
Theme
68-
<svg width="12px" height="12px" class="inline-block h-2 w-2 fill-current opacity-60" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2048 2048">
69-
<path d="M1799 349l242 241-1017 1017L7 590l242-241 775 775 775-775z"></path>
58+
</label>
59+
60+
<div class="grow text-2xl font-bold ml-2">llama.cpp</div>
61+
62+
<!-- action buttons (top right) -->
63+
<div class="flex items-center">
64+
<div v-if="messages.length > 0" class="dropdown dropdown-end">
65+
<!-- "more" button -->
66+
<div tabindex="0" role="button" class="btn m-1">
67+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-three-dots-vertical" viewBox="0 0 16 16">
68+
<path d="M9.5 13a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0m0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0m0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0"/>
69+
</svg>
70+
</div>
71+
<!-- "more" dropdown menu -->
72+
<ul tabindex="0" class="dropdown-content menu bg-base-100 rounded-box z-[1] w-52 p-2 shadow">
73+
<li @click="downloadConv(viewingConvId)" :disabled="isGenerating"><a>Download</a></li>
74+
<li class="text-error" @click="deleteConv(viewingConvId)" :disabled="isGenerating"><a>Delete</a></li>
75+
</ul>
76+
</div>
77+
<button class="btn" @click="showConfigDialog = true" :disabled="isGenerating">
78+
<!-- settings button -->
79+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-gear" viewBox="0 0 16 16">
80+
<path d="M8 4.754a3.246 3.246 0 1 0 0 6.492 3.246 3.246 0 0 0 0-6.492M5.754 8a2.246 2.246 0 1 1 4.492 0 2.246 2.246 0 0 1-4.492 0"/>
81+
<path d="M9.796 1.343c-.527-1.79-3.065-1.79-3.592 0l-.094.319a.873.873 0 0 1-1.255.52l-.292-.16c-1.64-.892-3.433.902-2.54 2.541l.159.292a.873.873 0 0 1-.52 1.255l-.319.094c-1.79.527-1.79 3.065 0 3.592l.319.094a.873.873 0 0 1 .52 1.255l-.16.292c-.892 1.64.901 3.434 2.541 2.54l.292-.159a.873.873 0 0 1 1.255.52l.094.319c.527 1.79 3.065 1.79 3.592 0l.094-.319a.873.873 0 0 1 1.255-.52l.292.16c1.64.893 3.434-.902 2.54-2.541l-.159-.292a.873.873 0 0 1 .52-1.255l.319-.094c1.79-.527 1.79-3.065 0-3.592l-.319-.094a.873.873 0 0 1-.52-1.255l.16-.292c.893-1.64-.902-3.433-2.541-2.54l-.292.159a.873.873 0 0 1-1.255-.52zm-2.633.283c.246-.835 1.428-.835 1.674 0l.094.319a1.873 1.873 0 0 0 2.693 1.115l.291-.16c.764-.415 1.6.42 1.184 1.185l-.159.292a1.873 1.873 0 0 0 1.116 2.692l.318.094c.835.246.835 1.428 0 1.674l-.319.094a1.873 1.873 0 0 0-1.115 2.693l.16.291c.415.764-.42 1.6-1.185 1.184l-.291-.159a1.873 1.873 0 0 0-2.693 1.116l-.094.318c-.246.835-1.428.835-1.674 0l-.094-.319a1.873 1.873 0 0 0-2.692-1.115l-.292.16c-.764.415-1.6-.42-1.184-1.185l.159-.291A1.873 1.873 0 0 0 1.945 8.93l-.319-.094c-.835-.246-.835-1.428 0-1.674l.319-.094A1.873 1.873 0 0 0 3.06 4.377l-.16-.292c-.415-.764.42-1.6 1.185-1.184l.292.159a1.873 1.873 0 0 0 2.692-1.115z"/>
7082
</svg>
83+
</button>
84+
85+
<!-- theme controller is copied from https://daisyui.com/components/theme-controller/ -->
86+
<div class="dropdown dropdown-end dropdown-bottom">
87+
<div tabindex="0" role="button" class="btn m-1">
88+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-palette2" viewBox="0 0 16 16">
89+
<path d="M0 .5A.5.5 0 0 1 .5 0h5a.5.5 0 0 1 .5.5v5.277l4.147-4.131a.5.5 0 0 1 .707 0l3.535 3.536a.5.5 0 0 1 0 .708L10.261 10H15.5a.5.5 0 0 1 .5.5v5a.5.5 0 0 1-.5.5H3a3 3 0 0 1-2.121-.879A3 3 0 0 1 0 13.044m6-.21 7.328-7.3-2.829-2.828L6 7.188zM4.5 13a1.5 1.5 0 1 0-3 0 1.5 1.5 0 0 0 3 0M15 15v-4H9.258l-4.015 4zM0 .5v12.495zm0 12.495V13z"/>
90+
</svg>
91+
</div>
92+
<ul tabindex="0" class="dropdown-content bg-base-300 rounded-box z-[1] w-52 p-2 shadow-2xl h-80 overflow-y-auto">
93+
<li>
94+
<button
95+
class="btn btn-sm btn-block btn-ghost justify-start"
96+
:class="{ 'btn-active': selectedTheme === 'auto' }"
97+
@click="setSelectedTheme('auto')">
98+
auto
99+
</button>
100+
</li>
101+
<li v-for="theme in themes">
102+
<input
103+
type="radio"
104+
name="theme-dropdown"
105+
class="theme-controller btn btn-sm btn-block btn-ghost justify-start"
106+
:aria-label="theme"
107+
:value="theme"
108+
:checked="selectedTheme === theme"
109+
@click="setSelectedTheme(theme)" />
110+
</li>
111+
</ul>
71112
</div>
72-
<ul tabindex="0" class="dropdown-content bg-base-300 rounded-box z-[1] w-52 p-2 shadow-2xl h-80 overflow-y-auto">
73-
<li>
74-
<button
75-
class="btn btn-sm btn-block w-full btn-ghost justify-start"
76-
:class="{ 'btn-active': selectedTheme === 'auto' }"
77-
@click="setSelectedTheme('auto')">
78-
auto
79-
</button>
80-
</li>
81-
<li v-for="theme in themes">
82-
<input
83-
type="radio"
84-
name="theme-dropdown"
85-
class="theme-controller btn btn-sm btn-block w-full btn-ghost justify-start"
86-
:aria-label="theme"
87-
:value="theme"
88-
:checked="selectedTheme === theme"
89-
@click="setSelectedTheme(theme)" />
90-
</li>
91-
</ul>
92113
</div>
93114
</div>
94-
</div>
95115

96-
<!-- chat messages -->
97-
<div id="messages-list" class="flex flex-col grow overflow-y-auto">
98-
<div class="mt-auto flex justify-center">
99-
<!-- placeholder to shift the message to the bottom -->
100-
{{ messages.length === 0 ? 'Send a message to start' : '' }}
101-
</div>
102-
<div v-for="msg in messages" class="group">
103-
<div :class="{
104-
'chat': true,
105-
'chat-start': msg.role !== 'user',
106-
'chat-end': msg.role === 'user',
107-
}">
116+
<!-- chat messages -->
117+
<div id="messages-list" class="flex flex-col grow overflow-y-auto">
118+
<div class="mt-auto flex justify-center">
119+
<!-- placeholder to shift the message to the bottom -->
120+
{{ messages.length === 0 ? 'Send a message to start' : '' }}
121+
</div>
122+
<div v-for="msg in messages" class="group">
108123
<div :class="{
109-
'chat-bubble markdown': true,
110-
'chat-bubble-base-300': msg.role !== 'user',
124+
'chat': true,
125+
'chat-start': msg.role !== 'user',
126+
'chat-end': msg.role === 'user',
111127
}">
112-
<!-- textarea for editing message -->
113-
<template v-if="editingMsg && editingMsg.id === msg.id">
114-
<textarea
115-
class="textarea textarea-bordered bg-base-100 text-base-content w-96"
116-
v-model="msg.content"></textarea>
117-
<br/>
118-
<button class="btn btn-ghost mt-2 mr-2" @click="editingMsg = null">Cancel</button>
119-
<button class="btn mt-2" @click="editUserMsgAndRegenerate(msg)">Submit</button>
120-
</template>
121-
<!-- render message as markdown -->
122-
<vue-markdown v-else :source="msg.content" />
128+
<div :class="{
129+
'chat-bubble markdown': true,
130+
'chat-bubble-base-300': msg.role !== 'user',
131+
}">
132+
<!-- textarea for editing message -->
133+
<template v-if="editingMsg && editingMsg.id === msg.id">
134+
<textarea
135+
class="textarea textarea-bordered bg-base-100 text-base-content w-96"
136+
v-model="msg.content"></textarea>
137+
<br/>
138+
<button class="btn btn-ghost mt-2 mr-2" @click="editingMsg = null">Cancel</button>
139+
<button class="btn mt-2" @click="editUserMsgAndRegenerate(msg)">Submit</button>
140+
</template>
141+
<!-- render message as markdown -->
142+
<vue-markdown v-else :source="msg.content" />
143+
</div>
144+
</div>
145+
146+
<!-- actions for each message -->
147+
<div :class="{'text-right': msg.role === 'user'}" class="mx-4 mt-2 mb-2">
148+
<!-- user message -->
149+
<button v-if="msg.role === 'user'" class="badge btn-mini show-on-hover" @click="editingMsg = msg" :disabled="isGenerating">
150+
✍️ Edit
151+
</button>
152+
<!-- assistant message -->
153+
<button v-if="msg.role === 'assistant'" class="badge btn-mini show-on-hover mr-2" @click="regenerateMsg(msg)" :disabled="isGenerating">
154+
🔄 Regenerate
155+
</button>
156+
<button v-if="msg.role === 'assistant'" class="badge btn-mini show-on-hover mr-2" @click="copyMsg(msg)" :disabled="isGenerating">
157+
📋 Copy
158+
</button>
123159
</div>
124160
</div>
125161

126-
<!-- actions for each message -->
127-
<div :class="{'text-right': msg.role === 'user'}" class="mx-4 mt-2 mb-2">
128-
<!-- user message -->
129-
<button v-if="msg.role === 'user'" class="badge btn-mini show-on-hover" @click="editingMsg = msg" :disabled="isGenerating">
130-
✍️ Edit
131-
</button>
132-
<!-- assistant message -->
133-
<button v-if="msg.role === 'assistant'" class="badge btn-mini show-on-hover mr-2" @click="regenerateMsg(msg)" :disabled="isGenerating">
134-
🔄 Regenerate
135-
</button>
136-
<button v-if="msg.role === 'assistant'" class="badge btn-mini show-on-hover mr-2" @click="copyMsg(msg)" :disabled="isGenerating">
137-
📋 Copy
138-
</button>
162+
<!-- pending (ongoing) assistant message -->
163+
<div id="pending-msg" class="chat chat-start">
164+
<div v-if="pendingMsg" class="chat-bubble markdown chat-bubble-base-300">
165+
<span v-if="!pendingMsg.content" class="loading loading-dots loading-md"></span>
166+
<vue-markdown v-else :source="pendingMsg.content" />
167+
</div>
139168
</div>
140169
</div>
141170

142-
<!-- pending (ongoing) assistant message -->
143-
<div id="pending-msg" class="chat chat-start">
144-
<div v-if="pendingMsg" class="chat-bubble markdown chat-bubble-base-300">
145-
<span v-if="!pendingMsg.content" class="loading loading-dots loading-md"></span>
146-
<vue-markdown v-else :source="pendingMsg.content" />
147-
</div>
171+
<!-- chat input -->
172+
<div class="flex flex-row items-center mt-8 mb-6">
173+
<textarea
174+
class="textarea textarea-bordered w-full"
175+
placeholder="Type a message (Shift+Enter to add a new line)"
176+
v-model="inputMsg"
177+
@keydown.enter.exact.prevent="sendMessage"
178+
@keydown.enter.shift.exact.prevent="inputMsg += '\n'"
179+
:disabled="isGenerating"
180+
id="msg-input"
181+
></textarea>
182+
<button v-if="!isGenerating" class="btn btn-primary ml-2" @click="sendMessage" :disabled="inputMsg.length === 0">Send</button>
183+
<button v-else class="btn btn-neutral ml-2" @click="stopGeneration">Stop</button>
148184
</div>
149185
</div>
150186

151-
<!-- chat input -->
152-
<div class="flex flex-row items-center mt-8 mb-6">
153-
<textarea
154-
class="textarea textarea-bordered w-full"
155-
placeholder="Type a message (Shift+Enter to add a new line)"
156-
v-model="inputMsg"
157-
@keydown.enter.exact.prevent="sendMessage"
158-
@keydown.enter.shift.exact.prevent="inputMsg += '\n'"
159-
:disabled="isGenerating"
160-
id="msg-input"
161-
></textarea>
162-
<button v-if="!isGenerating" class="btn btn-primary ml-2" @click="sendMessage" :disabled="inputMsg.length === 0">Send</button>
163-
<button v-else class="btn btn-neutral ml-2" @click="stopGeneration">Stop</button>
164-
</div>
165187
</div>
166188

189+
167190
<!-- modal for editing config -->
168191
<dialog class="modal" :class="{'modal-open': showConfigDialog}">
169192
<div class="modal-box">
@@ -217,10 +240,11 @@ <h3 class="text-lg font-bold mb-6">Settings</h3>
217240
<div class="modal-action">
218241
<button class="btn" @click="resetConfigDialog">Reset to default</button>
219242
<button class="btn" @click="closeAndDiscardConfigDialog">Close</button>
220-
<button class="btn btn-primary" @click="closeAndSaveConfigDialog">Save and close</button>
243+
<button class="btn btn-primary" @click="closeAndSaveConfigDialog">Save</button>
221244
</div>
222245
</div>
223246
</dialog>
247+
224248
</div>
225249

226250
<!-- Template to be used by settings modal -->

0 commit comments

Comments
 (0)