Skip to content

Commit 94771ea

Browse files
feat(ui): add auto-links to text, heading, field description and workflow descriptions
1 parent 51d6610 commit 94771ea

File tree

8 files changed

+65
-6
lines changed

8 files changed

+65
-6
lines changed

invokeai/frontend/web/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@
7575
"idb-keyval": "^6.2.1",
7676
"jsondiffpatch": "^0.6.0",
7777
"konva": "^9.3.15",
78+
"linkify-react": "^4.2.0",
79+
"linkifyjs": "^4.2.0",
7880
"lodash-es": "^4.17.21",
7981
"lru-cache": "^11.0.1",
8082
"mtwist": "^1.0.2",

invokeai/frontend/web/pnpm-lock.yaml

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import type { SystemStyleObject } from '@invoke-ai/ui-library';
2+
import type { Opts as LinkifyOpts } from 'linkifyjs';
3+
4+
export const linkifySx: SystemStyleObject = {
5+
a: {
6+
fontWeight: 'semibold',
7+
},
8+
'a:hover': {
9+
textDecoration: 'underline',
10+
},
11+
};
12+
13+
export const linkifyOptions: LinkifyOpts = {
14+
target: '_blank',
15+
rel: 'noopener noreferrer',
16+
validate: (value) => /^https?:\/\//.test(value),
17+
};

invokeai/frontend/web/src/features/nodes/components/sidePanel/WorkflowListMenu/ActiveWorkflowDescription.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { Text } from '@invoke-ai/ui-library';
22
import { useAppSelector } from 'app/store/storeHooks';
3+
import { linkifyOptions, linkifySx } from 'common/components/linkify';
34
import { selectWorkflowDescription } from 'features/nodes/store/workflowSlice';
5+
import Linkify from 'linkify-react';
46
import { memo } from 'react';
57

68
export const ActiveWorkflowDescription = memo(() => {
@@ -11,8 +13,8 @@ export const ActiveWorkflowDescription = memo(() => {
1113
}
1214

1315
return (
14-
<Text color="base.300" fontStyle="italic" pb={2}>
15-
{description}
16+
<Text color="base.300" fontStyle="italic" pb={2} sx={linkifySx}>
17+
<Linkify options={linkifyOptions}>{description}</Linkify>
1618
</Text>
1719
);
1820
});

invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/HeadingElementContent.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import type { HeadingProps, SystemStyleObject } from '@invoke-ai/ui-library';
22
import { Text } from '@invoke-ai/ui-library';
3+
import { linkifyOptions, linkifySx } from 'common/components/linkify';
4+
import Linkify from 'linkify-react';
35
import { memo } from 'react';
46
import { useTranslation } from 'react-i18next';
57

@@ -9,13 +11,14 @@ const headingSx: SystemStyleObject = {
911
'&[data-is-empty="true"]': {
1012
opacity: 0.3,
1113
},
14+
...linkifySx,
1215
};
1316

1417
export const HeadingElementContent = memo(({ content, ...rest }: { content: string } & HeadingProps) => {
1518
const { t } = useTranslation();
1619
return (
1720
<Text sx={headingSx} data-is-empty={content === ''} {...rest}>
18-
{content || t('workflows.builder.headingPlaceholder')}
21+
<Linkify options={linkifyOptions}>{content || t('workflows.builder.headingPlaceholder')}</Linkify>
1922
</Text>
2023
);
2124
});

invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementDescriptionEditable.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import { FormHelperText, Textarea } from '@invoke-ai/ui-library';
22
import { useAppDispatch } from 'app/store/storeHooks';
3+
import { linkifyOptions, linkifySx } from 'common/components/linkify';
34
import { useEditable } from 'common/hooks/useEditable';
45
import { useInputFieldDescription } from 'features/nodes/hooks/useInputFieldDescription';
56
import { useInputFieldTemplate } from 'features/nodes/hooks/useInputFieldTemplate';
67
import { fieldDescriptionChanged } from 'features/nodes/store/nodesSlice';
78
import type { NodeFieldElement } from 'features/nodes/types/workflow';
9+
import Linkify from 'linkify-react';
810
import { memo, useCallback, useRef } from 'react';
911

1012
export const NodeFieldElementDescriptionEditable = memo(({ el }: { el: NodeFieldElement }) => {
@@ -36,7 +38,11 @@ export const NodeFieldElementDescriptionEditable = memo(({ el }: { el: NodeField
3638
});
3739

3840
if (!editable.isEditing) {
39-
return <FormHelperText onDoubleClick={editable.startEditing}>{editable.value}</FormHelperText>;
41+
return (
42+
<FormHelperText onDoubleClick={editable.startEditing} sx={linkifySx}>
43+
<Linkify options={linkifyOptions}>{editable.value}</Linkify>
44+
</FormHelperText>
45+
);
4046
}
4147

4248
return (

invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/NodeFieldElementViewMode.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import type { SystemStyleObject } from '@invoke-ai/ui-library';
22
import { Flex, FormControl, FormHelperText } from '@invoke-ai/ui-library';
3+
import { linkifyOptions, linkifySx } from 'common/components/linkify';
34
import { InputFieldRenderer } from 'features/nodes/components/flow/nodes/Invocation/fields/InputFieldRenderer';
45
import { useContainerContext } from 'features/nodes/components/sidePanel/builder/contexts';
56
import { NodeFieldElementLabel } from 'features/nodes/components/sidePanel/builder/NodeFieldElementLabel';
67
import { useInputFieldDescription } from 'features/nodes/hooks/useInputFieldDescription';
78
import { useInputFieldTemplate } from 'features/nodes/hooks/useInputFieldTemplate';
89
import type { NodeFieldElement } from 'features/nodes/types/workflow';
910
import { NODE_FIELD_CLASS_NAME } from 'features/nodes/types/workflow';
11+
import Linkify from 'linkify-react';
1012
import { memo, useMemo } from 'react';
1113

1214
const sx: SystemStyleObject = {
@@ -43,7 +45,11 @@ export const NodeFieldElementViewMode = memo(({ el }: { el: NodeFieldElement })
4345
settings={data.settings}
4446
/>
4547
</Flex>
46-
{showDescription && _description && <FormHelperText>{_description}</FormHelperText>}
48+
{showDescription && _description && (
49+
<FormHelperText sx={linkifySx}>
50+
<Linkify options={linkifyOptions}>{_description}</Linkify>
51+
</FormHelperText>
52+
)}
4753
</FormControl>
4854
</Flex>
4955
);

invokeai/frontend/web/src/features/nodes/components/sidePanel/builder/TextElementContent.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import type { SystemStyleObject, TextProps } from '@invoke-ai/ui-library';
22
import { Text } from '@invoke-ai/ui-library';
3+
import { linkifyOptions, linkifySx } from 'common/components/linkify';
4+
import Linkify from 'linkify-react';
35
import { memo } from 'react';
46
import { useTranslation } from 'react-i18next';
57

@@ -9,13 +11,14 @@ const textSx: SystemStyleObject = {
911
'&[data-is-empty="true"]': {
1012
opacity: 0.3,
1113
},
14+
...linkifySx,
1215
};
1316

1417
export const TextElementContent = memo(({ content, ...rest }: { content: string } & TextProps) => {
1518
const { t } = useTranslation();
1619
return (
1720
<Text sx={textSx} data-is-empty={content === ''} {...rest}>
18-
{content || t('workflows.builder.textPlaceholder')}
21+
<Linkify options={linkifyOptions}>{content || t('workflows.builder.textPlaceholder')}</Linkify>
1922
</Text>
2023
);
2124
});

0 commit comments

Comments
 (0)