@kyro-cms/admin 0.10.10 → 0.10.11
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/dist/index.cjs +18 -18
- package/dist/index.js +18 -18
- package/package.json +1 -1
- package/src/components/ActionBar.tsx +1 -1
- package/src/components/GraphQLPlayground.tsx +7 -7
- package/src/components/MediaGallery.tsx +1 -1
- package/src/components/RestPlayground.tsx +5 -5
- package/src/components/UserMenu.tsx +18 -6
- package/src/components/fields/ArrayLayout.tsx +9 -0
- package/src/components/fields/RichTextField.tsx +2 -2
- package/src/layouts/AdminLayout.astro +1 -1
- package/src/pages/graphql-explorer.astro +24 -5
- package/src/pages/graphql.astro +24 -5
- package/src/pages/index.astro +0 -191
package/package.json
CHANGED
|
@@ -165,7 +165,7 @@ export function ActionBar({
|
|
|
165
165
|
<button key={v} type="button" onClick={() => { setView(v); setMoreOpen(false); }}
|
|
166
166
|
className={`px-2.5 py-1 text-[11px] font-semibold rounded-md transition-all flex-1 ${
|
|
167
167
|
view === v
|
|
168
|
-
? "bg-[var(--kyro-primary)] text-
|
|
168
|
+
? "bg-[var(--kyro-primary)] text-[var(--kyro-sidebar-text-active)]"
|
|
169
169
|
: "text-[var(--kyro-text-muted)] hover:text-[var(--kyro-text-primary)]"
|
|
170
170
|
}`}
|
|
171
171
|
>
|
|
@@ -698,10 +698,10 @@ export function GraphQLPlayground({
|
|
|
698
698
|
</div>
|
|
699
699
|
)}
|
|
700
700
|
<div className="ml-auto flex items-center gap-1 flex-wrap justify-end">
|
|
701
|
-
<button onClick={() => { setShowDocs(!showDocs); setRightTab("docs"); }} className={`p-1.5 rounded-lg transition-all ${rightTab === "docs" && showDocs ? "bg-[var(--kyro-primary)] text-
|
|
701
|
+
<button onClick={() => { setShowDocs(!showDocs); setRightTab("docs"); }} className={`p-1.5 rounded-lg transition-all ${rightTab === "docs" && showDocs ? "bg-[var(--kyro-primary)] text-[var(--kyro-sidebar-text-active)]" : "text-[var(--kyro-text-muted)] hover:text-[var(--kyro-text-primary)] hover:bg-[var(--kyro-surface-accent)]"}`} title="Schema docs">
|
|
702
702
|
<Book className="w-3.5 h-3.5" />
|
|
703
703
|
</button>
|
|
704
|
-
<button onClick={() => setRightTab("history")} className={`p-1.5 rounded-lg transition-all ${rightTab === "history" ? "bg-[var(--kyro-primary)] text-
|
|
704
|
+
<button onClick={() => setRightTab("history")} className={`p-1.5 rounded-lg transition-all ${rightTab === "history" ? "bg-[var(--kyro-primary)] text-[var(--kyro-sidebar-text-active)]" : "text-[var(--kyro-text-muted)] hover:text-[var(--kyro-text-primary)] hover:bg-[var(--kyro-surface-accent)]"}`} title="History">
|
|
705
705
|
<Clock className="w-3.5 h-3.5" />
|
|
706
706
|
</button>
|
|
707
707
|
<button onClick={handlePrettify} className="p-1.5 rounded-lg text-[var(--kyro-text-muted)] hover:text-[var(--kyro-text-primary)] hover:bg-[var(--kyro-surface-accent)]" title="Prettify (Cmd+Shift+P)">
|
|
@@ -720,7 +720,7 @@ export function GraphQLPlayground({
|
|
|
720
720
|
<button
|
|
721
721
|
onClick={handleRun}
|
|
722
722
|
disabled={isLoading}
|
|
723
|
-
className="flex items-center gap-1 px-3 py-1.5 rounded-lg bg-[var(--kyro-primary)] text-
|
|
723
|
+
className="flex items-center gap-1 px-3 py-1.5 rounded-lg bg-[var(--kyro-primary)] text-[var(--kyro-sidebar-text-active)] text-xs font-semibold hover:opacity-90 disabled:opacity-50 transition-all shadow-sm"
|
|
724
724
|
>
|
|
725
725
|
<Play className="w-3 h-3" />
|
|
726
726
|
{isLoading ? "..." : "Run"}
|
|
@@ -734,13 +734,13 @@ export function GraphQLPlayground({
|
|
|
734
734
|
<div className="flex md:hidden gap-1 px-3 py-1.5 border-b border-[var(--kyro-border)] bg-[var(--kyro-surface)] shrink-0">
|
|
735
735
|
<button
|
|
736
736
|
onClick={() => setMobilePanel("editor")}
|
|
737
|
-
className={`flex-1 px-3 py-1.5 text-[10px] font-semibold rounded-md transition-all ${mobilePanel === "editor" ? "bg-[var(--kyro-primary)] text-
|
|
737
|
+
className={`flex-1 px-3 py-1.5 text-[10px] font-semibold rounded-md transition-all ${mobilePanel === "editor" ? "bg-[var(--kyro-primary)] text-[var(--kyro-sidebar-text-active)]" : "text-[var(--kyro-text-muted)] hover:text-[var(--kyro-text-primary)] hover:bg-[var(--kyro-surface-accent)]"}`}
|
|
738
738
|
>
|
|
739
739
|
Editor
|
|
740
740
|
</button>
|
|
741
741
|
<button
|
|
742
742
|
onClick={() => setMobilePanel("response")}
|
|
743
|
-
className={`flex-1 px-3 py-1.5 text-[10px] font-semibold rounded-md transition-all ${mobilePanel === "response" ? "bg-[var(--kyro-primary)] text-
|
|
743
|
+
className={`flex-1 px-3 py-1.5 text-[10px] font-semibold rounded-md transition-all ${mobilePanel === "response" ? "bg-[var(--kyro-primary)] text-[var(--kyro-sidebar-text-active)]" : "text-[var(--kyro-text-muted)] hover:text-[var(--kyro-text-primary)] hover:bg-[var(--kyro-surface-accent)]"}`}
|
|
744
744
|
>
|
|
745
745
|
Output
|
|
746
746
|
</button>
|
|
@@ -755,7 +755,7 @@ export function GraphQLPlayground({
|
|
|
755
755
|
key={p.key}
|
|
756
756
|
onClick={() => setActiveEditorTab(p.key)}
|
|
757
757
|
className={`px-2.5 py-1 text-[10px] font-semibold rounded-md transition-all ${activeEditorTab === p.key
|
|
758
|
-
? "bg-[var(--kyro-primary)] text-
|
|
758
|
+
? "bg-[var(--kyro-primary)] text-[var(--kyro-sidebar-text-active)]"
|
|
759
759
|
: "text-[var(--kyro-text-muted)] hover:text-[var(--kyro-text-primary)] hover:bg-[var(--kyro-surface-accent)]"
|
|
760
760
|
}`}
|
|
761
761
|
>
|
|
@@ -810,7 +810,7 @@ export function GraphQLPlayground({
|
|
|
810
810
|
key={p.key}
|
|
811
811
|
onClick={() => { setRightTab(p.key); if (p.key === "docs") setShowDocs(true); }}
|
|
812
812
|
className={`px-2.5 py-1 text-[10px] font-semibold rounded-md transition-all ${rightTab === p.key
|
|
813
|
-
? "bg-[var(--kyro-primary)] text-
|
|
813
|
+
? "bg-[var(--kyro-primary)] text-[var(--kyro-sidebar-text-active)]"
|
|
814
814
|
: "text-[var(--kyro-text-muted)] hover:text-[var(--kyro-text-primary)] hover:bg-[var(--kyro-surface-accent)]"
|
|
815
815
|
}`}
|
|
816
816
|
>
|
|
@@ -682,7 +682,7 @@ export function MediaGallery({
|
|
|
682
682
|
</div>
|
|
683
683
|
|
|
684
684
|
{selectedIds.has(item.id) && (
|
|
685
|
-
<div className="absolute top-3 left-3 w-6 h-6 rounded-lg bg-[var(--kyro-primary)] text-
|
|
685
|
+
<div className="absolute top-3 left-3 w-6 h-6 rounded-lg bg-[var(--kyro-primary)] text-[var(--kyro-sidebar-text-active)] flex items-center justify-center shadow-lg border-2 border-white/20 animate-in zoom-in duration-300">
|
|
686
686
|
<Check className="w-4 h-4" />
|
|
687
687
|
</div>
|
|
688
688
|
)}
|
|
@@ -378,7 +378,7 @@ export function RestPlayground({ collections = [] }: RestPlaygroundProps) {
|
|
|
378
378
|
<button
|
|
379
379
|
onClick={handleSend}
|
|
380
380
|
disabled={loading || !currentRequest.url}
|
|
381
|
-
className="flex items-center gap-1 px-3 py-1.5 rounded-md bg-[var(--kyro-primary)] text-
|
|
381
|
+
className="flex items-center gap-1 px-3 py-1.5 rounded-md bg-[var(--kyro-primary)] text-[var(--kyro-sidebar-text-active)] text-xs font-semibold hover:opacity-90 disabled:opacity-50 transition-all shadow-sm"
|
|
382
382
|
>
|
|
383
383
|
<Play className="w-3 h-3" />
|
|
384
384
|
{loading ? "..." : "Send"}
|
|
@@ -479,7 +479,7 @@ export function RestPlayground({ collections = [] }: RestPlaygroundProps) {
|
|
|
479
479
|
key={req.id}
|
|
480
480
|
className={`flex items-center gap-2 px-2 py-1 rounded cursor-pointer transition-colors ${
|
|
481
481
|
selectedRequest?.id === req.id
|
|
482
|
-
? "bg-[var(--kyro-primary)] text-
|
|
482
|
+
? "bg-[var(--kyro-primary)] text-[var(--kyro-sidebar-text-active)]"
|
|
483
483
|
: "hover:bg-[var(--kyro-surface-accent)]"
|
|
484
484
|
}`}
|
|
485
485
|
onClick={() => loadRequest(req)}
|
|
@@ -573,7 +573,7 @@ export function RestPlayground({ collections = [] }: RestPlaygroundProps) {
|
|
|
573
573
|
onClick={() => setMobilePanel("editor")}
|
|
574
574
|
className={`flex-1 px-3 py-1 text-[10px] font-semibold rounded-md transition-all ${
|
|
575
575
|
mobilePanel === "editor"
|
|
576
|
-
? "bg-[var(--kyro-primary)] text-
|
|
576
|
+
? "bg-[var(--kyro-primary)] text-[var(--kyro-sidebar-text-active)]"
|
|
577
577
|
: "text-[var(--kyro-text-muted)] hover:text-[var(--kyro-text-primary)] hover:bg-[var(--kyro-surface-accent)]"
|
|
578
578
|
}`}
|
|
579
579
|
>
|
|
@@ -583,7 +583,7 @@ export function RestPlayground({ collections = [] }: RestPlaygroundProps) {
|
|
|
583
583
|
onClick={() => setMobilePanel("response")}
|
|
584
584
|
className={`flex-1 px-3 py-1 text-[10px] font-semibold rounded-md transition-all ${
|
|
585
585
|
mobilePanel === "response"
|
|
586
|
-
? "bg-[var(--kyro-primary)] text-
|
|
586
|
+
? "bg-[var(--kyro-primary)] text-[var(--kyro-sidebar-text-active)]"
|
|
587
587
|
: "text-[var(--kyro-text-muted)] hover:text-[var(--kyro-text-primary)] hover:bg-[var(--kyro-surface-accent)]"
|
|
588
588
|
}`}
|
|
589
589
|
>
|
|
@@ -604,7 +604,7 @@ export function RestPlayground({ collections = [] }: RestPlaygroundProps) {
|
|
|
604
604
|
onClick={() => setActiveEditorTab(p.key)}
|
|
605
605
|
className={`px-2.5 py-1 text-[10px] font-semibold rounded-md transition-all ${
|
|
606
606
|
activeEditorTab === p.key
|
|
607
|
-
? "bg-[var(--kyro-primary)] text-
|
|
607
|
+
? "bg-[var(--kyro-primary)] text-[var(--kyro-sidebar-text-active)]"
|
|
608
608
|
: "text-[var(--kyro-text-muted)] hover:text-[var(--kyro-text-primary)] hover:bg-[var(--kyro-surface-accent)]"
|
|
609
609
|
}`}
|
|
610
610
|
>
|
|
@@ -11,6 +11,7 @@ interface UserMenuProps {
|
|
|
11
11
|
export function UserMenu({ adminPath }: UserMenuProps) {
|
|
12
12
|
const currentUser = useAuthStore((s) => s.user);
|
|
13
13
|
const [avatarUrl, setAvatarUrl] = useState<string | null>(null);
|
|
14
|
+
const [apiAccess, setApiAccess] = useState<{ graphqlEnabled?: boolean } | null>(null);
|
|
14
15
|
|
|
15
16
|
useEffect(() => {
|
|
16
17
|
const avatar = currentUser?.avatar;
|
|
@@ -25,6 +26,15 @@ export function UserMenu({ adminPath }: UserMenuProps) {
|
|
|
25
26
|
}
|
|
26
27
|
}, [currentUser?.avatar]);
|
|
27
28
|
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
apiGet<any>("/api/globals/access-settings")
|
|
31
|
+
.then((res: any) => {
|
|
32
|
+
const data = res?.data || res;
|
|
33
|
+
setApiAccess(data?.apiAccess || {});
|
|
34
|
+
})
|
|
35
|
+
.catch(() => setApiAccess({}));
|
|
36
|
+
}, []);
|
|
37
|
+
|
|
28
38
|
return (
|
|
29
39
|
<Dropdown
|
|
30
40
|
align="right"
|
|
@@ -109,12 +119,14 @@ export function UserMenu({ adminPath }: UserMenuProps) {
|
|
|
109
119
|
API Explorer
|
|
110
120
|
</DropdownItem>
|
|
111
121
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
122
|
+
{(apiAccess === null || apiAccess?.graphqlEnabled) && (
|
|
123
|
+
<DropdownItem
|
|
124
|
+
icon={<Zap className="w-4 h-4" />}
|
|
125
|
+
onClick={() => (window.location.href = `${adminPath}/graphql`)}
|
|
126
|
+
>
|
|
127
|
+
GraphQL Playground
|
|
128
|
+
</DropdownItem>
|
|
129
|
+
)}
|
|
118
130
|
|
|
119
131
|
<DropdownSeparator />
|
|
120
132
|
|
|
@@ -47,6 +47,15 @@ export function ArrayLayout({
|
|
|
47
47
|
const val = item[f.name!];
|
|
48
48
|
if (val && typeof val === "string") return val;
|
|
49
49
|
}
|
|
50
|
+
if (f.type === "group" && (f as any).fields) {
|
|
51
|
+
for (const inner of (f as any).fields) {
|
|
52
|
+
if (inner.type === "text" || inner.type === "textarea") {
|
|
53
|
+
const group = item[f.name!] as Record<string, unknown> | undefined;
|
|
54
|
+
const val = group?.[inner.name!];
|
|
55
|
+
if (val && typeof val === "string") return val;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
50
59
|
}
|
|
51
60
|
return "";
|
|
52
61
|
}
|
|
@@ -166,7 +166,7 @@ const MenuBar = ({
|
|
|
166
166
|
disabled={disabled}
|
|
167
167
|
title={title}
|
|
168
168
|
className={`p-1 rounded flex items-center justify-center transition-all duration-150 relative
|
|
169
|
-
${isActive ? "bg-[var(--kyro-primary)] text-
|
|
169
|
+
${isActive ? "bg-[var(--kyro-primary)] text-[var(--kyro-sidebar-text-active)] shadow-xs scale-95" : "text-[var(--kyro-text)] hover:bg-[var(--kyro-bg-hover)]"}
|
|
170
170
|
${disabled ? "opacity-35 cursor-not-allowed" : "cursor-pointer active:scale-90"}`}
|
|
171
171
|
>
|
|
172
172
|
{children}
|
|
@@ -656,7 +656,7 @@ function RichTextEditor({
|
|
|
656
656
|
<button
|
|
657
657
|
type="button"
|
|
658
658
|
onClick={() => setIsExpanded(false)}
|
|
659
|
-
className="px-2.5 py-1 bg-[var(--kyro-primary)] text-
|
|
659
|
+
className="px-2.5 py-1 bg-[var(--kyro-primary)] text-[var(--kyro-sidebar-text-active)] text-xs rounded-md shadow-sm font-medium hover:scale-102 active:scale-98 transition-transform cursor-pointer"
|
|
660
660
|
>
|
|
661
661
|
Minimize Workspace
|
|
662
662
|
</button>
|
|
@@ -298,7 +298,7 @@ if (includeSiteName) {
|
|
|
298
298
|
id="kyro-fab"
|
|
299
299
|
type="button"
|
|
300
300
|
onclick="window.openCommandPalette?.()"
|
|
301
|
-
class="fixed bottom-6 right-6 z-50 w-12 h-12 flex md:hidden items-center justify-center rounded-2xl bg-[var(--kyro-primary)] text-
|
|
301
|
+
class="fixed bottom-6 right-6 z-50 w-12 h-12 flex md:hidden items-center justify-center rounded-2xl bg-[var(--kyro-primary)] text-[var(--kyro-sidebar-text-active)] shadow-lg shadow-[var(--kyro-primary)]/30 hover:shadow-xl hover:shadow-[var(--kyro-primary)]/40 hover:scale-110 active:scale-95 transition-all duration-200"
|
|
302
302
|
title="Open command palette (Cmd+K)"
|
|
303
303
|
>
|
|
304
304
|
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
@@ -2,14 +2,33 @@
|
|
|
2
2
|
import AdminLayout from "../layouts/AdminLayout.astro";
|
|
3
3
|
import { GraphQLPlayground } from "../components/GraphQLPlayground";
|
|
4
4
|
import { apiPath } from "../lib/paths";
|
|
5
|
+
import { getGlobal } from "../lib/globals";
|
|
6
|
+
|
|
7
|
+
const accessSettings = await getGlobal("access-settings");
|
|
8
|
+
const apiAccess = accessSettings?.apiAccess || {};
|
|
9
|
+
const graphqlEnabled = apiAccess.graphqlEnabled !== false;
|
|
5
10
|
---
|
|
6
11
|
|
|
7
12
|
<AdminLayout title="GraphQL Explorer">
|
|
8
13
|
<div class="flex-1 overflow-hidden h-[calc(100vh-64px)] md:h-[calc(100vh-80px)]">
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
{graphqlEnabled ? (
|
|
15
|
+
<GraphQLPlayground
|
|
16
|
+
client:load
|
|
17
|
+
endpoint={`${apiPath}/graphql`}
|
|
18
|
+
initialShowDocs={true}
|
|
19
|
+
/>
|
|
20
|
+
) : (
|
|
21
|
+
<div class="flex items-center justify-center h-full">
|
|
22
|
+
<div class="text-center p-8 max-w-md">
|
|
23
|
+
<div class="w-16 h-16 mx-auto mb-4 rounded-2xl bg-[var(--kyro-surface-accent)] flex items-center justify-center">
|
|
24
|
+
<svg class="w-8 h-8 text-[var(--kyro-text-muted)]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
25
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
|
26
|
+
</svg>
|
|
27
|
+
</div>
|
|
28
|
+
<h2 class="text-xl font-bold text-[var(--kyro-text-primary)] mb-2">GraphQL Disabled</h2>
|
|
29
|
+
<p class="text-sm text-[var(--kyro-text-secondary)]">The GraphQL API is currently disabled. Enable it in Access Control settings to use the GraphQL Explorer.</p>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
)}
|
|
14
33
|
</div>
|
|
15
34
|
</AdminLayout>
|
package/src/pages/graphql.astro
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
import AdminLayout from "../layouts/AdminLayout.astro";
|
|
3
3
|
import { GraphQLPlayground } from "../components/GraphQLPlayground";
|
|
4
4
|
import { apiPath } from "../lib/paths";
|
|
5
|
+
import { getGlobal } from "../lib/globals";
|
|
6
|
+
|
|
7
|
+
const accessSettings = await getGlobal("access-settings");
|
|
8
|
+
const apiAccess = accessSettings?.apiAccess || {};
|
|
9
|
+
const graphqlEnabled = apiAccess.graphqlEnabled !== false;
|
|
5
10
|
|
|
6
11
|
const url = Astro.request.url;
|
|
7
12
|
const params = new URL(url).searchParams;
|
|
@@ -10,10 +15,24 @@ const initialQuery = params.get("query") || "";
|
|
|
10
15
|
|
|
11
16
|
<AdminLayout title="GraphQL Playground">
|
|
12
17
|
<div class="flex-1 overflow-hidden h-[calc(100vh-64px)] md:h-[calc(100vh-80px)]">
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
{graphqlEnabled ? (
|
|
19
|
+
<GraphQLPlayground
|
|
20
|
+
client:load
|
|
21
|
+
endpoint={`${apiPath}/graphql`}
|
|
22
|
+
initialQuery={initialQuery}
|
|
23
|
+
/>
|
|
24
|
+
) : (
|
|
25
|
+
<div class="flex items-center justify-center h-full">
|
|
26
|
+
<div class="text-center p-8 max-w-md">
|
|
27
|
+
<div class="w-16 h-16 mx-auto mb-4 rounded-2xl bg-[var(--kyro-surface-accent)] flex items-center justify-center">
|
|
28
|
+
<svg class="w-8 h-8 text-[var(--kyro-text-muted)]" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
29
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
|
30
|
+
</svg>
|
|
31
|
+
</div>
|
|
32
|
+
<h2 class="text-xl font-bold text-[var(--kyro-text-primary)] mb-2">GraphQL Disabled</h2>
|
|
33
|
+
<p class="text-sm text-[var(--kyro-text-secondary)]">The GraphQL API is currently disabled. Enable it in Access Control settings to use the GraphQL playground.</p>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
)}
|
|
18
37
|
</div>
|
|
19
38
|
</AdminLayout>
|
package/src/pages/index.astro
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
import AdminLayout from "../layouts/AdminLayout.astro";
|
|
3
3
|
import { collections } from "../lib/config";
|
|
4
|
-
|
|
5
4
|
const authCollections = ["users", "audit_logs"];
|
|
6
5
|
import { adminPath, apiPath } from "../lib/paths";
|
|
7
6
|
|
|
@@ -238,196 +237,6 @@ const authItems = authCollections.map((slug) => ({
|
|
|
238
237
|
</div>
|
|
239
238
|
</div>
|
|
240
239
|
|
|
241
|
-
<!-- Developer Tools Section -->
|
|
242
|
-
<div class="surface-tile overflow-hidden">
|
|
243
|
-
<div class="p-6 border-b border-[var(--kyro-border)]">
|
|
244
|
-
<h2
|
|
245
|
-
class="text-xl font-bold tracking-tight text-[var(--kyro-text-primary)]"
|
|
246
|
-
>
|
|
247
|
-
Developer Tools
|
|
248
|
-
</h2>
|
|
249
|
-
<p class="text-xs text-[var(--kyro-text-secondary)] font-medium mt-1">
|
|
250
|
-
REST API & GraphQL testing tools
|
|
251
|
-
</p>
|
|
252
|
-
</div>
|
|
253
|
-
|
|
254
|
-
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8 p-8">
|
|
255
|
-
<!-- REST API -->
|
|
256
|
-
<div class="bg-[var(--kyro-surface-accent)] rounded-2xl p-6">
|
|
257
|
-
<div class="flex items-center gap-3 mb-6">
|
|
258
|
-
<div
|
|
259
|
-
class="w-10 h-10 rounded-xl bg-pink-500/10 flex items-center justify-center text-pink-500"
|
|
260
|
-
>
|
|
261
|
-
<svg
|
|
262
|
-
class="w-5 h-5"
|
|
263
|
-
fill="none"
|
|
264
|
-
stroke="currentColor"
|
|
265
|
-
viewBox="0 0 24 24"
|
|
266
|
-
>
|
|
267
|
-
<path
|
|
268
|
-
stroke-linecap="round"
|
|
269
|
-
stroke-linejoin="round"
|
|
270
|
-
stroke-width="2"
|
|
271
|
-
d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
|
|
272
|
-
></path>
|
|
273
|
-
</svg>
|
|
274
|
-
</div>
|
|
275
|
-
<div>
|
|
276
|
-
<h3 class="text-xl font-bold text-[var(--kyro-text-primary)]">
|
|
277
|
-
REST API
|
|
278
|
-
</h3>
|
|
279
|
-
<p class="text-xs text-[var(--kyro-text-secondary)]">
|
|
280
|
-
HTTP endpoints
|
|
281
|
-
</p>
|
|
282
|
-
</div>
|
|
283
|
-
</div>
|
|
284
|
-
<div class="grid grid-cols-1 gap-4">
|
|
285
|
-
<a
|
|
286
|
-
href={`${adminPath}/rest-playground`}
|
|
287
|
-
class="group p-4 bg-[var(--kyro-surface)] rounded-lg hover:bg-[var(--kyro-surface-accent)] transition-all border border-transparent hover:border-[var(--kyro-border)]"
|
|
288
|
-
>
|
|
289
|
-
<div class="flex items-center gap-3 mb-2">
|
|
290
|
-
<div
|
|
291
|
-
class="w-8 h-8 rounded-lg bg-blue-500/10 flex items-center justify-center text-blue-500 group-hover:scale-110 transition-transform"
|
|
292
|
-
>
|
|
293
|
-
<svg
|
|
294
|
-
class="w-4 h-4"
|
|
295
|
-
fill="none"
|
|
296
|
-
stroke="currentColor"
|
|
297
|
-
viewBox="0 0 24 24"
|
|
298
|
-
>
|
|
299
|
-
<path
|
|
300
|
-
stroke-linecap="round"
|
|
301
|
-
stroke-linejoin="round"
|
|
302
|
-
stroke-width="2"
|
|
303
|
-
d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"
|
|
304
|
-
></path>
|
|
305
|
-
</svg>
|
|
306
|
-
</div>
|
|
307
|
-
<h4 class="font-medium text-[var(--kyro-text-primary)]">
|
|
308
|
-
Playground
|
|
309
|
-
</h4>
|
|
310
|
-
</div>
|
|
311
|
-
<p class="text-xs text-[var(--kyro-text-secondary)]">
|
|
312
|
-
Saved requests & history
|
|
313
|
-
</p>
|
|
314
|
-
</a>
|
|
315
|
-
</div>
|
|
316
|
-
<div class="mt-4 pt-4 border-t border-[var(--kyro-border)]">
|
|
317
|
-
<a
|
|
318
|
-
href={`${apiPath}/collections`}
|
|
319
|
-
target="_blank"
|
|
320
|
-
class="flex items-center gap-2 text-xs text-[var(--kyro-text-muted)] hover:text-[var(--kyro-text-secondary)] transition-colors"
|
|
321
|
-
>
|
|
322
|
-
<svg
|
|
323
|
-
class="w-3 h-3"
|
|
324
|
-
fill="none"
|
|
325
|
-
stroke="currentColor"
|
|
326
|
-
viewBox="0 0 24 24"
|
|
327
|
-
>
|
|
328
|
-
<path
|
|
329
|
-
stroke-linecap="round"
|
|
330
|
-
stroke-linejoin="round"
|
|
331
|
-
stroke-width="2"
|
|
332
|
-
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
|
|
333
|
-
></path>
|
|
334
|
-
</svg>
|
|
335
|
-
View Collections JSON
|
|
336
|
-
</a>
|
|
337
|
-
</div>
|
|
338
|
-
</div>
|
|
339
|
-
|
|
340
|
-
<!-- GraphQL -->
|
|
341
|
-
<div class="bg-[var(--kyro-surface-accent)] rounded-2xl p-6">
|
|
342
|
-
<div class="flex items-center gap-3 mb-6">
|
|
343
|
-
<div
|
|
344
|
-
class="w-10 h-10 rounded-xl bg-pink-500/10 flex items-center justify-center text-pink-500"
|
|
345
|
-
>
|
|
346
|
-
<svg
|
|
347
|
-
class="w-5 h-5"
|
|
348
|
-
fill="none"
|
|
349
|
-
stroke="currentColor"
|
|
350
|
-
viewBox="0 0 24 24"
|
|
351
|
-
>
|
|
352
|
-
<path
|
|
353
|
-
stroke-linecap="round"
|
|
354
|
-
stroke-linejoin="round"
|
|
355
|
-
stroke-width="2"
|
|
356
|
-
d="M13 10V3L4 14h7v7l9-11h-7z"></path>
|
|
357
|
-
</svg>
|
|
358
|
-
</div>
|
|
359
|
-
<div>
|
|
360
|
-
<h3 class="text-xl font-bold text-[var(--kyro-text-primary)]">
|
|
361
|
-
GraphQL
|
|
362
|
-
</h3>
|
|
363
|
-
<p class="text-xs text-[var(--kyro-text-secondary)]">
|
|
364
|
-
Query language API
|
|
365
|
-
</p>
|
|
366
|
-
</div>
|
|
367
|
-
</div>
|
|
368
|
-
<div class="grid grid-cols-1 gap-4">
|
|
369
|
-
<a
|
|
370
|
-
href={`${adminPath}/graphql`}
|
|
371
|
-
class="group p-4 bg-[var(--kyro-surface)] rounded-lg hover:bg-[var(--kyro-surface-accent)] transition-all border border-transparent hover:border-[var(--kyro-border)]"
|
|
372
|
-
>
|
|
373
|
-
<div class="flex items-center gap-3 mb-2">
|
|
374
|
-
<div
|
|
375
|
-
class="w-8 h-8 rounded-lg bg-purple-500/10 flex items-center justify-center text-purple-500 group-hover:scale-110 transition-transform"
|
|
376
|
-
>
|
|
377
|
-
<svg
|
|
378
|
-
class="w-4 h-4"
|
|
379
|
-
fill="none"
|
|
380
|
-
stroke="currentColor"
|
|
381
|
-
viewBox="0 0 24 24"
|
|
382
|
-
>
|
|
383
|
-
<path
|
|
384
|
-
stroke-linecap="round"
|
|
385
|
-
stroke-linejoin="round"
|
|
386
|
-
stroke-width="2"
|
|
387
|
-
d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"
|
|
388
|
-
></path>
|
|
389
|
-
<path
|
|
390
|
-
stroke-linecap="round"
|
|
391
|
-
stroke-linejoin="round"
|
|
392
|
-
stroke-width="2"
|
|
393
|
-
d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
|
|
394
|
-
</svg>
|
|
395
|
-
</div>
|
|
396
|
-
<h4 class="font-medium text-[var(--kyro-text-primary)]">
|
|
397
|
-
Explorer
|
|
398
|
-
</h4>
|
|
399
|
-
</div>
|
|
400
|
-
<p class="text-xs text-[var(--kyro-text-secondary)]">
|
|
401
|
-
Write & test queries
|
|
402
|
-
</p>
|
|
403
|
-
</a>
|
|
404
|
-
</div>
|
|
405
|
-
<div class="mt-4 pt-4 border-t border-[var(--kyro-border)]">
|
|
406
|
-
<a
|
|
407
|
-
href={`${apiPath}/graphql`}
|
|
408
|
-
target="_blank"
|
|
409
|
-
class="flex items-center gap-2 text-xs text-[var(--kyro-text-muted)] hover:text-[var(--kyro-text-secondary)] transition-colors"
|
|
410
|
-
>
|
|
411
|
-
<svg
|
|
412
|
-
class="w-3 h-3"
|
|
413
|
-
fill="none"
|
|
414
|
-
stroke="currentColor"
|
|
415
|
-
viewBox="0 0 24 24"
|
|
416
|
-
>
|
|
417
|
-
<path
|
|
418
|
-
stroke-linecap="round"
|
|
419
|
-
stroke-linejoin="round"
|
|
420
|
-
stroke-width="2"
|
|
421
|
-
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
|
|
422
|
-
></path>
|
|
423
|
-
</svg>
|
|
424
|
-
View GraphQL Endpoint
|
|
425
|
-
</a>
|
|
426
|
-
</div>
|
|
427
|
-
</div>
|
|
428
|
-
</div>
|
|
429
|
-
</div>
|
|
430
|
-
|
|
431
240
|
<script is:inline define:vars={{ apiPath, adminPath }}>
|
|
432
241
|
(function () {
|
|
433
242
|
var searchInput = document.getElementById("header-search-input");
|