@basementstudio/sanity-ai-image-plugin 0.0.0 → 0.1.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/README.md +281 -257
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/presets/article-featured-image.d.ts +3 -0
- package/dist/presets/article-featured-image.d.ts.map +1 -0
- package/dist/presets/article-featured-image.js +17 -0
- package/dist/presets/article-featured-image.js.map +1 -0
- package/dist/server/constants.d.ts +6 -0
- package/dist/server/constants.d.ts.map +1 -0
- package/{src/server/constants.ts → dist/server/constants.js} +17 -20
- package/dist/server/constants.js.map +1 -0
- package/dist/server/handle-request.d.ts +5 -0
- package/dist/server/handle-request.d.ts.map +1 -0
- package/dist/server/handle-request.js +122 -0
- package/dist/server/handle-request.js.map +1 -0
- package/dist/server/types.d.ts +28 -0
- package/dist/server/types.d.ts.map +1 -0
- package/dist/server/types.js +2 -0
- package/dist/server/types.js.map +1 -0
- package/dist/server/utils.d.ts +50 -0
- package/dist/server/utils.d.ts.map +1 -0
- package/dist/server/utils.js +274 -0
- package/dist/server/utils.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +3 -0
- package/dist/server.js.map +1 -0
- package/dist/studio/components/asset-source.d.ts +4 -0
- package/dist/studio/components/asset-source.d.ts.map +1 -0
- package/dist/studio/components/asset-source.js +163 -0
- package/dist/studio/components/asset-source.js.map +1 -0
- package/dist/studio/components/generate-button-input.d.ts +8 -0
- package/dist/studio/components/generate-button-input.d.ts.map +1 -0
- package/dist/studio/components/generate-button-input.js +186 -0
- package/dist/studio/components/generate-button-input.js.map +1 -0
- package/dist/studio/components/input-router.d.ts +7 -0
- package/dist/studio/components/input-router.d.ts.map +1 -0
- package/dist/studio/components/input-router.js +21 -0
- package/dist/studio/components/input-router.js.map +1 -0
- package/dist/studio/files.d.ts +9 -0
- package/dist/studio/files.d.ts.map +1 -0
- package/dist/studio/files.js +86 -0
- package/dist/studio/files.js.map +1 -0
- package/dist/studio/plugin.d.ts +3 -0
- package/dist/studio/plugin.d.ts.map +1 -0
- package/dist/studio/plugin.js +47 -0
- package/dist/studio/plugin.js.map +1 -0
- package/dist/studio/settings/schema.d.ts +8 -0
- package/dist/studio/settings/schema.d.ts.map +1 -0
- package/dist/studio/settings/schema.js +110 -0
- package/dist/studio/settings/schema.js.map +1 -0
- package/dist/studio/settings/tool.d.ts +3 -0
- package/dist/studio/settings/tool.d.ts.map +1 -0
- package/dist/studio/settings/tool.js +292 -0
- package/dist/studio/settings/tool.js.map +1 -0
- package/dist/studio/settings-data.d.ts +24 -0
- package/dist/studio/settings-data.d.ts.map +1 -0
- package/dist/studio/settings-data.js +99 -0
- package/dist/studio/settings-data.js.map +1 -0
- package/dist/studio/shared-secret.d.ts +9 -0
- package/dist/studio/shared-secret.d.ts.map +1 -0
- package/dist/studio/shared-secret.js +37 -0
- package/dist/studio/shared-secret.js.map +1 -0
- package/dist/utils/config.d.ts +3 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +41 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/context-fields.d.ts +16 -0
- package/dist/utils/context-fields.d.ts.map +1 -0
- package/dist/utils/context-fields.js +104 -0
- package/dist/utils/context-fields.js.map +1 -0
- package/dist/utils/document-paths.d.ts +10 -0
- package/dist/utils/document-paths.d.ts.map +1 -0
- package/dist/utils/document-paths.js +33 -0
- package/dist/utils/document-paths.js.map +1 -0
- package/dist/utils/models.d.ts +22 -0
- package/dist/utils/models.d.ts.map +1 -0
- package/dist/utils/models.js +76 -0
- package/dist/utils/models.js.map +1 -0
- package/dist/utils/prompts.d.ts +2 -0
- package/dist/utils/prompts.d.ts.map +1 -0
- package/dist/utils/prompts.js +7 -0
- package/dist/utils/prompts.js.map +1 -0
- package/dist/utils/shared.d.ts +82 -0
- package/dist/utils/shared.d.ts.map +1 -0
- package/dist/utils/shared.js +13 -0
- package/dist/utils/shared.js.map +1 -0
- package/package.json +53 -47
- package/src/index.ts +0 -23
- package/src/presets/article-featured-image.ts +0 -23
- package/src/server/handle-request.ts +0 -207
- package/src/server/types.ts +0 -30
- package/src/server/utils.ts +0 -395
- package/src/server.ts +0 -14
- package/src/studio/components/asset-source.tsx +0 -297
- package/src/studio/components/generate-button-input.tsx +0 -380
- package/src/studio/components/input-router.tsx +0 -41
- package/src/studio/files.ts +0 -114
- package/src/studio/plugin.tsx +0 -54
- package/src/studio/settings/schema.ts +0 -122
- package/src/studio/settings/tool.tsx +0 -587
- package/src/studio/settings-data.ts +0 -172
- package/src/utils/config.ts +0 -55
- package/src/utils/context-fields.ts +0 -172
- package/src/utils/document-paths.ts +0 -51
- package/src/utils/models.ts +0 -126
- package/src/utils/prompts.ts +0 -6
- package/src/utils/shared.ts +0 -88
package/README.md
CHANGED
|
@@ -1,257 +1,281 @@
|
|
|
1
|
-
# `sanity-ai-image-plugin`
|
|
2
|
-
|
|
3
|
-
Portable Sanity Studio plugin and server helper for generating images with
|
|
4
|
-
Gemini, OpenAI, and other package-supported image models and dropping the
|
|
5
|
-
result straight into Sanity image fields.
|
|
6
|
-
|
|
7
|
-
This package is intentionally self-contained so it can be copied into a new repo
|
|
8
|
-
or published later without dragging along an app-specific schema layout.
|
|
9
|
-
|
|
10
|
-
This repo is source-first while it is being developed here:
|
|
11
|
-
|
|
12
|
-
- package exports point at `src/*`
|
|
13
|
-
|
|
14
|
-
## What It Owns
|
|
15
|
-
|
|
16
|
-
- a Studio plugin via `aiImagePlugin(...)`
|
|
17
|
-
- a plugin-owned settings document and settings tool
|
|
18
|
-
- a generic image asset source
|
|
19
|
-
- optional generate-button targets for specific image fields
|
|
20
|
-
- a server helper export for the app-owned API route
|
|
21
|
-
|
|
22
|
-
## Install Shape
|
|
23
|
-
|
|
24
|
-
The package exposes two entrypoints:
|
|
25
|
-
|
|
26
|
-
- `sanity-ai-image-plugin`
|
|
27
|
-
- `sanity-ai-image-plugin/server`
|
|
28
|
-
|
|
29
|
-
## Consumer Setup
|
|
30
|
-
|
|
31
|
-
### 1. Add the Studio plugin
|
|
32
|
-
|
|
33
|
-
```ts
|
|
34
|
-
import { defineConfig } from "sanity";
|
|
35
|
-
import {
|
|
36
|
-
SUPPORTED_AI_IMAGE_MODELS,
|
|
37
|
-
createArticleFeaturedImageTarget,
|
|
38
|
-
aiImagePlugin,
|
|
39
|
-
} from "sanity-ai-image-plugin";
|
|
40
|
-
|
|
41
|
-
const allowedModels = [
|
|
42
|
-
SUPPORTED_AI_IMAGE_MODELS[0],
|
|
43
|
-
SUPPORTED_AI_IMAGE_MODELS[2],
|
|
44
|
-
] as const;
|
|
45
|
-
|
|
46
|
-
export default defineConfig({
|
|
47
|
-
// ...your existing config
|
|
48
|
-
plugins: [
|
|
49
|
-
aiImagePlugin({
|
|
50
|
-
apiVersion: "2025-02-19",
|
|
51
|
-
allowedModels: [...allowedModels],
|
|
52
|
-
assetSource: true,
|
|
53
|
-
targets: [
|
|
54
|
-
createArticleFeaturedImageTarget(),
|
|
55
|
-
{
|
|
56
|
-
id: "home-page-featured-image",
|
|
57
|
-
type: "generateButton",
|
|
58
|
-
title: "Home Page Featured Image",
|
|
59
|
-
documentType: "homePage",
|
|
60
|
-
fieldPath: "featuredImage",
|
|
61
|
-
suggestedContextFieldPaths: ["title", "description"],
|
|
62
|
-
},
|
|
63
|
-
],
|
|
64
|
-
}),
|
|
65
|
-
],
|
|
66
|
-
});
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
### 2. Add the thin app-owned route.
|
|
70
|
-
|
|
71
|
-
```ts
|
|
72
|
-
import {
|
|
73
|
-
handleAiImageRequest,
|
|
74
|
-
SUPPORTED_AI_IMAGE_MODELS,
|
|
75
|
-
} from "sanity-ai-image-plugin/server";
|
|
76
|
-
|
|
77
|
-
const allowedModels = [
|
|
78
|
-
SUPPORTED_AI_IMAGE_MODELS[0],
|
|
79
|
-
SUPPORTED_AI_IMAGE_MODELS[2],
|
|
80
|
-
] as const;
|
|
81
|
-
|
|
82
|
-
export async function POST(request: Request) {
|
|
83
|
-
return handleAiImageRequest(request, {
|
|
84
|
-
allowedModels: [...allowedModels],
|
|
85
|
-
apiKey: process.env.GEMINI_API_KEY,
|
|
86
|
-
openAiApiKey: process.env.OPENAI_API_KEY,
|
|
87
|
-
|
|
88
|
-
//
|
|
89
|
-
//
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
- `
|
|
100
|
-
- `
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
- `
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
- `
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
- `
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
-
|
|
202
|
-
|
|
203
|
-
- `
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
- `
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
`
|
|
223
|
-
`
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
1
|
+
# `sanity-ai-image-plugin`
|
|
2
|
+
|
|
3
|
+
Portable Sanity Studio plugin and server helper for generating images with
|
|
4
|
+
Gemini, OpenAI, and other package-supported image models and dropping the
|
|
5
|
+
result straight into Sanity image fields.
|
|
6
|
+
|
|
7
|
+
This package is intentionally self-contained so it can be copied into a new repo
|
|
8
|
+
or published later without dragging along an app-specific schema layout.
|
|
9
|
+
|
|
10
|
+
This repo is source-first while it is being developed here:
|
|
11
|
+
|
|
12
|
+
- package exports point at `src/*`
|
|
13
|
+
|
|
14
|
+
## What It Owns
|
|
15
|
+
|
|
16
|
+
- a Studio plugin via `aiImagePlugin(...)`
|
|
17
|
+
- a plugin-owned settings document and settings tool
|
|
18
|
+
- a generic image asset source
|
|
19
|
+
- optional generate-button targets for specific image fields
|
|
20
|
+
- a server helper export for the app-owned API route
|
|
21
|
+
|
|
22
|
+
## Install Shape
|
|
23
|
+
|
|
24
|
+
The package exposes two entrypoints:
|
|
25
|
+
|
|
26
|
+
- `sanity-ai-image-plugin`
|
|
27
|
+
- `sanity-ai-image-plugin/server`
|
|
28
|
+
|
|
29
|
+
## Consumer Setup
|
|
30
|
+
|
|
31
|
+
### 1. Add the Studio plugin
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { defineConfig } from "sanity";
|
|
35
|
+
import {
|
|
36
|
+
SUPPORTED_AI_IMAGE_MODELS,
|
|
37
|
+
createArticleFeaturedImageTarget,
|
|
38
|
+
aiImagePlugin,
|
|
39
|
+
} from "sanity-ai-image-plugin";
|
|
40
|
+
|
|
41
|
+
const allowedModels = [
|
|
42
|
+
SUPPORTED_AI_IMAGE_MODELS[0],
|
|
43
|
+
SUPPORTED_AI_IMAGE_MODELS[2],
|
|
44
|
+
] as const;
|
|
45
|
+
|
|
46
|
+
export default defineConfig({
|
|
47
|
+
// ...your existing config
|
|
48
|
+
plugins: [
|
|
49
|
+
aiImagePlugin({
|
|
50
|
+
apiVersion: "2025-02-19",
|
|
51
|
+
allowedModels: [...allowedModels],
|
|
52
|
+
assetSource: true,
|
|
53
|
+
targets: [
|
|
54
|
+
createArticleFeaturedImageTarget(),
|
|
55
|
+
{
|
|
56
|
+
id: "home-page-featured-image",
|
|
57
|
+
type: "generateButton",
|
|
58
|
+
title: "Home Page Featured Image",
|
|
59
|
+
documentType: "homePage",
|
|
60
|
+
fieldPath: "featuredImage",
|
|
61
|
+
suggestedContextFieldPaths: ["title", "description"],
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
}),
|
|
65
|
+
],
|
|
66
|
+
});
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 2. Add the thin app-owned route.
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
import {
|
|
73
|
+
handleAiImageRequest,
|
|
74
|
+
SUPPORTED_AI_IMAGE_MODELS,
|
|
75
|
+
} from "sanity-ai-image-plugin/server";
|
|
76
|
+
|
|
77
|
+
const allowedModels = [
|
|
78
|
+
SUPPORTED_AI_IMAGE_MODELS[0],
|
|
79
|
+
SUPPORTED_AI_IMAGE_MODELS[2],
|
|
80
|
+
] as const;
|
|
81
|
+
|
|
82
|
+
export async function POST(request: Request) {
|
|
83
|
+
return handleAiImageRequest(request, {
|
|
84
|
+
allowedModels: [...allowedModels],
|
|
85
|
+
apiKey: process.env.GEMINI_API_KEY,
|
|
86
|
+
openAiApiKey: process.env.OPENAI_API_KEY,
|
|
87
|
+
sharedSecret: process.env.AI_IMAGE_PLUGIN_SHARED_SECRET!,
|
|
88
|
+
// Shared-secret auth and same-origin protection are enabled by default.
|
|
89
|
+
// Optional overrides:
|
|
90
|
+
// model: process.env.AI_IMAGE_MODEL as (typeof allowedModels)[number],
|
|
91
|
+
// maxReferenceFileBytes: 8 * 1024 * 1024,
|
|
92
|
+
// maxTotalReferenceBytes: 5 * 8 * 1024 * 1024,
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 3. Set env vars
|
|
98
|
+
|
|
99
|
+
- `GEMINI_API_KEY` is required when you allow Google models
|
|
100
|
+
- `OPENAI_API_KEY` is required when you allow OpenAI models
|
|
101
|
+
- `AI_IMAGE_PLUGIN_SHARED_SECRET` is required for the app-owned route
|
|
102
|
+
- `AI_IMAGE_MODEL` is optional and can still override the server default
|
|
103
|
+
|
|
104
|
+
### 4. Configure the shared secret in Studio
|
|
105
|
+
|
|
106
|
+
Open the AI Image Plugin settings tool and configure the same shared secret
|
|
107
|
+
value there.
|
|
108
|
+
|
|
109
|
+
The plugin stores that Studio-side value with `@sanity/studio-secrets`, so it
|
|
110
|
+
is fetched at runtime for logged-in Studio users instead of being bundled into
|
|
111
|
+
the Studio source code.
|
|
112
|
+
|
|
113
|
+
## Supported Models
|
|
114
|
+
|
|
115
|
+
The package has an internal supported-model registry. In this first pass it
|
|
116
|
+
contains exactly:
|
|
117
|
+
|
|
118
|
+
- `gemini-2.5-flash-image`
|
|
119
|
+
- `gemini-3.1-flash-image-preview`
|
|
120
|
+
- `gpt-image-1`
|
|
121
|
+
|
|
122
|
+
Each installation can opt into any non-empty subset of those models with the
|
|
123
|
+
ordered `allowedModels` config. The first allowed model becomes the fallback
|
|
124
|
+
default for both the Studio UI and the server helper unless the settings
|
|
125
|
+
document or route overrides it.
|
|
126
|
+
|
|
127
|
+
## Settings Model
|
|
128
|
+
|
|
129
|
+
The plugin owns one settings document:
|
|
130
|
+
|
|
131
|
+
- `_id`: `aiImagePlugin.settings`
|
|
132
|
+
- `_type`: `aiImagePluginSettings`
|
|
133
|
+
|
|
134
|
+
It stores:
|
|
135
|
+
|
|
136
|
+
- `globalModel`
|
|
137
|
+
- `globalPrompt`
|
|
138
|
+
- `globalReferenceImages`
|
|
139
|
+
- `targetConfigs[]`
|
|
140
|
+
|
|
141
|
+
Each target config can override:
|
|
142
|
+
|
|
143
|
+
- `targetId`
|
|
144
|
+
- `prompt`
|
|
145
|
+
- `referenceImages`
|
|
146
|
+
|
|
147
|
+
## Behavior
|
|
148
|
+
|
|
149
|
+
### Server helper
|
|
150
|
+
|
|
151
|
+
The server helper requires a valid shared secret and same-origin browser
|
|
152
|
+
requests by default.
|
|
153
|
+
|
|
154
|
+
That means requests are accepted only when:
|
|
155
|
+
|
|
156
|
+
1. the request includes the correct `x-ai-image-plugin-secret` header
|
|
157
|
+
2. the browser `Origin` matches the API route origin exactly
|
|
158
|
+
|
|
159
|
+
Same-origin matches look like this:
|
|
160
|
+
|
|
161
|
+
- `http://localhost:3000/studio` -> `http://localhost:3000/api/ai-image-plugin`
|
|
162
|
+
- `https://myapp.vercel.app/studio` -> `https://myapp.vercel.app/api/ai-image-plugin`
|
|
163
|
+
|
|
164
|
+
The helper does not inspect the `/studio` path directly because browser
|
|
165
|
+
`Origin` headers only include the scheme, host, and port. The shared secret is
|
|
166
|
+
managed from the plugin settings tool and stored separately from the plugin's
|
|
167
|
+
normal prompt/reference-image settings document.
|
|
168
|
+
|
|
169
|
+
By default it also enforces:
|
|
170
|
+
|
|
171
|
+
- `8 MiB` maximum per reference image
|
|
172
|
+
- a combined reference-image cap of `maxReferences * 8 MiB`
|
|
173
|
+
- the requested `model` must be both package-supported and present in the
|
|
174
|
+
route's configured `allowedModels`
|
|
175
|
+
|
|
176
|
+
If your framework supports route-level body limits, keep those enabled too.
|
|
177
|
+
|
|
178
|
+
### Asset source
|
|
179
|
+
|
|
180
|
+
The generic asset source composes:
|
|
181
|
+
|
|
182
|
+
1. global prompt
|
|
183
|
+
2. asset-source target prompt
|
|
184
|
+
3. editor prompt
|
|
185
|
+
|
|
186
|
+
Reference images are combined from:
|
|
187
|
+
|
|
188
|
+
1. global reference images
|
|
189
|
+
2. asset-source target reference images
|
|
190
|
+
3. local editor-uploaded reference images
|
|
191
|
+
|
|
192
|
+
The asset-source model picker starts on:
|
|
193
|
+
|
|
194
|
+
1. `settings.globalModel` when it is present and allowed
|
|
195
|
+
2. otherwise the first configured `allowedModels` entry
|
|
196
|
+
|
|
197
|
+
Editors can override that selection for the current request before generating.
|
|
198
|
+
|
|
199
|
+
### Generate button targets
|
|
200
|
+
|
|
201
|
+
Generate-button targets match against:
|
|
202
|
+
|
|
203
|
+
- `documentType`
|
|
204
|
+
- `fieldPath`
|
|
205
|
+
|
|
206
|
+
When matched, the plugin renders a `Generate` button above the normal Sanity
|
|
207
|
+
image input.
|
|
208
|
+
|
|
209
|
+
Targets can also declare:
|
|
210
|
+
|
|
211
|
+
- `suggestedContextFieldPaths`
|
|
212
|
+
|
|
213
|
+
When the dialog opens, the plugin inspects the current document schema and shows
|
|
214
|
+
eligible top-level document fields as toggle tags. In this first pass, eligible
|
|
215
|
+
field types are:
|
|
216
|
+
|
|
217
|
+
- `string`
|
|
218
|
+
- `text`
|
|
219
|
+
- `number`
|
|
220
|
+
- `boolean`
|
|
221
|
+
- `date`
|
|
222
|
+
- `datetime`
|
|
223
|
+
- `slug`
|
|
224
|
+
|
|
225
|
+
Suggested context field paths are only default-on tags. They are filtered to
|
|
226
|
+
fields that exist on the current document type, and editors can toggle them on
|
|
227
|
+
or off for each generation.
|
|
228
|
+
|
|
229
|
+
The generate dialog also includes a model picker. It starts from the same
|
|
230
|
+
global default resolution as the asset source, but editors can switch to a
|
|
231
|
+
different allowed model for that one generation request.
|
|
232
|
+
|
|
233
|
+
Prompt composition order is:
|
|
234
|
+
|
|
235
|
+
1. global prompt
|
|
236
|
+
2. target prompt
|
|
237
|
+
3. optional selected document context
|
|
238
|
+
4. optional editor prompt
|
|
239
|
+
|
|
240
|
+
Selected document context is built as generic lines such as:
|
|
241
|
+
|
|
242
|
+
- `The field called "title" has content "...".`
|
|
243
|
+
|
|
244
|
+
## Optional Preset
|
|
245
|
+
|
|
246
|
+
`createArticleFeaturedImageTarget(...)` is an optional preset for
|
|
247
|
+
`article.featuredImage`.
|
|
248
|
+
|
|
249
|
+
It feeds the model:
|
|
250
|
+
|
|
251
|
+
- shared global settings
|
|
252
|
+
- target-specific article settings
|
|
253
|
+
- editor-selectable document-derived title + excerpt context suggestions
|
|
254
|
+
- optional editor prompt
|
|
255
|
+
|
|
256
|
+
If you do not use that preset, the package still works as a generic asset source
|
|
257
|
+
and generic generate-button plugin.
|
|
258
|
+
|
|
259
|
+
## Desk Structure
|
|
260
|
+
|
|
261
|
+
The plugin does not require custom desk structure wiring. If a consuming app
|
|
262
|
+
uses a custom structure and wants to hide `aiImagePluginSettings` from the normal
|
|
263
|
+
document list, that is optional and app-owned.
|
|
264
|
+
|
|
265
|
+
## PNG Normalization
|
|
266
|
+
|
|
267
|
+
All reference images are converted to PNG before they are sent to the server
|
|
268
|
+
helper. That includes:
|
|
269
|
+
|
|
270
|
+
- locally uploaded reference files
|
|
271
|
+
- stored settings images downloaded from Sanity
|
|
272
|
+
- new images uploaded through the settings tool
|
|
273
|
+
|
|
274
|
+
## Development
|
|
275
|
+
|
|
276
|
+
```sh
|
|
277
|
+
bun run check
|
|
278
|
+
bun run build
|
|
279
|
+
bun test
|
|
280
|
+
```
|
|
281
|
+
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"article-featured-image.d.ts","sourceRoot":"","sources":["../../src/presets/article-featured-image.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAE3D,wBAAgB,gCAAgC,CAC9C,OAAO,GAAE,OAAO,CAAC,IAAI,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAM,GACxD,oBAAoB,CAkBtB"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export function createArticleFeaturedImageTarget(options = {}) {
|
|
2
|
+
return {
|
|
3
|
+
id: options.id || "article-featured-image",
|
|
4
|
+
type: "generateButton",
|
|
5
|
+
title: options.title || "Article Featured Image",
|
|
6
|
+
description: options.description ||
|
|
7
|
+
"Adds a Generate button that combines shared art direction, article context, and an optional editor prompt.",
|
|
8
|
+
dialogTitle: options.dialogTitle || "Generate Featured Image",
|
|
9
|
+
documentType: options.documentType || "article",
|
|
10
|
+
fieldPath: options.fieldPath || "featuredImage",
|
|
11
|
+
promptLabel: options.promptLabel || "Custom prompt",
|
|
12
|
+
promptPlaceholder: options.promptPlaceholder ||
|
|
13
|
+
"Optional custom instructions for this article image.",
|
|
14
|
+
suggestedContextFieldPaths: options.suggestedContextFieldPaths || ["title", "excerpt"],
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=article-featured-image.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"article-featured-image.js","sourceRoot":"","sources":["../../src/presets/article-featured-image.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,gCAAgC,CAC9C,UAAuD,EAAE;IAEzD,OAAO;QACL,EAAE,EAAE,OAAO,CAAC,EAAE,IAAI,wBAAwB;QAC1C,IAAI,EAAE,gBAAgB;QACtB,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,wBAAwB;QAChD,WAAW,EACT,OAAO,CAAC,WAAW;YACnB,4GAA4G;QAC9G,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,yBAAyB;QAC7D,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,SAAS;QAC/C,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,eAAe;QAC/C,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,eAAe;QACnD,iBAAiB,EACf,OAAO,CAAC,iBAAiB;YACzB,sDAAsD;QACxD,0BAA0B,EACxB,OAAO,CAAC,0BAA0B,IAAI,CAAC,OAAO,EAAE,SAAS,CAAC;KAC7D,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const DEFAULT_MODEL: "gemini-2.5-flash-image" | "gemini-3.1-flash-image-preview" | "gpt-image-1";
|
|
2
|
+
export declare const DEFAULT_PROVIDER_API_URL = "https://generativelanguage.googleapis.com/v1beta";
|
|
3
|
+
export declare const DEFAULT_MAX_REFERENCE_FILE_BYTES: number;
|
|
4
|
+
export declare const SUPPORTED_REFERENCE_IMAGE_TYPES: Set<string>;
|
|
5
|
+
export declare const OPENAI_SUPPORTED_REFERENCE_IMAGE_TYPES: Set<string>;
|
|
6
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/server/constants.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,aAAa,6EAAmC,CAAC;AAC9D,eAAO,MAAM,wBAAwB,qDACe,CAAC;AACrD,eAAO,MAAM,gCAAgC,QAAkB,CAAC;AAEhE,eAAO,MAAM,+BAA+B,aAM1C,CAAC;AAEH,eAAO,MAAM,sCAAsC,aAIjD,CAAC"}
|
|
@@ -1,20 +1,17 @@
|
|
|
1
|
-
import { DEFAULT_SUPPORTED_AI_IMAGE_MODEL } from "../utils/models";
|
|
2
|
-
|
|
3
|
-
export const
|
|
4
|
-
export const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"image/png",
|
|
19
|
-
"image/webp",
|
|
20
|
-
]);
|
|
1
|
+
import { DEFAULT_SUPPORTED_AI_IMAGE_MODEL } from "../utils/models";
|
|
2
|
+
export const DEFAULT_MODEL = DEFAULT_SUPPORTED_AI_IMAGE_MODEL;
|
|
3
|
+
export const DEFAULT_PROVIDER_API_URL = "https://generativelanguage.googleapis.com/v1beta";
|
|
4
|
+
export const DEFAULT_MAX_REFERENCE_FILE_BYTES = 8 * 1024 * 1024;
|
|
5
|
+
export const SUPPORTED_REFERENCE_IMAGE_TYPES = new Set([
|
|
6
|
+
"image/heic",
|
|
7
|
+
"image/heif",
|
|
8
|
+
"image/jpeg",
|
|
9
|
+
"image/png",
|
|
10
|
+
"image/webp",
|
|
11
|
+
]);
|
|
12
|
+
export const OPENAI_SUPPORTED_REFERENCE_IMAGE_TYPES = new Set([
|
|
13
|
+
"image/jpeg",
|
|
14
|
+
"image/png",
|
|
15
|
+
"image/webp",
|
|
16
|
+
]);
|
|
17
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/server/constants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gCAAgC,EAAE,MAAM,iBAAiB,CAAC;AAEnE,MAAM,CAAC,MAAM,aAAa,GAAG,gCAAgC,CAAC;AAC9D,MAAM,CAAC,MAAM,wBAAwB,GACnC,kDAAkD,CAAC;AACrD,MAAM,CAAC,MAAM,gCAAgC,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AAEhE,MAAM,CAAC,MAAM,+BAA+B,GAAG,IAAI,GAAG,CAAC;IACrD,YAAY;IACZ,YAAY;IACZ,YAAY;IACZ,WAAW;IACX,YAAY;CACb,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,sCAAsC,GAAG,IAAI,GAAG,CAAC;IAC5D,YAAY;IACZ,WAAW;IACX,YAAY;CACb,CAAC,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { RequestOptions } from "./types";
|
|
2
|
+
export { DEFAULT_MAX_REFERENCE_FILE_BYTES, DEFAULT_MODEL, DEFAULT_PROVIDER_API_URL, SUPPORTED_REFERENCE_IMAGE_TYPES, } from "./constants";
|
|
3
|
+
export type { RequestOptions, SuccessResponse } from "./types";
|
|
4
|
+
export declare function handleAiImageRequest(request: Request, options: RequestOptions): Promise<Response>;
|
|
5
|
+
//# sourceMappingURL=handle-request.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handle-request.d.ts","sourceRoot":"","sources":["../../src/server/handle-request.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,cAAc,EAAmB,MAAM,SAAS,CAAC;AAC/D,OAAO,EACL,gCAAgC,EAChC,aAAa,EACb,wBAAwB,EACxB,+BAA+B,GAChC,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/D,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,QAAQ,CAAC,CA4LnB"}
|