@kyro-cms/admin 0.3.1 → 0.3.4

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 (242) hide show
  1. package/dist/EditorClient-XEUOVAAC.js +466 -0
  2. package/dist/EditorClient-XEUOVAAC.js.map +1 -0
  3. package/dist/EditorClient-YLCGVDXY.cjs +468 -0
  4. package/dist/EditorClient-YLCGVDXY.cjs.map +1 -0
  5. package/dist/chunk-7KPIUCGT.js +384 -0
  6. package/dist/chunk-7KPIUCGT.js.map +1 -0
  7. package/dist/chunk-GOACG6R7.cjs +473 -0
  8. package/dist/chunk-GOACG6R7.cjs.map +1 -0
  9. package/dist/index.cjs +14861 -0
  10. package/dist/index.cjs.map +1 -0
  11. package/dist/index.css +1661 -0
  12. package/dist/index.css.map +1 -0
  13. package/dist/index.d.ts +563 -0
  14. package/dist/index.js +14784 -0
  15. package/dist/index.js.map +1 -0
  16. package/package.json +19 -19
  17. package/src/components/ActionBar.tsx +7 -43
  18. package/src/components/Admin.tsx +138 -277
  19. package/src/components/ApiKeysManager.tsx +428 -419
  20. package/src/components/AuditLogsPage.tsx +35 -39
  21. package/src/components/AuthBridge.tsx +51 -0
  22. package/src/components/AutoForm.tsx +495 -1230
  23. package/src/components/BrandingHub.tsx +18 -19
  24. package/src/components/BulkActionsBar.tsx +1 -1
  25. package/src/components/CreateView.tsx +22 -36
  26. package/src/components/Dashboard.tsx +60 -84
  27. package/src/components/DetailView.tsx +113 -91
  28. package/src/components/DeveloperCenter.tsx +200 -198
  29. package/src/components/FieldRenderer.tsx +206 -0
  30. package/src/components/GraphQLPlayground.tsx +340 -480
  31. package/src/components/ListView.tsx +828 -254
  32. package/src/components/LoginPage.tsx +3 -4
  33. package/src/components/MarketplaceManager.tsx +254 -0
  34. package/src/components/MediaGallery.tsx +856 -1192
  35. package/src/components/PluginsManager.tsx +277 -0
  36. package/src/components/RestPlayground.tsx +398 -560
  37. package/src/components/SessionsManager.tsx +211 -0
  38. package/src/components/Sidebar.astro +179 -151
  39. package/src/components/ThemeProvider.tsx +7 -161
  40. package/src/components/UserManagement.tsx +162 -146
  41. package/src/components/UserMenu.tsx +110 -0
  42. package/src/components/WebhookManager.tsx +305 -367
  43. package/src/components/blocks/AccordionBlock.tsx +4 -4
  44. package/src/components/blocks/ArrayBlock.tsx +3 -3
  45. package/src/components/blocks/BlockEditModal.tsx +8 -8
  46. package/src/components/blocks/BlockWrapper.tsx +61 -0
  47. package/src/components/blocks/ButtonBlock.tsx +4 -4
  48. package/src/components/blocks/ChildBlocksTree.tsx +23 -25
  49. package/src/components/blocks/CodeBlock.tsx +15 -15
  50. package/src/components/blocks/ColumnsBlock.tsx +6 -44
  51. package/src/components/blocks/DividerBlock.tsx +3 -3
  52. package/src/components/blocks/FileBlock.tsx +4 -4
  53. package/src/components/blocks/HeadingBlock.tsx +6 -38
  54. package/src/components/blocks/HeroBlock.tsx +4 -4
  55. package/src/components/blocks/ImageBlock.tsx +4 -4
  56. package/src/components/blocks/LinkBlock.tsx +4 -4
  57. package/src/components/blocks/ListBlock.tsx +3 -3
  58. package/src/components/blocks/ParagraphBlock.tsx +12 -42
  59. package/src/components/blocks/RelationshipBlock.tsx +4 -4
  60. package/src/components/blocks/RichTextBlock.tsx +4 -4
  61. package/src/components/blocks/VStackBlock.tsx +5 -37
  62. package/src/components/blocks/VideoBlock.tsx +4 -4
  63. package/src/components/blocks/types.ts +11 -0
  64. package/src/components/fields/AccordionField.tsx +1 -1
  65. package/src/components/fields/ArrayField.tsx +2 -2
  66. package/src/components/fields/ArrayLayout.tsx +93 -0
  67. package/src/components/fields/BlocksField.tsx +122 -111
  68. package/src/components/fields/ButtonField.tsx +1 -1
  69. package/src/components/fields/CheckboxField.tsx +14 -15
  70. package/src/components/fields/ChildrenField.tsx +2 -2
  71. package/src/components/fields/CodeField.tsx +3 -3
  72. package/src/components/fields/ColumnsField.tsx +2 -2
  73. package/src/components/fields/DateField.tsx +13 -26
  74. package/src/components/fields/EditorClient.tsx +26 -28
  75. package/src/components/fields/FieldLayout.tsx +52 -0
  76. package/src/components/fields/GroupLayout.tsx +35 -0
  77. package/src/components/fields/JSONField.tsx +7 -7
  78. package/src/components/fields/LinkField.tsx +1 -1
  79. package/src/components/fields/MarkdownField.tsx +1 -1
  80. package/src/components/fields/NumberField.tsx +13 -26
  81. package/src/components/fields/PortableTextField.tsx +4 -4
  82. package/src/components/fields/PortableTextRenderer.tsx +1 -1
  83. package/src/components/fields/RelationshipBlockField.tsx +31 -23
  84. package/src/components/fields/RelationshipField.tsx +14 -14
  85. package/src/components/fields/SelectField.tsx +17 -26
  86. package/src/components/fields/TabsLayout.tsx +69 -0
  87. package/src/components/fields/TextField.tsx +85 -38
  88. package/src/components/fields/UploadField.tsx +71 -41
  89. package/src/components/fields/VideoField.tsx +1 -1
  90. package/src/components/fields/extensions/blockComponents.tsx +2 -2
  91. package/src/components/fields/extensions/blocksStore.ts +207 -193
  92. package/src/components/fields/types.ts +22 -0
  93. package/src/components/layout/Layout.tsx +1 -1
  94. package/src/components/ui/ActionMenu.tsx +63 -0
  95. package/src/components/ui/Badge.tsx +59 -5
  96. package/src/components/ui/BlockDrawer.tsx +4 -5
  97. package/src/components/ui/CommandPalette.tsx +58 -36
  98. package/src/components/ui/CommandPaletteWrapper.tsx +18 -17
  99. package/src/components/ui/Dropdown.tsx +18 -16
  100. package/src/components/ui/EmptyState.tsx +25 -0
  101. package/src/components/ui/GlobalModal.tsx +49 -0
  102. package/src/components/ui/IconButton.tsx +44 -0
  103. package/src/components/ui/Modal.tsx +19 -20
  104. package/src/components/ui/PageHeader.tsx +158 -0
  105. package/src/components/ui/Pagination.tsx +61 -0
  106. package/src/components/ui/PromptModal.tsx +1 -1
  107. package/src/components/ui/SearchInput.tsx +57 -0
  108. package/src/components/ui/SeoPreview.tsx +31 -0
  109. package/src/components/ui/SessionModal.tsx +0 -0
  110. package/src/components/ui/SlidePanel.tsx +2 -0
  111. package/src/components/ui/Toast.tsx +65 -122
  112. package/src/components/ui/Toaster.tsx +18 -0
  113. package/src/components/ui/icons.tsx +112 -0
  114. package/src/components/users/UserDetail.tsx +290 -0
  115. package/src/components/users/UserForm.tsx +242 -0
  116. package/src/components/users/UsersList.tsx +338 -0
  117. package/src/env.d.ts +13 -13
  118. package/src/fields/index.ts +2 -1
  119. package/src/global.d.ts +7 -0
  120. package/src/hooks/data.ts +2 -9
  121. package/src/hooks/useAsyncData.ts +36 -0
  122. package/src/hooks/useAutoFormState.ts +527 -0
  123. package/src/hooks/useSelection.ts +49 -0
  124. package/src/hooks/useSession.ts +0 -0
  125. package/src/index.ts +11 -1
  126. package/src/integration.ts +86 -11
  127. package/src/kyro-cms.d.ts +209 -0
  128. package/src/layouts/AdminLayout.astro +128 -11
  129. package/src/layouts/AuthLayout.astro +21 -5
  130. package/src/lib/api.ts +175 -55
  131. package/src/lib/autoform-store.ts +435 -0
  132. package/src/lib/config.ts +82 -34
  133. package/src/lib/createRegistry.ts +29 -0
  134. package/src/lib/default-kyro-config.ts +4 -0
  135. package/src/lib/globals.ts +50 -0
  136. package/src/lib/media-utils.ts +18 -0
  137. package/src/lib/object-utils.ts +77 -0
  138. package/src/lib/paths.ts +61 -0
  139. package/src/lib/stores/index.ts +370 -0
  140. package/src/lib/types.ts +43 -0
  141. package/src/lib/useResourceManager.ts +105 -0
  142. package/src/pages/403.astro +67 -0
  143. package/src/pages/[collection]/[id].astro +14 -180
  144. package/src/pages/[collection]/index.astro +11 -6
  145. package/src/pages/api-explorer.astro +173 -0
  146. package/src/pages/audit/index.astro +2 -0
  147. package/src/pages/auth/login.astro +122 -0
  148. package/src/pages/auth/register.astro +167 -0
  149. package/src/pages/graphql-explorer.astro +59 -0
  150. package/src/pages/{admin/graphql.astro → graphql.astro} +51 -17
  151. package/src/pages/index.astro +577 -0
  152. package/src/pages/index_ALT.astro +3 -0
  153. package/src/pages/keys.astro +11 -0
  154. package/src/pages/marketplace.astro +11 -0
  155. package/src/pages/media.astro +3 -0
  156. package/src/pages/plugins.astro +8 -0
  157. package/src/pages/preview/[collection]/[id].astro +188 -123
  158. package/src/pages/rest-playground.astro +62 -0
  159. package/src/pages/roles/index.astro +183 -76
  160. package/src/pages/sessions.astro +8 -0
  161. package/src/pages/settings/[slug].astro +92 -114
  162. package/src/pages/settings/index.astro +5 -3
  163. package/src/pages/users/[id].astro +25 -154
  164. package/src/pages/users/index.astro +19 -130
  165. package/src/pages/users/new.astro +9 -86
  166. package/src/pages/webhooks.astro +11 -0
  167. package/src/routes.ts +80 -0
  168. package/src/styles/main.css +119 -79
  169. package/src/theme/tokens.ts +1 -0
  170. package/src/vite-env.d.ts +14 -0
  171. package/src/collections/auth/index.ts +0 -155
  172. package/src/collections/portfolio/index.ts +0 -343
  173. package/src/components/ApiExplorer.tsx +0 -325
  174. package/src/components/EnhancedListView.tsx +0 -889
  175. package/src/components/GraphQLExplorer.tsx +0 -675
  176. package/src/components/Icons.tsx +0 -23
  177. package/src/components/StatusBadge.tsx +0 -76
  178. package/src/lib/MediaService.ts +0 -541
  179. package/src/lib/auth/sqlite-adapter.ts +0 -319
  180. package/src/lib/dataStore.ts +0 -226
  181. package/src/lib/db/adapter.ts +0 -54
  182. package/src/lib/db/drizzle-mysql-adapter.ts +0 -194
  183. package/src/lib/db/drizzle-mysql-auth-adapter.ts +0 -327
  184. package/src/lib/db/drizzle-postgres-adapter.ts +0 -202
  185. package/src/lib/db/drizzle-postgres-auth-adapter.ts +0 -304
  186. package/src/lib/db/drizzle-sqlite-adapter.ts +0 -227
  187. package/src/lib/db/drizzle-sqlite-auth-adapter.ts +0 -548
  188. package/src/lib/db/index.ts +0 -449
  189. package/src/lib/db/mongodb-adapter.ts +0 -207
  190. package/src/lib/db/mongodb-auth-adapter.ts +0 -305
  191. package/src/lib/db/schema/mysql-auth.ts +0 -113
  192. package/src/lib/db/schema/mysql-content.ts +0 -20
  193. package/src/lib/db/schema/postgres-auth.ts +0 -116
  194. package/src/lib/db/schema/postgres-content.ts +0 -35
  195. package/src/lib/db/schema/postgres-media.ts +0 -52
  196. package/src/lib/db/schema/postgres-settings.ts +0 -11
  197. package/src/lib/db/schema/sqlite-auth.ts +0 -112
  198. package/src/lib/db/schema/sqlite-content.ts +0 -20
  199. package/src/lib/db/version-adapter.ts +0 -248
  200. package/src/lib/graphql/index.ts +0 -1
  201. package/src/lib/graphql/schema.ts +0 -443
  202. package/src/lib/rate-limit.ts +0 -267
  203. package/src/lib/storage.ts +0 -374
  204. package/src/lib/store.ts +0 -85
  205. package/src/middleware.ts +0 -177
  206. package/src/pages/admin/api-explorer.astro +0 -98
  207. package/src/pages/admin/graphql-explorer.astro +0 -40
  208. package/src/pages/admin/index.astro +0 -286
  209. package/src/pages/admin/keys.astro +0 -8
  210. package/src/pages/admin/rest-playground.astro +0 -44
  211. package/src/pages/admin/webhooks.astro +0 -8
  212. package/src/pages/api/[collection]/[id]/publish.ts +0 -52
  213. package/src/pages/api/[collection]/[id]/unpublish.ts +0 -42
  214. package/src/pages/api/[collection]/[id]/versions.ts +0 -66
  215. package/src/pages/api/[collection]/[id].ts +0 -213
  216. package/src/pages/api/[collection]/index.ts +0 -209
  217. package/src/pages/api/auth/[id].ts +0 -121
  218. package/src/pages/api/auth/audit-logs.ts +0 -57
  219. package/src/pages/api/auth/login.ts +0 -211
  220. package/src/pages/api/auth/logout.ts +0 -66
  221. package/src/pages/api/auth/me.ts +0 -36
  222. package/src/pages/api/auth/refresh.ts +0 -119
  223. package/src/pages/api/auth/register.ts +0 -188
  224. package/src/pages/api/auth/users.ts +0 -97
  225. package/src/pages/api/collections.ts +0 -59
  226. package/src/pages/api/globals/[slug].ts +0 -42
  227. package/src/pages/api/graphql.ts +0 -90
  228. package/src/pages/api/health.ts +0 -426
  229. package/src/pages/api/keys/[id].ts +0 -26
  230. package/src/pages/api/keys/index.ts +0 -75
  231. package/src/pages/api/media/[id].ts +0 -309
  232. package/src/pages/api/media/folders.ts +0 -609
  233. package/src/pages/api/media/index.ts +0 -146
  234. package/src/pages/api/media/resize.ts +0 -267
  235. package/src/pages/api/search.ts +0 -82
  236. package/src/pages/api/slug-availability.ts +0 -70
  237. package/src/pages/api/storage-config.ts +0 -20
  238. package/src/pages/api/storage-status.ts +0 -206
  239. package/src/pages/api/upload.ts +0 -334
  240. package/src/pages/api/webhooks/index.ts +0 -71
  241. package/src/pages/login.astro +0 -82
  242. package/src/pages/register.astro +0 -102
@@ -0,0 +1,466 @@
1
+ import { LuUndo, LuRedo, LuLink, LuX, LuExternalLink, LuChevronDown, LuList, LuListOrdered, LuBold, LuItalic, LuUnderline, LuStrikethrough, LuCode } from './chunk-7KPIUCGT.js';
2
+ import { useState, useRef, useEffect, useCallback } from 'react';
3
+ import { EditorProvider, PortableTextEditable, useEditor } from '@portabletext/editor';
4
+ import { defineSchema } from '@portabletext/schema';
5
+ import { EventListenerPlugin } from '@portabletext/editor/plugins';
6
+ import { useHistoryButtons, useToolbarSchema, useDecoratorButton, useListButton, useAnnotationButton, useAnnotationPopover, useStyleSelector } from '@portabletext/toolbar';
7
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
8
+
9
+ function sanitizeInitialValue(value) {
10
+ if (!value || !Array.isArray(value))
11
+ return [];
12
+ return value.filter((block) => {
13
+ if (!block || typeof block !== "object")
14
+ return false;
15
+ if (!("_type" in block))
16
+ return false;
17
+ if (block._type === "block" && Array.isArray(block.children)) {
18
+ block.children = block.children.map((child) => ({
19
+ ...child,
20
+ _type: child._type || "span",
21
+ text: typeof child.text === "string" ? child.text : ""
22
+ }));
23
+ }
24
+ return true;
25
+ });
26
+ }
27
+ var schemaDefinition = defineSchema({
28
+ decorators: [
29
+ { name: "strong", title: "Bold" },
30
+ { name: "em", title: "Italic" },
31
+ { name: "underline", title: "Underline" },
32
+ { name: "strikeThrough", title: "Strikethrough" },
33
+ { name: "code", title: "Code" }
34
+ ],
35
+ styles: [
36
+ { name: "normal", title: "Normal" },
37
+ { name: "h1", title: "H1" },
38
+ { name: "h2", title: "H2" },
39
+ { name: "h3", title: "H3" },
40
+ { name: "blockquote", title: "Quote" }
41
+ ],
42
+ lists: [
43
+ { name: "bullet", title: "Bullet" },
44
+ { name: "number", title: "Number" }
45
+ ],
46
+ annotations: [
47
+ {
48
+ name: "link",
49
+ title: "Link",
50
+ fields: [{ name: "href", type: "string", title: "URL" }]
51
+ }
52
+ ],
53
+ inlineObjects: [],
54
+ blockObjects: []
55
+ });
56
+ var renderStyle = (props) => {
57
+ if (props.schemaType.value === "h1") {
58
+ return /* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold mb-2", children: props.children });
59
+ }
60
+ if (props.schemaType.value === "h2") {
61
+ return /* @__PURE__ */ jsx("h2", { className: "text-xl font-bold mb-2", children: props.children });
62
+ }
63
+ if (props.schemaType.value === "h3") {
64
+ return /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold mb-1", children: props.children });
65
+ }
66
+ if (props.schemaType.value === "blockquote") {
67
+ return /* @__PURE__ */ jsx("blockquote", { className: "border-l-2 border-[var(--kyro-primary)] pl-4 italic text-[var(--kyro-text-muted)] my-2", children: props.children });
68
+ }
69
+ return /* @__PURE__ */ jsx(Fragment, { children: props.children });
70
+ };
71
+ var renderDecorator = (props) => {
72
+ if (props.value === "strong") {
73
+ return /* @__PURE__ */ jsx("strong", { children: props.children });
74
+ }
75
+ if (props.value === "em") {
76
+ return /* @__PURE__ */ jsx("em", { children: props.children });
77
+ }
78
+ if (props.value === "underline") {
79
+ return /* @__PURE__ */ jsx("u", { children: props.children });
80
+ }
81
+ if (props.value === "strikeThrough") {
82
+ return /* @__PURE__ */ jsx("s", { children: props.children });
83
+ }
84
+ if (props.value === "code") {
85
+ return /* @__PURE__ */ jsx("code", { className: "px-1 py-0.5 rounded bg-[var(--kyro-surface-accent)] text-[var(--kyro-primary)] text-sm font-mono", children: props.children });
86
+ }
87
+ return /* @__PURE__ */ jsx(Fragment, { children: props.children });
88
+ };
89
+ var renderBlock = (props) => {
90
+ return /* @__PURE__ */ jsx("div", { children: props.children });
91
+ };
92
+ var renderListItem = (props) => {
93
+ if (props.schemaType.value === "bullet") {
94
+ return /* @__PURE__ */ jsx("li", { className: "list-disc ml-4", children: props.children });
95
+ }
96
+ if (props.schemaType.value === "number") {
97
+ return /* @__PURE__ */ jsx("li", { className: "list-decimal ml-4", children: props.children });
98
+ }
99
+ return /* @__PURE__ */ jsx("li", { children: props.children });
100
+ };
101
+ var renderAnnotation = (props) => {
102
+ if (props.schemaType.name === "link") {
103
+ return /* @__PURE__ */ jsx(
104
+ "a",
105
+ {
106
+ href: props.value.href,
107
+ className: "text-[var(--kyro-primary)] underline hover:opacity-80",
108
+ target: "_blank",
109
+ rel: "noopener noreferrer",
110
+ children: props.children
111
+ }
112
+ );
113
+ }
114
+ return /* @__PURE__ */ jsx(Fragment, { children: props.children });
115
+ };
116
+ function FocusRestoringButton({
117
+ onClick,
118
+ children,
119
+ ...props
120
+ }) {
121
+ const editor = useEditor();
122
+ return /* @__PURE__ */ jsx(
123
+ "button",
124
+ {
125
+ type: "button",
126
+ onClick: (e) => {
127
+ onClick?.(e);
128
+ editor.send({ type: "focus" });
129
+ },
130
+ ...props,
131
+ children
132
+ }
133
+ );
134
+ }
135
+ var decoratorIcons = {
136
+ strong: LuBold,
137
+ em: LuItalic,
138
+ underline: LuUnderline,
139
+ strikeThrough: LuStrikethrough,
140
+ code: LuCode
141
+ };
142
+ var DecoratorButton = ({
143
+ name,
144
+ title
145
+ }) => {
146
+ const schema = useToolbarSchema({});
147
+ const decoratorSchema = schema.decorators?.find((d) => d.name === name);
148
+ if (!decoratorSchema)
149
+ return null;
150
+ const { snapshot, send } = useDecoratorButton({
151
+ schemaType: decoratorSchema
152
+ });
153
+ const Icon = decoratorIcons[name];
154
+ const isActive = snapshot.matches({ enabled: "active" }) || snapshot.matches({ disabled: "active" });
155
+ const isEnabled = snapshot.matches("enabled");
156
+ return /* @__PURE__ */ jsx(
157
+ FocusRestoringButton,
158
+ {
159
+ disabled: !isEnabled,
160
+ "data-state": isActive ? "on" : "off",
161
+ onClick: () => send({ type: "toggle" }),
162
+ title,
163
+ className: "p-1.5 rounded transition-colors disabled:opacity-30 hover:bg-[var(--kyro-surface-accent)] data-[state=on]:bg-[var(--kyro-primary)] data-[state=on]:text-[var(--kyro-sidebar-text-active)]",
164
+ children: Icon && /* @__PURE__ */ jsx(Icon, { className: "w-4 h-4" })
165
+ }
166
+ );
167
+ };
168
+ var ListButton = ({
169
+ name,
170
+ title
171
+ }) => {
172
+ const schema = useToolbarSchema({});
173
+ const listSchema = schema.lists?.find((l) => l.name === name);
174
+ if (!listSchema)
175
+ return null;
176
+ const { snapshot, send } = useListButton({ schemaType: listSchema });
177
+ const Icon = name === "bullet" ? LuList : LuListOrdered;
178
+ const isActive = snapshot.matches({ enabled: "active" }) || snapshot.matches({ disabled: "active" });
179
+ const isEnabled = snapshot.matches("enabled");
180
+ return /* @__PURE__ */ jsx(
181
+ FocusRestoringButton,
182
+ {
183
+ disabled: !isEnabled,
184
+ "data-state": isActive ? "on" : "off",
185
+ onClick: () => send({ type: "toggle" }),
186
+ title,
187
+ className: "p-1.5 rounded transition-colors disabled:opacity-30 hover:bg-[var(--kyro-surface-accent)] data-[state=on]:bg-[var(--kyro-primary)] data-[state=on]:text-[var(--kyro-sidebar-text-active)]",
188
+ children: Icon && /* @__PURE__ */ jsx(Icon, { className: "w-4 h-4" })
189
+ }
190
+ );
191
+ };
192
+ var AnnotationButton = ({
193
+ name,
194
+ title
195
+ }) => {
196
+ const schema = useToolbarSchema({});
197
+ const annotationSchema = schema.annotations?.find((a) => a.name === name);
198
+ if (!annotationSchema)
199
+ return null;
200
+ const { snapshot, send } = useAnnotationButton({
201
+ schemaType: annotationSchema
202
+ });
203
+ const isActive = snapshot.matches({ enabled: "active" }) || snapshot.matches({ disabled: "active" });
204
+ const isEnabled = snapshot.matches("enabled");
205
+ const isShowingDialog = snapshot.matches({
206
+ enabled: { inactive: "showing dialog" }
207
+ });
208
+ return /* @__PURE__ */ jsx(
209
+ FocusRestoringButton,
210
+ {
211
+ disabled: !isEnabled,
212
+ "data-state": isActive ? "on" : "off",
213
+ onClick: () => send({
214
+ type: isShowingDialog ? "close dialog" : "open dialog"
215
+ }),
216
+ title,
217
+ className: "p-1.5 rounded transition-colors disabled:opacity-30 hover:bg-[var(--kyro-surface-accent)] data-[state=on]:bg-[var(--kyro-primary)] data-[state=on]:text-[var(--kyro-sidebar-text-active)]",
218
+ children: name === "link" && /* @__PURE__ */ jsx(LuLink, { className: "w-4 h-4" })
219
+ }
220
+ );
221
+ };
222
+ var LinkDialog = () => {
223
+ const schema = useToolbarSchema({});
224
+ const popover = useAnnotationPopover({
225
+ schemaTypes: schema.annotations || []
226
+ });
227
+ const dialogRef = useRef(null);
228
+ const inputRef = useRef(null);
229
+ useEffect(() => {
230
+ if (popover.snapshot.matches({ enabled: "active" }) && inputRef.current) {
231
+ inputRef.current.focus();
232
+ }
233
+ }, [popover.snapshot]);
234
+ if (!popover.snapshot.matches({ enabled: "active" }))
235
+ return null;
236
+ const activeAnnotations = popover.snapshot.context.annotations || [];
237
+ const activeLink = activeAnnotations.find(
238
+ (a) => a.schemaType.name === "link"
239
+ );
240
+ const currentHref = activeLink?.value?.href;
241
+ const handleSubmit = (e) => {
242
+ e.preventDefault();
243
+ const formData = new FormData(e.target);
244
+ const href = formData.get("href");
245
+ if (activeLink) {
246
+ if (href.trim()) {
247
+ popover.send({
248
+ type: "edit",
249
+ at: activeLink.at,
250
+ props: { href: href.trim() }
251
+ });
252
+ } else {
253
+ popover.send({
254
+ type: "remove",
255
+ schemaType: activeLink.schemaType
256
+ });
257
+ }
258
+ } else {
259
+ if (href.trim()) {
260
+ popover.send({
261
+ type: "edit",
262
+ at: [],
263
+ props: { href: href.trim() }
264
+ });
265
+ }
266
+ }
267
+ popover.send({ type: "close" });
268
+ };
269
+ const handleRemove = () => {
270
+ if (activeLink) {
271
+ popover.send({
272
+ type: "remove",
273
+ schemaType: activeLink.schemaType
274
+ });
275
+ }
276
+ popover.send({ type: "close" });
277
+ };
278
+ return /* @__PURE__ */ jsxs(
279
+ "div",
280
+ {
281
+ ref: dialogRef,
282
+ className: "absolute top-full left-0 z-50 mt-1 w-72 p-3 rounded-lg border border-[var(--kyro-border)] bg-[var(--kyro-bg-primary)] shadow-lg",
283
+ children: [
284
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-2", children: [
285
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-[var(--kyro-text-primary)]", children: "Link" }),
286
+ /* @__PURE__ */ jsx(
287
+ "button",
288
+ {
289
+ type: "button",
290
+ onClick: () => popover.send({ type: "close" }),
291
+ className: "p-1 rounded hover:bg-[var(--kyro-surface-accent)]",
292
+ children: /* @__PURE__ */ jsx(LuX, { className: "w-3.5 h-3.5 text-[var(--kyro-text-muted)]" })
293
+ }
294
+ )
295
+ ] }),
296
+ /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "space-y-2", children: [
297
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-1.5", children: [
298
+ /* @__PURE__ */ jsx(
299
+ "input",
300
+ {
301
+ ref: inputRef,
302
+ name: "href",
303
+ type: "url",
304
+ defaultValue: currentHref || "https://",
305
+ placeholder: "Enter URL...",
306
+ className: "flex-1 px-2.5 py-1.5 text-sm rounded border border-[var(--kyro-border)] bg-transparent text-[var(--kyro-text-primary)] placeholder:text-[var(--kyro-text-muted)] focus:outline-none focus:ring-1 focus:ring-[var(--kyro-primary)]"
307
+ }
308
+ ),
309
+ /* @__PURE__ */ jsx(
310
+ "button",
311
+ {
312
+ type: "submit",
313
+ className: "px-2.5 py-1.5 text-sm rounded bg-[var(--kyro-primary)] text-[var(--kyro-sidebar-text-active)] hover:opacity-90",
314
+ children: activeLink ? "Update" : "Add"
315
+ }
316
+ )
317
+ ] }),
318
+ activeLink && /* @__PURE__ */ jsxs(
319
+ "button",
320
+ {
321
+ type: "button",
322
+ onClick: handleRemove,
323
+ className: "w-full text-xs text-[var(--kyro-error)] hover:opacity-80 flex items-center justify-center gap-1 py-1",
324
+ children: [
325
+ /* @__PURE__ */ jsx(LuExternalLink, { className: "w-3 h-3" }),
326
+ "Remove link"
327
+ ]
328
+ }
329
+ )
330
+ ] })
331
+ ]
332
+ }
333
+ );
334
+ };
335
+ var StyleSelector = () => {
336
+ const schema = useToolbarSchema({});
337
+ const editor = useEditor();
338
+ const { snapshot, send } = useStyleSelector({
339
+ schemaTypes: schema.styles || []
340
+ });
341
+ if (!snapshot.matches("enabled"))
342
+ return null;
343
+ const activeStyle = snapshot.context.activeStyle || "normal";
344
+ return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
345
+ /* @__PURE__ */ jsx(
346
+ "select",
347
+ {
348
+ value: activeStyle,
349
+ onChange: (e) => {
350
+ send({ type: "toggle", style: e.target.value });
351
+ editor.send({ type: "focus" });
352
+ },
353
+ className: "appearance-none bg-transparent text-sm pr-6 pl-2 py-1 rounded hover:bg-[var(--kyro-surface-accent)] cursor-pointer focus:outline-none focus:ring-1 focus:ring-[var(--kyro-primary)]",
354
+ children: schema.styles?.map((s) => /* @__PURE__ */ jsx("option", { value: s.name, children: s.title }, s.name))
355
+ }
356
+ ),
357
+ /* @__PURE__ */ jsx(LuChevronDown, { className: "w-3 h-3 absolute right-1.5 top-1/2 -translate-y-1/2 pointer-events-none text-[var(--kyro-text-muted)]" })
358
+ ] });
359
+ };
360
+ var Toolbar = () => {
361
+ const { snapshot: historySnapshot, send: historySend } = useHistoryButtons();
362
+ return /* @__PURE__ */ jsxs("div", { className: "relative flex items-center gap-0.5 p-1.5 border-b border-[var(--kyro-border)]", children: [
363
+ /* @__PURE__ */ jsx(
364
+ FocusRestoringButton,
365
+ {
366
+ disabled: !historySnapshot.matches("enabled"),
367
+ onClick: () => historySend({ type: "history.undo" }),
368
+ title: "Undo",
369
+ className: "p-1.5 rounded transition-colors hover:bg-[var(--kyro-surface-accent)] disabled:opacity-30",
370
+ children: /* @__PURE__ */ jsx(LuUndo, { className: "w-4 h-4 text-[var(--kyro-text-secondary)]" })
371
+ }
372
+ ),
373
+ /* @__PURE__ */ jsx(
374
+ FocusRestoringButton,
375
+ {
376
+ disabled: !historySnapshot.matches("enabled"),
377
+ onClick: () => historySend({ type: "history.redo" }),
378
+ title: "Redo",
379
+ className: "p-1.5 rounded transition-colors hover:bg-[var(--kyro-surface-accent)] disabled:opacity-30",
380
+ children: /* @__PURE__ */ jsx(LuRedo, { className: "w-4 h-4 text-[var(--kyro-text-secondary)]" })
381
+ }
382
+ ),
383
+ /* @__PURE__ */ jsx("div", { className: "w-px h-5 bg-[var(--kyro-border)] mx-1" }),
384
+ /* @__PURE__ */ jsx(StyleSelector, {}),
385
+ /* @__PURE__ */ jsx("div", { className: "w-px h-5 bg-[var(--kyro-border)] mx-1" }),
386
+ /* @__PURE__ */ jsx(DecoratorButton, { name: "strong", title: "Bold" }),
387
+ /* @__PURE__ */ jsx(DecoratorButton, { name: "em", title: "Italic" }),
388
+ /* @__PURE__ */ jsx(DecoratorButton, { name: "underline", title: "Underline" }),
389
+ /* @__PURE__ */ jsx(DecoratorButton, { name: "strikeThrough", title: "Strikethrough" }),
390
+ /* @__PURE__ */ jsx(DecoratorButton, { name: "code", title: "Code" }),
391
+ /* @__PURE__ */ jsx("div", { className: "w-px h-5 bg-[var(--kyro-border)] mx-1" }),
392
+ /* @__PURE__ */ jsx(AnnotationButton, { name: "link", title: "Link" }),
393
+ /* @__PURE__ */ jsx("div", { className: "w-px h-5 bg-[var(--kyro-border)] mx-1" }),
394
+ /* @__PURE__ */ jsx(ListButton, { name: "bullet", title: "Bullet List" }),
395
+ /* @__PURE__ */ jsx(ListButton, { name: "number", title: "Numbered List" }),
396
+ /* @__PURE__ */ jsx(LinkDialog, {})
397
+ ] });
398
+ };
399
+ var EditorInner = ({ onChange, disabled }) => {
400
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
401
+ /* @__PURE__ */ jsx(Toolbar, {}),
402
+ /* @__PURE__ */ jsx(
403
+ PortableTextEditable,
404
+ {
405
+ className: "min-h-[200px] p-4 focus:outline-none text-[var(--kyro-text-primary)]",
406
+ placeholder: "Start typing...",
407
+ readOnly: disabled,
408
+ renderStyle,
409
+ renderDecorator,
410
+ renderBlock,
411
+ renderListItem,
412
+ renderAnnotation
413
+ }
414
+ )
415
+ ] });
416
+ };
417
+ var EditorClient = ({
418
+ initialValue,
419
+ onChange,
420
+ disabled
421
+ }) => {
422
+ const [value, setValue] = useState(() => sanitizeInitialValue(initialValue));
423
+ const prevInitialValueRef = useRef(initialValue);
424
+ useEffect(() => {
425
+ const sanitized = sanitizeInitialValue(initialValue);
426
+ const prevSanitized = sanitizeInitialValue(prevInitialValueRef.current);
427
+ if (JSON.stringify(sanitized) !== JSON.stringify(prevSanitized)) {
428
+ setValue(sanitized);
429
+ prevInitialValueRef.current = initialValue;
430
+ }
431
+ }, [initialValue]);
432
+ const handleChange = useCallback(
433
+ (newValue) => {
434
+ setValue(newValue);
435
+ onChange(newValue);
436
+ },
437
+ [onChange]
438
+ );
439
+ return /* @__PURE__ */ jsxs(
440
+ EditorProvider,
441
+ {
442
+ initialConfig: {
443
+ schemaDefinition,
444
+ initialValue: value
445
+ },
446
+ children: [
447
+ /* @__PURE__ */ jsx(
448
+ EventListenerPlugin,
449
+ {
450
+ on: (event) => {
451
+ if (event.type === "mutation" && event.value) {
452
+ handleChange(event.value);
453
+ }
454
+ }
455
+ }
456
+ ),
457
+ /* @__PURE__ */ jsx(EditorInner, { onChange: handleChange, disabled })
458
+ ]
459
+ },
460
+ JSON.stringify(value)
461
+ );
462
+ };
463
+
464
+ export { EditorClient };
465
+ //# sourceMappingURL=out.js.map
466
+ //# sourceMappingURL=EditorClient-XEUOVAAC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/fields/EditorClient.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, {\n useState,\n useCallback,\n useRef,\n useEffect,\n useMemo,\n} from \"react\";\nimport {\n EditorProvider,\n PortableTextEditable,\n useEditor,\n type RenderStyleFunction,\n type RenderDecoratorFunction,\n type RenderBlockFunction,\n type RenderListItemFunction,\n type RenderAnnotationFunction,\n} from \"@portabletext/editor\";\nimport { defineSchema } from \"@portabletext/schema\";\nimport { EventListenerPlugin } from \"@portabletext/editor/plugins\";\nimport {\n useToolbarSchema,\n useDecoratorButton,\n useListButton,\n useStyleSelector,\n useHistoryButtons,\n useAnnotationButton,\n useAnnotationPopover,\n} from \"@portabletext/toolbar\";\nimport {\n Bold,\n Italic,\n Underline,\n Strikethrough,\n Code,\n Link,\n List,\n ListOrdered,\n Undo,\n Redo,\n ChevronDown,\n X,\n ExternalLink,\n} from \"../ui/icons\";\n\ninterface EditorClientProps {\n initialValue: Record<string, unknown>[];\n onChange: (blocks: Record<string, unknown>[]) => void;\n disabled?: boolean;\n}\n\nfunction sanitizeInitialValue(value: unknown): Record<string, unknown>[] {\n if (!value || !Array.isArray(value)) return [];\n return (value as Record<string, unknown>[]).filter((block) => {\n if (!block || typeof block !== \"object\") return false;\n if (!(\"_type\" in block)) return false;\n if ((block as { _type?: string })._type === \"block\" && Array.isArray((block as { children?: unknown[] }).children)) {\n (block as { children: Record<string, unknown>[] }).children = (block.children as Record<string, unknown>[]).map((child) => ({\n ...child,\n _type: (child as { _type?: string })._type || \"span\",\n text: typeof (child as { text?: unknown }).text === \"string\" ? child.text : \"\",\n }));\n }\n return true;\n });\n}\n\nconst schemaDefinition = defineSchema({\n decorators: [\n { name: \"strong\", title: \"Bold\" },\n { name: \"em\", title: \"Italic\" },\n { name: \"underline\", title: \"Underline\" },\n { name: \"strikeThrough\", title: \"Strikethrough\" },\n { name: \"code\", title: \"Code\" },\n ],\n styles: [\n { name: \"normal\", title: \"Normal\" },\n { name: \"h1\", title: \"H1\" },\n { name: \"h2\", title: \"H2\" },\n { name: \"h3\", title: \"H3\" },\n { name: \"blockquote\", title: \"Quote\" },\n ],\n lists: [\n { name: \"bullet\", title: \"Bullet\" },\n { name: \"number\", title: \"Number\" },\n ],\n annotations: [\n {\n name: \"link\",\n title: \"Link\",\n fields: [{ name: \"href\", type: \"string\", title: \"URL\" }],\n },\n ],\n inlineObjects: [],\n blockObjects: [],\n});\n\nconst renderStyle: RenderStyleFunction = (props) => {\n if (props.schemaType.value === \"h1\") {\n return <h1 className=\"text-2xl font-bold mb-2\">{props.children}</h1>;\n }\n if (props.schemaType.value === \"h2\") {\n return <h2 className=\"text-xl font-bold mb-2\">{props.children}</h2>;\n }\n if (props.schemaType.value === \"h3\") {\n return <h3 className=\"text-lg font-semibold mb-1\">{props.children}</h3>;\n }\n if (props.schemaType.value === \"blockquote\") {\n return (\n <blockquote className=\"border-l-2 border-[var(--kyro-primary)] pl-4 italic text-[var(--kyro-text-muted)] my-2\">\n {props.children}\n </blockquote>\n );\n }\n return <>{props.children}</>;\n};\n\nconst renderDecorator: RenderDecoratorFunction = (props) => {\n if (props.value === \"strong\") {\n return <strong>{props.children}</strong>;\n }\n if (props.value === \"em\") {\n return <em>{props.children}</em>;\n }\n if (props.value === \"underline\") {\n return <u>{props.children}</u>;\n }\n if (props.value === \"strikeThrough\") {\n return <s>{props.children}</s>;\n }\n if (props.value === \"code\") {\n return (\n <code className=\"px-1 py-0.5 rounded bg-[var(--kyro-surface-accent)] text-[var(--kyro-primary)] text-sm font-mono\">\n {props.children}\n </code>\n );\n }\n return <>{props.children}</>;\n};\n\nconst renderBlock: RenderBlockFunction = (props) => {\n return <div>{props.children}</div>;\n};\n\nconst renderListItem: RenderListItemFunction = (props) => {\n if (props.schemaType.value === \"bullet\") {\n return <li className=\"list-disc ml-4\">{props.children}</li>;\n }\n if (props.schemaType.value === \"number\") {\n return <li className=\"list-decimal ml-4\">{props.children}</li>;\n }\n return <li>{props.children}</li>;\n};\n\nconst renderAnnotation: RenderAnnotationFunction = (props) => {\n if (props.schemaType.name === \"link\") {\n return (\n <a\n href={props.value.href as string}\n className=\"text-[var(--kyro-primary)] underline hover:opacity-80\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n {props.children}\n </a>\n );\n }\n return <>{props.children}</>;\n};\n\nfunction FocusRestoringButton({\n onClick,\n children,\n ...props\n}: React.ButtonHTMLAttributes<HTMLButtonElement>) {\n const editor = useEditor();\n return (\n <button\n type=\"button\"\n onClick={(e) => {\n onClick?.(e);\n editor.send({ type: \"focus\" });\n }}\n {...props}\n >\n {children}\n </button>\n );\n}\n\nconst decoratorIcons: Record<\n string,\n React.ComponentType<{ className?: string }>\n> = {\n strong: Bold,\n em: Italic,\n underline: Underline,\n strikeThrough: Strikethrough,\n code: Code,\n};\n\nconst DecoratorButton: React.FC<{ name: string; title: string }> = ({\n name,\n title,\n}) => {\n const schema = useToolbarSchema({});\n const decoratorSchema = schema.decorators?.find((d) => d.name === name);\n if (!decoratorSchema) return null;\n const { snapshot, send } = useDecoratorButton({\n schemaType: decoratorSchema,\n });\n const Icon = decoratorIcons[name];\n const isActive =\n snapshot.matches({ enabled: \"active\" }) ||\n snapshot.matches({ disabled: \"active\" });\n const isEnabled = snapshot.matches(\"enabled\");\n\n return (\n <FocusRestoringButton\n disabled={!isEnabled}\n data-state={isActive ? \"on\" : \"off\"}\n onClick={() => send({ type: \"toggle\" })}\n title={title}\n className=\"p-1.5 rounded transition-colors disabled:opacity-30 hover:bg-[var(--kyro-surface-accent)] data-[state=on]:bg-[var(--kyro-primary)] data-[state=on]:text-[var(--kyro-sidebar-text-active)]\"\n >\n {Icon && <Icon className=\"w-4 h-4\" />}\n </FocusRestoringButton>\n );\n};\n\nconst ListButton: React.FC<{ name: string; title: string }> = ({\n name,\n title,\n}) => {\n const schema = useToolbarSchema({});\n const listSchema = schema.lists?.find((l) => l.name === name);\n if (!listSchema) return null;\n const { snapshot, send } = useListButton({ schemaType: listSchema });\n const Icon = name === \"bullet\" ? List : ListOrdered;\n const isActive =\n snapshot.matches({ enabled: \"active\" }) ||\n snapshot.matches({ disabled: \"active\" });\n const isEnabled = snapshot.matches(\"enabled\");\n\n return (\n <FocusRestoringButton\n disabled={!isEnabled}\n data-state={isActive ? \"on\" : \"off\"}\n onClick={() => send({ type: \"toggle\" })}\n title={title}\n className=\"p-1.5 rounded transition-colors disabled:opacity-30 hover:bg-[var(--kyro-surface-accent)] data-[state=on]:bg-[var(--kyro-primary)] data-[state=on]:text-[var(--kyro-sidebar-text-active)]\"\n >\n {Icon && <Icon className=\"w-4 h-4\" />}\n </FocusRestoringButton>\n );\n};\n\nconst AnnotationButton: React.FC<{ name: string; title: string }> = ({\n name,\n title,\n}) => {\n const schema = useToolbarSchema({});\n const annotationSchema = schema.annotations?.find((a) => a.name === name);\n if (!annotationSchema) return null;\n const { snapshot, send } = useAnnotationButton({\n schemaType: annotationSchema,\n });\n const isActive =\n snapshot.matches({ enabled: \"active\" }) ||\n snapshot.matches({ disabled: \"active\" });\n const isEnabled = snapshot.matches(\"enabled\");\n const isShowingDialog = snapshot.matches({\n enabled: { inactive: \"showing dialog\" },\n });\n\n return (\n <FocusRestoringButton\n disabled={!isEnabled}\n data-state={isActive ? \"on\" : \"off\"}\n onClick={() =>\n send({\n type: isShowingDialog ? \"close dialog\" : \"open dialog\",\n })\n }\n title={title}\n className=\"p-1.5 rounded transition-colors disabled:opacity-30 hover:bg-[var(--kyro-surface-accent)] data-[state=on]:bg-[var(--kyro-primary)] data-[state=on]:text-[var(--kyro-sidebar-text-active)]\"\n >\n {name === \"link\" && <Link className=\"w-4 h-4\" />}\n </FocusRestoringButton>\n );\n};\n\nconst LinkDialog: React.FC = () => {\n const schema = useToolbarSchema({});\n const popover = useAnnotationPopover({\n schemaTypes: schema.annotations || [],\n });\n const dialogRef = useRef<HTMLDivElement>(null);\n const inputRef = useRef<HTMLInputElement>(null);\n\n useEffect(() => {\n if (popover.snapshot.matches({ enabled: \"active\" }) && inputRef.current) {\n inputRef.current.focus();\n }\n }, [popover.snapshot]);\n\n if (!popover.snapshot.matches({ enabled: \"active\" })) return null;\n\n const activeAnnotations = popover.snapshot.context.annotations || [];\n const activeLink = activeAnnotations.find(\n (a) => a.schemaType.name === \"link\",\n );\n const currentHref = activeLink?.value?.href as string | undefined;\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n const formData = new FormData(e.target as HTMLFormElement);\n const href = formData.get(\"href\") as string;\n\n if (activeLink) {\n if (href.trim()) {\n popover.send({\n type: \"edit\",\n at: activeLink.at,\n props: { href: href.trim() },\n });\n } else {\n popover.send({\n type: \"remove\",\n schemaType: activeLink.schemaType,\n });\n }\n } else {\n if (href.trim()) {\n popover.send({\n type: \"edit\",\n at: [] as string[],\n props: { href: href.trim() },\n });\n }\n }\n popover.send({ type: \"close\" });\n };\n\n const handleRemove = () => {\n if (activeLink) {\n popover.send({\n type: \"remove\",\n schemaType: activeLink.schemaType,\n });\n }\n popover.send({ type: \"close\" });\n };\n\n return (\n <div\n ref={dialogRef}\n className=\"absolute top-full left-0 z-50 mt-1 w-72 p-3 rounded-lg border border-[var(--kyro-border)] bg-[var(--kyro-bg-primary)] shadow-lg\"\n >\n <div className=\"flex items-center justify-between mb-2\">\n <span className=\"text-sm font-medium text-[var(--kyro-text-primary)]\">\n Link\n </span>\n <button\n type=\"button\"\n onClick={() => popover.send({ type: \"close\" })}\n className=\"p-1 rounded hover:bg-[var(--kyro-surface-accent)]\"\n >\n <X className=\"w-3.5 h-3.5 text-[var(--kyro-text-muted)]\" />\n </button>\n </div>\n <form onSubmit={handleSubmit} className=\"space-y-2\">\n <div className=\"flex gap-1.5\">\n <input\n ref={inputRef}\n name=\"href\"\n type=\"url\"\n defaultValue={currentHref || \"https://\"}\n placeholder=\"Enter URL...\"\n className=\"flex-1 px-2.5 py-1.5 text-sm rounded border border-[var(--kyro-border)] bg-transparent text-[var(--kyro-text-primary)] placeholder:text-[var(--kyro-text-muted)] focus:outline-none focus:ring-1 focus:ring-[var(--kyro-primary)]\"\n />\n <button\n type=\"submit\"\n className=\"px-2.5 py-1.5 text-sm rounded bg-[var(--kyro-primary)] text-[var(--kyro-sidebar-text-active)] hover:opacity-90\"\n >\n {activeLink ? \"Update\" : \"Add\"}\n </button>\n </div>\n {activeLink && (\n <button\n type=\"button\"\n onClick={handleRemove}\n className=\"w-full text-xs text-[var(--kyro-error)] hover:opacity-80 flex items-center justify-center gap-1 py-1\"\n >\n <ExternalLink className=\"w-3 h-3\" />\n Remove link\n </button>\n )}\n </form>\n </div>\n );\n};\n\nconst StyleSelector: React.FC = () => {\n const schema = useToolbarSchema({});\n const editor = useEditor();\n const { snapshot, send } = useStyleSelector({\n schemaTypes: schema.styles || [],\n });\n if (!snapshot.matches(\"enabled\")) return null;\n const activeStyle = snapshot.context.activeStyle || \"normal\";\n\n return (\n <div className=\"relative\">\n <select\n value={activeStyle}\n onChange={(e) => {\n send({ type: \"toggle\", style: e.target.value as string });\n editor.send({ type: \"focus\" });\n }}\n className=\"appearance-none bg-transparent text-sm pr-6 pl-2 py-1 rounded hover:bg-[var(--kyro-surface-accent)] cursor-pointer focus:outline-none focus:ring-1 focus:ring-[var(--kyro-primary)]\"\n >\n {schema.styles?.map((s) => (\n <option key={s.name} value={s.name}>\n {s.title}\n </option>\n ))}\n </select>\n <ChevronDown className=\"w-3 h-3 absolute right-1.5 top-1/2 -translate-y-1/2 pointer-events-none text-[var(--kyro-text-muted)]\" />\n </div>\n );\n};\n\nconst Toolbar: React.FC = () => {\n const { snapshot: historySnapshot, send: historySend } = useHistoryButtons();\n return (\n <div className=\"relative flex items-center gap-0.5 p-1.5 border-b border-[var(--kyro-border)]\">\n <FocusRestoringButton\n disabled={!historySnapshot.matches(\"enabled\")}\n onClick={() => historySend({ type: \"history.undo\" })}\n title=\"Undo\"\n className=\"p-1.5 rounded transition-colors hover:bg-[var(--kyro-surface-accent)] disabled:opacity-30\"\n >\n <Undo className=\"w-4 h-4 text-[var(--kyro-text-secondary)]\" />\n </FocusRestoringButton>\n <FocusRestoringButton\n disabled={!historySnapshot.matches(\"enabled\")}\n onClick={() => historySend({ type: \"history.redo\" })}\n title=\"Redo\"\n className=\"p-1.5 rounded transition-colors hover:bg-[var(--kyro-surface-accent)] disabled:opacity-30\"\n >\n <Redo className=\"w-4 h-4 text-[var(--kyro-text-secondary)]\" />\n </FocusRestoringButton>\n <div className=\"w-px h-5 bg-[var(--kyro-border)] mx-1\" />\n <StyleSelector />\n <div className=\"w-px h-5 bg-[var(--kyro-border)] mx-1\" />\n <DecoratorButton name=\"strong\" title=\"Bold\" />\n <DecoratorButton name=\"em\" title=\"Italic\" />\n <DecoratorButton name=\"underline\" title=\"Underline\" />\n <DecoratorButton name=\"strikeThrough\" title=\"Strikethrough\" />\n <DecoratorButton name=\"code\" title=\"Code\" />\n <div className=\"w-px h-5 bg-[var(--kyro-border)] mx-1\" />\n <AnnotationButton name=\"link\" title=\"Link\" />\n <div className=\"w-px h-5 bg-[var(--kyro-border)] mx-1\" />\n <ListButton name=\"bullet\" title=\"Bullet List\" />\n <ListButton name=\"number\" title=\"Numbered List\" />\n <LinkDialog />\n </div>\n );\n};\n\nconst EditorInner: React.FC<{\n onChange: (blocks: Record<string, unknown>[]) => void;\n disabled?: boolean;\n}> = ({ onChange, disabled }) => {\n return (\n <>\n <Toolbar />\n <PortableTextEditable\n className=\"min-h-[200px] p-4 focus:outline-none text-[var(--kyro-text-primary)]\"\n placeholder=\"Start typing...\"\n readOnly={disabled}\n renderStyle={renderStyle}\n renderDecorator={renderDecorator}\n renderBlock={renderBlock}\n renderListItem={renderListItem}\n renderAnnotation={renderAnnotation}\n />\n </>\n );\n};\n\nexport const EditorClient: React.FC<EditorClientProps> = ({\n initialValue,\n onChange,\n disabled,\n}) => {\n const [value, setValue] = useState(() => sanitizeInitialValue(initialValue));\n const prevInitialValueRef = useRef(initialValue);\n\n useEffect(() => {\n const sanitized = sanitizeInitialValue(initialValue);\n const prevSanitized = sanitizeInitialValue(prevInitialValueRef.current);\n if (JSON.stringify(sanitized) !== JSON.stringify(prevSanitized)) {\n setValue(sanitized);\n prevInitialValueRef.current = initialValue;\n }\n }, [initialValue]);\n\nconst handleChange = useCallback(\n (newValue: Record<string, unknown>[]) => {\n setValue(newValue);\n onChange(newValue);\n },\n [onChange],\n );\n\n return (\n <EditorProvider\n key={JSON.stringify(value)}\n initialConfig={{\n schemaDefinition: schemaDefinition as Record<string, unknown>,\n initialValue: value,\n }}\n >\n <EventListenerPlugin\n on={(event: Record<string, unknown>) => {\n if (event.type === \"mutation\" && event.value) {\n handleChange(event.value as Record<string, unknown>);\n }\n }}\n />\n<EditorInner onChange={handleChange} disabled={disabled} />\n </EditorProvider>\n);\n};\n"]}