Skip to content

Commit bed2e89

Browse files
committed
fix: fix setting resource and change formatting
1 parent 8d3c1e3 commit bed2e89

File tree

12 files changed

+144
-61
lines changed

12 files changed

+144
-61
lines changed

src/components/App/ExtensibilityRoutes.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ const ColumnWrapper = ({ resourceType, extension, urlPath }) => {
3737
apiGroup: extension?.general.resource.group,
3838
apiVersion: extension?.general.resource.version,
3939
resourceName: resourceName,
40+
resource:
41+
layoutState?.showCreate?.resource ||
42+
layoutState?.showEdit?.resource ||
43+
null,
4044
});
4145

4246
const overrides = { resourceType: urlPath };

src/components/KymaCompanion/components/Chat/messages/CodePanel.tsx

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import {
44
Title,
55
Icon,
66
FlexBox,
7-
Link,
87
Button,
98
} from '@ui5/webcomponents-react';
10-
import { formatCodeSegment } from 'components/KymaCompanion/utils/formatMarkdown';
9+
import {
10+
Segment,
11+
formatCodeSegment,
12+
} from 'components/KymaCompanion/utils/formatMarkdown';
1113
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
1214
import { useRecoilState, useRecoilValue } from 'recoil';
1315
import {
@@ -18,7 +20,6 @@ import {
1820
import copyToCliboard from 'copy-to-clipboard';
1921
import './CodePanel.scss';
2022
import { columnLayoutState } from 'state/columnLayoutAtom';
21-
import { useUrl } from 'hooks/useUrl';
2223
import { useNavigate, useSearchParams } from 'react-router-dom';
2324
import { clusterState } from 'state/clusterAtom';
2425
import jsyaml from 'js-yaml';
@@ -65,15 +66,16 @@ function getCustomTheme(theme: Theme) {
6566
}
6667

6768
interface CodePanelProps {
68-
segment: object;
69+
segment: CodeSegment;
6970
}
7071

71-
export default function CodePanel({ segment }: CodePanelProps): JSX.Element {
72+
export default function CodePanel({
73+
segment,
74+
}: CodePanelProps | Segment): JSX.Element {
7275
const theme = useRecoilValue(themeState);
7376
const syntaxTheme = getCustomTheme(theme);
7477
const { language, code } = formatCodeSegment(segment?.content ?? segment);
7578
const [layoutState, setLayoutColumn] = useRecoilState(columnLayoutState);
76-
const currUrl = useUrl();
7779
const navigate = useNavigate();
7880
const [searchParams] = useSearchParams();
7981
const cluster = useRecoilValue(clusterState);
@@ -83,20 +85,27 @@ export default function CodePanel({ segment }: CodePanelProps): JSX.Element {
8385
const resourcePath = `${
8486
namespace ? `/namespaces/${namespace}` : ''
8587
}/${pluralize(resType).toLowerCase()}${resName ? '/' + resName : ''}`;
86-
// const layoutParam = searchParams.get('layout')
87-
// ? `?layout=${searchParams.get('layout')}`
88-
// : '';
89-
const createParams =
90-
type === 'Update'
91-
? '?layout=TwoColumnsMidExpanded&showEdit=true'
92-
: '?layout=TwoColumnsMidExpanded&showCreate=true';
9388

94-
return `${basePath}${resourcePath}${createParams}`;
89+
const params = new URLSearchParams();
90+
if (layoutState.layout !== 'OneColumn') {
91+
params.set('layout', layoutState.layout);
92+
}
93+
if (type === 'Update') {
94+
params.set('showEdit', 'true');
95+
} else {
96+
params.set('showCreate', 'true');
97+
}
98+
99+
return `${basePath}${resourcePath}?${params}`;
95100
};
96101

97102
const handleSetupInEditor = (url, resource, type) => {
98103
const parts = url.split('/').slice(1); // Remove the leading empty string from split
99104
let [namespace, resType, resName] = [null, '', ''];
105+
const parsedResource = jsyaml.load(resource.replace('yaml', '')) as
106+
| object
107+
| null;
108+
100109
if (parts[0] === 'namespaces') {
101110
[namespace, resType, resName] = [parts[1], parts[2], parts[3]];
102111

@@ -107,7 +116,7 @@ export default function CodePanel({ segment }: CodePanelProps): JSX.Element {
107116
type === 'New'
108117
? {
109118
...layoutState.showCreate,
110-
resource: jsyaml.load(resource.replace('yaml', '')),
119+
resource: parsedResource,
111120
resourceType: resType,
112121
namespaceId: namespace,
113122
}
@@ -116,9 +125,10 @@ export default function CodePanel({ segment }: CodePanelProps): JSX.Element {
116125
type === 'Update'
117126
? {
118127
...layoutState.showEdit,
119-
resource: jsyaml.load(resource.replace('yaml', '')),
128+
resource: parsedResource,
120129
resourceType: resType,
121130
namespaceId: namespace,
131+
resourceName: resName,
122132
}
123133
: null,
124134
});
@@ -148,17 +158,22 @@ export default function CodePanel({ segment }: CodePanelProps): JSX.Element {
148158
<Title level="H6" size="H6">
149159
{language}
150160
</Title>
151-
<Button icon="copy" onClick={() => copyToCliboard(code)}>
152-
{segment?.link?.name}
161+
<Button
162+
design="Transparent"
163+
icon="copy"
164+
onClick={() => copyToCliboard(code)}
165+
>
166+
Copy
153167
</Button>
154168
{segment?.type === 'codeWithAction' && (
155169
<Button
170+
design="Transparent"
156171
icon="sys-add"
157172
onClick={() =>
158173
handleSetupInEditor(
159174
segment?.link?.address,
160175
segment?.content,
161-
segment?.link?.type,
176+
segment?.link?.actionType,
162177
)
163178
}
164179
>

src/components/KymaCompanion/components/Chat/messages/Message.tsx

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,11 @@
1-
import { Link, Text } from '@ui5/webcomponents-react';
1+
import { Text } from '@ui5/webcomponents-react';
22
import CodePanel from './CodePanel';
33
import TasksList from './TasksList';
44
import {
55
handleResponseFormatting,
66
segmentMarkdownText,
77
} from 'components/KymaCompanion/utils/formatMarkdown';
88
import './Message.scss';
9-
import { useRecoilState, useRecoilValue } from 'recoil';
10-
import { columnLayoutState } from 'state/columnLayoutAtom';
11-
import { useUrl } from 'hooks/useUrl';
12-
import { useNavigate, useSearchParams } from 'react-router-dom';
13-
import { clusterState } from 'state/clusterAtom';
14-
import pluralize from 'pluralize';
159

1610
interface MessageProps {
1711
className: string;
@@ -45,14 +39,9 @@ export default function Message({
4539
return <TasksList messageChunks={messageChunks} />;
4640
}
4741

48-
const test = segmentMarkdownText(
49-
messageChunks.slice(-1)[0]?.data?.answer?.content,
50-
);
5142
const segmentedText = handleResponseFormatting(
5243
messageChunks.slice(-1)[0]?.data?.answer?.content,
5344
);
54-
console.log(segmentedText);
55-
console.log(test);
5645

5746
return (
5847
<div className={'message ' + className}>

src/components/KymaCompanion/utils/formatMarkdown.ts

Lines changed: 68 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
1+
export interface CodeSegmentLink {
2+
address: string;
3+
actionType: string;
4+
name: string;
5+
}
6+
export interface CodeSegment {
7+
content: string;
8+
type: 'codeWithAction' | 'code';
9+
link: CodeSegmentLink;
10+
}
111
export type Segment =
212
| { type: 'bold'; content: string }
313
| { type: 'code'; content: string }
414
| { type: 'highlighted'; content: string }
515
| { type: 'normal'; content: string }
6-
| {
7-
type: 'codeWithAction';
8-
content: string;
9-
link: { name: string; type: string; address: string };
10-
};
16+
| CodeSegment;
1117

1218
export function segmentMarkdownText(text: string): Segment[] {
1319
if (!text) return [];
@@ -49,30 +55,71 @@ export function formatCodeSegment(
4955
return { language, code };
5056
}
5157

58+
function extractYamlBlocks(text: string) {
59+
const yamlMatches = [];
60+
61+
function findClosingDiv(text: string, startIndex: number) {
62+
let openDivs = 0;
63+
let i = startIndex;
64+
while (i < text.length) {
65+
if (text.startsWith('<div', i)) openDivs++;
66+
if (text.startsWith('</div>', i)) {
67+
openDivs--;
68+
if (openDivs === 0) return i + 6; // End of matching closing div
69+
}
70+
i++;
71+
}
72+
return -1;
73+
}
74+
75+
while (true) {
76+
const blockStart = text.indexOf('<div class="yaml-block');
77+
if (blockStart === -1) break;
78+
79+
const blockEnd = findClosingDiv(text, blockStart);
80+
if (blockEnd === -1) break;
81+
82+
const block = text.substring(blockStart, blockEnd);
83+
84+
const yamlStart = block.indexOf('```') + 3;
85+
const yamlEnd = block.indexOf('```', yamlStart);
86+
if (yamlStart === -1 || yamlEnd === -1) break;
87+
const yamlContent = block.substring(yamlStart, yamlEnd).trim();
88+
89+
const linkMatch = block.match(
90+
/<div class="link" link-type="(.*?)">\s*\[(.*?)\]\((.*?)\)\s*<\/div>/,
91+
);
92+
93+
if (!linkMatch) break;
94+
95+
yamlMatches.push({
96+
content: yamlContent,
97+
link: {
98+
name: linkMatch[2].trim(),
99+
address: linkMatch[3].trim(),
100+
actionType: linkMatch[1].trim(),
101+
},
102+
});
103+
104+
text =
105+
text.substring(0, blockStart) +
106+
`[YAML_PLACEHOLDER_${yamlMatches.length - 1}]` +
107+
text.substring(blockEnd);
108+
}
109+
110+
return { text, yamlMatches };
111+
}
112+
52113
export function handleResponseFormatting(text: string) {
53114
if (!text) return [];
54115

55116
let formattedContent: Segment[] = [];
117+
const { text: updatedText, yamlMatches } = extractYamlBlocks(text);
56118

57-
// Step 1: Extract YAML blocks & links
58-
const yamlMatches: { content: string; link: string }[] = [];
59-
text = text.replace(
60-
/<div class="yaml-block>\s*<div class="yaml">\s*```([\s\S]*?)\n```\s*<\/div>\s*<div class="link" link-type="(.*?)">\s*\[(.*?)\]\((.*?)\)\s*<\/div>\s*<\/div>/g,
61-
(_, yamlContent, yamlLinkType, yamlLinkName, yamlLinkUrl) => {
62-
yamlMatches.push({
63-
content: yamlContent.trim(),
64-
link: { name: yamlLinkName, address: yamlLinkUrl, type: yamlLinkType },
65-
});
66-
return `[YAML_PLACEHOLDER_${yamlMatches.length - 1}]`; // Placeholder for reinsertion
67-
},
68-
);
119+
formattedContent = formattedContent.concat(segmentMarkdownText(updatedText));
69120

70-
formattedContent = formattedContent.concat(segmentMarkdownText(text));
71-
72-
// Step 3: Reinsert YAML blocks at their placeholders
73121
for (let i = 0; i < formattedContent.length; i++) {
74122
const match = formattedContent[i].content.match(/YAML_PLACEHOLDER_(\d+)/);
75-
76123
if (match) {
77124
const yamlIndex = parseInt(match[1], 10);
78125
formattedContent[i] = {

src/resources/createResourceRoutes.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ const ColumnWrapper = ({ list, details, create, ...props }) => {
5656
apiGroup: props.apiGroup,
5757
apiVersion: props.apiVersion,
5858
resourceName: resourceName,
59+
resource:
60+
layoutState?.showCreate?.resource ||
61+
layoutState?.showEdit?.resource ||
62+
null,
5963
});
6064

6165
const defaultColumn = resourceName ? 'details' : 'list';

src/resources/other/BusolaExtensions.routes.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ const ColumnWrapper = ({ defaultColumn = 'list' }) => {
3737
apiGroup: '',
3838
apiVersion: 'v1',
3939
resourceName: name,
40+
resource:
41+
layoutState?.showCreate?.resource ||
42+
layoutState?.showEdit?.resource ||
43+
null,
4044
});
4145

4246
const elementCreateProps = usePrepareCreateProps({

src/resources/other/CustomResources.routes.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ export const ColumnWrapper = () => {
4343
resourceName: crdName,
4444
isCustomResource: true,
4545
crName: crName,
46+
resource:
47+
layoutState?.showCreate?.resource ||
48+
layoutState?.showEdit?.resource ||
49+
null,
4650
});
4751
const defaultColumn = crName ? 'details' : 'list';
4852

src/resources/other/helmReleases.routes.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ const ColumnWrapper = ({ defaultColumn = 'list' }) => {
2424
apiGroup: '',
2525
apiVersion: 'v1',
2626
resourceName: releaseName,
27+
resource:
28+
layoutState?.showCreate?.resource ||
29+
layoutState?.showEdit?.resource ||
30+
null,
2731
});
2832

2933
let startColumnComponent = null;

src/resources/other/kymaModules.routes.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ const ColumnWraper = ({ defaultColumn = 'list', namespaced = false }) => {
4444
apiGroup: '',
4545
apiVersion: 'v1',
4646
resourceName: resourceName,
47+
resource:
48+
layoutState?.showCreate?.resource ||
49+
layoutState?.showEdit?.resource ||
50+
null,
4751
});
4852

4953
const [DeleteMessageBox, handleResourceDelete] = useDeleteResource({

src/shared/ResourceForm/components/ResourceForm.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,15 @@ export function ResourceForm({
7575
formWithoutPanel,
7676
}) {
7777
const layoutState = useRecoilValue(columnLayoutState);
78-
if (layoutState?.showCreate?.resource)
79-
resource = layoutState.showCreate.resource;
80-
if (layoutState?.showEdit?.resource) resource = layoutState.showEdit.resource;
78+
79+
useEffect(() => {
80+
if (layoutState?.showCreate?.resource) {
81+
setResource(layoutState.showCreate.resource);
82+
} else if (layoutState?.showEdit?.resource) {
83+
setResource(layoutState.showEdit.resource);
84+
}
85+
}, [layoutState?.showCreate?.resource, layoutState?.showEdit?.resource]);
86+
8187
// readonly schema ID, set only once
8288
const resourceSchemaId = useMemo(
8389
() => resource?.apiVersion + '/' + resource?.kind,

0 commit comments

Comments
 (0)