Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 16 additions & 10 deletions apps/webapp/app/components/code/AIQueryInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,16 @@ interface AIQueryInputProps {
onQueryGenerated: (query: string) => void;
/** Set this to a prompt to auto-populate and immediately submit */
autoSubmitPrompt?: string;
/** Change this to force re-submission even if prompt is the same */
autoSubmitKey?: number;
/** Get the current query in the editor (used for edit mode) */
getCurrentQuery?: () => string;
}

export function AIQueryInput({
onQueryGenerated,
autoSubmitPrompt,
autoSubmitKey,
getCurrentQuery,
}: AIQueryInputProps) {
const [prompt, setPrompt] = useState("");
Expand All @@ -50,7 +53,7 @@ export function AIQueryInput({
const [lastResult, setLastResult] = useState<"success" | "error" | null>(null);
const textareaRef = useRef<HTMLTextAreaElement>(null);
const abortControllerRef = useRef<AbortController | null>(null);
const lastAutoSubmitRef = useRef<string | null>(null);
const lastAutoSubmitRef = useRef<{ prompt: string; key?: number } | null>(null);

const organization = useOrganization();
const project = useProject();
Expand Down Expand Up @@ -197,19 +200,22 @@ export function AIQueryInput({
[prompt, submitQuery]
);

// Auto-submit when autoSubmitPrompt changes
// Auto-submit when autoSubmitPrompt or autoSubmitKey changes
useEffect(() => {
if (
autoSubmitPrompt &&
autoSubmitPrompt.trim() &&
autoSubmitPrompt !== lastAutoSubmitRef.current &&
!isLoading
) {
lastAutoSubmitRef.current = autoSubmitPrompt;
if (!autoSubmitPrompt || !autoSubmitPrompt.trim() || isLoading) {
return;
}

const last = lastAutoSubmitRef.current;
const isDifferent =
last === null || autoSubmitPrompt !== last.prompt || autoSubmitKey !== last.key;

if (isDifferent) {
lastAutoSubmitRef.current = { prompt: autoSubmitPrompt, key: autoSubmitKey };
setPrompt(autoSubmitPrompt);
submitQuery(autoSubmitPrompt);
}
}, [autoSubmitPrompt, isLoading, submitQuery]);
}, [autoSubmitPrompt, autoSubmitKey, isLoading, submitQuery]);

// Cleanup on unmount
useEffect(() => {
Expand Down
20 changes: 19 additions & 1 deletion apps/webapp/app/components/code/TSQLEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { sql, StandardSQL } from "@codemirror/lang-sql";
import { autocompletion } from "@codemirror/autocomplete";
import { autocompletion, startCompletion } from "@codemirror/autocomplete";
import { linter, lintGutter } from "@codemirror/lint";
import { EditorView } from "@codemirror/view";
import type { ViewUpdate } from "@codemirror/view";
import { CheckIcon, ClipboardIcon, SparklesIcon, TrashIcon } from "@heroicons/react/20/solid";
import {
Expand Down Expand Up @@ -103,6 +104,23 @@ export function TSQLEditor(opts: TSQLEditorProps) {
maxRenderedOptions: 50,
})
);

// Trigger autocomplete when ' is typed in value context
// CodeMirror's activateOnTyping only triggers on alphanumeric characters,
// so we manually trigger for quotes after comparison operators
exts.push(
EditorView.domEventHandlers({
keyup: (event, view) => {
// Trigger on quote key (both ' and shift+' on some keyboards)
if (event.key === "'" || event.key === '"' || event.code === "Quote") {
setTimeout(() => {
startCompletion(view);
}, 50);
}
return false;
},
})
);
}

// Add TSQL linter
Expand Down
45 changes: 27 additions & 18 deletions apps/webapp/app/components/code/TSQLResultsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ function CellValue({
}) {
// Plain text mode - render everything as monospace text with truncation
if (!prettyFormatting) {
if (column.type === "JSON") {
return <JSONCellValue value={value} />;
}

const plainValue = value === null ? "NULL" : String(value);
const isTruncated = plainValue.length > MAX_STRING_DISPLAY_LENGTH;

Expand Down Expand Up @@ -277,24 +281,7 @@ function CellValue({

// JSON type
if (type === "JSON") {
const jsonString = JSON.stringify(value);
const isTruncated = jsonString.length > MAX_STRING_DISPLAY_LENGTH;

if (isTruncated) {
return (
<SimpleTooltip
content={
<pre className="max-w-sm whitespace-pre-wrap break-all font-mono text-xs">
{jsonString}
</pre>
}
button={
<span className="font-mono text-xs text-text-dimmed">{truncateString(jsonString)}</span>
}
/>
);
}
return <span className="font-mono text-xs text-text-dimmed">{jsonString}</span>;
return <JSONCellValue value={value} />;
}

// Array types
Expand Down Expand Up @@ -382,6 +369,28 @@ function EnvironmentCellValue({ value }: { value: string }) {
return <EnvironmentLabel environment={environment} />;
}

function JSONCellValue({ value }: { value: any }) {
const jsonString = JSON.stringify(value);
const isTruncated = jsonString.length > MAX_STRING_DISPLAY_LENGTH;

if (isTruncated) {
return (
<SimpleTooltip
content={
<pre className="max-w-sm whitespace-pre-wrap break-all font-mono text-xs">
{jsonString}
</pre>
}
button={
<span className="font-mono text-xs text-text-dimmed">{truncateString(jsonString)}</span>
}
/>
);
}

return <span className="font-mono text-xs text-text-dimmed">{jsonString}</span>;
}

/**
* Check if a column should be right-aligned (numeric columns, duration, cost)
*/
Expand Down
172 changes: 0 additions & 172 deletions apps/webapp/app/components/code/tsql/tsqlCompletion.test.ts

This file was deleted.

Loading