@basementstudio/sanity-ai-image-plugin 0.0.1 → 0.1.1

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.
Files changed (118) hide show
  1. package/README.md +300 -257
  2. package/dist/index.d.ts +3 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +3 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/presets/article-featured-image.d.ts +3 -0
  7. package/dist/presets/article-featured-image.d.ts.map +1 -0
  8. package/dist/presets/article-featured-image.js +17 -0
  9. package/dist/presets/article-featured-image.js.map +1 -0
  10. package/dist/server/constants.d.ts +6 -0
  11. package/dist/server/constants.d.ts.map +1 -0
  12. package/{src/server/constants.ts → dist/server/constants.js} +17 -20
  13. package/dist/server/constants.js.map +1 -0
  14. package/dist/server/handle-request.d.ts +5 -0
  15. package/dist/server/handle-request.d.ts.map +1 -0
  16. package/dist/server/handle-request.js +122 -0
  17. package/dist/server/handle-request.js.map +1 -0
  18. package/dist/server/types.d.ts +28 -0
  19. package/dist/server/types.d.ts.map +1 -0
  20. package/dist/server/types.js +2 -0
  21. package/dist/server/types.js.map +1 -0
  22. package/dist/server/utils.d.ts +50 -0
  23. package/dist/server/utils.d.ts.map +1 -0
  24. package/dist/server/utils.js +274 -0
  25. package/dist/server/utils.js.map +1 -0
  26. package/dist/server.d.ts +3 -0
  27. package/dist/server.d.ts.map +1 -0
  28. package/dist/server.js +3 -0
  29. package/dist/server.js.map +1 -0
  30. package/dist/studio/components/asset-source.d.ts +4 -0
  31. package/dist/studio/components/asset-source.d.ts.map +1 -0
  32. package/dist/studio/components/asset-source.js +169 -0
  33. package/dist/studio/components/asset-source.js.map +1 -0
  34. package/dist/studio/components/frontend-rate-limit.d.ts +29 -0
  35. package/dist/studio/components/frontend-rate-limit.d.ts.map +1 -0
  36. package/dist/studio/components/frontend-rate-limit.js +197 -0
  37. package/dist/studio/components/frontend-rate-limit.js.map +1 -0
  38. package/dist/studio/components/generate-button-input.d.ts +8 -0
  39. package/dist/studio/components/generate-button-input.d.ts.map +1 -0
  40. package/dist/studio/components/generate-button-input.js +194 -0
  41. package/dist/studio/components/generate-button-input.js.map +1 -0
  42. package/dist/studio/components/input-router.d.ts +7 -0
  43. package/dist/studio/components/input-router.d.ts.map +1 -0
  44. package/dist/studio/components/input-router.js +21 -0
  45. package/dist/studio/components/input-router.js.map +1 -0
  46. package/dist/studio/files.d.ts +9 -0
  47. package/dist/studio/files.d.ts.map +1 -0
  48. package/dist/studio/files.js +86 -0
  49. package/dist/studio/files.js.map +1 -0
  50. package/dist/studio/frontend-rate-limit.d.ts +29 -0
  51. package/dist/studio/frontend-rate-limit.d.ts.map +1 -0
  52. package/dist/studio/frontend-rate-limit.js +197 -0
  53. package/dist/studio/frontend-rate-limit.js.map +1 -0
  54. package/dist/studio/plugin.d.ts +3 -0
  55. package/dist/studio/plugin.d.ts.map +1 -0
  56. package/dist/studio/plugin.js +47 -0
  57. package/dist/studio/plugin.js.map +1 -0
  58. package/dist/studio/settings/schema.d.ts +8 -0
  59. package/dist/studio/settings/schema.d.ts.map +1 -0
  60. package/dist/studio/settings/schema.js +110 -0
  61. package/dist/studio/settings/schema.js.map +1 -0
  62. package/dist/studio/settings/tool.d.ts +3 -0
  63. package/dist/studio/settings/tool.d.ts.map +1 -0
  64. package/dist/studio/settings/tool.js +292 -0
  65. package/dist/studio/settings/tool.js.map +1 -0
  66. package/dist/studio/settings-data.d.ts +24 -0
  67. package/dist/studio/settings-data.d.ts.map +1 -0
  68. package/dist/studio/settings-data.js +99 -0
  69. package/dist/studio/settings-data.js.map +1 -0
  70. package/dist/studio/shared-secret.d.ts +9 -0
  71. package/dist/studio/shared-secret.d.ts.map +1 -0
  72. package/dist/studio/shared-secret.js +37 -0
  73. package/dist/studio/shared-secret.js.map +1 -0
  74. package/dist/utils/config.d.ts +3 -0
  75. package/dist/utils/config.d.ts.map +1 -0
  76. package/dist/utils/config.js +59 -0
  77. package/dist/utils/config.js.map +1 -0
  78. package/dist/utils/context-fields.d.ts +16 -0
  79. package/dist/utils/context-fields.d.ts.map +1 -0
  80. package/dist/utils/context-fields.js +104 -0
  81. package/dist/utils/context-fields.js.map +1 -0
  82. package/dist/utils/document-paths.d.ts +10 -0
  83. package/dist/utils/document-paths.d.ts.map +1 -0
  84. package/dist/utils/document-paths.js +33 -0
  85. package/dist/utils/document-paths.js.map +1 -0
  86. package/dist/utils/models.d.ts +22 -0
  87. package/dist/utils/models.d.ts.map +1 -0
  88. package/dist/utils/models.js +76 -0
  89. package/dist/utils/models.js.map +1 -0
  90. package/dist/utils/prompts.d.ts +2 -0
  91. package/dist/utils/prompts.d.ts.map +1 -0
  92. package/dist/utils/prompts.js +7 -0
  93. package/dist/utils/prompts.js.map +1 -0
  94. package/dist/utils/shared.d.ts +96 -0
  95. package/dist/utils/shared.d.ts.map +1 -0
  96. package/dist/utils/shared.js +17 -0
  97. package/dist/utils/shared.js.map +1 -0
  98. package/package.json +73 -67
  99. package/src/index.ts +0 -23
  100. package/src/presets/article-featured-image.ts +0 -23
  101. package/src/server/handle-request.ts +0 -207
  102. package/src/server/types.ts +0 -30
  103. package/src/server/utils.ts +0 -395
  104. package/src/server.ts +0 -14
  105. package/src/studio/components/asset-source.tsx +0 -297
  106. package/src/studio/components/generate-button-input.tsx +0 -380
  107. package/src/studio/components/input-router.tsx +0 -41
  108. package/src/studio/files.ts +0 -114
  109. package/src/studio/plugin.tsx +0 -54
  110. package/src/studio/settings/schema.ts +0 -122
  111. package/src/studio/settings/tool.tsx +0 -587
  112. package/src/studio/settings-data.ts +0 -172
  113. package/src/utils/config.ts +0 -55
  114. package/src/utils/context-fields.ts +0 -172
  115. package/src/utils/document-paths.ts +0 -51
  116. package/src/utils/models.ts +0 -126
  117. package/src/utils/prompts.ts +0 -6
  118. package/src/utils/shared.ts +0 -88
@@ -1,41 +0,0 @@
1
- "use client"
2
-
3
- import { useFormValue, type InputProps, type ObjectInputProps } from "sanity"
4
- import type { ImageValue } from "sanity"
5
- import { GenerateButtonInput } from "./generate-button-input"
6
- import { doesTargetMatchField } from "../../utils/document-paths"
7
- import type { ResolvedOptions } from "../../utils/shared"
8
-
9
- export function InputRouter({
10
- options,
11
- props,
12
- }: {
13
- options: ResolvedOptions
14
- props: InputProps
15
- }) {
16
- const documentType = useFormValue(["_type"]) as string | undefined
17
-
18
- if (props.schemaType?.name !== "image") {
19
- return props.renderDefault(props)
20
- }
21
-
22
- const target = options.targets.find((candidate) =>
23
- doesTargetMatchField({
24
- documentType,
25
- path: props.path,
26
- target: candidate,
27
- })
28
- )
29
-
30
- if (!target) {
31
- return props.renderDefault(props)
32
- }
33
-
34
- return (
35
- <GenerateButtonInput
36
- options={options}
37
- props={props as ObjectInputProps<ImageValue>}
38
- target={target}
39
- />
40
- )
41
- }
@@ -1,114 +0,0 @@
1
- "use client"
2
-
3
- function getPngFilename(filename: string): string {
4
- const basename = filename.replace(/\.[^/.]+$/, "")
5
- return `${basename || "reference"}.png`
6
- }
7
-
8
- export async function convertImageFileToPng(file: File): Promise<File> {
9
- if (file.type === "image/png") {
10
- return file
11
- }
12
-
13
- const imageUrl = URL.createObjectURL(file)
14
-
15
- try {
16
- const image = await new Promise<HTMLImageElement>((resolve, reject) => {
17
- const nextImage = new Image()
18
- nextImage.onload = () => resolve(nextImage)
19
- nextImage.onerror = () =>
20
- reject(new Error(`Unable to decode ${file.name} as an image.`))
21
- nextImage.src = imageUrl
22
- })
23
- const canvas = document.createElement("canvas")
24
- const width = image.naturalWidth || image.width
25
- const height = image.naturalHeight || image.height
26
-
27
- canvas.width = width
28
- canvas.height = height
29
-
30
- const context = canvas.getContext("2d")
31
-
32
- if (!context) {
33
- throw new Error("Unable to convert the reference image to PNG.")
34
- }
35
-
36
- context.drawImage(image, 0, 0, width, height)
37
-
38
- const blob = await new Promise<Blob>((resolve, reject) => {
39
- canvas.toBlob(
40
- (nextBlob) => {
41
- if (nextBlob) {
42
- resolve(nextBlob)
43
- return
44
- }
45
-
46
- reject(new Error("Unable to convert the reference image to PNG."))
47
- },
48
- "image/png",
49
- 1
50
- )
51
- })
52
-
53
- return new File([blob], getPngFilename(file.name), { type: "image/png" })
54
- } finally {
55
- URL.revokeObjectURL(imageUrl)
56
- }
57
- }
58
-
59
- export function base64ToBlob(base64: string, mimeType: string): Blob {
60
- const binaryString = atob(base64)
61
- const bytes = new Uint8Array(binaryString.length)
62
-
63
- for (const [index, character] of Array.from(binaryString).entries()) {
64
- bytes[index] = character.charCodeAt(0)
65
- }
66
-
67
- return new Blob([bytes], { type: mimeType })
68
- }
69
-
70
- export function fileExtensionFromMimeType(mimeType: string): string {
71
- switch (mimeType) {
72
- case "image/jpeg":
73
- return "jpg"
74
- case "image/webp":
75
- return "webp"
76
- default:
77
- return "png"
78
- }
79
- }
80
-
81
- export function formatFileSize(bytes: number): string {
82
- if (bytes < 1024) {
83
- return `${bytes} B`
84
- }
85
-
86
- const kilobytes = bytes / 1024
87
-
88
- if (kilobytes < 1024) {
89
- return `${kilobytes.toFixed(1)} KB`
90
- }
91
-
92
- return `${(kilobytes / 1024).toFixed(1)} MB`
93
- }
94
-
95
- export function createGeneratedFile(
96
- result: { data: string; mimeType: string },
97
- basename: string,
98
- prefix = "ai-image-plugin"
99
- ): File {
100
- const extension = fileExtensionFromMimeType(result.mimeType)
101
- const timestamp = new Date().toISOString().replaceAll(/[:.]/g, "-")
102
- const safeBasename = basename
103
- .trim()
104
- .toLowerCase()
105
- .replaceAll(/[^a-z0-9]+/g, "-")
106
- .replaceAll(/^-+|-+$/g, "")
107
- .slice(0, 40)
108
- const suffix = safeBasename || "generated"
109
- const blob = base64ToBlob(result.data, result.mimeType)
110
-
111
- return new File([blob], `${prefix}-${suffix}-${timestamp}.${extension}`, {
112
- type: result.mimeType,
113
- })
114
- }
@@ -1,54 +0,0 @@
1
- import { definePlugin, type AssetSource, type InputProps } from "sanity"
2
- import { createAssetSource } from "./components/asset-source"
3
- import { InputRouter } from "./components/input-router"
4
- import { createSettingsSchema } from "./settings/schema"
5
- import { createSettingsTool } from "./settings/tool"
6
- import { normalizeOptions } from "../utils/config"
7
- import type { PluginOptions } from "../utils/shared"
8
-
9
- export const aiImagePlugin = definePlugin<PluginOptions>((rawOptions) => {
10
- const options = normalizeOptions(rawOptions)
11
- const assetSource = options.assetSourceTarget
12
- ? createAssetSource(options)
13
- : null
14
- const settingsTool = createSettingsTool(options)
15
- const settingsSchema = createSettingsSchema(options)
16
-
17
- return {
18
- name: "ai-image-plugin",
19
- ...(options.registerSettingsSchemaType
20
- ? {
21
- schema: {
22
- types: [settingsSchema],
23
- },
24
- }
25
- : {}),
26
- tools: [
27
- {
28
- name: options.settingsToolName,
29
- title: options.settingsToolTitle,
30
- component: settingsTool,
31
- },
32
- ],
33
- form: {
34
- ...(assetSource
35
- ? {
36
- image: {
37
- assetSources: (previousAssetSources: AssetSource[]) =>
38
- previousAssetSources.some(
39
- (previousAssetSource) =>
40
- previousAssetSource.name === assetSource.name
41
- )
42
- ? previousAssetSources
43
- : [...previousAssetSources, assetSource],
44
- },
45
- }
46
- : {}),
47
- components: {
48
- input: (props: InputProps) => (
49
- <InputRouter options={options} props={props} />
50
- ),
51
- },
52
- },
53
- }
54
- })
@@ -1,122 +0,0 @@
1
- import { defineArrayMember, defineField, defineType } from "sanity"
2
- import {
3
- getDefaultAllowedAiImageModel,
4
- getSupportedAiImageModelOptions,
5
- } from "../../utils/models"
6
- import {
7
- SETTINGS_DOCUMENT_ID,
8
- SETTINGS_SCHEMA_TYPE,
9
- type ResolvedOptions,
10
- } from "../../utils/shared"
11
-
12
- export function createSettingsSchema(options: ResolvedOptions) {
13
- return defineType({
14
- name: SETTINGS_SCHEMA_TYPE,
15
- title: "AI Image Plugin Settings",
16
- type: "document",
17
- fields: [
18
- defineField({
19
- name: "title",
20
- title: "Title",
21
- type: "string",
22
- initialValue: "AI Image Plugin Settings",
23
- readOnly: true,
24
- }),
25
- defineField({
26
- name: "globalPrompt",
27
- title: "Global Prompt",
28
- type: "text",
29
- rows: 8,
30
- description:
31
- "Prompt instructions automatically included in every AI Image Plugin generation.",
32
- }),
33
- defineField({
34
- name: "globalModel",
35
- title: "Default Model",
36
- type: "string",
37
- initialValue: getDefaultAllowedAiImageModel(options.allowedModels),
38
- description:
39
- "The AI image model used by the plugin unless a different allowed default is configured later.",
40
- options: {
41
- list: getSupportedAiImageModelOptions(options.allowedModels),
42
- },
43
- }),
44
- defineField({
45
- name: "globalReferenceImages",
46
- title: "Global Reference Images",
47
- type: "array",
48
- description:
49
- "Reference images automatically included in every AI Image Plugin generation.",
50
- of: [
51
- defineArrayMember({
52
- type: "image",
53
- options: { hotspot: true },
54
- }),
55
- ],
56
- }),
57
- defineField({
58
- name: "targetConfigs",
59
- title: "Target Overrides",
60
- type: "array",
61
- description:
62
- "Per-target prompt and reference images for specific AI Image Plugin entry points.",
63
- of: [
64
- defineArrayMember({
65
- type: "object",
66
- fields: [
67
- defineField({
68
- name: "targetId",
69
- title: "Target ID",
70
- type: "string",
71
- validation: (Rule) => Rule.required(),
72
- options: {
73
- list: [
74
- ...(options.assetSourceTarget
75
- ? [
76
- {
77
- title:
78
- options.assetSourceTarget.title ||
79
- options.assetSourceTarget.id,
80
- value: options.assetSourceTarget.id,
81
- },
82
- ]
83
- : []),
84
- ...options.targets.map((target) => ({
85
- title: target.title || target.id,
86
- value: target.id,
87
- })),
88
- ],
89
- },
90
- }),
91
- defineField({
92
- name: "prompt",
93
- title: "Prompt",
94
- type: "text",
95
- rows: 6,
96
- }),
97
- defineField({
98
- name: "referenceImages",
99
- title: "Reference Images",
100
- type: "array",
101
- of: [
102
- defineArrayMember({
103
- type: "image",
104
- options: { hotspot: true },
105
- }),
106
- ],
107
- }),
108
- ],
109
- }),
110
- ],
111
- }),
112
- ],
113
- preview: {
114
- prepare() {
115
- return {
116
- title: "AI Image Plugin Settings",
117
- subtitle: SETTINGS_DOCUMENT_ID,
118
- }
119
- },
120
- },
121
- })
122
- }