@sanity/assist 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +205 -0
- package/dist/index.d.ts +52 -0
- package/dist/index.esm.js +2341 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +2341 -0
- package/dist/index.js.map +1 -0
- package/package.json +98 -0
- package/sanity.json +8 -0
- package/src/_lib/connector/ConnectFromRegion.tsx +24 -0
- package/src/_lib/connector/ConnectToRegion.tsx +22 -0
- package/src/_lib/connector/ConnectorRegion.tsx +23 -0
- package/src/_lib/connector/ConnectorsProvider.tsx +19 -0
- package/src/_lib/connector/ConnectorsStore.ts +122 -0
- package/src/_lib/connector/ConnectorsStoreContext.ts +4 -0
- package/src/_lib/connector/helpers.ts +5 -0
- package/src/_lib/connector/index.ts +9 -0
- package/src/_lib/connector/mapConnectorToLine.ts +83 -0
- package/src/_lib/connector/types.ts +56 -0
- package/src/_lib/connector/useConnectorsStore.ts +13 -0
- package/src/_lib/connector/useRegionRects.ts +141 -0
- package/src/_lib/fixedListenQuery.ts +101 -0
- package/src/_lib/form/DocumentForm.tsx +197 -0
- package/src/_lib/form/helpers.ts +31 -0
- package/src/_lib/form/index.ts +1 -0
- package/src/_lib/randomKey.ts +29 -0
- package/src/_lib/useListeningQuery.ts +61 -0
- package/src/_lib/usePrevious.ts +9 -0
- package/src/assistConnectors/AssistConnectorsOverlay.tsx +132 -0
- package/src/assistConnectors/ConnectorPath.tsx +62 -0
- package/src/assistConnectors/draw/arrowPath.ts +9 -0
- package/src/assistConnectors/draw/connectorPath.ts +142 -0
- package/src/assistConnectors/index.ts +1 -0
- package/src/assistDocument/AssistDocumentContext.tsx +31 -0
- package/src/assistDocument/AssistDocumentContextProvider.tsx +17 -0
- package/src/assistDocument/AssistDocumentInput.tsx +46 -0
- package/src/assistDocument/RequestRunInstructionProvider.tsx +50 -0
- package/src/assistDocument/components/AssistDocumentForm.tsx +188 -0
- package/src/assistDocument/components/FieldRefPreview.tsx +27 -0
- package/src/assistDocument/components/InstructionsArrayField.tsx +8 -0
- package/src/assistDocument/components/InstructionsArrayInput.tsx +26 -0
- package/src/assistDocument/components/SelectedFieldContext.tsx +10 -0
- package/src/assistDocument/components/generic/HiddenFieldTitle.tsx +5 -0
- package/src/assistDocument/components/helpers.ts +21 -0
- package/src/assistDocument/components/instruction/BackToInstructionsLink.tsx +31 -0
- package/src/assistDocument/components/instruction/FieldRefInput.tsx +33 -0
- package/src/assistDocument/components/instruction/InstructionInput.tsx +87 -0
- package/src/assistDocument/components/instruction/PromptInput.tsx +52 -0
- package/src/assistDocument/components/instruction/appearance/IconInput.tsx +46 -0
- package/src/assistDocument/components/instruction/appearance/InstructionVisibility.tsx +37 -0
- package/src/assistDocument/hooks/useAssistDocumentContextValue.tsx +68 -0
- package/src/assistDocument/hooks/useDocumentState.ts +6 -0
- package/src/assistDocument/hooks/useInstructionToaster.tsx +74 -0
- package/src/assistDocument/hooks/useStudioAssistDocument.ts +119 -0
- package/src/assistDocument/index.ts +1 -0
- package/src/assistFormComponents/AssistField.tsx +51 -0
- package/src/assistFormComponents/AssistFormBlock.tsx +31 -0
- package/src/assistFormComponents/AssistInlineFormBlock.tsx +14 -0
- package/src/assistFormComponents/AssistItem.tsx +20 -0
- package/src/assistFormComponents/validation/listItem.tsx +63 -0
- package/src/assistFormComponents/validation/validationList.tsx +89 -0
- package/src/assistInspector/AssistInspector.tsx +379 -0
- package/src/assistInspector/FieldAutocomplete.tsx +119 -0
- package/src/assistInspector/InstructionTaskHistoryButton.tsx +261 -0
- package/src/assistInspector/constants.ts +1 -0
- package/src/assistInspector/helpers.ts +125 -0
- package/src/assistInspector/index.ts +26 -0
- package/src/assistLayout/AiAssistanceConfigContext.tsx +81 -0
- package/src/assistLayout/AlphaMigration.tsx +311 -0
- package/src/assistLayout/AssistLayout.tsx +38 -0
- package/src/assistLayout/RunInstructionProvider.tsx +222 -0
- package/src/components/AssistFeatureBadge.tsx +9 -0
- package/src/components/Delay.tsx +25 -0
- package/src/components/HideReferenceChangedBannerInput.tsx +25 -0
- package/src/components/SafeValueInput.tsx +73 -0
- package/src/components/TimeAgo.tsx +18 -0
- package/src/constants.ts +20 -0
- package/src/fieldActions/PrivateIcon.tsx +20 -0
- package/src/fieldActions/assistFieldActions.tsx +230 -0
- package/src/globals.d.ts +4 -0
- package/src/helpers/assistSupported.ts +44 -0
- package/src/helpers/ids.ts +19 -0
- package/src/helpers/misc.ts +16 -0
- package/src/helpers/typeUtils.ts +15 -0
- package/src/helpers/useAssistSupported.ts +10 -0
- package/src/index.ts +6 -0
- package/src/legacy-types.ts +72 -0
- package/src/onboarding/FieldActionsOnboarding.tsx +90 -0
- package/src/onboarding/FirstAssistedPathProvider.tsx +29 -0
- package/src/onboarding/InspectorOnboarding.tsx +46 -0
- package/src/onboarding/onboardingStore.ts +33 -0
- package/src/plugin.tsx +80 -0
- package/src/presence/AiFieldPresence.tsx +28 -0
- package/src/presence/AssistAvatar.tsx +96 -0
- package/src/presence/AssistDocumentPresence.tsx +58 -0
- package/src/presence/useAssistPresence.ts +61 -0
- package/src/schemas/assistDocumentSchema.tsx +450 -0
- package/src/schemas/contextDocumentSchema.tsx +56 -0
- package/src/schemas/index.ts +25 -0
- package/src/schemas/serialize/SchemTypeTool.tsx +102 -0
- package/src/schemas/serialize/schemaUtils.ts +37 -0
- package/src/schemas/serialize/serializeSchema.test.ts +382 -0
- package/src/schemas/serialize/serializeSchema.ts +162 -0
- package/src/schemas/serializedSchemaTypeSchema.ts +59 -0
- package/src/schemas/typeDefExtensions.ts +30 -0
- package/src/types.ts +167 -0
- package/src/useApiClient.ts +140 -0
- package/src/vite.config.ts +9 -0
- package/v2-incompatible.js +11 -0
package/src/plugin.tsx
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import {definePlugin, ObjectSchemaType} from 'sanity'
|
|
2
|
+
import {assistInspector} from './assistInspector'
|
|
3
|
+
import {AssistFieldWrapper} from './assistFormComponents/AssistField'
|
|
4
|
+
import {AssistLayout} from './assistLayout/AssistLayout'
|
|
5
|
+
import {AssistFormBlock} from './assistFormComponents/AssistFormBlock'
|
|
6
|
+
import {AssistItem} from './assistFormComponents/AssistItem'
|
|
7
|
+
import {SanityClient} from '@sanity/client'
|
|
8
|
+
import {SafeValueInput} from './components/SafeValueInput'
|
|
9
|
+
import {schemaTypes} from './schemas'
|
|
10
|
+
import {AssistInlineFormBlock} from './assistFormComponents/AssistInlineFormBlock'
|
|
11
|
+
import {assistFieldActions} from './fieldActions/assistFieldActions'
|
|
12
|
+
import {packageName} from './constants'
|
|
13
|
+
import {AssistDocumentInputWrapper} from './assistDocument/AssistDocumentInput'
|
|
14
|
+
import {createAssistDocumentPresence} from './presence/AssistDocumentPresence'
|
|
15
|
+
import {isSchemaAssistEnabled} from './helpers/assistSupported'
|
|
16
|
+
|
|
17
|
+
export interface AssistPluginConfig {
|
|
18
|
+
/**
|
|
19
|
+
* Set this to false to disable model migration from the alpha version of this plugin
|
|
20
|
+
*/
|
|
21
|
+
alphaMigration?: boolean
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
__customApiClient?: (defaultClient: SanityClient) => SanityClient
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const assist = definePlugin<AssistPluginConfig | void>((config) => {
|
|
30
|
+
const configWithDefaults = config ?? {}
|
|
31
|
+
return {
|
|
32
|
+
name: packageName,
|
|
33
|
+
|
|
34
|
+
schema: {
|
|
35
|
+
types: schemaTypes,
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
document: {
|
|
39
|
+
inspectors: (prev, context) => {
|
|
40
|
+
const docSchema = context.schema.get(context.documentType)
|
|
41
|
+
if (docSchema && isSchemaAssistEnabled(docSchema)) {
|
|
42
|
+
return [...prev, assistInspector]
|
|
43
|
+
}
|
|
44
|
+
return prev
|
|
45
|
+
},
|
|
46
|
+
unstable_fieldActions: (prev) => {
|
|
47
|
+
return [...prev, assistFieldActions]
|
|
48
|
+
},
|
|
49
|
+
unstable_languageFilter: (prev, {documentId, schema, schemaType}) => {
|
|
50
|
+
const docSchema = schema.get(schemaType) as ObjectSchemaType
|
|
51
|
+
return [...prev, createAssistDocumentPresence(documentId, docSchema)]
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
studio: {
|
|
56
|
+
components: {
|
|
57
|
+
layout: function Layout(props) {
|
|
58
|
+
return <AssistLayout {...props} config={configWithDefaults} />
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
form: {
|
|
64
|
+
components: {
|
|
65
|
+
input: AssistDocumentInputWrapper,
|
|
66
|
+
field: AssistFieldWrapper,
|
|
67
|
+
item: AssistItem,
|
|
68
|
+
block: AssistFormBlock,
|
|
69
|
+
inlineBlock: AssistInlineFormBlock,
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
plugins: [
|
|
74
|
+
definePlugin({
|
|
75
|
+
name: `${packageName}/safe-value-input`,
|
|
76
|
+
form: {components: {input: SafeValueInput}},
|
|
77
|
+
})(),
|
|
78
|
+
],
|
|
79
|
+
}
|
|
80
|
+
})
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// eslint-disable-next-line react/no-unused-prop-types
|
|
2
|
+
import {FormNodePresence} from 'sanity'
|
|
3
|
+
import {Card, Flex, Text, Tooltip} from '@sanity/ui'
|
|
4
|
+
import {Delay} from '../components/Delay'
|
|
5
|
+
import {AssistAvatar} from './AssistAvatar'
|
|
6
|
+
|
|
7
|
+
export function AiFieldPresence(props: {presence: FormNodePresence}) {
|
|
8
|
+
return (
|
|
9
|
+
<Card style={{position: 'relative', background: 'transparent'}} contentEditable={false}>
|
|
10
|
+
<Tooltip
|
|
11
|
+
placement="left"
|
|
12
|
+
content={
|
|
13
|
+
<Card padding={3} border>
|
|
14
|
+
<Flex align="center">
|
|
15
|
+
<Text size={1}>Running instruction...</Text>
|
|
16
|
+
</Flex>
|
|
17
|
+
</Card>
|
|
18
|
+
}
|
|
19
|
+
>
|
|
20
|
+
<div>
|
|
21
|
+
<Delay durationMs={200} ms={250}>
|
|
22
|
+
<AssistAvatar state="active" />
|
|
23
|
+
</Delay>
|
|
24
|
+
</div>
|
|
25
|
+
</Tooltip>
|
|
26
|
+
</Card>
|
|
27
|
+
)
|
|
28
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import {purple} from '@sanity/color'
|
|
2
|
+
import {SparklesIcon} from '@sanity/icons'
|
|
3
|
+
import {Text} from '@sanity/ui'
|
|
4
|
+
import {CSSProperties, useMemo} from 'react'
|
|
5
|
+
import {useColorSchemeValue} from 'sanity'
|
|
6
|
+
import styled, {keyframes} from 'styled-components'
|
|
7
|
+
|
|
8
|
+
const Root = styled.span`
|
|
9
|
+
display: block;
|
|
10
|
+
width: 25px;
|
|
11
|
+
height: 25px;
|
|
12
|
+
position: relative;
|
|
13
|
+
`
|
|
14
|
+
|
|
15
|
+
const dash = keyframes`
|
|
16
|
+
0% {
|
|
17
|
+
transform: rotate(0);
|
|
18
|
+
}
|
|
19
|
+
100% {
|
|
20
|
+
transform: rotate(43deg);
|
|
21
|
+
}
|
|
22
|
+
`
|
|
23
|
+
|
|
24
|
+
const Outline = styled.svg`
|
|
25
|
+
display: block;
|
|
26
|
+
position: absolute;
|
|
27
|
+
top: 0;
|
|
28
|
+
left: 0;
|
|
29
|
+
|
|
30
|
+
& > circle {
|
|
31
|
+
stroke: var(--ai-avatar-stroke-color);
|
|
32
|
+
stroke-width: 1.5px;
|
|
33
|
+
stroke-linecap: round;
|
|
34
|
+
transform-origin: center;
|
|
35
|
+
animation: ${dash} 500ms ease-in-out infinite;
|
|
36
|
+
transition: stroke-dasharray 200ms ease-in-out;
|
|
37
|
+
|
|
38
|
+
stroke-dasharray: 2.34px 0;
|
|
39
|
+
|
|
40
|
+
[data-state='active'] > & {
|
|
41
|
+
stroke-dasharray: 2px 2.34px;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
`
|
|
45
|
+
|
|
46
|
+
const IconDisc = styled.span`
|
|
47
|
+
background: var(--ai-avatar-disc-color);
|
|
48
|
+
color: white;
|
|
49
|
+
width: 21px;
|
|
50
|
+
height: 21px;
|
|
51
|
+
display: flex;
|
|
52
|
+
align-items: center;
|
|
53
|
+
justify-content: center;
|
|
54
|
+
border-radius: 10.5px;
|
|
55
|
+
position: absolute;
|
|
56
|
+
top: 2px;
|
|
57
|
+
left: 2px;
|
|
58
|
+
`
|
|
59
|
+
|
|
60
|
+
export function AssistAvatar(props: {state?: 'present' | 'active'}) {
|
|
61
|
+
const {state = 'present'} = props
|
|
62
|
+
const scheme = useColorSchemeValue()
|
|
63
|
+
|
|
64
|
+
const style = useMemo(() => {
|
|
65
|
+
if (scheme === 'dark') {
|
|
66
|
+
return {
|
|
67
|
+
[`--ai-avatar-stroke-color`]: purple[400].hex,
|
|
68
|
+
[`--ai-avatar-disc-color`]: purple[600].hex,
|
|
69
|
+
} as CSSProperties
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
[`--ai-avatar-stroke-color`]: purple[500].hex,
|
|
74
|
+
[`--ai-avatar-disc-color`]: purple[600].hex,
|
|
75
|
+
} as CSSProperties
|
|
76
|
+
}, [scheme])
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<Root data-state={state} style={style}>
|
|
80
|
+
<Outline
|
|
81
|
+
width="25"
|
|
82
|
+
height="25"
|
|
83
|
+
viewBox="0 0 25 25"
|
|
84
|
+
fill="none"
|
|
85
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
86
|
+
>
|
|
87
|
+
<circle cx="12.5" cy="12.5" r="11.75" />
|
|
88
|
+
</Outline>
|
|
89
|
+
<IconDisc>
|
|
90
|
+
<Text as="span" size={0} style={{color: 'inherit'}}>
|
|
91
|
+
<SparklesIcon />
|
|
92
|
+
</Text>
|
|
93
|
+
</IconDisc>
|
|
94
|
+
</Root>
|
|
95
|
+
)
|
|
96
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import {ObjectSchemaType} from 'sanity'
|
|
2
|
+
import {useMemo} from 'react'
|
|
3
|
+
import {useAssistDocumentContextValue} from '../assistDocument/hooks/useAssistDocumentContextValue'
|
|
4
|
+
import {aiPresence} from './useAssistPresence'
|
|
5
|
+
import {documentRootKey, fieldPresenceTypeName} from '../types'
|
|
6
|
+
import {Card, Flex} from '@sanity/ui'
|
|
7
|
+
import {AiFieldPresence} from './AiFieldPresence'
|
|
8
|
+
|
|
9
|
+
export function createAssistDocumentPresence(
|
|
10
|
+
documentId: string | undefined,
|
|
11
|
+
schemaType: ObjectSchemaType
|
|
12
|
+
) {
|
|
13
|
+
return function AssistDocumentPresenceWrapper() {
|
|
14
|
+
return documentId ? (
|
|
15
|
+
<AssistDocumentPresence documentId={documentId} schemaType={schemaType} />
|
|
16
|
+
) : null
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function AssistDocumentPresence(props: {documentId: string; schemaType: ObjectSchemaType}) {
|
|
21
|
+
const {assistDocument} = useAssistDocumentContextValue(
|
|
22
|
+
props.documentId,
|
|
23
|
+
props.schemaType as ObjectSchemaType
|
|
24
|
+
)
|
|
25
|
+
const anyPresence = useMemo(() => {
|
|
26
|
+
const anyPresence = assistDocument?.tasks
|
|
27
|
+
?.filter((run) => !run.ended && !run.reason)
|
|
28
|
+
?.flatMap((run) => run.presence ?? [])
|
|
29
|
+
.find((f) => f.started && new Date().getTime() - new Date(f.started).getTime() < 30000)
|
|
30
|
+
if (anyPresence) {
|
|
31
|
+
return aiPresence(anyPresence, [])
|
|
32
|
+
}
|
|
33
|
+
const anyRun = assistDocument?.tasks
|
|
34
|
+
?.filter((run) => !run.ended && !run.reason)
|
|
35
|
+
?.find((f) => f.started && new Date().getTime() - new Date(f.started).getTime() < 30000)
|
|
36
|
+
return anyRun
|
|
37
|
+
? aiPresence(
|
|
38
|
+
{
|
|
39
|
+
started: anyRun.started,
|
|
40
|
+
path: documentRootKey,
|
|
41
|
+
_key: anyRun._key,
|
|
42
|
+
_type: fieldPresenceTypeName,
|
|
43
|
+
},
|
|
44
|
+
[]
|
|
45
|
+
)
|
|
46
|
+
: undefined
|
|
47
|
+
}, [assistDocument?.tasks])
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<Card>
|
|
51
|
+
<Flex flex={1} justify="flex-end">
|
|
52
|
+
<Flex gap={2} align={'center'}>
|
|
53
|
+
{anyPresence && <AiFieldPresence presence={anyPresence} />}
|
|
54
|
+
</Flex>
|
|
55
|
+
</Flex>
|
|
56
|
+
</Card>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {useMemo} from 'react'
|
|
2
|
+
import {FormNodePresence, isKeySegment, Path, stringToPath} from 'sanity'
|
|
3
|
+
import {useAssistDocumentContext} from '../assistDocument/AssistDocumentContext'
|
|
4
|
+
import {AiPresence} from '../types'
|
|
5
|
+
import {maxHistoryVisibilityMs, pluginTitle} from '../constants'
|
|
6
|
+
|
|
7
|
+
const NO_PRESENCE: FormNodePresence[] = []
|
|
8
|
+
|
|
9
|
+
export function useAssistPresence(path: Path, showFocusWithin?: boolean): FormNodePresence[] {
|
|
10
|
+
const context = useAssistDocumentContext()
|
|
11
|
+
const assistDocument = context && 'assistDocument' in context ? context.assistDocument : undefined
|
|
12
|
+
const tasks = assistDocument?.tasks
|
|
13
|
+
|
|
14
|
+
return useMemo(() => {
|
|
15
|
+
const activePresence = tasks
|
|
16
|
+
?.filter((task) => !task.ended)
|
|
17
|
+
?.flatMap((task) => task.presence ?? [])
|
|
18
|
+
?.filter(
|
|
19
|
+
(p) =>
|
|
20
|
+
p.started && new Date().getTime() - new Date(p.started).getTime() < maxHistoryVisibilityMs
|
|
21
|
+
)
|
|
22
|
+
.filter((presence) => {
|
|
23
|
+
if (!presence.path || !path.length) {
|
|
24
|
+
return false
|
|
25
|
+
}
|
|
26
|
+
const statusPath = stringToPath(presence.path)
|
|
27
|
+
|
|
28
|
+
if (!showFocusWithin && statusPath.length !== path.length) {
|
|
29
|
+
return false
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return path.every((pathSegment, i) => {
|
|
33
|
+
const statusSegment = statusPath[i]
|
|
34
|
+
if (typeof pathSegment === 'string') {
|
|
35
|
+
return pathSegment === statusSegment
|
|
36
|
+
}
|
|
37
|
+
if (isKeySegment(pathSegment) && isKeySegment(statusSegment)) {
|
|
38
|
+
return pathSegment._key === statusSegment._key
|
|
39
|
+
}
|
|
40
|
+
return false
|
|
41
|
+
})
|
|
42
|
+
})
|
|
43
|
+
if (!activePresence?.length) {
|
|
44
|
+
return NO_PRESENCE
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return activePresence.map((status) => aiPresence(status, path))
|
|
48
|
+
}, [showFocusWithin, tasks, path])
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function aiPresence(presence: AiPresence, path: Path, title?: string): FormNodePresence {
|
|
52
|
+
return {
|
|
53
|
+
user: {
|
|
54
|
+
id: `sanity-assistant_${presence._key}`,
|
|
55
|
+
displayName: pluginTitle,
|
|
56
|
+
},
|
|
57
|
+
path: path,
|
|
58
|
+
sessionId: 'not-available',
|
|
59
|
+
lastActiveAt: presence?.started ?? new Date().toISOString(),
|
|
60
|
+
}
|
|
61
|
+
}
|