elastic-input 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +461 -0
  3. package/dist/autocomplete/AutocompleteEngine.d.ts +36 -0
  4. package/dist/autocomplete/suggestionTypes.d.ts +16 -0
  5. package/dist/components/AutocompleteDropdown.d.ts +24 -0
  6. package/dist/components/DatePicker.d.ts +9 -0
  7. package/dist/components/DateRangePicker.d.ts +16 -0
  8. package/dist/components/ElasticInput.d.ts +39 -0
  9. package/dist/components/HighlightedContent.d.ts +7 -0
  10. package/dist/components/ValidationSquiggles.d.ts +13 -0
  11. package/dist/constants.d.ts +18 -0
  12. package/dist/elastic-input.es.js +3670 -0
  13. package/dist/highlighting/parenMatch.d.ts +17 -0
  14. package/dist/highlighting/rangeHighlight.d.ts +10 -0
  15. package/dist/highlighting/regexHighlight.d.ts +10 -0
  16. package/dist/index.d.ts +18 -0
  17. package/dist/lexer/Lexer.d.ts +32 -0
  18. package/dist/lexer/tokens.d.ts +28 -0
  19. package/dist/parser/Parser.d.ts +37 -0
  20. package/dist/parser/ast.d.ts +97 -0
  21. package/dist/styles/inlineStyles.d.ts +19 -0
  22. package/dist/types.d.ts +383 -0
  23. package/dist/utils/cursorUtils.d.ts +16 -0
  24. package/dist/utils/dateUtils.d.ts +8 -0
  25. package/dist/utils/domUtils.d.ts +19 -0
  26. package/dist/utils/expandSelection.d.ts +20 -0
  27. package/dist/utils/extractValues.d.ts +26 -0
  28. package/dist/utils/smartSelect.d.ts +13 -0
  29. package/dist/utils/textUtils.d.ts +57 -0
  30. package/dist/utils/undoStack.d.ts +23 -0
  31. package/dist/validation/Validator.d.ts +39 -0
  32. package/dist/validation/dateValidator.d.ts +1 -0
  33. package/dist/validation/numberValidator.d.ts +1 -0
  34. package/package.json +74 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 krtools
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,461 @@
1
+ # elastic-input
2
+
3
+ A syntax-aware smart autocomplete input for building structured queries. Supports field:value pairs, boolean operators, comparison operators, saved searches, history references, date pickers, and real-time validation — all in a single React component.
4
+
5
+ Built with React functional components and hooks (compatible with React 16.8+), zero runtime dependencies beyond React/ReactDOM, and fully inline-styled (no CSS imports required).
6
+
7
+ Vibe-coded as a proof of concept with Claude Code
8
+
9
+ ## Features
10
+
11
+ - **Syntax highlighting** — field names, values, operators, quoted strings, and special tokens are color-coded in real time
12
+ - **Context-aware autocomplete** — suggestions adapt based on cursor position (field names, values, operators, saved searches, history)
13
+ - **Built-in date picker** — calendar UI with single date and date range selection for date-typed fields
14
+ - **Validation with squiggly underlines** — unknown fields, type mismatches, and custom validators shown as red wavy underlines with hover tooltips
15
+ - **Deferred error display** — validation errors only appear after the cursor leaves the error range
16
+ - **Saved searches (`#`)** — reference saved queries by name with autocomplete
17
+ - **History references (`!`)** — recall previous searches with autocomplete
18
+ - **Keyboard-driven** — Tab to accept + continue, Enter to accept + submit, Ctrl+Enter to force submit, arrow keys to navigate
19
+ - **Fully configurable** — colors, structural styles, fonts, and layout are all customizable via props
20
+ - **Dark mode ready** — ships with `DARK_COLORS` and `DARK_STYLES` presets
21
+
22
+ ## Quick Start
23
+
24
+ ```tsx
25
+ import { ElasticInput } from 'elastic-input';
26
+ import type { FieldConfig } from 'elastic-input';
27
+
28
+ const fields: FieldConfig[] = [
29
+ {
30
+ name: 'status',
31
+ label: 'Status',
32
+ type: 'string',
33
+ suggestions: ['active', 'inactive', 'pending'],
34
+ description: 'Account status',
35
+ },
36
+ {
37
+ name: 'created',
38
+ label: 'Created Date',
39
+ type: 'date',
40
+ description: 'When the record was created',
41
+ },
42
+ {
43
+ name: 'price',
44
+ label: 'Price',
45
+ type: 'number',
46
+ description: 'Item price',
47
+ },
48
+ {
49
+ name: 'is_active',
50
+ label: 'Active',
51
+ type: 'boolean',
52
+ },
53
+ ];
54
+
55
+ function App() {
56
+ return (
57
+ <ElasticInput
58
+ fields={fields}
59
+ placeholder="Search... e.g. status:active AND price:>100"
60
+ onSearch={(query, ast) => {
61
+ console.log('Search:', query);
62
+ console.log('AST:', ast);
63
+ }}
64
+ onChange={(query, ast) => {
65
+ console.log('Changed:', query);
66
+ }}
67
+ />
68
+ );
69
+ }
70
+ ```
71
+
72
+ ## Query Syntax
73
+
74
+ | Syntax | Example | Description |
75
+ |--------|---------|-------------|
76
+ | `field:value` | `status:active` | Field equals value |
77
+ | `field:"quoted value"` | `name:"John Doe"` | Quoted value with spaces |
78
+ | `field:>value` | `price:>100` | Greater than (also `>=`, `<`, `<=`) |
79
+ | `AND` / `OR` / `NOT` | `a AND b OR NOT c` | Boolean operators (case-insensitive) |
80
+ | `(...)` | `(a OR b) AND c` | Grouping with parentheses |
81
+ | `-field:value` | `-status:inactive` | Negation (shorthand for NOT) |
82
+ | `#name` | `#vip-active` | Saved search reference |
83
+ | `!partial` | `!recent` | History search reference |
84
+ | `value*` | `stat*` | Wildcard matching |
85
+ | `"phrase"` | `"error occurred"` | Bare phrase (full-text) |
86
+
87
+ Implicit AND is supported — `status:active level:ERROR` is equivalent to `status:active AND level:ERROR`.
88
+
89
+ ## Props
90
+
91
+ ### Required
92
+
93
+ | Prop | Type | Description |
94
+ |------|------|-------------|
95
+ | `fields` | `FieldConfig[] \| () => Promise<FieldConfig[]>` | Field definitions for autocomplete and validation |
96
+
97
+ ### Optional
98
+
99
+ | Prop | Type | Default | Description |
100
+ |------|------|---------|-------------|
101
+ | `onSearch` | `(query, ast) => void` | — | Called on search submission |
102
+ | `onChange` | `(query, ast) => void` | — | Called on every input change |
103
+ | `onValidationChange` | `(errors) => void` | — | Called when validation errors change |
104
+ | `value` | `string` | — | Controlled input value |
105
+ | `defaultValue` | `string` | — | Initial uncontrolled value |
106
+ | `savedSearches` | `SavedSearch[] \| () => Promise<SavedSearch[]>` | — | Saved search definitions (sync or async) |
107
+ | `searchHistory` | `HistoryEntry[] \| () => Promise<HistoryEntry[]>` | — | Search history entries (sync or async) |
108
+ | `fetchSuggestions` | `(field, partial) => Promise<SuggestionItem[]>` | — | Async suggestion provider for field values |
109
+ | `colors` | `ColorConfig` | `DEFAULT_COLORS` | Syntax highlighting and UI colors |
110
+ | `styles` | `StyleConfig` | `DEFAULT_STYLES` | Structural/layout style overrides |
111
+ | `placeholder` | `string` | `"Search..."` | Placeholder text |
112
+ | `className` | `string` | — | CSS class for the outer container |
113
+ | `style` | `CSSProperties` | — | Inline styles for the outer container |
114
+ | `inputRef` | `(api) => void` | — | Receive an imperative API handle |
115
+ | `dropdown` | `DropdownConfig` | `{}` | Dropdown behavior and rendering (mode, triggers, renderers) |
116
+ | `features` | `FeaturesConfig` | `{}` | Feature toggles (multiline, smartSelectAll, expandSelection, wildcardWrap, savedSearches, historySearch) |
117
+ | `onKeyDown` | `(e) => void` | — | Called before internal keyboard handling |
118
+ | `onFocus` | `() => void` | — | Called when the input gains focus |
119
+ | `onBlur` | `() => void` | — | Called when the input loses focus |
120
+ | `onTab` | `(context) => TabActionResult` | — | Override Tab key behavior (accept/blur/submit) |
121
+ | `validateValue` | `(ctx) => ValidateReturn` | — | Custom validation for all value types |
122
+
123
+ ## Field Configuration
124
+
125
+ ```typescript
126
+ interface FieldConfig {
127
+ name: string; // Field identifier used in queries
128
+ label?: string; // Display label (used in autocomplete)
129
+ type: FieldType; // 'string' | 'number' | 'date' | 'boolean' | 'ip'
130
+ suggestions?: string[];// Autocomplete values (any field type can have suggestions)
131
+ operators?: string[]; // Allowed operators (future use)
132
+ description?: string; // Shown in autocomplete dropdown
133
+ }
134
+ ```
135
+
136
+ ### Field Types
137
+
138
+ | Type | Autocomplete | Validation | Comparison Ops |
139
+ |------|-------------|------------|----------------|
140
+ | `boolean` | Shows `true` / `false` | Must be `true` or `false` | No |
141
+ | `number` | Shows hint "Enter a number" | Must be numeric | Yes (`>`, `>=`, `<`, `<=`) |
142
+ | `date` | Opens date picker with calendar | ISO dates, relative dates (`now-7d`) | Yes |
143
+ | `ip` | Shows hint "Enter an IP address" | Valid IPv4, supports wildcards (`192.168.*`) | No |
144
+ | `string` | No default hint (use `placeholder` for custom) | No validation (anything accepted) | No |
145
+
146
+ ## Imperative API
147
+
148
+ Access via `inputRef`:
149
+
150
+ ```tsx
151
+ let api;
152
+
153
+ <ElasticInput
154
+ fields={fields}
155
+ inputRef={(ref) => { api = ref; }}
156
+ />
157
+
158
+ // Later:
159
+ api.getValue(); // Returns current query string
160
+ api.setValue('status:active');// Sets query programmatically
161
+ api.focus(); // Focuses the input
162
+ api.blur(); // Blurs the input
163
+ api.getAST(); // Returns the parsed AST
164
+ api.getValidationErrors(); // Returns current validation errors
165
+ ```
166
+
167
+ ## Validation
168
+
169
+ Validation runs automatically on every input change. Errors appear as red wavy underlines beneath the invalid text. Hover over a squiggly to see the error message.
170
+
171
+ ### Deferred Display
172
+
173
+ Errors are only shown visually after the cursor moves away from the error range, so the user isn't distracted while still typing.
174
+
175
+ ### External Error Access
176
+
177
+ Use `onValidationChange` to receive errors outside the component:
178
+
179
+ ```tsx
180
+ <ElasticInput
181
+ fields={fields}
182
+ onValidationChange={(errors) => {
183
+ // errors: Array<{ message: string, start: number, end: number, field?: string }>
184
+ if (errors.length > 0) {
185
+ console.log('Validation errors:', errors);
186
+ }
187
+ }}
188
+ />
189
+ ```
190
+
191
+ Or use the imperative API:
192
+
193
+ ```tsx
194
+ const errors = api.getValidationErrors();
195
+ ```
196
+
197
+ ### Custom Validators
198
+
199
+ ```typescript
200
+ const fields: FieldConfig[] = [
201
+ {
202
+ name: 'rating',
203
+ type: 'number',
204
+ validate: (value) => {
205
+ const n = Number(value);
206
+ return (n >= 1 && n <= 5) ? null : 'Rating must be between 1 and 5';
207
+ },
208
+ },
209
+ {
210
+ name: 'phone',
211
+ type: 'string',
212
+ validate: (value) =>
213
+ /^[\d\-\+\(\)\s]+$/.test(value) ? null : 'Invalid phone format',
214
+ },
215
+ ];
216
+ ```
217
+
218
+ ## Saved Searches
219
+
220
+ Reference saved queries with `#`:
221
+
222
+ ```tsx
223
+ const savedSearches = [
224
+ { id: '1', name: 'vip-active', query: 'status:active AND is_vip:true', description: 'All active VIPs' },
225
+ { id: '2', name: 'high-value', query: 'deal_value:>10000', description: 'Deals over $10k' },
226
+ ];
227
+
228
+ <ElasticInput
229
+ fields={fields}
230
+ savedSearches={savedSearches}
231
+ />
232
+ ```
233
+
234
+ Type `#` in the input to see saved search suggestions. Selecting one replaces the `#token` with the saved query text.
235
+
236
+ Supports async loading:
237
+
238
+ ```tsx
239
+ <ElasticInput
240
+ fields={fields}
241
+ savedSearches={() => fetch('/api/saved-searches').then(r => r.json())}
242
+ />
243
+ ```
244
+
245
+ ## Search History
246
+
247
+ Reference previous searches with `!`:
248
+
249
+ ```tsx
250
+ const history = [
251
+ { query: 'status:active AND deal_value:>5000', label: 'Active high-value deals' },
252
+ { query: 'level:ERROR AND service:api-gateway', label: 'API errors' },
253
+ ];
254
+
255
+ <ElasticInput
256
+ fields={fields}
257
+ searchHistory={history}
258
+ />
259
+ ```
260
+
261
+ Type `!` to see history suggestions. Selecting one inserts the query (wrapped in parentheses if it contains boolean operators).
262
+
263
+ ## Async Suggestions
264
+
265
+ Provide dynamic suggestions for field values:
266
+
267
+ ```tsx
268
+ <ElasticInput
269
+ fields={fields}
270
+ fetchSuggestions={async (fieldName, partial) => {
271
+ const res = await fetch(`/api/suggest?field=${fieldName}&q=${partial}`);
272
+ const data = await res.json();
273
+ return data.map(item => ({
274
+ text: item.value,
275
+ label: item.display,
276
+ description: item.desc,
277
+ type: fieldName,
278
+ }));
279
+ }}
280
+ dropdown={{ suggestDebounceMs: 300 }}
281
+ />
282
+ ```
283
+
284
+ ## Keyboard Shortcuts
285
+
286
+ | Key | Context | Behavior |
287
+ |-----|---------|----------|
288
+ | Tab | Dropdown open | Accept suggestion; append space if completing a value/search/history at end of input |
289
+ | Enter | Dropdown open (field value) | Accept value and submit search |
290
+ | Enter | Dropdown open (other) | Accept suggestion without submitting |
291
+ | Enter | No dropdown | Submit search |
292
+ | Ctrl+Enter | Any | Force submit, bypassing autocomplete |
293
+ | Escape | Dropdown/picker open | Close without accepting |
294
+ | Arrow Up/Down | Dropdown open | Navigate suggestions |
295
+ | Arrow Left/Right | Any | Move cursor; suggestions update for new position |
296
+
297
+ ## Theming
298
+
299
+ ### Colors
300
+
301
+ ```tsx
302
+ import { ElasticInput, DARK_COLORS } from 'elastic-input';
303
+ import type { ColorConfig } from 'elastic-input';
304
+
305
+ // Use the built-in dark preset
306
+ <ElasticInput fields={fields} colors={DARK_COLORS} />
307
+
308
+ // Or customize individual colors
309
+ const myColors: ColorConfig = {
310
+ fieldName: '#0550ae',
311
+ fieldValue: '#1a7f37',
312
+ operator: '#cf222e',
313
+ booleanOp: '#8250df',
314
+ quoted: '#0a3069',
315
+ paren: '#656d76',
316
+ savedSearch: '#bf8700',
317
+ historyRef: '#6639ba',
318
+ wildcard: '#953800',
319
+ error: '#cf222e',
320
+ background: '#ffffff',
321
+ text: '#1f2328',
322
+ placeholder: '#656d76',
323
+ cursor: '#1f2328',
324
+ dropdownSelected: '#0969da',
325
+ dropdownHover: '#f6f8fa',
326
+ };
327
+ ```
328
+
329
+ ### Structural Styles
330
+
331
+ ```tsx
332
+ import { ElasticInput, DARK_STYLES } from 'elastic-input';
333
+ import type { StyleConfig } from 'elastic-input';
334
+
335
+ const myStyles: StyleConfig = {
336
+ fontFamily: "'JetBrains Mono', monospace",
337
+ fontSize: '16px',
338
+ inputPadding: '12px 16px',
339
+ inputBorderRadius: '12px',
340
+ inputFocusBorderColor: '#7c3aed',
341
+ inputFocusShadow: '0 0 0 3px rgba(124, 58, 237, 0.3)',
342
+ dropdownBorderRadius: '12px',
343
+ dropdownShadow: '0 12px 32px rgba(0, 0, 0, 0.2)',
344
+ };
345
+
346
+ <ElasticInput fields={fields} styles={myStyles} />
347
+ ```
348
+
349
+ ### Full Dark Mode
350
+
351
+ ```tsx
352
+ import { DARK_COLORS, DARK_STYLES } from 'elastic-input';
353
+
354
+ <ElasticInput
355
+ fields={fields}
356
+ colors={DARK_COLORS}
357
+ styles={DARK_STYLES}
358
+ />
359
+ ```
360
+
361
+ ## AST Output
362
+
363
+ The `onSearch` and `onChange` callbacks receive a parsed AST alongside the raw query string. AST node types:
364
+
365
+ | Node Type | Description | Example |
366
+ |-----------|-------------|---------|
367
+ | `FieldValue` | Field:value pair | `status:active` |
368
+ | `BooleanExpr` | AND/OR expression | `a AND b` |
369
+ | `Not` | Negation | `NOT x`, `-x` |
370
+ | `Group` | Parenthesized group | `(a OR b)` |
371
+ | `BareTerm` | Unstructured text | `hello`, `"phrase"` |
372
+ | `SavedSearch` | Saved search ref | `#my-search` |
373
+ | `HistoryRef` | History ref | `!recent` |
374
+ | `Error` | Parse error | malformed input |
375
+
376
+ All nodes include `start` and `end` character offsets for mapping back to the source text.
377
+
378
+ ## Advanced: Using the Parser Directly
379
+
380
+ The lexer, parser, and validator are exported for standalone use:
381
+
382
+ ```typescript
383
+ import { Lexer, Parser, Validator } from 'elastic-input';
384
+ import type { FieldConfig } from 'elastic-input';
385
+
386
+ const query = 'status:active AND price:>100';
387
+
388
+ // Tokenize
389
+ const lexer = new Lexer(query);
390
+ const tokens = lexer.tokenize();
391
+
392
+ // Parse to AST
393
+ const parser = new Parser(tokens);
394
+ const ast = parser.parse();
395
+
396
+ // Validate
397
+ const fields: FieldConfig[] = [
398
+ { name: 'status', type: 'string', suggestions: ['active', 'inactive'] },
399
+ { name: 'price', type: 'number' },
400
+ ];
401
+ const validator = new Validator(fields);
402
+ const errors = validator.validate(ast);
403
+ ```
404
+
405
+ ## Standalone Syntax Highlighting
406
+
407
+ The syntax highlighter is a pure function — no React or DOM required. Use it to render highlighted queries anywhere (read-only displays, logs, documentation):
408
+
409
+ ```typescript
410
+ import { Lexer, buildHighlightedHTML, DEFAULT_COLORS } from 'elastic-input';
411
+
412
+ const tokens = new Lexer('status:active AND price:>100').tokenize();
413
+ const html = buildHighlightedHTML(tokens, DEFAULT_COLORS);
414
+ // Returns an HTML string with inline styles — set innerHTML on any element
415
+ ```
416
+
417
+ Pass `HighlightOptions` for matched-paren highlighting:
418
+
419
+ ```typescript
420
+ buildHighlightedHTML(tokens, DEFAULT_COLORS, { cursorOffset: 5 });
421
+ ```
422
+
423
+ ## Requirements
424
+
425
+ ### Runtime (Browser)
426
+
427
+ | Browser | Minimum Version |
428
+ |---------|----------------|
429
+ | Chrome | 85+ |
430
+ | Firefox | 103+ |
431
+ | Safari | 16.4+ |
432
+ | Edge | 85+ (Chromium) |
433
+
434
+ The compiled output targets **ES2018**. Uses modern Range/Selection APIs for text insertion (no deprecated `document.execCommand`).
435
+
436
+ ### Build / Development
437
+
438
+ | Dependency | Minimum Version |
439
+ |------------|----------------|
440
+ | Node.js | 18.0.0+ |
441
+ | React | 16.8.0+ (hooks) |
442
+ | React DOM | 16.8.0+ |
443
+
444
+ These constraints are also declared in `package.json` via `engines` and `browserslist`.
445
+
446
+ No runtime dependencies beyond React/ReactDOM.
447
+
448
+ ## Development
449
+
450
+ ```bash
451
+ yarn install
452
+ yarn dev # Start demo dev server
453
+ yarn test # Run tests
454
+ yarn test:watch # Run tests in watch mode
455
+ yarn build # Build library (ES + CJS)
456
+ yarn build:demo # Build demo page
457
+ ```
458
+
459
+ ## License
460
+
461
+ MIT
@@ -0,0 +1,36 @@
1
+ import { Token } from '../lexer/tokens';
2
+ import { CursorContext } from '../parser/Parser';
3
+ import { FieldConfig, SavedSearch, HistoryEntry } from '../types';
4
+ import { Suggestion } from './suggestionTypes';
5
+
6
+ export interface AutocompleteResult {
7
+ suggestions: Suggestion[];
8
+ showDatePicker: boolean;
9
+ dateFieldName?: string;
10
+ context: CursorContext;
11
+ }
12
+ export interface AutocompleteOptions {
13
+ showSavedSearchHint?: boolean;
14
+ showHistoryHint?: boolean;
15
+ }
16
+ export declare class AutocompleteEngine {
17
+ private fields;
18
+ private fieldMap;
19
+ private savedSearches;
20
+ private searchHistory;
21
+ private maxSuggestions;
22
+ private options;
23
+ constructor(fields: FieldConfig[], savedSearches?: SavedSearch[], searchHistory?: HistoryEntry[], maxSuggestions?: number, options?: AutocompleteOptions);
24
+ /** Resolve a field name (or alias) to its FieldConfig. */
25
+ resolveField(fieldName: string): FieldConfig | undefined;
26
+ updateSavedSearches(searches: SavedSearch[]): void;
27
+ updateSearchHistory(history: HistoryEntry[]): void;
28
+ getSuggestions(tokens: Token[], cursorOffset: number): AutocompleteResult;
29
+ private getFieldSuggestions;
30
+ private getValueSuggestions;
31
+ private getOperatorSuggestions;
32
+ private sortByPriority;
33
+ private getSpecialHints;
34
+ private getSavedSearchSuggestions;
35
+ private getHistorySuggestions;
36
+ }
@@ -0,0 +1,16 @@
1
+ export interface Suggestion {
2
+ text: string;
3
+ label: string;
4
+ description?: string;
5
+ type?: string;
6
+ replaceStart: number;
7
+ replaceEnd: number;
8
+ matchPartial?: string;
9
+ /** Higher priority items appear first. Operators=30, hints=20, fields=10. */
10
+ priority?: number;
11
+ /** Custom React content to render instead of the default label/description. Used by `renderFieldHint`. */
12
+ customContent?: any;
13
+ /** Original source data (HistoryEntry or SavedSearch) for custom renderers. */
14
+ sourceData?: any;
15
+ }
16
+ export type SuggestionSource = 'field' | 'value' | 'operator' | 'savedSearch' | 'history' | 'hint';
@@ -0,0 +1,24 @@
1
+ import { Suggestion } from '../autocomplete/suggestionTypes';
2
+ import { ColorConfig, StyleConfig, HistoryEntry, SavedSearch } from '../types';
3
+ import { CursorContext } from '../parser/Parser';
4
+ import * as React from 'react';
5
+ interface AutocompleteDropdownProps {
6
+ suggestions: Suggestion[];
7
+ selectedIndex: number;
8
+ onSelect: (suggestion: Suggestion) => void;
9
+ position: {
10
+ top: number;
11
+ left: number;
12
+ } | null;
13
+ colors?: ColorConfig;
14
+ styles?: StyleConfig;
15
+ visible: boolean;
16
+ /** When set, the dropdown uses this fixed width instead of min/max width constraints. */
17
+ fixedWidth?: number;
18
+ renderHistoryItem?: (entry: HistoryEntry, isSelected: boolean) => React.ReactNode | null | undefined;
19
+ renderSavedSearchItem?: (search: SavedSearch, isSelected: boolean) => React.ReactNode | null | undefined;
20
+ renderDropdownHeader?: (context: CursorContext) => React.ReactNode | null | undefined;
21
+ cursorContext?: CursorContext | null;
22
+ }
23
+ export declare function AutocompleteDropdown({ suggestions, selectedIndex, onSelect, position, colors, styles, visible, fixedWidth, renderHistoryItem, renderSavedSearchItem, renderDropdownHeader, cursorContext, }: AutocompleteDropdownProps): React.ReactPortal | null;
24
+ export {};
@@ -0,0 +1,9 @@
1
+ import { ColorConfig } from '../types';
2
+ import * as React from 'react';
3
+ interface DatePickerProps {
4
+ onSelect: (dateStr: string) => void;
5
+ colors?: ColorConfig;
6
+ selectedDate?: string;
7
+ }
8
+ export declare function DatePicker({ onSelect, colors, selectedDate }: DatePickerProps): React.JSX.Element;
9
+ export {};
@@ -0,0 +1,16 @@
1
+ import { ColorConfig, StyleConfig } from '../types';
2
+ import * as React from 'react';
3
+ interface DateRangePickerProps {
4
+ onSelect: (dateStr: string) => void;
5
+ colors?: ColorConfig;
6
+ styles?: StyleConfig;
7
+ initialMode?: 'single' | 'range';
8
+ initialStart?: Date | null;
9
+ initialEnd?: Date | null;
10
+ presets?: {
11
+ label: string;
12
+ value: string;
13
+ }[];
14
+ }
15
+ export declare function DateRangePicker({ onSelect, colors, styles: styleConfig, initialMode, initialStart, initialEnd, presets: presetsProp }: DateRangePickerProps): React.JSX.Element;
16
+ export {};
@@ -0,0 +1,39 @@
1
+ import { ElasticInputProps } from '../types';
2
+ import * as React from 'react';
3
+ export interface DatePickerInit {
4
+ mode: 'single' | 'range';
5
+ start: Date | null;
6
+ end: Date | null;
7
+ }
8
+ /**
9
+ * Compute the date picker initialization state from an autocomplete result.
10
+ * Returns { mode: 'range', start, end } when cursor is inside a range
11
+ * expression on a date field, or null for single-date (FIELD_VALUE) contexts.
12
+ */
13
+ export declare function computeDatePickerInit(context: {
14
+ type: string;
15
+ partial?: string;
16
+ token?: {
17
+ value: string;
18
+ };
19
+ }): DatePickerInit | null;
20
+ /**
21
+ * Determine whether the date picker needs to be unmounted and remounted.
22
+ * This is necessary when initial state changes because DateRangePicker
23
+ * uses useState which only reads the initial value on first mount.
24
+ * Returns true when mode or selected dates differ.
25
+ */
26
+ export declare function shouldRemountDatePicker(prevInit: DatePickerInit | null, newInit: DatePickerInit | null): boolean;
27
+ /**
28
+ * A rich search input component with Elasticsearch query_string syntax support.
29
+ *
30
+ * Features syntax highlighting, autocomplete, field validation, date picking,
31
+ * saved search/history references, and real-time error squiggles.
32
+ *
33
+ * Pasted text is automatically normalized (smart quotes → ASCII, em dashes → hyphens, etc.).
34
+ * Selected text can be wrapped with brackets/quotes by typing the opening character (VS Code style).
35
+ *
36
+ * @see {@link ElasticInputProps} for available props
37
+ * @see {@link ElasticInputAPI} for the imperative API (via `inputRef`)
38
+ */
39
+ export declare function ElasticInput(props: ElasticInputProps): React.JSX.Element;
@@ -0,0 +1,7 @@
1
+ import { Token } from '../lexer/tokens';
2
+ import { ColorConfig } from '../types';
3
+
4
+ export interface HighlightOptions {
5
+ cursorOffset?: number;
6
+ }
7
+ export declare function buildHighlightedHTML(tokens: Token[], colorConfig?: ColorConfig, options?: HighlightOptions): string;
@@ -0,0 +1,13 @@
1
+ import { ValidationError } from '../validation/Validator';
2
+ import { ColorConfig, StyleConfig } from '../types';
3
+ import * as React from 'react';
4
+ interface ValidationSquigglesProps {
5
+ errors: ValidationError[];
6
+ editorRef: HTMLDivElement | null;
7
+ cursorOffset: number;
8
+ colors?: ColorConfig;
9
+ styles?: StyleConfig;
10
+ containerRef?: HTMLDivElement | null;
11
+ }
12
+ export declare function ValidationSquiggles({ errors, editorRef, cursorOffset, colors, styles, containerRef }: ValidationSquigglesProps): React.JSX.Element | null;
13
+ export {};
@@ -0,0 +1,18 @@
1
+ import { ColorConfig, StyleConfig } from './types';
2
+
3
+ /** Default color palette for light backgrounds (GitHub-inspired). */
4
+ export declare const DEFAULT_COLORS: Required<ColorConfig>;
5
+ /** Dark mode color palette (GitHub Dark-inspired). */
6
+ export declare const DARK_COLORS: Required<ColorConfig>;
7
+ /** Default layout/structural styles for light mode. */
8
+ export declare const DEFAULT_STYLES: Required<StyleConfig>;
9
+ /** Dark mode style overrides. Extends `DEFAULT_STYLES` with darker borders, shadows, and badges. */
10
+ export declare const DARK_STYLES: Required<StyleConfig>;
11
+ /** Recognized boolean operators in query syntax. */
12
+ export declare const BOOLEAN_OPERATORS: string[];
13
+ /** Recognized comparison operators for numeric and date fields. */
14
+ export declare const COMPARISON_OPERATORS: string[];
15
+ /** Default debounce delay (ms) for async `fetchSuggestions` calls. */
16
+ export declare const DEFAULT_DEBOUNCE_MS = 200;
17
+ /** Default maximum number of suggestions shown in the autocomplete dropdown. */
18
+ export declare const DEFAULT_MAX_SUGGESTIONS = 10;