@neo4j-cypher/react-codemirror
Advanced tools
Comparing version 2.0.0-canary-48fd427 to 2.0.0-canary-50cc73e
# @neo4j-cypher/react-codemirror | ||
## 2.0.0-next.17 | ||
### Patch Changes | ||
- 245fb6a: Automatically opens autocompletions after "YIELD " | ||
- Updated dependencies [8ec797d] | ||
- Updated dependencies [7aa9c3a] | ||
- Updated dependencies [2be5469] | ||
- Updated dependencies [245fb6a] | ||
- Updated dependencies [c587b81] | ||
- Updated dependencies [3f8b64f] | ||
- Updated dependencies [043d766] | ||
- Updated dependencies [704d1c5] | ||
- @neo4j-cypher/language-support@2.0.0-next.14 | ||
## 2.0.0-next.16 | ||
### Patch Changes | ||
- Updated dependencies [84a12fc] | ||
- Updated dependencies [d329252] | ||
- Updated dependencies [b0e419e] | ||
- @neo4j-cypher/language-support@2.0.0-next.13 | ||
## 2.0.0-next.15 | ||
### Patch Changes | ||
- adc5b64: using custom light color theme for the cypher editor | ||
- Updated dependencies [88fbe63] | ||
- Updated dependencies [22081b0] | ||
- Updated dependencies [62ac442] | ||
- @neo4j-cypher/language-support@2.0.0-next.12 | ||
## 2.0.0-next.14 | ||
### Patch Changes | ||
- d85c1e0: Fixes bug with auto-completions | ||
- Updated dependencies [bccf518] | ||
- @neo4j-cypher/language-support@2.0.0-next.11 | ||
## 2.0.0-next.13 | ||
### Patch Changes | ||
- Updated dependencies [8760c02] | ||
- @neo4j-cypher/language-support@2.0.0-next.10 | ||
## 2.0.0-next.12 | ||
### Patch Changes | ||
- dcbe67d: Expose moveFocusOnTab property on the CypherEditor component to conditionally disable tab keymappings | ||
- e9621c8: Re-export language support from react codemirror | ||
- Updated dependencies [2e72ac8] | ||
- @neo4j-cypher/language-support@2.0.0-next.9 | ||
## 2.0.0-next.11 | ||
### Patch Changes | ||
- Updated dependencies [05663bd] | ||
- @neo4j-cypher/language-support@2.0.0-next.8 | ||
## 2.0.0-next.10 | ||
### Patch Changes | ||
- bb7e9d3: Simplify detection and handling of value prop updates | ||
## 2.0.0-next.9 | ||
### Patch Changes | ||
- fbd5f7e: allow signature help panel to render below editor when there's not enough space above it | ||
- 09dfae2: Add an ariaLabel prop to CypherEditor | ||
- 7154e94: Fix bug causing debouncing to override value | ||
- 62c152f: execute single line query on enter by default | ||
- cbfc75e: Fix a bug causing debounced value updates to get cancelled erroneously | ||
- 04ae35e: Set initial latestDispatchedValue and flush debounced changes onExecute | ||
Add tests for debounce behaviour | ||
- Updated dependencies [3661e9d] | ||
- Updated dependencies [b76af58] | ||
- Updated dependencies [21699b7] | ||
- Updated dependencies [6afc0e3] | ||
- Updated dependencies [39b924d] | ||
- @neo4j-cypher/language-support@2.0.0-next.7 | ||
## 2.0.0-next.8 | ||
@@ -4,0 +93,0 @@ |
@@ -25,2 +25,9 @@ import { EditorState, Extension } from '@codemirror/state'; | ||
/** | ||
* If true, pressing enter will add a new line to the editor and cmd/ctrl + enter will execute the query. | ||
* Otherwise pressing enter on a single line will execute the query. | ||
* | ||
* @default false | ||
*/ | ||
newLineOnEnter?: boolean; | ||
/** | ||
* The editor history navigable via up/down arrow keys. Order newest to oldest. | ||
@@ -72,3 +79,3 @@ * Add to this list with the `onExecute` callback for REPL style history. | ||
consoleCommands?: boolean; | ||
signatureInfoOnAutoCompletions?: boolean; | ||
cypher25?: boolean; | ||
}; | ||
@@ -133,5 +140,14 @@ /** | ||
/** | ||
* String value to assign to the aria-label attribute of the editor | ||
* String value to assign to the aria-label attribute of the editor. | ||
*/ | ||
ariaLabel?: string; | ||
/** | ||
* Whether keybindings for inserting indents with the Tab key should be disabled. | ||
* | ||
* true will not create keybindings for inserting indents. | ||
* false will create keybindings for inserting indents. | ||
* | ||
* @default false | ||
*/ | ||
moveFocusOnTab?: boolean; | ||
} | ||
@@ -156,2 +172,6 @@ type CypherEditorState = { | ||
/** | ||
* Format Cypher query | ||
*/ | ||
format(): void; | ||
/** | ||
* Focus the editor | ||
@@ -158,0 +178,0 @@ */ |
import { jsx as _jsx } from "react/jsx-runtime"; | ||
import { insertNewline } from '@codemirror/commands'; | ||
import { Annotation, Compartment, EditorState, } from '@codemirror/state'; | ||
import { EditorView, keymap, lineNumbers, placeholder, } from '@codemirror/view'; | ||
import { formatQuery, _internalFeatureFlags, } from '@neo4j-cypher/language-support'; | ||
import debounce from 'lodash.debounce'; | ||
import { Component, createRef } from 'react'; | ||
import { DEBOUNCE_TIME } from './constants'; | ||
import { replaceHistory, replMode as historyNavigation, } from './historyNavigation'; | ||
@@ -11,6 +14,32 @@ import { cypher } from './lang-cypher/langCypher'; | ||
import { getThemeExtension } from './themes'; | ||
const executeKeybinding = (onExecute) => onExecute | ||
? [ | ||
{ | ||
const format = (view) => { | ||
const doc = view.state.doc.toString(); | ||
const { formattedString, newCursorPos } = formatQuery(doc, view.state.selection.main.anchor); | ||
view.dispatch({ | ||
changes: { | ||
from: 0, | ||
to: doc.length, | ||
insert: formattedString, | ||
}, | ||
selection: { anchor: newCursorPos }, | ||
}); | ||
}; | ||
const executeKeybinding = (onExecute, newLineOnEnter, flush) => { | ||
const keybindings = { | ||
'Shift-Enter': { | ||
key: 'Shift-Enter', | ||
run: insertNewline, | ||
}, | ||
'Ctrl-Enter': { | ||
key: 'Ctrl-Enter', | ||
run: () => true, | ||
}, | ||
Enter: { | ||
key: 'Enter', | ||
run: insertNewline, | ||
}, | ||
}; | ||
if (onExecute) { | ||
keybindings['Ctrl-Enter'] = { | ||
key: 'Ctrl-Enter', | ||
mac: 'Mod-Enter', | ||
@@ -21,2 +50,3 @@ preventDefault: true, | ||
if (doc.trim() !== '') { | ||
flush?.(); | ||
onExecute(doc); | ||
@@ -26,5 +56,25 @@ } | ||
}, | ||
}, | ||
] | ||
: []; | ||
}; | ||
if (!newLineOnEnter) { | ||
keybindings['Enter'] = { | ||
key: 'Enter', | ||
preventDefault: true, | ||
run: (view) => { | ||
const doc = view.state.doc.toString(); | ||
if (doc.includes('\n')) { | ||
// Returning false means the event will mark the event | ||
// as not handled and the default behavior will be executed | ||
return false; | ||
} | ||
if (doc.trim() !== '') { | ||
flush?.(); | ||
onExecute(doc); | ||
} | ||
return true; | ||
}, | ||
}; | ||
} | ||
} | ||
return Object.values(keybindings); | ||
}; | ||
const themeCompartment = new Compartment(); | ||
@@ -58,2 +108,8 @@ const keyBindingCompartment = new Compartment(); | ||
/** | ||
* Format Cypher query | ||
*/ | ||
format() { | ||
format(this.editorView.current); | ||
} | ||
/** | ||
* Focus the editor | ||
@@ -98,8 +154,13 @@ */ | ||
lineNumbers: true, | ||
newLineOnEnter: false, | ||
moveFocusOnTab: false, | ||
}; | ||
debouncedOnChange = this.props.onChange | ||
? debounce(this.props.onChange, 200) | ||
? debounce(((value, viewUpdate) => { | ||
this.props.onChange(value, viewUpdate); | ||
}), DEBOUNCE_TIME) | ||
: undefined; | ||
componentDidMount() { | ||
const { theme, extraKeybindings, lineWrap, overrideThemeBackgroundColor, schema, lint, showSignatureTooltipBelow, featureFlags, onExecute, } = this.props; | ||
const { theme, extraKeybindings, lineWrap, overrideThemeBackgroundColor, schema, lint, showSignatureTooltipBelow, featureFlags, onExecute, newLineOnEnter, } = this.props; | ||
_internalFeatureFlags.cypher25 = featureFlags?.cypher25 ?? false; | ||
this.schemaRef.current = { | ||
@@ -135,5 +196,8 @@ schema, | ||
extensions: [ | ||
keyBindingCompartment.of(keymap.of([...executeKeybinding(onExecute), ...extraKeybindings])), | ||
keyBindingCompartment.of(keymap.of([ | ||
...executeKeybinding(onExecute, newLineOnEnter, () => this.debouncedOnChange?.flush()), | ||
...extraKeybindings, | ||
])), | ||
historyNavigation(this.props), | ||
basicNeo4jSetup(), | ||
basicNeo4jSetup(this.props), | ||
themeCompartment.of(themeExtension), | ||
@@ -179,3 +243,6 @@ changeListener, | ||
const currentCmValue = this.editorView.current.state?.doc.toString() ?? ''; | ||
if (this.props.value !== undefined && currentCmValue !== this.props.value) { | ||
if (this.props.value !== undefined && // If the component becomes uncontolled, we just leave the value as is | ||
this.props.value !== prevProps.value && // The value prop has changed, we need to update the editor | ||
this.props.value !== currentCmValue // No need to dispatch an update if the value is the same | ||
) { | ||
this.editorView.current.dispatch({ | ||
@@ -221,3 +288,3 @@ changes: { | ||
effects: keyBindingCompartment.reconfigure(keymap.of([ | ||
...executeKeybinding(this.props.onExecute), | ||
...executeKeybinding(this.props.onExecute, this.props.newLineOnEnter, () => this.debouncedOnChange?.flush()), | ||
...this.props.extraKeybindings, | ||
@@ -224,0 +291,0 @@ ])), |
import { jsx as _jsx } from "react/jsx-runtime"; | ||
/* eslint-disable @typescript-eslint/unbound-method */ | ||
import { testData } from '@neo4j-cypher/language-support'; | ||
@@ -41,2 +42,20 @@ import { expect, test } from '@playwright/experimental-ct-react'; | ||
}); | ||
test('get completions when typing in controlled component', async ({ mount, page, }) => { | ||
let value = ''; | ||
const onChange = (val) => { | ||
value = val; | ||
void component.update(_jsx(CypherEditor, { value: val, onChange: onChange })); | ||
}; | ||
const component = await mount(_jsx(CypherEditor, { value: value, onChange: onChange })); | ||
const textField = page.getByRole('textbox'); | ||
await textField.fill('RETU'); | ||
await page.waitForTimeout(500); // wait for debounce | ||
await expect(page.locator('.cm-tooltip-autocomplete').getByText('RETURN')).toBeVisible(); | ||
// We need to wait for the editor to realise there is a completion open | ||
// so that it does not just indent with tab key | ||
await page.waitForTimeout(500); | ||
await textField.press('Tab'); | ||
await expect(page.locator('.cm-tooltip-autocomplete')).not.toBeVisible(); | ||
await expect(component).toContainText('RETURN'); | ||
}); | ||
test('can complete labels', async ({ mount, page }) => { | ||
@@ -52,2 +71,14 @@ const component = await mount(_jsx(CypherEditor, { schema: { | ||
}); | ||
test('can complete properties with backticks', async ({ mount, page }) => { | ||
const component = await mount(_jsx(CypherEditor, { schema: { | ||
propertyKeys: ['foo bar'], | ||
} })); | ||
const textField = page.getByRole('textbox'); | ||
await textField.fill('MATCH (n) RETURN n.foo'); | ||
await textField.press('Escape'); | ||
await textField.press('Control+ '); | ||
await page.locator('.cm-tooltip-autocomplete').getByText('foo bar').click(); | ||
await expect(page.locator('.cm-tooltip-autocomplete')).not.toBeVisible(); | ||
await expect(component).toContainText('MATCH (n) RETURN n.`foo bar`'); | ||
}); | ||
test('can update dbschema', async ({ mount, page }) => { | ||
@@ -79,8 +110,30 @@ const component = await mount(_jsx(CypherEditor, { schema: { | ||
}); | ||
test('can complete YIELD clauses without manual trigger', async ({ page, mount, }) => { | ||
const component = await mount(_jsx(CypherEditor, { schema: { | ||
procedures: testData.mockSchema.procedures, | ||
} })); | ||
const textField = page.getByRole('textbox'); | ||
await textField.fill('CALL dbms.components() YIELD '); | ||
await page.locator('.cm-tooltip-autocomplete').getByText('edition').click(); | ||
await expect(page.locator('.cm-tooltip-autocomplete')).not.toBeVisible(); | ||
await expect(component).toContainText('CALL dbms.components() YIELD edition'); | ||
}); | ||
test('automatic yield trigger is not case sensitive', async ({ page, mount, }) => { | ||
const component = await mount(_jsx(CypherEditor, { schema: { | ||
procedures: testData.mockSchema.procedures, | ||
} })); | ||
const textField = page.getByRole('textbox'); | ||
await textField.fill('CALL dbms.components() yIeLd '); | ||
await page.locator('.cm-tooltip-autocomplete').getByText('edition').click(); | ||
await expect(page.locator('.cm-tooltip-autocomplete')).not.toBeVisible(); | ||
await expect(component).toContainText('CALL dbms.components() yIeLd edition'); | ||
}); | ||
test('can complete functions', async ({ page, mount }) => { | ||
const component = await mount(_jsx(CypherEditor, { schema: { | ||
functions: { | ||
function123: { | ||
...testData.emptyFunction, | ||
name: 'function123', | ||
'CYPHER 5': { | ||
function123: { | ||
...testData.emptyFunction, | ||
name: 'function123', | ||
}, | ||
}, | ||
@@ -101,3 +154,5 @@ }, | ||
procedures: { | ||
'db.ping': { ...testData.emptyProcedure, name: 'db.ping' }, | ||
'CYPHER 5': { | ||
'db.ping': { ...testData.emptyProcedure, name: 'db.ping' }, | ||
}, | ||
}, | ||
@@ -150,7 +205,5 @@ } })); | ||
test('shows signature help information on auto-completion for procedures', async ({ page, mount, }) => { | ||
await mount(_jsx(CypherEditor, { schema: testData.mockSchema, featureFlags: { | ||
signatureInfoOnAutoCompletions: true, | ||
} })); | ||
await mount(_jsx(CypherEditor, { schema: testData.mockSchema })); | ||
const procName = 'apoc.periodic.iterate'; | ||
const procedure = testData.mockSchema.procedures[procName]; | ||
const procedure = testData.mockSchema.procedures['CYPHER 5'][procName]; | ||
const textField = page.getByRole('textbox'); | ||
@@ -164,7 +217,5 @@ await textField.fill('CALL apoc.periodic.'); | ||
test('shows signature help information on auto-completion for functions', async ({ page, mount, }) => { | ||
await mount(_jsx(CypherEditor, { schema: testData.mockSchema, featureFlags: { | ||
signatureInfoOnAutoCompletions: true, | ||
} })); | ||
await mount(_jsx(CypherEditor, { schema: testData.mockSchema })); | ||
const fnName = 'apoc.coll.combinations'; | ||
const fn = testData.mockSchema.functions[fnName]; | ||
const fn = testData.mockSchema.functions['CYPHER 5'][fnName]; | ||
const textField = page.getByRole('textbox'); | ||
@@ -180,5 +231,7 @@ await textField.fill('RETURN apoc.coll.'); | ||
await mount(_jsx(CypherEditor, { schema: { | ||
procedures: { [procName]: testData.mockSchema.procedures[procName] }, | ||
}, featureFlags: { | ||
signatureInfoOnAutoCompletions: true, | ||
procedures: { | ||
'CYPHER 5': { | ||
[procName]: testData.mockSchema.procedures['CYPHER 5'][procName], | ||
}, | ||
}, | ||
} })); | ||
@@ -189,3 +242,3 @@ const textField = page.getByRole('textbox'); | ||
// and trusting the CSS is making this truly strikethrough | ||
await expect(page.locator('.cm-deprecated-completion')).toBeVisible(); | ||
await expect(page.locator('.cm-deprecated-element')).toBeVisible(); | ||
}); | ||
@@ -195,5 +248,7 @@ test('shows deprecated function as strikethrough on auto-completion', async ({ page, mount, }) => { | ||
await mount(_jsx(CypherEditor, { schema: { | ||
functions: { [fnName]: testData.mockSchema.functions[fnName] }, | ||
}, featureFlags: { | ||
signatureInfoOnAutoCompletions: true, | ||
functions: { | ||
'CYPHER 5': { | ||
[fnName]: testData.mockSchema.functions['CYPHER 5'][fnName], | ||
}, | ||
}, | ||
} })); | ||
@@ -204,16 +259,7 @@ const textField = page.getByRole('textbox'); | ||
// and trusting the CSS is making this truly strikethrough | ||
await expect(page.locator('.cm-deprecated-completion')).toBeVisible(); | ||
await expect(page.locator('.cm-deprecated-element')).toBeVisible(); | ||
}); | ||
test('does not signature help information on auto-completion if flag not enabled explicitly', async ({ page, mount, }) => { | ||
test('does not signature help information on auto-completion if docs and signature are empty', async ({ page, mount, }) => { | ||
await mount(_jsx(CypherEditor, { schema: testData.mockSchema })); | ||
const textField = page.getByRole('textbox'); | ||
await textField.fill('CALL apoc.periodic.'); | ||
await expect(page.locator('.cm-tooltip-autocomplete')).toBeVisible(); | ||
await expect(page.locator('.cm-completionInfo')).not.toBeVisible(); | ||
}); | ||
test('does not signature help information on auto-completion if docs and signature are empty', async ({ page, mount, }) => { | ||
await mount(_jsx(CypherEditor, { schema: testData.mockSchema, featureFlags: { | ||
signatureInfoOnAutoCompletions: true, | ||
} })); | ||
const textField = page.getByRole('textbox'); | ||
await textField.fill('C'); | ||
@@ -226,11 +272,11 @@ await expect(page.locator('.cm-tooltip-autocomplete')).toBeVisible(); | ||
procedures: { | ||
'db.ping': { | ||
...testData.emptyProcedure, | ||
description: 'foo', | ||
signature: '', | ||
name: 'db.ping', | ||
'CYPHER 5': { | ||
'db.ping': { | ||
...testData.emptyProcedure, | ||
description: 'foo', | ||
signature: '', | ||
name: 'db.ping', | ||
}, | ||
}, | ||
}, | ||
}, featureFlags: { | ||
signatureInfoOnAutoCompletions: true, | ||
} })); | ||
@@ -245,11 +291,11 @@ const textField = page.getByRole('textbox'); | ||
procedures: { | ||
'db.ping': { | ||
...testData.emptyProcedure, | ||
description: '', | ||
signature: 'foo', | ||
name: 'db.ping', | ||
'CYPHER 5': { | ||
'db.ping': { | ||
...testData.emptyProcedure, | ||
description: '', | ||
signature: 'foo', | ||
name: 'db.ping', | ||
}, | ||
}, | ||
}, | ||
}, featureFlags: { | ||
signatureInfoOnAutoCompletions: true, | ||
} })); | ||
@@ -261,2 +307,25 @@ const textField = page.getByRole('textbox'); | ||
}); | ||
test('completions depend on the Cypher version', async ({ page, mount }) => { | ||
await mount(_jsx(CypherEditor, { schema: { | ||
functions: { | ||
'CYPHER 5': { | ||
cypher5Function: { | ||
...testData.emptyFunction, | ||
name: 'cypher5Function', | ||
}, | ||
}, | ||
'CYPHER 25': { | ||
cypher25Function: { | ||
...testData.emptyFunction, | ||
name: 'cypher25Function', | ||
}, | ||
}, | ||
}, | ||
}, featureFlags: { cypher25: true } })); | ||
const textField = page.getByRole('textbox'); | ||
await textField.fill('CYPHER 5 RETURN cypher'); | ||
await expect(page.locator('.cm-tooltip-autocomplete').getByText('cypher5Function')).toBeVisible(); | ||
await textField.fill('CYPHER 25 RETURN cypher'); | ||
await expect(page.locator('.cm-tooltip-autocomplete').getByText('cypher25Function')).toBeVisible(); | ||
}); | ||
//# sourceMappingURL=autoCompletion.spec.js.map |
@@ -11,3 +11,4 @@ import type { Locator, Page } from 'playwright/test'; | ||
checkWarningMessage(queryChunk: string, expectedMsg: string): Promise<void>; | ||
checkNoNotificationMessage(type: 'error' | 'warning'): Promise<void>; | ||
private checkNotificationMessage; | ||
} |
@@ -45,6 +45,17 @@ import { expect } from '@playwright/experimental-ct-react'; | ||
} | ||
async checkNoNotificationMessage(type) { | ||
await this.page.waitForTimeout(10000); | ||
await expect(this.page.locator('.cm-lintRange-' + type)).toHaveCount(0, { | ||
timeout: 10000, | ||
}); | ||
await expect(this.page.locator('.cm-lintPoint-' + type)).toHaveCount(0, { | ||
timeout: 10000, | ||
}); | ||
} | ||
async checkNotificationMessage(type, queryChunk, expectedMsg) { | ||
await expect(this.page.locator('.cm-lintRange-' + type).last()).toBeVisible({ timeout: 3000 }); | ||
await expect(this.page.locator('.cm-lintRange-' + type).last()).toBeVisible({ timeout: 10000 }); | ||
await this.page.getByText(queryChunk, { exact: true }).hover(); | ||
await expect(this.page.locator('.cm-tooltip-hover').last()).toBeVisible(); | ||
await expect(this.page.locator('.cm-tooltip-hover').last()).toBeVisible({ | ||
timeout: 10000, | ||
}); | ||
await expect(this.page.getByText(expectedMsg)).toBeVisible(); | ||
@@ -51,0 +62,0 @@ /* Return the mouse to the beginning of the query and |
@@ -17,3 +17,5 @@ import { jsx as _jsx } from "react/jsx-runtime"; | ||
await expect(page.getByText('second')).toBeVisible(); | ||
// First arrow down is to get to end of line | ||
await editorPage.getEditor().press('ArrowDown'); | ||
await editorPage.getEditor().press('ArrowDown'); | ||
await expect(page.getByText('first')).toBeVisible(); | ||
@@ -23,4 +25,48 @@ await editorPage.getEditor().press('ArrowDown'); | ||
}); | ||
test('can execute queries and see them in history', async ({ page, mount }) => { | ||
test('can add new lines without onExecute', async ({ page, mount }) => { | ||
const editorPage = new CypherEditorPage(page); | ||
const editorComponent = await mount(_jsx(CypherEditor, {})); | ||
// Ctrl-Enter does nothing when onExecute is false | ||
await editorPage.getEditor().press('Control+Enter'); | ||
await expect(editorComponent).toHaveText('1\n', { | ||
useInnerText: true, | ||
}); | ||
// Enter adds new lines | ||
await editorPage.getEditor().fill('Brock'); | ||
await editorPage.getEditor().press('Enter'); | ||
await editorPage.getEditor().press('Enter'); | ||
await expect(editorComponent).toHaveText('1\n2\n3\nBrock', { | ||
useInnerText: true, | ||
}); | ||
// Shift-Enter adds new lines | ||
await editorPage.getEditor().press('Shift+Enter'); | ||
await editorPage.getEditor().press('Shift+Enter'); | ||
await expect(editorComponent).toHaveText('1\n2\n3\n4\n5\nBrock', { | ||
useInnerText: true, | ||
}); | ||
}); | ||
test('can add new lines with newLineOnEnter and without onExecute', async ({ page, mount, }) => { | ||
const editorPage = new CypherEditorPage(page); | ||
const editorComponent = await mount(_jsx(CypherEditor, { newLineOnEnter: true })); | ||
// Ctrl-Enter does nothing when onExecute is false | ||
await editorPage.getEditor().press('Control+Enter'); | ||
await expect(editorComponent).toHaveText('1\n', { | ||
useInnerText: true, | ||
}); | ||
// Enter adds new lines | ||
await editorPage.getEditor().fill('Brock'); | ||
await editorPage.getEditor().press('Enter'); | ||
await editorPage.getEditor().press('Enter'); | ||
await expect(editorComponent).toHaveText('1\n2\n3\nBrock', { | ||
useInnerText: true, | ||
}); | ||
// Shift-Enter adds new lines | ||
await editorPage.getEditor().press('Shift+Enter'); | ||
await editorPage.getEditor().press('Shift+Enter'); | ||
await expect(editorComponent).toHaveText('1\n2\n3\n4\n5\nBrock', { | ||
useInnerText: true, | ||
}); | ||
}); | ||
test('can execute queries and see them in history with newLineOnEnter', async ({ page, mount, }) => { | ||
const editorPage = new CypherEditorPage(page); | ||
const initialValue = `MATCH (n) | ||
@@ -32,3 +78,3 @@ RETURN n;`; | ||
}; | ||
const editor = await mount(_jsx(CypherEditor, { value: initialValue, history: history, onExecute: onExecute })); | ||
const editor = await mount(_jsx(CypherEditor, { value: initialValue, history: history, onExecute: onExecute, newLineOnEnter: true })); | ||
// Execute initial query | ||
@@ -43,3 +89,3 @@ await editorPage.getEditor().press('Control+Enter'); | ||
expect(history.length).toBe(1); | ||
// Ensure only enter doesn't execute query | ||
// Ensure cmd+enter is required in multiline | ||
await editorPage.getEditor().fill('multiline'); | ||
@@ -49,3 +95,3 @@ await editorPage.getEditor().press('Enter'); | ||
await editorPage.getEditor().press('Enter'); | ||
await editorPage.getEditor().press('Enter'); | ||
await editorPage.getEditor().press('Shift+Enter'); | ||
await page.keyboard.type('entry'); | ||
@@ -72,2 +118,4 @@ expect(history.length).toBe(1); | ||
// arrow movements don't matter until bottom is hit | ||
await editorPage.getEditor().press('ArrowDown'); | ||
await editorPage.getEditor().press('ArrowDown'); | ||
await editorPage.getEditor().press('ArrowUp'); | ||
@@ -77,2 +125,5 @@ await editorPage.getEditor().press('ArrowUp'); | ||
await editorPage.getEditor().press('ArrowDown'); | ||
await editorPage.getEditor().press('ArrowDown'); | ||
await editorPage.getEditor().press('ArrowDown'); | ||
await editorPage.getEditor().press('ArrowDown'); | ||
// editor still multiline | ||
@@ -84,2 +135,43 @@ await expect(page.getByText('multiline')).toBeVisible(); | ||
}); | ||
test('can execute queries without newLineOnEnter', async ({ page, mount }) => { | ||
const editorPage = new CypherEditorPage(page); | ||
const initialValue = 'Brock'; | ||
const history = []; | ||
const onExecute = (cmd) => { | ||
history.unshift(cmd); | ||
}; | ||
const editorComponent = await mount(_jsx(CypherEditor, { value: initialValue, onExecute: onExecute })); | ||
// Cmd/Control still executes initial query | ||
await editorPage.getEditor().press('Control+Enter'); | ||
await editorPage.getEditor().press('Meta+Enter'); | ||
expect(history.length).toBe(1); | ||
// Ensure query execution doesn't fire if the query is only whitespace | ||
await editorPage.getEditor().fill(' '); | ||
await editorPage.getEditor().press('Control+Enter'); | ||
await editorPage.getEditor().press('Meta+Enter'); | ||
await editorPage.getEditor().press('Enter'); | ||
expect(history.length).toBe(1); | ||
// Ensure enter executes query when in single line | ||
await editorPage.getEditor().fill('Misty'); | ||
await editorPage.getEditor().press('Enter'); | ||
expect(history.length).toBe(2); | ||
expect(history).toEqual(['Misty', 'Brock']); | ||
// Ensure cmd+enter is required in multiline | ||
await editorPage.getEditor().fill('multiline'); | ||
await editorPage.getEditor().press('Shift+Enter'); | ||
await editorPage.getEditor().press('Enter'); | ||
await editorPage.getEditor().press('A'); | ||
// line numbers and the text | ||
await expect(editorComponent).toHaveText('1\n2\n3\nmultiline\nA', { | ||
useInnerText: true, | ||
}); | ||
await editorPage.getEditor().press('Enter'); | ||
await editorPage.getEditor().press('Enter'); | ||
await editorPage.getEditor().press('Enter'); | ||
await editorPage.getEditor().press('Enter'); | ||
expect(history.length).toBe(2); | ||
await editorPage.getEditor().press('Control+Enter'); | ||
await editorPage.getEditor().press('Meta+Enter'); | ||
expect(history.length).toBe(3); | ||
}); | ||
test('can navigate with cmd+up as well', async ({ page, mount }) => { | ||
@@ -92,2 +184,3 @@ const editorPage = new CypherEditorPage(page); | ||
await mount(_jsx(CypherEditor, { value: initialValue, history: [ | ||
'first', | ||
`one | ||
@@ -97,3 +190,2 @@ multiline | ||
.`, | ||
'second', | ||
], onExecute: () => { | ||
@@ -103,18 +195,18 @@ /* needed to turn on history movements */ | ||
await editorPage.getEditor().press(metaUp); | ||
await expect(page.getByText('first')).toBeVisible(); | ||
// move in history | ||
await editorPage.getEditor().press(metaUp); | ||
await expect(page.getByText('multiline')).toBeVisible(); | ||
// Single meta up moves all the way to top of editor on mac | ||
// Single meta down moves all the way to top of editor on mac | ||
if (isMac) { | ||
await editorPage.getEditor().press(metaUp); | ||
await editorPage.getEditor().press(metaDown); | ||
} | ||
else { | ||
await editorPage.getEditor().press('ArrowUp'); | ||
await editorPage.getEditor().press('ArrowUp'); | ||
await editorPage.getEditor().press('ArrowUp'); | ||
await editorPage.getEditor().press('ArrowUp'); | ||
await editorPage.getEditor().press('ArrowDown'); | ||
await editorPage.getEditor().press('ArrowDown'); | ||
await editorPage.getEditor().press('ArrowDown'); | ||
await editorPage.getEditor().press('ArrowDown'); | ||
} | ||
// move in history | ||
await editorPage.getEditor().press(metaUp); | ||
await expect(page.getByText('second')).toBeVisible(); | ||
await editorPage.getEditor().press(metaDown); | ||
await expect(page.getByText('multiline')).toBeVisible(); | ||
await expect(page.getByText('first')).toBeVisible(); | ||
}); | ||
@@ -121,0 +213,0 @@ test('test onExecute', async ({ page, mount }) => { |
@@ -7,3 +7,4 @@ import { jsx as _jsx } from "react/jsx-runtime"; | ||
test.use({ viewport: { width: 1000, height: 500 } }); | ||
test('benchmarking & performance test session', async ({ mount, page }) => { | ||
test('benchmarking & performance test session', async ({ browserName, mount, page, }) => { | ||
test.skip(browserName !== 'chromium'); | ||
const client = await page.context().newCDPSession(page); | ||
@@ -50,3 +51,3 @@ if (process.env.BENCHMARKING === 'true') { | ||
await expect(component).toContainText('MATCH (n:Person) RETRN m'); | ||
await editorPage.checkErrorMessage('RETRN', 'Unexpected token. Did you mean RETURN?'); | ||
await editorPage.checkErrorMessage('RETRN', `Invalid input 'RETRN': expected a graph pattern, 'FOREACH', ',', 'ORDER BY', 'CALL', 'CREATE', 'LOAD CSV', 'DELETE', 'DETACH', 'FINISH', 'INSERT', 'LIMIT', 'MATCH', 'MERGE', 'NODETACH', 'OFFSET', 'OPTIONAL', 'REMOVE', 'RETURN', 'SET', 'SKIP', 'UNION', 'UNWIND', 'USE', 'USING', 'WHERE', 'WITH' or <EOF>`); | ||
await editorPage | ||
@@ -53,0 +54,0 @@ .getEditor() |
@@ -24,3 +24,5 @@ import { jsx as _jsx } from "react/jsx-runtime"; | ||
const textField = page.getByRole('textbox'); | ||
await textField.fill(''); | ||
await textField.fill('RETURN 12'); | ||
await expect(textField).toHaveText('RETURN 12'); | ||
// editor update is debounced, retry wait for debounced | ||
@@ -30,6 +32,2 @@ await expect(() => { | ||
}).toPass({ intervals: [300, 300, 1000] }); | ||
await page.keyboard.type('34'); | ||
await expect(() => { | ||
expect(editorValueCopy).toBe('RETURN 12'); | ||
}).toPass({ intervals: [300, 300, 1000] }); | ||
}); | ||
@@ -36,0 +34,0 @@ test('can complete RETURN', async ({ page, mount }) => { |
import { jsx as _jsx } from "react/jsx-runtime"; | ||
/* eslint-disable @typescript-eslint/unbound-method */ | ||
import { testData } from '@neo4j-cypher/language-support'; | ||
@@ -6,2 +7,3 @@ import { expect, test } from '@playwright/experimental-ct-react'; | ||
test.use({ viewport: { width: 1000, height: 500 } }); | ||
const importCsvProc = testData.mockSchema.procedures['CYPHER 5']['apoc.import.csv']; | ||
function testTooltip(tooltip, expectations) { | ||
@@ -12,3 +14,3 @@ const includes = expectations.includes ?? []; | ||
return expect(tooltip).toContainText(text, { | ||
timeout: 2000, | ||
timeout: 10000, | ||
}); | ||
@@ -18,3 +20,3 @@ })); | ||
return expect(tooltip).not.toContainText(text, { | ||
timeout: 2000, | ||
timeout: 10000, | ||
}); | ||
@@ -28,3 +30,3 @@ })); | ||
await expect(page.locator('.cm-signature-help-panel')).toBeVisible({ | ||
timeout: 2000, | ||
timeout: 10000, | ||
}); | ||
@@ -36,3 +38,3 @@ }); | ||
await expect(page.locator('.cm-signature-help-panel')).toBeVisible({ | ||
timeout: 2000, | ||
timeout: 10000, | ||
}); | ||
@@ -46,4 +48,5 @@ }); | ||
includes: [ | ||
'nodes :: LIST<MAP>', | ||
'Imports `NODE` and `RELATIONSHIP` values with the given labels and types from the provided CSV file', | ||
testData.mockSchema.procedures['CYPHER 5']['apoc.import.csv'] | ||
.argumentDescription[0].description, | ||
testData.mockSchema.procedures['CYPHER 5']['apoc.import.csv'].description, | ||
], | ||
@@ -58,4 +61,4 @@ }); | ||
includes: [ | ||
'nodes :: LIST<MAP>', | ||
'Imports `NODE` and `RELATIONSHIP` values with the given labels and types from the provided CSV file', | ||
importCsvProc.argumentDescription[0].description, | ||
importCsvProc.description, | ||
], | ||
@@ -70,4 +73,4 @@ }); | ||
includes: [ | ||
'rels :: LIST<MAP>', | ||
'Imports `NODE` and `RELATIONSHIP` values with the given labels and types from the provided CSV file', | ||
importCsvProc.argumentDescription[1].description, | ||
importCsvProc.description, | ||
], | ||
@@ -82,4 +85,4 @@ }); | ||
includes: [ | ||
'rels :: LIST<MAP>', | ||
'Imports `NODE` and `RELATIONSHIP` values with the given labels and types from the provided CSV file', | ||
importCsvProc.argumentDescription[1].description, | ||
importCsvProc.description, | ||
], | ||
@@ -151,3 +154,3 @@ }); | ||
await expect(page.locator('.cm-signature-help-panel')).not.toBeVisible({ | ||
timeout: 2000, | ||
timeout: 10000, | ||
}); | ||
@@ -159,3 +162,3 @@ }); | ||
await expect(page.locator('.cm-signature-help-panel')).not.toBeVisible({ | ||
timeout: 2000, | ||
timeout: 10000, | ||
}); | ||
@@ -169,3 +172,3 @@ }); | ||
await expect(page.locator('.cm-signature-help-panel.cm-tooltip-below')).toBeVisible({ | ||
timeout: 2000, | ||
timeout: 10000, | ||
}); | ||
@@ -179,3 +182,3 @@ }); | ||
await expect(page.locator('.cm-signature-help-panel.cm-tooltip-below')).toBeVisible({ | ||
timeout: 2000, | ||
timeout: 10000, | ||
}); | ||
@@ -189,5 +192,51 @@ }); | ||
await expect(page.locator('.cm-signature-help-panel.cm-tooltip-above')).toBeVisible({ | ||
timeout: 2000, | ||
timeout: 10000, | ||
}); | ||
}); | ||
test('Signature help depends on the Cypher version', async ({ page, mount, }) => { | ||
const cypher5ArgDescription = 'The Cypher 5 statement to run.'; | ||
const cypher25ArgDescription = 'The Cypher 25 statement to run.'; | ||
await mount(_jsx(CypherEditor, { schema: { | ||
functions: { | ||
'CYPHER 5': { | ||
cypher5Function: { | ||
...testData.emptyFunction, | ||
argumentDescription: [ | ||
{ | ||
isDeprecated: false, | ||
description: cypher5ArgDescription, | ||
name: 'statement', | ||
type: 'STRING', | ||
}, | ||
], | ||
name: 'cypher5Function', | ||
}, | ||
}, | ||
'CYPHER 25': { | ||
cypher25Function: { | ||
...testData.emptyFunction, | ||
argumentDescription: [ | ||
{ | ||
isDeprecated: false, | ||
description: cypher25ArgDescription, | ||
name: 'statement', | ||
type: 'STRING', | ||
}, | ||
], | ||
name: 'cypher25Function', | ||
}, | ||
}, | ||
}, | ||
}, featureFlags: { cypher25: true } })); | ||
const textField = page.getByRole('textbox'); | ||
await textField.fill('CYPHER 5 RETURN cypher5Function('); | ||
const tooltip = page.locator('.cm-signature-help-panel'); | ||
await testTooltip(tooltip, { | ||
includes: [cypher5ArgDescription], | ||
}); | ||
await textField.fill('CYPHER 25 RETURN cypher25Function('); | ||
await testTooltip(tooltip, { | ||
includes: [cypher25ArgDescription], | ||
}); | ||
}); | ||
//# sourceMappingURL=signatureHelp.spec.js.map |
@@ -46,6 +46,6 @@ import { jsx as _jsx } from "react/jsx-runtime"; | ||
await expect(page.locator('.cm-tooltip-autocomplete').getByText('City')).toBeVisible(); | ||
await textField.press('Tab'); | ||
await textField.press('Tab', { delay: 300 }); | ||
await expect(page.locator('.cm-tooltip-autocomplete')).not.toBeVisible(); | ||
// tab out of the snippet | ||
await textField.press('Tab'); | ||
await textField.press('Tab', { delay: 300 }); | ||
await expect(textField).toHaveText('MATCH ()-[]->()-[ ]->(:City)'); | ||
@@ -52,0 +52,0 @@ }); |
@@ -17,11 +17,11 @@ import { jsx as _jsx } from "react/jsx-runtime"; | ||
const keywordcolors = await Promise.all(['MATCH', 'WHERE', 'RETURN'].map((kw) => editorPage.getHexColorOfLocator(page.getByText(kw)))); | ||
keywordcolors.every((kw) => expect(kw).toEqual(lightThemeConstants.highlightStyles.keyword)); | ||
keywordcolors.every((kw) => expect(kw).toEqual(lightThemeConstants.highlightStyles.keyword.toLowerCase())); | ||
const labelReltype = await Promise.all(['Label', 'REL_TYPE'].map((kw) => editorPage.getHexColorOfLocator(page.getByText(kw)))); | ||
labelReltype.every((kw) => expect(kw).toEqual(lightThemeConstants.highlightStyles.label)); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('parameter'))).toEqual(lightThemeConstants.highlightStyles.paramValue); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('property'))).toEqual(lightThemeConstants.highlightStyles.property); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('false'))).toEqual(lightThemeConstants.highlightStyles.booleanLiteral); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('String'))).toEqual(lightThemeConstants.highlightStyles.stringLiteral); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('comment'))).toEqual(lightThemeConstants.highlightStyles.comment); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('1234', { exact: true }))).toEqual(lightThemeConstants.highlightStyles.numberLiteral); | ||
labelReltype.every((kw) => expect(kw).toEqual(lightThemeConstants.highlightStyles.label.toLowerCase())); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('parameter'))).toEqual(lightThemeConstants.highlightStyles.paramValue.toLowerCase()); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('property'))).toEqual(lightThemeConstants.highlightStyles.property.toLowerCase()); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('false'))).toEqual(lightThemeConstants.highlightStyles.booleanLiteral.toLowerCase()); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('String'))).toEqual(lightThemeConstants.highlightStyles.stringLiteral.toLowerCase()); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('comment'))).toEqual(lightThemeConstants.highlightStyles.comment.toLowerCase()); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('1234', { exact: true }))).toEqual(lightThemeConstants.highlightStyles.numberLiteral.toLowerCase()); | ||
expect(await editorPage.editorBackgroundIsUnset()).toEqual(false); | ||
@@ -40,11 +40,11 @@ }); | ||
const keywordcolors = await Promise.all(['MATCH', 'WHERE', 'RETURN'].map((kw) => editorPage.getHexColorOfLocator(page.getByText(kw)))); | ||
keywordcolors.every((kw) => expect(kw).toEqual(darkThemeConstants.highlightStyles.keyword)); | ||
keywordcolors.every((kw) => expect(kw).toEqual(darkThemeConstants.highlightStyles.keyword.toLowerCase())); | ||
const labelReltype = await Promise.all(['Label', 'REL_TYPE'].map((kw) => editorPage.getHexColorOfLocator(page.getByText(kw)))); | ||
labelReltype.every((kw) => expect(kw).toEqual(darkThemeConstants.highlightStyles.label)); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('parameter'))).toEqual(darkThemeConstants.highlightStyles.paramValue); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('property'))).toEqual(darkThemeConstants.highlightStyles.property); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('false'))).toEqual(darkThemeConstants.highlightStyles.booleanLiteral); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('String'))).toEqual(darkThemeConstants.highlightStyles.stringLiteral); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('comment'))).toEqual(darkThemeConstants.highlightStyles.comment); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('1234', { exact: true }))).toEqual(darkThemeConstants.highlightStyles.numberLiteral); | ||
labelReltype.every((kw) => expect(kw).toEqual(darkThemeConstants.highlightStyles.label.toLowerCase())); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('parameter'))).toEqual(darkThemeConstants.highlightStyles.paramValue.toLowerCase()); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('property'))).toEqual(darkThemeConstants.highlightStyles.property.toLowerCase()); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('false'))).toEqual(darkThemeConstants.highlightStyles.booleanLiteral.toLowerCase()); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('String'))).toEqual(darkThemeConstants.highlightStyles.stringLiteral.toLowerCase()); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('comment'))).toEqual(darkThemeConstants.highlightStyles.comment.toLowerCase()); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('1234', { exact: true }))).toEqual(darkThemeConstants.highlightStyles.numberLiteral.toLowerCase()); | ||
expect(await editorPage.editorBackgroundIsUnset()).toEqual(false); | ||
@@ -55,5 +55,5 @@ }); | ||
const component = await mount(_jsx(CypherEditor, { theme: "light", value: "RETURN" })); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('RETURN', { exact: true }))).toEqual(lightThemeConstants.highlightStyles.keyword); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('RETURN', { exact: true }))).toEqual(lightThemeConstants.highlightStyles.keyword.toLowerCase()); | ||
await component.update(_jsx(CypherEditor, { theme: "dark", value: "RETURN" })); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('RETURN', { exact: true }))).toEqual(darkThemeConstants.highlightStyles.keyword); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('RETURN', { exact: true }))).toEqual(darkThemeConstants.highlightStyles.keyword.toLowerCase()); | ||
}); | ||
@@ -71,3 +71,3 @@ test('respects prop to allow overriding bkg color', async ({ page, mount }) => { | ||
await mount(_jsx(CypherEditor, { theme: "light", value: query })); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('multilinestring'))).toEqual(lightThemeConstants.highlightStyles.stringLiteral); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('multilinestring'))).toEqual(lightThemeConstants.highlightStyles.stringLiteral.toLowerCase()); | ||
}); | ||
@@ -82,3 +82,3 @@ test('highlights multiline label correctly', async ({ page, mount }) => { | ||
await mount(_jsx(CypherEditor, { theme: "light", value: query })); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('Label'))).toEqual(lightThemeConstants.highlightStyles.label); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('Label'))).toEqual(lightThemeConstants.highlightStyles.label.toLowerCase()); | ||
}); | ||
@@ -93,4 +93,4 @@ test('highlights multiline comment correctly', async ({ page, mount }) => { | ||
await mount(_jsx(CypherEditor, { theme: "light", value: query })); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('comment'))).toEqual(lightThemeConstants.highlightStyles.comment); | ||
expect(await editorPage.getHexColorOfLocator(page.getByText('comment'))).toEqual(lightThemeConstants.highlightStyles.comment.toLowerCase()); | ||
}); | ||
//# sourceMappingURL=syntaxHighlighting.spec.js.map |
import { jsx as _jsx } from "react/jsx-runtime"; | ||
import { testData } from '@neo4j-cypher/language-support'; | ||
import { expect, test } from '@playwright/experimental-ct-react'; | ||
@@ -10,3 +11,3 @@ import { CypherEditor } from '../CypherEditor'; | ||
await expect(page.locator('.cm-lintRange-error').last()).not.toBeVisible({ | ||
timeout: 2000, | ||
timeout: 10000, | ||
}); | ||
@@ -19,7 +20,7 @@ }); | ||
await expect(page.locator('.cm-lintRange-error').last()).not.toBeVisible({ | ||
timeout: 2000, | ||
timeout: 10000, | ||
}); | ||
await component.update(_jsx(CypherEditor, { value: query, lint: true })); | ||
await editorPage.getEditor().fill('METCH (n) RETURN n'); | ||
await editorPage.checkErrorMessage('METCH', 'Unrecognized keyword. Did you mean MATCH?'); | ||
await editorPage.checkErrorMessage('METCH', `Invalid input 'METCH': expected 'FOREACH', 'ALTER', 'ORDER BY', 'CALL', 'USING PERIODIC COMMIT', 'CREATE', 'LOAD CSV', 'START DATABASE', 'STOP DATABASE', 'DEALLOCATE', 'DELETE', 'DENY', 'DETACH', 'DROP', 'DRYRUN', 'FINISH', 'GRANT', 'INSERT', 'LIMIT', 'MATCH', 'MERGE', 'NODETACH', 'OFFSET', 'OPTIONAL', 'REALLOCATE', 'REMOVE', 'RENAME', 'RETURN', 'REVOKE', 'ENABLE SERVER', 'SET', 'SHOW', 'SKIP', 'TERMINATE', 'UNWIND', 'USE' or 'WITH'`); | ||
}); | ||
@@ -30,4 +31,10 @@ test('Syntactic errors are surfaced', async ({ page, mount }) => { | ||
await mount(_jsx(CypherEditor, { value: query })); | ||
await editorPage.checkErrorMessage('METCH', 'Unrecognized keyword. Did you mean MATCH?'); | ||
await editorPage.checkErrorMessage('METCH', `Invalid input 'METCH': expected 'FOREACH', 'ALTER', 'ORDER BY', 'CALL', 'USING PERIODIC COMMIT', 'CREATE', 'LOAD CSV', 'START DATABASE', 'STOP DATABASE', 'DEALLOCATE', 'DELETE', 'DENY', 'DETACH', 'DROP', 'DRYRUN', 'FINISH', 'GRANT', 'INSERT', 'LIMIT', 'MATCH', 'MERGE', 'NODETACH', 'OFFSET', 'OPTIONAL', 'REALLOCATE', 'REMOVE', 'RENAME', 'RETURN', 'REVOKE', 'ENABLE SERVER', 'SET', 'SHOW', 'SKIP', 'TERMINATE', 'UNWIND', 'USE' or 'WITH'`); | ||
}); | ||
test('Does not trigger syntax errors for backticked parameters in parameter creation', async ({ page, mount, }) => { | ||
const editorPage = new CypherEditorPage(page); | ||
const query = ':param x => "abc"'; | ||
await mount(_jsx(CypherEditor, { value: query })); | ||
await editorPage.checkNoNotificationMessage('error'); | ||
}); | ||
test('Errors for undefined labels are surfaced', async ({ page, mount }) => { | ||
@@ -49,2 +56,9 @@ const editorPage = new CypherEditorPage(page); | ||
}); | ||
test('Semantic errors work in firefox', async ({ browserName, page, mount, }) => { | ||
test.skip(browserName !== 'firefox'); | ||
const editorPage = new CypherEditorPage(page); | ||
const query = 'MATCH (n:OperationalPoint)--(m:OperationalPoint) RETURN s,m,n'; | ||
await mount(_jsx(CypherEditor, { value: query, schema: testData.mockSchema })); | ||
await editorPage.checkErrorMessage('s,m,n', 'Variable `s` not defined'); | ||
}); | ||
test('Semantic errors are surfaced when there are no syntactic errors', async ({ page, mount, }) => { | ||
@@ -83,2 +97,25 @@ const editorPage = new CypherEditorPage(page); | ||
}); | ||
test('Strikethroughs are shown for deprecated functions', async ({ page, mount, }) => { | ||
const editorPage = new CypherEditorPage(page); | ||
const query = `RETURN id()`; | ||
await mount(_jsx(CypherEditor, { value: query, schema: testData.mockSchema })); | ||
await expect(editorPage.page.locator('.cm-deprecated-element').last()).toBeVisible({ timeout: 10000 }); | ||
await editorPage.checkWarningMessage('id', 'Function id is deprecated.'); | ||
}); | ||
test('Strikethroughs are shown for deprecated procedures', async ({ page, mount, }) => { | ||
const editorPage = new CypherEditorPage(page); | ||
const query = `CALL apoc.create.uuids()`; | ||
await mount(_jsx(CypherEditor, { value: query, schema: testData.mockSchema })); | ||
await expect(editorPage.page.locator('.cm-deprecated-element').last()).toBeVisible({ timeout: 10000 }); | ||
await editorPage.checkWarningMessage('apoc.create.uuids', 'Procedure apoc.create.uuids is deprecated.'); | ||
}); | ||
test('Syntax validation depends on the Cypher version', async ({ page, mount, }) => { | ||
await mount(_jsx(CypherEditor, { schema: testData.mockSchema, featureFlags: { cypher25: true } })); | ||
const editorPage = new CypherEditorPage(page); | ||
const textField = page.getByRole('textbox'); | ||
await textField.fill('CYPHER 5 CALL apoc.create.uuids(5)'); | ||
await editorPage.checkWarningMessage('apoc.create.uuids', 'Procedure apoc.create.uuids is deprecated.'); | ||
await textField.fill('CYPHER 25 CALL apoc.create.uuids(5)'); | ||
await editorPage.checkErrorMessage('apoc.create.uuids', 'Procedure apoc.create.uuids is not present in the database.'); | ||
}); | ||
//# sourceMappingURL=syntaxValidation.spec.js.map |
@@ -84,3 +84,3 @@ import { StateEffect } from '@codemirror/state'; | ||
effects: moveInHistory.of(direction), | ||
selection: { anchor: view.state.doc.length }, | ||
selection: { anchor: direction === 'BACK' ? 0 : view.state.doc.length }, | ||
})); | ||
@@ -87,0 +87,0 @@ const updatedHistory = view.state.field(historyState, false); |
@@ -1,4 +0,4 @@ | ||
export { CypherParser, _internalFeatureFlags, } from '@neo4j-cypher/language-support'; | ||
export * as LanguageSupport from '@neo4j-cypher/language-support'; | ||
export { CypherEditor } from './CypherEditor'; | ||
export { cypher } from './lang-cypher/langCypher'; | ||
export { darkThemeConstants, lightThemeConstants } from './themes'; |
@@ -1,2 +0,2 @@ | ||
export { CypherParser, _internalFeatureFlags, } from '@neo4j-cypher/language-support'; | ||
export * as LanguageSupport from '@neo4j-cypher/language-support'; | ||
export { CypherEditor } from './CypherEditor'; | ||
@@ -3,0 +3,0 @@ export { cypher } from './lang-cypher/langCypher'; |
import { snippet, } from '@codemirror/autocomplete'; | ||
import { autocomplete } from '@neo4j-cypher/language-support'; | ||
import { autocomplete, shouldAutoCompleteYield, } from '@neo4j-cypher/language-support'; | ||
import { CompletionItemKind, CompletionItemTag, } from 'vscode-languageserver-types'; | ||
@@ -38,3 +38,3 @@ import { getDocString } from './utils'; | ||
if (completion.deprecated) { | ||
return 'cm-deprecated-completion'; | ||
return 'cm-deprecated-element'; | ||
} | ||
@@ -46,8 +46,13 @@ else { | ||
export const cypherAutocomplete = (config) => (context) => { | ||
const textUntilCursor = context.state.doc.toString().slice(0, context.pos); | ||
const documentText = context.state.doc.toString(); | ||
const offset = context.pos; | ||
const triggerCharacters = ['.', ':', '{', '$', ')']; | ||
const lastCharacter = textUntilCursor.slice(-1); | ||
const lastCharacter = documentText.at(offset - 1); | ||
const yieldTriggered = shouldAutoCompleteYield(documentText, offset); | ||
const lastWord = context.matchBefore(/\w*/); | ||
const inWord = lastWord.from !== lastWord.to; | ||
const shouldTriggerCompletion = inWord || context.explicit || triggerCharacters.includes(lastCharacter); | ||
const shouldTriggerCompletion = inWord || | ||
context.explicit || | ||
triggerCharacters.includes(lastCharacter) || | ||
yieldTriggered; | ||
if (config.useLightVersion && !context.explicit) { | ||
@@ -59,59 +64,43 @@ return null; | ||
} | ||
const options = autocomplete(textUntilCursor, config.schema ?? {}, undefined, context.explicit); | ||
if (config.featureFlags.signatureInfoOnAutoCompletions) { | ||
return { | ||
from: context.matchBefore(/(\w|\$)*$/).from, | ||
options: options.map((o) => { | ||
let maybeInfo = {}; | ||
let emptyInfo = true; | ||
const newDiv = document.createElement('div'); | ||
if (o.signature) { | ||
const header = document.createElement('p'); | ||
header.setAttribute('class', 'cm-completionInfo-signature'); | ||
header.textContent = o.signature; | ||
if (header.textContent.length > 0) { | ||
emptyInfo = false; | ||
newDiv.appendChild(header); | ||
} | ||
const options = autocomplete( | ||
// TODO This is a temporary hack because completions are not working well | ||
documentText.slice(0, offset), config.schema ?? {}, offset, context.explicit); | ||
return { | ||
from: context.matchBefore(/(\w|\$)*$/).from, | ||
options: options.map((o) => { | ||
let maybeInfo = {}; | ||
let emptyInfo = true; | ||
const newDiv = document.createElement('div'); | ||
if (o.signature) { | ||
const header = document.createElement('p'); | ||
header.setAttribute('class', 'cm-completionInfo-signature'); | ||
header.textContent = o.signature; | ||
if (header.textContent.length > 0) { | ||
emptyInfo = false; | ||
newDiv.appendChild(header); | ||
} | ||
if (o.documentation) { | ||
const paragraph = document.createElement('p'); | ||
paragraph.textContent = getDocString(o.documentation); | ||
if (paragraph.textContent.length > 0) { | ||
emptyInfo = false; | ||
newDiv.appendChild(paragraph); | ||
} | ||
} | ||
if (o.documentation) { | ||
const paragraph = document.createElement('p'); | ||
paragraph.textContent = getDocString(o.documentation); | ||
if (paragraph.textContent.length > 0) { | ||
emptyInfo = false; | ||
newDiv.appendChild(paragraph); | ||
} | ||
if (!emptyInfo) { | ||
maybeInfo = { | ||
info: () => Promise.resolve(newDiv), | ||
}; | ||
} | ||
const deprecated = o.tags?.find((tag) => tag === CompletionItemTag.Deprecated) ?? | ||
false; | ||
// The negative boost moves the deprecation down the list | ||
// so we offer the user the completions that are | ||
// deprecated the last | ||
const maybeDeprecated = deprecated | ||
? { boost: -99, deprecated: true } | ||
: {}; | ||
return { | ||
label: o.label, | ||
type: completionKindToCodemirrorIcon(o.kind), | ||
apply: o.kind === CompletionItemKind.Snippet | ||
? // codemirror requires an empty snippet space to be able to tab out of the completion | ||
snippet((o.insertText ?? o.label) + '${}') | ||
: undefined, | ||
detail: o.detail, | ||
...maybeDeprecated, | ||
...maybeInfo, | ||
} | ||
if (!emptyInfo) { | ||
maybeInfo = { | ||
info: () => Promise.resolve(newDiv), | ||
}; | ||
}), | ||
}; | ||
} | ||
else { | ||
return { | ||
from: context.matchBefore(/(\w|\$)*$/).from, | ||
options: options.map((o) => ({ | ||
label: o.label, | ||
} | ||
const deprecated = o.tags?.find((tag) => tag === CompletionItemTag.Deprecated) ?? false; | ||
// The negative boost moves the deprecation down the list | ||
// so we offer the user the completions that are | ||
// deprecated the last | ||
const maybeDeprecated = deprecated | ||
? { boost: -99, deprecated: true } | ||
: {}; | ||
return { | ||
label: o.insertText ? o.insertText : o.label, | ||
displayLabel: o.label, | ||
type: completionKindToCodemirrorIcon(o.kind), | ||
@@ -123,6 +112,8 @@ apply: o.kind === CompletionItemKind.Snippet | ||
detail: o.detail, | ||
})), | ||
}; | ||
} | ||
...maybeDeprecated, | ||
...maybeInfo, | ||
}; | ||
}), | ||
}; | ||
}; | ||
//# sourceMappingURL=autocomplete.js.map |
@@ -35,2 +35,4 @@ import type { Facet } from '@codemirror/state'; | ||
number: NodeType; | ||
setting: NodeType; | ||
settingValue: NodeType; | ||
}; | ||
@@ -37,0 +39,0 @@ export type PrismSpecificTokenType = 'class-name' | 'identifier' | 'string' | 'relationship' | 'boolean' | 'number'; |
@@ -40,2 +40,4 @@ import { languageDataProp } from '@codemirror/language'; | ||
number: NodeType.define({ id: 28, name: 'numberLiteral' }), | ||
setting: NodeType.define({ id: 29, name: 'setting' }), | ||
settingValue: NodeType.define({ id: 30, name: 'settingValue' }), | ||
}); | ||
@@ -64,4 +66,6 @@ export const tokenTypeToStyleTag = { | ||
consoleCommand: tags.macroName, | ||
setting: tags.attributeName, | ||
settingValue: tags.attributeValue, | ||
}; | ||
export const parserAdapterNodeSet = (nodes) => new NodeSet(Object.values(nodes)).extend(styleTags(tokenTypeToStyleTag)); | ||
//# sourceMappingURL=constants.js.map |
import { tags } from '@lezer/highlight'; | ||
import { applySyntaxColouring, CypherTokenType, } from '@neo4j-cypher/language-support'; | ||
import { expect, test } from 'vitest'; | ||
import { tokenTypeToStyleTag } from './constants'; | ||
@@ -4,0 +5,0 @@ const cypherQueryWithAllTokenTypes = `MATCH (variable :Label)-[:REL_TYPE]->() |
@@ -79,2 +79,5 @@ import { HighlightStyle, syntaxHighlighting, } from '@codemirror/language'; | ||
}, | ||
'& .cm-signature-help-panel-arg-description': { | ||
padding: '5px', | ||
}, | ||
'& .cm-signature-help-panel-description': { | ||
@@ -86,3 +89,3 @@ padding: '5px', | ||
}, | ||
'.cm-deprecated-completion': { | ||
'.cm-deprecated-element': { | ||
'text-decoration': 'line-through', | ||
@@ -89,0 +92,0 @@ }, |
@@ -7,4 +7,4 @@ import { LanguageSupport } from '@codemirror/language'; | ||
featureFlags?: { | ||
signatureInfoOnAutoCompletions?: boolean; | ||
consoleCommands?: boolean; | ||
cypher25?: boolean; | ||
}; | ||
@@ -11,0 +11,0 @@ schema?: DbSchema; |
import { autocompletion } from '@codemirror/autocomplete'; | ||
import { defineLanguageFacet, Language, LanguageSupport, } from '@codemirror/language'; | ||
import { _internalFeatureFlags, } from '@neo4j-cypher/language-support'; | ||
import { completionStyles, cypherAutocomplete } from './autocomplete'; | ||
import { ParserAdapter } from './parser-adapter'; | ||
import { signatureHelpTooltip } from './signatureHelp'; | ||
import { cypherLinter, semanticAnalysisLinter } from './syntaxValidation'; | ||
import { cypherLinter } from './syntaxValidation'; | ||
const facet = defineLanguageFacet({ | ||
@@ -13,7 +12,2 @@ commentTokens: { block: { open: '/*', close: '*/' }, line: '//' }, | ||
export function cypher(config) { | ||
const featureFlags = config.featureFlags; | ||
// We allow to override the consoleCommands feature flag | ||
if (featureFlags.consoleCommands !== undefined) { | ||
_internalFeatureFlags.consoleCommands = featureFlags.consoleCommands; | ||
} | ||
const parserAdapter = new ParserAdapter(facet, config); | ||
@@ -27,3 +21,2 @@ const cypherLanguage = new Language(facet, parserAdapter, [], 'cypher'); | ||
cypherLinter(config), | ||
semanticAnalysisLinter(config), | ||
signatureHelpTooltip(config), | ||
@@ -30,0 +23,0 @@ ]); |
@@ -1,8 +0,12 @@ | ||
import { validateSemantics } from '@neo4j-cypher/language-support'; | ||
import { DbSchema } from '@neo4j-cypher/language-support'; | ||
import workerpool from 'workerpool'; | ||
type LinterArgs = Parameters<typeof validateSemantics>; | ||
export type LinterTask = workerpool.Promise<ReturnType<typeof validateSemantics>>; | ||
declare function lintCypherQuery(query: string, dbSchema: DbSchema, featureFlags?: { | ||
consoleCommands?: boolean; | ||
cypher25?: boolean; | ||
}): import("@neo4j-cypher/language-support").SyntaxDiagnostic[]; | ||
type LinterArgs = Parameters<typeof lintCypherQuery>; | ||
export type LinterTask = workerpool.Promise<ReturnType<typeof lintCypherQuery>>; | ||
export type LintWorker = { | ||
validateSemantics: (...args: LinterArgs) => LinterTask; | ||
lintCypherQuery: (...args: LinterArgs) => LinterTask; | ||
}; | ||
export {}; |
@@ -1,4 +0,14 @@ | ||
import { validateSemantics } from '@neo4j-cypher/language-support'; | ||
import { lintCypherQuery as _lintCypherQuery, _internalFeatureFlags, } from '@neo4j-cypher/language-support'; | ||
import workerpool from 'workerpool'; | ||
workerpool.worker({ validateSemantics }); | ||
function lintCypherQuery(query, dbSchema, featureFlags = {}) { | ||
// We allow to override the consoleCommands feature flag | ||
if (featureFlags.consoleCommands !== undefined) { | ||
_internalFeatureFlags.consoleCommands = featureFlags.consoleCommands; | ||
} | ||
if (featureFlags.cypher25 !== undefined) { | ||
_internalFeatureFlags.cypher25 = featureFlags.cypher25; | ||
} | ||
return _lintCypherQuery(query, dbSchema); | ||
} | ||
workerpool.worker({ lintCypherQuery }); | ||
//# sourceMappingURL=lintWorker.js.map |
import { StateField } from '@codemirror/state'; | ||
import { showTooltip } from '@codemirror/view'; | ||
import { signatureHelp } from '@neo4j-cypher/language-support'; | ||
import { MarkupContent, } from 'vscode-languageserver-types'; | ||
import { getDocString } from './utils'; | ||
@@ -25,7 +26,10 @@ function getTriggerCharacter(query, caretPosition) { | ||
signatureLabel.className = 'cm-signature-help-panel-name'; | ||
signatureLabel.appendChild(document.createTextNode(`${signature.label}(`)); | ||
const methodName = signature.label.slice(0, signature.label.indexOf('(')); | ||
const returnType = signature.label.slice(signature.label.indexOf(')') + 1); | ||
signatureLabel.appendChild(document.createTextNode(`${methodName}(`)); | ||
let currentParamDescription = undefined; | ||
parameters.forEach((param, index) => { | ||
if (typeof param.documentation === 'string') { | ||
if (typeof param.label === 'string') { | ||
const span = document.createElement('span'); | ||
span.appendChild(document.createTextNode(param.documentation)); | ||
span.appendChild(document.createTextNode(param.label)); | ||
if (index !== parameters.length - 1) { | ||
@@ -36,2 +40,6 @@ span.appendChild(document.createTextNode(', ')); | ||
span.className = 'cm-signature-help-panel-current-argument'; | ||
const paramDoc = param.documentation; | ||
currentParamDescription = MarkupContent.is(paramDoc) | ||
? paramDoc.value | ||
: paramDoc; | ||
} | ||
@@ -42,2 +50,3 @@ signatureLabel.appendChild(span); | ||
signatureLabel.appendChild(document.createTextNode(')')); | ||
signatureLabel.appendChild(document.createTextNode(returnType)); | ||
contents.appendChild(signatureLabel); | ||
@@ -47,6 +56,12 @@ const separator = document.createElement('div'); | ||
contents.appendChild(separator); | ||
const description = document.createElement('div'); | ||
description.className = 'cm-signature-help-panel-description'; | ||
description.appendChild(document.createTextNode(doc)); | ||
contents.appendChild(description); | ||
if (currentParamDescription !== undefined) { | ||
const argDescription = document.createElement('div'); | ||
argDescription.className = 'cm-signature-help-panel-arg-description'; | ||
argDescription.appendChild(document.createTextNode(currentParamDescription)); | ||
contents.appendChild(argDescription); | ||
} | ||
const methodDescription = document.createElement('div'); | ||
methodDescription.className = 'cm-signature-help-panel-description'; | ||
methodDescription.appendChild(document.createTextNode(doc)); | ||
contents.appendChild(methodDescription); | ||
return { dom }; | ||
@@ -53,0 +68,0 @@ }; |
import { Extension } from '@codemirror/state'; | ||
import type { CypherConfig } from './langCypher'; | ||
export declare const cypherLinter: (config: CypherConfig) => Extension; | ||
export declare const semanticAnalysisLinter: (config: CypherConfig) => Extension; | ||
export declare const cleanupWorkers: () => void; |
import { linter } from '@codemirror/lint'; | ||
import { parserWrapper, validateSyntax } from '@neo4j-cypher/language-support'; | ||
import { DiagnosticSeverity } from 'vscode-languageserver-types'; | ||
import { DiagnosticSeverity, DiagnosticTag } from 'vscode-languageserver-types'; | ||
import workerpool from 'workerpool'; | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore ignore: https://v3.vitejs.dev/guide/features.html#import-with-query-suffixes | ||
import WorkerURL from './lintWorker?url&worker'; | ||
import WorkerURL from './lintWorker?worker&url'; | ||
const pool = workerpool.pool(WorkerURL, { | ||
@@ -14,3 +11,3 @@ minWorkers: 2, | ||
let lastSemanticJob; | ||
export const cypherLinter = (config) => linter((view) => { | ||
export const cypherLinter = (config) => linter(async (view) => { | ||
if (!config.lint) { | ||
@@ -20,28 +17,5 @@ return []; | ||
const query = view.state.doc.toString(); | ||
const syntaxErrors = validateSyntax(query, config.schema ?? {}); | ||
return syntaxErrors.map((diagnostic) => ({ | ||
from: diagnostic.offsets.start, | ||
to: diagnostic.offsets.end, | ||
severity: diagnostic.severity === DiagnosticSeverity.Error | ||
? 'error' | ||
: 'warning', | ||
message: diagnostic.message, | ||
})); | ||
}); | ||
export const semanticAnalysisLinter = (config) => linter(async (view) => { | ||
if (!config.lint) { | ||
return []; | ||
} | ||
const query = view.state.doc.toString(); | ||
if (query.length === 0) { | ||
return []; | ||
} | ||
// we want to avoid the ANTLR4 reparse in the worker thread, this should hit our main thread cache | ||
const parse = parserWrapper.parse(query); | ||
const statements = parse.statementsParsing; | ||
const anySyntacticError = statements.filter((statement) => statement.diagnostics.length !== 0) | ||
.length > 0; | ||
if (anySyntacticError) { | ||
return []; | ||
} | ||
try { | ||
@@ -52,12 +26,19 @@ if (lastSemanticJob !== undefined && !lastSemanticJob.resolved) { | ||
const proxyWorker = (await pool.proxy()); | ||
lastSemanticJob = proxyWorker.validateSemantics(query, config.schema ?? {}); | ||
lastSemanticJob = proxyWorker.lintCypherQuery(query, config.schema ?? {}, config.featureFlags ?? {}); | ||
const result = await lastSemanticJob; | ||
return result.map((diag) => { | ||
const a = result.map((diagnostic) => { | ||
return { | ||
from: diag.offsets.start, | ||
to: diag.offsets.end, | ||
severity: diag.severity === DiagnosticSeverity.Error ? 'error' : 'warning', | ||
message: diag.message, | ||
from: diagnostic.offsets.start, | ||
to: diagnostic.offsets.end, | ||
severity: diagnostic.severity === DiagnosticSeverity.Error | ||
? 'error' | ||
: 'warning', | ||
message: diagnostic.message, | ||
...(diagnostic.tags !== undefined && | ||
diagnostic.tags.includes(DiagnosticTag.Deprecated) | ||
? { markClass: 'cm-deprecated-element' } | ||
: {}), | ||
}; | ||
}); | ||
return a; | ||
} | ||
@@ -64,0 +45,0 @@ catch (err) { |
export declare const tokens: { | ||
transitions: { | ||
values: { | ||
properties: { | ||
default: string; | ||
}; | ||
duration: { | ||
quick: string; | ||
slow: string; | ||
}; | ||
'timing-function': { | ||
default: string; | ||
}; | ||
}; | ||
stripped: { | ||
quick: string; | ||
slow: string; | ||
}; | ||
full: { | ||
quick: string; | ||
slow: string; | ||
}; | ||
}; | ||
borderRadius: { | ||
none: string; | ||
sm: string; | ||
md: string; | ||
lg: string; | ||
xl: string; | ||
'1xl': string; | ||
@@ -9,329 +36,472 @@ '2xl': string; | ||
full: string; | ||
lg: string; | ||
md: string; | ||
none: string; | ||
sm: string; | ||
xl: string; | ||
}; | ||
boxShadow: { | ||
l2: string; | ||
l3: string; | ||
l4: string; | ||
l5: string; | ||
}; | ||
breakpoints: { | ||
'2xl': string; | ||
xs: string; | ||
sm: string; | ||
md: string; | ||
lg: string; | ||
md: string; | ||
sm: string; | ||
xl: string; | ||
xs: string; | ||
'2xl': string; | ||
}; | ||
palette: { | ||
categorical: { | ||
'1': string; | ||
'2': string; | ||
'3': string; | ||
'4': string; | ||
'5': string; | ||
'6': string; | ||
'7': string; | ||
'8': string; | ||
'9': string; | ||
'10': string; | ||
'11': string; | ||
'12': string; | ||
}; | ||
graph: { | ||
'1': string; | ||
'2': string; | ||
'3': string; | ||
'4': string; | ||
'5': string; | ||
'6': string; | ||
'7': string; | ||
'8': string; | ||
'9': string; | ||
'10': string; | ||
'11': string; | ||
'12': string; | ||
}; | ||
}; | ||
colors: { | ||
blueberry: { | ||
baltic: { | ||
'10': string; | ||
'15': string; | ||
'20': string; | ||
'25': string; | ||
'30': string; | ||
'35': string; | ||
'40': string; | ||
'45': string; | ||
'50': string; | ||
'55': string; | ||
'60': string; | ||
'65': string; | ||
'70': string; | ||
'75': string; | ||
'80': string; | ||
}; | ||
danger: { | ||
hibiscus: { | ||
'10': string; | ||
'15': string; | ||
'20': string; | ||
'25': string; | ||
'30': string; | ||
'35': string; | ||
'40': string; | ||
'45': string; | ||
'50': string; | ||
'55': string; | ||
'60': string; | ||
'65': string; | ||
'70': string; | ||
'75': string; | ||
'80': string; | ||
}; | ||
mint: { | ||
forest: { | ||
'10': string; | ||
'15': string; | ||
'20': string; | ||
'25': string; | ||
'30': string; | ||
'35': string; | ||
'40': string; | ||
'45': string; | ||
'50': string; | ||
'55': string; | ||
'60': string; | ||
'65': string; | ||
'70': string; | ||
'75': string; | ||
'80': string; | ||
}; | ||
neutral: { | ||
lemon: { | ||
'10': string; | ||
'15': string; | ||
'20': string; | ||
'25': string; | ||
'30': string; | ||
'35': string; | ||
'40': string; | ||
'45': string; | ||
'50': string; | ||
'55': string; | ||
'60': string; | ||
'65': string; | ||
'70': string; | ||
'75': string; | ||
'80': string; | ||
'90': string; | ||
}; | ||
primary: { | ||
lavender: { | ||
'10': string; | ||
'15': string; | ||
'20': string; | ||
'25': string; | ||
'30': string; | ||
'35': string; | ||
'40': string; | ||
'45': string; | ||
'50': string; | ||
'55': string; | ||
'60': string; | ||
'65': string; | ||
'70': string; | ||
'75': string; | ||
'80': string; | ||
}; | ||
success: { | ||
marigold: { | ||
'10': string; | ||
'15': string; | ||
'20': string; | ||
'25': string; | ||
'30': string; | ||
'35': string; | ||
'40': string; | ||
'45': string; | ||
'50': string; | ||
'55': string; | ||
'60': string; | ||
'65': string; | ||
'70': string; | ||
'75': string; | ||
'80': string; | ||
}; | ||
warning: { | ||
earth: { | ||
'10': string; | ||
'15': string; | ||
'20': string; | ||
'25': string; | ||
'30': string; | ||
'35': string; | ||
'40': string; | ||
'45': string; | ||
'50': string; | ||
'55': string; | ||
'60': string; | ||
'65': string; | ||
'70': string; | ||
'75': string; | ||
'80': string; | ||
}; | ||
}; | ||
font: { | ||
size: { | ||
'body-large': string; | ||
'body-medium': string; | ||
'body-small': string; | ||
code: string; | ||
h1: string; | ||
h2: string; | ||
h3: string; | ||
h4: string; | ||
h5: string; | ||
h6: string; | ||
label: string; | ||
'subheading-large': string; | ||
'subheading-medium': string; | ||
'subheading-small': string; | ||
neutral: { | ||
'10': string; | ||
'15': string; | ||
'20': string; | ||
'25': string; | ||
'30': string; | ||
'35': string; | ||
'40': string; | ||
'45': string; | ||
'50': string; | ||
'55': string; | ||
'60': string; | ||
'65': string; | ||
'70': string; | ||
'75': string; | ||
'80': string; | ||
}; | ||
weight: { | ||
bold: string; | ||
light: string; | ||
medium: string; | ||
normal: string; | ||
semibold: string; | ||
beige: { | ||
'10': string; | ||
'20': string; | ||
'30': string; | ||
'40': string; | ||
'50': string; | ||
'60': string; | ||
'70': string; | ||
}; | ||
highlights: { | ||
yellow: string; | ||
periwinkle: string; | ||
}; | ||
}; | ||
palette: { | ||
categorical: { | ||
'1': string; | ||
'10': string; | ||
'11': string; | ||
'12': string; | ||
'2': string; | ||
'3': string; | ||
'4': string; | ||
'5': string; | ||
'6': string; | ||
'7': string; | ||
'8': string; | ||
'9': string; | ||
}; | ||
theme: { | ||
dark: { | ||
danger: { | ||
bg: { | ||
strong: string; | ||
weak: string; | ||
}; | ||
border: { | ||
strong: string; | ||
weak: string; | ||
}; | ||
hover: { | ||
strong: string; | ||
weak: string; | ||
}; | ||
icon: string; | ||
pressed: { | ||
strong: string; | ||
weak: string; | ||
}; | ||
text: string; | ||
boxShadow: { | ||
raised: string; | ||
overlay: string; | ||
}; | ||
neutral: { | ||
bg: { | ||
default: string; | ||
strong: string; | ||
strongest: string; | ||
weak: string; | ||
palette: { | ||
neutral: { | ||
text: { | ||
weakest: string; | ||
weaker: string; | ||
weak: string; | ||
default: string; | ||
inverse: string; | ||
}; | ||
icon: string; | ||
bg: { | ||
weak: string; | ||
default: string; | ||
strong: string; | ||
stronger: string; | ||
strongest: string; | ||
status: string; | ||
'on-bg-weak': string; | ||
}; | ||
border: { | ||
weak: string; | ||
strong: string; | ||
strongest: string; | ||
}; | ||
hover: string; | ||
pressed: string; | ||
}; | ||
border: { | ||
strong: string; | ||
weak: string; | ||
primary: { | ||
text: string; | ||
icon: string; | ||
bg: { | ||
weak: string; | ||
strong: string; | ||
status: string; | ||
selected: string; | ||
}; | ||
border: { | ||
strong: string; | ||
weak: string; | ||
}; | ||
focus: string; | ||
hover: { | ||
weak: string; | ||
strong: string; | ||
}; | ||
pressed: { | ||
weak: string; | ||
strong: string; | ||
}; | ||
}; | ||
hover: string; | ||
icon: string; | ||
pressed: string; | ||
text: { | ||
default: string; | ||
inverse: string; | ||
weak: string; | ||
weaker: string; | ||
weakest: string; | ||
danger: { | ||
text: string; | ||
icon: string; | ||
bg: { | ||
strong: string; | ||
weak: string; | ||
status: string; | ||
}; | ||
border: { | ||
strong: string; | ||
weak: string; | ||
}; | ||
hover: { | ||
weak: string; | ||
strong: string; | ||
}; | ||
pressed: { | ||
weak: string; | ||
strong: string; | ||
}; | ||
}; | ||
}; | ||
primary: { | ||
bg: { | ||
strong: string; | ||
weak: string; | ||
warning: { | ||
text: string; | ||
icon: string; | ||
bg: { | ||
strong: string; | ||
weak: string; | ||
status: string; | ||
}; | ||
border: { | ||
strong: string; | ||
weak: string; | ||
}; | ||
}; | ||
border: { | ||
strong: string; | ||
weak: string; | ||
success: { | ||
text: string; | ||
icon: string; | ||
bg: { | ||
strong: string; | ||
weak: string; | ||
status: string; | ||
}; | ||
border: { | ||
strong: string; | ||
weak: string; | ||
}; | ||
}; | ||
focus: string; | ||
hover: { | ||
strong: string; | ||
weak: string; | ||
discovery: { | ||
text: string; | ||
icon: string; | ||
bg: { | ||
strong: string; | ||
weak: string; | ||
status: string; | ||
}; | ||
border: { | ||
strong: string; | ||
weak: string; | ||
}; | ||
}; | ||
icon: string; | ||
pressed: { | ||
strong: string; | ||
weak: string; | ||
}; | ||
text: string; | ||
}; | ||
success: { | ||
bg: { | ||
strong: string; | ||
weak: string; | ||
}; | ||
border: { | ||
strong: string; | ||
weak: string; | ||
}; | ||
icon: string; | ||
text: string; | ||
}; | ||
warning: { | ||
bg: { | ||
strong: string; | ||
weak: string; | ||
}; | ||
border: { | ||
strong: string; | ||
weak: string; | ||
}; | ||
icon: string; | ||
text: string; | ||
}; | ||
}; | ||
graph: { | ||
'1': string; | ||
'10': string; | ||
'11': string; | ||
'12': string; | ||
'2': string; | ||
'3': string; | ||
'4': string; | ||
'5': string; | ||
'6': string; | ||
'7': string; | ||
'8': string; | ||
'9': string; | ||
}; | ||
light: { | ||
danger: { | ||
bg: { | ||
strong: string; | ||
weak: string; | ||
}; | ||
border: { | ||
strong: string; | ||
weak: string; | ||
}; | ||
hover: { | ||
strong: string; | ||
weak: string; | ||
}; | ||
icon: string; | ||
pressed: { | ||
strong: string; | ||
weak: string; | ||
}; | ||
text: string; | ||
boxShadow: { | ||
raised: string; | ||
overlay: string; | ||
}; | ||
neutral: { | ||
bg: { | ||
default: string; | ||
strong: string; | ||
strongest: string; | ||
weak: string; | ||
palette: { | ||
neutral: { | ||
text: { | ||
weakest: string; | ||
weaker: string; | ||
weak: string; | ||
default: string; | ||
inverse: string; | ||
}; | ||
icon: string; | ||
bg: { | ||
weak: string; | ||
default: string; | ||
'on-bg-weak': string; | ||
strong: string; | ||
stronger: string; | ||
strongest: string; | ||
status: string; | ||
}; | ||
border: { | ||
weak: string; | ||
strong: string; | ||
strongest: string; | ||
}; | ||
hover: string; | ||
pressed: string; | ||
}; | ||
border: { | ||
strong: string; | ||
weak: string; | ||
primary: { | ||
text: string; | ||
icon: string; | ||
bg: { | ||
weak: string; | ||
strong: string; | ||
status: string; | ||
selected: string; | ||
}; | ||
border: { | ||
strong: string; | ||
weak: string; | ||
}; | ||
focus: string; | ||
hover: { | ||
weak: string; | ||
strong: string; | ||
}; | ||
pressed: { | ||
weak: string; | ||
strong: string; | ||
}; | ||
}; | ||
hover: string; | ||
icon: string; | ||
pressed: string; | ||
text: { | ||
default: string; | ||
inverse: string; | ||
weak: string; | ||
weaker: string; | ||
weakest: string; | ||
danger: { | ||
text: string; | ||
icon: string; | ||
bg: { | ||
strong: string; | ||
weak: string; | ||
status: string; | ||
}; | ||
border: { | ||
strong: string; | ||
weak: string; | ||
}; | ||
hover: { | ||
weak: string; | ||
strong: string; | ||
}; | ||
pressed: { | ||
weak: string; | ||
strong: string; | ||
}; | ||
}; | ||
}; | ||
primary: { | ||
bg: { | ||
strong: string; | ||
weak: string; | ||
warning: { | ||
text: string; | ||
icon: string; | ||
bg: { | ||
strong: string; | ||
weak: string; | ||
status: string; | ||
}; | ||
border: { | ||
strong: string; | ||
weak: string; | ||
}; | ||
}; | ||
border: { | ||
strong: string; | ||
weak: string; | ||
success: { | ||
text: string; | ||
icon: string; | ||
bg: { | ||
strong: string; | ||
weak: string; | ||
status: string; | ||
}; | ||
border: { | ||
strong: string; | ||
weak: string; | ||
}; | ||
}; | ||
focus: string; | ||
hover: { | ||
strong: string; | ||
weak: string; | ||
discovery: { | ||
text: string; | ||
icon: string; | ||
bg: { | ||
strong: string; | ||
weak: string; | ||
status: string; | ||
}; | ||
border: { | ||
strong: string; | ||
weak: string; | ||
}; | ||
}; | ||
icon: string; | ||
pressed: { | ||
strong: string; | ||
weak: string; | ||
}; | ||
text: string; | ||
}; | ||
success: { | ||
bg: { | ||
strong: string; | ||
weak: string; | ||
}; | ||
border: { | ||
strong: string; | ||
weak: string; | ||
}; | ||
icon: string; | ||
text: string; | ||
}; | ||
warning: { | ||
bg: { | ||
strong: string; | ||
weak: string; | ||
}; | ||
border: { | ||
strong: string; | ||
weak: string; | ||
}; | ||
icon: string; | ||
text: string; | ||
}; | ||
}; | ||
}; | ||
font: { | ||
size: { | ||
h1: string; | ||
h2: string; | ||
h3: string; | ||
h4: string; | ||
h5: string; | ||
h6: string; | ||
'subheading-large': string; | ||
'subheading-medium': string; | ||
'subheading-small': string; | ||
'body-large': string; | ||
'body-medium': string; | ||
'body-small': string; | ||
code: string; | ||
label: string; | ||
}; | ||
weight: { | ||
bold: string; | ||
semibold: string; | ||
normal: string; | ||
medium: string; | ||
light: string; | ||
}; | ||
'font-family': { | ||
h1: string; | ||
h2: string; | ||
h3: string; | ||
h4: string; | ||
h5: string; | ||
h6: string; | ||
'subheading-large': string; | ||
'subheading-medium': string; | ||
'subheading-small': string; | ||
'body-large': string; | ||
'body-medium': string; | ||
'body-small': string; | ||
code: string; | ||
label: string; | ||
}; | ||
}; | ||
space: { | ||
'0': string; | ||
'1': string; | ||
'10': string; | ||
'11': string; | ||
'12': string; | ||
'13': string; | ||
'2': string; | ||
@@ -345,18 +515,7 @@ '3': string; | ||
'9': string; | ||
'10': string; | ||
'11': string; | ||
'12': string; | ||
'13': string; | ||
}; | ||
transitions: { | ||
default: string; | ||
stripped: string; | ||
values: { | ||
duration: { | ||
default: string; | ||
}; | ||
properties: { | ||
default: string; | ||
}; | ||
'timing-function': { | ||
default: string; | ||
}; | ||
}; | ||
}; | ||
zIndex: { | ||
@@ -370,13 +529,13 @@ '0': number; | ||
'60': number; | ||
deep: number; | ||
auto: string; | ||
alias: { | ||
overlay: number; | ||
banner: number; | ||
blanket: number; | ||
modal: number; | ||
overlay: number; | ||
popover: number; | ||
tooltip: number; | ||
modal: number; | ||
}; | ||
auto: string; | ||
deep: number; | ||
}; | ||
}; |
export const tokens = { | ||
transitions: { | ||
values: { | ||
properties: { | ||
default: 'all', | ||
}, | ||
duration: { | ||
quick: '100ms', | ||
slow: '250ms', | ||
}, | ||
'timing-function': { | ||
default: 'cubic-bezier(0.420, 0.000, 0.580, 1.000)', | ||
}, | ||
}, | ||
stripped: { | ||
quick: '100ms cubic-bezier(0.420, 0.000, 0.580, 1.000)', | ||
slow: '250ms cubic-bezier(0.420, 0.000, 0.580, 1.000)', | ||
}, | ||
full: { | ||
quick: 'all 100ms cubic-bezier(0.420, 0.000, 0.580, 1.000)', | ||
slow: 'all 250ms cubic-bezier(0.420, 0.000, 0.580, 1.000)', | ||
}, | ||
}, | ||
borderRadius: { | ||
none: '0px', | ||
sm: '4px', | ||
md: '6px', | ||
lg: '8px', | ||
xl: '10px', | ||
'1xl': '12px', | ||
@@ -9,120 +36,14 @@ '2xl': '14px', | ||
full: '9999px', | ||
lg: '8px', | ||
md: '6px', | ||
none: '0px', | ||
sm: '4px', | ||
xl: '10px', | ||
}, | ||
boxShadow: { | ||
l2: '0px 1px 2px 0px rgba(12, 26, 37, 0.18)', | ||
l3: '0px 4px 8px 0px rgba(12, 26, 37, 0.04)', | ||
l4: '0px 4px 8px 0px rgba(12, 26, 37, 0.08)', | ||
l5: '0px 8px 20px 0px rgba(12, 26, 37, 0.12)', | ||
}, | ||
breakpoints: { | ||
'2xl': '1536px', | ||
xs: '450px', | ||
sm: '640px', | ||
md: '768px', | ||
lg: '1024px', | ||
md: '768px', | ||
sm: '640px', | ||
xl: '1280px', | ||
xs: '450px', | ||
'2xl': '1536px', | ||
}, | ||
colors: { | ||
blueberry: { | ||
'10': '#E8EBF6', | ||
'20': '#C4CCE9', | ||
'30': '#9DABD9', | ||
'40': '#768ACA', | ||
'50': '#3557B4', | ||
'60': '#25459E', | ||
'70': '#0B297D', | ||
}, | ||
danger: { | ||
'10': '#ffe6e9', | ||
'20': '#ffb8c4', | ||
'30': '#ff668a', | ||
'40': '#ed1252', | ||
'50': '#cc254b', | ||
'60': '#a1003b', | ||
'70': '#7a0031', | ||
}, | ||
mint: { | ||
'10': '#F0FFFA', | ||
'20': '#D1FFF4', | ||
'30': '#A8FFEE', | ||
'40': '#55F9E2', | ||
'50': '#3DD4C5', | ||
'60': '#2AADA5', | ||
'70': '#116161', | ||
}, | ||
neutral: { | ||
'10': '#FFFFFF', | ||
'20': '#F5F7FA', | ||
'30': '#EEF1F6', | ||
'40': '#E6E9EE', | ||
'50': '#C4C8CD', | ||
'60': '#B2B7BD', | ||
'70': '#717780', | ||
'80': '#535B66', | ||
'90': '#151E29', | ||
}, | ||
primary: { | ||
'10': '#e6f8ff', | ||
'20': '#a3e2ff', | ||
'30': '#7ad1ff', | ||
'40': '#018bff', | ||
'50': '#006FD6', | ||
'60': '#0056b3', | ||
'70': '#004092', | ||
}, | ||
success: { | ||
'10': '#E1FAEF', | ||
'20': '#98EDCB', | ||
'30': '#44D4A4', | ||
'40': '#00BA88', | ||
'50': '#327D60', | ||
'60': '#006E58', | ||
'70': '#00473B', | ||
}, | ||
warning: { | ||
'10': '#FFFBDE', | ||
'20': '#FFF4B5', | ||
'30': '#FFEA8C', | ||
'40': '#FFDE63', | ||
'50': '#D9B54A', | ||
'60': '#966c2e', | ||
'70': '#664817', | ||
}, | ||
}, | ||
font: { | ||
size: { | ||
'body-large': '1rem', | ||
'body-medium': '0.875rem', | ||
'body-small': '0.75rem', | ||
code: '0.875rem', | ||
h1: '3rem', | ||
h2: '2.5rem', | ||
h3: '1.875rem', | ||
h4: '1.5rem', | ||
h5: '1.25rem', | ||
h6: '1rem', | ||
label: '0.875rem', | ||
'subheading-large': '1.25rem', | ||
'subheading-medium': '1rem', | ||
'subheading-small': '0.875rem', | ||
}, | ||
weight: { | ||
bold: '700', | ||
light: '300', | ||
medium: '500', | ||
normal: '400', | ||
semibold: '600', | ||
}, | ||
}, | ||
palette: { | ||
categorical: { | ||
'1': '#55BDC5', | ||
'10': '#BF732D', | ||
'11': '#478A6E', | ||
'12': '#ADE86B', | ||
'2': '#4D49CB', | ||
@@ -136,97 +57,8 @@ '3': '#DC8B39', | ||
'9': '#DBBF40', | ||
'10': '#BF732D', | ||
'11': '#478A6E', | ||
'12': '#ADE86B', | ||
}, | ||
dark: { | ||
danger: { | ||
bg: { | ||
strong: '#ffb8c4', | ||
weak: '68, 61, 72', | ||
}, | ||
border: { | ||
strong: '#ffb8c4', | ||
weak: '114, 91, 103', | ||
}, | ||
hover: { | ||
strong: '#ff668a', | ||
weak: 'rgba(255, 102, 138,0.08)', | ||
}, | ||
icon: '#ffb8c4', | ||
pressed: { | ||
strong: '#ff668a', | ||
weak: 'rgba(255, 102, 138,0.12)', | ||
}, | ||
text: '#ffb8c4', | ||
}, | ||
neutral: { | ||
bg: { | ||
default: '#151E29', | ||
strong: '45, 53, 63', | ||
strongest: '#FFFFFF', | ||
weak: '29, 38, 49', | ||
}, | ||
border: { | ||
strong: '#717780', | ||
weak: '37, 47, 59', | ||
}, | ||
hover: 'rgba(196, 200, 205,0.1)', | ||
icon: '#C4C8CD', | ||
pressed: 'rgba(196, 200, 205,0.2)', | ||
text: { | ||
default: '#F5F7FA', | ||
inverse: '#151E29', | ||
weak: '#C4C8CD', | ||
weaker: '#B2B7BD', | ||
weakest: '#717780', | ||
}, | ||
}, | ||
primary: { | ||
bg: { | ||
strong: '#a3e2ff', | ||
weak: '49, 69, 84', | ||
}, | ||
border: { | ||
strong: '#a3e2ff', | ||
weak: '78, 108, 126', | ||
}, | ||
focus: '#7ad1ff', | ||
hover: { | ||
strong: '#7ad1ff', | ||
weak: 'rgba(122, 209, 255,0.08)', | ||
}, | ||
icon: '#a3e2ff', | ||
pressed: { | ||
strong: '#7ad1ff', | ||
weak: 'rgba(122, 209, 255,0.12)', | ||
}, | ||
text: '#a3e2ff', | ||
}, | ||
success: { | ||
bg: { | ||
strong: '#98EDCB', | ||
weak: '47, 71, 73', | ||
}, | ||
border: { | ||
strong: '#98EDCB', | ||
weak: '73, 113, 106', | ||
}, | ||
icon: '#98EDCB', | ||
text: '#98EDCB', | ||
}, | ||
warning: { | ||
bg: { | ||
strong: '#FFEA8C', | ||
weak: '68, 71, 60', | ||
}, | ||
border: { | ||
strong: '#FFEA8C', | ||
weak: '114, 111, 80', | ||
}, | ||
icon: '#FFEA8C', | ||
text: '#FFEA8C', | ||
}, | ||
}, | ||
graph: { | ||
'1': '#FFDF81', | ||
'10': '#FFC354', | ||
'11': '#DA7294', | ||
'12': '#579380', | ||
'2': '#C990C0', | ||
@@ -240,100 +72,438 @@ '3': '#F79767', | ||
'9': '#4D8DDA', | ||
'10': '#FFC354', | ||
'11': '#DA7294', | ||
'12': '#579380', | ||
}, | ||
light: { | ||
danger: { | ||
bg: { | ||
strong: '#cc254b', | ||
weak: '#ffe6e9', | ||
}, | ||
colors: { | ||
baltic: { | ||
'10': '#E7FAFB', | ||
'15': '#C3F8FB', | ||
'20': '#8FE3E8', | ||
'25': '#5CC3C9', | ||
'30': '#5DB3BF', | ||
'35': '#51A6B1', | ||
'40': '#4C99A4', | ||
'45': '#30839D', | ||
'50': '#0A6190', | ||
'55': '#02507B', | ||
'60': '#014063', | ||
'65': '#262F31', | ||
'70': '#081E2B', | ||
'75': '#041823', | ||
'80': '#01121C', | ||
}, | ||
hibiscus: { | ||
'10': '#FFE9E7', | ||
'15': '#FFD7D2', | ||
'20': '#FFAA97', | ||
'25': '#FF8E6A', | ||
'30': '#F96746', | ||
'35': '#E84E2C', | ||
'40': '#D43300', | ||
'45': '#BB2D00', | ||
'50': '#961200', | ||
'55': '#730E00', | ||
'60': '#432520', | ||
'65': '#4E0900', | ||
'70': '#3F0800', | ||
'75': '#360700', | ||
'80': '#280500', | ||
}, | ||
forest: { | ||
'10': '#E7FCD7', | ||
'15': '#BCF194', | ||
'20': '#90CB62', | ||
'25': '#80BB53', | ||
'30': '#6FA646', | ||
'35': '#5B992B', | ||
'40': '#4D8622', | ||
'45': '#3F7824', | ||
'50': '#296127', | ||
'55': '#145439', | ||
'60': '#0C4D31', | ||
'65': '#0A4324', | ||
'70': '#262D24', | ||
'75': '#052618', | ||
'80': '#021D11', | ||
}, | ||
lemon: { | ||
'10': '#FFFAD1', | ||
'15': '#FFF8BD', | ||
'20': '#FFF178', | ||
'25': '#FFE500', | ||
'30': '#FFD600', | ||
'35': '#F4C318', | ||
'40': '#D7AA0A', | ||
'45': '#B48409', | ||
'50': '#996E00', | ||
'55': '#765500', | ||
'60': '#614600', | ||
'65': '#4D3700', | ||
'70': '#312E1A', | ||
'75': '#2E2100', | ||
'80': '#251B00', | ||
}, | ||
lavender: { | ||
'10': '#F7F3FF', | ||
'15': '#E9DEFF', | ||
'20': '#CCB4FF', | ||
'25': '#B38EFF', | ||
'30': '#A07BEC', | ||
'35': '#8C68D9', | ||
'40': '#754EC8', | ||
'45': '#5A34AA', | ||
'50': '#4B2894', | ||
'55': '#3B1982', | ||
'60': '#2C2A34', | ||
'65': '#220954', | ||
'70': '#170146', | ||
'75': '#0E002D', | ||
'80': '#09001C', | ||
}, | ||
marigold: { | ||
'10': '#FFF0D2', | ||
'15': '#FFDE9D', | ||
'20': '#FFCF72', | ||
'25': '#FFC450', | ||
'30': '#FFB422', | ||
'35': '#FFA901', | ||
'40': '#EC9C00', | ||
'45': '#DA9105', | ||
'50': '#BA7A00', | ||
'55': '#986400', | ||
'60': '#795000', | ||
'65': '#624100', | ||
'70': '#543800', | ||
'75': '#422C00', | ||
'80': '#251900', | ||
}, | ||
earth: { | ||
'10': '#FFF7F0', | ||
'15': '#FDEDDA', | ||
'20': '#FFE1C5', | ||
'25': '#F8D1AE', | ||
'30': '#ECBF96', | ||
'35': '#E0AE7F', | ||
'40': '#D19660', | ||
'45': '#AF7C4D', | ||
'50': '#8D5D31', | ||
'55': '#763F18', | ||
'60': '#66310B', | ||
'65': '#5B2B09', | ||
'70': '#481F01', | ||
'75': '#361700', | ||
'80': '#220E00', | ||
}, | ||
neutral: { | ||
'10': '#FFFFFF', | ||
'15': '#F5F6F6', | ||
'20': '#E2E3E5', | ||
'25': '#CFD1D4', | ||
'30': '#BBBEC3', | ||
'35': '#A8ACB2', | ||
'40': '#959AA1', | ||
'45': '#818790', | ||
'50': '#6F757E', | ||
'55': '#5E636A', | ||
'60': '#4D5157', | ||
'65': '#3C3F44', | ||
'70': '#212325', | ||
'75': '#1A1B1D', | ||
'80': '#09090A', | ||
}, | ||
beige: { | ||
'10': '#FFFCF4', | ||
'20': '#FFF7E3', | ||
'30': '#F2EAD4', | ||
'40': '#C1B9A0', | ||
'50': '#999384', | ||
'60': '#666050', | ||
'70': '#3F3824', | ||
}, | ||
highlights: { | ||
yellow: '#FAFF00', | ||
periwinkle: '#6A82FF', | ||
}, | ||
}, | ||
theme: { | ||
dark: { | ||
boxShadow: { | ||
raised: '0px 1px 2px 0px rgba(9, 9, 10, 0.50)', | ||
overlay: '0px 8px 20px 0px rgba(9, 9, 10, 0.50)', | ||
}, | ||
palette: { | ||
neutral: { | ||
text: { | ||
weakest: '#818790', | ||
weaker: '#A8ACB2', | ||
weak: '#CFD1D4', | ||
default: '#F5F6F6', | ||
inverse: '#1A1B1D', | ||
}, | ||
icon: '#CFD1D4', | ||
bg: { | ||
weak: '#212325', | ||
default: '#1A1B1D', | ||
strong: '#3C3F44', | ||
stronger: '#6F757E', | ||
strongest: '#F5F6F6', | ||
status: '#A8ACB2', | ||
'on-bg-weak': '#818790', | ||
}, | ||
border: { | ||
weak: '#3C3F44', | ||
strong: '#6F757E', | ||
strongest: '#BBBEC3', | ||
}, | ||
hover: '#959AA1', | ||
pressed: '#959AA1', | ||
}, | ||
border: { | ||
strong: '#cc254b', | ||
weak: '#ffb8c4', | ||
primary: { | ||
text: '#8FE3E8', | ||
icon: '#8FE3E8', | ||
bg: { | ||
weak: '#262F31', | ||
strong: '#8FE3E8', | ||
status: '#8FE3E8', | ||
selected: '#262F31', | ||
}, | ||
border: { | ||
strong: '#8FE3E8', | ||
weak: '#02507B', | ||
}, | ||
focus: '#5DB3BF', | ||
hover: { | ||
weak: '#8FE3E8', | ||
strong: '#5DB3BF', | ||
}, | ||
pressed: { | ||
weak: '#8FE3E8', | ||
strong: '#4C99A4', | ||
}, | ||
}, | ||
hover: { | ||
strong: '#a1003b', | ||
weak: 'rgba(237,18,82,0.08)', | ||
danger: { | ||
text: '#FFAA97', | ||
icon: '#FFAA97', | ||
bg: { | ||
strong: '#FFAA97', | ||
weak: '#432520', | ||
status: '#FFAA97', | ||
}, | ||
border: { | ||
strong: '#FFAA97', | ||
weak: '#730E00', | ||
}, | ||
hover: { | ||
weak: '#FFAA97', | ||
strong: '#F96746', | ||
}, | ||
pressed: { | ||
weak: '#FFAA97', | ||
strong: '#E84E2C', | ||
}, | ||
}, | ||
icon: '#cc254b', | ||
pressed: { | ||
strong: '#7a0031', | ||
weak: 'rgba(237,18,82,0.12)', | ||
warning: { | ||
text: '#FFD600', | ||
icon: '#FFD600', | ||
bg: { | ||
strong: '#FFD600', | ||
weak: '#312E1A', | ||
status: '#FFD600', | ||
}, | ||
border: { | ||
strong: '#FFD600', | ||
weak: '#765500', | ||
}, | ||
}, | ||
text: '#cc254b', | ||
}, | ||
neutral: { | ||
bg: { | ||
default: '#F5F7FA', | ||
strong: '#E6E9EE', | ||
strongest: '#535B66', | ||
weak: '#FFFFFF', | ||
success: { | ||
text: '#90CB62', | ||
icon: '#90CB62', | ||
bg: { | ||
strong: '#90CB62', | ||
weak: '#262D24', | ||
status: '#90CB62', | ||
}, | ||
border: { | ||
strong: '#90CB62', | ||
weak: '#296127', | ||
}, | ||
}, | ||
border: { | ||
strong: '#C4C8CD', | ||
weak: '#EEF1F6', | ||
discovery: { | ||
text: '#CCB4FF', | ||
icon: '#CCB4FF', | ||
bg: { | ||
strong: '#CCB4FF', | ||
weak: '#2C2A34', | ||
status: '#CCB4FF', | ||
}, | ||
border: { | ||
strong: '#CCB4FF', | ||
weak: '#4B2894', | ||
}, | ||
}, | ||
hover: 'rgba(113,119,128,0.1)', | ||
icon: '#535B66', | ||
pressed: 'rgba(113,119,128,0.2)', | ||
text: { | ||
default: '#151E29', | ||
inverse: '#FFFFFF', | ||
weak: '#535B66', | ||
weaker: '#717780', | ||
weakest: '#B2B7BD', | ||
}, | ||
}, | ||
primary: { | ||
bg: { | ||
strong: '#006FD6', | ||
weak: '#e6f8ff', | ||
}, | ||
light: { | ||
boxShadow: { | ||
raised: '0px 1px 2px 0px rgba(26, 27, 29, 0.18)', | ||
overlay: '0px 4px 8px 0px rgba(26, 27, 29, 0.12)', | ||
}, | ||
palette: { | ||
neutral: { | ||
text: { | ||
weakest: '#A8ACB2', | ||
weaker: '#5E636A', | ||
weak: '#4D5157', | ||
default: '#1A1B1D', | ||
inverse: '#FFFFFF', | ||
}, | ||
icon: '#4D5157', | ||
bg: { | ||
weak: '#FFFFFF', | ||
default: '#F5F6F6', | ||
'on-bg-weak': '#F5F6F6', | ||
strong: '#E2E3E5', | ||
stronger: '#A8ACB2', | ||
strongest: '#3C3F44', | ||
status: '#A8ACB2', | ||
}, | ||
border: { | ||
weak: '#E2E3E5', | ||
strong: '#BBBEC3', | ||
strongest: '#6F757E', | ||
}, | ||
hover: '#6F757E', | ||
pressed: '#6F757E', | ||
}, | ||
border: { | ||
strong: '#006FD6', | ||
weak: '#7ad1ff', | ||
primary: { | ||
text: '#0A6190', | ||
icon: '#0A6190', | ||
bg: { | ||
weak: '#E7FAFB', | ||
strong: '#0A6190', | ||
status: '#4C99A4', | ||
selected: '#E7FAFB', | ||
}, | ||
border: { | ||
strong: '#0A6190', | ||
weak: '#8FE3E8', | ||
}, | ||
focus: '#30839D', | ||
hover: { | ||
weak: '#30839D', | ||
strong: '#02507B', | ||
}, | ||
pressed: { | ||
weak: '#30839D', | ||
strong: '#014063', | ||
}, | ||
}, | ||
focus: '#018bff', | ||
hover: { | ||
strong: '#0056b3', | ||
weak: 'rgba(1,139,255,0.08)', | ||
danger: { | ||
text: '#BB2D00', | ||
icon: '#BB2D00', | ||
bg: { | ||
strong: '#BB2D00', | ||
weak: '#FFE9E7', | ||
status: '#E84E2C', | ||
}, | ||
border: { | ||
strong: '#BB2D00', | ||
weak: '#FFAA97', | ||
}, | ||
hover: { | ||
weak: '#D43300', | ||
strong: '#961200', | ||
}, | ||
pressed: { | ||
weak: '#D43300', | ||
strong: '#730E00', | ||
}, | ||
}, | ||
icon: '#006FD6', | ||
pressed: { | ||
strong: '#004092', | ||
weak: 'rgba(1,139,255,0.12)', | ||
warning: { | ||
text: '#765500', | ||
icon: '#765500', | ||
bg: { | ||
strong: '#765500', | ||
weak: '#FFFAD1', | ||
status: '#D7AA0A', | ||
}, | ||
border: { | ||
strong: '#996E00', | ||
weak: '#FFD600', | ||
}, | ||
}, | ||
text: '#006FD6', | ||
}, | ||
success: { | ||
bg: { | ||
strong: '#327D60', | ||
weak: '#E1FAEF', | ||
success: { | ||
text: '#3F7824', | ||
icon: '#3F7824', | ||
bg: { | ||
strong: '#3F7824', | ||
weak: '#E7FCD7', | ||
status: '#5B992B', | ||
}, | ||
border: { | ||
strong: '#3F7824', | ||
weak: '#90CB62', | ||
}, | ||
}, | ||
border: { | ||
strong: '#327D60', | ||
weak: '#98EDCB', | ||
discovery: { | ||
text: '#5A34AA', | ||
icon: '#5A34AA', | ||
bg: { | ||
strong: '#5A34AA', | ||
weak: '#E9DEFF', | ||
status: '#754EC8', | ||
}, | ||
border: { | ||
strong: '#5A34AA', | ||
weak: '#B38EFF', | ||
}, | ||
}, | ||
icon: '#327D60', | ||
text: '#327D60', | ||
}, | ||
warning: { | ||
bg: { | ||
strong: '#966c2e', | ||
weak: '#FFFBDE', | ||
}, | ||
border: { | ||
strong: '#966c2e', | ||
weak: '#FFEA8C', | ||
}, | ||
icon: '#966c2e', | ||
text: '#966c2e', | ||
}, | ||
}, | ||
}, | ||
font: { | ||
size: { | ||
h1: '3rem', | ||
h2: '2.5rem', | ||
h3: '1.875rem', | ||
h4: '1.5rem', | ||
h5: '1.25rem', | ||
h6: '1rem', | ||
'subheading-large': '1.25rem', | ||
'subheading-medium': '1rem', | ||
'subheading-small': '0.875rem', | ||
'body-large': '1rem', | ||
'body-medium': '0.875rem', | ||
'body-small': '0.75rem', | ||
code: '0.875rem', | ||
label: '0.875rem', | ||
}, | ||
weight: { | ||
bold: '700', | ||
semibold: '600', | ||
normal: '400', | ||
medium: '500', | ||
light: '300', | ||
}, | ||
'font-family': { | ||
h1: 'Syne Neo', | ||
h2: 'Syne Neo', | ||
h3: 'Public Sans', | ||
h4: 'Public Sans', | ||
h5: 'Public Sans', | ||
h6: 'Public Sans', | ||
'subheading-large': 'Public Sans', | ||
'subheading-medium': 'Public Sans', | ||
'subheading-small': 'Public Sans', | ||
'body-large': 'Public Sans', | ||
'body-medium': 'Public Sans', | ||
'body-small': 'Public Sans', | ||
code: 'Fira Code', | ||
label: 'Public Sans', | ||
}, | ||
}, | ||
space: { | ||
'0': '0px', | ||
'1': '1px', | ||
'10': '64px', | ||
'11': '96px', | ||
'12': '128px', | ||
'13': '320px', | ||
'2': '2px', | ||
@@ -347,18 +517,7 @@ '3': '4px', | ||
'9': '48px', | ||
'10': '64px', | ||
'11': '96px', | ||
'12': '128px', | ||
'13': '320px', | ||
}, | ||
transitions: { | ||
default: 'all 100ms cubic-bezier(0.420, 0.000, 0.580, 1.000)', | ||
stripped: '100ms cubic-bezier(0.420, 0.000, 0.580, 1.000)', | ||
values: { | ||
duration: { | ||
default: '100ms', | ||
}, | ||
properties: { | ||
default: 'all', | ||
}, | ||
'timing-function': { | ||
default: 'cubic-bezier(0.420, 0.000, 0.580, 1.000)', | ||
}, | ||
}, | ||
}, | ||
zIndex: { | ||
@@ -372,14 +531,14 @@ '0': 0, | ||
'60': 60, | ||
deep: -999999, | ||
auto: 'auto', | ||
alias: { | ||
overlay: 10, | ||
banner: 20, | ||
blanket: 30, | ||
modal: 60, | ||
overlay: 10, | ||
popover: 40, | ||
tooltip: 50, | ||
modal: 60, | ||
}, | ||
auto: 'auto', | ||
deep: -999999, | ||
}, | ||
}; | ||
//# sourceMappingURL=ndlTokensCopy.js.map |
import { tokens } from '@neo4j-ndl/base'; | ||
import { expect, test } from 'vitest'; | ||
import { tokens as tokensCopy } from './ndlTokensCopy'; | ||
@@ -3,0 +4,0 @@ /* |
import { Extension } from '@codemirror/state'; | ||
export declare const basicNeo4jSetup: () => Extension[]; | ||
type SetupProps = { | ||
moveFocusOnTab?: boolean; | ||
}; | ||
export declare const basicNeo4jSetup: ({ moveFocusOnTab, }: SetupProps) => Extension[]; | ||
export {}; |
@@ -27,3 +27,3 @@ import { acceptCompletion, autocompletion, clearSnippet, closeBrackets, closeBracketsKeymap, closeCompletion, completionKeymap, nextSnippetField, prevSnippetField, snippetKeymap, } from '@codemirror/autocomplete'; | ||
}; | ||
export const basicNeo4jSetup = () => { | ||
export const basicNeo4jSetup = ({ moveFocusOnTab = false, }) => { | ||
const keymaps = [ | ||
@@ -37,18 +37,18 @@ closeBracketsKeymap, | ||
lintKeymap, | ||
{ | ||
].flat(); | ||
if (!moveFocusOnTab) { | ||
keymaps.push({ | ||
key: 'Tab', | ||
preventDefault: true, | ||
run: acceptCompletion, | ||
}, | ||
{ | ||
}, { | ||
key: 'Tab', | ||
preventDefault: true, | ||
run: insertTab, | ||
}, | ||
{ | ||
}, { | ||
key: 'Shift-Tab', | ||
preventDefault: true, | ||
run: indentLess, | ||
}, | ||
].flat(); | ||
}); | ||
} | ||
const extensions = []; | ||
@@ -55,0 +55,0 @@ extensions.push(highlightSpecialChars()); |
@@ -1,26 +0,12 @@ | ||
import { light, mirage } from 'ayu'; | ||
import { mirage } from 'ayu'; | ||
import { createCypherTheme, } from './lang-cypher/createCypherTheme'; | ||
import { tokens } from './ndlTokensCopy'; | ||
/* ndl exports most tokens as hex colors but some tokens are exported as rgb colors, in the form of "10, 20, 30" | ||
This should be fixed in version 2 of ndl. | ||
Meanwhile we can use this function */ | ||
const convertToHex = (color) => { | ||
if (color.startsWith('#')) { | ||
return color; | ||
} | ||
const rgb = color.match(/\d+/g); | ||
if (!rgb) { | ||
return color; | ||
} | ||
const [r, g, b] = rgb; | ||
return `#${Number(r).toString(16)}${Number(g).toString(16)}${Number(b).toString(16)}`; | ||
}; | ||
export const lightThemeConstants = { | ||
dark: false, | ||
editorSettings: { | ||
background: light.editor.bg.hex(), | ||
foreground: light.editor.fg.hex(), | ||
gutterForeground: light.editor.gutter.normal.hex(), | ||
selection: light.editor.selection.active.hex(), | ||
textMatchingSelection: light.editor.findMatch.active.hex(), | ||
background: '#FEFEFE', | ||
foreground: '#545454', | ||
gutterForeground: '#a3a7ae', | ||
selection: tokens.colors.neutral['20'], | ||
textMatchingSelection: tokens.colors.lavender['15'], | ||
cursor: '#000000', | ||
@@ -33,24 +19,21 @@ autoCompletionPanel: { | ||
searchPanel: { | ||
background: tokens.palette.light.neutral.bg.default, | ||
text: tokens.palette.light.neutral.text.default, | ||
buttonHoverBackground: tokens.palette.light.neutral.bg.strong, | ||
background: '#FEFEFE', | ||
text: '#545454', | ||
buttonHoverBackground: tokens.theme.light.palette.neutral.bg.strong, | ||
}, | ||
}, | ||
highlightStyles: { | ||
comment: light.syntax.comment.hex(), | ||
keyword: light.syntax.keyword.hex(), | ||
keywordLiteral: light.syntax.keyword.hex(), | ||
label: light.syntax.markup.hex(), | ||
predicateFunction: light.syntax.func.hex(), | ||
function: light.syntax.func.hex(), | ||
procedure: light.syntax.func.hex(), | ||
stringLiteral: light.syntax.string.hex(), | ||
numberLiteral: light.syntax.constant.hex(), | ||
booleanLiteral: light.syntax.constant.hex(), | ||
operator: light.syntax.operator.hex(), | ||
property: light.syntax.tag.hex(), | ||
paramDollar: light.syntax.regexp.hex(), | ||
paramValue: light.syntax.regexp.hex(), | ||
namespace: light.syntax.special.hex(), | ||
consoleCommand: light.editor.fg.hex(), | ||
comment: '#a3a7ae', | ||
keyword: '#008561', | ||
keywordLiteral: '#008561', | ||
label: '#de064e', | ||
predicateFunction: '#0177b8', | ||
function: '#0177b8', | ||
procedure: '#0177b8', | ||
stringLiteral: '#8c6b41', | ||
numberLiteral: '#9a4fcb', | ||
booleanLiteral: '#9a4fcb', | ||
operator: '#008561', | ||
property: '#0055ae', | ||
paramValue: '#9a4fcb', | ||
}, | ||
@@ -73,5 +56,5 @@ }; | ||
searchPanel: { | ||
background: convertToHex(tokens.palette.dark.neutral.bg.default), | ||
text: convertToHex(tokens.palette.dark.neutral.text.default), | ||
buttonHoverBackground: convertToHex(tokens.palette.dark.neutral.bg.strong), | ||
background: tokens.theme.dark.palette.neutral.bg.default, | ||
text: tokens.theme.dark.palette.neutral.text.default, | ||
buttonHoverBackground: tokens.theme.dark.palette.neutral.bg.strong, | ||
}, | ||
@@ -78,0 +61,0 @@ }, |
@@ -20,3 +20,3 @@ { | ||
], | ||
"version": "2.0.0-canary-48fd427", | ||
"version": "2.0.0-canary-50cc73e", | ||
"main": "./dist/index.js", | ||
@@ -29,3 +29,3 @@ "types": "./dist/index.d.ts", | ||
"clean": "rm -rf dist", | ||
"test": "jest", | ||
"test": "vitest run", | ||
"test:e2e": "playwright test -c playwright-ct.config.ts", | ||
@@ -47,12 +47,12 @@ "test:e2e-ui": "playwright test -c playwright-ct.config.ts --ui", | ||
"dependencies": { | ||
"@codemirror/autocomplete": "^6.5.1", | ||
"@codemirror/commands": "^6.2.2", | ||
"@codemirror/language": "^6.6.0", | ||
"@codemirror/lint": "^6.2.2", | ||
"@codemirror/search": "^6.5.0", | ||
"@codemirror/state": "^6.2.1", | ||
"@codemirror/view": "^6.13.2", | ||
"@codemirror/autocomplete": "^6.17.0", | ||
"@codemirror/commands": "^6.6.0", | ||
"@codemirror/language": "^6.10.2", | ||
"@codemirror/lint": "^6.8.1", | ||
"@codemirror/search": "^6.5.6", | ||
"@codemirror/state": "^6.4.1", | ||
"@codemirror/view": "^6.29.1", | ||
"@lezer/common": "^1.0.2", | ||
"@lezer/highlight": "^1.1.3", | ||
"@neo4j-cypher/language-support": "2.0.0-canary-48fd427", | ||
"@neo4j-cypher/language-support": "2.0.0-canary-50cc73e", | ||
"@types/prismjs": "^1.26.3", | ||
@@ -68,14 +68,16 @@ "@types/workerpool": "^6.4.7", | ||
"devDependencies": { | ||
"@neo4j-ndl/base": "^1.10.1", | ||
"@playwright/experimental-ct-react": "^1.39.0", | ||
"@playwright/test": "^1.36.2", | ||
"@neo4j-ndl/base": "^3.0.10", | ||
"@playwright/experimental-ct-react": "^1.45.3", | ||
"@playwright/test": "^1.45.3", | ||
"@types/lodash.debounce": "^4.0.9", | ||
"@types/react": "^18.0.28", | ||
"@vitejs/plugin-react": "^3.1.0", | ||
"@vitejs/plugin-react": "^4.3.1", | ||
"concurrently": "^8.2.1", | ||
"esbuild": "^0.19.4", | ||
"jsdom": "^24.1.1", | ||
"lodash": "^4.17.21", | ||
"playwright": "^1.36.2", | ||
"playwright": "^1.45.3", | ||
"react": "^18.2.0", | ||
"typescript": "^4.9.5" | ||
"typescript": "^4.9.5", | ||
"vite": "^5.3.5" | ||
}, | ||
@@ -82,0 +84,0 @@ "peerDependencies": { |
@@ -62,2 +62,12 @@ import { expect } from '@playwright/experimental-ct-react'; | ||
async checkNoNotificationMessage(type: 'error' | 'warning') { | ||
await this.page.waitForTimeout(10000); | ||
await expect(this.page.locator('.cm-lintRange-' + type)).toHaveCount(0, { | ||
timeout: 10000, | ||
}); | ||
await expect(this.page.locator('.cm-lintPoint-' + type)).toHaveCount(0, { | ||
timeout: 10000, | ||
}); | ||
} | ||
private async checkNotificationMessage( | ||
@@ -69,7 +79,9 @@ type: 'error' | 'warning', | ||
await expect(this.page.locator('.cm-lintRange-' + type).last()).toBeVisible( | ||
{ timeout: 3000 }, | ||
{ timeout: 10000 }, | ||
); | ||
await this.page.getByText(queryChunk, { exact: true }).hover(); | ||
await expect(this.page.locator('.cm-tooltip-hover').last()).toBeVisible(); | ||
await expect(this.page.locator('.cm-tooltip-hover').last()).toBeVisible({ | ||
timeout: 10000, | ||
}); | ||
await expect(this.page.getByText(expectedMsg)).toBeVisible(); | ||
@@ -76,0 +88,0 @@ /* Return the mouse to the beginning of the query and |
@@ -100,3 +100,3 @@ import { Extension, StateEffect } from '@codemirror/state'; | ||
effects: moveInHistory.of(direction), | ||
selection: { anchor: view.state.doc.length }, | ||
selection: { anchor: direction === 'BACK' ? 0 : view.state.doc.length }, | ||
}), | ||
@@ -103,0 +103,0 @@ ); |
@@ -1,7 +0,4 @@ | ||
export { | ||
CypherParser, | ||
_internalFeatureFlags, | ||
} from '@neo4j-cypher/language-support'; | ||
export * as LanguageSupport from '@neo4j-cypher/language-support'; | ||
export { CypherEditor } from './CypherEditor'; | ||
export { cypher } from './lang-cypher/langCypher'; | ||
export { darkThemeConstants, lightThemeConstants } from './themes'; |
@@ -6,4 +6,7 @@ import { | ||
} from '@codemirror/autocomplete'; | ||
import { autocomplete } from '@neo4j-cypher/language-support'; | ||
import { | ||
autocomplete, | ||
shouldAutoCompleteYield, | ||
} from '@neo4j-cypher/language-support'; | ||
import { | ||
CompletionItemKind, | ||
@@ -53,3 +56,3 @@ CompletionItemTag, | ||
if (completion.deprecated) { | ||
return 'cm-deprecated-completion'; | ||
return 'cm-deprecated-element'; | ||
} else { | ||
@@ -62,7 +65,7 @@ return null; | ||
(config) => (context) => { | ||
const textUntilCursor = context.state.doc.toString().slice(0, context.pos); | ||
const documentText = context.state.doc.toString(); | ||
const offset = context.pos; | ||
const triggerCharacters = ['.', ':', '{', '$', ')']; | ||
const lastCharacter = textUntilCursor.slice(-1); | ||
const lastCharacter = documentText.at(offset - 1); | ||
const yieldTriggered = shouldAutoCompleteYield(documentText, offset); | ||
const lastWord = context.matchBefore(/\w*/); | ||
@@ -72,3 +75,6 @@ const inWord = lastWord.from !== lastWord.to; | ||
const shouldTriggerCompletion = | ||
inWord || context.explicit || triggerCharacters.includes(lastCharacter); | ||
inWord || | ||
context.explicit || | ||
triggerCharacters.includes(lastCharacter) || | ||
yieldTriggered; | ||
@@ -84,69 +90,52 @@ if (config.useLightVersion && !context.explicit) { | ||
const options = autocomplete( | ||
textUntilCursor, | ||
// TODO This is a temporary hack because completions are not working well | ||
documentText.slice(0, offset), | ||
config.schema ?? {}, | ||
undefined, | ||
offset, | ||
context.explicit, | ||
); | ||
if (config.featureFlags.signatureInfoOnAutoCompletions) { | ||
return { | ||
from: context.matchBefore(/(\w|\$)*$/).from, | ||
options: options.map((o) => { | ||
let maybeInfo = {}; | ||
let emptyInfo = true; | ||
const newDiv = document.createElement('div'); | ||
return { | ||
from: context.matchBefore(/(\w|\$)*$/).from, | ||
options: options.map((o) => { | ||
let maybeInfo = {}; | ||
let emptyInfo = true; | ||
const newDiv = document.createElement('div'); | ||
if (o.signature) { | ||
const header = document.createElement('p'); | ||
header.setAttribute('class', 'cm-completionInfo-signature'); | ||
header.textContent = o.signature; | ||
if (header.textContent.length > 0) { | ||
emptyInfo = false; | ||
newDiv.appendChild(header); | ||
} | ||
if (o.signature) { | ||
const header = document.createElement('p'); | ||
header.setAttribute('class', 'cm-completionInfo-signature'); | ||
header.textContent = o.signature; | ||
if (header.textContent.length > 0) { | ||
emptyInfo = false; | ||
newDiv.appendChild(header); | ||
} | ||
} | ||
if (o.documentation) { | ||
const paragraph = document.createElement('p'); | ||
paragraph.textContent = getDocString(o.documentation); | ||
if (paragraph.textContent.length > 0) { | ||
emptyInfo = false; | ||
newDiv.appendChild(paragraph); | ||
} | ||
if (o.documentation) { | ||
const paragraph = document.createElement('p'); | ||
paragraph.textContent = getDocString(o.documentation); | ||
if (paragraph.textContent.length > 0) { | ||
emptyInfo = false; | ||
newDiv.appendChild(paragraph); | ||
} | ||
} | ||
if (!emptyInfo) { | ||
maybeInfo = { | ||
info: () => Promise.resolve(newDiv), | ||
}; | ||
} | ||
const deprecated = | ||
o.tags?.find((tag) => tag === CompletionItemTag.Deprecated) ?? | ||
false; | ||
// The negative boost moves the deprecation down the list | ||
// so we offer the user the completions that are | ||
// deprecated the last | ||
const maybeDeprecated = deprecated | ||
? { boost: -99, deprecated: true } | ||
: {}; | ||
if (!emptyInfo) { | ||
maybeInfo = { | ||
info: () => Promise.resolve(newDiv), | ||
}; | ||
} | ||
const deprecated = | ||
o.tags?.find((tag) => tag === CompletionItemTag.Deprecated) ?? false; | ||
// The negative boost moves the deprecation down the list | ||
// so we offer the user the completions that are | ||
// deprecated the last | ||
const maybeDeprecated = deprecated | ||
? { boost: -99, deprecated: true } | ||
: {}; | ||
return { | ||
label: o.label, | ||
type: completionKindToCodemirrorIcon(o.kind), | ||
apply: | ||
o.kind === CompletionItemKind.Snippet | ||
? // codemirror requires an empty snippet space to be able to tab out of the completion | ||
snippet((o.insertText ?? o.label) + '${}') | ||
: undefined, | ||
detail: o.detail, | ||
...maybeDeprecated, | ||
...maybeInfo, | ||
}; | ||
}), | ||
}; | ||
} else { | ||
return { | ||
from: context.matchBefore(/(\w|\$)*$/).from, | ||
options: options.map((o) => ({ | ||
label: o.label, | ||
return { | ||
label: o.insertText ? o.insertText : o.label, | ||
displayLabel: o.label, | ||
type: completionKindToCodemirrorIcon(o.kind), | ||
@@ -159,5 +148,7 @@ apply: | ||
detail: o.detail, | ||
})), | ||
}; | ||
} | ||
...maybeDeprecated, | ||
...maybeInfo, | ||
}; | ||
}), | ||
}; | ||
}; |
@@ -43,2 +43,4 @@ import { languageDataProp } from '@codemirror/language'; | ||
number: NodeType.define({ id: 28, name: 'numberLiteral' }), | ||
setting: NodeType.define({ id: 29, name: 'setting' }), | ||
settingValue: NodeType.define({ id: 30, name: 'settingValue' }), | ||
}); | ||
@@ -82,2 +84,4 @@ | ||
consoleCommand: tags.macroName, | ||
setting: tags.attributeName, | ||
settingValue: tags.attributeValue, | ||
}; | ||
@@ -84,0 +88,0 @@ |
@@ -6,2 +6,3 @@ import { tags } from '@lezer/highlight'; | ||
} from '@neo4j-cypher/language-support'; | ||
import { expect, test } from 'vitest'; | ||
import { tokenTypeToStyleTag } from './constants'; | ||
@@ -8,0 +9,0 @@ |
@@ -124,2 +124,5 @@ import { | ||
}, | ||
'& .cm-signature-help-panel-arg-description': { | ||
padding: '5px', | ||
}, | ||
'& .cm-signature-help-panel-description': { | ||
@@ -131,3 +134,3 @@ padding: '5px', | ||
}, | ||
'.cm-deprecated-completion': { | ||
'.cm-deprecated-element': { | ||
'text-decoration': 'line-through', | ||
@@ -134,0 +137,0 @@ }, |
@@ -7,10 +7,7 @@ import { autocompletion } from '@codemirror/autocomplete'; | ||
} from '@codemirror/language'; | ||
import { | ||
_internalFeatureFlags, | ||
type DbSchema, | ||
} from '@neo4j-cypher/language-support'; | ||
import { type DbSchema } from '@neo4j-cypher/language-support'; | ||
import { completionStyles, cypherAutocomplete } from './autocomplete'; | ||
import { ParserAdapter } from './parser-adapter'; | ||
import { signatureHelpTooltip } from './signatureHelp'; | ||
import { cypherLinter, semanticAnalysisLinter } from './syntaxValidation'; | ||
import { cypherLinter } from './syntaxValidation'; | ||
@@ -26,4 +23,4 @@ const facet = defineLanguageFacet({ | ||
featureFlags?: { | ||
signatureInfoOnAutoCompletions?: boolean; | ||
consoleCommands?: boolean; | ||
cypher25?: boolean; | ||
}; | ||
@@ -36,8 +33,2 @@ schema?: DbSchema; | ||
export function cypher(config: CypherConfig) { | ||
const featureFlags = config.featureFlags; | ||
// We allow to override the consoleCommands feature flag | ||
if (featureFlags.consoleCommands !== undefined) { | ||
_internalFeatureFlags.consoleCommands = featureFlags.consoleCommands; | ||
} | ||
const parserAdapter = new ParserAdapter(facet, config); | ||
@@ -53,5 +44,4 @@ | ||
cypherLinter(config), | ||
semanticAnalysisLinter(config), | ||
signatureHelpTooltip(config), | ||
]); | ||
} |
@@ -1,14 +0,31 @@ | ||
import { validateSemantics } from '@neo4j-cypher/language-support'; | ||
import { | ||
DbSchema, | ||
lintCypherQuery as _lintCypherQuery, | ||
_internalFeatureFlags, | ||
} from '@neo4j-cypher/language-support'; | ||
import workerpool from 'workerpool'; | ||
workerpool.worker({ validateSemantics }); | ||
function lintCypherQuery( | ||
query: string, | ||
dbSchema: DbSchema, | ||
featureFlags: { consoleCommands?: boolean; cypher25?: boolean } = {}, | ||
) { | ||
// We allow to override the consoleCommands feature flag | ||
if (featureFlags.consoleCommands !== undefined) { | ||
_internalFeatureFlags.consoleCommands = featureFlags.consoleCommands; | ||
} | ||
if (featureFlags.cypher25 !== undefined) { | ||
_internalFeatureFlags.cypher25 = featureFlags.cypher25; | ||
} | ||
return _lintCypherQuery(query, dbSchema); | ||
} | ||
type LinterArgs = Parameters<typeof validateSemantics>; | ||
workerpool.worker({ lintCypherQuery }); | ||
export type LinterTask = workerpool.Promise< | ||
ReturnType<typeof validateSemantics> | ||
>; | ||
type LinterArgs = Parameters<typeof lintCypherQuery>; | ||
export type LinterTask = workerpool.Promise<ReturnType<typeof lintCypherQuery>>; | ||
export type LintWorker = { | ||
validateSemantics: (...args: LinterArgs) => LinterTask; | ||
lintCypherQuery: (...args: LinterArgs) => LinterTask; | ||
}; |
import { EditorState, StateField } from '@codemirror/state'; | ||
import { showTooltip, Tooltip } from '@codemirror/view'; | ||
import { signatureHelp } from '@neo4j-cypher/language-support'; | ||
import { SignatureInformation } from 'vscode-languageserver-types'; | ||
import { | ||
MarkupContent, | ||
SignatureInformation, | ||
} from 'vscode-languageserver-types'; | ||
import { CypherConfig } from './langCypher'; | ||
@@ -41,8 +44,11 @@ import { getDocString } from './utils'; | ||
signatureLabel.className = 'cm-signature-help-panel-name'; | ||
signatureLabel.appendChild(document.createTextNode(`${signature.label}(`)); | ||
const methodName = signature.label.slice(0, signature.label.indexOf('(')); | ||
const returnType = signature.label.slice(signature.label.indexOf(')') + 1); | ||
signatureLabel.appendChild(document.createTextNode(`${methodName}(`)); | ||
let currentParamDescription: string | undefined = undefined; | ||
parameters.forEach((param, index) => { | ||
if (typeof param.documentation === 'string') { | ||
if (typeof param.label === 'string') { | ||
const span = document.createElement('span'); | ||
span.appendChild(document.createTextNode(param.documentation)); | ||
span.appendChild(document.createTextNode(param.label)); | ||
if (index !== parameters.length - 1) { | ||
@@ -54,2 +60,6 @@ span.appendChild(document.createTextNode(', ')); | ||
span.className = 'cm-signature-help-panel-current-argument'; | ||
const paramDoc = param.documentation; | ||
currentParamDescription = MarkupContent.is(paramDoc) | ||
? paramDoc.value | ||
: paramDoc; | ||
} | ||
@@ -61,2 +71,3 @@ signatureLabel.appendChild(span); | ||
signatureLabel.appendChild(document.createTextNode(')')); | ||
signatureLabel.appendChild(document.createTextNode(returnType)); | ||
@@ -70,8 +81,15 @@ contents.appendChild(signatureLabel); | ||
const description = document.createElement('div'); | ||
description.className = 'cm-signature-help-panel-description'; | ||
description.appendChild(document.createTextNode(doc)); | ||
if (currentParamDescription !== undefined) { | ||
const argDescription = document.createElement('div'); | ||
argDescription.className = 'cm-signature-help-panel-arg-description'; | ||
argDescription.appendChild( | ||
document.createTextNode(currentParamDescription), | ||
); | ||
contents.appendChild(argDescription); | ||
} | ||
const methodDescription = document.createElement('div'); | ||
methodDescription.className = 'cm-signature-help-panel-description'; | ||
methodDescription.appendChild(document.createTextNode(doc)); | ||
contents.appendChild(methodDescription); | ||
contents.appendChild(description); | ||
return { dom }; | ||
@@ -78,0 +96,0 @@ }; |
import { Diagnostic, linter } from '@codemirror/lint'; | ||
import { Extension } from '@codemirror/state'; | ||
import { parserWrapper, validateSyntax } from '@neo4j-cypher/language-support'; | ||
import { DiagnosticSeverity } from 'vscode-languageserver-types'; | ||
import { DiagnosticSeverity, DiagnosticTag } from 'vscode-languageserver-types'; | ||
import workerpool from 'workerpool'; | ||
import type { CypherConfig } from './langCypher'; | ||
import type { LinterTask, LintWorker } from './lintWorker'; | ||
import WorkerURL from './lintWorker?worker&url'; | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore ignore: https://v3.vitejs.dev/guide/features.html#import-with-query-suffixes | ||
import WorkerURL from './lintWorker?url&worker'; | ||
const pool = workerpool.pool(WorkerURL as string, { | ||
const pool = workerpool.pool(WorkerURL, { | ||
minWorkers: 2, | ||
@@ -22,26 +18,2 @@ workerOpts: { type: 'module' }, | ||
export const cypherLinter: (config: CypherConfig) => Extension = (config) => | ||
linter((view) => { | ||
if (!config.lint) { | ||
return []; | ||
} | ||
const query = view.state.doc.toString(); | ||
const syntaxErrors = validateSyntax(query, config.schema ?? {}); | ||
return syntaxErrors.map( | ||
(diagnostic): Diagnostic => ({ | ||
from: diagnostic.offsets.start, | ||
to: diagnostic.offsets.end, | ||
severity: | ||
diagnostic.severity === DiagnosticSeverity.Error | ||
? 'error' | ||
: 'warning', | ||
message: diagnostic.message, | ||
}), | ||
); | ||
}); | ||
export const semanticAnalysisLinter: (config: CypherConfig) => Extension = ( | ||
config, | ||
) => | ||
linter(async (view) => { | ||
@@ -51,3 +23,2 @@ if (!config.lint) { | ||
} | ||
const query = view.state.doc.toString(); | ||
@@ -58,14 +29,2 @@ if (query.length === 0) { | ||
// we want to avoid the ANTLR4 reparse in the worker thread, this should hit our main thread cache | ||
const parse = parserWrapper.parse(query); | ||
const statements = parse.statementsParsing; | ||
const anySyntacticError = | ||
statements.filter((statement) => statement.diagnostics.length !== 0) | ||
.length > 0; | ||
if (anySyntacticError) { | ||
return []; | ||
} | ||
try { | ||
@@ -77,17 +36,25 @@ if (lastSemanticJob !== undefined && !lastSemanticJob.resolved) { | ||
const proxyWorker = (await pool.proxy()) as unknown as LintWorker; | ||
lastSemanticJob = proxyWorker.validateSemantics( | ||
lastSemanticJob = proxyWorker.lintCypherQuery( | ||
query, | ||
config.schema ?? {}, | ||
config.featureFlags ?? {}, | ||
); | ||
const result = await lastSemanticJob; | ||
return result.map((diag) => { | ||
const a: Diagnostic[] = result.map((diagnostic) => { | ||
return { | ||
from: diag.offsets.start, | ||
to: diag.offsets.end, | ||
from: diagnostic.offsets.start, | ||
to: diagnostic.offsets.end, | ||
severity: | ||
diag.severity === DiagnosticSeverity.Error ? 'error' : 'warning', | ||
message: diag.message, | ||
diagnostic.severity === DiagnosticSeverity.Error | ||
? 'error' | ||
: 'warning', | ||
message: diagnostic.message, | ||
...(diagnostic.tags !== undefined && | ||
diagnostic.tags.includes(DiagnosticTag.Deprecated) | ||
? { markClass: 'cm-deprecated-element' } | ||
: {}), | ||
}; | ||
}); | ||
return a; | ||
} catch (err) { | ||
@@ -94,0 +61,0 @@ if (!(err instanceof workerpool.Promise.CancellationError)) { |
import { tokens } from '@neo4j-ndl/base'; | ||
import { expect, test } from 'vitest'; | ||
import { tokens as tokensCopy } from './ndlTokensCopy'; | ||
@@ -3,0 +4,0 @@ |
export const tokens = { | ||
transitions: { | ||
values: { | ||
properties: { | ||
default: 'all', | ||
}, | ||
duration: { | ||
quick: '100ms', | ||
slow: '250ms', | ||
}, | ||
'timing-function': { | ||
default: 'cubic-bezier(0.420, 0.000, 0.580, 1.000)', | ||
}, | ||
}, | ||
stripped: { | ||
quick: '100ms cubic-bezier(0.420, 0.000, 0.580, 1.000)', | ||
slow: '250ms cubic-bezier(0.420, 0.000, 0.580, 1.000)', | ||
}, | ||
full: { | ||
quick: 'all 100ms cubic-bezier(0.420, 0.000, 0.580, 1.000)', | ||
slow: 'all 250ms cubic-bezier(0.420, 0.000, 0.580, 1.000)', | ||
}, | ||
}, | ||
borderRadius: { | ||
none: '0px', | ||
sm: '4px', | ||
md: '6px', | ||
lg: '8px', | ||
xl: '10px', | ||
'1xl': '12px', | ||
@@ -9,120 +36,14 @@ '2xl': '14px', | ||
full: '9999px', | ||
lg: '8px', | ||
md: '6px', | ||
none: '0px', | ||
sm: '4px', | ||
xl: '10px', | ||
}, | ||
boxShadow: { | ||
l2: '0px 1px 2px 0px rgba(12, 26, 37, 0.18)', | ||
l3: '0px 4px 8px 0px rgba(12, 26, 37, 0.04)', | ||
l4: '0px 4px 8px 0px rgba(12, 26, 37, 0.08)', | ||
l5: '0px 8px 20px 0px rgba(12, 26, 37, 0.12)', | ||
}, | ||
breakpoints: { | ||
'2xl': '1536px', | ||
xs: '450px', | ||
sm: '640px', | ||
md: '768px', | ||
lg: '1024px', | ||
md: '768px', | ||
sm: '640px', | ||
xl: '1280px', | ||
xs: '450px', | ||
'2xl': '1536px', | ||
}, | ||
colors: { | ||
blueberry: { | ||
'10': '#E8EBF6', | ||
'20': '#C4CCE9', | ||
'30': '#9DABD9', | ||
'40': '#768ACA', | ||
'50': '#3557B4', | ||
'60': '#25459E', | ||
'70': '#0B297D', | ||
}, | ||
danger: { | ||
'10': '#ffe6e9', | ||
'20': '#ffb8c4', | ||
'30': '#ff668a', | ||
'40': '#ed1252', | ||
'50': '#cc254b', | ||
'60': '#a1003b', | ||
'70': '#7a0031', | ||
}, | ||
mint: { | ||
'10': '#F0FFFA', | ||
'20': '#D1FFF4', | ||
'30': '#A8FFEE', | ||
'40': '#55F9E2', | ||
'50': '#3DD4C5', | ||
'60': '#2AADA5', | ||
'70': '#116161', | ||
}, | ||
neutral: { | ||
'10': '#FFFFFF', | ||
'20': '#F5F7FA', | ||
'30': '#EEF1F6', | ||
'40': '#E6E9EE', | ||
'50': '#C4C8CD', | ||
'60': '#B2B7BD', | ||
'70': '#717780', | ||
'80': '#535B66', | ||
'90': '#151E29', | ||
}, | ||
primary: { | ||
'10': '#e6f8ff', | ||
'20': '#a3e2ff', | ||
'30': '#7ad1ff', | ||
'40': '#018bff', | ||
'50': '#006FD6', | ||
'60': '#0056b3', | ||
'70': '#004092', | ||
}, | ||
success: { | ||
'10': '#E1FAEF', | ||
'20': '#98EDCB', | ||
'30': '#44D4A4', | ||
'40': '#00BA88', | ||
'50': '#327D60', | ||
'60': '#006E58', | ||
'70': '#00473B', | ||
}, | ||
warning: { | ||
'10': '#FFFBDE', | ||
'20': '#FFF4B5', | ||
'30': '#FFEA8C', | ||
'40': '#FFDE63', | ||
'50': '#D9B54A', | ||
'60': '#966c2e', | ||
'70': '#664817', | ||
}, | ||
}, | ||
font: { | ||
size: { | ||
'body-large': '1rem', | ||
'body-medium': '0.875rem', | ||
'body-small': '0.75rem', | ||
code: '0.875rem', | ||
h1: '3rem', | ||
h2: '2.5rem', | ||
h3: '1.875rem', | ||
h4: '1.5rem', | ||
h5: '1.25rem', | ||
h6: '1rem', | ||
label: '0.875rem', | ||
'subheading-large': '1.25rem', | ||
'subheading-medium': '1rem', | ||
'subheading-small': '0.875rem', | ||
}, | ||
weight: { | ||
bold: '700', | ||
light: '300', | ||
medium: '500', | ||
normal: '400', | ||
semibold: '600', | ||
}, | ||
}, | ||
palette: { | ||
categorical: { | ||
'1': '#55BDC5', | ||
'10': '#BF732D', | ||
'11': '#478A6E', | ||
'12': '#ADE86B', | ||
'2': '#4D49CB', | ||
@@ -136,97 +57,8 @@ '3': '#DC8B39', | ||
'9': '#DBBF40', | ||
'10': '#BF732D', | ||
'11': '#478A6E', | ||
'12': '#ADE86B', | ||
}, | ||
dark: { | ||
danger: { | ||
bg: { | ||
strong: '#ffb8c4', | ||
weak: '68, 61, 72', | ||
}, | ||
border: { | ||
strong: '#ffb8c4', | ||
weak: '114, 91, 103', | ||
}, | ||
hover: { | ||
strong: '#ff668a', | ||
weak: 'rgba(255, 102, 138,0.08)', | ||
}, | ||
icon: '#ffb8c4', | ||
pressed: { | ||
strong: '#ff668a', | ||
weak: 'rgba(255, 102, 138,0.12)', | ||
}, | ||
text: '#ffb8c4', | ||
}, | ||
neutral: { | ||
bg: { | ||
default: '#151E29', | ||
strong: '45, 53, 63', | ||
strongest: '#FFFFFF', | ||
weak: '29, 38, 49', | ||
}, | ||
border: { | ||
strong: '#717780', | ||
weak: '37, 47, 59', | ||
}, | ||
hover: 'rgba(196, 200, 205,0.1)', | ||
icon: '#C4C8CD', | ||
pressed: 'rgba(196, 200, 205,0.2)', | ||
text: { | ||
default: '#F5F7FA', | ||
inverse: '#151E29', | ||
weak: '#C4C8CD', | ||
weaker: '#B2B7BD', | ||
weakest: '#717780', | ||
}, | ||
}, | ||
primary: { | ||
bg: { | ||
strong: '#a3e2ff', | ||
weak: '49, 69, 84', | ||
}, | ||
border: { | ||
strong: '#a3e2ff', | ||
weak: '78, 108, 126', | ||
}, | ||
focus: '#7ad1ff', | ||
hover: { | ||
strong: '#7ad1ff', | ||
weak: 'rgba(122, 209, 255,0.08)', | ||
}, | ||
icon: '#a3e2ff', | ||
pressed: { | ||
strong: '#7ad1ff', | ||
weak: 'rgba(122, 209, 255,0.12)', | ||
}, | ||
text: '#a3e2ff', | ||
}, | ||
success: { | ||
bg: { | ||
strong: '#98EDCB', | ||
weak: '47, 71, 73', | ||
}, | ||
border: { | ||
strong: '#98EDCB', | ||
weak: '73, 113, 106', | ||
}, | ||
icon: '#98EDCB', | ||
text: '#98EDCB', | ||
}, | ||
warning: { | ||
bg: { | ||
strong: '#FFEA8C', | ||
weak: '68, 71, 60', | ||
}, | ||
border: { | ||
strong: '#FFEA8C', | ||
weak: '114, 111, 80', | ||
}, | ||
icon: '#FFEA8C', | ||
text: '#FFEA8C', | ||
}, | ||
}, | ||
graph: { | ||
'1': '#FFDF81', | ||
'10': '#FFC354', | ||
'11': '#DA7294', | ||
'12': '#579380', | ||
'2': '#C990C0', | ||
@@ -240,100 +72,438 @@ '3': '#F79767', | ||
'9': '#4D8DDA', | ||
'10': '#FFC354', | ||
'11': '#DA7294', | ||
'12': '#579380', | ||
}, | ||
light: { | ||
danger: { | ||
bg: { | ||
strong: '#cc254b', | ||
weak: '#ffe6e9', | ||
}, | ||
colors: { | ||
baltic: { | ||
'10': '#E7FAFB', | ||
'15': '#C3F8FB', | ||
'20': '#8FE3E8', | ||
'25': '#5CC3C9', | ||
'30': '#5DB3BF', | ||
'35': '#51A6B1', | ||
'40': '#4C99A4', | ||
'45': '#30839D', | ||
'50': '#0A6190', | ||
'55': '#02507B', | ||
'60': '#014063', | ||
'65': '#262F31', | ||
'70': '#081E2B', | ||
'75': '#041823', | ||
'80': '#01121C', | ||
}, | ||
hibiscus: { | ||
'10': '#FFE9E7', | ||
'15': '#FFD7D2', | ||
'20': '#FFAA97', | ||
'25': '#FF8E6A', | ||
'30': '#F96746', | ||
'35': '#E84E2C', | ||
'40': '#D43300', | ||
'45': '#BB2D00', | ||
'50': '#961200', | ||
'55': '#730E00', | ||
'60': '#432520', | ||
'65': '#4E0900', | ||
'70': '#3F0800', | ||
'75': '#360700', | ||
'80': '#280500', | ||
}, | ||
forest: { | ||
'10': '#E7FCD7', | ||
'15': '#BCF194', | ||
'20': '#90CB62', | ||
'25': '#80BB53', | ||
'30': '#6FA646', | ||
'35': '#5B992B', | ||
'40': '#4D8622', | ||
'45': '#3F7824', | ||
'50': '#296127', | ||
'55': '#145439', | ||
'60': '#0C4D31', | ||
'65': '#0A4324', | ||
'70': '#262D24', | ||
'75': '#052618', | ||
'80': '#021D11', | ||
}, | ||
lemon: { | ||
'10': '#FFFAD1', | ||
'15': '#FFF8BD', | ||
'20': '#FFF178', | ||
'25': '#FFE500', | ||
'30': '#FFD600', | ||
'35': '#F4C318', | ||
'40': '#D7AA0A', | ||
'45': '#B48409', | ||
'50': '#996E00', | ||
'55': '#765500', | ||
'60': '#614600', | ||
'65': '#4D3700', | ||
'70': '#312E1A', | ||
'75': '#2E2100', | ||
'80': '#251B00', | ||
}, | ||
lavender: { | ||
'10': '#F7F3FF', | ||
'15': '#E9DEFF', | ||
'20': '#CCB4FF', | ||
'25': '#B38EFF', | ||
'30': '#A07BEC', | ||
'35': '#8C68D9', | ||
'40': '#754EC8', | ||
'45': '#5A34AA', | ||
'50': '#4B2894', | ||
'55': '#3B1982', | ||
'60': '#2C2A34', | ||
'65': '#220954', | ||
'70': '#170146', | ||
'75': '#0E002D', | ||
'80': '#09001C', | ||
}, | ||
marigold: { | ||
'10': '#FFF0D2', | ||
'15': '#FFDE9D', | ||
'20': '#FFCF72', | ||
'25': '#FFC450', | ||
'30': '#FFB422', | ||
'35': '#FFA901', | ||
'40': '#EC9C00', | ||
'45': '#DA9105', | ||
'50': '#BA7A00', | ||
'55': '#986400', | ||
'60': '#795000', | ||
'65': '#624100', | ||
'70': '#543800', | ||
'75': '#422C00', | ||
'80': '#251900', | ||
}, | ||
earth: { | ||
'10': '#FFF7F0', | ||
'15': '#FDEDDA', | ||
'20': '#FFE1C5', | ||
'25': '#F8D1AE', | ||
'30': '#ECBF96', | ||
'35': '#E0AE7F', | ||
'40': '#D19660', | ||
'45': '#AF7C4D', | ||
'50': '#8D5D31', | ||
'55': '#763F18', | ||
'60': '#66310B', | ||
'65': '#5B2B09', | ||
'70': '#481F01', | ||
'75': '#361700', | ||
'80': '#220E00', | ||
}, | ||
neutral: { | ||
'10': '#FFFFFF', | ||
'15': '#F5F6F6', | ||
'20': '#E2E3E5', | ||
'25': '#CFD1D4', | ||
'30': '#BBBEC3', | ||
'35': '#A8ACB2', | ||
'40': '#959AA1', | ||
'45': '#818790', | ||
'50': '#6F757E', | ||
'55': '#5E636A', | ||
'60': '#4D5157', | ||
'65': '#3C3F44', | ||
'70': '#212325', | ||
'75': '#1A1B1D', | ||
'80': '#09090A', | ||
}, | ||
beige: { | ||
'10': '#FFFCF4', | ||
'20': '#FFF7E3', | ||
'30': '#F2EAD4', | ||
'40': '#C1B9A0', | ||
'50': '#999384', | ||
'60': '#666050', | ||
'70': '#3F3824', | ||
}, | ||
highlights: { | ||
yellow: '#FAFF00', | ||
periwinkle: '#6A82FF', | ||
}, | ||
}, | ||
theme: { | ||
dark: { | ||
boxShadow: { | ||
raised: '0px 1px 2px 0px rgba(9, 9, 10, 0.50)', | ||
overlay: '0px 8px 20px 0px rgba(9, 9, 10, 0.50)', | ||
}, | ||
palette: { | ||
neutral: { | ||
text: { | ||
weakest: '#818790', | ||
weaker: '#A8ACB2', | ||
weak: '#CFD1D4', | ||
default: '#F5F6F6', | ||
inverse: '#1A1B1D', | ||
}, | ||
icon: '#CFD1D4', | ||
bg: { | ||
weak: '#212325', | ||
default: '#1A1B1D', | ||
strong: '#3C3F44', | ||
stronger: '#6F757E', | ||
strongest: '#F5F6F6', | ||
status: '#A8ACB2', | ||
'on-bg-weak': '#818790', | ||
}, | ||
border: { | ||
weak: '#3C3F44', | ||
strong: '#6F757E', | ||
strongest: '#BBBEC3', | ||
}, | ||
hover: '#959AA1', | ||
pressed: '#959AA1', | ||
}, | ||
border: { | ||
strong: '#cc254b', | ||
weak: '#ffb8c4', | ||
primary: { | ||
text: '#8FE3E8', | ||
icon: '#8FE3E8', | ||
bg: { | ||
weak: '#262F31', | ||
strong: '#8FE3E8', | ||
status: '#8FE3E8', | ||
selected: '#262F31', | ||
}, | ||
border: { | ||
strong: '#8FE3E8', | ||
weak: '#02507B', | ||
}, | ||
focus: '#5DB3BF', | ||
hover: { | ||
weak: '#8FE3E8', | ||
strong: '#5DB3BF', | ||
}, | ||
pressed: { | ||
weak: '#8FE3E8', | ||
strong: '#4C99A4', | ||
}, | ||
}, | ||
hover: { | ||
strong: '#a1003b', | ||
weak: 'rgba(237,18,82,0.08)', | ||
danger: { | ||
text: '#FFAA97', | ||
icon: '#FFAA97', | ||
bg: { | ||
strong: '#FFAA97', | ||
weak: '#432520', | ||
status: '#FFAA97', | ||
}, | ||
border: { | ||
strong: '#FFAA97', | ||
weak: '#730E00', | ||
}, | ||
hover: { | ||
weak: '#FFAA97', | ||
strong: '#F96746', | ||
}, | ||
pressed: { | ||
weak: '#FFAA97', | ||
strong: '#E84E2C', | ||
}, | ||
}, | ||
icon: '#cc254b', | ||
pressed: { | ||
strong: '#7a0031', | ||
weak: 'rgba(237,18,82,0.12)', | ||
warning: { | ||
text: '#FFD600', | ||
icon: '#FFD600', | ||
bg: { | ||
strong: '#FFD600', | ||
weak: '#312E1A', | ||
status: '#FFD600', | ||
}, | ||
border: { | ||
strong: '#FFD600', | ||
weak: '#765500', | ||
}, | ||
}, | ||
text: '#cc254b', | ||
}, | ||
neutral: { | ||
bg: { | ||
default: '#F5F7FA', | ||
strong: '#E6E9EE', | ||
strongest: '#535B66', | ||
weak: '#FFFFFF', | ||
success: { | ||
text: '#90CB62', | ||
icon: '#90CB62', | ||
bg: { | ||
strong: '#90CB62', | ||
weak: '#262D24', | ||
status: '#90CB62', | ||
}, | ||
border: { | ||
strong: '#90CB62', | ||
weak: '#296127', | ||
}, | ||
}, | ||
border: { | ||
strong: '#C4C8CD', | ||
weak: '#EEF1F6', | ||
discovery: { | ||
text: '#CCB4FF', | ||
icon: '#CCB4FF', | ||
bg: { | ||
strong: '#CCB4FF', | ||
weak: '#2C2A34', | ||
status: '#CCB4FF', | ||
}, | ||
border: { | ||
strong: '#CCB4FF', | ||
weak: '#4B2894', | ||
}, | ||
}, | ||
hover: 'rgba(113,119,128,0.1)', | ||
icon: '#535B66', | ||
pressed: 'rgba(113,119,128,0.2)', | ||
text: { | ||
default: '#151E29', | ||
inverse: '#FFFFFF', | ||
weak: '#535B66', | ||
weaker: '#717780', | ||
weakest: '#B2B7BD', | ||
}, | ||
}, | ||
primary: { | ||
bg: { | ||
strong: '#006FD6', | ||
weak: '#e6f8ff', | ||
}, | ||
light: { | ||
boxShadow: { | ||
raised: '0px 1px 2px 0px rgba(26, 27, 29, 0.18)', | ||
overlay: '0px 4px 8px 0px rgba(26, 27, 29, 0.12)', | ||
}, | ||
palette: { | ||
neutral: { | ||
text: { | ||
weakest: '#A8ACB2', | ||
weaker: '#5E636A', | ||
weak: '#4D5157', | ||
default: '#1A1B1D', | ||
inverse: '#FFFFFF', | ||
}, | ||
icon: '#4D5157', | ||
bg: { | ||
weak: '#FFFFFF', | ||
default: '#F5F6F6', | ||
'on-bg-weak': '#F5F6F6', | ||
strong: '#E2E3E5', | ||
stronger: '#A8ACB2', | ||
strongest: '#3C3F44', | ||
status: '#A8ACB2', | ||
}, | ||
border: { | ||
weak: '#E2E3E5', | ||
strong: '#BBBEC3', | ||
strongest: '#6F757E', | ||
}, | ||
hover: '#6F757E', | ||
pressed: '#6F757E', | ||
}, | ||
border: { | ||
strong: '#006FD6', | ||
weak: '#7ad1ff', | ||
primary: { | ||
text: '#0A6190', | ||
icon: '#0A6190', | ||
bg: { | ||
weak: '#E7FAFB', | ||
strong: '#0A6190', | ||
status: '#4C99A4', | ||
selected: '#E7FAFB', | ||
}, | ||
border: { | ||
strong: '#0A6190', | ||
weak: '#8FE3E8', | ||
}, | ||
focus: '#30839D', | ||
hover: { | ||
weak: '#30839D', | ||
strong: '#02507B', | ||
}, | ||
pressed: { | ||
weak: '#30839D', | ||
strong: '#014063', | ||
}, | ||
}, | ||
focus: '#018bff', | ||
hover: { | ||
strong: '#0056b3', | ||
weak: 'rgba(1,139,255,0.08)', | ||
danger: { | ||
text: '#BB2D00', | ||
icon: '#BB2D00', | ||
bg: { | ||
strong: '#BB2D00', | ||
weak: '#FFE9E7', | ||
status: '#E84E2C', | ||
}, | ||
border: { | ||
strong: '#BB2D00', | ||
weak: '#FFAA97', | ||
}, | ||
hover: { | ||
weak: '#D43300', | ||
strong: '#961200', | ||
}, | ||
pressed: { | ||
weak: '#D43300', | ||
strong: '#730E00', | ||
}, | ||
}, | ||
icon: '#006FD6', | ||
pressed: { | ||
strong: '#004092', | ||
weak: 'rgba(1,139,255,0.12)', | ||
warning: { | ||
text: '#765500', | ||
icon: '#765500', | ||
bg: { | ||
strong: '#765500', | ||
weak: '#FFFAD1', | ||
status: '#D7AA0A', | ||
}, | ||
border: { | ||
strong: '#996E00', | ||
weak: '#FFD600', | ||
}, | ||
}, | ||
text: '#006FD6', | ||
}, | ||
success: { | ||
bg: { | ||
strong: '#327D60', | ||
weak: '#E1FAEF', | ||
success: { | ||
text: '#3F7824', | ||
icon: '#3F7824', | ||
bg: { | ||
strong: '#3F7824', | ||
weak: '#E7FCD7', | ||
status: '#5B992B', | ||
}, | ||
border: { | ||
strong: '#3F7824', | ||
weak: '#90CB62', | ||
}, | ||
}, | ||
border: { | ||
strong: '#327D60', | ||
weak: '#98EDCB', | ||
discovery: { | ||
text: '#5A34AA', | ||
icon: '#5A34AA', | ||
bg: { | ||
strong: '#5A34AA', | ||
weak: '#E9DEFF', | ||
status: '#754EC8', | ||
}, | ||
border: { | ||
strong: '#5A34AA', | ||
weak: '#B38EFF', | ||
}, | ||
}, | ||
icon: '#327D60', | ||
text: '#327D60', | ||
}, | ||
warning: { | ||
bg: { | ||
strong: '#966c2e', | ||
weak: '#FFFBDE', | ||
}, | ||
border: { | ||
strong: '#966c2e', | ||
weak: '#FFEA8C', | ||
}, | ||
icon: '#966c2e', | ||
text: '#966c2e', | ||
}, | ||
}, | ||
}, | ||
font: { | ||
size: { | ||
h1: '3rem', | ||
h2: '2.5rem', | ||
h3: '1.875rem', | ||
h4: '1.5rem', | ||
h5: '1.25rem', | ||
h6: '1rem', | ||
'subheading-large': '1.25rem', | ||
'subheading-medium': '1rem', | ||
'subheading-small': '0.875rem', | ||
'body-large': '1rem', | ||
'body-medium': '0.875rem', | ||
'body-small': '0.75rem', | ||
code: '0.875rem', | ||
label: '0.875rem', | ||
}, | ||
weight: { | ||
bold: '700', | ||
semibold: '600', | ||
normal: '400', | ||
medium: '500', | ||
light: '300', | ||
}, | ||
'font-family': { | ||
h1: 'Syne Neo', | ||
h2: 'Syne Neo', | ||
h3: 'Public Sans', | ||
h4: 'Public Sans', | ||
h5: 'Public Sans', | ||
h6: 'Public Sans', | ||
'subheading-large': 'Public Sans', | ||
'subheading-medium': 'Public Sans', | ||
'subheading-small': 'Public Sans', | ||
'body-large': 'Public Sans', | ||
'body-medium': 'Public Sans', | ||
'body-small': 'Public Sans', | ||
code: 'Fira Code', | ||
label: 'Public Sans', | ||
}, | ||
}, | ||
space: { | ||
'0': '0px', | ||
'1': '1px', | ||
'10': '64px', | ||
'11': '96px', | ||
'12': '128px', | ||
'13': '320px', | ||
'2': '2px', | ||
@@ -347,18 +517,7 @@ '3': '4px', | ||
'9': '48px', | ||
'10': '64px', | ||
'11': '96px', | ||
'12': '128px', | ||
'13': '320px', | ||
}, | ||
transitions: { | ||
default: 'all 100ms cubic-bezier(0.420, 0.000, 0.580, 1.000)', | ||
stripped: '100ms cubic-bezier(0.420, 0.000, 0.580, 1.000)', | ||
values: { | ||
duration: { | ||
default: '100ms', | ||
}, | ||
properties: { | ||
default: 'all', | ||
}, | ||
'timing-function': { | ||
default: 'cubic-bezier(0.420, 0.000, 0.580, 1.000)', | ||
}, | ||
}, | ||
}, | ||
zIndex: { | ||
@@ -372,13 +531,13 @@ '0': 0, | ||
'60': 60, | ||
deep: -999999, | ||
auto: 'auto', | ||
alias: { | ||
overlay: 10, | ||
banner: 20, | ||
blanket: 30, | ||
modal: 60, | ||
overlay: 10, | ||
popover: 40, | ||
tooltip: 50, | ||
modal: 60, | ||
}, | ||
auto: 'auto', | ||
deep: -999999, | ||
}, | ||
}; |
import { Extension } from '@codemirror/state'; | ||
import { light, mirage } from 'ayu'; | ||
import { mirage } from 'ayu'; | ||
import { | ||
@@ -9,28 +9,10 @@ createCypherTheme, | ||
/* ndl exports most tokens as hex colors but some tokens are exported as rgb colors, in the form of "10, 20, 30" | ||
This should be fixed in version 2 of ndl. | ||
Meanwhile we can use this function */ | ||
const convertToHex = (color: string) => { | ||
if (color.startsWith('#')) { | ||
return color; | ||
} | ||
const rgb = color.match(/\d+/g); | ||
if (!rgb) { | ||
return color; | ||
} | ||
const [r, g, b] = rgb; | ||
return `#${Number(r).toString(16)}${Number(g).toString(16)}${Number( | ||
b, | ||
).toString(16)}`; | ||
}; | ||
export const lightThemeConstants: ThemeOptions = { | ||
dark: false, | ||
editorSettings: { | ||
background: light.editor.bg.hex(), | ||
foreground: light.editor.fg.hex(), | ||
gutterForeground: light.editor.gutter.normal.hex(), | ||
selection: light.editor.selection.active.hex(), | ||
textMatchingSelection: light.editor.findMatch.active.hex(), | ||
background: '#FEFEFE', | ||
foreground: '#545454', | ||
gutterForeground: '#a3a7ae', | ||
selection: tokens.colors.neutral['20'], | ||
textMatchingSelection: tokens.colors.lavender['15'], | ||
cursor: '#000000', | ||
@@ -43,24 +25,21 @@ autoCompletionPanel: { | ||
searchPanel: { | ||
background: tokens.palette.light.neutral.bg.default, | ||
text: tokens.palette.light.neutral.text.default, | ||
buttonHoverBackground: tokens.palette.light.neutral.bg.strong, | ||
background: '#FEFEFE', | ||
text: '#545454', | ||
buttonHoverBackground: tokens.theme.light.palette.neutral.bg.strong, | ||
}, | ||
}, | ||
highlightStyles: { | ||
comment: light.syntax.comment.hex(), | ||
keyword: light.syntax.keyword.hex(), | ||
keywordLiteral: light.syntax.keyword.hex(), | ||
label: light.syntax.markup.hex(), | ||
predicateFunction: light.syntax.func.hex(), | ||
function: light.syntax.func.hex(), | ||
procedure: light.syntax.func.hex(), | ||
stringLiteral: light.syntax.string.hex(), | ||
numberLiteral: light.syntax.constant.hex(), | ||
booleanLiteral: light.syntax.constant.hex(), | ||
operator: light.syntax.operator.hex(), | ||
property: light.syntax.tag.hex(), | ||
paramDollar: light.syntax.regexp.hex(), | ||
paramValue: light.syntax.regexp.hex(), | ||
namespace: light.syntax.special.hex(), | ||
consoleCommand: light.editor.fg.hex(), | ||
comment: '#a3a7ae', | ||
keyword: '#008561', | ||
keywordLiteral: '#008561', | ||
label: '#de064e', | ||
predicateFunction: '#0177b8', | ||
function: '#0177b8', | ||
procedure: '#0177b8', | ||
stringLiteral: '#8c6b41', | ||
numberLiteral: '#9a4fcb', | ||
booleanLiteral: '#9a4fcb', | ||
operator: '#008561', | ||
property: '#0055ae', | ||
paramValue: '#9a4fcb', | ||
}, | ||
@@ -84,7 +63,5 @@ }; | ||
searchPanel: { | ||
background: convertToHex(tokens.palette.dark.neutral.bg.default), | ||
text: convertToHex(tokens.palette.dark.neutral.text.default), | ||
buttonHoverBackground: convertToHex( | ||
tokens.palette.dark.neutral.bg.strong, | ||
), | ||
background: tokens.theme.dark.palette.neutral.bg.default, | ||
text: tokens.theme.dark.palette.neutral.text.default, | ||
buttonHoverBackground: tokens.theme.dark.palette.neutral.bg.strong, | ||
}, | ||
@@ -91,0 +68,0 @@ }, |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
713446
138
9828
14
+ Added@neo4j-cypher/language-support@2.0.0-canary-50cc73e(transitive)
- Removed@neo4j-cypher/language-support@2.0.0-canary-48fd427(transitive)
Updated@codemirror/commands@^6.6.0
Updated@codemirror/language@^6.10.2
Updated@codemirror/lint@^6.8.1
Updated@codemirror/search@^6.5.6
Updated@codemirror/state@^6.4.1
Updated@codemirror/view@^6.29.1