Skip to content

Commit 071ce5c

Browse files
authored
Merge pull request #26 from beekeeper-studio/feat/editable-query
Editable query
2 parents 95cada3 + 6693feb commit 071ce5c

File tree

18 files changed

+1798
-238
lines changed

18 files changed

+1798
-238
lines changed

index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<meta charset="UTF-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1.0">
66
<title>AI Shell Plugin</title>
7-
<style id="injected-style"></style>
7+
<style id="app-theme"></style>
88
</head>
99
<body>
1010
<div id="app"></div>

package.json

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,28 @@
1616
},
1717
"dependencies": {
1818
"@ai-sdk/amazon-bedrock": "^3.0.33",
19-
"@ai-sdk/anthropic": "^2.0.25",
19+
"@ai-sdk/anthropic": "^2.0.53",
2020
"@ai-sdk/google": "^2.0.18",
2121
"@ai-sdk/openai": "^2.0.46",
2222
"@ai-sdk/openai-compatible": "^1.0.20",
23-
"@ai-sdk/vue": "^2.0.62",
23+
"@ai-sdk/vue": "^2.0.106",
2424
"@beekeeperstudio/plugin": "^1.4.0",
25+
"@beekeeperstudio/ui-kit": "0.3.1",
2526
"@langchain/core": "^0.3.61",
2627
"@material-symbols/font-400": "^0.31.2",
27-
"ai": "^5.0.101",
2828
"@pdanpdan/vue-keyboard-trap": "^1.2.0",
29+
"ai": "^5.0.106",
2930
"highlight.js": "^11.11.1",
3031
"langchain": "^0.3.19",
3132
"lodash": "^4.17.21",
3233
"marked": "^15.0.12",
3334
"marked-highlight": "^2.2.1",
35+
"mitt": "^3.0.1",
3436
"ollama-ai-provider-v2": "^1.3.1",
3537
"pinia": "^3.0.2",
3638
"pluralize": "^8.0.0",
3739
"primevue": "^4.4.1",
40+
"semver": "^7.7.3",
3841
"sql-query-identifier": "^2.8.0",
3942
"typeface-roboto": "^0.0.75",
4043
"vue": "^3.4.21",
@@ -43,12 +46,14 @@
4346
"devDependencies": {
4447
"@beekeeperstudio/vite-plugin": "^1.0.3",
4548
"@types/lodash": "^4.17.16",
49+
"@types/semver": "^7.7.1",
4650
"@vitejs/plugin-vue": "^5.0.4",
4751
"@vitest/browser": "^3.2.4",
4852
"@vue/test-utils": "^2.4.6",
4953
"happy-dom": "^18.0.1",
5054
"sass": "^1.87.0",
5155
"vite": "^6.2.3",
56+
"vite-plugin-vue-devtools": "^8.0.5",
5257
"vitest": "^3.2.4",
5358
"vitest-browser-vue": "^1.1.0"
5459
}

src/App.vue

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
<h1>AI Shell</h1>
55
<div class="progress-bar"></div>
66
</div>
7-
<ChatInterface v-if="page === 'chat-interface'" :initialMessages="messages" :openaiApiKey="openaiApiKey"
8-
:anthropicApiKey="anthropicApiKey" :googleApiKey="googleApiKey" @manage-models="handleManageModels"
7+
<ChatInterface v-if="page === 'chat-interface'" :initialMessages="messages" @manage-models="handleManageModels"
98
@open-configuration="handleOpenConfiguration" />
109
<Configuration v-model:visible="showConfiguration" :reactivePage="configurationPage" @close="closeConfiguration" />
1110
<Dialog :visible="showOnboarding" :closable="false" :draggable="false">
@@ -26,7 +25,7 @@ import Configuration, {
2625
PageId as ConfigurationPageId,
2726
} from "@/components/configuration/Configuration.vue";
2827
import OnboardingScreen from "./components/OnboardingScreen.vue";
29-
import { getData, notify } from "@beekeeperstudio/plugin";
28+
import { getData, log } from "@beekeeperstudio/plugin";
3029
import { Dialog } from "primevue";
3130
3231
type Page = "starting" | "chat-interface";
@@ -72,11 +71,7 @@ export default {
7271
} catch (e) {
7372
this.showConfiguration = true;
7473
this.error = e;
75-
notify("pluginError", {
76-
message: `Failed to initialize: ${e?.message || e}`,
77-
error: e?.name,
78-
stack: e?.stack,
79-
});
74+
log.error(e);
8075
} finally {
8176
clearTimeout(loadingTimer);
8277
}

src/assets/styles/_base.scss

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
:root {
2-
font-family: "Roboto", Helvetica, Arial, sans-serif;
2+
font-family: var(--font-family, "Roboto", Helvetica, Arial, sans-serif);
33
}
44

55
* {
@@ -52,6 +52,10 @@ p {
5252
line-height: 1.5;
5353
}
5454

55+
pre, code {
56+
font-family: var(--font-family-mono, monospace);
57+
}
58+
5559
/* Make the scrollbar look better */
5660
::-webkit-scrollbar {
5761
width: 12px;

src/assets/styles/components/_message.scss

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,6 @@
115115

116116
.tool {
117117
.tool-error {
118-
margin-top: 0.5rem;
119118
color: var(--brand-danger);
120119
}
121120

@@ -145,7 +144,7 @@
145144
flex-direction: column;
146145

147146
.tool-name {
148-
margin-bottom: 0.25rem;
147+
margin-bottom: 0.5rem;
149148
}
150149

151150
.no-rows {
@@ -170,6 +169,9 @@
170169
border-top-right-radius: 0;
171170
height: 2rem;
172171
font-weight: normal;
172+
border-width: 0 1px 1px;
173+
border-style: solid;
174+
border-color: var(--border-color);
173175

174176
.open-icon {
175177
font-size: 1.2rem;

src/assets/styles/main.scss

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
@use "sass:color";
66
@use "primevue";
77

8+
@import "@beekeeperstudio/ui-kit/style.css" layer(ui-kit);
9+
810
#app {
911
height: 100%;
1012
display: flex;
@@ -28,3 +30,8 @@
2830
align-items: center;
2931
justify-content: center;
3032
}
33+
34+
.BksTextEditor .cm-scroller,
35+
.BksTextEditor .cm-tooltip.cm-tooltip-autocomplete>ul {
36+
font-family: var(--font-family-mono, monospace);
37+
}

src/assets/styles/pages/_chat-interface.scss

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@
168168
padding: 0.5rem;
169169
width: 100%;
170170
line-height: 1.07rem;
171-
font-size: 0.765rem;
171+
font-size: 0.865rem;
172172

173173
code {
174174

@@ -204,7 +204,7 @@
204204

205205
.lang {
206206
padding-inline: 0.5rem;
207-
height: 1.8rem;
207+
height: 2rem;
208208
display: flex;
209209
justify-content: space-between;
210210
font-size: 0.75rem;
@@ -222,7 +222,7 @@
222222

223223
.group {
224224
position: absolute;
225-
bottom: 0rem;
225+
bottom: 0.15rem;
226226
right: 0;
227227
display: flex;
228228
justify-content: flex-end;
@@ -412,17 +412,19 @@
412412
.tool-permission-buttons {
413413
display: flex;
414414
gap: 0.5rem;
415-
margin-top: 0.2rem;
415+
margin-top: 0.5rem;
416416

417417
button {
418418
border: none;
419-
background-color: transparent;
420419
cursor: pointer;
421420
display: flex;
422421
align-items: center;
423422
justify-content: center;
424-
color: currentColor;
425-
padding: 0;
423+
min-width: 0;
424+
425+
.accept-icon, .reject-icon {
426+
font-size: 1.2em;
427+
}
426428

427429
&:hover {
428430
.accept-icon {

src/components/ChatInterface.vue

Lines changed: 38 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
<template>
2-
<div
3-
class="chat-container"
4-
:class="{ 'empty-chat': messages.length === 0 }"
5-
:data-status="status"
6-
>
2+
<div class="chat-container" :class="{ 'empty-chat': messages.length === 0 }" :data-status="status">
73
<div class="scroll-container" ref="scrollContainerRef">
84
<div class="header">
95
<button class="btn btn-flat-2 settings-btn" @click="$emit('open-configuration')">
@@ -20,31 +16,27 @@
2016
</p>
2117
</div>
2218
<div class="chat-messages">
23-
<message
24-
v-for="(message, index) in messages"
25-
:key="message.id"
26-
:message="message"
27-
:pending-tool-call-ids="pendingToolCallIds"
28-
:status="index === messages.length - 1 ? (status === 'ready' || status === 'error' ? 'ready' : 'processing') : 'ready'"
29-
@accept-permission="acceptPermission"
30-
@reject-permission="rejectPermission"
31-
/>
32-
<div
33-
class="message error"
34-
v-if="isUnexpectedError"
35-
>
19+
<template v-for="(message, index) in messages" :key="message.id">
20+
<message
21+
v-if="
22+
!(message.role === 'assistant' && message.parts.find((p) => p.type === 'data-userEditedToolCall'))
23+
&& !(message.role === 'user' && message.parts.find((p) => p.type === 'data-editedQuery'))
24+
"
25+
:message="message"
26+
:pending-tool-call-ids="pendingToolCallIds"
27+
:status="index === messages.length - 1 ? (status === 'ready' || status === 'error' ? 'ready' : 'processing') : 'ready'"
28+
@accept-permission="acceptPermission" @reject-permission="handleRejectPermission" />
29+
</template>
30+
<div class="message error" v-if="isUnexpectedError">
3631
<div class="message-content">
3732
Something went wrong.
3833
<div v-if="isOllamaToolError" class="error-hint">
39-
💡 <strong>Hint:</strong> This might be because your Ollama model doesn't support tools. Try using a different model, or switch to a different provider.
34+
💡 <strong>Hint:</strong> This might be because your Ollama model doesn't support tools. Try using a
35+
different model, or switch to a different provider.
4036
</div>
4137
<pre v-if="!isErrorTruncated || showFullError" v-text="error" />
4238
<pre v-else v-text="truncatedError" />
43-
<button
44-
v-if="isErrorTruncated"
45-
@click="showFullError = !showFullError"
46-
class="btn show-more-btn"
47-
>
39+
<button v-if="isErrorTruncated" @click="showFullError = !showFullError" class="btn show-more-btn">
4840
{{ showFullError ? "Show less" : "Show more" }}
4941
</button>
5042
<button class="btn" @click="() => reload()">
@@ -53,31 +45,21 @@
5345
</button>
5446
</div>
5547
</div>
56-
<div
57-
class="message error"
58-
v-if="noModelError"
59-
>
48+
<div class="message error" v-if="noModelError">
6049
<div class="message-content">No model selected</div>
6150
</div>
62-
<div
63-
class="spinner-container"
64-
:style="{ visibility: showSpinner ? 'visible' : 'hidden' }"
65-
>
51+
<div class="spinner-container" :style="{ visibility: showSpinner ? 'visible' : 'hidden' }">
6652
<span class="spinner" />
6753
</div>
6854
</div>
69-
<button
70-
v-if="!isAtBottom"
71-
@click="scrollToBottom({ smooth: true })"
72-
class="btn scroll-down-btn"
73-
title="Scroll to bottom"
74-
>
55+
<button v-if="!isAtBottom" @click="scrollToBottom({ smooth: true })" class="btn scroll-down-btn"
56+
title="Scroll to bottom">
7557
<span class="material-symbols-outlined">keyboard_arrow_down</span>
7658
</button>
7759
</div>
7860
<div class="chat-input-container-container">
7961
<PromptInput ref="promptInput" storage-key="inputHistory" :processing="processing" :selected-model="model"
80-
@select-model="selectModel" @manage-models="$emit('manage-models')" @submit="submit" @stop="stop" />
62+
@select-model="selectModel" @manage-models="$emit('manage-models')" @submit="submit" @stop="stop" />
8163
</div>
8264
</div>
8365
</template>
@@ -88,9 +70,9 @@ import { useChatStore, Model } from "@/stores/chat";
8870
import _ from "lodash";
8971
import Markdown from "@/components/messages/Markdown.vue";
9072
import Message from "@/components/messages/Message.vue";
91-
import { Message as MessageType } from "ai";
73+
import type { UIMessage } from "ai";
9274
import { PropType } from "vue";
93-
import { mapActions, mapGetters, mapState, mapWritableState } from "pinia";
75+
import { mapActions, mapGetters, mapWritableState } from "pinia";
9476
import { RootBinding } from "@/plugins/appEvent";
9577
import { useInternalDataStore } from "@/stores/internalData";
9678
import BaseInput from "@/components/common/BaseInput.vue";
@@ -113,12 +95,9 @@ export default {
11395
11496
props: {
11597
initialMessages: {
116-
type: Array as PropType<MessageType[]>,
98+
type: Array as PropType<UIMessage[]>,
11799
required: true,
118100
},
119-
anthropicApiKey: String,
120-
openaiApiKey: String,
121-
googleApiKey: String,
122101
},
123102
124103
setup(props) {
@@ -148,7 +127,7 @@ export default {
148127
},
149128
150129
computed: {
151-
...mapGetters(useChatStore, ["systemPrompt"]),
130+
...mapGetters(useChatStore, ["systemPrompt", "sendOptions"]),
152131
...mapWritableState(useChatStore, ["model"]),
153132
processing() {
154133
if (this.askingPermission) return false;
@@ -238,8 +217,8 @@ export default {
238217
// Calculate if we're near bottom (within 50px of bottom)
239218
const isNearBottom =
240219
scrollContainer.scrollHeight -
241-
scrollContainer.scrollTop -
242-
scrollContainer.clientHeight <
220+
scrollContainer.scrollTop -
221+
scrollContainer.clientHeight <
243222
50;
244223
245224
this.isAtBottom = isNearBottom;
@@ -265,11 +244,11 @@ export default {
265244
this.rejectPermission();
266245
}
267246
268-
this.send(input, this.getSendOptions());
247+
this.send(input, this.sendOptions);
269248
},
270249
271250
async reload() {
272-
await this.retry(this.getSendOptions());
251+
await this.retry(this.sendOptions);
273252
},
274253
275254
stop() {
@@ -300,17 +279,15 @@ export default {
300279
this.model = model;
301280
},
302281
303-
getSendOptions() {
304-
if (!this.model) {
305-
throw new Error("No model selected");
306-
}
307-
308-
return {
309-
modelId: this.model.id,
310-
providerId: this.model.provider,
311-
systemPrompt: this.systemPrompt,
312-
}
313-
}
282+
handleRejectPermission(options: {
283+
toolCallId: string;
284+
editedQuery?: string;
285+
}) {
286+
this.rejectPermission({
287+
...options,
288+
sendOptions: this.sendOptions,
289+
});
290+
},
314291
},
315292
};
316293
</script>

0 commit comments

Comments
 (0)