@liiift-studio/deploy-vercel-from-sanity 1.1.1 → 1.2.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.
@@ -1,146 +0,0 @@
1
- // Dialog form for creating and editing vercel_deploy documents
2
- import { useState, useCallback } from 'react'
3
- import { useClient } from 'sanity'
4
- import {
5
- Dialog, Box, Flex, Text, TextInput, Button, Switch, Label, Card,
6
- } from '@sanity/ui'
7
- import { Stack } from '../ui'
8
- import { CheckmarkCircleIcon } from '../icons'
9
- import type { DeployTarget } from '../types'
10
-
11
- const VERCEL_HOOK_RE = /^https:\/\/api\.vercel\.com\/v1\/integrations\/deploy\//
12
-
13
- interface DeployTargetFormProps {
14
- /** When provided, form is in edit mode; otherwise create mode */
15
- initial?: DeployTarget
16
- onSaved: () => void
17
- onClose: () => void
18
- }
19
-
20
- export function DeployTargetForm({ initial, onSaved, onClose }: DeployTargetFormProps) {
21
- const client = useClient({ apiVersion: '2025-01-01' })
22
- const isEdit = Boolean(initial)
23
-
24
- const [name, setName] = useState(initial?.name ?? '')
25
- const [url, setUrl] = useState(initial?.url ?? '')
26
- const [teamId, setTeamId] = useState(initial?.teamId ?? '')
27
- const [disableDelete, setDisableDelete] = useState(initial?.disableDeleteAction ?? false)
28
- const [saving, setSaving] = useState(false)
29
- const [error, setError] = useState<string | null>(null)
30
-
31
- const urlValid = !url || VERCEL_HOOK_RE.test(url.trim())
32
- const canSave = name.trim() && url.trim() && urlValid
33
-
34
- const save = useCallback(async () => {
35
- if (!canSave) return
36
- setSaving(true)
37
- setError(null)
38
- const fields: { name: string; url: string; teamId: string | null; disableDeleteAction: boolean } = {
39
- name: name.trim(),
40
- url: url.trim(),
41
- teamId: teamId.trim() || null,
42
- disableDeleteAction: disableDelete,
43
- }
44
- try {
45
- if (isEdit && initial) {
46
- await client.patch(initial._id).set(fields).commit()
47
- } else {
48
- await client.create({ _type: 'vercel_deploy', ...fields })
49
- }
50
- onSaved()
51
- } catch (err) {
52
- setError(err instanceof Error ? err.message : 'Save failed')
53
- } finally {
54
- setSaving(false)
55
- }
56
- }, [canSave, isEdit, initial, client, name, url, teamId, disableDelete, onSaved])
57
-
58
- return (
59
- <Dialog
60
- header={isEdit ? `Edit "${initial?.name}"` : 'Add deploy target'}
61
- id="deploy-target-form"
62
- onClose={onClose}
63
- width={1}
64
- footer={
65
- <Flex padding={3} gap={2} justify="flex-end">
66
- <Button text="Cancel" mode="ghost" onClick={onClose} style={{ cursor: 'pointer' }} />
67
- <Button
68
- text={isEdit ? 'Save changes' : 'Add target'}
69
- tone="primary"
70
- icon={CheckmarkCircleIcon}
71
- loading={saving}
72
- disabled={!canSave || saving}
73
- onClick={save}
74
- style={{ cursor: 'pointer' }}
75
- />
76
- </Flex>
77
- }
78
- >
79
- <Box padding={4}>
80
- <Stack space={4}>
81
-
82
- {/* Name */}
83
- <Stack space={2}>
84
- <Label size={1}>Name <span style={{ color: 'var(--card-fg-color)' }}>*</span></Label>
85
- <TextInput
86
- value={name}
87
- onChange={e => setName((e.target as HTMLInputElement).value)}
88
- placeholder="Production"
89
- />
90
- </Stack>
91
-
92
- {/* Deploy hook URL */}
93
- <Stack space={2}>
94
- <Label size={1}>Deploy hook URL <span style={{ color: 'var(--card-fg-color)' }}>*</span></Label>
95
- <TextInput
96
- value={url}
97
- onChange={e => setUrl((e.target as HTMLInputElement).value)}
98
- placeholder="https://api.vercel.com/v1/integrations/deploy/…"
99
- />
100
- {url && !urlValid && (
101
- <Text size={1} style={{ color: 'var(--card-critical-fg-color, red)' }}>
102
- Must be a Vercel deploy hook URL (api.vercel.com/v1/integrations/deploy/…)
103
- </Text>
104
- )}
105
- <Text size={0} muted>
106
- Vercel dashboard → Project → Settings → Git → Deploy Hooks
107
- </Text>
108
- </Stack>
109
-
110
- {/* Team ID */}
111
- <Stack space={2}>
112
- <Label size={1}>Team ID <span style={{ opacity: 0.5, fontWeight: 'normal' }}>— optional</span></Label>
113
- <TextInput
114
- value={teamId}
115
- onChange={e => setTeamId((e.target as HTMLInputElement).value)}
116
- placeholder="team_xxxxxxxx"
117
- />
118
- <Text size={0} muted>
119
- Required for team-owned Vercel projects. Find it at Vercel → Settings → General → Team ID (starts with <code>team_</code>).
120
- </Text>
121
- </Stack>
122
-
123
- {/* Disable delete */}
124
- <Flex align="center" gap={3}>
125
- <Switch
126
- checked={disableDelete}
127
- onChange={e => setDisableDelete((e.target as HTMLInputElement).checked)}
128
- id="disable-delete"
129
- />
130
- <Stack space={1}>
131
- <Label as="label" size={1} htmlFor="disable-delete">Disable delete action</Label>
132
- <Text size={0} muted>Hides the delete button for this target in the studio.</Text>
133
- </Stack>
134
- </Flex>
135
-
136
- {error && (
137
- <Card tone="critical" padding={3} radius={2}>
138
- <Text size={1}>{error}</Text>
139
- </Card>
140
- )}
141
-
142
- </Stack>
143
- </Box>
144
- </Dialog>
145
- )
146
- }
@@ -1,296 +0,0 @@
1
- // Main deploy tool — fetches targets + token, renders per-project cards
2
- import { useState, useEffect, useCallback } from 'react'
3
- import { useClient } from 'sanity'
4
- import {
5
- Card, Box, Flex, Text, Heading, Spinner, Button, Dialog,
6
- } from '@sanity/ui'
7
- import { Stack, useToast, ToastViewport } from '../ui'
8
- import { TokenIcon, TrashIcon, WarningOutlineIcon, AddIcon } from '../icons'
9
- import { DeployItem } from './DeployItem'
10
- import { TokenSetup } from './TokenSetup'
11
- import { DeployTargetForm } from './DeployTargetForm'
12
- import { VERSION } from '../version'
13
- import type { DeployTarget } from '../types'
14
-
15
- const TOKEN_QUERY = `*[_id == "config.vercelDeploy"][0].accessToken`
16
- const TARGETS_QUERY = `*[_type == "vercel_deploy"] | order(_createdAt asc)`
17
-
18
- export function DeployTool() {
19
- const client = useClient({ apiVersion: '2025-01-01' })
20
- const toast = useToast()
21
-
22
- const [token, setToken] = useState<string | null>(null)
23
- const [targets, setTargets] = useState<DeployTarget[]>([])
24
- const [loading, setLoading] = useState(true)
25
- const [showTokenSetup, setShowTokenSetup] = useState(false)
26
- const [showCreateForm, setShowCreateForm] = useState(false)
27
- const [pendingEdit, setPendingEdit] = useState<DeployTarget | null>(null)
28
- const [pendingDelete, setPendingDelete] = useState<DeployTarget | null>(null)
29
- const [deleting, setDeleting] = useState(false)
30
-
31
- // ── Fetch token + targets ─────────────────────────────────────────────────
32
- const load = useCallback(async () => {
33
- setLoading(true)
34
- try {
35
- const [fetchedToken, fetchedTargets] = await Promise.all([
36
- client.fetch<string | null>(TOKEN_QUERY),
37
- client.fetch<DeployTarget[]>(TARGETS_QUERY),
38
- ])
39
- setToken(fetchedToken ?? null)
40
- setTargets(fetchedTargets)
41
- } catch (err) {
42
- console.error('deploy-vercel-from-sanity: load error', err)
43
- } finally {
44
- setLoading(false)
45
- }
46
- }, [client])
47
-
48
- useEffect(() => { load() }, [load])
49
-
50
- // Inject responsive styles once on mount
51
- useEffect(() => {
52
- if (document.getElementById('dvfs-styles')) return
53
- const style = document.createElement('style')
54
- style.id = 'dvfs-styles'
55
- style.textContent = `
56
- @media (max-width: 768px) {
57
- .dvfs-header { flex-wrap: wrap !important; row-gap: 8px !important; }
58
- .dvfs-header-actions { width: 100% !important; flex-wrap: wrap !important; justify-content: flex-start !important; }
59
- .dvfs-grid { grid-template-columns: 1fr !important; }
60
- .dvfs-card-flex { flex-direction: column !important; }
61
- .dvfs-deploy-col { width: 100% !important; align-self: auto !important; }
62
- .dvfs-deploy-col button { border-radius: 3px !important; }
63
- }
64
- @keyframes dvfs-open {
65
- from { opacity: 0; transform: translateY(-4px); }
66
- to { opacity: 1; transform: translateY(0); }
67
- }
68
- .dvfs-accordion-content {
69
- animation: dvfs-open 0.15s ease-out;
70
- }
71
- `
72
- document.head.appendChild(style)
73
- return () => { document.getElementById('dvfs-styles')?.remove() }
74
- }, [])
75
-
76
- // Live subscription — update targets when documents change
77
- useEffect(() => {
78
- const sub = client
79
- .listen<DeployTarget>(TARGETS_QUERY)
80
- .subscribe(() => {
81
- client.fetch<DeployTarget[]>(TARGETS_QUERY).then(setTargets).catch(err => {
82
- console.error('deploy-vercel-from-sanity: subscription refresh error', err)
83
- })
84
- })
85
- return () => sub.unsubscribe()
86
- }, [client])
87
-
88
- // ── Delete target ─────────────────────────────────────────────────────────
89
- const confirmDelete = useCallback(async () => {
90
- if (!pendingDelete) return
91
- setDeleting(true)
92
- try {
93
- await client.delete(pendingDelete._id)
94
- setTargets(prev => prev.filter(t => t._id !== pendingDelete._id))
95
- toast.push({ status: 'success', title: `Deleted "${pendingDelete.name}"` })
96
- } catch (err) {
97
- toast.push({ status: 'error', title: 'Delete failed', description: String(err) })
98
- } finally {
99
- setDeleting(false)
100
- setPendingDelete(null)
101
- }
102
- }, [client, pendingDelete, toast])
103
-
104
- // ── Render ────────────────────────────────────────────────────────────────
105
- if (loading) {
106
- return (
107
- <Card height="fill" tone="transparent">
108
- <Flex align="center" justify="center" height="fill">
109
- <Spinner muted />
110
- </Flex>
111
- </Card>
112
- )
113
- }
114
-
115
- return (
116
- <Card height="fill" tone="transparent">
117
- <Box padding={5}>
118
- <Stack space={5}>
119
-
120
- {/* ── Header ──────────────────────────────────────────────── */}
121
- <Flex align="center" justify="space-between" className="dvfs-header">
122
- <Heading size={2}>Deploy with Vercel</Heading>
123
- <Flex align="center" gap={3} className="dvfs-header-actions">
124
- <Button
125
- text={token ? 'Token connected' : 'Connect API token'}
126
- mode="ghost"
127
- icon={TokenIcon}
128
- fontSize={1}
129
- tone={token ? 'positive' : 'caution'}
130
- onClick={() => setShowTokenSetup(true)}
131
- style={{ cursor: 'pointer' }}
132
- />
133
- <Button
134
- text="Add target"
135
- mode="ghost"
136
- icon={AddIcon}
137
- fontSize={1}
138
- onClick={() => setShowCreateForm(true)}
139
- style={{ cursor: 'pointer' }}
140
- />
141
- </Flex>
142
- </Flex>
143
-
144
- {/* ── No-token upgrade banner ──────────────────────────────── */}
145
- {!token && (
146
- <Card padding={4} radius={2} tone="caution" shadow={1}>
147
- <Flex align="center" justify="space-between" gap={4}>
148
- <Stack space={2}>
149
- <Text size={1} weight="semibold">Deploy status is not connected</Text>
150
- <Text size={1} muted>
151
- You can trigger deploys now. Connect a Vercel API token to also see
152
- deployment status, build logs, history, and commit metadata.
153
- </Text>
154
- </Stack>
155
- <Button
156
- text="Connect"
157
- tone="caution"
158
- fontSize={1}
159
- onClick={() => setShowTokenSetup(true)}
160
- style={{ cursor: 'pointer', flexShrink: 0 }}
161
- />
162
- </Flex>
163
- </Card>
164
- )}
165
-
166
- {/* ── No targets ──────────────────────────────────────────── */}
167
- {targets.length === 0 && (
168
- <Card padding={5} radius={2} tone="transparent" shadow={1}>
169
- <Stack space={4} style={{ textAlign: 'center' }}>
170
- <Text size={2} weight="semibold">No deploy targets configured</Text>
171
- <Text size={1} muted>
172
- Add a deploy target using the button above, or create a{' '}
173
- <code>vercel_deploy</code> document directly in the dataset.
174
- </Text>
175
- <Flex justify="center">
176
- <Button
177
- text="Add deploy target"
178
- tone="primary"
179
- icon={AddIcon}
180
- onClick={() => setShowCreateForm(true)}
181
- style={{ cursor: 'pointer' }}
182
- />
183
- </Flex>
184
- </Stack>
185
- </Card>
186
- )}
187
-
188
- {/* ── Deploy targets — responsive 2-col grid ──────────────── */}
189
- {targets.length > 0 && (
190
- <div className="dvfs-grid" style={{
191
- display: 'grid',
192
- gridTemplateColumns: 'repeat(auto-fill, minmax(min(540px, 100%), 1fr))',
193
- gap: '16px',
194
- alignItems: 'start',
195
- }}>
196
- {targets.map(target => (
197
- <DeployItem
198
- key={target._id}
199
- target={target}
200
- token={token ?? ''}
201
- onDelete={setPendingDelete}
202
- onEdit={setPendingEdit}
203
- />
204
- ))}
205
- </div>
206
- )}
207
-
208
- </Stack>
209
- </Box>
210
-
211
- {/* ── Version watermark ──────────────────────────────────────────── */}
212
- <Box
213
- style={{
214
- position: 'fixed',
215
- bottom: 12,
216
- right: 16,
217
- opacity: 0.25,
218
- pointerEvents: 'none',
219
- userSelect: 'none',
220
- }}
221
- >
222
- <Text size={0} muted>v{VERSION}</Text>
223
- </Box>
224
-
225
- {/* ── Token setup dialog ──────────────────────────────────────────── */}
226
- {showTokenSetup && (
227
- <TokenSetup
228
- onSaved={() => { setShowTokenSetup(false); load() }}
229
- onCancel={token ? () => setShowTokenSetup(false) : undefined}
230
- />
231
- )}
232
-
233
- {/* ── Create form ─────────────────────────────────────────────────── */}
234
- {showCreateForm && (
235
- <DeployTargetForm
236
- onSaved={() => { setShowCreateForm(false); toast.push({ status: 'success', title: 'Deploy target added' }) }}
237
- onClose={() => setShowCreateForm(false)}
238
- />
239
- )}
240
-
241
- {/* ── Edit form ───────────────────────────────────────────────────── */}
242
- {pendingEdit && (
243
- <DeployTargetForm
244
- initial={pendingEdit}
245
- onSaved={() => { setPendingEdit(null); toast.push({ status: 'success', title: 'Deploy target updated' }) }}
246
- onClose={() => setPendingEdit(null)}
247
- />
248
- )}
249
-
250
- {/* ── Delete confirmation ─────────────────────────────────────────── */}
251
- {pendingDelete && (
252
- <Dialog
253
- header="Delete deploy target?"
254
- id="confirm-delete"
255
- onClose={() => setPendingDelete(null)}
256
- width={1}
257
- footer={
258
- <Flex padding={3} gap={2} justify="flex-end">
259
- <Button
260
- text="Cancel"
261
- mode="ghost"
262
- onClick={() => setPendingDelete(null)}
263
- style={{ cursor: 'pointer' }}
264
- />
265
- <Button
266
- text="Delete"
267
- tone="critical"
268
- icon={TrashIcon}
269
- loading={deleting}
270
- disabled={deleting}
271
- onClick={confirmDelete}
272
- style={{ cursor: 'pointer' }}
273
- />
274
- </Flex>
275
- }
276
- >
277
- <Box padding={4}>
278
- <Stack space={3}>
279
- <Flex align="center" gap={2}>
280
- <WarningOutlineIcon />
281
- <Text size={2} weight="semibold">{pendingDelete.name}</Text>
282
- </Flex>
283
- <Text size={1} muted>
284
- This removes the deploy target from the dataset. The Vercel deploy hook
285
- itself is not affected.
286
- </Text>
287
- </Stack>
288
- </Box>
289
- </Dialog>
290
- )}
291
-
292
- {/* Fallback toast surface — renders nothing when @sanity/ui exports its own useToast */}
293
- <ToastViewport />
294
- </Card>
295
- )
296
- }
@@ -1,23 +0,0 @@
1
- // Status badge for a Vercel deployment state
2
- import { Badge, Spinner, Flex } from '@sanity/ui'
3
- import { stateLabel } from '../lib/helpers'
4
- import type { VercelDeployState } from '../types'
5
-
6
- interface StatusBadgeProps {
7
- state: VercelDeployState | undefined
8
- /** When true, shows a spinner alongside the label */
9
- showSpinner?: boolean
10
- }
11
-
12
- export function StatusBadge({ state, showSpinner }: StatusBadgeProps) {
13
- const { label, tone } = stateLabel(state)
14
- const spinning = showSpinner && (state === 'QUEUED' || state === 'INITIALIZING' || state === 'BUILDING')
15
- return (
16
- <Flex align="center" gap={2}>
17
- {spinning && <Spinner muted />}
18
- <Badge tone={tone} padding={2}>
19
- {label}
20
- </Badge>
21
- </Flex>
22
- )
23
- }
@@ -1,98 +0,0 @@
1
- // Vercel API token form — rendered inside a Dialog by DeployTool
2
- import { useState, useCallback } from 'react'
3
- import { useClient } from 'sanity'
4
- import {
5
- Text, TextInput, Button, Card, Dialog, Flex,
6
- } from '@sanity/ui'
7
- import { Stack } from '../ui'
8
- import { CheckmarkCircleIcon } from '../icons'
9
-
10
- interface TokenSetupProps {
11
- /** Called after the token is successfully saved */
12
- onSaved: () => void
13
- /** Called when the user dismisses — only available when a token already exists */
14
- onCancel?: () => void
15
- }
16
-
17
- const TOKEN_DOC_ID = 'config.vercelDeploy'
18
-
19
- export function TokenSetup({ onSaved, onCancel }: TokenSetupProps) {
20
- const client = useClient({ apiVersion: '2025-01-01' })
21
- const [token, setToken] = useState('')
22
- const [saving, setSaving] = useState(false)
23
- const [error, setError] = useState<string | null>(null)
24
-
25
- const save = useCallback(async () => {
26
- if (!token.trim()) return
27
- setSaving(true)
28
- setError(null)
29
- try {
30
- await client.createOrReplace({
31
- _id: TOKEN_DOC_ID,
32
- _type: 'vercelDeploy.config',
33
- accessToken: token.trim(),
34
- })
35
- onSaved()
36
- } catch (err) {
37
- setError(err instanceof Error ? err.message : 'Failed to save token')
38
- } finally {
39
- setSaving(false)
40
- }
41
- }, [client, token, onSaved])
42
-
43
- return (
44
- <Dialog
45
- header="Connect Vercel API token"
46
- id="token-setup"
47
- onClose={onCancel}
48
- width={1}
49
- footer={
50
- <Flex padding={3} gap={2} justify="flex-end">
51
- {onCancel && (
52
- <Button text="Cancel" mode="ghost" onClick={onCancel} style={{ cursor: 'pointer' }} />
53
- )}
54
- <Button
55
- text="Save and connect"
56
- tone="primary"
57
- icon={CheckmarkCircleIcon}
58
- loading={saving}
59
- disabled={!token.trim() || saving}
60
- onClick={save}
61
- style={{ cursor: 'pointer' }}
62
- />
63
- </Flex>
64
- }
65
- >
66
- <Stack space={4} padding={4}>
67
- <Stack space={3}>
68
- <Text size={1} muted>
69
- A Vercel API token lets this tool read deployment status, history, build logs,
70
- and branch metadata. Without it you can still trigger deploys — you just won't
71
- see any feedback.
72
- </Text>
73
- <Text size={1} muted>
74
- Create one at <strong>vercel.com → Settings → Tokens</strong> with{' '}
75
- <strong>Full Account</strong> scope. The token is stored in your Sanity dataset
76
- and shared across all authenticated studio users.
77
- </Text>
78
- </Stack>
79
-
80
- <Stack space={2}>
81
- <Text size={1} weight="semibold">Vercel API Token</Text>
82
- <TextInput
83
- value={token}
84
- onChange={e => setToken((e.target as HTMLInputElement).value)}
85
- placeholder="xxxxxxxxxxxxxxxxxxxxxxxx"
86
- type="password"
87
- />
88
- </Stack>
89
-
90
- {error && (
91
- <Card tone="critical" padding={3} radius={2}>
92
- <Text size={1}>{error}</Text>
93
- </Card>
94
- )}
95
- </Stack>
96
- </Dialog>
97
- )
98
- }
package/src/icons.tsx DELETED
@@ -1,71 +0,0 @@
1
- // Version-agnostic access to @sanity/icons — resolves named exports (icons v3/v4) or <Icon symbol> (icons v5+)
2
- import { forwardRef } from 'react'
3
- import type { ComponentType, SVGProps } from 'react'
4
- import * as sanityIcons from '@sanity/icons'
5
-
6
- /** Props every Sanity icon accepts — it renders a plain sized SVG. */
7
- export type IconProps = SVGProps<SVGSVGElement>
8
-
9
- /** An icon component, whichever shape the installed @sanity/icons exposes it in. */
10
- export type IconComponent = ComponentType<IconProps>
11
-
12
- /**
13
- * The installed @sanity/icons namespace, read through an index signature.
14
- *
15
- * icons v3 and v4 export one named component per glyph (`RocketIcon`). v5.0.0
16
- * dropped those from the barrel and replaced them with a single `<Icon symbol>`
17
- * that lazy-loads from an internal map. Its `index.d.ts` still declares the old
18
- * named exports, so the mismatch is invisible to TypeScript and only surfaces at
19
- * runtime — reading the namespace dynamically lets one build serve every major.
20
- */
21
- const INSTALLED = sanityIcons as unknown as Record<string, unknown>
22
-
23
- /** Sizing of a Sanity icon glyph — 1em square on a 25-unit viewBox, matching @sanity/icons. */
24
- const GLYPH = { width: '1em', height: '1em', viewBox: '0 0 25 25', fill: 'none' } as const
25
-
26
- /** Last-resort placeholder when the installed @sanity/icons exposes neither shape. Holds layout, draws nothing. */
27
- const MissingIcon = forwardRef<SVGSVGElement, IconProps>(function MissingIcon(props, ref) {
28
- return <svg {...GLYPH} xmlns="http://www.w3.org/2000/svg" {...props} ref={ref} />
29
- })
30
-
31
- /** The v5+ `<Icon>` component, absent on icons v3 and v4. */
32
- type SymbolIcon = ComponentType<IconProps & { symbol: string }>
33
-
34
- /**
35
- * Resolve one glyph against whichever @sanity/icons the host Studio installed.
36
- * Always reads from the host package, so new and revised artwork is picked up
37
- * on the consumer's next `@sanity/icons` update without a release here.
38
- *
39
- * @param name Named export used by icons v3 and v4, e.g. `RocketIcon`.
40
- * @param symbol Kebab-case symbol used by the v5+ `<Icon>` component, e.g. `rocket`.
41
- */
42
- function resolveIcon(name: string, symbol: string): IconComponent {
43
- const named = INSTALLED[name] as IconComponent | undefined
44
- if (named) return named
45
-
46
- const Icon = INSTALLED.Icon as SymbolIcon | undefined
47
- if (!Icon) return MissingIcon
48
-
49
- const Resolved = forwardRef<SVGSVGElement, IconProps>(function SanityIcon(props, ref) {
50
- return <Icon symbol={symbol} {...props} ref={ref as never} />
51
- })
52
- Resolved.displayName = name
53
- return Resolved as IconComponent
54
- }
55
-
56
- export const AddIcon = resolveIcon('AddIcon', 'add')
57
- export const CheckmarkCircleIcon = resolveIcon('CheckmarkCircleIcon', 'checkmark-circle')
58
- export const CheckmarkIcon = resolveIcon('CheckmarkIcon', 'checkmark')
59
- export const ChevronDownIcon = resolveIcon('ChevronDownIcon', 'chevron-down')
60
- export const ChevronUpIcon = resolveIcon('ChevronUpIcon', 'chevron-up')
61
- export const ClockIcon = resolveIcon('ClockIcon', 'clock')
62
- export const CloseIcon = resolveIcon('CloseIcon', 'close')
63
- export const CopyIcon = resolveIcon('CopyIcon', 'copy')
64
- export const EditIcon = resolveIcon('EditIcon', 'edit')
65
- export const EllipsisVerticalIcon = resolveIcon('EllipsisVerticalIcon', 'ellipsis-vertical')
66
- export const LaunchIcon = resolveIcon('LaunchIcon', 'launch')
67
- export const RocketIcon = resolveIcon('RocketIcon', 'rocket')
68
- export const SchemaIcon = resolveIcon('SchemaIcon', 'schema')
69
- export const TokenIcon = resolveIcon('TokenIcon', 'token')
70
- export const TrashIcon = resolveIcon('TrashIcon', 'trash')
71
- export const WarningOutlineIcon = resolveIcon('WarningOutlineIcon', 'warning-outline')
package/src/index.ts DELETED
@@ -1,42 +0,0 @@
1
- // deploy-vercel-from-sanity — Sanity Studio v5 plugin for Vercel deployments
2
- import { definePlugin } from 'sanity'
3
- import { RocketIcon } from './icons'
4
- import { DeployTool } from './components/DeployTool'
5
- import { vercelDeploySchema } from './schema/vercelDeploy'
6
- import type { VercelDeployPluginConfig } from './types'
7
-
8
- export { vercelDeploySchema } from './schema/vercelDeploy'
9
- export type { VercelDeployPluginConfig, DeployTarget, VercelDeployment, VercelDeployState } from './types'
10
-
11
- /**
12
- * Sanity Studio v5 plugin — trigger and monitor Vercel deployments.
13
- *
14
- * @example
15
- * // sanity.config.ts
16
- * import { vercelDeploy } from '@liiift-studio/deploy-vercel-from-sanity'
17
- *
18
- * export default defineConfig({
19
- * plugins: [
20
- * vercelDeploy(),
21
- * // or with options:
22
- * vercelDeploy({ title: 'Deploy', name: 'vercel-deploy' }),
23
- * ],
24
- * })
25
- */
26
- export const vercelDeploy = definePlugin<VercelDeployPluginConfig | void>(options => {
27
- const config = options ?? {}
28
- return {
29
- name: 'deploy-vercel-from-sanity',
30
- schema: {
31
- types: [vercelDeploySchema],
32
- },
33
- tools: [
34
- {
35
- name: config.name ?? 'vercel-deploy',
36
- title: config.title ?? 'Deploy',
37
- icon: config.icon ?? RocketIcon,
38
- component: DeployTool,
39
- },
40
- ],
41
- }
42
- })