Skip to content

Commit 0338cb6

Browse files
committed
fix: show file picker for assistant with mime types
closes #1652
1 parent 049d32e commit 0338cb6

File tree

1 file changed

+128
-118
lines changed

1 file changed

+128
-118
lines changed

src/lib/components/chat/ChatInput.svelte

Lines changed: 128 additions & 118 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,14 @@
150150
![documentParserToolId, imageGenToolId, webSearchToolId, fetchUrlToolId].includes(t._id)
151151
) satisfies ToolFront[]
152152
);
153+
154+
let showWebSearch = $derived(!assistant);
155+
let showImageGen = $derived(modelHasTools && !assistant);
156+
let showFileUpload = $derived((modelIsMultimodal || modelHasTools) && mimeTypes.length > 0);
157+
let showExtraTools = $derived(modelHasTools && !assistant);
158+
159+
let showNoTools = $derived(!showWebSearch && !showImageGen && !showFileUpload && !showExtraTools);
160+
153161
</script>
154162

155163
<div class="flex min-h-full flex-1 flex-col" onpaste={onPaste}>
@@ -175,10 +183,13 @@
175183
{disabled}
176184
></textarea>
177185

178-
{#if !assistant}
179-
<div
180-
class="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"
181-
>
186+
{#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}
182193
<HoverTooltip
183194
label="Search the web"
184195
position="top"
@@ -215,135 +226,134 @@
215226
{/if}
216227
</button>
217228
</HoverTooltip>
218-
{#if modelHasTools}
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">
276+
<HoverTooltip
277+
label={mimeTypesString.includes("*")
278+
? "Upload any file"
279+
: `Upload ${mimeTypesString} files`}
280+
position="top"
281+
TooltipClassNames="text-xs !text-left !w-auto whitespace-nowrap !py-1 !mb-0 max-sm:hidden"
282+
>
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
295+
{/if}
296+
</label>
297+
</HoverTooltip>
298+
</div>
299+
{#if mimeTypes.includes("image/*")}
219300
<HoverTooltip
220-
label="Generate images"
301+
label="Capture screenshot"
221302
position="top"
222-
TooltipClassNames="text-xs !text-left !w-auto whitespace-nowrap !py-1 !mb-0 max-sm:hidden {imageGenIsOn
223-
? 'hidden'
224-
: ''}"
303+
TooltipClassNames="text-xs !text-left !w-auto whitespace-nowrap !py-1 !mb-0 max-sm:hidden"
225304
>
226305
<button
227306
class="base-tool"
228-
class:active-tool={imageGenIsOn}
229-
disabled={loading}
230307
onclick={async (e) => {
231308
e.preventDefault();
232-
if (modelHasTools) {
233-
if (imageGenIsOn) {
234-
await settings.instantSet({
235-
tools: ($settings.tools ?? []).filter((t) => t !== imageGenToolId),
236-
});
237-
} else {
238-
await settings.instantSet({
239-
tools: [...($settings.tools ?? []), imageGenToolId],
240-
});
241-
}
242-
}
243-
}}
244-
>
245-
<IconImageGen classNames="text-xl" />
246-
{#if imageGenIsOn}
247-
Image Gen
248-
{/if}
249-
</button>
250-
</HoverTooltip>
251-
{/if}
252-
{#if modelIsMultimodal || modelHasTools}
253-
{@const mimeTypesString = mimeTypes
254-
.map((m) => {
255-
// if the mime type ends in *, grab the first part so image/* becomes image
256-
if (m.endsWith("*")) {
257-
return m.split("/")[0];
258-
}
259-
// otherwise, return the second part for example application/pdf becomes pdf
260-
return m.split("/")[1];
261-
})
262-
.join(", ")}
263-
<div class="flex items-center">
264-
<HoverTooltip
265-
label={mimeTypesString.includes("*")
266-
? "Upload any file"
267-
: `Upload ${mimeTypesString} files`}
268-
position="top"
269-
TooltipClassNames="text-xs !text-left !w-auto whitespace-nowrap !py-1 !mb-0 max-sm:hidden"
270-
>
271-
<label class="base-tool relative" class:active-tool={documentParserIsOn}>
272-
<input
273-
disabled={loading}
274-
class="absolute hidden size-0"
275-
aria-label="Upload file"
276-
type="file"
277-
onchange={onFileChange}
278-
accept={mimeTypes.join(",")}
279-
/>
280-
<IconPaperclip classNames="text-xl" />
281-
{#if documentParserIsOn}
282-
Document Parser
283-
{/if}
284-
</label>
285-
</HoverTooltip>
286-
</div>
287-
{#if mimeTypes.includes("image/*")}
288-
<HoverTooltip
289-
label="Capture screenshot"
290-
position="top"
291-
TooltipClassNames="text-xs !text-left !w-auto whitespace-nowrap !py-1 !mb-0 max-sm:hidden"
292-
>
293-
<button
294-
class="base-tool"
295-
onclick={async (e) => {
296-
e.preventDefault();
297-
const screenshot = await captureScreen();
309+
const screenshot = await captureScreen();
298310

299-
// Convert base64 to blob
300-
const base64Response = await fetch(screenshot);
301-
const blob = await base64Response.blob();
311+
// Convert base64 to blob
312+
const base64Response = await fetch(screenshot);
313+
const blob = await base64Response.blob();
302314

303-
// Create a File object from the blob
304-
const file = new File([blob], "screenshot.png", { type: "image/png" });
315+
// Create a File object from the blob
316+
const file = new File([blob], "screenshot.png", { type: "image/png" });
305317

306-
files = [...files, file];
307-
}}
308-
>
309-
<IconScreenshot classNames="text-xl" />
310-
</button>
311-
</HoverTooltip>
312-
{/if}
313-
{/if}
314-
{#if modelHasTools}
315-
{#each extraTools as tool}
316-
<button
317-
class="active-tool base-tool"
318-
disabled={loading}
319-
onclick={async (e) => {
320-
e.preventDefault();
321-
goto(`${base}/tools/${tool._id}`);
318+
files = [...files, file];
322319
}}
323320
>
324-
{#key tool.icon + tool.color}
325-
<ToolLogo icon={tool.icon} color={tool.color} size="xs" />
326-
{/key}
327-
{tool.displayName}
321+
<IconScreenshot classNames="text-xl" />
328322
</button>
329-
{/each}
330-
{/if}
331-
{#if modelHasTools}
332-
<HoverTooltip
333-
label="Browse more tools"
334-
position="right"
335-
TooltipClassNames="text-xs !text-left !w-auto whitespace-nowrap !py-1 max-sm:hidden"
336-
>
337-
<a
338-
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"
339-
href={`${base}/tools`}
340-
title="Browse more tools"
341-
>
342-
<IconAdd class="text-sm" />
343-
</a>
344323
</HoverTooltip>
345324
{/if}
346-
</div>
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>
347357
{/if}
348358
{@render children?.()}
349359
</div>

0 commit comments

Comments
 (0)