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
13 changes: 1 addition & 12 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,18 +123,7 @@ Working on between operator in kql-lezer.

## HIGH: MCP Servers

Available MCP (Model Context Protocol) servers for enhanced functionality:

- **vibe-check-mcp**: Use `vibe_check` to analyze code quality, style, or overall "vibe" of code snippets. Useful for code reviews or improvements.
- **taskmanager**: Use `add_tasks_to_request` to break down complex tasks, `approve_request_completion` to track progress. Helps manage multi-step development tasks.
- **sequential-thinking**: Use `sequentialthinking` for step-by-step reasoning and problem-solving. Ideal for debugging or complex logic analysis.
- **basic-memory**: Use `basic-memory_fetch` to retrieve stored information, `create_memory_project` for organizing project-related memories. Provides persistent context across sessions.
- **ESLint**: Use `lint-files` to run ESLint on specific files or directories. Integrates linting directly into workflows.
- **mcp-server-github**: Use for GitHub API interactions, such as fetching issues, PRs, or repository data. Enhances GitHub workflow integration.
- **bun-docs-mcp**: Use `SearchBun` to query Bun runtime documentation. Essential for Bun-specific questions or API lookups.
- **mcp-server-context7**: Use `get-library-docs` to retrieve documentation and code examples for libraries, `resolve-library-id` to find compatible library identifiers. Critical for researching external libraries and APIs.

Always prefer MCP servers over manual searches when available, especially for documentation (context7), linting (ESLint), and task management (taskmanager).
Always prefer MCP servers over manual searches when available.

## MEDIUM: Tool Usage

Expand Down
106 changes: 71 additions & 35 deletions packages/kql-lezer/src/grammar/plugins/rules/query.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,82 @@
import { seq, many, ref, choice, optional, type RuleDef, kw } from "@fossiq/lezer-grammar-generator";
import {
seq,
many,
ref,
choice,
optional,
type RuleDef,
kw,
} from "@fossiq/lezer-grammar-generator";

/**
* Top-level query rules.
*/
export const queryRules: Record<string, RuleDef> = {
Query: {
expression: seq(
many(choice(ref("LetStatement"), ref("SetStatement"), ref("DeclareQueryParametersStatement"))),
ref("QueryExpression")
),
},

QueryExpression: {
expression: choice(
ref("PipelineExpression"),
ref("UnionExpression"),
ref("SearchExpression"),
ref("FindExpression")
Query: {
expression: seq(
many(
choice(
ref("LetStatement"),
ref("SetStatement"),
ref("DeclareQueryParametersStatement")
)
},
),
optional(ref("QueryExpression"))
),
},

UnionExpression: {
expression: seq(
kw("union"),
optional(ref("UnionParameters")),
ref("TableList")
)
},
QueryExpression: {
expression: choice(
ref("PipelineExpression"),
ref("UnionExpression"),
ref("SearchExpression"),
ref("FindExpression")
),
},

UnionExpression: {
expression: seq(
kw("union"),
optional(ref("UnionParameters")),
ref("TableList")
),
},

SearchExpression: {
expression: seq(
kw("search"),
many(choice(ref("Identifier"), ref("String"), ref("Pipe"), ref("OpenParen"), ref("CloseParen"), kw("in"), kw("kind"), ref("Equals")))
// TODO: Improve search grammar
SearchExpression: {
expression: seq(
kw("search"),
many(
choice(
ref("Identifier"),
ref("String"),
ref("Pipe"),
ref("OpenParen"),
ref("CloseParen"),
kw("in"),
kw("kind"),
ref("Equals")
)
},
)
// TODO: Improve search grammar
),
},

FindExpression: {
expression: seq(
kw("find"),
many(choice(ref("Identifier"), ref("String"), ref("Pipe"), ref("OpenParen"), ref("CloseParen"), kw("in"), kw("kind"), ref("Equals")))
// TODO: Improve find grammar
FindExpression: {
expression: seq(
kw("find"),
many(
choice(
ref("Identifier"),
ref("String"),
ref("Pipe"),
ref("OpenParen"),
ref("CloseParen"),
kw("in"),
kw("kind"),
ref("Equals")
)
}
};
)
// TODO: Improve find grammar
),
},
};
4 changes: 2 additions & 2 deletions packages/ui/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@
<link
rel="icon"
type="image/svg+xml"
href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path fill='%232c3e50' d='M8.976 12 4.733 7.757 3.32 9.172 6.147 12 3.32 14.828l1.414 1.415zM12 19a1 1 0 0 1-1-1V6a1 1 0 1 1 2 0v12a1 1 0 0 1-1 1M15.024 12l4.243 4.243 1.414-1.415L17.853 12l2.828-2.828-1.414-1.415z'/></svg>"
href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke-linecap='round' stroke-linejoin='round'><path d='M8 8L4 12l4 4M16 8l4 4-4 4M12 6v12' stroke='white' stroke-width='4' /><path d='M8 8L4 12l4 4M16 8l4 4-4 4M12 6v12' stroke='%230c3b66' stroke-width='2' /></svg>"
/>
<link
rel="apple-touch-icon"
href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path fill='%232c3e50' d='M8.976 12 4.733 7.757 3.32 9.172 6.147 12 3.32 14.828l1.414 1.415zM12 19a1 1 0 0 1-1-1V6a1 1 0 1 1 2 0v12a1 1 0 0 1-1 1M15.024 12l4.243 4.243 1.414-1.415L17.853 12l2.828-2.828-1.414-1.415z'/></svg>"
href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 192 192' fill='none' stroke-linecap='round' stroke-linejoin='round'><path d='M64 64L32 96l32 32M128 64l32 32-32 32M96 48v96' stroke='white' stroke-width='24' /><path d='M64 64L32 96l32 32M128 64l32 32-32 32M96 48v96' stroke='%230c3b66' stroke-width='12' /></svg>"
/>
<title>Fossiq - KQL Query Explorer</title>
<link
Expand Down
38 changes: 0 additions & 38 deletions packages/ui/public/index.html

This file was deleted.

4 changes: 2 additions & 2 deletions packages/ui/public/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@
"categories": ["productivity", "utilities"],
"icons": [
{
"src": "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 192 192'><rect fill='%230c3b66' width='192' height='192'/><text x='96' y='120' font-size='80' font-weight='bold' text-anchor='middle' fill='white' font-family='system-ui'>F</text></svg>",
"src": "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 192 192' fill='none' stroke-linecap='round' stroke-linejoin='round'><path d='M64 64L32 96l32 32M128 64l32 32-32 32M96 48v96' stroke='white' stroke-width='24' /><path d='M64 64L32 96l32 32M128 64l32 32-32 32M96 48v96' stroke='%230c3b66' stroke-width='12' /></svg>",
"sizes": "192x192",
"type": "image/svg+xml",
"purpose": "any"
},
{
"src": "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'><rect fill='%230c3b66' width='512' height='512'/><text x='256' y='380' font-size='300' font-weight='bold' text-anchor='middle' fill='white' font-family='system-ui'>F</text></svg>",
"src": "data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512' fill='none' stroke-linecap='round' stroke-linejoin='round'><path d='M170 170L85 256l85 86M342 170l85 86-85 86M256 128v256' stroke='white' stroke-width='64' /><path d='M170 170L85 256l85 86M342 170l85 86-85 86M256 128v256' stroke='%230c3b66' stroke-width='32' /></svg>",
"sizes": "512x512",
"type": "image/svg+xml",
"purpose": "any maskable"
Expand Down
23 changes: 15 additions & 8 deletions packages/ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ const STORAGE_KEY_RESULTS = "fossiq-results";

const AppContent: Component = () => {
// Load persisted query and results from localStorage
const savedQuery =
localStorage.getItem(STORAGE_KEY_QUERY) || "";
const savedQuery = localStorage.getItem(STORAGE_KEY_QUERY) || "";
const savedResults = (() => {
try {
const stored = localStorage.getItem(STORAGE_KEY_RESULTS);
Expand Down Expand Up @@ -47,7 +46,6 @@ const AppContent: Component = () => {
}
});


const handleRun = async () => {
const connection = conn();
if (!connection) {
Expand Down Expand Up @@ -116,8 +114,7 @@ const AppContent: Component = () => {
</button>
</div>
}
>
<div class="panes-container">
editorPane={
<div class="editor-pane">
<div class="editor-container">
<Editor
Expand All @@ -127,9 +124,19 @@ const AppContent: Component = () => {
/>
</div>
</div>
}
resultsPane={
<div class="results-pane">
<div class="pane-header">
<h2>Results {results().length > 0 && `(${results().length})`}</h2>
<div
style={{
display: "flex",
"align-items": "baseline",
gap: "1rem",
}}
>
<h2>Results {results().length > 0 && `(${results().length})`}</h2>
</div>
</div>
<Show when={error()}>
<div
Expand All @@ -144,8 +151,8 @@ const AppContent: Component = () => {
</Show>
<ResultsTable data={results()} />
</div>
</div>
</Layout>
}
/>
);
};

Expand Down
66 changes: 61 additions & 5 deletions packages/ui/src/components/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,85 @@
import { Component } from "solid-js";
import { Component, createSignal, JSX, createEffect } from "solid-js";
import Header from "./Header";
import Sidebar from "./Sidebar";
import { useTheme } from "../hooks/useTheme";

const STORAGE_KEY_SIDEBAR = "fossiq-sidebar-collapsed";

interface LayoutProps {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- SolidJS children can be any renderable type
children?: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
headerContent?: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
editorPane?: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
resultsPane?: any;
}

const Layout: Component<LayoutProps> = (props) => {
const { theme, toggleTheme } = useTheme();

// Initialize from localStorage
const [sidebarCollapsed, setSidebarCollapsed] = createSignal(
localStorage.getItem(STORAGE_KEY_SIDEBAR) === "true"
);

const [resultsHeight, setResultsHeight] = createSignal(300);
const [isResizing, setIsResizing] = createSignal(false);

// Persist sidebar state
createEffect(() => {
localStorage.setItem(STORAGE_KEY_SIDEBAR, String(sidebarCollapsed()));
});

const handleAddSource = () => {
console.log("Add source clicked");
// TODO: Implement add source dialog
};

const handleMouseDown: JSX.EventHandler<HTMLDivElement, MouseEvent> = (e) => {
e.preventDefault();
setIsResizing(true);

const startY = e.clientY;
const startHeight = resultsHeight();

const handleMouseMove = (moveEvent: MouseEvent) => {
const deltaY = startY - moveEvent.clientY;
const newHeight = Math.min(Math.max(startHeight + deltaY, 100), 600);
Copy link

Copilot AI Jan 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The minimum (100) and maximum (600) height constraints are magic numbers. Consider extracting these as named constants (e.g., MIN_RESULTS_HEIGHT and MAX_RESULTS_HEIGHT) to improve code readability and maintainability.

Copilot uses AI. Check for mistakes.
setResultsHeight(newHeight);
};

const handleMouseUp = () => {
setIsResizing(false);
document.removeEventListener("mousemove", handleMouseMove);
document.removeEventListener("mouseup", handleMouseUp);
};

document.addEventListener("mousemove", handleMouseMove);
document.addEventListener("mouseup", handleMouseUp);
};

return (
<div class="container" data-theme={theme()}>
<Header onThemeToggle={toggleTheme}>{props.headerContent}</Header>
<div class="content">
{props.children}
<Sidebar onAddSource={handleAddSource} />
<div class="main-area">
<div class="top-section">
<Sidebar
onAddSource={handleAddSource}
collapsed={sidebarCollapsed()}
onToggleCollapse={() => setSidebarCollapsed(!sidebarCollapsed())}
/>
{props.editorPane}
</div>
</div>
<div
class="resize-handle"
onMouseDown={handleMouseDown}
classList={{ resizing: isResizing() }}
/>
<div class="results-area" style={{ height: `${resultsHeight()}px` }}>
{props.resultsPane}
</div>
</div>
</div>
);
Expand Down
Loading