|
157 | 157 | let showExtraTools = $derived(modelHasTools && !assistant);
|
158 | 158 |
|
159 | 159 | let showNoTools = $derived(!showWebSearch && !showImageGen && !showFileUpload && !showExtraTools);
|
160 |
| - |
161 | 160 | </script>
|
162 | 161 |
|
163 | 162 | <div class="flex min-h-full flex-1 flex-col" onpaste={onPaste}>
|
|
184 | 183 | ></textarea>
|
185 | 184 |
|
186 | 185 | {#if !showNoTools}
|
187 |
| - <div |
188 |
| - class={[ |
189 |
| - "scrollbar-custom -ml-0.5 flex max-w-[calc(100%-40px)] flex-wrap items-center justify-start gap-2.5 px-3 pb-2.5 pt-1.5 text-gray-500 dark:text-gray-400 max-md:flex-nowrap max-md:overflow-x-auto sm:gap-2", |
190 |
| - ]} |
191 |
| - > |
192 |
| - {#if showWebSearch} |
193 |
| - <HoverTooltip |
194 |
| - label="Search the web" |
195 |
| - position="top" |
196 |
| - TooltipClassNames="text-xs !text-left !w-auto whitespace-nowrap !py-1 !mb-0 max-sm:hidden {webSearchIsOn |
197 |
| - ? 'hidden' |
198 |
| - : ''}" |
199 |
| - > |
200 |
| - <button |
201 |
| - class="base-tool" |
202 |
| - class:active-tool={webSearchIsOn} |
203 |
| - disabled={loading} |
204 |
| - onclick={async (e) => { |
205 |
| - e.preventDefault(); |
206 |
| - if (modelHasTools) { |
207 |
| - if (webSearchIsOn) { |
208 |
| - await settings.instantSet({ |
209 |
| - tools: ($settings.tools ?? []).filter( |
210 |
| - (t) => t !== webSearchToolId && t !== fetchUrlToolId |
211 |
| - ), |
212 |
| - }); |
213 |
| - } else { |
214 |
| - await settings.instantSet({ |
215 |
| - tools: [...($settings.tools ?? []), webSearchToolId, fetchUrlToolId], |
216 |
| - }); |
217 |
| - } |
218 |
| - } else { |
219 |
| - $webSearchParameters.useSearch = !webSearchIsOn; |
220 |
| - } |
221 |
| - }} |
222 |
| - > |
223 |
| - <IconInternet classNames="text-xl" /> |
224 |
| - {#if webSearchIsOn} |
225 |
| - Search |
226 |
| - {/if} |
227 |
| - </button> |
228 |
| - </HoverTooltip> |
229 |
| - {/if} |
230 |
| - {#if showImageGen} |
231 |
| - <HoverTooltip |
232 |
| - label="Generate images" |
233 |
| - position="top" |
234 |
| - TooltipClassNames="text-xs !text-left !w-auto whitespace-nowrap !py-1 !mb-0 max-sm:hidden {imageGenIsOn |
235 |
| - ? 'hidden' |
236 |
| - : ''}" |
237 |
| - > |
238 |
| - <button |
239 |
| - class="base-tool" |
240 |
| - class:active-tool={imageGenIsOn} |
241 |
| - disabled={loading} |
242 |
| - onclick={async (e) => { |
243 |
| - e.preventDefault(); |
244 |
| - if (modelHasTools) { |
245 |
| - if (imageGenIsOn) { |
246 |
| - await settings.instantSet({ |
247 |
| - tools: ($settings.tools ?? []).filter((t) => t !== imageGenToolId), |
248 |
| - }); |
249 |
| - } else { |
250 |
| - await settings.instantSet({ |
251 |
| - tools: [...($settings.tools ?? []), imageGenToolId], |
252 |
| - }); |
253 |
| - } |
254 |
| - } |
255 |
| - }} |
256 |
| - > |
257 |
| - <IconImageGen classNames="text-xl" /> |
258 |
| - {#if imageGenIsOn} |
259 |
| - Image Gen |
260 |
| - {/if} |
261 |
| - </button> |
262 |
| - </HoverTooltip> |
263 |
| - {/if} |
264 |
| - {#if showFileUpload} |
265 |
| - {@const mimeTypesString = mimeTypes |
266 |
| - .map((m) => { |
267 |
| - // if the mime type ends in *, grab the first part so image/* becomes image |
268 |
| - if (m.endsWith("*")) { |
269 |
| - return m.split("/")[0]; |
270 |
| - } |
271 |
| - // otherwise, return the second part for example application/pdf becomes pdf |
272 |
| - return m.split("/")[1]; |
273 |
| - }) |
274 |
| - .join(", ")} |
275 |
| - <div class="flex items-center"> |
| 186 | + <div |
| 187 | + class={[ |
| 188 | + "scrollbar-custom -ml-0.5 flex max-w-[calc(100%-40px)] flex-wrap items-center justify-start gap-2.5 px-3 pb-2.5 pt-1.5 text-gray-500 dark:text-gray-400 max-md:flex-nowrap max-md:overflow-x-auto sm:gap-2", |
| 189 | + ]} |
| 190 | + > |
| 191 | + {#if showWebSearch} |
276 | 192 | <HoverTooltip
|
277 |
| - label={mimeTypesString.includes("*") |
278 |
| - ? "Upload any file" |
279 |
| - : `Upload ${mimeTypesString} files`} |
| 193 | + label="Search the web" |
280 | 194 | position="top"
|
281 |
| - TooltipClassNames="text-xs !text-left !w-auto whitespace-nowrap !py-1 !mb-0 max-sm:hidden" |
| 195 | + TooltipClassNames="text-xs !text-left !w-auto whitespace-nowrap !py-1 !mb-0 max-sm:hidden {webSearchIsOn |
| 196 | + ? 'hidden' |
| 197 | + : ''}" |
282 | 198 | >
|
283 |
| - <label class="base-tool relative" class:active-tool={documentParserIsOn}> |
284 |
| - <input |
285 |
| - disabled={loading} |
286 |
| - class="absolute hidden size-0" |
287 |
| - aria-label="Upload file" |
288 |
| - type="file" |
289 |
| - onchange={onFileChange} |
290 |
| - accept={mimeTypes.join(",")} |
291 |
| - /> |
292 |
| - <IconPaperclip classNames="text-xl" /> |
293 |
| - {#if documentParserIsOn} |
294 |
| - Document Parser |
| 199 | + <button |
| 200 | + class="base-tool" |
| 201 | + class:active-tool={webSearchIsOn} |
| 202 | + disabled={loading} |
| 203 | + onclick={async (e) => { |
| 204 | + e.preventDefault(); |
| 205 | + if (modelHasTools) { |
| 206 | + if (webSearchIsOn) { |
| 207 | + await settings.instantSet({ |
| 208 | + tools: ($settings.tools ?? []).filter( |
| 209 | + (t) => t !== webSearchToolId && t !== fetchUrlToolId |
| 210 | + ), |
| 211 | + }); |
| 212 | + } else { |
| 213 | + await settings.instantSet({ |
| 214 | + tools: [...($settings.tools ?? []), webSearchToolId, fetchUrlToolId], |
| 215 | + }); |
| 216 | + } |
| 217 | + } else { |
| 218 | + $webSearchParameters.useSearch = !webSearchIsOn; |
| 219 | + } |
| 220 | + }} |
| 221 | + > |
| 222 | + <IconInternet classNames="text-xl" /> |
| 223 | + {#if webSearchIsOn} |
| 224 | + Search |
295 | 225 | {/if}
|
296 |
| - </label> |
| 226 | + </button> |
297 | 227 | </HoverTooltip>
|
298 |
| - </div> |
299 |
| - {#if mimeTypes.includes("image/*")} |
| 228 | + {/if} |
| 229 | + {#if showImageGen} |
300 | 230 | <HoverTooltip
|
301 |
| - label="Capture screenshot" |
| 231 | + label="Generate images" |
302 | 232 | position="top"
|
303 |
| - TooltipClassNames="text-xs !text-left !w-auto whitespace-nowrap !py-1 !mb-0 max-sm:hidden" |
| 233 | + TooltipClassNames="text-xs !text-left !w-auto whitespace-nowrap !py-1 !mb-0 max-sm:hidden {imageGenIsOn |
| 234 | + ? 'hidden' |
| 235 | + : ''}" |
304 | 236 | >
|
305 | 237 | <button
|
306 | 238 | class="base-tool"
|
| 239 | + class:active-tool={imageGenIsOn} |
| 240 | + disabled={loading} |
307 | 241 | onclick={async (e) => {
|
308 | 242 | e.preventDefault();
|
309 |
| - const screenshot = await captureScreen(); |
| 243 | + if (modelHasTools) { |
| 244 | + if (imageGenIsOn) { |
| 245 | + await settings.instantSet({ |
| 246 | + tools: ($settings.tools ?? []).filter((t) => t !== imageGenToolId), |
| 247 | + }); |
| 248 | + } else { |
| 249 | + await settings.instantSet({ |
| 250 | + tools: [...($settings.tools ?? []), imageGenToolId], |
| 251 | + }); |
| 252 | + } |
| 253 | + } |
| 254 | + }} |
| 255 | + > |
| 256 | + <IconImageGen classNames="text-xl" /> |
| 257 | + {#if imageGenIsOn} |
| 258 | + Image Gen |
| 259 | + {/if} |
| 260 | + </button> |
| 261 | + </HoverTooltip> |
| 262 | + {/if} |
| 263 | + {#if showFileUpload} |
| 264 | + {@const mimeTypesString = mimeTypes |
| 265 | + .map((m) => { |
| 266 | + // if the mime type ends in *, grab the first part so image/* becomes image |
| 267 | + if (m.endsWith("*")) { |
| 268 | + return m.split("/")[0]; |
| 269 | + } |
| 270 | + // otherwise, return the second part for example application/pdf becomes pdf |
| 271 | + return m.split("/")[1]; |
| 272 | + }) |
| 273 | + .join(", ")} |
| 274 | + <div class="flex items-center"> |
| 275 | + <HoverTooltip |
| 276 | + label={mimeTypesString.includes("*") |
| 277 | + ? "Upload any file" |
| 278 | + : `Upload ${mimeTypesString} files`} |
| 279 | + position="top" |
| 280 | + TooltipClassNames="text-xs !text-left !w-auto whitespace-nowrap !py-1 !mb-0 max-sm:hidden" |
| 281 | + > |
| 282 | + <label class="base-tool relative" class:active-tool={documentParserIsOn}> |
| 283 | + <input |
| 284 | + disabled={loading} |
| 285 | + class="absolute hidden size-0" |
| 286 | + aria-label="Upload file" |
| 287 | + type="file" |
| 288 | + onchange={onFileChange} |
| 289 | + accept={mimeTypes.join(",")} |
| 290 | + /> |
| 291 | + <IconPaperclip classNames="text-xl" /> |
| 292 | + {#if documentParserIsOn} |
| 293 | + Document Parser |
| 294 | + {/if} |
| 295 | + </label> |
| 296 | + </HoverTooltip> |
| 297 | + </div> |
| 298 | + {#if mimeTypes.includes("image/*")} |
| 299 | + <HoverTooltip |
| 300 | + label="Capture screenshot" |
| 301 | + position="top" |
| 302 | + TooltipClassNames="text-xs !text-left !w-auto whitespace-nowrap !py-1 !mb-0 max-sm:hidden" |
| 303 | + > |
| 304 | + <button |
| 305 | + class="base-tool" |
| 306 | + onclick={async (e) => { |
| 307 | + e.preventDefault(); |
| 308 | + const screenshot = await captureScreen(); |
310 | 309 |
|
311 |
| - // Convert base64 to blob |
312 |
| - const base64Response = await fetch(screenshot); |
313 |
| - const blob = await base64Response.blob(); |
| 310 | + // Convert base64 to blob |
| 311 | + const base64Response = await fetch(screenshot); |
| 312 | + const blob = await base64Response.blob(); |
314 | 313 |
|
315 |
| - // Create a File object from the blob |
316 |
| - const file = new File([blob], "screenshot.png", { type: "image/png" }); |
| 314 | + // Create a File object from the blob |
| 315 | + const file = new File([blob], "screenshot.png", { type: "image/png" }); |
317 | 316 |
|
318 |
| - files = [...files, file]; |
| 317 | + files = [...files, file]; |
| 318 | + }} |
| 319 | + > |
| 320 | + <IconScreenshot classNames="text-xl" /> |
| 321 | + </button> |
| 322 | + </HoverTooltip> |
| 323 | + {/if} |
| 324 | + {/if} |
| 325 | + {#if showExtraTools} |
| 326 | + {#each extraTools as tool} |
| 327 | + <button |
| 328 | + class="active-tool base-tool" |
| 329 | + disabled={loading} |
| 330 | + onclick={async (e) => { |
| 331 | + e.preventDefault(); |
| 332 | + goto(`${base}/tools/${tool._id}`); |
319 | 333 | }}
|
320 | 334 | >
|
321 |
| - <IconScreenshot classNames="text-xl" /> |
| 335 | + {#key tool.icon + tool.color} |
| 336 | + <ToolLogo icon={tool.icon} color={tool.color} size="xs" /> |
| 337 | + {/key} |
| 338 | + {tool.displayName} |
322 | 339 | </button>
|
| 340 | + {/each} |
| 341 | + <HoverTooltip |
| 342 | + label="Browse more tools" |
| 343 | + position="right" |
| 344 | + TooltipClassNames="text-xs !text-left !w-auto whitespace-nowrap !py-1 max-sm:hidden" |
| 345 | + > |
| 346 | + <a |
| 347 | + class="base-tool flex !size-[20px] items-center justify-center rounded-full border !border-gray-200 !bg-white !transition-none dark:!border-gray-500 dark:!bg-transparent" |
| 348 | + href={`${base}/tools`} |
| 349 | + title="Browse more tools" |
| 350 | + > |
| 351 | + <IconAdd class="text-sm" /> |
| 352 | + </a> |
323 | 353 | </HoverTooltip>
|
324 | 354 | {/if}
|
325 |
| - {/if} |
326 |
| - {#if showExtraTools} |
327 |
| - {#each extraTools as tool} |
328 |
| - <button |
329 |
| - class="active-tool base-tool" |
330 |
| - disabled={loading} |
331 |
| - onclick={async (e) => { |
332 |
| - e.preventDefault(); |
333 |
| - goto(`${base}/tools/${tool._id}`); |
334 |
| - }} |
335 |
| - > |
336 |
| - {#key tool.icon + tool.color} |
337 |
| - <ToolLogo icon={tool.icon} color={tool.color} size="xs" /> |
338 |
| - {/key} |
339 |
| - {tool.displayName} |
340 |
| - </button> |
341 |
| - {/each} |
342 |
| - <HoverTooltip |
343 |
| - label="Browse more tools" |
344 |
| - position="right" |
345 |
| - TooltipClassNames="text-xs !text-left !w-auto whitespace-nowrap !py-1 max-sm:hidden" |
346 |
| - > |
347 |
| - <a |
348 |
| - class="base-tool flex !size-[20px] items-center justify-center rounded-full border !border-gray-200 !bg-white !transition-none dark:!border-gray-500 dark:!bg-transparent" |
349 |
| - href={`${base}/tools`} |
350 |
| - title="Browse more tools" |
351 |
| - > |
352 |
| - <IconAdd class="text-sm" /> |
353 |
| - </a> |
354 |
| - </HoverTooltip> |
355 |
| - {/if} |
356 |
| - </div> |
| 355 | + </div> |
357 | 356 | {/if}
|
358 | 357 | {@render children?.()}
|
359 | 358 | </div>
|
|
0 commit comments