enhanced webui

This commit is contained in:
Saifeddine ALOUI 2025-04-01 17:51:06 +02:00
parent 4921568940
commit 9217c26690
7 changed files with 1206 additions and 1196 deletions

File diff suppressed because one or more lines are too long

26
web/dist/assets/index-Bnd2MSQo.css vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

4
web/dist/index.html vendored
View File

@ -6,8 +6,8 @@
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LoLLMS WebUI</title>
<script type="module" crossorigin src="/assets/index-DrzOneYA.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-BOIpO8LK.css">
<script type="module" crossorigin src="/assets/index-Cfxx9YJo.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-Bnd2MSQo.css">
</head>
<body>
<div id="app"></div>

View File

@ -1,416 +1,386 @@
<template>
<!-- Chatbar Container: Fixed, centered, and compact with a modern look -->
<div
class="fixed bottom-8 left-1/2 transform -translate-x-1/2 w-full max-w-2xl p-6 bg-white/95 dark:bg-gray-900/95 backdrop-blur-sm rounded-xl border border-gray-200 dark:border-gray-700 shadow-2xl transition-all duration-300 ease-in-out z-50"
>
<!-- Files Panel (if any files are attached) -->
<div v-if="filesList.length > 0" class="mb-3">
<div class="flex items-center justify-between mb-2">
<button
class="p-2 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg transition-colors"
:title="showfilesList ? 'Hide file list' : 'Show file list'"
@click.stop="showfilesList = !showfilesList"
>
<i data-feather="list" class="w-5 h-5"></i>
</button>
<div class="flex items-center gap-2">
<span class="text-sm" title="Total file size and number of files">
{{ totalSize }} ({{ filesList.length }})
</span>
<button @click="clear_files" class="p-2 hover:text-red-500 transition-colors" title="Clear all files">
<i data-feather="trash" class="w-4 h-4"></i>
</button>
<button @click="download_files" class="p-2 hover:text-primary transition-colors" title="Download all files">
<i data-feather="download-cloud" class="w-4 h-4"></i>
</button>
</div>
</div>
<!-- Expandable files list -->
<TransitionGroup
v-show="showfilesList"
name="list"
tag="div"
class="max-h-40 overflow-y-auto rounded-lg bg-gray-50 dark:bg-gray-800 divide-y divide-gray-200 dark:divide-gray-700"
<!-- Chatbar Container: Fixed, centered, and compact with theme styling -->
<div
class="chatbox-color fixed bottom-8 left-1/2 transform -translate-x-1/2 w-full max-w-2xl p-4 bg-opacity-95 backdrop-blur-sm rounded-xl border border-blue-300 dark:border-blue-600 shadow-xl transition-all duration-300 ease-in-out z-50"
>
<!-- Files Panel (if any files are attached) -->
<div v-if="filesList.length > 0" class="mb-3 border-b border-blue-200 dark:border-blue-700 pb-3">
<div class="flex items-center justify-between mb-2">
<button
class="svg-button"
:title="showfilesList ? 'Hide file list' : 'Show file list'"
@click.stop="showfilesList = !showfilesList"
>
<div
v-for="(file, index) in filesList"
:key="index + '-' + file.name"
class="flex items-center justify-between p-2 group hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"
>
<div class="flex items-center gap-2 min-w-0">
<div v-if="!isFileSentList[index]" class="animate-spin" title="Uploading...">
<i data-feather="loader" class="w-4 h-4 text-secondary"></i>
</div>
<i data-feather="file" class="w-4 h-4 flex-shrink-0" title="File"></i>
<span
class="truncate text-sm"
:class="isFileSentList[index] ? 'text-green-500' : 'text-gray-500'"
:title="file.name"
>
{{ file.name }}
</span>
</div>
<div class="flex items-center gap-2 flex-shrink-0">
<span class="text-xs text-gray-500" :title="computedFileSize(file.size)">
{{ computedFileSize(file.size) }}
</span>
<button
@click="removeItem(file)"
class="opacity-0 group-hover:opacity-100 p-1 hover:text-red-500 transition-all"
title="Remove file"
>
<i data-feather="x" class="w-4 h-4"></i>
</button>
</div>
</div>
</TransitionGroup>
</div>
<!-- Main Chat Input and Actions -->
<div class="flex flex-col gap-2">
<div class="flex flex-row gap-2 w-full">
<!-- Input Box with Integrated Send Buttons -->
<div class="relative flex-grow">
<textarea
id="chat"
:disabled="loading"
v-model="message"
@paste="handlePaste"
@keydown.enter.exact="submitOnEnter($event)"
rows="1"
class="w-full p-3 pr-24 text-sm rounded-lg bg-gray-100 dark:bg-gray-800 focus:ring-2 focus:ring-primary border border-gray-300 dark:border-gray-700 resize-y min-h-[3rem] max-h-32 overflow-auto transition-colors"
placeholder="Write your message to the AI here..."
title="Enter your message here"
></textarea>
<!-- Integrated Send Buttons inside the input box -->
<div class="absolute inset-y-0 right-0 flex items-center pr-2 space-x-1">
<template v-if="loading">
<button
@click="stopGenerating"
class="p-2 bg-red-500 text-white rounded-lg hover:bg-red-600
transform hover:scale-105 active:scale-95
transition-all duration-200 ease-in-out
shadow-md hover:shadow-lg
animate-pulse
focus:outline-none focus:ring-2 focus:ring-red-400
disabled:opacity-50"
title="Stop generating"
aria-label="Stop generation process"
>
<i data-feather="stop-circle"
class="w-5 h-5 animate-spin-slow"
></i>
<span class="sr-only">Stop Generation</span>
</button>
</template> <template v-else>
<button
@click="submit"
class="p-2 hover:bg-gray-200 dark:hover:bg-gray-700 rounded-lg transition-colors"
title="Send message"
>
<i data-feather="send" class="w-5 h-5"></i>
</button>
<button
@click="submitWithInternetSearch"
class="p-2 hover:bg-gray-200 dark:hover:bg-gray-700 rounded-lg transition-colors"
title="Send with internet search"
>
<i data-feather="globe" class="w-5 h-5"></i>
</button>
</template>
</div>
</div>
<i data-feather="list" class="w-5 h-5"></i>
</button>
<div class="flex items-center gap-2">
<span class="text-sm text-blue-600 dark:text-blue-300" title="Total file size and number of files">
{{ totalSize }} ({{ filesList.length }})
</span>
<button @click="clear_files" class="svg-button hover:text-red-500" title="Clear all files">
<i data-feather="trash" class="w-4 h-4"></i>
</button>
<button @click="download_files" class="svg-button hover:text-blue-500" title="Download all files">
<i data-feather="download-cloud" class="w-4 h-4"></i>
</button>
</div>
</div>
<!-- Expandable files list -->
<TransitionGroup
v-show="showfilesList"
name="list"
tag="div"
class="max-h-40 overflow-y-auto rounded-lg bg-blue-100 dark:bg-blue-800 divide-y divide-blue-200 dark:divide-blue-700 scrollbar"
>
<div
v-for="(file, index) in filesList"
:key="index + '-' + file.name"
class="flex items-center justify-between p-2 group hover:bg-blue-200 dark:hover:bg-blue-700 transition-colors"
>
<div class="flex items-center gap-2 min-w-0">
<div v-if="!isFileSentList[index]" class="animate-spin" title="Uploading...">
<i data-feather="loader" class="w-4 h-4 text-blue-500 dark:text-blue-400"></i>
</div>
<i data-feather="file" class="w-4 h-4 flex-shrink-0 text-blue-600 dark:text-blue-300" title="File"></i>
<span
class="truncate text-sm"
:class="isFileSentList[index] ? 'text-green-500 dark:text-green-400' : 'text-blue-700 dark:text-blue-200'"
:title="file.name"
>
{{ file.name }}
</span>
</div>
<div class="flex items-center gap-2 flex-shrink-0">
<span class="text-xs text-blue-500 dark:text-blue-400" :title="computedFileSize(file.size)">
{{ computedFileSize(file.size) }}
</span>
<button
@click="removeItem(file)"
class="svg-button opacity-0 group-hover:opacity-100 hover:text-red-500 transition-all"
title="Remove file"
>
<i data-feather="x" class="w-4 h-4"></i>
</button>
</div>
</div>
</TransitionGroup>
</div>
<!-- Additional Actions Row -->
<div class="flex items-center justify-between relative">
<!-- Left Panel Toggle -->
<button
<!-- Main Chat Input and Actions -->
<div class="flex flex-col gap-2">
<div class="flex flex-row gap-2 w-full">
<!-- Input Box with Integrated Send Buttons -->
<div class="relative flex-grow">
<textarea
id="chat"
:disabled="loading"
v-model="message"
@paste="handlePaste"
@keydown.enter.exact="submitOnEnter($event)"
rows="1"
class="input w-full p-3 pr-24 text-sm rounded-lg focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400 border-blue-300 dark:border-blue-600 resize-y min-h-[3rem] max-h-32 overflow-auto transition-colors scrollbar text-blue-900 dark:text-blue-100 placeholder-blue-400 dark:placeholder-blue-500"
placeholder="Write your message to the AI here..."
title="Enter your message here"
></textarea>
<!-- Integrated Send Buttons inside the input box -->
<div class="absolute inset-y-0 right-0 flex items-center pr-2 space-x-1">
<template v-if="loading">
<button
@click="stopGenerating"
class="btn bg-red-500 text-white hover:bg-red-600 focus:ring-red-400 transform hover:scale-105 active:scale-95 transition-all duration-200 ease-in-out shadow-md hover:shadow-lg animate-pulse p-2"
title="Stop generating"
aria-label="Stop generation process"
>
<i data-feather="stop-circle" class="w-5 h-5 animate-spin-slow"></i>
<span class="sr-only">Stop Generation</span>
</button>
</template>
<template v-else>
<button
@click="submit"
class="svg-button"
title="Send message"
>
<i data-feather="send" class="w-5 h-5"></i>
</button>
<button
@click="submitWithInternetSearch"
class="svg-button"
title="Send with internet search"
>
<i data-feather="globe" class="w-5 h-5"></i>
</button>
</template>
</div>
</div>
</div>
<!-- Additional Actions Row -->
<div class="flex items-center justify-between relative">
<!-- Left Panel Toggle -->
<button
@click="toggleLeftPanel"
class="p-2 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg transition-colors"
:class="$store.state.leftPanelCollapsed ? '' : 'bg-gray-300'"
class="svg-button"
:class="$store.state.leftPanelCollapsed ? '' : 'bg-blue-300 dark:bg-blue-700'"
:title="$store.state.leftPanelCollapsed ? 'Expand Left Panel' : 'Collapse Left Panel'"
>
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<!-- Main vertical bar with rounded corners -->
>
<!-- Custom Icon for Left Panel Toggle -->
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5">
<rect x="3" y="2" width="4" height="20" rx="2" fill="currentColor"/>
<!-- Three horizontal lines with rounded corners -->
<rect x="9" y="6" width="12" height="2" rx="1" fill="currentColor"/>
<rect x="9" y="11" width="12" height="2" rx="1" fill="currentColor"/>
<rect x="9" y="16" width="12" height="2" rx="1" fill="currentColor"/>
</svg>
</button>
<!-- Left Side: Personalities / Commands -->
<div class="flex items-center gap-1">
<!-- Assuming PersonalitiesCommands has internal styling -->
<PersonalitiesCommands
v-if="isCommandsValid"
:help="'Personality commands'"
:commandsList="$store.state.mountedPersArr[$store.state.config.active_personality_id].commands"
:sendCommand="sendCMDEvent"
:on-show-toast-message="onShowToastMessage"
ref="personalityCMD"
/>
<PersonalitiesCommands
v-if="isdataLakeNamesValid"
:help="'Datalakes'"
icon="feather:book"
:commandsList="dataLakeNames"
:sendCommand="mountDB"
:on-show-toast-message="onShowToastMessage"
ref="databasesList"
/>
<PersonalitiesCommands
v-if="$store.state.config.mounted_function_calls.length>0"
icon="feather:zap"
:help="'Function calls (WIP)'"
:commandsList="functionCalls"
:sendCommand="toggleFunctionCall"
:showSettings="showFunctionSettings"
:on-show-toast-message="onShowToastMessage"
ref="functioncalls"
/>
</div>
<!-- Central Buttons: Modes -->
<div class="flex items-center gap-1">
<button
@click="toggleThinkFirstMode"
class="svg-button"
:class="{ 'text-blue-600 dark:text-blue-400 bg-blue-200 dark:bg-blue-700': $store.state.config.think_first_mode }"
title="Toggle Think First Mode"
>
<!-- Bulb Icon -->
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 2a6 6 0 0 1 6 6c0 2.42-1.61 4.5-4 5.25V15a2 2 0 0 1-4 0v-1.75C7.61 12.5 6 10.42 6 8a6 6 0 0 1 6-6z" />
<path d="M9 18h6" /> <path d="M10 22h4" />
</svg>
</button>
<button
@click="toggleFunMode"
class="svg-button"
:class="{ 'text-blue-600 dark:text-blue-400 bg-blue-200 dark:bg-blue-700': $store.state.config.fun_mode }"
title="Toggle Fun Mode"
>
<!-- Smiley Icon -->
<svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10" /> <path d="M8 14s1.5 2 4 2 4-2 4-2M9 9h.01M15 9h.01" />
</svg>
</button>
</div>
<!-- Right Side: Additional Options -->
<div class="flex items-center gap-1">
<button
@click="startSpeechRecognition"
class="svg-button"
:class="{ 'text-red-500 dark:text-red-400 animate-pulse': isListeningToVoice }"
title="Voice input"
>
<i data-feather="mic" class="w-5 h-5"></i>
</button>
<!-- Left Side: Personalities / Commands -->
<div class="flex items-center gap-2">
<PersonalitiesCommands
v-if="isCommandsValid"
:help="'Personality commands'"
:commandsList="$store.state.mountedPersArr[$store.state.config.active_personality_id].commands"
:sendCommand="sendCMDEvent"
:on-show-toast-message="onShowToastMessage"
ref="personalityCMD"
/>
<PersonalitiesCommands
v-if="isdataLakeNamesValid"
:help="'Datalakes'"
icon="feather:book"
:commandsList="dataLakeNames"
:sendCommand="mountDB"
:on-show-toast-message="onShowToastMessage"
ref="databasesList"
/>
<PersonalitiesCommands
v-if="$store.state.config.mounted_function_calls.length>0"
icon="feather:zap"
:help="'Function calls (WIP)'"
:commandsList="functionCalls"
:sendCommand="toggleFunctionCall"
:showSettings="showFunctionSettings"
:on-show-toast-message="onShowToastMessage"
ref="functioncalls"
/>
</div>
<!-- Right Side: Additional Options -->
<!-- Think First Mode Toggle -->
<button
@click="toggleThinkFirstMode"
class="p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors"
:class="{ 'text-primary': $store.state.config.think_first_mode }"
title="Toggle Think First Mode"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="w-5 h-5"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<!-- Bulb outline with filament -->
<path d="M12 2a6 6 0 0 1 6 6c0 2.42-1.61 4.5-4 5.25V15a2 2 0 0 1-4 0v-1.75C7.61 12.5 6 10.42 6 8a6 6 0 0 1 6-6z" />
<!-- Bulb base -->
<path d="M9 18h6" />
<path d="M10 22h4" />
</svg>
</button>
<!-- Fun Mode Toggle -->
<button
@click="toggleFunMode"
class="p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors"
:class="{ 'text-primary': $store.state.config.fun_mode }"
title="Toggle Fun Mode"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="w-5 h-5"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<!-- Smiley face icon for Fun Mode -->
<path d="M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20z" />
<!-- Smiley face icon -->
<circle cx="12" cy="12" r="10" />
<path d="M8 14s1.5 2 4 2 4-2 4-2M9 9h.01M15 9h.01" />
</svg>
</button>
<div class="flex items-center gap-2">
<button
@click="startSpeechRecognition"
class="p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors"
:class="{ 'text-red-500': isListeningToVoice }"
title="Voice input"
>
<i data-feather="mic" class="w-5 h-5"></i>
</button>
<button
v-if="$store.state.config.active_tts_service !== 'None' && $store.state.config.active_tts_service && $store.state.config.active_stt_service !== 'None'"
@click="updateRT"
class="p-2 rounded-lg transition-colors"
:class="is_rt ? 'bg-red-500 text-white' : 'bg-green-500 text-white'"
title="Toggle real-time audio mode"
>
🌟
</button>
<!-- More Actions Dropdown Trigger -->
<button
@click="toggleSendMenu"
class="p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors"
title="More actions (Add file, take picture, etc.)"
>
<i data-feather="plus-circle" class="w-5 h-5"></i>
</button>
<!-- Dropdown Menu for Extra Actions -->
<div
v-show="isSendMenuVisible"
class="absolute right-0 bottom-full mb-12 w-48 bg-white dark:bg-gray-900 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 z-10"
>
<div class="p-2 space-y-1">
<button
@click="add_file"
class="w-full p-2 flex items-center gap-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
title="Add a file"
>
<i data-feather="file-plus" class="w-4 h-4"></i>
<span>Add File</span>
</button>
<button
@click="takePicture"
class="w-full p-2 flex items-center gap-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
title="Take a picture"
>
<i data-feather="camera" class="w-4 h-4"></i>
<span>Take Picture</span>
</button>
<button
@click="addWebLink"
class="w-full p-2 flex items-center gap-2 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
title="Add a web link"
>
<i data-feather="link" class="w-4 h-4"></i>
<span>Add Web Link</span>
</button>
</div>
<button
v-if="$store.state.config.active_tts_service !== 'None' && $store.state.config.active_tts_service && $store.state.config.active_stt_service !== 'None'"
@click="updateRT"
class="btn btn-sm p-1.5"
:class="is_rt ? 'bg-red-500 hover:bg-red-600 text-white' : 'bg-green-500 hover:bg-green-600 text-white'"
title="Toggle real-time audio mode"
>
<span class="text-xs font-bold">RT</span> <!-- Using text instead of emoji for better control -->
</button>
<!-- More Actions Dropdown Trigger -->
<button
@click="toggleSendMenu"
class="svg-button"
title="More actions (Add file, take picture, etc.)"
>
<i data-feather="plus-circle" class="w-5 h-5"></i>
</button>
<!-- Dropdown Menu for Extra Actions -->
<div
v-show="isSendMenuVisible"
class="absolute right-0 bottom-full mb-2 w-48 bg-blue-100 dark:bg-blue-800 rounded-lg shadow-lg border border-blue-300 dark:border-blue-600 z-10"
@mouseleave="isSendMenuVisible=false"
>
<div class="p-2 space-y-1">
<button
@click="add_file"
class="w-full p-2 flex items-center gap-2 rounded-lg hover:bg-blue-200 dark:hover:bg-blue-700 transition-colors text-blue-700 dark:text-blue-200"
title="Add a file"
>
<i data-feather="file-plus" class="w-4 h-4"></i>
<span class="text-sm">Add File</span>
</button>
<button
@click="takePicture"
class="w-full p-2 flex items-center gap-2 rounded-lg hover:bg-blue-200 dark:hover:bg-blue-700 transition-colors text-blue-700 dark:text-blue-200"
title="Take a picture"
>
<i data-feather="camera" class="w-4 h-4"></i>
<span class="text-sm">Take Picture</span>
</button>
<button
@click="addWebLink"
class="w-full p-2 flex items-center gap-2 rounded-lg hover:bg-blue-200 dark:hover:bg-blue-700 transition-colors text-blue-700 dark:text-blue-200"
title="Add a web link"
>
<i data-feather="link" class="w-4 h-4"></i>
<span class="text-sm">Add Web Link</span>
</button>
</div>
<button
@click="makeAnEmptyUserMessage"
class="p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors"
title="Insert an empty user message"
>
<i data-feather="message-circle" class="w-5 h-5"></i>
</button>
<button
@click="makeAnEmptyAIMessage"
class="p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors text-red-400"
title="Insert an empty AI message"
>
<i data-feather="cpu" class="w-5 h-5"></i>
</button>
</div>
<!-- Right Panel Toggle -->
<button
@click="toggleRightPanel"
class="p-2 hover:bg-gray-100 dark:hover:bg-gray-800 rounded-lg transition-colors"
:class="$store.state.rightPanelCollapsed ? '' : 'bg-gray-300'"
:title="$store.state.rightPanelCollapsed ? 'Expand Right Panel' : 'Collapse Right Panel'"
>
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<!-- Main vertical bar with rounded corners (right side) -->
<rect x="17" y="2" width="4" height="20" rx="2" fill="currentColor"/>
<!-- Three horizontal lines with rounded corners -->
<rect x="3" y="6" width="12" height="2" rx="1" fill="currentColor"/>
<rect x="3" y="11" width="12" height="2" rx="1" fill="currentColor"/>
<rect x="3" y="16" width="12" height="2" rx="1" fill="currentColor"/>
</svg>
</button>
</div>
</div>
</div>
<!-- Hidden File Input -->
<input type="file" ref="fileDialog" @change="addFiles" multiple class="hidden" />
<!-- Tutorial Help Overlay -->
<div v-if="showHelpModal" class="fixed inset-0 z-50 flex items-center justify-center bg-black/70">
<div class="bg-white dark:bg-gray-900 rounded-lg p-6 max-w-xl w-full relative overflow-y-auto max-h-[80vh]">
<h2 class="text-2xl font-bold mb-4 text-center">Tutorial</h2>
<button
@click="toggleHelpModal"
class="absolute top-2 right-2 text-gray-500 hover:text-gray-700 dark:hover:text-gray-300"
title="Close tutorial"
>
<i data-feather="x" class="w-6 h-6"></i>
</button>
<p class="mb-4 text-center">Below is an overview of the chatbar buttons and what they do.</p>
<div class="space-y-4">
<div class="flex items-center gap-3">
<button class="p-2 bg-primary text-white rounded-lg">
<i data-feather="send" class="w-5 h-5"></i>
</button>
<span>Sends your message to the AI.</span>
</div>
<div class="flex items-center gap-3">
<button class="p-2 bg-gray-50 rounded-lg border">
<i data-feather="globe" class="w-5 h-5"></i>
</button>
<span>Sends your message with an internet search.</span>
</div>
<div class="flex items-center gap-3">
<button class="p-2 hover:bg-gray-100 rounded-lg">
<i data-feather="mic" class="w-5 h-5"></i>
</button>
<span>Activates voice input.</span>
</div>
<div class="flex items-center gap-3">
<button class="p-2 bg-green-500 text-white rounded-lg">
<span>🌟</span>
</button>
<span>Toggles real-time audio mode.</span>
</div>
<div class="flex items-center gap-3">
<button class="p-2 hover:bg-gray-100 rounded-lg">
<i data-feather="plus-circle" class="w-5 h-5"></i>
</button>
<span>Opens more actions (Add File, Take Picture, Add Web Link).</span>
</div>
<div class="flex items-center gap-3">
<button class="p-2 hover:bg-gray-100 rounded-lg">
<button
@click="makeAnEmptyUserMessage"
class="svg-button"
title="Insert an empty user message"
>
<i data-feather="message-circle" class="w-5 h-5"></i>
</button>
<button
@click="makeAnEmptyAIMessage"
class="svg-button text-red-400 hover:text-red-500"
title="Insert an empty AI message"
>
<i data-feather="cpu" class="w-5 h-5"></i>
</button>
<button @click="toggleHelpModal" class="svg-button" title="Show Help">
<i data-feather="info" class="w-5 h-5"></i>
</button>
<span>Opens this tutorial overlay.</span>
</div>
<div class="flex items-center gap-3">
<button class="p-2 hover:bg-gray-100 rounded-lg">
<i data-feather="message-circle" class="w-5 h-5"></i>
</button>
<span>Inserts an empty user message.</span>
</div>
<div class="flex items-center gap-3">
<button class="p-2 hover:bg-gray-100 rounded-lg text-red-400">
<i data-feather="cpu" class="w-5 h-5"></i>
</button>
<span>Inserts an empty AI message.</span>
</div>
<div class="flex items-center gap-3">
<button class="p-2 hover:bg-gray-100 rounded-lg">
<i data-feather="chevron-left" class="w-5 h-5"></i>
</button>
<span>Toggles the left panel.</span>
</div>
<div class="flex items-center gap-3">
<button class="p-2 hover:bg-gray-100 rounded-lg">
<i data-feather="chevron-right" class="w-5 h-5"></i>
</button>
<span>Toggles the right panel.</span>
</div>
</button>
</div>
<button
@click="toggleHelpModal"
class="absolute top-2 right-2 text-gray-500 hover:text-gray-700 dark:hover:text-gray-300"
title="Close tutorial"
>
<i data-feather="x" class="w-6 h-6"></i>
</button>
<!-- Right Panel Toggle -->
<button
@click="toggleRightPanel"
class="svg-button"
:class="$store.state.rightPanelCollapsed ? '' : 'bg-blue-300 dark:bg-blue-700'"
:title="$store.state.rightPanelCollapsed ? 'Expand Right Panel' : 'Collapse Right Panel'"
>
<!-- Custom Icon for Right Panel Toggle -->
<svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5">
<rect x="17" y="2" width="4" height="20" rx="2" fill="currentColor"/>
<rect x="3" y="6" width="12" height="2" rx="1" fill="currentColor"/>
<rect x="3" y="11" width="12" height="2" rx="1" fill="currentColor"/>
<rect x="3" y="16" width="12" height="2" rx="1" fill="currentColor"/>
</svg>
</button>
</div>
</div>
</template>
</div>
<!-- Hidden File Input -->
<input type="file" ref="fileDialog" @change="addFiles" multiple class="hidden" />
<!-- Tutorial Help Overlay -->
<div v-if="showHelpModal" class="fixed inset-0 z-[100] flex items-center justify-center bg-black/70 backdrop-blur-sm">
<div class="card max-w-xl w-full relative overflow-y-auto max-h-[80vh] scrollbar">
<button
@click="toggleHelpModal"
class="svg-button absolute top-3 right-3 z-10"
title="Close tutorial"
>
<i data-feather="x" class="w-6 h-6"></i>
</button>
<h2 class="text-2xl font-bold mb-4 text-center text-blue-700 dark:text-blue-200 border-b border-blue-300 dark:border-blue-600 pb-2">Chatbar Help</h2>
<p class="mb-6 text-center text-blue-600 dark:text-blue-300">Overview of the chat controls:</p>
<div class="space-y-3">
<!-- Row 1 -->
<div class="grid grid-cols-2 gap-3 items-center">
<div class="flex items-center gap-3 p-2 rounded-lg bg-blue-100 dark:bg-blue-700/50">
<button class="svg-button" disabled><i data-feather="send" class="w-5 h-5"></i></button>
<span class="text-sm text-blue-700 dark:text-blue-200">Sends your message to the AI.</span>
</div>
<div class="flex items-center gap-3 p-2 rounded-lg bg-blue-100 dark:bg-blue-700/50">
<button class="svg-button" disabled><i data-feather="globe" class="w-5 h-5"></i></button>
<span class="text-sm text-blue-700 dark:text-blue-200">Sends your message with internet search.</span>
</div>
</div>
<!-- Row 2 -->
<div class="grid grid-cols-2 gap-3 items-center">
<div class="flex items-center gap-3 p-2 rounded-lg bg-blue-100 dark:bg-blue-700/50">
<button class="svg-button" disabled><svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5"><rect x="3" y="2" width="4" height="20" rx="2" fill="currentColor"/><rect x="9" y="6" width="12" height="2" rx="1" fill="currentColor"/><rect x="9" y="11" width="12" height="2" rx="1" fill="currentColor"/><rect x="9" y="16" width="12" height="2" rx="1" fill="currentColor"/></svg></button>
<span class="text-sm text-blue-700 dark:text-blue-200">Toggles the left panel.</span>
</div>
<div class="flex items-center gap-3 p-2 rounded-lg bg-blue-100 dark:bg-blue-700/50">
<button class="svg-button" disabled><svg width="24" height="24" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" class="w-5 h-5"><rect x="17" y="2" width="4" height="20" rx="2" fill="currentColor"/><rect x="3" y="6" width="12" height="2" rx="1" fill="currentColor"/><rect x="3" y="11" width="12" height="2" rx="1" fill="currentColor"/><rect x="3" y="16" width="12" height="2" rx="1" fill="currentColor"/></svg></button>
<span class="text-sm text-blue-700 dark:text-blue-200">Toggles the right panel.</span>
</div>
</div>
<!-- Row 3 -->
<div class="grid grid-cols-2 gap-3 items-center">
<div class="flex items-center gap-3 p-2 rounded-lg bg-blue-100 dark:bg-blue-700/50">
<button class="svg-button" disabled><svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a6 6 0 0 1 6 6c0 2.42-1.61 4.5-4 5.25V15a2 2 0 0 1-4 0v-1.75C7.61 12.5 6 10.42 6 8a6 6 0 0 1 6-6z" /><path d="M9 18h6" /> <path d="M10 22h4" /></svg></button>
<span class="text-sm text-blue-700 dark:text-blue-200">Toggle 'Think First' mode.</span>
</div>
<div class="flex items-center gap-3 p-2 rounded-lg bg-blue-100 dark:bg-blue-700/50">
<button class="svg-button" disabled><svg xmlns="http://www.w3.org/2000/svg" class="w-5 h-5" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10" /> <path d="M8 14s1.5 2 4 2 4-2 4-2M9 9h.01M15 9h.01" /></svg></button>
<span class="text-sm text-blue-700 dark:text-blue-200">Toggle 'Fun' mode.</span>
</div>
</div>
<!-- Row 4 -->
<div class="grid grid-cols-2 gap-3 items-center">
<div class="flex items-center gap-3 p-2 rounded-lg bg-blue-100 dark:bg-blue-700/50">
<button class="svg-button" disabled><i data-feather="mic" class="w-5 h-5"></i></button>
<span class="text-sm text-blue-700 dark:text-blue-200">Activates voice input.</span>
</div>
<div class="flex items-center gap-3 p-2 rounded-lg bg-blue-100 dark:bg-blue-700/50">
<button class="btn btn-sm p-1.5 bg-green-500 text-white" disabled><span class="text-xs font-bold">RT</span></button>
<span class="text-sm text-blue-700 dark:text-blue-200">Toggles real-time audio mode.</span>
</div>
</div>
<!-- Row 5 -->
<div class="grid grid-cols-2 gap-3 items-center">
<div class="flex items-center gap-3 p-2 rounded-lg bg-blue-100 dark:bg-blue-700/50">
<button class="svg-button" disabled><i data-feather="plus-circle" class="w-5 h-5"></i></button>
<span class="text-sm text-blue-700 dark:text-blue-200">More actions (Add File, etc.).</span>
</div>
<div class="flex items-center gap-3 p-2 rounded-lg bg-blue-100 dark:bg-blue-700/50">
<button class="svg-button" disabled><i data-feather="message-circle" class="w-5 h-5"></i></button>
<span class="text-sm text-blue-700 dark:text-blue-200">Inserts empty user message.</span>
</div>
</div>
<!-- Row 6 -->
<div class="grid grid-cols-2 gap-3 items-center">
<div class="flex items-center gap-3 p-2 rounded-lg bg-blue-100 dark:bg-blue-700/50">
<button class="svg-button text-red-400" disabled><i data-feather="cpu" class="w-5 h-5"></i></button>
<span class="text-sm text-blue-700 dark:text-blue-200">Inserts empty AI message.</span>
</div>
<div class="flex items-center gap-3 p-2 rounded-lg bg-blue-100 dark:bg-blue-700/50">
<button class="svg-button" disabled><i data-feather="info" class="w-5 h-5"></i></button>
<span class="text-sm text-blue-700 dark:text-blue-200">Shows this help information.</span>
</div>
</div>
</div>
</div>
</div>
</template>
<style scoped>

File diff suppressed because it is too large Load Diff

View File

@ -1,142 +1,165 @@
<!-- ToolbarButton.vue -->
<template>
<button
class="text-lg hover:text-secondary duration-75 active:scale-90 p-2 cursor-pointer"
:title="title"
@click="emit_click"
>
<svg class="w-6 h-6" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round">
<g v-html="iconPath"></g>
</svg>
</button>
</template>
<script>
export default {
props: {
icon: {
type: String,
required: true
},
title: {
type: String,
required: true
}
<button
class="svg-button"
:title="title"
@click="emit_click"
>
<svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<g v-html="iconPath"></g>
</svg>
</button>
</template>
<script>
export default {
name: 'ToolbarButton',
emits: ['click'],
props: {
icon: {
type: String,
required: true
},
computed: {
iconPath() {
return this.getIconPath()
}
},
methods: {
emit_click(){
console.log("emitting click")
this.$emit('click')
},
getIconPath() {
// Your switch statement remains the same
switch (this.icon) {
case 'x':
return '<path d="M18 6L6 18M6 6l12 12"/>'
case 'check':
return '<path d="M20 6L9 17l-5-5"/>'
case 'code':
return '<path d="M8 7l-5 5 5 5"/><path d="M16 7l5 5-5 5"/><line x1="10" y1="4" x2="14" y2="20"/>'
case 'python':
return '<path d="M12 2C5.4 2 6 4.5 6 4.5v2.8h6.5v1H4.5S2 7.7 2 12c0 4.3 2.5 4.2 2.5 4.2h2.3v-2.5c0-2.5 2.2-2.5 2.2-2.5h6.1c2.3 0 2.2-2.2 2.2-2.2V5c0-2.8-2.9-3-5.8-3zm-3.5 1.7c.5 0 1 .4 1 1s-.4 1-1 1-1-.4-1-1 .4-1 1-1z" fill="currentColor"/><path d="M12 22c6.6 0 6-2.5 6-2.5v-2.8h-6.5v-1H19.5s2.5-.6 2.5-4.9c0-4.3-2.5-4.2-2.5-4.2h-2.3v2.5c0 2.5-2.2 2.5-2.2 2.5H8.9c-2.3 0-2.2 2.2-2.2 2.2V19c0 2.8 2.9 3 5.8 3zm3.5-1.7c-.5 0-1-.4-1-1s.4-1 1-1 1 .4 1 1-.4 1-1 1z" fill="currentColor"/>'
case 'js':
return '<text x="12" y="17" font-family="Arial, sans-serif" font-size="14" font-weight="bold" text-anchor="middle" fill="currentColor">JS</text>'
case 'typescript':
return '<text x="12" y="17" font-family="Arial, sans-serif" font-size="14" font-weight="bold" text-anchor="middle" fill="currentColor">TS</text>'
case 'braces':
return '<path d="M7 7H4a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h3m10-10h3a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2h-3"/>'
case 'cplusplus':
case 'c++':
return '<text x="12" y="17" font-family="Arial, sans-serif" font-size="10" font-weight="bold" text-anchor="middle" fill="currentColor">C++</text>'
case 'csharp':
return '<text x="12" y="17" font-family="Arial, sans-serif" font-size="14" font-weight="bold" text-anchor="middle" fill="currentColor">C#</text>'
case 'go':
return '<text x="12" y="17" font-family="Arial, sans-serif" font-size="14" font-weight="bold" text-anchor="middle" fill="currentColor">Go</text>'
case 'r-project':
return '<text x="12" y="17" font-family="Arial, sans-serif" font-size="16" font-weight="bold" text-anchor="middle" fill="currentColor">R</text>'
case 'rust':
return '<path d="M12 2L2 7v10l10 5 10-5V7L12 2z M12 22v-6 M22 7l-10 5-10-5 M2 17l10-5 10 5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>'
case 'swift':
return '<path d="M21 12c0-4.4-3.6-8-8-8s-8 3.6-8 8 3.6 8 8 8c1.4 0 2.8-.4 4-1 M14 9c-1.7-1.7-3.7-2.6-5.5-2.5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>'
case 'kotlin':
return '<path d="M2 2v20h20L2 2z M2 22L22 2" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>'
case 'java':
return '<path fill="currentColor" d="M8.5 18.5c0 0-1.3.7.9 1 2.7.3 4.1.3 7-.3 0 0 .8.5 1.9.9-6.7 2.9-15.2-.7-9.8-1.6zm-1-3.5c0 0-1.5 1.1.8 1.3 2.9.3 5.2.3 9.1-.4 0 0 .6.6 1.4.9-7.9 2.3-16.7.2-11.3-1.8zm9.2-6.5c1.7 1.9-.4 3.6-.4 3.6s4.2-2.2 2.3-4.9c-1.8-2.5-3.2-3.8 4.3-8.1 0 0-11.8 3-6.2 9.4zm4.9 12.4c0 0 1 .8-1.1 1.4-3.9 1.2-16.3 1.5-19.7.1-1.2-.5 1.1-1.3 1.8-1.4.8-.2 1.2-.1 1.2-.1-1.4-1-9 1.9-3.8 2.7 14.1 2.3 25.8-1 21.6-2.7zm-15.4-9.3c0 0-6.3 1.5-2.2 2 1.7.2 5.1.2 8.3-.1 2.6-.2 5.2-.7 5.2-.7s-.9.4-1.6.8c-6.4 1.7-18.8.9-15.2-.8 3-1.4 5.5-1.2 5.5-1.2zm12.4 6.9c6.5-3.4 3.5-6.6 1.4-6.2-.5.1-.7.2-.7.2s.2-.3.6-.4c4.2-1.5 7.4 4.3-1.4 6.6 0-.1.1-.1.1-.2z"/>'
title: {
type: String,
required: true
}
},
computed: {
iconPath() {
// Uses feather icons paths/structure where possible
// Custom icons for specific languages/formats
switch (this.icon) {
// Basic Controls
case 'x':
return '<line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="12"></line>';
case 'check':
return '<polyline points="20 6 9 17 4 12"></polyline>';
case 'edit':
return '<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>';
case 'copy':
return '<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>';
case 'trash':
return '<polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>';
case 'plus-square':
return '<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="12" y1="8" x2="12" y2="16"></line><line x1="8" y1="12" x2="16" y2="12"></line>';
case 'send':
return '<line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon>';
case 'globe':
return '<circle cx="12" cy="12" r="10"></circle><line x1="2" y1="12" x2="22" y2="12"></line><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path>';
case 'fast-forward':
return '<polygon points="13 19 22 12 13 5 13 19"></polygon><polygon points="2 19 11 12 2 5 2 19"></polygon>';
case 'refresh-cw': // Used for "sendSimple"
return '<polyline points="23 4 23 10 17 10"></polyline><polyline points="1 20 1 14 7 14"></polyline><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path>';
case 'thumbs-up':
return '<path d="M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3"></path>';
case 'thumbs-down':
return '<path d="M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h3a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2h-3"></path>';
case 'volume-2':
return '<polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon><path d="M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07"></path>';
case 'mic':
return '<path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"></path><path d="M19 10v2a7 7 0 0 1-14 0v-2"></path><line x1="12" y1="19" x2="12" y2="23"></line><line x1="8" y1="23" x2="16" y2="23"></line>';
case 'html5':
return '<path fill="currentColor" d="M3 2l1.5 17L12 22l7.5-3L21 2H3zm4.5 6h9l-.3 3H8.4l.2 2h8.5l-.6 6.5L12 21l-4.5-1.5L7 16h2.2l.2 2.2 2.6.7 2.6-.7.4-3.7H7.3L6.5 8z"/>'
case 'css3':
return '<path d="M4 3l1.5 17L12 22l6.5-2L20 3H4zm3.5 7h9l-.3 3H8.4l.2 2h8l-.5 5.5-3.8 1.3-3.8-1.3-.3-3H5.5l.5 5.5L12 22l6-2 .8-9H7.5l.3-3z" fill="currentColor"/>'
case 'vuejs':
return '<path d="M2 3l10 18L22 3h-4l-6 10.5L6 3H2z" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>'
case 'react':
return '<circle cx="12" cy="12" r="2.5" fill="currentColor"/><path d="M12 9.5c1.8-2.3 3.7-3.8 5.5-3.8 1.3 0 2.4.6 3.1 1.7 1.1 1.9.4 4.5-1.8 7.1M12 14.5c-1.8 2.3-3.7 3.8-5.5 3.8-1.3 0-2.4-.6-3.1-1.7-1.1-1.9-.4-4.5 1.8-7.1M5.5 6.5c1.9-1.1 4.5-.4 7.1 1.8M18.5 17.5c-1.9 1.1-4.5.4-7.1-1.8" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>'
case 'angular':
return '<path d="M12 2L3 6l1.5 13L12 22l7.5-3L21 6L12 2z M12 2v20 M3 6l9 4 M21 6l-9 4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>'
// Code / Language Icons
case 'code':
return '<polyline points="16 18 22 12 16 6"></polyline><polyline points="8 6 2 12 8 18"></polyline>';
case 'python': // Using simplified representation for clarity in small size
return '<path d="M15 9l-2 10m-6-10l2 10" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/><ellipse cx="12" cy="14" rx="8" ry="5" fill="none" stroke="currentColor" stroke-width="2"/><ellipse cx="12" cy="6" rx="4" ry="2.5" fill="currentColor"/>';
case 'js': // Keeping Text for JS/TS/etc. as it's standard
return '<text x="12" y="17" font-family="sans-serif" font-size="12" font-weight="bold" text-anchor="middle" fill="currentColor">JS</text>';
case 'typescript':
return '<text x="12" y="17" font-family="sans-serif" font-size="12" font-weight="bold" text-anchor="middle" fill="currentColor">TS</text>';
case 'java': // Simple representation
return '<path d="M8 8c4 0 4 4 8 4M8 16c4 0 4-4 8-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/><circle cx="12" cy="12" r="9" fill="none" stroke="currentColor" stroke-width="2"/>';
case 'cplusplus':
case 'c++':
return '<text x="12" y="17" font-family="sans-serif" font-size="10" font-weight="bold" text-anchor="middle" fill="currentColor">C++</text>';
case 'csharp':
return '<text x="12" y="17" font-family="sans-serif" font-size="12" font-weight="bold" text-anchor="middle" fill="currentColor">C#</text>';
case 'go':
return '<text x="12" y="17" font-family="sans-serif" font-size="12" font-weight="bold" text-anchor="middle" fill="currentColor">Go</text>';
case 'rust': // Simple 'R' in a cog/gear like shape
return '<circle cx="12" cy="12" r="8" fill="none" stroke="currentColor" stroke-width="2"/><path d="M12 8v8M8 12h8" fill="none" stroke="currentColor" stroke-width="2"/><path d="M12 2l1 4h-2zm6.4 4.4l3 1.8-2.5 3zm-.8 9.2l-3 1.8-1-4zm-9.2.8l-3-1.8 1-4zm-.8-9.2l3-1.8 2.5 3z" fill="currentColor"/>';
case 'swift':
return '<path d="M22 12a10 10 0 11-20 0 10 10 0 0120 0z" fill="none" stroke="currentColor" stroke-width="2"/><path d="M21 10c-2 0-5-1-7-4M3 14c2 0 5 1 7 4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>';
case 'kotlin': // Two triangles forming K-like shape
return '<path d="M2 2 l10 10 L2 22 M 12 12 l10 -10 v 20 l -10 -10" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>';
case 'r-project':
return '<text x="12" y="17" font-family="sans-serif" font-size="14" font-weight="bold" text-anchor="middle" fill="currentColor">R</text>';
case 'xml':
return '<path d="M16 3l-4 18M8 8l-4 4 4 4M16 8l4 4-4 4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>'
// Web Tech
case 'html5':
return '<polygon points="3 2 21 2 19.5 19 12 22 4.5 19 3 2"></polygon><polygon points="12 4 12 20 17.5 18.5 18.5 6 12 4"></polygon><polygon points="12 4 5.5 6 6 10.5 12 10.5 12 4"></polygon><polygon points="12 12.5 6.5 12.5 7 16.5 12 18 12 12.5"></polygon><polygon points="12 12.5 12 10.5 17 10.5 16.5 16.5 12 18 12 12.5"></polygon>'; // Simplified HTML5 logo structure
case 'css3':
return '<polygon points="3 2 21 2 19.5 19 12 22 4.5 19 3 2"></polygon><polygon points="12 4 12 20 17.5 18.5 18.5 6 12 4"></polygon><polygon points="12 7 8.5 7 8 10.5 12 10.5 12 7"></polygon><polygon points="12 12.5 8 12.5 7.5 15.5 12 17 12 12.5"></polygon>'; // Simplified CSS3 logo structure
case 'vuejs': // Simple V shape
return '<path d="M2 3l10 17L22 3H17l-5 10L7 3H2z" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>';
case 'react': // Atom symbol
return '<circle cx="12" cy="12" r="2" fill="none" stroke="currentColor" stroke-width="2"></circle><ellipse cx="12" cy="12" rx="10" ry="4" stroke="currentColor" stroke-width="2" fill="none"></ellipse><ellipse cx="12" cy="12" rx="4" ry="10" transform="rotate(60 12 12)" stroke="currentColor" stroke-width="2" fill="none"></ellipse><ellipse cx="12" cy="12" rx="4" ry="10" transform="rotate(-60 12 12)" stroke="currentColor" stroke-width="2" fill="none"></ellipse>';
case 'angular': // Shield with A
return '<polygon points="12 2 2 7 4 21 12 23 20 21 22 7 12 2"></polygon><polygon points="12 4 18 7 16 18 12 20 8 18 6 7 12 4"></polygon><polygon points="12 6 8 14 10 14 11 11.5 13 11.5 14 14 16 14 12 6"></polygon>';
case 'json':
return '<path d="M4 6h16M4 10h16M4 14h10M4 18h6" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><circle cx="18" cy="18" r="2" fill="currentColor"/>'
case 'yaml':
return '<path d="M3 6h18M3 10h18M3 14l4 4M11 14l-4 4M17 14l4 4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>'
case 'markdown':
return '<path d="M3 3h18v18H3zM5 7v10M9 7v10M9 12l3-3 3 3M15 7l4 5-4 5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>'
case 'latex':
return '<text x="12" y="17" font-family="Times New Roman, serif" font-size="14" font-style="italic" font-weight="bold" text-anchor="middle" fill="currentColor">TEX</text>'
// Markup/Data
case 'xml':
return '<polyline points="16 18 22 12 16 6"></polyline><polyline points="8 6 2 12 8 18"></polyline><line x1="4.5" y1="12" x2="19.5" y2="12"></line>'; // Simplified tags
case 'json': // Braces icon
return '<path d="M8 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h1m8-18h1a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-1M12 8a2 2 0 0 1-2-2V3m4 5a2 2 0 0 0 2-2V3"></path>';
case 'yaml': // Hyphen list items
return '<line x1="5" y1="6" x2="19" y2="6"></line><line x1="5" y1="12" x2="19" y2="12"></line><line x1="5" y1="18" x2="19" y2="18"></line><line x1="9" y1="6" x2="9" y2="18"></line>';
case 'markdown': // 'M' down arrow
return '<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line>';
case 'latex': // Simple TeX representation
return '<text x="12" y="17" font-family="serif" font-size="12" font-style="italic" text-anchor="middle" fill="currentColor">TeX</text>';
case 'bash':
return '<path d="M3 3h18v18H3zM6 7l4 4-4 4M12 15h6" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>'
// Scripting/Shell
case 'terminal':
return '<polyline points="4 17 10 11 4 5"></polyline><line x1="12" y1="19" x2="20" y2="19"></line>';
case 'bash': // Terminal icon
return '<polyline points="4 17 10 11 4 5"></polyline><line x1="12" y1="19" x2="20" y2="19"></line>';
case 'powershell': // Terminal icon with >_ prompt stylized
return '<polyline points="4 17 10 11 4 5"></polyline><line x1="12" y1="19" x2="20" y2="19"></line><polyline points="13 15 15 17 13 19" stroke-width="1.5"></polyline>';
case 'perl': // Camel icon (often associated with Perl)
return '<path d="M17 16c-2 2-4 2-5 0M7 16c2 2 4 2 5 0M12 14v4M9 10h6M10 20a6 6 0 0 1-6-6V9a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v5a6 6 0 0 1-6 6z"></path>';
case 'powershell':
return '<path d="M3 3h18v18H3zM6 7l6 5-6 5M13 17h5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>'
// Diagramming
case 'mermaid': // Abstract flow chart
return '<rect x="3" y="3" width="7" height="7" rx="1"></rect><rect x="14" y="3" width="7" height="7" rx="1"></rect><rect x="3" y="14" width="7" height="7" rx="1"></rect><rect x="14" y="14" width="7" height="7" rx="1"></rect><line x1="7" y1="10" x2="7" y2="14"></line><line x1="17" y1="10" x2="17" y2="14"></line><line x1="10" y1="7" x2="14" y2="7"></line><line x1="10" y1="17" x2="14" y2="17"></line>';
case 'graphviz': // Simple directed graph
return '<circle cx="6" cy="6" r="3"></circle><circle cx="18" cy="18" r="3"></circle><line x1="8" y1="8" x2="16" y2="16"></line><polyline points="12 16 16 16 16 12"></polyline>';
case 'plantuml': // UML class box style
return '<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><line x1="3" y1="9" x2="21" y2="9"></line><line x1="3" y1="15" x2="21" y2="15"></line>';
case 'perl':
return '<path d="M12 2C6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10S17.523 2 12 2z" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M12 7v10M7 12h10" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>'
// Database
case 'database':
return '<ellipse cx="12" cy="5" rx="9" ry="3"></ellipse><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"></path><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"></path>';
case 'sql': // Database symbol
return '<ellipse cx="12" cy="5" rx="9" ry="3"></ellipse><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"></path><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"></path>';
case 'mongodb': // Leaf icon
return '<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8c2.38 0 4.53.97 6.08 2.56L12 12l6.08 6.44C16.53 19.03 14.38 20 12 20z" fill="currentColor"></path>';
case 'mermaid':
return '<circle cx="5" cy="12" r="3" fill="currentColor"/><circle cx="19" cy="12" r="3" fill="currentColor"/><circle cx="12" cy="5" r="3" fill="currentColor"/><circle cx="12" cy="19" r="3" fill="currentColor"/><path d="M5 12h14M12 5v14" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>'
case 'graphviz':
return '<circle cx="6" cy="6" r="3" fill="currentColor"/><circle cx="18" cy="6" r="3" fill="currentColor"/><circle cx="6" cy="18" r="3" fill="currentColor"/><circle cx="18" cy="18" r="3" fill="currentColor"/><path d="M6 9v6M18 9v6M9 6h6M9 18h6" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>'
case 'plantuml':
return '<path d="M3 3h18v18H3z" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M7 7h10v4H7zM7 15h10M12 11v7" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>'
case 'sql':
return '<path d="M3 5h18M3 10h18M3 15h18M3 20h18M7 5v15M17 5v15" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>'
case 'mongodb':
return '<path d="M12 2v20M12 2c-3.5 0-7 3-7 8s3.5 8 7 8 7-3 7-8-3.5-8-7-8z" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><path d="M12 18c1.5-1 2-2.5 2-4" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>'
case 'mathFunction':
return '<path d="M3 20h18M3 4v16M3 12h4c3 0 4-3 4-3s1 6 3 6h7" fill="none" stroke="currentColor" stroke-width="2"/>'
case 'terminal':
return '<rect x="2" y="4" width="20" height="16" rx="2" ry="2" fill="none"/><path d="M6 8l4 4-4 4M12 16h6"/>'
case 'edit':
return '<path d="M17 3a2.85 2.85 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z"/>'
case 'copy':
return '<rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>'
case 'send':
return '<path d="M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z"/>'
case 'globe':
return '<circle cx="12" cy="12" r="10"/><path d="M2 12h20M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/>'
case 'fastForward':
return '<polygon points="13 19 22 12 13 5 13 19"/><polygon points="2 19 11 12 2 5 2 19"/>'
case 'sendSimple':
return '<path d="M22 2L11 13M22 2l-7 20-4-9"/>'
default:
return ''
}
// Other generic icons used in the parent context
case 'chrome': // Representing Web
return '<circle cx="12" cy="12" r="10"></circle><circle cx="12" cy="12" r="4"></circle><line x1="21.17" y1="8" x2="12" y2="8"></line><line x1="3.95" y1="6.06" x2="8.54" y2="14"></line><line x1="10.88" y1="21.94" x2="15.46" y2="14"></line>';
case 'file-text': // Representing Markup/Data
return '<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline>';
case 'git-branch': // Representing Diagramming/Flow
return '<line x1="6" y1="3" x2="6" y2="15"></line><circle cx="18" cy="6" r="3"></circle><circle cx="6" cy="18" r="3"></circle><path d="M18 9a9 9 0 0 1-9 9"></path>';
default: // Fallback to a simple circle or question mark if icon not found
console.warn(`ToolbarButton: Icon "${this.icon}" not found.`);
return '<circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line>'; // Question mark in circle
}
}
},
methods: {
emit_click(event){
console.log("ToolbarButton clicked:", this.title);
this.$emit('click', event); // Pass the event object
}
}
</script>
}
</script>
<style scoped>
/* Scoped styles if needed, but svg-button from the theme should handle most styling */
</style>