@pagebridge/sanity 0.0.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 (38) hide show
  1. package/.turbo/turbo-build.log +2 -0
  2. package/LICENSE +21 -0
  3. package/README.md +214 -0
  4. package/dist/components/RefreshQueueTool.d.ts +2 -0
  5. package/dist/components/RefreshQueueTool.d.ts.map +1 -0
  6. package/dist/components/RefreshQueueTool.js +55 -0
  7. package/dist/components/SearchPerformancePane.d.ts +6 -0
  8. package/dist/components/SearchPerformancePane.d.ts.map +1 -0
  9. package/dist/components/SearchPerformancePane.js +80 -0
  10. package/dist/index.d.ts +5 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +4 -0
  13. package/dist/plugin.d.ts +40 -0
  14. package/dist/plugin.d.ts.map +1 -0
  15. package/dist/plugin.js +89 -0
  16. package/dist/schemas/gscRefreshTask.d.ts +24 -0
  17. package/dist/schemas/gscRefreshTask.d.ts.map +1 -0
  18. package/dist/schemas/gscRefreshTask.js +196 -0
  19. package/dist/schemas/gscSite.d.ts +7 -0
  20. package/dist/schemas/gscSite.d.ts.map +1 -0
  21. package/dist/schemas/gscSite.js +68 -0
  22. package/dist/schemas/gscSnapshot.d.ts +22 -0
  23. package/dist/schemas/gscSnapshot.d.ts.map +1 -0
  24. package/dist/schemas/gscSnapshot.js +124 -0
  25. package/dist/schemas/index.d.ts +4 -0
  26. package/dist/schemas/index.d.ts.map +1 -0
  27. package/dist/schemas/index.js +3 -0
  28. package/eslint.config.js +3 -0
  29. package/package.json +50 -0
  30. package/src/components/RefreshQueueTool.tsx +196 -0
  31. package/src/components/SearchPerformancePane.tsx +237 -0
  32. package/src/index.ts +18 -0
  33. package/src/plugin.ts +115 -0
  34. package/src/schemas/gscRefreshTask.ts +203 -0
  35. package/src/schemas/gscSite.ts +71 -0
  36. package/src/schemas/gscSnapshot.ts +131 -0
  37. package/src/schemas/index.ts +11 -0
  38. package/tsconfig.json +23 -0
@@ -0,0 +1,2 @@
1
+ [?9001h[?1004h[?25l> @pagebridge/sanity@0.0.1 build F:\Code\pagebridge\oss\packages\sanity-plugin
2
+ > pnpm exec tsc]0;C:\WINDOWS\system32\cmd.exe[?25h[?9001l[?1004l
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Soma Somorjai
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,214 @@
1
+ # @pagebridge/sanity-plugin
2
+
3
+ Sanity Studio v3 plugin for PageBridge. Provides document schemas, UI components, and tools for viewing search performance data and managing content refresh tasks.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add @pagebridge/sanity-plugin
9
+ ```
10
+
11
+ ## Setup
12
+
13
+ ### 1. Add the Plugin
14
+
15
+ In your `sanity.config.ts`:
16
+
17
+ ```typescript
18
+ import { defineConfig } from 'sanity';
19
+ import { gscPlugin } from '@pagebridge/sanity-plugin';
20
+
21
+ export default defineConfig({
22
+ // ... other config
23
+ plugins: [
24
+ gscPlugin({
25
+ contentTypes: ['post', 'page'], // Document types to track
26
+ }),
27
+ ],
28
+ });
29
+ ```
30
+
31
+ ### 2. Add the Structure Resolver
32
+
33
+ To display the Performance pane on your content documents:
34
+
35
+ ```typescript
36
+ import { defineConfig } from 'sanity';
37
+ import { structureTool } from 'sanity/structure';
38
+ import { gscPlugin, createGscStructureResolver } from '@pagebridge/sanity-plugin';
39
+
40
+ export default defineConfig({
41
+ // ... other config
42
+ plugins: [
43
+ structureTool({
44
+ structure: createGscStructureResolver({
45
+ contentTypes: ['post', 'page'],
46
+ }),
47
+ }),
48
+ gscPlugin({
49
+ contentTypes: ['post', 'page'],
50
+ }),
51
+ ],
52
+ });
53
+ ```
54
+
55
+ ## Document Schemas
56
+
57
+ The plugin registers three document types:
58
+
59
+ ### gscSite
60
+
61
+ Represents a Google Search Console property.
62
+
63
+ | Field | Type | Description |
64
+ |-------|------|-------------|
65
+ | siteUrl | string | GSC site URL (e.g., `sc-domain:example.com`) |
66
+ | slug | slug | URL-friendly identifier |
67
+ | defaultLocale | string | Default locale (default: "en") |
68
+ | pathPrefix | string | Path prefix for URL matching (e.g., `/blog`) |
69
+ | lastSyncedAt | datetime | Last sync timestamp (read-only) |
70
+
71
+ ### gscSnapshot
72
+
73
+ Performance metrics snapshot linked to content documents.
74
+
75
+ | Field | Type | Description |
76
+ |-------|------|-------------|
77
+ | site | reference | Reference to gscSite |
78
+ | page | string | Page URL |
79
+ | linkedDocument | reference | Matched content document |
80
+ | period | string | `last7`, `last28`, or `last90` |
81
+ | clicks | number | Total clicks |
82
+ | impressions | number | Total impressions |
83
+ | ctr | number | Click-through rate |
84
+ | position | number | Average position |
85
+ | topQueries | array | Top search queries with metrics |
86
+ | fetchedAt | datetime | When data was fetched |
87
+ | indexStatus | object | Google index status details |
88
+
89
+ ### gscRefreshTask
90
+
91
+ Content refresh task with decay signal information.
92
+
93
+ | Field | Type | Description |
94
+ |-------|------|-------------|
95
+ | site | reference | Reference to gscSite |
96
+ | linkedDocument | reference | Content document needing refresh |
97
+ | reason | string | `position_decay`, `low_ctr`, `impressions_drop`, `manual` |
98
+ | severity | string | `low`, `medium`, `high` |
99
+ | status | string | `open`, `snoozed`, `in_progress`, `done`, `dismissed` |
100
+ | snoozedUntil | datetime | When to resurface (if snoozed) |
101
+ | metrics | object | Position, CTR, impressions data |
102
+ | queryContext | array | Top 5 queries with stats |
103
+ | notes | text | Resolution notes |
104
+ | createdAt | datetime | Task creation time |
105
+ | resolvedAt | datetime | Task resolution time |
106
+
107
+ ## Components
108
+
109
+ ### SearchPerformancePane
110
+
111
+ Document view pane showing performance metrics for a content document.
112
+
113
+ ```typescript
114
+ import { SearchPerformancePane } from '@pagebridge/sanity-plugin';
115
+
116
+ // Used automatically when you configure the structure resolver
117
+ // Can also be used directly in custom document views
118
+ ```
119
+
120
+ The pane displays:
121
+ - Clicks, impressions, CTR, and position metrics
122
+ - Top search queries driving traffic
123
+ - Google index status
124
+ - Link to associated refresh tasks
125
+
126
+ ### RefreshQueueTool
127
+
128
+ Sanity tool for managing content refresh tasks. Accessible from the Studio sidebar.
129
+
130
+ Features:
131
+ - Filter tasks by status (open, in progress, snoozed, done, dismissed)
132
+ - Sort by severity or creation date
133
+ - View decay signal details
134
+ - Update task status
135
+ - Add resolution notes
136
+
137
+ ## Configuration Options
138
+
139
+ ### gscPlugin
140
+
141
+ ```typescript
142
+ interface GscPluginConfig {
143
+ // Document types that can be linked to snapshots and tasks
144
+ contentTypes: string[];
145
+ }
146
+ ```
147
+
148
+ ### createGscStructureResolver
149
+
150
+ ```typescript
151
+ interface StructureResolverConfig {
152
+ // Document types to show the Performance pane on
153
+ contentTypes: string[];
154
+ }
155
+ ```
156
+
157
+ ## Using Schemas Directly
158
+
159
+ If you need to customize the schemas or use them without the plugin:
160
+
161
+ ```typescript
162
+ import {
163
+ gscSite,
164
+ createGscSnapshot,
165
+ createGscRefreshTask,
166
+ } from '@pagebridge/sanity-plugin/schemas';
167
+
168
+ // Create snapshot schema with custom content types
169
+ const customSnapshot = createGscSnapshot({
170
+ contentTypes: ['article', 'guide'],
171
+ });
172
+
173
+ // Create task schema with custom content types
174
+ const customTask = createGscRefreshTask({
175
+ contentTypes: ['article', 'guide'],
176
+ });
177
+ ```
178
+
179
+ ## Styling
180
+
181
+ The components use Sanity UI and follow the Studio's theme. No additional CSS is required.
182
+
183
+ ## Peer Dependencies
184
+
185
+ - `sanity` >= 3.0.0
186
+ - `react` >= 18.0.0
187
+ - `react-dom` >= 18.0.0
188
+ - `@sanity/ui` >= 2.0.0
189
+ - `@sanity/icons` >= 3.0.0
190
+
191
+ ## Exports
192
+
193
+ ```typescript
194
+ // Plugin
195
+ export { gscPlugin, createGscStructureResolver } from '@pagebridge/sanity-plugin';
196
+ export type { GscPluginConfig } from '@pagebridge/sanity-plugin';
197
+
198
+ // Components
199
+ export { SearchPerformancePane, RefreshQueueTool } from '@pagebridge/sanity-plugin';
200
+
201
+ // Schemas
202
+ export {
203
+ gscSite,
204
+ gscSnapshot,
205
+ gscRefreshTask,
206
+ createGscSnapshot,
207
+ createGscRefreshTask,
208
+ } from '@pagebridge/sanity-plugin/schemas';
209
+ export type { GscSnapshotOptions, GscRefreshTaskOptions } from '@pagebridge/sanity-plugin/schemas';
210
+ ```
211
+
212
+ ## License
213
+
214
+ MIT
@@ -0,0 +1,2 @@
1
+ export declare function RefreshQueueTool(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=RefreshQueueTool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RefreshQueueTool.d.ts","sourceRoot":"","sources":["../../src/components/RefreshQueueTool.tsx"],"names":[],"mappings":"AAoDA,wBAAgB,gBAAgB,4CA+I/B"}
@@ -0,0 +1,55 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useClient } from "sanity";
3
+ import { Card, Stack, Text, Button, Flex, Badge, Menu, MenuButton, MenuItem, } from "@sanity/ui";
4
+ import { EllipsisVerticalIcon, CheckmarkIcon, ClockIcon, CloseIcon, } from "@sanity/icons";
5
+ import { useEffect, useState, useCallback } from "react";
6
+ const severityTone = {
7
+ high: "critical",
8
+ medium: "caution",
9
+ low: "default",
10
+ };
11
+ const reasonLabels = {
12
+ position_decay: "Position Drop",
13
+ low_ctr: "Low CTR",
14
+ impressions_drop: "Traffic Drop",
15
+ manual: "Manual",
16
+ };
17
+ export function RefreshQueueTool() {
18
+ const client = useClient({ apiVersion: "2024-01-01" });
19
+ const [tasks, setTasks] = useState([]);
20
+ const [filter, setFilter] = useState("open");
21
+ const fetchTasks = useCallback(async () => {
22
+ const query = filter === "all"
23
+ ? `*[_type == "gscRefreshTask" && status != "done" && status != "dismissed"]`
24
+ : `*[_type == "gscRefreshTask" && status == $status]`;
25
+ const results = await client.fetch(`${query} | order(severity desc, createdAt desc) {
26
+ _id,
27
+ reason,
28
+ severity,
29
+ status,
30
+ snoozedUntil,
31
+ metrics,
32
+ "documentTitle": linkedDocument->title,
33
+ "documentSlug": linkedDocument->slug.current,
34
+ createdAt
35
+ }`, { status: filter });
36
+ setTasks(results);
37
+ }, [filter, client]);
38
+ useEffect(() => {
39
+ fetchTasks();
40
+ }, [fetchTasks]);
41
+ const updateStatus = async (taskId, status, snoozeDays) => {
42
+ const patch = { status };
43
+ if (status === "snoozed" && snoozeDays) {
44
+ const until = new Date();
45
+ until.setDate(until.getDate() + snoozeDays);
46
+ patch.snoozedUntil = until.toISOString();
47
+ }
48
+ if (status === "done") {
49
+ patch.resolvedAt = new Date().toISOString();
50
+ }
51
+ await client.patch(taskId).set(patch).commit();
52
+ setTasks((prev) => prev.filter((t) => t._id !== taskId || status === filter));
53
+ };
54
+ return (_jsx(Card, { padding: 4, children: _jsxs(Stack, { space: 4, children: [_jsxs(Flex, { justify: "space-between", align: "center", children: [_jsx(Text, { size: 3, weight: "semibold", children: "Content Refresh Queue" }), _jsx(Flex, { gap: 2, children: ["open", "snoozed", "all"].map((f) => (_jsx(Button, { mode: filter === f ? "default" : "ghost", text: f.charAt(0).toUpperCase() + f.slice(1), onClick: () => setFilter(f), fontSize: 1 }, f))) })] }), tasks.length === 0 ? (_jsx(Card, { padding: 5, tone: "positive", radius: 2, children: _jsx(Text, { align: "center", children: "\uD83C\uDF89 No pending refresh tasks!" }) })) : (_jsx(Stack, { space: 2, children: tasks.map((task) => (_jsx(Card, { padding: 3, radius: 2, shadow: 1, children: _jsxs(Flex, { justify: "space-between", align: "flex-start", children: [_jsxs(Stack, { space: 2, style: { flex: 1 }, children: [_jsxs(Flex, { gap: 2, align: "center", children: [_jsx(Badge, { tone: severityTone[task.severity], children: task.severity }), _jsx(Badge, { children: reasonLabels[task.reason] })] }), _jsx(Text, { weight: "semibold", children: task.documentTitle || "Untitled" }), _jsxs(Text, { size: 1, muted: true, children: ["/", task.documentSlug] }), task.metrics && (_jsxs(Text, { size: 1, muted: true, children: ["Position: ", task.metrics.positionBefore?.toFixed(1), " \u2192", " ", task.metrics.positionNow?.toFixed(1), " (", (task.metrics.positionDelta ?? 0) > 0 ? "+" : "", task.metrics.positionDelta?.toFixed(1), ")"] }))] }), _jsx(MenuButton, { button: _jsx(Button, { icon: EllipsisVerticalIcon, mode: "ghost" }), id: `menu-${task._id}`, menu: _jsxs(Menu, { children: [_jsx(MenuItem, { icon: CheckmarkIcon, text: "Mark as Done", onClick: () => updateStatus(task._id, "done") }), _jsx(MenuItem, { icon: ClockIcon, text: "Snooze 7 days", onClick: () => updateStatus(task._id, "snoozed", 7) }), _jsx(MenuItem, { icon: ClockIcon, text: "Snooze 30 days", onClick: () => updateStatus(task._id, "snoozed", 30) }), _jsx(MenuItem, { icon: CloseIcon, text: "Dismiss", tone: "critical", onClick: () => updateStatus(task._id, "dismissed") })] }) })] }) }, task._id))) }))] }) }));
55
+ }
@@ -0,0 +1,6 @@
1
+ interface SearchPerformancePaneProps {
2
+ documentId: string;
3
+ }
4
+ export declare function SearchPerformancePane({ documentId, }: SearchPerformancePaneProps): import("react/jsx-runtime").JSX.Element;
5
+ export {};
6
+ //# sourceMappingURL=SearchPerformancePane.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SearchPerformancePane.d.ts","sourceRoot":"","sources":["../../src/components/SearchPerformancePane.tsx"],"names":[],"mappings":"AAsBA,UAAU,0BAA0B;IAClC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,qBAAqB,CAAC,EACpC,UAAU,GACX,EAAE,0BAA0B,2CAgH5B"}
@@ -0,0 +1,80 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import { useClient } from "sanity";
4
+ import { Card, Stack, Text, Badge, Flex, Box, Spinner, Tooltip } from "@sanity/ui";
5
+ import { ArrowUpIcon, ArrowDownIcon, ArrowRightIcon, CheckmarkCircleIcon, CloseCircleIcon, WarningOutlineIcon } from "@sanity/icons";
6
+ export function SearchPerformancePane({ documentId, }) {
7
+ const client = useClient({ apiVersion: "2024-01-01" });
8
+ const [data, setData] = useState(undefined);
9
+ const [loading, setLoading] = useState(true);
10
+ useEffect(() => {
11
+ async function fetchData() {
12
+ const snapshot = await client.fetch(`*[_type == "gscSnapshot" && linkedDocument._ref == $id && period == "last28"][0]{
13
+ clicks,
14
+ impressions,
15
+ ctr,
16
+ position,
17
+ topQueries,
18
+ fetchedAt,
19
+ indexStatus
20
+ }`, { id: documentId });
21
+ const previousSnapshot = await client.fetch(`*[_type == "gscSnapshot" && linkedDocument._ref == $id && period == "last28"] | order(fetchedAt desc)[1]{
22
+ position
23
+ }`, { id: documentId });
24
+ if (snapshot) {
25
+ setData({
26
+ ...snapshot,
27
+ positionDelta: previousSnapshot
28
+ ? snapshot.position - previousSnapshot.position
29
+ : 0,
30
+ lastUpdated: snapshot.fetchedAt,
31
+ indexStatus: snapshot.indexStatus,
32
+ });
33
+ }
34
+ setLoading(false);
35
+ }
36
+ fetchData();
37
+ }, [documentId, client]);
38
+ if (loading) {
39
+ return (_jsx(Card, { padding: 4, children: _jsx(Flex, { justify: "center", children: _jsx(Spinner, {}) }) }));
40
+ }
41
+ if (!data) {
42
+ return (_jsx(Card, { padding: 4, tone: "caution", children: _jsx(Text, { children: "No search data available for this document." }) }));
43
+ }
44
+ return (_jsx(Card, { padding: 4, children: _jsxs(Stack, { space: 4, children: [_jsxs(Flex, { justify: "space-between", align: "center", children: [_jsx(Text, { weight: "semibold", size: 2, children: "Search Performance (Last 28 Days)" }), data.indexStatus && _jsx(IndexStatusBadge, { status: data.indexStatus })] }), _jsxs(Flex, { gap: 3, wrap: "wrap", children: [_jsx(MetricCard, { label: "Clicks", value: data.clicks.toLocaleString() }), _jsx(MetricCard, { label: "Impressions", value: data.impressions.toLocaleString() }), _jsx(MetricCard, { label: "CTR", value: `${(data.ctr * 100).toFixed(1)}%` }), _jsx(MetricCard, { label: "Avg. Position", value: data.position.toFixed(1), trend: data.positionDelta, invertTrend: true })] }), data.topQueries?.length > 0 && (_jsxs(Box, { children: [_jsx(Text, { size: 1, weight: "semibold", muted: true, style: { marginBottom: 8 }, children: "Top Queries" }), _jsx(Stack, { space: 2, children: data.topQueries.slice(0, 5).map((q, i) => (_jsxs(Flex, { justify: "space-between", align: "center", children: [_jsx(Text, { size: 1, style: { flex: 1 }, children: q.query }), _jsxs(Text, { size: 1, muted: true, children: [q.clicks, " clicks \u00B7 pos ", q.position.toFixed(1)] })] }, i))) })] })), _jsxs(Text, { size: 0, muted: true, children: ["Last updated: ", new Date(data.lastUpdated).toLocaleDateString()] })] }) }));
45
+ }
46
+ function MetricCard({ label, value, trend, invertTrend }) {
47
+ const TrendIcon = trend === undefined || trend === 0
48
+ ? ArrowRightIcon
49
+ : (invertTrend ? trend < 0 : trend > 0)
50
+ ? ArrowUpIcon
51
+ : ArrowDownIcon;
52
+ const trendTone = trend === undefined || trend === 0
53
+ ? "default"
54
+ : (invertTrend ? trend < 0 : trend > 0)
55
+ ? "positive"
56
+ : "critical";
57
+ return (_jsx(Card, { padding: 3, radius: 2, shadow: 1, style: { minWidth: 100 }, children: _jsxs(Stack, { space: 2, children: [_jsx(Text, { size: 0, muted: true, children: label }), _jsxs(Flex, { align: "center", gap: 2, children: [_jsx(Text, { size: 3, weight: "semibold", children: value }), trend !== undefined && trend !== 0 && (_jsxs(Badge, { tone: trendTone, fontSize: 0, children: [_jsx(TrendIcon, {}), Math.abs(trend).toFixed(1)] }))] })] }) }));
58
+ }
59
+ function IndexStatusBadge({ status }) {
60
+ const config = {
61
+ indexed: {
62
+ tone: "positive",
63
+ icon: CheckmarkCircleIcon,
64
+ label: "Indexed",
65
+ },
66
+ not_indexed: {
67
+ tone: "critical",
68
+ icon: CloseCircleIcon,
69
+ label: "Not Indexed",
70
+ },
71
+ excluded: {
72
+ tone: "caution",
73
+ icon: WarningOutlineIcon,
74
+ label: "Excluded",
75
+ },
76
+ };
77
+ const { tone, icon: Icon, label } = config[status.verdict];
78
+ const tooltipContent = status.coverageState || label;
79
+ return (_jsx(Tooltip, { content: _jsx(Box, { padding: 2, children: _jsxs(Stack, { space: 2, children: [_jsx(Text, { size: 1, children: tooltipContent }), status.lastCrawlTime && (_jsxs(Text, { size: 0, muted: true, children: ["Last crawled: ", new Date(status.lastCrawlTime).toLocaleDateString()] }))] }) }), placement: "top", children: _jsx(Badge, { tone: tone, fontSize: 1, style: { cursor: "help" }, children: _jsxs(Flex, { align: "center", gap: 1, children: [_jsx(Icon, {}), label] }) }) }));
80
+ }
@@ -0,0 +1,5 @@
1
+ export { SearchPerformancePane } from "./components/SearchPerformancePane";
2
+ export { RefreshQueueTool } from "./components/RefreshQueueTool";
3
+ export { gscPlugin, createGscStructureResolver, createPageBridgeStructure, PAGEBRIDGE_TYPES, type GscPluginConfig, } from "./plugin";
4
+ export { gscSite, gscSnapshot, gscRefreshTask, createGscSnapshot, createGscRefreshTask, type GscSnapshotOptions, type GscRefreshTaskOptions, } from "./schemas";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AAC3E,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EACL,SAAS,EACT,0BAA0B,EAC1B,yBAAyB,EACzB,gBAAgB,EAChB,KAAK,eAAe,GACrB,MAAM,UAAU,CAAC;AAClB,OAAO,EACL,OAAO,EACP,WAAW,EACX,cAAc,EACd,iBAAiB,EACjB,oBAAoB,EACpB,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,GAC3B,MAAM,WAAW,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { SearchPerformancePane } from "./components/SearchPerformancePane";
2
+ export { RefreshQueueTool } from "./components/RefreshQueueTool";
3
+ export { gscPlugin, createGscStructureResolver, createPageBridgeStructure, PAGEBRIDGE_TYPES, } from "./plugin";
4
+ export { gscSite, gscSnapshot, gscRefreshTask, createGscSnapshot, createGscRefreshTask, } from "./schemas";
@@ -0,0 +1,40 @@
1
+ import type { DefaultDocumentNodeResolver, StructureBuilder } from "sanity/structure";
2
+ export interface GscPluginConfig {
3
+ /**
4
+ * Array of Sanity document type names that represent your content.
5
+ * These will be available for linking in gscSnapshot and gscRefreshTask schemas.
6
+ * Example: ['post', 'article', 'page']
7
+ */
8
+ contentTypes?: string[];
9
+ }
10
+ /** Document type names registered by the PageBridge plugin */
11
+ export declare const PAGEBRIDGE_TYPES: readonly ["gscSite", "gscSnapshot", "gscRefreshTask"];
12
+ /**
13
+ * Creates a "PageBridge" folder list item for the desk structure.
14
+ * Use with structureTool's `structure` option to group PageBridge
15
+ * documents into a single folder and filter them from the default list.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * structureTool({
20
+ * structure: (S, context) =>
21
+ * S.list()
22
+ * .title("Content")
23
+ * .items([
24
+ * createPageBridgeStructure(S),
25
+ * S.divider(),
26
+ * ...S.documentTypeListItems().filter(
27
+ * (item) => !PAGEBRIDGE_TYPES.includes(item.getId() as any),
28
+ * ),
29
+ * ]),
30
+ * })
31
+ * ```
32
+ */
33
+ export declare const createPageBridgeStructure: (S: StructureBuilder) => import("sanity/structure").ListItemBuilder;
34
+ /**
35
+ * Creates a structure resolver that adds the Performance view to content types
36
+ * Use this with structureTool's defaultDocumentNode option
37
+ */
38
+ export declare const createGscStructureResolver: (contentTypes?: string[]) => DefaultDocumentNodeResolver;
39
+ export declare const gscPlugin: import("sanity").Plugin<void | GscPluginConfig>;
40
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,2BAA2B,EAC3B,gBAAgB,EACjB,MAAM,kBAAkB,CAAC;AAQ1B,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,8DAA8D;AAC9D,eAAO,MAAM,gBAAgB,uDAInB,CAAC;AAEX;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,yBAAyB,GAAI,GAAG,gBAAgB,+CAuBxD,CAAC;AAEN;;;GAGG;AACH,eAAO,MAAM,0BAA0B,GACrC,eAAc,MAAM,EAAO,KAC1B,2BAaF,CAAC;AAEF,eAAO,MAAM,SAAS,iDAmBpB,CAAC"}
package/dist/plugin.js ADDED
@@ -0,0 +1,89 @@
1
+ import { definePlugin } from "sanity";
2
+ import { ChartUpwardIcon, EarthGlobeIcon } from "@sanity/icons";
3
+ import { gscSite } from "./schemas/gscSite";
4
+ import { createGscSnapshot } from "./schemas/gscSnapshot";
5
+ import { createGscRefreshTask } from "./schemas/gscRefreshTask";
6
+ import { RefreshQueueTool } from "./components/RefreshQueueTool";
7
+ import { SearchPerformancePane } from "./components/SearchPerformancePane";
8
+ /** Document type names registered by the PageBridge plugin */
9
+ export const PAGEBRIDGE_TYPES = [
10
+ "gscSite",
11
+ "gscSnapshot",
12
+ "gscRefreshTask",
13
+ ];
14
+ /**
15
+ * Creates a "PageBridge" folder list item for the desk structure.
16
+ * Use with structureTool's `structure` option to group PageBridge
17
+ * documents into a single folder and filter them from the default list.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * structureTool({
22
+ * structure: (S, context) =>
23
+ * S.list()
24
+ * .title("Content")
25
+ * .items([
26
+ * createPageBridgeStructure(S),
27
+ * S.divider(),
28
+ * ...S.documentTypeListItems().filter(
29
+ * (item) => !PAGEBRIDGE_TYPES.includes(item.getId() as any),
30
+ * ),
31
+ * ]),
32
+ * })
33
+ * ```
34
+ */
35
+ export const createPageBridgeStructure = (S) => S.listItem()
36
+ .title("PageBridge")
37
+ .icon(EarthGlobeIcon)
38
+ .child(S.list()
39
+ .title("PageBridge")
40
+ .items([
41
+ S.listItem()
42
+ .title("GSC Sites")
43
+ .schemaType("gscSite")
44
+ .child(S.documentTypeList("gscSite").title("GSC Sites")),
45
+ S.listItem()
46
+ .title("Snapshots")
47
+ .schemaType("gscSnapshot")
48
+ .child(S.documentTypeList("gscSnapshot").title("Snapshots")),
49
+ S.listItem()
50
+ .title("Refresh Tasks")
51
+ .schemaType("gscRefreshTask")
52
+ .child(S.documentTypeList("gscRefreshTask").title("Refresh Tasks")),
53
+ ]));
54
+ /**
55
+ * Creates a structure resolver that adds the Performance view to content types
56
+ * Use this with structureTool's defaultDocumentNode option
57
+ */
58
+ export const createGscStructureResolver = (contentTypes = []) => {
59
+ return (S, { schemaType }) => {
60
+ if (contentTypes.includes(schemaType)) {
61
+ return S.document().views([
62
+ S.view.form(),
63
+ S.view
64
+ .component(SearchPerformancePane)
65
+ .title("Performance")
66
+ .icon(ChartUpwardIcon),
67
+ ]);
68
+ }
69
+ return S.document().views([S.view.form()]);
70
+ };
71
+ };
72
+ export const gscPlugin = definePlugin((config) => {
73
+ const contentTypes = config?.contentTypes ?? [];
74
+ const gscSnapshot = createGscSnapshot({ contentTypes });
75
+ const gscRefreshTask = createGscRefreshTask({ contentTypes });
76
+ return {
77
+ name: "pagebridge-sanity",
78
+ schema: {
79
+ types: [gscSite, gscSnapshot, gscRefreshTask],
80
+ },
81
+ tools: [
82
+ {
83
+ name: "gsc-refresh-queue",
84
+ title: "Refresh Queue",
85
+ component: RefreshQueueTool,
86
+ },
87
+ ],
88
+ };
89
+ });
@@ -0,0 +1,24 @@
1
+ export interface GscRefreshTaskOptions {
2
+ contentTypes?: string[];
3
+ }
4
+ export declare const createGscRefreshTask: (options?: GscRefreshTaskOptions) => {
5
+ type: "document";
6
+ name: "gscRefreshTask";
7
+ } & Omit<import("sanity").DocumentDefinition, "preview"> & {
8
+ preview?: import("sanity").PreviewConfig<{
9
+ title: string;
10
+ reason: string;
11
+ severity: string;
12
+ }, Record<"title" | "reason" | "severity", any>> | undefined;
13
+ };
14
+ export declare const gscRefreshTask: {
15
+ type: "document";
16
+ name: "gscRefreshTask";
17
+ } & Omit<import("sanity").DocumentDefinition, "preview"> & {
18
+ preview?: import("sanity").PreviewConfig<{
19
+ title: string;
20
+ reason: string;
21
+ severity: string;
22
+ }, Record<"title" | "reason" | "severity", any>> | undefined;
23
+ };
24
+ //# sourceMappingURL=gscRefreshTask.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gscRefreshTask.d.ts","sourceRoot":"","sources":["../../src/schemas/gscRefreshTask.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,qBAAqB;IACpC,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,eAAO,MAAM,oBAAoB,GAAI,UAAS,qBAA0B;;;;;;;;;CAiMvE,CAAC;AAGF,eAAO,MAAM,cAAc;;;;;;;;;CAAyB,CAAC"}