@kyro-cms/admin 0.1.6 → 0.1.7

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 (163) hide show
  1. package/README.md +149 -51
  2. package/package.json +53 -6
  3. package/src/collections/auth/index.ts +2 -2
  4. package/src/collections/portfolio/index.ts +343 -0
  5. package/src/components/ActionBar.tsx +153 -16
  6. package/src/components/Admin.tsx +136 -27
  7. package/src/components/ApiExplorer.tsx +325 -0
  8. package/src/components/ApiKeysManager.tsx +563 -0
  9. package/src/components/AuditLogsPage.tsx +664 -0
  10. package/src/components/AutoForm.tsx +1417 -661
  11. package/src/components/BrandingHub.tsx +267 -0
  12. package/src/components/BulkActionsBar.tsx +3 -3
  13. package/src/components/CreateView.tsx +3 -3
  14. package/src/components/Dashboard.tsx +393 -0
  15. package/src/components/DetailView.tsx +199 -57
  16. package/src/components/DeveloperCenter.tsx +403 -0
  17. package/src/components/EnhancedListView.tsx +786 -0
  18. package/src/components/GraphQLExplorer.tsx +675 -0
  19. package/src/components/GraphQLPlayground.tsx +627 -0
  20. package/src/components/ListView.tsx +191 -53
  21. package/src/components/MediaGallery.tsx +1569 -0
  22. package/src/components/Modal.tsx +149 -0
  23. package/src/components/RestPlayground.tsx +951 -0
  24. package/src/components/Sidebar.astro +237 -0
  25. package/src/components/UserManagement.tsx +204 -0
  26. package/src/components/VersionHistoryPanel.tsx +3 -3
  27. package/src/components/WebhookManager.tsx +608 -0
  28. package/src/components/blocks/AccordionBlock.tsx +97 -0
  29. package/src/components/blocks/ArrayBlock.tsx +75 -0
  30. package/src/components/blocks/BlockEditModal.MARKER +12 -0
  31. package/src/components/blocks/BlockEditModal.tsx +774 -0
  32. package/src/components/blocks/ButtonBlock.tsx +165 -0
  33. package/src/components/blocks/ChildBlocksTree.tsx +551 -0
  34. package/src/components/blocks/CodeBlock.tsx +66 -0
  35. package/src/components/blocks/ColumnsBlock.tsx +151 -0
  36. package/src/components/blocks/DividerBlock.tsx +43 -0
  37. package/src/components/blocks/FileBlock.tsx +64 -0
  38. package/src/components/blocks/HeadingBlock.tsx +81 -0
  39. package/src/components/blocks/HeroBlock.tsx +157 -0
  40. package/src/components/blocks/ImageBlock.tsx +83 -0
  41. package/src/components/blocks/LinkBlock.tsx +71 -0
  42. package/src/components/blocks/ListBlock.tsx +39 -0
  43. package/src/components/blocks/ParagraphBlock.tsx +61 -0
  44. package/src/components/blocks/RelationshipBlock.tsx +279 -0
  45. package/src/components/blocks/VStackBlock.tsx +75 -0
  46. package/src/components/blocks/VideoBlock.tsx +45 -0
  47. package/src/components/blocks/index.ts +10 -0
  48. package/src/components/fields/BlocksField.tsx +323 -0
  49. package/src/components/fields/CheckboxField.tsx +15 -9
  50. package/src/components/fields/CodeField.tsx +234 -0
  51. package/src/components/fields/DateField.tsx +38 -11
  52. package/src/components/fields/EditorClient.tsx +271 -0
  53. package/src/components/fields/FileField.tsx +390 -0
  54. package/src/components/fields/HybridContentField.tsx +109 -0
  55. package/src/components/fields/ImageField.tsx +429 -0
  56. package/src/components/fields/JSONField.tsx +361 -0
  57. package/src/components/fields/MarkdownField.tsx +282 -0
  58. package/src/components/fields/NumberField.tsx +42 -12
  59. package/src/components/fields/PortableTextField.tsx +143 -0
  60. package/src/components/fields/PortableTextRenderer.tsx +68 -0
  61. package/src/components/fields/RelationshipField.tsx +231 -59
  62. package/src/components/fields/SelectField.tsx +25 -15
  63. package/src/components/fields/TextField.tsx +45 -14
  64. package/src/components/fields/extensions/blockComponents.tsx +237 -0
  65. package/src/components/fields/extensions/blocksStore.ts +273 -0
  66. package/src/components/fields/index.ts +13 -0
  67. package/src/components/index.ts +1 -2
  68. package/src/components/layout/Header.tsx +2 -2
  69. package/src/components/layout/Layout.tsx +2 -2
  70. package/src/components/ui/Badge.tsx +9 -4
  71. package/src/components/ui/BlockDrawer.tsx +79 -0
  72. package/src/components/ui/Button.tsx +1 -1
  73. package/src/components/ui/CommandPalette.tsx +362 -0
  74. package/src/components/ui/CommandPaletteWrapper.tsx +97 -0
  75. package/src/components/ui/Dropdown.tsx +1 -1
  76. package/src/components/ui/Modal.tsx +37 -12
  77. package/src/components/ui/PromptModal.tsx +94 -0
  78. package/src/components/ui/SlidePanel.tsx +43 -16
  79. package/src/components/ui/Toast.tsx +80 -14
  80. package/src/env.d.ts +16 -0
  81. package/src/env.ts +20 -0
  82. package/src/index.ts +0 -1
  83. package/src/layouts/AdminLayout.astro +164 -170
  84. package/src/layouts/AuthLayout.astro +23 -6
  85. package/src/lib/MediaService.ts +541 -0
  86. package/src/lib/auth/sqlite-adapter.ts +319 -0
  87. package/src/lib/config.ts +22 -6
  88. package/src/lib/dataStore.ts +132 -74
  89. package/src/lib/db/adapter.ts +54 -0
  90. package/src/lib/db/drizzle-mysql-adapter.ts +194 -0
  91. package/src/lib/db/drizzle-mysql-auth-adapter.ts +327 -0
  92. package/src/lib/db/drizzle-postgres-adapter.ts +202 -0
  93. package/src/lib/db/drizzle-postgres-auth-adapter.ts +304 -0
  94. package/src/lib/db/drizzle-sqlite-adapter.ts +227 -0
  95. package/src/lib/db/drizzle-sqlite-auth-adapter.ts +548 -0
  96. package/src/lib/db/index.ts +449 -0
  97. package/src/lib/db/mongodb-adapter.ts +207 -0
  98. package/src/lib/db/mongodb-auth-adapter.ts +305 -0
  99. package/src/lib/db/schema/mysql-auth.ts +113 -0
  100. package/src/lib/db/schema/mysql-content.ts +20 -0
  101. package/src/lib/db/schema/postgres-auth.ts +116 -0
  102. package/src/lib/db/schema/postgres-content.ts +35 -0
  103. package/src/lib/db/schema/postgres-media.ts +52 -0
  104. package/src/lib/db/schema/postgres-settings.ts +11 -0
  105. package/src/lib/db/schema/sqlite-auth.ts +112 -0
  106. package/src/lib/db/schema/sqlite-content.ts +20 -0
  107. package/src/lib/graphql/index.ts +1 -0
  108. package/src/lib/graphql/schema.ts +443 -0
  109. package/src/lib/rate-limit.ts +267 -0
  110. package/src/lib/storage.ts +374 -0
  111. package/src/lib/store.ts +85 -0
  112. package/src/middleware.ts +70 -11
  113. package/src/pages/[collection]/[id].astro +178 -122
  114. package/src/pages/[collection]/index.astro +24 -156
  115. package/src/pages/admin/api-explorer.astro +98 -0
  116. package/src/pages/admin/graphql-explorer.astro +40 -0
  117. package/src/pages/admin/graphql.astro +97 -0
  118. package/src/pages/admin/index.astro +200 -139
  119. package/src/pages/admin/keys.astro +8 -0
  120. package/src/pages/admin/rest-playground.astro +44 -0
  121. package/src/pages/admin/webhooks.astro +8 -0
  122. package/src/pages/api/[collection]/[id]/publish.ts +44 -0
  123. package/src/pages/api/[collection]/[id]/unpublish.ts +42 -0
  124. package/src/pages/api/[collection]/[id]/versions.ts +36 -0
  125. package/src/pages/api/[collection]/[id].ts +102 -159
  126. package/src/pages/api/[collection]/index.ts +151 -230
  127. package/src/pages/api/auth/[id].ts +48 -69
  128. package/src/pages/api/auth/audit-logs.ts +20 -43
  129. package/src/pages/api/auth/login.ts +159 -45
  130. package/src/pages/api/auth/logout.ts +42 -24
  131. package/src/pages/api/auth/refresh.ts +119 -0
  132. package/src/pages/api/auth/register.ts +110 -40
  133. package/src/pages/api/auth/users.ts +22 -97
  134. package/src/pages/api/collections.ts +59 -0
  135. package/src/pages/api/globals/[slug]/test.ts +172 -0
  136. package/src/pages/api/globals/[slug].ts +42 -0
  137. package/src/pages/api/graphql.ts +90 -0
  138. package/src/pages/api/health.ts +417 -40
  139. package/src/pages/api/keys/[id].ts +26 -0
  140. package/src/pages/api/keys/index.ts +75 -0
  141. package/src/pages/api/media/[id].ts +309 -0
  142. package/src/pages/api/media/folders.ts +609 -0
  143. package/src/pages/api/media/index.ts +146 -0
  144. package/src/pages/api/media/resize.ts +267 -0
  145. package/src/pages/api/search.ts +82 -0
  146. package/src/pages/api/slug-availability.ts +70 -0
  147. package/src/pages/api/storage-config.ts +20 -0
  148. package/src/pages/api/storage-status.ts +206 -0
  149. package/src/pages/api/upload.ts +334 -0
  150. package/src/pages/api/webhooks/index.ts +71 -0
  151. package/src/pages/audit/index.astro +2 -104
  152. package/src/pages/login.astro +11 -11
  153. package/src/pages/media.astro +10 -0
  154. package/src/pages/preview/[collection]/[id].astro +178 -0
  155. package/src/pages/register.astro +13 -13
  156. package/src/pages/roles/index.astro +21 -21
  157. package/src/pages/settings/[slug].astro +162 -0
  158. package/src/pages/settings/index.astro +9 -0
  159. package/src/pages/users/[id].astro +29 -21
  160. package/src/pages/users/index.astro +22 -17
  161. package/src/pages/users/new.astro +18 -17
  162. package/src/styles/main.css +553 -128
  163. package/src/components/layout/Sidebar.tsx +0 -497
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  import "../styles/main.css";
3
- import { getRegistry } from "@kyro-cms/core";
4
-
5
- const registry = getRegistry();
6
- const collections = registry.getCollections();
3
+ import { nonAuthCollections, collections, globals } from "@/lib/config";
4
+ import { CommandPaletteWrapper } from "@/components/ui/CommandPaletteWrapper";
5
+ import Sidebar from "../components/Sidebar.astro";
7
6
 
8
7
  interface Props {
9
8
  title: string;
10
9
  }
11
10
 
12
11
  const { title } = Astro.props;
12
+ const user = Astro.locals.user;
13
13
  ---
14
14
 
15
15
  <!doctype html>
@@ -19,179 +19,173 @@ const { title } = Astro.props;
19
19
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
20
20
  <title>{title} - Kyro CMS</title>
21
21
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
22
+ <script is:inline>
23
+ (() => {
24
+ const getTheme = () => {
25
+ const stored = localStorage.getItem("theme");
26
+ if (stored) return stored;
27
+ return window.matchMedia("(prefers-color-scheme: dark)").matches
28
+ ? "dark"
29
+ : "light";
30
+ };
31
+ const theme = getTheme();
32
+ if (theme === "dark") {
33
+ document.documentElement.classList.add("dark");
34
+ } else {
35
+ document.documentElement.classList.remove("dark");
36
+ }
37
+ })();
38
+ </script>
22
39
  </head>
23
- <body class="bg-[#eaeff2] antialiased text-[#0b1222]">
40
+ <body class="bg-[var(--kyro-bg)] antialiased text-[var(--kyro-text-primary)]">
24
41
  <div class="flex h-screen p-6 gap-6 overflow-hidden">
25
- <!-- Sidebar Tile -->
26
- <aside
27
- class="surface-tile w-[320px] flex flex-col flex-shrink-0 overflow-hidden"
28
- >
29
- <!-- Logo Section -->
30
- <div class="px-4 py-8">
31
- <div class="flex items-center gap-4">
32
- <span class="text-3xl font-black tracking-tighter text-[#0b1222]"
33
- >KYRO.</span
34
- >
35
- </div>
36
- </div>
37
-
38
- <!-- Navigation -->
39
- <nav class="flex-1 px-4 overflow-y-auto">
40
- <div class="space-y-2 mt-4">
41
- <a
42
- href="/"
43
- class={`flex items-center gap-4 px-6 py-4 rounded-2xl transition-all font-bold ${
44
- title === "Dashboard"
45
- ? "bg-[#0b1222] text-white shadow-lg"
46
- : "text-[#64748b] hover:bg-gray-50 hover:text-[#0b1222]"
47
- }`}
48
- >
49
- <svg
50
- class="w-5 h-5"
51
- fill="none"
52
- stroke="currentColor"
53
- viewBox="0 0 24 24"
54
- ><path
55
- stroke-linecap="round"
56
- stroke-linejoin="round"
57
- stroke-width="2.5"
58
- d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"
59
- ></path></svg
60
- >
61
- <span>Dashboard</span>
62
- </a>
63
-
64
- <div class="pt-8 pb-4">
65
- <p
66
- class="px-6 text-[10px] font-black text-[#64748b] uppercase tracking-[0.3em] opacity-50"
67
- >
68
- Authentication
69
- </p>
70
- </div>
71
-
72
- <a
73
- href="/users"
74
- class={`flex items-center gap-4 px-6 py-4 rounded-2xl transition-all font-bold ${
75
- title === "Users"
76
- ? "bg-[#0b1222] text-white shadow-lg"
77
- : "text-[#64748b] hover:bg-gray-50 hover:text-[#0b1222]"
78
- }`}
79
- >
80
- <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
81
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z"></path>
82
- </svg>
83
- <span>Users</span>
84
- </a>
85
-
86
- <a
87
- href="/roles"
88
- class={`flex items-center gap-4 px-6 py-4 rounded-2xl transition-all font-bold ${
89
- title === "Roles"
90
- ? "bg-[#0b1222] text-white shadow-lg"
91
- : "text-[#64748b] hover:bg-gray-50 hover:text-[#0b1222]"
92
- }`}
93
- >
94
- <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
95
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"></path>
96
- </svg>
97
- <span>Roles</span>
98
- </a>
99
-
100
- <a
101
- href="/audit"
102
- class={`flex items-center gap-4 px-6 py-4 rounded-2xl transition-all font-bold ${
103
- title === "Audit Logs"
104
- ? "bg-[#0b1222] text-white shadow-lg"
105
- : "text-[#64748b] hover:bg-gray-50 hover:text-[#0b1222]"
106
- }`}
107
- >
108
- <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
109
- <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path>
110
- </svg>
111
- <span>Audit Logs</span>
112
- </a>
113
-
114
- <div class="pt-8 pb-4">
115
- <p
116
- class="px-6 text-[10px] font-black text-[#64748b] uppercase tracking-[0.3em] opacity-50"
117
- >
118
- Collections
119
- </p>
120
- </div>
121
-
122
- {
123
- collections.map((col) => (
124
- <a
125
- href={`/${col.slug}`}
126
- class="flex items-center gap-4 px-6 py-4 rounded-2xl text-[#64748b] font-bold transition-all hover:bg-gray-50 hover:text-[#0b1222] group"
127
- >
128
- <div class="w-2 h-2 rounded-full bg-gray-200 group-hover:bg-[#0b1222] transition-colors" />
129
- <span>{col.label}</span>
130
- </a>
131
- ))
132
- }
133
-
134
- <div class="pt-8 pb-4">
135
- <p
136
- class="px-6 text-[10px] font-black text-[#64748b] uppercase tracking-[0.3em] opacity-50"
137
- >
138
- System
139
- </p>
140
- </div>
141
-
142
- <a
143
- href="/admin"
144
- class="flex items-center gap-4 px-6 py-4 rounded-2xl text-[#64748b] font-bold transition-all hover:bg-gray-50 hover:text-[#0b1222]"
145
- >
146
- <svg
147
- class="w-5 h-5"
148
- fill="none"
149
- stroke="currentColor"
150
- viewBox="0 0 24 24"
151
- ><path
152
- stroke-linecap="round"
153
- stroke-linejoin="round"
154
- stroke-width="2.5"
155
- d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"
156
- ></path><path
157
- stroke-linecap="round"
158
- stroke-linejoin="round"
159
- stroke-width="2.5"
160
- d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path></svg
161
- >
162
- <span>Settings</span>
163
- </a>
164
- </div>
165
- </nav>
166
-
167
- <!-- User Section Tile-Adjacent -->
168
- <div class="p-10 mt-auto bg-gray-50/50">
169
- <div class="flex flex-col items-center text-center">
170
- <div class="relative mb-5">
171
- <img
172
- src="https://images.unsplash.com/photo-1494790108377-be9c29b29330?q=80&w=150&auto=format&fit=crop"
173
- class="w-20 h-20 rounded-full object-cover border-4 border-white shadow-md"
174
- alt="Emily Jonson"
175
- />
176
- <div
177
- class="absolute bottom-1 right-1 w-5 h-5 bg-orange-400 border-4 border-white rounded-full"
178
- >
179
- </div>
180
- </div>
181
- <h3 class="font-black text-lg text-[#0b1222] tracking-tight">
182
- Emily Jonson
183
- </h3>
184
- <p class="text-xs text-[#64748b] mt-1 uppercase tracking-widest">
185
- jonson@bress.com
186
- </p>
187
- </div>
188
- </div>
189
- </aside>
42
+ <Sidebar title={title} user={user} />
190
43
 
191
44
  <!-- Main Content Column -->
192
45
  <main class="flex-1 flex flex-col gap-6 overflow-hidden">
193
46
  <slot />
194
47
  </main>
195
48
  </div>
49
+
50
+ <!-- Logout Confirmation Modal -->
51
+ <div
52
+ id="logout-modal"
53
+ class="fixed inset-0 z-50 hidden flex items-center justify-center"
54
+ >
55
+ <div
56
+ class="absolute inset-0 bg-black/50 backdrop-blur-sm"
57
+ id="logout-modal-backdrop"
58
+ >
59
+ </div>
60
+ <div
61
+ class="relative w-full max-w-sm mx-4 bg-[var(--kyro-surface)] rounded-lg shadow-2xl border border-[var(--kyro-border)]"
62
+ >
63
+ <div
64
+ class="flex items-center justify-between px-6 py-4 border-b border-[var(--kyro-border)]"
65
+ >
66
+ <h2 class="text-lg font-semibold text-[var(--kyro-text-primary)]">
67
+ Sign Out
68
+ </h2>
69
+ </div>
70
+ <div class="px-6 py-4">
71
+ <p class="text-[var(--kyro-text-secondary)]">
72
+ Are you sure you want to sign out?
73
+ </p>
74
+ </div>
75
+ <div
76
+ class="flex items-center justify-end gap-3 px-6 py-4 border-t border-[var(--kyro-border)] bg-[var(--kyro-surface-accent)] rounded-b-lg"
77
+ >
78
+ <button
79
+ id="logout-modal-cancel"
80
+ class="px-4 py-2 rounded-lg font-medium text-sm border border-[var(--kyro-border)] text-[var(--kyro-text-secondary)] hover:bg-[var(--kyro-surface-accent)] hover:text-[var(--kyro-text-primary)] transition-colors"
81
+ >Cancel</button
82
+ >
83
+ <button
84
+ id="logout-modal-confirm"
85
+ class="px-4 py-2 rounded-lg font-medium text-sm bg-red-500 text-white hover:bg-red-600 transition-colors"
86
+ >Sign Out</button
87
+ >
88
+ </div>
89
+ </div>
90
+ </div>
91
+
92
+ <!-- Command Palette (React) -->
93
+ <CommandPaletteWrapper
94
+ client:only="react"
95
+ collections={collections}
96
+ globals={globals}
97
+ />
98
+
99
+ <!-- Theme UI Logic -->
100
+ <script is:inline>
101
+ const lightBtn = document.getElementById("theme-light-btn");
102
+ const darkBtn = document.getElementById("theme-dark-btn");
103
+
104
+ const updateUI = (theme) => {
105
+ const isDark = theme === "dark";
106
+
107
+ // Update Buttons
108
+ if (isDark) {
109
+ darkBtn?.classList.add(
110
+ "bg-[var(--kyro-sidebar-active)]",
111
+ "text-[var(--kyro-sidebar-text-active)]",
112
+ "shadow-lg",
113
+ );
114
+ darkBtn?.classList.remove("text-[var(--kyro-text-secondary)]");
115
+ lightBtn?.classList.remove(
116
+ "bg-[var(--kyro-sidebar-active)]",
117
+ "text-[var(--kyro-sidebar-text-active)]",
118
+ "shadow-lg",
119
+ );
120
+ lightBtn?.classList.add("text-[var(--kyro-text-secondary)]");
121
+ } else {
122
+ lightBtn?.classList.add(
123
+ "bg-[var(--kyro-sidebar-active)]",
124
+ "text-[var(--kyro-sidebar-text-active)]",
125
+ "shadow-lg",
126
+ );
127
+ lightBtn?.classList.remove("text-[var(--kyro-text-secondary)]");
128
+ darkBtn?.classList.remove(
129
+ "bg-[var(--kyro-sidebar-active)]",
130
+ "text-[var(--kyro-sidebar-text-active)]",
131
+ "shadow-lg",
132
+ );
133
+ darkBtn?.classList.add("text-[var(--kyro-text-secondary)]");
134
+ }
135
+
136
+ // Update Document
137
+ if (isDark) {
138
+ document.documentElement.classList.add("dark");
139
+ } else {
140
+ document.documentElement.classList.remove("dark");
141
+ }
142
+ };
143
+
144
+ // Initial Sync
145
+ const currentTheme =
146
+ localStorage.getItem("theme") ||
147
+ (window.matchMedia("(prefers-color-scheme: dark)").matches
148
+ ? "dark"
149
+ : "light");
150
+ updateUI(currentTheme);
151
+
152
+ // Listeners
153
+ lightBtn?.addEventListener("click", () => {
154
+ localStorage.setItem("theme", "light");
155
+ updateUI("light");
156
+ });
157
+
158
+ darkBtn?.addEventListener("click", () => {
159
+ localStorage.setItem("theme", "dark");
160
+ updateUI("dark");
161
+ });
162
+
163
+ // System Preference Listener
164
+ window
165
+ .matchMedia("(prefers-color-scheme: dark)")
166
+ .addEventListener("change", (e) => {
167
+ if (!localStorage.getItem("theme")) {
168
+ updateUI(e.matches ? "dark" : "light");
169
+ }
170
+ });
171
+
172
+ // Logout Modal
173
+ const logoutModal = document.getElementById("logout-modal");
174
+ const logoutBackdrop = document.getElementById("logout-modal-backdrop");
175
+ const logoutCancel = document.getElementById("logout-modal-cancel");
176
+ const logoutConfirm = document.getElementById("logout-modal-confirm");
177
+
178
+ document.getElementById("logout-btn")?.addEventListener("click", () => {
179
+ logoutModal?.classList.remove("hidden");
180
+ });
181
+
182
+ const closeLogoutModal = () => logoutModal?.classList.add("hidden");
183
+
184
+ logoutBackdrop?.addEventListener("click", closeLogoutModal);
185
+ logoutCancel?.addEventListener("click", closeLogoutModal);
186
+ logoutConfirm?.addEventListener("click", () => {
187
+ window.location.href = "/login";
188
+ });
189
+ </script>
196
190
  </body>
197
191
  </html>
@@ -15,18 +15,35 @@ const { title } = Astro.props;
15
15
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
16
16
  <title>{title} - Kyro CMS</title>
17
17
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
18
+ <script is:inline>
19
+ (() => {
20
+ const getTheme = () => {
21
+ const stored = localStorage.getItem("theme");
22
+ if (stored) return stored;
23
+ return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
24
+ };
25
+ const theme = getTheme();
26
+ if (theme === "dark") {
27
+ document.documentElement.classList.add("dark");
28
+ } else {
29
+ document.documentElement.classList.remove("dark");
30
+ }
31
+ })();
32
+ </script>
18
33
  </head>
19
- <body class="bg-[#eaeff2] antialiased text-[#0b1222]">
20
- <div class="min-h-screen flex items-center justify-center p-6">
21
- <div class="w-full">
34
+ <body class="bg-[var(--kyro-bg)] antialiased text-[var(--kyro-text-primary)]">
35
+ <div class="min-h-screen flex flex-col items-center justify-center p-6">
36
+ <div class="w-full max-w-md flex flex-col items-center">
22
37
  <!-- Logo -->
23
- <div class="text-center mb-8">
38
+ <div class="w-full text-center mb-8">
24
39
  <a href="/" class="inline-block">
25
- <span class="text-4xl font-black tracking-tighter text-[#0b1222]">KYRO.</span>
40
+ <span class="text-4xl font-black tracking-tighter text-[var(--kyro-text-primary)]">KYRO.</span>
26
41
  </a>
27
42
  </div>
28
43
 
29
- <slot />
44
+ <div class="w-full flex justify-center">
45
+ <slot />
46
+ </div>
30
47
  </div>
31
48
  </div>
32
49
  </body>