Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
94 commits
Select commit Hold shift + click to select a range
2561bbd
add rules and deps
dimaMachina Dec 9, 2025
f597cb9
wip
dimaMachina Dec 9, 2025
11ffaa1
wip
dimaMachina Dec 9, 2025
c857b56
remove forwardRef
dimaMachina Dec 9, 2025
31b3417
cleanup monaco-editor
dimaMachina Dec 9, 2025
4ceb280
remove tippy.js
dimaMachina Dec 9, 2025
6cb2844
cleanup css
dimaMachina Dec 9, 2025
478a23a
cleanup
dimaMachina Dec 9, 2025
8709673
wip from tiptap docs
dimaMachina Dec 9, 2025
3922386
add tailwindcss typography
dimaMachina Dec 9, 2025
2ee8125
remove custom react component
dimaMachina Dec 9, 2025
9c068d2
remove mathematics
dimaMachina Dec 9, 2025
f9db483
remove youtube
dimaMachina Dec 9, 2025
18e8740
remove details
dimaMachina Dec 9, 2025
fe123bd
cleanup
dimaMachina Dec 9, 2025
fdba458
cleanup
dimaMachina Dec 9, 2025
0b6f3bf
remove images extension
dimaMachina Dec 9, 2025
0b2c663
put button inside editor
dimaMachina Dec 9, 2025
544c9ed
use toggle icon instead of text
dimaMachina Dec 9, 2025
eec3a7d
polish
dimaMachina Dec 9, 2025
7f139e2
add persistency
dimaMachina Dec 9, 2025
01e5dad
setup tables
dimaMachina Dec 9, 2025
bc09856
upd
dimaMachina Dec 9, 2025
02adf32
enable raw text content mode
dimaMachina Dec 9, 2025
636345a
fixes
dimaMachina Dec 9, 2025
cc94bd1
add focus feature
dimaMachina Dec 10, 2025
2d8ecce
implement suggestions v0
dimaMachina Dec 10, 2025
c7c00c6
replace command with dropdown
dimaMachina Dec 10, 2025
e4e03ee
cleanup
dimaMachina Dec 10, 2025
ba93752
better selectItem
dimaMachina Dec 10, 2025
e6eaa33
cleanup
dimaMachina Dec 10, 2025
089d44d
dropdown shows, but not sticks on scroll
dimaMachina Dec 10, 2025
36fe18b
polish
dimaMachina Dec 10, 2025
0a03988
suggestion wip
dimaMachina Dec 11, 2025
fba7d36
styles
dimaMachina Dec 11, 2025
2fc1577
add missing imports
dimaMachina Dec 11, 2025
4a53391
DROPDOWN KEEPS ITS POSITION WOOOHOOO
dimaMachina Dec 11, 2025
3054907
move variable-list in separate file
dimaMachina Dec 11, 2025
8ec9709
improve ts types
dimaMachina Dec 11, 2025
e8d69b9
cleanup
dimaMachina Dec 11, 2025
d3abf44
make toggle works on real content
dimaMachina Dec 11, 2025
33bee6c
rename to isMarkdownMode
dimaMachina Dec 11, 2025
20b7375
remove mention list
dimaMachina Dec 11, 2025
05ee6fb
cleanup
dimaMachina Dec 11, 2025
ac8aeb7
dropdown stays on its place when scrolling
dimaMachina Dec 11, 2025
cd8c758
remove not needed stuff
dimaMachina Dec 11, 2025
6fa5932
fix type error
dimaMachina Dec 11, 2025
32b2ed0
polish
dimaMachina Dec 11, 2025
f2639fe
Merge branch 'main' into prd-5263
dimaMachina Dec 14, 2025
82cde55
format
dimaMachina Dec 14, 2025
af09dfa
format
dimaMachina Dec 14, 2025
7616e9a
back original extension plugin
dimaMachina Dec 14, 2025
a96ddaf
add allow allowEmptyBraceOnly
dimaMachina Dec 14, 2025
033e904
wip
dimaMachina Dec 14, 2025
af27fd7
{{var}} is tokenized to text and back to md
dimaMachina Dec 14, 2025
aec1c8e
wip
dimaMachina Dec 14, 2025
b825605
wip
dimaMachina Dec 14, 2025
c768d16
polish
dimaMachina Dec 14, 2025
6969678
workaround
dimaMachina Dec 14, 2025
8842f61
remove unneeded
dimaMachina Dec 14, 2025
39d6e79
refactor
dimaMachina Dec 14, 2025
c10cfb7
remove unneeded command
dimaMachina Dec 14, 2025
ae2567f
remove buildPromptContent
dimaMachina Dec 14, 2025
3aedf71
remove highlight extension
dimaMachina Dec 14, 2025
34f196e
remove highlight extension
dimaMachina Dec 14, 2025
408ebde
variables for `text -> md` and `md -> text` works fine
dimaMachina Dec 14, 2025
23a29a9
fix insertVariableTrigger
dimaMachina Dec 14, 2025
72c3416
back old prompt-editor code
dimaMachina Dec 15, 2025
87e25bd
set value and aria-invalid
dimaMachina Dec 15, 2025
712d056
add placeholder
dimaMachina Dec 15, 2025
77a09b5
placeholder setup
dimaMachina Dec 15, 2025
23ba55b
fix placeholder
dimaMachina Dec 15, 2025
417e2f6
upd
dimaMachina Dec 15, 2025
5a740fa
autofocus
dimaMachina Dec 15, 2025
2c61918
setup editable
dimaMachina Dec 15, 2025
e12e439
dynamic editable
dimaMachina Dec 15, 2025
89864bd
cleanup
dimaMachina Dec 15, 2025
c322941
setup onUpdate
dimaMachina Dec 15, 2025
e032256
update starter kit options
dimaMachina Dec 15, 2025
e6434f5
add deps to useEditor
dimaMachina Dec 15, 2025
31bd5f7
add errors for invalid variables
dimaMachina Dec 15, 2025
a5f657b
add changeset
dimaMachina Dec 15, 2025
6d6ea69
upd
dimaMachina Dec 15, 2025
2e131c0
fix cypress
dimaMachina Dec 15, 2025
ad71e3f
improve cursor rule
dimaMachina Dec 15, 2025
a306614
Merge branch 'main' into prd-5263
dimaMachina Dec 16, 2025
93696c8
remove old dropdown
dimaMachina Dec 16, 2025
01e15d8
use shadcn dropdown
dimaMachina Dec 16, 2025
5a0c034
use shadcn dropdown
dimaMachina Dec 16, 2025
9573489
do not format code blocks in tiptap
dimaMachina Dec 16, 2025
2e4808f
enhance `query` function
dimaMachina Dec 16, 2025
71842cd
improve variable list
dimaMachina Dec 16, 2025
7e37499
avoid losing focus on editor
dimaMachina Dec 16, 2025
0750309
remove codeBlock formatting
dimaMachina Dec 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/spicy-aliens-fry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@inkeep/agents-manage-ui": minor
---

replace `monaco-editor` with TipTap editor for `Prompt` and `Agent prompt` inputs fields
29 changes: 29 additions & 0 deletions .cursor/rules/react.mdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
alwaysApply: true
---

### Always use the `FC` type from React when declaring components

### Do *not* use `React.forwardRef`

React 19 no longer requires `forwardRef` for normal ref propagation.

✅ Do

```tsx
import { FC, RefObject } from 'react'

const MyComponent: FC<{ ref: RefObject<HTMLDivElement> }> = ({ ref, ...props }) => <>...</>
```

❌Don't

```tsx
import { forwardRef } from 'react'

const MyComponent = forwardRef<HTMLDivElement, MyProps>((props, ref) => <>...</>)
```

### Always the shorthand syntax for defining object literal methods `{ foo(...) {} }` instead of the long form `{ foo: function(...) {} }` or `{ foo: (...) => {} }`.

Use the long form only when the function body needs `this` — otherwise default to the concise shorthand version.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { contextConfig, fetchDefinition, headers } from '@inkeep/agents-core';
import { contextConfig, fetchDefinition } from '@inkeep/agents-core';
import { z } from 'zod';
import { inkeepApiKey } from '../credentials/inkeep-api-key';

Expand All @@ -9,7 +9,7 @@ const projectDescription = fetchDefinition({
fetchConfig: {
url: 'https://api.management.inkeep.com/graphql',
body: {
query: `
query: /* GraphQL */ `
query GetProjectAutogenInfo {
projectByIntegrationAuth {
chatSubjectName
Expand Down
8 changes: 5 additions & 3 deletions agents-manage-ui/cypress/e2e/agent.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,18 @@ describe('Agent', () => {
cy.typeInMonaco('headersSchema.json', JSON.stringify(headersJsonSchema));
cy.contains('Save changes').click();

cy.typeInMonaco('agent-prompt.template', '{');
cy.get('.tiptap').type('{');
cy.get('[aria-label=Suggest]').contains('contextVariablesValue');
cy.get('[aria-label=Suggest]').contains('headers.testHeadersJsonSchemaValue');
cy.get('[aria-label=Suggest]').contains('$env.');
});

it('should highlight as error unknown variables', () => {
cy.visit('/default/projects/my-weather-project/agents/weather-agent?pane=agent');
cy.typeInMonaco('agent-prompt.template', 'Hello {{unknown}} {{$env.MY_ENV}}');
cy.get('.squiggly-error').should('have.length', 1);
cy.get('.tiptap').type('Hello {{unknown}} {{$env.MY_ENV}}', {
parseSpecialCharSequences: false,
});
cy.contains('Unknown variables: unknown').should('exist');
});
});
});
11 changes: 11 additions & 0 deletions agents-manage-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,16 @@
"@radix-ui/react-tooltip": "^1.2.7",
"@shikijs/monaco": "^3.13.0",
"@sinclair/typebox": "^0.34.40",
"@tiptap/core": "^3.13.0",
"@tiptap/extension-placeholder": "^3.13.0",
"@tiptap/react": "^3.13.0",
"@tiptap/starter-kit": "^3.13.0",
"@tiptap/suggestion": "^3.13.0",
"@tiptap/extension-list": "^3.13.0",
"@tiptap/extension-table": "^3.13.0",
"@tiptap/extension-mention": "^3.13.0",
"@floating-ui/dom": "^1.7.4",
"@tiptap/markdown": "^3.13.0",
"@xyflow/react": "^12.8.2",
"ai": "6.0.0-beta.124",
"axios": "^1.11.0",
Expand Down Expand Up @@ -122,6 +132,7 @@
"zustand": "^5.0.7"
},
"devDependencies": {
"@tailwindcss/typography": "^0.5.19",
"@svgr/webpack": "^8.1.0",
"@tailwindcss/postcss": "^4",
"@testing-library/jest-dom": "^6.8.0",
Expand Down
12 changes: 1 addition & 11 deletions agents-manage-ui/src/app/[tenantId]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use client';

import type { IDisposable } from 'monaco-editor';
import { useTheme } from 'next-themes';
import { useEffect } from 'react';
import { AppSidebarProvider } from '@/components/sidebar-nav/app-sidebar-provider';
Expand Down Expand Up @@ -39,16 +38,7 @@ export default function Layout({ children }: LayoutProps<'/[tenantId]'>) {
}, [contextConfig, setVariableSuggestions]);

useEffect(() => {
let disposables: IDisposable[] = [];
setMonaco().then(($disposables) => {
disposables = $disposables;
});

return () => {
for (const disposable of disposables) {
disposable.dispose();
}
};
setMonaco();
}, [setMonaco]);

useEffect(() => {
Expand Down
1 change: 1 addition & 0 deletions agents-manage-ui/src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
@import "tailwindcss";
@import "tw-animate-css";
@plugin 'tailwind-scrollbar';
@plugin "@tailwindcss/typography";
@source "../../node_modules/streamdown/dist/index.js";

@custom-variant dark (&:is(.dark *));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { extractInvalidVariables } from '../prompt-editor-utils';

describe('extractInvalidVariables', () => {
const suggestions = ['user.name', 'headers.x-api-key', 'items[*].id'];

it('detects variables not in the suggestion list', () => {
const invalid = extractInvalidVariables('Hello {{unknown}} and {{another}}', suggestions);

expect(invalid).toEqual(['unknown', 'another']);
});

it('ignores allowed variable patterns', () => {
const invalid = extractInvalidVariables(
'{{$env.API_KEY}} {{items[*].id}} {{length(items)}}',
suggestions
);

expect(invalid).toEqual([]);
});
});
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import { Braces } from 'lucide-react';
import type { ComponentProps } from 'react';
import { useCallback, useState } from 'react';
import { PromptEditor } from '@/components/editors/prompt-editor';
import type { ComponentPropsWithoutRef } from 'react';
import { useCallback, useRef, useState } from 'react';
import { PromptEditor, type PromptEditorHandle } from '@/components/editors/prompt-editor';
import { extractInvalidVariables } from '@/components/editors/prompt-editor-utils';
import { ExpandableField } from '@/components/form/expandable-field';
import { Button } from '@/components/ui/button';
import { useMonacoStore } from '@/features/agent/state/use-monaco-store';
import { cn } from '@/lib/utils';

type PromptEditorProps = ComponentProps<typeof PromptEditor> & {
type PromptEditorProps = ComponentPropsWithoutRef<typeof PromptEditor> & {
name: string;
};

const engFormatter = new Intl.ListFormat('en', {
style: 'long',
type: 'conjunction',
});

export function ExpandablePromptEditor({
label,
isRequired = false,
Expand All @@ -24,41 +30,32 @@ export function ExpandablePromptEditor({
error?: string;
} & PromptEditorProps) {
const [open, onOpenChange] = useState(false);
const monaco = useMonacoStore((state) => state.monaco);
const uri = `${open ? 'expanded-' : ''}${name}.template` as const;
const editorRef = useRef<PromptEditorHandle>(null);

const handleAddVariable = useCallback(() => {
if (!monaco) {
return;
}
const model = monaco.editor.getModel(monaco.Uri.parse(uri));
const [editor] = monaco.editor.getEditors().filter((editor) => editor.getModel() === model);
if (!editor) {
return;
}

const selection = editor.getSelection();
const pos = selection ? selection.getStartPosition() : editor.getPosition();
if (!pos) return;

const range = new monaco.Range(pos.lineNumber, pos.column, pos.lineNumber, pos.column);
editor.executeEdits('insert-template-variable', [{ range, text: '{' }]);
editor.setPosition({ lineNumber: pos.lineNumber, column: pos.column + 1 });
editor.focus();
editor.trigger('insert-template-variable', 'editor.action.triggerSuggest', {});
}, [monaco, uri]);
editorRef.current?.insertVariableTrigger();
}, []);

const id = `${name}-label`;
const variableSuggestions = useMonacoStore((state) => state.variableSuggestions);

const allErrors =
error ||
((invalidVariables: string[]) => {
if (invalidVariables.length) {
return `Unknown variables: ${engFormatter.format(invalidVariables)}`;
}
})(extractInvalidVariables(props.value ?? '', variableSuggestions));

return (
<ExpandableField
id={id}
open={open}
onOpenChange={onOpenChange}
uri={uri}
label={label}
isRequired={isRequired}
hasError={!!error}
onLabelClick={() => editorRef.current?.focus()}
actions={
<Button
size="sm"
Expand All @@ -73,15 +70,15 @@ export function ExpandablePromptEditor({
}
>
<PromptEditor
uri={uri}
ref={editorRef}
autoFocus={open}
aria-invalid={error ? 'true' : undefined}
aria-invalid={allErrors ? 'true' : undefined}
className={cn(!open && 'max-h-96', 'min-h-16', className)}
hasDynamicHeight={!open}
aria-labelledby={id}
{...props}
/>
{error && <p className="text-sm text-red-600">{error}</p>}
{allErrors && <p className="text-sm text-red-600">{allErrors}</p>}
</ExpandableField>
);
}
20 changes: 20 additions & 0 deletions agents-manage-ui/src/components/editors/prompt-editor-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export function extractInvalidVariables(text: string, suggestions: string[]): string[] {
const validVariables = new Set(suggestions);
const matches = text.matchAll(/\{\{([^}]+)}}/g);
const invalid: string[] = [];

for (const [, variableName] of matches) {
const name = variableName.trim();
const isValid =
validVariables.has(name) ||
name.startsWith('$env.') ||
name.includes('[') ||
name.startsWith('length(');

if (!isValid) {
invalid.push(name);
}
}

return [...new Set(invalid)];
}
21 changes: 21 additions & 0 deletions agents-manage-ui/src/components/editors/prompt-editor.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.tiptap {
[data-type="taskList"] {
li {
display: flex;
align-items: flex-start;
gap: 0.25rem;
}
& > li > div > p:first-child {
margin: 0;
line-height: 1.2;
}
}
/* https://tiptap.dev/docs/editor/extensions/functionality/placeholder#additional-setup */
p.is-editor-empty:first-child::before {
color: var(--muted-foreground);
content: attr(data-placeholder);
float: left;
height: 0;
pointer-events: none;
}
}
Loading
Loading