mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-30 06:53:58 +00:00
SYNC: Merge pull request #14 from dbgate/ai-sql
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
export let square = false;
|
export let square = false;
|
||||||
export let narrow = false;
|
export let narrow = false;
|
||||||
export let title = null;
|
export let title = null;
|
||||||
|
export let inlineBlock=false;
|
||||||
|
|
||||||
let domButton;
|
let domButton;
|
||||||
|
|
||||||
@@ -17,6 +18,7 @@
|
|||||||
class:disabled
|
class:disabled
|
||||||
class:square
|
class:square
|
||||||
class:narrow
|
class:narrow
|
||||||
|
class:inlineBlock
|
||||||
on:click
|
on:click
|
||||||
bind:this={domButton}
|
bind:this={domButton}
|
||||||
data-testid={$$props['data-testid']}
|
data-testid={$$props['data-testid']}
|
||||||
@@ -71,4 +73,8 @@
|
|||||||
.square {
|
.square {
|
||||||
width: 18px;
|
width: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.inlineBlock {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
|
|
||||||
const thisInstance = get_current_component();
|
const thisInstance = get_current_component();
|
||||||
|
|
||||||
export const activator = createActivator('ToolStripContainer', true);
|
export let showAlways = false;
|
||||||
|
export const activator = showAlways ? null : createActivator('ToolStripContainer', true);
|
||||||
|
|
||||||
export function activate() {
|
export function activate() {
|
||||||
activator?.activate();
|
activator?.activate();
|
||||||
@@ -13,7 +14,7 @@
|
|||||||
export let scrollContent = false;
|
export let scrollContent = false;
|
||||||
export let hideToolStrip = false;
|
export let hideToolStrip = false;
|
||||||
|
|
||||||
$: isComponentActive = $isComponentActiveStore('ToolStripContainer', thisInstance) && !hideToolStrip;
|
$: isComponentActive = showAlways || ($isComponentActiveStore('ToolStripContainer', thisInstance) && !hideToolStrip);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
export let showCaller = false;
|
export let showCaller = false;
|
||||||
export let startLine = 0;
|
export let startLine = 0;
|
||||||
export let onMessageClick = null;
|
export let onMessageClick = null;
|
||||||
|
export let onExplainError = null;
|
||||||
|
|
||||||
export let filter = '';
|
export let filter = '';
|
||||||
|
|
||||||
@@ -90,6 +91,7 @@
|
|||||||
{startLine}
|
{startLine}
|
||||||
previousRow={index > 0 ? items[index - 1] : null}
|
previousRow={index > 0 ? items[index - 1] : null}
|
||||||
{onMessageClick}
|
{onMessageClick}
|
||||||
|
{onExplainError}
|
||||||
/>
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
import JSONTree from '../jsontree/JSONTree.svelte';
|
import JSONTree from '../jsontree/JSONTree.svelte';
|
||||||
import FontIcon from '../icons/FontIcon.svelte';
|
import FontIcon from '../icons/FontIcon.svelte';
|
||||||
import { plusExpandIcon } from '../icons/expandIcons';
|
import { plusExpandIcon } from '../icons/expandIcons';
|
||||||
|
import InlineButton from '../buttons/InlineButton.svelte';
|
||||||
|
|
||||||
export let row;
|
export let row;
|
||||||
export let index;
|
export let index;
|
||||||
@@ -27,6 +28,7 @@
|
|||||||
|
|
||||||
export let previousRow = null;
|
export let previousRow = null;
|
||||||
export let onMessageClick = null;
|
export let onMessageClick = null;
|
||||||
|
export let onExplainError = null;
|
||||||
|
|
||||||
let isExpanded = false;
|
let isExpanded = false;
|
||||||
</script>
|
</script>
|
||||||
@@ -43,6 +45,15 @@
|
|||||||
<FontIcon icon={plusExpandIcon(isExpanded)} />
|
<FontIcon icon={plusExpandIcon(isExpanded)} />
|
||||||
</span>
|
</span>
|
||||||
{row.message}
|
{row.message}
|
||||||
|
{#if row.severity == 'error' && onExplainError}
|
||||||
|
<InlineButton
|
||||||
|
title="Explain error"
|
||||||
|
inlineBlock
|
||||||
|
on:click={e => {
|
||||||
|
onExplainError(row);
|
||||||
|
}}><FontIcon icon="img ai" /> Explain</InlineButton
|
||||||
|
>
|
||||||
|
{/if}
|
||||||
</td>
|
</td>
|
||||||
<td>{moment(row.time).format('HH:mm:ss')}</td>
|
<td>{moment(row.time).format('HH:mm:ss')}</td>
|
||||||
<td>{formatDuration(new Date(row.time).getTime() - time0)}</td>
|
<td>{formatDuration(new Date(row.time).getTime() - time0)}</td>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
export let startLine = 0;
|
export let startLine = 0;
|
||||||
export let onChangeErrors = null;
|
export let onChangeErrors = null;
|
||||||
export let onMessageClick = null;
|
export let onMessageClick = null;
|
||||||
|
export let onExplainError = null;
|
||||||
|
|
||||||
const cachedMessagesRef = createRef([]);
|
const cachedMessagesRef = createRef([]);
|
||||||
const lastErrorMessageCountRef = createRef(0);
|
const lastErrorMessageCountRef = createRef(0);
|
||||||
@@ -70,5 +71,13 @@
|
|||||||
{#if showNoMessagesAlert && (!displayedMessages || displayedMessages.length == 0)}
|
{#if showNoMessagesAlert && (!displayedMessages || displayedMessages.length == 0)}
|
||||||
<ErrorInfo message="No messages" icon="img alert" />
|
<ErrorInfo message="No messages" icon="img alert" />
|
||||||
{:else}
|
{:else}
|
||||||
<MessageView items={displayedMessages} {onMessageClick} {showProcedure} {showLine} {showCaller} {startLine} />
|
<MessageView
|
||||||
|
items={displayedMessages}
|
||||||
|
{onMessageClick}
|
||||||
|
{showProcedure}
|
||||||
|
{showLine}
|
||||||
|
{showCaller}
|
||||||
|
{startLine}
|
||||||
|
{onExplainError}
|
||||||
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ function getParsedLocalStorage(key) {
|
|||||||
|
|
||||||
const saveHandlersList = [];
|
const saveHandlersList = [];
|
||||||
|
|
||||||
export default function useEditorData({ tabid, reloadToken = 0, loadFromArgs = null, onInitialData = null }) {
|
export default function useEditorData({ tabid, reloadToken = 0, loadFromArgs = null, onInitialData = null, editorKeyword = 'editor' }) {
|
||||||
const localStorageKey = `tabdata_editor_${tabid}`;
|
const localStorageKey = `tabdata_${editorKeyword}_${tabid}`;
|
||||||
let changeCounter = 0;
|
let changeCounter = 0;
|
||||||
let savedCounter = 0;
|
let savedCounter = 0;
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,15 @@
|
|||||||
testEnabled: () => getCurrentEditor()?.isSqlEditor(),
|
testEnabled: () => getCurrentEditor()?.isSqlEditor(),
|
||||||
onClick: () => getCurrentEditor().formatCode(),
|
onClick: () => getCurrentEditor().formatCode(),
|
||||||
});
|
});
|
||||||
|
registerCommand({
|
||||||
|
id: 'query.switchAiAssistant',
|
||||||
|
category: 'Query',
|
||||||
|
name: 'AI Assistant',
|
||||||
|
keyText: 'Shift+Alt+A',
|
||||||
|
icon: 'icon ai',
|
||||||
|
testEnabled: () => isProApp(),
|
||||||
|
onClick: () => getCurrentEditor().toggleAiAssistant(),
|
||||||
|
});
|
||||||
registerCommand({
|
registerCommand({
|
||||||
id: 'query.insertSqlJoin',
|
id: 'query.insertSqlJoin',
|
||||||
category: 'Query',
|
category: 'Query',
|
||||||
@@ -155,6 +164,7 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import FontIcon from '../icons/FontIcon.svelte';
|
import FontIcon from '../icons/FontIcon.svelte';
|
||||||
import hasPermission from '../utility/hasPermission';
|
import hasPermission from '../utility/hasPermission';
|
||||||
|
import QueryAiAssistant from '../ai/QueryAiAssistant.svelte';
|
||||||
|
|
||||||
export let tabid;
|
export let tabid;
|
||||||
export let conid;
|
export let conid;
|
||||||
@@ -227,6 +237,9 @@
|
|||||||
let queryRowsLimit = getInitialRowsLimit();
|
let queryRowsLimit = getInitialRowsLimit();
|
||||||
$: localStorage.setItem(queryRowsLimitLocalStorageKey, queryRowsLimit ? queryRowsLimit.toString() : 'nolimit');
|
$: localStorage.setItem(queryRowsLimitLocalStorageKey, queryRowsLimit ? queryRowsLimit.toString() : 'nolimit');
|
||||||
|
|
||||||
|
let isAiAssistantVisible = isProApp() && localStorage.getItem(`tabdata_isAiAssistantVisible_${tabid}`) == 'true';
|
||||||
|
let domAiAssistant;
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
intervalId = setInterval(() => {
|
intervalId = setInterval(() => {
|
||||||
if (!driver?.singleConnectionOnly && sessionId) {
|
if (!driver?.singleConnectionOnly && sessionId) {
|
||||||
@@ -301,6 +314,10 @@
|
|||||||
visibleResultTabs = !visibleResultTabs;
|
visibleResultTabs = !visibleResultTabs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function toggleAiAssistant() {
|
||||||
|
isAiAssistantVisible = !isAiAssistantVisible;
|
||||||
|
}
|
||||||
|
|
||||||
function getParameterSplitterOptions() {
|
function getParameterSplitterOptions() {
|
||||||
if (!queryParameterStyle) {
|
if (!queryParameterStyle) {
|
||||||
return null;
|
return null;
|
||||||
@@ -571,6 +588,16 @@
|
|||||||
errorMessages = errors;
|
errorMessages = errors;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handleExplainError(errorObject) {
|
||||||
|
if (!isProApp()) return;
|
||||||
|
isAiAssistantVisible = true;
|
||||||
|
await tick();
|
||||||
|
domAiAssistant?.explainError({
|
||||||
|
userQuery: $editorValue,
|
||||||
|
errorObject,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function handleSetFrontMatterField(field, value) {
|
function handleSetFrontMatterField(field, value) {
|
||||||
const text = $editorValue;
|
const text = $editorValue;
|
||||||
setEditorData(
|
setEditorData(
|
||||||
@@ -651,9 +678,13 @@
|
|||||||
localStorage.getItem(`tabdata_queryParamStyle_${tabid}`) ??
|
localStorage.getItem(`tabdata_queryParamStyle_${tabid}`) ??
|
||||||
initialArgs?.queryParameterStyle ??
|
initialArgs?.queryParameterStyle ??
|
||||||
(initialArgs?.scriptTemplate == 'CALL OBJECT' ? ':' : '');
|
(initialArgs?.scriptTemplate == 'CALL OBJECT' ? ':' : '');
|
||||||
|
|
||||||
|
$: localStorage.setItem(`tabdata_isAiAssistantVisible_${tabid}`, isAiAssistantVisible ? 'true' : 'false');
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ToolStripContainer bind:this={domToolStrip} hideToolStrip={hideEditor}>
|
<ToolStripContainer bind:this={domToolStrip} hideToolStrip={hideEditor}>
|
||||||
|
<HorizontalSplitter isSplitter={isAiAssistantVisible} initialSizeRight={300}>
|
||||||
|
<svelte:fragment slot="1">
|
||||||
<VerticalSplitter isSplitter={visibleResultTabs} initialValue={splitterInitialValue} hideFirst={hideEditor}>
|
<VerticalSplitter isSplitter={visibleResultTabs} initialValue={splitterInitialValue} hideFirst={hideEditor}>
|
||||||
<svelte:fragment slot="1">
|
<svelte:fragment slot="1">
|
||||||
{#if driver?.databaseEngineTypes?.includes('sql')}
|
{#if driver?.databaseEngineTypes?.includes('sql')}
|
||||||
@@ -731,11 +762,43 @@
|
|||||||
showProcedure
|
showProcedure
|
||||||
showLine
|
showLine
|
||||||
onChangeErrors={handleChangeErrors}
|
onChangeErrors={handleChangeErrors}
|
||||||
|
onExplainError={isProApp() ? handleExplainError : null}
|
||||||
/>
|
/>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</ResultTabs>
|
</ResultTabs>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</VerticalSplitter>
|
</VerticalSplitter>
|
||||||
|
</svelte:fragment>
|
||||||
|
<svelte:fragment slot="2">
|
||||||
|
<QueryAiAssistant
|
||||||
|
bind:this={domAiAssistant}
|
||||||
|
{conid}
|
||||||
|
{database}
|
||||||
|
{driver}
|
||||||
|
onClose={() => {
|
||||||
|
isAiAssistantVisible = false;
|
||||||
|
}}
|
||||||
|
text={$editorValue}
|
||||||
|
getLine={() => domEditor.getEditor().getSelectionRange().start.row}
|
||||||
|
onInsertAtCursor={text => {
|
||||||
|
const editor = domEditor.getEditor();
|
||||||
|
editor.session.insert(editor.getCursorPosition(), text);
|
||||||
|
domEditor?.getEditor()?.focus();
|
||||||
|
}}
|
||||||
|
getTextOrSelectedText={() => domEditor.getEditor().getSelectedText() || $editorValue}
|
||||||
|
onSetSelectedText={text => {
|
||||||
|
const editor = domEditor.getEditor();
|
||||||
|
if (editor.getSelectedText()) {
|
||||||
|
const range = editor.selection.getRange();
|
||||||
|
editor.session.replace(range, text);
|
||||||
|
} else {
|
||||||
|
editor.setValue(text);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
{tabid}
|
||||||
|
/>
|
||||||
|
</svelte:fragment>
|
||||||
|
</HorizontalSplitter>
|
||||||
<svelte:fragment slot="toolstrip">
|
<svelte:fragment slot="toolstrip">
|
||||||
<ToolStripCommandSplitButton
|
<ToolStripCommandSplitButton
|
||||||
commands={['query.execute', 'query.executeCurrent']}
|
commands={['query.execute', 'query.executeCurrent']}
|
||||||
@@ -774,6 +837,11 @@
|
|||||||
icon="icon at"
|
icon="icon at"
|
||||||
title="Query parameter style"
|
title="Query parameter style"
|
||||||
/>
|
/>
|
||||||
|
<ToolStripCommandButton
|
||||||
|
command="query.switchAiAssistant"
|
||||||
|
hideDisabled
|
||||||
|
data-testid="QueryTab_switchAiAssistantButton"
|
||||||
|
></ToolStripCommandButton>
|
||||||
<ToolStripCommandButton
|
<ToolStripCommandButton
|
||||||
command="query.beginTransaction"
|
command="query.beginTransaction"
|
||||||
data-testid="QueryTab_beginTransactionButton"
|
data-testid="QueryTab_beginTransactionButton"
|
||||||
|
|||||||
Reference in New Issue
Block a user